aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-07-09 11:26:28 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-07-09 11:26:28 +0200
commit80f55f5857d340c31fcd951f645d3f337ed66a6b (patch)
tree79f0ed384be1a27df7bbd0a3fabfaa7dfa85cb6d
parentc865883666028da1982b082a0754cc4a261afd27 (diff)
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.
-rw-r--r--build2/bin/module.cxx111
-rw-r--r--build2/cxx/module.cxx87
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<string> (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<string> ("config.bin.target", true);
+ v.insert<string> ("config.bin.pattern", true);
v.insert<path> ("config.bin.ar", true);
v.insert<path> ("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<dir_paths> (
- 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<string> (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<string> (*v));
+
+ if (s.find ('*') == string::npos)
+ fail << "missing '*' in binutils pattern '" << s << "'";
+
+ r.assign<string> ("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<string> (r["bin.target"]);
+
+ if (auto l = r["bin.pattern"])
+ dr << '\n'
+ << " pattern " << cast<string> (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<string> (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<path> (p.first));
const path& ranlib (v ? cast<path> (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<strings> (
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<string> ("cxx.signature") = move (ci.signature);
r.assign<string> ("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<string> (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<string> ("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<string> (r["cxx.target"]));
-
if (!cast_false<bool> (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<string> (r["bin.target"]));
+ const string& ct (cast<string> (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.