diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d
index bcaae0f..3e96cd0 100644
--- a/source/dub/generators/build.d
+++ b/source/dub/generators/build.d
@@ -56,6 +56,11 @@
buildTargetRec(dep);
auto bs = ti.buildSettings.dup;
+ if (bs.targetType != TargetType.staticLibrary)
+ foreach (ldep; ti.linkDependencies) {
+ auto dbs = targets[ldep].buildSettings;
+ bs.addSourceFiles((Path(dbs.targetPath) ~ getTargetFileName(dbs, settings.platform)).toNativeString());
+ }
buildTarget(settings, bs, ti.pack, ti.config);
}
diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d
index c368377..ad5e147 100644
--- a/source/dub/generators/generator.d
+++ b/source/dub/generators/generator.d
@@ -30,9 +30,11 @@
{
struct TargetInfo {
Package pack;
+ Package[] packages;
string config;
BuildSettings buildSettings;
string[] dependencies;
+ string[] linkDependencies;
}
protected {
@@ -92,9 +94,9 @@
main_files ~= buildsettings.mainSourceFile;
}
- logInfo("Generate target %s (%s %s %s)", pack.name, buildsettings.targetType, buildsettings.targetPath, buildsettings.targetName);
+ logDiagnostic("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);
+ targets[pack.name] = TargetInfo(pack, [pack], configs[pack.name], buildsettings, null);
foreach (depname, depspec; pack.dependencies) {
if (!pack.hasDependency(depname, configs[pack.name])) continue;
@@ -106,21 +108,26 @@
if (depbs.targetType != TargetType.sourceLibrary && depbs.targetType != TargetType.none) {
// add a reference to the target binary and remove all source files in the dependency build settings
depbs.sourceFiles = depbs.sourceFiles.filter!(f => f.isLinkerFile()).array;
- auto target = Path(depbs.targetPath) ~ getTargetFileName(depbs, settings.platform);
- if (!target.absolute) target = pack.path ~ target;
- depbs.prependSourceFiles(target.toNativeString());
+ depbs.importFiles = null;
}
buildsettings.add(depbs);
- if (depname in targets)
- targets[pack.name].dependencies ~= dep.name;
+ if (auto pdt = depname in targets) {
+ auto pt = pack.name in targets;
+ pt.dependencies ~= depname;
+ pt.linkDependencies ~= depname;
+ if (depbs.targetType == TargetType.staticLibrary)
+ pt.linkDependencies ~= pdt.linkDependencies.filter!(d => !pt.linkDependencies.canFind(d)).array;
+ }
+ else targets[pack.name].packages ~= dep;
}
if (generates_binary) {
// add build type settings and convert plain DFLAGS to build options
m_project.addBuildTypeSettings(buildsettings, settings.platform, settings.buildType);
settings.compiler.extractBuildOptions(buildsettings);
+ enforceBuildRequirements(buildsettings);
targets[pack.name].buildSettings = buildsettings.dup;
}
@@ -185,7 +192,7 @@
/**
Runs pre-build commands and performs other required setup before project files are generated.
*/
-void prepareGeneration(BuildSettings buildsettings)
+void prepareGeneration(in BuildSettings buildsettings)
{
if( buildsettings.preGenerateCommands.length ){
logInfo("Running pre-generate commands...");
@@ -196,7 +203,7 @@
/**
Runs post-build commands and copies required files to the binary directory.
*/
-void finalizeGeneration(BuildSettings buildsettings, bool generate_binary)
+void finalizeGeneration(in BuildSettings buildsettings, bool generate_binary)
{
if (buildsettings.postGenerateCommands.length) {
logInfo("Running post-generate commands...");
@@ -221,7 +228,7 @@
}
}
-void runBuildCommands(string[] commands, in BuildSettings build_settings)
+void runBuildCommands(in string[] commands, in BuildSettings build_settings)
{
import std.process;
import dub.internal.utils;
diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d
index 495cc33..4285251 100644
--- a/source/dub/generators/visuald.d
+++ b/source/dub/generators/visuald.d
@@ -44,62 +44,82 @@
m_pkgMgr = mgr;
}
- override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) { assert(false); }
-
- override void generate(GeneratorSettings settings)
+ override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets)
{
if (settings.combined) m_combinedProject = true;
-
- auto buildsettings = settings.buildSettings;
- m_project.addBuildSettings(buildsettings, settings.platform, settings.config);
-
- prepareGeneration(buildsettings);
+ auto bs = targets[m_project.name].buildSettings;
+ prepareGeneration(bs);
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);
+ generateProjectFiles(settings, targets);
+ generateSolutionFile(settings, targets);
logInfo("VisualD project generated.");
-
- finalizeGeneration(buildsettings, true);
+ finalizeGeneration(bs, true);
}
-
+
private {
- void generateSolution(GeneratorSettings settings)
+ void generateSolutionFile(GeneratorSettings settings, in TargetInfo[string] targets)
{
auto ret = appender!(char[])();
auto configs = m_project.getPackageConfigs(settings.platform, settings.config);
+ auto some_uuid = generateUUID();
// Solution header
- ret.formattedWrite("
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010");
+ ret.put("Microsoft Visual Studio Solution File, Format Version 11.00\n");
+ ret.put("# Visual Studio 2010\n");
- generateSolutionEntry(ret, m_project.mainPackage, settings);
- if (!m_combinedProject) {
- performOnDependencies(m_project.mainPackage, configs, (pack){
- generateSolutionEntry(ret, pack, settings);
- });
+ bool[string] visited;
+ void generateSolutionEntry(string pack) {
+ if (pack in visited) return;
+ visited[pack] = true;
+
+ auto ti = targets[pack];
+
+ auto uuid = guid(pack);
+ ret.formattedWrite("Project(\"%s\") = \"%s\", \"%s\", \"%s\"\n",
+ some_uuid, pack, projFileName(pack), uuid);
+
+ if (ti.linkDependencies.length && ti.buildSettings.targetType != TargetType.staticLibrary) {
+ ret.put("\tProjectSection(ProjectDependencies) = postProject\n");
+ foreach (d; ti.linkDependencies)
+ if (!isHeaderOnlyPackage(d, targets)) {
+ // TODO: clarify what "uuid = uuid" should mean
+ ret.formattedWrite("\t\t%s = %s\n", guid(d), guid(d));
+ }
+ ret.put("\tEndProjectSection\n");
+ }
+
+ ret.put("EndProject\n");
+
+ foreach (d; ti.dependencies) generateSolutionEntry(d);
}
+
+ auto mainpack = m_project.mainPackage.name;
+
+ generateSolutionEntry(mainpack);
// Global section contains configurations
- ret.formattedWrite("
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Release|Win32 = Release|Win32
- Unittest|Win32 = Unittest|Win32
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution");
+ ret.put("Global\n");
+ ret.put("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n");
+ ret.formattedWrite("\t\t%s|Win32 = %s|Win32\n", settings.buildType, settings.buildType);
+ ret.put("\tEndGlobalSection\n");
+ ret.put("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n");
- generateSolutionConfig(ret, m_project.mainPackage());
+ const string[] sub = ["ActiveCfg", "Build.0"];
+ const string[] conf = [settings.buildType~"|Win32"];
+ auto projectUuid = guid(mainpack);
+ foreach (t; targets.byKey)
+ foreach (c; conf)
+ foreach (s; sub)
+ formattedWrite(ret, "\t\t%s.%s.%s = %s\n", guid(t), c, s, c);
// TODO: for all dependencies
+ ret.put("\tEndGlobalSection\n");
- ret.formattedWrite("
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal");
+ ret.put("\tGlobalSection(SolutionProperties) = preSolution\n");
+ ret.put("\t\tHideSolutionNode = FALSE\n");
+ ret.put("\tEndGlobalSection\n");
+ ret.put("EndGlobal\n");
// Writing solution file
logDebug("About to write to .sln file with %s bytes", to!string(ret.data().length));
@@ -109,165 +129,76 @@
sln.flush();
}
- void generateSolutionEntry(Appender!(char[]) ret, const Package pack, GeneratorSettings settings)
+
+ void generateProjectFiles(GeneratorSettings settings, in TargetInfo[string] targets)
{
- auto projUuid = generateUUID();
- auto projName = pack.name;
- auto projPath = projFileName(pack);
- auto projectUuid = guid(projName);
-
- // Write project header, like so
- // Project("{002A2DE9-8BB6-484D-9802-7E4AD4084715}") = "derelict", "..\inbase\source\derelict.visualdproj", "{905EF5DA-649E-45F9-9C15-6630AA815ACB}"
- ret.formattedWrite("\nProject(\"%s\") = \"%s\", \"%s\", \"%s\"",
- projUuid, projName, projPath, projectUuid);
-
- if (!m_combinedProject) {
- void addDepsRec(in Package p)
- {
- foreach(id, dependency; p.dependencies) {
- auto deppack = m_project.getDependency(id, true);
- if (!deppack) continue;
- if (isHeaderOnlyPackage(deppack, settings)) {
- addDepsRec(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);
- }
- }
- }
-
- if(pack.dependencies.length > 0) {
- ret.formattedWrite("
- ProjectSection(ProjectDependencies) = postProject");
- addDepsRec(pack);
- ret.formattedWrite("
- EndProjectSection");
- }
+ bool[string] visited;
+ void performRec(string name) {
+ if (name in visited) return;
+ visited[name] = true;
+ generateProjectFile(name, settings, targets);
+ foreach (d; targets[name].dependencies)
+ performRec(d);
}
-
- ret.formattedWrite("\nEndProject");
+
+ performRec(m_project.mainPackage.name);
}
- void generateSolutionConfig(Appender!(char[]) ret, const Package pack) {
- const string[] sub = [ "ActiveCfg", "Build.0" ];
- const string[] conf = [ "Debug|Win32", "Release|Win32" /*, "Unittest|Win32" */];
- auto projectUuid = guid(pack.name());
- foreach(c; conf)
- foreach(s; sub)
- formattedWrite(ret, "\n\t\t%s.%s.%s = %s", to!string(projectUuid), c, s, c);
- }
-
- void generateProjects(const Package main, GeneratorSettings settings) {
-
- // TODO: cyclic check
- auto configs = m_project.getPackageConfigs(settings.platform, settings.config);
-
- generateProj(main, settings);
-
- if (!m_combinedProject) {
- bool[string] generatedProjects;
- generatedProjects[main.name] = true;
- performOnDependencies(main, configs, (const Package dependency) {
- if(dependency.name in generatedProjects)
- return;
- generateProj(dependency, settings);
- } );
- }
- }
-
- bool isHeaderOnlyPackage(in Package pack, in GeneratorSettings settings)
+ bool isHeaderOnlyPackage(string pack, in TargetInfo[string] targets)
const {
- 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"))())
+ auto buildsettings = targets[pack].buildSettings;
+ if (!buildsettings.sourceFiles.any!(f => f.endsWith(".d"))())
return true;
return false;
}
- void generateProj(const Package pack, GeneratorSettings settings)
+ void generateProjectFile(string packname, GeneratorSettings settings, in TargetInfo[string] targets)
{
int i = 0;
auto ret = appender!(char[])();
- auto projName = pack.name;
- auto project_file_dir = m_project.mainPackage.path ~ projFileName(pack).parentPath;
+ auto project_file_dir = m_project.mainPackage.path ~ projFileName(packname).parentPath;
ret.put("\n");
- ret.formattedWrite(" %s\n", guid(projName));
+ ret.formattedWrite(" %s\n", guid(packname));
// Several configurations (debug, release, unittest)
- generateProjectConfiguration(ret, pack, "debug", settings);
- generateProjectConfiguration(ret, pack, "release", settings);
- generateProjectConfiguration(ret, pack, "unittest", settings);
+ generateProjectConfiguration(ret, packname, settings.buildType, settings, targets);
+ //generateProjectConfiguration(ret, packname, "release", settings, targets);
+ //generateProjectConfiguration(ret, packname, "unittest", settings, targets);
// Add all files
- auto configs = m_project.getPackageConfigs(settings.platform, settings.config);
- auto files = pack.getBuildSettings(settings.platform, configs[pack.name]);
- bool[SourceFile] sourceFiles;
+ auto files = targets[packname].buildSettings;
+ SourceFile[string] sourceFiles;
void addSourceFile(Path file_path, Path structure_path, bool build)
{
- SourceFile sf;
+ auto key = file_path.toString();
+ auto sf = sourceFiles.get(key, SourceFile.init);
sf.filePath = file_path;
- sf.structurePath = structure_path;
- if (build) {
- sf.build = false;
- if (sf in sourceFiles) sourceFiles.remove(sf);
- } else {
- sf.build = true;
- if (sf in sourceFiles) return;
+ if (!sf.build) {
+ sf.build = build;
+ sf.structurePath = structure_path;
}
- sf.build = build;
- sourceFiles[sf] = true;
- }
- if (m_combinedProject) {
- bool[const(Package)] basePackagesAdded;
-
- // add all package.json files to the project
- // and all source files
- performOnDependencies(pack, configs, (prj) {
- void addFile(string s, bool build) {
- auto sp = Path(s);
- if( !sp.absolute ) sp = prj.path ~ sp;
- // regroup in Folder by base package
- addSourceFile(sp.relativeTo(project_file_dir), Path(prj.basePackage().name) ~ sp.relativeTo(prj.path), build);
- }
-
- string[] prjFiles;
-
- // Avoid multiples package.json when using sub-packages.
- // Only add the package info file if no other package/sub-package from the same base package
- // has been seen yet.
- {
- const(Package) base = prj.basePackage();
-
- if (base !in basePackagesAdded) {
- prjFiles ~= prj.packageInfoFile.toNativeString();
- basePackagesAdded[base] = true;
- }
- }
-
- auto settings = prj.getBuildSettings(settings.platform, configs[prj.name]);
- foreach (f; prjFiles) addFile(f, false);
- foreach (f; settings.sourceFiles) addFile(f, true);
- foreach (f; settings.importFiles) addFile(f, false);
- foreach (f; settings.stringImportFiles) addFile(f, false);
- });
+ sourceFiles[key] = sf;
}
void addFile(string s, bool build) {
auto sp = Path(s);
- if( !sp.absolute ) sp = pack.path ~ sp;
- addSourceFile(sp.relativeTo(project_file_dir), sp.relativeTo(pack.path), build);
+ assert(sp.absolute);
+ //if( !sp.absolute ) sp = pack.path ~ sp;
+ addSourceFile(sp.relativeTo(project_file_dir), determineStructurePath(sp, targets[packname]), build);
}
- addFile(pack.packageInfoFile.toNativeString(), false);
- foreach(s; files.sourceFiles) addFile(s, true);
+ foreach (p; targets[packname].packages) addFile(p.packageInfoFile.toNativeString(), false);
+ if (files.targetType == TargetType.staticLibrary)
+ foreach(s; files.sourceFiles.filter!(s => !isLinkerFile(s))) addFile(s, true);
+ else
+ foreach(s; files.sourceFiles.filter!(s => !s.endsWith(".lib"))) addFile(s, true);
foreach(s; files.importFiles) addFile(s, false);
foreach(s; files.stringImportFiles) addFile(s, false);
// Create folders and files
- ret.formattedWrite(" ", getPackageFileName(pack));
+ ret.formattedWrite(" ", getPackageFileName(packname));
Path lastFolder;
- foreach(source; sortedSources(sourceFiles.keys)) {
+ foreach(source; sortedSources(sourceFiles.values)) {
logDebug("source looking at %s", source.structurePath);
auto cur = source.structurePath[0 .. source.structurePath.length-1];
if(lastFolder != cur) {
@@ -292,23 +223,17 @@
ret.put("\n ");
ret.put("\n \n");
- logDebug("About to write to '%s.visualdproj' file %s bytes", getPackageFileName(pack), ret.data().length);
- auto proj = openFile(projFileName(pack), FileMode.CreateTrunc);
+ logDebug("About to write to '%s.visualdproj' file %s bytes", getPackageFileName(packname), ret.data().length);
+ auto proj = openFile(projFileName(packname), FileMode.CreateTrunc);
scope(exit) proj.close();
proj.put(ret.data());
proj.flush();
}
- void generateProjectConfiguration(Appender!(char[]) ret, const Package pack, string type, GeneratorSettings settings)
+ void generateProjectConfiguration(Appender!(char[]) ret, string pack, string type, GeneratorSettings settings, in TargetInfo[string] targets)
{
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_project.addBuildSettings(buildsettings, settings.platform, settings.config, pack);
- m_project.addBuildTypeSettings(buildsettings, settings.platform, type);
- settings.compiler.extractBuildOptions(buildsettings);
- enforceBuildRequirements(buildsettings);
+ auto buildsettings = targets[pack].buildSettings.dup;
string[] getSettings(string setting)(){ return __traits(getMember, buildsettings, setting); }
string[] getPathSettings(string setting)()
@@ -351,21 +276,21 @@
int output_type = StaticLib; // library
string output_ext = "lib";
- if (pbuildsettings.targetType == TargetType.executable)
+ if (buildsettings.targetType == TargetType.executable)
{
output_type = Executable;
output_ext = "exe";
}
- else if (pbuildsettings.targetType == TargetType.dynamicLibrary)
+ else if (buildsettings.targetType == TargetType.dynamicLibrary)
{
output_type = DynamicLib;
output_ext = "dll";
}
string debugSuffix = type == "debug" ? "_d" : "";
- auto bin_path = pack is m_project.mainPackage ? Path(pbuildsettings.targetPath) : Path(".dub/lib/");
+ auto bin_path = pack == m_project.mainPackage.name ? Path(buildsettings.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);
+ ret.formattedWrite(" %s%s%s.%s\n", bin_path.toNativeString(), buildsettings.targetName, debugSuffix, output_ext);
// include paths and string imports
string imports = join(getPathSettings!"importPaths"(), " ");
@@ -383,17 +308,23 @@
// Add libraries, system libs need to be suffixed by ".lib".
string linkLibs = join(map!(a => a~".lib")(getSettings!"libs"()), " ");
string addLinkFiles = join(getSettings!"sourceFiles"().filter!(s => s.endsWith(".lib"))(), " ");
- if (output_type != StaticLib) ret.formattedWrite(" %s %s phobos.lib\n", linkLibs, addLinkFiles);
+ if (arch == "x86") addLinkFiles ~= " phobos.lib";
+ if (output_type != StaticLib) ret.formattedWrite(" %s %s\n", linkLibs, addLinkFiles);
// Unittests
ret.formattedWrite(" %s\n", buildsettings.options & BuildOptions.unittests ? "1" : "0");
// compute directory for intermediate files (need dummy/ because of how -op determines the resulting path)
- auto relpackpath = pack.path.relativeTo(project_file_dir);
- uint ndummy = 0;
- foreach (i; 0 .. relpackpath.length)
- if (relpackpath[i] == "..") ndummy++;
- string intersubdir = (ndummy*2 > relpackpath.length ? replicate("dummy/", ndummy*2-relpackpath.length) : "") ~ getPackageFileName(pack);
+ size_t ndummy = 0;
+ foreach (f; buildsettings.sourceFiles) {
+ auto rpath = Path(f).relativeTo(project_file_dir);
+ size_t nd = 0;
+ foreach (i; 0 .. rpath.length)
+ if (rpath[i] == "..")
+ nd++;
+ if (nd > ndummy) ndummy = nd;
+ }
+ string intersubdir = replicate("dummy/", ndummy) ~ getPackageFileName(pack);
ret.put(" 0\n");
ret.put(" 0\n");
@@ -485,7 +416,8 @@
}
string generateUUID() const {
- return "{" ~ randomUUID().toString() ~ "}";
+ import std.string;
+ return "{" ~ toUpper(randomUUID().toString()) ~ "}";
}
string guid(string projectName) {
@@ -496,10 +428,10 @@
auto solutionFileName() const {
version(DUBBING) return getPackageFileName(m_project.mainPackage()) ~ ".dubbed.sln";
- else return getPackageFileName(m_project.mainPackage()) ~ ".sln";
+ else return getPackageFileName(m_project.mainPackage.name) ~ ".sln";
}
- Path projFileName(ref const Package pack) const {
+ Path projFileName(string pack) const {
auto basepath = Path(".");//Path(".dub/");
version(DUBBING) return basepath ~ (getPackageFileName(pack) ~ ".dubbed.visualdproj");
else return basepath ~ (getPackageFileName(pack) ~ ".visualdproj");
@@ -510,14 +442,14 @@
struct SourceFile {
Path structurePath;
Path filePath;
- bool build = true;
+ bool build;
hash_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) {
- enforce(!a.structurePath.empty());
- enforce(!b.structurePath.empty());
+ assert(!a.structurePath.empty());
+ assert(!b.structurePath.empty());
auto as = a.structurePath;
auto bs = b.structurePath;
@@ -565,7 +497,16 @@
}
}
-private string getPackageFileName(in Package pack)
+private Path determineStructurePath(Path file_path, in ProjectGenerator.TargetInfo target)
{
- return pack.name.replace(":", "_");
+ foreach (p; target.packages) {
+ if (file_path.startsWith(p.path))
+ return Path(getPackageFileName(p.name)) ~ file_path[p.path.length .. $];
+ }
+ return Path("misc/") ~ file_path.head;
+}
+
+private string getPackageFileName(string pack)
+{
+ return pack.replace(":", "_");
}
diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d
index dbaa455..5c4a45f 100644
--- a/source/dub/internal/utils.d
+++ b/source/dub/internal/utils.d
@@ -90,7 +90,7 @@
return fi.isDirectory;
}
-void runCommands(string[] commands, string[string] env = null)
+void runCommands(in string[] commands, string[string] env = null)
{
foreach(cmd; commands){
logDiagnostic("Running %s", cmd);