aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/compile-rule.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/cc/compile-rule.cxx')
-rw-r--r--libbuild2/cc/compile-rule.cxx302
1 files changed, 158 insertions, 144 deletions
diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx
index 2e4775e..7629ed5 100644
--- a/libbuild2/cc/compile-rule.cxx
+++ b/libbuild2/cc/compile-rule.cxx
@@ -6151,180 +6151,185 @@ namespace build2
}
};
- // Pre-resolve std modules in an ad hoc way for certain compilers.
+ // Pre-resolve standard library modules (std and std.compat) in an ad
+ // hoc way.
//
- // @@ TODO: cache x_stdlib value.
+
+ // Similar logic to check_exact() above.
//
- if ((ctype == compiler_type::msvc) ||
- (ctype == compiler_type::clang &&
- cmaj >= 17 &&
- cast<string> (rs[x_stdlib]) == "libc++"))
+ done = true;
+
+ for (size_t i (0); i != n; ++i)
{
- // Similar logic to check_exact() above.
- //
- done = true;
+ module_import& m (imports[i]);
- for (size_t i (0); i != n; ++i)
+ if (m.name == "std" || m.name == "std.compat")
{
- module_import& m (imports[i]);
+ otype ot (otype::e);
+ const target* mt (nullptr);
- if (m.name == "std" || m.name == "std.compat")
+ switch (ctype)
{
- otype ot (otype::e);
- const target* mt (nullptr);
-
- switch (ctype)
+ case compiler_type::clang:
{
- case compiler_type::clang:
- {
- if (m.name != "std")
- fail << "module " << m.name << " not yet provided by libc++";
+ // @@ TODO: cache x_stdlib value.
+ //
+ if (cast<string> (rs[x_stdlib]) != "libc++")
+ fail << "standard library module '" << m.name << "' is "
+ << "currently only supported in libc++" <<
+ info << "try adding -stdlib=libc++ as compiler mode option";
- // Find or insert std.cppm (similar code to pkgconfig.cxx).
- //
- // Note: build_install_data is absolute and normalized.
- //
- mt = &ctx.targets.insert_locked (
- *x_mod,
- (dir_path (build_install_data) /= "libbuild2") /= "cc",
- dir_path (),
- "std",
- string ("cppm"), // For C++14 during bootstrap.
- target_decl::implied,
- trace).first;
-
- // Which output type should we use, static or shared? The
- // correct way would be to detect whether static or shared
- // version of libc++ is to be linked and use the corresponding
- // type. And we could do that by looking for -static-libstdc++
- // in loption (and no, it's not -static-libc++).
- //
- // But, looking at the object file produced from std.cppm, it
- // only contains one symbol, the static object initializer.
- // And this is unlikely to change since all other non-inline
- // or template symbols should be in libc++. So feels like it's
- // not worth the trouble and one variant should be good enough
- // for both cases. Let's use the shared one for less
- // surprising diagnostics (as in, "why are you linking obje{}
- // to a shared library?")
- //
- // (Of course, theoretically, std.cppm could detect via a
- // macro whether it's being compiled with -fPIC or not and do
- // things differently, but this seems far-fetched).
- //
- ot = otype::s;
+ if (cmaj < 18)
+ fail << "standard library module '" << m.name << "' is "
+ << "only supported in Clang 18 or later";
- break;
- }
- case compiler_type::msvc:
+ // Find or insert std*.cppm (similar code to pkgconfig.cxx).
+ //
+ // Note: build_install_data is absolute and normalized.
+ //
+ mt = &ctx.targets.insert_locked (
+ *x_mod,
+ (dir_path (build_install_data) /= "libbuild2") /= "cc",
+ dir_path (),
+ m.name,
+ string ("cppm"), // For C++14 during bootstrap.
+ target_decl::implied,
+ trace).first;
+
+ // Which output type should we use, static or shared? The
+ // correct way would be to detect whether static or shared
+ // version of libc++ is to be linked and use the corresponding
+ // type. And we could do that by looking for -static-libstdc++
+ // in loption (and no, it's not -static-libc++).
+ //
+ // But, looking at the object file produced from std*.cppm, they
+ // only contain one symbol, the static object initializer. And
+ // this is unlikely to change since all other non-inline or
+ // template symbols should be in libc++. So feels like it's not
+ // worth the trouble and one variant should be good enough for
+ // both cases. Let's use the shared one for less surprising
+ // diagnostics (as in, "why are you linking obje{} to a shared
+ // library?")
+ //
+ // (Of course, theoretically, std*.cppm could detect via a macro
+ // whether they are being compiled with -fPIC or not and do
+ // things differently, but this seems far-fetched).
+ //
+ ot = otype::s;
+
+ break;
+ }
+ case compiler_type::msvc:
+ {
+ // For MSVC, the source files std.ixx and std.compat.ixx are
+ // found in the modules/ subdirectory which is a sibling of
+ // include/ in the MSVC toolset (and "that is a contract with
+ // customers" to quote one of the developers).
+ //
+ // The problem of course is that there are multiple system
+ // header search directories (for example, as specified in the
+ // INCLUDE environment variable) and which one of them is for
+ // the MSVC toolset is not specified. So what we are going to do
+ // is search for one of the well-known standard C++ headers and
+ // assume that the directory where we found it is the one we are
+ // looking for. Or we could look for something MSVC-specific
+ // like vcruntime.h.
+ //
+ dir_path modules;
+ if (optional<path> p = find_system_header (path ("vcruntime.h")))
{
- // For MSVC, the source files std.ixx and std.compat.ixx are
- // found in the modules/ subdirectory which is a sibling of
- // include/ in the MSVC toolset (and "that is a contract with
- // customers" to quote one of the developers).
- //
- // The problem of course is that there are multiple system
- // header search directories (for example, as specified in the
- // INCLUDE environment variable) and which one of them is for
- // the MSVC toolset is not specified. So what we are going to
- // do is search for one of the well-known standard C++ headers
- // and assume that the directory where we found it is the one
- // we are looking for. Or we could look for something
- // MSVC-specific like vcruntime.h.
- //
- dir_path modules;
- if (optional<path> p = find_system_header (path ("vcruntime.h")))
+ p->make_directory (); // Strip vcruntime.h.
+ if (p->leaf () == path ("include")) // Sanity check.
{
- p->make_directory (); // Strip vcruntime.h.
- if (p->leaf () == path ("include")) // Sanity check.
- {
- modules = path_cast<dir_path> (move (p->make_directory ()));
- modules /= "modules";
- }
+ modules = path_cast<dir_path> (move (p->make_directory ()));
+ modules /= "modules";
}
+ }
- if (modules.empty ())
- fail << "unable to locate MSVC standard modules directory";
-
- mt = &ctx.targets.insert_locked (
- *x_mod,
- move (modules),
- dir_path (),
- m.name,
- string ("ixx"), // For C++14 during bootstrap.
- target_decl::implied,
- trace).first;
+ if (modules.empty ())
+ fail << "unable to locate MSVC standard modules directory";
- // For MSVC it's easier to detect the runtime being used since
- // it's specified with the compile options (/MT[d], /MD[d]).
- //
- // Similar semantics as in extract_headers() except here we
- // use options visible from the root scope. Note that
- // find_option_prefixes() looks in reverse, so look in the
- // cmode, x_coptions, c_coptions order.
- //
- initializer_list<const char*> os {"/MD", "/MT", "-MD", "-MT"};
+ mt = &ctx.targets.insert_locked (
+ *x_mod,
+ move (modules),
+ dir_path (),
+ m.name,
+ string ("ixx"), // For C++14 during bootstrap.
+ target_decl::implied,
+ trace).first;
- const string* o;
- if ((o = find_option_prefixes (os, cmode)) != nullptr ||
- (o = find_option_prefixes (os, rs, x_coptions)) != nullptr ||
- (o = find_option_prefixes (os, rs, c_coptions)) != nullptr)
- {
- ot = (*o)[2] == 'D' ? otype::s : otype::a;
- }
- else
- ot = otype::s; // The default is /MD.
+ // For MSVC it's easier to detect the runtime being used since
+ // it's specified with the compile options (/MT[d], /MD[d]).
+ //
+ // Similar semantics as in extract_headers() except here we use
+ // options visible from the root scope. Note that
+ // find_option_prefixes() looks in reverse, so look in the
+ // cmode, x_coptions, c_coptions order.
+ //
+ initializer_list<const char*> os {"/MD", "/MT", "-MD", "-MT"};
- break;
+ const string* o;
+ if ((o = find_option_prefixes (os, cmode)) != nullptr ||
+ (o = find_option_prefixes (os, rs, x_coptions)) != nullptr ||
+ (o = find_option_prefixes (os, rs, c_coptions)) != nullptr)
+ {
+ ot = (*o)[2] == 'D' ? otype::s : otype::a;
}
- case compiler_type::gcc:
- case compiler_type::icc:
- assert (false);
- };
+ else
+ ot = otype::s; // The default is /MD.
+
+ break;
+ }
+ case compiler_type::gcc:
+ case compiler_type::icc:
+ {
+ fail << "standard library module '" << m.name << "' is "
+ << "not yet supported in this compiler";
+ }
+ };
- pair<target&, ulock> tl (
- this->make_module_sidebuild ( // GCC 4.9
- a, bs, nullptr, ot, *mt, m.name));
+ pair<target&, ulock> tl (
+ this->make_module_sidebuild ( // GCC 4.9
+ a, bs, nullptr, ot, *mt, m.name));
- if (tl.second.owns_lock ())
+ if (tl.second.owns_lock ())
+ {
+ // Special compile options for the std modules.
+ //
+ if (ctype == compiler_type::clang)
{
- // Special compile options for the std modules.
- //
- if (ctype == compiler_type::clang)
- {
- value& v (tl.first.append_locked (x_coptions));
+ value& v (tl.first.append_locked (x_coptions));
- if (v.null)
- v = strings {};
+ if (v.null)
+ v = strings {};
- strings& cops (v.as<strings> ());
+ strings& cops (v.as<strings> ());
- switch (ctype)
+ switch (ctype)
+ {
+ case compiler_type::clang:
{
- case compiler_type::clang:
- {
- cops.push_back ("-Wno-reserved-module-identifier");
- break;
- }
- case compiler_type::msvc:
- // It appears nothing special is needed to compile MSVC
- // standard modules.
- case compiler_type::gcc:
- case compiler_type::icc:
- assert (false);
- };
- }
-
- tl.second.unlock ();
+ cops.push_back ("-Wno-reserved-module-identifier");
+ break;
+ }
+ case compiler_type::msvc:
+ // It appears nothing special is needed to compile MSVC
+ // standard modules.
+ case compiler_type::gcc:
+ case compiler_type::icc:
+ assert (false);
+ };
}
- pts[start + i].target = &tl.first;
- m.score = match_max (m.name) + 1;
- continue; // Scan the rest to detect if all done.
+ tl.second.unlock ();
}
- done = false;
+ pts[start + i].target = &tl.first;
+ m.score = match_max (m.name) + 1;
+ continue; // Scan the rest to detect if all done.
}
+
+ done = false;
}
// Go over prerequisites and try to resolve imported modules with them.
@@ -6672,6 +6677,15 @@ namespace build2
//
string extra;
+ // @@ What happens if different projects used different standards?
+ // Specifically, how do we detect this and what can the user do
+ // about it? For the latter question, forcing the same standard
+ // with config.cxx.std seems like the only sensible option. For
+ // the former, we could read the value of cxx.std using our
+ // buildfile first-line peeking mechanism. But doing that for
+ // every module interface feels inefficient so we will probably
+ // need to cache it on the per-project basis. Maybe/later.
+ //
if (const string* std = cast_null<string> (rs[x_std]))
extra += string (x) + ".std = " + *std + '\n';