aboutsummaryrefslogtreecommitdiff
path: root/build2/cc
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-01-04 15:35:57 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-01-05 15:10:01 +0200
commit76de594667b370094f5da5c0871c155d788d135e (patch)
tree610d67fc2350a4fdeaa2de7b6583f5da9067bc24 /build2/cc
parentd6427addaf7de41d401dd2a871b4789022e5f7cf (diff)
Initial support for c/cxx runtime/stdlib detection
Diffstat (limited to 'build2/cc')
-rw-r--r--build2/cc/common.hxx5
-rw-r--r--build2/cc/guess.cxx141
-rw-r--r--build2/cc/guess.hxx34
-rw-r--r--build2/cc/init.cxx22
-rw-r--r--build2/cc/module.cxx100
5 files changed, 226 insertions, 76 deletions
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index f4cd0c7..5ed7173 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -69,6 +69,11 @@ namespace build2
const variable& c_export_loptions;
const variable& c_export_libs;
+ const variable& x_stdlib; // x.stdlib
+
+ const variable& c_runtime; // cc.runtime
+ const variable& c_stdlib; // cc.stdlib
+
const variable& c_type; // cc.type
const variable& c_system; // cc.system
const variable& c_module_name; // cc.module_name
diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx
index c39c02e..e3eeb82 100644
--- a/build2/cc/guess.cxx
+++ b/build2/cc/guess.cxx
@@ -502,6 +502,8 @@ namespace build2
fail << "unable to extract target architecture from " << xc
<< " -print-multiarch or -dumpmachine output";
+ string ot (t);
+
// Derive the toolchain pattern. Try cc/c++ as a fallback.
//
string pat (pattern (xc, xl == lang::c ? "gcc" : "g++"));
@@ -509,6 +511,12 @@ namespace build2
if (pat.empty ())
pat = pattern (xc, xl == lang::c ? "cc" : "c++");
+
+ // GCC always uses libgcc (even on MinGW). Even with -nostdlib GCC's
+ // documentation says that you should usually specify -lgcc.
+ //
+ string rt ("libgcc");
+
return compiler_info {
move (gr.path),
move (gr.id),
@@ -517,8 +525,12 @@ namespace build2
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
move (t),
+ move (ot),
move (pat),
- string ()};
+ "",
+ move (rt),
+ "",
+ ""};
}
static compiler_info
@@ -615,6 +627,32 @@ namespace build2
fail << "unable to extract target architecture from " << xc
<< " -dumpmachine output";
+ string ot (t);
+
+ // Parse the target into triplet (for further tests) ignoring any
+ // failures.
+ //
+ target_triplet tt;
+ try {tt = target_triplet (t);} catch (const invalid_argument&) {}
+
+ // For Clang on Windows targeting MSVC we remap the target to match
+ // MSVC's.
+ //
+ if (tt.system == "windows-msvc")
+ {
+ // Keep the CPU and replace the rest.
+ //
+ // @@ Note that currently there is no straightforward way to determine
+ // the VC version Clang is using. See:
+ //
+ // http://lists.llvm.org/pipermail/cfe-dev/2017-December/056240.html
+ //
+ tt.vendor = "microsoft";
+ tt.system = "win32-msvc";
+ tt.version = "14.1";
+ t = tt.string ();
+ }
+
// Derive the toolchain pattern. Try clang/clang++, the gcc/g++ alias,
// as well as cc/c++.
//
@@ -626,6 +664,38 @@ namespace build2
if (pat.empty ())
pat = pattern (xc, xl == lang::c ? "cc" : "c++");
+ // Clang can use libgcc, its own compiler-rt, or, on Windows targeting
+ // MSVC, the VC's runtime. As usual, there is no straightforward way
+ // to query this and silence on the mailing list. See:
+ //
+ // http://lists.llvm.org/pipermail/cfe-dev/2018-January/056494.html
+ //
+ // So for now we will just look for --rtlib and if none specified,
+ // assume some platform-specific defaults.
+ //
+ string rt;
+ {
+ auto find_rtlib = [] (const strings* ops) -> const string*
+ {
+ return ops != nullptr
+ ? find_option_prefix ("--rtlib=", *ops, false)
+ : nullptr;
+ };
+
+ const string* o;
+ if ((o = find_rtlib (x_coptions)) != nullptr ||
+ (o = find_rtlib (c_coptions)) != nullptr)
+ {
+ rt = string (*o, 8);
+ }
+ // Defaults.
+ //
+ else if (tt.system == "win32-msvc") rt = "msvc";
+ else if (tt.system == "linux-gnu" ||
+ tt.system == "freebsd") rt = "libgcc";
+ else /* Mac OS, etc. */ rt = "compiler-rt";
+ }
+
return compiler_info {
move (gr.path),
move (gr.id),
@@ -634,8 +704,12 @@ namespace build2
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
move (t),
+ move (ot),
move (pat),
- string ()};
+ "",
+ move (rt),
+ "",
+ ""};
}
static compiler_info
@@ -836,7 +910,16 @@ namespace build2
if (p == string::npos)
fail << "unable to parse icc target architecture '" << t << "'";
- arch.append (t, p, string::npos);
+ t.swap (arch);
+ t.append (arch, p, string::npos);
+
+ string ot (t);
+
+ // Parse the target into triplet (for further tests) ignoring any
+ // failures.
+ //
+ target_triplet tt;
+ try {tt = target_triplet (t);} catch (const invalid_argument&) {}
// Derive the toolchain pattern.
//
@@ -846,6 +929,10 @@ namespace build2
//
sha256 cs (s);
+ // Runtime and standard library.
+ //
+ string rt (tt.system == "win32-msvc" ? "msvc" : "libgcc");
+
return compiler_info {
move (gr.path),
move (gr.id),
@@ -853,9 +940,13 @@ namespace build2
move (v),
move (gr.signature),
cs.string (),
- move (arch),
+ move (t),
+ move (ot),
move (pat),
- string ()};
+ "",
+ move (rt),
+ "",
+ ""};
}
static compiler_info
@@ -1016,14 +1107,16 @@ namespace build2
// x64 x86_64-microsoft-win32-msvc14.0
// ARM arm-microsoft-winup-???
//
+ string t;
+
if (arch == "ARM")
fail << "cl.exe ARM/WinRT/UWP target is not yet supported";
else
{
if (arch == "x64")
- arch = "x86_64-microsoft-win32-msvc";
+ t = "x86_64-microsoft-win32-msvc";
else if (arch == "x86" || arch == "80x86")
- arch = "i386-microsoft-win32-msvc";
+ t = "i386-microsoft-win32-msvc";
else
assert (false);
@@ -1045,20 +1138,22 @@ namespace build2
// 2005 8 14.00 8.0/80
// 2003 7.1 13.10 7.1/71
//
- /**/ if (v.major == 19 && v.minor == 12) arch += "14.1";
- else if (v.major == 19 && v.minor == 11) arch += "14.1";
- else if (v.major == 19 && v.minor == 10) arch += "14.1";
- else if (v.major == 19 && v.minor == 0) arch += "14.0";
- else if (v.major == 18 && v.minor == 0) arch += "12.0";
- else if (v.major == 17 && v.minor == 0) arch += "11.0";
- else if (v.major == 16 && v.minor == 0) arch += "10.0";
- else if (v.major == 15 && v.minor == 0) arch += "9.0";
- else if (v.major == 14 && v.minor == 0) arch += "8.0";
- else if (v.major == 13 && v.minor == 10) arch += "7.1";
+ /**/ if (v.major == 19 && v.minor == 12) t += "14.1";
+ else if (v.major == 19 && v.minor == 11) t += "14.1";
+ else if (v.major == 19 && v.minor == 10) t += "14.1";
+ else if (v.major == 19 && v.minor == 0) t += "14.0";
+ else if (v.major == 18 && v.minor == 0) t += "12.0";
+ else if (v.major == 17 && v.minor == 0) t += "11.0";
+ else if (v.major == 16 && v.minor == 0) t += "10.0";
+ else if (v.major == 15 && v.minor == 0) t += "9.0";
+ else if (v.major == 14 && v.minor == 0) t += "8.0";
+ else if (v.major == 13 && v.minor == 10) t += "7.1";
else fail << "unable to map msvc compiler version '" << v.string
<< "' to runtime version";
}
+ string ot (t);
+
// Derive the toolchain pattern.
//
// If the compiler name is/starts with 'cl' (e.g., cl.exe, cl-14),
@@ -1072,6 +1167,10 @@ namespace build2
//
sha256 cs (s);
+ // Runtime and standard library.
+ //
+ string rt ("msvc");
+
return compiler_info {
move (gr.path),
move (gr.id),
@@ -1079,9 +1178,13 @@ namespace build2
move (v),
move (gr.signature),
cs.string (),
- move (arch),
+ move (t),
+ move (ot),
move (cpat),
- move (bpat)};
+ move (bpat),
+ move (rt),
+ "",
+ ""};
}
compiler_info
diff --git a/build2/cc/guess.hxx b/build2/cc/guess.hxx
index 86c9793..9d87cb4 100644
--- a/build2/cc/guess.hxx
+++ b/build2/cc/guess.hxx
@@ -151,8 +151,42 @@ namespace build2
string signature;
string checksum;
string target;
+ string original_target; // As reported by the compiler.
string pattern;
string bin_pattern;
+
+ // Compiler runtime, C standard library, and language (e.g., C++)
+ // standard library.
+ //
+ // The runtime is the low-level compiler runtime library and its name is
+ // the library/project name. Current values are (but can also be some
+ // custom name specified with Clang's --rtlib):
+ //
+ // libgcc
+ // compiler-rt (clang)
+ // msvc
+ //
+ // The C standard library is normally the library/project name (e.g,
+ // glibc, musl, newlib, klibc, etc) but if there is none, then we
+ // fallback to the vendor name (e.g., freebsd). Proposed values are (any
+ // BSD-derived libc should end with the *bsd suffix):
+ //
+ // glibc
+ // msvc (msvcrt.lib/msvcrNNN.dll)
+ // freebsd
+ // applebsd
+ // cygwin? (apparently newlib)
+ //
+ // The C++ standard library is normally the library/project name.
+ // Current values are:
+ //
+ // libstdc++
+ // libc++
+ // msvcp (msvcprt.lib/msvcpNNN.dll)
+ //
+ string runtime;
+ string c_stdlib;
+ string x_stdlib;
};
// In a sense this is analagous to the language standard which we handle
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index b49de15..258e071 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -100,6 +100,11 @@ namespace build2
v.insert<string> ("config.cc.pattern");
v.insert<target_triplet> ("config.cc.target");
+ // Compiler runtime and C standard library.
+ //
+ v.insert<string> ("cc.runtime");
+ v.insert<string> ("cc.stdlib");
+
// Target type, for example, "C library" or "C++ library". Should be set
// on the target by the matching rule to the name of the module (e.g.,
// "c", "cxx"). Currenly only set for libraries and is used to decide
@@ -145,7 +150,7 @@ namespace build2
unique_ptr<module_base>&,
bool first,
bool,
- const variable_map& hints)
+ const variable_map& h)
{
tracer trace ("cc::core_guess_init");
l5 ([&]{trace << "for " << rs.out_path ();});
@@ -162,9 +167,8 @@ namespace build2
{
// These values must be hinted.
//
- rs.assign<string> ("cc.id") = cast<string> (hints["config.cc.id"]);
- rs.assign<string> ("cc.hinter") =
- cast<string> (hints["config.cc.hinter"]);
+ rs.assign<string> ("cc.id") = cast<string> (h["config.cc.id"]);
+ rs.assign<string> ("cc.hinter") = cast<string> (h["config.cc.hinter"]);
}
// config.cc.target
@@ -172,7 +176,7 @@ namespace build2
{
// This value must be hinted.
//
- const auto& t (cast<target_triplet> (hints["config.cc.target"]));
+ const auto& t (cast<target_triplet> (h["config.cc.target"]));
// Also enter as cc.target.{cpu,vendor,system,version,class} for
// convenience of access.
@@ -192,9 +196,15 @@ namespace build2
// This value could be hinted.
//
rs.assign<string> ("cc.pattern") =
- cast_empty<string> (hints["config.cc.pattern"]);
+ cast_empty<string> (h["config.cc.pattern"]);
}
+ // cc.runtime
+ // cc.stdlib
+ //
+ rs.assign ("cc.runtime") = cast<string> (h["cc.runtime"]);
+ rs.assign ("cc.stdlib") = cast<string> (h["cc.stdlib"]);
+
return true;
}
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index d4962a4..5b1a79a 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -143,24 +143,6 @@ namespace build2
}
}
- // For some compilers we have to remap the target to something more
- // appropriate.
- //
- if (tt.system == "windows-msvc") // Clang on Windows targeting MSVC.
- {
- // Remap to the same triplet as used for MSVC.
- //
- // @@ This should probably be done in guess(), especially since we may
- // need to extra extra info from the compiler (like the runtime
- // version). Perhaps have original_target in compiler_info (so can
- // print it in report below)?
- //
- tt.vendor = "microsoft";
- tt.system = "win32-msvc";
- tt.version = "14.1"; //@@ TMP hardcoded.
- assert (tt.class_ == "windows");
- }
-
// Assign values to variables that describe the compiler.
//
rs.assign (x_id) = ci.id.string ();
@@ -188,6 +170,9 @@ namespace build2
rs.assign (x_pattern) = ci.pattern;
+ if (!x_stdlib.aliases (c_stdlib))
+ rs.assign (x_stdlib) = move (ci.x_stdlib);
+
new_ = p.second;
// Load cc.core.guess.
@@ -207,6 +192,9 @@ namespace build2
if (!ci.pattern.empty ())
h.assign ("config.cc.pattern") = ci.pattern;
+ h.assign (c_runtime) = ci.runtime;
+ h.assign (c_stdlib) = ci.c_stdlib;
+
load_module (rs, rs, "cc.core.guess", loc, false, h);
}
else
@@ -217,45 +205,49 @@ namespace build2
//
const auto& h (cast<string> (rs["cc.hinter"]));
+ auto check = [&loc, &h, this] (const auto& cv,
+ const auto& xv,
+ const char* what,
+ bool error = true)
{
- const auto& cv (cast<string> (rs["cc.id"]));
- const auto& xv (cast<string> (rs[x_id]));
-
if (cv != xv)
- fail (loc) << h << " and " << x << " module toolchain mismatch" <<
- info << h << " is " << cv <<
- info << x << " is " << xv <<
- info << "consider explicitly specifying config." << h
- << " and config." << x;
- }
+ {
+ diag_record dr (error ? fail (loc) : warn (loc));
+
+ dr << h << " and " << x << " module " << what << " mismatch" <<
+ info << h << " is '" << cv << "'" <<
+ info << x << " is '" << xv << "'" <<
+ info << "consider explicitly specifying config." << h
+ << " and config." << x;
+ }
+ };
+
+ check (cast<string> (rs["cc.id"]),
+ cast<string> (rs[x_id]),
+ "toolchain");
// We used to not require that patterns match assuming that if the
// toolchain id and target are the same, then where exactly the tools
// come from doesn't really matter. But in most cases it will be the
// g++-7 vs gcc kind of mistakes. So now we warn since even if
- // intentional, it is still a bad idea.
+ // intentional, it is still probably a bad idea.
//
- {
- const auto& cv (cast<string> (rs["cc.pattern"]));
- const auto& xv (cast<string> (rs[x_pattern]));
-
- if (cv != xv)
- warn (loc) << h << " and " << x << " toolchain pattern mismatch" <<
- info << h << " is '" << cv << "'" <<
- info << x << " is '" << xv << "'" <<
- info << "consider explicitly specifying config." << h
- << " and config." << x;
- }
-
- {
- const auto& cv (cast<target_triplet> (rs["cc.target"]));
- const auto& xv (cast<target_triplet> (rs[x_target]));
-
- if (cv != xv)
- fail (loc) << h << " and " << x << " module target mismatch" <<
- info << h << " target is " << cv <<
- info << x << " target is " << xv;
- }
+ check (cast<string> (rs["cc.pattern"]),
+ cast<string> (rs[x_pattern]),
+ "toolchain pattern",
+ false);
+
+ check (cast<target_triplet> (rs["cc.target"]),
+ cast<target_triplet> (rs[x_target]),
+ "target");
+
+ check (cast<string> (rs["cc.runtime"]),
+ ci.runtime,
+ "runtime");
+
+ check (cast<string> (rs["cc.stdlib"]),
+ ci.c_stdlib,
+ "c standard library");
}
}
@@ -356,8 +348,14 @@ namespace build2
<< " checksum " << ci.checksum << '\n'
<< " target " << ct;
- if (ct != ci.target)
- dr << " (" << ci.target << ")";
+ if (ct != ci.original_target)
+ dr << " (" << ci.original_target << ")";
+
+
+ // @@ Print c_stdlib?
+ //
+ dr << "\n runtime " << ci.runtime
+ << "\n stdlib " << ci.x_stdlib;
}
if (!tstd.empty ())