diff --git a/source/dub/project.d b/source/dub/project.d index 5eadb2e..ffe4295 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -118,7 +118,7 @@ If this function returns `false`, it may be necessary to add more entries to `selections`, or to use `Dub.upgrade` to automatically select all - missing dependencies. + missing dependencies. */ bool hasAllDependencies() const { return m_hasAllDependencies; } @@ -608,7 +608,7 @@ if (usedefflags) { BuildSettings btsettings; m_rootPackage.addBuildTypeSettings(btsettings, platform, build_type); - + if (!for_root_package) { // don't propagate unittest switch to dependencies, as dependent // unit tests aren't run anyway and the additional code may @@ -665,7 +665,7 @@ return listBuildSetting!attributeName(platform, getPackageConfigs(platform, config), projectDescription, compiler, disableEscaping); } - + private string[] listBuildSetting(string attributeName)(BuildPlatform platform, string[string] configs, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping) { @@ -674,7 +674,7 @@ else return formatBuildSettingPlain!attributeName(platform, configs, projectDescription); } - + // Output a build setting formatted for a compiler private string[] formatBuildSettingCompiler(string attributeName)(BuildPlatform platform, string[string] configs, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping) @@ -706,7 +706,7 @@ case "options": auto bs = buildSettings.dup; bs.dflags = null; - + // Ensure trailing slash on directory paths auto ensureTrailingSlash = (string path) => path.endsWith(dirSeparator) ? path : path ~ dirSeparator; static if (attributeName == "importPaths") @@ -724,9 +724,9 @@ bs.lflags = null; 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)); - + if (bs.lflags) values = compiler.lflagsToDFlags( bs.lflags ); else if (bs.sourceFiles) @@ -738,7 +738,7 @@ default: assert(0); } - + // Escape filenames and paths if(!disableEscaping) { @@ -753,7 +753,7 @@ case "importPaths": case "stringImportPaths": return values.map!(escapeShellFileName).array(); - + default: return values; } @@ -761,7 +761,7 @@ return values; } - + // Output a build setting without formatting for any particular compiler private string[] formatBuildSettingPlain(string attributeName)(BuildPlatform platform, string[string] configs, ProjectDescription projectDescription) { @@ -772,21 +772,21 @@ enforce(attributeName == "targetType" || projectDescription.lookupRootPackage().targetType != TargetType.none, "Target type is 'none'. Cannot list build settings."); - + static if (attributeName == "targetType") if (projectDescription.rootPackage !in projectDescription.targetLookup) return ["none"]; auto targetDescription = projectDescription.lookupTarget(projectDescription.rootPackage); auto buildSettings = targetDescription.buildSettings; - + // Return any BuildSetting member attributeName as a range of strings. Don't attempt to fixup values. // allowEmptyString: When the value is a string (as opposed to string[]), // is empty string an actual permitted value instead of // a missing value? auto getRawBuildSetting(Package pack, bool allowEmptyString) { auto value = __traits(getMember, buildSettings, attributeName); - + static if( is(typeof(value) == string[]) ) return value; else static if( is(typeof(value) == string) ) @@ -811,7 +811,7 @@ else static assert(false, "Type of BuildSettings."~attributeName~" is unsupported."); } - + // Adjust BuildSetting member attributeName as needed. // Returns a range of strings. auto getFixedBuildSetting(Package pack) { @@ -825,16 +825,16 @@ attributeName == "sourceFiles" || attributeName == "linkerFiles" || attributeName == "importFiles" || attributeName == "stringImportFiles" || attributeName == "copyFiles" || attributeName == "mainSourceFile"; - + // For these, empty string means "main project directory", not "missing value" enum allowEmptyString = attributeName == "targetPath" || attributeName == "workingDirectory"; - + enum isEnumBitfield = attributeName == "requirements" || attributeName == "options"; enum isEnum = attributeName == "targetType"; - + auto values = getRawBuildSetting(pack, allowEmptyString); string fixRelativePath(string importPath) { return buildNormalizedPath(pack.path.toString(), importPath); } static string ensureTrailingSlash(string path) { return path.endsWith(dirSeparator) ? path : path ~ dirSeparator; } @@ -926,7 +926,7 @@ enforce(false, "--data="~requestedData~ " is not a valid option. See 'dub describe --help' for accepted --data= values."); } - + assert(0); } @@ -948,7 +948,7 @@ auto target = projectDescription.lookupTarget(projectDescription.rootPackage); foreach (file; target.buildSettings.sourceFiles.filter!(isLinkerFile)) target.buildSettings.addLinkerFiles(file); - + // Remove linker files from sourceFiles target.buildSettings.sourceFiles = target.buildSettings.sourceFiles @@ -1097,7 +1097,7 @@ dst.addDFlags(processVars(project, pack, settings.dflags)); dst.addLFlags(processVars(project, pack, settings.lflags)); dst.addLibs(processVars(project, pack, settings.libs)); - dst.addSourceFiles(processVars(project, pack, settings.sourceFiles, true)); + dst.addSourceFiles(processVars!true(project, pack, settings.sourceFiles, true)); dst.addImportFiles(processVars(project, pack, settings.importFiles, true)); dst.addStringImportFiles(processVars(project, pack, settings.stringImportFiles, true)); dst.addCopyFiles(processVars(project, pack, settings.copyFiles, true)); @@ -1123,19 +1123,19 @@ } } -private string[] processVars(in Project project, in Package pack, string[] vars, bool are_paths = false) +private string[] processVars(bool glob = false)(in Project project, in Package pack, string[] vars, bool are_paths = false) { auto ret = appender!(string[])(); - processVars(ret, project, pack, vars, are_paths); + processVars!glob(ret, project, pack, vars, are_paths); return ret.data; } -private void processVars(ref Appender!(string[]) dst, in Project project, in Package pack, string[] vars, bool are_paths = false) +private void processVars(bool glob = false)(ref Appender!(string[]) dst, in Project project, in Package pack, string[] vars, bool are_paths = false) { - foreach (var; vars) dst.put(processVars(var, project, pack, are_paths)); + foreach (var; vars) dst.put(processVars!glob(var, project, pack, are_paths)); } -private string processVars(string var, in Project project, in Package pack, bool is_path) +private auto processVars(bool glob = false)(string var, in Project project, in Package pack, bool is_path) { auto idx = std.string.indexOf(var, '$'); if (idx >= 0) { @@ -1161,12 +1161,41 @@ vres.put(var); var = vres.data; } - if (is_path) { - auto p = Path(var); - if (!p.absolute) { - return (pack.path ~ p).toNativeString(); - } else return p.toNativeString(); - } else return var; + + static if (glob) + assert(is_path, "can't glob something that isn't a path"); + else { + if (!is_path) + return var; + } + auto p = Path(var); + string res; + if (!p.absolute) + res = (pack.path ~ p).toNativeString(); + else + res = p.toNativeString(); + static if (!glob) + return res; + else { + // 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; + return dirEntries(res[0 .. sepIdx], SpanMode.depth) + .map!(de => de.name) + .filter!(name => globMatch(name, res)) + .array; + } } private string getVariable(string name, in Project project, in Package pack)