diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-08-12 12:46:21 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-08-12 17:04:22 +0200 |
commit | 0fd7815cbc6557811df4f1b6ffb40461474b8534 (patch) | |
tree | f67b2d8a94f85027b3f1c98c4bf9acadd4b27d56 /build2/cc | |
parent | 9fa5f73d00905568e8979d0c93ec4a8f645c81d5 (diff) |
Implement c/cxx toolchain cross-hinting
Diffstat (limited to 'build2/cc')
-rw-r--r-- | build2/cc/guess | 16 | ||||
-rw-r--r-- | build2/cc/guess.cxx | 137 | ||||
-rw-r--r-- | build2/cc/init.cxx | 3 | ||||
-rw-r--r-- | build2/cc/module.cxx | 96 |
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"); } } |