Skip to content

Commit 29ba18e

Browse files
committed
Make special version a set of versions
Special versions are made a set of versions. Those are aliases with which a single package can be referred. For example, a package can be simultaneously versions: * 0.1.0 - the normal version from the Nimble file. * #head - the latest commit in the main branch * #master - the main branch name * 3c91b869 - part of the sha1 hash of the latest commit in the main branch When the same package is downloaded a second time (determined by the checksum) instead of proposing to replace it just print a warning that the package is already installed and merge the special version of the new package with a special version of the already installed one. Additionally this commit: - Removes some legacy code for supporting the old package format in the reverse dependencies. - The names of the packages in the reverse dependencies are written without converting to lower case. - The tests are fixed according to the new behavior. Related to #127
1 parent ace80a4 commit 29ba18e

File tree

12 files changed

+183
-107
lines changed

12 files changed

+183
-107
lines changed

src/nimble.nim

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ proc processFreeDependencies(pkgInfo: PackageInfo, options: Options):
7676
var pkgList {.global.}: seq[PackageInfo] = @[]
7777
once: pkgList = initPkgList(pkgInfo, options)
7878

79-
display("Verifying",
80-
"dependencies for $1@$2" % [pkgInfo.basicInfo.name, $pkgInfo.metaData.specialVersion],
79+
display("Verifying", "dependencies for $1@$2" %
80+
[pkgInfo.basicInfo.name, $pkgInfo.basicInfo.version],
8181
priority = HighPriority)
8282

8383
var reverseDependencies: seq[PackageBasicInfo] = @[]
@@ -106,7 +106,15 @@ proc processFreeDependencies(pkgInfo: PackageInfo, options: Options):
106106
let (packages, installedPkg) = install(toInstall, options,
107107
doPrompt = false, first = false, fromLockFile = false)
108108

109-
result.incl packages
109+
for pkg in packages:
110+
if result.contains pkg:
111+
# If the result already contains the newly tried to install package
112+
# we had to merge its special versions set into the set of the old
113+
# one.
114+
result[pkg].metaData.specialVersions.incl(
115+
pkg.metaData.specialVersions)
116+
else:
117+
result.incl pkg
110118

111119
pkg = installedPkg # For addRevDep
112120
fillMetaData(pkg, pkg.getRealDir(), false)
@@ -119,7 +127,7 @@ proc processFreeDependencies(pkgInfo: PackageInfo, options: Options):
119127
# Process the dependencies of this dependency.
120128
result.incl processFreeDependencies(pkg.toFullInfo(options), options)
121129
if not pkg.isLink:
122-
reverseDependencies.add((pkg.basicInfo.name, pkg.metaData.specialVersion, pkg.basicInfo.checksum))
130+
reverseDependencies.add(pkg.basicInfo)
123131

124132
# Check if two packages of the same name (but different version) are listed
125133
# in the path.
@@ -260,27 +268,24 @@ proc removePackage(pkgInfo: PackageInfo, options: Options) =
260268
reinstallSymlinksForOlderVersion(pkgDestDir, options)
261269
options.nimbleData.removeRevDep(pkgInfo)
262270

263-
proc packageExists(pkgInfo: PackageInfo, options: Options): bool =
264-
let pkgDestDir = pkgInfo.getPkgDest(options)
265-
return fileExists(pkgDestDir / packageMetaDataFileName)
266-
267-
proc promptOverwriteExistingPackage(pkgInfo: PackageInfo,
268-
options: Options): bool =
269-
let message = "$1@$2 already exists. Overwrite?" %
270-
[pkgInfo.basicInfo.name, $pkgInfo.metaData.specialVersion]
271-
return options.prompt(message)
272-
273-
proc removeOldPackage(pkgInfo: PackageInfo, options: Options) =
271+
proc packageExists(pkgInfo: PackageInfo, options: Options):
272+
Option[PackageInfo] =
273+
## Checks whether a package `pkgInfo` already exists in the Nimble cache. If a
274+
## package already exists returns the `PackageInfo` of the package in the
275+
## cache otherwise returns `none`. Raises a `NimbleError` in the case the
276+
## package exists in the cache but it is not valid.
274277
let pkgDestDir = pkgInfo.getPkgDest(options)
275-
let oldPkgInfo = getPkgInfo(pkgDestDir, options)
276-
removePackage(oldPkgInfo, options)
277-
278-
proc promptRemovePackageIfExists(pkgInfo: PackageInfo, options: Options): bool =
279-
if packageExists(pkgInfo, options):
280-
if not promptOverwriteExistingPackage(pkgInfo, options):
281-
return false
282-
removeOldPackage(pkgInfo, options)
283-
return true
278+
if not fileExists(pkgDestDir / packageMetaDataFileName):
279+
return none[PackageInfo]()
280+
else:
281+
var oldPkgInfo = initPackageInfo()
282+
try:
283+
oldPkgInfo = pkgDestDir.getPkgInfo(options)
284+
except CatchableError as error:
285+
raise nimbleError(&"The package inside \"{pkgDestDir}\" is invalid.",
286+
details = error)
287+
fillMetaData(oldPkgInfo, pkgDestDir, true)
288+
return some(oldPkgInfo)
284289

285290
proc processLockedDependencies(pkgInfo: PackageInfo, options: Options):
286291
HashSet[PackageInfo]
@@ -329,9 +334,10 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
329334
var depsOptions = options
330335
depsOptions.depsOnly = false
331336

332-
# Overwrite the version if the requested version is "#head" or similar.
333337
if requestedVer.kind == verSpecial:
334-
pkgInfo.metaData.specialVersion = requestedVer.spe
338+
# Add a version alias to special versions set if requested version is a
339+
# special one.
340+
pkgInfo.metaData.specialVersions.incl requestedVer.spe
335341

336342
# Dependencies need to be processed before the creation of the pkg dir.
337343
if first and pkgInfo.lockedDeps.len > 0:
@@ -343,10 +349,24 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
343349
result.pkg = pkgInfo
344350
return result
345351

346-
display("Installing", "$1@$2" % [pkginfo.basicInfo.name, $pkginfo.metaData.specialVersion],
347-
priority = HighPriority)
352+
display("Installing", "$1@$2" %
353+
[pkginfo.basicInfo.name, $pkginfo.basicInfo.version],
354+
priority = HighPriority)
348355

349-
let isPackageAlreadyInCache = pkgInfo.packageExists(options)
356+
let oldPkg = pkgInfo.packageExists(options)
357+
if oldPkg.isSome:
358+
# In the case we already have the same package in the cache then only merge
359+
# the new package special versions to the old one.
360+
displayWarning(pkgAlreadyExistsInTheCacheMsg(pkgInfo))
361+
var oldPkg = oldPkg.get
362+
oldPkg.metaData.specialVersions.incl pkgInfo.metaData.specialVersions
363+
saveMetaData(oldPkg.metaData, oldPkg.getNimbleFileDir, changeRoots = false)
364+
if result.deps.contains oldPkg:
365+
result.deps[oldPkg].metaData.specialVersions.incl(
366+
oldPkg.metaData.specialVersions)
367+
result.deps.incl oldPkg
368+
result.pkg = oldPkg
369+
return
350370

351371
# Build before removing an existing package (if one exists). This way
352372
# if the build fails then the old package will still be installed.
@@ -361,8 +381,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
361381
try:
362382
buildFromDir(pkgInfo, paths, "-d:release" & flags, options)
363383
except CatchableError:
364-
if not isPackageAlreadyInCache:
365-
removeRevDep(options.nimbleData, pkgInfo)
384+
removeRevDep(options.nimbleData, pkgInfo)
366385
raise
367386

368387
let pkgDestDir = pkgInfo.getPkgDest(options)
@@ -374,9 +393,6 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
374393
# Don't copy artifacts if project local deps mode and "installing" the top
375394
# level package.
376395
if not (options.localdeps and options.isInstallingTopLevel(dir)):
377-
if not promptRemovePackageIfExists(pkgInfo, options):
378-
return
379-
380396
createDir(pkgDestDir)
381397
# Copy this package's files based on the preferences specified in PkgInfo.
382398
var filesInstalled: HashSet[string]
@@ -797,18 +813,22 @@ proc list(options: Options) =
797813
echo(" ")
798814

799815
proc listInstalled(options: Options) =
800-
var h: OrderedTable[string, seq[Version]]
816+
type
817+
VersionChecksumTuple = tuple[version: Version, checksum: Sha1Hash]
818+
var h: OrderedTable[string, seq[VersionChecksumTuple]]
801819
let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
802820
for pkg in pkgs:
803821
let
804822
pName = pkg.basicInfo.name
805-
pVer = pkg.metaData.specialVersion
823+
pVersion = pkg.basicInfo.version
824+
pChecksum = pkg.basicInfo.checksum
806825
if not h.hasKey(pName): h[pName] = @[]
807826
var s = h[pName]
808-
add(s, pVer)
827+
add(s, (pVersion, pChecksum))
809828
h[pName] = s
810829

811-
h.sort(proc (a, b: (string, seq[Version])): int = cmpIgnoreCase(a[0], b[0]))
830+
h.sort(proc (a, b: (string, seq[VersionChecksumTuple])): int =
831+
cmpIgnoreCase(a[0], b[0]))
812832
for k in keys(h):
813833
echo k & " [" & h[k].join(", ") & "]"
814834

@@ -837,7 +857,7 @@ proc listPaths(options: Options) =
837857
# There may be several, list all available ones and sort by version.
838858
for pkg in pkgs:
839859
if name == pkg.basicInfo.name:
840-
installed.add((pkg.metaData.specialVersion, pkg.getRealDir))
860+
installed.add((pkg.basicInfo.version, pkg.getRealDir))
841861

842862
if installed.len > 0:
843863
sort(installed, cmp[VersionAndPath], Descending)
@@ -1127,7 +1147,7 @@ proc uninstall(options: var Options) =
11271147
if len(revDeps - pkgsToDelete) > 0:
11281148
let pkgs = revDeps.collectNames(true)
11291149
displayWarning(
1130-
cannotUninstallPkgMsg(pkgTup.name, pkg.metaData.specialVersion, pkgs))
1150+
cannotUninstallPkgMsg(pkgTup.name, pkg.basicInfo.version, pkgs))
11311151
else:
11321152
pkgsToDelete.incl pkg.toRevDep
11331153

