From 0d6f835ffb582296d24a8d1dd479e2703e075ee3 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 18 Oct 2019 14:05:03 +0200 Subject: Add ability to specify "compiler mode" options as part of config.{c,cxx} Such options are (normally) not overridden by buildfiles and are passed last (after cc.coptions and {c,cxx}.coptions) in the resulting command lines. They are also cross-hinted between config.c and config.cxx. For example: $ b config.cxx="g++ -m64" --- libbuild2/c/init.cxx | 4 +- libbuild2/cc/common.hxx | 5 +- libbuild2/cc/compile-rule.cxx | 76 ++++++----- libbuild2/cc/compile-rule.hxx | 12 +- libbuild2/cc/gcc.cxx | 12 +- libbuild2/cc/guess.cxx | 127 ++++++++++++++----- libbuild2/cc/guess.hxx | 24 ++-- libbuild2/cc/init.cxx | 10 ++ libbuild2/cc/link-rule.cxx | 20 +-- libbuild2/cc/module.cxx | 284 ++++++++++++++++++++++++------------------ libbuild2/cc/msvc.cxx | 6 +- libbuild2/cxx/init.cxx | 4 +- libbuild2/utility.cxx | 16 ++- libbuild2/utility.hxx | 29 +++-- libbuild2/utility.ixx | 8 +- 15 files changed, 402 insertions(+), 235 deletions(-) (limited to 'libbuild2') diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index bec20b5..893d285 100644 --- a/libbuild2/c/init.cxx +++ b/libbuild2/c/init.cxx @@ -159,7 +159,7 @@ namespace build2 // Note: some overridable, some not. // - v.insert ("config.c", true), + v.insert ("config.c", true), v.insert ("config.c.id", true), v.insert ("config.c.version", true), v.insert ("config.c.target", true), @@ -172,6 +172,7 @@ namespace build2 nullptr /* config.c.translatable_headers */, v.insert ("c.path"), + v.insert ("c.mode"), v.insert ("c.sys_lib_dirs"), v.insert ("c.sys_inc_dirs"), @@ -335,6 +336,7 @@ namespace build2 cm.x_info->version.major, cm.x_info->version.minor, cast (rs[cm.x_path]), + cast (rs[cm.x_mode]), cast (rs[cm.x_target]), cm.tstd, diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx index 68546f6..0548d6f 100644 --- a/libbuild2/cc/common.hxx +++ b/libbuild2/cc/common.hxx @@ -56,6 +56,7 @@ namespace build2 const variable* config_x_translatable_headers; const variable& x_path; // Compiler process path. + const variable& x_mode; // Compiler mode options. const variable& x_sys_lib_dirs; // System library search directories. const variable& x_sys_inc_dirs; // System header search directories. @@ -147,6 +148,7 @@ namespace build2 uint64_t cmaj; // x.version.major uint64_t cmin; // x.version.minor const process_path& cpath; // x.path + const strings& cmode; // x.mode (options) const target_triplet& ctgt; // x.target const string& tsys; // x.target.system @@ -204,6 +206,7 @@ namespace build2 compiler_class cl, uint64_t mj, uint64_t mi, const process_path& path, + const strings& mode, const target_triplet& tgt, const strings& std, bool fm, @@ -223,7 +226,7 @@ namespace build2 x_uninstall (uninstall), ctype (ct), cvariant (cv), cclass (cl), cmaj (mj), cmin (mi), - cpath (path), + cpath (path), cmode (mode), ctgt (tgt), tsys (ctgt.system), tclass (ctgt.class_), tstd (std), modules (fm), diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index c29769f..c0f876c 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -884,6 +884,8 @@ namespace build2 cs.append ("-fPIC"); } + append_options (cs, cmode); + if (dd.expect (cs.string ()) != nullptr) l4 ([&]{trace << "options mismatch forcing update of " << t;}); } @@ -2972,6 +2974,8 @@ namespace build2 args.push_back ("/nologo"); + append_options (args, cmode); + // See perform_update() for details on overriding the default // exceptions and runtime. // @@ -3018,13 +3022,16 @@ namespace build2 if (ctype == compiler_type::clang && tsys == "win32-msvc") { - if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) + initializer_list os {"-nostdlib", "-nostartfiles"}; + if (!find_options (os, cmode) && !find_options (os, args)) { args.push_back ("-D_MT"); args.push_back ("-D_DLL"); } } + append_options (args, cmode); + // Setup the dynamic module mapper if needed. // // Note that it's plausible in the future we will use it even if @@ -4089,7 +4096,7 @@ namespace build2 append_options (args, tstd, tstd.size () - (modules && clang ? 1 : 0)); - append_headers (env, args, header_args, a, t, md, dd); + append_header_options (env, args, header_args, a, t, md, dd); switch (cclass) { @@ -4097,6 +4104,8 @@ namespace build2 { args.push_back ("/nologo"); + append_options (args, cmode); + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) args.push_back ("/EHsc"); @@ -4122,13 +4131,16 @@ namespace build2 if (ctype == compiler_type::clang && tsys == "win32-msvc") { - if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) + initializer_list os {"-nostdlib", "-nostartfiles"}; + if (!find_options (os, cmode) && !find_options (os, args)) { args.push_back ("-D_MT"); args.push_back ("-D_DLL"); } } + append_options (args, cmode); + args.push_back ("-E"); append_lang_options (args, md); @@ -4659,8 +4671,8 @@ namespace build2 // handle this: after match all our prerequisite BMIs will have their // prerequisite BMIs known, recursively. The only bit that is missing is // the re-export flag of some sorts. As well as deciding where to handle - // it: here or in append_modules(). After some meditation it became - // clear handling it here will be simpler: we need to weed out + // it: here or in append_module_options(). After some meditation it + // became clear handling it here will be simpler: we need to weed out // duplicates for which we can re-use the imports vector. And we may // also need to save this "flattened" list of modules in depdb. // @@ -4678,8 +4690,8 @@ namespace build2 // (with all the re-exported by us at the back), we will go over them // and copy all of their re-exported bmi{}s (using the position we // saved on step #1). The end result will be a recursively-explored - // list of imported bmi{}s that append_modules() can simply convert - // to the list of options. + // list of imported bmi{}s that append_module_options() can simply + // convert to the list of options. // // One issue with this approach is that these copied targets will be // executed which means we need to adjust their dependent counts @@ -5363,16 +5375,16 @@ namespace build2 // // Note that this function is called for both full preprocessing and // compilation proper and in the latter case it is followed by a call - // to append_modules(). + // to append_module_options(). // void compile_rule:: - append_headers (environment&, - cstrings& args, - small_vector& stor, - action, - const file&, - const match_data& md, - const path& dd) const + append_header_options (environment&, + cstrings& args, + small_vector& stor, + action, + const file&, + const match_data& md, + const path& dd) const { switch (ctype) { @@ -5404,16 +5416,17 @@ namespace build2 // Append module-related options. // // Note that this function is only called for the compilation proper and - // after a call to append_headers() (so watch out for duplicate options). + // after a call to append_header_options() (so watch out for duplicate + // options). // void compile_rule:: - append_modules (environment& env, - cstrings& args, - small_vector& stor, - action a, - const file& t, - const match_data& md, - const path& dd) const + append_module_options (environment& env, + cstrings& args, + small_vector& stor, + action a, + const file& t, + const match_data& md, + const path& dd) const { unit_type ut (md.type); const module_positions& ms (md.modules); @@ -5428,7 +5441,7 @@ namespace build2 // // Note that it is also used to specify the output BMI file. // - if (md.headers == 0 && // Done in append_headers()? + if (md.headers == 0 && // In append_header_options()? (ms.start != 0 || ut == unit_type::module_iface || ut == unit_type::module_header)) @@ -5686,6 +5699,8 @@ namespace build2 args.push_back ("/nologo"); + append_options (args, cmode); + // While we want to keep the low-level build as "pure" as possible, // the two misguided defaults, exceptions and runtime, just have to be // fixed. Otherwise the default build is pretty much unusable. But we @@ -5722,8 +5737,8 @@ namespace build2 msvc_sanitize_cl (args); - append_headers (env, args, header_args, a, t, md, md.dd); - append_modules (env, args, module_args, a, t, md, md.dd); + append_header_options (env, args, header_args, a, t, md, md.dd); + append_module_options (env, args, module_args, a, t, md, md.dd); // The presence of /Zi or /ZI causes the compiler to write debug info // to the .pdb file. By default it is a shared file called vcNN.pdb @@ -5803,7 +5818,8 @@ namespace build2 // Clang's MSVC.cpp will not link the default runtime if either // -nostdlib or -nostartfiles is specified. Let's do the same. // - if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) + initializer_list os {"-nostdlib", "-nostartfiles"}; + if (!find_options (os, cmode) && !find_options (os, args)) { args.push_back ("-D_MT"); args.push_back ("-D_DLL"); @@ -5846,8 +5862,10 @@ namespace build2 } } - append_headers (env, args, header_args, a, t, md, md.dd); - append_modules (env, args, module_args, a, t, md, md.dd); + append_options (args, cmode); + + append_header_options (env, args, header_args, a, t, md, md.dd); + append_module_options (env, args, module_args, a, t, md, md.dd); // Note: the order of the following options is relied upon below. // diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx index 4c74016..1b9d9cc 100644 --- a/libbuild2/cc/compile-rule.hxx +++ b/libbuild2/cc/compile-rule.hxx @@ -160,14 +160,14 @@ namespace build2 make_header_sidebuild (action, const scope&, linfo, const file&) const; void - append_headers (environment&, cstrings&, small_vector&, - action, const file&, - const match_data&, const path&) const; + append_header_options (environment&, cstrings&, small_vector&, + action, const file&, + const match_data&, const path&) const; void - append_modules (environment&, cstrings&, small_vector&, - action, const file&, - const match_data&, const path&) const; + append_module_options (environment&, cstrings&, small_vector&, + action, const file&, + const match_data&, const path&) const; // Compiler-specific language selection option. Return the number of // options (arguments, really) appended. diff --git a/libbuild2/cc/gcc.cxx b/libbuild2/cc/gcc.cxx index 9eb8925..cf0ccdc 100644 --- a/libbuild2/cc/gcc.cxx +++ b/libbuild2/cc/gcc.cxx @@ -31,13 +31,11 @@ namespace build2 { dir_paths r; - cstrings args; - string std; // Storage. - - args.push_back (xc.recall_string ()); + cstrings args {xc.recall_string ()}; append_options (args, rs, c_coptions); append_options (args, rs, x_coptions); append_options (args, tstd); + append_options (args, rs, x_mode); // Compile as. // @@ -182,15 +180,13 @@ namespace build2 // dir_paths r; - cstrings args; - string std; // Storage. - - args.push_back (xc.recall_string ()); + cstrings args {xc.recall_string ()}; append_options (args, rs, c_coptions); append_options (args, rs, x_coptions); append_options (args, tstd); append_options (args, rs, c_loptions); append_options (args, rs, x_loptions); + append_options (args, rs, x_mode); args.push_back ("-print-search-dirs"); args.push_back (nullptr); diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index 6ee13a0..f40bb88 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -155,6 +155,7 @@ namespace build2 static string stdlib (lang xl, const process_path& xp, + const strings& x_mo, const strings* c_po, const strings* x_po, const strings* c_co, const strings* x_co, const char* src) @@ -164,6 +165,7 @@ namespace build2 if (x_po != nullptr) append_options (args, *x_po); if (c_co != nullptr) append_options (args, *c_co); if (x_co != nullptr) append_options (args, *x_co); + append_options (args, x_mo); args.push_back ("-x"); switch (xl) { @@ -784,6 +786,7 @@ namespace build2 guess (const char* xm, lang, const path& xc, + const strings& x_mo, const optional& xi, pre_guess_result& pre, sha256& cs) @@ -945,6 +948,36 @@ namespace build2 run_search_fail (xc); } + // Run the compiler with the specified option (-v, --version, etc; can + // also be NULL) calling the specified function on each trimmed output + // line (see build2::run() for details). + // + // Note that we suppress all the compiler errors because we may be + // trying an unsupported option (but still consider the exit code). + // + // + cstrings args {xp.recall_string ()}; + append_options (args, x_mo); + args.push_back (nullptr); // Placeholder for the option. + args.push_back (nullptr); + + auto run = [&cs, &xp, &args] (const char* o, + auto&& f, + const char* const* env = nullptr, + bool checksum = false) -> guess_result + { + args[args.size () - 2] = o; + + return build2::run ( + 3 /* verbosity */, + process_env (xp, env), + args.data (), + forward (f), + false /* error */, + false /* ignore_exit */, + checksum ? &cs : nullptr); + }; + // Start with -v. This will cover gcc and clang (including clang-cl). // // While icc also writes what may seem like something we can use to @@ -1056,11 +1089,8 @@ namespace build2 // One notable consequence of this is that if the locale changes // (e.g., via LC_ALL), then the compiler signature will most likely // change as well because of the translated text. - - // Suppress all the compiler errors because we may be trying an - // unsupported option (but still consider the exit code). // - r = run (3, xp, "-v", f, false, false, &cs); + r = run ("-v", f, nullptr /* env */, true /* checksum */); if (r.empty ()) { @@ -1120,7 +1150,7 @@ namespace build2 return guess_result (); }; - r = run (3, xp, "--version", f, false); + r = run ("--version", f); if (r.empty ()) { @@ -1171,9 +1201,11 @@ namespace build2 // going to unset these variables for our test (interestingly, only CL // seem to cause the problem but let's unset both, for good measure). // + // This is also the reason why we don't pass the mode options. + // const char* env[] = {"CL=", "_CL_=", nullptr}; - r = run (3, process_env (xp, env), f, false); + r = build2::run (3, process_env (xp, env), f, false); if (r.empty ()) { @@ -1426,6 +1458,7 @@ namespace build2 const path& xc, const string* xv, const string* xt, + const strings&, const strings*, const strings*, const strings*, const strings*, const strings*, const strings*, @@ -1649,6 +1682,7 @@ namespace build2 const path& xc, const string* xv, const string* xt, + const strings& x_mo, const strings* c_po, const strings* x_po, const strings* c_co, const strings* x_co, const strings*, const strings*, @@ -1747,9 +1781,11 @@ namespace build2 if (xt == nullptr) { - cstrings args {xp.recall_string (), "-print-multiarch"}; + cstrings args {xp.recall_string ()}; if (c_co != nullptr) append_options (args, *c_co); if (x_co != nullptr) append_options (args, *x_co); + append_options (args, x_mo); + args.push_back ("-print-multiarch"); // Note: position relied upon. args.push_back (nullptr); // The output of both -print-multiarch and -dumpmachine is a single @@ -1764,7 +1800,7 @@ namespace build2 l5 ([&]{trace << xc << " doesn's support -print-multiarch, " << "falling back to -dumpmachine";}); - args[1] = "-dumpmachine"; + args[args.size () - 2] = "-dumpmachine"; t = run (3, xp, args.data (), f, false); } @@ -1797,9 +1833,10 @@ namespace build2 // documentation says that you should usually specify -lgcc. // string rt ("libgcc"); - string csl (tt.system == "mingw32" - ? "msvc" - : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src)); + string csl ( + tt.system == "mingw32" + ? "msvc" + : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, c_stdlib_src)); string xsl; switch (xl) { @@ -1813,7 +1850,7 @@ namespace build2 "#include \n" "stdlib:=\"libstdc++\" \n"; - xsl = stdlib (xl, xp, c_po, x_po, c_co, x_co, src); + xsl = stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, src); break; } } @@ -1847,6 +1884,7 @@ namespace build2 static clang_msvc_info guess_clang_msvc (lang xl, const process_path& xp, + const strings& x_mo, const strings* c_co, const strings* x_co, bool cl) { @@ -1855,6 +1893,7 @@ namespace build2 cstrings args {xp.recall_string ()}; if (c_co != nullptr) append_options (args, *c_co); if (x_co != nullptr) append_options (args, *x_co); + append_options (args, x_mo); if (cl) { @@ -2060,6 +2099,7 @@ namespace build2 const path& xc, const string* xv, const string* xt, + const strings& x_mo, const strings* c_po, const strings* x_po, const strings* c_co, const strings* x_co, const strings* c_lo, const strings* x_lo, @@ -2222,9 +2262,10 @@ namespace build2 if (xt == nullptr) { cstrings args {xp.recall_string ()}; - args.push_back (cl ? "/clang:-dumpmachine" : "-dumpmachine"); if (c_co != nullptr) append_options (args, *c_co); if (x_co != nullptr) append_options (args, *x_co); + append_options (args, x_mo); + args.push_back (cl ? "/clang:-dumpmachine" : "-dumpmachine"); args.push_back (nullptr); // The output of -dumpmachine is a single line containing just the @@ -2266,7 +2307,7 @@ namespace build2 // (plus a couple of other useful bits like the VC installation // directory and Platform SDK). // - clang_msvc_info mi (guess_clang_msvc (xl, xp, c_co, x_co, cl)); + clang_msvc_info mi (guess_clang_msvc (xl, xp, x_mo, c_co, x_co, cl)); // Keep the CPU and replace the rest. // @@ -2352,8 +2393,9 @@ namespace build2 }; const string* o; - if ((o = find_rtlib (x_lo)) != nullptr || - (o = find_rtlib (c_lo)) != nullptr) + if ((o = find_rtlib (&x_mo)) != nullptr || + (o = find_rtlib (x_lo)) != nullptr || + (o = find_rtlib (c_lo)) != nullptr) { rt = string (*o, 8); } @@ -2363,9 +2405,10 @@ namespace build2 else /* Mac OS, etc. */ rt = "compiler-rt"; } - string csl (tt.system == "win32-msvc" || tt.system == "mingw32" - ? "msvc" - : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src)); + string csl ( + tt.system == "win32-msvc" || tt.system == "mingw32" + ? "msvc" + : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, c_stdlib_src)); string xsl; switch (xl) @@ -2394,7 +2437,7 @@ namespace build2 xsl = tt.system == "win32-msvc" ? "msvcp" - : stdlib (xl, xp, c_po, x_po, c_co, x_co, src); + : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, src); break; } } @@ -2424,11 +2467,15 @@ namespace build2 const path& xc, const string* xv, const string* xt, + const strings& x_mo, const strings* c_po, const strings* x_po, const strings* c_co, const strings* x_co, const strings*, const strings*, guess_result&& gr, sha256&) { + //@@ TODO: this should be reviewed/revised if/when we get access + // to more recent ICC versions. + const process_path& xp (gr.path); // Extract the version. If the version has the fourth component, then @@ -2470,6 +2517,8 @@ namespace build2 // The -V output is sent to STDERR. // + // @@ TODO: running without the mode options. + // s = run (3, xp, "-V", f, false); if (s.empty ()) @@ -2575,6 +2624,8 @@ namespace build2 // "Intel(R)" "64" // "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux) // + // @@ TODO: why can't we combine it with the previous -V run? + // string t, ot; if (xt == nullptr) @@ -2585,9 +2636,11 @@ namespace build2 dr << info << "use config." << xm << ".target to override"; }); - cstrings args {xp.recall_string (), "-V"}; + cstrings args {xp.recall_string ()}; if (c_co != nullptr) append_options (args, *c_co); if (x_co != nullptr) append_options (args, *x_co); + append_options (args, x_mo); + args.push_back ("-V"); args.push_back (nullptr); // The -V output is sent to STDERR. @@ -2637,6 +2690,8 @@ namespace build2 // in the future. So instead we are going to use -dumpmachine and // substitute the CPU. // + // @@ TODO: running without the mode options. + // { auto f = [] (string& l, bool) {return move (l);}; t = run (3, xp, "-dumpmachine", f); @@ -2677,9 +2732,10 @@ namespace build2 // Linux/GCC. // string rt (tt.system == "win32-msvc" ? "msvc" : "libgcc"); - string csl (tt.system == "win32-msvc" - ? "msvc" - : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src)); + string csl ( + tt.system == "win32-msvc" + ? "msvc" + : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, c_stdlib_src)); string xsl; switch (xl) { @@ -2722,6 +2778,7 @@ namespace build2 const string* xis, const string* xv, const string* xt, + const strings& x_mo, const strings* c_po, const strings* x_po, const strings* c_co, const strings* x_co, const strings* c_lo, const strings* x_lo) @@ -2734,6 +2791,7 @@ namespace build2 cs.append (static_cast (xl)); cs.append (xc.string ()); if (xis != nullptr) cs.append (*xis); + append_options (cs, x_mo); if (c_po != nullptr) append_options (cs, *c_po); if (x_po != nullptr) append_options (cs, *x_po); if (c_co != nullptr) append_options (cs, *c_co); @@ -2773,7 +2831,7 @@ namespace build2 if (pre.type != invalid_compiler_type) { - gr = guess (xm, xl, xc, xi, pre, cs); + gr = guess (xm, xl, xc, x_mo, xi, pre, cs); if (gr.empty ()) { @@ -2789,7 +2847,7 @@ namespace build2 } if (gr.empty ()) - gr = guess (xm, xl, xc, xi, pre, cs); + gr = guess (xm, xl, xc, x_mo, xi, pre, cs); if (gr.empty ()) fail << "unable to guess " << xl << " compiler type of " << xc << @@ -2797,6 +2855,7 @@ namespace build2 compiler_info (*gf) ( const char*, lang, const path&, const string*, const string*, + const strings&, const strings*, const strings*, const strings*, const strings*, const strings*, const strings*, @@ -2815,7 +2874,7 @@ namespace build2 } compiler_info r (gf (xm, xl, xc, xv, xt, - c_po, x_po, c_co, x_co, c_lo, x_lo, + x_mo, c_po, x_po, c_co, x_co, c_lo, x_lo, move (gr), cs)); // By default use the signature line to generate the checksum. @@ -2893,8 +2952,11 @@ namespace build2 return (cache[key] = move (r)); } - path - guess_default (lang xl, const string& cid, const string& pat) + strings + guess_default (lang xl, + const string& cid, + const string& pat, + const strings& mode) { compiler_id id (cid); const char* s (nullptr); @@ -2937,7 +2999,12 @@ namespace build2 } } - return path (apply_pattern (s, pat)); + strings r; + r.reserve (mode.size () + 1); + r.push_back (apply_pattern (s, pat)); + r.insert (r.end (), mode.begin (), mode.end ()); + + return r; } } } diff --git a/libbuild2/cc/guess.hxx b/libbuild2/cc/guess.hxx index d9172dc..d93aaf9 100644 --- a/libbuild2/cc/guess.hxx +++ b/libbuild2/cc/guess.hxx @@ -239,23 +239,27 @@ namespace build2 // that most of it will be the same, at least for C and C++. // const compiler_info& - 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). + guess (const char* xm, // Module (for var 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& x_mode, // Compiler mode options. 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, compiler id, and optionally an (empty) pattern, - // return an appropriate default compiler path. + // Given a language, compiler id, optional (empty) pattern, and mode + // return an appropriate default config.x value (compiler path and mode) // // For example, for (lang::cxx, gcc, *-4.9) we will get g++-4.9. // - path - guess_default (lang, const string& cid, const string& pattern); + strings + guess_default (lang, + const string& cid, + const string& pattern, + const strings& mode); } } diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx index f45a1bf..ac5823b 100644 --- a/libbuild2/cc/init.cxx +++ b/libbuild2/cc/init.cxx @@ -105,6 +105,7 @@ namespace build2 v.insert ("config.cc.id"); v.insert ("config.cc.hinter"); // Hinting module. v.insert ("config.cc.pattern"); + v.insert ("config.cc.mode"); v.insert ("config.cc.target"); // Compiler runtime and C standard library. @@ -209,6 +210,15 @@ namespace build2 cast_empty (h["config.cc.pattern"]); } + // config.cc.mode + // + { + // This value could be hinted. + // + rs.assign ("cc.mode") = + cast_empty (h["config.cc.mode"]); + } + // cc.runtime // cc.stdlib // diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index f10bd42..16a4ce7 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -2237,12 +2237,11 @@ namespace build2 } else { - if (tsys == "win32-msvc") - { - // We are using link.exe directly so don't pass the compiler - // options. - } - else + // Are we using the compiler or the linker (e.g., link.exe) directly? + // + bool ldc (tsys != "win32-msvc"); + + if (ldc) { append_options (args, t, c_coptions); append_options (args, t, x_coptions); @@ -2364,6 +2363,9 @@ namespace build2 sargs.push_back ("-Wl,-rpath-link," + p.string ()); } } + + if (ldc) + append_options (args, cmode); } // All the options should now be in. Hash them and compare with the db. @@ -2560,8 +2562,10 @@ namespace build2 // See the runtime selection code in the compile rule for details // on what's going on here. // - if (!find_options ({"-nostdlib", "-nostartfiles"}, t, c_coptions) && - !find_options ({"-nostdlib", "-nostartfiles"}, t, x_coptions)) + initializer_list os {"-nostdlib", "-nostartfiles"}; + if (!find_options (os, cmode) && + !find_options (os, t, c_coptions) && + !find_options (os, t, x_coptions)) { args.push_back ("/DEFAULTLIB:msvcrt"); args.push_back ("/DEFAULTLIB:oldnames"); diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 641da5e..5f8652e 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -51,89 +51,114 @@ namespace build2 // config.x // - - // Normally we will have a persistent configuration and computing the - // default value every time will be a waste. So try without a default - // first. - // - auto p (config::omitted (rs, config_x)); - - if (!p.first) + strings mode; { - // If there is a config.x value for one of the modules that can hint - // us the toolchain, load it's .guess module. This makes sure that the - // order in which we load the modules is unimportant and that the user - // can specify the toolchain using any of the config.x values. + // Normally we will have a persistent configuration and computing the + // default value every time will be a waste. So try without a default + // first. // - if (!cc_loaded) + auto p (config::omitted (rs, config_x)); + + if (!p.first) { - for (const char* const* pm (x_hinters); *pm != nullptr; ++pm) + // If there is a config.x value for one of the modules that can hint + // us the toolchain, load it's .guess module. This makes sure that + // the order in which we load the modules is unimportant and that + // the user can specify the toolchain using any of the config.x + // values. + // + if (!cc_loaded) { - string m (*pm); + for (const char* const* pm (x_hinters); *pm != nullptr; ++pm) + { + string m (*pm); - // Must be the same as in module's init(). - // - const variable& v (vp.insert ("config." + m, true)); + // Must be the same as in module's init(). + // + const variable& v (vp.insert ("config." + m, true)); - if (rs[v].defined ()) - { - load_module (rs, rs, m + ".guess", loc); - cc_loaded = true; - break; + if (rs[v].defined ()) + { + load_module (rs, rs, m + ".guess", loc); + cc_loaded = true; + break; + } } } + + // If cc.core.config is already loaded then use its toolchain id, + // (optional) pattern, and mode to guess an appropriate default + // (e.g., for {gcc, *-4.9 -m64} we will get g++-4.9 -m64). + // + strings d; + + if (cc_loaded) + d = guess_default (x_lang, + cast (rs["cc.id"]), + cast (rs["cc.pattern"]), + cast (rs["cc.mode"])); + else + { + // Note that we don't have the default mode: it doesn't feel + // correct to default to, say, -m64 simply because that's how + // build2 was built. + // + d.push_back (x_default); + + if (d.front ().empty ()) + fail << "not built with default " << x_lang << " compiler" << + info << "use " << config_x << " to specify"; + } + + // If this value was hinted, save it as commented out so that if the + // user changes the source of the pattern/mode, this one will get + // updated as well. + // + p = config::required (rs, + config_x, + move (d), + false, + cc_loaded ? config::save_commented : 0); } - // If cc.core.config is already loaded then use its toolchain id and - // (optional) pattern to guess an appropriate default (e.g., for {gcc, - // *-4.9} we will get g++-4.9). + // Split the value into the compiler path and mode. // - path d; + const strings& v (cast (*p.first)); - if (cc_loaded) - d = guess_default (x_lang, - cast (rs["cc.id"]), - cast (rs["cc.pattern"])); - else - { - d = path (x_default); + path xc; + try { xc = path (v.front ()); } catch (const invalid_path&) {} - if (d.empty ()) - fail << "not built with default " << x_lang << " compiler" << - info << "use config." << x << " to specify"; - } + if (xc.empty ()) + fail << "invalid path '" << v.front () << "' in " << config_x; - // If this value was hinted, save it as commented out so that if the - // user changes the source of the pattern, this one will get updated - // as well. + mode.assign (++v.begin (), v.end ()); + + // Figure out which compiler we are dealing with, its target, etc. + // + // Note that we could allow guess() to modify mode to support + // imaginary options (such as /MACHINE for cl.exe). Though it's not + // clear what cc.mode would contain (original or modified). // - p = config::required (rs, - config_x, - d, - false, - cc_loaded ? config::save_commented : 0); + x_info = &build2::cc::guess ( + x, x_lang, move (xc), + cast_null (config::omitted (rs, config_x_id).first), + cast_null (config::omitted (rs, config_x_version).first), + cast_null (config::omitted (rs, config_x_target).first), + mode, + cast_null (rs[config_c_poptions]), + cast_null (rs[config_x_poptions]), + cast_null (rs[config_c_coptions]), + cast_null (rs[config_x_coptions]), + cast_null (rs[config_c_loptions]), + cast_null (rs[config_x_loptions])); + + new_ = p.second; } - // Figure out which compiler we are dealing with, its target, etc. - // - x_info = &build2::cc::guess ( - x, - x_lang, - cast (*p.first), - cast_null (config::omitted (rs, config_x_id).first), - cast_null (config::omitted (rs, config_x_version).first), - cast_null (config::omitted (rs, config_x_target).first), - cast_null (rs[config_c_poptions]), - cast_null (rs[config_x_poptions]), - cast_null (rs[config_c_coptions]), - cast_null (rs[config_x_coptions]), - cast_null (rs[config_c_loptions]), - cast_null (rs[config_x_loptions])); - const compiler_info& xi (*x_info); - // Split/canonicalize the target. First see if the user asked us to - // use config.sub. + // Split/canonicalize the target. First see if the user asked us to use + // config.sub. // target_triplet tt; { @@ -167,6 +192,9 @@ namespace build2 // Assign values to variables that describe the compiler. // + rs.assign (x_path) = process_path (xi.path, false /* init */); + const strings& xm (cast (rs.assign (x_mode) = move (mode))); + rs.assign (x_id) = xi.id.string (); rs.assign (x_id_type) = to_string (xi.id.type); rs.assign (x_id_variant) = xi.id.variant; @@ -187,6 +215,9 @@ namespace build2 assign_version (&x_variant_version, xi.variant_version ? &*xi.variant_version : nullptr); + rs.assign (x_signature) = xi.signature; + rs.assign (x_checksum) = xi.checksum; + // Also enter as x.target.{cpu,vendor,system,version,class} for // convenience of access. // @@ -203,8 +234,6 @@ namespace build2 if (!x_stdlib.alias (c_stdlib)) rs.assign (x_stdlib) = xi.x_stdlib; - new_ = p.second; - // Load cc.core.guess. // if (!cc_loaded) @@ -222,6 +251,9 @@ namespace build2 if (!xi.pattern.empty ()) h.assign ("config.cc.pattern") = xi.pattern; + if (!xm.empty ()) + h.assign ("config.cc.mode") = xm; + h.assign (c_runtime) = xi.runtime; h.assign (c_stdlib) = xi.c_stdlib; @@ -262,6 +294,9 @@ namespace build2 // g++-7 vs gcc kind of mistakes. So now we warn since even if // intentional, it is still probably a bad idea. // + // Note also that it feels right to allow different modes (think + // -fexceptions for C or -fno-rtti for C++). + // check (cast (rs["cc.pattern"]), cast (rs[x_pattern]), "toolchain pattern", @@ -299,6 +334,37 @@ namespace build2 const compiler_info& xi (*x_info); const target_triplet& tt (cast (rs[x_target])); + // config.x.{p,c,l}options + // config.x.libs + // + // These are optional. We also merge them into the corresponding + // x.* variables. + // + // The merging part gets a bit tricky if this module has already + // been loaded in one of the outer scopes. By doing the straight + // append we would just be repeating the same options over and + // over. So what we are going to do is only append to a value if + // it came from this scope. Then the usage for merging becomes: + // + // x.coptions = # Note: '='. + // using x + // x.coptions += # Note: '+='. + // + rs.assign (x_poptions) += cast_null ( + config::optional (rs, config_x_poptions)); + + rs.assign (x_coptions) += cast_null ( + config::optional (rs, config_x_coptions)); + + rs.assign (x_loptions) += cast_null ( + config::optional (rs, config_x_loptions)); + + rs.assign (x_aoptions) += cast_null ( + config::optional (rs, config_x_aoptions)); + + rs.assign (x_libs) += cast_null ( + config::optional (rs, config_x_libs)); + // config.x.std overrides x.std // { @@ -318,6 +384,22 @@ namespace build2 tstd = translate_std (xi, rs, v); } + // config.x.translatable_header + // + // It's still fuzzy whether specifying (or maybe tweaking) this list in + // the configuration will be a common thing to do so for now we use + // omitted. It's also probably too early to think whether we should have + // the cc.* version and what the semantics should be. + // + if (x_translatable_headers != nullptr) + { + lookup l (config::omitted (rs, *config_x_translatable_headers).first); + + // @@ MODHDR: if(modules) ? + // + rs.assign (x_translatable_headers) += cast_null (l); + } + // Extract system header/library search paths from the compiler and // determine if we need any additional search paths. // @@ -443,12 +525,27 @@ namespace build2 // if (verb >= (new_ ? 2 : 3)) { + const strings& mode (cast (rs[x_mode])); + diag_record dr (text); { dr << x << ' ' << project (rs) << '@' << rs << '\n' - << " " << left << setw (11) << x << xi.path << '\n' - << " id " << xi.id << '\n' + << " " << left << setw (11) << x << xi.path << '\n'; + } + + if (!mode.empty ()) + { + dr << " mode "; // One space short. + + for (const string& o: mode) + dr << ' ' << o; + + dr << '\n'; + } + + { + dr << " id " << xi.id << '\n' << " version " << xi.version.string << '\n' << " major " << xi.version.major << '\n' << " minor " << xi.version.minor << '\n' @@ -525,60 +622,9 @@ namespace build2 } } - rs.assign (x_path) = process_path (xi.path, false /* init */); rs.assign (x_sys_lib_dirs) = move (lib_dirs); rs.assign (x_sys_inc_dirs) = move (inc_dirs); - rs.assign (x_signature) = xi.signature; - rs.assign (x_checksum) = xi.checksum; - - // config.x.{p,c,l}options - // config.x.libs - // - // These are optional. We also merge them into the corresponding - // x.* variables. - // - // The merging part gets a bit tricky if this module has already - // been loaded in one of the outer scopes. By doing the straight - // append we would just be repeating the same options over and - // over. So what we are going to do is only append to a value if - // it came from this scope. Then the usage for merging becomes: - // - // x.coptions = # Note: '='. - // using x - // x.coptions += # Note: '+='. - // - rs.assign (x_poptions) += cast_null ( - config::optional (rs, config_x_poptions)); - - rs.assign (x_coptions) += cast_null ( - config::optional (rs, config_x_coptions)); - - rs.assign (x_loptions) += cast_null ( - config::optional (rs, config_x_loptions)); - - rs.assign (x_aoptions) += cast_null ( - config::optional (rs, config_x_aoptions)); - - rs.assign (x_libs) += cast_null ( - config::optional (rs, config_x_libs)); - - // config.x.translatable_header - // - // It's still fuzzy whether specifying (or maybe tweaking) this list in - // the configuration will be a common thing to do so for now we use - // omitted. It's also probably too early to think whether we should have - // the cc.* version and what the semantics should be. - // - if (x_translatable_headers != nullptr) - { - lookup l (config::omitted (rs, *config_x_translatable_headers).first); - - // @@ MODHDR: if(modules) ? - // - rs.assign (x_translatable_headers) += cast_null (l); - } - // Load cc.core.config. // if (!cast_false (rs["cc.core.config.loaded"])) diff --git a/libbuild2/cc/msvc.cxx b/libbuild2/cc/msvc.cxx index e7251ac..a85e7a0 100644 --- a/libbuild2/cc/msvc.cxx +++ b/libbuild2/cc/msvc.cxx @@ -220,7 +220,8 @@ namespace build2 msvc_header_search_paths (const process_path&, scope&) const { // The compiler doesn't seem to have any built-in paths and all of them - // come from the INCLUDE environment variable. + // either come from the INCLUDE environment variable or are specified + // explicitly on the command line. // @@ VC: how are we going to do this? E.g., cl-14 does this internally. // cl.exe /Be prints INCLUDE. @@ -239,7 +240,8 @@ namespace build2 msvc_library_search_paths (const process_path&, scope&) const { // The linker doesn't seem to have any built-in paths and all of them - // come from the LIB environment variable. + // either come from the LIB environment variable or are specified + // explicitly on the command line. // @@ VC: how are we going to do this? E.g., cl-14 does this internally. // cl.exe /Be prints LIB. diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx index 9bf54eb..6237d99 100644 --- a/libbuild2/cxx/init.cxx +++ b/libbuild2/cxx/init.cxx @@ -391,7 +391,7 @@ namespace build2 // Note: some overridable, some not. // - v.insert ("config.cxx", true), + v.insert ("config.cxx", true), v.insert ("config.cxx.id", true), v.insert ("config.cxx.version", true), v.insert ("config.cxx.target", true), @@ -414,6 +414,7 @@ namespace build2 &v.insert ("config.cxx.translatable_headers", true), v.insert ("cxx.path"), + v.insert ("cxx.mode"), v.insert ("cxx.sys_lib_dirs"), v.insert ("cxx.sys_inc_dirs"), @@ -612,6 +613,7 @@ namespace build2 cm.x_info->version.major, cm.x_info->version.minor, cast (rs[cm.x_path]), + cast (rs[cm.x_mode]), cast (rs[cm.x_target]), cm.tstd, diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx index af4768c..78d2df2 100644 --- a/libbuild2/utility.cxx +++ b/libbuild2/utility.cxx @@ -386,13 +386,17 @@ namespace build2 } bool - find_options (initializer_list os, const lookup& l, bool ic) + find_options (const initializer_list& os, + const lookup& l, + bool ic) { return l && find_options (os, cast (l), ic); } bool - find_options (initializer_list os, const strings& strs, bool ic) + find_options (const initializer_list& os, + const strings& strs, + bool ic) { for (const string& s: strs) for (const char* o: os) @@ -403,7 +407,7 @@ namespace build2 } bool - find_options (initializer_list os, + find_options (const initializer_list& os, const cstrings& cstrs, bool ic) { @@ -447,7 +451,7 @@ namespace build2 } const string* - find_option_prefixes (initializer_list ps, + find_option_prefixes (const initializer_list& ps, const lookup& l, bool ic) { @@ -455,7 +459,7 @@ namespace build2 } const string* - find_option_prefixes (initializer_list ps, + find_option_prefixes (const initializer_list& ps, const strings& strs, bool ic) { @@ -470,7 +474,7 @@ namespace build2 } const char* - find_option_prefixes (initializer_list ps, + find_option_prefixes (const initializer_list& ps, const cstrings& cstrs, bool ic) { diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index beacd2f..0422786 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -607,23 +607,32 @@ namespace build2 // template bool - find_options (initializer_list, + find_options (const initializer_list&, T&, const variable&, bool = false); template bool - find_options (initializer_list, T&, const char*, bool = false); + find_options (const initializer_list&, + T&, + const char*, + bool = false); LIBBUILD2_SYMEXPORT bool - find_options (initializer_list, const lookup&, bool = false); + find_options (const initializer_list&, + const lookup&, + bool = false); LIBBUILD2_SYMEXPORT bool - find_options (initializer_list, const strings&, bool = false); + find_options (const initializer_list&, + const strings&, + bool = false); LIBBUILD2_SYMEXPORT bool - find_options (initializer_list, const cstrings&, bool = false); + find_options (const initializer_list&, + const cstrings&, + bool = false); // As above but look for an option that has the specified prefix. Return the // pointer to option or NULL if not found (thus can be used as bool). @@ -651,29 +660,29 @@ namespace build2 // template const string* - find_option_prefixes (initializer_list, + find_option_prefixes (const initializer_list&, T&, const variable&, bool = false); template const string* - find_option_prefixes (initializer_list, + find_option_prefixes (const initializer_list&, T&, const char*, bool = false); LIBBUILD2_SYMEXPORT const string* - find_option_prefixes (initializer_list, + find_option_prefixes (const initializer_list&, const lookup&, bool = false); LIBBUILD2_SYMEXPORT const string* - find_option_prefixes (initializer_list, + find_option_prefixes (const initializer_list&, const strings&, bool = false); LIBBUILD2_SYMEXPORT const char* - find_option_prefixes (initializer_list, + find_option_prefixes (const initializer_list&, const cstrings&, bool = false); diff --git a/libbuild2/utility.ixx b/libbuild2/utility.ixx index dcfd128..2846756 100644 --- a/libbuild2/utility.ixx +++ b/libbuild2/utility.ixx @@ -101,7 +101,7 @@ namespace build2 template inline bool - find_options (initializer_list os, + find_options (const initializer_list& os, T& s, const variable& var, bool ic) @@ -111,7 +111,7 @@ namespace build2 template inline bool - find_options (initializer_list os, + find_options (const initializer_list& os, T& s, const char* var, bool ic) @@ -135,7 +135,7 @@ namespace build2 template inline const string* - find_option_prefixes (initializer_list ps, + find_option_prefixes (const initializer_list& ps, T& s, const variable& var, bool ic) @@ -145,7 +145,7 @@ namespace build2 template inline const string* - find_option_prefixes (initializer_list ps, + find_option_prefixes (const initializer_list& ps, T& s, const char* var, bool ic) -- cgit v1.1