aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-08-12 12:46:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-08-12 17:04:22 +0200
commit0fd7815cbc6557811df4f1b6ffb40461474b8534 (patch)
treef67b2d8a94f85027b3f1c98c4bf9acadd4b27d56
parent9fa5f73d00905568e8979d0c93ec4a8f645c81d5 (diff)
Implement c/cxx toolchain cross-hinting
-rw-r--r--build2/bin/module.cxx71
-rw-r--r--build2/cc/guess16
-rw-r--r--build2/cc/guess.cxx137
-rw-r--r--build2/cc/init.cxx3
-rw-r--r--build2/cc/module.cxx96
-rw-r--r--build2/utility7
-rw-r--r--build2/utility.cxx15
7 files changed, 248 insertions, 97 deletions
diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx
index 6d9cda7..3a3f612 100644
--- a/build2/bin/module.cxx
+++ b/build2/bin/module.cxx
@@ -370,26 +370,6 @@ namespace build2
return true;
}
- // 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
ar_config_init (scope& r,
scope& b,
@@ -435,7 +415,6 @@ namespace build2
// 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.
//
@@ -446,17 +425,21 @@ namespace build2
// changes, say, the C++ compiler (which hinted the pattern), then
// ar will automatically change as well.
//
- auto ap (config::required (r,
- "config.bin.ar",
- path (apply (pattern, ar_d)),
- false,
- config::save_commented));
-
- auto rp (config::required (r,
- "config.bin.ranlib",
- nullptr,
- false,
- config::save_commented));
+ auto ap (
+ config::required (
+ r,
+ "config.bin.ar",
+ path (apply_pattern (ar_d, cast_null<string> (r["bin.pattern"]))),
+ false,
+ config::save_commented));
+
+ auto rp (
+ config::required (
+ r,
+ "config.bin.ranlib",
+ nullptr,
+ false,
+ config::save_commented));
const path& ar (cast<path> (ap.first));
const path* ranlib (cast_null<path> (rp.first));
@@ -566,11 +549,13 @@ namespace build2
const string& tsys (cast<string> (r["bin.target.system"]));
const char* ld_d (tsys == "win32-msvc" ? "link" : "ld");
- auto p (config::required (r,
- "config.bin.ld",
- path (apply (r["bin.pattern"], ld_d)),
- false,
- config::save_commented));
+ auto p (
+ config::required (
+ r,
+ "config.bin.ld",
+ path (apply_pattern (ld_d, cast_null<string> (r["bin.pattern"]))),
+ false,
+ config::save_commented));
const path& ld (cast<path> (p.first));
ld_info ldi (guess_ld (ld));
@@ -668,11 +653,13 @@ namespace build2
const string& tsys (cast<string> (r["bin.target.system"]));
const char* rc_d (tsys == "win32-msvc" ? "rc" : "windres");
- auto p (config::required (r,
- "config.bin.rc",
- path (apply (r["bin.pattern"], rc_d)),
- false,
- config::save_commented));
+ auto p (
+ config::required (
+ r,
+ "config.bin.rc",
+ path (apply_pattern (rc_d, cast_null<string> (r["bin.pattern"]))),
+ false,
+ config::save_commented));
const path& rc (cast<path> (p.first));
rc_info rci (guess_rc (rc));
diff --git a/build2/cc/guess b/build2/cc/guess
index 977e081..d852a5c 100644
--- a/build2/cc/guess
+++ b/build2/cc/guess
@@ -96,7 +96,10 @@ namespace build2
// unlike all the preceding fields, this one takes into account the
// compile options (e.g., -m32).
//
- // The pattern is the toolchain program pattern that could sometimes be
+ // The cc_pattern is the toolchain program pattern that could sometimes be
+ // derived for some toolchains. For example, i686-w64-mingw32-*-4.9.
+ //
+ // The bin_pattern is the binutils program pattern that could sometimes be
// derived for some toolchains. For example, i686-w64-mingw32-*.
//
struct compiler_info
@@ -106,7 +109,8 @@ namespace build2
string signature;
string checksum;
string target;
- string pattern;
+ string cc_pattern;
+ string bin_pattern;
};
// In a sense this is analagous to the language standard which we handle
@@ -119,6 +123,14 @@ namespace build2
const path& xc,
const strings* c_coptions,
const strings* x_coptions);
+
+ // Given a language, toolchain id, and optionally a pattern, return an
+ // appropriate default compiler path.
+ //
+ // For example, for (lang::cxx, gcc, *-4.9) we will get g++-4.9.
+ //
+ path
+ guess_default (lang, const string& cid, const string* pattern);
}
}
diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx
index d80dddd..5001879 100644
--- a/build2/cc/guess.cxx
+++ b/build2/cc/guess.cxx
@@ -4,7 +4,7 @@
#include <build2/cc/guess>
-#include <cstring> // strlen()
+#include <cstring> // strlen(), strchr()
#include <build2/diagnostics>
@@ -299,8 +299,55 @@ namespace build2
return r;
}
+ // Try to derive the toolchain pattern.
+ //
+ // The s argument is the stem to look for in the leaf of the path. The ls
+ // and rs arguments are the left/right separator characters. If either is
+ // NULL, then the stem should be the prefix/suffix of the leaf,
+ // respectively. Note that a path that is equal to stem is not considered
+ // a pattern.
+ //
+ static string
+ pattern (const path& xc,
+ const char* s,
+ const char* ls = "-_.",
+ const char* rs = "-_.")
+ {
+ string r;
+ size_t sn (strlen (s));
+
+ if (xc.size () > sn)
+ {
+ string l (xc.leaf ().string ());
+ size_t ln (l.size ());
+
+ size_t b;
+ if (ln >= sn && (b = l.find (s)) != string::npos)
+ {
+ // Check left separators.
+ //
+ if (b == 0 || (ls != nullptr && strchr (ls, l[b - 1]) != nullptr))
+ {
+ // Check right separators.
+ //
+ size_t e (b + sn);
+ if (e == ln || (rs != nullptr && strchr (rs, l[e]) != nullptr))
+ {
+ l.replace (b, sn, "*", 1);
+ path p (xc.directory ());
+ p /= l;
+ r = move (p).string ();
+ }
+ }
+ }
+ }
+
+ return r;
+ }
+
+
static compiler_info
- guess_gcc (lang,
+ guess_gcc (lang xl,
const path& xc,
const strings* c_coptions,
const strings* x_coptions,
@@ -408,17 +455,25 @@ namespace build2
fail << "unable to extract target architecture from " << xc
<< " -print-multiarch or -dumpmachine output";
+ // Derive the toolchain pattern. Try cc/c++ as a fallback.
+ //
+ string pat (pattern (xc, xl == lang::c ? "gcc" : "g++"));
+
+ if (pat.empty ())
+ pat = pattern (xc, xl == lang::c ? "cc" : "c++");
+
return compiler_info {
move (gr.id),
move (v),
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
move (t),
+ move (pat),
string ()};
}
static compiler_info
- guess_clang (lang,
+ guess_clang (lang xl,
const path& xc,
const strings* c_coptions,
const strings* x_coptions,
@@ -512,12 +567,24 @@ namespace build2
fail << "unable to extract target architecture from " << xc
<< " -dumpmachine output";
+ // Derive the toolchain pattern. Try clang/clang++, the gcc/g++ alias,
+ // as well as cc/c++.
+ //
+ string pat (pattern (xc, xl == lang::c ? "clang" : "clang++"));
+
+ if (pat.empty ())
+ pat = pattern (xc, xl == lang::c ? "gcc" : "g++");
+
+ if (pat.empty ())
+ pat = pattern (xc, xl == lang::c ? "cc" : "c++");
+
return compiler_info {
move (gr.id),
move (v),
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
move (t),
+ move (pat),
string ()};
}
@@ -722,6 +789,10 @@ namespace build2
arch.append (t, p, string::npos);
+ // Derive the toolchain pattern.
+ //
+ string pat (pattern (xc, xl == lang::c ? "icc" : "icpc"));
+
// Use the signature line to generate the checksum.
//
sha256 cs (s);
@@ -732,6 +803,7 @@ namespace build2
move (gr.signature),
cs.string (),
move (arch),
+ move (pat),
string ()};
}
@@ -931,24 +1003,8 @@ namespace build2
// then replace it with '*' and use it as a pattern for lib, link,
// etc.
//
- string pat;
-
- if (xc.size () > 2)
- {
- const string& l (xc.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 (xc.directory ());
- p /= "*";
- p += l.c_str () + 2;
- pat = move (p).string ();
- }
- }
+ string cpat (pattern (xc, "cl", nullptr, ".-"));
+ string bpat (cpat); // Binutils pattern is the same as toolchain.
// Use the signature line to generate the checksum.
//
@@ -960,7 +1016,8 @@ namespace build2
move (gr.signature),
cs.string (),
move (arch),
- move (pat)};
+ move (cpat),
+ move (bpat)};
}
compiler_info
@@ -1018,7 +1075,7 @@ namespace build2
// Derive binutils pattern unless this has already been done by the
// compiler-specific code.
//
- if (r.pattern.empty ())
+ if (r.bin_pattern.empty ())
{
// When cross-compiling the whole toolchain is normally prefixed with
// the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}.
@@ -1041,12 +1098,44 @@ namespace build2
path p (xc.directory ());
p /= t;
p += "-*";
- r.pattern = move (p).string ();
+ r.bin_pattern = move (p).string ();
}
}
}
return r;
}
+
+ path
+ guess_default (lang xl, const string& c, const string* pat)
+ {
+ const char* s (nullptr);
+
+ switch (xl)
+ {
+ case lang::c:
+ {
+ if (c == "gcc") s = "gcc";
+ else if (c == "clang") s = "clang";
+ else if (c == "clang-apple") s = "clang";
+ else if (c == "icc") s = "icc";
+ else if (c == "msvc") s = "cl";
+
+ break;
+ }
+ case lang::cxx:
+ {
+ if (c == "gcc") s = "g++";
+ else if (c == "clang") s = "clang++";
+ else if (c == "clang-apple") s = "clang++";
+ else if (c == "icc") s = "icpc";
+ else if (c == "msvc") s = "cl";
+
+ break;
+ }
+ }
+
+ return path (apply_pattern (s, pat));
+ }
}
}
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index 2623c79..80e5027 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -167,7 +167,8 @@ namespace build2
if (first)
{
h.assign ("config.bin.target") = cast<string> (r["cc.target"]);
- if (auto l = r["cc.pattern"])
+
+ if (auto l = hints["config.bin.pattern"])
h.assign ("config.bin.pattern") = cast<string> (l);
}
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index 3a7dad2..4b51905 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -35,9 +35,11 @@ namespace build2
{
tracer trace (x, "config_init");
+ bool cc_loaded (cast_false<bool> (b["cc.config.loaded"]));
+
// Configure.
//
- string pattern; // Toolchain pattern.
+ compiler_info ci; // For program patterns.
if (first)
{
@@ -45,33 +47,69 @@ namespace build2
// config.x
//
- auto p (config::required (r, config_x, path (x_default)));
+
+ // Normally we will have a persistent configuration and computing the
+ // default value every time will be a waste. So try without a default
+ // first.
+ //
+ auto p (config::required (r, config_x));
+
+ if (p.first == nullptr)
+ {
+ // If someone already loaded cc.config then use its toolchain id
+ // and (optional) pattern to guess an appropriate default (e.g.,
+ // for {gcc, *-4.9} we will get g++-4.9).
+ //
+ path d (cc_loaded
+ ? guess_default (x_lang,
+ cast<string> (r["cc.id"]),
+ cast_null<string> (r["cc.pattern"]))
+ : path (x_default));
+
+ auto p1 (config::required (r, config_x, d));
+ p.first = &p1.first.get ();
+ p.second = p1.second;
+ }
// Figure out which compiler we are dealing with, its target, etc.
//
- const path& xc (cast<path> (p.first));
- compiler_info ci (
- guess (x_lang,
- xc,
- cast_null<strings> (r[config_c_coptions]),
- cast_null<strings> (r[config_x_coptions])));
+ const path& xc (cast<path> (*p.first));
+ ci = guess (x_lang,
+ xc,
+ cast_null<strings> (r[config_c_coptions]),
+ cast_null<strings> (r[config_x_coptions]));
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
//
if (verb >= (p.second ? 2 : 3))
{
- text << x << ' ' << project (r) << '@' << r.out_path () << '\n'
+ diag_record dr (text);
+
+ {
+ dr << x << ' ' << project (r) << '@' << r.out_path () << '\n'
<< " " << left << setw (11) << x << xc << '\n'
<< " id " << ci.id << '\n'
<< " version " << ci.version.string << '\n'
<< " major " << ci.version.major << '\n'
<< " minor " << ci.version.minor << '\n'
- << " patch " << ci.version.patch << '\n'
- << " build " << ci.version.build << '\n'
- << " signature " << ci.signature << '\n'
- << " checksum " << ci.checksum << '\n'
- << " target " << ci.target;
+ << " patch " << ci.version.patch << '\n';
+ }
+
+ if (!ci.version.build.empty ())
+ dr << " build " << ci.version.build << '\n';
+
+ {
+ dr << " signature " << ci.signature << '\n'
+ << " target " << ci.target << '\n';
+ }
+
+ if (!ci.cc_pattern.empty ()) // bin_pattern printed by bin
+ dr << " pattern " << ci.cc_pattern << '\n';
+
+ {
+ dr << " checksum " << ci.checksum;
+ }
}
r.assign (x_id) = ci.id.string ();
@@ -87,8 +125,6 @@ namespace build2
r.assign (x_signature) = move (ci.signature);
r.assign (x_checksum) = move (ci.checksum);
- pattern = move (ci.pattern);
-
// Split/canonicalize the target. First see if the user asked us to
// use config.sub.
//
@@ -158,7 +194,7 @@ namespace build2
// Load cc.config.
//
- if (!cast_false<bool> (b["cc.config.loaded"]))
+ if (!cc_loaded)
{
// Prepare configuration hints. They are only used on the first load
// of cc.config so we only populate them on our first load.
@@ -168,8 +204,12 @@ namespace build2
{
h.assign ("config.cc.id") = cast<string> (r[x_id]);
h.assign ("config.cc.target") = cast<string> (r[x_target]);
- if (!pattern.empty ())
- h.assign ("config.cc.pattern") = move (pattern);
+
+ if (!ci.cc_pattern.empty ())
+ h.assign ("config.cc.pattern") = move (ci.cc_pattern);
+
+ if (!ci.bin_pattern.empty ())
+ h.assign ("config.bin.pattern") = move (ci.bin_pattern);
}
load_module ("cc.config", r, b, loc, false, h);
@@ -179,24 +219,24 @@ namespace build2
// If cc.config is already loaded, verify its configuration matched
// ours since it could have been loaded by another c-family module.
//
- auto check = [&r, &loc, this](const char* cv,
- const variable& xv,
+ auto check = [&r, &loc, this](const char* cvar,
+ const variable& xvar,
const char* w)
{
- const string& c (cast<string> (r[cv]));
- const string& x (cast<string> (r[xv]));
+ const string& cv (cast<string> (r[cvar]));
+ const string& xv (cast<string> (r[xvar]));
- if (c != x)
+ if (cv != xv)
fail (loc) << "cc and " << x << " module " << w << " mismatch" <<
- info << cv << " is " << c <<
- info << xv.name << " is " << x;
+ info << cvar << " is " << cv <<
+ info << xvar.name << " is " << xv;
};
// Note that we don't require that patterns match. Presumably, if the
// toolchain id and target are the same, then where exactly the tools
- // (e.g., ar) come from doesn't really matter.
+ // come from doesn't really matter.
//
- check ("cc.id", x_id, "toolchain id");
+ check ("cc.id", x_id, "toolchain");
check ("cc.target", x_target, "target");
}
}
diff --git a/build2/utility b/build2/utility
index 3ecceed..a636c01 100644
--- a/build2/utility
+++ b/build2/utility
@@ -278,6 +278,13 @@ namespace build2
const cstrings&,
bool = false);
+ // Apply the specified substitution (stem) to a '*'-pattern. If pattern
+ // is NULL, then return the stem itself. Assume the pattern is valid,
+ // i.e., contains a single '*' character.
+ //
+ string
+ apply_pattern (const char* stem, const string* pattern);
+
// Parse version string in the X.Y.Z[-{a|b}N] to a version integer in the
// AABBCCDD form, where:
//
diff --git a/build2/utility.cxx b/build2/utility.cxx
index 971c1fb..3eda7e1 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -287,6 +287,21 @@ namespace build2
return false;
}
+ string
+ apply_pattern (const char* s, const string* p)
+ {
+ if (p == nullptr)
+ return s;
+
+ size_t i (p->find ('*'));
+ assert (i != string::npos);
+
+ string r (*p, 0, i++);
+ r.append (s);
+ r.append (*p, i, p->size () - i);
+ return r;
+ }
+
unsigned int
to_version (const string& s)
{