diff --git a/.github/workflows/alpine.yml b/.github/workflows/alpine.yml index e0088f9..288c9cb 100644 --- a/.github/workflows/alpine.yml +++ b/.github/workflows/alpine.yml @@ -6,6 +6,8 @@ branches: - master - stable + paths-ignore: + - 'changelog/**' push: branches: - master diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cac0e71..0fac3c3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,6 +13,8 @@ branches: - master - stable + paths-ignore: + - 'changelog/**' push: branches: - master @@ -88,14 +90,22 @@ if: runner.os == 'Windows' env: DUB: ${{ github.workspace }}\bin\dub.exe - # 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 }} if [[ ${{ matrix.do_test }} == 'true' ]]; then dub test --compiler=${{ env.DC }} dub run --compiler=${{ env.DC }} --single test/issue2051_running_unittests_from_dub_single_file_packages_fails.d - cd test - dub --single run-unittest.d + dub --single test/run-unittest.d + + # FIXME: DMD fails a few tests on Windows; remove them for now + if [[ '${{ matrix.dc }}' = dmd* ]]; then + # DLL support is lacking + rm -rf test/{1-dynLib-simple,2-dynLib-dep,2-dynLib-with-staticLib-dep} + # Unicode in paths too + rm -rf test/issue130-unicode-СНА* + # ImportC probably requires set-up MSVC environment variables + rm -rf test/use-c-sources + fi + test/run-unittest.sh fi shell: bash diff --git a/.github/workflows/pr_info_intro.yml b/.github/workflows/pr_info_intro.yml index 89db541..a38f673 100644 --- a/.github/workflows/pr_info_intro.yml +++ b/.github/workflows/pr_info_intro.yml @@ -8,6 +8,9 @@ - master - stable +permissions: + pull-requests: write + jobs: intro_comment: name: Make intro comment diff --git a/.github/workflows/pr_info_post.yml b/.github/workflows/pr_info_post.yml index c3a0914..6774e4f 100644 --- a/.github/workflows/pr_info_post.yml +++ b/.github/workflows/pr_info_post.yml @@ -6,6 +6,9 @@ types: - completed +permissions: + pull-requests: write + jobs: comment: name: PR Info diff --git a/changelog/deep.dd b/changelog/deep.dd new file mode 100644 index 0000000..ebff9c2 --- /dev/null +++ b/changelog/deep.dd @@ -0,0 +1,14 @@ +Added `--deep=` switch to dub build + +By specifying this flag, you can now build all the dependencies +of a staticLibrary. The default behavior is to only build the +library located in the root directory of the dub configuration +file. This allows better integration with other build systems +which require the libraries to be built upfront. + +``` +dub build --deep +``` + +If a staticLibrary A depends on staticLibrary B, and the --deep +flag is specified, dub will output both the A and B libraries. diff --git a/changelog/describe-configs.dd b/changelog/describe-configs.dd new file mode 100644 index 0000000..76c4c2a --- /dev/null +++ b/changelog/describe-configs.dd @@ -0,0 +1,6 @@ +Added `default-config`, `configs`, `default-build`, `builds` data to dub describe + +- `default-config` will be a single string that is the `--config` configuration that DUB would pick when not provided any configuration such as in a simple `dub build` call +- `configs` is a list of all available configurations (default generated application and/or library, or the manually specified ones in the recipe) +- `default-build` will be a single string that is the `--build` build type that DUB would pick when not provided any (currently always "debug") +- `builds` is a list of all available build types (built-in + custom defined) diff --git a/changelog/init_license.dd b/changelog/init_license.dd new file mode 100644 index 0000000..b585e60 --- /dev/null +++ b/changelog/init_license.dd @@ -0,0 +1,3 @@ +Dub init now has a select menu for package format and license + +When creating a package using `dub init` you are now prompted to select a license for the package. \ No newline at end of file diff --git a/changelog/recipefile.dd b/changelog/recipefile.dd new file mode 100644 index 0000000..2941dbc --- /dev/null +++ b/changelog/recipefile.dd @@ -0,0 +1,15 @@ +Added `--recipe=` switch to DUB + +You can now override which file is used as recipe, instead of the default +`dub.sdl` and `dub.json`. This means you can define multiple dub.json files for +local development, for example for special local-machine-only operations, and +select which one to use over the CLI. + +``` +dub build --recipe=custom-dub.json +``` + +This can also be used to pick dub.sdl over dub.json, if both of them exist in +the same directory. Although this is discouraged for interoperability with other +DUB-supporting tools and general confusion for users. Both existing at the same +time may also become an error when this switch is not specified in the future. diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 56edc9d..5bb5db0 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -501,11 +501,13 @@ return 1; } - // initialize the root package - Dub dub = cmd.prepareDub(handler.options); + try { + // initialize the root package + Dub dub = cmd.prepareDub(handler.options); - // execute the command - try return cmd.execute(dub, remaining_args, command_args.appArgs); + // execute the command + return cmd.execute(dub, remaining_args, command_args.appArgs); + } catch (UsageException e) { // usage exceptions get thrown before any logging, so we are // making the errors more narrow to better fit on small screens. @@ -534,7 +536,7 @@ bool verbose, vverbose, quiet, vquiet, verror, version_; bool help, annotate, bare; string[] registry_urls; - string root_path; + string root_path, recipeFile; enum Color { automatic, on, off } Color colorMode = Color.automatic; SkipPackageSuppliers skipRegistry = SkipPackageSuppliers.none; @@ -568,6 +570,7 @@ { args.getopt("h|help", &help, ["Display general or command specific help"]); args.getopt("root", &root_path, ["Path to operate in instead of the current working dir"]); + args.getopt("recipe", &recipeFile, ["Loads a custom recipe path instead of dub.json/dub.sdl"]); args.getopt("registry", ®istry_urls, [ "Search the given registry URL first when resolving dependencies. Can be specified multiple times. Available registry types:", " DUB: URL to DUB registry (default)", @@ -836,11 +839,25 @@ dub = new Dub(options.root_path, package_suppliers, options.skipRegistry); dub.dryRun = options.annotate; dub.defaultPlacementLocation = options.placementLocation; - + dub.mainRecipePath = options.recipeFile; // make the CWD package available so that for example sub packages can reference their // parent package. - try dub.packageManager.getOrLoadPackage(NativePath(options.root_path), NativePath.init, false, StrictMode.Warn); - catch (Exception e) { logDiagnostic("No valid package found in current working directory: %s", e.msg); } + try dub.packageManager.getOrLoadPackage(NativePath(options.root_path), NativePath(options.recipeFile), false, StrictMode.Warn); + catch (Exception e) { + // by default we ignore CWD package load fails in prepareDUB, since + // they will fail again later when they are actually requested. This + // is done to provide custom options to the loading logic and should + // ideally be moved elsewhere. (This catch has been around since 10 + // years when it was first introduced in _app.d_) + logDiagnostic("No valid package found in current working directory: %s", e.msg); + + // for now, we work around not knowing if the package is needed or + // not, simply by trusting the user to only use `--recipe` when the + // recipe file actually exists, otherwise we throw the error. + bool loadMustSucceed = options.recipeFile.length > 0; + if (loadMustSucceed) + throw e; + } return dub; } @@ -973,27 +990,190 @@ static string input(string caption, string default_value) { - writef("%s [%s]: ", caption, default_value); + writef("%s [%s]: ", caption.color(Mode.bold), default_value); stdout.flush(); auto inp = readln(); return inp.length > 1 ? inp[0 .. $-1] : default_value; } + static string select(string caption, bool free_choice, string default_value, const string[] options...) + { + assert(options.length); + import std.math : floor, log10; + auto ndigits = (size_t val) => log10(cast(double) val).floor.to!uint + 1; + + immutable default_idx = options.countUntil(default_value); + immutable max_width = options.map!(s => s.length).reduce!max + ndigits(options.length) + " ".length; + immutable num_columns = max(1, 82 / max_width); + immutable num_rows = (options.length + num_columns - 1) / num_columns; + + string[] options_matrix; + options_matrix.length = num_rows * num_columns; + foreach (i, option; options) + { + size_t y = i % num_rows; + size_t x = i / num_rows; + options_matrix[x + y * num_columns] = option; + } + + auto idx_to_user = (string option) => cast(uint)options.countUntil(option) + 1; + auto user_to_idx = (size_t i) => cast(uint)i - 1; + + assert(default_idx >= 0); + writeln((free_choice ? "Select or enter " : "Select ").color(Mode.bold), caption.color(Mode.bold), ":".color(Mode.bold)); + foreach (i, option; options_matrix) + { + if (i != 0 && (i % num_columns) == 0) writeln(); + if (!option.length) + continue; + auto user_id = idx_to_user(option); + writef("%*u)".color(Color.cyan, Mode.bold) ~ " %s", ndigits(options.length), user_id, + leftJustifier(option, max_width)); + } + writeln(); + immutable default_choice = (default_idx + 1).to!string; + while (true) + { + auto choice = input(free_choice ? "?" : "#?", default_choice); + if (choice is default_choice) + return default_value; + choice = choice.strip; + uint option_idx = uint.max; + try + option_idx = cast(uint)user_to_idx(to!uint(choice)); + catch (ConvException) + {} + if (option_idx != uint.max) + { + if (option_idx < options.length) + return options[option_idx]; + } + else if (free_choice || options.canFind(choice)) + return choice; + logError("Select an option between 1 and %u%s.", options.length, + free_choice ? " or enter a custom value" : null); + } + } + + static string license_select(string def) + { + static immutable licenses = [ + "BSL-1.0 (Boost)", + "MIT", + "Unlicense (public domain)", + "Apache-", + "-1.0", + "-1.1", + "-2.0", + "AGPL-", + "-1.0-only", + "-1.0-or-later", + "-3.0-only", + "-3.0-or-later", + "GPL-", + "-2.0-only", + "-2.0-or-later", + "-3.0-only", + "-3.0-or-later", + "LGPL-", + "-2.0-only", + "-2.0-or-later", + "-2.1-only", + "-2.1-or-later", + "-3.0-only", + "-3.0-or-later", + "BSD-", + "-1-Clause", + "-2-Clause", + "-3-Clause", + "-4-Clause", + "MPL- (Mozilla)", + "-1.0", + "-1.1", + "-2.0", + "-2.0-no-copyleft-exception", + "EUPL-", + "-1.0", + "-1.1", + "-2.0", + "CC- (Creative Commons)", + "-BY-4.0 (Attribution 4.0 International)", + "-BY-SA-4.0 (Attribution Share Alike 4.0 International)", + "Zlib", + "ISC", + "proprietary", + ]; + + static string sanitize(string license) + { + auto desc = license.countUntil(" ("); + if (desc != -1) + license = license[0 .. desc]; + return license; + } + + string[] root; + foreach (l; licenses) + if (!l.startsWith("-")) + root ~= l; + + string result; + while (true) + { + string picked; + if (result.length) + { + auto start = licenses.countUntil!(a => a == result || a.startsWith(result ~ " (")) + 1; + auto end = start; + while (end < licenses.length && licenses[end].startsWith("-")) + end++; + picked = select( + "variant of " ~ result[0 .. $ - 1], + false, + "(back)", + // https://dub.pm/package-format-json.html#licenses + licenses[start .. end].map!"a[1..$]".array ~ "(back)" + ); + if (picked == "(back)") + { + result = null; + continue; + } + picked = sanitize(picked); + } + else + { + picked = select( + "an SPDX license-identifier (" + ~ "https://spdx.org/licenses/".color(Color.light_blue, Mode.underline) + ~ ")".color(Mode.bold), + true, + def, + // https://dub.pm/package-format-json.html#licenses + root + ); + picked = sanitize(picked); + } + if (picked == def) + return def; + + if (result.length) + result ~= picked; + else + result = picked; + + if (!result.endsWith("-")) + return result; + } + } + void depCallback(ref PackageRecipe p, ref PackageFormat fmt) { import std.datetime: Clock; if (m_nonInteractive) return; - while (true) { - string rawfmt = input("Package recipe format (sdl/json)", fmt.to!string); - if (!rawfmt.length) break; - try { - fmt = rawfmt.to!PackageFormat; - break; - } catch (Exception) { - logError(`Invalid format '%s', enter either 'sdl' or 'json'.`, rawfmt); - } - } + enum free_choice = true; + fmt = select("a package recipe format", !free_choice, fmt.to!string, "sdl", "json").to!PackageFormat; auto author = p.authors.join(", "); while (true) { // Tries getting the name until a valid one is given. @@ -1009,7 +1189,7 @@ } p.description = input("Description", p.description); p.authors = input("Author name", author).split(",").map!(a => a.strip).array; - p.license = input("License", p.license); + p.license = license_select(p.license); string copyrightString = .format("Copyright © %s, %-(%s, %)", Clock.currTime().year, p.authors); p.copyright = input("Copyright string", copyrightString); @@ -1168,6 +1348,7 @@ return true; } + bool from_cwd = package_name.length == 0 || package_name.startsWith(":"); // load package in root_path to enable searching for sub packages if (loadCwdPackage(dub, from_cwd)) { @@ -1198,6 +1379,7 @@ protected { string m_generator; bool m_printPlatform, m_printBuilds, m_printConfigs; + bool m_deep; // only set in BuildCommand } this() @safe pure nothrow @@ -1272,8 +1454,10 @@ if (!gensettings.config.length) gensettings.config = m_defaultConfig; gensettings.runArgs = app_args; + gensettings.recipeName = dub.mainRecipePath; // legacy compatibility, default working directory is always CWD gensettings.overrideToolWorkingDirectory = getWorkingDirectory(); + gensettings.buildDeep = m_deep; logDiagnostic("Generating using %s", m_generator); dub.generateProject(m_generator, gensettings); @@ -1316,6 +1500,9 @@ args.getopt("n|non-interactive", &m_nonInteractive, [ "Don't enter interactive mode." ]); + args.getopt("d|deep", &m_deep, [ + "Build all dependencies, even when main target is a static library." + ]); super.prepare(args); m_generator = "build"; } diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index ece4884..e98de64 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -152,12 +152,12 @@ int status; if (output_callback) { - auto result = executeShell(escapeShellCommand(args), + auto result = execute(args, env, Config.none, size_t.max, cwd.toNativeString()); output_callback(result.status, result.output); status = result.status; } else { - auto compiler_pid = spawnShell(escapeShellCommand(args), + auto compiler_pid = spawnProcess(args, env, Config.none, cwd.toNativeString()); status = compiler_pid.wait(); } @@ -185,7 +185,7 @@ auto fil = generatePlatformProbeFile(); - auto result = executeShell(escapeShellCommand(compiler_binary ~ args ~ fil.toNativeString())); + auto result = execute(compiler_binary ~ args ~ fil.toNativeString()); enforce!CompilerInvocationException(result.status == 0, format("Failed to invoke the compiler %s to determine the build platform: %s", compiler_binary, result.output)); diff --git a/source/dub/description.d b/source/dub/description.d index fb2dc8f..c63ec8f 100644 --- a/source/dub/description.d +++ b/source/dub/description.d @@ -121,6 +121,7 @@ string[] packages; /// All packages contained in this target (e.g. for target type "sourceLibrary") string rootConfiguration; /// Build configuration of the target's root package used for building BuildSettings buildSettings; /// Final build settings to use when building the target + string cacheArtifactPath; /// The full path of the built target in the cache string[] dependencies; /// List of all dependencies of this target (package names) string[] linkDependencies; /// List of all link-dependencies of this target (target names) } diff --git a/source/dub/dub.d b/source/dub/dub.d index 87a78bc..b64a386 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -126,6 +126,7 @@ PackageManager m_packageManager; PackageSupplier[] m_packageSuppliers; NativePath m_rootPath; + string m_mainRecipePath; SpecialDirs m_dirs; Settings m_config; Project m_project; @@ -391,6 +392,14 @@ /// application. @property string projectName() const { return m_project.name; } + @property string mainRecipePath() const { return m_mainRecipePath; } + /// Whenever the switch --recipe= is supplied, this member will be populated. + @property string mainRecipePath(string recipePath) + { + return m_mainRecipePath = recipePath; + } + + @property NativePath projectPath() const { return this.m_project.rootPackage.path; } @property string[] configurations() const { return m_project.configurations; } diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index 73fe18b..7cfc7cf 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -46,8 +46,16 @@ addHash(settings.platform.architecture); addHash(settings.platform.compiler); addHash(settings.platform.compilerVersion); + if(settings.recipeName != "") + addHash(settings.recipeName); const hashstr = Base64URL.encode(hash.finish()[0 .. $ / 2]).stripRight("="); + if(settings.recipeName != "") + { + import std.path:stripExtension, baseName; + string recipeName = settings.recipeName.baseName.stripExtension; + return format("%s-%s-%s-%s", config, settings.buildType, recipeName, hashstr); + } return format("%s-%s-%s", config, settings.buildType, hashstr); } @@ -89,6 +97,17 @@ settings.buildType.color(Color.magenta), settings.platform.compilerBinary, settings.platform.architecture); + if (settings.rdmd || (rootTT == TargetType.staticLibrary && !settings.buildDeep)) { + // Only build the main target. + // RDMD always builds everything at once and static libraries don't need their + // dependencies to be built, unless --deep flag is specified + NativePath tpath; + buildTarget(settings, root_ti.buildSettings.dup, m_project.rootPackage, root_ti.config, root_ti.packages, null, tpath); + return; + } + + // Recursive build starts here + bool any_cached = false; NativePath[string] target_paths; @@ -158,15 +177,6 @@ target_paths[target] = tpath; } - // build all targets - if (settings.rdmd || rootTT == TargetType.staticLibrary) { - // RDMD always builds everything at once and static libraries don't need their - // dependencies to be built - NativePath tpath; - buildTarget(settings, root_ti.buildSettings.dup, m_project.rootPackage, root_ti.config, root_ti.packages, null, tpath); - return; - } - buildTargetRec(m_project.rootPackage.name); if (dynamicLibDepsFilesToCopy.length) { @@ -253,7 +263,7 @@ m_tempTargetExecutablePath = target_path = getTempDir() ~ format(".dub/build/%s-%s/%s/", packageName, pack.version_, build_id); } else - target_path = packageCache(settings.cache, pack) ~ "build/" ~ build_id; + target_path = targetCacheDir(settings.cache, pack, build_id); if (!settings.force && isUpToDate(target_path, buildsettings, settings, pack, packages, additional_dep_files)) { logInfo("Up-to-date", Color.green, "%s %s: target for configuration [%s] is up to date.", @@ -721,39 +731,6 @@ } } -/** - * Provides a unique (per build) identifier - * - * When building a package, it is important to have a unique but stable - * identifier to differentiate builds and allow their caching. - * This function provides such an identifier. - * Example: - * ``` - * application-debug-linux.posix-x86_64-dmd_v2.100.2-D80285212AEC1FF9855F18AD52C68B9EEB5C7690609C224575F920096FB1965B - * ``` - */ -private string computeBuildID(in BuildSettings buildsettings, string config, GeneratorSettings settings) -{ - const(string[])[] hashing = [ - buildsettings.versions, - buildsettings.debugVersions, - buildsettings.dflags, - buildsettings.lflags, - buildsettings.stringImportPaths, - buildsettings.importPaths, - buildsettings.cImportPaths, - settings.platform.architecture, - [ - (cast(uint)(buildsettings.options & ~BuildOption.color)).to!string, // exclude color option from id - settings.platform.compilerBinary, - settings.platform.compiler, - settings.platform.compilerVersion, - ], - ]; - - return computeBuildName(config, settings, hashing); -} - private NativePath getMainSourceFile(in Package prj) { foreach (f; ["source/app.d", "src/app.d", "source/"~prj.name~".d", "src/"~prj.name~".d"]) diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index 5816058..b602142 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -803,12 +803,61 @@ return cachePath ~ pkg.name ~ pkg.version_.toString(); } +/** + * Compute and return the directory where a target should be cached. + * + * Params: + * cachePath = Base path at which the build cache is located, + * e.g. `$HOME/.dub/cache/` + * pkg = The package. Cannot be `null`. + * buildId = The build identifier of the target. + */ +package(dub) NativePath targetCacheDir(NativePath cachePath, in Package pkg, string buildId) +{ + return packageCache(cachePath, pkg) ~ "build" ~ buildId; +} + +/** + * Provides a unique (per build) identifier + * + * When building a package, it is important to have a unique but stable + * identifier to differentiate builds and allow their caching. + * This function provides such an identifier. + * Example: + * ``` + * library-debug-Z7qINYX4IxM8muBSlyNGrw + * ``` + */ +package(dub) string computeBuildID(in BuildSettings buildsettings, string config, GeneratorSettings settings) +{ + import std.conv : to; + + const(string[])[] hashing = [ + buildsettings.versions, + buildsettings.debugVersions, + buildsettings.dflags, + buildsettings.lflags, + buildsettings.stringImportPaths, + buildsettings.importPaths, + buildsettings.cImportPaths, + settings.platform.architecture, + [ + (cast(uint)(buildsettings.options & ~BuildOption.color)).to!string, // exclude color option from id + settings.platform.compilerBinary, + settings.platform.compiler, + settings.platform.compilerVersion, + ], + ]; + + return computeBuildName(config, settings, hashing); +} struct GeneratorSettings { NativePath cache; BuildPlatform platform; Compiler compiler; string config; + string recipeName; string buildType; BuildSettings buildSettings; BuildMode buildMode = BuildMode.separate; @@ -824,6 +873,9 @@ /// single file dub package bool single; + /// build all dependencies for static libraries + bool buildDeep; + string[] runArgs; void delegate(int status, string output) compileCallback; void delegate(int status, string output) linkCallback; diff --git a/source/dub/generators/targetdescription.d b/source/dub/generators/targetdescription.d index a84883c..077126e 100644 --- a/source/dub/generators/targetdescription.d +++ b/source/dub/generators/targetdescription.d @@ -45,6 +45,9 @@ d.packages = ti.packages.map!(p => p.name).array; d.rootConfiguration = ti.config; d.buildSettings = ti.buildSettings.dup; + const buildId = computeBuildID(d.buildSettings, ti.config, settings); + const filename = settings.compiler.getTargetFileName(d.buildSettings, settings.platform); + d.cacheArtifactPath = (targetCacheDir(settings.cache, ti.pack, buildId) ~ filename).toNativeString(); d.dependencies = ti.dependencies.dup; d.linkDependencies = ti.linkDependencies.dup; diff --git a/source/dub/init.d b/source/dub/init.d index bb7da7f..c395956 100644 --- a/source/dub/init.d +++ b/source/dub/init.d @@ -56,6 +56,8 @@ PackageRecipe p; p.name = root_path.head.name.toLower(); p.authors ~= username; + // Use proprietary as conservative default, so that we don't announce a more + // permissive license than actually chosen in case the dub.json wasn't updated. p.license = "proprietary"; foreach (pack, v; deps) { p.buildSettings.dependencies[pack] = Dependency(v); diff --git a/source/dub/internal/git.d b/source/dub/internal/git.d index dd3e128..e8913a2 100644 --- a/source/dub/internal/git.d +++ b/source/dub/internal/git.d @@ -72,7 +72,7 @@ auto git_dir_param = "--git-dir=" ~ git_dir.toNativeString(); static string exec(scope string[] params...) { - auto ret = executeShell(escapeShellCommand(params)); + auto ret = execute(params); if (ret.status == 0) return ret.output.strip; logDebug("'%s' failed with exit code %s: %s", params.join(" "), ret.status, ret.output.strip); return null; diff --git a/source/dub/project.d b/source/dub/project.d index f68155a..5abfd45 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -987,7 +987,7 @@ case "stringImportFiles": case "sourceFiles": case "importPaths": - case "CImportPaths": + case "cImportPaths": case "stringImportPaths": return values.map!(escapeShellFileName).array(); @@ -1143,6 +1143,10 @@ case "post-build-environments": case "pre-run-environments": case "post-run-environments": + case "default-config": + case "configs": + case "default-build": + case "builds": enforce(false, "--data="~requestedData~" can only be used with `--data-list` or `--data-list --data-0`."); break; @@ -1194,6 +1198,10 @@ case "post-run-environments": return listBuildSetting!"postRunEnvironments"(args); case "requirements": return listBuildSetting!"requirements"(args); case "options": return listBuildSetting!"options"(args); + case "default-config": return [getDefaultConfiguration(settings.platform)]; + case "configs": return configurations; + case "default-build": return [builds[0]]; + case "builds": return builds; default: enforce(false, "--data="~requestedData~ diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d index c8bb5ec..30445d1 100644 --- a/source/dub/recipe/packagerecipe.d +++ b/source/dub/recipe/packagerecipe.d @@ -716,8 +716,9 @@ import std.traits : isSomeString, isDynamicArray, isAssociativeArray, isBasicType, ValueType; static if (is(T == immutable)) return val; - else static if (isBasicType!T) return val; - else static if (isDynamicArray!T) { + else static if (isBasicType!T || is(T Base == enum) && isBasicType!Base) { + return val; + } else static if (isDynamicArray!T) { alias V = typeof(T.init[0]); static if (is(V == immutable)) return val; else { diff --git a/test/0-init-interactive.default_name.dub.sdl b/test/0-init-interactive.default_name.dub.sdl new file mode 100644 index 0000000..7b8f8ba --- /dev/null +++ b/test/0-init-interactive.default_name.dub.sdl @@ -0,0 +1,5 @@ +name "0-init-interactive" +description "desc" +authors "author" +copyright "copy" +license "gpl" diff --git a/test/0-init-interactive.dub.json b/test/0-init-interactive.dub.json new file mode 100644 index 0000000..70481fd --- /dev/null +++ b/test/0-init-interactive.dub.json @@ -0,0 +1,9 @@ +{ + "authors": [ + "author" + ], + "copyright": "copy", + "description": "desc", + "license": "gpl", + "name": "test" +} \ No newline at end of file diff --git a/test/0-init-interactive.license_gpl3.dub.sdl b/test/0-init-interactive.license_gpl3.dub.sdl new file mode 100644 index 0000000..8b29799 --- /dev/null +++ b/test/0-init-interactive.license_gpl3.dub.sdl @@ -0,0 +1,5 @@ +name "test" +description "desc" +authors "author" +copyright "copy" +license "GPL-3.0-only" diff --git a/test/0-init-interactive.license_mpl2.dub.sdl b/test/0-init-interactive.license_mpl2.dub.sdl new file mode 100644 index 0000000..b2a5ee4 --- /dev/null +++ b/test/0-init-interactive.license_mpl2.dub.sdl @@ -0,0 +1,5 @@ +name "test" +description "desc" +authors "author" +copyright "copy" +license "MPL-2.0" diff --git a/test/0-init-interactive.license_proprietary.dub.sdl b/test/0-init-interactive.license_proprietary.dub.sdl new file mode 100644 index 0000000..166cc6c --- /dev/null +++ b/test/0-init-interactive.license_proprietary.dub.sdl @@ -0,0 +1,5 @@ +name "test" +description "desc" +authors "author" +copyright "copy" +license "proprietary" diff --git a/test/0-init-interactive.sh b/test/0-init-interactive.sh index 945838b..323cec3 100755 --- a/test/0-init-interactive.sh +++ b/test/0-init-interactive.sh @@ -3,20 +3,45 @@ . $(dirname "${BASH_SOURCE[0]}")/common.sh packname="0-init-interactive" -echo -e "sdl\ntest\ndesc\nauthor\ngpl\ncopy\n\n" | $DUB init $packname - function cleanup { rm -rf $packname } -if [ ! -e $packname/dub.sdl ]; then # it failed +function runTest { + local inp=$1 + local comp=$2 + local dub_ext=${comp##*.} + local outp=$(echo -e $inp | $DUB init $packname) + if [ ! -e $packname/dub.$dub_ext ]; then # it failed + cleanup + die $LINENO "No dub.$dub_ext file has been generated for test $comp with input '$inp'. Output: $outp" + fi + if ! diff $packname/dub.$dub_ext "$CURR_DIR"/$comp; then + cleanup + die $LINENO "Contents of generated dub.$dub_ext not as expected." + fi cleanup - die $LINENO 'No dub.sdl file has been generated.' -fi +} -if ! diff $packname/dub.sdl "$CURR_DIR"/0-init-interactive.dub.sdl; then - cleanup - die $LINENO 'Contents of generated dub.sdl not as expected.' -fi - -cleanup +# sdl package format +runTest '1\ntest\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.dub.sdl +# select package format out of bounds +runTest '3\n1\ntest\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.dub.sdl +# select package format not numeric, but in list +runTest 'sdl\ntest\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.dub.sdl +# selected value not numeric and not in list +runTest 'sdlf\n1\ntest\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.dub.sdl +# default name +runTest '1\n\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.default_name.dub.sdl +# json package format +runTest '2\ntest\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.dub.json +# default package format +runTest '\ntest\ndesc\nauthor\ngpl\ncopy\n\n' 0-init-interactive.dub.json +# select license +runTest '1\ntest\ndesc\nauthor\n6\n3\ncopy\n\n' 0-init-interactive.license_gpl3.dub.sdl +# select license (with description) +runTest '1\ntest\ndesc\nauthor\n9\n3\ncopy\n\n' 0-init-interactive.license_mpl2.dub.sdl +# select license out of bounds +runTest '1\ntest\ndesc\nauthor\n21\n6\n3\ncopy\n\n' 0-init-interactive.license_gpl3.dub.sdl +# default license +runTest '1\ntest\ndesc\nauthor\n\ncopy\n\n' 0-init-interactive.license_proprietary.dub.sdl diff --git a/test/3-copyFiles/source/app.d b/test/3-copyFiles/source/app.d index a39cc7b..e3ff57e 100644 --- a/test/3-copyFiles/source/app.d +++ b/test/3-copyFiles/source/app.d @@ -4,21 +4,35 @@ { immutable root = args[0].dirName; // get the bin dir immutable pfx = root.length + "/".length; - auto files = dirEntries(root, SpanMode.breadth).map!(n => n[pfx .. $]).array.sort().release; + auto actualFiles = dirEntries(root, SpanMode.breadth).map!(n => n[pfx .. $]).array.sort().release; - assert(files == - [ - "copyfiles-test", "file_to_copy.txt", "file_to_copy_mask1.txt", - "file_to_copy_mask2.txt", "hdpi", "hdpi/file1.txt", "hdpi/file2.txt", - "hdpi/file3.txt", "hdpi/nested_dir", "hdpi/nested_dir/nested_file.txt", - "ldpi", "ldpi/file1.txt", "ldpi/file2.txt", "ldpi/file3.txt", "mdpi", - "mdpi/file1.txt", "mdpi/file2.txt", "mdpi/file3.txt", "res", - "res/.nocopy", "res/.nocopy/file_inside_dot_prefixed_dir.txt", - "res/hdpi", "res/hdpi/file1.txt", "res/hdpi/file2.txt", "res/hdpi/file3.txt", - "res/hdpi/nested_dir", "res/hdpi/nested_dir/nested_file.txt", "res/i18n", - "res/i18n/resource_en.txt", "res/i18n/resource_fr.txt", "res/ldpi", - "res/ldpi/file1.txt", "res/ldpi/file2.txt", "res/ldpi/file3.txt", "res/mdpi", - "res/mdpi/file1.txt", "res/mdpi/file2.txt", "res/mdpi/file3.txt" - ], files.join(", ") - ); + string[] expectedFiles; + version (Windows) + { + expectedFiles ~= "copyfiles-test.exe"; + expectedFiles ~= "copyfiles-test.pdb"; + } + else + { + expectedFiles ~= "copyfiles-test"; + } + expectedFiles ~= [ + "file_to_copy.txt", "file_to_copy_mask1.txt", + "file_to_copy_mask2.txt", "hdpi", "hdpi/file1.txt", "hdpi/file2.txt", + "hdpi/file3.txt", "hdpi/nested_dir", "hdpi/nested_dir/nested_file.txt", + "ldpi", "ldpi/file1.txt", "ldpi/file2.txt", "ldpi/file3.txt", "mdpi", + "mdpi/file1.txt", "mdpi/file2.txt", "mdpi/file3.txt", "res", + "res/.nocopy", "res/.nocopy/file_inside_dot_prefixed_dir.txt", + "res/hdpi", "res/hdpi/file1.txt", "res/hdpi/file2.txt", "res/hdpi/file3.txt", + "res/hdpi/nested_dir", "res/hdpi/nested_dir/nested_file.txt", "res/i18n", + "res/i18n/resource_en.txt", "res/i18n/resource_fr.txt", "res/ldpi", + "res/ldpi/file1.txt", "res/ldpi/file2.txt", "res/ldpi/file3.txt", "res/mdpi", + "res/mdpi/file1.txt", "res/mdpi/file2.txt", "res/mdpi/file3.txt" + ]; + version (Windows) + { + expectedFiles = expectedFiles.map!(f => f.replace('/', '\\')).array; + } + + assert(actualFiles == expectedFiles, actualFiles.join(", ")); } diff --git a/test/4-describe-data-1-list.sh b/test/4-describe-data-1-list.sh index 58f5293..934243e 100755 --- a/test/4-describe-data-1-list.sh +++ b/test/4-describe-data-1-list.sh @@ -30,6 +30,10 @@ --data=pre-build-commands \ --data=post-build-commands \ '--data=requirements, options' \ + --data=default-config \ + --data=configs \ + --data=default-build \ + --data=builds \ > "$temp_file"; then die $LINENO 'Printing project data failed!' fi @@ -130,6 +134,33 @@ echo "debugInfo" >> "$expected_file" echo "stackStomping" >> "$expected_file" echo "warnings" >> "$expected_file" +echo >> "$expected_file" +# --data=default-config +echo "my-project-config" >> "$expected_file" +echo >> "$expected_file" +# --data=configs +echo "my-project-config" >> "$expected_file" +echo >> "$expected_file" +# --data=default-build +echo "debug" >> "$expected_file" +echo >> "$expected_file" +# --data=builds +echo "debug" >> "$expected_file" +echo "plain" >> "$expected_file" +echo "release" >> "$expected_file" +echo "release-debug" >> "$expected_file" +echo "release-nobounds" >> "$expected_file" +echo "unittest" >> "$expected_file" +echo "profile" >> "$expected_file" +echo "profile-gc" >> "$expected_file" +echo "docs" >> "$expected_file" +echo "ddox" >> "$expected_file" +echo "cov" >> "$expected_file" +echo "cov-ctfe" >> "$expected_file" +echo "unittest-cov" >> "$expected_file" +echo "unittest-cov-ctfe" >> "$expected_file" +echo "syntax" >> "$expected_file" +# echo >> "$expected_file" if ! diff "$expected_file" "$temp_file"; then echo "Result:" diff --git a/test/4-describe-data-2-dmd.sh b/test/4-describe-data-2-dmd.sh index 7d994dc..46e25f7 100755 --- a/test/4-describe-data-2-dmd.sh +++ b/test/4-describe-data-2-dmd.sh @@ -34,8 +34,12 @@ # Create the expected output path file to compare against. expected_file="$CURR_DIR/expected-describe-data-2-dmd-output" + +# check if escaping is required +. "$CURR_DIR/4-describe-data-check-escape" + # --data=main-source-file -echo -n "'$CURR_DIR/describe-project/src/dummy.d' " > "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-project/src/dummy.d") " > "$expected_file" # --data=dflags echo -n "--some-dflag " >> "$expected_file" echo -n "--another-dflag " >> "$expected_file" @@ -46,12 +50,12 @@ echo -n "-L-lsomelib " >> "$expected_file" echo -n "-L-lanotherlib " >> "$expected_file" # --data=linker-files -echo -n "'$CURR_DIR/describe-dependency-3/libdescribe-dependency-3.a' " >> "$expected_file" -echo -n "'$CURR_DIR/describe-project/some.a' " >> "$expected_file" -echo -n "'$CURR_DIR/describe-dependency-1/dep.a' " >> "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-dependency-3/libdescribe-dependency-3.a") " >> "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-project/some.a") " >> "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-dependency-1/dep.a") " >> "$expected_file" # --data=source-files -echo -n "'$CURR_DIR/describe-project/src/dummy.d' " >> "$expected_file" -echo -n "'$CURR_DIR/describe-dependency-1/source/dummy.d' " >> "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-project/src/dummy.d") " >> "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-dependency-1/source/dummy.d") " >> "$expected_file" # --data=versions echo -n "-version=someVerIdent " >> "$expected_file" echo -n "-version=anotherVerIdent " >> "$expected_file" @@ -60,16 +64,16 @@ echo -n "-debug=someDebugVerIdent " >> "$expected_file" echo -n "-debug=anotherDebugVerIdent " >> "$expected_file" # --data=import-paths -echo -n "'-I$CURR_DIR/describe-project/src/' " >> "$expected_file" -echo -n "'-I$CURR_DIR/describe-dependency-1/source/' " >> "$expected_file" -echo -n "'-I$CURR_DIR/describe-dependency-2/some-path/' " >> "$expected_file" -echo -n "'-I$CURR_DIR/describe-dependency-3/dep3-source/' " >> "$expected_file" +echo -n "$(escaped "-I$CURR_DIR/describe-project/src/") " >> "$expected_file" +echo -n "$(escaped "-I$CURR_DIR/describe-dependency-1/source/") " >> "$expected_file" +echo -n "$(escaped "-I$CURR_DIR/describe-dependency-2/some-path/") " >> "$expected_file" +echo -n "$(escaped "-I$CURR_DIR/describe-dependency-3/dep3-source/") " >> "$expected_file" # --data=string-import-paths -echo -n "'-J$CURR_DIR/describe-project/views/' " >> "$expected_file" -echo -n "'-J$CURR_DIR/describe-dependency-2/some-extra-string-import-path/' " >> "$expected_file" -echo -n "'-J$CURR_DIR/describe-dependency-3/dep3-string-import-path/' " >> "$expected_file" +echo -n "$(escaped "-J$CURR_DIR/describe-project/views/") " >> "$expected_file" +echo -n "$(escaped "-J$CURR_DIR/describe-dependency-2/some-extra-string-import-path/") " >> "$expected_file" +echo -n "$(escaped "-J$CURR_DIR/describe-dependency-3/dep3-string-import-path/") " >> "$expected_file" # --data=import-files -echo -n "'$CURR_DIR/describe-dependency-2/some-path/dummy.d' " >> "$expected_file" +echo -n "$(escaped "$CURR_DIR/describe-dependency-2/some-path/dummy.d") " >> "$expected_file" # --data=options echo -n "-debug " >> "$expected_file" # releaseMode is not included, even though it's specified, because the requireContracts requirement drops it diff --git a/test/4-describe-data-3-zero-delim.sh b/test/4-describe-data-3-zero-delim.sh index 0a628bd..e36aeb3 100755 --- a/test/4-describe-data-3-zero-delim.sh +++ b/test/4-describe-data-3-zero-delim.sh @@ -111,6 +111,9 @@ die $LINENO 'The null-delimited dmd-style --data=versions did not match the expected output!' fi +# check if escaping is required +. "$CURR_DIR/4-describe-data-check-escape" + # Test dmd-style --data=source-files if ! $DUB describe --compiler=$DC --data=source-files \ > "$temp_file_normal"; then @@ -118,7 +121,7 @@ fi if ! $DUB describe --compiler=$DC --data-0 --data=source-files \ - | xargs -0 printf "'%s' " > "$temp_file_zero_delim"; then + | xargs -0 printf "$(escaped "%s") " > "$temp_file_zero_delim"; then die $LINENO 'Printing null-delimited dmd-style --data=source-files failed!' fi diff --git a/test/4-describe-data-check-escape b/test/4-describe-data-check-escape new file mode 100644 index 0000000..8f37947 --- /dev/null +++ b/test/4-describe-data-check-escape @@ -0,0 +1,19 @@ +dmd_ver=$($DC --version | grep -Eo "v2\.[0-9][0-9][0-9].[0-9]") +dmd_minor=$(echo $dmd_ver | grep -Eo "[0-9][0-9][0-9]") +dmd_micro=${dmd_ver: -1} + +if [[ $dmd_minor$dmd_micro < 1022 || "$CURR_DIR" =~ [[:space:]] ]]; then + echo "Expecting escaped paths" + escape=1 +else + echo "Not expecting escaped paths" + escape=0 +fi + +function escaped { + if [ $escape -eq 1 ]; then + echo -n "'$1'" + else + echo -n "$1" + fi +} diff --git a/test/issue2452/.no_test b/test/issue2452/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue2452/.no_test diff --git a/test/issue2452/dub.json b/test/issue2452/dub.json index 40f2fd0..1260386 100644 --- a/test/issue2452/dub.json +++ b/test/issue2452/dub.json @@ -2,8 +2,12 @@ "name": "generated-sources-and-source-files-without-glob", "description": "Example of using pre generate commands and sourceFiles without glob.", "sourceFiles": ["ext/fortytwo.d"], - "preGenerateCommands": [ + "preGenerateCommands-posix": [ "mkdir -p ext", "echo 'extern(C) int fun42 () { return 42; }' > ext/fortytwo.d" + ], + "preGenerateCommands-windows": [ + "if not exist ext mkdir ext", + "echo extern(C) int fun42 () { return 42; } > ext/fortytwo.d" ] } diff --git a/test/issue2684-recipe-file.sh b/test/issue2684-recipe-file.sh new file mode 100755 index 0000000..2567395 --- /dev/null +++ b/test/issue2684-recipe-file.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd ${CURR_DIR}/issue2684-recipe-file +${DUB} | grep -c "This was built using dub.json" > /dev/null +${DUB} --recipe=dubWithAnotherSource.json | grep -c "This was built using dubWithAnotherSource.json" > /dev/null \ No newline at end of file diff --git a/test/issue2684-recipe-file/.gitignore b/test/issue2684-recipe-file/.gitignore new file mode 100644 index 0000000..3de530e --- /dev/null +++ b/test/issue2684-recipe-file/.gitignore @@ -0,0 +1,16 @@ +.dub +docs.json +__dummy.html +docs/ +/issue2684-recipe-file +issue2684-recipe-file.so +issue2684-recipe-file.dylib +issue2684-recipe-file.dll +issue2684-recipe-file.a +issue2684-recipe-file.lib +issue2684-recipe-file-test-* +*.exe +*.pdb +*.o +*.obj +*.lst diff --git a/test/issue2684-recipe-file/anotherSource/app.d b/test/issue2684-recipe-file/anotherSource/app.d new file mode 100644 index 0000000..f28bb40 --- /dev/null +++ b/test/issue2684-recipe-file/anotherSource/app.d @@ -0,0 +1,7 @@ +module app; +import std.stdio; + +void main() +{ + writeln("This was built using dubWithAnotherSource.json"); +} \ No newline at end of file diff --git a/test/issue2684-recipe-file/dub.json b/test/issue2684-recipe-file/dub.json new file mode 100644 index 0000000..17ac6ce --- /dev/null +++ b/test/issue2684-recipe-file/dub.json @@ -0,0 +1,9 @@ +{ + "authors": [ + "Hipreme" + ], + "copyright": "Copyright © 2023, Hipreme", + "description": "A minimal D application.", + "license": "public domain", + "name": "issue2684-recipe-file" +} \ No newline at end of file diff --git a/test/issue2684-recipe-file/dubWithAnotherSource.json b/test/issue2684-recipe-file/dubWithAnotherSource.json new file mode 100644 index 0000000..0ef1676 --- /dev/null +++ b/test/issue2684-recipe-file/dubWithAnotherSource.json @@ -0,0 +1,10 @@ +{ + "authors": [ + "Hipreme" + ], + "copyright": "Copyright © 2023, Hipreme", + "description": "A minimal D application.", + "sourcePaths": ["anotherSource"], + "license": "public domain", + "name": "issue2684-recipe-file" +} \ No newline at end of file diff --git a/test/issue2684-recipe-file/source/app.d b/test/issue2684-recipe-file/source/app.d new file mode 100644 index 0000000..022b7ee --- /dev/null +++ b/test/issue2684-recipe-file/source/app.d @@ -0,0 +1,10 @@ +module app; +import std.stdio; + +void main() +{ + writeln("This was built using dub.json. +Try using the other configuration by calling dub with: +dub --recipe=dubWithAnotherSource.json +"); +} diff --git a/test/issue895-local-configuration.sh b/test/issue895-local-configuration.sh index 8593c00..7ce00bb 100755 --- a/test/issue895-local-configuration.sh +++ b/test/issue895-local-configuration.sh @@ -61,7 +61,7 @@ rm ../etc/dub/settings.json echo "Empty file named ldc2." > ../bin/ldc2 -if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Failed to invoke the compiler $(dirname $CURR_DIR)/bin/ldc2 to determine the build platform"; then +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Failed to execute '$(dirname $CURR_DIR)/bin/ldc2'"; then rm ../bin/ldc2 die $LINENO 'DUB did not find ldc2 adjacent to it.' fi diff --git a/test/pr2644-describe-artifact-path/.gitignore b/test/pr2644-describe-artifact-path/.gitignore new file mode 100644 index 0000000..0c7a7b4 --- /dev/null +++ b/test/pr2644-describe-artifact-path/.gitignore @@ -0,0 +1,2 @@ +dubhome/ +pr2644-describe-artifact-path diff --git a/test/pr2644-describe-artifact-path/.no_test b/test/pr2644-describe-artifact-path/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/pr2644-describe-artifact-path/.no_test diff --git a/test/pr2644-describe-artifact-path/dub.sdl b/test/pr2644-describe-artifact-path/dub.sdl new file mode 100644 index 0000000..4bb3254 --- /dev/null +++ b/test/pr2644-describe-artifact-path/dub.sdl @@ -0,0 +1,2 @@ +name "pr2644-describe-artifact-path"; +targetType "executable"; diff --git a/test/pr2644-describe-artifact-path/source/describe_artifact_path.d b/test/pr2644-describe-artifact-path/source/describe_artifact_path.d new file mode 100644 index 0000000..0e7b2bb --- /dev/null +++ b/test/pr2644-describe-artifact-path/source/describe_artifact_path.d @@ -0,0 +1,61 @@ +module describe_artifact_path; + +import std.path; +import std.file; +import std.process; +import std.stdio; +import std.json; + +void main() +{ + const dubhome = __FILE_FULL_PATH__.dirName().dirName().buildNormalizedPath("dubhome"); + if (exists(dubhome)) + { + rmdirRecurse(dubhome); + } + scope (success) + { + // leave dubhome in the tree for analysis in case of failure + rmdirRecurse(dubhome); + } + + const string[string] env = [ + "DUB_HOME": dubhome, + ]; + const fetchProgram = [ + environment["DUB"], + "fetch", + "gitcompatibledubpackage@1.0.4", + ]; + auto dubFetch = spawnProcess(fetchProgram, stdin, stdout, stderr, env); + wait(dubFetch); + + + const describeProgram = [ + environment["DUB"], + "describe", + "--compiler=" ~ environment["DC"], + "--build=debug", + "--config=lib", + "gitcompatibledubpackage@1.0.4", + ]; + auto result = execute(describeProgram, env); + assert(result.status == 0, "expected dub describe to return zero"); + auto json = parseJSON(result.output); + + auto cacheFile = json["targets"][0]["cacheArtifactPath"].str; + assert(!exists(cacheFile), "found cache file in virgin dubhome"); + + const buildProgram = [ + environment["DUB"], + "build", + "--compiler=" ~ environment["DC"], + "--build=debug", + "--config=lib", + "gitcompatibledubpackage@1.0.4", + ]; + auto dubBuild = spawnProcess(buildProgram, stdin, stdout, stderr, env); + wait(dubBuild); + + assert(exists(cacheFile), "did not find cache file after build"); +} diff --git a/test/pr2647-build-deep/.gitignore b/test/pr2647-build-deep/.gitignore new file mode 100644 index 0000000..a71baff --- /dev/null +++ b/test/pr2647-build-deep/.gitignore @@ -0,0 +1,2 @@ +dubhome/ +pr2647-build-deep diff --git a/test/pr2647-build-deep/.no_test b/test/pr2647-build-deep/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/pr2647-build-deep/.no_test diff --git a/test/pr2647-build-deep/dub.sdl b/test/pr2647-build-deep/dub.sdl new file mode 100644 index 0000000..639f6d5 --- /dev/null +++ b/test/pr2647-build-deep/dub.sdl @@ -0,0 +1,2 @@ +name "pr2647-build-deep"; +targetType "executable"; diff --git a/test/pr2647-build-deep/pack/dub.sdl b/test/pr2647-build-deep/pack/dub.sdl new file mode 100644 index 0000000..d173b9a --- /dev/null +++ b/test/pr2647-build-deep/pack/dub.sdl @@ -0,0 +1,3 @@ +name "pack" +targetType "staticLibrary" +dependency "urld" version="==2.1.1" diff --git a/test/pr2647-build-deep/pack/source/lib.d b/test/pr2647-build-deep/pack/source/lib.d new file mode 100644 index 0000000..868cb18 --- /dev/null +++ b/test/pr2647-build-deep/pack/source/lib.d @@ -0,0 +1,14 @@ +module lib; + +import url; + +string getDlangUrl() +{ + URL url; + with(url) + { + scheme = "https"; + host = "dlang.org"; + } + return url.toString(); +} diff --git a/test/pr2647-build-deep/source/test_build_deep.d b/test/pr2647-build-deep/source/test_build_deep.d new file mode 100644 index 0000000..79e4a41 --- /dev/null +++ b/test/pr2647-build-deep/source/test_build_deep.d @@ -0,0 +1,53 @@ +module test_build_deep; + +import std.array; +import std.file; +import std.path; +import std.process; +import std.stdio; + +void main() +{ + const dubhome = __FILE_FULL_PATH__.dirName().dirName().buildNormalizedPath("dubhome"); + const packdir = __FILE_FULL_PATH__.dirName().dirName().buildNormalizedPath("pack"); + const dub = absolutePath(environment["DUB"]); + + if (exists(dubhome)) + { + rmdirRecurse(dubhome); + } + + scope (success) + { + // leave dubhome in the tree for analysis in case of failure + rmdirRecurse(dubhome); + } + + const string[string] env = [ + "DUB_HOME": dubhome, + ]; + + // testing the regular way first: `dub build` only builds what is needed + // (urld is downloaded but not built) + const dubBuildProg = [dub, "build"]; + writefln("running %s ...", dubBuildProg.join(" ")); + auto dubBuild = spawnProcess(dubBuildProg, stdin, stdout, stderr, env, Config.none, packdir); + wait(dubBuild); + assert(exists(buildPath(dubhome, "cache", "pack"))); + assert(isDir(buildPath(dubhome, "cache", "pack"))); + assert(exists(buildPath(dubhome, "packages", "urld"))); + assert(isDir(buildPath(dubhome, "packages", "urld"))); + assert(!exists(buildPath(dubhome, "cache", "urld"))); + + // now testing the --deep switch: `dub build --deep` will build urld + const dubBuildDeepProg = [dub, "build", "--deep"]; + writefln("running %s ...", dubBuildDeepProg.join(" ")); + auto dubBuildDeep = spawnProcess(dubBuildDeepProg, stdin, stdout, stderr, env, Config.none, packdir); + wait(dubBuildDeep); + assert(exists(buildPath(dubhome, "cache", "pack"))); + assert(isDir(buildPath(dubhome, "cache", "pack"))); + assert(exists(buildPath(dubhome, "packages", "urld"))); + assert(isDir(buildPath(dubhome, "packages", "urld"))); + assert(exists(buildPath(dubhome, "cache", "urld"))); + assert(isDir(buildPath(dubhome, "cache", "urld"))); +} diff --git a/test/run-unittest.sh b/test/run-unittest.sh index 00786e6..5ef1c1c 100755 --- a/test/run-unittest.sh +++ b/test/run-unittest.sh @@ -39,10 +39,10 @@ if [ -z ${FRONTEND:-} ]; then if [ "$DC_BIN" == "ldc2" ]; then - FRONTEND=$(ldc2 --version | grep -Po "based on DMD v\K(2\.\d+\.\d)") + FRONTEND=$(ldc2 --version | grep 'based on DMD v2.' | sed -E -n 's/^.*DMD v(2\.[0-9]+\.[0-9]).*$/\1/p') fi if [ "$DC_BIN" == "dmd" ]; then - FRONTEND=$(dmd --version | grep -Po "D Compiler v\K(2\.\d+\.\d)") + FRONTEND=$(dmd --version | grep 'D Compiler v2.' | sed -E -n 's/^.*D Compiler v(2\.[0-9]+\.[0-9]).*$/\1/p') fi fi