From 8323bfa529cc16dfe1d526728edd1309264e0b21 Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Thu, 23 Dec 2021 20:04:48 +0200 Subject: [PATCH 1/4] Add possibility to run executables from dependency packages Nimble's run command is extended to be able to run executables not only from the current package but also from some of its dependencies. The package from which we want to run an executable is specified with a newly added to Nimble `--package` command-line option. There are two cases: - For installed in the Nimble cache packages the build step is skipped and the installed package binary is being run. - For develop mode packages first the package's binary is being built with provided compilation flags and the package is being run. --- src/nimble.nim | 41 +++++++++--- src/nimblepkg/displaymessages.nim | 10 +++ src/nimblepkg/options.nim | 12 +++- .../runDependencyBinary/dependency/binary.nim | 4 ++ .../dependency/dependency.nim | 2 + .../dependency/dependency.nimble | 8 +++ .../dependent/dependent.nimble | 6 ++ tests/truncommand.nim | 67 +++++++++++++++++++ 8 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 tests/runDependencyBinary/dependency/binary.nim create mode 100644 tests/runDependencyBinary/dependency/dependency.nim create mode 100644 tests/runDependencyBinary/dependency/dependency.nimble create mode 100644 tests/runDependencyBinary/dependent/dependent.nimble diff --git a/src/nimble.nim b/src/nimble.nim index 16625021..417c511d 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) @@ -1829,21 +1833,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 046b5321..00e624b5 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." @@ -157,3 +160,10 @@ proc pkgAlreadyExistsInTheCacheMsg*(pkgInfo: PackageInfo): string = 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." diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index 5cb56e3c..769f846a 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, @@ -189,7 +193,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 @@ -485,6 +494,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..73ababab --- /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.7.1" diff --git a/tests/runDependencyBinary/dependent/dependent.nimble b/tests/runDependencyBinary/dependent/dependent.nimble new file mode 100644 index 00000000..41d6c76f --- /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.7.1", "dependency" diff --git a/tests/truncommand.nim b/tests/truncommand.nim index 8a613576..d7484a0c 100644 --- a/tests/truncommand.nim +++ b/tests/truncommand.nim @@ -5,7 +5,9 @@ import unittest, os, strutils import testscommon +import nimblepkg/displaymessages from nimblepkg/common import cd +from nimblepkg/developfile import developFileName suite "nimble run": test "Invalid binary": @@ -162,3 +164,68 @@ 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 + check lines.inLinesOrdered("Building dependency/binary 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 + check output.contains(binaryNotDefinedInPkgMsg("bin", "dependency")) From eeb1a4ad48dea7c4e13e12e4d5065137124b3342 Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Thu, 23 Dec 2021 20:31:01 +0200 Subject: [PATCH 2/4] Update the change log and the documentation --- changelog.markdown | 1 + readme.markdown | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) 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 f6e1707b..6432d8dc 100644 --- a/readme.markdown +++ b/readme.markdown @@ -436,7 +436,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 From 2ae979915e25abc226a3b5f027e2a1dc5e74784f Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Wed, 5 Jan 2022 20:11:51 +0200 Subject: [PATCH 3/4] Set older required version of Nim for the test packages The new test packages for the "run dependency" feature were required Nim version 1.7.1 but the one used by the CI is 1.5.1. Now the version is changed for CI tests to pass. --- tests/runDependencyBinary/dependency/dependency.nimble | 2 +- tests/runDependencyBinary/dependent/dependent.nimble | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/runDependencyBinary/dependency/dependency.nimble b/tests/runDependencyBinary/dependency/dependency.nimble index 73ababab..1929e2be 100644 --- a/tests/runDependencyBinary/dependency/dependency.nimble +++ b/tests/runDependencyBinary/dependency/dependency.nimble @@ -5,4 +5,4 @@ license = "MIT" bin = @["binary"] -requires "nim >= 1.7.1" +requires "nim >= 1.5.1" diff --git a/tests/runDependencyBinary/dependent/dependent.nimble b/tests/runDependencyBinary/dependent/dependent.nimble index 41d6c76f..dfcccf5c 100644 --- a/tests/runDependencyBinary/dependent/dependent.nimble +++ b/tests/runDependencyBinary/dependent/dependent.nimble @@ -3,4 +3,4 @@ author = "Ivan Bobev" description = "Test package." license = "MIT" -requires "nim >= 1.7.1", "dependency" +requires "nim >= 1.5.1", "dependency" From 5833669cda0f72c7a7e8494dc14c4103a671a485 Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Thu, 6 Jan 2022 17:51:49 +0200 Subject: [PATCH 4/4] Fix the tests on Windows The name of the executable file on Windows must end with ".exe". --- tests/truncommand.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/truncommand.nim b/tests/truncommand.nim index d7484a0c..14477880 100644 --- a/tests/truncommand.nim +++ b/tests/truncommand.nim @@ -3,7 +3,7 @@ {.used.} -import unittest, os, strutils +import unittest, os, strutils, strformat import testscommon import nimblepkg/displaymessages from nimblepkg/common import cd @@ -200,7 +200,9 @@ suite "nimble run": "-d:danger", "binary", "--arg1", "--arg2") check exitCode == QuitSuccess var lines = output.processOutput - check lines.inLinesOrdered("Building dependency/binary using c backend") + 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") @@ -228,4 +230,6 @@ suite "nimble run": let (output, exitCode) = execNimble("--package:dependency", "run", "-d:danger", "bin", "--arg1", "--arg2") check exitCode == QuitFailure - check output.contains(binaryNotDefinedInPkgMsg("bin", "dependency")) + const binaryName = when defined(windows): "bin.exe" else: "bin" + check output.contains(binaryNotDefinedInPkgMsg( + binaryName, "dependency"))