aboutsummaryrefslogtreecommitdiff
path: root/build2/cxx/link.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-07-19 11:10:27 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-07-19 11:10:27 +0200
commit3ec07c196c9ab86db09c77bff7eb11cd5a5a9b1e (patch)
tree64fa1a8ec1e0152898843bb82a775c4ba1f194e0 /build2/cxx/link.cxx
parent1470c64377bfd29d434261208cd91b001b17c3f4 (diff)
Add support for building DLLs with VC
Diffstat (limited to 'build2/cxx/link.cxx')
-rw-r--r--build2/cxx/link.cxx265
1 files changed, 145 insertions, 120 deletions
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<dir_paths>& 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<liba> (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<liba> (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<path> (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<path> (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<file*> (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<path> (rs["config.cxx"]).string ().c_str ();
- args.push_back ("-o");
- args.push_back (relt.string ().c_str ());
- }
-
- break;
- }
- case otype::a:
- {
- args[0] = cast<path> (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<path> (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<file*> (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<file*> (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;
}
}