diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index daf5c5a..67db009 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -177,25 +177,25 @@ BuildSettings settings; auto compiler = new DMDCompiler; auto bp = compiler.determinePlatform(settings, "dmd", "x86"); - assert(bp.platform.canFind("windows")); + assert(bp.isWindows()); assert(bp.architecture.canFind("x86")); assert(bp.architecture.canFind("x86_omf")); assert(!bp.architecture.canFind("x86_mscoff")); settings = BuildSettings.init; bp = compiler.determinePlatform(settings, "dmd", "x86_omf"); - assert(bp.platform.canFind("windows")); + assert(bp.isWindows()); assert(bp.architecture.canFind("x86")); assert(bp.architecture.canFind("x86_omf")); assert(!bp.architecture.canFind("x86_mscoff")); settings = BuildSettings.init; bp = compiler.determinePlatform(settings, "dmd", "x86_mscoff"); - assert(bp.platform.canFind("windows")); + assert(bp.isWindows()); assert(bp.architecture.canFind("x86")); assert(!bp.architecture.canFind("x86_omf")); assert(bp.architecture.canFind("x86_mscoff")); settings = BuildSettings.init; bp = compiler.determinePlatform(settings, "dmd", "x86_64"); - assert(bp.platform.canFind("windows")); + assert(bp.isWindows()); assert(bp.architecture.canFind("x86_64")); assert(!bp.architecture.canFind("x86")); assert(!bp.architecture.canFind("x86_omf")); @@ -255,7 +255,7 @@ if (!(fields & BuildSetting.libs)) { resolveLibs(settings, platform); - if (platform.platform.canFind("windows")) + if (platform.isWindows()) settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array()); else settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); @@ -306,22 +306,22 @@ case TargetType.none: return null; case TargetType.sourceLibrary: return null; case TargetType.executable: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".exe"; else return settings.targetName.idup; case TargetType.library: case TargetType.staticLibrary: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".lib"; else return "lib" ~ settings.targetName ~ ".a"; case TargetType.dynamicLibrary: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".dll"; else if (platform.platform.canFind("darwin")) return "lib" ~ settings.targetName ~ ".dylib"; else return "lib" ~ settings.targetName ~ ".so"; case TargetType.object: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".obj"; else return settings.targetName ~ ".o"; } @@ -339,7 +339,7 @@ settings.addDFlags("-lib"); break; case TargetType.dynamicLibrary: - if (platform.compiler != "dmd" || platform.platform.canFind("windows") || platform.platform.canFind("osx")) + if (platform.compiler != "dmd" || platform.isWindows() || platform.platform.canFind("osx")) settings.addDFlags("-shared"); else settings.prependDFlags("-shared", "-defaultlib=libphobos2.so"); diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index 04ed9dd..bce6259 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -160,20 +160,20 @@ case TargetType.none: return null; case TargetType.sourceLibrary: return null; case TargetType.executable: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".exe"; else return settings.targetName.idup; case TargetType.library: case TargetType.staticLibrary: return "lib" ~ settings.targetName ~ ".a"; case TargetType.dynamicLibrary: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".dll"; else if (platform.platform.canFind("darwin")) return "lib" ~ settings.targetName ~ ".dylib"; else return "lib" ~ settings.targetName ~ ".so"; case TargetType.object: - if (platform.platform.canFind("windows")) + if (platform.isWindows()) return settings.targetName ~ ".obj"; else return settings.targetName ~ ".o"; } diff --git a/source/dub/compilers/utils.d b/source/dub/compilers/utils.d index d170b4c..d6632af 100644 --- a/source/dub/compilers/utils.d +++ b/source/dub/compilers/utils.d @@ -47,9 +47,9 @@ default: return false; case ".lib", ".obj", ".res", ".def": - return platform.platform.canFind("windows"); + return platform.isWindows(); case ".a", ".o", ".so", ".dylib": - return !platform.platform.canFind("windows"); + return !platform.isWindows(); } } @@ -74,6 +74,50 @@ /** + Determines if a specific file name has the extension related to dynamic libraries. + + This includes dynamic libraries and for Windows pdb, export and import library files. +*/ +bool isDynamicLibraryFile(const scope ref BuildPlatform platform, string f) +{ + import std.path; + switch (extension(f)) { + default: + return false; + case ".lib", ".pdb", ".dll", ".exp": + return platform.isWindows(); + case ".so", ".dylib": + return !platform.isWindows(); + } +} + +unittest { + BuildPlatform p; + + p.platform = ["windows"]; + assert(!isDynamicLibraryFile(p, "test.obj")); + assert(isDynamicLibraryFile(p, "test.lib")); + assert(isDynamicLibraryFile(p, "test.dll")); + assert(isDynamicLibraryFile(p, "test.pdb")); + assert(!isDynamicLibraryFile(p, "test.res")); + assert(!isDynamicLibraryFile(p, "test.o")); + assert(!isDynamicLibraryFile(p, "test.d")); + assert(!isDynamicLibraryFile(p, "test.dylib")); + + p.platform = ["something else"]; + assert(!isDynamicLibraryFile(p, "test.o")); + assert(!isDynamicLibraryFile(p, "test.a")); + assert(isDynamicLibraryFile(p, "test.so")); + assert(isDynamicLibraryFile(p, "test.dylib")); + assert(!isDynamicLibraryFile(p, "test.obj")); + assert(!isDynamicLibraryFile(p, "test.d")); + assert(!isDynamicLibraryFile(p, "test.lib")); + assert(!isDynamicLibraryFile(p, "test.dll")); + assert(!isDynamicLibraryFile(p, "test.pdb")); +} + + +/** Replaces each referenced import library by the appropriate linker flags. This function tries to invoke "pkg-config" if possible and falls back to @@ -89,7 +133,7 @@ if (settings.targetType == TargetType.library || settings.targetType == TargetType.staticLibrary) { logDiagnostic("Ignoring all import libraries for static library build."); settings.libs = null; - if (platform.platform.canFind("windows")) + if (platform.isWindows()) settings.sourceFiles = settings.sourceFiles.filter!(f => !f.endsWith(".lib")).array; } diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index e089e39..4ba7331 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -29,7 +29,7 @@ string getObjSuffix(const scope ref BuildPlatform platform) { - return platform.platform.canFind("windows") ? ".obj" : ".o"; + return platform.isWindows() ? ".obj" : ".o"; } string computeBuildName(string config, GeneratorSettings settings, const string[][] hashing...) @@ -64,6 +64,7 @@ override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) { + import std.path : setExtension; scope (exit) cleanupTemporaries(); void checkPkgRequirements(const(Package) pkg) @@ -99,18 +100,44 @@ foreach (dep; ti.dependencies) buildTargetRec(dep); - NativePath[] additional_dep_files; + NativePath[] additional_dep_files, dependencyBinariesToCopy; auto bs = ti.buildSettings.dup; foreach (ldep; ti.linkDependencies) { - if (bs.targetType != TargetType.staticLibrary && !(bs.options & BuildOption.syntaxOnly)) { - bs.addSourceFiles(target_paths[ldep].toNativeString()); + auto location = target_paths[ldep].toNativeString(); + + if (bs.targetType != TargetType.staticLibrary && !(bs.options & BuildOption.syntaxOnly) && isLinkerFile(settings.platform, location)) { + bs.addSourceFiles(location); + } else if (settings.platform.isWindows() && location.endsWith(".dll")) { + // switch from linking against the dll to against the import library, + // and copy any dependent build artifacts if found too + + const pdbFilename = location.setExtension(".pdb"); + if (existsFile(pdbFilename)) + dependencyBinariesToCopy ~= NativePath(pdbFilename); + + const importLibraryLocation = location.setExtension(".lib"); + if (existsFile(importLibraryLocation)) { + bs.addSourceFiles(importLibraryLocation); + dependencyBinariesToCopy ~= NativePath(importLibraryLocation); + } + + const exportFilesLocation = location.setExtension(".exp"); + if (existsFile(exportFilesLocation)) + dependencyBinariesToCopy ~= NativePath(exportFilesLocation); + + additional_dep_files ~= target_paths[ldep]; } else { additional_dep_files ~= target_paths[ldep]; } + + // copy any dynamic library dependencies to our target directory + if (isDynamicLibraryFile(settings.platform, location)) { + dependencyBinariesToCopy ~= target_paths[ldep]; + } } NativePath tpath; if (bs.targetType != TargetType.none) - if (buildTarget(settings, bs, ti.pack, ti.config, ti.packages, additional_dep_files, tpath)) + if (buildTarget(settings, bs, ti.pack, ti.config, ti.packages, additional_dep_files, dependencyBinariesToCopy, tpath)) any_cached = true; target_paths[target] = tpath; } @@ -120,7 +147,7 @@ // RDMD always builds everything at once and static libraries don't need their // dependencies to be built NativePath tpath; - buildTarget(settings, root_ti.buildSettings.dup, m_project.rootPackage, root_ti.config, root_ti.packages, null, tpath); + buildTarget(settings, root_ti.buildSettings.dup, m_project.rootPackage, root_ti.config, root_ti.packages, null, null, tpath); } else { buildTargetRec(m_project.rootPackage.name); @@ -144,7 +171,7 @@ } } - private bool buildTarget(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config, in Package[] packages, in NativePath[] additional_dep_files, out NativePath target_path) + private bool buildTarget(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config, in Package[] packages, in NativePath[] additional_dep_files, in NativePath[] dependencyBinariesToCopy, out NativePath target_path) { import std.path : absolutePath; @@ -161,9 +188,9 @@ // perform the actual build bool cached = false; - if (settings.rdmd) performRDMDBuild(settings, buildsettings, pack, config, target_path); - else if (settings.direct || !generate_binary) performDirectBuild(settings, buildsettings, pack, config, target_path); - else cached = performCachedBuild(settings, buildsettings, pack, config, build_id, packages, additional_dep_files, target_path); + if (settings.rdmd) performRDMDBuild(settings, buildsettings, pack, config, dependencyBinariesToCopy, target_path); + else if (settings.direct || !generate_binary) performDirectBuild(settings, buildsettings, pack, config, dependencyBinariesToCopy, target_path); + else cached = performCachedBuild(settings, buildsettings, pack, config, build_id, packages, additional_dep_files, dependencyBinariesToCopy, target_path); // HACK: cleanup dummy doc files, we shouldn't specialize on buildType // here and the compiler shouldn't need dummy doc output. @@ -185,7 +212,7 @@ } private bool performCachedBuild(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config, - string build_id, in Package[] packages, in NativePath[] additional_dep_files, out NativePath target_binary_path) + string build_id, in Package[] packages, in NativePath[] additional_dep_files, in NativePath[] dependencyBinariesToCopy, out NativePath target_binary_path) { auto cwd = NativePath(getcwd()); @@ -201,14 +228,14 @@ logDiagnostic("Using existing build in %s.", target_path.toNativeString()); target_binary_path = target_path ~ settings.compiler.getTargetFileName(buildsettings, settings.platform); if (!settings.tempBuild) - copyTargetFile(target_path, buildsettings, settings); + copyTargetFile(target_path, buildsettings, settings, dependencyBinariesToCopy); return true; } if (!isWritableDir(target_path, true)) { if (!settings.tempBuild) 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, pack, config, target_path); + performDirectBuild(settings, buildsettings, pack, config, dependencyBinariesToCopy, target_path); return false; } @@ -226,12 +253,12 @@ target_binary_path = getTargetPath(cbuildsettings, settings); if (!settings.tempBuild) - copyTargetFile(target_path, buildsettings, settings); + copyTargetFile(target_path, buildsettings, settings, dependencyBinariesToCopy); return false; } - private void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out NativePath target_path) + private void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, in NativePath[] dependencyBinariesToCopy, out NativePath target_path) { auto cwd = NativePath(getcwd()); //Added check for existence of [AppNameInPackagejson].d @@ -296,7 +323,7 @@ } } - private void performDirectBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out NativePath target_path) + private void performDirectBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, in NativePath[] dependencyBinariesToCopy, out NativePath target_path) { auto cwd = NativePath(getcwd()); auto generate_binary = !(buildsettings.options & BuildOption.syntaxOnly); @@ -369,7 +396,7 @@ return computeBuildName(config, settings, hashing); } - private void copyTargetFile(NativePath build_path, BuildSettings buildsettings, GeneratorSettings settings) + private void copyTargetFile(in NativePath build_path, in BuildSettings buildsettings, in GeneratorSettings settings, in NativePath[] dependencyBinariesToCopy) { if (!existsFile(NativePath(buildsettings.targetPath))) mkdirRecurse(buildsettings.targetPath); @@ -378,15 +405,23 @@ settings.compiler.getTargetFileName(buildsettings, settings.platform) ]; - // Windows: add .pdb if found + // Windows: add .pdb, .lib and .exp if found const tt = buildsettings.targetType; if ((tt == TargetType.executable || tt == TargetType.dynamicLibrary) && - settings.platform.platform.canFind("windows")) + settings.platform.isWindows()) { import std.path : setExtension; const pdbFilename = filenames[0].setExtension(".pdb"); if (existsFile(build_path ~ pdbFilename)) filenames ~= pdbFilename; + + const importLibraryLocation = filenames[0].setExtension(".lib"); + if (existsFile(build_path ~ importLibraryLocation)) + filenames ~= importLibraryLocation; + + const exportFilesLocation = filenames[0].setExtension(".exp"); + if (existsFile(build_path ~ exportFilesLocation)) + filenames ~= exportFilesLocation; } foreach (filename; filenames) @@ -395,6 +430,11 @@ logDiagnostic("Copying target from %s to %s", src.toNativeString(), buildsettings.targetPath); hardLinkFile(src, NativePath(buildsettings.targetPath) ~ filename, true); } + + foreach(src; dependencyBinariesToCopy) { + logDiagnostic("Copying target from %s to %s", src.toNativeString(), buildsettings.targetPath); + hardLinkFile(src, NativePath(buildsettings.targetPath) ~ src.head, true); + } } private bool isUpToDate(NativePath target_path, BuildSettings buildsettings, GeneratorSettings settings, in Package main_pack, in Package[] packages, in NativePath[] additional_dep_files) diff --git a/source/dub/platform.d b/source/dub/platform.d index 39e5557..face9ca 100644 --- a/source/dub/platform.d +++ b/source/dub/platform.d @@ -266,6 +266,20 @@ bp.frontendVersion = 2067; assert(bp.frontendVersionString == "2.067"); } + + /// Checks to see if platform field contains windows + bool isWindows() const { + import std.algorithm : canFind; + return this.platform.canFind("windows"); + } + /// + unittest { + BuildPlatform bp; + bp.platform = ["windows"]; + assert(bp.isWindows); + bp.platform = ["posix"]; + assert(!bp.isWindows); + } } diff --git a/test/1-dynLib-simple/.no_build_ldc2 b/test/1-dynLib-simple/.no_build_ldc2 deleted file mode 100644 index e69de29..0000000 --- a/test/1-dynLib-simple/.no_build_ldc2 +++ /dev/null diff --git a/test/1-dynLib-simple/dub.json b/test/1-dynLib-simple/dub.json index 71da415..03aa6d8 100644 --- a/test/1-dynLib-simple/dub.json +++ b/test/1-dynLib-simple/dub.json @@ -1,5 +1,5 @@ { "name": "dynlib-simple", "targetType": "dynamicLibrary", - "dflags-ldc": ["-link-defaultlib-shared"] + "dflags-ldc": ["-link-defaultlib-shared", "--fvisibility=public"] } diff --git a/test/issue2258-dynLib-exe-dep/.no_build_dmd b/test/issue2258-dynLib-exe-dep/.no_build_dmd new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue2258-dynLib-exe-dep/.no_build_dmd diff --git a/test/issue2258-dynLib-exe-dep/.no_build_gdc b/test/issue2258-dynLib-exe-dep/.no_build_gdc new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue2258-dynLib-exe-dep/.no_build_gdc diff --git a/test/issue2258-dynLib-exe-dep/dub.json b/test/issue2258-dynLib-exe-dep/dub.json new file mode 100644 index 0000000..bd99fdb --- /dev/null +++ b/test/issue2258-dynLib-exe-dep/dub.json @@ -0,0 +1,9 @@ +{ + "name": "dynlib-exe-dep", + "targetType": "executable", + "dependencies": { + "dynlib-simple": { "path": "../1-dynLib-simple/" } + }, + "dflags-ldc": ["-link-defaultlib-shared"] + +} diff --git a/test/issue2258-dynLib-exe-dep/source/app.d b/test/issue2258-dynLib-exe-dep/source/app.d new file mode 100644 index 0000000..52022fb --- /dev/null +++ b/test/issue2258-dynLib-exe-dep/source/app.d @@ -0,0 +1,7 @@ +module app; +import dynlib.app; + +extern(C) void main() +{ + entry(); +}