parent
3c1e669eaa
commit
92e9ec5e59
6 changed files with 203 additions and 74 deletions
|
|
@ -13,7 +13,7 @@ from sequtils import toSeq
|
||||||
import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
|
import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
|
||||||
nimblepkg/download, nimblepkg/config, nimblepkg/common,
|
nimblepkg/download, nimblepkg/config, nimblepkg/common,
|
||||||
nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser,
|
nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser,
|
||||||
nimblepkg/cli, nimblepkg/packageinstaller
|
nimblepkg/cli, nimblepkg/packageinstaller, nimblepkg/reversedeps
|
||||||
|
|
||||||
import nimblepkg/nimscriptsupport
|
import nimblepkg/nimscriptsupport
|
||||||
|
|
||||||
|
|
@ -141,61 +141,6 @@ proc copyFilesRec(origDir, currentDir, dest: string,
|
||||||
result.incl copyFileD(pkgInfo.mypath,
|
result.incl copyFileD(pkgInfo.mypath,
|
||||||
changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath))
|
changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath))
|
||||||
|
|
||||||
proc saveNimbleData(options: Options) =
|
|
||||||
# TODO: This file should probably be locked.
|
|
||||||
writeFile(options.getNimbleDir() / "nimbledata.json",
|
|
||||||
pretty(options.nimbleData))
|
|
||||||
|
|
||||||
proc addRevDep(options: Options, dep: tuple[name, version: string],
|
|
||||||
pkg: PackageInfo) =
|
|
||||||
# let depNameVer = dep.name & '-' & dep.version
|
|
||||||
if not options.nimbleData["reverseDeps"].hasKey(dep.name):
|
|
||||||
options.nimbleData["reverseDeps"][dep.name] = newJObject()
|
|
||||||
if not options.nimbleData["reverseDeps"][dep.name].hasKey(dep.version):
|
|
||||||
options.nimbleData["reverseDeps"][dep.name][dep.version] = newJArray()
|
|
||||||
let revDep = %{ "name": %pkg.name, "version": %pkg.specialVersion}
|
|
||||||
let thisDep = options.nimbleData["reverseDeps"][dep.name][dep.version]
|
|
||||||
if revDep notin thisDep:
|
|
||||||
thisDep.add revDep
|
|
||||||
|
|
||||||
proc removeRevDep(options: Options, pkg: PackageInfo) =
|
|
||||||
## Removes ``pkg`` from the reverse dependencies of every package.
|
|
||||||
assert(not pkg.isMinimal)
|
|
||||||
proc remove(options: Options, pkg: PackageInfo, depTup: PkgTuple,
|
|
||||||
thisDep: JsonNode) =
|
|
||||||
for ver, val in thisDep:
|
|
||||||
if ver.newVersion in depTup.ver:
|
|
||||||
var newVal = newJArray()
|
|
||||||
for revDep in val:
|
|
||||||
if not (revDep["name"].str == pkg.name and
|
|
||||||
revDep["version"].str == pkg.specialVersion):
|
|
||||||
newVal.add revDep
|
|
||||||
thisDep[ver] = newVal
|
|
||||||
|
|
||||||
for depTup in pkg.requires:
|
|
||||||
if depTup.name.isURL():
|
|
||||||
# We sadly must go through everything in this case...
|
|
||||||
for key, val in options.nimbleData["reverseDeps"]:
|
|
||||||
options.remove(pkg, depTup, val)
|
|
||||||
else:
|
|
||||||
let thisDep = options.nimbleData{"reverseDeps", depTup.name}
|
|
||||||
if thisDep.isNil: continue
|
|
||||||
options.remove(pkg, depTup, thisDep)
|
|
||||||
|
|
||||||
# Clean up empty objects/arrays
|
|
||||||
var newData = newJObject()
|
|
||||||
for key, val in options.nimbleData["reverseDeps"]:
|
|
||||||
if val.len != 0:
|
|
||||||
var newVal = newJObject()
|
|
||||||
for ver, elem in val:
|
|
||||||
if elem.len != 0:
|
|
||||||
newVal[ver] = elem
|
|
||||||
if newVal.len != 0:
|
|
||||||
newData[key] = newVal
|
|
||||||
options.nimbleData["reverseDeps"] = newData
|
|
||||||
|
|
||||||
saveNimbleData(options)
|
|
||||||
|
|
||||||
proc install(packages: seq[PkgTuple],
|
proc install(packages: seq[PkgTuple],
|
||||||
options: Options,
|
options: Options,
|
||||||
doPrompt = true): tuple[deps: seq[PackageInfo], pkg: PackageInfo]
|
doPrompt = true): tuple[deps: seq[PackageInfo], pkg: PackageInfo]
|
||||||
|
|
@ -270,7 +215,7 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] =
|
||||||
# (unsatisfiable dependendencies).
|
# (unsatisfiable dependendencies).
|
||||||
# N.B. NimbleData is saved in installFromDir.
|
# N.B. NimbleData is saved in installFromDir.
|
||||||
for i in reverseDeps:
|
for i in reverseDeps:
|
||||||
addRevDep(options, i, pkginfo)
|
addRevDep(options.nimbleData, i, pkginfo)
|
||||||
|
|
||||||
proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string],
|
proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string],
|
||||||
args: var seq[string]) =
|
args: var seq[string]) =
|
||||||
|
|
@ -416,6 +361,11 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
|
||||||
[pkgInfo.name, pkgInfo.specialVersion]
|
[pkgInfo.name, pkgInfo.specialVersion]
|
||||||
if not options.prompt(msg):
|
if not options.prompt(msg):
|
||||||
raise NimbleQuit(msg: "")
|
raise NimbleQuit(msg: "")
|
||||||
|
|
||||||
|
# Remove reverse deps.
|
||||||
|
let pkgInfo = getPkgInfo(pkgDestDir, options)
|
||||||
|
options.nimbleData.removeRevDep(pkgInfo)
|
||||||
|
|
||||||
removePkgDir(pkgDestDir, options)
|
removePkgDir(pkgDestDir, options)
|
||||||
# Remove any symlinked binaries
|
# Remove any symlinked binaries
|
||||||
for bin in pkgInfo.bin:
|
for bin in pkgInfo.bin:
|
||||||
|
|
@ -426,7 +376,6 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
|
||||||
else:
|
else:
|
||||||
removeFile(binDir / bin)
|
removeFile(binDir / bin)
|
||||||
|
|
||||||
|
|
||||||
createDir(pkgDestDir)
|
createDir(pkgDestDir)
|
||||||
# Copy this package's files based on the preferences specified in PkgInfo.
|
# Copy this package's files based on the preferences specified in PkgInfo.
|
||||||
var filesInstalled = initSet[string]()
|
var filesInstalled = initSet[string]()
|
||||||
|
|
@ -831,21 +780,20 @@ proc uninstall(options: Options) =
|
||||||
for pkg in pkgList:
|
for pkg in pkgList:
|
||||||
# Check whether any packages depend on the ones the user is trying to
|
# Check whether any packages depend on the ones the user is trying to
|
||||||
# uninstall.
|
# uninstall.
|
||||||
let thisPkgsDep = options.nimbleData["reverseDeps"]{pkg.name}{pkg.specialVersion}
|
let revDeps = getRevDeps(options, pkg)
|
||||||
if not thisPkgsDep.isNil:
|
|
||||||
var reason = ""
|
var reason = ""
|
||||||
if thisPkgsDep.len == 1:
|
if revDeps.len == 1:
|
||||||
reason = "$1 ($2) depends on it" % [thisPkgsDep[0]["name"].str,
|
reason = "$1 ($2) depends on it" % [revDeps[0].name, $revDeps[0].ver]
|
||||||
thisPkgsDep[0]["version"].str]
|
|
||||||
else:
|
else:
|
||||||
for i in 0 .. <thisPkgsDep.len:
|
for i in 0 .. <revDeps.len:
|
||||||
reason.add("$1 ($2)" % [thisPkgsDep[i]["name"].str,
|
reason.add("$1 ($2)" % [revDeps[i].name, $revDeps[i].ver])
|
||||||
thisPkgsDep[i]["version"].str])
|
if i != <revDeps.len:
|
||||||
if i != <thisPkgsDep.len:
|
|
||||||
reason.add ", "
|
reason.add ", "
|
||||||
reason.add " depend on it"
|
reason.add " depend on it"
|
||||||
errors.add("Cannot uninstall $1 ($2) because $3" % [pkgTup.name,
|
|
||||||
pkg.specialVersion, reason])
|
if revDeps.len > 0:
|
||||||
|
errors.add("Cannot uninstall $1 ($2) because $3" %
|
||||||
|
[pkgTup.name, pkg.specialVersion, reason])
|
||||||
else:
|
else:
|
||||||
pkgsToDelete.add pkg
|
pkgsToDelete.add pkg
|
||||||
|
|
||||||
|
|
@ -869,12 +817,14 @@ proc uninstall(options: Options) =
|
||||||
|
|
||||||
# removeRevDep needs the package dependency info, so we can't just pass
|
# removeRevDep needs the package dependency info, so we can't just pass
|
||||||
# a minimal pkg info.
|
# a minimal pkg info.
|
||||||
removeRevDep(options, pkg.toFullInfo(options))
|
removeRevDep(options.nimbleData, pkg.toFullInfo(options))
|
||||||
removePkgDir(options.getPkgsDir / (pkg.name & '-' & pkg.specialVersion),
|
removePkgDir(options.getPkgsDir / (pkg.name & '-' & pkg.specialVersion),
|
||||||
options)
|
options)
|
||||||
display("Removed", "$1 ($2)" % [pkg.name, $pkg.specialVersion], Success,
|
display("Removed", "$1 ($2)" % [pkg.name, $pkg.specialVersion], Success,
|
||||||
HighPriority)
|
HighPriority)
|
||||||
|
|
||||||
|
saveNimbleData(options)
|
||||||
|
|
||||||
proc listTasks(options: Options) =
|
proc listTasks(options: Options) =
|
||||||
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
let nimbleFile = findNimbleFile(getCurrentDir(), true)
|
||||||
nimscriptsupport.listTasks(nimbleFile, options)
|
nimscriptsupport.listTasks(nimbleFile, options)
|
||||||
|
|
|
||||||
115
src/nimblepkg/reversedeps.nim
Normal file
115
src/nimblepkg/reversedeps.nim
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||||
|
# BSD License. Look at license.txt for more info.
|
||||||
|
|
||||||
|
import os, json
|
||||||
|
|
||||||
|
import options, common, version, download, packageinfo
|
||||||
|
|
||||||
|
proc saveNimbleData*(options: Options) =
|
||||||
|
# TODO: This file should probably be locked.
|
||||||
|
writeFile(options.getNimbleDir() / "nimbledata.json",
|
||||||
|
pretty(options.nimbleData))
|
||||||
|
|
||||||
|
proc addRevDep*(nimbleData: JsonNode, dep: tuple[name, version: string],
|
||||||
|
pkg: PackageInfo) =
|
||||||
|
# Add a record which specifies that `pkg` has a dependency on `dep`, i.e.
|
||||||
|
# the reverse dependency of `dep` is `pkg`.
|
||||||
|
if not nimbleData["reverseDeps"].hasKey(dep.name):
|
||||||
|
nimbleData["reverseDeps"][dep.name] = newJObject()
|
||||||
|
if not nimbleData["reverseDeps"][dep.name].hasKey(dep.version):
|
||||||
|
nimbleData["reverseDeps"][dep.name][dep.version] = newJArray()
|
||||||
|
let revDep = %{ "name": %pkg.name, "version": %pkg.specialVersion}
|
||||||
|
let thisDep = nimbleData["reverseDeps"][dep.name][dep.version]
|
||||||
|
if revDep notin thisDep:
|
||||||
|
thisDep.add revDep
|
||||||
|
|
||||||
|
proc removeRevDep*(nimbleData: JsonNode, pkg: PackageInfo) =
|
||||||
|
## Removes ``pkg`` from the reverse dependencies of every package.
|
||||||
|
assert(not pkg.isMinimal)
|
||||||
|
proc remove(pkg: PackageInfo, depTup: PkgTuple, thisDep: JsonNode) =
|
||||||
|
for ver, val in thisDep:
|
||||||
|
if ver.newVersion in depTup.ver:
|
||||||
|
var newVal = newJArray()
|
||||||
|
for revDep in val:
|
||||||
|
if not (revDep["name"].str == pkg.name and
|
||||||
|
revDep["version"].str == pkg.specialVersion):
|
||||||
|
newVal.add revDep
|
||||||
|
thisDep[ver] = newVal
|
||||||
|
|
||||||
|
for depTup in pkg.requires:
|
||||||
|
if depTup.name.isURL():
|
||||||
|
# We sadly must go through everything in this case...
|
||||||
|
for key, val in nimbleData["reverseDeps"]:
|
||||||
|
remove(pkg, depTup, val)
|
||||||
|
else:
|
||||||
|
let thisDep = nimbleData{"reverseDeps", depTup.name}
|
||||||
|
if thisDep.isNil: continue
|
||||||
|
remove(pkg, depTup, thisDep)
|
||||||
|
|
||||||
|
# Clean up empty objects/arrays
|
||||||
|
var newData = newJObject()
|
||||||
|
for key, val in nimbleData["reverseDeps"]:
|
||||||
|
if val.len != 0:
|
||||||
|
var newVal = newJObject()
|
||||||
|
for ver, elem in val:
|
||||||
|
if elem.len != 0:
|
||||||
|
newVal[ver] = elem
|
||||||
|
if newVal.len != 0:
|
||||||
|
newData[key] = newVal
|
||||||
|
nimbleData["reverseDeps"] = newData
|
||||||
|
|
||||||
|
proc getRevDeps*(options: Options, pkg: PackageInfo): seq[PkgTuple] =
|
||||||
|
## Returns a list of *currently installed* reverse dependencies for `pkg`.
|
||||||
|
result = @[]
|
||||||
|
let thisPkgsDep =
|
||||||
|
options.nimbleData["reverseDeps"]{pkg.name}{pkg.specialVersion}
|
||||||
|
if not thisPkgsDep.isNil:
|
||||||
|
let pkgList = getInstalledPkgsMin(options.getPkgsDir(), options)
|
||||||
|
for pkg in thisPkgsDep:
|
||||||
|
let pkgTup = (
|
||||||
|
name: pkg["name"].getStr(),
|
||||||
|
ver: parseVersionRange(pkg["version"].getStr())
|
||||||
|
)
|
||||||
|
var pkgInfo: PackageInfo
|
||||||
|
if not findPkg(pkgList, pkgTup, pkgInfo):
|
||||||
|
continue
|
||||||
|
|
||||||
|
result.add(pkgTup)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var nimbleData = %{"reverseDeps": newJObject()}
|
||||||
|
|
||||||
|
let nimforum1 = PackageInfo(
|
||||||
|
isMinimal: false,
|
||||||
|
name: "nimforum",
|
||||||
|
specialVersion: "0.1.0",
|
||||||
|
requires: @[("jester", parseVersionRange("0.1.0")),
|
||||||
|
("captcha", parseVersionRange("1.0.0")),
|
||||||
|
("auth", parseVersionRange("#head"))]
|
||||||
|
)
|
||||||
|
let nimforum2 = PackageInfo(isMinimal: false, name: "nimforum", specialVersion: "0.2.0")
|
||||||
|
let play = PackageInfo(isMinimal: false, name: "play", specialVersion: "#head")
|
||||||
|
|
||||||
|
nimbleData.addRevDep(("jester", "0.1.0"), nimforum1)
|
||||||
|
nimbleData.addRevDep(("jester", "0.1.0"), play)
|
||||||
|
nimbleData.addRevDep(("captcha", "1.0.0"), nimforum1)
|
||||||
|
nimbleData.addRevDep(("auth", "#head"), nimforum1)
|
||||||
|
nimbleData.addRevDep(("captcha", "1.0.0"), nimforum2)
|
||||||
|
nimbleData.addRevDep(("auth", "#head"), nimforum2)
|
||||||
|
|
||||||
|
doAssert nimbleData["reverseDeps"]["jester"]["0.1.0"].len == 2
|
||||||
|
doAssert nimbleData["reverseDeps"]["captcha"]["1.0.0"].len == 2
|
||||||
|
doAssert nimbleData["reverseDeps"]["auth"]["#head"].len == 2
|
||||||
|
|
||||||
|
block:
|
||||||
|
nimbleData.removeRevDep(nimforum1)
|
||||||
|
let jester = nimbleData["reverseDeps"]["jester"]["0.1.0"][0]
|
||||||
|
doAssert jester["name"].getStr() == play.name
|
||||||
|
doAssert jester["version"].getStr() == play.specialVersion
|
||||||
|
|
||||||
|
let captcha = nimbleData["reverseDeps"]["captcha"]["1.0.0"][0]
|
||||||
|
doAssert captcha["name"].getStr() == nimforum2.name
|
||||||
|
doAssert captcha["version"].getStr() == nimforum2.specialVersion
|
||||||
|
|
||||||
|
echo("Everything works!")
|
||||||
|
|
||||||
10
tests/revdep/mydep/mydep.nimble
Normal file
10
tests/revdep/mydep/mydep.nimble
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Dominik Picheta"
|
||||||
|
description = "Random dep"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 0.15.0"
|
||||||
11
tests/revdep/pkgNoDep/pkgA.nimble
Normal file
11
tests/revdep/pkgNoDep/pkgA.nimble
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Dominik Picheta"
|
||||||
|
description = "Correctly structured package A"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 0.15.0"
|
||||||
|
|
||||||
11
tests/revdep/pkgWithDep/pkgA.nimble
Normal file
11
tests/revdep/pkgWithDep/pkgA.nimble
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Dominik Picheta"
|
||||||
|
description = "Correctly structured package A"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 0.15.0", "mydep"
|
||||||
|
|
||||||
|
|
@ -38,6 +38,15 @@ proc execNimble(args: varargs[string]): tuple[output: string, exitCode: int] =
|
||||||
|
|
||||||
result = execCmdEx(quotedArgs.join(" "))
|
result = execCmdEx(quotedArgs.join(" "))
|
||||||
|
|
||||||
|
proc execNimbleYes(args: varargs[string]): tuple[output: string, exitCode: int]=
|
||||||
|
# issue #6314
|
||||||
|
execNimble(@args & "-y")
|
||||||
|
|
||||||
|
template verify(res: (string, int)) =
|
||||||
|
let r = res
|
||||||
|
checkpoint r[0]
|
||||||
|
check r[1] == QuitSuccess
|
||||||
|
|
||||||
proc processOutput(output: string): seq[string] =
|
proc processOutput(output: string): seq[string] =
|
||||||
output.strip.splitLines().filter((x: string) => (x.len > 0))
|
output.strip.splitLines().filter((x: string) => (x.len > 0))
|
||||||
|
|
||||||
|
|
@ -466,6 +475,29 @@ test "can pass args with spaces to Nim (#351)":
|
||||||
checkpoint output
|
checkpoint output
|
||||||
check exitCode == QuitSuccess
|
check exitCode == QuitSuccess
|
||||||
|
|
||||||
|
suite "reverse dependencies":
|
||||||
|
test "basic test":
|
||||||
|
cd "revdep/mydep":
|
||||||
|
verify execNimbleYes("install")
|
||||||
|
|
||||||
|
cd "revdep/pkgWithDep":
|
||||||
|
verify execNimbleYes("install")
|
||||||
|
|
||||||
|
verify execNimbleYes("remove", "pkgA")
|
||||||
|
verify execNimbleYes("remove", "mydep")
|
||||||
|
|
||||||
|
test "issue #373":
|
||||||
|
cd "revdep/mydep":
|
||||||
|
verify execNimbleYes("install")
|
||||||
|
|
||||||
|
cd "revdep/pkgWithDep":
|
||||||
|
verify execNimbleYes("install")
|
||||||
|
|
||||||
|
cd "revdep/pkgNoDep":
|
||||||
|
verify execNimbleYes("install")
|
||||||
|
|
||||||
|
verify execNimbleYes("remove", "mydep")
|
||||||
|
|
||||||
suite "develop feature":
|
suite "develop feature":
|
||||||
test "can reject binary packages":
|
test "can reject binary packages":
|
||||||
cd "develop/binary":
|
cd "develop/binary":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue