diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2021-10-01 11:05:49 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2021-10-01 12:04:38 +0200 |
commit | db8336a686a85f0e458acb2d5f1ad442585bfc9a (patch) | |
tree | d0e9aa2ce76eb2f208a5d63a1258cf218af2aacc /libbuild2 | |
parent | d4457a6427401ed4d5c09eba00cac84c5664f250 (diff) |
Add notion of internal scope, translate external -I to -isystem or equivalent
Diffstat (limited to 'libbuild2')
-rw-r--r-- | libbuild2/c/init.cxx | 16 | ||||
-rw-r--r-- | libbuild2/cc/common.hxx | 15 | ||||
-rw-r--r-- | libbuild2/cc/common.ixx | 34 | ||||
-rw-r--r-- | libbuild2/cc/compile-rule.cxx | 272 | ||||
-rw-r--r-- | libbuild2/cc/compile-rule.hxx | 4 | ||||
-rw-r--r-- | libbuild2/cc/guess.cxx | 2 | ||||
-rw-r--r-- | libbuild2/cc/init.cxx | 20 | ||||
-rw-r--r-- | libbuild2/cc/module.cxx | 125 | ||||
-rw-r--r-- | libbuild2/cc/module.hxx | 5 | ||||
-rw-r--r-- | libbuild2/cc/msvc.cxx | 15 | ||||
-rw-r--r-- | libbuild2/cc/pkgconfig.cxx | 5 | ||||
-rw-r--r-- | libbuild2/cxx/init.cxx | 72 | ||||
-rw-r--r-- | libbuild2/utility.hxx | 20 |
13 files changed, 552 insertions, 53 deletions
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index d6622a8..9a62a5e 100644 --- a/libbuild2/c/init.cxx +++ b/libbuild2/c/init.cxx @@ -180,7 +180,12 @@ namespace build2 vp.insert<strings> ("config.c.loptions"), vp.insert<strings> ("config.c.aoptions"), vp.insert<strings> ("config.c.libs"), - nullptr /* config.c.translate_include */, + + // See config.cxx.internal.scope for details. + // + vp.insert<string> ("config.c.internal.scope"), + + nullptr /* config.c.translate_include */, vp.insert<process_path_ex> ("c.path"), vp.insert<strings> ("c.mode"), @@ -197,7 +202,9 @@ namespace build2 vp.insert<strings> ("c.aoptions"), vp.insert<strings> ("c.libs"), - nullptr /* c.translate_include */, + vp.insert<string> ("c.internal.scope"), + + nullptr /* c.translate_include */, vp["cc.poptions"], vp["cc.coptions"], @@ -345,6 +352,8 @@ namespace build2 cm.x_info->class_, cm.x_info->version.major, cm.x_info->version.minor, + cm.x_info->variant_version ? cm.x_info->variant_version->major : 0, + cm.x_info->variant_version ? cm.x_info->variant_version->minor : 0, cast<process_path> (rs[cm.x_path]), cast<strings> (rs[cm.x_mode]), cast<target_triplet> (rs[cm.x_target]), @@ -353,6 +362,9 @@ namespace build2 false, // No C modules yet. false, // No __symexport support since no modules. + cm.internal_scope, + cm.internal_scope_current, + cast<dir_paths> (rs[cm.x_sys_lib_dirs]), cast<dir_paths> (rs[cm.x_sys_hdr_dirs]), cm.x_info->sys_mod_dirs ? &cm.x_info->sys_mod_dirs->first : nullptr, diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx index 64de228..a3cd6b6 100644 --- a/libbuild2/cc/common.hxx +++ b/libbuild2/cc/common.hxx @@ -64,6 +64,7 @@ namespace build2 const variable& config_x_loptions; const variable& config_x_aoptions; const variable& config_x_libs; + const variable& config_x_internal_scope; const variable* config_x_translate_include; const variable& x_path; // Compiler process path. @@ -79,6 +80,7 @@ namespace build2 const variable& x_loptions; const variable& x_aoptions; const variable& x_libs; + const variable& x_internal_scope; const variable* x_translate_include; const variable& c_poptions; // cc.* @@ -163,6 +165,8 @@ namespace build2 compiler_class cclass; // x.class uint64_t cmaj; // x.version.major uint64_t cmin; // x.version.minor + uint64_t cvmaj; // x.variant_version.major (0 if no variant) + uint64_t cvmin; // x.variant_version.minor (0 if no variant) const process_path& cpath; // x.path const strings& cmode; // x.mode (options) @@ -175,6 +179,12 @@ namespace build2 bool modules; // x.features.modules bool symexport; // x.features.symexport + const string* internal_scope; // x.internal.scope + const scope* internal_scope_current; + + const scope* + effective_internal_scope (const scope& bs) const; + build2::cc::importable_headers* importable_headers; // The order of sys_*_dirs is the mode entries first, followed by the @@ -229,12 +239,14 @@ namespace build2 const string& cv, compiler_class cl, uint64_t mj, uint64_t mi, + uint64_t vmj, uint64_t vmi, const process_path& path, const strings& mode, const target_triplet& tgt, const string& env_cs, bool fm, bool fs, + const string* ints, const scope* intsc, const dir_paths& sld, const dir_paths& shd, const dir_paths* smd, @@ -251,11 +263,13 @@ namespace build2 x_uninstall (uninstall), ctype (ct), cvariant (cv), cclass (cl), cmaj (mj), cmin (mi), + cvmaj (vmj), cvmin (vmi), cpath (path), cmode (mode), ctgt (tgt), tsys (ctgt.system), tclass (ctgt.class_), env_checksum (env_cs), modules (fm), symexport (fs), + internal_scope (ints), internal_scope_current (intsc), importable_headers (nullptr), sys_lib_dirs (sld), sys_hdr_dirs (shd), sys_mod_dirs (smd), sys_lib_dirs_mode (slm), sys_hdr_dirs_mode (shm), @@ -412,6 +426,7 @@ namespace build2 } } +#include <libbuild2/cc/common.ixx> #include <libbuild2/cc/common.txx> #endif // LIBBUILD2_CC_COMMON_HXX diff --git a/libbuild2/cc/common.ixx b/libbuild2/cc/common.ixx new file mode 100644 index 0000000..ce28890 --- /dev/null +++ b/libbuild2/cc/common.ixx @@ -0,0 +1,34 @@ +// file : libbuild2/cc/common.ixx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +namespace build2 +{ + namespace cc + { + inline const scope* data:: + effective_internal_scope (const scope& bs) const + { + if (internal_scope == nullptr) + return nullptr; + else + { + const string& s (*internal_scope); + + if (s == "current") + return internal_scope_current; + else if (s == "base") + return &bs; + else if (s == "root") + return bs.root_scope (); + else if (s == "bundle") + return bs.bundle_scope (); + else if (s == "strong") + return bs.strong_scope (); + else if (s == "weak") + return bs.weak_scope (); + else + return nullptr; + } + } + } +} diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 278f0cc..7636722 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -4,7 +4,7 @@ #include <libbuild2/cc/compile-rule.hxx> #include <cstdlib> // exit() -#include <cstring> // strlen(), strchr() +#include <cstring> // strlen(), strchr(), strncmp() #include <libbuild2/file.hxx> #include <libbuild2/depdb.hxx> @@ -175,6 +175,42 @@ namespace build2 throw invalid_argument ("invalid preprocessed value '" + s + "'"); } + // Return true if the compiler supports -isystem (GCC class) or + // /external:I (MSVC class). + // + static inline bool + isystem (const data& d) + { + switch (d.cclass) + { + case compiler_class::gcc: + { + return true; + } + case compiler_class::msvc: + { + if (d.cvariant.empty ()) + { + // While /external:I is available since 15.6, it required + // /experimental:external (and was rather buggy) until 16.10. + // + return d.cmaj > 19 || (d.cmaj == 19 && d.cmin >= 29); + } + else if (d.cvariant != "clang") + { + // clang-cl added support for /external:I (by translating it to + // -isystem) in version 13. + // + return d.cvmaj >= 13; + } + else + return false; + } + } + + return false; + } + optional<path> compile_rule:: find_system_header (const path& f) const { @@ -230,13 +266,15 @@ namespace build2 auto m (sys_hdr_dirs.begin () + sys_hdr_dirs_extra); auto e (sys_hdr_dirs.end ()); - // Note: starting from 15.6, MSVC gained /external:I option though it + // Note: starting from 16.10, MSVC gained /external:I option though it // doesn't seem to affect the order, only "system-ness". // append_option_values ( args, cclass == compiler_class::gcc ? "-idirafter" : - cclass == compiler_class::msvc ? "/I" : "-I", + cclass == compiler_class::msvc ? (isystem (*this) + ? "/external:I" + : "/I") : "-I", m, e, [] (const dir_path& d) {return d.string ().c_str ();}); @@ -244,6 +282,9 @@ namespace build2 // add all of them. But we want extras to come first. Note also that // clang-cl takes care of this itself. // + // Note also that we don't use /external:I to have consistent semantics + // with when INCLUDE is set (there is separate /external:env for that). + // if (ctype == compiler_type::msvc && cvariant != "clang") { if (!getenv ("INCLUDE")) @@ -418,6 +459,7 @@ namespace build2 void compile_rule:: append_library_options (appended_libraries& ls, T& args, const scope& bs, + const scope* is, // Internal scope. action a, const file& l, bool la, linfo li, library_cache* lib_cache) const { @@ -425,7 +467,8 @@ namespace build2 { appended_libraries& ls; T& args; - } d {ls, args}; + const scope* is; + } d {ls, args, is}; // See through utility libraries. // @@ -458,7 +501,118 @@ namespace build2 ? x_export_poptions : l.ctx.var_pool[t + ".export.poptions"])); - append_options (d.args, l, var); + if (const strings* ops = cast_null<strings> (l[var])) + { + for (auto i (ops->begin ()), e (ops->end ()); i != e; ++i) + { + const string& o (*i); + + // If enabled, remap -I to -isystem or /external:I for paths that + // are outside of the internal scope. + // + if (d.is != nullptr) + { + // See if this is -I<dir> or -I <dir> (or /I... for MSVC). + // + // While strictly speaking we can only attempt to recognize + // options until we hit something unknown (after that, we don't + // know what's an option and what's a value), it doesn't seem + // likely to cause issues here, where we only expect to see -I, + // -D, and -U. + // + bool msvc (cclass == compiler_class::msvc); + + if ((o[0] == '-' || (msvc && o[0] == '/')) && o[1] == 'I') + { + bool sep (o.size () == 2); // -I<dir> vs -I <dir> + + const char* v (nullptr); + size_t vn (0); + if (sep) + { + if (i + 1 == e) + ; // Append as is and let the compiler complain. + else + { + ++i; + v = i->c_str (); + vn = i->size (); + } + } + else + { + v = o.c_str () + 2; + vn = o.size () - 2; + } + + if (v != nullptr) + { + // See if we need to translate the option for this path. We + // only do this for absolute paths and try to optimize for + // the already normalized ones. + // + if (path_traits::absolute (v)) + { + const char* p (nullptr); + size_t pn (0); + + dir_path nd; + if (path_traits::normalized (v, vn, true /* separators */)) + { + p = v; + pn = vn; + } + else + try + { + nd = dir_path (v, vn); + nd.normalize (); + p = nd.string ().c_str (); + pn = nd.string ().size (); + } + catch (const invalid_path&) + { + // Ignore this path. + } + + if (p != nullptr) + { + auto sub = [p, pn] (const dir_path& d) + { + return path_traits::sub ( + p, pn, + d.string ().c_str (), d.string ().size ()); + }; + + // Translate if it's neither in src nor in out of the + // internal scope. + // + if (!sub (d.is->src_path ()) && + (d.is->out_eq_src () || !sub (d.is->out_path ()))) + { + // Note: must use original value (path is temporary). + // + append_option (d.args, + msvc ? "/external:I" : "-isystem"); + append_option (d.args, v); + continue; + } + } + } + + // If not translated, preserve the original form. + // + append_option (d.args, o.c_str ()); + if (sep) append_option (d.args, v); + + continue; + } + } + } + + append_option (d.args, o.c_str ()); + } + } // From the process_libraries() semantics we know that the final call // is always for the common options. @@ -479,7 +633,11 @@ namespace build2 const scope& bs, action a, const file& l, bool la, linfo li) const { - append_library_options<strings> (ls, args, bs, a, l, la, li, nullptr); + const scope* is (isystem (*this) + ? effective_internal_scope (bs) + : nullptr); + + append_library_options (ls, args, bs, is, a, l, la, li, nullptr); } template <typename T> @@ -488,6 +646,14 @@ namespace build2 const scope& bs, action a, const target& t, linfo li) const { + auto internal_scope = [this, &bs, is = optional<const scope*> ()] () mutable + { + if (!is) + is = isystem (*this) ? effective_internal_scope (bs) : nullptr; + + return *is; + }; + appended_libraries ls; library_cache lc; @@ -509,7 +675,11 @@ namespace build2 (la = (f = pt->is_a<libux> ())) || ( (f = pt->is_a<libs> ()))) { - append_library_options (ls, args, bs, a, *f, la, li, &lc); + append_library_options (ls, + args, + bs, internal_scope (), + a, *f, la, li, + &lc); } } } @@ -1452,12 +1622,17 @@ namespace build2 for (auto i (v.begin ()), e (v.end ()); i != e; ++i) { - // -I can either be in the "-Ifoo" or "-I foo" form. For VC it can - // also be /I. - // const string& o (*i); - if (o.size () < 2 || (o[0] != '-' && o[0] != '/') || o[1] != 'I') + // -I can either be in the "-Ifoo" or "-I foo" form. For MSVC it + // can also be /I. + // + // Note that we naturally assume that -isystem, /external:I, etc., + // are not relevant here. + // + bool msvc (cclass == compiler_class::msvc); + + if (!((o[0] == '-' || (msvc && o[0] == '/')) && o[1] == 'I')) continue; dir_path d; @@ -3487,27 +3662,43 @@ namespace build2 for (auto i (args.begin ()), e (args.end ()); i != e; ++i) { + const char* o (*i); + // -I can either be in the "-Ifoo" or "-I foo" form. For VC it // can also be /I. // - const char* o (*i); - size_t n (strlen (o)); - - if (n < 2 || (o[0] != '-' && o[0] != '/') || o[1] != 'I') + // Note also that append_library_options() may have translated + // -I to -isystem or /external:I so we have to recognize those + // as well. + // { - s = nullptr; - continue; - } + bool msvc (cclass == compiler_class::msvc); - if (n == 2) - { - if (++i == e) - break; // Let the compiler complain. + size_t p (0); + if (o[0] == '-' || (msvc && o[0] == '/')) + { + p = (o[1] == 'I' ? 2 : + !msvc && strncmp (o + 1, "isystem", 7) == 0 ? 8 : + msvc && strncmp (o + 1, "external:I", 10) == 0 ? 11 : 0); + } + + if (p == 0) + { + s = nullptr; + continue; + } + + size_t n (strlen (o)); + if (n == p) + { + if (++i == e) + break; // Let the compiler complain. - ds = *i; + ds = *i; + } + else + ds.assign (o + p, n - p); } - else - ds.assign (o + 2, n - 2); if (!ds.empty ()) { @@ -3527,7 +3718,7 @@ namespace build2 if (!d.empty ()) { // Ignore any paths containing '.', '..' components. Allow - // any directory separators thought (think -I$src_root/foo + // any directory separators though (think -I$src_root/foo // on Windows). // if (d.absolute () && d.normalized (false)) @@ -3634,9 +3825,15 @@ namespace build2 append_options (args, cmode); append_sys_hdr_options (args); // Extra system header dirs (last). - // See perform_update() for details on overriding the default - // exceptions and runtime. + // See perform_update() for details on /external:W0, /EHsc, /MD. // + if (cvariant != "clang" && isystem (*this)) + { + if (find_option_prefix ("/external:I", args) && + !find_option_prefix ("/external:W", args)) + args.push_back ("/external:W0"); + } + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) args.push_back ("/EHsc"); @@ -4950,6 +5147,15 @@ namespace build2 append_options (args, cmode); append_sys_hdr_options (args); + // See perform_update() for details on /external:W0, /EHsc, /MD. + // + if (cvariant != "clang" && isystem (*this)) + { + if (find_option_prefix ("/external:I", args) && + !find_option_prefix ("/external:W", args)) + args.push_back ("/external:W0"); + } + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) args.push_back ("/EHsc"); @@ -6849,6 +7055,16 @@ namespace build2 if (md.pp != preprocessed::all) append_sys_hdr_options (args); // Extra system header dirs (last). + // If we have any /external:I options but no /external:Wn, then add + // /external:W0 to emulate the -isystem semantics. + // + if (cvariant != "clang" && isystem (*this)) + { + if (find_option_prefix ("/external:I", args) && + !find_option_prefix ("/external:W", args)) + args.push_back ("/external:W0"); + } + // While we want to keep the low-level build as "pure" as possible, // the two misguided defaults, C++ exceptions and runtime, just have // to be fixed. Otherwise the default build is pretty much unusable. diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx index d65089e..daea600 100644 --- a/libbuild2/cc/compile-rule.hxx +++ b/libbuild2/cc/compile-rule.hxx @@ -36,7 +36,8 @@ namespace build2 size_t copied; // First copied-over bmi*{}, 0 if none. }; - class LIBBUILD2_CC_SYMEXPORT compile_rule: public simple_rule, virtual common + class LIBBUILD2_CC_SYMEXPORT compile_rule: public simple_rule, + virtual common { public: compile_rule (data&&); @@ -80,6 +81,7 @@ namespace build2 void append_library_options (appended_libraries&, T&, const scope&, + const scope*, action, const file&, bool, linfo, library_cache*) const; diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index 69e8219..098dc86 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -1478,7 +1478,7 @@ namespace build2 { dir_paths r; - // Extract /I paths from the compiler mode. + // Extract /I paths and similar from the compiler mode. // msvc_extract_header_search_dirs (mo, r); size_t rn (r.size ()); diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx index 07f082f..769f6bb 100644 --- a/libbuild2/cc/init.cxx +++ b/libbuild2/cc/init.cxx @@ -97,12 +97,14 @@ namespace build2 vp.insert<strings> ("config.cc.loptions"); vp.insert<strings> ("config.cc.aoptions"); vp.insert<strings> ("config.cc.libs"); + vp.insert<string> ("config.cc.internal.scope"); vp.insert<strings> ("cc.poptions"); vp.insert<strings> ("cc.coptions"); vp.insert<strings> ("cc.loptions"); vp.insert<strings> ("cc.aoptions"); vp.insert<strings> ("cc.libs"); + vp.insert<string> ("cc.internal.scope"); vp.insert<strings> ("cc.export.poptions"); vp.insert<strings> ("cc.export.coptions"); @@ -298,6 +300,24 @@ namespace build2 rs.assign ("cc.libs") += cast_null<strings> ( lookup_config (rs, "config.cc.libs", nullptr)); + // config.cc.internal.scope + // + // Note: save omitted. + // + if (lookup l = lookup_config (rs, "config.cc.internal.scope")) + { + if (cast<string> (l) == "current") + fail << "'current' value in config.cc.internal.scope"; + + // This is necessary in case we are acting as bundle amalgamation. + // + rs.assign ("cc.internal.scope") = *l; + } + + // config.cc.reprocess + // + // Note: save omitted. + // if (lookup l = lookup_config (rs, "config.cc.reprocess")) rs.assign ("cc.reprocess") = *l; diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 959d315..117c8c9 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -372,6 +372,18 @@ namespace build2 const compiler_info& xi (*x_info); const target_triplet& tt (cast<target_triplet> (rs[x_target])); + // Load cc.core.config. + // + if (!cast_false<bool> (rs["cc.core.config.loaded"])) + { + variable_map h (rs.ctx); + + if (!xi.bin_pattern.empty ()) + h.assign ("config.bin.pattern") = xi.bin_pattern; + + init_module (rs, rs, "cc.core.config", loc, false, h); + } + // Configuration. // using config::lookup_config; @@ -441,6 +453,97 @@ namespace build2 translate_std (xi, tt, rs, mode, v); } + // config.x.internal.scope + // + // Note: save omitted. + // + // The effective internal_scope value is chosen based on the following + // priority list: + // + // 1. config.x.internal.scope + // + // 2. config.cc.internal.scope + // + // 3. effective value from bundle amalgamation + // + // 4. x.internal.scope + // + // 5. cc.internal.scope + // + // Note also that we only update x.internal.scope (and not cc.*) to + // reflect the effective value. + // + { + if (lookup l = lookup_config (rs, config_x_internal_scope)) // 1 + { + internal_scope = &cast<string> (l); + + if (*internal_scope == "current") + fail << "'current' value in " << config_x_internal_scope; + } + else if (lookup l = rs["config.cc.internal.scope"]) // 2 + { + internal_scope = &cast<string> (l); + } + else // 3 + { + const scope& as (*rs.bundle_scope ()); + + if (as != rs) + { + // Only use the value if the corresponding module is loaded. + // + bool xl (cast_false<bool> (as[string (x) + ".config.loaded"])); + if (xl) + internal_scope = cast_null<string> (as[x_internal_scope]); + + if (internal_scope == nullptr) + { + if (xl || cast_false<bool> (as["cc.core.config.loaded"])) + internal_scope = cast_null<string> (as["cc.internal.scope"]); + } + + if (internal_scope != nullptr && *internal_scope == "current") + internal_scope_current = &as; + } + } + + lookup l; + if (internal_scope == nullptr) + { + internal_scope = cast_null<string> (l = rs[x_internal_scope]); // 4 + + if (internal_scope == nullptr) + internal_scope = cast_null<string> (rs["cc.internal.scope"]); // 5 + } + + if (internal_scope != nullptr) + { + const string& s (*internal_scope); + + // Assign effective. + // + if (!l) + rs.assign (x_internal_scope) = s; + + if (s == "current") + { + if (internal_scope_current == nullptr) + internal_scope_current = &rs; + } + else if (s == "base" || + s == "root" || + s == "bundle" || + s == "strong" || + s == "weak") + ; + else if (s == "global") + internal_scope = nullptr; // Nothing to translate; + else + fail << "invalid " << x_internal_scope << " value '" << s << "'"; + } + } + // config.x.translate_include // // It's still fuzzy whether specifying (or maybe tweaking) this list in @@ -686,6 +789,16 @@ namespace build2 auto& incs (hdr_dirs.first); auto& libs (lib_dirs.first); + if (verb >= 3 && internal_scope != nullptr) + { + dr << "\n int scope "; + + if (*internal_scope == "current") + dr << internal_scope_current->out_path (); + else + dr << *internal_scope; + } + if (verb >= 3 && !mods.empty ()) { dr << "\n mod dirs"; @@ -723,18 +836,6 @@ namespace build2 config::save_environment (rs, xi.compiler_environment); config::save_environment (rs, xi.platform_environment); - - // Load cc.core.config. - // - if (!cast_false<bool> (rs["cc.core.config.loaded"])) - { - variable_map h (rs.ctx); - - if (!xi.bin_pattern.empty ()) - h.assign ("config.bin.pattern") = xi.bin_pattern; - - init_module (rs, rs, "cc.core.config", loc, false, h); - } } // Global cache of ad hoc importable headers. diff --git a/libbuild2/cc/module.hxx b/libbuild2/cc/module.hxx index e21fb9e..5c68482 100644 --- a/libbuild2/cc/module.hxx +++ b/libbuild2/cc/module.hxx @@ -61,6 +61,11 @@ namespace build2 string env_checksum; // Environment checksum (also in x.path). + // Cached x.internal.scope value. + // + const string* internal_scope = nullptr; + const scope* internal_scope_current = nullptr; + // Temporary storage for data::sys_*_dirs_*. // size_t sys_lib_dirs_mode; diff --git a/libbuild2/cc/msvc.cxx b/libbuild2/cc/msvc.cxx index 9e8ae18..f95cab0 100644 --- a/libbuild2/cc/msvc.cxx +++ b/libbuild2/cc/msvc.cxx @@ -233,12 +233,15 @@ namespace build2 dir_path d; try { - // -I can either be in the "-Ifoo" or "-I foo" form. For VC it can - // also be /I. + // -I can either be in the "-Ifoo" or "-I foo" form. For MSVC it can + // also be /I. And from 16.10 it can also be /external:I. // - if (o.size () > 1 && (o[0] == '-' || o[0] == '/') && o[1] == 'I') + size_t p; + if ((o[0] == '-' || o[0] == '/') && + (p = (o[1] == 'I' ? 2 : + o.compare (1, 10, "external:I") == 0 ? 11 : 0)) != 0) { - if (o.size () == 2) + if (o.size () == p) { if (++i == e) break; // Let the compiler complain. @@ -246,7 +249,7 @@ namespace build2 d = dir_path (*i); } else - d = dir_path (o, 2, string::npos); + d = dir_path (o, p, string::npos); } else continue; @@ -331,7 +334,7 @@ namespace build2 // see guess). Note that this is not used for Clang targeting MSVC (but // is for clang-cl). - // Extract -I paths from the compiler mode. + // Extract /I and similar paths from the compiler mode. // dir_paths r; msvc_extract_header_search_dirs (cast<strings> (rs[x_mode]), r); diff --git a/libbuild2/cc/pkgconfig.cxx b/libbuild2/cc/pkgconfig.cxx index fd2cd07..7b4f86d 100644 --- a/libbuild2/cc/pkgconfig.cxx +++ b/libbuild2/cc/pkgconfig.cxx @@ -1525,13 +1525,12 @@ namespace build2 for (auto i (v->begin ()); i != v->end (); ++i) { const string& o (*i); - size_t n (o.size ()); // Filter out -I (both -I<dir> and -I <dir> forms). // - if (n >= 2 && o[0] == '-' && o[1] == 'I') + if (o[0] == '-' && o[1] == 'I') { - if (n == 2) + if (o.size () == 2) ++i; continue; diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx index cc2a135..a10fc5c 100644 --- a/libbuild2/cxx/init.cxx +++ b/libbuild2/cxx/init.cxx @@ -497,6 +497,71 @@ namespace build2 vp.insert<strings> ("config.cxx.aoptions"), vp.insert<strings> ("config.cxx.libs"), + // Project's internal scope. + // + // A header search path (-I) exported by a library that is outside of + // the internal scope is considered external and, if supported by the + // compiler, the corresponding -I option is translated to an + // appropriate "external header search path" option (-isystem for + // GCC/Clang, /external:I for MSVC 16.10 and later or clang-cl 13 and + // later). In particular, this suppresses compiler warnings in such + // external headers (/external:W0 is automatically added unless a + // custom /external:Wn is specified). + // + // The internal scope can be specified by the project with the + // cxx.internal.scope variable and overridden by the user with the + // config.cxx.internal.scope variable. Note that cxx.internal.scope + // must be specified before loading the cxx module (cxx.config, more + // precisely) and after which it contains the effective value (see + // below). For example: + // + // # root.build + // + // cxx.internal.scope = current + // + // using cxx + // + // Valid values for cxx.internal.scope are: + // + // current -- current root scope (where variable is assigned) + // base -- target's base scope + // root -- target's root scope + // bundle -- target's bundle amalgamation (see scope::bundle_root()) + // strong -- target's strong amalgamation (see scope::strong_root()) + // weak -- target's weak amalgamation (see scope::weak_root()) + // global -- global scope (everything is internal) + // + // Valid values for config.cxx.internal.scope are the same except for + // `current`. + // + // Note also that there are [config.]cc.internal.scope variables that + // can be used to specify the internal scope for all the cc-based + // modules. + // + // The project's effective internal scope is chosen based on the + // following priority list: + // + // 1. config.cxx.internal.scope + // + // 2. config.cc.internal.scope + // + // 3. effective scope from bundle amalgamation + // + // 4. cxx.internal.scope + // + // 5. cc.internal.scope + // + // In particular, item #3 allows an amalgamation that bundles a + // project to override its internal scope. + // + // The recommended value for a typical project is `current`, meaning + // that only headers inside the project will be considered internal. + // The tests subproject, if present, will inherit its value from the + // project (which acts as a bundle amalgamation), unless it is being + // built out of source (for example, to test an installed library). + // + vp.insert<string> ("config.cxx.internal.scope"), + // Headers and header groups whose inclusion should or should not be // translated to the corresponding header unit imports. // @@ -549,6 +614,8 @@ namespace build2 vp.insert<strings> ("cxx.aoptions"), vp.insert<strings> ("cxx.libs"), + vp.insert<string> ("cxx.internal.scope"), + &vp.insert<cc::translatable_headers> ("cxx.translate_include"), vp["cc.poptions"], @@ -731,6 +798,8 @@ namespace build2 cm.x_info->class_, cm.x_info->version.major, cm.x_info->version.minor, + cm.x_info->variant_version ? cm.x_info->variant_version->major : 0, + cm.x_info->variant_version ? cm.x_info->variant_version->minor : 0, cast<process_path> (rs[cm.x_path]), cast<strings> (rs[cm.x_mode]), cast<target_triplet> (rs[cm.x_target]), @@ -739,6 +808,9 @@ namespace build2 modules, symexport, + cm.internal_scope, + cm.internal_scope_current, + cast<dir_paths> (rs[cm.x_sys_lib_dirs]), cast<dir_paths> (rs[cm.x_sys_hdr_dirs]), cm.x_info->sys_mod_dirs ? &cm.x_info->sys_mod_dirs->first : nullptr, diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index f9f1daa..b62d2ab 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -650,6 +650,26 @@ namespace build2 I begin, I end, F&& get = [] (const string& s) {return s;}); + // As above but append a single option (used for append/hash uniformity). + // + inline void + append_option (cstrings& args, const char* o) + { + args.push_back (o); + } + + inline void + append_option (strings& args, const char* o) + { + args.push_back (o); + } + + inline void + append_option (sha256& csum, const char* o) + { + csum.append (o); + } + // Check if a specified option is present in the variable or value. T is // either target or scope. For the interator version use rbegin()/rend() to // search backwards. |