diff options
Diffstat (limited to 'build2/cc/compile.cxx')
-rw-r--r-- | build2/cc/compile.cxx | 518 |
1 files changed, 288 insertions, 230 deletions
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"}); |