diff --git a/changelog.markdown b/changelog.markdown index afac2b54..75ee2880 100644 --- a/changelog.markdown +++ b/changelog.markdown @@ -11,6 +11,7 @@ This is a major release containing four new features: - Support for lock files. - Download tarballs when downloading packages from GitHub. - A setup command. +- Added a `--package, -p` command line option. ## 0.13.0 diff --git a/readme.markdown b/readme.markdown index e75ef32c..bbceffd8 100644 --- a/readme.markdown +++ b/readme.markdown @@ -427,7 +427,12 @@ files. The `run` command can be used to build and run any binary specified in your package's `bin` list. The binary needs to be specified after any compilation flags if there are several binaries defined. Any flags after the binary or `--` -are passed to the binary when it is run. +are passed to the binary when it is run. It is possible to run a binary from some +dependency package. To do this pass the `--package, -p` option to Nimble. For example: + +``` +nimble --package:foo run bar +``` ### nimble c diff --git a/src/nimble.nim b/src/nimble.nim index 3f04339c..5991452b 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -703,14 +703,18 @@ proc getDependenciesPaths(pkgInfo: PackageInfo, options: Options): let deps = pkgInfo.processAllDependencies(options) return deps.map(dep => dep.getRealDir()) -proc build(options: Options) = - let dir = getCurrentDir() - let pkgInfo = getPkgInfo(dir, options) +proc build(pkgInfo: PackageInfo, options: Options) = + ## Builds the package `pkgInfo`. nimScriptHint(pkgInfo) let paths = pkgInfo.getDependenciesPaths(options) var args = options.getCompilationFlags() buildFromDir(pkgInfo, paths, args, options) +proc build(options: Options) = + let dir = getCurrentDir() + let pkgInfo = getPkgInfo(dir, options) + pkgInfo.build(options) + proc clean(options: Options) = let dir = getCurrentDir() let pkgInfo = getPkgInfo(dir, options) @@ -1846,21 +1850,40 @@ proc setup(options: Options) = setupNimbleConfig(options) setupVcsIgnoreFile() +proc getPackageForAction(pkgInfo: PackageInfo, options: Options): PackageInfo = + ## Returns the `PackageInfo` for the package in `pkgInfo`'s dependencies tree + ## with the name specified in `options.package`. If `options.package` is empty + ## or it matches the name of the `pkgInfo` then `pkgInfo` is returned. Raises + ## a `NimbleError` if the package with the provided name is not found. + + result = initPackageInfo() + + if options.package.len == 0 or pkgInfo.basicInfo.name == options.package: + return pkgInfo + + let deps = pkgInfo.processAllDependencies(options) + for dep in deps: + if dep.basicInfo.name == options.package: + return dep.toFullInfo(options) + + raise nimbleError(notFoundPkgWithNameInPkgDepTree(options.package)) + proc run(options: Options) = - # Verify parameters. var pkgInfo = getPkgInfo(getCurrentDir(), options) + pkgInfo = getPackageForAction(pkgInfo, options) let binary = options.getCompilationBinary(pkgInfo).get("") if binary.len == 0: raise nimbleError("Please specify a binary to run") if binary notin pkgInfo.bin: - raise nimbleError( - "Binary '$#' is not defined in '$#' package." % [binary, pkgInfo.basicInfo.name] - ) + raise nimbleError(binaryNotDefinedInPkgMsg(binary, pkgInfo.basicInfo.name)) - # Build the binary. - build(options) + if pkgInfo.isLink: + # If this is not installed package then build the binary. + pkgInfo.build(options) + elif options.getCompilationFlags.len > 0: + displayWarning(ignoringCompilationFlagsMsg) let binaryPath = pkgInfo.getOutputDir(binary) let cmd = quoteShellCommand(binaryPath & options.action.runFlags) diff --git a/src/nimblepkg/displaymessages.nim b/src/nimblepkg/displaymessages.nim index d6d6c3f1..46e6d27e 100644 --- a/src/nimblepkg/displaymessages.nim +++ b/src/nimblepkg/displaymessages.nim @@ -34,6 +34,9 @@ const multipleDevelopFileOptionsGivenMsg* = "Multiple develop file options are given." + ignoringCompilationFlagsMsg* = + "Ignoring compilation flags for installed package." + updatingTheLockFileMsg* = "Updating the lock file..." generatingTheLockFileMsg* = "Generating the lock file..." lockFileIsUpdatedMsg* = "The lock file is updated." @@ -158,5 +161,12 @@ proc skipDownloadingInAlreadyExistingDirectoryMsg*(dir, name: string): string = &"The download directory \"{dir}\" already exists.\n" & &"Skipping the download of \"{name}\"." +proc binaryNotDefinedInPkgMsg*(binaryName, pkgName: string): string = + &"Binary '{binaryName}' is not defined in '{pkgName}' package." + +proc notFoundPkgWithNameInPkgDepTree*(pkgName: string): string = + &"Not found package with name '{pkgName}' in the current package's " & + "dependency tree." + proc pkgLinkFileSavedMsg*(path: string): string = &"Package link file \"{path}\" is saved." diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index 60196e58..7e2193c1 100644 --- a/src/nimblepkg/options.nim +++ b/src/nimblepkg/options.nim @@ -43,6 +43,10 @@ type developLocaldeps*: bool # True if local deps + nimble develop pkg1 ... disableSslCertCheck*: bool enableTarballs*: bool # Enable downloading of packages as tarballs from GitHub. + package*: string + # For which package in the dependency tree the command should be executed. + # If not provided by default it applies to the current directory package. + # For now, it is used only by the run action and it is ignored by others. ActionType* = enum actionNil, actionRefresh, actionInit, actionDump, actionPublish, @@ -195,7 +199,12 @@ Nimble Options: -v, --version Print version information. -y, --accept Accept all interactive prompts. -n, --reject Reject all interactive prompts. - -l, --localdeps Run in project local dependency mode + -l, --localdeps Run in project local dependency mode. + -p, --package For which package in the dependency tree the + command should be executed. If not provided by + default it applies to the current directory + package. For now, it is used only by the run + action and it is ignored by others. -t, --tarballs Enable downloading of packages as tarballs when working with GitHub repositories. --ver Query remote server for package version @@ -494,6 +503,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = of "localdeps", "l": result.localdeps = true of "nosslcheck": result.disableSslCertCheck = true of "tarballs", "t": result.enableTarballs = true + of "package", "p": result.package = val else: isGlobalFlag = false var wasFlagHandled = true diff --git a/tests/runDependencyBinary/dependency/binary.nim b/tests/runDependencyBinary/dependency/binary.nim new file mode 100644 index 00000000..5ce89c98 --- /dev/null +++ b/tests/runDependencyBinary/dependency/binary.nim @@ -0,0 +1,4 @@ +when isMainModule: + import os + for i in 1 .. paramCount(): + echo paramStr(i) diff --git a/tests/runDependencyBinary/dependency/dependency.nim b/tests/runDependencyBinary/dependency/dependency.nim new file mode 100644 index 00000000..6befe192 --- /dev/null +++ b/tests/runDependencyBinary/dependency/dependency.nim @@ -0,0 +1,2 @@ +proc foo* = + echo "Hello from dependency.foo" diff --git a/tests/runDependencyBinary/dependency/dependency.nimble b/tests/runDependencyBinary/dependency/dependency.nimble new file mode 100644 index 00000000..1929e2be --- /dev/null +++ b/tests/runDependencyBinary/dependency/dependency.nimble @@ -0,0 +1,8 @@ +version = "0.1.0" +author = "Ivan Bobev" +description = "Test package." +license = "MIT" + +bin = @["binary"] + +requires "nim >= 1.5.1" diff --git a/tests/runDependencyBinary/dependent/dependent.nimble b/tests/runDependencyBinary/dependent/dependent.nimble new file mode 100644 index 00000000..dfcccf5c --- /dev/null +++ b/tests/runDependencyBinary/dependent/dependent.nimble @@ -0,0 +1,6 @@ +version = "0.1.0" +author = "Ivan Bobev" +description = "Test package." +license = "MIT" + +requires "nim >= 1.5.1", "dependency" diff --git a/tests/truncommand.nim b/tests/truncommand.nim index 8a613576..14477880 100644 --- a/tests/truncommand.nim +++ b/tests/truncommand.nim @@ -3,9 +3,11 @@ {.used.} -import unittest, os, strutils +import unittest, os, strutils, strformat import testscommon +import nimblepkg/displaymessages from nimblepkg/common import cd +from nimblepkg/developfile import developFileName suite "nimble run": test "Invalid binary": @@ -162,3 +164,72 @@ suite "nimble run": [$DirSep, "run".changeFileExt(ExeExt)]) check output.contains("""Testing `nimble run`: @["--test"]""") check output.contains("""Whee!""") + + const + testPkgsPath = "runDependencyBinary" + dependencyPkgPath = testPkgsPath / "dependency" + dependentPkgPath = testPkgsPath / "dependent" + dependencyPkgBinary = dependencyPkgPath / "binary".addFileExt(ExeExt) + dependentPkgDevelopFile = dependentPkgPath / developFileName + packagesFilePath = "develop/packages.json" + + test "Run binary from dependency in Nimble cache": + cleanDir installDir + cleanFile dependencyPkgBinary + usePackageListFile(packagesFilePath): + cd dependencyPkgPath: + let (_, exitCode) = execNimble("install") + check exitCode == QuitSuccess + cd dependentPkgPath: + let (output, exitCode) = execNimble("--package:dependency", "run", + "-d:danger", "binary", "--arg1", "--arg2") + check exitCode == QuitSuccess + var lines = output.processOutput + check lines.inLinesOrdered(ignoringCompilationFlagsMsg) + check lines.inLinesOrdered("--arg1") + check lines.inLinesOrdered("--arg2") + + test "Run binary from develop mode dependency": + cleanDir installDir + cleanFiles dependencyPkgBinary, dependentPkgDevelopFile + usePackageListFile(packagesFilePath): + cd dependentPkgPath: + var (output, exitCode) = execNimble("develop", "-a:../dependency") + check exitCode == QuitSuccess + (output, exitCode) = execNimble("--package:dependency", "run", + "-d:danger", "binary", "--arg1", "--arg2") + check exitCode == QuitSuccess + var lines = output.processOutput + const binaryName = when defined(windows): "binary.exe" else: "binary" + check lines.inLinesOrdered( + &"Building dependency/{binaryName} using c backend") + check lines.inLinesOrdered("--arg1") + check lines.inLinesOrdered("--arg2") + + test "Error when specified package does not exist": + cleanDir installDir + cleanFile dependencyPkgBinary + usePackageListFile(packagesFilePath): + cd dependencyPkgPath: + let (_, exitCode) = execNimble("install") + check exitCode == QuitSuccess + cd dependentPkgPath: + let (output, exitCode) = execNimble("--package:dep", "run", + "-d:danger", "binary", "--arg1", "--arg2") + check exitCode == QuitFailure + check output.contains(notFoundPkgWithNameInPkgDepTree("dep")) + + test "Error when specified binary does not exist in specified package": + cleanDir installDir + cleanFile dependencyPkgBinary + usePackageListFile(packagesFilePath): + cd dependencyPkgPath: + let (_, exitCode) = execNimble("install") + check exitCode == QuitSuccess + cd dependentPkgPath: + let (output, exitCode) = execNimble("--package:dependency", "run", + "-d:danger", "bin", "--arg1", "--arg2") + check exitCode == QuitFailure + const binaryName = when defined(windows): "bin.exe" else: "bin" + check output.contains(binaryNotDefinedInPkgMsg( + binaryName, "dependency"))