From 3ec07c196c9ab86db09c77bff7eb11cd5a5a9b1e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 19 Jul 2016 11:10:27 +0200 Subject: Add support for building DLLs with VC --- build2/cxx/link.cxx | 265 ++++++++++++++++++++++++++++------------------------ 1 file changed, 145 insertions(+), 120 deletions(-) (limited to 'build2/cxx/link.cxx') diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx index 7111aa5..e13cfe3 100644 --- a/build2/cxx/link.cxx +++ b/build2/cxx/link.cxx @@ -122,16 +122,10 @@ namespace build2 } } - // Extract system library search paths from MSVC. The linker doesn't seem - // to have any built-in paths and all of them are passed via the LIB - // environment variable. + // Extract system library search paths from MSVC. // - static void - msvc_library_search_paths (scope&, const string&, dir_paths&) - { - // @@ VC: how are we going to do this? E.g., cl-14 does this internally. - // Maybe that cld.c hack, seems to be passing stuff from INCLUDE..? - } + void + msvc_library_search_paths (scope&, const string&, dir_paths&); // msvc.cxx dir_paths link:: extract_library_paths (scope& bs) @@ -195,6 +189,14 @@ namespace build2 return r; } + // Alternative search for VC (msvc.cxx). + // + liba* + msvc_search_static (const path& ld, const dir_path&, prerequisite&); + + libs* + msvc_search_shared (const path& ld, const dir_path&, prerequisite&); + target* link:: search_library (optional& spc, prerequisite& p) { @@ -227,6 +229,14 @@ namespace build2 // prefix/extension that correspond to this compiler and/or its // target. // + // Unlike MinGW, VC's .lib/.dll.lib naming is by no means standard and + // we might need to search for other names. In fact, there is no + // reliable way to guess from the file name what kind of library it + // is, static or import and we will have to do deep inspection of such + // alternative names. However, if we did find .dll.lib, then we can + // assume that .lib is the static library without any deep inspection + // overhead. + // const char* e (""); if (cid == "msvc") @@ -262,24 +272,8 @@ namespace build2 if (cid == "msvc") { - // @@ VC TODO: still .lib, right? - // - // @@ Unlike MinGW, .dll.lib naming is by no means standard. So - // we might need to search for other names. In fact, there is - // no reliable way to guess from the file name what kind of - // library it is, static or import lib. I wonder if there is - // any way to tell by examining it (e.g., presence of __imp_* - // symbols)? - // - // Yes, there are several, in fact. One is lib.exe /LIST -- if - // there aren't any members, then it is most likely an import (or - // an empty static library -- is such a thing possible?). - // - // Another approach is dumpbin.exe (or link.exe /DUMP equivalent) - // /ARCHIVEMEMBERS and /LINKERMEMBER options and the __impl__ - // symbols (or _IMPORT_DESCRIPTOR_). Note, however, that - // apparently it is possible to have a hybrid library. - // + sn = path (p.name); + e = "dll.lib"; } else { @@ -315,32 +309,12 @@ namespace build2 { timestamp mt; - // liba - // - if (!an.empty ()) - { - f = d; - f /= an; - - if ((mt = file_mtime (f)) != timestamp_nonexistent) - { - // Enter the target. Note that because the search paths are - // normalized, the result is automatically normalized as well. - // - // Note that this target is outside any project which we treat - // as out trees. - // - a = &targets.insert (d, dir_path (), p.name, ae, trace); - - if (a->path ().empty ()) - a->path (move (f)); - - a->mtime (mt); - } - } - // libs // + // Look for the shared library first. The order is important for VC: + // only if we found .dll.lib can we safely assumy that just .lib is a + // static library. + // if (!sn.empty ()) { f = d; @@ -374,6 +348,45 @@ namespace build2 } } + // liba + // + // If we didn't find .dll.lib then we cannot assume .lib is static. + // + if (!an.empty () && (s != nullptr || cid != "msvc")) + { + f = d; + f /= an; + + if ((mt = file_mtime (f)) != timestamp_nonexistent) + { + // Enter the target. Note that because the search paths are + // normalized, the result is automatically normalized as well. + // + // Note that this target is outside any project which we treat + // as out trees. + // + a = &targets.insert (d, dir_path (), p.name, ae, trace); + + if (a->path ().empty ()) + a->path (move (f)); + + a->mtime (mt); + } + } + + // Alternative search for VC. + // + if (cid == "msvc") + { + const path& ld (cast (rs["config.bin.ld"])); + + if (s == nullptr && !sn.empty ()) + s = msvc_search_shared (ld, d, p); + + if (a == nullptr && !an.empty ()) + a = msvc_search_static (ld, d, p); + } + if (a != nullptr || s != nullptr) { pd = &d; @@ -635,8 +648,6 @@ namespace build2 } case otype::s: { - //@@ VC: DLL name. - if (tclass == "macosx") { p = "lib"; @@ -1401,7 +1412,7 @@ namespace build2 // Ok, so we are updating. Finish building the command line. // - string out, out1; // Storage. + string out, out1, out2; // Storage. // Translate paths to relative (to working directory) ones. This results // in easier to read diagnostics. @@ -1410,7 +1421,31 @@ namespace build2 switch (lt) { + case otype::a: + { + args[0] = cast (rs["config.bin.ar"]).string ().c_str (); + + if (cid == "msvc") + { + // lib.exe has /LIBPATH but it's not clear/documented what it's + // used for. Perhaps for link-time code generation (/LTCG)? If + // that's the case, then we may need to pass cxx.loptions. + // + if (verb < 3) + args.push_back ("/NOLOGO"); + + out = "/OUT:" + relt.string (); + args.push_back (out.c_str ()); + } + else + args.push_back (relt.string ().c_str ()); + + break; + } + // The options are usually similar enough to handle them together. + // case otype::e: + case otype::s: { if (cid == "msvc") { @@ -1421,6 +1456,9 @@ namespace build2 if (verb < 3) args.push_back ("/NOLOGO"); + if (lt == otype::s) + args.push_back ("/DLL"); + // Unless explicitly enabled with /INCREMENTAL, disable // incremental linking (it is implicitly enabled if /DEBUG is // specified). The reason is the .ilk file: its name cannot be @@ -1437,7 +1475,7 @@ namespace build2 if (!find_option ("/INCREMENTAL", args, true)) args.push_back ("/INCREMENTAL:NO"); - // Take care of the manifest. + // Take care of the manifest (will be empty for the DLL). // if (!manifest.empty ()) { @@ -1447,9 +1485,24 @@ namespace build2 args.push_back (std.c_str ()); } + if (lt == otype::s) + { + // On Windows libs{} is the import stub and its first ad hoc + // group member is dll{}. + // + // This will also create the .exp export file. Its name will be + // derived from the import library by changing the extension. + // Lucky us -- there is no option to name it. + // + out2 = "/IMPLIB:" + relt.string (); + args.push_back (out2.c_str ()); + + relt = relative (static_cast (t.member)->path ()); + } + // If we have /DEBUG then name the .pdb file. We call it - // foo.exe.pdb rather than foo.pdb because we can have, say, - // foo.dll in the same directory. + // foo.{exe,dll}.pdb rather than just foo.pdb because we can have, + // both foo.exe and foo.dll in the same directory. // if (find_option ("/DEBUG", args, true)) { @@ -1459,6 +1512,7 @@ namespace build2 // @@ An executable can have an import library and VS seems to // always name it. I wonder what would trigger its generation? + // Could it be the presence of export symbols? out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); @@ -1466,67 +1520,31 @@ namespace build2 else { args[0] = cast (rs["config.cxx"]).string ().c_str (); - args.push_back ("-o"); - args.push_back (relt.string ().c_str ()); - } - - break; - } - case otype::a: - { - args[0] = cast (rs["config.bin.ar"]).string ().c_str (); - if (cid == "msvc") - { - // lib.exe has /LIBPATH but it's not clear/documented what it's - // used for. Perhaps for link-time code generation (/LTCG)? If - // that's the case, then we may need to pass cxx.loptions. + // Add the option that triggers building a shared library and take + // care of any extras (e.g., import library). // - if (verb < 3) - args.push_back ("/NOLOGO"); - - out = "/OUT:" + relt.string (); - args.push_back (out.c_str ()); - } - else - args.push_back (relt.string ().c_str ()); - - break; - } - case otype::s: - { - if (cid == "msvc") - { - //@@ VC TODO: DLL building (names via /link?) - } - else - { - args[0] = cast (rs["config.cxx"]).string ().c_str (); - - // Add the option that triggers building a shared library. - // - if (tclass == "macosx") - args.push_back ("-dynamiclib"); - else - args.push_back ("-shared"); - - if (tsys == "mingw32") + if (lt == otype::s) { - // On Windows libs{} is the import stub and its first ad hoc - // group member is dll{}. - // - out = "-Wl,--out-implib=" + relt.string (); - relt = relative (static_cast (t.member)->path ()); + if (tclass == "macosx") + args.push_back ("-dynamiclib"); + else + args.push_back ("-shared"); - args.push_back ("-o"); - args.push_back (relt.string ().c_str ()); - args.push_back (out.c_str ()); - } - else - { - args.push_back ("-o"); - args.push_back (relt.string ().c_str ()); + if (tsys == "mingw32") + { + // On Windows libs{} is the import stub and its first ad hoc + // group member is dll{}. + // + out = "-Wl,--out-implib=" + relt.string (); + args.push_back (out.c_str ()); + + relt = relative (static_cast (t.member)->path ()); + } } + + args.push_back ("-o"); + args.push_back (relt.string ().c_str ()); } break; @@ -1678,25 +1696,32 @@ namespace build2 { if (tsys == "mingw32") { - e = {"+.d", "/+.dlls", "+.manifest.o", "+.manifest"}; + e = {".d", "/.dlls", ".manifest.o", ".manifest"}; } else { - // Assuming it's VC or alike. + // Assuming it's VC or alike. Clean up .ilk in case the user + // enabled incremental linking (note that .ilk replaces .exe). // - // Clean up .ilk in case the user enabled incremental linking. - // - e = {"+.d", "/+.dlls", "+.manifest", ".ilk"}; + e = {".d", "/.dlls", ".manifest", "-.ilk"}; } } else - e = {"+.d"}; + e = {".d"}; break; } case otype::s: { - e = {"+.d"}; + if (tclass == "windows" && tsys != "mingw32") + { + // Assuming it's VC or alike. Clean up .exp and .ilk. + // + e = {".d", "-.exp", "--.ilk"}; + } + else + e = {".d"}; + break; } } -- cgit v1.1