Skip to content

Commit e5dfeb2

Browse files
authored
Fixes an issue where sometimes, when using special versions a duplicated entry appears in nimble.paths (#1420)
* Fixes an issue where sometimes, when using special versions a duplicated entry appears in `nimble.paths` * fix issue * Progress * fix test (misses `setup`) * Preprocess urls before SAT * adds extra test * dont normalize requires, comment urls in test (fix will come in a subsequent pr) * use proc
1 parent ccd29a3 commit e5dfeb2

File tree

3 files changed

+87
-7
lines changed

3 files changed

+87
-7
lines changed

src/nimblepkg/nimblesat.nim

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type
6666
subdir*: string
6767
downloadDir*: string
6868
pv*: PkgTuple #Require request
69+
70+
var urlToName: Table[string, string] = initTable[string, string]()
6971

7072
const TaggedVersionsFileName* = "tagged_versions.json"
7173

@@ -111,9 +113,12 @@ proc getMinimalInfo*(pkg: PackageInfo, options: Options): PackageMinimalInfo =
111113
result.requires = pkg.requires.map(convertNimAliasToNim)
112114
if options.action.typ in {actionLock, actionDeps} or options.hasNimInLockFile():
113115
result.requires = result.requires.filterIt(not it.isNim)
116+
if pkg.metadata.url != "":
117+
urlToName[pkg.metadata.url] = result.name
114118

115119
proc getMinimalInfo*(nimbleFile: string, pkgName: string, options: Options): PackageMinimalInfo =
116120
#TODO we can use the new getPkgInfoFromDirWithDeclarativeParser to get the minimal info and add the features to the packageinfo type so this whole function can be removed
121+
#TODO we need to handle the url here as well.
117122
assert options.useDeclarativeParser, "useDeclarativeParser must be set"
118123
let nimbleFileInfo = extractRequiresInfo(nimbleFile)
119124
result.name = if pkgName.isNim: "nim" else: pkgName
@@ -500,6 +505,13 @@ proc getSolvedPackages*(pkgVersionTable: Table[string, PackageVersions], output:
500505
if otherPkg.pkgName == depName and otherPkg.version.withinRange(depVer):
501506
solvedPkg.deps.add(otherPkg)
502507
break
508+
# Collect reverse deps as solved package
509+
for solvedPkg in result.mitems:
510+
for (depName, depVer) in solvedPkg.reverseDependencies:
511+
for otherPkg in result:
512+
if otherPkg.pkgName == depName:
513+
solvedPkg.reverseDeps.add(otherPkg)
514+
break
503515

504516
proc getCacheDownloadDir*(url: string, ver: VersionRange, options: Options): string =
505517
options.pkgCachePath / getDownloadDirName(url, ver, notSetSha1Hash)
@@ -523,9 +535,11 @@ proc downloadPkgFromUrl*(pv: PkgTuple, options: Options, doPrompt = false): (Dow
523535
proc downloadPkInfoForPv*(pv: PkgTuple, options: Options, doPrompt = false): PackageInfo =
524536
let downloadRes = downloadPkgFromUrl(pv, options, doPrompt)
525537
if options.satResult.pass in {satNimSelection, satFallbackToVmParser}:
526-
getPkgInfoFromDirWithDeclarativeParser(downloadRes[0].dir, options)
538+
result = getPkgInfoFromDirWithDeclarativeParser(downloadRes[0].dir, options)
527539
else:
528-
downloadRes[0].dir.getPkgInfo(options)
540+
result = downloadRes[0].dir.getPkgInfo(options)
541+
if result.metadata.url != "":
542+
urlToName[result.metadata.url] = result.basicInfo.name
529543

530544
proc getAllNimReleases(options: Options): seq[PackageMinimalInfo] =
531545
let releases = getOfficialReleases(options)
@@ -764,21 +778,68 @@ proc solveLocalPackages*(rootPkgInfo: PackageInfo, pkgList: seq[PackageInfo], so
764778
(pkgInfo.basicInfo.version == solvedPkg.version or solvedPkg.version in pkgInfo.metadata.specialVersions):
765779
result.incl pkgInfo
766780

781+
proc areAllReqAny(dep: SolvedPackage): bool =
782+
#Checks where all the requirements by other packages in the solution are any
783+
#This allows for using a special version to meet the requirement of the solution
784+
#Scenario will be, int he package list there is only a special version but all requirements
785+
#are any. So it wont need to download a regular version but just use the special version.
786+
for rev in dep.reverseDeps:
787+
for req in rev.requirements:
788+
if dep.pkgName == req.name:
789+
if req.ver.kind != verAny:
790+
return false
791+
true
792+
793+
proc normalizeRequirements*(pkgVersionTable: var Table[string, PackageVersions]) =
794+
#changes the url in the name of the requirements to the real name of the package
795+
#so the resulting solvedPackages dont have url in the name
796+
var recordsToRemove: seq[tuple[url: string, name: string]] = @[]
797+
for pkgName, pkgVersions in pkgVersionTable.mpairs:
798+
if pkgName.isUrl:
799+
recordsToRemove.add((pkgName, urlToName[pkgName]))
800+
for pkgVersion in pkgVersions.versions.mitems:
801+
for req in pkgVersion.requires.mitems:
802+
if req.name.isUrl and req.name in urlToName:
803+
# echo "DEBUG: Normalizing requirement ", req.name, " to ", urlToName[req.name], "with version ", $req.ver, " for package ", pkgName, " version ", $pkgVersion.version
804+
req.name = urlToName[req.name]
805+
for (url, name) in recordsToRemove:
806+
if pkgVersionTable.hasKey(name):
807+
pkgVersionTable[name].versions.add(pkgVersionTable[url].versions)
808+
else:
809+
pkgVersionTable[name] = PackageVersions(pkgName: name, versions: pkgVersionTable[url].versions)
810+
pkgVersionTable.del(url)
811+
812+
#if there are special versions, we need to remove the regular versions
813+
for pkgName, pkgVersions in pkgVersionTable.mpairs:
814+
if pkgVersions.versions.filterIt(it.version.isSpecial).len > 0:
815+
pkgVersions.versions = pkgVersions.versions.filterIt(it.version.isSpecial)
816+
817+
# if pkgVersionTable.hasKey("json_serialization"):
818+
# echo "DEBUG NEW VERSIONS FOR ", "json_serialization", " ", pkgVersionTable["json_serialization"].versions.mapIt(it.version).join(", ")
819+
767820
proc solvePackages*(rootPkg: PackageInfo, pkgList: seq[PackageInfo], pkgsToInstall: var seq[(string, Version)], options: Options, output: var string, solvedPkgs: var seq[SolvedPackage]): HashSet[PackageInfo] =
768821
var root: PackageMinimalInfo = rootPkg.getMinimalInfo(options)
769822
root.isRoot = true
770823
var pkgVersionTable = initTable[string, PackageVersions]()
771824
pkgVersionTable[root.name] = PackageVersions(pkgName: root.name, versions: @[root])
772825
collectAllVersions(pkgVersionTable, root, options, downloadMinimalPackage, pkgList.mapIt(it.getMinimalInfo(options)))
826+
# pkgVersionTable.normalizeRequirements() dont use it for now
773827
solvedPkgs = pkgVersionTable.getSolvedPackages(output).topologicalSort()
828+
# echo "DEBUG: SolvedPkgs before post processing: ", solvedPkgs.mapIt(it.pkgName & " " & $it.version).join(", ")
774829
let systemNimCompatible = solvedPkgs.isSystemNimCompatible(options)
775-
830+
# echo "DEBUG: SolvedPkgs after post processing: ", solvedPkgs.mapIt(it.pkgName & " " & $it.version).join(", ")
776831
for solvedPkg in solvedPkgs:
777832
if solvedPkg.pkgName == root.name: continue
778833
var foundInList = false
834+
let canUseAny = solvedPkg.areAllReqAny()
779835
for pkgInfo in pkgList:
836+
let specialVersions = if pkgInfo.metadata.specialVersions.len > 1: pkgInfo.metadata.specialVersions.toSeq()[1..^1] else: @[]
837+
let isSpecial = specialVersions.len > 0
780838
if (pkgInfo.basicInfo.name == solvedPkg.pkgName or pkgInfo.metadata.url == solvedPkg.pkgName) and
781-
(pkgInfo.basicInfo.version == solvedPkg.version or solvedPkg.version in pkgInfo.metadata.specialVersions):
839+
(pkgInfo.basicInfo.version == solvedPkg.version and (not isSpecial or canUseAny) or solvedPkg.version in specialVersions) and
840+
#only add one (we could fall into adding two if there are multiple special versiosn in the package list and we can add any).
841+
#But we still allow it on upgrade as they are post proccessed in a later stage
842+
(result.toSeq.filterIt(it.basicInfo.name == solvedPkg.pkgName or it.metadata.url == solvedPkg.pkgName).len == 0 or options.action.typ in {actionUpgrade}):
782843
result.incl pkgInfo
783844
foundInList = true
784845
if not foundInList:

src/nimblepkg/packageinfotypes.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ type
103103
requirements*: seq[PkgTuple]
104104
reverseDependencies*: seq[(string, Version)]
105105
deps*: seq[SolvedPackage]
106+
reverseDeps*: seq[SolvedPackage]
106107

107108
SATPass* = enum
108109
satNone

tests/trequireflag.nim

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{.used.}
2-
import unittest, os, strutils# strformat, osproc
2+
import unittest, os, strutils, sequtils# strformat, osproc
33
import testscommon
44
from nimblepkg/common import cd
5+
from nimble import nimblePathsFileName
56

67
suite "requires flag":
78
test "can add additional requirements to package with legacy solver":
@@ -45,8 +46,8 @@ suite "requires flag":
4546
- json_serialization#4cd31594f868a3d3cb81ec9d5f479efbe2466ebd
4647
]#
4748
let requires = [
48-
"https://github.com/status-im/nim-json-serialization.git#nimble_test_dont_delete",
49-
"https://github.com/status-im/nim-json-serialization.git#26bea5ffce20ae0d0855b3d61072de04d3bf9826",
49+
# "https://github.com/status-im/nim-json-serialization.git#nimble_test_dont_delete",
50+
# "https://github.com/status-im/nim-json-serialization.git#26bea5ffce20ae0d0855b3d61072de04d3bf9826",
5051
"json_serialization#nimble_test_dont_delete",
5152
"json_serialization#26bea5ffce20ae0d0855b3d61072de04d3bf9826",
5253
"json_serialization == 0.2.9"
@@ -65,6 +66,23 @@ suite "requires flag":
6566
let (_, exitCodeTest) = execNimble("test", require, no_test)
6667
check exitCodeTest == QuitSuccess
6768

69+
let (_, exitCodeSetup) = execNimble("setup", require)
70+
check exitCodeSetup == QuitSuccess
71+
72+
# Check nimble.paths file for correct path and no duplicates
73+
check fileExists(nimblePathsFileName)
74+
let pathsContent = nimblePathsFileName.readFile
75+
let jsonSerializationLines = pathsContent.splitLines.filterIt(it.contains("json_serialization"))
76+
check jsonSerializationLines.len == 1
77+
78+
# Verify the path is correctly formatted
79+
let pathLine = jsonSerializationLines[0]
80+
let pathStart = pathLine.find('"') + 1
81+
let pathEnd = pathLine.rfind('"')
82+
check pathStart > 0 and pathEnd > pathStart
83+
let packagePath = pathLine[pathStart..<pathEnd]
84+
check packagePath.contains("json_serialization")
85+
6886
var pkgDir = ""
6987
if isVersion:
7088
pkgDir = getPackageDir(pkgsDir, "json_serialization")

0 commit comments

Comments
 (0)