diff options
-rw-r--r-- | build2/buildfile | 2 | ||||
-rw-r--r-- | build2/c/init.cxx | 13 | ||||
-rw-r--r-- | build2/cc/common.cxx | 10 | ||||
-rw-r--r-- | build2/cc/common.hxx | 16 | ||||
-rw-r--r-- | build2/cc/compile.cxx | 518 | ||||
-rw-r--r-- | build2/cc/guess.cxx | 18 | ||||
-rw-r--r-- | build2/cc/guess.hxx | 29 | ||||
-rw-r--r-- | build2/cc/link.cxx | 30 | ||||
-rw-r--r-- | build2/cc/module.cxx | 23 | ||||
-rw-r--r-- | build2/cc/pkgconfig.cxx | 4 | ||||
-rw-r--r-- | build2/cxx/init.cxx | 22 |
11 files changed, 399 insertions, 286 deletions
diff --git a/build2/buildfile b/build2/buildfile index ce75f07..9294738 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -28,7 +28,7 @@ else else stack_size = 4194304 # 4M - if ($cxx.id == 'msvc') + if ($cxx.class == 'msvc') cxx.loptions += "/STACK:$stack_size" else cxx.loptions += "-Wl,--stack,$stack_size" diff --git a/build2/c/init.cxx b/build2/c/init.cxx index e473e58..4158a09 100644 --- a/build2/c/init.cxx +++ b/build2/c/init.cxx @@ -21,6 +21,7 @@ namespace build2 namespace c { using cc::compiler_id; + using cc::compiler_class; using cc::compiler_info; class config_module: public cc::config_module @@ -43,9 +44,9 @@ namespace build2 { strings r; - switch (ci.id.value ()) + switch (ci.class_) { - case compiler_id::msvc: + case compiler_class::msvc: { // Standard-wise, with VC you get what you get. The question is // whether we should verify that the requested standard is provided @@ -79,9 +80,7 @@ namespace build2 } break; } - case compiler_id::gcc: - case compiler_id::clang: - case compiler_id::icc: + case compiler_class::gcc: { // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x // for compatibility with older versions of the compilers. @@ -188,6 +187,8 @@ namespace build2 v.insert<string> ("c.id.type"), v.insert<string> ("c.id.variant"), + v.insert<string> ("c.class"), + v.insert<string> ("c.version"), v.insert<uint64_t> ("c.version.major"), v.insert<uint64_t> ("c.version.minor"), @@ -287,7 +288,7 @@ namespace build2 "c.uninstall", cm.ci.id.value (), - cm.ci.id.variant, + cm.ci.class_, cm.ci.version.major, cm.ci.version.minor, cast<process_path> (rs[cm.x_path]), diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx index e022abb..951cc47 100644 --- a/build2/cc/common.cxx +++ b/build2/cc/common.cxx @@ -532,7 +532,7 @@ namespace build2 // const char* e (""); - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { an = path (name); e = "lib"; @@ -560,7 +560,7 @@ namespace build2 { const char* e (""); - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { sn = path (name); e = "dll.lib"; @@ -679,7 +679,7 @@ namespace build2 // // If we didn't find .dll.lib then we cannot assume .lib is static. // - if (!an.empty () && (s != nullptr || cid != compiler_id::msvc)) + if (!an.empty () && (s != nullptr || cclass != compiler_class::msvc)) { f = d; f /= an; @@ -700,7 +700,7 @@ namespace build2 // Alternative search for VC. // - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { const scope& rs (*p.scope->root_scope ()); const process_path& ld (cast<process_path> (rs["bin.ld.path"])); @@ -899,7 +899,7 @@ namespace build2 dir_path d; - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { // /LIBPATH:<dir> (case-insensitive). // diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx index d4f8bd2..ba14471 100644 --- a/build2/cc/common.hxx +++ b/build2/cc/common.hxx @@ -78,6 +78,8 @@ namespace build2 const variable& x_id_type; const variable& x_id_variant; + const variable& x_class; + const variable& x_version; const variable& x_version_major; const variable& x_version_minor; @@ -105,13 +107,13 @@ namespace build2 // Cached values for some commonly-used variables/values. // - compiler_id::value_type cid; // x.id (no variant) - const string& cvar; // x.id.variant + compiler_id::value_type cid; // x.id + compiler_class cclass; // x.class uint64_t cmaj; // x.version.major uint64_t cmin; // x.version.minor const process_path& cpath; // x.path - const target_triplet& ctg; // x.target + const target_triplet& ctgt; // x.target const string& tsys; // x.target.system const string& tclass; // x.target.class @@ -158,10 +160,10 @@ namespace build2 const char* link, const char* install, const char* uninstall, - compiler_id::value_type id, const string& var, + compiler_id::value_type id, compiler_class cl, uint64_t mj, uint64_t mi, const process_path& path, - const target_triplet& tg, + const target_triplet& tgt, const strings& std, bool fm, bool fs, @@ -178,8 +180,8 @@ namespace build2 x_link (link), x_install (install), x_uninstall (uninstall), - cid (id), cvar (var), cmaj (mj), cmin (mi), cpath (path), - ctg (tg), tsys (ctg.system), tclass (ctg.class_), + cid (id), cclass (cl), cmaj (mj), cmin (mi), cpath (path), + ctgt (tgt), tsys (ctgt.system), tclass (ctgt.class_), tstd (std), modules (fm), symexport (fs), diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index 65c1362..fef7eb6 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -168,6 +168,7 @@ namespace build2 } } case compiler_id::clang: + case compiler_id::clang_apple: { // Clang has *-cpp-output (but not c++-module-cpp-output) and they // handle comments and line continuations. However, currently this @@ -540,16 +541,21 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { e += mod ? "pcm" : o; break; } case compiler_id::msvc: - case compiler_id::icc: { e += mod ? "ifc" : o; break; } + case compiler_id::icc: + { + assert (!mod); + e += o; + } } // If we are compiling a module, then the obj*{} is an ad hoc member @@ -1417,6 +1423,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { // -frewrite-includes is available since vanilla Clang 3.2.0. // @@ -1424,8 +1431,9 @@ namespace build2 // option (4.2 is based on 3.2svc so it may or may not have it and, // no, we are not going to try to find out). // - if (cvar == "" ? (cmaj > 3 || (cmaj == 3 && cmin >= 2)) : - cvar == "apple" ? (cmaj >= 5) : false) + if (cid == compiler_id::clang_apple + ? (cmaj >= 5) + : (cmaj > 3 || (cmaj == 3 && cmin >= 2))) pp = "-frewrite-includes"; break; @@ -1786,126 +1794,141 @@ namespace build2 // Don't treat warnings as errors. // - const char* werror (cid == compiler_id::msvc ? "/WX" : "-Werror"); + const char* werror (nullptr); + switch (cclass) + { + case compiler_class::gcc: werror = "-Werror"; break; + case compiler_class::msvc: werror = "/WX"; break; + } + + bool clang (cid == compiler_id::clang || + cid == compiler_id::clang_apple); append_options (args, t, c_coptions, werror); append_options (args, t, x_coptions, werror); append_options (args, tstd, - tstd.size () - - (modules && cid == compiler_id::clang ? 1 : 0)); + tstd.size () - (modules && clang ? 1 : 0)); - if (cid == compiler_id::msvc) + switch (cclass) { - assert (pp != nullptr); + case compiler_class::msvc: + { + assert (pp != nullptr); - args.push_back ("/nologo"); + args.push_back ("/nologo"); - // See perform_update() for details on overriding the default - // exceptions and runtime. - // - if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) - args.push_back ("/EHsc"); + // See perform_update() for details on overriding the default + // exceptions and runtime. + // + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) + args.push_back ("/EHsc"); + + if (!find_option_prefixes ({"/MD", "/MT"}, args)) + args.push_back ("/MD"); - if (!find_option_prefixes ({"/MD", "/MT"}, args)) - args.push_back ("/MD"); + args.push_back ("/P"); // Preprocess to file. + args.push_back ("/showIncludes"); // Goes to stdout (with diag). + args.push_back (pp); // /C (preserve comments). + args.push_back ("/WX"); // Warning as error (see above). - args.push_back ("/P"); // Preprocess to file. - args.push_back ("/showIncludes"); // Goes to stdout (with diag). - args.push_back (pp); // /C (preserve comments). - args.push_back ("/WX"); // Warning as error (see above). + psrc = auto_rmfile (t.path () + x_pext); - psrc = auto_rmfile (t.path () + x_pext); + if (cast<uint64_t> (rs[x_version_major]) >= 18) + { + args.push_back ("/Fi:"); + args.push_back (psrc.path.string ().c_str ()); + } + else + { + out = "/Fi" + psrc.path.string (); + args.push_back (out.c_str ()); + } - if (cast<uint64_t> (rs[x_version_major]) >= 18) - { - args.push_back ("/Fi:"); - args.push_back (psrc.path.string ().c_str ()); + args.push_back (langopt (md)); // Compile as. + gen = args_gen = true; + break; } - else + case compiler_class::gcc: { - out = "/Fi" + psrc.path.string (); - args.push_back (out.c_str ()); - } + if (t.is_a<objs> ()) + { + // On Darwin, Win32 -fPIC is the default. + // + if (tclass == "linux" || tclass == "bsd") + args.push_back ("-fPIC"); + } - args.push_back (langopt (md)); // Compile as. - gen = args_gen = true; - } - else - { - if (t.is_a<objs> ()) - { - // On Darwin, Win32 -fPIC is the default. + // Depending on the compiler, decide whether (and how) we can + // produce preprocessed output as a side effect of dependency + // extraction. // - if (tclass == "linux" || tclass == "bsd") - args.push_back ("-fPIC"); - } + // Note: -MM -MG skips missing <>-included. - // Depending on the compiler, decide whether (and how) we can - // produce preprocessed output as a side effect of dependency - // extraction. - // - // Note: -MM -MG skips missing <>-included. + // Clang's -M does not imply -w (disable warnings). We also + // don't need them in the -MD case (see above) so disable for + // both. + // + if (clang) + args.push_back ("-w"); + + // Previously we used '*' as a target name but it gets expanded + // to the current directory file names by GCC (4.9) that comes + // with MSYS2 (2.4). Yes, this is the (bizarre) behavior of GCC + // being executed in the shell with -MQ '*' option and not just + // -MQ *. + // + args.push_back ("-MQ"); // Quoted target name. + args.push_back ("^"); // Old versions can't do empty target. - // Clang's -M does not imply -w (disable warnings). We also don't - // need them in the -MD case (see above) so disable for both. - // - if (cid == compiler_id::clang) - args.push_back ("-w"); + args.push_back ("-x"); + args.push_back (langopt (md)); - // Previously we used '*' as a target name but it gets expanded to - // the current directory file names by GCC (4.9) that comes with - // MSYS2 (2.4). Yes, this is the (bizarre) behavior of GCC being - // executed in the shell with -MQ '*' option and not just -MQ *. - // - args.push_back ("-MQ"); // Quoted target name. - args.push_back ("^"); // Old versions can't do empty target name. + if (pp != nullptr) + { + // Note that the options are carefully laid out to be easy to + // override (see below). + // + args_i = args.size (); - args.push_back ("-x"); - args.push_back (langopt (md)); + args.push_back ("-MD"); + args.push_back ("-E"); + args.push_back (pp); - if (pp != nullptr) - { - // Note that the options are carefully laid out to be easy to - // override (see below). - // - args_i = args.size (); + // Dependency output. + // + args.push_back ("-MF"); - args.push_back ("-MD"); - args.push_back ("-E"); - args.push_back (pp); + // GCC is not capable of writing the dependency info to + // stdout. We also need to sense the diagnostics on the -E + // runs. + // + if (cid == compiler_id::gcc) + { + // Use the .t extension (for "temporary"; .d is taken). + // + r = &(drm = auto_rmfile (t.path () + ".t")).path; + args.push_back (r->string ().c_str ()); - // Dependency output. - // - args.push_back ("-MF"); + sense_diag = true; + } + else + args.push_back ("-"); - // GCC is not capable of writing the dependency info to stdout. - // We also need to sense the diagnostics on the -E runs. - // - if (cid == compiler_id::gcc) - { - // Use the .t extension (for "temporary"; .d is taken). + // Preprocessor output. // - r = &(drm = auto_rmfile (t.path () + ".t")).path; - args.push_back (r->string ().c_str ()); - - sense_diag = true; + psrc = auto_rmfile (t.path () + x_pext); + args.push_back ("-o"); + args.push_back (psrc.path.string ().c_str ()); } else - args.push_back ("-"); + { + args.push_back ("-M"); + args.push_back ("-MG"); // Treat missing headers as generated. + } - // Preprocessor output. - // - psrc = auto_rmfile (t.path () + x_pext); - args.push_back ("-o"); - args.push_back (psrc.path.string ().c_str ()); - } - else - { - args.push_back ("-M"); - args.push_back ("-MG"); // Treat missing headers as generated. + gen = args_gen = (pp == nullptr); + break; } - - gen = args_gen = (pp == nullptr); } args.push_back (src.path ().string ().c_str ()); @@ -2376,13 +2399,14 @@ namespace build2 // For VC with /P the dependency info and diagnostics all go // to stderr so redirect it to stdout. // - pr = process (cpath, - args.data (), - 0, - -1, - cid == compiler_id::msvc ? 1 : gen ? 2 : -2, - nullptr, // CWD - env.empty () ? nullptr : env.data ()); + pr = process ( + cpath, + args.data (), + 0, + -1, + cclass == compiler_class::msvc ? 1 : gen ? 2 : -2, + nullptr, // CWD + env.empty () ? nullptr : env.data ()); } else { @@ -2441,134 +2465,145 @@ namespace build2 // Parse different dependency output formats. // - if (cid == compiler_id::msvc) + switch (cclass) { - if (first) + case compiler_class::msvc: { - // The first line should be the file we are compiling. If - // it is not, then something went wrong even before we - // could compile anything (e.g., file does not exist). In - // this case the first line (and everything after it) is - // presumably diagnostics. - // - if (l != src.path ().leaf ().string ()) + if (first) + { + // The first line should be the file we are compiling. + // If it is not, then something went wrong even before + // we could compile anything (e.g., file does not + // exist). In this case the first line (and everything + // after it) is presumably diagnostics. + // + if (l != src.path ().leaf ().string ()) + { + text << l; + bad_error = true; + break; + } + + first = false; + continue; + } + + string f (next_show (l, good_error)); + + if (f.empty ()) // Some other diagnostics. { text << l; bad_error = true; break; } - first = false; - continue; - } + // Skip until where we left off. + // + if (skip != 0) + { + // We can't be skipping over a non-existent header. + // + assert (!good_error); + skip--; + } + else + { + restart = add (path (move (f)), false, pmt); + + // If the header does not exist (good_error), then + // restart must be true. Except that it is possible that + // someone running in parallel has already updated it. + // In this case we must force a restart since we haven't + // yet seen what's after this at-that-time-non-existent + // header. + // + // We also need to force the target update (normally + // done by add()). + // + if (good_error) + restart = updating = true; - string f (next_show (l, good_error)); + if (restart) + l6 ([&]{trace << "restarting";}); + } - if (f.empty ()) // Some other diagnostics. - { - text << l; - bad_error = true; break; } - - // Skip until where we left off. - // - if (skip != 0) - { - // We can't be skipping over a non-existent header. - // - assert (!good_error); - skip--; - } - else + case compiler_class::gcc: { - restart = add (path (move (f)), false, pmt); - - // If the header does not exist (good_error) then restart - // must be true. Except that it is possible that someone - // running in parallel has already updated it. In this - // case we must force a restart since we haven't yet seen - // what's after this at-that-time-non-existent header. - // - // We also need to force the target update (normally done - // by add()). + // Make dependency declaration. // - if (good_error) - restart = updating = true; + size_t pos (0); - if (restart) - l6 ([&]{trace << "restarting";}); - } - } - else - { - // Make dependency declaration. - // - size_t pos (0); - - if (first) - { - // Empty/invalid output should mean the wait() call below - // will return false. - // - if (l.empty () || - l[0] != '^' || l[1] != ':' || l[2] != ' ') + if (first) { - if (!l.empty ()) - text << l; - - bad_error = true; - break; - } + // Empty/invalid output should mean the wait() call + // below will return false. + // + if (l.empty () || + l[0] != '^' || l[1] != ':' || l[2] != ' ') + { + if (!l.empty ()) + text << l; - first = false; - second = true; + bad_error = true; + break; + } - // While normally we would have the source file on the - // first line, if too long, it will be moved to the next - // line and all we will have on this line is "^: \". - // - if (l.size () == 4 && l[3] == '\\') - continue; - else - pos = 3; // Skip "^: ". + first = false; + second = true; - // Fall through to the 'second' block. - } - - if (second) - { - second = false; - next_make (l, pos); // Skip the source file. - } + // While normally we would have the source file on the + // first line, if too long, it will be moved to the next + // line and all we will have on this line is "^: \". + // + if (l.size () == 4 && l[3] == '\\') + continue; + else + pos = 3; // Skip "^: ". - while (pos != l.size ()) - { - string f (next_make (l, pos)); + // Fall through to the 'second' block. + } - // Skip until where we left off. - // - if (skip != 0) + if (second) { - skip--; - continue; + second = false; + next_make (l, pos); // Skip the source file. } - restart = add (path (move (f)), false, pmt); - - if (restart) + while (pos != l.size ()) { - l6 ([&]{trace << "restarting";}); - break; + string f (next_make (l, pos)); + + // Skip until where we left off. + // + if (skip != 0) + { + skip--; + continue; + } + + restart = add (path (move (f)), false, pmt); + + if (restart) + { + l6 ([&]{trace << "restarting";}); + break; + } } + + break; } } + + if (bad_error) + break; } // In case of VC, we are parsing stderr and if things go south, // we need to copy the diagnostics for the user to see. // - if (bad_error && cid == compiler_id::msvc) + if (bad_error && cclass == compiler_class::msvc) { // We used to just dump the whole rdbuf but it turns out VC // may continue writing include notes interleaved with the @@ -2742,7 +2777,7 @@ namespace build2 // VC's preprocessed output, if present, is fully preprocessed. // - if (cid != compiler_id::msvc || !ps) + if (cclass != compiler_class::msvc || !ps) { // This should match with how we setup preprocessing and is pretty // similar to init_args() from extract_headers(). @@ -2767,51 +2802,65 @@ namespace build2 // // @@ Can be both -WX and /WX. // - const char* werror (cid == compiler_id::msvc ? "/WX" : "-Werror"); + const char* werror (nullptr); + switch (cclass) + { + case compiler_class::gcc: werror = "-Werror"; break; + case compiler_class::msvc: werror = "/WX"; break; + } + + bool clang (cid == compiler_id::clang || + cid == compiler_id::clang_apple); append_options (args, t, c_coptions, werror); append_options (args, t, x_coptions, werror); append_options (args, tstd, - tstd.size () - - (modules && cid == compiler_id::clang ? 1 : 0)); + tstd.size () - (modules && clang ? 1 : 0)); - if (cid == compiler_id::msvc) + switch (cclass) { - args.push_back ("/nologo"); + case compiler_class::msvc: + { + args.push_back ("/nologo"); - if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) - args.push_back ("/EHsc"); + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) + args.push_back ("/EHsc"); - if (!find_option_prefixes ({"/MD", "/MT"}, args)) - args.push_back ("/MD"); + if (!find_option_prefixes ({"/MD", "/MT"}, args)) + args.push_back ("/MD"); - args.push_back ("/E"); - args.push_back ("/C"); - args.push_back (langopt (md)); // Compile as. - } - else - { - if (t.is_a<objs> ()) - { - if (tclass == "linux" || tclass == "bsd") - args.push_back ("-fPIC"); - } - - // Options that trigger preprocessing of partially preprocessed - // output are a bit of a compiler-specific voodoo. - // - args.push_back ("-E"); + args.push_back ("/E"); + args.push_back ("/C"); + args.push_back (langopt (md)); // Compile as. - if (ps) + break; + } + case compiler_class::gcc: { - args.push_back ("-x"); - args.push_back (langopt (md)); + if (t.is_a<objs> ()) + { + if (tclass == "linux" || tclass == "bsd") + args.push_back ("-fPIC"); + } - if (cid == compiler_id::gcc) + // Options that trigger preprocessing of partially preprocessed + // output are a bit of a compiler-specific voodoo. + // + args.push_back ("-E"); + + if (ps) { - args.push_back ("-fpreprocessed"); - args.push_back ("-fdirectives-only"); + args.push_back ("-x"); + args.push_back (langopt (md)); + + if (cid == compiler_id::gcc) + { + args.push_back ("-fpreprocessed"); + args.push_back ("-fdirectives-only"); + } } + + break; } } @@ -3034,6 +3083,7 @@ namespace build2 { case compiler_id::gcc: case compiler_id::clang: + case compiler_id::clang_apple: { // We don't need to redo this if the above hash hasn't changed and // the database is valid. @@ -3791,6 +3841,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { // In Clang the module implementation's unit .pcm is special and // must be "loaded". @@ -3870,6 +3921,7 @@ namespace build2 switch (cid) { case compiler_id::gcc: break; // All of them. + case compiler_id::clang_apple: case compiler_id::clang: n = ms.copied != 0 ? ms.copied : n; break; case compiler_id::msvc: break; // All of them. case compiler_id::icc: assert (false); @@ -3898,6 +3950,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { // In Clang the module implementation's unit .pcm is special and // must be "loaded". @@ -4061,7 +4114,7 @@ namespace build2 strings mods; // Module options storage. size_t out_i (0); // Index of the -o option. - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { // The /F*: option variants with separate names only became available // in VS2013/12.0. Why do we bother? Because the command line suddenly @@ -4194,6 +4247,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { args.push_back ("-o"); args.push_back (relm.string ().c_str ()); @@ -4242,6 +4296,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { // Clang handles comments and line continuations in the // preprocessed source (it does not have -fpreprocessed). @@ -4297,6 +4352,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { // Note that without -x Clang will treat .i/.ii as fully // preprocessed. @@ -4390,7 +4446,8 @@ namespace build2 // Clang's module compilation requires two separate compiler // invocations. // - if (mod && cid == compiler_id::clang) + if (mod && (cid == compiler_id::clang || + cid == compiler_id::clang_apple)) { // Remove the target file if this fails. If we don't do that, we will // end up with a broken build that is up-to-date. @@ -4452,6 +4509,7 @@ namespace build2 switch (cid) { case id::gcc: return clean_extra (a, t, {".d", x_pext, ".t"}); + case id::clang_apple: case id::clang: return clean_extra (a, t, {".d", x_pext}); case id::msvc: return clean_extra (a, t, {".d", x_pext, ".idb", ".pdb"}); case id::icc: return clean_extra (a, t, {".d"}); diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx index da5be97..627283f 100644 --- a/build2/cc/guess.cxx +++ b/build2/cc/guess.cxx @@ -18,7 +18,7 @@ namespace build2 value () const -> value_type { if (type == "gcc") return gcc; - if (type == "clang") return clang; + if (type == "clang") return variant.empty () ? clang : clang_apple; if (type == "msvc") return msvc; if (type == "icc") return icc; @@ -26,6 +26,17 @@ namespace build2 return gcc; } + string + to_string (compiler_class cl) + { + switch (cl) + { + case compiler_class::gcc: return "gcc"; + case compiler_class::msvc: return "msvc"; + } + return string (); // Never reached. + } + // Pre-guess the compiler type based on the compiler executable name. // Return empty string if can't make a guess (for example, because the // compiler name is a generic 'c++'). Note that it only guesses the type, @@ -501,6 +512,7 @@ namespace build2 return compiler_info { move (gr.path), move (gr.id), + compiler_class::gcc, move (v), move (gr.signature), move (gr.checksum), // Calculated on whole -v output. @@ -617,6 +629,7 @@ namespace build2 return compiler_info { move (gr.path), move (gr.id), + compiler_class::gcc, move (v), move (gr.signature), move (gr.checksum), // Calculated on whole -v output. @@ -836,6 +849,7 @@ namespace build2 return compiler_info { move (gr.path), move (gr.id), + compiler_class::gcc, //@@ TODO: msvc on Windows? move (v), move (gr.signature), cs.string (), @@ -1059,6 +1073,7 @@ namespace build2 return compiler_info { move (gr.path), move (gr.id), + compiler_class::msvc, move (v), move (gr.signature), cs.string (), @@ -1107,6 +1122,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { assert (id.variant.empty () || id.variant == "apple"); r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr)); diff --git a/build2/cc/guess.hxx b/build2/cc/guess.hxx index 4d5d036..2eaa9d7 100644 --- a/build2/cc/guess.hxx +++ b/build2/cc/guess.hxx @@ -37,12 +37,11 @@ namespace build2 std::string string () const {return variant.empty () ? type : type + "-" + variant;} - // Note: does not include variant. - // enum value_type { gcc, clang, + clang_apple, msvc, icc }; @@ -57,6 +56,31 @@ namespace build2 return os << id.string (); } + // Compiler class describes a set of compilers that follow more or less + // the same command line interface. Compilers that don't belong to any of + // the existing classes are in classes of their own (say, Sun CC would be + // on its own if we were to support it). + // + // Currently defined compiler classes: + // + // gcc gcc, clang, clang-apple, icc (on non-Windows) + // msvc msvc, clang-cl, icc (Windows) + // + enum class compiler_class + { + gcc, + msvc + }; + + string + to_string (compiler_class); + + inline ostream& + operator<< (ostream& os, compiler_class cl) + { + return os << to_string (cl); + } + // Compiler version. Here we map the various compiler version formats to // something that resembles the MAJOR.MINOR.PATCH-BUILD form of the // Semantic Versioning. While the MAJOR.MINOR part is relatively @@ -122,6 +146,7 @@ namespace build2 { process_path path; compiler_id id; + compiler_class class_; compiler_version version; string signature; string checksum; diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 2b70fe9..24e81a6 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -220,7 +220,7 @@ namespace build2 // specified, then it should explicitly handle all the targets. // if (i == m->end ()) - fail << "no version for " << ctg << " in bin.lib.version" << + fail << "no version for " << ctgt << " in bin.lib.version" << info << "considere adding " << tsys << "@<ver> or " << tclass << "@<ver>"; @@ -400,7 +400,7 @@ namespace build2 } case otype::a: { - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) e = "lib"; else { @@ -431,7 +431,7 @@ namespace build2 // Add VC's .pdb. // if (ot != otype::a && - cid == compiler_id::msvc && + cclass == compiler_class::msvc && (find_option ("/DEBUG", t, c_loptions, true) || find_option ("/DEBUG", t, x_loptions, true))) { @@ -1363,7 +1363,7 @@ namespace build2 // const string& cs ( cast<string> ( - rs[cid == compiler_id::msvc + rs[cclass == compiler_class::msvc ? var_pool["bin.ld.checksum"] : x_checksum])); @@ -1374,7 +1374,7 @@ namespace build2 // Next check the target. While it might be incorporated into the linker // checksum, it also might not (e.g., VC link.exe). // - if (dd.expect (ctg.string ()) != nullptr) + if (dd.expect (ctgt.string ()) != nullptr) l4 ([&]{trace << "target mismatch forcing update of " << t;}); // Start building the command line. While we don't yet know whether we @@ -1394,7 +1394,7 @@ namespace build2 if (lt.static_library ()) { - if (cid == compiler_id::msvc) ; + if (cclass == compiler_class::msvc) ; else { // If the user asked for ranlib, don't try to do its function with @@ -1406,7 +1406,7 @@ namespace build2 } else { - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { // We are using link.exe directly so don't pass the compiler // options. @@ -1428,7 +1428,7 @@ namespace build2 assert (sys_lib_dirs_extra <= sys_lib_dirs.size ()); append_option_values ( args, - cid == compiler_id::msvc ? "/LIBPATH:" : "-L", + cclass == compiler_class::msvc ? "/LIBPATH:" : "-L", sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (), [] (const dir_path& d) {return d.string ().c_str ();}); @@ -1442,7 +1442,7 @@ namespace build2 auto l (t["bin.rpath"]); if (l && !l->empty ()) - fail << ctg << " does not support rpath"; + fail << ctgt << " does not support rpath"; } else { @@ -1640,9 +1640,9 @@ namespace build2 { ld = &cast<process_path> (rs["bin.ar.path"]); - switch (cid) + switch (cclass) { - case compiler_id::msvc: + case compiler_class::msvc: { // lib.exe has /LIBPATH but it's not clear/documented what it's // used for. Perhaps for link-time code generation (/LTCG)? If @@ -1670,9 +1670,9 @@ namespace build2 // The options are usually similar enough to handle executables // and shared libraries together. // - switch (cid) + switch (cclass) { - case compiler_id::msvc: + case compiler_class::msvc: { // Using link.exe directly. // @@ -1767,7 +1767,7 @@ namespace build2 args.push_back (out.c_str ()); break; } - default: + case compiler_class::gcc: { ld = &cpath; @@ -1929,7 +1929,7 @@ namespace build2 // something like this) we are going to redirect stdout to stderr. For // sane compilers this should be harmless. // - bool filter (cid == compiler_id::msvc && !lt.static_library ()); + bool filter (cclass == compiler_class::msvc && !lt.static_library ()); process pr (*ld, args.data (), 0, (filter ? -1 : 2)); diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index badd0ef..92a04a7 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -122,6 +122,8 @@ namespace build2 rs.assign (x_id_type) = ci.id.type; rs.assign (x_id_variant) = ci.id.variant; + rs.assign (x_class) = to_string (ci.class_); + rs.assign (x_version) = ci.version.string; rs.assign (x_version_major) = ci.version.major; rs.assign (x_version_minor) = ci.version.minor; @@ -159,15 +161,20 @@ namespace build2 dir_paths lib_dirs; dir_paths inc_dirs; - if (ci.id.value () == compiler_id::msvc) - { - lib_dirs = msvc_library_search_paths (ci.path, rs); - inc_dirs = msvc_header_search_paths (ci.path, rs); - } - else + switch (ci.class_) { - lib_dirs = gcc_library_search_paths (ci.path, rs); - inc_dirs = gcc_header_search_paths (ci.path, rs); + case compiler_class::gcc: + { + lib_dirs = gcc_library_search_paths (ci.path, rs); + inc_dirs = gcc_header_search_paths (ci.path, rs); + break; + } + case compiler_class::msvc: + { + lib_dirs = msvc_library_search_paths (ci.path, rs); + inc_dirs = msvc_header_search_paths (ci.path, rs); + break; + } } sys_lib_dirs_extra = lib_dirs.size (); diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx index a9e3a34..13ba401 100644 --- a/build2/cc/pkgconfig.cxx +++ b/build2/cc/pkgconfig.cxx @@ -859,7 +859,7 @@ namespace build2 if (!lops.empty ()) { - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { // Translate -L to /LIBPATH. // @@ -1221,7 +1221,7 @@ namespace build2 // n = l.path ().leaf ().base ().string (); - if (cid != compiler_id::msvc) + if (cclass != compiler_class::msvc) strip_lib (); } diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index 1ba452b..5b568fc 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -21,6 +21,7 @@ namespace build2 namespace cxx { using cc::compiler_id; + using cc::compiler_class; using cc::compiler_info; class config_module: public cc::config_module @@ -43,7 +44,8 @@ namespace build2 { strings r; - auto id (ci.id.value ()); + compiler_id::value_type id (ci.id.value ()); + compiler_class cl (ci.class_); uint64_t mj (ci.version.major); uint64_t mi (ci.version.minor); uint64_t p (ci.version.patch); @@ -89,8 +91,9 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { - // Re-map Apple versions to vanilla Clang based on the following + // Remap Apple versions to vanilla Clang based on the following // release point: // // 5.1 -> 3.4 @@ -99,7 +102,7 @@ namespace build2 // Note that this mapping is also used to enable experimental // features below. // - if (ci.id.variant == "apple") + if (id == compiler_id::clang_apple) { if (mj >= 6) {mj = 3; mi = 5;} else if (mj == 5 && mi >= 1) {mj = 3; mi = 4;} @@ -170,6 +173,7 @@ namespace build2 break; } case compiler_id::clang: + case compiler_id::clang_apple: { // Enable starting with Clang 6.0.0. // @@ -196,9 +200,9 @@ namespace build2 { // Otherwise translate the standard value. // - switch (id) + switch (cl) { - case compiler_id::msvc: + case compiler_class::msvc: { // C++ standard-wise, with VC you got what you got up until 14u2. // Starting with 14u3 there is now the /std: switch which defaults @@ -251,9 +255,7 @@ namespace build2 } break; } - case compiler_id::gcc: - case compiler_id::clang: - case compiler_id::icc: + case compiler_class::gcc: { // Translate 11 to 0x, 14 to 1y, 17 to 1z, and 20 to 2a for // compatibility with older versions of the compilers. @@ -377,6 +379,8 @@ namespace build2 v.insert<string> ("cxx.id.type"), v.insert<string> ("cxx.id.variant"), + v.insert<string> ("cxx.class"), + v.insert<string> ("cxx.version"), v.insert<uint64_t> ("cxx.version.major"), v.insert<uint64_t> ("cxx.version.minor"), @@ -497,7 +501,7 @@ namespace build2 "cxx.uninstall", cm.ci.id.value (), - cm.ci.id.variant, + cm.ci.class_, cm.ci.version.major, cm.ci.version.minor, cast<process_path> (rs[cm.x_path]), |