diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..caba05c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,73 @@ +# Cross platform tests for DUB + +name: Testsuite + +# Only triggers on pushes to master & stable, as well as PR to master and stable +# Sometimes reverts appear in the upstream repository (e.g. when the revert button +# is clicked by a contributor with commit access), this should be tested as PR). +# +# Also note that Github actions does not retrigger on target branch changes, +# hence the check on push. +on: + pull_request: + branches: + - master + - stable + push: + branches: + - master + - stable + # Use this branch name in your fork to test changes + - github-actions + +jobs: + main: + name: Run + strategy: + # Default, disable if you want to debug + fail-fast: false + matrix: + # Latest stable version, update at will + os: [ macOS-10.15, ubuntu-18.04, windows-2019 ] + dc: [ dmd-2.092.0, ldc-1.21.0, dmd-master, ldc-master ] + + runs-on: ${{ matrix.os }} + steps: + + # Install required dependencies + - name: '[OSX] Install dependencies' + if: runner.os == 'macOS' + run: | + brew install pkg-config coreutils + + - name: '[Linux] Install dependencies' + if: runner.os == 'Linux' + run: | + sudo apt-get install -y libcurl4-openssl-dev netcat + + # Compiler to test with + - name: Prepare compiler + uses: mihails-strasuns/setup-dlang@v1 + with: + compiler: ${{ matrix.dc }} + + # Checkout the repository + - name: Checkout + uses: actions/checkout@v2 + + - name: '[POSIX] Test' + if: runner.os != 'Windows' + env: + COVERAGE: false + # The value doesn't matter as long as it's > 2.087 + FRONTEND: 2.091.0 + run: | + ./scripts/ci/travis.sh + + - name: '[Windows] Test' + if: runner.os == 'Windows' + # Only run `dub test` to run unittests so far, + # the test-suite needs to be overhauled to support Windows + run: | + dub build --compiler=${{ env.DC }} + dub test --compiler=${{ env.DC }} diff --git a/.gitignore b/.gitignore index 250273f..bc0b97f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ # Ignore auto-generated docs /docs +scripts/man/dub*.1.gz diff --git a/README.md b/README.md index 11b8dfe..fbad53d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ There is a central [package registry](https://github.com/dlang/dub-registry/) located at . -[![GitHub tag](https://img.shields.io/github/tag/dlang/dub.svg?maxAge=86400)](#) [![Travis](https://travis-ci.org/dlang/dub.svg?branch=master)](https://travis-ci.org/dlang/dub) [![Coverage Status](https://coveralls.io/repos/dlang/dub/badge.svg)](https://coveralls.io/r/dlang/dub) +[![GitHub tag](https://img.shields.io/github/tag/dlang/dub.svg?maxAge=86400)](#) [![Travis](https://travis-ci.com/dlang/dub.svg?branch=master)](https://travis-ci.com/dlang/dub) [![Coverage Status](https://coveralls.io/repos/dlang/dub/badge.svg)](https://coveralls.io/r/dlang/dub) [![Buildkite](https://badge.buildkite.com/c54d71c42284a042b9d578e28e093dff35f20cc8528319b1b6.svg?branch=master)](https://buildkite.com/dlang/dub) ## Introduction diff --git a/changelog/dub_root_package_target_env_vars_added.dd b/changelog/dub_root_package_target_env_vars_added.dd new file mode 100644 index 0000000..f0769c3 --- /dev/null +++ b/changelog/dub_root_package_target_env_vars_added.dd @@ -0,0 +1,4 @@ +Dub root package target environment variables added + +Environment variables `DUB_ROOT_PACKAGE_TARGET_TYPE`, `DUB_ROOT_PACKAGE_TARGET_PATH` +and `DUB_ROOT_PACKAGE_TARGET_NAME` have been added and exposed. diff --git a/changelog/improved_cli_extensibility.dd b/changelog/improved_cli_extensibility.dd new file mode 100644 index 0000000..2f9b7cf --- /dev/null +++ b/changelog/improved_cli_extensibility.dd @@ -0,0 +1,9 @@ +Improved CLI extensibility + +The code from the runDubCommandLine was moved to some other structs. The new +CommandLineHandler structure takes care of the CLI state and commands, which +allows DUB commands to be extended in other projects. + +The CommandArgs class now has new `hasAppArgs` and `appArgs` methods which +allows you to get the app arguments. In addition, the `extractAllRemainingArgs` +method will return all unused arguments and the app arguments. diff --git a/source/dub/commandline.d b/source/dub/commandline.d index c07b37b..666b79d 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -28,6 +28,7 @@ import std.exception; import std.file; import std.getopt; +import std.path : absolutePath, buildNormalizedPath; import std.process; import std.stdio; import std.string; @@ -39,7 +40,7 @@ Commands are grouped by category. */ -CommandGroup[] getCommands() +CommandGroup[] getCommands() @safe pure nothrow { return [ CommandGroup("Package creation", @@ -77,6 +78,307 @@ ]; } +/** Extract the command name from the argument list + + Params: + args = a list of string arguments that will be processed + + Returns: + A structure with two members. `value` is the command name + `remaining` is a list of unprocessed arguments +*/ +auto extractCommandNameArgument(string[] args) +{ + struct Result { + string value; + string[] remaining; + } + + if (args.length >= 1 && !args[0].startsWith("-")) { + return Result(args[0], args[1 .. $]); + } + + return Result(null, args); +} + +/// test extractCommandNameArgument usage +unittest { + /// It returns an empty string on when there are no args + assert(extractCommandNameArgument([]).value == ""); + assert(extractCommandNameArgument([]).remaining == []); + + /// It returns the first argument when it does not start with `-` + assert(extractCommandNameArgument(["test"]).value == "test"); + + /// There is nothing to extract when the arguments only contain the `test` cmd + assert(extractCommandNameArgument(["test"]).remaining == []); + + /// It extracts two arguments when they are not a command + assert(extractCommandNameArgument(["-a", "-b"]).remaining == ["-a", "-b"]); + + /// It returns the an empty string when it starts with `-` + assert(extractCommandNameArgument(["-test"]).value == ""); +} + +/** Handles the Command Line options and commands. +*/ +struct CommandLineHandler +{ + /// The list of commands that can be handled + CommandGroup[] commandGroups; + + /// General options parser + CommonOptions options; + + /** Create the list of all supported commands + + Returns: + Returns the list of the supported command names + */ + string[] commandNames() + { + return commandGroups.map!(g => g.commands.map!(c => c.name).array).join; + } + + /** Parses the general options and sets up the log level + and the root_path + */ + void prepareOptions(CommandArgs args) { + LogLevel loglevel = LogLevel.info; + + options.prepare(args); + + if (options.vverbose) loglevel = LogLevel.debug_; + else if (options.verbose) loglevel = LogLevel.diagnostic; + else if (options.vquiet) loglevel = LogLevel.none; + else if (options.quiet) loglevel = LogLevel.warn; + else if (options.verror) loglevel = LogLevel.error; + setLogLevel(loglevel); + + if (options.root_path.empty) + { + options.root_path = getcwd(); + } + else + { + options.root_path = options.root_path.absolutePath.buildNormalizedPath; + } + } + + /** Get an instance of the requested command. + + If there is no command in the argument list, the `run` command is returned + by default. + + If the `--help` argument previously handled by `prepareOptions`, + `this.options.help` is already `true`, with this returning the requested + command. If no command was requested (just dub --help) this returns the + help command. + + Params: + name = the command name + + Returns: + Returns the command instance if it exists, null otherwise + */ + Command getCommand(string name) { + if (name == "help" || (name == "" && options.help)) + { + return new HelpCommand(); + } + + if (name == "") + { + name = "run"; + } + + foreach (grp; commandGroups) + foreach (c; grp.commands) + if (c.name == name) { + return c; + } + + return null; + } + + /** Get an instance of the requested command after the args are sent. + + It uses getCommand to get the command instance and then calls prepare. + + Params: + name = the command name + args = the command arguments + + Returns: + Returns the command instance if it exists, null otherwise + */ + Command prepareCommand(string name, CommandArgs args) { + auto cmd = getCommand(name); + + if (cmd !is null && !(cast(HelpCommand)cmd)) + { + // process command line options for the selected command + cmd.prepare(args); + enforceUsage(cmd.acceptsAppArgs || !args.hasAppArgs, name ~ " doesn't accept application arguments."); + } + + return cmd; + } + + /** Get a configured dub instance. + + Returns: + A dub instance + */ + Dub prepareDub() { + Dub dub; + + if (options.bare) { + dub = new Dub(NativePath(getcwd())); + dub.rootPath = NativePath(options.root_path); + dub.defaultPlacementLocation = options.placementLocation; + + return dub; + } + + // initialize DUB + auto package_suppliers = options.registry_urls + .map!((url) { + // Allow to specify fallback mirrors as space separated urls. Undocumented as we + // should simply retry over all registries instead of using a special + // FallbackPackageSupplier. + auto urls = url.splitter(' '); + PackageSupplier ps = getRegistryPackageSupplier(urls.front); + urls.popFront; + if (!urls.empty) + ps = new FallbackPackageSupplier(ps ~ urls.map!getRegistryPackageSupplier.array); + return ps; + }) + .array; + + dub = new Dub(options.root_path, package_suppliers, options.skipRegistry); + dub.dryRun = options.annotate; + dub.defaultPlacementLocation = options.placementLocation; + + // make the CWD package available so that for example sub packages can reference their + // parent package. + try dub.packageManager.getOrLoadPackage(NativePath(options.root_path)); + catch (Exception e) { logDiagnostic("No valid package found in current working directory: %s", e.msg); } + + return dub; + } +} + +/// Can get the command names +unittest { + CommandLineHandler handler; + handler.commandGroups = getCommands(); + + assert(handler.commandNames == ["init", "run", "build", "test", "lint", "generate", + "describe", "clean", "dustmite", "fetch", "install", "add", "remove", "uninstall", + "upgrade", "add-path", "remove-path", "add-local", "remove-local", "list", "search", + "add-override", "remove-override", "list-overrides", "clean-caches", "convert"]); +} + +/// It sets the cwd as root_path by default +unittest { + CommandLineHandler handler; + + auto args = new CommandArgs([]); + handler.prepareOptions(args); + assert(handler.options.root_path == getcwd()); +} + +/// It can set a custom root_path +unittest { + CommandLineHandler handler; + + auto args = new CommandArgs(["--root=/tmp/test"]); + handler.prepareOptions(args); + assert(handler.options.root_path == "/tmp/test".absolutePath.buildNormalizedPath); + + args = new CommandArgs(["--root=./test"]); + handler.prepareOptions(args); + assert(handler.options.root_path == "./test".absolutePath.buildNormalizedPath); +} + +/// It sets the info log level by default +unittest { + scope(exit) setLogLevel(LogLevel.info); + CommandLineHandler handler; + + auto args = new CommandArgs([]); + handler.prepareOptions(args); + assert(getLogLevel() == LogLevel.info); +} + +/// It can set a custom error level +unittest { + scope(exit) setLogLevel(LogLevel.info); + CommandLineHandler handler; + + auto args = new CommandArgs(["--vverbose"]); + handler.prepareOptions(args); + assert(getLogLevel() == LogLevel.debug_); + + handler = CommandLineHandler(); + args = new CommandArgs(["--verbose"]); + handler.prepareOptions(args); + assert(getLogLevel() == LogLevel.diagnostic); + + handler = CommandLineHandler(); + args = new CommandArgs(["--vquiet"]); + handler.prepareOptions(args); + assert(getLogLevel() == LogLevel.none); + + handler = CommandLineHandler(); + args = new CommandArgs(["--quiet"]); + handler.prepareOptions(args); + assert(getLogLevel() == LogLevel.warn); + + handler = CommandLineHandler(); + args = new CommandArgs(["--verror"]); + handler.prepareOptions(args); + assert(getLogLevel() == LogLevel.error); +} + +/// It returns the `run` command by default +unittest { + CommandLineHandler handler; + handler.commandGroups = getCommands(); + assert(handler.getCommand("").name == "run"); +} + +/// It returns the `help` command when there is none set and the --help arg +/// was set +unittest { + CommandLineHandler handler; + auto args = new CommandArgs(["--help"]); + handler.prepareOptions(args); + handler.commandGroups = getCommands(); + assert(cast(HelpCommand)handler.getCommand("") !is null); +} + +/// It returns the `help` command when the `help` command is sent +unittest { + CommandLineHandler handler; + handler.commandGroups = getCommands(); + assert(cast(HelpCommand) handler.getCommand("help") !is null); +} + +/// It returns the `init` command when the `init` command is sent +unittest { + CommandLineHandler handler; + handler.commandGroups = getCommands(); + assert(handler.getCommand("init").name == "init"); +} + +/// It returns null when a missing command is sent +unittest { + CommandLineHandler handler; + handler.commandGroups = getCommands(); + assert(handler.getCommand("missing") is null); +} /** Processes the given command line and executes the appropriate actions. @@ -98,6 +400,9 @@ environment["TEMP"] = environment["TEMP"].replace("/", "\\"); } + auto handler = CommandLineHandler(getCommands()); + auto commandNames = handler.commandNames(); + // special stdin syntax if (args.length >= 2 && args[1] == "-") { @@ -106,10 +411,6 @@ args = args[0] ~ [path.toNativeString()] ~ args[2..$]; } - // create the list of all supported commands - CommandGroup[] commands = getCommands(); - string[] commandNames = commands.map!(g => g.commands.map!(c => c.name).array).join.array; - // Shebang syntax support for files without .d extension if (args.length >= 2 && !args[1].endsWith(".d") && !args[1].startsWith("-") && !commandNames.canFind(args[1])) { if (exists(args[1])) { @@ -126,96 +427,32 @@ args = args[0] ~ ["run", "-q", "--temp-build", "--single", args[1], "--"] ~ args[2 ..$]; } - // split application arguments from DUB arguments - string[] app_args; - auto app_args_idx = args.countUntil("--"); - if (app_args_idx >= 0) { - app_args = args[app_args_idx+1 .. $]; - args = args[0 .. app_args_idx]; - } - args = args[1 .. $]; // strip the application name + auto common_args = new CommandArgs(args[1..$]); - // handle direct dub options - if (args.length) switch (args[0]) - { - case "--version": - showVersion(); - return 0; - - default: - break; - } - - // parse general options - CommonOptions options; - LogLevel loglevel = LogLevel.info; - - auto common_args = new CommandArgs(args); - try { - options.prepare(common_args); - - if (options.vverbose) loglevel = LogLevel.debug_; - else if (options.verbose) loglevel = LogLevel.diagnostic; - else if (options.vquiet) loglevel = LogLevel.none; - else if (options.quiet) loglevel = LogLevel.warn; - else if (options.verror) loglevel = LogLevel.error; - setLogLevel(loglevel); - } catch (Throwable e) { + try handler.prepareOptions(common_args); + catch (Throwable e) { logError("Error processing arguments: %s", e.msg); logDiagnostic("Full exception: %s", e.toString().sanitize); logInfo("Run 'dub help' for usage information."); return 1; } - if (options.root_path.empty) - options.root_path = getcwd(); - else + if (handler.options.version_) { - import std.path : absolutePath, buildNormalizedPath; - - options.root_path = options.root_path.absolutePath.buildNormalizedPath; + showVersion(); + return 0; } // extract the command - string cmdname; - args = common_args.extractRemainingArgs(); - if (args.length >= 1 && !args[0].startsWith("-")) { - cmdname = args[0]; - args = args[1 .. $]; - } else { - if (options.help) { - showHelp(commands, common_args); - return 0; - } - cmdname = "run"; - } - auto command_args = new CommandArgs(args); + args = common_args.extractAllRemainingArgs(); - if (cmdname == "help") { - showHelp(commands, common_args); - return 0; - } + auto command_name_argument = extractCommandNameArgument(args); - // find the selected command + auto command_args = new CommandArgs(command_name_argument.remaining); Command cmd; - foreach (grp; commands) - foreach (c; grp.commands) - if (c.name == cmdname) { - cmd = c; - break; - } - if (!cmd) { - logError("Unknown command: %s", cmdname); - writeln(); - showHelp(commands, common_args); - return 1; - } - - // process command line options for the selected command try { - cmd.prepare(command_args); - enforceUsage(cmd.acceptsAppArgs || app_args.length == 0, cmd.name ~ " doesn't accept application arguments."); + cmd = handler.prepareCommand(command_name_argument.value, command_args); } catch (Throwable e) { logError("Error processing arguments: %s", e.msg); logDiagnostic("Full exception: %s", e.toString().sanitize); @@ -223,7 +460,19 @@ return 1; } - if (options.help) { + if (cmd is null) { + logError("Unknown command: %s", command_name_argument.value); + writeln(); + showHelp(handler.commandGroups, common_args); + return 1; + } + + if (cast(HelpCommand)cmd !is null) { + showHelp(handler.commandGroups, common_args); + return 0; + } + + if (handler.options.help) { showCommandHelp(cmd, command_args, common_args); return 0; } @@ -231,7 +480,7 @@ auto remaining_args = command_args.extractRemainingArgs(); if (remaining_args.any!(a => a.startsWith("-"))) { logError("Unknown command line flags: %s", remaining_args.filter!(a => a.startsWith("-")).array.join(" ")); - logError(`Type "dub %s -h" to get a list of all supported flags.`, cmdname); + logError(`Type "dub %s -h" to get a list of all supported flags.`, cmd.name); return 1; } @@ -239,42 +488,15 @@ // initialize the root package if (!cmd.skipDubInitialization) { - if (options.bare) { - dub = new Dub(NativePath(getcwd())); - dub.rootPath = NativePath(options.root_path); - dub.defaultPlacementLocation = options.placementLocation; - } else { - // initialize DUB - auto package_suppliers = options.registry_urls - .map!((url) { - // Allow to specify fallback mirrors as space separated urls. Undocumented as we - // should simply retry over all registries instead of using a special - // FallbackPackageSupplier. - auto urls = url.splitter(' '); - PackageSupplier ps = getRegistryPackageSupplier(urls.front); - urls.popFront; - if (!urls.empty) - ps = new FallbackPackageSupplier(ps ~ urls.map!getRegistryPackageSupplier.array); - return ps; - }) - .array; - dub = new Dub(options.root_path, package_suppliers, options.skipRegistry); - dub.dryRun = options.annotate; - dub.defaultPlacementLocation = options.placementLocation; - - // make the CWD package available so that for example sub packages can reference their - // parent package. - try dub.packageManager.getOrLoadPackage(NativePath(options.root_path)); - catch (Exception e) { logDiagnostic("No valid package found in current working directory: %s", e.msg); } - } + dub = handler.prepareDub; } // execute the command - try return cmd.execute(dub, remaining_args, app_args); + try return cmd.execute(dub, remaining_args, command_args.appArgs); catch (UsageException e) { logError("%s", e.msg); logDebug("Full exception: %s", e.toString().sanitize); - logInfo(`Run "dub %s -h" for more information about the "%s" command.`, cmdname, cmdname); + logInfo(`Run "dub %s -h" for more information about the "%s" command.`, cmd.name, cmd.name); return 1; } catch (Throwable e) { @@ -288,7 +510,7 @@ /** Contains and parses options common to all commands. */ struct CommonOptions { - bool verbose, vverbose, quiet, vquiet, verror; + bool verbose, vverbose, quiet, vquiet, verror, version_; bool help, annotate, bare; string[] registry_urls; string root_path; @@ -320,6 +542,8 @@ args.getopt("verror", &verror, ["Only print errors"]); args.getopt("vquiet", &vquiet, ["Print no messages"]); args.getopt("cache", &placementLocation, ["Puts any fetched packages in the specified location [local|system|user]."]); + + version_ = args.hasAppVersion; } } @@ -340,6 +564,7 @@ private { string[] m_args; Arg[] m_recognizedArgs; + string[] m_appArgs; } /** Initializes the list of source arguments. @@ -347,11 +572,36 @@ Note that all array entries are considered application arguments (i.e. no application name entry is present as the first entry) */ - this(string[] args) + this(string[] args) @safe pure nothrow { - m_args = "dummy" ~ args; + auto app_args_idx = args.countUntil("--"); + + m_appArgs = app_args_idx >= 0 ? args[app_args_idx+1 .. $] : []; + m_args = "dummy" ~ (app_args_idx >= 0 ? args[0..app_args_idx] : args); } + /** Checks if the app arguments are present. + + Returns: + true if an -- argument is given with arguments after it, otherwise false + */ + @property bool hasAppArgs() { return m_appArgs.length > 0; } + + + /** Checks if the `--version` argument is present on the first position in + the list. + + Returns: + true if the application version argument was found on the first position + */ + @property bool hasAppVersion() { return m_args.length > 1 && m_args[1] == "--version"; } + + /** Returns the list of app args. + + The app args are provided after the `--` argument. + */ + @property string[] appArgs() { return m_appArgs; } + /** Returns the list of all options recognized. This list is created by recording all calls to `getopt`. @@ -383,16 +633,79 @@ m_args = null; } - /** Returns the list of unprocessed arguments and calls `dropAllArgs`. + /** Returns the list of unprocessed arguments, ignoring the app arguments, + and resets the list of available source arguments. */ string[] extractRemainingArgs() { + assert(m_args !is null, "extractRemainingArgs must be called only once."); + auto ret = m_args[1 .. $]; m_args = null; return ret; } + + /** Returns the list of unprocessed arguments, including the app arguments + and resets the list of available source arguments. + */ + string[] extractAllRemainingArgs() + { + auto ret = extractRemainingArgs(); + + if (this.hasAppArgs) + { + ret ~= "--" ~ m_appArgs; + } + + return ret; + } } +/// Using CommandArgs +unittest { + /// It should not find the app version for an empty arg list + assert(new CommandArgs([]).hasAppVersion == false); + + /// It should find the app version when `--version` is the first arg + assert(new CommandArgs(["--version"]).hasAppVersion == true); + + /// It should not find the app version when `--version` is the second arg + assert(new CommandArgs(["a", "--version"]).hasAppVersion == false); + + /// It returns an empty app arg list when `--` arg is missing + assert(new CommandArgs(["1", "2"]).appArgs == []); + + /// It returns an empty app arg list when `--` arg is missing + assert(new CommandArgs(["1", "2"]).appArgs == []); + + /// It returns app args set after "--" + assert(new CommandArgs(["1", "2", "--", "a"]).appArgs == ["a"]); + assert(new CommandArgs(["1", "2", "--"]).appArgs == []); + assert(new CommandArgs(["--"]).appArgs == []); + assert(new CommandArgs(["--", "a"]).appArgs == ["a"]); + + /// It returns the list of all args when no args are processed + assert(new CommandArgs(["1", "2", "--", "a"]).extractAllRemainingArgs == ["1", "2", "--", "a"]); +} + +/// It removes the extracted args +unittest { + auto args = new CommandArgs(["-a", "-b", "--", "-c"]); + bool value; + args.getopt("b", &value, [""]); + + assert(args.extractAllRemainingArgs == ["-a", "--", "-c"]); +} + +/// It should not be able to remove app args +unittest { + auto args = new CommandArgs(["-a", "-b", "--", "-c"]); + bool value; + args.getopt("-c", &value, [""]); + + assert(!value); + assert(args.extractAllRemainingArgs == ["-a", "-b", "--", "-c"]); +} /** Base class for all commands. @@ -463,13 +776,41 @@ /// List of commands contained inthis group Command[] commands; - this(string caption, Command[] commands...) + this(string caption, Command[] commands...) @safe pure nothrow { this.caption = caption; this.commands = commands.dup; } } +/******************************************************************************/ +/* HELP */ +/******************************************************************************/ + +class HelpCommand : Command { + + this() @safe pure nothrow + { + this.name = "help"; + this.description = "Shows the help message"; + this.helpText = [ + "Shows the help message and the supported command options." + ]; + } + + /// HelpCommand.prepare is not supposed to be called, use + /// cast(HelpCommand)this to check if help was requested before execution. + override void prepare(scope CommandArgs args) + { + assert(false, "HelpCommand.prepare is not supposed to be called, use cast(HelpCommand)this to check if help was requested before execution."); + } + + /// HelpCommand.execute is not supposed to be called, use + /// cast(HelpCommand)this to check if help was requested before execution. + override int execute(Dub dub, string[] free_args, string[] app_args) { + assert(false, "HelpCommand.execute is not supposed to be called, use cast(HelpCommand)this to check if help was requested before execution."); + } +} /******************************************************************************/ /* INIT */ @@ -481,7 +822,7 @@ PackageFormat m_format = PackageFormat.json; bool m_nonInteractive; } - this() + this() @safe pure nothrow { this.name = "init"; this.argumentsPattern = "[ [...]]"; @@ -645,7 +986,7 @@ ]); } - protected void setupPackage(Dub dub, string package_name, string default_build_type = "debug") + protected void setupPackage(Dub dub, string package_name, string default_build_type = "debug", Version ver = Version.unknown) { if (!m_compilerName.length) m_compilerName = dub.defaultCompiler; if (!m_arch.length) m_arch = dub.defaultArchitecture; @@ -654,7 +995,7 @@ m_buildSettings.addDebugVersions(m_debugVersions); m_defaultConfig = null; - enforce (loadSpecificPackage(dub, package_name), "Failed to load package."); + enforce (loadSpecificPackage(dub, package_name, ver), "Failed to load package."); if (m_buildConfig.length != 0 && !dub.configurations.canFind(m_buildConfig)) { @@ -689,7 +1030,7 @@ } } - private bool loadSpecificPackage(Dub dub, string package_name) + private bool loadSpecificPackage(Dub dub, string package_name, Version ver) { if (m_single) { enforce(package_name.length, "Missing file name of single-file package."); @@ -711,7 +1052,9 @@ enforce(package_name.length, "No valid root package found - aborting."); - auto pack = dub.packageManager.getFirstPackage(package_name); + auto pack = ver.isUnknown + ? dub.packageManager.getLatestPackage(package_name) + : dub.packageManager.getPackage(package_name, ver); enforce(pack, "Failed to find a package named '"~package_name~"' locally."); logInfo("Building package %s in %s", pack.name, pack.path.toNativeString()); dub.loadPackage(pack); @@ -731,7 +1074,7 @@ bool m_printPlatform, m_printBuilds, m_printConfigs; } - this() + this() @safe pure nothrow { this.name = "generate"; this.argumentsPattern = " []"; @@ -772,17 +1115,19 @@ override int execute(Dub dub, string[] free_args, string[] app_args) { - string package_name; + PackageAndVersion package_info; if (!m_generator.length) { enforceUsage(free_args.length >= 1 && free_args.length <= 2, "Expected one or two arguments."); m_generator = free_args[0]; - if (free_args.length >= 2) package_name = free_args[1]; + if (free_args.length >= 2) package_info = splitPackageName(free_args[1]); } else { enforceUsage(free_args.length <= 1, "Expected one or zero arguments."); - if (free_args.length >= 1) package_name = free_args[0]; + if (free_args.length >= 1) package_info = splitPackageName(free_args[0]); } - setupPackage(dub, package_name); + string package_name = package_info.name; + Version package_version = package_info.version_.length == 0 ? Version.unknown : Version(package_info.version_); + setupPackage(dub, package_name, "debug", package_version); if (m_printBuilds) { // FIXME: use actual package data logInfo("Available build types:"); @@ -828,7 +1173,7 @@ bool m_yes; // automatic yes to prompts; bool m_nonInteractive; } - this() + this() @safe pure nothrow { this.name = "build"; this.argumentsPattern = "[]"; @@ -868,7 +1213,6 @@ const packageParts = splitPackageName(free_args[0]); if (auto rc = fetchMissingPackages(dub, packageParts)) return rc; - free_args[0] = packageParts.name; } return super.execute(dub, free_args, app_args); } @@ -932,7 +1276,7 @@ } class RunCommand : BuildCommand { - this() + this() @safe pure nothrow { this.name = "run"; this.argumentsPattern = "[]"; @@ -967,7 +1311,7 @@ bool m_force = false; } - this() + this() @safe pure nothrow { this.name = "test"; this.argumentsPattern = "[]"; @@ -1023,7 +1367,7 @@ enforceUsage(free_args.length <= 1, "Expected one or zero arguments."); if (free_args.length >= 1) package_name = free_args[0]; - setupPackage(dub, package_name, "unittest"); + setupPackage(dub, package_name, "unittest", Version.unknown); GeneratorSettings settings; settings.platform = m_buildPlatform; @@ -1056,7 +1400,7 @@ string m_config; } - this() + this() @safe pure nothrow { this.name = "lint"; this.argumentsPattern = "[]"; @@ -1141,7 +1485,7 @@ string[] m_data; } - this() + this() @safe pure nothrow { this.name = "describe"; this.argumentsPattern = "[]"; @@ -1261,7 +1605,7 @@ bool m_allPackages; } - this() + this() @safe pure nothrow { this.name = "clean"; this.argumentsPattern = "[]"; @@ -1321,7 +1665,7 @@ /******************************************************************************/ class AddCommand : Command { - this() + this() @safe pure nothrow { this.name = "add"; this.argumentsPattern = "[@] []"; @@ -1365,7 +1709,7 @@ bool m_dryRun = false; } - this() + this() @safe pure nothrow { this.name = "upgrade"; this.argumentsPattern = "[]"; @@ -1436,7 +1780,7 @@ } class FetchCommand : FetchRemoveCommand { - this() + this() @safe pure nothrow { this.name = "fetch"; this.argumentsPattern = "[@]"; @@ -1497,7 +1841,11 @@ } class InstallCommand : FetchCommand { - this() { this.name = "install"; hidden = true; } + this() @safe pure nothrow + { + this.name = "install"; + this.hidden = true; + } override void prepare(scope CommandArgs args) { super.prepare(args); } override int execute(Dub dub, string[] free_args, string[] app_args) { @@ -1511,7 +1859,7 @@ bool m_nonInteractive; } - this() + this() @safe pure nothrow { this.name = "remove"; this.argumentsPattern = ""; @@ -1571,7 +1919,11 @@ } class UninstallCommand : RemoveCommand { - this() { this.name = "uninstall"; hidden = true; } + this() @safe pure nothrow + { + this.name = "uninstall"; + this.hidden = true; + } override void prepare(scope CommandArgs args) { super.prepare(args); } override int execute(Dub dub, string[] free_args, string[] app_args) { @@ -1601,7 +1953,7 @@ } class AddPathCommand : RegistrationCommand { - this() + this() @safe pure nothrow { this.name = "add-path"; this.argumentsPattern = ""; @@ -1627,7 +1979,7 @@ } class RemovePathCommand : RegistrationCommand { - this() + this() @safe pure nothrow { this.name = "remove-path"; this.argumentsPattern = ""; @@ -1644,7 +1996,7 @@ } class AddLocalCommand : RegistrationCommand { - this() + this() @safe pure nothrow { this.name = "add-local"; this.argumentsPattern = " []"; @@ -1668,7 +2020,7 @@ } class RemoveLocalCommand : RegistrationCommand { - this() + this() @safe pure nothrow { this.name = "remove-local"; this.argumentsPattern = ""; @@ -1686,7 +2038,7 @@ } class ListCommand : Command { - this() + this() @safe pure nothrow { this.name = "list"; this.argumentsPattern = ""; @@ -1709,7 +2061,7 @@ } class SearchCommand : Command { - this() + this() @safe pure nothrow { this.name = "search"; this.argumentsPattern = ""; @@ -1754,7 +2106,7 @@ bool m_system = false; } - this() + this() @safe pure nothrow { this.name = "add-override"; this.argumentsPattern = " "; @@ -1796,7 +2148,7 @@ bool m_system = false; } - this() + this() @safe pure nothrow { this.name = "remove-override"; this.argumentsPattern = " "; @@ -1823,7 +2175,7 @@ } class ListOverridesCommand : Command { - this() + this() @safe pure nothrow { this.name = "list-overrides"; this.argumentsPattern = ""; @@ -1855,7 +2207,7 @@ /******************************************************************************/ class CleanCachesCommand : Command { - this() + this() @safe pure nothrow { this.name = "clean-caches"; this.argumentsPattern = ""; @@ -1889,7 +2241,7 @@ bool m_combined; } - this() + this() @safe pure nothrow { this.name = "dustmite"; this.argumentsPattern = ""; @@ -2102,7 +2454,7 @@ bool m_stdout; } - this() + this() @safe pure nothrow { this.name = "convert"; this.argumentsPattern = ""; @@ -2358,6 +2710,7 @@ // https://github.com/dlang/dub/issues/1681 assert(splitPackageName("") == PackageAndVersion("", null)); + assert(splitPackageName("foo") == PackageAndVersion("foo", null)); assert(splitPackageName("foo=1.0.1") == PackageAndVersion("foo", "1.0.1")); assert(splitPackageName("foo@1.0.1") == PackageAndVersion("foo", "1.0.1")); assert(splitPackageName("foo@==1.0.1") == PackageAndVersion("foo", "==1.0.1")); diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index ea2a596..bb0c27b 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -80,7 +80,7 @@ return probePlatform( compiler_binary, - arch_flags ~ ["-S", "-v"], + arch_flags ~ ["-fsyntax-only", "-v"], arch_override ); } diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index 02559c4..f62e964 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -83,6 +83,8 @@ case "": break; case "x86": arch_flags = ["-march=x86"]; break; case "x86_64": arch_flags = ["-march=x86-64"]; break; + case "aarch64": arch_flags = ["-march=aarch64"]; break; + case "powerpc64": arch_flags = ["-march=powerpc64"]; break; default: if (arch_override.canFind('-')) arch_flags = ["-mtriple="~arch_override]; diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index 0809b4e..97d2985 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -812,6 +812,12 @@ env["DUB_RUN_ARGS"] = (cast(string[])settings.runArgs).map!(escapeShellFileName).join(" "); + auto cfgs = proj.getPackageConfigs(settings.platform, settings.config, true); + auto rootPackageBuildSettings = proj.rootPackage.getBuildSettings(settings.platform, cfgs[proj.rootPackage.name]); + env["DUB_ROOT_PACKAGE_TARGET_TYPE"] = to!string(rootPackageBuildSettings.targetType); + env["DUB_ROOT_PACKAGE_TARGET_PATH"] = rootPackageBuildSettings.targetPath; + env["DUB_ROOT_PACKAGE_TARGET_NAME"] = rootPackageBuildSettings.targetName; + auto depNames = proj.dependencies.map!((a) => a.name).array(); storeRecursiveInvokations(env, proj.rootPackage.name ~ depNames); runCommands(commands, env, pack.path().toString()); diff --git a/source/dub/internal/vibecompat/data/serialization.d b/source/dub/internal/vibecompat/data/serialization.d index 0f9b7c4..8216310 100644 --- a/source/dub/internal/vibecompat/data/serialization.d +++ b/source/dub/internal/vibecompat/data/serialization.d @@ -1230,7 +1230,7 @@ static struct S { int i; - ref int foo() { return i; } + ref int foo() return { return i; } } static assert(__traits(compiles, { S().serializeToJson(); })); diff --git a/source/dub/internal/vibecompat/data/utils.d b/source/dub/internal/vibecompat/data/utils.d index ace6a76..fd75584 100644 --- a/source/dub/internal/vibecompat/data/utils.d +++ b/source/dub/internal/vibecompat/data/utils.d @@ -194,7 +194,7 @@ // write-only property (NO) @property void p3(int value) { privateJ = value; } // ref returning property (OK) - @property ref int p4() { return i; } + @property ref int p4() return { return i; } // parameter-less template property (OK) @property ref int p5()() { return i; } // not treated as a property by DMD, so not a field @@ -204,7 +204,7 @@ static @property int p7() { return k; } static @property void p7(int value) { k = value; } - ref int f1() { return i; } // ref returning function (no field) + ref int f1() return { return i; } // ref returning function (no field) int f2(Args...)(Args args) { return i; } @@ -309,7 +309,7 @@ enum c = 42; void f(); static void g(); - ref int h() { return a; } + ref int h() return { return a; } static ref int i() { return b; } } static assert(isNonStaticMember!(S, "a")); diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 76c2e0c..a4a23f2 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -188,9 +188,10 @@ /// ditto Package getPackage(string name, Version ver, NativePath path) { - auto ret = getPackage(name, path); - if (!ret || ret.version_ != ver) return null; - return ret; + foreach (p; getPackageIterator(name)) + if (p.version_ == ver && p.path.startsWith(path)) + return p; + return null; } /// ditto @@ -218,6 +219,17 @@ return null; } + /** Looks up the latest package matching the given name. + */ + Package getLatestPackage(string name) + { + Package pkg; + foreach (ep; getPackageIterator(name)) + if (pkg is null || pkg.version_ < ep.version_) + pkg = ep; + return pkg; + } + /** For a given package path, returns the corresponding package. If the package is already loaded, a reference is returned. Otherwise diff --git a/test/3-copyFiles/.no_test b/test/3-copyFiles/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/3-copyFiles/.no_test diff --git a/test/4-describe-data-3-zero-delim.sh b/test/4-describe-data-3-zero-delim.sh index aee4d08..a6924ca 100755 --- a/test/4-describe-data-3-zero-delim.sh +++ b/test/4-describe-data-3-zero-delim.sh @@ -71,7 +71,7 @@ die 'Printing null-delimited list-style project data failed!' fi -if ! diff -Z "$temp_file_normal" "$temp_file_zero_delim"; then +if ! diff -b -B "$temp_file_normal" "$temp_file_zero_delim"; then die 'The null-delimited list-style project data did not match the expected output!' fi @@ -86,7 +86,7 @@ die 'Printing null-delimited --import-paths failed!' fi -if ! diff -Z -B "$temp_file_normal" "$temp_file_zero_delim"; then +if ! diff -b -B "$temp_file_normal" "$temp_file_zero_delim"; then die 'The null-delimited --import-paths data did not match the expected output!' fi @@ -107,7 +107,7 @@ die 'Printing null-delimited dmd-style --data=versions failed!' fi -if ! diff -Z "$temp_file_normal" "$temp_file_zero_delim"; then +if ! diff -b -B "$temp_file_normal" "$temp_file_zero_delim"; then die 'The null-delimited dmd-style --data=versions did not match the expected output!' fi @@ -122,6 +122,6 @@ die 'Printing null-delimited dmd-style --data=source-files failed!' fi -if ! diff -Z "$temp_file_normal" "$temp_file_zero_delim"; then +if ! diff -b -B "$temp_file_normal" "$temp_file_zero_delim"; then die 'The null-delimited dmd-style --data=source-files did not match the expected output!' fi diff --git a/test/common.sh b/test/common.sh index eba424d..054f007 100644 --- a/test/common.sh +++ b/test/common.sh @@ -19,3 +19,40 @@ exit 1 } trap 'die $LINENO' ERR + +# Get a random port for the test to use +# This isn't foolproof but should fail less than handcrafted approaches +function getRandomPort() { + # Get the PID of this script as a way to get a random port, + # and make sure the value is > 1024, as ports < 1024 are priviledged + # and require root priviledges. + # We also need to make sure the value is not > ushort.max + PORT=$(($$ % 65536)) + if [ $PORT -le 1024 ]; then + PORT=$(($PORT + 1025)) + fi + echo $PORT +} + +# Emulate GNU readlink's behavior on non-GNU readlink (e.g. MacOSX / BSD's) +# Credit to https://stackoverflow.com/a/1116890 +function gnureadlink() { + TARGET_FILE=$1 + + cd `dirname $TARGET_FILE` + TARGET_FILE=`basename $TARGET_FILE` + + # Iterate down a (possible) chain of symlinks + while [ -L "$TARGET_FILE" ] + do + TARGET_FILE=`readlink $TARGET_FILE` + cd `dirname $TARGET_FILE` + TARGET_FILE=`basename $TARGET_FILE` + done + + # Compute the canonicalized name by finding the physical path + # for the directory we're in and appending the target file. + PHYS_DIR=`pwd -P` + RESULT=$PHYS_DIR/$TARGET_FILE + echo $RESULT +} diff --git a/test/fetchzip.sh b/test/fetchzip.sh index 723e698..15b906e 100755 --- a/test/fetchzip.sh +++ b/test/fetchzip.sh @@ -3,7 +3,7 @@ . "$DIR"/common.sh -PORT=$(($$ + 1024)) # PID + 1024 +PORT=$(getRandomPort) dub remove gitcompatibledubpackage --non-interactive --version=* 2>/dev/null || true diff --git a/test/help.sh b/test/help.sh new file mode 100755 index 0000000..89cd08c --- /dev/null +++ b/test/help.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +. $(dirname "${BASH_SOURCE[0]}")/common.sh + +DUB=dub + +### It shows the general help message +if ! { ${DUB} help | grep "Manages the DUB project in the current directory."; } then + die 'DUB did not print the default help message, with the `help` command.' +fi + +if ! { ${DUB} -h | grep "Manages the DUB project in the current directory."; } then + die 'DUB did not print the default help message, with the `-h` argument.' +fi + +if ! { ${DUB} --help | grep "Manages the DUB project in the current directory."; } then + die 'DUB did not print the default help message, with the `--help` argument.' +fi + +### It shows the build command help +if ! { ${DUB} build -h | grep "Builds a package"; } then + die 'DUB did not print the build help message, with the `-h` argument.' +fi + +if ! { ${DUB} build --help | grep "Builds a package"; } then + die 'DUB did not print the build help message, with the `--help` argument.' +fi diff --git a/test/interactive-remove.sh b/test/interactive-remove.sh index d00a41c..387323d 100755 --- a/test/interactive-remove.sh +++ b/test/interactive-remove.sh @@ -2,6 +2,13 @@ . $(dirname "${BASH_SOURCE[0]}")/common.sh +# This test messes with the user's package directory +# Hence it's a pretty bad test, but we need it. +# Ideally, we should not have this run by default / run it in a container. +# In the meantime, in order to make it pass on developer's machines, +# we need to nuke every `dub` version in the user cache... +$DUB remove dub -n --version=* || true + $DUB fetch dub --version=1.9.0 && [ -d $HOME/.dub/packages/dub-1.9.0/dub ] $DUB fetch dub --version=1.10.0 && [ -d $HOME/.dub/packages/dub-1.10.0/dub ] if $DUB remove dub --non-interactive 2>/dev/null; then diff --git a/test/issue1040-run-with-ver.sh b/test/issue1040-run-with-ver.sh new file mode 100755 index 0000000..db3ea12 --- /dev/null +++ b/test/issue1040-run-with-ver.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +. $(dirname "${BASH_SOURCE[0]}")/common.sh + +if ! [ -d ${CURR_DIR}/issue1040-tmpdir ]; then + mkdir ${CURR_DIR}/issue1040-tmpdir + touch ${CURR_DIR}/issue1040-tmpdir/.no_build + touch ${CURR_DIR}/issue1040-tmpdir/.no_run + touch ${CURR_DIR}/issue1040-tmpdir/.no_test + function cleanup { + rm -rf ${CURR_DIR}/issue1040-tmpdir + } + trap cleanup EXIT +fi + +cd ${CURR_DIR}/issue1040-tmpdir + +$DUB fetch dub@1.17.0 --cache=local +$DUB fetch dub@1.18.0 --cache=local +$DUB fetch dub@1.19.0 --cache=local + +if { $DUB fetch dub@1.18.0 --cache=local || true; } | grep -cF 'Fetching' > /dev/null; then + die $LINENO 'Test for doubly fetch of the specified version has failed.' +fi +if ! { $DUB run dub -q --cache=local -- --version || true; } | grep -cF 'DUB version 1.19.0' > /dev/null; then + die $LINENO 'Test for selection of the latest fetched version has failed.' +fi +if ! { $DUB run dub@1.18.0 -q --cache=local -- --version || true; } | grep -cF 'DUB version 1.18.0' > /dev/null; then + die $LINENO 'Test for selection of the specified version has failed.' +fi diff --git a/test/issue1091-bogus-rebuild.sh b/test/issue1091-bogus-rebuild.sh index ae440eb..3198d42 100755 --- a/test/issue1091-bogus-rebuild.sh +++ b/test/issue1091-bogus-rebuild.sh @@ -4,5 +4,6 @@ cd ${CURR_DIR}/1-exec-simple rm -f dub.selections.json +${DUB} clean ${DUB} build --compiler=${DC} 2>&1 | grep -e 'building configuration' -c ${DUB} build --compiler=${DC} 2>&1 | { ! grep -e 'building configuration' -c; } diff --git a/test/issue1117-extra-dependency-files.sh b/test/issue1117-extra-dependency-files.sh index 1059dbc..7bb268c 100755 --- a/test/issue1117-extra-dependency-files.sh +++ b/test/issue1117-extra-dependency-files.sh @@ -3,6 +3,8 @@ . $(dirname "${BASH_SOURCE[0]}")/common.sh cd ${CURR_DIR}/issue1117-extra-dependency-files +# Ensure the test can be re-run +${DUB} clean if ! { ${DUB} build 2>&1 || true; } | grep -cF 'building configuration'; then die $LINENO 'Build was not executed.' diff --git a/test/issue1180-local-cache-broken.sh b/test/issue1180-local-cache-broken.sh index e46970a..0a83177 100755 --- a/test/issue1180-local-cache-broken.sh +++ b/test/issue1180-local-cache-broken.sh @@ -3,7 +3,8 @@ . "$DIR"/common.sh -PORT=$(($$ + 1024)) # PID + 1024 +PORT=$(getRandomPort) + "$DUB" remove maven-dubpackage --root="$DIR/issue1180-local-cache-broken" --non-interactive --version=* 2>/dev/null || true "$DUB" build --single "$DIR"/test_registry.d diff --git a/test/issue1416-maven-repo-pkg-supplier.sh b/test/issue1416-maven-repo-pkg-supplier.sh index 636ef31..3aca25b 100755 --- a/test/issue1416-maven-repo-pkg-supplier.sh +++ b/test/issue1416-maven-repo-pkg-supplier.sh @@ -3,7 +3,7 @@ . "$DIR"/common.sh -PORT=$(($$ + 1024)) # PID + 1024 +PORT=$(getRandomPort) dub remove maven-dubpackage --non-interactive --version=* 2>/dev/null || true diff --git a/test/issue1524-maven-upgrade-dependency-tree.sh b/test/issue1524-maven-upgrade-dependency-tree.sh index 711cded..24f5f65 100755 --- a/test/issue1524-maven-upgrade-dependency-tree.sh +++ b/test/issue1524-maven-upgrade-dependency-tree.sh @@ -3,7 +3,7 @@ . "$DIR"/common.sh -PORT=$(($$ + 1024)) # PID + 1024 +PORT=$(getRandomPort) dub remove maven-dubpackage-a --non-interactive --version=* 2>/dev/null || true dub remove maven-dubpackage-b --non-interactive --version=* 2>/dev/null || true diff --git a/test/issue1574-addcommand.sh b/test/issue1574-addcommand.sh index dddedca..94d90f1 100755 --- a/test/issue1574-addcommand.sh +++ b/test/issue1574-addcommand.sh @@ -3,7 +3,7 @@ . "$DIR"/common.sh -PORT=$(($$ + 1024)) # PID + 1024 +PORT=$(getRandomPort) tempDir="issue1574-addcommand" "$DUB" build --single "$DIR"/test_registry.d diff --git a/test/issue782-gtkd-pkg-config.sh b/test/issue782-gtkd-pkg-config.sh index a91d738..4b014ca 100755 --- a/test/issue782-gtkd-pkg-config.sh +++ b/test/issue782-gtkd-pkg-config.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash . $(dirname "${BASH_SOURCE[0]}")/common.sh -if [ "${DC}" != "dmd" ]; then + +if [ $(uname) != "Linux" ]; then + echo "Skipping issue782-dtkd-pkg-config test on non-Linux platform..." +elif [ "${DC}" != "dmd" ]; then echo "Skipping issue782-dtkd-pkg-config test for ${DC}..." else echo ${CURR_DIR-$(pwd)} diff --git a/test/issue877-auto-fetch-package-on-run.sh b/test/issue877-auto-fetch-package-on-run.sh index e46146e..b350722 100755 --- a/test/issue877-auto-fetch-package-on-run.sh +++ b/test/issue877-auto-fetch-package-on-run.sh @@ -19,7 +19,8 @@ $DUB run -y gitcompatibledubpackage | grep "Hello DUB" $DUB remove gitcompatibledubpackage -(! $DUB run --non-interactive gitcompatibledubpackage) 2>&1 | grep "Failed to find.*gitcompatibledubpackage.*locally" +(! $DUB run --non-interactive gitcompatibledubpackage || true) 2>&1 | \ + grep "Failed to find.*gitcompatibledubpackage.*locally" # check supplying versions directly dub_log="$($DUB run gitcompatibledubpackage@1.0.3)" @@ -28,7 +29,7 @@ $DUB remove gitcompatibledubpackage # check supplying an invalid version -(! $DUB run gitcompatibledubpackage@0.42.43) 2>&1 | \ +(! $DUB run gitcompatibledubpackage@0.42.43 || true) 2>&1 | \ grep 'No package gitcompatibledubpackage was found matching the dependency 0[.]42[.]43' ! $DUB remove gitcompatibledubpackage diff --git a/test/issue990-download-optional-selected.sh b/test/issue990-download-optional-selected.sh index 8b548a3..f46e4ca 100755 --- a/test/issue990-download-optional-selected.sh +++ b/test/issue990-download-optional-selected.sh @@ -2,6 +2,6 @@ . $(dirname "${BASH_SOURCE[0]}")/common.sh cd ${CURR_DIR}/issue990-download-optional-selected -rm -rf b/.dub +${DUB} clean ${DUB} remove gitcompatibledubpackage -n --version=* 2>/dev/null || true ${DUB} run diff --git a/test/run-unittest.sh b/test/run-unittest.sh index 5f27b93..b0f29a5 100755 --- a/test/run-unittest.sh +++ b/test/run-unittest.sh @@ -41,7 +41,7 @@ for script in $(ls $CURR_DIR/*.sh); do if [[ ! "$script" =~ $FILTER ]]; then continue; fi - if [ "$script" = "$(readlink -f ${BASH_SOURCE[0]})" ] || [ "$(basename $script)" = "common.sh" ]; then continue; fi + if [ "$script" = "$(gnureadlink ${BASH_SOURCE[0]})" ] || [ "$(basename $script)" = "common.sh" ]; then continue; fi if [ -e $script.min_frontend ] && [ ! -z "$FRONTEND" ] && [ ${FRONTEND} \< $(cat $script.min_frontend) ]; then continue; fi log "Running $script..." DUB=$DUB DC=$DC CURR_DIR="$CURR_DIR" $script || logError "Script failure." diff --git a/test/test_registry.d b/test/test_registry.d index f5d14c1..7b6f67f 100755 --- a/test/test_registry.d +++ b/test/test_registry.d @@ -38,7 +38,7 @@ { import std.conv; immutable folder = readRequiredOption!string("folder", "Folder to service files from."); - immutable port = readRequiredOption!uint("port", "Port to use"); + immutable port = readRequiredOption!ushort("port", "Port to use"); auto router = new URLRouter; router.get("stop", (HTTPServerRequest req, HTTPServerResponse res){ res.writeVoidBody; diff --git a/test/timeout.sh b/test/timeout.sh index 3e841c9..31209b1 100755 --- a/test/timeout.sh +++ b/test/timeout.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash +set -euo pipefail . $(dirname "${BASH_SOURCE[0]}")/common.sh -PORT=$(($$ + 1024)) # PID + 1024 +PORT=$(getRandomPort) log ' Testing unconnectable registry' if timeout 1s $DUB fetch dub --skip-registry=all --registry=http://localhost:$PORT; then