diff --git a/.travis.yml b/.travis.yml index 3f824f3..f1ff69e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ os: + - windows - linux - osx @@ -7,23 +8,17 @@ language: c env: - BRANCH=0.19.6 - BRANCH=0.20.2 + - BRANCH=1.0.6 # This is the latest working Nim version against which Nimble is being tested - - BRANCH=#2565d3d102efd21ba02ed1f3b96d892fe2637d2b + - BRANCH=#ab525cc48abdbbbed1f772e58e9fe21474f70f07 cache: directories: - - "$HOME/.choosenim/toolchains/nim-0.19.6" - - "$HOME/.choosenim/toolchains/nim-0.20.2" + - "$HOME/.choosenim" install: - - export CHOOSENIM_CHOOSE_VERSION=$BRANCH - - | - curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh - sh init.sh -y - -before_script: - - export CHOOSENIM_NO_ANALYTICS=1 - - export PATH=$HOME/.nimble/bin:$PATH + - curl https://gist.github.com/genotrance/fb53504a4fba88bc5201d3783df5c522/raw/travis.sh -LsSf -o travis.sh + - source travis.sh script: - cd tests diff --git a/readme.markdown b/readme.markdown index e047e1b..89f7a22 100644 --- a/readme.markdown +++ b/readme.markdown @@ -172,12 +172,13 @@ example: This is of course Git-specific, for Mercurial, use ``tip`` instead of ``head``. A branch, tag, or commit hash may also be specified in the place of ``head``. -Instead of specifying a VCS branch, you may also specify a version range, for -example: +Instead of specifying a VCS branch, you may also specify a concrete version or a +version range, for example: + $ nimble install nimgame@0.5 $ nimble install nimgame@"> 0.5" -In this case a version which is greater than ``0.5`` will be installed. +The latter command will install a version which is greater than ``0.5``. If you don't specify a parameter and there is a ``package.nimble`` file in your current working directory then Nimble will install the package residing in @@ -504,7 +505,7 @@ For a package named "foobar", the recommended project structure is the following └── src └── foobar.nim # Imported via `import foobar` └── tests # Contains the tests - ├── nim.cfg + ├── config.nims ├── tfoo1.nim # First test └── tfoo2.nim # Second test diff --git a/src/nimble.nim b/src/nimble.nim index bb74ac0..d7e011e 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -3,12 +3,13 @@ import system except TResult -import os, tables, strtabs, json, algorithm, sets, uri, sugar, sequtils +import os, tables, strtabs, json, algorithm, sets, uri, sugar, sequtils, osproc import std/options as std_opt import strutils except toLower from unicode import toLower from sequtils import toSeq +from strformat import fmt import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, nimblepkg/download, nimblepkg/config, nimblepkg/common, @@ -220,7 +221,7 @@ proc buildFromDir( options: Options ) = ## Builds a package as specified by ``pkgInfo``. - let binToBuild = options.getCompilationBinary() + let binToBuild = options.getCompilationBinary(pkgInfo) # Handle pre-`build` hook. let realDir = pkgInfo.getRealDir() cd realDir: # Make sure `execHook` executes the correct .nimble file. @@ -250,10 +251,14 @@ proc buildFromDir( if not existsDir(outputDir): createDir(outputDir) + let input = realDir / bin.changeFileExt("nim") + # `quoteShell` would be more robust than `\"` (and avoid quoting when + # un-necessary) but would require changing `extractBin` + let cmd = "\"$#\" $# --noNimblePath $# $# $# \"$#\"" % + [getNimBin(), pkgInfo.backend, nimblePkgVersion, + join(args, " "), outputOpt, input] try: - doCmd("\"" & getNimBin() & "\" $# --noNimblePath $# $# $# \"$#\"" % - [pkgInfo.backend, nimblePkgVersion, join(args, " "), outputOpt, - realDir / bin.changeFileExt("nim")]) + doCmd(cmd, showCmd = true) binariesBuilt.inc() except NimbleError: let currentExc = (ref NimbleError)(getCurrentException()) @@ -381,7 +386,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, options.action.passNimFlags else: @[] - buildFromDir(pkgInfo, paths, flags & "-d:release", options) + buildFromDir(pkgInfo, paths, "-d:release" & flags, options) let pkgDestDir = pkgInfo.getPkgDest(options) if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"): @@ -534,9 +539,9 @@ proc build(options: Options) = var args = options.getCompilationFlags() buildFromDir(pkgInfo, paths, args, options) -proc execBackend(options: Options) = +proc execBackend(pkgInfo: PackageInfo, options: Options) = let - bin = options.getCompilationBinary().get() + bin = options.getCompilationBinary(pkgInfo).get() binDotNim = bin.addFileExt("nim") if bin == "": raise newException(NimbleError, "You need to specify a file.") @@ -724,6 +729,11 @@ proc dump(options: Options) = echo "backend: ", p.backend.escape proc init(options: Options) = + # Check whether the vcs is installed. + let vcsBin = options.action.vcsOption + if vcsBin != "" and findExe(vcsBin, true) == "": + raise newException(NimbleError, "Please install git or mercurial first") + # Determine the package name. let pkgName = if options.action.projName != "": @@ -858,6 +868,17 @@ js - Compile using JavaScript backend.""", pkgRoot ) + # Create a git or hg repo in the new nimble project. + if vcsBin != "": + let cmd = fmt"cd {pkgRoot} && {vcsBin} init" + let ret: tuple[output: string, exitCode: int] = execCmdEx(cmd) + if ret.exitCode != 0: quit ret.output + + var ignoreFile = if vcsBin == "git": ".gitignore" else: ".hgignore" + var fd = open(joinPath(pkgRoot, ignoreFile), fmWrite) + fd.write(pkgName & "\n") + fd.close() + display("Success:", "Package $# created successfully" % [pkgName], Success, HighPriority) @@ -1053,17 +1074,21 @@ proc test(options: Options) = if options.continueTestsOnFailure: inc tests try: - execBackend(optsCopy) + execBackend(pkgInfo, optsCopy) except NimbleError: inc failures else: - execBackend(optsCopy) + execBackend(pkgInfo, optsCopy) let existsAfter = existsFile(binFileName) canRemove = not existsBefore and existsAfter if canRemove: - removeFile(binFileName) + try: + removeFile(binFileName) + except OSError as exc: + display("Warning:", "Failed to delete " & binFileName & ": " & + exc.msg, Warning, MediumPriority) if failures == 0: display("Success:", "All tests passed", Success, HighPriority) @@ -1092,25 +1117,25 @@ proc check(options: Options) = proc run(options: Options) = # Verify parameters. - let binary = options.getCompilationBinary().get("") + var pkgInfo = getPkgInfo(getCurrentDir(), options) + + let binary = options.getCompilationBinary(pkgInfo).get("") if binary.len == 0: raiseNimbleError("Please specify a binary to run") - var pkgInfo = getPkgInfo(getCurrentDir(), options) if binary notin pkgInfo.bin: raiseNimbleError( "Binary '$#' is not defined in '$#' package." % [binary, pkgInfo.name] ) - let binaryPath = pkgInfo.getOutputDir(binary) - # Build the binary. build(options) - # Now run it. - let args = options.action.runFlags.join(" ") + let binaryPath = pkgInfo.getOutputDir(binary) + let cmd = quoteShellCommand(binaryPath & options.action.runFlags) + displayDebug("Executing", cmd) + cmd.execCmd.quit - doCmd("$# $#" % [binaryPath, args], showOutput = true) proc doAction(options: var Options) = if options.showHelp: @@ -1155,7 +1180,8 @@ proc doAction(options: var Options) = of actionRun: run(options) of actionCompile, actionDoc: - execBackend(options) + var pkgInfo = getPkgInfo(getCurrentDir(), options) + execBackend(pkgInfo, options) of actionInit: init(options) of actionPublish: diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim index 85bf54f..3bbfa25 100644 --- a/src/nimblepkg/download.nim +++ b/src/nimblepkg/download.nim @@ -300,4 +300,17 @@ when isMainModule: }) doAssert expected == getVersionList(data) + + block: + let data2 = @["v0.1.0", "v0.1.1", "v0.2.0", + "0.4.0", "v0.4.2"] + let expected2 = toOrderedTable[Version, string]({ + newVersion("0.4.2"): "v0.4.2", + newVersion("0.4.0"): "0.4.0", + newVersion("0.2.0"): "v0.2.0", + newVersion("0.1.1"): "v0.1.1", + newVersion("0.1.0"): "v0.1.0", + }) + doAssert expected2 == getVersionList(data2) + echo("Everything works!") diff --git a/src/nimblepkg/nimscriptapi.nim b/src/nimblepkg/nimscriptapi.nim index a8b4ba3..ec2f46c 100644 --- a/src/nimblepkg/nimscriptapi.nim +++ b/src/nimblepkg/nimscriptapi.nim @@ -17,7 +17,7 @@ var author*: string ## The package's author. description*: string ## The package's description. license*: string ## The package's license. - srcdir*: string ## The package's source directory. + srcDir*: string ## The package's source directory. binDir*: string ## The package's binary directory. backend*: string ## The package's backend. @@ -101,7 +101,7 @@ proc printPkgInfo(): string = printIfLen author printIfLen description printIfLen license - printIfLen srcdir + printIfLen srcDir printIfLen binDir printIfLen backend diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index bf050fe..14d1c31 100644 --- a/src/nimblepkg/options.nim +++ b/src/nimblepkg/options.nim @@ -51,12 +51,13 @@ type search*: seq[string] # Search string. of actionInit, actionDump: projName*: string + vcsOption*: string of actionCompile, actionDoc, actionBuild: file*: string backend*: string compileOptions: seq[string] of actionRun: - runFile: string + runFile: Option[string] compileFlags: seq[string] runFlags*: seq[string] of actionCustom: @@ -80,17 +81,19 @@ Commands: init [pkgname] Initializes a new Nimble project in the current directory or if a name is provided a new directory of the same name. + --git + --hg Create a git or hg repo in the new nimble project. publish Publishes a package on nim-lang/packages. The current working directory needs to be the toplevel directory of the Nimble package. uninstall [pkgname, ...] Uninstalls a list of packages. [-i, --inclDeps] Uninstall package and dependent package(s). build [opts, ...] [bin] Builds a package. - run [opts, ...] bin Builds and runs a package. - A binary name needs - to be specified after any compilation options, - any flags after the binary name are passed to - the binary when it is run. + run [opts, ...] [bin] Builds and runs a package. + Binary needs to be specified after any + compilation options if there are several + binaries defined, any flags after the binary + or -- arg are passed to the binary when it is run. c, cc, js [opts, ...] f.nim Builds a file inside a package. Passes options to the Nim compiler. test Compiles and executes tests @@ -201,8 +204,10 @@ proc initAction*(options: var Options, key: string) = else: options.action.backend = keyNorm of actionInit: options.action.projName = "" + options.action.vcsOption = "" of actionDump: options.action.projName = "" + options.action.vcsOption = "" options.forcePrompts = forcePromptYes of actionRefresh: options.action.optionalURL = "" @@ -261,6 +266,12 @@ proc parseCommand*(key: string, result: var Options) = result.action = Action(typ: parseActionType(key)) initAction(result, key) +proc setRunOptions(result: var Options, key, val: string, isArg: bool) = + if result.action.runFile.isNone() and (isArg or val == "--"): + result.action.runFile = some(key) + else: + result.action.runFlags.add(val) + proc parseArgument*(key: string, result: var Options) = case result.action.typ of actionNil: @@ -292,10 +303,7 @@ proc parseArgument*(key: string, result: var Options) = of actionBuild: result.action.file = key of actionRun: - if result.action.runFile.len == 0: - result.action.runFile = key - else: - result.action.runFlags.add(key) + result.setRunOptions(key, key, true) of actionCustom: result.action.arguments.add(key) else: @@ -349,6 +357,12 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = result.action.passNimFlags.add(val) else: wasFlagHandled = false + of actionInit: + case f + of "git", "hg": + result.action.vcsOption = f + else: + wasFlagHandled = false of actionUninstall: case f of "incldeps", "i": @@ -359,7 +373,8 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = if not isGlobalFlag: result.action.compileOptions.add(getFlagString(kind, flag, val)) of actionRun: - result.action.runFlags.add(getFlagString(kind, flag, val)) + result.showHelp = false + result.setRunOptions(flag, getFlagString(kind, flag, val), false) of actionCustom: if result.action.command.normalize == "test": if f == "continue" or f == "c": @@ -371,7 +386,8 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = if not wasFlagHandled and not isGlobalFlag: result.unknownFlags.add((kind, flag, val)) -proc initOptions(): Options = +proc initOptions*(): Options = + # Exported for choosenim Options( action: Action(typ: actionNil), pkgInfoCache: newTable[string, PackageInfo](), @@ -428,7 +444,7 @@ proc parseCmdLine*(): Options = else: parseArgument(key, result) of cmdLongOption, cmdShortOption: - parseFlag(key, val, result, kind) + parseFlag(key, val, result, kind) of cmdEnd: assert(false) # cannot happen handleUnknownFlags(result) @@ -513,15 +529,23 @@ proc getCompilationFlags*(options: Options): seq[string] = var opt = options return opt.getCompilationFlags() -proc getCompilationBinary*(options: Options): Option[string] = +proc getCompilationBinary*(options: Options, pkgInfo: PackageInfo): Option[string] = case options.action.typ of actionBuild, actionDoc, actionCompile: let file = options.action.file.changeFileExt("") if file.len > 0: return some(file) of actionRun: - let runFile = options.action.runFile.changeFileExt(ExeExt) + let optRunFile = options.action.runFile + let runFile = + if optRunFile.get("").len > 0: + optRunFile.get() + elif pkgInfo.bin.len == 1: + pkgInfo.bin[0] + else: + "" + if runFile.len > 0: - return some(runFile) + return some(runFile.changeFileExt(ExeExt)) else: - discard \ No newline at end of file + discard diff --git a/src/nimblepkg/tools.nim b/src/nimblepkg/tools.nim index b39fea7..a0e6a0e 100644 --- a/src/nimblepkg/tools.nim +++ b/src/nimblepkg/tools.nim @@ -11,7 +11,7 @@ proc extractBin(cmd: string): string = else: return cmd.split(' ')[0] -proc doCmd*(cmd: string, showOutput = false) = +proc doCmd*(cmd: string, showOutput = false, showCmd = false) = let bin = extractBin(cmd) if findExe(bin) == "": raise newException(NimbleError, "'" & bin & "' not in PATH.") @@ -20,7 +20,10 @@ proc doCmd*(cmd: string, showOutput = false) = stdout.flushFile() stderr.flushFile() - displayDebug("Executing", cmd) + if showCmd: + display("Executing", cmd, priority = MediumPriority) + else: + displayDebug("Executing", cmd) if showOutput: let exitCode = execCmd(cmd) displayDebug("Finished", "with exit code " & $exitCode) diff --git a/src/nimblepkg/version.nim b/src/nimblepkg/version.nim index 4834b6d..e4114f1 100644 --- a/src/nimblepkg/version.nim +++ b/src/nimblepkg/version.nim @@ -147,7 +147,7 @@ proc makeRange*(version: string, op: string): VersionRange = result = VersionRange(kind: verEqLater) of "<=": result = VersionRange(kind: verEqEarlier) - of "": + of "", "==": result = VersionRange(kind: verEq) else: raise newException(ParseVersionError, "Invalid operator: " & op) @@ -298,9 +298,10 @@ when isMainModule: doAssert(newVersion("0.1.0") <= newVersion("0.1")) var inter1 = parseVersionRange(">= 1.0 & <= 1.5") - doAssert inter1.kind == verIntersect + doAssert(inter1.kind == verIntersect) var inter2 = parseVersionRange("1.0") doAssert(inter2.kind == verEq) + doAssert(parseVersionRange("== 3.4.2") == parseVersionRange("3.4.2")) doAssert(not withinRange(newVersion("1.5.1"), inter1)) doAssert(withinRange(newVersion("1.0.2.3.4.5.6.7.8.9.10.11.12"), inter1)) diff --git a/tests/nimbleVersionDefine/src/nimbleVersionDefine b/tests/nimbleVersionDefine/src/nimbleVersionDefine index 3b089c0..af3cc22 100755 Binary files a/tests/nimbleVersionDefine/src/nimbleVersionDefine and b/tests/nimbleVersionDefine/src/nimbleVersionDefine differ diff --git a/tests/recursive/recursive.nimble b/tests/recursive/recursive.nimble index 972dd0b..d6b155d 100644 --- a/tests/recursive/recursive.nimble +++ b/tests/recursive/recursive.nimble @@ -9,10 +9,9 @@ license = "BSD" requires "nim >= 0.12.1" -when defined(windows): - let callNimble = "..\\..\\src\\nimble.exe" -else: - let callNimble = "../../src/nimble" +let + callNimble = getEnv("NIMBLE_TEST_BINARY_PATH") +doAssert callNimble.len != 0, "NIMBLE_TEST_BINARY_PATH not set" task recurse, "Level 1": echo 1 diff --git a/tests/tester.nim b/tests/tester.nim index ceb1fc6..446fecb 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -12,6 +12,9 @@ var installDir = rootDir / "tests" / "nimbleDir" const path = "../src/nimble" const stringNotFound = -1 +# Set env var to propagate nimble binary path +putEnv("NIMBLE_TEST_BINARY_PATH", nimblePath) + # Clear nimble dir. removeDir(installDir) createDir(installDir) @@ -35,7 +38,7 @@ proc execNimble(args: varargs[string]): tuple[output: string, exitCode: int] = var quotedArgs = @args quotedArgs.insert("--nimbleDir:" & installDir) quotedArgs.insert(nimblePath) - quotedArgs = quotedArgs.map((x: string) => ("\"" & x & "\"")) + quotedArgs = quotedArgs.map((x: string) => x.quoteShell) let path {.used.} = getCurrentDir().parentDir() / "src" @@ -480,17 +483,17 @@ test "issue #349": ] proc checkName(name: string) = - when defined(windows): - if name.toLowerAscii() in @["con", "nul"]: - return let (outp, code) = execNimble("init", "-y", name) let msg = outp.strip.processOutput() check code == QuitFailure check inLines(msg, "\"$1\" is an invalid package name: reserved name" % name) - removeFile(name.changeFileExt("nimble")) - removeDir("src") - removeDir("tests") + try: + removeFile(name.changeFileExt("nimble")) + removeDir("src") + removeDir("tests") + except OSError: + discard for reserved in reservedNames: checkName(reserved.toUpperAscii()) @@ -930,7 +933,8 @@ suite "nimble run": "blahblah", # The command to run ) check exitCode == QuitFailure - check output.contains("Binary 'blahblah' is not defined in 'run' package.") + check output.contains("Binary '$1' is not defined in 'run' package." % + "blahblah".changeFileExt(ExeExt)) test "Parameters passed to executable": cd "run": @@ -942,9 +946,54 @@ suite "nimble run": "check" # Second argument passed to the executed command. ) check exitCode == QuitSuccess - check output.contains("tests/run/run --debug check") + check output.contains("tests$1run$1$2 --debug check" % + [$DirSep, "run".changeFileExt(ExeExt)]) check output.contains("""Testing `nimble run`: @["--debug", "check"]""") + test "Parameters not passed to single executable": + cd "run": + var (output, exitCode) = execNimble( + "--debug", # Flag to enable debug verbosity in Nimble + "run", # Run command invokation + "--debug" # First argument passed to the executed command + ) + check exitCode == QuitSuccess + check output.contains("tests$1run$1$2 --debug" % + [$DirSep, "run".changeFileExt(ExeExt)]) + check output.contains("""Testing `nimble run`: @["--debug"]""") + + test "Parameters passed to single executable": + cd "run": + var (output, exitCode) = execNimble( + "--debug", # Flag to enable debug verbosity in Nimble + "run", # Run command invokation + "--", # Flag to set run file to "" before next argument + "--debug", # First argument passed to the executed command + "check" # Second argument passed to the executed command. + ) + check exitCode == QuitSuccess + check output.contains("tests$1run$1$2 --debug check" % + [$DirSep, "run".changeFileExt(ExeExt)]) + check output.contains("""Testing `nimble run`: @["--debug", "check"]""") + + test "Executable output is shown even when not debugging": + cd "run": + var (output, exitCode) = + execNimble("run", "run", "--option1", "arg1") + check exitCode == QuitSuccess + check output.contains("""Testing `nimble run`: @["--option1", "arg1"]""") + + test "Quotes and whitespace are well handled": + cd "run": + var (output, exitCode) = execNimble( + "run", "run", "\"", "\'", "\t", "arg with spaces" + ) + check exitCode == QuitSuccess + check output.contains( + """Testing `nimble run`: @["\"", "\'", "\t", "arg with spaces"]""" + ) + + test "NimbleVersion is defined": cd "nimbleVersionDefine": var (output, exitCode) = execNimble("c", "-r", "src/nimbleVersionDefine.nim")