aboutsummaryrefslogtreecommitdiff
path: root/build2/cc
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 /build2/cc
parent9fa5f73d00905568e8979d0c93ec4a8f645c81d5 (diff)
Implement c/cxx toolchain cross-hinting
Diffstat (limited to 'build2/cc')
-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
4 files changed, 197 insertions, 55 deletions
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");
}
}