diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 1f92121..057a88b 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -393,6 +393,7 @@ bool m_rdmd = false; bool m_run = false; bool m_force = false; + bool m_combined = false; bool m_print_platform, m_print_builds, m_print_configs; } @@ -416,6 +417,10 @@ { super.prepare(args); + args.getopt("combined", &m_combined, [ + "Tries to build the whole project in a single compiler run." + ]); + args.getopt("print-builds", &m_print_builds, [ "Prints the list of available build types" ]); @@ -466,6 +471,7 @@ gensettings.buildType = m_build_type; gensettings.compiler = m_compiler; gensettings.buildSettings = m_buildSettings; + gensettings.combined = m_combined; gensettings.run = m_run; gensettings.runArgs = app_args; gensettings.force = m_force; diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index a8ee98a..b647aa5 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -178,7 +178,6 @@ void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects); } - /// BuildPlatform specific settings, like needed libraries or additional /// include paths. struct BuildSettings { @@ -205,17 +204,53 @@ BuildRequirements requirements; BuildOptions options; + BuildSettings dup() + const { + BuildSettings ret; + foreach (m; __traits(allMembers, BuildSettings)) { + static if (is(typeof(__traits(getMember, ret, m) = __traits(getMember, this, m).dup))) + __traits(getMember, ret, m) = __traits(getMember, this, m).dup; + else static if (is(typeof(__traits(getMember, ret, m) = __traits(getMember, this, m)))) + __traits(getMember, ret, m) = __traits(getMember, this, m); + } + assert(ret.targetType == targetType); + assert(ret.targetName == targetName); + assert(ret.importPaths == importPaths); + return ret; + } + + void add(in BuildSettings bs) + { + addDFlags(bs.dflags); + addLFlags(bs.lflags); + addLibs(bs.libs); + addSourceFiles(bs.sourceFiles); + addCopyFiles(bs.copyFiles); + addVersions(bs.versions); + addDebugVersions(bs.debugVersions); + addImportPaths(bs.importPaths); + addStringImportPaths(bs.stringImportPaths); + addImportFiles(bs.importFiles); + addStringImportFiles(bs.stringImportFiles); + addPreGenerateCommands(bs.preGenerateCommands); + addPostGenerateCommands(bs.postGenerateCommands); + addPreBuildCommands(bs.preBuildCommands); + addPostBuildCommands(bs.postBuildCommands); + } + void addDFlags(in string[] value...) { dflags ~= value; } void removeDFlags(in string[] value...) { remove(dflags, value); } void addLFlags(in string[] value...) { lflags ~= value; } void addLibs(in string[] value...) { add(libs, value); } void addSourceFiles(in string[] value...) { add(sourceFiles, value); } + 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 addVersions(in string[] value...) { add(versions, value); } void addDebugVersions(in string[] value...) { add(debugVersions, value); } void addImportPaths(in string[] value...) { add(importPaths, value); } void addStringImportPaths(in string[] value...) { add(stringImportPaths, value); } + void prependStringImportPaths(in string[] value...) { prepend(stringImportPaths, value); } void addImportFiles(in string[] value...) { add(importFiles, value); } void removeImportFiles(in string[] value...) { removePaths(importFiles, value); } void addStringImportFiles(in string[] value...) { add(stringImportFiles, value); } @@ -246,6 +281,24 @@ } } + private void prepend(ref string[] arr, in string[] vals, bool no_duplicates = true) + { + if (!no_duplicates) { + arr = vals ~ arr; + return; + } + + foreach_reverse (v; vals) { + bool found = false; + foreach (i; 0 .. arr.length) + if (arr[i] == v) { + found = true; + break; + } + if (!found) arr = v ~ arr; + } + } + private void removePaths(ref string[] arr, in string[] vals) { bool matches(string s) @@ -429,6 +482,21 @@ } +bool isLinkerFile(string f) +{ + import std.path; + switch (extension(f)) { + default: + return false; + version (Windows) { + case ".lib", ".obj", ".res": + return true; + } else { + case ".a", ".o", ".so", ".dylib": + return true; + } + } +} private { Compiler[] s_compilers; diff --git a/source/dub/dub.d b/source/dub/dub.d index bc6faa9..029842f 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -195,7 +195,7 @@ void generateProject(string ide, GeneratorSettings settings) { auto generator = createProjectGenerator(ide, m_project, m_packageManager); if (m_dryRun) return; // TODO: pass m_dryRun to the generator - generator.generateProject(settings); + generator.generate(settings); } void testProject(BuildSettings build_settings, BuildPlatform platform, string config, Path custom_main_file, string[] run_args) @@ -304,7 +304,7 @@ settings.config = test_config; } - generator.generateProject(settings); + generator.generate(settings); } /// Outputs a JSON description of the project, including its dependencies. diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index c2040a4..202928a 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -35,24 +35,45 @@ this(Project app, PackageManager mgr) { + super(app); m_project = app; m_packageMan = mgr; } - void generateProject(GeneratorSettings settings) + override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) { - scope(exit) cleanupTemporaries(); + scope (exit) cleanupTemporaries(); + bool[string] visited; + void buildTargetRec(string target) + { + if (target in visited) return; + visited[target] = true; + + auto ti = targets[target]; + + foreach (dep; ti.dependencies) + buildTargetRec(dep); + + auto bs = ti.buildSettings.dup; + buildTarget(settings, bs, ti.pack, ti.config); + } + + // build all targets + buildTargetRec(m_project.mainPackage.name); + + // run the generated executable + auto buildsettings = targets[m_project.mainPackage.name].buildSettings; + if (settings.run && !(buildsettings.options & BuildOptions.syntaxOnly)) { + auto exe_file_path = Path(buildsettings.targetPath) ~ getTargetFileName(buildsettings, settings.platform); + runTarget(exe_file_path, buildsettings, settings.runArgs); + } + } + + private void buildTarget(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config) + { auto cwd = Path(getcwd()); - if (!settings.config.length) settings.config = m_project.getDefaultConfiguration(settings.platform); - - auto buildsettings = settings.buildSettings; - m_project.addBuildSettings(buildsettings, settings.platform, settings.config, null, settings.buildType == "ddox"); - m_project.addBuildTypeSettings(buildsettings, settings.platform, settings.buildType); bool generate_binary = !(buildsettings.options & BuildOptions.syntaxOnly); - // determine the absolute target path - if (!Path(buildsettings.targetPath).absolute) - buildsettings.targetPath = (m_project.mainPackage.path ~ Path(buildsettings.targetPath)).toNativeString(); // make all paths relative to shrink the command line string makeRelative(string path) { auto p = Path(path); if (p.absolute) p = p.relativeTo(cwd); return p.toNativeString(); } @@ -60,42 +81,33 @@ foreach (ref p; buildsettings.importPaths) p = makeRelative(p); foreach (ref p; buildsettings.stringImportPaths) p = makeRelative(p); - // convert plain DFLAGS to build options - settings.compiler.extractBuildOptions(buildsettings); - // perform the actual build - if (settings.rdmd) performRDMDBuild(settings, buildsettings); - else if (settings.direct || !generate_binary) performDirectBuild(settings, buildsettings); - else performCachedBuild(settings, buildsettings); + if (settings.rdmd) performRDMDBuild(settings, buildsettings, pack, config); + else if (settings.direct || !generate_binary) performDirectBuild(settings, buildsettings, pack, config); + else performCachedBuild(settings, buildsettings, pack, config); // run post-build commands if (buildsettings.postBuildCommands.length) { logInfo("Running post-build commands..."); runBuildCommands(buildsettings.postBuildCommands, buildsettings); } - - // run the generated executable - if (generate_binary && settings.run) { - auto exe_file_path = Path(buildsettings.targetPath) ~ getTargetFileName(buildsettings, settings.platform); - runTarget(exe_file_path, buildsettings, settings.runArgs); - } } - void performCachedBuild(GeneratorSettings settings, BuildSettings buildsettings) + void performCachedBuild(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config) { auto cwd = Path(getcwd()); - auto build_id = computeBuildID(settings); - auto target_path = m_project.mainPackage.path ~ format(".dub/build/%s/", build_id); + auto build_id = computeBuildID(config, buildsettings, settings); + auto target_path = pack.path ~ format(".dub/build/%s/", build_id); if (!settings.force && isUpToDate(target_path, buildsettings, settings.platform)) { - logInfo("Target is up to date. Using existing build in .dub/build/%s/. Use --force to force a rebuild.", build_id); + logInfo("Target is up to date. Using existing build in %s. Use --force to force a rebuild.", target_path.toNativeString()); copyTargetFile(target_path, buildsettings, settings.platform); return; } if (!isWritableDir(target_path, true)) { logInfo("Build directory %s is not writable. Falling back to direct build in the system's temp folder.", target_path.relativeTo(cwd).toNativeString()); - performDirectBuild(settings, buildsettings); + performDirectBuild(settings, buildsettings, pack, config); return; } @@ -106,7 +118,7 @@ prepareGeneration(buildsettings); finalizeGeneration(buildsettings, generate_binary); - logInfo("Building configuration \""~settings.config~"\", build type "~settings.buildType); + logInfo("Building %s configuration \"%s\", build type %s.", pack.name, config, settings.buildType); if( buildsettings.preBuildCommands.length ){ logInfo("Running pre-build commands..."); @@ -121,12 +133,12 @@ copyTargetFile(target_path, buildsettings, settings.platform); } - void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings) + void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config) { auto cwd = Path(getcwd()); //Added check for existance of [AppNameInPackagejson].d //If exists, use that as the starting file. - auto mainsrc = buildsettings.mainSourceFile.length ? m_project.mainPackage.path ~ buildsettings.mainSourceFile : getMainSourceFile(m_project); + auto mainsrc = buildsettings.mainSourceFile.length ? pack.path ~ buildsettings.mainSourceFile : getMainSourceFile(pack); // do not pass all source files to RDMD, only the main source file buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(s => !s.endsWith(".d"))().array(); @@ -168,7 +180,7 @@ runCommands(buildsettings.preBuildCommands); } - logInfo("Building configuration "~settings.config~", build type "~settings.buildType); + logInfo("Building configuration "~config~", build type "~settings.buildType); logInfo("Running rdmd..."); logDiagnostic("rdmd %s", join(flags, " ")); @@ -183,7 +195,7 @@ } } - void performDirectBuild(GeneratorSettings settings, ref BuildSettings buildsettings) + void performDirectBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config) { auto cwd = Path(getcwd()); @@ -197,7 +209,7 @@ f = fp.toNativeString(); } - logInfo("Building configuration \""~settings.config~"\", build type "~settings.buildType); + logInfo("Building configuration \""~config~"\", build type "~settings.buildType); prepareGeneration(buildsettings); @@ -237,7 +249,7 @@ } } - private string computeBuildID(GeneratorSettings settings) + private string computeBuildID(string config, in BuildSettings buildsettings, GeneratorSettings settings) { import std.digest.digest; import std.digest.sha; @@ -246,7 +258,7 @@ // ... auto hashstr = hash.finish().toHexString().idup; - return format("%s-%s-%s-%s-%s", settings.config, settings.buildType, + return format("%s-%s-%s-%s-%s", config, settings.buildType, settings.platform.architecture.join("."), settings.platform.compilerBinary, hashstr); } @@ -264,22 +276,28 @@ import std.datetime; auto targetfile = target_path ~ getTargetFileName(buildsettings, platform); - if (!existsFile(targetfile)) return false; + if (!existsFile(targetfile)) { + logDiagnostic("Target '%s' doesn't exist, need rebuild.", targetfile.toNativeString()); + return false; + } auto targettime = getFileInfo(targetfile).timeModified; auto allfiles = appender!(string[]); allfiles ~= buildsettings.sourceFiles; allfiles ~= buildsettings.importFiles; allfiles ~= buildsettings.stringImportFiles; - foreach (p; m_project.getTopologicalPackageList()) - allfiles ~= p.packageInfoFile.toNativeString(); + // TODO: add library files + /*foreach (p; m_project.getTopologicalPackageList()) + allfiles ~= p.packageInfoFile.toNativeString();*/ foreach (file; allfiles.data) { auto ftime = getFileInfo(file).timeModified; if (ftime > Clock.currTime) logWarn("File '%s' was modified in the future. Please re-save.", file); - if (ftime > targettime) + if (ftime > targettime) { + logDiagnostic("File '%s' modified, need rebuild.", file); return false; + } } return true; } @@ -291,6 +309,7 @@ Path target_file; scope (failure) { + logInfo("FAIL %s %s %s" , buildsettings.targetPath, buildsettings.targetName, buildsettings.targetType); auto tpath = Path(buildsettings.targetPath) ~ getTargetFileName(buildsettings, settings.platform); if (generate_binary && existsFile(tpath)) removeFile(tpath); @@ -337,7 +356,7 @@ } } - void runTarget(Path exe_file_path, BuildSettings buildsettings, string[] run_args) + void runTarget(Path exe_file_path, in BuildSettings buildsettings, string[] run_args) { if (buildsettings.targetType == TargetType.executable) { auto cwd = Path(getcwd()); @@ -377,28 +396,12 @@ } } -private Path getMainSourceFile(in Project prj) +private Path getMainSourceFile(in Package prj) { foreach (f; ["source/app.d", "src/app.d", "source/"~prj.name~".d", "src/"~prj.name~".d"]) - if (existsFile(prj.mainPackage.path ~ f)) - return prj.mainPackage.path ~ f; - return prj.mainPackage.path ~ "source/app.d"; -} - -private bool isLinkerFile(string f) -{ - import std.path; - switch (extension(f)) { - default: - return false; - version (Windows) { - case ".lib", ".obj", ".res": - return true; - } else { - case ".a", ".o", ".so", ".dylib": - return true; - } - } + if (existsFile(prj.path ~ f)) + return prj.path ~ f; + return prj.path ~ "source/app.d"; } unittest { diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index 97b0894..3148a30 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -18,6 +18,7 @@ import dub.packagemanager; import dub.project; +import std.array; import std.exception; import std.file; import std.string; @@ -26,9 +27,121 @@ /** Common interface for project generators/builders. */ -interface ProjectGenerator +class ProjectGenerator { - void generateProject(GeneratorSettings settings); + struct TargetInfo { + Package pack; + string config; + BuildSettings buildSettings; + string[] dependencies; + } + + protected { + Project m_project; + } + + this(Project project) + { + m_project = project; + } + + void generate(GeneratorSettings settings) + { + if (!settings.config.length) settings.config = m_project.getDefaultConfiguration(settings.platform); + + TargetInfo[string] targets; + string[string] configs = m_project.getPackageConfigs(settings.platform, settings.config); + + string[] mainfiles; + collect(settings, m_project.mainPackage, targets, configs, mainfiles); + downwardsInheritSettings(m_project.mainPackage.name, targets, targets[m_project.mainPackage.name].buildSettings); + auto bs = &targets[m_project.mainPackage.name].buildSettings; + if (bs.targetType == TargetType.executable) bs.addSourceFiles(mainfiles); + + generateTargets(settings, targets); + } + + abstract void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets); + + private BuildSettings collect(GeneratorSettings settings, Package pack, ref TargetInfo[string] targets, in string[string] configs, ref string[] main_files) + { + if (auto pt = pack.name in targets) return pt.buildSettings; + + // determine the actual target type + auto shallowbs = pack.getBuildSettings(settings.platform, configs[pack.name]); + logInfo("TT %s: %s", pack.name, shallowbs.targetType); + TargetType tt = shallowbs.targetType; + if (pack is m_project.mainPackage) { + if (tt == TargetType.autodetect || tt == TargetType.library) tt = TargetType.staticLibrary; + } else { + if (tt == TargetType.autodetect || tt == TargetType.library) tt = settings.combined ? TargetType.sourceLibrary : TargetType.staticLibrary; + else if (tt == TargetType.dynamicLibrary) { + logWarn("Dynamic libraries are not yet supported as dependencies - building as static library."); + tt = TargetType.staticLibrary; + } + } + shallowbs.targetType = tt; + bool generates_binary = tt != TargetType.sourceLibrary && tt != TargetType.none; + + // start to build up the build settings + BuildSettings buildsettings; + processVars(buildsettings, pack.path.toNativeString(), shallowbs, true); + buildsettings.addVersions("Have_" ~ stripDlangSpecialChars(pack.name)); + + // remove any mainSourceFile from library builds + if (buildsettings.targetType != TargetType.executable && buildsettings.mainSourceFile.length) { + buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(f => f != buildsettings.mainSourceFile)().array; + main_files ~= buildsettings.mainSourceFile; + } + + logInfo("Generate target %s (%s %s %s)", pack.name, buildsettings.targetType, buildsettings.targetPath, buildsettings.targetName); + if (generates_binary) + targets[pack.name] = TargetInfo(pack, configs[pack.name], buildsettings, null); + + foreach (depname, depspec; pack.dependencies) { + if (!pack.hasDependency(depname, configs[pack.name])) continue; + auto dep = m_project.getDependency(depname, depspec.optional); + if (!dep) continue; + + auto depbs = collect(settings, dep, targets, configs, main_files); + + if (depbs.targetType != TargetType.sourceLibrary) + depbs.sourceFiles = depbs.sourceFiles.filter!(f => f.isLinkerFile()).array; + + buildsettings.add(depbs); + + if (depname in targets) + targets[pack.name].dependencies ~= dep.name; + } + + if (generates_binary) { + // add a reference to the target binary + auto target = Path(buildsettings.targetPath) ~ getTargetFileName(buildsettings, settings.platform); + if (!target.absolute) target = pack.path ~ target; + buildsettings.prependSourceFiles(target.toNativeString()); + + // add build type settings and convert plain DFLAGS to build options + m_project.addBuildTypeSettings(buildsettings, settings.platform, settings.buildType); + settings.compiler.extractBuildOptions(buildsettings); + targets[pack.name].buildSettings = buildsettings; + + logInfo("TARGET %s %s", buildsettings.targetPath, buildsettings.targetName); + } + + return buildsettings; + } + + private void downwardsInheritSettings(string target, TargetInfo[string] targets, in BuildSettings root_settings) + { + auto ti = &targets[target]; + ti.buildSettings.addVersions(root_settings.versions); + ti.buildSettings.addDebugVersions(root_settings.debugVersions); + ti.buildSettings.addOptions(root_settings.options); + ti.buildSettings.prependStringImportPaths(root_settings.stringImportPaths); + + foreach (d; ti.dependencies) + downwardsInheritSettings(d, targets, root_settings); + } } @@ -39,6 +152,8 @@ string buildType; BuildSettings buildSettings; + bool combined; // compile all in one go instead of each dependency separately + // only used for generator "build" bool run, force, direct, clean, rdmd; string[] runArgs; diff --git a/source/dub/generators/monod.d b/source/dub/generators/monod.d index 7d9aa8d..6fcd953 100644 --- a/source/dub/generators/monod.d +++ b/source/dub/generators/monod.d @@ -28,7 +28,6 @@ class MonoDGenerator : ProjectGenerator { private { - Project m_app; PackageManager m_pkgMgr; string[string] m_projectUuids; bool m_singleProject = true; @@ -37,22 +36,24 @@ this(Project app, PackageManager mgr) { - m_app = app; + super(app); m_pkgMgr = mgr; m_allConfigs ~= Config("Debug", "AnyCPU", "Any CPU"); } + + override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) { assert(false); } - void generateProject(GeneratorSettings settings) + override void generate(GeneratorSettings settings) { logWarn("Note that the latest Mono-D has direct support for building DUB projects. It is recommended to directly open package.json instead of generating a Mono-D project."); auto buildsettings = settings.buildSettings; - m_app.addBuildSettings(buildsettings, settings.platform, settings.config); + m_project.addBuildSettings(buildsettings, settings.platform, settings.config); prepareGeneration(buildsettings); - logDebug("About to generate projects for %s, with %s direct dependencies.", m_app.mainPackage().name, m_app.mainPackage().dependencies().length); - generateProjects(m_app.mainPackage(), settings); + logDebug("About to generate projects for %s, with %s direct dependencies.", m_project.mainPackage().name, m_project.mainPackage().dependencies().length); + generateProjects(m_project.mainPackage(), settings); generateSolution(settings); finalizeGeneration(buildsettings, true); @@ -60,7 +61,7 @@ private void generateSolution(GeneratorSettings settings) { - auto sln = openFile(m_app.mainPackage().name ~ ".sln", FileMode.CreateTrunc); + auto sln = openFile(m_project.mainPackage().name ~ ".sln", FileMode.CreateTrunc); scope(exit) sln.close(); // Writing solution file @@ -71,9 +72,9 @@ sln.put("Microsoft Visual Studio Solution File, Format Version 11.00\n"); sln.put("# Visual Studio 2010\n"); - generateSolutionEntry(sln, settings, m_app.mainPackage); + generateSolutionEntry(sln, settings, m_project.mainPackage); if( !m_singleProject ) - performOnDependencies(m_app.mainPackage, pack => generateSolutionEntry(sln, settings, pack)); + performOnDependencies(m_project.mainPackage, pack => generateSolutionEntry(sln, settings, pack)); sln.put("Global\n"); @@ -86,7 +87,7 @@ // configuration platforms per project sln.put("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"); - auto projectUuid = guid(m_app.mainPackage.name); + auto projectUuid = guid(m_project.mainPackage.name); foreach(config; m_allConfigs) foreach(s; ["ActiveCfg", "Build.0"]) sln.formattedWrite("\t\t%s.%s|%s.%s = %s|%s\n", @@ -164,7 +165,7 @@ auto projName = pack.name; auto buildsettings = settings.buildSettings; - m_app.addBuildSettings(buildsettings, settings.platform, m_app.getDefaultConfiguration(settings.platform)); + m_project.addBuildSettings(buildsettings, settings.platform, m_project.getDefaultConfiguration(settings.platform)); // Mono-D does not have a setting for string import paths settings.compiler.prepareBuildSettings(buildsettings, BuildSetting.all & ~BuildSetting.stringImportPaths); @@ -260,7 +261,7 @@ } // TODO: add all files in stringImportFolders // add package.json files - foreach (p; m_app.getTopologicalPackageList()) + foreach (p; m_project.getTopologicalPackageList()) generateSourceEntry(p.packageInfoFile, pack.path, false); sln.put(" \n"); sln.put(""); diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d index ef82fc4..495cc33 100644 --- a/source/dub/generators/visuald.d +++ b/source/dub/generators/visuald.d @@ -32,7 +32,6 @@ class VisualDGenerator : ProjectGenerator { private { - Project m_app; PackageManager m_pkgMgr; string[string] m_projectUuids; bool m_combinedProject; @@ -40,20 +39,24 @@ this(Project app, PackageManager mgr, bool combined_project) { + super(app); m_combinedProject = combined_project; - m_app = app; m_pkgMgr = mgr; } - void generateProject(GeneratorSettings settings) + override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) { assert(false); } + + override void generate(GeneratorSettings settings) { + if (settings.combined) m_combinedProject = true; + auto buildsettings = settings.buildSettings; - m_app.addBuildSettings(buildsettings, settings.platform, settings.config); + m_project.addBuildSettings(buildsettings, settings.platform, settings.config); prepareGeneration(buildsettings); - logDebug("About to generate projects for %s, with %s direct dependencies.", m_app.mainPackage().name, m_app.mainPackage().dependencies().length); - generateProjects(m_app.mainPackage(), settings); + logDebug("About to generate projects for %s, with %s direct dependencies.", m_project.mainPackage().name, m_project.mainPackage().dependencies().length); + generateProjects(m_project.mainPackage(), settings); generateSolution(settings); logInfo("VisualD project generated."); @@ -64,16 +67,16 @@ void generateSolution(GeneratorSettings settings) { auto ret = appender!(char[])(); - auto configs = m_app.getPackageConfigs(settings.platform, settings.config); + auto configs = m_project.getPackageConfigs(settings.platform, settings.config); // Solution header ret.formattedWrite(" Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010"); - generateSolutionEntry(ret, m_app.mainPackage, settings); + generateSolutionEntry(ret, m_project.mainPackage, settings); if (!m_combinedProject) { - performOnDependencies(m_app.mainPackage, configs, (pack){ + performOnDependencies(m_project.mainPackage, configs, (pack){ generateSolutionEntry(ret, pack, settings); }); } @@ -88,7 +91,7 @@ EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution"); - generateSolutionConfig(ret, m_app.mainPackage()); + generateSolutionConfig(ret, m_project.mainPackage()); // TODO: for all dependencies @@ -122,11 +125,11 @@ void addDepsRec(in Package p) { foreach(id, dependency; p.dependencies) { - auto deppack = m_app.getDependency(id, true); + auto deppack = m_project.getDependency(id, true); if (!deppack) continue; if (isHeaderOnlyPackage(deppack, settings)) { addDepsRec(deppack); - } else if (!m_app.isRedundantDependency(p, deppack)) { + } else if (!m_project.isRedundantDependency(p, deppack)) { // TODO: clarify what "uuid = uuid" should mean auto uuid = guid(id); ret.formattedWrite("\n %s = %s", uuid, uuid); @@ -158,7 +161,7 @@ void generateProjects(const Package main, GeneratorSettings settings) { // TODO: cyclic check - auto configs = m_app.getPackageConfigs(settings.platform, settings.config); + auto configs = m_project.getPackageConfigs(settings.platform, settings.config); generateProj(main, settings); @@ -175,7 +178,7 @@ bool isHeaderOnlyPackage(in Package pack, in GeneratorSettings settings) const { - auto configs = m_app.getPackageConfigs(settings.platform, settings.config); + auto configs = m_project.getPackageConfigs(settings.platform, settings.config); auto pbuildsettings = pack.getBuildSettings(settings.platform, configs[pack.name]); if (!pbuildsettings.sourceFiles.any!(f => f.endsWith(".d"))()) return true; @@ -188,7 +191,7 @@ auto ret = appender!(char[])(); auto projName = pack.name; - auto project_file_dir = m_app.mainPackage.path ~ projFileName(pack).parentPath; + auto project_file_dir = m_project.mainPackage.path ~ projFileName(pack).parentPath; ret.put("\n"); ret.formattedWrite(" %s\n", guid(projName)); @@ -198,7 +201,7 @@ generateProjectConfiguration(ret, pack, "unittest", settings); // Add all files - auto configs = m_app.getPackageConfigs(settings.platform, settings.config); + auto configs = m_project.getPackageConfigs(settings.platform, settings.config); auto files = pack.getBuildSettings(settings.platform, configs[pack.name]); bool[SourceFile] sourceFiles; void addSourceFile(Path file_path, Path structure_path, bool build) @@ -298,12 +301,12 @@ void generateProjectConfiguration(Appender!(char[]) ret, const Package pack, string type, GeneratorSettings settings) { - auto project_file_dir = m_app.mainPackage.path ~ projFileName(pack).parentPath; - auto configs = m_app.getPackageConfigs(settings.platform, settings.config); + auto project_file_dir = m_project.mainPackage.path ~ projFileName(pack).parentPath; + auto configs = m_project.getPackageConfigs(settings.platform, settings.config); auto buildsettings = settings.buildSettings; auto pbuildsettings = pack.getBuildSettings(settings.platform, configs[pack.name]); - m_app.addBuildSettings(buildsettings, settings.platform, settings.config, pack); - m_app.addBuildTypeSettings(buildsettings, settings.platform, type); + m_project.addBuildSettings(buildsettings, settings.platform, settings.config, pack); + m_project.addBuildTypeSettings(buildsettings, settings.platform, type); settings.compiler.extractBuildOptions(buildsettings); enforceBuildRequirements(buildsettings); @@ -359,7 +362,7 @@ output_ext = "dll"; } string debugSuffix = type == "debug" ? "_d" : ""; - auto bin_path = pack is m_app.mainPackage ? Path(pbuildsettings.targetPath) : Path(".dub/lib/"); + auto bin_path = pack is m_project.mainPackage ? Path(pbuildsettings.targetPath) : Path(".dub/lib/"); bin_path.endsWithSlash = true; ret.formattedWrite(" %s\n", output_type); ret.formattedWrite(" %s%s%s.%s\n", bin_path.toNativeString(), pbuildsettings.targetName, debugSuffix, output_ext); @@ -475,7 +478,7 @@ void performOnDependencies(const Package main, string[string] configs, void delegate(const Package pack) op) { - foreach (p; m_app.getTopologicalPackageList(false, main, configs)) { + foreach (p; m_project.getTopologicalPackageList(false, main, configs)) { if (p is main) continue; op(p); } @@ -492,8 +495,8 @@ } auto solutionFileName() const { - version(DUBBING) return getPackageFileName(m_app.mainPackage()) ~ ".dubbed.sln"; - else return getPackageFileName(m_app.mainPackage()) ~ ".sln"; + version(DUBBING) return getPackageFileName(m_project.mainPackage()) ~ ".dubbed.sln"; + else return getPackageFileName(m_project.mainPackage()) ~ ".sln"; } Path projFileName(ref const Package pack) const { diff --git a/source/dub/project.d b/source/dub/project.d index 1ef1610..34df429 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -754,7 +754,7 @@ systemWide } -private void processVars(ref BuildSettings dst, string project_path, BuildSettings settings) +package void processVars(ref BuildSettings dst, string project_path, BuildSettings settings, bool include_target_settings = false) { dst.addDFlags(processVars(project_path, settings.dflags)); dst.addLFlags(processVars(project_path, settings.lflags)); @@ -773,6 +773,15 @@ dst.addPostBuildCommands(processVars(project_path, settings.postBuildCommands)); dst.addRequirements(settings.requirements); dst.addOptions(settings.options); + + if (include_target_settings) { + dst.targetType = settings.targetType; + dst.targetPath = processVars(settings.targetPath, project_path, true); + dst.targetName = settings.targetName; + dst.workingDirectory = processVars(settings.workingDirectory, project_path, true); + if (settings.mainSourceFile.length) + dst.mainSourceFile = processVars(settings.mainSourceFile, project_path, true); + } } private string[] processVars(string project_path, string[] vars, bool are_paths = false) @@ -831,7 +840,7 @@ return ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' || ch == '_'; } -private string stripDlangSpecialChars(string s) +package string stripDlangSpecialChars(string s) { import std.array; import std.uni;