diff --git a/src/nimble.nim b/src/nimble.nim index b8e7ffb..0d93af9 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -4,7 +4,7 @@ import system except TResult import httpclient, parseopt, os, osproc, pegs, tables, parseutils, - strtabs, json, algorithm, sets, uri + strtabs, json, algorithm, sets, uri, future, sequtils import strutils except toLower from unicode import toLower @@ -198,11 +198,12 @@ proc removeRevDep(options: Options, pkg: PackageInfo) = proc install(packages: seq[PkgTuple], options: Options, - doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] -proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = + doPrompt = true): tuple[deps: seq[PackageInfo], pkg: PackageInfo] +proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] = ## Verifies and installs dependencies. ## - ## Returns the list of paths to pass to the compiler during build phase. + ## Returns the list of PackageInfo (for paths) to pass to the compiler + ## during build phase. result = @[] assert(not pkginfo.isMinimal, "processDeps needs pkginfo.requires") display("Verifying", @@ -233,8 +234,8 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = if not found: display("Installing", $resolvedDep, priority = HighPriority) let toInstall = @[(resolvedDep.name, resolvedDep.ver)] - let (paths, installedPkg) = install(toInstall, options) - result.add(paths) + let (pkgs, installedPkg) = install(toInstall, options) + result.add(pkgs) pkg = installedPkg # For addRevDep @@ -243,7 +244,12 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = else: display("Info:", "Dependency on $1 already satisfied" % $dep, priority = HighPriority) - result.add(pkg.mypath.splitFile.dir) + if pkg.isLinked: + # TODO (#393): This can be optimised since the .nimble-link files have + # a secondary line that specifies the srcDir. + pkg = pkg.toFullInfo(options) + + result.add(pkg) # Process the dependencies of this dependency. result.add(processDeps(pkg.toFullInfo(options), options)) reverseDeps.add((pkg.name, pkg.specialVersion)) @@ -251,8 +257,7 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = # Check if two packages of the same name (but different version) are listed # in the path. var pkgsInPath: StringTableRef = newStringTable(modeCaseSensitive) - for p in result: - let pkgInfo = getPkgInfo(p, options) + for pkgInfo in result: if pkgsInPath.hasKey(pkgInfo.name) and pkgsInPath[pkgInfo.name] != pkgInfo.version: raise newException(NimbleError, @@ -267,7 +272,8 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = for i in reverseDeps: addRevDep(options, i, pkginfo) -proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], args: var seq[string]) = +proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], + args: var seq[string]) = ## Builds a package as specified by ``pkgInfo``. if pkgInfo.bin.len == 0: raise newException(NimbleError, @@ -369,7 +375,10 @@ proc vcsRevisionInDir(dir: string): string = discard proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, - url: string): tuple[paths: seq[string], pkg: PackageInfo] = + url: string): tuple[ + deps: seq[PackageInfo], + pkg: PackageInfo + ] = ## Returns where package has been installed to, together with paths ## to the packages this package depends on. ## The return value of this function is used by @@ -385,7 +394,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, pkgInfo.specialVersion = $requestedVer.spe # Dependencies need to be processed before the creation of the pkg dir. - result.paths = processDeps(pkgInfo, depsOptions) + result.deps = processDeps(pkgInfo, depsOptions) if options.depsOnly: result.pkg = pkgInfo @@ -396,7 +405,9 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, # Build before removing an existing package (if one exists). This way # if the build fails then the old package will still be installed. - if pkgInfo.bin.len > 0: buildFromDir(pkgInfo, result.paths, true) + if pkgInfo.bin.len > 0: + let paths = result.deps.map(dep => dep.getRealDir()) + buildFromDir(pkgInfo, paths, true) let pkgDestDir = pkgInfo.getPkgDest(options) if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"): @@ -462,8 +473,8 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, # processDeps). saveNimbleData(options) - # Return the paths to the dependencies of this package. - result.paths.add pkgDestDir + # Return the dependencies of this package (mainly for paths). + result.deps.add pkgInfo result.pkg = pkgInfo result.pkg.isInstalled = true result.pkg.myPath = dest @@ -496,7 +507,7 @@ proc getDownloadInfo*(pv: PkgTuple, options: Options, proc install(packages: seq[PkgTuple], options: Options, - doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] = + doPrompt = true): tuple[deps: seq[PackageInfo], pkg: PackageInfo] = if packages == @[]: result = installFromDir(getCurrentDir(), newVRAny(), options, "") else: @@ -530,7 +541,8 @@ proc install(packages: seq[PkgTuple], proc build(options: Options) = var pkgInfo = getPkgInfo(getCurrentDir(), options) nimScriptHint(pkgInfo) - let paths = processDeps(pkginfo, options) + let deps = processDeps(pkginfo, options) + let paths = deps.map(dep => dep.getRealDir()) var args = options.action.compileOptions buildFromDir(pkgInfo, paths, args) @@ -544,10 +556,10 @@ proc execBackend(options: Options) = var pkgInfo = getPkgInfo(getCurrentDir(), options) nimScriptHint(pkgInfo) - let paths = processDeps(pkginfo, options) + let deps = processDeps(pkginfo, options) var args = "" - for path in paths: args.add("--path:\"" & path & "\" ") + for dep in deps: args.add("--path:\"" & dep.getRealDir() & "\" ") for option in options.action.compileOptions: args.add("\"" & option & "\" ") diff --git a/src/nimblepkg/common.nim b/src/nimblepkg/common.nim index ffdd323..81bd0f9 100644 --- a/src/nimblepkg/common.nim +++ b/src/nimblepkg/common.nim @@ -18,6 +18,7 @@ when not defined(nimscript): isNimScript*: bool ## Determines if this pkg info was read from a nims file isMinimal*: bool isInstalled*: bool ## Determines if the pkg this info belongs to is installed + isLinked*: bool ## Determines if the pkg this info belongs to has been linked via `develop` postHooks*: HashSet[string] ## Useful to know so that Nimble doesn't execHook unnecessarily preHooks*: HashSet[string] name*: string diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index b2f0ea7..85821d5 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -66,7 +66,7 @@ proc getNameVersion*(pkgpath: string): tuple[name, version: string] = ## ## Also works for file paths like: ## ``/home/user/.nimble/pkgs/package-0.1/package.nimble`` - if pkgPath.splitFile.ext == ".nimble" or pkgPath.splitFile.ext == ".babel": + if pkgPath.splitFile.ext in [".nimble", ".nimble-link", ".babel"]: return getNameVersion(pkgPath.splitPath.head) result.name = "" @@ -281,7 +281,7 @@ proc findNimbleFile*(dir: string; error: bool): string = if kind in {pcFile, pcLinkToFile}: let ext = path.splitFile.ext case ext - of ".babel", ".nimble": + of ".babel", ".nimble", ".nimble-link": result = path inc hits else: discard @@ -293,8 +293,16 @@ proc findNimbleFile*(dir: string; error: bool): string = raise newException(NimbleError, "Specified directory does not contain a .nimble file.") else: - display("Warning:", "No .nimble file found for " & dir, Warning, - HighPriority) + display("Warning:", "No .nimble or .nimble-link file found for " & + dir, Warning, HighPriority) + + if result.splitFile.ext == ".nimble-link": + # Return the path of the real .nimble file. + let lines = readFile(result).splitLines() + result = lines[0] + if not fileExists(result): + raiseNimbleError("The .nimble-link file is pointing to a missing" & + " file: " & result) proc getInstalledPkgsMin*(libsDir: string, options: Options): seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = @@ -309,13 +317,15 @@ proc getInstalledPkgsMin*(libsDir: string, options: Options): let nimbleFile = findNimbleFile(path, false) if nimbleFile != "": let meta = readMetaData(path) - let (name, version) = getNameVersion(nimbleFile) + let (name, version) = getNameVersion(path) var pkg = initPackageInfo(nimbleFile) pkg.name = name pkg.version = version pkg.specialVersion = version pkg.isMinimal = true pkg.isInstalled = true + pkg.isLinked = + cmpPaths(nimbleFile.splitFile().dir, path) != 0 result.add((pkg, meta)) proc withinRange*(pkgInfo: PackageInfo, verRange: VersionRange): bool = @@ -369,7 +379,7 @@ proc findAllPkgs*(pkglist: seq[tuple[pkgInfo: PackageInfo, meta: MetaData]], proc getRealDir*(pkgInfo: PackageInfo): string = ## Returns the directory containing the package source files. - if pkgInfo.srcDir != "" and not pkgInfo.isInstalled: + if pkgInfo.srcDir != "" and (not pkgInfo.isInstalled or pkgInfo.isLinked): result = pkgInfo.mypath.splitFile.dir / pkgInfo.srcDir else: result = pkgInfo.mypath.splitFile.dir @@ -515,6 +525,8 @@ when isMainModule: ("package", "#head") doAssert getNameVersion("/home/user/.nimble/libs/package-#branch-with-dashes") == ("package", "#branch-with-dashes") + # readPackageInfo (and possibly more) depends on this not raising. + doAssert getNameVersion("/home/user/.nimble/libs/package") == ("package", "") doAssert toValidPackageName("foo__bar") == "foo_bar" doAssert toValidPackageName("jhbasdh!£$@%#^_&*_()qwe") == "jhbasdh_qwe" diff --git a/src/nimblepkg/packageparser.nim b/src/nimblepkg/packageparser.nim index b69bd75..befa513 100644 --- a/src/nimblepkg/packageparser.nim +++ b/src/nimblepkg/packageparser.nim @@ -292,6 +292,11 @@ proc readPackageInfo(nf: NimbleFile, options: Options, result.version = minimalInfo.version result.isNimScript = true result.isMinimal = true + + # It's possible this proc will receive a .nimble-link file eventually, + # I added this assert to hopefully make this error clear for everyone. + let msg = "No version detected. Received nimble-link?" + assert result.version.len > 0, msg else: try: readPackageInfoFromNims(nf, options, result) @@ -386,6 +391,8 @@ proc getInstalledPkgs*(libsDir: string, options: Options): raise exc pkg.isInstalled = true + pkg.isLinked = + cmpPaths(nimbleFile.splitFile().dir, path) != 0 result.add((pkg, meta)) proc isNimScript*(nf: string, options: Options): bool = @@ -393,7 +400,9 @@ proc isNimScript*(nf: string, options: Options): bool = proc toFullInfo*(pkg: PackageInfo, options: Options): PackageInfo = if pkg.isMinimal: - return getPkgInfoFromFile(pkg.mypath, options) + result = getPkgInfoFromFile(pkg.mypath, options) + result.isInstalled = pkg.isInstalled + result.isLinked = pkg.isLinked else: return pkg diff --git a/tests/develop/dependent/dependent.nimble b/tests/develop/dependent/dependent.nimble new file mode 100644 index 0000000..7bab4b7 --- /dev/null +++ b/tests/develop/dependent/dependent.nimble @@ -0,0 +1,12 @@ +# Package + +version = "1.0" +author = "Dominik Picheta" +description = "dependent" +license = "MIT" + +srcDir = "src" + +# Dependencies + +requires "nim >= 0.16.0", "srcdirtest" diff --git a/tests/develop/dependent/src/dependent.nim b/tests/develop/dependent/src/dependent.nim new file mode 100644 index 0000000..37231ba --- /dev/null +++ b/tests/develop/dependent/src/dependent.nim @@ -0,0 +1,3 @@ +import srcdirtest + +doAssert foo() == "correct" \ No newline at end of file diff --git a/tests/develop/srcdirtest/src/srcdirtest.nim b/tests/develop/srcdirtest/src/srcdirtest.nim index 53c687a..54b5b46 100644 --- a/tests/develop/srcdirtest/src/srcdirtest.nim +++ b/tests/develop/srcdirtest/src/srcdirtest.nim @@ -1 +1,4 @@ +proc foo*(): string = + return "correct" + echo("hello") \ No newline at end of file diff --git a/tests/tester.nim b/tests/tester.nim index af544f9..b835ec8 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -24,10 +24,11 @@ test "can compile with --os:windows": template cd*(dir: string, body: untyped) = ## Sets the current dir to ``dir``, executes ``body`` and restores the ## previous working dir. - let lastDir = getCurrentDir() - setCurrentDir(dir) - body - setCurrentDir(lastDir) + block: + let lastDir = getCurrentDir() + setCurrentDir(dir) + body + setCurrentDir(lastDir) proc execNimble(args: varargs[string]): tuple[output: string, exitCode: int] = var quotedArgs = @args @@ -500,4 +501,10 @@ suite "develop feature": let split = readFile(path).splitLines() check split.len == 2 check split[0].endsWith("develop/srcdirtest/srcdirtest.nimble") - check split[1].endsWith("develop/srcdirtest/src") \ No newline at end of file + check split[1].endsWith("develop/srcdirtest/src") + + cd "develop/dependent": + let (output, exitCode) = execNimble("c", "-r", "src/dependent") + checkpoint output + check(output.processOutput.inLines("hello")) + check exitCode == QuitSuccess \ No newline at end of file