diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index bfc4b84..c641972 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -8,6 +8,7 @@ module dub.compilers.compiler; public import dub.compilers.buildsettings; +public import dub.dependency : Dependency; public import dub.platform : BuildPlatform, matchesSpecification; import dub.internal.vibecompat.core.file; @@ -92,47 +93,12 @@ string[] lflagsToDFlags(in string[] lflags) const; /// Get the dependency requirement string for this compiler - string toolchainRequirementString(const ref ToolchainRequirements tr); + Dependency toolchainRequirement(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 => dmdVer ? dmdLikeVersionToSemverLike(r) : r) - .join(' ') - .Dependency - .matches(ver); - } - /** Runs a tool and provides common boilerplate code. This method should be used by `Compiler` implementations to invoke the diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index d362413..c2b10a1 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -265,16 +265,17 @@ return lflags.map!(f => "-L"~f)().array(); } - string toolchainRequirementString(const ref ToolchainRequirements tr) + Dependency toolchainRequirement(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); + auto ver = platform.compilerVersion.length + ? dmdLikeVersionToSemverLike(platform.compilerVersion) + : "0.0.0"; + return tr.dmd.matches(ver); } private auto escapeArgs(in string[] args) diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index dc4d118..112f756 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -257,16 +257,16 @@ return dflags; } - final string toolchainRequirementString(const ref ToolchainRequirements tr) + final Dependency toolchainRequirement(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); + auto ver = platform.compilerVersion.length + ? platform.compilerVersion : "0.0.0"; + return tr.gdc.matches(ver); } } diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index ae3ed22..d3ce920 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -233,16 +233,16 @@ return lflags.map!(s => "-L="~s)().array(); } - final string toolchainRequirementString(const ref ToolchainRequirements tr) + final Dependency toolchainRequirement(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); + auto ver = platform.compilerVersion.length + ? platform.compilerVersion : "0.0.0"; + return tr.ldc.matches(ver); } private auto escapeArgs(in string[] args) diff --git a/source/dub/compilers/utils.d b/source/dub/compilers/utils.d index fc60a35..03b3c33 100644 --- a/source/dub/compilers/utils.d +++ b/source/dub/compilers/utils.d @@ -247,7 +247,7 @@ Returns: A Semver compliant string */ -string dmdLikeVersionToSemverLike(string ver) +package(dub) string dmdLikeVersionToSemverLike(string ver) { import std.algorithm : countUntil, joiner, map, skipOver, splitter; import std.array : join, split; diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index c88535d..ff4ada4 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -54,18 +54,16 @@ const tr = pkg.recipe.toolchainRequirements; - enforce ( - dcl.checkCompilerSupported(tr), - format( + if (dcl.toolchainRequirement(tr) == Dependency.invalid) + throw new Exception(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) { + tr.supportedCompilers.map!((cs) { auto str = " - " ~ cs[0]; - if (cs[1].length) str ~= ": "~cs[1]; + if (cs[1] != Dependency.any) str ~= ": " ~ cs[1].toString(); return str; }).join("\n") - ) - ); + )); enforce( dcl.checkCompilerRequirement(settings.platform, tr), @@ -73,11 +71,12 @@ "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 ) + pkg.name, dcl.name, dcl.toolchainRequirement(tr) ) ); + enforce( - dcl.checkFrontendRequirement(settings.platform, tr), + tr.matchesFrontendVersion(settings.platform), format( "Installed %s-%s with frontend %s does not comply with %s frontend requirement: %s\n" ~ "Please consider upgrading your installation.", diff --git a/source/dub/package_.d b/source/dub/package_.d index ecad440..88e3765 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -641,23 +641,21 @@ import dub.version_ : dubVersion; import std.exception : enforce; - if (m_info.toolchainRequirements.dub.length) { - const dep = Dependency(m_info.toolchainRequirements.dub); + const dep = 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"); + 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.toString() + ~ "\nPlease consider upgrading your DUB installation"); } private void fillWithDefaults() diff --git a/source/dub/recipe/json.d b/source/dub/recipe/json.d index c1fd9a1..354a1e6 100644 --- a/source/dub/recipe/json.d +++ b/source/dub/recipe/json.d @@ -106,7 +106,7 @@ types[name] = settings.toJson(); ret["buildTypes"] = types; } - if (recipe.hasToolchainRequirements) { + if (!recipe.toolchainRequirements.empty) { ret["toolchainRequirements"] = recipe.toolchainRequirements.toJson(); } if (!recipe.ddoxFilterArgs.empty) ret["-ddoxFilterArgs"] = recipe.ddoxFilterArgs.serializeToJson(); @@ -300,46 +300,17 @@ 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; - } - } + foreach (string name, value; json) + tr.addRequirement(name, value.get!string); } 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); - } + if (tr.dub != Dependency.any) ret["dub"] = serializeToJson(tr.dub); + if (tr.frontend != Dependency.any) ret["frontend"] = serializeToJson(tr.frontend); + if (tr.dmd != Dependency.any) ret["dmd"] = serializeToJson(tr.dmd); + if (tr.ldc != Dependency.any) ret["ldc"] = serializeToJson(tr.ldc); + if (tr.gdc != Dependency.any) ret["gdc"] = serializeToJson(tr.gdc); return ret; } diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d index f1d3c3e..0c5962c 100644 --- a/source/dub/recipe/packagerecipe.d +++ b/source/dub/recipe/packagerecipe.d @@ -98,17 +98,6 @@ 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); } @@ -123,28 +112,45 @@ /// 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; + import std.typecons : Tuple, tuple; - 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 + /// DUB version requirement + Dependency dub = Dependency.any; + /// D front-end version requirement + Dependency frontend = Dependency.any; + /// DMD version requirement + Dependency dmd = Dependency.any; + /// LDC version requirement + Dependency ldc = Dependency.any; + /// GDC version requirement + Dependency gdc = Dependency.any; + + /** Get the list of supported compilers. + + Returns: + An array of couples of compiler name and compiler requirement + */ + @property Tuple!(string, Dependency)[] supportedCompilers() const { - string[2][] res; - if (dmd != noKwd) res ~= [ "dmd", dmd ]; - if (ldc != noKwd) res ~= [ "ldc", ldc ]; - if (gdc != noKwd) res ~= [ "gdc", gdc ]; + Tuple!(string, Dependency)[] res; + if (dmd != Dependency.invalid) res ~= Tuple!(string, Dependency)("dmd", dmd); + if (ldc != Dependency.invalid) res ~= Tuple!(string, Dependency)("ldc", ldc); + if (gdc != Dependency.invalid) res ~= Tuple!(string, Dependency)("gdc", gdc); return res; } + + bool empty() + const { + import std.algorithm.searching : all; + return only(dub, frontend, dmd, ldc, gdc) + .all!(r => r == Dependency.any); + } + + bool matchesFrontendVersion(in ref BuildPlatform platform) + const { + import dub.compilers.utils : dmdLikeVersionToSemverLike; + return frontend.matches(Version(dmdLikeVersionToSemverLike(platform.frontendVersionString))); + } } @@ -330,6 +336,40 @@ } } +package bool addRequirement(ref ToolchainRequirements req, string name, string value) +{ + switch (name) { + default: return false; + case "dub": req.dub = parseDependency(value); break; + case "frontend": req.frontend = parseDMDDependency(value); break; + case "ldc": req.ldc = parseDependency(value); break; + case "gdc": req.gdc = parseDependency(value); break; + case "dmd": req.dmd = parseDMDDependency(value); break; + } + return true; +} + +private static Dependency parseDependency(string dep) +{ + if (dep == "no") return Dependency.invalid; + return Dependency(dep); +} + +private static Dependency parseDMDDependency(string dep) +{ + import dub.compilers.utils : dmdLikeVersionToSemverLike; + import dub.dependency : Dependency; + import std.algorithm : map, splitter; + import std.array : join; + + if (dep == "no") return Dependency.invalid; + return dep + .splitter(' ') + .map!(r => dmdLikeVersionToSemverLike(r)) + .join(' ') + .Dependency; +} + private T clone(T)(ref const(T) val) { import std.traits : isSomeString, isDynamicArray, isAssociativeArray, isBasicType, ValueType; diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d index d3aaf14..09e8b8d 100644 --- a/source/dub/recipe/sdl.d +++ b/source/dub/recipe/sdl.d @@ -104,7 +104,7 @@ t.add(settings.toSDL()); ret.add(t); } - if (recipe.hasToolchainRequirements) { + if (!recipe.toolchainRequirements.empty) { ret.add(toSDL(recipe.toolchainRequirements)); } if (recipe.ddoxFilterArgs.length) @@ -280,47 +280,18 @@ 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; - } - } + foreach (attr; tag.attributes) + tr.addRequirement(attr.name, attr.value.get!string); } 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)); - } + if (tr.dub != Dependency.any) attrs ~= new Attribute("dub", Value(tr.dub.toString())); + if (tr.frontend != Dependency.any) attrs ~= new Attribute("frontend", Value(tr.frontend.toString())); + if (tr.dmd != Dependency.any) attrs ~= new Attribute("dmd", Value(tr.dmd.toString())); + if (tr.ldc != Dependency.any) attrs ~= new Attribute("ldc", Value(tr.ldc.toString())); + if (tr.gdc != Dependency.any) attrs ~= new Attribute("gdc", Value(tr.gdc.toString())); return new Tag(null, "toolchainRequirements", null, attrs); } @@ -510,11 +481,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.toolchainRequirements.dub == Dependency("~>1.11.0")); + assert(rec.toolchainRequirements.frontend == Dependency.any); + assert(rec.toolchainRequirements.dmd == Dependency("~>2.82.0")); + assert(rec.toolchainRequirements.ldc == Dependency.any); + assert(rec.toolchainRequirements.gdc == Dependency.any); assert(rec.ddoxFilterArgs == ["-arg1", "-arg2", "-arg3"], rec.ddoxFilterArgs.to!string); assert(rec.ddoxTool == "ddoxtool"); assert(rec.buildSettings.dependencies.length == 2);