1# Copyright (C) Dominik Picheta. All rights reserved. 2# BSD License. Look at license.txt for more info. 3 4import os, json, sets 5 6import options, common, version, download, packageinfo 7 8proc saveNimbleData*(options: Options) = 9 # TODO: This file should probably be locked. 10 writeFile(options.getNimbleDir() / "nimbledata.json", 11 pretty(options.nimbleData)) 12 13proc addRevDep*(nimbleData: JsonNode, dep: tuple[name, version: string], 14 pkg: PackageInfo) = 15 # Add a record which specifies that `pkg` has a dependency on `dep`, i.e. 16 # the reverse dependency of `dep` is `pkg`. 17 if not nimbleData["reverseDeps"].hasKey(dep.name): 18 nimbleData["reverseDeps"][dep.name] = newJObject() 19 if not nimbleData["reverseDeps"][dep.name].hasKey(dep.version): 20 nimbleData["reverseDeps"][dep.name][dep.version] = newJArray() 21 let revDep = %{ "name": %pkg.name, "version": %pkg.specialVersion} 22 let thisDep = nimbleData["reverseDeps"][dep.name][dep.version] 23 if revDep notin thisDep: 24 thisDep.add revDep 25 26proc removeRevDep*(nimbleData: JsonNode, pkg: PackageInfo) = 27 ## Removes ``pkg`` from the reverse dependencies of every package. 28 assert(not pkg.isMinimal) 29 proc remove(pkg: PackageInfo, depTup: PkgTuple, thisDep: JsonNode) = 30 for ver, val in thisDep: 31 if ver.newVersion in depTup.ver: 32 var newVal = newJArray() 33 for revDep in val: 34 if not (revDep["name"].str == pkg.name and 35 revDep["version"].str == pkg.specialVersion): 36 newVal.add revDep 37 thisDep[ver] = newVal 38 39 for depTup in pkg.requires: 40 if depTup.name.isURL(): 41 # We sadly must go through everything in this case... 42 for key, val in nimbleData["reverseDeps"]: 43 remove(pkg, depTup, val) 44 else: 45 let thisDep = nimbleData{"reverseDeps", depTup.name} 46 if thisDep.isNil: continue 47 remove(pkg, depTup, thisDep) 48 49 # Clean up empty objects/arrays 50 var newData = newJObject() 51 for key, val in nimbleData["reverseDeps"]: 52 if val.len != 0: 53 var newVal = newJObject() 54 for ver, elem in val: 55 if elem.len != 0: 56 newVal[ver] = elem 57 if newVal.len != 0: 58 newData[key] = newVal 59 nimbleData["reverseDeps"] = newData 60 61proc getRevDepTups*(options: Options, pkg: PackageInfo): seq[PkgTuple] = 62 ## Returns a list of *currently installed* reverse dependencies for `pkg`. 63 result = @[] 64 let thisPkgsDep = 65 options.nimbleData["reverseDeps"]{pkg.name}{pkg.specialVersion} 66 if not thisPkgsDep.isNil: 67 let pkgList = getInstalledPkgsMin(options.getPkgsDir(), options) 68 for pkg in thisPkgsDep: 69 let pkgTup = ( 70 name: pkg["name"].getStr(), 71 ver: parseVersionRange(pkg["version"].getStr()) 72 ) 73 var pkgInfo: PackageInfo 74 if not findPkg(pkgList, pkgTup, pkgInfo): 75 continue 76 77 result.add(pkgTup) 78 79proc getRevDeps*(options: Options, pkg: PackageInfo): HashSet[PackageInfo] = 80 result.init() 81 let installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options) 82 for rdepTup in getRevDepTups(options, pkg): 83 for rdepInfo in findAllPkgs(installedPkgs, rdepTup): 84 result.incl rdepInfo 85 86proc getAllRevDeps*(options: Options, pkg: PackageInfo, result: var HashSet[PackageInfo]) = 87 if pkg in result: 88 return 89 90 let installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options) 91 for rdepTup in getRevDepTups(options, pkg): 92 for rdepInfo in findAllPkgs(installedPkgs, rdepTup): 93 if rdepInfo in result: 94 continue 95 96 getAllRevDeps(options, rdepInfo, result) 97 result.incl pkg 98 99when isMainModule: 100 var nimbleData = %{"reverseDeps": newJObject()} 101 102 let nimforum1 = PackageInfo( 103 isMinimal: false, 104 name: "nimforum", 105 specialVersion: "0.1.0", 106 requires: @[("jester", parseVersionRange("0.1.0")), 107 ("captcha", parseVersionRange("1.0.0")), 108 ("auth", parseVersionRange("#head"))] 109 ) 110 let nimforum2 = PackageInfo(isMinimal: false, name: "nimforum", specialVersion: "0.2.0") 111 let play = PackageInfo(isMinimal: false, name: "play", specialVersion: "#head") 112 113 nimbleData.addRevDep(("jester", "0.1.0"), nimforum1) 114 nimbleData.addRevDep(("jester", "0.1.0"), play) 115 nimbleData.addRevDep(("captcha", "1.0.0"), nimforum1) 116 nimbleData.addRevDep(("auth", "#head"), nimforum1) 117 nimbleData.addRevDep(("captcha", "1.0.0"), nimforum2) 118 nimbleData.addRevDep(("auth", "#head"), nimforum2) 119 120 doAssert nimbleData["reverseDeps"]["jester"]["0.1.0"].len == 2 121 doAssert nimbleData["reverseDeps"]["captcha"]["1.0.0"].len == 2 122 doAssert nimbleData["reverseDeps"]["auth"]["#head"].len == 2 123 124 block: 125 nimbleData.removeRevDep(nimforum1) 126 let jester = nimbleData["reverseDeps"]["jester"]["0.1.0"][0] 127 doAssert jester["name"].getStr() == play.name 128 doAssert jester["version"].getStr() == play.specialVersion 129 130 let captcha = nimbleData["reverseDeps"]["captcha"]["1.0.0"][0] 131 doAssert captcha["name"].getStr() == nimforum2.name 132 doAssert captcha["version"].getStr() == nimforum2.specialVersion 133 134 echo("Everything works!") 135 136