From 80f55f5857d340c31fcd951f645d3f337ed66a6b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 9 Jul 2016 11:26:28 +0200 Subject: Add config.bin.pattern, pass it as hint from cxx module With this change we normally no longer need to specify config.bin.ar explicitly when cross-compiling or set it to lib.exe for VC. --- build2/bin/module.cxx | 111 ++++++++++++++++++++++++++++++++++++++++++-------- build2/cxx/module.cxx | 87 +++++++++++++++++++++++++++++++++------ 2 files changed, 169 insertions(+), 29 deletions(-) diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx index 5e668df..9ce069c 100644 --- a/build2/bin/module.cxx +++ b/build2/bin/module.cxx @@ -33,6 +33,26 @@ namespace build2 static const strings liba_lib {"static"}; static const strings libso_lib {"shared"}; + // Apply the specified stem to the config.bin.pattern. If there is no + // pattern, then return the stem itself. Assume the pattern is valid, + // i.e., contains single '*'. + // + static string + apply (const lookup& pattern, const char* stem) + { + if (!pattern) + return stem; + + const string& p (cast (pattern)); + size_t i (p.find ('*')); + assert (i != string::npos); + + string r (p, 0, i++); + r.append (stem); + r.append (p, i, p.size () - i); + return r; + } + bool init (scope& r, scope& b, @@ -54,6 +74,7 @@ namespace build2 // Note: some overridable, some not. // v.insert ("config.bin.target", true); + v.insert ("config.bin.pattern", true); v.insert ("config.bin.ar", true); v.insert ("config.bin.ranlib", true); @@ -76,6 +97,7 @@ namespace build2 // Configure. // using config::required; + using config::optional; // The idea here is as follows: if we already have one of // the bin.* variables set, then we assume this is static @@ -125,19 +147,21 @@ namespace build2 // See the cxx module for details on merging. // b.assign ("bin.rpath") += cast_null ( - config::optional (r, "config.bin.rpath")); + optional (r, "config.bin.rpath")); if (first) { + bool new_val (false); // Set any new values? + // config.bin.target // { - const variable& cbt (var_pool.find ("config.bin.target")); + const variable& var (var_pool.find ("config.bin.target")); // We first see if the value was specified via the configuration // mechanism. // - auto p (config::required (r, cbt)); + auto p (required (r, var)); const value* v (p.first); // Then see if there is a config hint (e.g., from the C++ module). @@ -145,7 +169,7 @@ namespace build2 bool hint (false); if (v == nullptr) { - if (auto l = config_hints[cbt]) + if (auto l = config_hints[var]) { v = l.value; hint = true; @@ -154,7 +178,7 @@ namespace build2 if (v == nullptr) fail (loc) << "unable to determine binutils target" << - info << "consider specifying it with config.bin.target" << + info << "consider specifying it with " << var.name << info << "or first load a module that can provide it as a hint, " << "such as c or cxx"; @@ -202,26 +226,81 @@ namespace build2 info << "consider using the --config-sub option"; } - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). Note that a hinted value - // will automatically only be printed at level 3 and up. + new_val = new_val || p.second; // False for a hinted value. + } + + // config.bin.pattern + // + { + const variable& var (var_pool.find ("config.bin.pattern")); + + // We first see if the value was specified via the configuration + // mechanism. // - if (verb >= (p.second ? 2 : 3)) + auto p (required (r, var)); + const value* v (p.first); + + // Then see if there is a config hint (e.g., from the C++ module). + // + if (v == nullptr) { - text << "bin\n" - << " target " << cast (r["bin.target"]); + if (auto l = config_hints[var]) + v = l.value; } + + // For ease of use enter it as bin.pattern (it can come from + // different places). + // + if (v != nullptr) + { + const string& s (cast (*v)); + + if (s.find ('*') == string::npos) + fail << "missing '*' in binutils pattern '" << s << "'"; + + r.assign ("bin.pattern") = s; + new_val = new_val || p.second; // False for a hinted value. + } + } + + // If we set any new values (e.g., we are configuring), then print the + // report at verbosity level 2 and up (-v). + // + if (verb >= (new_val ? 2 : 3)) + { + diag_record dr (text); + + dr << "bin\n" + << " target " << cast (r["bin.target"]); + + if (auto l = r["bin.pattern"]) + dr << '\n' + << " pattern " << cast (l); } // config.bin.ar // config.bin.ranlib // - // For config.bin.ar we default to 'ar' while ranlib should be - // explicitly specified by the user in order for us to use it (most - // targets support the -s option to ar). + // For config.bin.ar we have the default (plus the pattern) while + // ranlib should be explicitly specified by the user in order for us + // to use it (all targets that we currently care to support have the + // ar -s option but if that changes we can always force the use of + // ranlib for certain targets). // - auto p (config::required (r, "config.bin.ar", path ("ar"))); - auto& v (config::optional (r, "config.bin.ranlib")); + // Another idea is to refuse to use default 'ar' (without the pattern) + // if the host/build targets don't match. On the other hand, a cross- + // toolchain can be target-unprefixed. Also, without canonicalization, + // comparing targets will be unreliable. + // + auto pattern (r["bin.pattern"]); + + // Use the target to decide on the default binutils program names. + // + const string& tsys (cast (r["bin.target.system"])); + const char* ar_d (tsys == "win32-msvc" ? "lib" : "ar"); + + auto p (required (r, "config.bin.ar", path (apply (pattern, ar_d)))); + auto& v (optional (r, "config.bin.ranlib")); const path& ar (cast (p.first)); const path& ranlib (v ? cast (v) : path ()); diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx index aa60be9..28892cf 100644 --- a/build2/cxx/module.cxx +++ b/build2/cxx/module.cxx @@ -101,6 +101,12 @@ namespace build2 b.assign ("cxx.libs") += cast_null ( config::optional (r, "config.cxx.libs")); + // Configuration hints for the bin module. They will only be used on the + // first loading of the bin module (for this project) so we only + // populate them on our first loading. + // + variable_map bin_hints; + // config.cxx // if (first) @@ -145,6 +151,65 @@ namespace build2 r.assign ("cxx.signature") = move (ci.signature); r.assign ("cxx.checksum") = move (ci.checksum); + // While we still have the original, compiler-reported target, see if + // we can derive a binutils program pattern. + // + // BTW, for GCC we also get gcc-{ar,ranlib} which add support for the + // LTO plugin though it seems more recent GNU binutils (2.25) are able + // to load the plugin when needed automatically. So it doesn't seem we + // should bother trying to support this on our end (the way we could + // do it is by passing config.bin.{ar,ranlib} as hints). + // + string pattern; + + if (cast (r["cxx.id"]) == "msvc") + { + // If the compiler name is/starts with 'cl' (e.g., cl.exe, cl-14), + // then replace it with '*' and use it as a pattern for lib, link, + // etc. + // + if (cxx.size () > 2) + { + const string& l (cxx.leaf ().string ()); + size_t n (l.size ()); + + if (n >= 2 && + (l[0] == 'c' || l[0] == 'C') && + (l[1] == 'l' || l[1] == 'L') && + (n == 2 || l[2] == '.' || l[2] == '-')) + { + path p (cxx.directory ()); + p /= "*"; + p += l.c_str () + 2; + pattern = move (p).string (); + } + } + } + else + { + // When cross-compiling the whole toolchain is normally prefixed + // with the target triplet, e.g., x86_64-w64-mingw32-{g++,ar,ld}. + // + const string& t (ci.target); + size_t n (t.size ()); + + if (cxx.size () > n + 1) + { + const string& l (cxx.leaf ().string ()); + + if (l.size () > n + 1 && l.compare (0, n, t) == 0 && l[n] == '-') + { + path p (cxx.directory ()); + p /= t; + p += "-*"; + pattern = move (p).string (); + } + } + } + + if (!pattern.empty ()) + bin_hints.assign ("config.bin.pattern") = move (pattern); + // Split/canonicalize the target. // @@ -166,6 +231,11 @@ namespace build2 l5 ([&]{trace << "canonical target: '" << canon << "'; " << "class: " << t.class_;}); + // Pass the target we extracted from the C++ compiler as a config + // hint to the bin module. + // + bin_hints.assign ("config.bin.target") = canon; + // Enter as cxx.target.{cpu,vendor,system,version,class}. // r.assign ("cxx.target") = move (canon); @@ -189,28 +259,19 @@ namespace build2 // Initialize the bin module. Only do this if it hasn't already been // loaded so that we don't overwrite user's bin.* settings. // - const string& target (cast (r["cxx.target"])); - if (!cast_false (b["bin.loaded"])) - { - // Pass the target we extracted from the C++ compiler as a config hint - // to the bin module. - // - variable_map hints; - hints.assign ("config.bin.target") = target; - - load_module ("bin", r, b, loc, false, hints); - } + load_module ("bin", r, b, loc, false, bin_hints); // Verify bin's target matches ours. // { const string& bt (cast (r["bin.target"])); + const string& ct (cast (r["cxx.target"])); - if (bt != target) + if (bt != ct) fail (loc) << "bin and cxx module target platform mismatch" << info << "bin.target is " << bt << - info << "cxx.target is " << target; + info << "cxx.target is " << ct; } // Register target types. -- cgit v1.1