src/nimblepkg/displaymessages.nim

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
## the message to be repeated both in Nimble and the testing code.
77

88
import strformat, strutils
9-
import version
9+
import version, packageinfotypes, sha1hashes
1010

1111
const
1212
validationFailedMsg* = "Validation failed."
@@ -143,3 +143,13 @@ proc invalidDevelopDependenciesVersionsMsg*(errors: seq[string]): string =
143143
for error in errors:
144144
result &= "\n"
145145
result &= error
146+
147+
proc pkgAlreadyExistsInTheCacheMsg*(name, version, checksum: string): string =
148+
&"A package \"{name}@{version}\" with checksum \"{checksum}\" already " &
149+
"exists the the cache."
150+
151+
proc pkgAlreadyExistsInTheCacheMsg*(pkgInfo: PackageInfo): string =
152+
pkgAlreadyExistsInTheCacheMsg(
153+
pkgInfo.basicInfo.name,
154+
$pkgInfo.basicInfo.version,
155+
$pkgInfo.basicInfo.checksum)

src/nimblepkg/packageinfo.nim

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ proc setNameVersionChecksum*(pkgInfo: var PackageInfo, pkgDir: string) =
279279
if pkgInfo.basicInfo.version == notSetVersion:
280280
# if there is no previously set version from the `.nimble` file
281281
pkgInfo.basicInfo.version = version
282-
pkgInfo.metaData.specialVersion = version
282+
pkgInfo.metaData.specialVersions.incl version
283283
pkgInfo.basicInfo.checksum = checksum
284284

