diff --git a/source/dub/project.d b/source/dub/project.d index 988d83c..8069dc0 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -1181,32 +1181,12 @@ foreach (var; vars) dst.put(processVars(var, project, pack, gsettings, are_paths)); } -private string processVars(string var, in Project project, in Package pack, in GeneratorSettings gsettings, bool is_path) +private string processVars(Project, Package)(string var, in Project project, in Package pack,in GeneratorSettings gsettings, bool is_path) { - auto idx = std.string.indexOf(var, '$'); - if (idx >= 0) { - auto vres = appender!string(); - while (idx >= 0) { - if (idx+1 >= var.length) break; - if (var[idx+1] == '$') { - vres.put(var[0 .. idx+1]); - var = var[idx+2 .. $]; - } else { - vres.put(var[0 .. idx]); - var = var[idx+1 .. $]; + import std.regex : ctRegex, replaceAll; - size_t idx2 = 0; - while( idx2 < var.length && isIdentChar(var[idx2]) ) idx2++; - auto varname = var[0 .. idx2]; - var = var[idx2 .. $]; - - vres.put(getVariable(varname, project, pack, gsettings)); - } - idx = std.string.indexOf(var, '$'); - } - vres.put(var); - var = vres.data; - } + enum varRE = ctRegex!`\$([\w_]+)|\$\{([\w_]+)\}`; + var = var.replaceAll!(m => getVariable(m[1].length ? m[1] : m[2], project, pack, gsettings))(varRE); if (is_path) { auto p = NativePath(var); if (!p.absolute) { @@ -1221,7 +1201,7 @@ "ARCH", "PLATFORM", "PLATFORM_POSIX", "BUILD_TYPE" ]; -private string getVariable(string name, in Project project, in Package pack, in GeneratorSettings gsettings) +private string getVariable(Project, Package)(string name, in Project project, in Package pack, in GeneratorSettings gsettings) { import std.process : environment; if (name == "PACKAGE_DIR") return pack.path.toNativeString(); @@ -1267,6 +1247,58 @@ } +unittest +{ + static struct MockPackage + { + this(string name) + { + this.name = name; + version (Posix) + path = NativePath("/pkgs/"~name~"/"); + else version (Windows) + path = NativePath(`C:\pkgs\`~name~`\`); + } + string name; + NativePath path; + } + + static struct MockProject + { + MockPackage rootPackage; + inout(MockPackage)[] getTopologicalPackageList() inout + { + return _dependencies; + } + private: + MockPackage[] _dependencies; + } + + MockProject proj = { + rootPackage: MockPackage("root"), + _dependencies: [MockPackage("dep1"), MockPackage("dep2")] + }; + auto pack = MockPackage("test"); + GeneratorSettings gsettings; + enum isPath = true; + // basic vars + assert(processVars("Hello $PACKAGE_DIR", proj, pack, gsettings, !isPath) == "Hello "~pack.path.toNativeString); + assert(processVars("Hello $ROOT_PACKAGE_DIR", proj, pack, gsettings, !isPath) == "Hello "~proj.rootPackage.path.toNativeString); + assert(processVars("Hello $DEP1_PACKAGE_DIR", proj, pack, gsettings, !isPath) == "Hello "~proj._dependencies[0].path.toNativeString); + // ${VAR} replacements, NOTE: PACKAGE_DIR et.al. end on / + assert(processVars("Hello ${PACKAGE_DIR}foobar", proj, pack, gsettings, !isPath) == "Hello "~(pack.path ~ "foobar").toNativeString); + // test with isPath + assert(processVars("local", proj, pack, gsettings, isPath) == (pack.path ~ "local").toNativeString); + // test other env variables + import std.process : environment; + environment["MY_ENV_VAR"] = "blablabla"; + assert(processVars("$MY_ENV_VAR", proj, pack, gsettings, !isPath) == "blablabla"); + assert(processVars("${MY_ENV_VAR}suffix", proj, pack, gsettings, !isPath) == "blablablasuffix"); + assert(processVars("$MY_ENV_VAR-suffix", proj, pack, gsettings, !isPath) == "blablabla-suffix"); + assert(processVars("$MY_ENV_VAR:suffix", proj, pack, gsettings, !isPath) == "blablabla:suffix"); + environment.remove("MY_ENV_VAR"); +} + /** Holds and stores a set of version selections for package dependencies. This is the runtime representation of the information contained in