From 0d34b2f7692aba066213c038b810623c216b6980 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 27 Nov 2017 11:42:35 +0200 Subject: Add {c,cxx}.class variables 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) --- build2/buildfile | 2 +- build2/c/init.cxx | 13 +- build2/cc/common.cxx | 10 +- build2/cc/common.hxx | 16 +- build2/cc/compile.cxx | 518 +++++++++++++++++++++++++++--------------------- build2/cc/guess.cxx | 18 +- build2/cc/guess.hxx | 29 ++- build2/cc/link.cxx | 30 +-- build2/cc/module.cxx | 23 ++- build2/cc/pkgconfig.cxx | 4 +- 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 ("c.id.type"), v.insert ("c.id.variant"), + v.insert ("c.class"), + v.insert ("c.version"), v.insert ("c.version.major"), v.insert ("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 (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 (rs["bin.ld.path"])); @@ -899,7 +899,7 @@ namespace build2 dir_path d; - if (cid == compiler_id::msvc) + if (cclass == compiler_class::msvc) { // /LIBPATH: (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 (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 (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 ()) + { + // 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 ()) - { - // 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 ()) - { - 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 ()) + { + 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 << "@ or " << tclass << "@"; @@ -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 ( - 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 (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 ("cxx.id.type"), v.insert ("cxx.id.variant"), + v.insert ("cxx.class"), + v.insert ("cxx.version"), v.insert ("cxx.version.major"), v.insert ("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 (rs[cm.x_path]), -- cgit v1.1