285285
proc getInstalledPackageMin*(pkgDir, nimbleFilePath: string): PackageInfo =
@@ -304,11 +304,10 @@ proc getInstalledPkgsMin*(libsDir: string, options: Options): seq[PackageInfo] =
304304
result.add pkg
305305

306306
proc withinRange*(pkgInfo: PackageInfo, verRange: VersionRange): bool =
307-
## Determines whether the specified package's version is within the
308-
## specified range. The check works with ordinary versions as well as
309-
## special ones.
310-
return withinRange(pkgInfo.basicInfo.version, verRange) or
311-
withinRange(pkgInfo.metaData.specialVersion, verRange)
307+
## Determines whether the specified package's version is within the specified
308+
## range. As the ordinary version is always added to the special versions set
309+
## checking only the special versions is enough.
310+
return withinRange(pkgInfo.metaData.specialVersions, verRange)
312311

313312
proc resolveAlias*(dep: PkgTuple, options: Options): PkgTuple =
314313
## Looks up the specified ``dep.name`` in the packages.json files to resolve
@@ -496,7 +495,7 @@ proc iterInstallFiles*(realDir: string, pkgInfo: PackageInfo,
496495
action(file)
497496

498497
proc getCacheDir*(pkgInfo: PackageBasicInfo): string =
499-
&"{pkgInfo.name}-{pkgInfo.version}-{pkgInfo.checksum}"
498+
&"{pkgInfo.name}-{pkgInfo.version}-{$pkgInfo.checksum}"
500499

501500
proc getPkgDest*(pkgInfo: PackageBasicInfo, options: Options): string =
502501
options.getPkgsDir() / pkgInfo.getCacheDir()

src/nimblepkg/packageinfotypes.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ type
2727
vcsRevision*: Sha1Hash
2828
files*: seq[string]
2929
binaries*: seq[string]
30-
specialVersion*: Version
30+
specialVersions*: HashSet[Version]
31+
# Special versions are aliases with which a single package can be
32+
# referred. For example a package can be versions `0.1.0`, `#head` and
33+
# `#master` at the same time.
3134

3235
PackageBasicInfo* = tuple
3336
name: string

src/nimblepkg/packagemetadatafile.nim

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (C) Dominik Picheta. All rights reserved.
22
# BSD License. Look at license.txt for more info.
33

4-
import json, os, strformat
4+
import json, os, strformat, sets, sequtils
55
import common, version, packageinfotypes, cli, tools, sha1hashes
66

77
type
@@ -17,20 +17,40 @@ const
1717

1818
proc initPackageMetaData*(): PackageMetaData =
1919
result = PackageMetaData(
20-
specialVersion: notSetVersion,
2120
vcsRevision: notSetSha1Hash)
2221

2322
proc metaDataError(msg: string): ref MetaDataError =
2423
newNimbleError[MetaDataError](msg)
2524

26-
proc saveMetaData*(metaData: PackageMetaData, dirName: string) =
25+
proc `%`(specialVersions: HashSet[Version]): JsonNode =
26+
%specialVersions.toSeq
27+
28+
proc initFromJson(specialVersions: var HashSet[Version], jsonNode: JsonNode,
29+
jsonPath: var string) =
30+
case jsonNode.kind
31+
of JArray:
32+
let originalJsonPathLen = jsonPath.len
33+
for i in 0 ..< jsonNode.len:
34+
jsonPath.add '['
35+
jsonPath.addInt i
36+
jsonPath.add ']'
37+
var version = newVersion("")
38+
initFromJson(version, jsonNode[i], jsonPath)
39+
specialVersions.incl version
40+
jsonPath.setLen originalJsonPathLen
41+
else:
42+
assert false, "The `jsonNode` must be of kind JArray."
43+
44+
proc saveMetaData*(metaData: PackageMetaData, dirName: string,
45+
changeRoots = true) =
2746
## Saves some important data to file in the package installation directory.
2847
var metaDataWithChangedPaths = metaData
29-
for i, file in metaData.files:
30-
metaDataWithChangedPaths.files[i] = changeRoot(dirName, "", file)
48+
if changeRoots:
49+
for i, file in metaData.files:
50+
metaDataWithChangedPaths.files[i] = changeRoot(dirName, "", file)
3151
let json = %{
3252
$pmdjkVersion: %packageMetaDataFileVersion,
33-
$pmdjkMetaData: %metaDataWithChangedPaths }
53+
$pmdjkMetaData: %metaDataWithChangedPaths}
3454
writeFile(dirName / packageMetaDataFileName, json.pretty)
3555

