aboutsummaryrefslogtreecommitdiff
path: root/build2/cxx/msvc.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/cxx/msvc.cxx')
-rw-r--r--build2/cxx/msvc.cxx249
1 files changed, 249 insertions, 0 deletions
diff --git a/build2/cxx/msvc.cxx b/build2/cxx/msvc.cxx
new file mode 100644
index 0000000..1c5a3fa
--- /dev/null
+++ b/build2/cxx/msvc.cxx
@@ -0,0 +1,249 @@
+// file : build2/cxx/msvc.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/scope>
+#include <build2/target>
+#include <build2/context>
+#include <build2/variable>
+#include <build2/filesystem>
+#include <build2/diagnostics>
+
+#include <build2/cxx/common>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cxx
+ {
+ using namespace bin;
+
+ // Extract system library search paths from MSVC.
+ //
+ void
+ msvc_library_search_paths (scope&, const string&, dir_paths&)
+ {
+ // The linker doesn't seem to have any built-in paths and all of them
+ // come from the LIB environment variable.
+
+ // @@ VC: how are we going to do this? E.g., cl-14 does this internally.
+ // cl.exe /Be prints LIB.
+ //
+ // Should we actually bother? LIB is normally used for system
+ // libraries and its highly unlikely we will see an explicit import
+ // for a library from one of those directories.
+ //
+ }
+
+ // Inspect the file and determine if it is static or import library.
+ // Return otype::e if it is neither (which we quietly ignore).
+ //
+ static otype
+ library_type (const path& ld, const path& l)
+ {
+ // The are several reasonably reliable methods to tell whether it is a
+ // static or import library. One is lib.exe /LIST -- if there aren't any
+ // .obj members, then it is most likely an import library (it can also
+ // be an empty static library in which case there won't be any members).
+ // For an import library /LIST will print a bunch of .dll members.
+ //
+ // Another approach is dumpbin.exe (link.exe /DUMP) with /ARCHIVEMEMBERS
+ // (similar to /LIST) and /LINKERMEMBER (looking for __impl__ symbols or
+ // _IMPORT_DESCRIPTOR_).
+ //
+ // Note also, that apparently it is possible to have a hybrid library.
+ //
+ // While the lib.exe approach is probably the simplest, the problem is
+ // it will require us loading the bin.ar module even if we are not
+ // building any static libraries. On the other hand, if we are searching
+ // for libraries then we have bin.ld. So we will use the link.exe /DUMP
+ // /ARCHIVEMEMBERS.
+ //
+ const char* args[] = {ld.string ().c_str (),
+ "/DUMP", // Must come first.
+ "/NOLOGO",
+ "/ARCHIVEMEMBERS",
+ l.string ().c_str (),
+ nullptr};
+
+ // Link.exe seem to always dump everything to stdout but just in case
+ // redirect stderr to stdout.
+ //
+ process pr (start_run (args, false));
+ ifdstream is (pr.in_ofd);
+
+ bool obj (false), dll (false);
+
+ string s;
+ while (getline (is, s))
+ {
+ // Detect the one error we should let through.
+ //
+ if (s.compare (0, 18, "unable to execute ") == 0)
+ break;
+
+ // The lines we are interested in seem to have this form (though
+ // presumably the "Archive member name at" part can be translated):
+ //
+ // Archive member name at 746: [...]hello.dll[/][ ]*
+ // Archive member name at 8C70: [...]hello.lib.obj[/][ ]*
+ //
+ size_t n (s.size ());
+
+ for (; n != 0 && s[n - 1] == ' '; --n) ; // Skip trailing spaces.
+
+ if (n >= 7) // At least ": X.obj" or ": X.dll".
+ {
+ --n;
+
+ if (s[n] == '/') // Skip trailing slash if one is there.
+ --n;
+
+ n -= 3; // Beginning of extension.
+
+ if (s[n] == '.')
+ {
+ // Make sure there is ": ".
+ //
+ size_t p (s.rfind (':', n - 1));
+
+ if (p != string::npos && s[p + 1] == ' ')
+ {
+ if (s.compare (n + 1, 3, "obj") == 0) // @@ CASE
+ obj = true;
+
+ if (s.compare (n + 1, 3, "dll") == 0) // @@ CASE
+ dll = true;
+ }
+ }
+ }
+ }
+
+ is.close (); // Don't block.
+
+ if (!finish_run (args, false, pr, s))
+ return otype::e;
+
+ if (obj && dll)
+ {
+ warn << l << " looks like hybrid static/import library, ignoring";
+ return otype::e;
+ }
+
+ if (!obj && !dll)
+ {
+ warn << l << " looks like empty static or import library, ignoring";
+ return otype::e;
+ }
+
+ return obj ? otype::a : otype::s;
+ }
+
+ template <typename T>
+ static T*
+ search_library (const path& ld,
+ const dir_path& d,
+ prerequisite& p,
+ otype lt,
+ const char* pfx,
+ const char* sfx)
+ {
+ // Pretty similar logic to link::search_library().
+ //
+ tracer trace ("cxx::msvc_search_library");
+
+ // Assemble the file path.
+ //
+ path f (d);
+
+ if (*pfx != '\0')
+ {
+ f /= pfx;
+ f += p.name;
+ }
+ else
+ f /= p.name;
+
+ if (*sfx != '\0')
+ f += sfx;
+
+ const string& e (
+ p.ext == nullptr || p.is_a<lib> () // Only for liba/libs.
+ ? extension_pool.find ("lib")
+ : *p.ext);
+
+ if (!e.empty ())
+ {
+ f += '.';
+ f += e;
+ }
+
+ // Check if the file exists and is of the expected type.
+ //
+ timestamp mt (file_mtime (f));
+
+ if (mt != timestamp_nonexistent && library_type (ld, f) == lt)
+ {
+ // Enter the target.
+ //
+ T& t (targets.insert<T> (d, dir_path (), p.name, &e, trace));
+
+ if (t.path ().empty ())
+ t.path (move (f));
+
+ t.mtime (mt);
+ return &t;
+ }
+
+ return nullptr;
+ }
+
+ liba*
+ msvc_search_static (const path& ld, const dir_path& d, prerequisite& p)
+ {
+ liba* r (nullptr);
+
+ auto search = [&r, &ld, &d, &p] (const char* pf, const char* sf) -> bool
+ {
+ r = search_library<liba> (ld, d, p, otype::a, pf, sf);
+ return r != nullptr;
+ };
+
+ // Try:
+ // foo.lib
+ // libfoo.lib
+ // foolib.lib
+ // foo_static.lib
+ //
+ return
+ search ("", "") ||
+ search ("lib", "") ||
+ search ("", "lib") ||
+ search ("", "_static") ? r : nullptr;
+ }
+
+ libs*
+ msvc_search_shared (const path& ld, const dir_path& d, prerequisite& p)
+ {
+ libs* r (nullptr);
+
+ auto search = [&r, &ld, &d, &p] (const char* pf, const char* sf) -> bool
+ {
+ r = search_library<libs> (ld, d, p, otype::s, pf, sf);
+ return r != nullptr;
+ };
+
+ // Try:
+ // foo.lib
+ // libfoo.lib
+ // foodll.lib
+ //
+ return
+ search ("", "") ||
+ search ("lib", "") ||
+ search ("", "dll") ? r : nullptr;
+ }
+ }
+}