From 76de594667b370094f5da5c0871c155d788d135e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 4 Jan 2018 15:35:57 +0200 Subject: Initial support for c/cxx runtime/stdlib detection --- build2/c/init.cxx | 9 ++++ build2/cc/common.hxx | 5 ++ build2/cc/guess.cxx | 141 ++++++++++++++++++++++++++++++++++++++++++++------- build2/cc/guess.hxx | 34 +++++++++++++ build2/cc/init.cxx | 22 +++++--- build2/cc/module.cxx | 100 ++++++++++++++++++------------------ build2/cxx/init.cxx | 6 +++ 7 files changed, 241 insertions(+), 76 deletions(-) diff --git a/build2/c/init.cxx b/build2/c/init.cxx index d55bd51..db512ef 100644 --- a/build2/c/init.cxx +++ b/build2/c/init.cxx @@ -182,6 +182,11 @@ namespace build2 v["cc.export.loptions"], v["cc.export.libs"], + v.insert_alias (v["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib. + + v["cc.runtime"], + v["cc.stdlib"], + v["cc.type"], v["cc.system"], v["cc.module_name"], @@ -218,6 +223,10 @@ namespace build2 v.insert ("c.target.class") }; + // Alias some cc. variables as c. + // + v.insert_alias (d.c_runtime, "c.runtime"); + assert (mod == nullptr); config_module* m (new config_module (move (d))); mod.reset (m); 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 ("config.cc.pattern"); v.insert ("config.cc.target"); + // Compiler runtime and C standard library. + // + v.insert ("cc.runtime"); + v.insert ("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&, 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 ("cc.id") = cast (hints["config.cc.id"]); - rs.assign ("cc.hinter") = - cast (hints["config.cc.hinter"]); + rs.assign ("cc.id") = cast (h["config.cc.id"]); + rs.assign ("cc.hinter") = cast (h["config.cc.hinter"]); } // config.cc.target @@ -172,7 +176,7 @@ namespace build2 { // This value must be hinted. // - const auto& t (cast (hints["config.cc.target"])); + const auto& t (cast (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 ("cc.pattern") = - cast_empty (hints["config.cc.pattern"]); + cast_empty (h["config.cc.pattern"]); } + // cc.runtime + // cc.stdlib + // + rs.assign ("cc.runtime") = cast (h["cc.runtime"]); + rs.assign ("cc.stdlib") = cast (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 (rs["cc.hinter"])); + auto check = [&loc, &h, this] (const auto& cv, + const auto& xv, + const char* what, + bool error = true) { - const auto& cv (cast (rs["cc.id"])); - const auto& xv (cast (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 (rs["cc.id"]), + cast (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 (rs["cc.pattern"])); - const auto& xv (cast (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 (rs["cc.target"])); - const auto& xv (cast (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 (rs["cc.pattern"]), + cast (rs[x_pattern]), + "toolchain pattern", + false); + + check (cast (rs["cc.target"]), + cast (rs[x_target]), + "target"); + + check (cast (rs["cc.runtime"]), + ci.runtime, + "runtime"); + + check (cast (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 ()) diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index 91e37e7..fc3bafb 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -383,6 +383,11 @@ namespace build2 v["cc.export.loptions"], v["cc.export.libs"], + v.insert ("cxx.stdlib"), + + v["cc.runtime"], + v["cc.stdlib"], + v["cc.type"], v["cc.system"], v["cc.module_name"], @@ -431,6 +436,7 @@ namespace build2 // Alias some cc. variables as cxx. // + v.insert_alias (d.c_runtime, "cxx.runtime"); v.insert_alias (d.c_module_name, "cxx.module_name"); assert (mod == nullptr); -- cgit v1.1