3656
proc loadMetaData*(dirName: string, raiseIfNotFound: bool): PackageMetaData =
@@ -39,7 +59,9 @@ proc loadMetaData*(dirName: string, raiseIfNotFound: bool): PackageMetaData =
3959
let fileName = dirName / packageMetaDataFileName
4060
if fileExists(fileName):
4161
{.warning[ProveInit]: off.}
62+
{.warning[UnsafeSetLen]: off.}
4263
result = parseFile(fileName)[$pmdjkMetaData].to(PackageMetaData)
64+
{.warning[UnsafeSetLen]: on.}
4365
{.warning[ProveInit]: on.}
4466
elif raiseIfNotFound:
4567
raise metaDataError(&"No {packageMetaDataFileName} file found in {dirName}")

src/nimblepkg/packageparser.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ proc readPackageInfo(nf: NimbleFile, options: Options, onlyMinimalInfo=false):
383383
# some of the package meta data from its directory.
384384
result.basicInfo.checksum = calculateDirSha1Checksum(fileDir)
385385
# By default specialVersion is the same as version.
386-
result.metaData.specialVersion = result.basicInfo.version
386+
result.metaData.specialVersions.incl result.basicInfo.version
387387
# If the `fileDir` is a VCS repository we can get some of the package meta
388388
# data from it.
389389
result.metaData.vcsRevision = getVcsRevision(fileDir)
@@ -497,7 +497,7 @@ proc toFullInfo*(pkg: PackageInfo, options: Options): PackageInfo =
497497
# The `isLink` data from the meta data file is with priority because of the
498498
# old format develop packages.
499499
result.isLink = pkg.isLink
500-
result.metaData.specialVersion = pkg.metaData.specialVersion
500+
result.metaData.specialVersions.incl pkg.metaData.specialVersions
501501

502502
assert not (pkg.isInstalled and pkg.isLink),
503503
"A package must not be simultaneously installed and linked."

0 commit comments

Comments
 (0)