aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-10-01 11:05:49 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-10-01 12:04:38 +0200
commitdb8336a686a85f0e458acb2d5f1ad442585bfc9a (patch)
treed0e9aa2ce76eb2f208a5d63a1258cf218af2aacc
parentd4457a6427401ed4d5c09eba00cac84c5664f250 (diff)
Add notion of internal scope, translate external -I to -isystem or equivalent
-rw-r--r--libbuild2/c/init.cxx16
-rw-r--r--libbuild2/cc/common.hxx15
-rw-r--r--libbuild2/cc/common.ixx34
-rw-r--r--libbuild2/cc/compile-rule.cxx272
-rw-r--r--libbuild2/cc/compile-rule.hxx4
-rw-r--r--libbuild2/cc/guess.cxx2
-rw-r--r--libbuild2/cc/init.cxx20
-rw-r--r--libbuild2/cc/module.cxx125
-rw-r--r--libbuild2/cc/module.hxx5
-rw-r--r--libbuild2/cc/msvc.cxx15
-rw-r--r--libbuild2/cc/pkgconfig.cxx5
-rw-r--r--libbuild2/cxx/init.cxx72
-rw-r--r--libbuild2/utility.hxx20
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.