diff --git a/.gitignore b/.gitignore index bac71d6..5744c47 100644 --- a/.gitignore +++ b/.gitignore @@ -6,15 +6,12 @@ nimcache/ # Absolute paths /src/babel /src/nimble +/tests/tester # executables from test and build /nimble -src/nimblepkg/cli -src/nimblepkg/packageinfo -src/nimblepkg/packageparser -src/nimblepkg/reversedeps -src/nimblepkg/version -src/nimblepkg/download +/tests/nimscript/nimscript +/tests/issue27/issue27 # Windows executables *.exe @@ -22,13 +19,4 @@ src/nimblepkg/download # VCC compiler and linker artifacts *.ilk -*.pdb - -# Editors and IDEs project files and folders -.vscode - -# VCS artifacts -*.orig - -# Test procedure artifacts -nimble_*.nims +*.pdb \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index f1ff69e..4f39d6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,26 @@ os: - - windows - linux - - osx +dist: trusty 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=#ab525cc48abdbbbed1f772e58e9fe21474f70f07 - cache: directories: + - "$HOME/.nimble" - "$HOME/.choosenim" install: - - curl https://gist.github.com/genotrance/fb53504a4fba88bc5201d3783df5c522/raw/travis.sh -LsSf -o travis.sh - - source travis.sh + - export CHOOSENIM_CHOOSE_VERSION="#7bb93c730ea87f" + - export NIM_LIB_PREFIX="$HOME/.choosenim/toolchains/nim-"$CHOOSENIM_CHOOSE_VERSION + - | + curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh + sh init.sh -y + +before_script: + - set -e + - set -x + - export CHOOSENIM_NO_ANALYTICS=1 + - export PATH=$HOME/.nimble/bin:$PATH script: - cd tests diff --git a/changelog.markdown b/changelog.markdown index 92f1d31..2b1e385 100644 --- a/changelog.markdown +++ b/changelog.markdown @@ -3,85 +3,6 @@ # Nimble changelog -## 0.11.0 - 22/09/2019 - -This is a major release containing nearly 60 commits. Most changes are -bug fixes, but this release also includes a couple new features: - -- Binaries can now be built and run using the new ``run`` command. -- The ``NimblePkgVersion`` is now defined so you can easily get the package - version in your source code - ([example](https://github.com/nim-lang/nimble/blob/4a2aaa07d/tests/nimbleVersionDefine/src/nimbleVersionDefine.nim)). - -Some other highlights: - -- Temporary files are now kept when the ``--debug`` flag is used. -- Fixed dependency resolution issues with "#head" packages (#432 and #672). -- The `install` command can now take Nim compiler flags via the new - ``--passNim`` flag. -- Command line arguments are now passed properly to tasks (#633). -- The ``test`` command now respects the specified backend (#631). -- The ``dump`` command will no longer prompt and now has an implicit ``-y``. -- Fixed bugs with the new nimscript executor (#665). -- Fixed multiple downloads and installs of the same package (#678). -- Nimble init no longer overwrites existing files (#581). -- Fixed incorrect submodule version being pulled when in a non-master branch (#675). - ----- - -Full changelog: https://github.com/nim-lang/nimble/compare/v0.10.2...v0.11.0 - -## 0.10.2 - 03/06/2019 - -This is a small release which avoids object variant changes that are now -treated as runtime errors (Nim #1286). It also adds support for `backend` -selection during `nimble init`. - -Multiple bug fixes are also included: -- Fixed an issue where failing tasks were not returning a non-zero return - value (#655). -- Error out if `bin` is a Nim source file (#597). -- Fixed an issue where nimble task would not run if file of same name exists. -- Fixed an issue that prevented multiple instances of nimble from running on - the same package. - ----- - -Full changelog: https://github.com/nim-lang/nimble/compare/v0.10.0...v0.10.2 - -## 0.10.0 - 27/05/2019 - -Nimble now uses the Nim compiler directly via `nim e` to execute nimble -scripts rather than embedding the Nim VM. This has multiple benefits: -- Evolve independently from Nim enabling new versions of Nimble to work - with multiple versions of Nim. -- Inherit all nimscript enhancements and bug fixes rather than having to - duplicate functionality. -- Fast build time and smaller binary. -- No dependency on the compiler package which could cause dependency issues - when nimble is used as a package. - -Several other features and fixes have been implemented to improve general -development and test workflows. -- `nimble test` now sports a `-continue` or `-c` flag that allows tests - to continue on failure, removes all created test binaries on completion - and warns if no tests found. -- The `--inclDeps` or `-i` flag enables `nimble uninstall` to remove all - dependent packages during uninstall. -- Added documentation on the usage of a custom `nimbleDir`. -- Package type interactive prompt is more readable. -- Save temporary files in a per-user temp dir to enable Nimble on multi-user - systems. -- CTRL-C is now handled correctly in interactive prompts. -- Fixed issue where empty package list led to error. -- Fixed issue where file:// was prepended incorrectly. -- Fixed miscellaneous issues in version parsing, Github auth and briefClone. -- Miscellaneous cleanup of deprecated procs. - ----- - -Full changelog: https://github.com/nim-lang/nimble/compare/v0.9.0...v0.10.0 - ## 0.9.0 - 19/09/2018 This is a major new release which contains at least one breaking change. diff --git a/nimble.nimble b/nimble.nimble index 6cd7e9e..7a8e416 100644 --- a/nimble.nimble +++ b/nimble.nimble @@ -1,6 +1,16 @@ +import ospaths +template thisModuleFile: string = instantiationInfo(fullPaths = true).filename + +when fileExists(thisModuleFile.parentDir / "src/nimblepkg/common.nim"): + # In the git repository the Nimble sources are in a ``src`` directory. + import src/nimblepkg/common +else: + # When the package is installed, the ``src`` directory disappears. + import nimblepkg/common + # Package -version = "0.11.0" +version = nimbleVersion author = "Dominik Picheta" description = "Nim package manager." license = "BSD" @@ -11,7 +21,7 @@ installExt = @["nim"] # Dependencies -requires "nim >= 0.13.0" +requires "nim >= 0.13.0", "compiler#head" when defined(nimdistros): import distros diff --git a/readme.markdown b/readme.markdown index 89f7a22..31ab0fc 100644 --- a/readme.markdown +++ b/readme.markdown @@ -15,7 +15,6 @@ Interested in learning **how to create a package**? Skip directly to that sectio - [nimble install](#nimble-install) - [nimble uninstall](#nimble-uninstall) - [nimble build](#nimble-build) - - [nimble run](#nimble-run) - [nimble c](#nimble-c) - [nimble list](#nimble-list) - [nimble search](#nimble-search) @@ -75,15 +74,10 @@ not need to install Nimble manually**. But in case you still want to install Nimble manually, you can follow the following instructions. -There are two ways to install Nimble manually. Using ``koch`` and using Nimble -itself. - -### Using koch - -The ``koch`` tool is included in the Nim distribution and +There are two ways to install Nimble manually. The first is using the +``koch`` tool included in the Nim distribution and [repository](https://github.com/nim-lang/Nim/blob/devel/koch.nim). -Simply navigate to the location of your Nim installation and execute the -following command to compile and install Nimble. +Simply execute the following command to compile and install Nimble. ``` ./koch nimble @@ -92,19 +86,23 @@ following command to compile and install Nimble. This will clone the Nimble repository, compile Nimble and copy it into Nim's bin directory. -### Using Nimble - -In most cases you will already have Nimble installed, you can install a newer -version of Nimble by simply running the following command: +The second approach is to install Nimble as a Nimble package. You can do this +by compiling Nimble, then running ``nimble install`` in Nimble's directory. ``` -nimble install nimble +git clone https://github.com/nim-lang/nimble.git +cd nimble +nim c src/nimble +src/nimble install ``` -This will download the latest release of Nimble and install it on your system. +**Note for Windows users**: You will need to rename ``nimble.exe`` after +compilation to something else like ``nimble1.exe``, then run +``src\nimble1.exe install``. -Note that you must have `~/.nimble/bin` in your PATH for this to work, if you're -using choosenim then you likely already have this set up correctly. +This will install Nimble to the default Nimble packages location: +``~/.nimble/pkgs``. The binary will be installed to ``~/.nimble/bin``, so you +will need to add this directory to your PATH. ## Nimble usage @@ -172,13 +170,12 @@ 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 concrete version or a -version range, for example: +Instead of specifying a VCS branch, you may also specify a version range, for +example: - $ nimble install nimgame@0.5 $ nimble install nimgame@"> 0.5" -The latter command will install a version which is greater than ``0.5``. +In this case a version which is greater than ``0.5`` will be installed. 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 @@ -239,13 +236,6 @@ flags, i.e. a debug build which includes stack traces but no GDB debug information. The ``install`` command will build the package in release mode instead. -### nimble run - -The ``run`` command can be used to build and run any binary specified in your -package's ``bin`` list. You can pass any compilation flags you wish by specifying -them before the ``run`` command, and you can specify arguments for your binary -by specifying them after the ``run`` command. - ### nimble c The ``c`` (or ``compile``, ``js``, ``cc``, ``cpp``) command can be used by @@ -428,7 +418,6 @@ You can also specify multiple dependencies like so: requires "nim >= 0.10.0", "foobar >= 0.1.0" requires "fizzbuzz >= 1.0" -requires "https://github.com/user/pkg#5a54b5e" ``` Nimble currently supports installation of packages from a local directory, a @@ -505,7 +494,7 @@ For a package named "foobar", the recommended project structure is the following └── src └── foobar.nim # Imported via `import foobar` └── tests # Contains the tests - ├── config.nims + ├── nim.cfg ├── tfoo1.nim # First test └── tfoo2.nim # Second test @@ -813,19 +802,6 @@ To summarise, the steps for release are: Once the new tag is in the remote repository, Nimble will be able to detect the new version. -##### Git Version Tagging - -Use dot separated numbers to represent the release version in the git -tag label. Nimble will parse these git tag labels to know which -versions of a package are published. - -``` text -v0.2.0 # 0.2.0 -v1 # 1 -v1.2.3-zuzu # 1.2.3 -foo-1.2.3.4 # 1.2.3.4 -``` - ## Publishing packages Publishing packages isn't a requirement. But doing so allows people to associate diff --git a/src/nimble.nim b/src/nimble.nim index d7e011e..e0b10f3 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -3,13 +3,12 @@ import system except TResult -import os, tables, strtabs, json, algorithm, sets, uri, sugar, sequtils, osproc -import std/options as std_opt +import httpclient, parseopt, os, osproc, pegs, tables, parseutils, + strtabs, json, algorithm, sets, uri, sugar, sequtils 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, @@ -17,7 +16,7 @@ import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, nimblepkg/cli, nimblepkg/packageinstaller, nimblepkg/reversedeps, nimblepkg/nimscriptexecutor, nimblepkg/init -import nimblepkg/nimscriptwrapper +import nimblepkg/nimscriptsupport proc refresh(options: Options) = ## Downloads the package list from the specified URL. @@ -96,7 +95,7 @@ proc copyFilesRec(origDir, currentDir, dest: string, ## Copies all the required files, skips files specified in the .nimble file ## (PackageInfo). ## Returns a list of filepaths to files which have been installed. - result = initHashSet[string]() + result = initSet[string]() let whitelistMode = pkgInfo.installDirs.len != 0 or pkgInfo.installFiles.len != 0 or @@ -157,8 +156,7 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] = "dependencies for $1@$2" % [pkginfo.name, pkginfo.specialVersion], priority = HighPriority) - var pkgList {.global.}: seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = @[] - once: pkgList = getInstalledPkgsMin(options.getPkgsDir(), options) + var pkgList = getInstalledPkgsMin(options.getPkgsDir(), options) var reverseDeps: seq[tuple[name, version: string]] = @[] for dep in pkginfo.requires: if dep.name == "nimrod" or dep.name == "nim": @@ -201,13 +199,12 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] = # in the path. var pkgsInPath: StringTableRef = newStringTable(modeCaseSensitive) for pkgInfo in result: - let currentVer = pkgInfo.getConcreteVersion(options) if pkgsInPath.hasKey(pkgInfo.name) and - pkgsInPath[pkgInfo.name] != currentVer: + pkgsInPath[pkgInfo.name] != pkgInfo.version: raise newException(NimbleError, "Cannot satisfy the dependency on $1 $2 and $1 $3" % - [pkgInfo.name, currentVer, pkgsInPath[pkgInfo.name]]) - pkgsInPath[pkgInfo.name] = currentVer + [pkgInfo.name, pkgInfo.version, pkgsInPath[pkgInfo.name]]) + pkgsInPath[pkgInfo.name] = pkgInfo.version # We add the reverse deps to the JSON file here because we don't want # them added if the above errorenous condition occurs @@ -216,33 +213,16 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] = for i in reverseDeps: addRevDep(options.nimbleData, i, pkginfo) -proc buildFromDir( - pkgInfo: PackageInfo, paths, args: seq[string], - options: Options -) = +proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], + args: var seq[string]) = ## Builds a package as specified by ``pkgInfo``. - let binToBuild = options.getCompilationBinary(pkgInfo) - # Handle pre-`build` hook. - let realDir = pkgInfo.getRealDir() - cd realDir: # Make sure `execHook` executes the correct .nimble file. - if not execHook(options, actionBuild, true): - raise newException(NimbleError, "Pre-hook prevented further execution.") - if pkgInfo.bin.len == 0: raise newException(NimbleError, "Nothing to build. Did you specify a module to build using the" & " `bin` key in your .nimble file?") - var args = args - let nimblePkgVersion = "-d:NimblePkgVersion=" & pkgInfo.version + let realDir = pkgInfo.getRealDir() for path in paths: args.add("--path:\"" & path & "\" ") - var binariesBuilt = 0 for bin in pkgInfo.bin: - # Check if this is the only binary that we want to build. - if binToBuild.isSome() and binToBuild.get() != bin: - let binToBuild = binToBuild.get() - if bin.extractFilename().changeFileExt("") != binToBuild: - continue - let outputOpt = "-o:\"" & pkgInfo.getOutputDir(bin) & "\"" display("Building", "$1/$2 using $3 backend" % [pkginfo.name, bin, pkgInfo.backend], priority = HighPriority) @@ -251,15 +231,10 @@ 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(cmd, showCmd = true) - binariesBuilt.inc() + doCmd("\"" & getNimBin() & "\" $# --noBabelPath $# $# \"$#\"" % + [pkgInfo.backend, join(args, " "), outputOpt, + realDir / bin.changeFileExt("nim")]) except NimbleError: let currentExc = (ref NimbleError)(getCurrentException()) let exc = newException(BuildFailed, "Build failed for package: " & @@ -269,14 +244,13 @@ proc buildFromDir( exc.hint = hint raise exc - if binariesBuilt == 0: - raiseNimbleError( - "No binaries built, did you specify a valid binary name?" - ) - - # Handle post-`build` hook. - cd realDir: # Make sure `execHook` executes the correct .nimble file. - discard execHook(options, actionBuild, false) +proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], forRelease: bool) = + var args: seq[string] + if forRelease: + args = @["-d:release"] + else: + args = @[] + buildFromDir(pkgInfo, paths, args) proc removePkgDir(dir: string, options: Options) = ## Removes files belonging to the package in ``dir``. @@ -355,7 +329,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, # Handle pre-`install` hook. if not options.depsOnly: cd dir: # Make sure `execHook` executes the correct .nimble file. - if not execHook(options, actionInstall, true): + if not execHook(options, true): raise newException(NimbleError, "Pre-hook prevented further execution.") var pkgInfo = getPkgInfo(dir, options) @@ -382,11 +356,7 @@ 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: let paths = result.deps.map(dep => dep.getRealDir()) - let flags = if options.action.typ in {actionInstall, actionPath, actionUninstall, actionDevelop}: - options.action.passNimFlags - else: - @[] - buildFromDir(pkgInfo, paths, "-d:release" & flags, options) + buildFromDir(pkgInfo, paths, true) let pkgDestDir = pkgInfo.getPkgDest(options) if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"): @@ -411,7 +381,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, createDir(pkgDestDir) # Copy this package's files based on the preferences specified in PkgInfo. - var filesInstalled = initHashSet[string]() + var filesInstalled = initSet[string]() iterInstallFiles(realDir, pkgInfo, options, proc (file: string) = createDir(changeRoot(realDir, pkgDestDir, file.splitFile.dir)) @@ -424,7 +394,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, pkgInfo.myPath) filesInstalled.incl copyFileD(pkgInfo.myPath, dest) - var binariesInstalled = initHashSet[string]() + var binariesInstalled = initSet[string]() if pkgInfo.bin.len > 0: # Make sure ~/.nimble/bin directory is created. createDir(binDir) @@ -469,7 +439,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, # executes the hook defined in the CWD, so we set it to where the package # has been installed. cd dest.splitFile.dir: - discard execHook(options, actionInstall, false) + discard execHook(options, false) proc getDownloadInfo*(pv: PkgTuple, options: Options, doPrompt: bool): (DownloadMethod, string, @@ -536,12 +506,12 @@ proc build(options: Options) = nimScriptHint(pkgInfo) let deps = processDeps(pkginfo, options) let paths = deps.map(dep => dep.getRealDir()) - var args = options.getCompilationFlags() - buildFromDir(pkgInfo, paths, args, options) + var args = options.action.compileOptions + buildFromDir(pkgInfo, paths, args) -proc execBackend(pkgInfo: PackageInfo, options: Options) = +proc execBackend(options: Options) = let - bin = options.getCompilationBinary(pkgInfo).get() + bin = options.action.file binDotNim = bin.addFileExt("nim") if bin == "": raise newException(NimbleError, "You need to specify a file.") @@ -554,10 +524,9 @@ proc execBackend(pkgInfo: PackageInfo, options: Options) = nimScriptHint(pkgInfo) let deps = processDeps(pkginfo, options) - let nimblePkgVersion = "-d:NimblePkgVersion=" & pkgInfo.version var args = "" for dep in deps: args.add("--path:\"" & dep.getRealDir() & "\" ") - for option in options.getCompilationFlags(): + for option in options.action.compileOptions: args.add("\"" & option & "\" ") let backend = @@ -572,8 +541,8 @@ proc execBackend(pkgInfo: PackageInfo, options: Options) = else: display("Generating", ("documentation for $1 (from package $2) using $3 " & "backend") % [bin, pkgInfo.name, backend], priority = HighPriority) - doCmd("\"" & getNimBin() & "\" $# --noNimblePath $# $# \"$#\"" % - [backend, nimblePkgVersion, args, bin], showOutput = true) + doCmd("\"" & getNimBin() & "\" $# --noNimblePath $# \"$#\"" % + [backend, args, bin], showOutput = true) display("Success:", "Execution finished", Success, HighPriority) proc search(options: Options) = @@ -589,7 +558,7 @@ proc search(options: Options) = var found = false template onFound {.dirty.} = echoPackage(pkg) - if pkg.alias.len == 0 and options.queryVersions: + if options.queryVersions: echoPackageVersions(pkg) echo(" ") found = true @@ -615,7 +584,7 @@ proc list(options: Options) = let pkgList = getPackageList(options) for pkg in pkgList: echoPackage(pkg) - if pkg.alias.len == 0 and options.queryVersions: + if options.queryVersions: echoPackageVersions(pkg) echo(" ") @@ -729,11 +698,6 @@ 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 != "": @@ -768,20 +732,20 @@ proc init(options: Options) = display("Using", "$# for new package name" % [pkgName.escape()], priority = HighPriority) - # Determine author by running an external command - proc getAuthorWithCmd(cmd: string): string = - let (name, exitCode) = doCmdEx(cmd) - if exitCode == QuitSuccess and name.len > 0: - result = name.strip() - display("Using", "$# for new package author" % [result], - priority = HighPriority) - - # Determine package author via git/hg or asking + # Ask for package author proc getAuthor(): string = if findExe("git") != "": - result = getAuthorWithCmd("git config --global user.name") + let (name, exitCode) = doCmdEx("git config --global user.name") + if exitCode == QuitSuccess and name.len > 0: + result = name.strip() + display("Using", "$# for new package author" % [result.escape()], + priority = HighPriority) elif findExe("hg") != "": - result = getAuthorWithCmd("hg config ui.username") + let (name, exitCode) = doCmdEx("hg config ui.username") + if exitCode == QuitSuccess and name.len > 0: + result = name.strip() + display("Using", "$# for new package author" % [result.escape()], + priority = HighPriority) if result.len == 0: result = promptCustom(options, "Your name?", "Anonymous") let pkgAuthor = getAuthor() @@ -838,14 +802,6 @@ This should ideally be a valid SPDX identifier. See https://spdx.org/licenses/. Please specify a valid SPDX identifier.""", "MIT" ) - var pkgBackend = options.promptList( - """Package Backend? -c - Compile using C backend. -cpp - Compile using C++ backend. -objc - Compile using Objective-C backend. -js - Compile using JavaScript backend.""", - ["c", "cpp", "objc", "js"] - ) # Ask for Nim dependency let nimDepDef = getNimrodVersion() @@ -860,7 +816,6 @@ js - Compile using JavaScript backend.""", pkgAuthor, pkgDesc, pkgLicense, - pkgBackend, pkgSrcDir, pkgNimDep, pkgType @@ -868,17 +823,6 @@ 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) @@ -887,8 +831,7 @@ proc uninstall(options: Options) = raise newException(NimbleError, "Please specify the package(s) to uninstall.") - var pkgsToDelete: HashSet[PackageInfo] - pkgsToDelete.init() + var pkgsToDelete: seq[PackageInfo] = @[] # Do some verification. for pkgTup in options.action.packages: display("Looking", "for $1 ($2)" % [pkgTup.name, $pkgTup.ver], @@ -899,33 +842,37 @@ proc uninstall(options: Options) = raise newException(NimbleError, "Package not found") display("Checking", "reverse dependencies", priority = HighPriority) + var errors: seq[string] = @[] for pkg in pkgList: # Check whether any packages depend on the ones the user is trying to # uninstall. if options.uninstallRevDeps: getAllRevDeps(options, pkg, pkgsToDelete) else: - let - revDeps = getRevDeps(options, pkg) + let revDeps = getRevDeps(options, pkg) var reason = "" - for revDep in revDeps: - if reason.len != 0: reason.add ", " - reason.add("$1 ($2)" % [revDep.name, revDep.version]) - if reason.len != 0: - reason &= " depend" & (if revDeps.len == 1: "s" else: "") & " on it" - - if len(revDeps - pkgsToDelete) > 0: - display("Cannot", "uninstall $1 ($2) because $3" % - [pkgTup.name, pkg.specialVersion, reason], Warning, HighPriority) + if revDeps.len == 1: + reason = "$1 ($2) depends on it" % [revDeps[0].name, $revDeps[0].ver] else: - pkgsToDelete.incl pkg + for i in 0 ..< revDeps.len: + reason.add("$1 ($2)" % [revDeps[i].name, $revDeps[i].ver]) + if i != revDeps.len-1: + reason.add ", " + reason.add " depend on it" - if pkgsToDelete.len == 0: - raise newException(NimbleError, "Failed uninstall - no packages to delete") + if revDeps.len > 0: + errors.add("Cannot uninstall $1 ($2) because $3" % + [pkgTup.name, pkg.specialVersion, reason]) + else: + pkgsToDelete.add pkg + + if pkgsToDelete.len == 0: + raise newException(NimbleError, "\n " & errors.join("\n ")) var pkgNames = "" - for pkg in pkgsToDelete.items: - if pkgNames.len != 0: pkgNames.add ", " + for i in 0 ..< pkgsToDelete.len: + if i != 0: pkgNames.add ", " + let pkg = pkgsToDelete[i] pkgNames.add("$1 ($2)" % [pkg.name, pkg.specialVersion]) # Let's confirm that the user wants these packages removed. @@ -949,14 +896,14 @@ proc uninstall(options: Options) = proc listTasks(options: Options) = let nimbleFile = findNimbleFile(getCurrentDir(), true) - nimscriptwrapper.listTasks(nimbleFile, options) + nimscriptsupport.listTasks(nimbleFile, options) proc developFromDir(dir: string, options: Options) = if options.depsOnly: raiseNimbleError("Cannot develop dependencies only.") cd dir: # Make sure `execHook` executes the correct .nimble file. - if not execHook(options, actionDevelop, true): + if not execHook(options, true): raise newException(NimbleError, "Pre-hook prevented further execution.") var pkgInfo = getPkgInfo(dir, options) @@ -1009,7 +956,7 @@ proc developFromDir(dir: string, options: Options) = # Execute the post-develop hook. cd dir: - discard execHook(options, actionDevelop, false) + discard execHook(options, false) proc develop(options: Options) = if options.action.packages == @[]: @@ -1045,11 +992,7 @@ proc develop(options: Options) = proc test(options: Options) = ## Executes all tests starting with 't' in the ``tests`` directory. ## Subdirectories are not walked. - var pkgInfo = getPkgInfo(getCurrentDir(), options) - - var - files = toSeq(walkDir(getCurrentDir() / "tests")) - tests, failures: int + var files = toSeq(walkDir(getCurrentDir() / "tests")) if files.len < 1: display("Warning:", "No tests found!", Warning, HighPriority) @@ -1061,43 +1004,28 @@ proc test(options: Options) = let (_, name, ext) = file.path.splitFile() if ext == ".nim" and name[0] == 't' and file.kind in {pcFile, pcLinkToFile}: var optsCopy = options.briefClone() - optsCopy.action = Action(typ: actionCompile) + optsCopy.action.typ = actionCompile optsCopy.action.file = file.path - optsCopy.action.backend = pkgInfo.backend - optsCopy.getCompilationFlags() = @[] - optsCopy.getCompilationFlags().add("-r") - optsCopy.getCompilationFlags().add("--path:.") + optsCopy.action.backend = "c" + optsCopy.action.compileOptions = @[] + optsCopy.action.compileOptions.add("-r") + optsCopy.action.compileOptions.add("--path:.") let binFileName = file.path.changeFileExt(ExeExt) existsBefore = existsFile(binFileName) - if options.continueTestsOnFailure: - inc tests - try: - execBackend(pkgInfo, optsCopy) - except NimbleError: - inc failures - else: - execBackend(pkgInfo, optsCopy) + execBackend(optsCopy) let existsAfter = existsFile(binFileName) canRemove = not existsBefore and existsAfter if canRemove: - try: - removeFile(binFileName) - except OSError as exc: - display("Warning:", "Failed to delete " & binFileName & ": " & - exc.msg, Warning, MediumPriority) + removeFile(binFileName) - if failures == 0: - display("Success:", "All tests passed", Success, HighPriority) - else: - let error = "Only " & $(tests - failures) & "/" & $tests & " tests passed" - display("Error:", error, Error, HighPriority) + display("Success:", "All tests passed", Success, HighPriority) proc check(options: Options) = - ## Validates a package in the current working directory. + ## Validates a package a in the current working directory. let nimbleFile = findNimbleFile(getCurrentDir(), true) var error: ValidationError var pkgInfo: PackageInfo @@ -1115,29 +1043,7 @@ proc check(options: Options) = display("Failure:", "Validation failed", Error, HighPriority) quit(QuitFailure) -proc run(options: Options) = - # Verify parameters. - var pkgInfo = getPkgInfo(getCurrentDir(), options) - - let binary = options.getCompilationBinary(pkgInfo).get("") - if binary.len == 0: - raiseNimbleError("Please specify a binary to run") - - if binary notin pkgInfo.bin: - raiseNimbleError( - "Binary '$#' is not defined in '$#' package." % [binary, pkgInfo.name] - ) - - # Build the binary. - build(options) - - let binaryPath = pkgInfo.getOutputDir(binary) - let cmd = quoteShellCommand(binaryPath & options.action.runFlags) - displayDebug("Executing", cmd) - cmd.execCmd.quit - - -proc doAction(options: var Options) = +proc doAction(options: Options) = if options.showHelp: writeHelp() if options.showVersion: @@ -1148,10 +1054,6 @@ proc doAction(options: var Options) = if not existsDir(options.getPkgsDir): createDir(options.getPkgsDir) - if options.action.typ in {actionTasks, actionRun, actionBuild, actionCompile}: - # Implicitly disable package validation for these commands. - options.disableValidation = true - case options.action.typ of actionRefresh: refresh(options) @@ -1177,11 +1079,8 @@ proc doAction(options: var Options) = listPaths(options) of actionBuild: build(options) - of actionRun: - run(options) of actionCompile, actionDoc: - var pkgInfo = getPkgInfo(getCurrentDir(), options) - execBackend(pkgInfo, options) + execBackend(options) of actionInit: init(options) of actionPublish: @@ -1198,33 +1097,30 @@ proc doAction(options: var Options) = of actionNil: assert false of actionCustom: - if not execHook(options, actionCustom, true): + if not execHook(options, true): display("Warning", "Pre-hook prevented further execution.", Warning, HighPriority) return let isPreDefined = options.action.command.normalize == "test" - var execResult: ExecutionResult[bool] + var execResult: ExecutionResult[void] if execCustom(options, execResult, failFast=not isPreDefined): if execResult.hasTaskRequestedCommand(): - var options = execResult.getOptionsForCommand(options) - doAction(options) + doAction(execResult.getOptionsForCommand(options)) else: # If there is no task defined for the `test` task, we run the pre-defined # fallback logic. if isPreDefined: test(options) # Run the post hook for `test` in case it exists. - discard execHook(options, actionCustom, false) + discard execHook(options, false) when isMainModule: var error = "" var hint = "" - var opt: Options try: - opt = parseCmdLine() - opt.doAction() + parseCmdLine().doAction() except NimbleError: let currentExc = (ref NimbleError)(getCurrentException()) (error, hint) = getOutputInfo(currentExc) @@ -1232,9 +1128,7 @@ when isMainModule: discard finally: try: - let folder = getNimbleTempDir() - if opt.shouldRemoveTmp(folder): - removeDir(folder) + removeDir(getNimbleTempDir()) except OSError: let msg = "Couldn't remove Nimble's temp dir" display("Warning:", msg, Warning, MediumPriority) diff --git a/src/nimblepkg/cli.nim b/src/nimblepkg/cli.nim index 3afda35..d8d52d5 100644 --- a/src/nimblepkg/cli.nim +++ b/src/nimblepkg/cli.nim @@ -12,11 +12,11 @@ # - Bright for HighPriority. # - Normal for MediumPriority. -import terminal, sets, strutils -import version +import logging, terminal, sets, strutils, os +import ./common -when not declared(initHashSet): - import common +when defined(windows): + import winlean type CLI* = ref object @@ -49,7 +49,7 @@ const proc newCLI(): CLI = result = CLI( level: HighPriority, - warnings: initHashSet[(string, string)](), + warnings: initSet[(string, string)](), suppressionCount: 0, showColor: true, suppressMessages: false @@ -62,18 +62,8 @@ proc calculateCategoryOffset(category: string): int = assert category.len <= longestCategory return longestCategory - category.len -proc isSuppressed(displayType: DisplayType): bool = - # Don't print any Warning, Message or Success messages when suppression of - # warnings is enabled. That is, unless the user asked for --verbose output. - if globalCLI.suppressMessages and displayType >= Warning and - globalCLI.level == HighPriority: - return true - proc displayCategory(category: string, displayType: DisplayType, priority: Priority) = - if isSuppressed(displayType): - return - # Calculate how much the `category` must be offset to align along a center # line. let offset = calculateCategoryOffset(category) @@ -90,9 +80,6 @@ proc displayCategory(category: string, displayType: DisplayType, proc displayLine(category, line: string, displayType: DisplayType, priority: Priority) = - if isSuppressed(displayType): - return - displayCategory(category, displayType, priority) # Display the message. @@ -100,6 +87,12 @@ proc displayLine(category, line: string, displayType: DisplayType, proc display*(category, msg: string, displayType = Message, priority = MediumPriority) = + # Don't print any Warning, Message or Success messages when suppression of + # warnings is enabled. That is, unless the user asked for --verbose output. + if globalCLI.suppressMessages and displayType >= Warning and + globalCLI.level == HighPriority: + return + # Multiple warnings containing the same messages should not be shown. let warningPair = (category, msg) if displayType == Warning: @@ -147,7 +140,7 @@ proc prompt*(forcePrompts: ForcePrompt, question: string): bool = display("Prompt:", question & " -> [forced no]", Warning, HighPriority) return false of dontForcePrompt: - displayLine("Prompt:", question & " [y/N]", Warning, HighPriority) + display("Prompt:", question & " [y/N]", Warning, HighPriority) displayCategory("Answer:", Warning, HighPriority) let yn = stdin.readLine() case yn.normalize diff --git a/src/nimblepkg/common.nim b/src/nimblepkg/common.nim index ab83175..19faed5 100644 --- a/src/nimblepkg/common.nim +++ b/src/nimblepkg/common.nim @@ -8,6 +8,7 @@ when not defined(nimscript): import sets import version + export version.NimbleError # TODO: Surely there is a better way? type BuildFailed* = object of NimbleError @@ -22,8 +23,7 @@ when not defined(nimscript): preHooks*: HashSet[string] name*: string ## The version specified in the .nimble file.Assuming info is non-minimal, - ## it will always be a non-special version such as '0.1.4'. - ## If in doubt, use `getConcreteVersion` instead. + ## it will always be a non-special version such as '0.1.4' version*: string specialVersion*: string ## Either `myVersion` or a special version such as #head. author*: string @@ -63,16 +63,4 @@ when not defined(nimscript): return (error, hint) const - nimbleVersion* = "0.11.0" - -when not declared(initHashSet): - import sets - - template initHashSet*[A](initialSize = 64): HashSet[A] = - initSet[A](initialSize) - -when not declared(toHashSet): - import sets - - template toHashSet*[A](keys: openArray[A]): HashSet[A] = - toSet(keys) + nimbleVersion* = "0.9.0" diff --git a/src/nimblepkg/config.nim b/src/nimblepkg/config.nim index c4a48fc..5706485 100644 --- a/src/nimblepkg/config.nim +++ b/src/nimblepkg/config.nim @@ -1,8 +1,8 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import parsecfg, streams, strutils, os, tables, uri +import parsecfg, streams, strutils, os, tables, Uri -import version, cli +import tools, version, common, cli type Config* = object diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim index 3bbfa25..f8954e9 100644 --- a/src/nimblepkg/download.nim +++ b/src/nimblepkg/download.nim @@ -2,15 +2,14 @@ # BSD License. Look at license.txt for more info. import parseutils, os, osproc, strutils, tables, pegs, uri + import packageinfo, packageparser, version, tools, common, options, cli -from algorithm import SortOrder, sorted -from sequtils import toSeq, filterIt, map type DownloadMethod* {.pure.} = enum git = "git", hg = "hg" -proc getSpecificDir(meth: DownloadMethod): string {.used.} = +proc getSpecificDir(meth: DownloadMethod): string = case meth of DownloadMethod.git: ".git" @@ -25,12 +24,11 @@ proc doCheckout(meth: DownloadMethod, downloadDir, branch: string) = # clone has happened. Like in the case of git on Windows where it # messes up the damn line endings. doCmd("git checkout --force " & branch) - doCmd("git submodule update --recursive") of DownloadMethod.hg: cd downloadDir: doCmd("hg checkout " & branch) -proc doPull(meth: DownloadMethod, downloadDir: string) {.used.} = +proc doPull(meth: DownloadMethod, downloadDir: string) = case meth of DownloadMethod.git: doCheckout(meth, downloadDir, "") @@ -104,21 +102,14 @@ proc getTagsListRemote*(url: string, meth: DownloadMethod): seq[string] = # http://stackoverflow.com/questions/2039150/show-tags-for-remote-hg-repository raise newException(ValueError, "Hg doesn't support remote tag querying.") -proc getVersionList*(tags: seq[string]): OrderedTable[Version, string] = - ## Return an ordered table of Version -> git tag label. Ordering is - ## in descending order with the most recent version first. - let taggedVers: seq[tuple[ver: Version, tag: string]] = - tags - .filterIt(it != "") - .map(proc(s: string): tuple[ver: Version, tag: string] = - # skip any chars before the version - let i = skipUntil(s, Digits) - # TODO: Better checking, tags can have any - # names. Add warnings and such. - result = (newVersion(s[i .. s.len-1]), s)) - .sorted(proc(a, b: (Version, string)): int = cmp(a[0], b[0]), - SortOrder.Descending) - result = toOrderedTable[Version, string](taggedVers) +proc getVersionList*(tags: seq[string]): Table[Version, string] = + # Returns: TTable of version -> git tag name + result = initTable[Version, string]() + for tag in tags: + if tag != "": + let i = skipUntil(tag, Digits) # skip any chars before the version + # TODO: Better checking, tags can have any names. Add warnings and such. + result[newVersion(tag[i .. tag.len-1])] = tag proc getDownloadMethod*(meth: string): DownloadMethod = case meth @@ -276,8 +267,14 @@ proc echoPackageVersions*(pkg: Package) = try: let versions = getTagsListRemote(pkg.url, downMethod).getVersionList() if versions.len > 0: - let sortedVersions = toSeq(values(versions)) - echo(" versions: " & join(sortedVersions, ", ")) + var vstr = "" + var i = 0 + for v in values(versions): + if i != 0: + vstr.add(", ") + vstr.add(v) + i.inc + echo(" versions: " & vstr) else: echo(" versions: (No versions tagged in the remote repository)") except OSError: @@ -285,32 +282,3 @@ proc echoPackageVersions*(pkg: Package) = of DownloadMethod.hg: echo(" versions: (Remote tag retrieval not supported by " & pkg.downloadMethod & ")") - -when isMainModule: - # Test version sorting - block: - let data = @["v9.0.0-taeyeon", "v9.0.1-jessica", "v9.2.0-sunny", - "v9.4.0-tiffany", "v9.4.2-hyoyeon"] - let expected = toOrderedTable[Version, string]({ - newVersion("9.4.2-hyoyeon"): "v9.4.2-hyoyeon", - newVersion("9.4.0-tiffany"): "v9.4.0-tiffany", - newVersion("9.2.0-sunny"): "v9.2.0-sunny", - newVersion("9.0.1-jessica"): "v9.0.1-jessica", - newVersion("9.0.0-taeyeon"): "v9.0.0-taeyeon" - }) - 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/init.nim b/src/nimblepkg/init.nim index 8e7c33b..42c7f4e 100644 --- a/src/nimblepkg/init.nim +++ b/src/nimblepkg/init.nim @@ -9,18 +9,10 @@ type pkgAuthor: string pkgDesc: string pkgLicense: string - pkgBackend: string pkgSrcDir: string pkgNimDep: string pkgType: string -proc writeExampleIfNonExistent(file: string, content: string) = - if not existsFile(file): - writeFile(file, content) - else: - display("Info:", "File " & file & " already exists, did not write " & - "example code", priority = HighPriority) - proc createPkgStructure*(info: PkgInitInfo, pkgRoot: string) = # Create source directory createDirD(pkgRoot / info.pkgSrcDir) @@ -30,7 +22,7 @@ proc createPkgStructure*(info: PkgInitInfo, pkgRoot: string) = case info.pkgType of "binary": let mainFile = pkgRoot / info.pkgSrcDir / info.pkgName.changeFileExt("nim") - writeExampleIfNonExistent(mainFile, + writeFile(mainFile, """ # This is just an example to get you started. A typical binary package # uses this file as the main entry point of the application. @@ -42,7 +34,7 @@ when isMainModule: nimbleFileOptions.add("bin = @[\"$1\"]\n" % info.pkgName) of "library": let mainFile = pkgRoot / info.pkgSrcDir / info.pkgName.changeFileExt("nim") - writeExampleIfNonExistent(mainFile, + writeFile(mainFile, """ # This is just an example to get you started. A typical library package # exports the main API in this file. Note that you cannot rename this file @@ -57,7 +49,7 @@ proc add*(x, y: int): int = createDirD(pkgRoot / info.pkgSrcDir / info.pkgName) let submodule = pkgRoot / info.pkgSrcDir / info.pkgName / "submodule".addFileExt("nim") - writeExampleIfNonExistent(submodule, + writeFile(submodule, """ # This is just an example to get you started. Users of your library will # import this file by writing ``import $1/submodule``. Feel free to rename or @@ -75,7 +67,7 @@ proc initSubmodule*(): Submodule = ) of "hybrid": let mainFile = pkgRoot / info.pkgSrcDir / info.pkgName.changeFileExt("nim") - writeExampleIfNonExistent(mainFile, + writeFile(mainFile, """ # This is just an example to get you started. A typical hybrid package # uses this file as the main entry point of the application. @@ -90,7 +82,7 @@ when isMainModule: let pkgSubDir = pkgRoot / info.pkgSrcDir / info.pkgName & "pkg" createDirD(pkgSubDir) let submodule = pkgSubDir / "submodule".addFileExt("nim") - writeExampleIfNonExistent(submodule, + writeFile(submodule, """ # This is just an example to get you started. Users of your hybrid library will # import this file by writing ``import $1pkg/submodule``. Feel free to rename or @@ -119,7 +111,7 @@ proc getWelcomeMessage*(): string = "Hello, World!" ) if info.pkgType == "library": - writeExampleIfNonExistent(pkgTestPath / "test1".addFileExt("nim"), + writeFile(pkgTestPath / "test1".addFileExt("nim"), """ # This is just an example to get you started. You may wish to put all of your # tests into a single file, or separate them into multiple `test1`, `test2` @@ -136,7 +128,7 @@ test "can add": """ % info.pkgName ) else: - writeExampleIfNonExistent(pkgTestPath / "test1".addFileExt("nim"), + writeFile(pkgTestPath / "test1".addFileExt("nim"), """ # This is just an example to get you started. You may wish to put all of your # tests into a single file, or separate them into multiple `test1`, `test2` @@ -157,28 +149,23 @@ test "correct welcome": # Write the nimble file let nimbleFile = pkgRoot / info.pkgName.changeFileExt("nimble") - # Only write backend if it isn't "c" - var pkgBackend = "" - if (info.pkgBackend != "c"): - pkgBackend = "backend = " & info.pkgbackend.escape() writeFile(nimbleFile, """# Package version = $# -author = "$#" -description = "$#" +author = $# +description = $# license = $# srcDir = $# $# -$# # Dependencies requires "nim >= $#" """ % [ - info.pkgVersion.escape(), info.pkgAuthor.replace("\"", "\\\""), info.pkgDesc.replace("\"", "\\\""), + info.pkgVersion.escape(), info.pkgAuthor.escape(), info.pkgDesc.escape(), info.pkgLicense.escape(), info.pkgSrcDir.escape(), nimbleFileOptions, - pkgBackend, info.pkgNimDep + info.pkgNimDep ] ) - display("Info:", "Nimble file created successfully", priority=MediumPriority) + display("Info:", "Nimble file created successfully", priority=MediumPriority) \ No newline at end of file diff --git a/src/nimblepkg/nimscriptapi.nim b/src/nimblepkg/nimscriptapi.nim index ec2f46c..0e0ce45 100644 --- a/src/nimblepkg/nimscriptapi.nim +++ b/src/nimblepkg/nimscriptapi.nim @@ -3,12 +3,6 @@ ## This module is implicitly imported in NimScript .nimble files. -import system except getCommand, setCommand, switch, `--` -import strformat, strutils, tables - -when not defined(nimscript): - import os - var packageName* = "" ## Set this to the package name. It ## is usually not required to do that, nims' filename is @@ -17,7 +11,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. @@ -28,141 +22,30 @@ var foreignDeps*: seq[string] = @[] ## The foreign dependencies. Only ## exported for 'distros.nim'. - beforeHooks: seq[string] = @[] - afterHooks: seq[string] = @[] - commandLineParams: seq[string] = @[] - flags: TableRef[string, seq[string]] - - command = "e" - project = "" - success = false - retVal = true - projectFile = "" - outFile = "" - proc requires*(deps: varargs[string]) = ## Call this to set the list of requirements of your Nimble ## package. for d in deps: requiresData.add(d) -proc getParams() = - # Called by nimscriptwrapper.nim:execNimscript() - # nim e --flags /full/path/to/file.nims /full/path/to/file.out action - for i in 2 .. paramCount(): - let - param = paramStr(i) - if param[0] != '-': - if projectFile.len == 0: - projectFile = param - elif outFile.len == 0: - outFile = param - else: - commandLineParams.add param.normalize - -proc getCommand*(): string = - return command - -proc setCommand*(cmd: string, prj = "") = - command = cmd - if prj.len != 0: - project = prj - -proc switch*(key: string, value="") = - if flags.isNil: - flags = newTable[string, seq[string]]() - - if flags.hasKey(key): - flags[key].add(value) - else: - flags[key] = @[value] - -template `--`*(key, val: untyped) = - switch(astToStr(key), strip astToStr(val)) - -template `--`*(key: untyped) = - switch(astToStr(key), "") - -template printIfLen(varName) = - if varName.len != 0: - result &= astToStr(varName) & ": \"\"\"" & varName & "\"\"\"\n" - -template printSeqIfLen(varName) = - if varName.len != 0: - result &= astToStr(varName) & ": \"" & varName.join(", ") & "\"\n" - -proc printPkgInfo(): string = - if backend.len == 0: - backend = "c" - - result = "[Package]\n" - if packageName.len != 0: - result &= "name: \"" & packageName & "\"\n" - printIfLen version - printIfLen author - printIfLen description - printIfLen license - printIfLen srcDir - printIfLen binDir - printIfLen backend - - printSeqIfLen skipDirs - printSeqIfLen skipFiles - printSeqIfLen skipExt - printSeqIfLen installDirs - printSeqIfLen installFiles - printSeqIfLen installExt - printSeqIfLen bin - printSeqIfLen beforeHooks - printSeqIfLen afterHooks - - if requiresData.len != 0: - result &= "\n[Deps]\n" - result &= &"requires: \"{requiresData.join(\", \")}\"\n" - -proc onExit*() = - if "printPkgInfo".normalize in commandLineParams: - if outFile.len != 0: - writeFile(outFile, printPkgInfo()) - else: - var - output = "" - output &= "\"success\": " & $success & ", " - output &= "\"command\": \"" & command & "\", " - if project.len != 0: - output &= "\"project\": \"" & project & "\", " - if not flags.isNil and flags.len != 0: - output &= "\"flags\": {" - for key, val in flags.pairs: - output &= "\"" & key & "\": [" - for v in val: - let v = if v.len > 0 and v[0] == '"': strutils.unescape(v) - else: v - output &= v.escape & ", " - output = output[0 .. ^3] & "], " - output = output[0 .. ^3] & "}, " - - output &= "\"retVal\": " & $retVal - - if outFile.len != 0: - writeFile(outFile, "{" & output & "}") - # TODO: New release of Nim will move this `task` template under a # `when not defined(nimble)`. This will allow us to override it in the future. -template task*(name: untyped; description: string; body: untyped): untyped = - ## Defines a task. Hidden tasks are supported via an empty description. - ## Example: - ## - ## .. code-block:: nim - ## task build, "default build is via the C backend": - ## setCommand "c" - proc `name Task`*() = body +when not declared(task): + template task*(name: untyped; description: string; body: untyped): untyped = + ## Defines a task. Hidden tasks are supported via an empty description. + ## Example: + ## + ## .. code-block:: nim + ## task build, "default build is via the C backend": + ## setCommand "c" + proc `name Task`*() = body - if commandLineParams.len == 0 or "help" in commandLineParams: - success = true - echo(astToStr(name), " ", description) - elif astToStr(name).normalize in commandLineParams: - success = true - `name Task`() + let cmd = getCommand() + if cmd.len == 0 or cmd == "help": + setCommand "help" + echo(astToStr(name), " ", description) + elif cmd == astToStr(name): + setCommand "nop" + `name Task`() template before*(action: untyped, body: untyped): untyped = ## Defines a block of code which is evaluated before ``action`` is executed. @@ -170,27 +53,15 @@ template before*(action: untyped, body: untyped): untyped = result = true body - beforeHooks.add astToStr(action) - - if (astToStr(action) & "Before").normalize in commandLineParams: - success = true - retVal = `action Before`() - template after*(action: untyped, body: untyped): untyped = ## Defines a block of code which is evaluated after ``action`` is executed. proc `action After`*(): bool = result = true body - afterHooks.add astToStr(action) - - if (astToStr(action) & "After").normalize in commandLineParams: - success = true - retVal = `action After`() +template builtin = discard proc getPkgDir*(): string = ## Returns the package directory containing the .nimble file currently ## being evaluated. - result = projectFile.rsplit(seps={'/', '\\', ':'}, maxsplit=1)[0] - -getParams() + builtin diff --git a/src/nimblepkg/nimscriptexecutor.nim b/src/nimblepkg/nimscriptexecutor.nim index 122c41c..70fc165 100644 --- a/src/nimblepkg/nimscriptexecutor.nim +++ b/src/nimblepkg/nimscriptexecutor.nim @@ -1,17 +1,16 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import os, strutils, sets +import os, tables, strutils, sets -import packageparser, common, packageinfo, options, nimscriptwrapper, cli, - version +import packageparser, common, packageinfo, options, nimscriptsupport, cli -proc execHook*(options: Options, hookAction: ActionType, before: bool): bool = +proc execHook*(options: Options, before: bool): bool = ## Returns whether to continue. result = true # For certain commands hooks should not be evaluated. - if hookAction in noHookActions: + if options.action.typ in noHookActions: return var nimbleFile = "" @@ -21,8 +20,8 @@ proc execHook*(options: Options, hookAction: ActionType, before: bool): bool = # PackageInfos are cached so we can read them as many times as we want. let pkgInfo = getPkgInfoFromFile(nimbleFile, options) let actionName = - if hookAction == actionCustom: options.action.command - else: ($hookAction)[6 .. ^1] + if options.action.typ == actionCustom: options.action.command + else: ($options.action.typ)[6 .. ^1] let hookExists = if before: actionName.normalize in pkgInfo.preHooks else: actionName.normalize in pkgInfo.postHooks @@ -32,7 +31,7 @@ proc execHook*(options: Options, hookAction: ActionType, before: bool): bool = result = res.retVal proc execCustom*(options: Options, - execResult: var ExecutionResult[bool], + execResult: var ExecutionResult[void], failFast = true): bool = ## Executes the custom command using the nimscript backend. ## @@ -58,7 +57,7 @@ proc execCustom*(options: Options, HighPriority) return - if not execHook(options, actionCustom, false): + if not execHook(options, false): return return true diff --git a/src/nimblepkg/nimscriptsupport.nim b/src/nimblepkg/nimscriptsupport.nim new file mode 100644 index 0000000..997f198 --- /dev/null +++ b/src/nimblepkg/nimscriptsupport.nim @@ -0,0 +1,731 @@ +# Copyright (C) Andreas Rumpf. All rights reserved. +# BSD License. Look at license.txt for more info. + +## Implements the new configuration system for Nimble. Uses Nim as a +## scripting language. + +import + compiler/ast, compiler/modules, compiler/passes, compiler/passaux, + compiler/condsyms, compiler/sem, compiler/semdata, + compiler/llstream, compiler/vm, compiler/vmdef, compiler/commands, + compiler/msgs, compiler/magicsys, compiler/idents, + compiler/nimconf, compiler/nversion + +from compiler/scriptconfig import setupVM +from compiler/astalgo import strTableGet +import compiler/options as compiler_options + +import common, version, options, packageinfo, cli +import os, strutils, strtabs, tables, times, osproc, sets, pegs + +when not declared(resetAllModulesHard): + import compiler/modulegraphs + +type + Flags = TableRef[string, seq[string]] + ExecutionResult*[T] = object + success*: bool + command*: string + arguments*: seq[string] + flags*: Flags + retVal*: T + +const + internalCmd = "NimbleInternal" + nimscriptApi = staticRead("nimscriptapi.nim") + +proc raiseVariableError(ident, typ: string) {.noinline.} = + raise newException(NimbleError, + "NimScript's variable '" & ident & "' needs a value of type '" & typ & "'.") + +proc isStrLit(n: PNode): bool = n.kind in {nkStrLit..nkTripleStrLit} + +when declared(NimCompilerApiVersion): + const finalApi = NimCompilerApiVersion >= 2 + + when NimCompilerApiVersion >= 3: + import compiler / pathutils +else: + const finalApi = false + +proc getGlobal(g: ModuleGraph; ident: PSym): string = + when finalApi: + let n = vm.getGlobalValue(PCtx g.vm, ident) + else: + let n = vm.globalCtx.getGlobalValue(ident) + if n.isStrLit: + result = n.strVal + else: + raiseVariableError(ident.name.s, "string") + +proc getGlobalAsSeq(g: ModuleGraph; ident: PSym): seq[string] = + when finalApi: + let n = vm.getGlobalValue(PCtx g.vm, ident) + else: + let n = vm.globalCtx.getGlobalValue(ident) + result = @[] + if n.kind == nkBracket: + for x in n: + if x.isStrLit: + result.add x.strVal + else: + raiseVariableError(ident.name.s, "seq[string]") + else: + raiseVariableError(ident.name.s, "seq[string]") + +proc extractRequires(g: ModuleGraph; ident: PSym, result: var seq[PkgTuple]) = + when finalApi: + let n = vm.getGlobalValue(PCtx g.vm, ident) + else: + let n = vm.globalCtx.getGlobalValue(ident) + if n.kind == nkBracket: + for x in n: + if x.kind == nkPar and x.len == 2 and x[0].isStrLit and x[1].isStrLit: + result.add(parseRequires(x[0].strVal & x[1].strVal)) + elif x.isStrLit: + result.add(parseRequires(x.strVal)) + else: + raiseVariableError("requiresData", "seq[(string, VersionReq)]") + else: + raiseVariableError("requiresData", "seq[(string, VersionReq)]") + +when declared(newIdentCache): + var identCache = newIdentCache() + +proc setupVM(graph: ModuleGraph; module: PSym; scriptName: string, flags: Flags): PEvalContext = + ## This procedure is exported in the compiler sources, but its implementation + ## is too Nim-specific to be used by Nimble. + ## Specifically, the implementation of ``switch`` is problematic. Sooo + ## I simply copied it here and edited it :) + when declared(NimCompilerApiVersion): + result = newCtx(module, identCache, graph) + elif declared(newIdentCache): + result = newCtx(module, identCache) + else: + result = newCtx(module) + result.mode = emRepl + registerAdditionalOps(result) + + # captured vars: + let conf = graph.config + var errorMsg: string + var vthisDir = scriptName.splitFile.dir + + proc listDirs(a: VmArgs, filter: set[PathComponent]) = + let dir = getString(a, 0) + var res: seq[string] = @[] + for kind, path in walkDir(dir): + if kind in filter: res.add path + setResult(a, res) + + template cbconf(name, body) {.dirty.} = + result.registerCallback "stdlib.system." & astToStr(name), + proc (a: VmArgs) = + body + + template cbos(name, body) {.dirty.} = + result.registerCallback "stdlib.system." & astToStr(name), + proc (a: VmArgs) = + try: + body + except OSError: + errorMsg = getCurrentExceptionMsg() + + # Idea: Treat link to file as a file, but ignore link to directory to prevent + # endless recursions out of the box. + cbos listFiles: + listDirs(a, {pcFile, pcLinkToFile}) + cbos listDirs: + listDirs(a, {pcDir}) + cbos removeDir: + os.removeDir getString(a, 0) + cbos removeFile: + os.removeFile getString(a, 0) + cbos createDir: + os.createDir getString(a, 0) + cbos getOsError: + setResult(a, errorMsg) + cbos setCurrentDir: + os.setCurrentDir getString(a, 0) + cbos getCurrentDir: + setResult(a, os.getCurrentDir()) + cbos moveFile: + os.moveFile(getString(a, 0), getString(a, 1)) + cbos copyFile: + os.copyFile(getString(a, 0), getString(a, 1)) + cbos getLastModificationTime: + setResult(a, toUnix(getLastModificationTime(getString(a, 0)))) + cbos findExe: + setResult(a, os.findExe(getString(a, 0))) + + cbos rawExec: + setResult(a, osproc.execCmd getString(a, 0)) + + cbconf getEnv: + setResult(a, os.getEnv(a.getString 0)) + cbconf existsEnv: + setResult(a, os.existsEnv(a.getString 0)) + cbconf dirExists: + setResult(a, os.dirExists(a.getString 0)) + cbconf fileExists: + setResult(a, os.fileExists(a.getString 0)) + + cbconf thisDir: + setResult(a, vthisDir) + cbconf put: + when declared(NimCompilerApiVersion): + compiler_options.setConfigVar(conf, getString(a, 0), getString(a, 1)) + else: + compiler_options.setConfigVar(getString(a, 0), getString(a, 1)) + cbconf get: + when declared(NimCompilerApiVersion): + setResult(a, compiler_options.getConfigVar(conf, a.getString 0)) + else: + setResult(a, compiler_options.getConfigVar(a.getString 0)) + cbconf exists: + when declared(NimCompilerApiVersion): + setResult(a, compiler_options.existsConfigVar(conf, a.getString 0)) + else: + setResult(a, compiler_options.existsConfigVar(a.getString 0)) + cbconf nimcacheDir: + when declared(NimCompilerApiVersion): + setResult(a, compiler_options.getNimcacheDir(conf)) + else: + setResult(a, compiler_options.getNimcacheDir()) + cbconf paramStr: + setResult(a, os.paramStr(int a.getInt 0)) + cbconf paramCount: + setResult(a, os.paramCount()) + cbconf cmpIgnoreStyle: + setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1)) + cbconf cmpIgnoreCase: + setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) + cbconf setCommand: + when declared(NimCompilerApiVersion): + conf.command = a.getString 0 + let arg = a.getString 1 + if arg.len > 0: + conf.projectName = arg + when NimCompilerApiVersion >= 3: + try: + conf.projectFull = canonicalizePath(conf, + conf.projectPath / RelativeFile(conf.projectName)) + except OSError: + conf.projectFull = AbsoluteFile conf.projectName + else: + try: + conf.projectFull = canonicalizePath(conf, conf.projectPath / conf.projectName) + except OSError: + conf.projectFull = conf.projectName + else: + compiler_options.command = a.getString 0 + let arg = a.getString 1 + if arg.len > 0: + gProjectName = arg + try: + gProjectFull = canonicalizePath(gProjectPath / gProjectName) + except OSError: + gProjectFull = gProjectName + cbconf getCommand: + when declared(NimCompilerApiVersion): + setResult(a, conf.command) + else: + setResult(a, compiler_options.command) + cbconf switch: + if not flags.isNil: + let + key = a.getString 0 + value = a.getString 1 + if flags.hasKey(key): + flags[key].add(value) + else: + flags[key] = @[value] + +proc isValidLibPath(lib: string): bool = + return fileExists(lib / "system.nim") + +proc getNimPrefixDir(options: Options): string = + let env = getEnv("NIM_LIB_PREFIX") + if env != "": + let msg = "Using env var NIM_LIB_PREFIX: " & env + display("Warning:", msg, Warning, HighPriority) + return env + + if options.config.nimLibPrefix != "": + result = options.config.nimLibPrefix + let msg = "Using Nim stdlib prefix from Nimble config file: " & result + display("Warning:", msg, Warning, HighPriority) + return + + result = splitPath(findExe("nim")).head.parentDir + # The above heuristic doesn't work for 'choosenim' proxies. Thankfully in + # that case the `nimble` binary is beside the `nim` binary so things should + # just work. + if not dirExists(result / "lib"): + # By specifying an empty string we instruct the Nim compiler to use + # getAppDir().head as the prefix dir. See compiler/options module for + # the code responsible for this. + result = "" + +proc getLibVersion(lib: string): Version = + ## This is quite a hacky procedure, but there is no other way to extract + ## this out of the ``system`` module. We could evaluate it, but that would + ## cause an error if the stdlib is out of date. The purpose of this + ## proc is to give a nice error message to the user instead of a confusing + ## Nim compile error. + let systemPath = lib / "system.nim" + if not fileExists(systemPath): + raiseNimbleError("system module not found in stdlib path: " & lib) + + let systemFile = readFile(systemPath) + let majorPeg = peg"'NimMajor' @ '=' \s* {\d*}" + let minorPeg = peg"'NimMinor' @ '=' \s* {\d*}" + let patchPeg = peg"'NimPatch' @ '=' \s* {\d*}" + + var majorMatches: array[1, string] + let major = find(systemFile, majorPeg, majorMatches) + var minorMatches: array[1, string] + let minor = find(systemFile, minorPeg, minorMatches) + var patchMatches: array[1, string] + let patch = find(systemFile, patchPeg, patchMatches) + + if major != -1 and minor != -1 and patch != -1: + return newVersion(majorMatches[0] & "." & minorMatches[0] & "." & patchMatches[0]) + else: + return system.NimVersion.newVersion() + +when finalApi: + var graph = newModuleGraph(identCache, newConfigRef()) + +elif declared(ModuleGraph): + var graph = newModuleGraph() + +proc execScript(scriptName: string, flags: Flags, options: Options): PSym = + ## Executes the specified script. Returns the script's module symbol. + ## + ## No clean up is performed and must be done manually! + when finalApi: + graph = newModuleGraph(graph.cache, graph.config) + else: + graph = newModuleGraph(graph.config) + + let conf = graph.config + conf.searchPaths = @[] + when declared(NimCompilerApiVersion): + if "nimblepkg/nimscriptapi" notin conf.implicitImports: + conf.implicitImports.add("nimblepkg/nimscriptapi") + elif declared(resetAllModulesHard): + # for compatibility with older Nim versions: + if "nimblepkg/nimscriptapi" notin compiler_options.implicitIncludes: + compiler_options.implicitIncludes.add("nimblepkg/nimscriptapi") + else: + if "nimblepkg/nimscriptapi" notin compiler_options.implicitImports: + compiler_options.implicitImports.add("nimblepkg/nimscriptapi") + + # Ensure the compiler can find its standard library #220. + when declared(NimCompilerApiVersion): + when NimCompilerApiVersion >= 3: + conf.prefixDir = AbsoluteDir getNimPrefixDir(options) + display("Setting", "Nim stdlib prefix to " & conf.prefixDir.string, + priority=LowPriority) + + template myLibPath(): untyped = conf.libpath.string + + else: + conf.prefixDir = getNimPrefixDir(options) + display("Setting", "Nim stdlib prefix to " & conf.prefixDir, + priority=LowPriority) + + template myLibPath(): untyped = conf.libpath + + # Verify that lib path points to existing stdlib. + setDefaultLibpath(conf) + else: + compiler_options.gPrefixDir = getNimPrefixDir(options) + display("Setting", "Nim stdlib prefix to " & compiler_options.gPrefixDir, + priority=LowPriority) + + template myLibPath(): untyped = compiler_options.libpath + + # Verify that lib path points to existing stdlib. + compiler_options.setDefaultLibpath() + + display("Setting", "Nim stdlib path to " & myLibPath(), + priority=LowPriority) + if not isValidLibPath(myLibPath()): + let msg = "Nimble cannot find Nim's standard library.\nLast try in:\n - $1" % + myLibPath() + let hint = "Nimble does its best to find Nim's standard library, " & + "sometimes this fails. You can set the environment variable " & + "NIM_LIB_PREFIX to where Nim's `lib` directory is located as " & + "a workaround. " & + "See https://github.com/nim-lang/nimble#troubleshooting for " & + "more info." + raiseNimbleError(msg, hint) + + # Verify that the stdlib that was found isn't older than the stdlib that Nimble + # was compiled with. + let libVersion = getLibVersion(myLibPath()) + if NimVersion.newVersion() > libVersion: + let msg = ("Nimble cannot use an older stdlib than the one it was compiled " & + "with.\n Stdlib in '$#' has version: $#.\n Nimble needs at least: $#.") % + [myLibPath(), $libVersion, NimVersion] + let hint = "You may be running a newer version of Nimble than you intended " & + "to. Run an older version of Nimble that is compatible with " & + "the stdlib that Nimble is attempting to use or set the environment variable " & + "NIM_LIB_PREFIX to where a different stdlib's `lib` directory is located as " & + "a workaround." & + "See https://github.com/nim-lang/nimble#troubleshooting for " & + "more info." + raiseNimbleError(msg, hint) + + let pkgName = scriptName.splitFile.name + + # Ensure that "nimblepkg/nimscriptapi" is in the PATH. + var issue620 = false + block: + echo(scriptName.splitFile.dir / "nimblepkg" / "nimscriptapi.nim") + if existsFile(scriptName.splitFile.dir / "nimblepkg" / "nimscriptapi.nim"): + # TODO: hacky workaround for #620. + issue620 = true + conf.disableNimblePath() + break + + let t = getTempDir() / "nimblecache" + let tmpNimscriptApiPath = t / "nimblepkg" / "nimscriptapi.nim" + createDir(tmpNimscriptApiPath.splitFile.dir) + writeFile(tmpNimscriptApiPath, nimscriptApi) + echo("before ", conf.searchPaths) + when declared(NimCompilerApiVersion): + when NimCompilerApiVersion >= 3: + conf.searchPaths.add(AbsoluteDir t) + else: + conf.searchPaths.add(t) + else: + searchPaths.add(t) + echo("after ", conf.searchPaths) + + when declared(NimCompilerApiVersion): + initDefines(conf.symbols) + when NimCompilerApiVersion >= 2: + loadConfigs(DefaultConfig, graph.cache, conf) + else: + loadConfigs(DefaultConfig, conf) + passes.gIncludeFile = includeModule + passes.gImportModule = importModule + + defineSymbol(conf.symbols, "nimscript") + defineSymbol(conf.symbols, "nimconfig") + defineSymbol(conf.symbols, "nimble") + when NimCompilerApiVersion >= 2: + registerPass(graph, semPass) + registerPass(graph, evalPass) + else: + registerPass(semPass) + registerPass(evalPass) + + conf.searchPaths.add(conf.libpath) + else: + initDefines() + loadConfigs(DefaultConfig) + passes.gIncludeFile = includeModule + passes.gImportModule = importModule + + defineSymbol("nimscript") + defineSymbol("nimconfig") + defineSymbol("nimble") + registerPass(semPass) + registerPass(evalPass) + + searchPaths.add(compiler_options.libpath) + + echo("after2 ", conf.searchPaths) + + when declared(resetAllModulesHard): + result = makeModule(scriptName) + else: + result = graph.makeModule(scriptName) + + incl(result.flags, sfMainModule) + when finalApi: + graph.vm = setupVM(graph, result, scriptName, flags) + + # Setup builtins defined in nimscriptapi.nim + template cbApi(name, body) {.dirty.} = + PCtx(graph.vm).registerCallback "nimscriptapi." & astToStr(name), + proc (a: VmArgs) = + body + + else: + vm.globalCtx = setupVM(graph, result, scriptName, flags) + + # Setup builtins defined in nimscriptapi.nim + template cbApi(name, body) {.dirty.} = + vm.globalCtx.registerCallback "nimscriptapi." & astToStr(name), + proc (a: VmArgs) = + body + + cbApi getPkgDir: + setResult(a, scriptName.splitFile.dir) + + when finalApi: + graph.compileSystemModule() + when NimCompilerApiVersion >= 3: + graph.processModule(result, llStreamOpen(AbsoluteFile scriptName, fmRead)) + else: + graph.processModule(result, llStreamOpen(scriptName, fmRead)) + elif declared(newIdentCache): + graph.compileSystemModule(identCache) + graph.processModule(result, llStreamOpen(scriptName, fmRead), nil, identCache) + else: + compileSystemModule() + processModule(result, llStreamOpen(scriptName, fmRead), nil) + +proc cleanup() = + # ensure everything can be called again: + when declared(NimCompilerApiVersion): + let conf = graph.config + conf.projectName = "" + conf.command = "" + else: + compiler_options.gProjectName = "" + compiler_options.command = "" + when declared(NimCompilerApiVersion): + resetSystemArtifacts(graph) + elif declared(resetAllModulesHard): + resetAllModulesHard() + else: + resetSystemArtifacts() + when finalApi: + clearPasses(graph) + else: + clearPasses() + when declared(NimCompilerApiVersion): + conf.errorMax = 1 + when NimCompilerApiVersion >= 2: + conf.writeLnHook = nil + graph.vm = nil + else: + msgs.writeLnHook = nil + vm.globalCtx = nil + initDefines(conf.symbols) + else: + msgs.gErrorMax = 1 + msgs.writeLnHook = nil + vm.globalCtx = nil + initDefines() + +proc readPackageInfoFromNims*(scriptName: string, options: Options, + result: var PackageInfo) = + ## Executes the `scriptName` nimscript file. Reads the package information + ## that it populates. + + # Setup custom error handling. + when declared(NimCompilerApiVersion): + let conf = graph.config + conf.errorMax = high(int) + else: + msgs.gErrorMax = high(int) + + template errCounter(): int = + when declared(NimCompilerApiVersion): conf.errorCounter + else: msgs.gErrorCounter + + var previousMsg = "" + + proc writelnHook(output: string) = + # The error counter is incremented after the writeLnHook is invoked. + if errCounter() > 0: + raise newException(NimbleError, previousMsg) + elif previousMsg.len > 0: + display("Info", previousMsg, priority = MediumPriority) + if output.normalize.startsWith("error"): + raise newException(NimbleError, output) + previousMsg = output + + when finalApi: + conf.writelnHook = writelnHook + else: + msgs.writeLnHook = writelnHook + + when declared(NimCompilerApiVersion): + conf.command = internalCmd + else: + compiler_options.command = internalCmd + + # Execute the nimscript file. + let thisModule = execScript(scriptName, nil, options) + + when declared(resetAllModulesHard): + let apiModule = thisModule + else: + var apiModule: PSym + for i in 0.. 0: + raise newException(NimbleError, previousMsg) + + # Extract all the necessary fields populated by the nimscript file. + proc getSym(apiModule: PSym, ident: string): PSym = + result = apiModule.tab.strTableGet(getIdent(identCache, ident)) + if result.isNil: + raise newException(NimbleError, "Ident not found: " & ident) + + template trivialField(field) = + result.field = getGlobal(graph, getSym(apiModule, astToStr field)) + + template trivialFieldSeq(field) = + result.field.add getGlobalAsSeq(graph, getSym(apiModule, astToStr field)) + + # keep reasonable default: + let name = getGlobal(graph, apiModule.tab.strTableGet(getIdent(identCache, "packageName"))) + if name.len > 0: result.name = name + + trivialField version + trivialField author + trivialField description + trivialField license + trivialField srcdir + trivialField bindir + trivialFieldSeq skipDirs + trivialFieldSeq skipFiles + trivialFieldSeq skipExt + trivialFieldSeq installDirs + trivialFieldSeq installFiles + trivialFieldSeq installExt + trivialFieldSeq foreignDeps + + extractRequires(graph, getSym(apiModule, "requiresData"), result.requires) + + let binSeq = getGlobalAsSeq(graph, getSym(apiModule, "bin")) + for i in binSeq: + result.bin.add(i.addFileExt(ExeExt)) + + let backend = getGlobal(graph, getSym(apiModule, "backend")) + if backend.len == 0: + result.backend = "c" + elif cmpIgnoreStyle(backend, "javascript") == 0: + result.backend = "js" + else: + result.backend = backend.toLowerAscii() + + # Grab all the global procs + for i in thisModule.tab.data: + if not i.isNil(): + let name = i.name.s.normalize() + if name.endsWith("before"): + result.preHooks.incl(name[0 .. ^7]) + if name.endsWith("after"): + result.postHooks.incl(name[0 .. ^6]) + + cleanup() + +when declared(NimCompilerApiVersion): + template nimCommand(): untyped = conf.command + template nimProjectName(): untyped = conf.projectName +else: + template nimCommand(): untyped = compiler_options.command + template nimProjectName(): untyped = compiler_options.gProjectName + +proc execTask*(scriptName, taskName: string, + options: Options): ExecutionResult[void] = + ## Executes the specified task in the specified script. + ## + ## `scriptName` should be a filename pointing to the nimscript file. + result.success = true + result.flags = newTable[string, seq[string]]() + when declared(NimCompilerApiVersion): + let conf = graph.config + nimCommand() = internalCmd + display("Executing", "task $# in $#" % [taskName, scriptName], + priority = HighPriority) + + let thisModule = execScript(scriptName, result.flags, options) + let prc = thisModule.tab.strTableGet(getIdent(identCache, taskName & "Task")) + if prc.isNil: + # Procedure not defined in the NimScript module. + result.success = false + cleanup() + return + when finalApi: + discard vm.execProc(PCtx(graph.vm), prc, []) + else: + discard vm.globalCtx.execProc(prc, []) + + # Read the command, arguments and flags set by the executed task. + result.command = nimCommand() + result.arguments = @[] + for arg in nimProjectName().split(): + result.arguments.add(arg) + + cleanup() + +proc execHook*(scriptName, actionName: string, before: bool, + options: Options): ExecutionResult[bool] = + ## Executes the specified action's hook. Depending on ``before``, either + ## the "before" or the "after" hook. + ## + ## `scriptName` should be a filename pointing to the nimscript file. + when declared(NimCompilerApiVersion): + let conf = graph.config + result.success = true + result.flags = newTable[string, seq[string]]() + nimCommand() = internalCmd + let hookName = + if before: actionName.toLowerAscii & "Before" + else: actionName.toLowerAscii & "After" + display("Attempting", "to execute hook $# in $#" % [hookName, scriptName], + priority = MediumPriority) + + let thisModule = execScript(scriptName, result.flags, options) + # Explicitly execute the task procedure, instead of relying on hack. + let prc = thisModule.tab.strTableGet(getIdent(identCache, hookName)) + if prc.isNil: + # Procedure not defined in the NimScript module. + result.success = false + cleanup() + return + when finalApi: + let returnVal = vm.execProc(PCtx(graph.vm), prc, []) + else: + let returnVal = vm.globalCtx.execProc(prc, []) + case returnVal.kind + of nkCharLit..nkUInt64Lit: + result.retVal = returnVal.intVal == 1 + else: assert false + + # Read the command, arguments and flags set by the executed task. + result.command = nimCommand() + result.arguments = @[] + for arg in nimProjectName().split(): + result.arguments.add(arg) + + cleanup() + +proc getNimScriptCommand(): string = + when declared(NimCompilerApiVersion): + let conf = graph.config + nimCommand() + +proc setNimScriptCommand(command: string) = + when declared(NimCompilerApiVersion): + let conf = graph.config + nimCommand() = command + +proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool = + ## Determines whether the last executed task used ``setCommand`` + return execResult.command != internalCmd + +proc listTasks*(scriptName: string, options: Options) = + setNimScriptCommand("help") + + discard execScript(scriptName, nil, options) + # TODO (#402): Make the 'task' template generate explicit data structure + # containing all the task names + descriptions. + cleanup() diff --git a/src/nimblepkg/nimscriptwrapper.nim b/src/nimblepkg/nimscriptwrapper.nim deleted file mode 100644 index 4cfe6ec..0000000 --- a/src/nimblepkg/nimscriptwrapper.nim +++ /dev/null @@ -1,216 +0,0 @@ -# Copyright (C) Andreas Rumpf. All rights reserved. -# BSD License. Look at license.txt for more info. - -## Implements the new configuration system for Nimble. Uses Nim as a -## scripting language. - -import hashes, json, os, strutils, tables, times, osproc, strtabs - -import version, options, cli, tools - -type - Flags = TableRef[string, seq[string]] - ExecutionResult*[T] = object - success*: bool - command*: string - arguments*: seq[string] - flags*: Flags - retVal*: T - stdout*: string - -const - internalCmd = "e" - nimscriptApi = staticRead("nimscriptapi.nim") - printPkgInfo = "printPkgInfo" - -proc isCustomTask(actionName: string, options: Options): bool = - options.action.typ == actionCustom and actionName != printPkgInfo - -proc needsLiveOutput(actionName: string, options: Options, isHook: bool): bool = - let isCustomTask = isCustomTask(actionName, options) - return isCustomTask or isHook or actionName == "" - -proc writeExecutionOutput(data: string) = - # TODO: in the future we will likely want this to be live, users will - # undoubtedly be doing loops and other crazy things in their top-level - # Nimble files. - display("Info", data) - -proc execNimscript( - nimsFile, projectDir, actionName: string, options: Options, isHook: bool -): tuple[output: string, exitCode: int, stdout: string] = - let - nimsFileCopied = projectDir / nimsFile.splitFile().name & "_" & getProcessId() & ".nims" - outFile = getNimbleTempDir() & ".out" - - let - isScriptResultCopied = - nimsFileCopied.fileExists() and - nimsFileCopied.getLastModificationTime() >= nimsFile.getLastModificationTime() - - if not isScriptResultCopied: - nimsFile.copyFile(nimsFileCopied) - - defer: - # Only if copied in this invocation, allows recursive calls of nimble - if not isScriptResultCopied and options.shouldRemoveTmp(nimsFileCopied): - nimsFileCopied.removeFile() - - var cmd = ( - "nim e $# -p:$# $# $# $#" % [ - "--hints:off --verbosity:0", - (getTempDir() / "nimblecache").quoteShell, - nimsFileCopied.quoteShell, - outFile.quoteShell, - actionName - ] - ).strip() - - let isCustomTask = isCustomTask(actionName, options) - if isCustomTask: - for i in options.action.arguments: - cmd &= " " & i.quoteShell() - for key, val in options.action.flags.pairs(): - cmd &= " $#$#" % [if key.len == 1: "-" else: "--", key] - if val.len != 0: - cmd &= ":" & val.quoteShell() - - displayDebug("Executing " & cmd) - - if needsLiveOutput(actionName, options, isHook): - result.exitCode = execCmd(cmd) - else: - # We want to capture any possible errors when parsing a .nimble - # file's metadata. See #710. - (result.stdout, result.exitCode) = execCmdEx(cmd) - if outFile.fileExists(): - result.output = outFile.readFile() - if options.shouldRemoveTmp(outFile): - discard outFile.tryRemoveFile() - -proc getNimsFile(scriptName: string, options: Options): string = - let - cacheDir = getTempDir() / "nimblecache" - shash = $scriptName.parentDir().hash().abs() - prjCacheDir = cacheDir / scriptName.splitFile().name & "_" & shash - nimscriptApiFile = cacheDir / "nimscriptapi.nim" - - result = prjCacheDir / scriptName.extractFilename().changeFileExt ".nims" - - let - iniFile = result.changeFileExt(".ini") - - isNimscriptApiCached = - nimscriptApiFile.fileExists() and nimscriptApiFile.getLastModificationTime() > - getAppFilename().getLastModificationTime() - - isScriptResultCached = - isNimscriptApiCached and result.fileExists() and result.getLastModificationTime() > - scriptName.getLastModificationTime() - - if not isNimscriptApiCached: - createDir(cacheDir) - writeFile(nimscriptApiFile, nimscriptApi) - - if not isScriptResultCached: - createDir(result.parentDir()) - writeFile(result, """ -import system except getCommand, setCommand, switch, `--`, - packageName, version, author, description, license, srcDir, binDir, backend, - skipDirs, skipFiles, skipExt, installDirs, installFiles, installExt, bin, foreignDeps, - requires, task, packageName -""" & - "import nimscriptapi, strutils\n" & scriptName.readFile() & "\nonExit()\n") - discard tryRemoveFile(iniFile) - -proc getIniFile*(scriptName: string, options: Options): string = - let - nimsFile = getNimsFile(scriptName, options) - - result = nimsFile.changeFileExt(".ini") - - let - isIniResultCached = - result.fileExists() and result.getLastModificationTime() > - scriptName.getLastModificationTime() - - if not isIniResultCached: - let (output, exitCode, stdout) = execNimscript( - nimsFile, scriptName.parentDir(), printPkgInfo, options, isHook=false - ) - - if exitCode == 0 and output.len != 0: - result.writeFile(output) - stdout.writeExecutionOutput() - else: - raise newException(NimbleError, stdout & "\nprintPkgInfo() failed") - -proc execScript( - scriptName, actionName: string, options: Options, isHook: bool -): ExecutionResult[bool] = - let nimsFile = getNimsFile(scriptName, options) - - let (output, exitCode, stdout) = - execNimscript( - nimsFile, scriptName.parentDir(), actionName, options, isHook - ) - - if exitCode != 0: - let errMsg = - if stdout.len != 0: - stdout - else: - "Exception raised during nimble script execution" - raise newException(NimbleError, errMsg) - - let - j = - if output.len != 0: - parseJson(output) - else: - parseJson("{}") - - result.flags = newTable[string, seq[string]]() - result.success = j{"success"}.getBool() - result.command = j{"command"}.getStr() - if "project" in j: - result.arguments.add j["project"].getStr() - if "flags" in j: - for flag, vals in j["flags"].pairs: - result.flags[flag] = @[] - for val in vals.items(): - result.flags[flag].add val.getStr() - result.retVal = j{"retVal"}.getBool() - - stdout.writeExecutionOutput() - -proc execTask*(scriptName, taskName: string, - options: Options): ExecutionResult[bool] = - ## Executes the specified task in the specified script. - ## - ## `scriptName` should be a filename pointing to the nimscript file. - display("Executing", "task $# in $#" % [taskName, scriptName], - priority = HighPriority) - - result = execScript(scriptName, taskName, options, isHook=false) - -proc execHook*(scriptName, actionName: string, before: bool, - options: Options): ExecutionResult[bool] = - ## Executes the specified action's hook. Depending on ``before``, either - ## the "before" or the "after" hook. - ## - ## `scriptName` should be a filename pointing to the nimscript file. - let hookName = - if before: actionName.toLowerAscii & "Before" - else: actionName.toLowerAscii & "After" - display("Attempting", "to execute hook $# in $#" % [hookName, scriptName], - priority = MediumPriority) - - result = execScript(scriptName, hookName, options, isHook=true) - -proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool = - ## Determines whether the last executed task used ``setCommand`` - return execResult.command != internalCmd - -proc listTasks*(scriptName: string, options: Options) = - discard execScript(scriptName, "", options, isHook=false) diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index 14d1c31..749a4ed 100644 --- a/src/nimblepkg/options.nim +++ b/src/nimblepkg/options.nim @@ -2,11 +2,9 @@ # BSD License. Look at license.txt for more info. import json, strutils, os, parseopt, strtabs, uri, tables, terminal -import sequtils, sugar -import std/options as std_opt from httpclient import Proxy, newProxy -import config, version, common, cli +import config, version, tools, common, cli type Options* = object @@ -25,18 +23,14 @@ type showVersion*: bool noColor*: bool disableValidation*: bool - continueTestsOnFailure*: bool ## Whether packages' repos should always be downloaded with their history. forceFullClone*: bool - # Temporary storage of flags that have not been captured by any specific Action. - unknownFlags*: seq[(CmdLineKind, string, string)] ActionType* = enum actionNil, actionRefresh, actionInit, actionDump, actionPublish, actionInstall, actionSearch, actionList, actionBuild, actionPath, actionUninstall, actionCompile, - actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck, - actionRun + actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck Action* = object case typ*: ActionType @@ -46,20 +40,14 @@ type of actionInstall, actionPath, actionUninstall, actionDevelop: packages*: seq[PkgTuple] # Optional only for actionInstall # and actionDevelop. - passNimFlags*: seq[string] of actionSearch: 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: Option[string] - compileFlags: seq[string] - runFlags*: seq[string] + compileOptions*: seq[string] of actionCustom: command*: string arguments*: seq[string] @@ -72,7 +60,6 @@ Usage: nimble COMMAND [opts] Commands: install [pkgname, ...] Installs a list of packages. [-d, --depsOnly] Install only dependencies. - [-p, --passNim] Forward specified flag to compiler. develop [pkgname, ...] Clones a list of packages for development. Symlinks the cloned packages or any package in the current working directory. @@ -81,23 +68,15 @@ 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. - 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. + build Builds a package. c, cc, js [opts, ...] f.nim Builds a file inside a package. Passes options to the Nim compiler. test Compiles and executes tests - [-c, --continue] Don't stop execution on a failed test. doc, doc2 [opts, ...] f.nim Builds documentation for a file inside a package. Passes options to the Nim compiler. refresh [url] Refreshes the package list. A package list URL @@ -160,8 +139,6 @@ proc parseActionType*(action: string): ActionType = result = actionPath of "build": result = actionBuild - of "run": - result = actionRun of "c", "compile", "js", "cpp", "cc": result = actionCompile of "doc", "doc2": @@ -196,7 +173,6 @@ proc initAction*(options: var Options, key: string) = case options.action.typ of actionInstall, actionPath, actionDevelop, actionUninstall: options.action.packages = @[] - options.action.passNimFlags = @[] of actionCompile, actionDoc, actionBuild: options.action.compileOptions = @[] options.action.file = "" @@ -204,11 +180,8 @@ 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 = "" of actionSearch: @@ -217,7 +190,7 @@ proc initAction*(options: var Options, key: string) = options.action.command = key options.action.arguments = @[] options.action.flags = newStringTable() - of actionPublish, actionList, actionTasks, actionCheck, actionRun, + of actionPublish, actionList, actionTasks, actionCheck, actionNil: discard proc prompt*(options: Options, question: string): bool = @@ -263,15 +236,9 @@ proc getBinDir*(options: Options): string = options.getNimbleDir() / "bin" proc parseCommand*(key: string, result: var Options) = - result.action = Action(typ: parseActionType(key)) + result.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,40 +259,23 @@ proc parseArgument*(key: string, result: var Options) = result.action.search.add(key) of actionInit, actionDump: if result.action.projName != "": - raise newException( - NimbleError, "Can only perform this action on one package at a time." - ) + raise newException(NimbleError, + "Can only initialize one package at a time.") result.action.projName = key of actionCompile, actionDoc: result.action.file = key - of actionList, actionPublish: + of actionList, actionBuild, actionPublish: result.showHelp = true - of actionBuild: - result.action.file = key - of actionRun: - result.setRunOptions(key, key, true) of actionCustom: result.action.arguments.add(key) else: discard -proc getFlagString(kind: CmdLineKind, flag, val: string): string = - let prefix = - case kind - of cmdShortOption: "-" - of cmdLongOption: "--" - else: "" - if val == "": - return prefix & flag - else: - return prefix & flag & ":" & val - proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = - + var wasFlagHandled = true let f = flag.normalize() # Global flags. - var isGlobalFlag = true case f of "help", "h": result.showHelp = true of "version", "v": result.showVersion = true @@ -336,64 +286,49 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = of "debug": result.verbosity = DebugPriority of "nocolor": result.noColor = true of "disablevalidation": result.disableValidation = true - else: isGlobalFlag = false - - var wasFlagHandled = true # Action-specific flags. - case result.action.typ - of actionSearch, actionList: - case f - of "installed", "i": - result.queryInstalled = true - of "ver": - result.queryVersions = true - else: - wasFlagHandled = false - of actionInstall: - case f - of "depsonly", "d": - result.depsOnly = true - of "passnim", "p": - 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": - result.uninstallRevDeps = true - else: - wasFlagHandled = false - of actionCompile, actionDoc, actionBuild: - if not isGlobalFlag: - result.action.compileOptions.add(getFlagString(kind, flag, val)) - of actionRun: - 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": - result.continueTestsOnFailure = true - result.action.flags[flag] = val else: - wasFlagHandled = false + case result.action.typ + of actionSearch, actionList: + case f + of "installed", "i": + result.queryInstalled = true + of "ver": + result.queryVersions = true + else: + wasFlagHandled = false + of actionInstall: + case f + of "depsonly", "d": + result.depsOnly = true + else: + wasFlagHandled = false + of actionUninstall: + case f + of "incldeps", "i": + result.uninstallRevDeps = true + else: + wasFlagHandled = false + of actionCompile, actionDoc, actionBuild: + let prefix = if kind == cmdShortOption: "-" else: "--" + if val == "": + result.action.compileOptions.add(prefix & flag) + else: + result.action.compileOptions.add(prefix & flag & ":" & val) + of actionCustom: + result.action.flags[flag] = val + else: + wasFlagHandled = false - if not wasFlagHandled and not isGlobalFlag: - result.unknownFlags.add((kind, flag, val)) + if not wasFlagHandled: + raise newException(NimbleError, "Unknown option: --" & flag) proc initOptions*(): Options = - # Exported for choosenim - Options( - action: Action(typ: actionNil), - pkgInfoCache: newTable[string, PackageInfo](), - verbosity: HighPriority, - noColor: not isatty(stdout) - ) + result.action.typ = actionNil + result.pkgInfoCache = newTable[string, PackageInfo]() + result.nimbleDir = "" + result.verbosity = HighPriority + result.noColor = not isatty(stdout) proc parseMisc(options: var Options) = # Load nimbledata.json @@ -408,29 +343,6 @@ proc parseMisc(options: var Options) = else: options.nimbleData = %{"reverseDeps": newJObject()} -proc handleUnknownFlags(options: var Options) = - if options.action.typ == actionRun: - # ActionRun uses flags that come before the command as compilation flags - # and flags that come after as run flags. - options.action.compileFlags = - map(options.unknownFlags, x => getFlagString(x[0], x[1], x[2])) - options.unknownFlags = @[] - else: - # For everything else, handle the flags that came before the command - # normally. - let unknownFlags = options.unknownFlags - options.unknownFlags = @[] - for flag in unknownFlags: - parseFlag(flag[1], flag[2], options, flag[0]) - - # Any unhandled flags? - if options.unknownFlags.len > 0: - let flag = options.unknownFlags[0] - raise newException( - NimbleError, - "Unknown option: " & getFlagString(flag[0], flag[1], flag[2]) - ) - proc parseCmdLine*(): Options = result = initOptions() @@ -444,11 +356,9 @@ 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) - # Set verbosity level. setVerbosity(result.verbosity) @@ -464,11 +374,6 @@ proc parseCmdLine*(): Options = if result.action.typ == actionNil and not result.showVersion: result.showHelp = true - if result.action.typ != actionNil and result.showVersion: - # We've got another command that should be handled. For example: - # nimble run foobar -v - result.showVersion = false - proc getProxy*(options: Options): Proxy = ## Returns ``nil`` if no proxy is specified. var url = "" @@ -508,44 +413,3 @@ proc briefClone*(options: Options): Options = newOptions.forcePrompts = options.forcePrompts newOptions.pkgInfoCache = options.pkgInfoCache return newOptions - -proc shouldRemoveTmp*(options: Options, file: string): bool = - result = true - if options.verbosity <= DebugPriority: - let msg = "Not removing temporary path because of debug verbosity: " & file - display("Warning:", msg, Warning, MediumPriority) - return false - -proc getCompilationFlags*(options: var Options): var seq[string] = - case options.action.typ - of actionBuild, actionDoc, actionCompile: - return options.action.compileOptions - of actionRun: - return options.action.compileFlags - else: - assert false - -proc getCompilationFlags*(options: Options): seq[string] = - var opt = options - return opt.getCompilationFlags() - -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 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.changeFileExt(ExeExt)) - else: - discard diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index f00c59f..1261494 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -3,7 +3,8 @@ # Stdlib imports import system except TResult -import hashes, json, strutils, os, sets, tables, httpclient +import parsecfg, json, streams, strutils, parseutils, os, sets, tables +import httpclient # Local imports import version, tools, common, options, cli, config @@ -277,7 +278,7 @@ proc getPackage*(pkg: string, options: Options, resPkg: var Package): bool = proc getPackageList*(options: Options): seq[Package] = ## Returns the list of packages found in the downloaded packages.json files. result = @[] - var namesAdded = initHashSet[string]() + var namesAdded = initSet[string]() for name, list in options.config.packageLists: let packages = readPackageList(name, options) for p in packages: @@ -310,6 +311,7 @@ proc findNimbleFile*(dir: string; error: bool): string = if result.splitFile.ext == ".nimble-link": # Return the path of the real .nimble file. + let nimbleLinkPath = result result = readNimbleLink(result).nimbleFilePath if not fileExists(result): let msg = "The .nimble-link file is pointing to a missing file: " & result @@ -322,7 +324,7 @@ proc getInstalledPkgsMin*(libsDir: string, options: Options): seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = ## Gets a list of installed packages. The resulting package info is ## minimal. This has the advantage that it does not depend on the - ## ``packageparser`` module, and so can be used by ``nimscriptwrapper``. + ## ``packageparser`` module, and so can be used by ``nimscriptsupport``. ## ## ``libsDir`` is in most cases: ~/.nimble/pkgs/ (options.getPkgsDir) result = @[] @@ -540,11 +542,6 @@ proc `==`*(pkg1: PackageInfo, pkg2: PackageInfo): bool = if pkg1.name == pkg2.name and pkg1.myPath == pkg2.myPath: return true -proc hash*(x: PackageInfo): Hash = - var h: Hash = 0 - h = h !& hash(x.myPath) - result = !$h - when isMainModule: doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") == ("packagea", "0.1") diff --git a/src/nimblepkg/packageinstaller.nim b/src/nimblepkg/packageinstaller.nim index 4bdc921..125db93 100644 --- a/src/nimblepkg/packageinstaller.nim +++ b/src/nimblepkg/packageinstaller.nim @@ -3,13 +3,7 @@ import os, strutils, sets, json # Local imports -import cli, options, tools - -when defined(windows): - import version - -when not declared(initHashSet) or not declared(toHashSet): - import common +import cli, common, options, tools when defined(windows): # This is just for Win XP support. @@ -109,4 +103,4 @@ proc saveNimbleMeta*(pkgDestDir, pkgDir, vcsRevision, nimbleLinkPath: string) = ## pkgDir - The directory where the original package files are. ## For example: ~/projects/jester/ saveNimbleMeta(pkgDestDir, "file://" & pkgDir, vcsRevision, - toHashSet[string]([nimbleLinkPath]), initHashSet[string](), true) \ No newline at end of file + toSet[string]([nimbleLinkPath]), initSet[string](), true) \ No newline at end of file diff --git a/src/nimblepkg/packageparser.nim b/src/nimblepkg/packageparser.nim index 9458074..cf77dcc 100644 --- a/src/nimblepkg/packageparser.nim +++ b/src/nimblepkg/packageparser.nim @@ -1,12 +1,12 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import parsecfg, sets, streams, strutils, os, tables, sugar +import parsecfg, json, streams, strutils, parseutils, os, tables, sugar from sequtils import apply, map -import version, tools, common, nimscriptwrapper, options, packageinfo, cli +import version, tools, common, nimscriptsupport, options, packageinfo, cli ## Contains procedures for parsing .nimble files. Moved here from ``packageinfo`` -## because it depends on ``nimscriptwrapper`` (``nimscriptwrapper`` also +## because it depends on ``nimscriptsupport`` (``nimscriptsupport`` also ## depends on other procedures in ``packageinfo``. type @@ -212,10 +212,7 @@ proc multiSplit(s: string): seq[string] = result.del(i) # Huh, nothing to return? Return given input. if len(result) < 1: - if s.strip().len != 0: - return @[s] - else: - return @[] + return @[s] proc readPackageInfoFromNimble(path: string; result: var PackageInfo) = var fs = newFileStream(path, fmRead) @@ -256,20 +253,12 @@ proc readPackageInfoFromNimble(path: string; result: var PackageInfo) = result.installExt.add(ev.value.multiSplit) of "bin": for i in ev.value.multiSplit: - if i.splitFile().ext == ".nim": - raise newException(NimbleError, "`bin` entry should not be a source file: " & i) result.bin.add(i.addFileExt(ExeExt)) of "backend": result.backend = ev.value.toLowerAscii() case result.backend.normalize of "javascript": result.backend = "js" else: discard - of "beforehooks": - for i in ev.value.multiSplit: - result.preHooks.incl(i.normalize) - of "afterhooks": - for i in ev.value.multiSplit: - result.postHooks.incl(i.normalize) else: raise newException(NimbleError, "Invalid field: " & ev.key) of "deps", "dependencies": @@ -288,14 +277,6 @@ proc readPackageInfoFromNimble(path: string; result: var PackageInfo) = else: raise newException(ValueError, "Cannot open package info: " & path) -proc readPackageInfoFromNims(scriptName: string, options: Options, - result: var PackageInfo) = - let - iniFile = getIniFile(scriptName, options) - - if iniFile.fileExists(): - readPackageInfoFromNimble(iniFile, result) - proc inferInstallRules(pkgInfo: var PackageInfo, options: Options) = # Binary packages shouldn't install .nim files by default. # (As long as the package info doesn't explicitly specify what should be @@ -487,15 +468,6 @@ proc toFullInfo*(pkg: PackageInfo, options: Options): PackageInfo = else: return pkg -proc getConcreteVersion*(pkgInfo: PackageInfo, options: Options): string = - ## Returns a non-special version from the specified ``pkgInfo``. If the - ## ``pkgInfo`` is minimal it looks it up and retrieves the concrete version. - result = pkgInfo.version - if pkgInfo.isMinimal: - let pkgInfo = pkgInfo.toFullInfo(options) - result = pkgInfo.version - assert(not newVersion(result).isSpecial) - when isMainModule: validatePackageName("foo_bar") validatePackageName("f_oo_b_a_r") diff --git a/src/nimblepkg/publish.nim b/src/nimblepkg/publish.nim index f11b979..2d73478 100644 --- a/src/nimblepkg/publish.nim +++ b/src/nimblepkg/publish.nim @@ -5,8 +5,8 @@ ## nim-lang/packages automatically. import system except TResult -import httpclient, strutils, json, os, browsers, times, uri -import version, tools, common, cli, config, options +import httpclient, base64, strutils, rdstdin, json, os, browsers, times, uri +import tools, common, cli, config, options type Auth = object @@ -155,7 +155,7 @@ proc editJson(p: PackageInfo; url, tags, downloadMethod: string) = proc publish*(p: PackageInfo, o: Options) = ## Publishes the package p. let auth = getGithubAuth(o) - var pkgsDir = getNimbleUserTempDir() / "nimble-packages-fork" + var pkgsDir = getTempDir() / "nimble-packages-fork" if not forkExists(auth): createFork(auth) display("Info:", "Waiting 10s to let Github create a fork", @@ -213,10 +213,7 @@ proc publish*(p: PackageInfo, o: Options) = url = promptCustom("Github URL of " & p.name & "?", "") if url.len == 0: userAborted() - let tags = promptCustom( - "Whitespace separated list of tags? (For example: web library wrapper)", - "" - ) + let tags = promptCustom("Whitespace separated list of tags?", "") cd pkgsDir: editJson(p, url, tags, downloadMethod) diff --git a/src/nimblepkg/reversedeps.nim b/src/nimblepkg/reversedeps.nim index 45d9940..0da2a84 100644 --- a/src/nimblepkg/reversedeps.nim +++ b/src/nimblepkg/reversedeps.nim @@ -1,7 +1,7 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import os, json, sets +import os, json import options, common, version, download, packageinfo @@ -58,7 +58,7 @@ proc removeRevDep*(nimbleData: JsonNode, pkg: PackageInfo) = newData[key] = newVal nimbleData["reverseDeps"] = newData -proc getRevDepTups*(options: Options, pkg: PackageInfo): seq[PkgTuple] = +proc getRevDeps*(options: Options, pkg: PackageInfo): seq[PkgTuple] = ## Returns a list of *currently installed* reverse dependencies for `pkg`. result = @[] let thisPkgsDep = @@ -76,25 +76,18 @@ proc getRevDepTups*(options: Options, pkg: PackageInfo): seq[PkgTuple] = result.add(pkgTup) -proc getRevDeps*(options: Options, pkg: PackageInfo): HashSet[PackageInfo] = - result.init() - let installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options) - for rdepTup in getRevDepTups(options, pkg): - for rdepInfo in findAllPkgs(installedPkgs, rdepTup): - result.incl rdepInfo - -proc getAllRevDeps*(options: Options, pkg: PackageInfo, result: var HashSet[PackageInfo]) = +proc getAllRevDeps*(options: Options, pkg: PackageInfo, result: var seq[PackageInfo]) = if pkg in result: return let installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options) - for rdepTup in getRevDepTups(options, pkg): + for rdepTup in getRevDeps(options, pkg): for rdepInfo in findAllPkgs(installedPkgs, rdepTup): if rdepInfo in result: continue getAllRevDeps(options, rdepInfo, result) - result.incl pkg + result.add pkg when isMainModule: var nimbleData = %{"reverseDeps": newJObject()} diff --git a/src/nimblepkg/tools.nim b/src/nimblepkg/tools.nim index a0e6a0e..8dc71e2 100644 --- a/src/nimblepkg/tools.nim +++ b/src/nimblepkg/tools.nim @@ -3,7 +3,7 @@ # # Various miscellaneous utility functions reside here. import osproc, pegs, strutils, os, uri, sets, json, parseutils -import version, cli +import version, common, cli proc extractBin(cmd: string): string = if cmd[0] == '"': @@ -11,7 +11,7 @@ proc extractBin(cmd: string): string = else: return cmd.split(' ')[0] -proc doCmd*(cmd: string, showOutput = false, showCmd = false) = +proc doCmd*(cmd: string, showOutput = false) = let bin = extractBin(cmd) if findExe(bin) == "": raise newException(NimbleError, "'" & bin & "' not in PATH.") @@ -20,10 +20,7 @@ proc doCmd*(cmd: string, showOutput = false, showCmd = false) = stdout.flushFile() stderr.flushFile() - if showCmd: - display("Executing", cmd, priority = MediumPriority) - else: - displayDebug("Executing", cmd) + displayDebug("Executing", cmd) if showOutput: let exitCode = execCmd(cmd) displayDebug("Finished", "with exit code " & $exitCode) @@ -151,15 +148,6 @@ proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool = when not defined(windows): from posix import getpid - -proc getProcessId*(): string = - when defined(windows): - proc GetCurrentProcessId(): int32 {.stdcall, dynlib: "kernel32", - importc: "GetCurrentProcessId".} - result = $GetCurrentProcessId() - else: - result = $getpid() - proc getNimbleTempDir*(): string = ## Returns a path to a temporary directory. ## @@ -167,18 +155,10 @@ proc getNimbleTempDir*(): string = ## 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_" & getProcessId() - -proc getNimbleUserTempDir*(): 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. - var tmpdir: string - if existsEnv("TMPDIR") and existsEnv("USER"): - tmpdir = joinPath(getEnv("TMPDIR"), getEnv("USER")) + result = getTempDir() / "nimble_" + when defined(windows): + proc GetCurrentProcessId(): int32 {.stdcall, dynlib: "kernel32", + importc: "GetCurrentProcessId".} + result.add($GetCurrentProcessId()) else: - tmpdir = getTempDir() - return tmpdir + result.add($getpid()) diff --git a/src/nimblepkg/version.nim b/src/nimblepkg/version.nim index e4114f1..ae85e99 100644 --- a/src/nimblepkg/version.nim +++ b/src/nimblepkg/version.nim @@ -93,11 +93,6 @@ proc `==`*(ver: Version, ver2: Version): bool = else: return false -proc cmp*(a, b: Version): int = - if a < b: -1 - elif a > b: 1 - else: 0 - proc `<=`*(ver: Version, ver2: Version): bool = return (ver == ver2) or (ver < ver2) @@ -135,32 +130,34 @@ proc contains*(ran: VersionRange, ver: Version): bool = return withinRange(ver, ran) proc makeRange*(version: string, op: string): VersionRange = + new(result) if version == "": raise newException(ParseVersionError, "A version needs to accompany the operator.") case op of ">": - result = VersionRange(kind: verLater) + result.kind = verLater of "<": - result = VersionRange(kind: verEarlier) + result.kind = verEarlier of ">=": - result = VersionRange(kind: verEqLater) + result.kind = verEqLater of "<=": - result = VersionRange(kind: verEqEarlier) - of "", "==": - result = VersionRange(kind: verEq) + result.kind = verEqEarlier + of "": + result.kind = verEq else: raise newException(ParseVersionError, "Invalid operator: " & op) result.ver = Version(version) proc parseVersionRange*(s: string): VersionRange = # >= 1.5 & <= 1.8 + new(result) if s.len == 0: - result = VersionRange(kind: verAny) + result.kind = verAny return if s[0] == '#': - result = VersionRange(kind: verSpecial) + result.kind = verSpecial result.spe = s.Version return @@ -172,7 +169,7 @@ proc parseVersionRange*(s: string): VersionRange = of '>', '<', '=': op.add(s[i]) of '&': - result = VersionRange(kind: verIntersect) + result.kind = verIntersect result.verILeft = makeRange(version, op) # Parse everything after & @@ -207,10 +204,10 @@ proc toVersionRange*(ver: Version): VersionRange = ## Converts a version to either a verEq or verSpecial VersionRange. new(result) if ver.isSpecial: - result = VersionRange(kind: verSpecial) + result.kind = verSpecial result.spe = ver else: - result = VersionRange(kind: verEq) + result.kind = verEq result.ver = ver proc parseRequires*(req: string): PkgTuple = @@ -266,18 +263,21 @@ proc getSimpleString*(verRange: VersionRange): string = result = "" proc newVRAny*(): VersionRange = - result = VersionRange(kind: verAny) + new(result) + result.kind = verAny proc newVREarlier*(ver: string): VersionRange = - result = VersionRange(kind: verEarlier) + new(result) + result.kind = verEarlier result.ver = newVersion(ver) proc newVREq*(ver: string): VersionRange = - result = VersionRange(kind: verEq) + new(result) + result.kind = verEq result.ver = newVersion(ver) proc findLatest*(verRange: VersionRange, - versions: OrderedTable[Version, string]): tuple[ver: Version, tag: string] = + versions: Table[Version, string]): tuple[ver: Version, tag: string] = result = (newVersion(""), "") for ver, tag in versions: if not withinRange(ver, verRange): continue @@ -298,10 +298,9 @@ 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)) @@ -315,11 +314,8 @@ when isMainModule: doAssert(newVersion("") < newVersion("1.0.0")) doAssert(newVersion("") < newVersion("0.1.0")) - var versions = toOrderedTable[Version, string]({ - newVersion("0.1.1"): "v0.1.1", - newVersion("0.2.3"): "v0.2.3", - newVersion("0.5"): "v0.5" - }) + var versions = toTable[Version, string]({newVersion("0.1.1"): "v0.1.1", + newVersion("0.2.3"): "v0.2.3", newVersion("0.5"): "v0.5"}) doAssert findLatest(parseVersionRange(">= 0.1 & <= 0.4"), versions) == (newVersion("0.2.3"), "v0.2.3") diff --git a/tests/.gitignore b/tests/.gitignore index 42549a0..8258bb3 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,10 +1,6 @@ -tester -/nimble-test -/buildDir /binaryPackage/v1/binaryPackage /binaryPackage/v2/binaryPackage /develop/dependent/src/dependent -/issue27/issue27 /issue206/issue/issue206bin /issue289/issue289 /issue428/nimbleDir/ @@ -17,7 +13,3 @@ tester /testCommand/testsPass/tests/one /testCommand/testsPass/tests/three /testCommand/testsPass/tests/two -/nimscript/nimscript -/packageStructure/validBinary/y -/testCommand/testsFail/tests/t2 -/passNimFlags/passNimFlags diff --git a/tests/caching/caching.nimble b/tests/caching/caching.nimble deleted file mode 100644 index d3035a9..0000000 --- a/tests/caching/caching.nimble +++ /dev/null @@ -1,10 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "Test package" -license = "BSD" - -# Dependencies - -requires "nim >= 0.12.1" diff --git a/tests/invalidPackage/invalidPackage.nimble b/tests/invalidPackage/invalidPackage.nimble deleted file mode 100644 index 08fcfcb..0000000 --- a/tests/invalidPackage/invalidPackage.nimble +++ /dev/null @@ -1,13 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "A new awesome nimble package" -license = "MIT" -srcDir = "src" - -thisFieldDoesNotExist = "hello" - -# Dependencies - -requires "nim >= 0.20.0" diff --git a/tests/issue432/issue432.nimble b/tests/issue432/issue432.nimble deleted file mode 100644 index 92937c5..0000000 --- a/tests/issue432/issue432.nimble +++ /dev/null @@ -1,15 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "A new awesome nimble package" -license = "MIT" -srcDir = "src" - - - -# Dependencies - -requires "nim >= 0.16.0" -requires "https://github.com/nimble-test/packagea#head", - "https://github.com/nimble-test/packagebin2" diff --git a/tests/issue432/src/issue432.nim b/tests/issue432/src/issue432.nim deleted file mode 100644 index 4b2a270..0000000 --- a/tests/issue432/src/issue432.nim +++ /dev/null @@ -1,7 +0,0 @@ -# This is just an example to get you started. A typical library package -# exports the main API in this file. Note that you cannot rename this file -# but you can remove it if you wish. - -proc add*(x, y: int): int = - ## Adds two files together. - return x + y diff --git a/tests/issue564/issue564.nimble b/tests/issue564/issue564.nimble deleted file mode 100644 index 7dc0013..0000000 --- a/tests/issue564/issue564.nimble +++ /dev/null @@ -1,14 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "A new awesome nimble package" -license = "MIT" -srcDir = "src" -bin = @["issue564/issue564build"] - - - -# Dependencies - -requires "nim >= 0.16.0" diff --git a/tests/issue564/src/issue564/issue564build.nim b/tests/issue564/src/issue564/issue564build.nim deleted file mode 100644 index 862d40c..0000000 --- a/tests/issue564/src/issue564/issue564build.nim +++ /dev/null @@ -1,5 +0,0 @@ -# This is just an example to get you started. A typical binary package -# uses this file as the main entry point of the application. - -when isMainModule: - echo("Hello, World!") diff --git a/tests/issue597/dummy.nimble b/tests/issue597/dummy.nimble deleted file mode 100644 index 13bf4be..0000000 --- a/tests/issue597/dummy.nimble +++ /dev/null @@ -1,12 +0,0 @@ -# Package - -version = "0.1.0" -author = "Author" -description = "dummy" -license = "MIT" - -# Dependencies - -requires "nim >= 0.17.0" - -bin = @["test.nim"] diff --git a/tests/issue597/test.nim b/tests/issue597/test.nim deleted file mode 100644 index e69de29..0000000 diff --git a/tests/issue633/issue633.nimble b/tests/issue633/issue633.nimble deleted file mode 100644 index cb786eb..0000000 --- a/tests/issue633/issue633.nimble +++ /dev/null @@ -1,16 +0,0 @@ -# Package - -version = "0.1.0" -author = "GT" -description = "Package for ensuring that issue #633 is resolved." -license = "MIT" - -# Dependencies - -requires "nim >= 0.19.6" -# to reproduce dependency 2 must be before 1 - -task testTask, "Test": - for i in 0 .. paramCount(): - if paramStr(i) == "--testTask": - echo "Got it" diff --git a/tests/issue678/issue678.nimble b/tests/issue678/issue678.nimble deleted file mode 100644 index 20239e7..0000000 --- a/tests/issue678/issue678.nimble +++ /dev/null @@ -1,12 +0,0 @@ -# Package - -version = "0.1.0" -author = "Ivan Bobev" -description = "Package for ensuring that issue #678 is resolved." -license = "MIT" - -# Dependencies - -requires "nim >= 0.19.6" -# to reproduce dependency 2 must be before 1 -requires "issue678_dependency_2", "issue678_dependency_1" diff --git a/tests/issue678/packages.json b/tests/issue678/packages.json deleted file mode 100644 index 972eb70..0000000 --- a/tests/issue678/packages.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "issue678_dependency_1", - "url": "https://github.com/nimble-test/issue678?subdir=dependency_1", - "method": "git", - "tags": [ "test" ], - "description": - "Both first and second level dependency of the issue678 package.", - "license": "MIT" - }, - { - "name": "issue678_dependency_2", - "url": "https://github.com/nimble-test/issue678?subdir=dependency_2", - "method": "git", - "tags": [ "test" ], - "description": "First level dependency of the issue678 package.", - "license": "MIT" - } -] diff --git a/tests/issue708/issue708.nimble b/tests/issue708/issue708.nimble deleted file mode 100644 index ebb079d..0000000 --- a/tests/issue708/issue708.nimble +++ /dev/null @@ -1,17 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "A new awesome nimble package" -license = "MIT" -srcDir = "src" - - - -# Dependencies - -requires "nim >= 0.16.0" - - -echo "hello" -echo "hello2" diff --git a/tests/issue708/src/issue708.nim b/tests/issue708/src/issue708.nim deleted file mode 100644 index 4b2a270..0000000 --- a/tests/issue708/src/issue708.nim +++ /dev/null @@ -1,7 +0,0 @@ -# This is just an example to get you started. A typical library package -# exports the main API in this file. Note that you cannot rename this file -# but you can remove it if you wish. - -proc add*(x, y: int): int = - ## Adds two files together. - return x + y diff --git a/tests/nimbleVersionDefine/nimbleVersionDefine.nimble b/tests/nimbleVersionDefine/nimbleVersionDefine.nimble deleted file mode 100644 index b47b049..0000000 --- a/tests/nimbleVersionDefine/nimbleVersionDefine.nimble +++ /dev/null @@ -1,14 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "A new awesome nimble package" -license = "MIT" -srcDir = "src" -bin = @["nimbleVersionDefine"] - - - -# Dependencies - -requires "nim >= 0.16.0" diff --git a/tests/nimbleVersionDefine/src/nimbleVersionDefine b/tests/nimbleVersionDefine/src/nimbleVersionDefine deleted file mode 100755 index af3cc22..0000000 Binary files a/tests/nimbleVersionDefine/src/nimbleVersionDefine and /dev/null differ diff --git a/tests/nimbleVersionDefine/src/nimbleVersionDefine.nim b/tests/nimbleVersionDefine/src/nimbleVersionDefine.nim deleted file mode 100644 index 572dab8..0000000 --- a/tests/nimbleVersionDefine/src/nimbleVersionDefine.nim +++ /dev/null @@ -1,3 +0,0 @@ -when isMainModule: - const NimblePkgVersion {.strdefine.} = "Unknown" - echo(NimblePkgVersion) diff --git a/tests/nimscript/nimscript.nimble b/tests/nimscript/nimscript.nimble index 39f3710..4625abb 100644 --- a/tests/nimscript/nimscript.nimble +++ b/tests/nimscript/nimscript.nimble @@ -2,9 +2,7 @@ version = "0.1.0" author = "Dominik Picheta" -description = """Test package -with multi-line description -""" +description = "Test package" license = "BSD" bin = @["nimscript"] @@ -26,8 +24,6 @@ task cr, "Testing `nimble c -r nimscript.nim` via setCommand": task repeated, "Testing `nimble c nimscript.nim` with repeated flags": --define: foo --define: bar - --define: "quoted" - --define: "quoted\\\"with\\\"quotes" setCommand "c", "nimscript.nim" task api, "Testing nimscriptapi module functionality": @@ -54,9 +50,3 @@ before install: after install: echo("After PkgDir: ", getPkgDir()) - -before build: - echo("Before build") - -after build: - echo("After build") \ No newline at end of file diff --git a/tests/passNimFlags/passNimFlags.nim b/tests/passNimFlags/passNimFlags.nim deleted file mode 100644 index b4c0b97..0000000 --- a/tests/passNimFlags/passNimFlags.nim +++ /dev/null @@ -1 +0,0 @@ -when not defined(passNimIsWorking): {.error: "-d:passNimIsWorking wasn't passed to the compiler"} diff --git a/tests/passNimFlags/passNimFlags.nimble b/tests/passNimFlags/passNimFlags.nimble deleted file mode 100644 index 8530524..0000000 --- a/tests/passNimFlags/passNimFlags.nimble +++ /dev/null @@ -1,11 +0,0 @@ -# Package - -version = "0.1.0" -author = "SolitudeSF" -description = "Test nimble install flag forwarding" -license = "BSD" -bin = @["passNimFlags"] - -# Dependencies - -requires "nim >= 0.13.0" diff --git a/tests/recursive/recursive.nimble b/tests/recursive/recursive.nimble deleted file mode 100644 index d6b155d..0000000 --- a/tests/recursive/recursive.nimble +++ /dev/null @@ -1,25 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "Test package" -license = "BSD" - -# Dependencies - -requires "nim >= 0.12.1" - -let - callNimble = getEnv("NIMBLE_TEST_BINARY_PATH") -doAssert callNimble.len != 0, "NIMBLE_TEST_BINARY_PATH not set" - -task recurse, "Level 1": - echo 1 - exec callNimble & " recurse2" - -task recurse2, "Level 2": - echo 2 - exec callNimble & " recurse3" - -task recurse3, "Level 3": - echo 3 diff --git a/tests/run/run.nimble b/tests/run/run.nimble deleted file mode 100644 index 055bc1e..0000000 --- a/tests/run/run.nimble +++ /dev/null @@ -1,14 +0,0 @@ -# Package - -version = "0.1.0" -author = "Dominik Picheta" -description = "A new awesome nimble package" -license = "MIT" -srcDir = "src" -bin = @["run"] - - - -# Dependencies - -requires "nim >= 0.19.0" diff --git a/tests/run/src/run.nim b/tests/run/src/run.nim deleted file mode 100644 index af98995..0000000 --- a/tests/run/src/run.nim +++ /dev/null @@ -1,4 +0,0 @@ -import os - -when isMainModule: - echo("Testing `nimble run`: ", commandLineParams()) diff --git a/tests/tester.nim b/tests/tester.nim index 446fecb..ff44add 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -1,6 +1,6 @@ # Copyright (C) Dominik Picheta. All rights reserved. # BSD License. Look at license.txt for more info. -import osproc, unittest, strutils, os, sequtils, sugar, strformat +import osproc, streams, unittest, strutils, os, sequtils, sugar # TODO: Each test should start off with a clean slate. Currently installed # packages are shared between each test which causes a multitude of issues @@ -10,10 +10,6 @@ var rootDir = getCurrentDir().parentDir() var nimblePath = rootDir / "src" / addFileExt("nimble", ExeExt) 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) @@ -36,23 +32,18 @@ template cd*(dir: string, body: untyped) = 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.quoteShell) + quotedArgs.add("--nimbleDir:" & installDir) + quotedArgs = quotedArgs.map((x: string) => ("\"" & x & "\"")) - let path {.used.} = getCurrentDir().parentDir() / "src" + let path = getCurrentDir().parentDir() / "src" - var cmd = - when not defined(windows): - "PATH=" & path & ":$PATH " & quotedArgs.join(" ") - else: - quotedArgs.join(" ") + var cmd = "PATH=" & path & ":$PATH " & quotedArgs.join(" ") when defined(macosx): # TODO: Yeah, this is really specific to my machine but for my own sanity... cmd = "DYLD_LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib " & cmd result = execCmdEx(cmd) - checkpoint(cmd) checkpoint(result.output) proc execNimbleYes(args: varargs[string]): tuple[output: string, exitCode: int]= @@ -76,55 +67,14 @@ proc inLines(lines: seq[string], line: string): bool = for i in lines: if line.normalize in i.normalize: return true -proc hasLineStartingWith(lines: seq[string], prefix: string): bool = - for line in lines: - if line.strip(trailing = false).startsWith(prefix): - return true - return false +suite "Issue 620": + test "install nimble": + cd "..": + check execNimble(["install", "-y"]).exitCode == QuitSuccess -test "issue 708": - cd "issue708": - # TODO: We need a way to filter out compiler messages from the messages - # written by our nimble scripts. - var (output, exitCode) = execNimble("install", "-y", "--verbose") - check exitCode == QuitSuccess - let lines = output.strip.processOutput() - check(inLines(lines, "hello")) - check(inLines(lines, "hello2")) - -test "issue 564": - cd "issue564": - var (_, exitCode) = execNimble("build") - check exitCode == QuitSuccess - -test "depsOnly + flag order test": - var (output, exitCode) = execNimble( - "--depsOnly", "install", "-y", "https://github.com/nimble-test/packagebin2" - ) - check(not output.contains("Success: packagebin2 installed successfully.")) - check exitCode == QuitSuccess - -test "nimscript evaluation error message": - cd "invalidPackage": - var (output, exitCode) = execNimble("check") - let lines = output.strip.processOutput() - check(lines[^2].endsWith("Error: undeclared identifier: 'thisFieldDoesNotExist'")) - check exitCode == QuitFailure - -test "caching of nims and ini detects changes": - cd "caching": - var (output, exitCode) = execNimble("dump") - check output.contains("0.1.0") - let - nfile = "caching.nimble" - writeFile(nfile, readFile(nfile).replace("0.1.0", "0.2.0")) - (output, exitCode) = execNimble("dump") - check output.contains("0.2.0") - writeFile(nfile, readFile(nfile).replace("0.2.0", "0.1.0")) - -test "tasks can be called recursively": - cd "recursive": - check execNimble("recurse").exitCode == QuitSuccess + test "nimble check the installed nimble file": + cd execNimble(["path", "nimble"]).output: + check execNimble(["check"]).exitCode == QuitSuccess test "picks #head when looking for packages": cd "versionClashes" / "aporiaScenario": @@ -156,7 +106,7 @@ test "can validate package structure (#144)": let (output, exitCode) = execNimble(["install", "-y"]) check exitCode == QuitSuccess let lines = output.strip.processOutput() - check(not lines.hasLineStartingWith("Warning:")) + check(not inLines(lines, "warning")) # Test that warnings are produced for the incorrectly structured packages. for package in ["x", "y", "z"]: @@ -167,22 +117,19 @@ test "can validate package structure (#144)": checkpoint(output) case package of "x": - check lines.hasLineStartingWith( - "Warning: Package 'x' has an incorrect structure. It should" & - " contain a single directory hierarchy for source files," & - " named 'x', but file 'foobar.nim' is in a directory named" & - " 'incorrect' instead.") + check inLines(lines, "Package 'x' has an incorrect structure. It should" & + " contain a single directory hierarchy for source files," & + " named 'x', but file 'foobar.nim' is in a directory named" & + " 'incorrect' instead.") of "y": - check lines.hasLineStartingWith( - "Warning: Package 'y' has an incorrect structure. It should" & - " contain a single directory hierarchy for source files," & - " named 'ypkg', but file 'foobar.nim' is in a directory named" & - " 'yWrong' instead.") + check inLines(lines, "Package 'y' has an incorrect structure. It should" & + " contain a single directory hierarchy for source files," & + " named 'ypkg', but file 'foobar.nim' is in a directory named" & + " 'yWrong' instead.") of "z": - check lines.hasLineStartingWith( - "Warning: Package 'z' has an incorrect structure. The top level" & - " of the package source directory should contain at most one module," & - " named 'z.nim', but a file named 'incorrect.nim' was found.") + check inLines(lines, "Package 'z' has an incorrect structure. The top level" & + " of the package source directory should contain at most one module," & + " named 'z.nim', but a file named 'incorrect.nim' was found.") else: assert false @@ -275,7 +222,7 @@ test "can refresh with local package list": [PackageList] name = "local" path = "$1" - """.unindent % (getCurrentDir() / "issue368" / "packages.json").replace("\\", "\\\\")) + """.unindent % (getCurrentDir() / "issue368" / "packages.json")) let (output, exitCode) = execNimble(["refresh", "--verbose"]) let lines = output.strip.processOutput() check inLines(lines, "config file at") @@ -318,20 +265,11 @@ suite "nimscript": cd "nimscript": let (output, exitCode) = execNimble(["install", "-y"]) check exitCode == QuitSuccess - check output.contains("Before build") - check output.contains("After build") let lines = output.strip.processOutput() check lines[0].startsWith("Before PkgDir:") - check lines[0].endsWith("tests" / "nimscript") + check lines[0].endsWith("tests/nimscript") check lines[^1].startsWith("After PkgDir:") - check lines[^1].endsWith("tests" / "nimbleDir" / "pkgs" / "nimscript-0.1.0") - - test "before/after on build": - cd "nimscript": - let (output, exitCode) = execNimble(["build"]) - check exitCode == QuitSuccess - check output.contains("Before build") - check output.contains("After build") + check lines[^1].endsWith("tests/nimbleDir/pkgs/nimscript-0.1.0") test "can execute nimscript tasks": cd "nimscript": @@ -488,12 +426,9 @@ test "issue #349": check code == QuitFailure check inLines(msg, "\"$1\" is an invalid package name: reserved name" % name) - try: - removeFile(name.changeFileExt("nimble")) - removeDir("src") - removeDir("tests") - except OSError: - discard + removeFile(name.changeFileExt("nimble")) + removeDir("src") + removeDir("tests") for reserved in reservedNames: checkName(reserved.toUpperAscii()) @@ -517,7 +452,8 @@ test "can uninstall": let ls = outp.strip.processOutput() check exitCode != QuitSuccess - check inLines(ls, "Cannot uninstall issue27b (0.1.0) because issue27a (0.1.0) depends") + check "Cannot uninstall issue27b (0.1.0) because issue27a (0.1.0) depends" & + " on it" in ls[ls.len-1] check execNimble("uninstall", "-y", "issue27").exitCode == QuitSuccess check execNimble("uninstall", "-y", "issue27a").exitCode == QuitSuccess @@ -599,15 +535,9 @@ suite "can handle two binary versions": cd "binaryPackage/v2": check execNimble("install", "-y").exitCode == QuitSuccess - var - cmd = installDir / "bin" / "binaryPackage" - - when defined(windows): - cmd = "cmd /c " & cmd & ".cmd" - test "can execute v2": let (output, exitCode) = - execCmdEx(cmd) + execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) check exitCode == QuitSuccess check output.strip() == "v2" @@ -615,7 +545,7 @@ suite "can handle two binary versions": check execNimble("remove", "binaryPackage@2.0", "-y").exitCode==QuitSuccess let (output, exitCode) = - execCmdEx(cmd) + execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) check exitCode == QuitSuccess check output.strip() == "v1" @@ -623,7 +553,7 @@ suite "can handle two binary versions": check execNimble("remove", "binaryPackage@1.0", "-y").exitCode==QuitSuccess let (output, exitCode) = - execCmdEx(cmd) + execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) check exitCode == QuitSuccess check output.strip() == "v2" @@ -636,12 +566,6 @@ test "can pass args with spaces to Nim (#351)": checkpoint output check exitCode == QuitSuccess -test "error if `bin` is a source file (#597)": - cd "issue597": - var (output, exitCode) = execNimble("build") - check exitCode != QuitSuccess - check output.contains("entry should not be a source file: test.nim") - suite "reverse dependencies": test "basic test": cd "revdep/mydep": @@ -763,9 +687,9 @@ suite "path command": test "can get correct path for srcDir (#531)": check execNimble("uninstall", "srcdirtest", "-y").exitCode == QuitSuccess cd "develop/srcdirtest": - let (_, exitCode) = execNimble("install", "-y") + let (output, exitCode) = execNimble("install", "-y") check exitCode == QuitSuccess - let (output, _) = execNimble("path", "srcdirtest") + let (output, exitCode) = execNimble("path", "srcdirtest") check output.strip() == installDir / "pkgs" / "srcdirtest-1.0" suite "test command": @@ -869,177 +793,3 @@ suite "Module tests": test "cli": cd "..": check execCmdEx("nim c -r src/nimblepkg/cli").exitCode == QuitSuccess - - test "download": - cd "..": - check execCmdEx("nim c -r src/nimblepkg/download").exitCode == QuitSuccess - -test "init does not overwrite existing files (#581)": - createDir("issue581/src") - cd "issue581": - const Src = "echo \"OK\"" - writeFile("src/issue581.nim", Src) - check execNimbleYes("init").exitCode == QuitSuccess - check readFile("src/issue581.nim") == Src - removeDir("issue581") - -test "remove skips packages with revDeps (#504)": - check execNimble("install", "nimboost@0.5.5", "nimfp@0.4.4", "-y").exitCode == QuitSuccess - - var (output, exitCode) = execNimble("uninstall", "nimboost", "nimfp", "-n") - var lines = output.strip.processOutput() - check inLines(lines, "Cannot uninstall nimboost") - - (output, exitCode) = execNimble("uninstall", "nimfp", "nimboost", "-y") - lines = output.strip.processOutput() - check (not inLines(lines, "Cannot uninstall nimboost")) - - check execNimble("path", "nimboost").exitCode != QuitSuccess - check execNimble("path", "nimfp").exitCode != QuitSuccess - -test "pass options to the compiler with `nimble install`": - cd "passNimFlags": - check execNimble("install", "--passNim:-d:passNimIsWorking").exitCode == QuitSuccess - -test "do not install single dependency multiple times (#678)": - # for the test to be correct, the tested package and its dependencies must not - # exist in the local cache - removeDir("nimbleDir") - cd "issue678": - testRefresh(): - writeFile(configFile, """ - [PackageList] - name = "local" - path = "$1" - """.unindent % (getCurrentDir() / "packages.json").replace("\\", "\\\\")) - check execNimble(["refresh"]).exitCode == QuitSuccess - let (output, exitCode) = execNimble("install", "-y") - check exitCode == QuitSuccess - let index = output.find("issue678_dependency_1@0.1.0 already exists") - check index == stringNotFound - -test "Passing command line arguments to a task (#633)": - cd "issue633": - var (output, exitCode) = execNimble("testTask --testTask") - check exitCode == QuitSuccess - check output.contains("Got it") - -suite "nimble run": - test "Invalid binary": - cd "run": - var (output, exitCode) = execNimble( - "--debug", # Flag to enable debug verbosity in Nimble - "run", # Run command invokation - "blahblah", # The command to run - ) - check exitCode == QuitFailure - check output.contains("Binary '$1' is not defined in 'run' package." % - "blahblah".changeFileExt(ExeExt)) - - test "Parameters passed to executable": - cd "run": - var (output, exitCode) = execNimble( - "--debug", # Flag to enable debug verbosity in Nimble - "run", # Run command invokation - "run", # The command to run - "--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 "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") - check output.contains("0.1.0") - check exitCode == QuitSuccess - - var (output2, exitCode2) = execNimble("run", "nimbleVersionDefine") - check output2.contains("0.1.0") - check exitCode2 == QuitSuccess - -test "issue 432": - cd "issue432": - check execNimble("install", "-y", "--depsOnly").exitCode == QuitSuccess - check execNimble("install", "-y", "--depsOnly").exitCode == QuitSuccess - -test "compilation without warnings": - const buildDir = "./buildDir/" - const filesToBuild = [ - "../src/nimble.nim", - "../src/nimblepkg/nimscriptapi.nim", - "./tester.nim", - ] - - proc execBuild(fileName: string): tuple[output: string, exitCode: int] = - result = execCmdEx( - fmt"nim c -o:{buildDir/fileName.splitFile.name} {fileName}") - - proc checkOutput(output: string): uint = - const warningsToCheck = [ - "[UnusedImport]", - "[Deprecated]", - "[XDeclaredButNotUsed]", - ] - - for line in output.splitLines(): - for warning in warningsToCheck: - if line.find(warning) != stringNotFound: - once: checkpoint("Detected warnings:") - checkpoint(line) - inc(result) - - removeDir(buildDir) - - var linesWithWarningsCount: uint = 0 - for file in filesToBuild: - let (output, exitCode) = execBuild(file) - check exitCode == QuitSuccess - linesWithWarningsCount += checkOutput(output) - check linesWithWarningsCount == 0