Implements develop command. Refs #240.

This commit is contained in:
Dominik Picheta 2017-08-13 15:04:40 +01:00
commit 56dd401831
12 changed files with 234 additions and 57 deletions

View file

@ -306,7 +306,8 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], forRelease: bool) =
buildFromDir(pkgInfo, paths, args)
proc saveNimbleMeta(pkgDestDir, url, vcsRevision: string,
filesInstalled, bins: HashSet[string]) =
filesInstalled, bins: HashSet[string],
isLink: bool = false) =
## Saves the specified data into a ``nimblemeta.json`` file inside
## ``pkgDestDir``.
##
@ -314,6 +315,9 @@ proc saveNimbleMeta(pkgDestDir, url, vcsRevision: string,
## installed.
## bins - A list of binary filenames which have been installed for this
## package.
##
## isLink - Determines whether the installed package is a .nimble-link.
# TODO: Move to packageinstaller.nim
var nimblemeta = %{"url": %url}
if not vcsRevision.isNil:
nimblemeta["vcsRevision"] = %vcsRevision
@ -325,8 +329,20 @@ proc saveNimbleMeta(pkgDestDir, url, vcsRevision: string,
nimblemeta["binaries"] = binaries
for bin in bins:
binaries.add(%bin)
nimblemeta["isLink"] = %isLink
writeFile(pkgDestDir / "nimblemeta.json", $nimblemeta)
proc saveNimbleMeta(pkgDestDir, pkgDir, vcsRevision: string) =
## Overload of saveNimbleMeta for linked (.nimble-link) packages.
##
## pkgDestDir - The directory where the package has been installed.
## For example: ~/.nimble/pkgs/jester-#head/
##
## pkgDir - The directory where the original package files are.
## For example: ~/projects/jester/
saveNimbleMeta(pkgDestDir, "file://" & pkgDir, vcsRevision,
initSet[string](), initSet[string](), true)
proc removePkgDir(dir: string, options: Options) =
## Removes files belonging to the package in ``dir``.
try:
@ -399,7 +415,6 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
var pkgInfo = getPkgInfo(dir, options)
let realDir = pkgInfo.getRealDir()
let binDir = options.getBinDir()
let pkgsDir = options.getPkgsDir()
var depsOptions = options
depsOptions.depsOnly = false
@ -421,12 +436,11 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
# if the build fails then the old package will still be installed.
if pkgInfo.bin.len > 0: buildFromDir(pkgInfo, result.paths, true)
let versionStr = '-' & pkgInfo.specialVersion
let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr)
let pkgDestDir = pkgInfo.getPkgDest(options)
if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"):
if not options.prompt(pkgInfo.name & versionStr &
" already exists. Overwrite?"):
let msg = "$1@$2 already exists. Overwrite?" %
[pkgInfo.name, pkgInfo.specialVersion]
if not options.prompt(msg):
raise NimbleQuit(msg: "")
removePkgDir(pkgDestDir, options)
# Remove any symlinked binaries
@ -495,49 +509,6 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
display("Success:", pkgInfo.name & " installed successfully.",
Success, HighPriority)
proc getNimbleTempDir(): string =
## Returns a path to a temporary directory.
##
## The returned path will be the same for the duration of the process but
## different for different runs of it. You have to make sure to create it
## first. In release builds the directory will be removed when nimble finishes
## its work.
result = getTempDir() / "nimble_"
when defined(windows):
proc GetCurrentProcessId(): int32 {.stdcall, dynlib: "kernel32",
importc: "GetCurrentProcessId".}
result.add($GetCurrentProcessId())
else:
result.add($getpid())
proc downloadPkg(url: string, verRange: VersionRange,
downMethod: DownloadMethod,
options: Options): (string, Version) =
## Downloads the repository as specified by ``url`` and ``verRange`` using
## the download method specified.
##
## Returns the directory where it was downloaded and the concrete version
## which was downloaded.
let downloadDir = (getNimbleTempDir() / getDownloadDirName(url, verRange))
createDir(downloadDir)
var modUrl =
if url.startsWith("git://") and options.config.cloneUsingHttps:
"https://" & url[6 .. ^1]
else: url
# Fixes issue #204
# github + https + trailing url slash causes a
# checkout/ls-remote to fail with Repository not found
if modUrl.contains("github.com") and modUrl.endswith("/"):
modUrl = modUrl[0 .. ^2]
display("Downloading", "$1 using $2" % [modUrl, $downMethod],
priority = HighPriority)
result = (
downloadDir,
doDownload(modUrl, downloadDir, verRange, downMethod, options)
)
proc getDownloadInfo*(pv: PkgTuple, options: Options,
doPrompt: bool): (DownloadMethod, string) =
if pv.name.isURL:
@ -931,6 +902,70 @@ proc listTasks(options: Options) =
let nimbleFile = findNimbleFile(getCurrentDir(), true)
nimscriptsupport.listTasks(nimbleFile, options)
proc developFromDir(dir: string, options: Options) =
if options.depsOnly:
raiseNimbleError("Cannot develop dependencies only.")
var pkgInfo = getPkgInfo(dir, options)
if pkgInfo.bin.len > 0:
if "nim" in pkgInfo.skipExt:
raiseNimbleError("Cannot develop packages that are binaries only.")
display("Warning:", "This package's binaries will not be compiled " &
"nor symlinked for development.", Warning, HighPriority)
# Overwrite the version to #head always.
pkgInfo.specialVersion = "#head"
# Dependencies need to be processed before the creation of the pkg dir.
discard processDeps(pkgInfo, options)
# This is similar to the code in `installFromDir`, except that we
# *consciously* not worry about the package's binaries.
let pkgDestDir = pkgInfo.getPkgDest(options)
if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"):
let msg = "$1@$2 already exists. Overwrite?" %
[pkgInfo.name, pkgInfo.specialVersion]
if not options.prompt(msg):
raise NimbleQuit(msg: "")
removePkgDir(pkgDestDir, options)
createDir(pkgDestDir)
# The .nimble-link file contains the path to the real .nimble file,
# and a secondary path to the source directory of the package.
# The secondary path is necessary so that the package's .nimble file doesn't
# need to be read. This will mean that users will need to re-run
# `nimble develop` if they change their `srcDir` but I think it's a worthy
# compromise.
let contents = pkgInfo.myPath & "\n" & pkgInfo.getRealDir()
writeFile(pkgDestDir / pkgInfo.name.addFileExt("nimble-link"), contents)
# TODO: Handle dependencies of the package we are developing (they need to be
# installed).
# Save a nimblemeta.json file.
saveNimbleMeta(pkgDestDir, "file://" & dir, vcsRevisionInDir(dir))
# Save the nimble data (which might now contain reverse deps added in
# processDeps).
saveNimbleData(options)
proc develop(options: Options) =
if options.action.packages == @[]:
developFromDir(getCurrentDir(), options)
else:
# Install each package.
for pv in options.action.packages:
let downloadDir = getCurrentDir() / pv.name
if dirExists(downloadDir):
let msg = "Cannot clone into '$1': directory exists." % downloadDir
let hint = "Remove the directory, or run this command somewhere else."
raiseNimbleError(msg, hint)
let (meth, url) = getDownloadInfo(pv, options, true)
discard downloadPkg(url, pv.ver, meth, options, downloadDir)
developFromDir(downloadDir, options)
proc execHook(options: Options, before: bool): bool =
## Returns whether to continue.
result = true
@ -1002,6 +1037,8 @@ proc doAction(options: Options) =
dump(options)
of actionTasks:
listTasks(options)
of actionDevelop:
develop(options)
of actionNil:
assert false
of actionCustom:

View file

@ -212,6 +212,42 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
verifyClone()
proc downloadPkg*(url: string, verRange: VersionRange,
downMethod: DownloadMethod,
options: Options,
downloadPath = ""): (string, Version) =
## Downloads the repository as specified by ``url`` and ``verRange`` using
## the download method specified.
##
## If `downloadPath` isn't specified a location in /tmp/ will be used.
##
## Returns the directory where it was downloaded and the concrete version
## which was downloaded.
let downloadDir =
if downloadPath == "":
(getNimbleTempDir() / getDownloadDirName(url, verRange))
else:
downloadPath
createDir(downloadDir)
var modUrl =
if url.startsWith("git://") and options.config.cloneUsingHttps:
"https://" & url[6 .. ^1]
else: url
# Fixes issue #204
# github + https + trailing url slash causes a
# checkout/ls-remote to fail with Repository not found
if modUrl.contains("github.com") and modUrl.endswith("/"):
modUrl = modUrl[0 .. ^2]
display("Downloading", "$1 using $2" % [modUrl, $downMethod],
priority = HighPriority)
result = (
downloadDir,
doDownload(modUrl, downloadDir, verRange, downMethod, options)
)
proc echoPackageVersions*(pkg: Package) =
let downMethod = pkg.downloadMethod.getDownloadMethod()
case downMethod

View file

@ -27,15 +27,16 @@ type
actionNil, actionRefresh, actionInit, actionDump, actionPublish,
actionInstall, actionSearch,
actionList, actionBuild, actionPath, actionUninstall, actionCompile,
actionDoc, actionCustom, actionTasks
actionDoc, actionCustom, actionTasks, actionDevelop
Action* = object
case typ*: ActionType
of actionNil, actionList, actionPublish, actionTasks: nil
of actionRefresh:
optionalURL*: string # Overrides default package list.
of actionInstall, actionPath, actionUninstall:
packages*: seq[PkgTuple] # Optional only for actionInstall.
of actionInstall, actionPath, actionUninstall, actionDevelop:
packages*: seq[PkgTuple] # Optional only for actionInstall
# and actionDevelop.
of actionSearch:
search*: seq[string] # Search string.
of actionInit, actionDump:
@ -56,6 +57,9 @@ Usage: nimble COMMAND [opts]
Commands:
install [pkgname, ...] Installs a list of packages.
[-d, --depsOnly] Install only dependencies.
develop [pkgname, ...] Clones a list of packages for development.
Symlinks the cloned packages or any package
in the current working directory.
init [pkgname] Initializes a new Nimble project.
publish Publishes a package on nim-lang/packages.
The current working directory needs to be the
@ -141,6 +145,8 @@ proc parseActionType*(action: string): ActionType =
result = actionPublish
of "tasks":
result = actionTasks
of "develop":
result = actionDevelop
else:
result = actionCustom
@ -149,7 +155,7 @@ proc initAction*(options: var Options, key: string) =
## `key`.
let keyNorm = key.normalize()
case options.action.typ
of actionInstall, actionPath:
of actionInstall, actionPath, actionDevelop, actionUninstall:
options.action.packages = @[]
of actionCompile, actionDoc, actionBuild:
options.action.compileOptions = @[]
@ -164,8 +170,6 @@ proc initAction*(options: var Options, key: string) =
options.action.optionalURL = ""
of actionSearch:
options.action.search = @[]
of actionUninstall:
options.action.packages = @[]
of actionCustom:
options.action.command = key
options.action.arguments = @[]

View file

@ -497,6 +497,11 @@ proc iterInstallFiles*(realDir: string, pkgInfo: PackageInfo,
action(file)
proc getPkgDest*(pkgInfo: PackageInfo, options: Options): string =
let versionStr = '-' & pkgInfo.specialVersion
let pkgDestDir = options.getPkgsDir() / (pkgInfo.name & versionStr)
return pkgDestDir
when isMainModule:
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
("packagea", "0.1")

View file

@ -141,3 +141,20 @@ proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool =
for key, val in pairs(j):
if key == elem.key and val == elem.val:
return true
when not defined(windows):
from posix import getpid
proc getNimbleTempDir*(): string =
## Returns a path to a temporary directory.
##
## The returned path will be the same for the duration of the process but
## different for different runs of it. You have to make sure to create it
## first. In release builds the directory will be removed when nimble finishes
## its work.
result = getTempDir() / "nimble_"
when defined(windows):
proc GetCurrentProcessId(): int32 {.stdcall, dynlib: "kernel32",
importc: "GetCurrentProcessId".}
result.add($GetCurrentProcessId())
else:
result.add($getpid())

View file

@ -0,0 +1 @@
echo("hello")

View file

@ -0,0 +1,14 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "binary"
license = "MIT"
bin = @["binary"]
skipExt = @["nim"]
# Dependencies
requires "nim >= 0.16.0"

View file

@ -0,0 +1 @@
echo("hello")

View file

@ -0,0 +1,12 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "hybrid"
license = "MIT"
bin = @["hybrid"]
# Dependencies
requires "nim >= 0.16.0"

View file

@ -0,0 +1 @@
echo("hello")

View file

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

View file

@ -460,4 +460,41 @@ test "can pass args with spaces to Nim (#351)":
" -d:myVar=\"string with spaces\"" &
" binaryPackage")
checkpoint output
check exitCode == QuitSuccess
check exitCode == QuitSuccess
suite "develop feature":
test "can reject binary packages":
cd "develop/binary":
let (output, exitCode) = execNimble("develop")
checkpoint output
check output.processOutput.inLines("cannot develop packages")
check exitCode == QuitFailure
test "can develop hybrid":
cd "develop/hybrid":
let (output, exitCode) = execNimble("develop")
checkpoint output
check output.processOutput.inLines("will not be compiled")
check exitCode == QuitSuccess
let path = installDir / "pkgs" / "hybrid-#head" / "hybrid.nimble-link"
check fileExists(path)
let split = readFile(path).splitLines()
check split.len == 2
check split[0].endsWith("develop/hybrid/hybrid.nimble")
check split[1].endsWith("develop/hybrid")
test "can develop with srcDir":
cd "develop/srcdirtest":
let (output, exitCode) = execNimble("develop")
checkpoint output
check(not output.processOutput.inLines("will not be compiled"))
check exitCode == QuitSuccess
let path = installDir / "pkgs" / "srcdirtest-#head" /
"srcdirtest.nimble-link"
check fileExists(path)
let split = readFile(path).splitLines()
check split.len == 2
check split[0].endsWith("develop/srcdirtest/srcdirtest.nimble")
check split[1].endsWith("develop/srcdirtest/src")