diff --git a/.travis.yml b/.travis.yml index 2efa367..327345e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -95,6 +95,34 @@ api_key: $GH_REPO_TOKEN on: tags: true + - d: ldc + script: echo "Deploying to GitHub releases (win32) ..." && ./release-windows.sh + addons: + apt: + packages: + - p7zip-full + deploy: + - provider: releases + file_glob: true + file: bin/dub-*.zip + skip_cleanup: true + api_key: $GH_REPO_TOKEN + on: + tags: true + - d: ldc + script: echo "Deploying to GitHub releases (win64) ..." && ARCH=64 ./release-windows.sh + addons: + apt: + packages: + - p7zip-full + deploy: + - provider: releases + file_glob: true + file: bin/dub-*.zip + skip_cleanup: true + api_key: $GH_REPO_TOKEN + on: + tags: true - stage: update-latest script: echo "Deploying to GitHub pages ..." && mkdir -p docs && git describe --abbrev=0 --tags > docs/LATEST deploy: diff --git a/README.md b/README.md index dee9cc5..7e1a0af 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ 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)](#) [![Build Status](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.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) +[![Buildkite](https://badge.buildkite.com/c54d71c42284a042b9d578e28e093dff35f20cc8528319b1b6.svg?branch=master)](https://buildkite.com/dlang/dub) ## Introduction diff --git a/changelog/dub-fetch-shortcut.dd b/changelog/dub-fetch-shortcut.dd new file mode 100644 index 0000000..a912a36 --- /dev/null +++ b/changelog/dub-fetch-shortcut.dd @@ -0,0 +1,9 @@ +`dub fetch` now supports `@` as a shortcut + +`dub fetch @` is a shortcut for +`dub fetch --version=`: + +$(CONSOLE +> dub fetch gitcompatibledubpackage@1.0.4 +Fetching gitcompatibledubpackage 1.0.4... +) diff --git a/changelog/extraDependencyFiles-attribute-added.dd b/changelog/extraDependencyFiles-attribute-added.dd new file mode 100644 index 0000000..213df5b --- /dev/null +++ b/changelog/extraDependencyFiles-attribute-added.dd @@ -0,0 +1,4 @@ +extraDependencyFiles attribute added + +Using attribute `extraDependencyFiles` a list of files can be specified +causing rebuild on change. diff --git a/changelog/file-system-supplier.dd b/changelog/file-system-supplier.dd new file mode 100644 index 0000000..3f0c95d --- /dev/null +++ b/changelog/file-system-supplier.dd @@ -0,0 +1,7 @@ +File system supplier enabled + +Existing file system supplier is enabled for usage. +It searches a certain directory for files with names of +the form "[package name]-[version].zip". + +Example `dub fetch mypackage --registry=file:///etc/dub/packages/` \ No newline at end of file diff --git a/changelog/toolchain_requirements.dd b/changelog/toolchain_requirements.dd new file mode 100644 index 0000000..b72fb79 --- /dev/null +++ b/changelog/toolchain_requirements.dd @@ -0,0 +1,52 @@ +`toolchainRequirements` recipe entry + +DUB now supports a new entry in `dub.json` or `dub.sdl` to restrict the versions of +DUB and of compilers supported by a package. + +dub.json: +--- +{ + "toolchainRequirements": { + "dub": "~>1.10", + "frontend": ">=2.068|<2.083" + } +} +--- + +dub.sdl: +--- +toolchainRequirements dub="~>1.10" frontend=">=2.068|<2.083" +--- + +Supported entries in `toolchainRequirements` are: +$(UL + $(LI dub) + $(LI frontend) + $(LI dmd) + $(LI ldc) + $(LI gdc) +) + +Each can be assigned to one or more version specifications, separated by `|`. +For compilers, instead of a version specification, the keyword `no` can also be +used to indicate that the compiler should not be used for this package. + +Example scenario:$(BR) +Package that needs DUB>=1.12, and that will only build with LDC>=1.10: + +dub.json: +--- +{ + "toolchainRequirements": { + "dub": ">=1.12", + "dmd": "no", + "gdc": "no", + "ldc": ">=1.10" + } +} +--- + +dub.sdl: +--- +toolchainRequirements dub=">=1.12" dmd="no" gdc="no" ldc=">=1.10" +--- diff --git a/release-windows.sh b/release-windows.sh new file mode 100755 index 0000000..0deab6b --- /dev/null +++ b/release-windows.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Build the Windows binaries under Linux +set -eux -o pipefail + +BIN_NAME=dub + +# Allow the script to be run from anywhere +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $DIR + +source setup-ldc-windows.sh + +# Run LDC with cross-compilation +archiveName="$BIN_NAME-$VERSION-$OS-$ARCH_SUFFIX.zip" +echo "Building $archiveName" +mkdir -p bin +DC=ldmd2 DFLAGS="-release" ./build.sh + +cd bin +mv "${BIN_NAME}" "${BIN_NAME}.exe" +zip "$archiveName" "${BIN_NAME}.exe" diff --git a/setup-ldc-windows.sh b/setup-ldc-windows.sh new file mode 100644 index 0000000..0656b5a --- /dev/null +++ b/setup-ldc-windows.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# sets up LDC for cross-compilation. Source this script, s.t. the new LDC is in PATH + +LDC_VERSION="1.13.0" +ARCH=${ARCH:-32} +VERSION=$(git describe --abbrev=0 --tags) +OS=windows + +# Step 0: install ldc +if [ ! -f install.sh ] ; then + wget https://dlang.org/install.sh +fi +. $(bash ./install.sh -a "ldc-${LDC_VERSION}") + +# for the install.sh script only +LDC_PATH="$(dirname $(dirname $(which ldc2)))" + +# Step 1a: download the LDC x64 windows binaries +if [ "${ARCH}" == 64 ] && [ ! -d "ldc2-${LDC_VERSION}-windows-x64" ] ; then + wget "https://github.com/ldc-developers/ldc/releases/download/v1.13.0/ldc2-${LDC_VERSION}-windows-x64.7z" + 7z x "ldc2-${LDC_VERSION}-windows-x64.7z" > /dev/null + # Step 2a: Add LDC windows binaries to LDC Linux + if [ ! -d "${LDC_PATH}/lib-win64" ] ; then + cp -r ldc2-1.13.0-windows-x64/lib "${LDC_PATH}/lib-win64" + cat >> "$LDC_PATH"/etc/ldc2.conf < /dev/null + # Step 2b: Add LDC windows binaries to LDC Linux + if [ ! -d "${LDC_PATH}/lib-win32" ] ; then + cp -r ldc2-1.13.0-windows-x86/lib "${LDC_PATH}/lib-win32" + cat >> "$LDC_PATH"/etc/ldc2.conf <= 2 && args[1].endsWith(".d")) { args = args[0] ~ ["run", "-q", "--temp-build", "--single", args[1], "--"] ~ args[2 ..$]; @@ -1146,7 +1146,7 @@ this() { this.name = "add"; - this.argumentsPattern = "[=] []"; + this.argumentsPattern = "[@] []"; this.description = "Adds dependencies to the package file."; this.helpText = [ "Adds as dependencies.", @@ -1261,7 +1261,7 @@ this() { this.name = "fetch"; - this.argumentsPattern = ""; + this.argumentsPattern = "[@]"; this.description = "Manually retrieves and caches a package"; this.helpText = [ "Note: Use \"dub add \" if you just want to use a certain package as a dependency, you don't have to explicitly fetch packages.", @@ -1298,7 +1298,10 @@ FetchOptions fetchOpts; fetchOpts |= FetchOptions.forceBranchUpgrade; if (m_version.length) dub.fetch(name, Dependency(m_version), location, fetchOpts); - else { + else if (name.canFind("@", "=")) { + const parts = name.splitPackageName; + dub.fetch(parts.name, Dependency(parts.version_), location, fetchOpts); + } else { try { dub.fetch(name, Dependency(">=0.0.0"), location, fetchOpts); logInfo( @@ -2139,15 +2142,14 @@ private bool addDependency(Dub dub, ref PackageRecipe recipe, string depspec) { Dependency dep; - // split = - auto parts = depspec.findSplit("="); - auto depname = parts[0]; - if (!parts[1].empty) - dep = Dependency(parts[2]); + const parts = splitPackageName(depspec); + const depname = parts.name; + if (parts.version_) + dep = Dependency(parts.version_); else { try { - auto ver = dub.getLatestVersion(depname); + const ver = dub.getLatestVersion(depname); dep = ver.isBranch ? Dependency(ver) : Dependency("~>" ~ ver.toString()); } catch (Exception e) { logError("Could not find package '%s'.", depname); @@ -2159,3 +2161,27 @@ logInfo("Adding dependency %s %s", depname, dep.versionSpec); return true; } + +/* Split = and @ + into `name` and `version_`. */ +private auto splitPackageName(string packageName) +{ + struct PackageAndVersion + { + string name; + string version_; + } + + // split = + auto parts = packageName.split("="); + if (parts.length == 1) { + // support splitting @ too + parts = packageName.split("@"); + } + + PackageAndVersion p; + p.name = parts[0]; + if (parts.length > 1) + p.version_ = parts[1]; + return p; +} diff --git a/source/dub/compilers/buildsettings.d b/source/dub/compilers/buildsettings.d index 87a4f1a..cfc39c4 100644 --- a/source/dub/compilers/buildsettings.d +++ b/source/dub/compilers/buildsettings.d @@ -31,6 +31,7 @@ string[] linkerFiles; string[] sourceFiles; string[] copyFiles; + string[] extraDependencyFiles; string[] versions; string[] debugVersions; string[] versionFilters; @@ -71,6 +72,7 @@ addLinkerFiles(bs.linkerFiles); addSourceFiles(bs.sourceFiles); addCopyFiles(bs.copyFiles); + addExtraDependencyFiles(bs.extraDependencyFiles); addVersions(bs.versions); addDebugVersions(bs.debugVersions); addVersionFilters(bs.versionFilters); @@ -97,6 +99,7 @@ void prependSourceFiles(in string[] value...) { prepend(sourceFiles, value); } void removeSourceFiles(in string[] value...) { removePaths(sourceFiles, value); } void addCopyFiles(in string[] value...) { add(copyFiles, value); } + void addExtraDependencyFiles(in string[] value...) { add(extraDependencyFiles, value); } void addVersions(in string[] value...) { add(versions, value); } void addDebugVersions(in string[] value...) { add(debugVersions, value); } void addVersionFilters(in string[] value...) { add(versionFilters, value); } diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index 2670bcf..b087cd8 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -14,12 +14,14 @@ import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; +import dub.recipe.packagerecipe : ToolchainRequirements; import std.algorithm; import std.array; import std.conv; import std.exception; import std.process; +import std.typecons : Flag; /** Returns a compiler handler for a given binary name. @@ -89,6 +91,46 @@ /// Convert linker flags to compiler format string[] lflagsToDFlags(in string[] lflags) const; + /// Get the dependency requirement string for this compiler + string toolchainRequirementString(const ref ToolchainRequirements tr); + + /// Check whether the compiler meet the compiler requirement specified + /// in the recipe. + bool checkCompilerRequirement(const ref BuildPlatform platform, const ref ToolchainRequirements tr); + + /// Check if the compiler is supported by the recipe + final bool checkCompilerSupported(const ref ToolchainRequirements tr) + { + const str = toolchainRequirementString(tr); + return str != ToolchainRequirements.noKwd; + } + + /// Check whether the compiler meet the frontend requirement specified + /// in the recipe. + final bool checkFrontendRequirement(const ref BuildPlatform platform, const ref ToolchainRequirements tr) + { + import std.typecons : Yes; + + return checkRequirement(tr.frontend, platform.frontendVersionString, Yes.dmdVer); + } + + /// Check that a particular tool version matches with a given requirement + final bool checkRequirement(const string requirement, const string toolVer, const Flag!"dmdVer" dmdVer) + { + import dub.compilers.utils : dmdLikeVersionToSemverLike; + import dub.dependency : Dependency, Version; + import std.algorithm : all, map, splitter; + + if (!requirement.length) return true; // no requirement + + const ver = Version(dmdVer ? dmdLikeVersionToSemverLike(toolVer) : toolVer); + + return requirement + .splitter('|') + .map!(r => Dependency(dmdVer ? dmdLikeVersionToSemverLike(r) : r)) + .all!(r => r.matches(ver)); + } + /** Runs a tool and provides common boilerplate code. This method should be used by `Compiler` implementations to invoke the @@ -115,11 +157,24 @@ enforce(status == 0, format("%s failed with exit code %s.", args[0], status)); } - /// Compiles platform probe file with the specified compiler and parses its output. - protected final BuildPlatform probePlatform(string compiler_binary, string[] args, string arch_override) + /** Compiles platform probe file with the specified compiler and parses its output. + Params: + compiler_binary = binary to invoke compiler with + args = arguments for the probe compilation + arch_override = special handler for x86_mscoff + versionRes = array of regular expressions to scan the output + and find the compiler version. For each, the + version must be in capture index 1. The output + is scanned in multi-line mode (i.e. ^ will match any line start) + */ + protected final BuildPlatform probePlatform(string compiler_binary, string[] args, + string arch_override, string[] versionRes) { + import dub.compilers.utils : generatePlatformProbeFile, readPlatformJsonProbe; + import std.algorithm : filter, map; + import std.range : takeOne; + import std.regex : matchFirst, regex; import std.string : format; - import dub.compilers.utils : generatePlatformProbeFile, readPlatformProbe; auto fil = generatePlatformProbeFile(); @@ -127,12 +182,25 @@ enforce(result.status == 0, format("Failed to invoke the compiler %s to determine the build platform: %s", compiler_binary, result.output)); - auto build_platform = readPlatformProbe(result.output); + auto build_platform = readPlatformJsonProbe(result.output); build_platform.compilerBinary = compiler_binary; if (build_platform.compiler != this.name) { - logWarn(`The determined compiler type "%s" doesn't match the expected type "%s". This will probably result in build errors.`, - build_platform.compiler, this.name); + logWarn(`The determined compiler type "%s" doesn't match the expected type "%s". `~ + `This will probably result in build errors.`, build_platform.compiler, this.name); + } + + auto ver = versionRes + .map!(re => matchFirst(result.output, regex(re, "m"))) + .filter!(c => c.length > 1) + .map!(c => c[1]) + .takeOne(); + if (ver.empty) { + logWarn(`Could not probe the compiler version for "%s". ` ~ + `Toolchain requirements might be ineffective`, build_platform.compiler); + } + else { + build_platform.compilerVersion = ver.front; } // Hack: see #1059 diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index be04a79..d362413 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -12,6 +12,7 @@ import dub.internal.utils; import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.recipe.packagerecipe : ToolchainRequirements; import std.algorithm; import std.array; @@ -54,6 +55,32 @@ @property string name() const { return "dmd"; } + enum dmdVersionRe = `^version\s+v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; + + unittest { + import std.regex : matchFirst, regex; + auto probe = ` +binary dmd +version v2.082.0 +config /etc/dmd.conf +`; + auto re = regex(dmdVersionRe, "m"); + auto c = matchFirst(probe, re); + assert(c && c.length > 1 && c[1] == "2.082.0"); + } + + unittest { + import std.regex : matchFirst, regex; + auto probe = ` +binary dmd +version v2.084.0-beta.1 +config /etc/dmd.conf +`; + auto re = regex(dmdVersionRe, "m"); + auto c = matchFirst(probe, re); + assert(c && c.length > 1 && c[1] == "2.084.0-beta.1"); + } + BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) { string[] arch_flags; @@ -66,7 +93,12 @@ } settings.addDFlags(arch_flags); - return probePlatform(compiler_binary, arch_flags ~ ["-quiet", "-c", "-o-"], arch_override); + return probePlatform( + compiler_binary, + arch_flags ~ ["-quiet", "-c", "-o-", "-v"], + arch_override, + [ dmdVersionRe ] + ); } void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const @@ -233,6 +265,18 @@ return lflags.map!(f => "-L"~f)().array(); } + string toolchainRequirementString(const ref ToolchainRequirements tr) + { + return tr.dmd; + } + + bool checkCompilerRequirement(const ref BuildPlatform platform, const ref ToolchainRequirements tr) + { + import std.typecons : Yes; + + return checkRequirement(tr.dmd, platform.compilerVersion, Yes.dmdVer); + } + private auto escapeArgs(in string[] args) { return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index 3a79b7e..dc4d118 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -12,6 +12,7 @@ import dub.internal.utils; import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.recipe.packagerecipe : ToolchainRequirements; import std.algorithm; import std.array; @@ -53,6 +54,33 @@ @property string name() const { return "gdc"; } + enum gdcVersionRe1 = `GDC\s+(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; + enum gdcVersionRe2 = `^GNU D \(.*?\) version (\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; + enum gdcVersionRe3 = `^gcc version (\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; + + unittest { + import std.algorithm : equal, map; + import std.range : only; + import std.regex : matchFirst, regex; + auto probe = ` +Target: x86_64-pc-linux-gnu +Thread model: posix +gcc version 8.2.1 20180831 (GDC 8.2.1 based on D v2.068.2 built with ISL 0.20 for Arch Linux) +GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 +GNU D (GDC 8.2.1 based on D v2.068.2 built with ISL 0.20 for Arch Linux) version 8.2.1 20180831 (x86_64-pc-linux-gnu) +GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 +binary /usr/lib/gcc/x86_64-pc-linux-gnu/8.2.1/cc1d +version v2.068.2 +predefs GNU D_Version2 LittleEndian GNU_DWARF2_Exceptions GNU_StackGrowsDown GNU_InlineAsm D_LP64 D_PIC assert all X86_64 D_HardFloat Posix linux CRuntime_Glibc +`; + auto vers = only(gdcVersionRe1, gdcVersionRe2, gdcVersionRe3) + .map!(re => matchFirst(probe, regex(re, "m"))) + .filter!(c => c && c.length > 1) + .map!(c => c[1]); + + assert(vers.equal([ "8.2.1", "8.2.1", "8.2.1" ])); + } + BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) { string[] arch_flags; @@ -66,9 +94,12 @@ } settings.addDFlags(arch_flags); - return probePlatform(compiler_binary, - arch_flags ~ ["-S"], - arch_override); + return probePlatform( + compiler_binary, + arch_flags ~ ["-S", "-v"], + arch_override, + [ gdcVersionRe1, gdcVersionRe2, gdcVersionRe3 ] + ); } void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const @@ -225,6 +256,18 @@ return dflags; } + + final string toolchainRequirementString(const ref ToolchainRequirements tr) + { + return tr.gdc; + } + + final bool checkCompilerRequirement(const ref BuildPlatform platform, const ref ToolchainRequirements tr) + { + import std.typecons : No; + + return checkRequirement(tr.gdc, platform.compilerVersion, No.dmdVer); + } } private string extractTarget(const string[] args) { auto i = args.countUntil("-o"); return i >= 0 ? args[i+1] : null; } diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index 776bf4b..ae3ed22 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -12,6 +12,7 @@ import dub.internal.utils; import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.recipe.packagerecipe : ToolchainRequirements; import std.algorithm; import std.array; @@ -54,6 +55,20 @@ @property string name() const { return "ldc"; } + enum ldcVersionRe = `^version\s+v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; + + unittest { + import std.regex : matchFirst, regex; + auto probe = ` +binary /usr/bin/ldc2 +version 1.11.0 (DMD v2.081.2, LLVM 6.0.1) +config /etc/ldc2.conf (x86_64-pc-linux-gnu) +`; + auto re = regex(ldcVersionRe, "m"); + auto c = matchFirst(probe, re); + assert(c && c.length > 1 && c[1] == "1.11.0"); + } + BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) { string[] arch_flags; @@ -65,7 +80,12 @@ } settings.addDFlags(arch_flags); - return probePlatform(compiler_binary, arch_flags ~ ["-c", "-o-"], arch_override); + return probePlatform( + compiler_binary, + arch_flags ~ ["-c", "-o-", "-v"], + arch_override, + [ ldcVersionRe ] + ); } void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const @@ -213,6 +233,18 @@ return lflags.map!(s => "-L="~s)().array(); } + final string toolchainRequirementString(const ref ToolchainRequirements tr) + { + return tr.ldc; + } + + final bool checkCompilerRequirement(const ref BuildPlatform platform, const ref ToolchainRequirements tr) + { + import std.typecons : No; + + return checkRequirement(tr.ldc, platform.compilerVersion, No.dmdVer); + } + private auto escapeArgs(in string[] args) { return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); diff --git a/source/dub/compilers/utils.d b/source/dub/compilers/utils.d index 6e57867..fc60a35 100644 --- a/source/dub/compilers/utils.d +++ b/source/dub/compilers/utils.d @@ -237,6 +237,56 @@ if (got_preamble) logWarn(""); } +/** + Turn a DMD-like version (e.g. 2.082.1) into a SemVer-like version (e.g. 2.82.1). + The function accepts a dependency operator prefix and some text postfix. + Prefix and postfix are returned verbatim. + Params: + ver = version string, possibly with a dependency operator prefix and some + test postfix. + Returns: + A Semver compliant string +*/ +string dmdLikeVersionToSemverLike(string ver) +{ + import std.algorithm : countUntil, joiner, map, skipOver, splitter; + import std.array : join, split; + import std.ascii : isDigit; + import std.conv : text; + import std.exception : enforce; + import std.functional : not; + import std.range : padRight; + + const start = ver.countUntil!isDigit; + enforce(start != -1, "Invalid semver: "~ver); + const prefix = ver[0 .. start]; + ver = ver[start .. $]; + + const end = ver.countUntil!(c => !c.isDigit && c != '.'); + const postfix = end == -1 ? null : ver[end .. $]; + auto verStr = ver[0 .. $-postfix.length]; + + auto comps = verStr + .splitter(".") + .map!((a) { if (a.length > 1) a.skipOver("0"); return a;}) + .padRight("0", 3); + + return text(prefix, comps.joiner("."), postfix); +} + +/// +unittest { + assert(dmdLikeVersionToSemverLike("2.082.1") == "2.82.1"); + assert(dmdLikeVersionToSemverLike("2.082.0") == "2.82.0"); + assert(dmdLikeVersionToSemverLike("2.082") == "2.82.0"); + assert(dmdLikeVersionToSemverLike("~>2.082") == "~>2.82.0"); + assert(dmdLikeVersionToSemverLike("~>2.082-beta1") == "~>2.82.0-beta1"); + assert(dmdLikeVersionToSemverLike("2.4.6") == "2.4.6"); + assert(dmdLikeVersionToSemverLike("2.4.6-alpha12") == "2.4.6-alpha12"); +} + +private enum probeBeginMark = "__dub_probe_begin__"; +private enum probeEndMark = "__dub_probe_end__"; /** Generate a file that will give, at compile time, information about the compiler (architecture, frontend version...) @@ -264,6 +314,7 @@ return res; } + pragma(msg, } ~ `"` ~ probeBeginMark ~ `"` ~q{ ); pragma(msg, `{`); pragma(msg,` "compiler": "`~ determineCompiler() ~ `",`); pragma(msg, ` "frontendVersion": ` ~ toString!__VERSION__ ~ `,`); @@ -275,6 +326,7 @@ pragma(msg, ` ` ~ determineArchitecture().stringArray); pragma(msg, ` ],`); pragma(msg, `}`); + pragma(msg, } ~ `"` ~ probeEndMark ~ `"` ~ q{ ); string[] determinePlatform() } ~ '{' ~ platformCheck ~ '}' ~ q{ string[] determineArchitecture() } ~ '{' ~ archCheck ~ '}' ~ q{ @@ -288,11 +340,11 @@ } /** - Processes the output generated by compiling the platform probe file. + Processes the JSON output generated by compiling the platform probe file. See_Also: `generatePlatformProbeFile`. */ -BuildPlatform readPlatformProbe(string output) +BuildPlatform readPlatformJsonProbe(string output) { import std.algorithm : map; import std.array : array; @@ -300,11 +352,11 @@ import std.string; // work around possible additional output of the compiler - auto idx1 = output.indexOf("{"); - auto idx2 = output.lastIndexOf("}"); + auto idx1 = output.indexOf(probeBeginMark); + auto idx2 = output.lastIndexOf(probeEndMark); enforce(idx1 >= 0 && idx1 < idx2, "Unexpected platform information output - does not contain a JSON object."); - output = output[idx1 .. idx2+1]; + output = output[idx1+probeBeginMark.length .. idx2]; import dub.internal.vibecompat.data.json; auto json = parseJsonString(output); diff --git a/source/dub/description.d b/source/dub/description.d index d38181a..7758751 100644 --- a/source/dub/description.d +++ b/source/dub/description.d @@ -85,6 +85,7 @@ string[] lflags; /// Flags passed to the linker string[] libs; /// Librariy names to link against (typically using "-l") string[] copyFiles; /// Files to copy to the target directory + string[] extraDependencyFiles; /// Files to check for rebuild dub project string[] versions; /// D version identifiers to set string[] debugVersions; /// D debug version identifiers to set string[] importPaths; diff --git a/source/dub/dub.d b/source/dub/dub.d index 027e349..dff7a97 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -87,12 +87,14 @@ */ PackageSupplier getRegistryPackageSupplier(string url) { - switch (url.startsWith("dub+", "mvn+")) + switch (url.startsWith("dub+", "mvn+", "file://")) { case 1: return new RegistryPackageSupplier(URL(url[4..$])); case 2: return new MavenRegistryPackageSupplier(URL(url[4..$])); + case 3: + return new FileSystemPackageSupplier(NativePath(url[7..$])); default: return new RegistryPackageSupplier(URL(url)); } @@ -108,6 +110,9 @@ auto mavenRegistryPackageSupplier = getRegistryPackageSupplier("mvn+http://localhost:8040/maven/libs-release/dubpackages"); assert(mavenRegistryPackageSupplier.description.canFind(" http://localhost:8040/maven/libs-release/dubpackages")); + + auto fileSystemPackageSupplier = getRegistryPackageSupplier("file:///etc/dubpackages"); + assert(fileSystemPackageSupplier.description.canFind(" " ~ NativePath("/etc/dubpackages").toNativeString)); } /** Provides a high-level entry point for DUB's functionality. diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index 3586aa7..c88535d 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -46,6 +46,52 @@ { scope (exit) cleanupTemporaries(); + auto dcl = settings.compiler; + + void checkPkgRequirements(const(Package) pkg) + { + import std.format : format; + + const tr = pkg.recipe.toolchainRequirements; + + enforce ( + dcl.checkCompilerSupported(tr), + format( + "Installed %s-%s is not supported by %s. Supported compiler(s):\n%s", + dcl.name, settings.platform.compilerVersion, pkg.name, + tr.supportedCompilers.map!((string[2] cs) { + auto str = " - " ~ cs[0]; + if (cs[1].length) str ~= ": "~cs[1]; + return str; + }).join("\n") + ) + ); + + enforce( + dcl.checkCompilerRequirement(settings.platform, tr), + format( + "Installed %s-%s does not comply with %s compiler requirement: %s %s\n" ~ + "Please consider upgrading your installation.", + dcl.name, settings.platform.compilerVersion, + pkg.name, dcl.name, dcl.toolchainRequirementString( tr ) + ) + ); + enforce( + dcl.checkFrontendRequirement(settings.platform, tr), + format( + "Installed %s-%s with frontend %s does not comply with %s frontend requirement: %s\n" ~ + "Please consider upgrading your installation.", + dcl.name, settings.platform.compilerVersion, + settings.platform.frontendVersionString, pkg.name, tr.frontend + ) + ); + } + + checkPkgRequirements(m_project.rootPackage); + foreach (pkg; m_project.dependencies) { + checkPkgRequirements(pkg); + } + auto root_ti = targets[m_project.rootPackage.name]; enforce(!(settings.rdmd && root_ti.buildSettings.targetType == TargetType.none), @@ -376,6 +422,7 @@ allfiles ~= buildsettings.sourceFiles; allfiles ~= buildsettings.importFiles; allfiles ~= buildsettings.stringImportFiles; + allfiles ~= buildsettings.extraDependencyFiles; // TODO: add library files foreach (p; packages) allfiles ~= (p.recipePath != NativePath.init ? p : p.basePackage).recipePath.toNativeString(); diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index 42ecdf1..6466e5b 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -16,7 +16,7 @@ import core.time : Duration; import std.algorithm : canFind, startsWith; -import std.array : appender; +import std.array : appender, array; import std.conv : to; import std.exception : enforce; import std.file; @@ -130,6 +130,39 @@ return parseJsonString(text, file.toNativeString()); } +/** + Read package info file content from archive. + File needs to be in root folder or in first + sub folder. + + Params: + zip = path to archive file + fileName = Package file name + Returns: + package file content. +*/ +string packageInfoFileFromZip(NativePath zip, out string fileName) { + import std.zip : ZipArchive, ArchiveMember; + import dub.package_ : packageInfoFiles; + + auto f = openFile(zip, FileMode.read); + ubyte[] b = new ubyte[cast(size_t)f.size]; + f.rawRead(b); + f.close(); + auto archive = new ZipArchive(b); + alias PSegment = typeof (NativePath.init.head); + foreach (ArchiveMember am; archive.directory) { + auto path = NativePath(am.name).bySegment.array; + foreach (fil; packageInfoFiles) { + if ((path.length == 1 && path[0] == fil.filename) || (path.length == 2 && path[$-1].toString == fil.filename)) { + fileName = fil.filename; + return stripUTF8Bom(cast(string) archive.expand(archive.directory[am.name])); + } + } + } + throw new Exception("No package descriptor found"); +} + Json jsonFromZip(NativePath zip, string filename) { import std.zip : ZipArchive; auto f = openFile(zip, FileMode.read); diff --git a/source/dub/internal/vibecompat/data/json.d b/source/dub/internal/vibecompat/data/json.d index 3671ddf..0000184 100644 --- a/source/dub/internal/vibecompat/data/json.d +++ b/source/dub/internal/vibecompat/data/json.d @@ -136,7 +136,7 @@ /** Allows assignment of D values to a JSON value. */ - ref Json opAssign(Json v) + ref Json opAssign(Json v) return { m_type = v.m_type; final switch(m_type){ diff --git a/source/dub/package_.d b/source/dub/package_.d index 5da9201..1c1858e 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -116,6 +116,7 @@ // use the given recipe as the basis m_info = recipe; + checkDubRequirements(); fillWithDefaults(); } @@ -635,6 +636,32 @@ return ret; } + private void checkDubRequirements() + { + import dub.dependency : Dependency; + import dub.semver : isValidVersion; + import dub.version_ : dubVersion; + import std.exception : enforce; + + if (m_info.toolchainRequirements.dub.length) { + const dep = Dependency(m_info.toolchainRequirements.dub); + + static assert(dubVersion.length); + static if (dubVersion[0] == 'v') { + enum dv = dubVersion[1 .. $]; + } + else { + enum dv = dubVersion; + } + static assert(isValidVersion(dv)); + + enforce(dep.matches(dv), + "dub-"~dv~" does not comply with toolchainRequirements.dub " ~ + "specification: "~m_info.toolchainRequirements.dub~ + "\nPlease consider upgrading your DUB installation"); + } + } + private void fillWithDefaults() { auto bs = &m_info.buildSettings; diff --git a/source/dub/packagesuppliers/filesystem.d b/source/dub/packagesuppliers/filesystem.d index 323e47b..61b125c 100644 --- a/source/dub/packagesuppliers/filesystem.d +++ b/source/dub/packagesuppliers/filesystem.d @@ -50,9 +50,19 @@ Json fetchPackageRecipe(string packageId, Dependency dep, bool pre_release) { - import dub.internal.utils : jsonFromZip; - auto filename = bestPackageFile(packageId, dep, pre_release); - return jsonFromZip(filename, "dub.json"); + import std.array : split; + import std.path : stripExtension; + import dub.internal.utils : packageInfoFileFromZip; + import dub.recipe.io : parsePackageRecipe; + import dub.recipe.json : toJson; + + auto filePath = bestPackageFile(packageId, dep, pre_release); + string packageFileName; + string packageFileContent = packageInfoFileFromZip(filePath, packageFileName); + auto recipe = parsePackageRecipe(packageFileContent, packageFileName); + Json json = toJson(recipe); + json["version"] = filePath.toNativeString().split("-")[$-1].stripExtension(); + return json; } SearchResult[] searchPackages(string query) diff --git a/source/dub/platform.d b/source/dub/platform.d index 1d91982..88da7b2 100644 --- a/source/dub/platform.d +++ b/source/dub/platform.d @@ -234,4 +234,25 @@ string compilerBinary; /// Compiled frontend version (e.g. `2067` for frontend versions 2.067.x) int frontendVersion; + /// Compiler version e.g. "1.11.0" + string compilerVersion; + /// Frontend version string from frontendVersion + /// e.g: 2067 => "2.067" + string frontendVersionString() const + { + import std.format : format; + + const maj = frontendVersion / 1000; + const min = frontendVersion % 1000; + return format("%d.%03d", maj, min); + } + /// + unittest + { + BuildPlatform bp; + bp.frontendVersion = 2067; + assert(bp.frontendVersionString == "2.067"); + } } + + diff --git a/source/dub/project.d b/source/dub/project.d index 5171433..4b4e85b 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -955,6 +955,7 @@ case "working-directory": case "string-import-files": case "copy-files": + case "extra-dependency-files": case "pre-generate-commands": case "post-generate-commands": case "pre-build-commands": @@ -985,6 +986,7 @@ case "linker-files": return listBuildSetting!"linkerFiles"(args); case "source-files": return listBuildSetting!"sourceFiles"(args); case "copy-files": return listBuildSetting!"copyFiles"(args); + case "extra-dependency-files": return listBuildSetting!"extraDependencyFiles"(args); case "versions": return listBuildSetting!"versions"(args); case "debug-versions": return listBuildSetting!"debugVersions"(args); case "import-paths": return listBuildSetting!"importPaths"(args); @@ -1153,6 +1155,7 @@ dst.addImportFiles(processVars(project, pack, gsettings, settings.importFiles, true)); dst.addStringImportFiles(processVars(project, pack, gsettings, settings.stringImportFiles, true)); dst.addCopyFiles(processVars(project, pack, gsettings, settings.copyFiles, true)); + dst.addExtraDependencyFiles(processVars(project, pack, gsettings, settings.extraDependencyFiles, true)); dst.addVersions(processVars(project, pack, gsettings, settings.versions)); dst.addDebugVersions(processVars(project, pack, gsettings, settings.debugVersions)); dst.addVersionFilters(processVars(project, pack, gsettings, settings.versionFilters)); diff --git a/source/dub/recipe/json.d b/source/dub/recipe/json.d index 418f827..c1fd9a1 100644 --- a/source/dub/recipe/json.d +++ b/source/dub/recipe/json.d @@ -41,6 +41,9 @@ recipe.buildTypes[name] = bs; } break; + case "toolchainRequirements": + recipe.toolchainRequirements.parseJson(value); + break; case "-ddoxFilterArgs": recipe.ddoxFilterArgs = deserializeJson!(string[])(value); break; case "-ddoxTool": recipe.ddoxTool = value.get!string; break; } @@ -103,6 +106,9 @@ types[name] = settings.toJson(); ret["buildTypes"] = types; } + if (recipe.hasToolchainRequirements) { + ret["toolchainRequirements"] = recipe.toolchainRequirements.toJson(); + } if (!recipe.ddoxFilterArgs.empty) ret["-ddoxFilterArgs"] = recipe.ddoxFilterArgs.serializeToJson(); if (!recipe.ddoxTool.empty) ret["-ddoxTool"] = recipe.ddoxTool; return ret; @@ -212,6 +218,7 @@ case "sourcePath": bs.sourcePaths[suffix] ~= [value.get!string]; break; // deprecated case "excludedSourceFiles": bs.excludedSourceFiles[suffix] = deserializeJson!(string[])(value); break; case "copyFiles": bs.copyFiles[suffix] = deserializeJson!(string[])(value); break; + case "extraDependencyFiles": bs.extraDependencyFiles[suffix] = deserializeJson!(string[])(value); break; case "versions": bs.versions[suffix] = deserializeJson!(string[])(value); break; case "debugVersions": bs.debugVersions[suffix] = deserializeJson!(string[])(value); break; case "-versionFilters": bs.versionFilters[suffix] = deserializeJson!(string[])(value); break; @@ -263,6 +270,7 @@ foreach (suffix, arr; bs.sourcePaths) ret["sourcePaths"~suffix] = serializeToJson(arr); foreach (suffix, arr; bs.excludedSourceFiles) ret["excludedSourceFiles"~suffix] = serializeToJson(arr); foreach (suffix, arr; bs.copyFiles) ret["copyFiles"~suffix] = serializeToJson(arr); + foreach (suffix, arr; bs.extraDependencyFiles) ret["extraDependencyFiles"~suffix] = serializeToJson(arr); foreach (suffix, arr; bs.versions) ret["versions"~suffix] = serializeToJson(arr); foreach (suffix, arr; bs.debugVersions) ret["debugVersions"~suffix] = serializeToJson(arr); foreach (suffix, arr; bs.versionFilters) ret["-versionFilters"~suffix] = serializeToJson(arr); @@ -289,3 +297,49 @@ } return ret; } + +private void parseJson(ref ToolchainRequirements tr, Json json) +{ + foreach (string name, value; json) { + switch (name) { + case "dub": + tr.dub = value.get!string(); + break; + case "frontend": + tr.frontend = value.get!string(); + break; + case "dmd": + tr.dmd = value.get!string(); + break; + case "ldc": + tr.ldc = value.get!string(); + break; + case "gdc": + tr.gdc = value.get!string(); + break; + default: + break; + } + } +} + +private Json toJson(in ref ToolchainRequirements tr) +{ + auto ret = Json.emptyObject; + if (tr.dub.length) { + ret["dub"] = serializeToJson(tr.dub); + } + if (tr.frontend.length) { + ret["frontend"] = serializeToJson(tr.frontend); + } + if (tr.dmd.length) { + ret["dmd"] = serializeToJson(tr.dmd); + } + if (tr.ldc.length) { + ret["ldc"] = serializeToJson(tr.ldc); + } + if (tr.gdc.length) { + ret["gdc"] = serializeToJson(tr.gdc); + } + return ret; +} diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d index d4382b2..f1d3c3e 100644 --- a/source/dub/recipe/packagerecipe.d +++ b/source/dub/recipe/packagerecipe.d @@ -86,6 +86,8 @@ ConfigurationInfo[] configurations; BuildSettingsTemplate[string] buildTypes; + ToolchainRequirements toolchainRequirements; + SubPackage[] subPackages; inout(ConfigurationInfo) getConfiguration(string name) @@ -96,6 +98,17 @@ throw new Exception("Unknown configuration: "~name); } + bool hasToolchainRequirements() const + { + import std.algorithm : any; + import std.range : only; + + with (toolchainRequirements) { + return only(dub, frontend, dmd, ldc, gdc) + .any!(r => r.length != 0); + } + } + /** Clones the package recipe recursively. */ PackageRecipe clone() const { return .clone(this); } @@ -107,6 +120,33 @@ PackageRecipe recipe; } +/// Describes minimal toolchain requirements +struct ToolchainRequirements +{ + /// DUB version requirement + string dub; + /// D front-end version requirement + string frontend; + /// DMD version requirement + string dmd; + /// LDC version requirement + string ldc; + /// GDC version requirement + string gdc; + + package(dub) enum noKwd = "no"; + /// Get the list of supported compilers. + /// Returns: array of couples of compiler name and compiler requirement + @property string[2][] supportedCompilers() const + { + string[2][] res; + if (dmd != noKwd) res ~= [ "dmd", dmd ]; + if (ldc != noKwd) res ~= [ "ldc", ldc ]; + if (gdc != noKwd) res ~= [ "gdc", gdc ]; + return res; + } +} + /// Bundles information about a build configuration. struct ConfigurationInfo { @@ -150,6 +190,7 @@ string[][string] sourcePaths; string[][string] excludedSourceFiles; string[][string] copyFiles; + string[][string] extraDependencyFiles; string[][string] versions; string[][string] debugVersions; string[][string] versionFilters; @@ -237,6 +278,7 @@ getPlatformSetting!("sourceFiles", "addSourceFiles")(dst, platform); getPlatformSetting!("excludedSourceFiles", "removeSourceFiles")(dst, platform); getPlatformSetting!("copyFiles", "addCopyFiles")(dst, platform); + getPlatformSetting!("extraDependencyFiles", "addExtraDependencyFiles")(dst, platform); getPlatformSetting!("versions", "addVersions")(dst, platform); getPlatformSetting!("debugVersions", "addDebugVersions")(dst, platform); getPlatformSetting!("versionFilters", "addVersionFilters")(dst, platform); diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d index cecca7c..d3aaf14 100644 --- a/source/dub/recipe/sdl.d +++ b/source/dub/recipe/sdl.d @@ -50,6 +50,9 @@ parseBuildSettings(n, bt, parent_name); recipe.buildTypes[name] = bt; break; + case "toolchainRequirements": + parseToolchainRequirements(recipe.toolchainRequirements, n); + break; case "x:ddoxFilterArgs": recipe.ddoxFilterArgs ~= n.stringArrayTagValue; break; case "x:ddoxTool": recipe.ddoxTool = n.stringTagValue; break; } @@ -101,6 +104,9 @@ t.add(settings.toSDL()); ret.add(t); } + if (recipe.hasToolchainRequirements) { + ret.add(toSDL(recipe.toolchainRequirements)); + } if (recipe.ddoxFilterArgs.length) ret.add(new Tag("x", "ddoxFilterArgs", recipe.ddoxFilterArgs.map!(a => Value(a)).array)); if (recipe.ddoxTool.length) ret.add(new Tag("x", "ddoxTool", [Value(recipe.ddoxTool)])); @@ -148,6 +154,7 @@ case "excludedSourceFiles": setting.parsePlatformStringArray(bs.excludedSourceFiles); break; case "mainSourceFile": bs.mainSourceFile = setting.stringTagValue; break; case "copyFiles": setting.parsePlatformStringArray(bs.copyFiles); break; + case "extraDependencyFiles": setting.parsePlatformStringArray(bs.extraDependencyFiles); break; case "versions": setting.parsePlatformStringArray(bs.versions); break; case "debugVersions": setting.parsePlatformStringArray(bs.debugVersions); break; case "x:versionFilters": setting.parsePlatformStringArray(bs.versionFilters); break; @@ -253,6 +260,7 @@ foreach (suffix, arr; bs.sourcePaths) adda("sourcePaths", suffix, arr); foreach (suffix, arr; bs.excludedSourceFiles) adda("excludedSourceFiles", suffix, arr); foreach (suffix, arr; bs.copyFiles) adda("copyFiles", suffix, arr); + foreach (suffix, arr; bs.extraDependencyFiles) adda("extraDependencyFiles", suffix, arr); foreach (suffix, arr; bs.versions) adda("versions", suffix, arr); foreach (suffix, arr; bs.debugVersions) adda("debugVersions", suffix, arr); foreach (suffix, arr; bs.versionFilters) adda("versionFilters", suffix, arr, "x"); @@ -270,6 +278,52 @@ return ret; } +private void parseToolchainRequirements(ref ToolchainRequirements tr, Tag tag) +{ + foreach (attr; tag.attributes) { + switch (attr.name) { + case "dub": + tr.dub = attr.value.get!string(); + break; + case "frontend": + tr.frontend = attr.value.get!string(); + break; + case "dmd": + tr.dmd = attr.value.get!string(); + break; + case "ldc": + tr.ldc = attr.value.get!string(); + break; + case "gdc": + tr.gdc = attr.value.get!string(); + break; + default: + break; + } + } +} + +private Tag toSDL(const ref ToolchainRequirements tr) +{ + Attribute[] attrs; + if (tr.dub.length) { + attrs ~= new Attribute("dub", Value(tr.dub)); + } + if (tr.frontend.length) { + attrs ~= new Attribute("frontend", Value(tr.frontend)); + } + if (tr.dmd.length) { + attrs ~= new Attribute("dmd", Value(tr.dmd)); + } + if (tr.ldc.length) { + attrs ~= new Attribute("ldc", Value(tr.ldc)); + } + if (tr.gdc.length) { + attrs ~= new Attribute("gdc", Value(tr.gdc)); + } + return new Tag(null, "toolchainRequirements", null, attrs); +} + private string expandPackageName(string name, string parent_name, Tag tag) { import std.algorithm : canFind; @@ -367,6 +421,7 @@ buildType "release" { dflags "-release" "-O" } +toolchainRequirements dub="~>1.11.0" dmd="~>2.082" x:ddoxFilterArgs "-arg1" "-arg2" x:ddoxFilterArgs "-arg3" x:ddoxTool "ddoxtool" @@ -392,6 +447,8 @@ mainSourceFile "main source" copyFiles "copy1" "copy2" copyFiles "copy3" +extraDependencyFiles "extradepfile1" "extradepfile2" +extraDependencyFiles "extradepfile3" versions "version1" "version2" versions "version3" debugVersions "debug1" "debug2" @@ -453,6 +510,11 @@ assert(rec.buildTypes.length == 2); assert(rec.buildTypes["debug"].dflags == ["": ["-g", "-debug"]]); assert(rec.buildTypes["release"].dflags == ["": ["-release", "-O"]]); + assert(rec.toolchainRequirements.dub == "~>1.11.0"); + assert(rec.toolchainRequirements.frontend is null); + assert(rec.toolchainRequirements.dmd == "~>2.082"); + assert(rec.toolchainRequirements.ldc is null); + assert(rec.toolchainRequirements.gdc is null); assert(rec.ddoxFilterArgs == ["-arg1", "-arg2", "-arg3"], rec.ddoxFilterArgs.to!string); assert(rec.ddoxTool == "ddoxtool"); assert(rec.buildSettings.dependencies.length == 2); @@ -476,6 +538,7 @@ assert(rec.buildSettings.excludedSourceFiles == ["": ["excluded1", "excluded2", "excluded3"]]); assert(rec.buildSettings.mainSourceFile == "main source"); assert(rec.buildSettings.copyFiles == ["": ["copy1", "copy2", "copy3"]]); + assert(rec.buildSettings.extraDependencyFiles == ["": ["extradepfile1", "extradepfile2", "extradepfile3"]]); assert(rec.buildSettings.versions == ["": ["version1", "version2", "version3"]]); assert(rec.buildSettings.debugVersions == ["": ["debug1", "debug2", "debug3"]]); assert(rec.buildSettings.versionFilters == ["": ["version1", "version2", "version3"]]); diff --git a/test/interactive-remove.sh b/test/interactive-remove.sh index 85053b8..d00a41c 100755 --- a/test/interactive-remove.sh +++ b/test/interactive-remove.sh @@ -23,7 +23,7 @@ die $LINENO 'Failed to remove all version of dub' fi $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 ] +$DUB fetch dub@1.10.0 && [ -d $HOME/.dub/packages/dub-1.10.0/dub ] # is non-interactive with --version= $DUB remove dub --version=\* if [ -d $HOME/.dub/packages/dub-1.9.0/dub ] || [ -d $HOME/.dub/packages/dub-1.10.0/dub ]; then diff --git a/test/issue1117-extra-dependency-files.sh b/test/issue1117-extra-dependency-files.sh new file mode 100755 index 0000000..1059dbc --- /dev/null +++ b/test/issue1117-extra-dependency-files.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +. $(dirname "${BASH_SOURCE[0]}")/common.sh +cd ${CURR_DIR}/issue1117-extra-dependency-files + + +if ! { ${DUB} build 2>&1 || true; } | grep -cF 'building configuration'; then + die $LINENO 'Build was not executed.' +fi + +if ! { ${DUB} build 2>&1 || true; } | grep -cF 'is up to date'; then + die $LINENO 'Build was executed.' +fi + +touch ./dependency.txt + +if ! { ${DUB} build 2>&1 || true; } | grep -cF 'building configuration'; then + die $LINENO 'Build was not executed.' +fi diff --git a/test/issue1117-extra-dependency-files/.gitignore b/test/issue1117-extra-dependency-files/.gitignore new file mode 100644 index 0000000..304e955 --- /dev/null +++ b/test/issue1117-extra-dependency-files/.gitignore @@ -0,0 +1,8 @@ +.dub +docs.json +__dummy.html +docs/ +*.exe +*.o +*.obj +*.lst diff --git a/test/issue1117-extra-dependency-files/.no_build b/test/issue1117-extra-dependency-files/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1117-extra-dependency-files/.no_build diff --git a/test/issue1117-extra-dependency-files/dependency.txt b/test/issue1117-extra-dependency-files/dependency.txt new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/test/issue1117-extra-dependency-files/dependency.txt @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/test/issue1117-extra-dependency-files/dub.json b/test/issue1117-extra-dependency-files/dub.json new file mode 100644 index 0000000..00e57a9 --- /dev/null +++ b/test/issue1117-extra-dependency-files/dub.json @@ -0,0 +1,4 @@ +{ + "name": "test", + "extraDependencyFiles": ["dependency.txt"] +} \ No newline at end of file diff --git a/test/issue1117-extra-dependency-files/source/app.d b/test/issue1117-extra-dependency-files/source/app.d new file mode 100644 index 0000000..ef93217 --- /dev/null +++ b/test/issue1117-extra-dependency-files/source/app.d @@ -0,0 +1 @@ +void main(){} \ No newline at end of file diff --git a/test/issue1401-file-system-pkg-supplier/.no_build b/test/issue1401-file-system-pkg-supplier/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1401-file-system-pkg-supplier/.no_build diff --git a/test/issue1401-file-system-pkg-supplier/fs-json-dubpackage-1.0.7.zip b/test/issue1401-file-system-pkg-supplier/fs-json-dubpackage-1.0.7.zip new file mode 100644 index 0000000..1b9915e --- /dev/null +++ b/test/issue1401-file-system-pkg-supplier/fs-json-dubpackage-1.0.7.zip Binary files differ diff --git a/test/issue1401-file-system-pkg-supplier/fs-sdl-dubpackage-1.0.5.zip b/test/issue1401-file-system-pkg-supplier/fs-sdl-dubpackage-1.0.5.zip new file mode 100644 index 0000000..6e58d2f --- /dev/null +++ b/test/issue1401-file-system-pkg-supplier/fs-sdl-dubpackage-1.0.5.zip Binary files differ diff --git a/test/issue1401-file-system-pkg-supplier/fs-sdl-dubpackage-1.0.6.zip b/test/issue1401-file-system-pkg-supplier/fs-sdl-dubpackage-1.0.6.zip new file mode 100644 index 0000000..76f3dcf --- /dev/null +++ b/test/issue1401-file-system-pkg-supplier/fs-sdl-dubpackage-1.0.6.zip Binary files differ diff --git a/test/issue1401-filesystem-supplier.sh b/test/issue1401-filesystem-supplier.sh new file mode 100755 index 0000000..1367fc6 --- /dev/null +++ b/test/issue1401-filesystem-supplier.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +DIR=$(dirname "${BASH_SOURCE[0]}") + +. "$DIR"/common.sh + +dub remove fs-json-dubpackage --non-interactive --version=* 2>/dev/null || true +dub remove fs-sdl-dubpackage --non-interactive --version=* 2>/dev/null || true + +echo "Trying to get fs-sdl-dubpackage (1.0.5)" +"$DUB" fetch fs-sdl-dubpackage --version=1.0.5 --skip-registry=all --registry=file://"$DIR"/issue1401-file-system-pkg-supplier + +if ! dub remove fs-sdl-dubpackage --non-interactive --version=1.0.5 2>/dev/null; then + die 'DUB did not install package from file system.' +fi + +echo "Trying to get fs-sdl-dubpackage (latest)" +"$DUB" fetch fs-sdl-dubpackage --skip-registry=all --registry=file://"$DIR"/issue1401-file-system-pkg-supplier + +if ! dub remove fs-sdl-dubpackage --non-interactive --version=1.0.6 2>/dev/null; then + die 'DUB did not install latest package from file system.' +fi + +echo "Trying to get fs-json-dubpackage (1.0.7)" +"$DUB" fetch fs-json-dubpackage --version=1.0.7 --skip-registry=all --registry=file://"$DIR"/issue1401-file-system-pkg-supplier + +if ! dub remove fs-json-dubpackage --non-interactive --version=1.0.7 2>/dev/null; then + die 'DUB did not install package from file system.' +fi diff --git a/test/issue1531-toolchain-requirements.sh b/test/issue1531-toolchain-requirements.sh new file mode 100755 index 0000000..77bf5f4 --- /dev/null +++ b/test/issue1531-toolchain-requirements.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -e + +. $(dirname "${BASH_SOURCE[0]}")/common.sh + +cat << EOF | $DUB - || die "Did not pass without toolchainRequirements" +/+ dub.sdl: ++/ +void main() {} +EOF + +# pass test dub requirement given as $1 +function test_dub_req_pass { + cat << EOF | $DUB - || die "Did not pass requirement dub=\"$1\"" +/+ dub.sdl: + toolchainRequirements dub="$1" ++/ +void main() {} +EOF +} + +# fail test dub requirement given as $1 +function test_dub_req_fail { + ! cat << EOF | $DUB - || die "Did not pass requirement dub=\"$1\"" +/+ dub.sdl: + toolchainRequirements dub="$1" ++/ +void main() {} +EOF +} + +test_dub_req_pass ">=1.7.0" +test_dub_req_fail "~>0.9" +test_dub_req_fail "~>999.0" + +# extract compiler version +if [[ $DC == *ldc* ]] || [[ $DC == *ldmd* ]]; then + VER_REG='\((([[:digit:]]+)(\.[[:digit:]]+\.[[:digit:]]+[A-Za-z0-9.+-]*))\)' + DC_NAME=ldc +elif [[ $DC == *dmd* ]]; then + VER_REG='v(([[:digit:]]+)(\.[[:digit:]]+\.[[:digit:]]+[A-Za-z0-9.+-]*))' + DC_NAME=dmd +elif [[ $DC == *gdc* ]]; then + VER_REG='\) (([[:digit:]]+)(\.[[:digit:]]+\.[[:digit:]]+[A-Za-z0-9.+-]*))' + DC_NAME=gdc +else + die "Did not recognize compiler" +fi +if [[ $($DC --version) =~ $VER_REG ]]; then + DC_VER=${BASH_REMATCH[1]} + DC_VER_MAJ=${BASH_REMATCH[2]} + DC_VER_REM=${BASH_REMATCH[3]} + $DC --version + echo $DC version is $DC_VER +else + $DC --version + die "Could not extract compiler version" +fi + +# create test app directory +TMPDIR=$(mktemp -d /tmp/dubtest1531_XXXXXX) +mkdir -p $TMPDIR/source +cat << EOF > $TMPDIR/source/app.d +module dubtest1531; +void main() {} +EOF + +# write dub.sdl with compiler requirement given as $1 +function write_cl_req { + cat << EOF > $TMPDIR/dub.sdl +name "dubtest1531" +toolchainRequirements ${DC_NAME}="$1" +EOF +} + +# pass test compiler requirement given as $1 +function test_cl_req_pass { + write_cl_req $1 + $DUB --compiler=$DC --root=$TMPDIR || die "Did not pass with $DC_NAME=\"$1\"" +} + +# fail test compiler requirement given as $1 +function test_cl_req_fail { + write_cl_req $1 + ! $DUB --compiler=$DC --root=$TMPDIR || die "Did not fail with $DC_NAME=\"$1\"" +} + + +test_cl_req_pass "==$DC_VER" +test_cl_req_pass ">=$DC_VER" +test_cl_req_fail ">$DC_VER" +test_cl_req_pass "<=$DC_VER" +test_cl_req_fail "<$DC_VER" +test_cl_req_pass ">=$DC_VER|<$(($DC_VER_MAJ + 1))$DC_VER_REM" +test_cl_req_pass "~>$DC_VER" +test_cl_req_fail "~>$(($DC_VER_MAJ + 1))$DC_VER_REM" +test_cl_req_fail no + +rm -rf $TMPDIR diff --git a/test/issue1574-addcommand.sh b/test/issue1574-addcommand.sh index 002acb1..dddedca 100755 --- a/test/issue1574-addcommand.sh +++ b/test/issue1574-addcommand.sh @@ -28,7 +28,7 @@ $DUB add gitcompatibledubpackage=1.0.2 non-existing-issue1574-pkg='~>9.8.7' --skip-registry=all grep -q '"gitcompatibledubpackage"\s*:\s*"1\.0\.2"' dub.json grep -q '"non-existing-issue1574-pkg"\s*:\s*"~>9\.8\.7"' dub.json -if $DUB add foo=1.2.3 gitcompatibledubpackage='~>a.b.c' --skip-registry=all; then +if $DUB add foo@1.2.3 gitcompatibledubpackage='~>a.b.c' --skip-registry=all; then die $LINENO 'Adding non-semver spec should error' fi if grep -q '"foo"' dub.json; then