aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-10-26 19:07:42 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-10-26 19:07:42 +0200
commit296575ba025ded840304c1e3b6365a6b6ee7ea48 (patch)
treeda8ecb3dfc8178b7ddf00c51a21fbe8d375cd435
parent1dba8a7b279c857e9c13cbd6496849f5501e9944 (diff)
Add config.{c,cxx}.{id,version,target} configuration variables
These variables allow overriding guessed compiler id/version/target, for example, in case of mis-guesses or when working with compilers that don't report their base (e.g., GCC, Clang) with -v/--version (common in the embedded space).
-rw-r--r--build2/bin/guess.cxx18
-rw-r--r--build2/bin/init.cxx2
-rw-r--r--build2/c/init.cxx3
-rw-r--r--build2/cc/common.hxx3
-rw-r--r--build2/cc/guess.cxx1120
-rw-r--r--build2/cc/guess.hxx14
-rw-r--r--build2/cc/module.cxx26
-rw-r--r--build2/context.cxx4
-rw-r--r--build2/cxx/init.cxx3
-rw-r--r--build2/utility.hxx4
-rw-r--r--build2/utility.txx10
-rw-r--r--build2/version/snapshot-git.cxx7
12 files changed, 714 insertions, 500 deletions
diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx
index 78a1940..61c9f20 100644
--- a/build2/bin/guess.cxx
+++ b/build2/bin/guess.cxx
@@ -74,7 +74,7 @@ namespace build2
// that.
//
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
// Normally GNU binutils ar --version output has a line that starts
// with "GNU ar" and ends with the version. For example:
@@ -155,7 +155,7 @@ namespace build2
//
if (arr.empty ())
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
return l.find (" ar ") != string::npos
? guess_result ("generic", move (l), semantic_version ())
@@ -185,7 +185,7 @@ namespace build2
// Binutils, LLVM, and FreeBSD.
//
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
// The same story as with ar: normally starts with "GNU ranlib "
// but can vary.
@@ -218,7 +218,7 @@ namespace build2
//
if (rlr.empty ())
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
return l.find ("ranlib") != string::npos
? guess_result ("generic", move (l), semantic_version ())
@@ -285,7 +285,7 @@ namespace build2
// Version extraction is a @@ TODO.
//
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
// Microsoft link.exe output starts with "Microsoft (R) ".
//
@@ -321,7 +321,7 @@ namespace build2
//
if (r.empty ())
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
// New ld64 has "PROJECT:ld64" in the first line (output to stderr),
// for example:
@@ -352,7 +352,7 @@ namespace build2
//
if (r.empty ())
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
// Unlike other LLVM tools (e.g., ar), the lld's version is printed
// (to stderr) as:
@@ -404,7 +404,7 @@ namespace build2
//
// Version extraction is a @@ TODO.
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
// Binutils windres --version output has a line that starts with
// "GNU windres " but search for "GNU ", similar to other tools.
@@ -430,7 +430,7 @@ namespace build2
//
if (r.empty ())
{
- auto f = [] (string& l) -> guess_result
+ auto f = [] (string& l, bool) -> guess_result
{
if (l.compare (0, 14, "Microsoft (R) ") == 0)
return guess_result ("msvc", move (l), semantic_version ());
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index 9112773..6b3aeb3 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -262,7 +262,7 @@ namespace build2
s = run<string> (3,
ops.config_sub (),
s.c_str (),
- [] (string& l) {return move (l);});
+ [] (string& l, bool) {return move (l);});
l5 ([&]{trace << "config.sub target: '" << s << "'";});
}
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
index 141c810..c0fbdde 100644
--- a/build2/c/init.cxx
+++ b/build2/c/init.cxx
@@ -161,6 +161,9 @@ namespace build2
// Note: some overridable, some not.
//
v.insert<path> ("config.c", true),
+ v.insert<string> ("config.c.id", true),
+ v.insert<string> ("config.c.version", true),
+ v.insert<string> ("config.c.target", true),
v.insert<strings> ("config.c.poptions", true),
v.insert<strings> ("config.c.coptions", true),
v.insert<strings> ("config.c.loptions", true),
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index 9629fa3..dccf62e 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -40,6 +40,9 @@ namespace build2
const char* const* x_hinters;
const variable& config_x;
+ const variable& config_x_id; // <type>[-<variant>]
+ const variable& config_x_version;
+ const variable& config_x_target;
const variable& config_x_poptions;
const variable& config_x_coptions;
const variable& config_x_loptions;
diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx
index 4e581a7..de262cc 100644
--- a/build2/cc/guess.cxx
+++ b/build2/cc/guess.cxx
@@ -31,19 +31,29 @@ namespace build2
return r;
}
-#if 0
compiler_id::
- compiler_id (const std::string& t, std::string v)
- : type (t == "gcc" ? compiler_type::gcc :
- t == "clang" ? compiler_type::clang :
- t == "msvc" ? compiler_type::msvc :
- t == "icc" ? compiler_type::icc : invalid_compiler_type),
- variant (move (v))
+ compiler_id (const std::string& id)
{
- if (t == invalid_compiler_type)
- throw invalid_argument ("invalid compiler type value '" + t + "'");
+ using std::string;
+
+ size_t p (id.find ('-'));
+
+ if (id.compare (0, p, "gcc" ) == 0) type = compiler_type::gcc;
+ else if (id.compare (0, p, "clang") == 0) type = compiler_type::clang;
+ else if (id.compare (0, p, "msvc" ) == 0) type = compiler_type::msvc;
+ else if (id.compare (0, p, "icc" ) == 0) type = compiler_type::icc;
+ else
+ throw invalid_argument (
+ "invalid compiler type '" + string (id, 0, p) + "'");
+
+ if (p != string::npos)
+ {
+ variant.assign (id, p + 1, string::npos);
+
+ if (variant.empty ())
+ throw invalid_argument ("empty compiler variant");
+ }
}
-#endif
string compiler_id::
string () const
@@ -202,7 +212,7 @@ namespace build2
// only guesses the type, not the variant.
//
static pair<compiler_type, size_t>
- pre_guess (lang xl, const path& xc)
+ pre_guess (lang xl, const path& xc, const optional<compiler_id>& xi)
{
tracer trace ("cc::pre_guess");
@@ -231,16 +241,30 @@ namespace build2
: string::npos;
};
+ using type = compiler_type;
+ using pair = std::pair<type, size_t>;
+
+ // If the user specified the compiler id, then only check the stem for
+ // that compiler.
+ //
+ auto check = [&xi, &stem] (type t, const char* s) -> optional<pair>
+ {
+ if (!xi || xi->type == t)
+ {
+ size_t p (stem (s));
+
+ if (p != string::npos)
+ return pair (t, p);
+ }
+
+ return nullopt;
+ };
+
// Warn if the user specified a C compiler instead of C++ or vice versa.
//
lang o; // Other language.
const char* as (nullptr); // Actual stem.
const char* es (nullptr); // Expected stem.
- size_t p; // Executable name position.
-
- using type = compiler_type;
- using pair = std::pair<type, size_t>;
- const size_t npos (string::npos);
switch (xl)
{
@@ -248,15 +272,15 @@ namespace build2
{
// Keep msvc last since 'cl' is very generic.
//
- if ((p = stem ("gcc")) != npos) return pair (type::gcc, p);
- if ((p = stem ("clang")) != npos) return pair (type::clang, p);
- if ((p = stem ("icc")) != npos) return pair (type::icc, p);
- if ((p = stem ("cl")) != npos) return pair (type::msvc, p);
+ if (auto r = check (type::gcc, "gcc") ) return *r;
+ if (auto r = check (type::clang, "clang")) return *r;
+ if (auto r = check (type::icc, "icc") ) return *r;
+ if (auto r = check (type::msvc, "cl") ) return *r;
- if (stem (as = "g++") != npos) es = "gcc";
- else if (stem (as = "clang++") != npos) es = "clang";
- else if (stem (as = "icpc") != npos) es = "icc";
- else if (stem (as = "c++") != npos) es = "cc";
+ if (check (type::gcc, as = "g++") ) es = "gcc";
+ else if (check (type::clang, as = "clang++")) es = "clang";
+ else if (check (type::icc, as = "icpc") ) es = "icc";
+ else if (check (type::msvc, as = "c++") ) es = "cc";
o = lang::cxx;
break;
@@ -265,15 +289,15 @@ namespace build2
{
// Keep msvc last since 'cl' is very generic.
//
- if ((p = stem ("g++")) != npos) return pair (type::gcc, p);
- if ((p = stem ("clang++")) != npos) return pair (type::clang, p);
- if ((p = stem ("icpc")) != npos) return pair (type::icc, p);
- if ((p = stem ("cl")) != npos) return pair (type::msvc, p);
+ if (auto r = check (type::gcc, "g++") ) return *r;
+ if (auto r = check (type::clang, "clang++")) return *r;
+ if (auto r = check (type::icc, "icpc") ) return *r;
+ if (auto r = check (type::msvc, "cl") ) return *r;
- if (stem (as = "gcc") != npos) es = "g++";
- else if (stem (as = "clang") != npos) es = "clang++";
- else if (stem (as = "icc") != npos) es = "icpc";
- else if (stem (as = "cc") != npos) es = "c++";
+ if (check (type::gcc, as = "gcc") ) es = "g++";
+ else if (check (type::clang, as = "clang")) es = "clang++";
+ else if (check (type::icc, as = "icc") ) es = "icpc";
+ else if (check (type::msvc, as = "cc") ) es = "c++";
o = lang::c;
break;
@@ -284,6 +308,11 @@ namespace build2
warn << xc << " looks like a " << o << " compiler" <<
info << "should it be '" << es << "' instead of '" << as << "'?";
+ // If the user specified the id, then continue as if we pre-guessed.
+ //
+ if (xi)
+ return pair (xi->type, string::npos);
+
l4 ([&]{trace << "unable to guess compiler type of " << xc;});
return pair (invalid_compiler_type, string::npos);
@@ -301,7 +330,7 @@ namespace build2
process_path path;
guess_result () = default;
- guess_result (compiler_id&& i, string&& s)
+ guess_result (compiler_id i, string&& s)
: id (move (i)), signature (move (s)) {}
bool
@@ -311,18 +340,24 @@ namespace build2
// Allowed to change pre if succeeds.
//
static guess_result
- guess (lang, const string& xv, const path& xc, compiler_type& pre)
+ guess (const char* xm,
+ lang,
+ const path& xc,
+ const optional<compiler_id>& xi,
+ compiler_type& pre)
{
tracer trace ("cc::guess");
+ assert (!xi || xi->type == pre);
+
guess_result r;
process_path xp;
{
auto df = make_diag_frame (
- [&xv](const diag_record& dr)
+ [&xm](const diag_record& dr)
{
- dr << info << "use " << xv << " to override";
+ dr << info << "use config." << xm << " to override";
});
xp = run_search (xc, false /* init */); // Note: cached.
@@ -350,10 +385,17 @@ namespace build2
pre == type::gcc ||
pre == type::clang))
{
- auto f = [] (string& l) -> guess_result
+ auto f = [&xi] (string& l, bool last) -> guess_result
{
- // The gcc/g++ -v output will have a line (currently last) in the
- // form:
+ if (xi)
+ {
+ // The signature line is first in Clang and last in GCC.
+ //
+ if (xi->type != type::gcc || last)
+ return guess_result (*xi, move (l));
+ }
+
+ // The gcc/g++ -v output will have a last line in the form:
//
// "gcc version X.Y.Z ..."
//
@@ -367,7 +409,7 @@ namespace build2
// gcc version 5.1.0 (Ubuntu 5.1.0-0ubuntu11~14.04.1)
// gcc version 6.0.0 20160131 (experimental) (GCC)
//
- if (l.compare (0, 4, "gcc ") == 0)
+ if (last && l.compare (0, 4, "gcc ") == 0)
return guess_result (compiler_id {type::gcc, ""}, move (l));
// The Apple clang/clang++ -v output will have a line (currently
@@ -400,8 +442,8 @@ namespace build2
l.compare (6, 6, "clang ") == 0))
return guess_result (compiler_id {type::clang, "apple"}, move (l));
- // The vanilla clang/clang++ -v output will have a line (currently
- // first) in the form:
+ // The vanilla clang/clang++ -v output will have a first line in the
+ // form:
//
// "[... ]clang version X.Y.Z[-...] ..."
//
@@ -434,7 +476,12 @@ namespace build2
//
r = run<guess_result> (3, xp, "-v", f, false, false, &cs);
- if (!r.empty ())
+ if (r.empty ())
+ {
+ if (xi)
+ fail << "unable to obtain " << xc << " signature with -v";
+ }
+ else
{
// If this is clang-apple and pre-guess was gcc then change it so
// that we don't issue any warnings.
@@ -452,8 +499,13 @@ namespace build2
//
if (r.empty () && (pre == invalid || pre == type::icc))
{
- auto f = [] (string& l) -> guess_result
+ auto f = [&xi] (string& l, bool) -> guess_result
{
+ // Assume the first line is the signature.
+ //
+ if (xi)
+ return guess_result (*xi, move (l));
+
// The first line has the " (ICC) " in it, for example:
//
// icpc (ICC) 9.0 20060120
@@ -471,6 +523,12 @@ namespace build2
};
r = run<guess_result> (3, xp, "--version", f, false);
+
+ if (r.empty ())
+ {
+ if (xi)
+ fail << "unable to obtain " << xc << " signature with --version";
+ }
}
// Finally try to run it without any options to detect msvc.
@@ -478,8 +536,13 @@ namespace build2
//
if (r.empty () && (pre == invalid || pre == type::msvc))
{
- auto f = [] (string& l) -> guess_result
+ auto f = [&xi] (string& l, bool) -> guess_result
{
+ // Assume the first line is the signature.
+ //
+ if (xi)
+ return guess_result (*xi, move (l));
+
// Check for "Microsoft (R)" and "C/C++" in the first line as a
// signature since all other words/positions can be translated. For
// example:
@@ -513,6 +576,12 @@ namespace build2
const char* env[] = {"CL=", "_CL_=", nullptr};
r = run<guess_result> (3, process_env (xp, env), f, false);
+
+ if (r.empty ())
+ {
+ if (xi)
+ fail << "unable to obtain " << xc << " signature";
+ }
}
if (!r.empty ())
@@ -590,8 +659,11 @@ namespace build2
static compiler_info
- guess_gcc (lang xl,
+ guess_gcc (const char* xm,
+ lang xl,
const path& xc,
+ const string* xv,
+ const string* xt,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings*, const strings*,
@@ -607,52 +679,63 @@ namespace build2
//
// "gcc version A.B.C[ ...]"
//
- string& s (gr.signature);
-
- // Scan the string as words and look for one that looks like a version.
- //
- size_t b (0), e (0);
- while (next_word (s, b, e))
+ compiler_version v;
{
- // The third argument to find_first_not_of() is the length of the
- // first argument, not the length of the interval to check. So to
- // limit it to [b, e) we are also going to compare the result to the
- // end of the word position (first space). In fact, we can just check
- // if it is >= e.
+ auto df = make_diag_frame (
+ [&xm](const diag_record& dr)
+ {
+ dr << info << "use config." << xm << ".version to override";
+ });
+
+ // Treat the custom version as just a tail of the signature.
//
- if (s.find_first_not_of ("1234567890.", b, 11) >= e)
- break;
- }
+ const string& s (xv == nullptr ? gr.signature : *xv);
- if (b == e)
- fail << "unable to extract gcc version from '" << s << "'";
+ // Scan the string as words and look for one that looks like a
+ // version.
+ //
+ size_t b (0), e (0);
+ while (next_word (s, b, e))
+ {
+ // The third argument to find_first_not_of() is the length of the
+ // first argument, not the length of the interval to check. So to
+ // limit it to [b, e) we are also going to compare the result to the
+ // end of the word position (first space). In fact, we can just
+ // check if it is >= e.
+ //
+ if (s.find_first_not_of ("1234567890.", b, 11) >= e)
+ break;
+ }
- compiler_version v;
- v.string.assign (s, b, string::npos);
+ if (b == e)
+ fail << "unable to extract gcc version from '" << s << "'";
- // Split the version into components.
- //
- size_t vb (b), ve (b);
- auto next = [&s, b, e, &vb, &ve] (const char* m) -> uint64_t
- {
- try
+ v.string.assign (s, b, string::npos);
+
+ // Split the version into components.
+ //
+ size_t vb (b), ve (b);
+ auto next = [&s, b, e, &vb, &ve] (const char* m) -> uint64_t
{
- if (next_word (s, e, vb, ve, '.'))
- return stoull (string (s, vb, ve - vb));
- }
- catch (const invalid_argument&) {}
- catch (const out_of_range&) {}
+ try
+ {
+ if (next_word (s, e, vb, ve, '.'))
+ return stoull (string (s, vb, ve - vb));
+ }
+ catch (const invalid_argument&) {}
+ catch (const out_of_range&) {}
- fail << "unable to extract gcc " << m << " version from '"
- << string (s, b, e - b) << "'" << endf;
- };
+ fail << "unable to extract gcc " << m << " version from '"
+ << string (s, b, e - b) << "'" << endf;
+ };
- v.major = next ("major");
- v.minor = next ("minor");
- v.patch = next ("patch");
+ v.major = next ("major");
+ v.minor = next ("minor");
+ v.patch = next ("patch");
- if (e != s.size ())
- v.build.assign (s, e + 1, string::npos);
+ if (e != s.size ())
+ v.build.assign (s, e + 1, string::npos);
+ }
// Figure out the target architecture. This is actually a lot trickier
// than one would have hoped.
@@ -675,32 +758,40 @@ namespace build2
// multi-arch support), then use the result. Otherwise, fallback to
// -dumpmachine (older gcc or not multi-arch).
//
- cstrings args {xp.recall_string (), "-print-multiarch"};
- if (c_co != nullptr) append_options (args, *c_co);
- if (x_co != nullptr) append_options (args, *x_co);
- args.push_back (nullptr);
+ string t, ot;
- // The output of both -print-multiarch and -dumpmachine is a single line
- // containing just the target triplet.
- //
- auto f = [] (string& l) {return move (l);};
+ if (xt == nullptr)
+ {
+ cstrings args {xp.recall_string (), "-print-multiarch"};
+ if (c_co != nullptr) append_options (args, *c_co);
+ if (x_co != nullptr) append_options (args, *x_co);
+ args.push_back (nullptr);
- string t (run<string> (3, xp, args.data (), f, false));
+ // The output of both -print-multiarch and -dumpmachine is a single
+ // line containing just the target triplet.
+ //
+ auto f = [] (string& l, bool) {return move (l);};
- if (t.empty ())
- {
- l5 ([&]{trace << xc << " doesn's support -print-multiarch, "
- << "falling back to -dumpmachine";});
+ t = run<string> (3, xp, args.data (), f, false);
- args[1] = "-dumpmachine";
- t = run<string> (3, xp, args.data (), f);
- }
+ if (t.empty ())
+ {
+ l5 ([&]{trace << xc << " doesn's support -print-multiarch, "
+ << "falling back to -dumpmachine";});
- if (t.empty ())
- fail << "unable to extract target architecture from " << xc
- << " -print-multiarch or -dumpmachine output";
+ args[1] = "-dumpmachine";
+ t = run<string> (3, xp, args.data (), f, false);
+ }
- string ot (t);
+ if (t.empty ())
+ fail << "unable to extract target architecture from " << xc
+ << " using -print-multiarch or -dumpmachine output" <<
+ info << "use config." << xm << ".target to override";
+
+ ot = t;
+ }
+ else
+ ot = t = *xt;
// Parse the target into triplet (for further tests) ignoring any
// failures.
@@ -759,8 +850,11 @@ namespace build2
}
static compiler_info
- guess_clang (lang xl,
+ guess_clang (const char* xm,
+ lang xl,
const path& xc,
+ const string* xv,
+ const string* xt,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings* c_lo, const strings* x_lo,
@@ -776,87 +870,105 @@ namespace build2
// "[... ]clang version A.B.C[( |-)...]"
// "Apple (clang|LLVM) version A.B[.C] ..."
//
- string& s (gr.signature);
+ compiler_version v;
+ {
+ auto df = make_diag_frame (
+ [&xm](const diag_record& dr)
+ {
+ dr << info << "use config." << xm << ".version to override";
+ });
- // Some overrides for testing.
- //
- //s = "clang version 3.7.0 (tags/RELEASE_370/final)";
- //
- //gr.id.variant = "apple";
- //s = "Apple LLVM version 7.3.0 (clang-703.0.16.1)";
- //s = "Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn)";
+ // Treat the custom version as just a tail of the signature.
+ //
+ const string& s (xv == nullptr ? gr.signature : *xv);
- // Scan the string as words and look for one that looks like a version.
- // Use '-' as a second delimiter to handle versions like
- // "3.6.0-2ubuntu1~trusty1".
- //
- size_t b (0), e (0);
- while (next_word (s, b, e, ' ', '-'))
- {
- // The third argument to find_first_not_of() is the length of the
- // first argument, not the length of the interval to check. So to
- // limit it to [b, e) we are also going to compare the result to the
- // end of the word position (first space). In fact, we can just check
- // if it is >= e.
+ // Some overrides for testing.
//
- if (s.find_first_not_of ("1234567890.", b, 11) >= e)
- break;
- }
+ //s = "clang version 3.7.0 (tags/RELEASE_370/final)";
+ //
+ //gr.id.variant = "apple";
+ //s = "Apple LLVM version 7.3.0 (clang-703.0.16.1)";
+ //s = "Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn)";
+
+ // Scan the string as words and look for one that looks like a
+ // version. Use '-' as a second delimiter to handle versions like
+ // "3.6.0-2ubuntu1~trusty1".
+ //
+ size_t b (0), e (0);
+ while (next_word (s, b, e, ' ', '-'))
+ {
+ // The third argument to find_first_not_of() is the length of the
+ // first argument, not the length of the interval to check. So to
+ // limit it to [b, e) we are also going to compare the result to the
+ // end of the word position (first space). In fact, we can just
+ // check if it is >= e.
+ //
+ if (s.find_first_not_of ("1234567890.", b, 11) >= e)
+ break;
+ }
- if (b == e)
- fail << "unable to extract clang version from '" << s << "'";
+ if (b == e)
+ fail << "unable to extract clang version from '" << s << "'";
- compiler_version v;
- v.string.assign (s, b, string::npos);
+ v.string.assign (s, b, string::npos);
- // Split the version into components.
- //
- size_t vb (b), ve (b);
- auto next = [&s, b, e, &vb, &ve] (const char* m, bool opt) -> uint64_t
- {
- try
+ // Split the version into components.
+ //
+ size_t vb (b), ve (b);
+ auto next = [&s, b, e, &vb, &ve] (const char* m, bool opt) -> uint64_t
{
- if (next_word (s, e, vb, ve, '.'))
- return stoull (string (s, vb, ve - vb));
+ try
+ {
+ if (next_word (s, e, vb, ve, '.'))
+ return stoull (string (s, vb, ve - vb));
- if (opt)
- return 0;
- }
- catch (const invalid_argument&) {}
- catch (const out_of_range&) {}
+ if (opt)
+ return 0;
+ }
+ catch (const invalid_argument&) {}
+ catch (const out_of_range&) {}
- fail << "unable to extract clang " << m << " version from '"
- << string (s, b, e - b) << "'" << endf;
- };
+ fail << "unable to extract clang " << m << " version from '"
+ << string (s, b, e - b) << "'" << endf;
+ };
- v.major = next ("major", false);
- v.minor = next ("minor", false);
- v.patch = next ("patch", gr.id.variant == "apple");
+ v.major = next ("major", false);
+ v.minor = next ("minor", false);
+ v.patch = next ("patch", gr.id.variant == "apple");
- if (e != s.size ())
- v.build.assign (s, e + 1, string::npos);
+ if (e != s.size ())
+ v.build.assign (s, e + 1, string::npos);
+ }
// Figure out the target architecture.
//
// Unlike gcc, clang doesn't have -print-multiarch. Its -dumpmachine,
// however, respects the compile options (e.g., -m32).
//
- cstrings args {xp.recall_string (), "-dumpmachine"};
- if (c_co != nullptr) append_options (args, *c_co);
- if (x_co != nullptr) append_options (args, *x_co);
- args.push_back (nullptr);
+ string t, ot;
- // The output of -dumpmachine is a single line containing just the
- // target triplet.
- //
- auto f = [] (string& l) {return move (l);};
- string t (run<string> (3, xp, args.data (), f));
+ if (xt == nullptr)
+ {
+ cstrings args {xp.recall_string (), "-dumpmachine"};
+ if (c_co != nullptr) append_options (args, *c_co);
+ if (x_co != nullptr) append_options (args, *x_co);
+ args.push_back (nullptr);
- if (t.empty ())
- fail << "unable to extract target architecture from " << xc
- << " -dumpmachine output";
+ // The output of -dumpmachine is a single line containing just the
+ // target triplet.
+ //
+ auto f = [] (string& l, bool) {return move (l);};
+ t = run<string> (3, xp, args.data (), f, false);
- string ot (t);
+ if (t.empty ())
+ fail << "unable to extract target architecture from " << xc
+ << " using -dumpmachine output" <<
+ info << "use config." << xm << ".target to override";
+
+ ot = t;
+ }
+ else
+ ot = t = *xt;
// Parse the target into triplet (for further tests) ignoring any
// failures.
@@ -978,8 +1090,11 @@ namespace build2
}
static compiler_info
- guess_icc (lang xl,
+ guess_icc (const char* xm,
+ lang xl,
const path& xc,
+ const string* xv,
+ const string* xt,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings*, const strings*,
@@ -1012,95 +1127,110 @@ namespace build2
// We should probably also assume the language words can be translated
// and even rearranged.
//
- string& s (gr.signature);
- s.clear ();
-
- auto f = [] (string& l)
+ auto f = [] (string& l, bool)
{
return l.compare (0, 5, "Intel") == 0 && (l[5] == '(' || l[5] == ' ')
? move (l)
: string ();
};
- // The -V output is sent to STDERR.
- //
- s = run<string> (3, xp, "-V", f, false);
+ if (xv == nullptr)
+ {
+ string& s (gr.signature);
+ s.clear ();
- if (s.empty ())
- fail << "unable to extract signature from " << xc << " -V output";
+ // The -V output is sent to STDERR.
+ //
+ s = run<string> (3, xp, "-V", f, false);
- if (s.find (xl == lang::c ? " C " : " C++ ") == string::npos)
- fail << xc << " does not appear to be the Intel " << xl
- << " compiler" <<
- info << "extracted signature: '" << s << "'";
+ if (s.empty ())
+ fail << "unable to extract signature from " << xc << " -V output";
+
+ if (s.find (xl == lang::c ? " C " : " C++ ") == string::npos)
+ fail << xc << " does not appear to be the Intel " << xl
+ << " compiler" <<
+ info << "extracted signature: '" << s << "'";
+ }
// Scan the string as words and look for the version. It consist of only
// digits and periods and contains at least one period.
//
+ compiler_version v;
+ {
+ auto df = make_diag_frame (
+ [&xm](const diag_record& dr)
+ {
+ dr << info << "use config." << xm << ".version to override";
+ });
- // Some overrides for testing.
- //
- //s = "Intel(R) C++ Compiler for 32-bit applications, Version 9.1 Build 20070215Z Package ID: l_cc_c_9.1.047";
- //s = "Intel(R) C++ Compiler for applications running on Intel(R) 64, Version 10.1 Build 20071116";
- //s = "Intel(R) C++ Compiler for applications running on IA-32, Version 10.1 Build 20071116 Package ID: l_cc_p_10.1.010";
- //s = "Intel C++ Intel 64 Compiler Professional for applications running on Intel 64, Version 11.0 Build 20081105 Package ID: l_cproc_p_11.0.074";
- //s = "Intel(R) C++ Intel(R) 64 Compiler Professional for applications running on Intel(R) 64, Version 11.1 Build 20091130 Package ID: l_cproc_p_11.1.064";
- //s = "Intel C++ Intel 64 Compiler XE for applications running on Intel 64, Version 12.0.4.191 Build 20110427";
+ // Treat the custom version as just a tail of the signature.
+ //
+ const string& s (xv == nullptr ? gr.signature : *xv);
- size_t b (0), e (0), n;
- while (next_word (s, b, e, ' ', ',') != 0)
- {
- // The third argument to find_first_not_of() is the length of the
- // first argument, not the length of the interval to check. So to
- // limit it to [b, e) we are also going to compare the result to the
- // end of the word position (first space). In fact, we can just check
- // if it is >= e. Similar logic for find_first_of() except that we add
- // space to the list of character to make sure we don't go too far.
+ // Some overrides for testing.
//
- if (s.find_first_not_of ("1234567890.", b, 11) >= e &&
- s.find_first_of (". ", b, 2) < e)
- break;
- }
+ //s = "Intel(R) C++ Compiler for 32-bit applications, Version 9.1 Build 20070215Z Package ID: l_cc_c_9.1.047";
+ //s = "Intel(R) C++ Compiler for applications running on Intel(R) 64, Version 10.1 Build 20071116";
+ //s = "Intel(R) C++ Compiler for applications running on IA-32, Version 10.1 Build 20071116 Package ID: l_cc_p_10.1.010";
+ //s = "Intel C++ Intel 64 Compiler Professional for applications running on Intel 64, Version 11.0 Build 20081105 Package ID: l_cproc_p_11.0.074";
+ //s = "Intel(R) C++ Intel(R) 64 Compiler Professional for applications running on Intel(R) 64, Version 11.1 Build 20091130 Package ID: l_cproc_p_11.1.064";
+ //s = "Intel C++ Intel 64 Compiler XE for applications running on Intel 64, Version 12.0.4.191 Build 20110427";
+
+ size_t b (0), e (0);
+ while (next_word (s, b, e, ' ', ',') != 0)
+ {
+ // The third argument to find_first_not_of() is the length of the
+ // first argument, not the length of the interval to check. So to
+ // limit it to [b, e) we are also going to compare the result to the
+ // end of the word position (first space). In fact, we can just
+ // check if it is >= e. Similar logic for find_first_of() except
+ // that we add space to the list of character to make sure we don't
+ // go too far.
+ //
+ if (s.find_first_not_of ("1234567890.", b, 11) >= e &&
+ s.find_first_of (". ", b, 2) < e)
+ break;
+ }
- if (b == e)
- fail << "unable to extract icc version from '" << s << "'";
+ if (b == e)
+ fail << "unable to extract icc version from '" << s << "'";
- compiler_version v;
- v.string.assign (s, b, string::npos);
+ v.string.assign (s, b, string::npos);
- // Split the version into components.
- //
- size_t vb (b), ve (b);
- auto next = [&s, b, e, &vb, &ve] (const char* m, bool opt) -> uint64_t
- {
- try
+ // Split the version into components.
+ //
+ size_t vb (b), ve (b);
+ auto next = [&s, b, e, &vb, &ve] (const char* m, bool opt) -> uint64_t
{
- if (next_word (s, e, vb, ve, '.'))
- return stoull (string (s, vb, ve - vb));
+ try
+ {
+ if (next_word (s, e, vb, ve, '.'))
+ return stoull (string (s, vb, ve - vb));
- if (opt)
- return 0;
- }
- catch (const invalid_argument&) {}
- catch (const out_of_range&) {}
+ if (opt)
+ return 0;
+ }
+ catch (const invalid_argument&) {}
+ catch (const out_of_range&) {}
- fail << "unable to extract icc " << m << " version from '"
- << string (s, b, e - b) << "'" << endf;
- };
+ fail << "unable to extract icc " << m << " version from '"
+ << string (s, b, e - b) << "'" << endf;
+ };
- v.major = next ("major", false);
- v.minor = next ("minor", false);
- v.patch = next ("patch", true);
+ v.major = next ("major", false);
+ v.minor = next ("minor", false);
+ v.patch = next ("patch", true);
- if (vb != ve && next_word (s, e, vb, ve, '.'))
- v.build.assign (s, vb, ve - vb);
+ if (vb != ve && next_word (s, e, vb, ve, '.'))
+ v.build.assign (s, vb, ve - vb);
- if (e != s.size ())
- {
- if (!v.build.empty ())
- v.build += ' ';
+ if (e != s.size ())
+ {
+ if (!v.build.empty ())
+ v.build += ' ';
- v.build.append (s, e + 1, string::npos);
+ v.build.append (s, e + 1, string::npos);
+ }
}
// Figure out the target CPU by re-running the compiler with -V and
@@ -1116,75 +1246,91 @@ namespace build2
// "Intel(R)" "64"
// "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux)
//
- cstrings args {xp.recall_string (), "-V"};
- if (c_co != nullptr) append_options (args, *c_co);
- if (x_co != nullptr) append_options (args, *x_co);
- args.push_back (nullptr);
+ string t, ot;
- // The -V output is sent to STDERR.
- //
- string t (run<string> (3, xp, args.data (), f, false));
+ if (xt == nullptr)
+ {
+ auto df = make_diag_frame (
+ [&xm](const diag_record& dr)
+ {
+ dr << info << "use config." << xm << ".target to override";
+ });
- if (t.empty ())
- fail << "unable to extract target architecture from " << xc
- << " -V output";
+ cstrings args {xp.recall_string (), "-V"};
+ if (c_co != nullptr) append_options (args, *c_co);
+ if (x_co != nullptr) append_options (args, *x_co);
+ args.push_back (nullptr);
- string arch;
- for (b = e = 0; (n = next_word (t, b, e, ' ', ',')) != 0; )
- {
- if (t.compare (b, n, "Intel(R)", 8) == 0 ||
- t.compare (b, n, "Intel", 5) == 0)
+ // The -V output is sent to STDERR.
+ //
+ t = run<string> (3, xp, args.data (), f, false);
+
+ if (t.empty ())
+ fail << "unable to extract target architecture from " << xc
+ << " -V output";
+
+ string arch;
+ for (size_t b (0), e (0), n;
+ (n = next_word (t, b, e, ' ', ',')) != 0; )
{
- if ((n = next_word (t, b, e, ' ', ',')) != 0)
+ if (t.compare (b, n, "Intel(R)", 8) == 0 ||
+ t.compare (b, n, "Intel", 5) == 0)
{
- if (t.compare (b, n, "64", 2) == 0)
- {
- arch = "x86_64";
- }
- else if (t.compare (b, n, "MIC", 3) == 0)
+ if ((n = next_word (t, b, e, ' ', ',')) != 0)
{
- arch = "x86_64"; // Plus "-k1om-linux" from -dumpmachine below.
+ if (t.compare (b, n, "64", 2) == 0)
+ {
+ arch = "x86_64";
+ }
+ else if (t.compare (b, n, "MIC", 3) == 0)
+ {
+ arch = "x86_64"; // Plus "-k1om-linux" from -dumpmachine below.
+ }
}
+ else
+ break;
}
- else
- break;
- }
- else if (t.compare (b, n, "IA-32", 5) == 0 ||
+ else if (t.compare (b, n, "IA-32", 5) == 0 ||
t.compare (b, n, "32-bit", 6) == 0)
- {
- arch = "i386";
+ {
+ arch = "i386";
+ }
}
- }
- if (arch.empty ())
- fail << "unable to extract icc target architecture from '" << t << "'";
+ if (arch.empty ())
+ fail << "unable to extract icc target architecture from '"
+ << t << "'";
- // So we have the CPU but we still need the rest of the triplet. While
- // icc currently doesn't support cross-compilation (at least on Linux)
- // and we could have just used the build triplet (i.e., the architecture
- // on which we are running), who knows what will happen in the future.
- // So instead we are going to use -dumpmachine and substitute the CPU.
- //
- {
- auto f = [] (string& l) {return move (l);};
- t = run<string> (3, xp, "-dumpmachine", f);
- }
+ // So we have the CPU but we still need the rest of the triplet. While
+ // icc currently doesn't support cross-compilation (at least on Linux)
+ // and we could have just used the build triplet (i.e., the
+ // architecture on which we are running), who knows what will happen
+ // in the future. So instead we are going to use -dumpmachine and
+ // substitute the CPU.
+ //
+ {
+ auto f = [] (string& l, bool) {return move (l);};
+ t = run<string> (3, xp, "-dumpmachine", f);
+ }
- if (t.empty ())
- fail << "unable to extract target architecture from " << xc
- << " -dumpmachine output";
+ if (t.empty ())
+ fail << "unable to extract target architecture from " << xc
+ << " using -dumpmachine output";
- // The first component in the triplet is always CPU.
- //
- size_t p (t.find ('-'));
+ // The first component in the triplet is always CPU.
+ //
+ size_t p (t.find ('-'));
- if (p == string::npos)
- fail << "unable to parse icc target architecture '" << t << "'";
+ if (p == string::npos)
+ fail << "unable to parse icc target architecture '" << t << "'";
- t.swap (arch);
- t.append (arch, p, string::npos);
+ t.swap (arch);
+ t.append (arch, p, string::npos);
- string ot (t);
+ ot = t;
+ }
+ else
+ ot = t = *xt;
// Parse the target into triplet (for further tests) ignoring any
// failures.
@@ -1198,7 +1344,7 @@ namespace build2
// Use the signature line to generate the checksum.
//
- sha256 cs (s);
+ sha256 cs (gr.signature);
// Runtime and standard library.
//
@@ -1237,8 +1383,11 @@ namespace build2
}
static compiler_info
- guess_msvc (lang xl,
+ guess_msvc (const char* xm,
+ lang xl,
const path& xc,
+ const string* xv,
+ const string* xt,
const strings*, const strings*,
const strings*, const strings*,
const strings*, const strings*,
@@ -1257,190 +1406,199 @@ namespace build2
// "x64"
// "ARM"
//
- string& s (gr.signature);
-
- // Some overrides for testing.
- //
- //s = "Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86";
- //s = "Compilador de optimizacion de C/C++ de Microsoft (R) version 16.00.30319.01 para x64";
+ compiler_version v;
+ {
+ auto df = make_diag_frame (
+ [&xm](const diag_record& dr)
+ {
+ dr << info << "use config." << xm << ".version to override";
+ });
- // Scan the string as words and look for the version. While doing this
- // also keep an eye on the CPU keywords.
- //
- string arch;
- size_t b (0), e (0);
+ // Treat the custom version as just a tail of the signature.
+ //
+ const string& s (xv == nullptr ? gr.signature : *xv);
- auto check_cpu = [&arch, &s, &b, &e] () -> bool
- {
- size_t n (e - b);
+ // Some overrides for testing.
+ //
+ //s = "Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86";
+ //s = "Compilador de optimizacion de C/C++ de Microsoft (R) version 16.00.30319.01 para x64";
- if (s.compare (b, n, "x64", 3) == 0 ||
- s.compare (b, n, "x86", 3) == 0 ||
- s.compare (b, n, "ARM", 3) == 0 ||
- s.compare (b, n, "80x86", 5) == 0)
+ // Scan the string as words and look for the version.
+ //
+ size_t b (0), e (0);
+ while (next_word (s, b, e, ' ', ','))
{
- arch.assign (s, b, n);
- return true;
+ // The third argument to find_first_not_of() is the length of the
+ // first argument, not the length of the interval to check. So to
+ // limit it to [b, e) we are also going to compare the result to the
+ // end of the word position (first space). In fact, we can just
+ // check if it is >= e.
+ //
+ if (s.find_first_not_of ("1234567890.", b, 11) >= e)
+ break;
}
- return false;
- };
+ if (b == e)
+ fail << "unable to extract msvc version from '" << s << "'";
- while (next_word (s, b, e, ' ', ','))
- {
- // First check for the CPU keywords in case in some language they come
- // before the version.
- //
- if (check_cpu ())
- continue;
-
- // The third argument to find_first_not_of() is the length of the
- // first argument, not the length of the interval to check. So to
- // limit it to [b, e) we are also going to compare the result to the
- // end of the word position (first space). In fact, we can just check
- // if it is >= e.
+ v.string.assign (s, b, e - b);
+
+ // Split the version into components.
//
- if (s.find_first_not_of ("1234567890.", b, 11) >= e)
- break;
- }
+ size_t vb (b), ve (b);
+ auto next = [&s, b, e, &vb, &ve] (const char* m) -> uint64_t
+ {
+ try
+ {
+ if (next_word (s, e, vb, ve, '.'))
+ return stoull (string (s, vb, ve - vb));
+ }
+ catch (const invalid_argument&) {}
+ catch (const out_of_range&) {}
- if (b == e)
- fail << "unable to extract msvc version from '" << s << "'";
+ fail << "unable to extract msvc " << m << " version from '"
+ << string (s, b, e - b) << "'" << endf;
+ };
- compiler_version v;
- v.string.assign (s, b, e - b);
+ v.major = next ("major");
+ v.minor = next ("minor");
+ v.patch = next ("patch");
- // Split the version into components.
+ if (next_word (s, e, vb, ve, '.'))
+ v.build.assign (s, vb, ve - vb);
+ }
+
+
+ // Figure out the target architecture.
//
- size_t vb (b), ve (b);
- auto next = [&s, b, e, &vb, &ve] (const char* m) -> uint64_t
- {
- try
- {
- if (next_word (s, e, vb, ve, '.'))
- return stoull (string (s, vb, ve - vb));
- }
- catch (const invalid_argument&) {}
- catch (const out_of_range&) {}
+ string t, ot;
- fail << "unable to extract msvc " << m << " version from '"
- << string (s, b, e - b) << "'" << endf;
- };
+ if (xt == nullptr)
+ {
+ auto df = make_diag_frame (
+ [&xm](const diag_record& dr)
+ {
+ dr << info << "use config." << xm << ".target to override";
+ });
- v.major = next ("major");
- v.minor = next ("minor");
- v.patch = next ("patch");
+ const string& s (gr.signature);
- if (next_word (s, e, vb, ve, '.'))
- v.build.assign (s, vb, ve - vb);
+ // Scan the string as words and look for the CPU.
+ //
+ string arch;
- // Continue scanning for the CPU.
- //
- if (e != s.size ())
- {
- while (next_word (s, b, e, ' ', ','))
+ for (size_t b (0), e (0), n;
+ (n = next_word (s, b, e, ' ', ',')) != 0; )
{
- if (check_cpu ())
+ if (s.compare (b, n, "x64", 3) == 0 ||
+ s.compare (b, n, "x86", 3) == 0 ||
+ s.compare (b, n, "ARM", 3) == 0 ||
+ s.compare (b, n, "80x86", 5) == 0)
+ {
+ arch.assign (s, b, n);
break;
+ }
}
- }
-
- if (arch.empty ())
- fail << "unable to extract msvc target architecture from "
- << "'" << s << "'";
- // Now we need to map x86, x64, and ARM to the target triplets. The
- // problem is, there aren't any established ones so we got to invent
- // them ourselves. Based on the discussion in
- // <libbutl/target-triplet.mxx>, we need something in the
- // CPU-VENDOR-OS-ABI form.
- //
- // The CPU part is fairly straightforward with x86 mapped to 'i386' (or
- // maybe 'i686'), x64 to 'x86_64', and ARM to 'arm' (it could also
- // include the version, e.g., 'amrv8').
- //
- // The (toolchain) VENDOR is also straightforward: 'microsoft'. Why not
- // omit it? Two reasons: firstly, there are other compilers with the
- // otherwise same target, for example Intel C/C++, and it could be
- // useful to distinguish between them. Secondly, by having all four
- // components we remove any parsing ambiguity.
- //
- // OS-ABI is where things are not as clear cut. The OS part shouldn't
- // probably be just 'windows' since we have Win32 and WinCE. And WinRT.
- // And Universal Windows Platform (UWP). So perhaps the following values
- // for OS: 'win32', 'wince', 'winrt', 'winup'.
- //
- // For 'win32' the ABI part could signal the Microsoft C/C++ runtime by
- // calling it 'msvc'. And seeing that the runtimes are incompatible from
- // version to version, we should probably add the 'X.Y' version at the
- // end (so we essentially mimic the DLL name, e.g, msvcr120.dll). Some
- // suggested we also encode the runtime type (those /M* options) though
- // I am not sure: the only "redistributable" runtime is multi-threaded
- // release DLL.
- //
- // The ABI part for the other OS values needs thinking. For 'winrt' and
- // 'winup' it probably makes sense to encode the WINAPI_FAMILY macro
- // value (perhaps also with the version). Some of its values:
- //
- // WINAPI_FAMILY_APP Windows 10
- // WINAPI_FAMILY_PC_APP Windows 8.1
- // WINAPI_FAMILY_PHONE_APP Windows Phone 8.1
- //
- // For 'wince' we may also want to add the OS version, e.g., 'wince4.2'.
- //
- // Putting it all together, Visual Studio 2015 will then have the
- // following target triplets:
- //
- // x86 i386-microsoft-win32-msvc14.0
- // x64 x86_64-microsoft-win32-msvc14.0
- // ARM arm-microsoft-winup-???
- //
- string t;
-
- if (arch == "ARM")
- fail << "cl.exe ARM/WinRT/UWP target is not yet supported";
- else
- {
- if (arch == "x64")
- t = "x86_64-microsoft-win32-msvc";
- else if (arch == "x86" || arch == "80x86")
- t = "i386-microsoft-win32-msvc";
- else
- assert (false);
+ if (arch.empty ())
+ fail << "unable to extract msvc target architecture from "
+ << "'" << s << "'";
- // Mapping of compiler versions to runtime versions:
+ // Now we need to map x86, x64, and ARM to the target triplets. The
+ // problem is, there aren't any established ones so we got to invent
+ // them ourselves. Based on the discussion in
+ // <libbutl/target-triplet.mxx>, we need something in the
+ // CPU-VENDOR-OS-ABI form.
//
- // Note that VC15 has runtime version 14.1 but the DLLs are still
- // called *140.dll (they are said to be backwards-compatible).
+ // The CPU part is fairly straightforward with x86 mapped to 'i386'
+ // (or maybe 'i686'), x64 to 'x86_64', and ARM to 'arm' (it could also
+ // include the version, e.g., 'amrv8').
//
- // year ver cl.exe crt/dll
+ // The (toolchain) VENDOR is also straightforward: 'microsoft'. Why
+ // not omit it? Two reasons: firstly, there are other compilers with
+ // the otherwise same target, for example Intel C/C++, and it could be
+ // useful to distinguish between them. Secondly, by having all four
+ // components we remove any parsing ambiguity.
//
- // 2017 15u7 19.14 14.1/140
- // 2017 15u6 19.13 14.1/140
- // 2017 15u5 19.12 14.1/140
- // 2017 15u3 19.11 14.1/140
- // 2017 15 19.10 14.1/140
- // 2015 14 19.00 14.0/140
- // 2013 12 18.00 12.0/120
- // 2012 11 17.00 11.0/110
- // 2010 10 16.00 10.0/100
- // 2008 9 15.00 9.0/90
- // 2005 8 14.00 8.0/80
- // 2003 7.1 13.10 7.1/71
+ // OS-ABI is where things are not as clear cut. The OS part shouldn't
+ // probably be just 'windows' since we have Win32 and WinCE. And
+ // WinRT. And Universal Windows Platform (UWP). So perhaps the
+ // following values for OS: 'win32', 'wince', 'winrt', 'winup'.
//
- /**/ if (v.major == 19 && v.minor >= 10) t += "14.1";
- else if (v.major == 19 && v.minor == 0) t += "14.0";
- else if (v.major == 18 && v.minor == 0) t += "12.0";
- else if (v.major == 17 && v.minor == 0) t += "11.0";
- else if (v.major == 16 && v.minor == 0) t += "10.0";
- else if (v.major == 15 && v.minor == 0) t += "9.0";
- else if (v.major == 14 && v.minor == 0) t += "8.0";
- else if (v.major == 13 && v.minor == 10) t += "7.1";
- else fail << "unable to map msvc compiler version '" << v.string
- << "' to runtime version";
- }
+ // For 'win32' the ABI part could signal the Microsoft C/C++ runtime
+ // by calling it 'msvc'. And seeing that the runtimes are incompatible
+ // from version to version, we should probably add the 'X.Y' version
+ // at the end (so we essentially mimic the DLL name, for example,
+ // msvcr120.dll). Some suggested we also encode the runtime type
+ // (those pesky /M* options) though I am not sure: the only
+ // "redistributable" runtime is multi-threaded release DLL.
+ //
+ // The ABI part for the other OS values needs thinking. For 'winrt'
+ // and 'winup' it probably makes sense to encode the WINAPI_FAMILY
+ // macro value (perhaps also with the version). Some of its values:
+ //
+ // WINAPI_FAMILY_APP Windows 10
+ // WINAPI_FAMILY_PC_APP Windows 8.1
+ // WINAPI_FAMILY_PHONE_APP Windows Phone 8.1
+ //
+ // For 'wince' we may also want to add the OS version, for example,
+ // 'wince4.2'.
+ //
+ // Putting it all together, Visual Studio 2015 will then have the
+ // following target triplets:
+ //
+ // x86 i386-microsoft-win32-msvc14.0
+ // x64 x86_64-microsoft-win32-msvc14.0
+ // ARM arm-microsoft-winup-???
+ //
+ if (arch == "ARM")
+ fail << "cl.exe ARM/WinRT/UWP target is not yet supported";
+ else
+ {
+ if (arch == "x64")
+ t = "x86_64-microsoft-win32-msvc";
+ else if (arch == "x86" || arch == "80x86")
+ t = "i386-microsoft-win32-msvc";
+ else
+ assert (false);
- string ot (t);
+ // Mapping of compiler versions to runtime versions:
+ //
+ // Note that VC15 has runtime version 14.1 but the DLLs are still
+ // called *140.dll (they are said to be backwards-compatible).
+ //
+ // year ver cl.exe crt/dll
+ //
+ // 2017 15u8 19.15 14.1/140
+ // 2017 15u7 19.14 14.1/140
+ // 2017 15u6 19.13 14.1/140
+ // 2017 15u5 19.12 14.1/140
+ // 2017 15u3 19.11 14.1/140
+ // 2017 15 19.10 14.1/140
+ // 2015 14 19.00 14.0/140
+ // 2013 12 18.00 12.0/120
+ // 2012 11 17.00 11.0/110
+ // 2010 10 16.00 10.0/100
+ // 2008 9 15.00 9.0/90
+ // 2005 8 14.00 8.0/80
+ // 2003 7.1 13.10 7.1/71
+ //
+ /**/ if (v.major == 19 && v.minor >= 10) t += "14.1";
+ else if (v.major == 19 && v.minor == 0) t += "14.0";
+ else if (v.major == 18 && v.minor == 0) t += "12.0";
+ else if (v.major == 17 && v.minor == 0) t += "11.0";
+ else if (v.major == 16 && v.minor == 0) t += "10.0";
+ else if (v.major == 15 && v.minor == 0) t += "9.0";
+ else if (v.major == 14 && v.minor == 0) t += "8.0";
+ else if (v.major == 13 && v.minor == 10) t += "7.1";
+ else fail << "unable to map msvc compiler version '" << v.string
+ << "' to runtime version";
+ }
+
+ ot = t;
+ }
+ else
+ ot = t = *xt;
// Derive the toolchain pattern.
//
@@ -1453,7 +1611,7 @@ namespace build2
// Use the signature line to generate the checksum.
//
- sha256 cs (s);
+ sha256 cs (gr.signature);
// Runtime and standard library.
//
@@ -1488,9 +1646,12 @@ namespace build2
static map<string, compiler_info> cache;
const compiler_info&
- guess (lang xl,
- const string& xv,
+ guess (const char* xm,
+ lang xl,
const path& xc,
+ const string* xis,
+ const string* xv,
+ const string* xt,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings* c_lo, const strings* x_lo)
@@ -1502,6 +1663,7 @@ namespace build2
sha256 cs;
cs.append (static_cast<size_t> (xl));
cs.append (xc.string ());
+ if (xis != nullptr) cs.append (*xis);
if (c_po != nullptr) hash_options (cs, *c_po);
if (x_po != nullptr) hash_options (cs, *x_po);
if (c_co != nullptr) hash_options (cs, *c_co);
@@ -1515,7 +1677,23 @@ namespace build2
return i->second;
}
- pair<compiler_type, size_t> pre (pre_guess (xl, xc));
+ // Parse the user-specified compiler id (config.x.id).
+ //
+ optional<compiler_id> xi;
+ if (xis != nullptr)
+ {
+ try
+ {
+ xi = compiler_id (*xis);
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "invalid compiler id '" << *xis << "' "
+ << "specified in variable config." << xm << ".id: " << e;
+ }
+ }
+
+ pair<compiler_type, size_t> pre (pre_guess (xl, xc, xi));
compiler_type& type (pre.first);
// If we could pre-guess the type based on the excutable name, then
@@ -1525,20 +1703,23 @@ namespace build2
if (type != invalid_compiler_type)
{
- gr = guess (xl, xv, xc, type);
+ gr = guess (xm, xl, xc, xi, type);
if (gr.empty ())
+ {
warn << xc << " looks like " << type << " but it is not" <<
- info << "use " << xv << " to override";
+ info << "use config." << xm << " to override";
- type = invalid_compiler_type;
+ type = invalid_compiler_type; // Clear pre-guess.
+ }
}
if (gr.empty ())
- gr = guess (xl, xv, xc, type);
+ gr = guess (xm, xl, xc, xi, type);
if (gr.empty ())
- fail << "unable to guess " << xl << " compiler type of " << xc;
+ fail << "unable to guess " << xl << " compiler type of " << xc <<
+ info << "use config." << xm << ".id to specify explicitly";
compiler_info r;
const compiler_id& id (gr.id);
@@ -1547,28 +1728,28 @@ namespace build2
{
case compiler_type::gcc:
{
- r = guess_gcc (xl, xc,
+ r = guess_gcc (xm, xl, xc, xv, xt,
c_po, x_po, c_co, x_co, c_lo, x_lo,
move (gr));
break;
}
case compiler_type::clang:
{
- r = guess_clang (xl, xc,
+ r = guess_clang (xm, xl, xc, xv, xt,
c_po, x_po, c_co, x_co, c_lo, x_lo,
move (gr));
break;
}
case compiler_type::msvc:
{
- r = guess_msvc (xl, xc,
+ r = guess_msvc (xm, xl, xc, xv, xt,
c_po, x_po, c_co, x_co, c_lo, x_lo,
move (gr));
break;
}
case compiler_type::icc:
{
- r = guess_icc (xl, xc,
+ r = guess_icc (xm, xl, xc, xv, xt,
c_po, x_po, c_co, x_co, c_lo, x_lo,
move (gr));
break;
@@ -1645,29 +1826,36 @@ namespace build2
}
path
- guess_default (lang xl, const string& c, const string& pat)
+ guess_default (lang xl, const string& cid, const string& pat)
{
+ compiler_id id (cid);
const char* s (nullptr);
+ using type = compiler_type;
+
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";
+ switch (id.type)
+ {
+ case type::gcc: s = "gcc"; break;
+ case type::clang: s = "clang"; break;
+ case type::icc: s = "icc"; break;
+ case type::msvc: s = "cl"; break;
+ }
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";
+ switch (id.type)
+ {
+ case type::gcc: s = "g++"; break;
+ case type::clang: s = "clang++"; break;
+ case type::icc: s = "icpc"; break;
+ case type::msvc: s = "cl"; break;
+ }
break;
}
diff --git a/build2/cc/guess.hxx b/build2/cc/guess.hxx
index 8b10b29..5fa4de7 100644
--- a/build2/cc/guess.hxx
+++ b/build2/cc/guess.hxx
@@ -66,7 +66,8 @@ namespace build2
compiler_id (compiler_type t, std::string v)
: type (t), variant (move (v)) {}
- //compiler_id (const std::string& type, std::string variant);
+ explicit
+ compiler_id (const std::string&);
};
inline ostream&
@@ -222,14 +223,17 @@ namespace build2
// that most of it will be the same, at least for C and C++.
//
const compiler_info&
- guess (lang,
- const string& xv, // Override variable (config.x) for diagnostics.
- const path& xc,
+ guess (const char* xm, // Module (for variable names in diagnostics).
+ lang xl, // Language.
+ const path& xc, // Compiler path.
+ const string* xi, // Compiler id (optional).
+ const string* xv, // Compiler version (optional).
+ const string* xt, // Compiler target (optional).
const strings* c_poptions, const strings* x_poptions,
const strings* c_coptions, const strings* x_coptions,
const strings* c_loptions, const strings* x_loptions);
- // Given a language, toolchain id, and optionally (empty) a pattern,
+ // Given a language, compiler id, and optionally an (empty) pattern,
// return an appropriate default compiler path.
//
// For example, for (lang::cxx, gcc, *-4.9) we will get g++-4.9.
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index 363f1d0..5ef1f2a 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -115,16 +115,20 @@ namespace build2
// Figure out which compiler we are dealing with, its target, etc.
//
- const path& xc (cast<path> (*p.first));
- ci_ = &build2::cc::guess (x_lang,
- config_x.name,
- xc,
- cast_null<strings> (rs[config_c_poptions]),
- cast_null<strings> (rs[config_x_poptions]),
- cast_null<strings> (rs[config_c_coptions]),
- cast_null<strings> (rs[config_x_coptions]),
- cast_null<strings> (rs[config_c_loptions]),
- cast_null<strings> (rs[config_x_loptions]));
+ ci_ = &build2::cc::guess (
+ x,
+ x_lang,
+ cast<path> (*p.first),
+ cast_null<string> (config::omitted (rs, config_x_id).first),
+ cast_null<string> (config::omitted (rs, config_x_version).first),
+ cast_null<string> (config::omitted (rs, config_x_target).first),
+ cast_null<strings> (rs[config_c_poptions]),
+ cast_null<strings> (rs[config_x_poptions]),
+ cast_null<strings> (rs[config_c_coptions]),
+ cast_null<strings> (rs[config_x_coptions]),
+ cast_null<strings> (rs[config_c_loptions]),
+ cast_null<strings> (rs[config_x_loptions]));
+
const compiler_info& ci (*ci_);
// Split/canonicalize the target. First see if the user asked us to
@@ -139,7 +143,7 @@ namespace build2
ct = run<string> (3,
ops.config_sub (),
ci.target.c_str (),
- [] (string& l) {return move (l);});
+ [] (string& l, bool) {return move (l);});
l5 ([&]{trace << "config.sub target: '" << ct << "'";});
}
diff --git a/build2/context.cxx b/build2/context.cxx
index a8cf042..066e620 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -498,7 +498,9 @@ namespace build2
//
string orig (
ops.config_guess_specified ()
- ? run<string> (3, ops.config_guess (), [](string& l) {return move (l);})
+ ? run<string> (3,
+ ops.config_guess (),
+ [](string& l, bool) {return move (l);})
: BUILD2_HOST_TRIPLET);
l5 ([&]{trace << "original host: '" << orig << "'";});
diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx
index 80a7b66..84b7bb6 100644
--- a/build2/cxx/init.cxx
+++ b/build2/cxx/init.cxx
@@ -366,6 +366,9 @@ namespace build2
// Note: some overridable, some not.
//
v.insert<path> ("config.cxx", true),
+ v.insert<string> ("config.cxx.id", true),
+ v.insert<string> ("config.cxx.version", true),
+ v.insert<string> ("config.cxx.target", true),
v.insert<strings> ("config.cxx.poptions", true),
v.insert<strings> ("config.cxx.coptions", true),
v.insert<strings> ("config.cxx.loptions", true),
diff --git a/build2/utility.hxx b/build2/utility.hxx
index cc34d59..b75cb5a 100644
--- a/build2/utility.hxx
+++ b/build2/utility.hxx
@@ -280,7 +280,9 @@ namespace build2
//
// The predicate can move the value out of the passed string but, if error
// is false, only in case of a "content match" (so that any diagnostics
- // lines are left intact).
+ // lines are left intact). The function signature should be:
+ //
+ // T (string& line, bool last)
//
// If ignore_exit is true, then the program's exist status is ignored (if it
// is false and the program exits with the non-zero status, then an empty T
diff --git a/build2/utility.txx b/build2/utility.txx
index 1f65b76..73bd47a 100644
--- a/build2/utility.txx
+++ b/build2/utility.txx
@@ -79,9 +79,13 @@ namespace build2
{
ifdstream is (move (pr.in_ofd), butl::fdstream_mode::skip);
- while (is.peek () != ifdstream::traits_type::eof () && // Keep last line.
- getline (is, l))
+ // Make sure we keep the last line.
+ //
+ for (bool last (is.peek () == ifdstream::traits_type::eof ());
+ !last && getline (is, l); )
{
+ last = (is.peek () == ifdstream::traits_type::eof ());
+
trim (l);
if (checksum != nullptr)
@@ -89,7 +93,7 @@ namespace build2
if (r.empty ())
{
- r = f (l);
+ r = f (l, last);
if (!r.empty () && checksum == nullptr)
break;
diff --git a/build2/version/snapshot-git.cxx b/build2/version/snapshot-git.cxx
index 278c5ce..3f19e62 100644
--- a/build2/version/snapshot-git.cxx
+++ b/build2/version/snapshot-git.cxx
@@ -31,9 +31,10 @@ namespace build2
//
{
const char* args[] {"git", "-C", d, "status", "--porcelain", nullptr};
- r.committed = run<string> (3 /* verbosity */,
- args,
- [](string& s) {return move (s);}).empty ();
+ r.committed = run<string> (
+ 3 /* verbosity */,
+ args,
+ [](string& s, bool) {return move (s);}).empty ();
}
// Now extract the commit id and date. One might think that would be