diff --git a/build.cmd b/build.cmd index f6d6a34..ece1cbb 100644 --- a/build.cmd +++ b/build.cmd @@ -7,7 +7,7 @@ @echo enum dubVersion = "%GITVER%"; >> source\dub\version_.d @echo Executing %DC%... -@%DC% -ofbin\dub.exe -g -debug -w -version=DubUseCurl -version=DubApplication -Isource curl.lib %* @build-files.txt +@%DC% -ofbin\dub.exe -g -O -w -version=DubUseCurl -version=DubApplication -Isource %* @build-files.txt @if errorlevel 1 exit /b 1 @echo DUB has been built. You probably also want to add the following entry to your diff --git a/build.sh b/build.sh index ba9fffc..7b2357c 100755 --- a/build.sh +++ b/build.sh @@ -16,31 +16,6 @@ exit 1 fi -VERSION=$($DMD --version 2>/dev/null | sed -En 's|.*DMD.* v([[:digit:]\.]+).*|\1|p') -# workaround for link order issues with libcurl (phobos needs to come before curl) -if [[ $VERSION < 2.069.0 ]]; then - # link against libcurl - LIBS=`pkg-config --libs libcurl 2>/dev/null || echo "-lcurl"` - - # fix for modern GCC versions with --as-needed by default - if [[ `$DMD --help | head -n1 | grep 'DMD\(32\|64\)'` ]]; then - if [ `uname` = "Linux" ]; then - LIBS="-l:libphobos2.a $LIBS" - else - LIBS="-lphobos2 $LIBS" - fi - elif [[ `$DMD --help | head -n1 | grep '^LDC '` ]]; then - if [ `uname` = "SunOS" ]; then - LIBS="-lnsl -lsocket -lphobos2-ldc $LIBS" - else - LIBS="-lphobos2-ldc $LIBS" - fi - fi - - # adjust linker flags for dmd command line - LIBS=`echo "$LIBS" | sed 's/^-L/-L-L/; s/ -L/ -L-L/g; s/^-l/-L-l/; s/ -l/ -L-l/g'` -fi - if [ "$GITVER" = "" ]; then GITVER=$(git describe) || echo "Could not determine a version with git." fi @@ -56,7 +31,7 @@ MACOSX_DEPLOYMENT_TARGET=10.8 echo Running $DMD... -$DMD -ofbin/dub -g -O -w -version=DubUseCurl -version=DubApplication -Isource $* $LIBS @build-files.txt +$DMD -ofbin/dub -g -O -w -version=DubUseCurl -version=DubApplication -Isource $* @build-files.txt bin/dub --version echo DUB has been built as bin/dub. echo diff --git a/changelog/ldc2.dd b/changelog/ldc2.dd new file mode 100644 index 0000000..86c54ca --- /dev/null +++ b/changelog/ldc2.dd @@ -0,0 +1,7 @@ +Improved support for LDC, incl. cross-compilation + +The ldc2 driver now supports +* some more command-line options (coverage, profiling, keeping stack frame), +* separate linking, +* cross-compilation by specifying the target triple (LDC `-mtriple`) as `--arch`, e.g., `--arch=x86_64-pc-windows-msvc`. + Check out https://wiki.dlang.org/Cross-compiling_with_LDC for how to setup LDC. diff --git a/changelog/lint.dd b/changelog/lint.dd new file mode 100644 index 0000000..d28bc9b --- /dev/null +++ b/changelog/lint.dd @@ -0,0 +1,9 @@ +Lint command added + +Dub supports now command `lint`, which will execute D-Scanner on the dub package. +By default `dub lint` will execute style check. Import paths to dependent dub +packages will be passed to D-Scanner. + +$(CONSOLE +> dub lint +) diff --git a/changelog/semver-compatibility-operator.dd b/changelog/semver-compatibility-operator.dd new file mode 100644 index 0000000..c21b0d8 --- /dev/null +++ b/changelog/semver-compatibility-operator.dd @@ -0,0 +1,8 @@ +Added SemVer compatibility operator "^" + +Dub now supports version specifications of the form `^x.y.z`. +This corresponds to a "semver compatible version", ie. any version up from +`x.y.z` with the same major number. If the major number is 0, only the same +version matches. This corresponds to the versions listed on https://semver.org/ as +compatible with the public API of the version given. +`^x.y` is equivalent to `^x.y.0`. diff --git a/curl.lib b/curl.lib deleted file mode 100644 index 1a1e192..0000000 --- a/curl.lib +++ /dev/null Binary files differ diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 738f30c..fc06a32 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -49,6 +49,7 @@ new RunCommand, new BuildCommand, new TestCommand, + new LintCommand, new GenerateCommand, new DescribeCommand, new CleanCommand, @@ -902,8 +903,9 @@ return 0; // search for the package and filter versions for exact matches - auto search = dub.searchPackages(packageParts.name) - .map!(tup => tup[1].find!(p => p.name == packageParts.name)) + auto basePackageName = getBasePackageName(packageParts.name); + auto search = dub.searchPackages(basePackageName) + .map!(tup => tup[1].find!(p => p.name == basePackageName)) .filter!(ps => !ps.empty); if (search.empty) { logWarn("Package '%s' was neither found locally nor online.", packageParts.name); @@ -1041,6 +1043,88 @@ } } +class LintCommand : PackageBuildCommand { + private { + bool m_syntaxCheck = false; + bool m_styleCheck = false; + string m_errorFormat; + bool m_report = false; + string m_reportFormat; + string[] m_importPaths; + string m_config; + } + + this() + { + this.name = "lint"; + this.argumentsPattern = "[]"; + this.description = "Executes the linter tests of the selected package"; + this.helpText = [ + `Builds the package and executes D-Scanner linter tests.` + ]; + this.acceptsAppArgs = true; + } + + override void prepare(scope CommandArgs args) + { + args.getopt("syntax-check", &m_syntaxCheck, [ + "Lexes and parses sourceFile, printing the line and column number of " ~ + "any syntax errors to stdout." + ]); + + args.getopt("style-check", &m_styleCheck, [ + "Lexes and parses sourceFiles, printing the line and column number of " ~ + "any static analysis check failures stdout." + ]); + + args.getopt("error-format", &m_errorFormat, [ + "Format errors produced by the style/syntax checkers." + ]); + + args.getopt("report", &m_report, [ + "Generate a static analysis report in JSON format." + ]); + + args.getopt("report-format", &m_reportFormat, [ + "Specifies the format of the generated report." + ]); + + if (m_reportFormat) m_report = true; + + args.getopt("import-paths", &m_importPaths, [ + "Import paths" + ]); + + args.getopt("config", &m_config, [ + "Use the given configuration file." + ]); + + super.prepare(args); + } + + override int execute(Dub dub, string[] free_args, string[] app_args) + { + string package_name; + enforceUsage(free_args.length <= 1, "Expected one or zero arguments."); + if (free_args.length >= 1) package_name = free_args[0]; + + string[] args; + if (!m_syntaxCheck && !m_styleCheck && !m_report && app_args.length == 0) { m_styleCheck = true; } + + if (m_syntaxCheck) args ~= "--syntaxCheck"; + if (m_styleCheck) args ~= "--styleCheck"; + if (m_errorFormat) args ~= ["--errorFormat", m_errorFormat]; + if (m_report) args ~= "--report"; + if (m_reportFormat) args ~= ["--reportFormat", m_reportFormat]; + foreach (import_path; m_importPaths) args ~= ["-I", import_path]; + if (m_config) args ~= ["--config", m_config]; + + setupPackage(dub, package_name); + dub.lintProject(args ~ app_args); + return 0; + } +} + class DescribeCommand : PackageBuildCommand { private { bool m_importPaths = false; diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index 42e6cf9..f81d0cf 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -72,7 +72,7 @@ /// Replaces high level fields with low level fields and converts /// dmd flags to compiler-specific flags - void prepareBuildSettings(ref BuildSettings settings, BuildSetting supported_fields = BuildSetting.all) const; + void prepareBuildSettings(ref BuildSettings settings, in ref BuildPlatform platform, BuildSetting supported_fields = BuildSetting.all) const; /// Removes any dflags that match one of the BuildOptions values and populates the BuildSettings.options field. void extractBuildOptions(ref BuildSettings settings) const; diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index bf06e4c..051e122 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -132,7 +132,7 @@ ); } - void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const + void prepareBuildSettings(ref BuildSettings settings, in ref BuildPlatform platform, BuildSetting fields = BuildSetting.all) const { enforceBuildRequirements(settings); @@ -163,9 +163,11 @@ } if (!(fields & BuildSetting.libs)) { - resolveLibs(settings); - version(Windows) settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array()); - else settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); + resolveLibs(settings, platform); + if (platform.platform.canFind("windows")) + settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array()); + else + settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); } if (!(fields & BuildSetting.sourceFiles)) { @@ -178,10 +180,8 @@ settings.lflags = null; } - version (Posix) { - if (settings.options & BuildOption.pic) - settings.addDFlags("-fPIC"); - } + if (platform.platform.canFind("posix") && (settings.options & BuildOption.pic)) + settings.addDFlags("-fPIC"); assert(fields & BuildSetting.dflags); assert(fields & BuildSetting.copyFiles); @@ -248,9 +248,10 @@ settings.addDFlags("-lib"); break; case TargetType.dynamicLibrary: - version (Windows) settings.addDFlags("-shared"); - else version (OSX) settings.addDFlags("-shared"); - else settings.prependDFlags("-shared", "-defaultlib=libphobos2.so"); + if (platform.compiler != "dmd" || platform.platform.canFind("windows") || platform.platform.canFind("osx")) + settings.addDFlags("-shared"); + else + settings.prependDFlags("-shared", "-defaultlib=libphobos2.so"); break; case TargetType.object: settings.addDFlags("-c"); @@ -280,7 +281,8 @@ auto args = ["-of"~tpath.toNativeString()]; args ~= objects; args ~= settings.sourceFiles; - version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order by DMD + if (platform.platform.canFind("linux")) + args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order by DMD args ~= lflagsToDFlags(settings.lflags); args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index 4a9c0d2..eb753d8 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -85,7 +85,7 @@ ); } - void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const + void prepareBuildSettings(ref BuildSettings settings, in ref BuildPlatform platform, BuildSetting fields = BuildSetting.all) const { enforceBuildRequirements(settings); @@ -121,7 +121,7 @@ } if (!(fields & BuildSetting.libs)) { - resolveLibs(settings); + resolveLibs(settings, platform); settings.addDFlags(settings.libs.map!(l => "-l"~l)().array()); } @@ -222,7 +222,8 @@ args = [ "ar", "rcs", tpath ] ~ objects; } else { args = platform.compilerBinary ~ objects ~ settings.sourceFiles ~ settings.lflags ~ settings.dflags.filter!(f => isLinkageFlag(f)).array; - version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order by DMD + if (platform.platform.canFind("linux")) + args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order } logDiagnostic("%s", args.join(" ")); invokeTool(args, output_callback); diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index 11c2872..7e77d0e 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -27,15 +27,15 @@ private static immutable s_options = [ tuple(BuildOption.debugMode, ["-d-debug"]), tuple(BuildOption.releaseMode, ["-release"]), - //tuple(BuildOption.coverage, ["-?"]), + tuple(BuildOption.coverage, ["-cov"]), tuple(BuildOption.debugInfo, ["-g"]), tuple(BuildOption.debugInfoC, ["-gc"]), - //tuple(BuildOption.alwaysStackFrame, ["-?"]), + tuple(BuildOption.alwaysStackFrame, ["-disable-fp-elim"]), //tuple(BuildOption.stackStomping, ["-?"]), tuple(BuildOption.inline, ["-enable-inlining", "-Hkeep-all-bodies"]), tuple(BuildOption.noBoundsCheck, ["-boundscheck=off"]), tuple(BuildOption.optimize, ["-O3"]), - //tuple(BuildOption.profile, ["-?"]), + tuple(BuildOption.profile, ["-fdmd-trace-functions"]), tuple(BuildOption.unittests, ["-unittest"]), tuple(BuildOption.verbose, ["-v"]), tuple(BuildOption.ignoreUnknownPragmas, ["-ignore"]), @@ -80,10 +80,15 @@ { string[] arch_flags; switch (arch_override) { - default: throw new Exception("Unsupported architecture: "~arch_override); case "": break; case "x86": arch_flags = ["-march=x86"]; break; case "x86_64": arch_flags = ["-march=x86-64"]; break; + default: + if (arch_override.canFind('-')) + arch_flags = ["-mtriple="~arch_override]; + else + throw new Exception("Unsupported architecture: "~arch_override); + break; } settings.addDFlags(arch_flags); @@ -94,7 +99,7 @@ ); } - void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const + void prepareBuildSettings(ref BuildSettings settings, in ref BuildPlatform platform, BuildSetting fields = BuildSetting.all) const { enforceBuildRequirements(settings); @@ -133,7 +138,7 @@ } if (!(fields & BuildSetting.libs)) { - resolveLibs(settings); + resolveLibs(settings, platform); settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); } @@ -169,26 +174,30 @@ const { assert(settings.targetName.length > 0, "No target name set."); + const p = platform.platform; final switch (settings.targetType) { case TargetType.autodetect: assert(false, "Configurations must have a concrete target type."); case TargetType.none: return null; case TargetType.sourceLibrary: return null; case TargetType.executable: - if (platform.platform.canFind("windows")) + if (p.canFind("windows")) return settings.targetName ~ ".exe"; + else if (p.canFind("wasm")) + return settings.targetName ~ ".wasm"; else return settings.targetName; case TargetType.library: case TargetType.staticLibrary: - if (generatesCOFF(platform)) return settings.targetName ~ ".lib"; + if (p.canFind("windows") && !p.canFind("mingw")) + return settings.targetName ~ ".lib"; else return "lib" ~ settings.targetName ~ ".a"; case TargetType.dynamicLibrary: - if (platform.platform.canFind("windows")) + if (p.canFind("windows")) return settings.targetName ~ ".dll"; - else if (platform.platform.canFind("osx")) + else if (p.canFind("osx")) return "lib" ~ settings.targetName ~ ".dylib"; else return "lib" ~ settings.targetName ~ ".so"; case TargetType.object: - if (platform.platform.canFind("windows")) + if (p.canFind("windows")) return settings.targetName ~ ".obj"; else return settings.targetName ~ ".o"; } @@ -231,7 +240,21 @@ void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) { - assert(false, "Separate linking not implemented for LDC"); + import std.string; + auto tpath = NativePath(settings.targetPath) ~ getTargetFileName(settings, platform); + auto args = ["-of"~tpath.toNativeString()]; + args ~= objects; + args ~= settings.sourceFiles; + if (platform.platform.canFind("linux")) + args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order + args ~= lflagsToDFlags(settings.lflags); + args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; + + auto res_file = getTempFile("dub-build", ".lnk"); + std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); + + logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); + invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); } string[] lflagsToDFlags(in string[] lflags) const @@ -244,28 +267,23 @@ return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); } - private static bool generatesCOFF(in BuildPlatform platform) + private static bool isLinkerDFlag(string arg) { - import std.string : splitLines, strip; - import std.uni : toLower; - - static bool[string] compiler_coff_map; - - if (auto pret = platform.compilerBinary in compiler_coff_map) - return *pret; - - auto result = executeShell(escapeShellCommand([platform.compilerBinary, "-version"])); - enforce (result.status == 0, "Failed to determine linker used by LDC. \"" - ~platform.compilerBinary~" -version\" failed with exit code " - ~result.status.to!string()~"."); - - bool ret = result.output - .splitLines - .find!(l => l.strip.toLower.startsWith("default target:")) - .front - .canFind("msvc"); - - compiler_coff_map[platform.compilerBinary] = ret; - return ret; + switch (arg) { + case "-g", "-gc", "-m32", "-m64", "-shared", "-lib", + "-betterC", "-disable-linker-strip-dead", "-static": + return true; + default: + return arg.startsWith("-L") + || arg.startsWith("-Xcc=") + || arg.startsWith("-defaultlib=") + || arg.startsWith("-flto") + || arg.startsWith("-fsanitize=") + || arg.startsWith("-link-") + || arg.startsWith("-linker=") + || arg.startsWith("-march=") + || arg.startsWith("-mscrtlib=") + || arg.startsWith("-mtriple="); + } } } diff --git a/source/dub/compilers/utils.d b/source/dub/compilers/utils.d index 03b3c33..da847bc 100644 --- a/source/dub/compilers/utils.d +++ b/source/dub/compilers/utils.d @@ -40,37 +40,36 @@ Linker files include static/dynamic libraries, resource files, object files and DLL definition files. */ -bool isLinkerFile(string f) +bool isLinkerFile(in ref BuildPlatform platform, string f) { import std.path; switch (extension(f)) { default: return false; - version (Windows) { - case ".lib", ".obj", ".res", ".def": - return true; - } else { - case ".a", ".o", ".so", ".dylib": - return true; - } + case ".lib", ".obj", ".res", ".def": + return platform.platform.canFind("windows"); + case ".a", ".o", ".so", ".dylib": + return !platform.platform.canFind("windows"); } } unittest { - version (Windows) { - assert(isLinkerFile("test.obj")); - assert(isLinkerFile("test.lib")); - assert(isLinkerFile("test.res")); - assert(!isLinkerFile("test.o")); - assert(!isLinkerFile("test.d")); - } else { - assert(isLinkerFile("test.o")); - assert(isLinkerFile("test.a")); - assert(isLinkerFile("test.so")); - assert(isLinkerFile("test.dylib")); - assert(!isLinkerFile("test.obj")); - assert(!isLinkerFile("test.d")); - } + BuildPlatform p; + + p.platform = ["windows"]; + assert(isLinkerFile(p, "test.obj")); + assert(isLinkerFile(p, "test.lib")); + assert(isLinkerFile(p, "test.res")); + assert(!isLinkerFile(p, "test.o")); + assert(!isLinkerFile(p, "test.d")); + + p.platform = ["something else"]; + assert(isLinkerFile(p, "test.o")); + assert(isLinkerFile(p, "test.a")); + assert(isLinkerFile(p, "test.so")); + assert(isLinkerFile(p, "test.dylib")); + assert(!isLinkerFile(p, "test.obj")); + assert(!isLinkerFile(p, "test.d")); } @@ -80,7 +79,7 @@ This function tries to invoke "pkg-config" if possible and falls back to direct flag translation if that fails. */ -void resolveLibs(ref BuildSettings settings) +void resolveLibs(ref BuildSettings settings, in ref BuildPlatform platform) { import std.string : format; import std.array : array; @@ -90,7 +89,8 @@ if (settings.targetType == TargetType.library || settings.targetType == TargetType.staticLibrary) { logDiagnostic("Ignoring all import libraries for static library build."); settings.libs = null; - version(Windows) settings.sourceFiles = settings.sourceFiles.filter!(f => !f.endsWith(".lib")).array; + if (platform.platform.canFind("windows")) + settings.sourceFiles = settings.sourceFiles.filter!(f => !f.endsWith(".lib")).array; } version (Posix) { diff --git a/source/dub/dependency.d b/source/dub/dependency.d index da1b552..1d2c51f 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -128,6 +128,8 @@ $(LI `">1.0.0 <2.0.0"` - version range with two bounds) $(LI `"~>1.0.0"` - a fuzzy version range) $(LI `"~>1.0"` - a fuzzy version range with partial version) + $(LI `"^1.0.0"` - semver compatible version range (same version if 0.x.y, ==major >=minor.patch if x.y.z)) + $(LI `"^1.0"` - same as ^1.0.0) $(LI `"~master"` - a branch name) $(LI `"*" - match any version (see also `any`)) ) @@ -156,6 +158,16 @@ ves = ves[2..$]; m_versA = Version(expandVersion(ves)); m_versB = Version(bumpVersion(ves) ~ "-0"); + } else if (ves.startsWith("^")) { + // Shortcut: "^x.y.z" variant. "Semver compatible" - no breaking changes. + // if 0.x.y, ==0.x.y + // if x.y.z, >=x.y.z <(x+1).0.0-0 + // ^x.y is equivalent to ^x.y.0. + m_inclusiveA = true; + m_inclusiveB = false; + ves = ves[1..$].expandVersion; + m_versA = Version(ves); + m_versB = Version(bumpIncompatibleVersion(ves) ~ "-0"); } else if (ves[0] == Version.branchPrefix) { m_inclusiveA = true; m_inclusiveB = true; @@ -214,7 +226,7 @@ else return m_versA.toString(); } - // "~>" case + // "~>", "^" case if (m_inclusiveA && !m_inclusiveB && !m_versA.isBranch) { auto vs = m_versA.toString(); auto i1 = std.string.indexOf(vs, '-'), i2 = std.string.indexOf(vs, '+'); @@ -228,6 +240,9 @@ auto ve = Version(expandVersion(vp)); auto veb = Version(bumpVersion(vp) ~ "-0"); if (ve == m_versA && veb == m_versB) return "~>" ~ vp; + + auto veb2 = Version(bumpIncompatibleVersion(expandVersion(vp)) ~ "-0"); + if (ve == m_versA && veb2 == m_versB) return "^" ~ vp; } } @@ -378,7 +393,7 @@ } /// ditto - hash_t toHash() + size_t toHash() const nothrow @trusted { try { size_t hash = 0; @@ -592,6 +607,13 @@ assert(a == Dependency(">=3.5.0 <3.6.0-0"), "Testing failed: " ~ a.toString()); assert(!Dependency("~>3.0.0").matches(Version("3.1.0-beta"))); + a = Dependency("^0.1.2"); + assert(a == Dependency(">=0.1.2 <0.1.3-0")); + a = Dependency("^1.2.3"); + assert(a == Dependency(">=1.2.3 <2.0.0-0"), "Testing failed: " ~ a.toString()); + a = Dependency("^1.2"); + assert(a == Dependency(">=1.2.0 <2.0.0-0"), "Testing failed: " ~ a.toString()); + a = Dependency("~>0.1.1"); b = Dependency("==0.1.0"); assert(!a.merge(b).valid); @@ -651,6 +673,9 @@ assert(Dependency("~>1.4").versionSpec == "~>1.4"); assert(Dependency("~>2").versionSpec == "~>2"); assert(Dependency("~>1.0.4+1.2.3").versionSpec == "~>1.0.4"); + assert(Dependency("^0.1.2").versionSpec == "^0.1.2"); + assert(Dependency("^1.2.3").versionSpec == "^1.2.3"); + assert(Dependency("^1.2").versionSpec == "~>1.2"); // equivalent; prefer ~> } diff --git a/source/dub/dependencyresolver.d b/source/dub/dependencyresolver.d index 3b61758..85e97de 100644 --- a/source/dub/dependencyresolver.d +++ b/source/dub/dependencyresolver.d @@ -40,7 +40,7 @@ CONFIGS configs; DependencyType depType = DependencyType.required; - hash_t toHash() const nothrow @trusted { + size_t toHash() const nothrow @trusted { size_t ret = typeid(string).getHash(&pack); ret ^= typeid(CONFIGS).getHash(&configs); return ret; @@ -61,7 +61,7 @@ string pack; CONFIG config; - hash_t toHash() const nothrow @trusted { + size_t toHash() const nothrow @trusted { size_t ret = pack.hashOf(); ret ^= typeid(CONFIG).getHash(&config); return ret; diff --git a/source/dub/dub.d b/source/dub/dub.d index c8d4845..3f0d9ca 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -32,9 +32,6 @@ import std.string; import std.encoding : sanitize; -// Workaround for libcurl liker errors when building with LDC -version (LDC) pragma(lib, "curl"); - // Set output path and options for coverage reports version (DigitalMars) version (D_Coverage) static if (__VERSION__ >= 2068) { @@ -747,6 +744,53 @@ generator.generate(settings); } + /** Executes D-Scanner tests on the current project. **/ + void lintProject(string[] args) + { + import std.path : buildPath, buildNormalizedPath; + + if (m_dryRun) return; + + auto tool = "dscanner"; + + auto tool_pack = m_packageManager.getBestPackage(tool, ">=0.0.0"); + if (!tool_pack) tool_pack = m_packageManager.getBestPackage(tool, "~master"); + if (!tool_pack) { + logInfo("%s is not present, getting and storing it user wide", tool); + tool_pack = fetch(tool, Dependency(">=0.0.0"), defaultPlacementLocation, FetchOptions.none); + } + + auto dscanner_dub = new Dub(null, m_packageSuppliers); + dscanner_dub.loadPackage(tool_pack.path); + dscanner_dub.upgrade(UpgradeOptions.select); + + auto compiler_binary = this.defaultCompiler; + + GeneratorSettings settings; + settings.config = "application"; + settings.compiler = getCompiler(compiler_binary); + settings.platform = settings.compiler.determinePlatform(settings.buildSettings, compiler_binary, m_defaultArchitecture); + settings.buildType = "debug"; + settings.run = true; + + foreach (dependencyPackage; m_project.dependencies) + { + auto cfgs = m_project.getPackageConfigs(settings.platform, null, true); + auto buildSettings = dependencyPackage.getBuildSettings(settings.platform, cfgs[dependencyPackage.name]); + foreach (importPath; buildSettings.importPaths) { + settings.runArgs ~= ["-I", buildNormalizedPath(dependencyPackage.path.toNativeString(), importPath.idup)]; + } + } + + string configFilePath = buildPath(m_project.rootPackage.path.toNativeString(), "dscanner.ini"); + if (!args.canFind("--config") && exists(configFilePath)) { + settings.runArgs ~= ["--config", configFilePath]; + } + + settings.runArgs ~= args ~ [m_project.rootPackage.path.toNativeString()]; + dscanner_dub.generateProject("build", settings); + } + /** Prints the specified build settings necessary for building the root package. */ void listProjectData(GeneratorSettings settings, string[] requestedData, ListBuildSettingsFormat list_type) @@ -798,11 +842,12 @@ /// Fetches the package matching the dependency and places it in the specified location. Package fetch(string packageId, const Dependency dep, PlacementLocation location, FetchOptions options, string reason = "") { + auto basePackageName = getBasePackageName(packageId); Json pinfo; PackageSupplier supplier; foreach(ps; m_packageSuppliers){ try { - pinfo = ps.fetchPackageRecipe(packageId, dep, (options & FetchOptions.usePrerelease) != 0); + pinfo = ps.fetchPackageRecipe(basePackageName, dep, (options & FetchOptions.usePrerelease) != 0); if (pinfo.type == Json.Type.null_) continue; supplier = ps; @@ -859,14 +904,14 @@ clean_package_version = clean_package_version.replace("+", "_"); // + has special meaning for Optlink if (!placement.existsFile()) mkdirRecurse(placement.toNativeString()); - NativePath dstpath = placement ~ (packageId ~ "-" ~ clean_package_version); + NativePath dstpath = placement ~ (basePackageName ~ "-" ~ clean_package_version); if (!dstpath.existsFile()) mkdirRecurse(dstpath.toNativeString()); // Support libraries typically used with git submodules like ae. // Such libraries need to have ".." as import path but this can create // import path leakage. - dstpath = dstpath ~ packageId; + dstpath = dstpath ~ basePackageName; import std.datetime : seconds; auto lock = lockFile(dstpath.toNativeString() ~ ".lock", 30.seconds); // possibly wait for other dub instance @@ -881,13 +926,14 @@ { import std.zip : ZipException; - auto path = getTempFile(packageId, ".zip"); - supplier.fetchPackage(path, packageId, dep, (options & FetchOptions.usePrerelease) != 0); // Q: continue on fail? + auto path = getTempFile(basePackageName, ".zip"); + supplier.fetchPackage(path, basePackageName, dep, (options & FetchOptions.usePrerelease) != 0); // Q: continue on fail? scope(exit) std.file.remove(path.toNativeString()); logDiagnostic("Placing to %s...", placement.toNativeString()); try { - return m_packageManager.storeFetchedPackage(path, pinfo, dstpath); + m_packageManager.storeFetchedPackage(path, pinfo, dstpath); + return m_packageManager.getPackage(packageId, ver, dstpath); } catch (ZipException e) { logInfo("Failed to extract zip archive for %s %s...", packageId, ver); // rethrow the exception at the end of the loop @@ -1126,8 +1172,9 @@ Version[] listPackageVersions(string name) { Version[] versions; + auto basePackageName = getBasePackageName(name); foreach (ps; this.m_packageSuppliers) { - try versions ~= ps.getVersions(name); + try versions ~= ps.getVersions(basePackageName); catch (Exception e) { logWarn("Failed to get versions for package %s on provider %s: %s", name, ps.description, e.msg); } diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index 4414bb8..971ce8f 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -27,8 +27,10 @@ import std.string; import std.encoding : sanitize; -version(Windows) enum objSuffix = ".obj"; -else enum objSuffix = ".o"; +string getObjSuffix(in ref BuildPlatform platform) +{ + return platform.platform.canFind("windows") ? ".obj" : ".o"; +} class BuildGenerator : ProjectGenerator { private { @@ -230,7 +232,7 @@ // do not pass all source files to RDMD, only the main source file buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(s => !s.endsWith(".d"))().array(); - settings.compiler.prepareBuildSettings(buildsettings, BuildSetting.commandLine); + settings.compiler.prepareBuildSettings(buildsettings, settings.platform, BuildSetting.commandLine); auto generate_binary = !buildsettings.dflags.canFind("-o-"); @@ -351,6 +353,7 @@ addHash(buildsettings.lflags); addHash((cast(uint)buildsettings.options).to!string); addHash(buildsettings.stringImportPaths); + addHash(buildsettings.importPaths); addHash(settings.platform.architecture); addHash(settings.platform.compilerBinary); addHash(settings.platform.compiler); @@ -416,13 +419,14 @@ /// Calls with path that resolve to the same file on the filesystem will return the same, /// unless they include different symbolic links (which are not resolved). - static string pathToObjName(string path) + static string pathToObjName(in ref BuildPlatform platform, string path) { import std.digest.crc : crc32Of; import std.path : buildNormalizedPath, dirSeparator, relativePath, stripDrive; if (path.endsWith(".d")) path = path[0 .. $-2]; auto ret = buildNormalizedPath(getcwd(), path).replace(dirSeparator, "."); auto idx = ret.lastIndexOf('.'); + const objSuffix = getObjSuffix(platform); return idx < 0 ? ret ~ objSuffix : format("%s_%(%02x%)%s", ret[idx+1 .. $], crc32Of(ret[0 .. idx]), objSuffix); } @@ -434,7 +438,7 @@ bs.lflags = null; bs.sourceFiles = [ srcFile ]; bs.targetType = TargetType.object; - gs.compiler.prepareBuildSettings(bs, BuildSetting.commandLine); + gs.compiler.prepareBuildSettings(bs, gs.platform, BuildSetting.commandLine); gs.compiler.setTarget(bs, gs.platform, objPath); gs.compiler.invoke(bs, gs.platform, gs.compileCallback); return objPath; @@ -456,12 +460,13 @@ import std.parallelism, std.range : walkLength; auto lbuildsettings = buildsettings; - auto srcs = buildsettings.sourceFiles.filter!(f => !isLinkerFile(f)); + auto srcs = buildsettings.sourceFiles.filter!(f => !isLinkerFile(settings.platform, f)); auto objs = new string[](srcs.walkLength); void compileSource(size_t i, string src) { logInfo("Compiling %s...", src); - objs[i] = compileUnit(src, pathToObjName(src), buildsettings, settings); + const objPath = pathToObjName(settings.platform, src); + objs[i] = compileUnit(src, objPath, buildsettings, settings); } if (settings.parallelBuild) { @@ -471,44 +476,40 @@ } logInfo("Linking..."); - lbuildsettings.sourceFiles = is_static_library ? [] : lbuildsettings.sourceFiles.filter!(f=> f.isLinkerFile()).array; + lbuildsettings.sourceFiles = is_static_library ? [] : lbuildsettings.sourceFiles.filter!(f => isLinkerFile(settings.platform, f)).array; settings.compiler.setTarget(lbuildsettings, settings.platform); - settings.compiler.prepareBuildSettings(lbuildsettings, BuildSetting.commandLineSeparate|BuildSetting.sourceFiles); + settings.compiler.prepareBuildSettings(lbuildsettings, settings.platform, BuildSetting.commandLineSeparate|BuildSetting.sourceFiles); settings.compiler.invokeLinker(lbuildsettings, settings.platform, objs, settings.linkCallback); - /* - NOTE: for DMD experimental separate compile/link is used, but this is not yet implemented - on the other compilers. Later this should be integrated somehow in the build process - (either in the dub.json, or using a command line flag) - */ - } else if (generate_binary && (settings.buildMode == BuildMode.allAtOnce || settings.compiler.name != "dmd" || is_static_library)) { + // NOTE: separate compile/link is not yet enabled for GDC. + } else if (generate_binary && (settings.buildMode == BuildMode.allAtOnce || settings.compiler.name == "gdc" || is_static_library)) { // don't include symbols of dependencies (will be included by the top level target) - if (is_static_library) buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(f => !f.isLinkerFile()).array; + if (is_static_library) buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(f => !isLinkerFile(settings.platform, f)).array; // setup for command line settings.compiler.setTarget(buildsettings, settings.platform); - settings.compiler.prepareBuildSettings(buildsettings, BuildSetting.commandLine); + settings.compiler.prepareBuildSettings(buildsettings, settings.platform, BuildSetting.commandLine); // invoke the compiler settings.compiler.invoke(buildsettings, settings.platform, settings.compileCallback); } else { // determine path for the temporary object file - string tempobjname = buildsettings.targetName ~ objSuffix; + string tempobjname = buildsettings.targetName ~ getObjSuffix(settings.platform); NativePath tempobj = NativePath(buildsettings.targetPath) ~ tempobjname; // setup linker command line auto lbuildsettings = buildsettings; - lbuildsettings.sourceFiles = lbuildsettings.sourceFiles.filter!(f => isLinkerFile(f)).array; + lbuildsettings.sourceFiles = lbuildsettings.sourceFiles.filter!(f => isLinkerFile(settings.platform, f)).array; if (generate_binary) settings.compiler.setTarget(lbuildsettings, settings.platform); - settings.compiler.prepareBuildSettings(lbuildsettings, BuildSetting.commandLineSeparate|BuildSetting.sourceFiles); + settings.compiler.prepareBuildSettings(lbuildsettings, settings.platform, BuildSetting.commandLineSeparate|BuildSetting.sourceFiles); // setup compiler command line buildsettings.libs = null; buildsettings.lflags = null; if (generate_binary) buildsettings.addDFlags("-c", "-of"~tempobj.toNativeString()); - buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(f => !isLinkerFile(f)).array; + buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(f => !isLinkerFile(settings.platform, f)).array; - settings.compiler.prepareBuildSettings(buildsettings, BuildSetting.commandLine); + settings.compiler.prepareBuildSettings(buildsettings, settings.platform, BuildSetting.commandLine); settings.compiler.invoke(buildsettings, settings.platform, settings.compileCallback); diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index ddbbc33..1d8990f 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -353,7 +353,7 @@ { auto pdepti = &targets[depname]; configureDependents(*pdepti, targets, level + 1); - mergeFromDependency(pdepti.buildSettings, ti.buildSettings); + mergeFromDependency(pdepti.buildSettings, ti.buildSettings, genSettings.platform); } } @@ -519,7 +519,7 @@ child.addOptions(BuildOptions(parent.options & inheritedBuildOptions)); } - private static void mergeFromDependency(in ref BuildSettings child, ref BuildSettings parent) + private static void mergeFromDependency(in ref BuildSettings child, ref BuildSettings parent, in ref BuildPlatform platform) { import dub.compilers.utils : isLinkerFile; @@ -532,7 +532,7 @@ parent.addStringImportPaths(child.stringImportPaths); // linking of static libraries is done by parent if (child.targetType == TargetType.staticLibrary) { - parent.addSourceFiles(child.sourceFiles.filter!isLinkerFile.array); + parent.addSourceFiles(child.sourceFiles.filter!(f => isLinkerFile(platform, f)).array); parent.addLibs(child.libs); parent.addLFlags(child.lflags); } diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d index 87c8e51..aa8bc59 100644 --- a/source/dub/generators/visuald.d +++ b/source/dub/generators/visuald.d @@ -194,7 +194,7 @@ addFile(p.recipePath.toNativeString(), false); if (files.targetType == TargetType.staticLibrary) - foreach(s; files.sourceFiles.filter!(s => !isLinkerFile(s))) addFile(s, true); + foreach(s; files.sourceFiles.filter!(s => !isLinkerFile(settings.platform, s))) addFile(s, true); else foreach(s; files.sourceFiles.filter!(s => !s.endsWith(".lib"))) addFile(s, true); @@ -455,7 +455,7 @@ NativePath filePath; bool build; - hash_t toHash() const nothrow @trusted { return structurePath.toHash() ^ filePath.toHash() ^ (build * 0x1f3e7b2c); } + size_t toHash() const nothrow @trusted { return structurePath.toHash() ^ filePath.toHash() ^ (build * 0x1f3e7b2c); } int opCmp(ref const SourceFile rhs) const { return sortOrder(this, rhs); } // "a < b" for folder structures (deepest folder first, else lexical) private final static int sortOrder(ref const SourceFile a, ref const SourceFile b) { diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index 6380791..5879e79 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -547,7 +547,7 @@ conn.handle.set(CurlOption.connecttimeout, timeout); // transfers time out after 8s below 10 byte/s conn.handle.set(CurlOption.low_speed_limit, 10); - conn.handle.set(CurlOption.low_speed_time, 5); + conn.handle.set(CurlOption.low_speed_time, timeout); } conn.addRequestHeader("User-Agent", "dub/"~getDUBVersion()~" (std.net.curl; +https://github.com/rejectedsoftware/dub)"); @@ -713,7 +713,7 @@ static Regex!char comments_pattern, module_pattern; if (!regex_initialized) { - comments_pattern = regex(`//[^\r\n]*\r?\n?|/\*.*?\*/|/\+.*\+/`, "g"); + comments_pattern = regex(`//[^\r\n]*\r?\n?|/\*.*?\*/|/\+.*\+/`, "gs"); module_pattern = regex(`module\s+([\w\.]+)\s*;`, "g"); regex_initialized = true; } @@ -744,6 +744,7 @@ assert(getModuleNameFromContent("/* module foo; */\nmodule/*module foo;*/bar/*module foo;*/;") == "bar", getModuleNameFromContent("/* module foo; */\nmodule/*module foo;*/bar/*module foo;*/;")); assert(getModuleNameFromContent("/+ /+ module foo; +/ module foo; +/ module bar;") == "bar"); //assert(getModuleNameFromContent("/+ /+ module foo; +/ module foo; +/ module bar/++/;") == "bar"); // nested comments require a context-free parser! + assert(getModuleNameFromContent("/*\nmodule sometest;\n*/\n\nmodule fakemath;\n") == "fakemath"); } /** diff --git a/source/dub/internal/vibecompat/inet/path.d b/source/dub/internal/vibecompat/inet/path.d index 047ac5d..4ad987e 100644 --- a/source/dub/internal/vibecompat/inet/path.d +++ b/source/dub/internal/vibecompat/inet/path.d @@ -266,9 +266,9 @@ return 0; } - hash_t toHash() + size_t toHash() const nothrow @trusted { - hash_t ret; + size_t ret; auto strhash = &typeid(string).getHash; try foreach (n; nodes) ret ^= strhash(&n.m_name); catch (Exception) assert(false); diff --git a/source/dub/internal/vibecompat/inet/url.d b/source/dub/internal/vibecompat/inet/url.d index 720d5d0..eb302e5 100644 --- a/source/dub/internal/vibecompat/inet/url.d +++ b/source/dub/internal/vibecompat/inet/url.d @@ -80,7 +80,7 @@ auto si = str.countUntil('/'); if( si < 0 ) si = str.length; auto ai = str[0 .. si].countUntil('@'); - sizediff_t hs = 0; + ptrdiff_t hs = 0; if( ai >= 0 ){ hs = ai+1; auto ci = str[0 .. ai].countUntil(':'); diff --git a/source/dub/platform.d b/source/dub/platform.d index 88da7b2..0c27199 100644 --- a/source/dub/platform.d +++ b/source/dub/platform.d @@ -42,6 +42,7 @@ version(Android) ret ~= "android"; version(Cygwin) ret ~= "cygwin"; version(MinGW) ret ~= "mingw"; + version(WebAssembly) ret ~= "wasm"; return ret; }; diff --git a/source/dub/project.d b/source/dub/project.d index 48b6752..8a807a9 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -790,7 +790,7 @@ else static if (attributeName == "stringImportPaths") bs.stringImportPaths = bs.stringImportPaths.map!(ensureTrailingSlash).array(); - compiler.prepareBuildSettings(bs, BuildSetting.all & ~to!BuildSetting(attributeName)); + compiler.prepareBuildSettings(bs, platform, BuildSetting.all & ~to!BuildSetting(attributeName)); values = bs.dflags; break; @@ -801,7 +801,7 @@ bs.sourceFiles = null; bs.targetType = TargetType.none; // Force Compiler to NOT omit dependency libs when package is a library. - compiler.prepareBuildSettings(bs, BuildSetting.all & ~to!BuildSetting(attributeName)); + compiler.prepareBuildSettings(bs, platform, BuildSetting.all & ~to!BuildSetting(attributeName)); if (bs.lflags) values = compiler.lflagsToDFlags( bs.lflags ); @@ -1026,13 +1026,13 @@ if (projectDescription.rootPackage in projectDescription.targetLookup) { // Copy linker files from sourceFiles to linkerFiles auto target = projectDescription.lookupTarget(projectDescription.rootPackage); - foreach (file; target.buildSettings.sourceFiles.filter!(isLinkerFile)) + foreach (file; target.buildSettings.sourceFiles.filter!(f => isLinkerFile(settings.platform, f))) target.buildSettings.addLinkerFiles(file); // Remove linker files from sourceFiles target.buildSettings.sourceFiles = target.buildSettings.sourceFiles - .filter!(a => !isLinkerFile(a)) + .filter!(a => !isLinkerFile(settings.platform, a)) .array(); projectDescription.lookupTarget(projectDescription.rootPackage) = target; } @@ -1151,7 +1151,7 @@ dst.addDFlags(processVars(project, pack, gsettings, settings.dflags)); dst.addLFlags(processVars(project, pack, gsettings, settings.lflags)); dst.addLibs(processVars(project, pack, gsettings, settings.libs)); - dst.addSourceFiles(processVars(project, pack, gsettings, settings.sourceFiles, true)); + dst.addSourceFiles(processVars!true(project, pack, gsettings, settings.sourceFiles, true)); 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)); @@ -1182,29 +1182,58 @@ } } -private string[] processVars(in Project project, in Package pack, in GeneratorSettings gsettings, string[] vars, bool are_paths = false) +private string[] processVars(bool glob = false)(in Project project, in Package pack, in GeneratorSettings gsettings, string[] vars, bool are_paths = false) { auto ret = appender!(string[])(); - processVars(ret, project, pack, gsettings, vars, are_paths); + processVars!glob(ret, project, pack, gsettings, vars, are_paths); return ret.data; - } -private void processVars(ref Appender!(string[]) dst, in Project project, in Package pack, in GeneratorSettings gsettings, string[] vars, bool are_paths = false) +private void processVars(bool glob = false)(ref Appender!(string[]) dst, in Project project, in Package pack, in GeneratorSettings gsettings, string[] vars, bool are_paths = false) { - foreach (var; vars) dst.put(processVars(var, project, pack, gsettings, are_paths)); + static if (glob) + alias process = processVarsWithGlob!(Project, Package); + else + alias process = processVars!(Project, Package); + foreach (var; vars) + dst.put(process(var, project, pack, gsettings, are_paths)); } private string processVars(Project, Package)(string var, in Project project, in Package pack, in GeneratorSettings gsettings, bool is_path) { var = var.expandVars!(varName => getVariable(varName, project, pack, gsettings)); - if (is_path) { - auto p = NativePath(var); - if (!p.absolute) { - return (pack.path ~ p).toNativeString(); - } else return p.toNativeString(); - } else return var; + if (!is_path) + return var; + auto p = NativePath(var); + if (!p.absolute) + return (pack.path ~ p).toNativeString(); + else + return p.toNativeString(); } +private string[] processVarsWithGlob(Project, Package)(string var, in Project project, in Package pack, in GeneratorSettings gsettings, bool is_path) +{ + assert(is_path, "can't glob something that isn't a path"); + string res = processVars(var, project, pack, gsettings, is_path); + // Find the unglobbed prefix and iterate from there. + size_t i = 0; + size_t sepIdx = 0; + loop: while (i < res.length) { + switch_: switch (res[i]) + { + case '*', '?', '[', '{': break loop; + case '/': sepIdx = i; goto default; + default: ++i; break switch_; + } + } + if (i == res.length) //no globbing found in the path + return [res]; + import std.path : globMatch; + import std.file : dirEntries, SpanMode; + return dirEntries(res[0 .. sepIdx], SpanMode.depth) + .map!(de => de.name) + .filter!(name => globMatch(name, res)) + .array; +} /// Expand variables using `$VAR_NAME` or `${VAR_NAME}` syntax. /// `$$` escapes itself and is expanded to a single `$`. private string expandVars(alias expandVar)(string s) diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d index 5a73030..5ecf3de 100644 --- a/source/dub/recipe/packagerecipe.d +++ b/source/dub/recipe/packagerecipe.d @@ -226,7 +226,11 @@ auto files = appender!(string[]); import dub.project : buildSettingsVars; - auto envVars = environment.toAA(); + import std.typecons : Nullable; + + static Nullable!(string[string]) envVarCache; + + if (envVarCache.isNull) envVarCache = environment.toAA(); foreach (suffix, paths; paths_map) { if (!platform.matchesSpecification(suffix)) @@ -238,7 +242,7 @@ if (!path.absolute) path = base_path ~ path; if (!existsFile(path) || !isDir(path.toNativeString())) { import std.algorithm : any, find; - const hasVar = chain(buildSettingsVars, envVars.byKey).any!((string var) { + const hasVar = chain(buildSettingsVars, envVarCache.get.byKey).any!((string var) { return spath.find("$"~var).length > 0 || spath.find("${"~var~"}").length > 0; }); if (!hasVar) diff --git a/source/dub/semver.d b/source/dub/semver.d index dd68102..e4c0143 100644 --- a/source/dub/semver.d +++ b/source/dub/semver.d @@ -252,6 +252,38 @@ } /** + Increments a given version number to the next incompatible version. + + Prerelease and build metadata information is removed. + + This implements the "^" comparison operator, which represents "nonbreaking semver compatibility." + With 0.x.y releases, any release can break. + With x.y.z releases, only major releases can break. +*/ +string bumpIncompatibleVersion(string ver) +pure { + // Cut off metadata and prerelease information. + auto mi = ver.indexOfAny("+-"); + if (mi > 0) ver = ver[0..mi]; + // Increment next to last version from a[.b[.c]]. + auto splitted = () @trusted { return split(ver, "."); } (); // DMD 2.065.0 + assert(splitted.length == 3, "Version corrupt: " ~ ver); + if (splitted[0] == "0") splitted[2] = to!string(to!int(splitted[2]) + 1); + else splitted = [to!string(to!int(splitted[0]) + 1), "0", "0"]; + return splitted.join("."); +} +/// +unittest { + assert(bumpIncompatibleVersion("0.0.0") == "0.0.1"); + assert(bumpIncompatibleVersion("0.1.2") == "0.1.3"); + assert(bumpIncompatibleVersion("1.0.0") == "2.0.0"); + assert(bumpIncompatibleVersion("1.2.3") == "2.0.0"); + assert(bumpIncompatibleVersion("1.2.3+metadata") == "2.0.0"); + assert(bumpIncompatibleVersion("1.2.3-pre.release") == "2.0.0"); + assert(bumpIncompatibleVersion("1.2.3-pre.release+metadata") == "2.0.0"); +} + +/** Takes a partial version and expands it to a valid SemVer version. This function corresponds to the semantivs of the "~>" comparison operator's @@ -375,9 +407,9 @@ return true; } -private sizediff_t indexOfAny(string str, in char[] chars) +private ptrdiff_t indexOfAny(string str, in char[] chars) pure @nogc { - sizediff_t ret = -1; + ptrdiff_t ret = -1; foreach (ch; chars) { auto idx = str.indexOf(ch); if (idx >= 0 && (ret < 0 || idx < ret)) diff --git a/test/.gitignore b/test/.gitignore index 77ea5b9..ff95cca 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -11,5 +11,6 @@ custom-unittest/custom-unittest path-subpackage-ref/test subpackage-ref/test +subpackage-common-with-sourcefile-globbing/mypackage* /test_registry diff --git a/test/issue1567-fetch-sub-package.sh b/test/issue1567-fetch-sub-package.sh new file mode 100755 index 0000000..98ec125 --- /dev/null +++ b/test/issue1567-fetch-sub-package.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +. $(dirname "${BASH_SOURCE[0]}")/common.sh +DIR=$(dirname "${BASH_SOURCE[0]}") +packname="fetch-sub-package-dubpackage" +sub_packagename="my-sub-package" + +$DUB remove $packname --non-interactive --version=* 2>/dev/null || true +$DUB fetch "$packname:$sub_packagename" --skip-registry=all --registry=file://"$DIR"/issue1567-fetch-sub-package + +if ! dub remove $packname --non-interactive --version=1.0.1 2>/dev/null; then + die 'DUB did not install package $packname:$sub_packagename.' +fi \ No newline at end of file diff --git a/test/issue1567-fetch-sub-package/.no_build b/test/issue1567-fetch-sub-package/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1567-fetch-sub-package/.no_build diff --git a/test/issue1567-fetch-sub-package/.no_run b/test/issue1567-fetch-sub-package/.no_run new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1567-fetch-sub-package/.no_run diff --git a/test/issue1567-fetch-sub-package/.no_test b/test/issue1567-fetch-sub-package/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1567-fetch-sub-package/.no_test diff --git a/test/issue1567-fetch-sub-package/fetch-sub-package-dubpackage-1.0.1.zip b/test/issue1567-fetch-sub-package/fetch-sub-package-dubpackage-1.0.1.zip new file mode 100644 index 0000000..1b4f72b --- /dev/null +++ b/test/issue1567-fetch-sub-package/fetch-sub-package-dubpackage-1.0.1.zip Binary files differ diff --git a/test/issue1773-lint.sh b/test/issue1773-lint.sh new file mode 100755 index 0000000..9a00262 --- /dev/null +++ b/test/issue1773-lint.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +. $(dirname "${BASH_SOURCE[0]}")/common.sh +DIR=$(dirname "${BASH_SOURCE[0]}") + +if ! { ${DUB} lint --root ${DIR}/issue1773-lint || true; } | grep -cF "Parameter args is never used."; then + die $LINENO 'DUB lint did not find expected warning.' +fi + diff --git a/test/issue1773-lint/.gitignore b/test/issue1773-lint/.gitignore new file mode 100644 index 0000000..304e955 --- /dev/null +++ b/test/issue1773-lint/.gitignore @@ -0,0 +1,8 @@ +.dub +docs.json +__dummy.html +docs/ +*.exe +*.o +*.obj +*.lst diff --git a/test/issue1773-lint/.no_build b/test/issue1773-lint/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1773-lint/.no_build diff --git a/test/issue1773-lint/dub.json b/test/issue1773-lint/dub.json new file mode 100644 index 0000000..016de7e --- /dev/null +++ b/test/issue1773-lint/dub.json @@ -0,0 +1,3 @@ +{ + "name": "test" +} \ No newline at end of file diff --git a/test/issue1773-lint/source/app.d b/test/issue1773-lint/source/app.d new file mode 100644 index 0000000..e1edea8 --- /dev/null +++ b/test/issue1773-lint/source/app.d @@ -0,0 +1,4 @@ +void main(string[] args) +{ + +} \ No newline at end of file diff --git a/test/subpackage-common-with-sourcefile-globbing.sh b/test/subpackage-common-with-sourcefile-globbing.sh new file mode 100755 index 0000000..1341acc --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +cd ${CURR_DIR}/subpackage-common-with-sourcefile-globbing +rm -rf .dub dub.selections.json +${DUB} build --compiler=${DC} :server -v +${DUB} build --compiler=${DC} :client -v +${DUB} build --compiler=${DC} :common -v diff --git a/test/subpackage-common-with-sourcefile-globbing/.no_build b/test/subpackage-common-with-sourcefile-globbing/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/.no_build diff --git a/test/subpackage-common-with-sourcefile-globbing/code/mypackage/client/app.d b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/client/app.d new file mode 100644 index 0000000..b44941f --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/client/app.d @@ -0,0 +1,3 @@ +import mypackage.client.extra; +import mypackage.common.blah; +void main() { foo(); blah(); } diff --git a/test/subpackage-common-with-sourcefile-globbing/code/mypackage/client/extra.d b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/client/extra.d new file mode 100644 index 0000000..16ada9b --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/client/extra.d @@ -0,0 +1,2 @@ +module mypackage.client.extra; +void foo() {} diff --git a/test/subpackage-common-with-sourcefile-globbing/code/mypackage/common/blah.d b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/common/blah.d new file mode 100644 index 0000000..7b4b17f --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/common/blah.d @@ -0,0 +1,2 @@ +module mypackage.common.blah; +void blah() {} diff --git a/test/subpackage-common-with-sourcefile-globbing/code/mypackage/server/app.d b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/server/app.d new file mode 100644 index 0000000..8912405 --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/server/app.d @@ -0,0 +1,3 @@ +import mypackage.server.extra; +import mypackage.common.blah; +void main() { foo(); blah(); } diff --git a/test/subpackage-common-with-sourcefile-globbing/code/mypackage/server/extra.d b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/server/extra.d new file mode 100644 index 0000000..07cc3ef --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/code/mypackage/server/extra.d @@ -0,0 +1,2 @@ +module mypackage.server.extra; +void foo() {} diff --git a/test/subpackage-common-with-sourcefile-globbing/dub.sdl b/test/subpackage-common-with-sourcefile-globbing/dub.sdl new file mode 100644 index 0000000..a659505 --- /dev/null +++ b/test/subpackage-common-with-sourcefile-globbing/dub.sdl @@ -0,0 +1,19 @@ +name "mypackage" +targetType "none" +subPackage { + name "server" + sourceFiles "code/mypackage/[sc][oe]*/*.d" + targetType "executable" +} +subPackage { + name "client" + sourceFiles "code/mypackage/client/*.d" + targetType "executable" + dependency "mypackage:common" version="*" +} +subPackage { + name "common" + sourceFiles "code/mypackage/common/*.d" + importPaths "code" + targetType "library" +}