Implements basic support for building with .nimble-link'ed packages.

This commit is contained in:
Dominik Picheta 2017-08-16 22:22:00 +01:00
commit 97dc0ffb45
8 changed files with 90 additions and 31 deletions

View file

@ -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 & "\" ")

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -0,0 +1,12 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "dependent"
license = "MIT"
srcDir = "src"
# Dependencies
requires "nim >= 0.16.0", "srcdirtest"

View file

@ -0,0 +1,3 @@
import srcdirtest
doAssert foo() == "correct"

View file

@ -1 +1,4 @@
proc foo*(): string =
return "correct"
echo("hello")

View file

@ -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")
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