diff options
Diffstat (limited to 'libbuild2/cc')
36 files changed, 12143 insertions, 1533 deletions
diff --git a/libbuild2/cc/buildfile b/libbuild2/cc/buildfile index e090e76..05e4c8c 100644 --- a/libbuild2/cc/buildfile +++ b/libbuild2/cc/buildfile @@ -11,19 +11,29 @@ libpkgconf = $config.build2.libpkgconf if $libpkgconf import impl_libs += libpkgconf%lib{pkgconf} else - import impl_libs += libpkg-config%lib{pkg-config} + import impl_libs += libbutl%lib{butl-pkg-config} include ../bin/ intf_libs = ../bin/lib{build2-bin} ./: lib{build2-cc}: libul{build2-cc}: \ {hxx ixx txx cxx}{** -pkgconfig-lib* -**.test...} \ - h{msvc-setup} \ - $intf_libs $impl_libs + h{msvc-setup} libul{build2-cc}: cxx{pkgconfig-libpkgconf}: include = $libpkgconf libul{build2-cc}: cxx{pkgconfig-libpkg-config}: include = (!$libpkgconf) +libul{build2-cc}: $intf_libs $impl_libs + +# libc++ std module interface translation units. +# +# Hopefully temporary, see llvm-project GH issues #73089. +# +# @@ TMP: make sure sync'ed with upstream before release (keep this note). +# +lib{build2-cc}: file{std.cppm std.compat.cppm} +file{std.cppm}@./ file{std.compat.cppm}@./: install = data/libbuild2/cc/ + # Unit tests. # exe{*.test}: diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx index 4a0c4b1..9a4a07c 100644 --- a/libbuild2/cc/common.cxx +++ b/libbuild2/cc/common.cxx @@ -162,8 +162,16 @@ namespace build2 // Add the library to the chain. // if (self && proc_lib) + { + if (find (chain->begin (), chain->end (), &l) != chain->end ()) + fail << "dependency cycle detected involving library " << l; + chain->push_back (&l); + } + // We only lookup public variables so go straight for the public + // variable pool. + // auto& vp (top_bs.ctx.var_pool); do // Breakout loop. @@ -347,7 +355,7 @@ namespace build2 // Find system search directories corresponding to this library, i.e., // from its project and for its type (C, C++, etc). // - auto find_sysd = [&top_sysd, t, cc, same, &bs, &sysd, this] () + auto find_sysd = [&top_sysd, &vp, t, cc, same, &bs, &sysd, this] () { // Use the search dirs corresponding to this library scope/type. // @@ -356,7 +364,7 @@ namespace build2 : &cast<dir_paths> ( bs.root_scope ()->vars[same ? x_sys_lib_dirs - : bs.ctx.var_pool[t + ".sys_lib_dirs"]]); + : vp[t + ".sys_lib_dirs"]]); }; auto find_linfo = [top_li, t, cc, &bs, &l, &li] () @@ -379,9 +387,10 @@ namespace build2 for (const prerequisite_target& pt: l.prerequisite_targets[a]) { // Note: adhoc prerequisites are not part of the library metadata - // protocol (and we should check for adhoc first to avoid races). + // protocol (and we should check for adhoc first to avoid races + // during execute). // - if (pt == nullptr || pt.adhoc ()) + if (pt.adhoc () || pt == nullptr) continue; if (marked (pt)) @@ -405,7 +414,7 @@ namespace build2 if (!li) find_linfo (); process_libraries_impl (a, bs, *li, *sysd, - g, *f, la, pt.data, + g, *f, la, pt.data /* lflags */, proc_impl, proc_lib, proc_opt, true /* self */, proc_opt_group, cache, chain, nullptr); @@ -639,18 +648,36 @@ namespace build2 << " dependency " << *t << " is " << w << info << "mentioned in *.export." << (impl ? "impl_" : "") << "libs of target " << l << - info << "is it a prerequisite of " << l << "?"; + info << "is it a prerequisite of " << l << "?" << endf; } // Process it recursively. // - // @@ Where can we get the link flags? Should we try to find - // them in the library's prerequisites? What about - // installed stuff? + bool u; + bool la ((u = t->is_a<libux> ()) || t->is_a<liba> ()); + lflags lf (0); + + // If this is a static library, see if we need to link it + // whole. // + if (la && proc_lib) + { + // Note: go straight for the public variable pool. + // + const variable& var (t->ctx.var_pool["bin.whole"]); + + // See the link rule for the lookup semantics. + // + lookup l ( + t->lookup_original (var, true /* target_only */).first); + + if (l ? cast<bool> (*l) : u) + lf |= lflag_whole; + } + process_libraries_impl ( a, bs, *li, *sysd, - g, *t, t->is_a<liba> () || t->is_a<libux> (), 0, + g, *t, la, lf, proc_impl, proc_lib, proc_opt, true /* self */, proc_opt_group, cache, chain, dedup); @@ -801,6 +828,8 @@ namespace build2 // always a file. The second half of the returned pair is the group, if // the member was picked. // + // Note: paths in sysd/usrd are expected to be absolute and normalized. + // // Note: may throw non_existent_library. // pair<const mtime_target&, const target*> common:: @@ -908,6 +937,8 @@ namespace build2 // Action should be absent if called during the load phase. Note that pk's // scope should not be NULL (even if dir is absolute). // + // Note: paths in sysd/usrd are expected to be absolute and normalized. + // // Note: see similar logic in find_system_library(). // target* common:: @@ -1036,6 +1067,21 @@ namespace build2 { context& ctx (p.scope->ctx); + // Whether to look for a binless variant using the common .pc file + // (see below). + // + // Normally we look for a binless version if the binful one was not + // found. However, sometimes we may find what looks like a binful + // library but on a closer examination realize that there is something + // wrong with it (for example, it's not a Windows import library). In + // such cases we want to omit looking for a binless library using the + // common .pc file since it most likely corresponds to the binful + // library (and we may end up in a infinite loop trying to resolve + // itself). + // + bool ba (true); + bool bs (true); + timestamp mt; // libs @@ -1107,6 +1153,31 @@ namespace build2 s->path_mtime (move (f), mt); } } + else if (!ext && tsys == "darwin") + { + // Besides .dylib, Mac OS now also has "text-based stub libraries" + // that use the .tbd extension. They appear to be similar to + // Windows import libraries and contain information such as the + // location of the .dylib library, its symbols, etc. For example, + // there is /Library/.../MacOSX13.3.sdk/usr/lib/libsqlite3.tbd + // which points to /usr/lib/libsqlite3.dylib (but which itself is + // invisible/inaccessible, presumably for security). + // + // Note that for now we are treating the .tbd library as the + // shared library but could probably do the more elaborate dance + // with ad hoc members like on Windows if really necessary. + // + se = string ("tbd"); + f = f.base (); // Remove .dylib. + f += ".tbd"; + mt = mtime (f); + + if (mt != timestamp_nonexistent) + { + insert_library (ctx, s, name, d, ld, se, exist, trace); + s->path_mtime (move (f), mt); + } + } } // liba @@ -1136,10 +1207,24 @@ namespace build2 if (tsys == "win32-msvc") { if (s == nullptr && !sn.empty ()) - s = msvc_search_shared (ld, d, p, exist); + { + pair<libs*, bool> r (msvc_search_shared (ld, d, p, exist)); + + if (r.first != nullptr) + s = r.first; + else if (!r.second) + bs = false; + } if (a == nullptr && !an.empty ()) - a = msvc_search_static (ld, d, p, exist); + { + pair<liba*, bool> r (msvc_search_static (ld, d, p, exist)); + + if (r.first != nullptr) + a = r.first; + else if (!r.second) + ba = false; + } } // Look for binary-less libraries via pkg-config .pc files. Note that @@ -1156,7 +1241,10 @@ namespace build2 // is no binful variant. // pair<path, path> r ( - pkgconfig_search (d, p.proj, name, na && ns /* common */)); + pkgconfig_search (d, + p.proj, + name, + na && ns && ba && bs /* common */)); if (na && !r.first.empty ()) { @@ -1209,6 +1297,8 @@ namespace build2 // making it the only one to allow things to be overriden (e.g., // if build2 was moved or some such). // + // Note: build_install_lib is already normalized. + // usrd->insert (usrd->begin (), build_install_lib); } } @@ -1266,7 +1356,7 @@ namespace build2 // idea is that in .pc files that we generate, we copy those macros (or // custom ones) from *.export.poptions. // - // @@ Should we add .pc files as ad hoc members so pkconfig_save() can + // @@ Should we add .pc files as ad hoc members so pkgconfig_save() can // use their names when deriving -l-names (this would be especially // helpful for binless libraries to get hold of prefix/suffix, etc). // @@ -1387,7 +1477,7 @@ namespace build2 // @@ TODO: we currently always reload pkgconfig for lt (and below). // mark_cc (*lt); - lt->mtime (mt); + lt->mtime (mt); // Note: problematic, see below for details. // We can only load metadata from here since we can only do this // during the load phase. But it's also possible that a racing match @@ -1438,6 +1528,9 @@ namespace build2 return l; }; + target_lock al (lock (a)); + target_lock sl (lock (s)); + target_lock ll (lock (lt)); // Set lib{} group members to indicate what's available. Note that we @@ -1465,9 +1558,6 @@ namespace build2 ll.unlock (); } - target_lock al (lock (a)); - target_lock sl (lock (s)); - if (!al) a = nullptr; if (!sl) s = nullptr; @@ -1501,6 +1591,34 @@ namespace build2 if (s != nullptr) match_rule (sl, file_rule::rule_match); if (ll) { + // @@ Turns out this has a problem: file_rule won't match/execute + // group members. So what happens is that if we have two installed + // libraries, say lib{build2} that depends on lib{butl}, then + // lib{build2} will have lib{butl} as a prerequisite and file_rule + // that matches lib{build2} will update lib{butl} (also matched by + // file_rule), but not its members. Later, someone (for example, + // the newer() call in append_libraries()) will pick one of the + // members assuming it is executed and things will go sideways. + // + // For now we hacked around the issue but the long term solution is + // probably to add to the bin module a special rule that is + // registered on the global scope and matches the installed lib{} + // targets. This rule will have to both update prerequisites like + // the file_rule and group members like the lib_rule (or maybe it + // can skip prerequisites since one of the member will do that; in + // which case maybe we will be able to reuse lib_rule maybe with + // the "all members" flag or some such). A few additional + // notes/thoughts: + // + // - Will be able to stop inheriting lib{} from mtime_target. + // + // - Will need to register for perform_update/clean like in context + // as well as for configure as in the config module (feels like + // shouldn't need to register for dist). + // + // - Will need to test batches, immediate import thoroughly (this + // stuff is notoriously tricky to get right in all situations). + // match_rule (ll, file_rule::rule_match); // Also bless the library group with a "trust me it exists" timestamp. @@ -1509,6 +1627,8 @@ namespace build2 // won't match. // lt->mtime (mt); + + ll.unlock (); // Unlock group before members, for good measure. } return r; @@ -1550,5 +1670,97 @@ namespace build2 return r; } + + void common:: + append_diag_color_options (cstrings& args) const + { + switch (cclass) + { + case compiler_class::msvc: + { + // MSVC has the /diagnostics: option which has an undocumented value + // `color`. It's unclear from which version of MSVC this value is + // supported, but it works in 17.0, so let's start from there. + // + // Note that there is currently no way to disable color in the MSVC + // diagnostics specifically (the /diagnostics:* option values are + // cumulative and there doesn't seem to be a `color-` value). This + // is probably not a big deal since one can just disable the color + // globally (--no-diag-color). + // + // Note that clang-cl appears to use -fansi-escape-codes. See GH + // issue #312 for background. + // + // Note that MSVC ignores /diagnostics:color if diagnostics is + // written to a pipe. See GH issue #312 for details and a link to + // the MSVC bug report. + // + if (show_diag_color ()) + { + if (cvariant.empty () && + (cmaj > 19 || (cmaj == 19 && cmin >= 30))) + { + // Check for the prefix in case /diagnostics:color- gets added + // eventually. + // + if (!find_option_prefixes ({"/diagnostics:color", + "-diagnostics:color"}, args)) + { + args.push_back ("/diagnostics:color"); + } + } + } + + break; + } + case compiler_class::gcc: + { + // Enable/disable diagnostics color unless a custom option is + // specified. + // + // Supported from GCC 4.9 (8.1 on Windows) and (at least) from Clang + // 3.5. Clang supports -f[no]color-diagnostics in addition to the + // GCC's spelling. Note that to enable color on Windows Clang also + // needs -fansi-escape-codes. + // + if ( +#ifndef _WIN32 + ctype == compiler_type::gcc ? cmaj > 4 || (cmaj == 4 && cmin >= 9) : +#else + ctype == compiler_type::gcc ? cmaj > 8 || (cmaj == 8 && cmin >= 1) : +#endif + ctype == compiler_type::clang ? cmaj > 3 || (cmaj == 3 && cmin >= 5) : + false) + { + if (!(find_option_prefix ("-fdiagnostics-color", args) || + find_option ("-fno-diagnostics-color", args) || + find_option ("-fdiagnostics-plain-output", args) || + (ctype == compiler_type::clang && + (find_option ("-fcolor-diagnostics", args) || + find_option ("-fno-color-diagnostics", args))))) + { + // Omit -fno-diagnostics-color if stderr is not a terminal (we + // know there will be no color in this case and the option will + // just add noise, for example, in build logs). + // + if (const char* o = ( + show_diag_color () ? "-fdiagnostics-color" : + stderr_term ? "-fno-diagnostics-color" : + nullptr)) + { + args.push_back (o); + +#ifdef _WIN32 + if (ctype == compiler_type::clang && o[2] != 'n') + args.push_back ("-fansi-escape-codes"); +#endif + } + } + } + + break; + } + } + } } } diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx index c11fa7a..cb85632 100644 --- a/libbuild2/cc/common.hxx +++ b/libbuild2/cc/common.hxx @@ -32,10 +32,12 @@ namespace build2 { lang x_lang; - const char* x; // Module name ("c", "cxx"). - const char* x_name; // Compiler name ("c", "c++"). - const char* x_default; // Compiler default ("gcc", "g++"). - const char* x_pext; // Preprocessed source extension (".i", ".ii"). + const char* x; // Module name ("c", "cxx"). + const char* x_name; // Compiler name ("c", "c++"). + const char* x_obj_name; // Same for Objective-X ("obj-c", "obj-c++"). + const char* x_default; // Compiler default ("gcc", "g++"). + const char* x_pext; // Preprocessed source extension (".i", ".ii"). + const char* x_obj_pext; // Same for Objective-X (".mi", ".mii"). // Array of modules that can hint us the toolchain, terminate with // NULL. @@ -115,6 +117,7 @@ namespace build2 const variable& c_module_name; // cc.module_name const variable& c_importable; // cc.importable const variable& c_reprocess; // cc.reprocess + const variable& c_serialize; // cc.serialize const variable& x_preprocessed; // x.preprocessed const variable* x_symexport; // x.features.symexport @@ -163,6 +166,7 @@ namespace build2 // Cached values for some commonly-used variables/values. // + const compiler_id& cid; // x.id compiler_type ctype; // x.id.type const string& cvariant; // x.id.variant compiler_class cclass; // x.class @@ -196,34 +200,68 @@ namespace build2 build2::cc::importable_headers* importable_headers; // The order of sys_*_dirs is the mode entries first, followed by the - // compiler built-in entries, and finished off with any extra entries - // (e.g., fallback directories such as /usr/local/*). + // extra entries (e.g., /usr/local/*), followed by the compiler built-in + // entries. + // + // Note that even if we wanted to, we wouldn't be able to support extra + // trailing (after built-in) directories since we would need a portable + // equivalent of -idirafter for both headers and libraries. // const dir_paths& sys_lib_dirs; // x.sys_lib_dirs const dir_paths& sys_hdr_dirs; // x.sys_hdr_dirs const dir_paths* sys_mod_dirs; // compiler_info::sys_mod_dirs - size_t sys_lib_dirs_mode; // Number of leading mode entries (0 if none). + size_t sys_lib_dirs_mode; // Number of mode entries (0 if none). size_t sys_hdr_dirs_mode; size_t sys_mod_dirs_mode; - size_t sys_lib_dirs_extra; // First trailing extra entry (size if none). + size_t sys_lib_dirs_extra; // Number of extra entries (0 if none). size_t sys_hdr_dirs_extra; + // Note that x_obj is patched in by the x.objx module. So it stays NULL + // if Objective-X compilation is not enabled. Similarly for x_asp except + // here we don't have duality and it's purely to signal (by the c.as-cpp + // module) that it's enabled. + // const target_type& x_src; // Source target type (c{}, cxx{}). const target_type* x_mod; // Module target type (mxx{}), if any. + const target_type& x_inc; // Includable base target type (e.g., c_inc{}). + const target_type* x_obj; // Objective-X target type (m{}, mm{}). + const target_type* x_asp; // Assembler with CPP target type (S{}). + + // Check if an object (target, prerequisite, etc) is an Objective-X + // source. + // + template <typename T> + bool + x_objective (const T& t) const + { + return x_obj != nullptr && t.is_a (*x_obj); + } + + // Check if an object (target, prerequisite, etc) is an Assembler with + // C preprocessor source. + // + template <typename T> + bool + x_assembler_cpp (const T& t) const + { + return x_asp != nullptr && t.is_a (*x_asp); + } // Array of target types that are considered the X-language headers // (excluding h{} except for C). Keep them in the most likely to appear // order with the "real header" first and terminated with NULL. // - const target_type* const* x_hdr; + const target_type* const* x_hdrs; + // Check if an object (target, prerequisite, etc) is a header. + // template <typename T> bool x_header (const T& t, bool c_hdr = true) const { - for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht) + for (const target_type* const* ht (x_hdrs); *ht != nullptr; ++ht) if (t.is_a (**ht)) return true; @@ -234,7 +272,7 @@ namespace build2 // extensions to target types. Keep them in the most likely to appear // order and terminate with NULL. // - const target_type* const* x_inc; + const target_type* const* x_incs; // Aggregate-like constructor with from-base support. // @@ -242,8 +280,7 @@ namespace build2 const char* compile, const char* link, const char* install, - compiler_type ct, - const string& cv, + const compiler_id& ci, compiler_class cl, uint64_t mj, uint64_t mi, uint64_t vmj, uint64_t vmi, @@ -262,13 +299,14 @@ namespace build2 size_t sle, size_t she, const target_type& src, const target_type* mod, - const target_type* const* hdr, - const target_type* const* inc) + const target_type& inc, + const target_type* const* hdrs, + const target_type* const* incs) : config_data (cd), x_compile (compile), x_link (link), x_install (install), - ctype (ct), cvariant (cv), cclass (cl), + cid (ci), ctype (ci.type), cvariant (ci.variant), cclass (cl), cmaj (mj), cmin (mi), cvmaj (vmj), cvmin (vmi), cpath (path), cmode (mode), @@ -283,7 +321,9 @@ namespace build2 sys_lib_dirs_mode (slm), sys_hdr_dirs_mode (shm), sys_mod_dirs_mode (smm), sys_lib_dirs_extra (sle), sys_hdr_dirs_extra (she), - x_src (src), x_mod (mod), x_hdr (hdr), x_inc (inc) {} + x_src (src), x_mod (mod), x_inc (inc), + x_obj (nullptr), x_asp (nullptr), + x_hdrs (hdrs), x_incs (incs) {} }; class LIBBUILD2_CC_SYMEXPORT common: public data @@ -421,13 +461,16 @@ namespace build2 // Alternative search logic for VC (msvc.cxx). // - bin::liba* + // The second half is false if we should poison the binless search via + // the common .pc file. + // + pair<bin::liba*, bool> msvc_search_static (const process_path&, const dir_path&, const prerequisite_key&, bool existing) const; - bin::libs* + pair<bin::libs*, bool> msvc_search_shared (const process_path&, const dir_path&, const prerequisite_key&, @@ -464,6 +507,11 @@ namespace build2 const dir_paths&, const dir_paths&, pair<bool, bool>) const; + + // Append compiler-specific diagnostics color options as necessary. + // + void + append_diag_color_options (cstrings&) const; }; } } diff --git a/libbuild2/cc/common.txx b/libbuild2/cc/common.txx index f55072c..8c80686 100644 --- a/libbuild2/cc/common.txx +++ b/libbuild2/cc/common.txx @@ -19,13 +19,14 @@ namespace build2 bool exist, tracer& trace) { - auto p (ctx.targets.insert_locked (T::static_type, - move (dir), - path_cast<dir_path> (out.effect), - name, - move (ext), - target_decl::implied, - trace)); + auto p (ctx.targets.insert_locked ( + T::static_type, + move (dir), + dir_path (out.effect_string ()).normalize (), + name, + move (ext), + target_decl::implied, + trace)); if (exist && p.second) throw non_existent_library {p.first.template as<mtime_target> ()}; diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 9720769..7629ed5 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -3,6 +3,7 @@ #include <libbuild2/cc/compile-rule.hxx> +#include <cerrno> #include <cstdlib> // exit() #include <cstring> // strlen(), strchr(), strncmp() @@ -175,7 +176,7 @@ namespace build2 if (s == "includes") return preprocessed::includes; if (s == "modules") return preprocessed::modules; if (s == "all") return preprocessed::all; - throw invalid_argument ("invalid preprocessed value '" + s + "'"); + throw invalid_argument ("invalid preprocessed value '" + s + '\''); } // Return true if the compiler supports -isystem (GCC class) or @@ -231,7 +232,8 @@ namespace build2 // Note that we don't really need this for clean (where we only need // unrefined unit type) so we could make this update-only. But let's keep - // it simple for now. + // it simple for now. Note that now we do need the source prerequisite + // type in clean to deal with Objective-X. // struct compile_rule::match_data { @@ -293,24 +295,25 @@ namespace build2 void compile_rule:: append_sys_hdr_options (T& args) const { - assert (sys_hdr_dirs_extra <= sys_hdr_dirs.size ()); + assert (sys_hdr_dirs_mode + sys_hdr_dirs_extra <= sys_hdr_dirs.size ()); // Note that the mode options are added as part of cmode. // auto b (sys_hdr_dirs.begin () + sys_hdr_dirs_mode); - auto m (sys_hdr_dirs.begin () + sys_hdr_dirs_extra); - auto e (sys_hdr_dirs.end ()); + auto x (b + sys_hdr_dirs_extra); + // Add extras. + // // 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::gcc ? "-isystem" : cclass == compiler_class::msvc ? (isystem (*this) ? "/external:I" : "/I") : "-I", - m, e, + b, x, [] (const dir_path& d) {return d.string ().c_str ();}); // For MSVC if we have no INCLUDE environment variable set, then we @@ -326,7 +329,7 @@ namespace build2 { append_option_values ( args, "/I", - b, m, + x, sys_hdr_dirs.end (), [] (const dir_path& d) {return d.string ().c_str ();}); } } @@ -351,6 +354,35 @@ namespace build2 case lang::c: o1 = "/TC"; break; case lang::cxx: o1 = "/TP"; break; } + + // Note: /interface and /internalPartition are in addition to /TP. + // + switch (md.type) + { + case unit_type::non_modular: + case unit_type::module_impl: + { + break; + } + case unit_type::module_intf: + case unit_type::module_intf_part: + { + o2 = "/interface"; + break; + } + case unit_type::module_impl_part: + { + o2 = "/internalPartition"; + break; + } + case unit_type::module_header: + { + //@@ MODHDR TODO: /exportHeader + assert (false); + break; + } + } + break; } case compiler_class::gcc: @@ -369,11 +401,20 @@ namespace build2 case unit_type::module_impl: { o1 = "-x"; - switch (x_lang) + + if (x_assembler_cpp (md.src)) + o2 = "assembler-with-cpp"; + else { - case lang::c: o2 = "c"; break; - case lang::cxx: o2 = "c++"; break; + bool obj (x_objective (md.src)); + + switch (x_lang) + { + case lang::c: o2 = obj ? "objective-c" : "c"; break; + case lang::cxx: o2 = obj ? "objective-c++" : "c++"; break; + } } + break; } case unit_type::module_intf: @@ -413,9 +454,11 @@ namespace build2 default: assert (false); } + break; } } + break; } } @@ -472,9 +515,11 @@ namespace build2 // For a header unit we check the "real header" plus the C header. // - if (ut == unit_type::module_header ? p.is_a (**x_hdr) || p.is_a<h> () : - ut == unit_type::module_intf ? p.is_a (*x_mod) : - p.is_a (x_src)) + if (ut == unit_type::module_header ? p.is_a (**x_hdrs) || p.is_a<h> () : + ut == unit_type::module_intf ? p.is_a (*x_mod) : + p.is_a (x_src) || + (x_asp != nullptr && p.is_a (*x_asp)) || + (x_obj != nullptr && p.is_a (*x_obj))) { // Save in the target's auxiliary storage. // @@ -530,6 +575,8 @@ namespace build2 if (find (d.ls.begin (), d.ls.end (), &l) != d.ls.end ()) return false; + // Note: go straight for the public variable pool. + // const variable& var ( com ? c_export_poptions @@ -781,6 +828,8 @@ namespace build2 // if (const scope* rs = l.base_scope ().root_scope ()) { + // Note: go straight for the public variable pool. + // const variable& var ( com ? c_export_poptions @@ -929,7 +978,9 @@ namespace build2 // // Note: ut is still unrefined. // - if (ut == unit_type::module_intf && cast_true<bool> (t[b_binless])) + if ((ut == unit_type::module_intf || + ut == unit_type::module_intf_part || + ut == unit_type::module_impl_part) && cast_true<bool> (t[b_binless])) { // The module interface unit can be the same as an implementation // (e.g., foo.mxx and foo.cxx) which means obj*{} targets could @@ -990,6 +1041,12 @@ namespace build2 // to match it if we may need its modules or importable headers // (see search_modules(), make_header_sidebuild() for details). // + // Well, that was the case until we've added support for immediate + // importation of libraries, which happens during the load phase + // and natually leaves the library unmatched. While we could have + // returned from search_library() an indication of whether the + // library has been matched, this doesn't seem worth the trouble. + // if (p.proj ()) { pt = search_library (a, @@ -997,8 +1054,10 @@ namespace build2 usr_lib_dirs, p.prerequisite); +#if 0 if (pt != nullptr && !modules) continue; +#endif } if (pt == nullptr) @@ -1026,7 +1085,8 @@ namespace build2 { pt = &p.search (t); - if (a.operation () == clean_id && !pt->dir.sub (rs.out_path ())) + if (pt == dir || + (a.operation () == clean_id && !pt->dir.sub (rs.out_path ()))) continue; } @@ -1118,12 +1178,14 @@ namespace build2 // this can very well be happening in parallel. But that's not a // problem since fsdir{}'s update is idempotent. // - fsdir_rule::perform_update_direct (a, t); + fsdir_rule::perform_update_direct (a, *dir); } // Note: the leading '@' is reserved for the module map prefix (see // extract_modules()) and no other line must start with it. // + // NOTE: see also the predefs rule if changing anything here. + // depdb dd (tp + ".d"); // First should come the rule name/version. @@ -1288,7 +1350,7 @@ namespace build2 // l5 ([&]{trace << "extracting headers from " << src;}); auto& is (tu.module_info.imports); - psrc = extract_headers (a, bs, t, li, src, md, dd, u, mt, is); + extract_headers (a, bs, t, li, src, md, dd, u, mt, is, psrc); is.clear (); // No longer needed. } @@ -1343,6 +1405,10 @@ namespace build2 // if (mt != timestamp_nonexistent) { + // Appended to by to_module_info() below. + // + tu.module_info.imports.clear (); + u = false; md.touch = true; } @@ -1427,24 +1493,6 @@ namespace build2 extract_modules (a, bs, t, li, tts, src, md, move (tu.module_info), dd, u); - - // Currently in VC module interface units must be compiled from - // the original source (something to do with having to detect and - // store header boundaries in the .ifc files). - // - // @@ MODHDR MSVC: should we do the same for header units? I guess - // we will figure it out when MSVC supports header units. - // - // @@ TMP: probably outdated. Probably the same for partitions. - // - // @@ See also similar check in extract_headers(), existing entry - // case. - // - if (ctype == compiler_type::msvc) - { - if (ut == unit_type::module_intf) - psrc.second = false; - } } } @@ -1463,7 +1511,7 @@ namespace build2 // to keep re-validating the file on every subsequent dry-run as well // on the real run). // - if (u && dd.reading () && !ctx.dry_run) + if (u && dd.reading () && !ctx.dry_run_option) dd.touch = timestamp_unknown; dd.close (false /* mtime_check */); @@ -1525,10 +1573,13 @@ namespace build2 switch (a) { case perform_update_id: return move (md); - case perform_clean_id: return [this] (action a, const target& t) + case perform_clean_id: { - return perform_clean (a, t); - }; + return [this, srct = &md.src.type ()] (action a, const target& t) + { + return perform_clean (a, t, *srct); + }; + } default: return noop_recipe; // Configure update. } } @@ -1820,7 +1871,7 @@ namespace build2 // Any unhandled io_error is handled by the caller as a generic module // mapper io error. Returning false terminates the communication. // - struct compile_rule::module_mapper_state //@@ gcc_module_mapper_state + struct compile_rule::gcc_module_mapper_state { size_t skip; // Number of depdb entries to skip. size_t header_units = 0; // Number of header units imported. @@ -1831,15 +1882,20 @@ namespace build2 optional<const build2::cc::translatable_headers*> translatable_headers; small_vector<string, 2> batch; // Reuse buffers. + size_t batch_n = 0; - module_mapper_state (size_t s, module_imports& i) + gcc_module_mapper_state (size_t s, module_imports& i) : skip (s), imports (i) {} }; - bool compile_rule:: - gcc_module_mapper (module_mapper_state& st, + // The module mapper is called on one line of input at a time. It should + // return nullopt if another line is expected (batch), false if the mapper + // interaction should be terminated, and true if it should be continued. + // + optional<bool> compile_rule:: + gcc_module_mapper (gcc_module_mapper_state& st, action a, const scope& bs, file& t, linfo li, - ifdstream& is, + const string& l, ofdstream& os, depdb& dd, bool& update, bool& bad_error, optional<prefix_map>& pfx_map, srcout_map& so_map) const @@ -1855,35 +1911,40 @@ namespace build2 // Read in the entire batch trying hard to reuse the buffers. // - auto& batch (st.batch); - size_t batch_n (0); + small_vector<string, 2>& batch (st.batch); + size_t& batch_n (st.batch_n); - for (;;) + // Add the next line. + // { if (batch.size () == batch_n) - batch.push_back (string ()); - - string& r (batch[batch_n]); - - if (eof (getline (is, r))) - break; + batch.push_back (l); + else + batch[batch_n] = l; batch_n++; + } - if (r.back () != ';') - break; + // Check if more is expected in this batch. + // + { + string& r (batch[batch_n - 1]); - // Strip the trailing `;` word. - // - r.pop_back (); - r.pop_back (); - } + if (r.back () == ';') + { + // Strip the trailing `;` word. + // + r.pop_back (); + r.pop_back (); - if (batch_n == 0) // EOF - return false; + return nullopt; + } + } if (verb >= 3) { + // It doesn't feel like buffering this would be useful. + // // Note that we show `;` in requests/responses so that the result // could be replayed. // @@ -1905,23 +1966,211 @@ namespace build2 for (size_t i (0); i != batch_n; ++i) { string& r (batch[i]); + size_t rn (r.size ()); + + // The protocol uses a peculiar quoting/escaping scheme that can be + // summarized as follows (see the libcody documentation for details): + // + // - Words are seperated with spaces and/or tabs. + // + // - Words need not be quoted if they only containing characters from + // the [-+_/%.A-Za-z0-9] set. + // + // - Otherwise words need to be single-quoted. + // + // - Inside single-quoted words, the \n \t \' and \\ escape sequences + // are recognized. + // + // Note that we currently don't treat abutted quotes (as in a' 'b) as + // a single word (it doesn't seem plausible that we will ever receive + // something like this). + // + size_t b (0), e (0), n; bool q; // Next word. + + auto next = [&r, rn, &b, &e, &n, &q] () -> size_t + { + if (b != e) + b = e; + + // Skip leading whitespaces. + // + for (; b != rn && (r[b] == ' ' || r[b] == '\t'); ++b) ; + + if (b != rn) + { + q = (r[b] == '\''); + + // Find first trailing whitespace or closing quote. + // + for (e = b + 1; e != rn; ++e) + { + // Note that we deal with invalid quoting/escaping in unquote(). + // + switch (r[e]) + { + case ' ': + case '\t': + if (q) + continue; + else + break; + case '\'': + if (q) + { + ++e; // Include closing quote (hopefully). + break; + } + else + { + assert (false); // Abutted quote. + break; + } + case '\\': + if (++e != rn) // Skip next character (hopefully). + continue; + else + break; + default: + continue; + } + + break; + } + + n = e - b; + } + else + { + q = false; + e = rn; + n = 0; + } + + return n; + }; - // @@ TODO: quoting and escaping. + // Unquote into tmp the current word returning false if malformed. // - size_t b (0), e (0), n; // Next word. + auto unquote = [&r, &b, &n, &q, &tmp] (bool clear = true) -> bool + { + if (q && n > 1) + { + size_t e (b + n - 1); - auto next = [&r, &b, &e, &n] () -> size_t + if (r[b] == '\'' && r[e] == '\'') + { + if (clear) + tmp.clear (); + + size_t i (b + 1); + for (; i != e; ++i) + { + char c (r[i]); + if (c == '\\') + { + if (++i == e) + { + i = 0; + break; + } + + c = r[i]; + if (c == 'n') c = '\n'; + else if (c == 't') c = '\t'; + } + tmp += c; + } + + if (i == e) + return true; + } + } + + return false; + }; + +#if 0 +#define UNQUOTE(x, y) \ + r = x; rn = r.size (); b = e = 0; \ + assert (next () && unquote () && tmp == y) + + UNQUOTE ("'foo bar'", "foo bar"); + UNQUOTE (" 'foo bar' ", "foo bar"); + UNQUOTE ("'foo\\\\bar'", "foo\\bar"); + UNQUOTE ("'\\'foo bar'", "'foo bar"); + UNQUOTE ("'foo bar\\''", "foo bar'"); + UNQUOTE ("'\\'foo\\\\bar\\''", "'foo\\bar'"); + + fail << "all good"; +#endif + + // Escape if necessary the specified string and append to r. + // + auto escape = [&r] (const string& s) { - return (n = next_word (r, b, e, ' ', '\t')); + size_t b (0), e, n (s.size ()); + while (b != n && (e = s.find_first_of ("\\'\n\t", b)) != string::npos) + { + r.append (s, b, e - b); // Preceding chunk. + + char c (s[e]); + r += '\\'; + r += (c == '\n' ? 'n' : c == '\t' ? 't' : c); + b = e + 1; + } + + if (b != n) + r.append (s, b, e); // Final chunk. }; + // Quote and escape if necessary the specified string and append to r. + // + auto quote = [&r, &escape] (const string& s) + { + if (find_if (s.begin (), s.end (), + [] (char c) + { + return !((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + c == '-' || c == '_' || c == '/' || + c == '.' || c == '+' || c == '%'); + }) == s.end ()) + { + r += s; + } + else + { + r += '\''; + escape (s); + r += '\''; + } + }; + +#if 0 +#define QUOTE(x, y) \ + r.clear (); quote (x); \ + assert (r == y) + + QUOTE ("foo/Bar-7.h", "foo/Bar-7.h"); + + QUOTE ("foo bar", "'foo bar'"); + QUOTE ("foo\\bar", "'foo\\\\bar'"); + QUOTE ("'foo bar", "'\\'foo bar'"); + QUOTE ("foo bar'", "'foo bar\\''"); + QUOTE ("'foo\\bar'", "'\\'foo\\\\bar\\''"); + + fail << "all good"; +#endif + next (); // Request name. - auto name = [&r, b, n] (const char* c) -> bool + auto name = [&r, b, n, q] (const char* c) -> bool { // We can reasonably assume a command will never be quoted. // - return (r.compare (b, n, c) == 0 && + return (!q && + r.compare (b, n, c) == 0 && (r[n] == ' ' || r[n] == '\t' || r[n] == '\0')); }; @@ -1970,7 +2219,17 @@ namespace build2 if (next ()) { - path f (r, b, n); + path f; + if (!q) + f = path (r, b, n); + else if (unquote ()) + f = path (tmp); + else + { + r = "ERROR 'malformed quoting/escaping in request'"; + continue; + } + bool exists (true); // The TU path we pass to the compiler is always absolute so any @@ -1981,8 +2240,9 @@ namespace build2 // if (exists && f.relative ()) { - tmp.assign (r, b, n); - r = "ERROR relative header path '"; r += tmp; r += '\''; + r = "ERROR 'relative header path "; + escape (f.string ()); + r += '\''; continue; } @@ -2029,7 +2289,7 @@ namespace build2 if (remapped) { - r = "ERROR remapping of headers not supported"; + r = "ERROR 'remapping of headers not supported'"; continue; } @@ -2089,8 +2349,8 @@ namespace build2 // // Note: if ht is NULL, f is still valid. // - r = "ERROR unable to update header '"; - r += (ht != nullptr ? ht->path () : f).string (); + r = "ERROR 'unable to update header "; + escape ((ht != nullptr ? ht->path () : f).string ()); r += '\''; continue; } @@ -2225,17 +2485,27 @@ namespace build2 // original (which we may need to normalize when we read // this mapping in extract_headers()). // - tmp = "@ "; tmp.append (r, b, n); tmp += ' '; tmp += bp; + // @@ This still breaks if the header path contains spaces. + // GCC bug 110153. + // + tmp = "@ "; + if (!q) tmp.append (r, b, n); + else unquote (false /* clear */); // Can't fail. + tmp += ' '; + tmp += bp; + dd.expect (tmp); st.header_units++; } - r = "PATHNAME "; r += bp; + r = "PATHNAME "; + quote (bp); } catch (const failed&) { r = "ERROR 'unable to update header unit for "; - r += hs; r += '\''; + escape (hs); + r += '\''; continue; } } @@ -2261,7 +2531,7 @@ namespace build2 // Truncate the response batch and terminate the communication (see // also libcody issue #22). // - tmp.assign (r, b, n); + tmp.assign (r, b, n); // Request name (unquoted). r = "ERROR '"; r += w; r += ' '; r += tmp; r += '\''; batch_n = i + 1; term = true; @@ -2277,6 +2547,9 @@ namespace build2 // Write the response batch. // + // @@ It's theoretically possible that we get blocked writing the + // response while the compiler gets blocked writing the diagnostics. + // for (size_t i (0);; ) { string& r (batch[i]); @@ -2297,6 +2570,8 @@ namespace build2 os.flush (); + batch_n = 0; // Start a new batch. + return !term; } @@ -2651,7 +2926,7 @@ namespace build2 rs = !exists ? string ("INCLUDE") : ("ERROR unable to update header '" + - (ht != nullptr ? ht->path () : f).string () + "'"); + (ht != nullptr ? ht->path () : f).string () + '\''); bad_error = true; break; @@ -2729,7 +3004,7 @@ namespace build2 } catch (const failed&) { - rs = "ERROR unable to update header unit '" + hp + "'"; + rs = "ERROR unable to update header unit '" + hp + '\''; bad_error = true; break; } @@ -2839,7 +3114,7 @@ namespace build2 // single "version" of a header. Seems reasonable. // // Note also that while it would have been nice to have a unified cc - // cache, the map_extension() call is passed x_inc which is module- + // cache, the map_extension() call is passed x_incs which is module- // specific. In other words, we may end up mapping the same header to // two different targets depending on whether it is included from, say, // C or C++ translation unit. We could have used a unified cache for @@ -2903,7 +3178,7 @@ namespace build2 fp, cache, norm, [this] (const scope& bs, const string& n, const string& e) { - return map_extension (bs, n, e, x_inc); + return map_extension (bs, n, e, x_incs); }, h::static_type, [this, &d] (action a, const scope& bs, const target& t) @@ -2957,16 +3232,18 @@ namespace build2 return inject_file (trace, "header", a, t, pt, mt, fail); } - // Extract and inject header dependencies. Return the preprocessed source - // file as well as an indication if it is usable for compilation (see - // below for details). + // Extract and inject header dependencies. Return (in result) the + // preprocessed source file as well as an indication if it is usable for + // compilation (see below for details). Note that result is expected to + // be initialized to {entry (), false}. Not using return type due to + // GCC bug #107555. // // This is also the place where we handle header units which are a lot // more like auto-generated headers than modules. In particular, if a // header unit BMI is out-of-date, then we have to re-preprocess this // translation unit. // - pair<file_cache::entry, bool> compile_rule:: + void compile_rule:: extract_headers (action a, const scope& bs, file& t, @@ -2976,7 +3253,8 @@ namespace build2 depdb& dd, bool& update, timestamp mt, - module_imports& imports) const + module_imports& imports, + pair<file_cache::entry, bool>& result) const { tracer trace (x, "compile_rule::extract_headers"); @@ -2989,9 +3267,16 @@ namespace build2 file_cache::entry psrc; bool puse (true); + // Preprocessed file extension. + // + const char* pext (x_assembler_cpp (src) ? ".Si" : + x_objective (src) ? x_obj_pext : + x_pext); + // Preprocesor mode that preserves as much information as possible while // still performing inclusions. Also serves as a flag indicating whether - // this compiler uses the separate preprocess and compile setup. + // this (non-MSVC) compiler uses the separate preprocess and compile + // setup. // const char* pp (nullptr); @@ -3002,7 +3287,16 @@ namespace build2 // -fdirectives-only is available since GCC 4.3.0. // if (cmaj > 4 || (cmaj == 4 && cmin >= 3)) - pp = "-fdirectives-only"; + { + // Note that for assembler-with-cpp GCC currently forces full + // preprocessing in (what appears to be) an attempt to paper over + // a deeper issue (see GCC bug 109534). If/when that bug gets + // fixed, we can enable this on our side. Note that Clang's + // -frewrite-includes also has issues (see below). + // + if (!x_assembler_cpp (src)) + pp = "-fdirectives-only"; + } break; } @@ -3011,7 +3305,16 @@ namespace build2 // -frewrite-includes is available since Clang 3.2.0. // if (cmaj > 3 || (cmaj == 3 && cmin >= 2)) - pp = "-frewrite-includes"; + { + // While Clang's -frewrite-includes appears to work, there are + // some issues with correctly tracking location information + // (manifests itself as wrong line numbers in debug info, for + // example). The result also appears to reference the .Si file + // instead of the original source file for some reason. + // + if (!x_assembler_cpp (src)) + pp = "-frewrite-includes"; + } break; } @@ -3092,7 +3395,7 @@ namespace build2 // // GCC's -fdirective-only, on the other hand, processes all the // directives so they are gone from the preprocessed source. Here is - // what we are going to do to work around this: we will detect if any + // what we are going to do to work around this: we will sense if any // diagnostics has been written to stderr on the -E run. If that's the // case (but the compiler indicated success) then we assume they are // warnings and disable the use of the preprocessed output for @@ -3130,7 +3433,9 @@ namespace build2 // not found, and there is no problem with outdated generated headers // since we update/remap them before the compiler has a chance to read // them. Overall, this "dependency mapper" approach is how it should - // have been done from the beginning. + // have been done from the beginning. Note: that's the ideal world, + // the reality is that the required mapper extensions are not (yet) + // in libcody/GCC. // Note: diagnostics sensing is currently only supported if dependency // info is written to a file (see above). @@ -3179,13 +3484,13 @@ namespace build2 // The gen argument to init_args() is in/out. The caller signals whether // to force the generated header support and on return it signals - // whether this support is enabled. The first call to init_args is - // expected to have gen false. + // whether this support is enabled. If gen is false, then stderr is + // expected to be either discarded or merged with sdtout. // // Return NULL if the dependency information goes to stdout and a // pointer to the temporary file path otherwise. // - auto init_args = [a, &t, ot, li, reprocess, + auto init_args = [a, &t, ot, li, reprocess, pext, &src, &md, &psrc, &sense_diag, &mod_mapper, &bs, pp, &env, &args, &args_gen, &args_i, &out, &drm, &so_map, this] @@ -3331,16 +3636,6 @@ namespace build2 // Some compile options (e.g., -std, -m) affect the preprocessor. // - // Currently Clang supports importing "header modules" even when in - // the TS mode. And "header modules" support macros which means - // imports have to be resolved during preprocessing. Which poses a - // bit of a chicken and egg problem for us. For now, the workaround - // is to remove the -fmodules-ts option when preprocessing. Hopefully - // there will be a "pure modules" mode at some point. - // - // @@ MODHDR Clang: should be solved with the dynamic module mapper - // if/when Clang supports it? - // // Don't treat warnings as errors. // @@ -3369,11 +3664,18 @@ namespace build2 append_options (args, cmode); append_sys_hdr_options (args); // Extra system header dirs (last). + // Note that for MSVC stderr is merged with stdout and is then + // parsed, so no append_diag_color_options() call. + // See perform_update() for details on the choice of options. // + // NOTE: see also the predefs rule if adding anything here. + // { - bool sc (find_option_prefix ("/source-charset:", args)); - bool ec (find_option_prefix ("/execution-charset:", args)); + bool sc (find_option_prefixes ( + {"/source-charset:", "-source-charset:"}, args)); + bool ec (find_option_prefixes ( + {"/execution-charset:", "-execution-charset:"}, args)); if (!sc && !ec) args.push_back ("/utf-8"); @@ -3389,15 +3691,18 @@ namespace build2 if (cvariant != "clang" && isystem (*this)) { - if (find_option_prefix ("/external:I", args) && - !find_option_prefix ("/external:W", args)) + if (find_option_prefixes ({"/external:I", "-external:I"}, args) && + !find_option_prefixes ({"/external:W", "-external:W"}, args)) args.push_back ("/external:W0"); } - if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) + if (x_lang == lang::cxx && + !find_option_prefixes ({"/EH", "-EH"}, args)) args.push_back ("/EHsc"); - if (!find_option_prefixes ({"/MD", "/MT"}, args)) + // NOTE: see similar code in search_modules(). + // + if (!find_option_prefixes ({"/MD", "/MT", "-MD", "-MT"}, args)) args.push_back ("/MD"); args.push_back ("/P"); // Preprocess to file. @@ -3408,7 +3713,7 @@ namespace build2 msvc_sanitize_cl (args); - psrc = ctx.fcache.create (t.path () + x_pext, !modules); + psrc = ctx.fcache->create (t.path () + pext, !modules); if (fc) { @@ -3427,12 +3732,18 @@ namespace build2 } case compiler_class::gcc: { - append_options (args, cmode, - cmode.size () - (modules && clang ? 1 : 0)); + append_options (args, cmode); append_sys_hdr_options (args); // Extra system header dirs (last). + // If not gen, then stderr is discarded. + // + if (gen) + append_diag_color_options (args); + // See perform_update() for details on the choice of options. // + // NOTE: see also the predefs rule if adding anything here. + // if (!find_option_prefix ("-finput-charset=", args)) args.push_back ("-finput-charset=UTF-8"); @@ -3444,8 +3755,7 @@ namespace build2 if (ctype == compiler_type::clang && tsys == "win32-msvc") { - initializer_list<const char*> os {"-nostdlib", "-nostartfiles"}; - if (!find_options (os, cmode) && !find_options (os, args)) + if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) { args.push_back ("-D_MT"); args.push_back ("-D_DLL"); @@ -3555,7 +3865,7 @@ namespace build2 // Preprocessor output. // - psrc = ctx.fcache.create (t.path () + x_pext, !modules); + psrc = ctx.fcache->create (t.path () + pext, !modules); args.push_back ("-o"); args.push_back (psrc.path ().string ().c_str ()); } @@ -3845,13 +4155,13 @@ namespace build2 // If modules are enabled, then we keep the preprocessed output // around (see apply() for details). // - // See apply() for details on the extra MSVC check. - // - return modules && (ctype != compiler_type::msvc || - md.type != unit_type::module_intf) - ? make_pair (ctx.fcache.create_existing (t.path () + x_pext), - true) - : make_pair (file_cache::entry (), false); + if (modules) + { + result.first = ctx.fcache->create_existing (t.path () + pext); + result.second = true; + } + + return; } // This can be a header or a header unit (mapping). @@ -3904,7 +4214,7 @@ namespace build2 // Bail out early if we have deferred a failure. // - return make_pair (file_cache::entry (), false); + return; } } } @@ -3930,6 +4240,12 @@ namespace build2 process pr; + // We use the fdstream_mode::skip mode on stdout (cannot be used + // on both) and so dbuf must be destroyed (closed) first. + // + ifdstream is (ifdstream::badbit); + diag_buffer dbuf (ctx); + try { // Assume the preprocessed output (if produced) is usable @@ -3950,217 +4266,229 @@ namespace build2 // bool good_error (false), bad_error (false); - // If we have no generated header support, then suppress all - // diagnostics (if things go badly we will restart with this - // support). - // - if (drmp == nullptr) // Dependency info goes to stdout. + if (mod_mapper) // Dependency info is implied by mapper requests. { - assert (!sense_diag); // Note: could support with fdselect(). + assert (gen && !sense_diag); // Not used in this mode. - // For VC with /P the dependency info and diagnostics all go - // to stderr so redirect it to stdout. + // Note that here we use the skip mode on the diagnostics + // stream which means we have to use own instance of stdout + // stream for the correct destruction order (see below). // - pr = process ( - cpath, - args.data (), - 0, - -1, - cclass == compiler_class::msvc ? 1 : gen ? 2 : -2, - nullptr, // CWD - env.empty () ? nullptr : env.data ()); - } - else // Dependency info goes to a temporary file. - { pr = process (cpath, - args.data (), - mod_mapper ? -1 : 0, - mod_mapper ? -1 : 2, // Send stdout to stderr. - gen ? 2 : sense_diag ? -1 : -2, + args, + -1, + -1, + diag_buffer::pipe (ctx), nullptr, // CWD env.empty () ? nullptr : env.data ()); - // Monitor for module mapper requests and/or diagnostics. If - // diagnostics is detected, mark the preprocessed output as - // unusable for compilation. - // - if (mod_mapper || sense_diag) + dbuf.open (args[0], + move (pr.in_efd), + fdstream_mode::non_blocking | + fdstream_mode::skip); + try { - module_mapper_state mm_state (skip_count, imports); + gcc_module_mapper_state mm_state (skip_count, imports); + + // Note that while we read both streams until eof in normal + // circumstances, we cannot use fdstream_mode::skip for the + // exception case on both of them: we may end up being + // blocked trying to read one stream while the process may + // be blocked writing to the other. So in case of an + // exception we only skip the diagnostics and close the + // mapper stream hard. The latter (together with closing of + // the stdin stream) should happen first so the order of + // the following variable is important. + // + // Note also that we open the stdin stream in the blocking + // mode. + // + ifdstream is (move (pr.in_ofd), + fdstream_mode::non_blocking, + ifdstream::badbit); // stdout + ofdstream os (move (pr.out_fd)); // stdin (badbit|failbit) + + // Read until we reach EOF on all streams. + // + // Note that if dbuf is not opened, then we automatically + // get an inactive nullfd entry. + // + fdselect_set fds {is.fd (), dbuf.is.fd ()}; + fdselect_state& ist (fds[0]); + fdselect_state& dst (fds[1]); - const char* w (nullptr); - try + bool more (false); + for (string l; ist.fd != nullfd || dst.fd != nullfd; ) { - // For now we don't need to do both so let's use a simpler - // blocking implementation. Note that the module mapper - // also needs to be adjusted when switching to the - // non-blocking version. + // @@ Currently we will accept a (potentially truncated) + // line that ends with EOF rather than newline. // -#if 1 - assert (mod_mapper != sense_diag); - - if (mod_mapper) + if (ist.fd != nullfd && getline_non_blocking (is, l)) { - w = "module mapper request"; - - // Note: the order is important (see the non-blocking - // verison for details). - // - ifdstream is (move (pr.in_ofd), - fdstream_mode::skip, - ifdstream::badbit); - ofdstream os (move (pr.out_fd)); - - do + if (eof (is)) { - if (!gcc_module_mapper (mm_state, - a, bs, t, li, - is, os, - dd, update, bad_error, - pfx_map, so_map)) - break; - - } while (!is.eof ()); + os.close (); + is.close (); - os.close (); - is.close (); - } + if (more) + throw_generic_ios_failure (EIO, "unexpected EOF"); - if (sense_diag) - { - w = "diagnostics"; - ifdstream is (move (pr.in_efd), fdstream_mode::skip); - puse = puse && (is.peek () == ifdstream::traits_type::eof ()); - is.close (); - } -#else - fdselect_set fds; - auto add = [&fds] (const auto_fd& afd) -> fdselect_state* - { - int fd (afd.get ()); - fdmode (fd, fdstream_mode::non_blocking); - fds.push_back (fd); - return &fds.back (); - }; - - // Note that while we read both streams until eof in - // normal circumstances, we cannot use fdstream_mode::skip - // for the exception case on both of them: we may end up - // being blocked trying to read one stream while the - // process may be blocked writing to the other. So in case - // of an exception we only skip the diagnostics and close - // the mapper stream hard. The latter should happen first - // so the order of the following variable is important. - // - ifdstream es; - ofdstream os; - ifdstream is; - - fdselect_state* ds (nullptr); - if (sense_diag) - { - w = "diagnostics"; - ds = add (pr.in_efd); - es.open (move (pr.in_efd), fdstream_mode::skip); - } - - fdselect_state* ms (nullptr); - if (mod_mapper) - { - w = "module mapper request"; - ms = add (pr.in_ofd); - is.open (move (pr.in_ofd)); - os.open (move (pr.out_fd)); // Note: blocking. - } - - // Set each state pointer to NULL when the respective - // stream reaches eof. - // - while (ds != nullptr || ms != nullptr) - { - w = "output"; - ifdselect (fds); - - // First read out the diagnostics in case the mapper - // interaction produces more. To make sure we don't get - // blocked by full stderr, the mapper should only handle - // one request at a time. - // - if (ds != nullptr && ds->ready) + ist.fd = nullfd; + } + else { - w = "diagnostics"; - - for (char buf[4096];;) - { - streamsize c (sizeof (buf)); - streamsize n (es.readsome (buf, c)); - - if (puse && n > 0) - puse = false; + optional<bool> r ( + gcc_module_mapper (mm_state, + a, bs, t, li, + l, os, + dd, update, bad_error, + pfx_map, so_map)); - if (n < c) - break; - } + more = !r.has_value (); - if (es.eof ()) - { - es.close (); - ds->fd = nullfd; - ds = nullptr; - } - } - - if (ms != nullptr && ms->ready) - { - w = "module mapper request"; - - gcc_module_mapper (mm_state, - a, bs, t, li, - is, os, - dd, update, bad_error, - pfx_map, so_map); - if (is.eof ()) + if (more || *r) + l.clear (); + else { os.close (); is.close (); - ms->fd = nullfd; - ms = nullptr; + ist.fd = nullfd; } } + + continue; } -#endif - } - catch (const io_error& e) - { - if (pr.wait ()) - fail << "io error handling " << x_lang << " compiler " - << w << ": " << e; - // Fall through. + ifdselect (fds); + + if (dst.ready) + { + if (!dbuf.read ()) + dst.fd = nullfd; + } } - if (mod_mapper) - md.header_units += mm_state.header_units; + md.header_units += mm_state.header_units; + } + catch (const io_error& e) + { + // Note that diag_buffer handles its own io errors so this + // is about mapper stdin/stdout. + // + if (pr.wait ()) + fail << "io error handling " << x_lang << " compiler " + << "module mapper request: " << e; + + // Fall through. } // The idea is to reduce this to the stdout case. // - pr.wait (); - - // With -MG we want to read dependency info even if there is - // an error (in case an outdated header file caused it). But - // with the GCC module mapper an error is non-negotiable, so - // to speak, and so we want to skip all of that. In fact, we - // now write directly to depdb without generating and then + // We now write directly to depdb without generating and then // parsing an intermadiate dependency makefile. // - pr.in_ofd = (ctype == compiler_type::gcc && mod_mapper) - ? auto_fd (nullfd) - : fdopen (*drmp, fdopen_mode::in); + pr.wait (); + pr.in_ofd = nullfd; + } + else + { + // If we have no generated header support, then suppress all + // diagnostics (if things go badly we will restart with this + // support). + // + if (drmp == nullptr) // Dependency info goes to stdout. + { + assert (!sense_diag); // Note: could support if necessary. + + // For VC with /P the dependency info and diagnostics all go + // to stderr so redirect it to stdout. + // + int err ( + cclass == compiler_class::msvc ? 1 : // stdout + !gen ? -2 : // /dev/null + diag_buffer::pipe (ctx, sense_diag /* force */)); + + pr = process ( + cpath, + args, + 0, + -1, + err, + nullptr, // CWD + env.empty () ? nullptr : env.data ()); + + if (cclass != compiler_class::msvc && gen) + { + dbuf.open (args[0], + move (pr.in_efd), + fdstream_mode::non_blocking); // Skip on stdout. + } + } + else // Dependency info goes to temporary file. + { + // Since we only need to read from one stream (dbuf) let's + // use the simpler blocking setup. + // + int err ( + !gen && !sense_diag ? -2 : // /dev/null + diag_buffer::pipe (ctx, sense_diag /* force */)); + + pr = process (cpath, + args, + 0, + 2, // Send stdout to stderr. + err, + nullptr, // CWD + env.empty () ? nullptr : env.data ()); + + if (gen || sense_diag) + { + dbuf.open (args[0], move (pr.in_efd)); + dbuf.read (sense_diag /* force */); + } + + if (sense_diag) + { + if (!dbuf.buf.empty ()) + { + puse = false; + dbuf.buf.clear (); // Discard. + } + } + + // The idea is to reduce this to the stdout case. + // + // Note that with -MG we want to read dependency info even + // if there is an error (in case an outdated header file + // caused it). + // + pr.wait (); + pr.in_ofd = fdopen (*drmp, fdopen_mode::in); + } } + // Read and process dependency information, if any. + // if (pr.in_ofd != nullfd) { + // We have two cases here: reading from stdout and potentially + // stderr (dbuf) or reading from file (see the process startup + // code above for details). If we have to read from two + // streams, then we have to use the non-blocking setup. But we + // cannot use the non-blocking setup uniformly because on + // Windows it's only suppored for pipes. So things are going + // to get a bit hairy. + // + // And there is another twist to this: for MSVC we redirect + // stderr to stdout since the header dependency information is + // part of the diagnostics. If, however, there is some real + // diagnostics, we need to pass it through, potentially with + // buffering. The way we achieve this is by later opening dbuf + // in the EOF state and using it to buffer or stream the + // diagnostics. + // + bool nb (dbuf.is.is_open ()); + // We may not read all the output (e.g., due to a restart). // Before we used to just close the file descriptor to signal // to the other end that we are not interested in the rest. @@ -4168,20 +4496,69 @@ namespace build2 // impolite and complains, loudly (broken pipe). So now we are // going to skip until the end. // - ifdstream is (move (pr.in_ofd), - fdstream_mode::text | fdstream_mode::skip, - ifdstream::badbit); + // Note that this means we are not using skip on dbuf (see + // above for the destruction order details). + // + { + fdstream_mode m (fdstream_mode::text | + fdstream_mode::skip); + + if (nb) + m |= fdstream_mode::non_blocking; + + is.open (move (pr.in_ofd), m); + } + + fdselect_set fds; + if (nb) + fds = {is.fd (), dbuf.is.fd ()}; size_t skip (skip_count); string l, l2; // Reuse. for (bool first (true), second (false); !restart; ) { - if (eof (getline (is, l))) + if (nb) { - if (bad_error && !l2.empty ()) - text << l2; + fdselect_state& ist (fds[0]); + fdselect_state& dst (fds[1]); - break; + // We read until we reach EOF on both streams. + // + if (ist.fd == nullfd && dst.fd == nullfd) + break; + + if (ist.fd != nullfd && getline_non_blocking (is, l)) + { + if (eof (is)) + { + ist.fd = nullfd; + continue; + } + + // Fall through to parse (and clear) the line. + } + else + { + ifdselect (fds); + + if (dst.ready) + { + if (!dbuf.read ()) + dst.fd = nullfd; + } + + continue; + } + } + else + { + if (eof (getline (is, l))) + { + if (bad_error && !l2.empty ()) // MSVC only (see below). + dbuf.write (l2, true /* newline */); + + break; + } } l6 ([&]{trace << "header dependency line '" << l << "'";}); @@ -4232,9 +4609,15 @@ namespace build2 else { l2 = l; - bad_error = true; + + if (!bad_error) + { + dbuf.open_eof (args[0]); + bad_error = true; + } } + l.clear (); continue; } @@ -4244,6 +4627,7 @@ namespace build2 } first = false; + l.clear (); continue; } @@ -4251,8 +4635,13 @@ namespace build2 if (f.empty ()) // Some other diagnostics. { - text << l; - bad_error = true; + if (!bad_error) + { + dbuf.open_eof (args[0]); + bad_error = true; + } + + dbuf.write (l, true /* newline */); break; } @@ -4346,12 +4735,9 @@ namespace build2 if (l.empty () || l[0] != '^' || l[1] != ':' || l[2] != ' ') { - // @@ Hm, we don't seem to redirect stderr to stdout - // for this class of compilers so I wonder why - // we are doing this? - // if (!l.empty ()) - text << l; + l5 ([&]{trace << "invalid header dependency line '" + << l << "'";}); bad_error = true; break; @@ -4366,7 +4752,10 @@ namespace build2 // "^: \". // if (l.size () == 4 && l[3] == '\\') + { + l.clear (); continue; + } else pos = 3; // Skip "^: ". @@ -4381,10 +4770,8 @@ namespace build2 if (pos != l.size () && l[pos] == ':') { - // @@ Hm, the same as above. - // - text << l; - + l5 ([&]{trace << "invalid header dependency line '" + << l << "'";}); bad_error = true; break; } @@ -4439,19 +4826,56 @@ namespace build2 } if (bad_error || md.deferred_failure) + { + // Note that it may be tempting to finish reading out the + // diagnostics before bailing out. But that may end up in + // a deadlock if the process gets blocked trying to write + // to stdout. + // break; + } + + l.clear (); + } + + // We may bail out early from the above loop in case of a + // restart or error. Which means the stderr stream (dbuf) may + // still be open and we need to close it before closing the + // stdout stream (which may try to skip). + // + // In this case we may also end up with incomplete diagnostics + // so discard it. + // + // Generally, it may be tempting to start thinking if we + // should discard buffered diagnostics in other cases, such as + // restart. But remember that during serial execution it will + // go straight to stderr so for consistency (and simplicity) + // we should just print it unless there are good reasons not + // to (also remember that in the restartable modes we normally + // redirect stderr to /dev/null; see the process startup code + // for details). + // + if (dbuf.is.is_open ()) + { + dbuf.is.close (); + dbuf.buf.clear (); } // Bail out early if we have deferred a failure. // + // Let's ignore any buffered diagnostics in this case since + // it would appear after the deferred failure note. + // if (md.deferred_failure) { is.close (); - return make_pair (file_cache::entry (), false); + return; } - // In case of VC, we are parsing stderr and if things go - // south, we need to copy the diagnostics for the user to see. + // In case of VC, we are parsing redirected stderr and if + // things go south, we need to copy the diagnostics for the + // user to see. Note that we should have already opened dbuf + // at EOF above. // if (bad_error && cclass == compiler_class::msvc) { @@ -4466,7 +4890,7 @@ namespace build2 l.compare (p.first, 4, "1083") != 0 && msvc_header_c1083 (l, p)) { - diag_stream_lock () << l << endl; + dbuf.write (l, true /* newline */); } } } @@ -4489,27 +4913,42 @@ namespace build2 if (pr.wait ()) { - if (!bad_error) // Ignore expected successes (we are done). { - if (!restart && psrc) - psrcw.close (); + diag_record dr; - continue; + if (bad_error) + dr << fail << "expected error exit status from " + << x_lang << " compiler"; + + if (dbuf.is_open ()) + dbuf.close (move (dr)); // Throws if error. } - fail << "expected error exit status from " << x_lang - << " compiler"; + // Ignore expected successes (we are done). + // + if (!restart && psrc) + psrcw.close (); + + continue; } else if (pr.exit->normal ()) { if (good_error) // Ignore expected errors (restart). + { + if (dbuf.is_open ()) + dbuf.close (); + continue; + } } // Fall through. } catch (const io_error& e) { + // Ignore buffered diagnostics (since reading it could be the + // cause of this failure). + // if (pr.wait ()) fail << "unable to read " << x_lang << " compiler header " << "dependency output: " << e; @@ -4518,18 +4957,23 @@ namespace build2 } assert (pr.exit && !*pr.exit); - const process_exit& e (*pr.exit); + const process_exit& pe (*pr.exit); // For normal exit we assume the child process issued some // diagnostics. // - if (e.normal ()) + if (pe.normal ()) { - // If this run was with the generated header support then we - // have issued diagnostics and it's time to give up. + // If this run was with the generated header support then it's + // time to give up. // if (gen) + { + if (dbuf.is_open ()) + dbuf.close (args, pe, 2 /* verbosity */); + throw failed (); + } // Just to recap, being here means something is wrong with the // source: it can be a missing generated header, it can be an @@ -4547,7 +4991,12 @@ namespace build2 // or will issue diagnostics. // if (restart) + { + if (dbuf.is_open ()) + dbuf.close (); + l6 ([&]{trace << "trying again without generated headers";}); + } else { // In some pathological situations we may end up switching @@ -4572,19 +5021,24 @@ namespace build2 // example, because we have removed all the partially // preprocessed source files). // - if (force_gen_skip && *force_gen_skip == skip_count) { - diag_record dr (fail); + diag_record dr; + if (force_gen_skip && *force_gen_skip == skip_count) + { + dr << + fail << "inconsistent " << x_lang << " compiler behavior" << + info << "run the following two commands to investigate"; - dr << "inconsistent " << x_lang << " compiler behavior" << - info << "run the following two commands to investigate"; + dr << info; + print_process (dr, args.data ()); // No pipes. - dr << info; - print_process (dr, args.data ()); // No pipes. + init_args ((gen = true)); + dr << info << ""; + print_process (dr, args.data ()); // No pipes. + } - init_args ((gen = true)); - dr << info << ""; - print_process (dr, args.data ()); // No pipes. + if (dbuf.is_open ()) + dbuf.close (move (dr)); // Throws if error. } restart = true; @@ -4595,7 +5049,15 @@ namespace build2 continue; } else - run_finish (args, pr); // Throws. + { + if (dbuf.is_open ()) + { + dbuf.close (args, pe, 2 /* verbosity */); + throw failed (); + } + else + run_finish (args, pr, 2 /* verbosity */); + } } catch (const process_error& e) { @@ -4621,7 +5083,9 @@ namespace build2 dd.expect (""); puse = puse && !reprocess && psrc; - return make_pair (move (psrc), puse); + + result.first = move (psrc); + result.second = puse; } // Return the translation unit information (last argument) and its @@ -4640,6 +5104,18 @@ namespace build2 { tracer trace (x, "compile_rule::parse_unit"); + // Scanning .S files with our parser is hazardous since such files + // sometimes use `#`-style comments. Presumably real compilers just + // ignore them in some way, but it doesn't seem worth it to bother in + // our case. Also, the checksum calculation over assembler tokens feels + // iffy. + // + if (x_assembler_cpp (src)) + { + tu.type = unit_type::non_modular; + return ""; + } + otype ot (li.type); // If things go wrong give the user a bit extra context. Let's call it @@ -4718,8 +5194,6 @@ namespace build2 case compiler_class::msvc: werror = "/WX"; break; } - bool clang (ctype == compiler_type::clang); - append_options (args, t, c_coptions, werror); append_options (args, t, x_coptions, werror); @@ -4734,11 +5208,16 @@ namespace build2 append_options (args, cmode); append_sys_hdr_options (args); + // Note: no append_diag_color_options() call since the + // diagnostics is discarded. + // See perform_update() for details on the choice of options. // { - bool sc (find_option_prefix ("/source-charset:", args)); - bool ec (find_option_prefix ("/execution-charset:", args)); + bool sc (find_option_prefixes ( + {"/source-charset:", "-source-charset:"}, args)); + bool ec (find_option_prefixes ( + {"/execution-charset:", "-execution-charset:"}, args)); if (!sc && !ec) args.push_back ("/utf-8"); @@ -4754,15 +5233,16 @@ namespace build2 if (cvariant != "clang" && isystem (*this)) { - if (find_option_prefix ("/external:I", args) && - !find_option_prefix ("/external:W", args)) + if (find_option_prefixes ({"/external:I", "-external:I"}, args) && + !find_option_prefixes ({"/external:W", "-external:W"}, args)) args.push_back ("/external:W0"); } - if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) + if (x_lang == lang::cxx && + !find_option_prefixes ({"/EH", "-EH"}, args)) args.push_back ("/EHsc"); - if (!find_option_prefixes ({"/MD", "/MT"}, args)) + if (!find_option_prefixes ({"/MD", "/MT", "-MD", "-MT"}, args)) args.push_back ("/MD"); args.push_back ("/E"); @@ -4776,10 +5256,12 @@ namespace build2 } case compiler_class::gcc: { - append_options (args, cmode, - cmode.size () - (modules && clang ? 1 : 0)); + append_options (args, cmode); append_sys_hdr_options (args); + // Note: no append_diag_color_options() call since the + // diagnostics is discarded. + // See perform_update() for details on the choice of options. // if (!find_option_prefix ("-finput-charset=", args)) @@ -4793,8 +5275,7 @@ namespace build2 if (ctype == compiler_type::clang && tsys == "win32-msvc") { - initializer_list<const char*> os {"-nostdlib", "-nostartfiles"}; - if (!find_options (os, cmode) && !find_options (os, args)) + if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) { args.push_back ("-D_MT"); args.push_back ("-D_DLL"); @@ -4821,12 +5302,36 @@ namespace build2 // if (ps) { - if (ctype == compiler_type::gcc) + switch (ctype) { - // Note that only these two *plus* -x do the trick. - // - args.push_back ("-fpreprocessed"); - args.push_back ("-fdirectives-only"); + case compiler_type::gcc: + { + // Note that only these two *plus* -x do the trick. + // + args.push_back ("-fpreprocessed"); + args.push_back ("-fdirectives-only"); + break; + } + case compiler_type::clang: + { + // See below for details. + // + if (ctype == compiler_type::clang && + cmaj >= (cvariant != "apple" ? 15 : 16)) + { + if (find_options ({"-pedantic", "-pedantic-errors", + "-Wpedantic", "-Werror=pedantic"}, + args)) + { + args.push_back ("-Wno-gnu-line-marker"); + } + } + + break; + } + case compiler_type::msvc: + case compiler_type::icc: + assert (false); } } @@ -4880,10 +5385,10 @@ namespace build2 print_process (args); // We don't want to see warnings multiple times so ignore all - // diagnostics. + // diagnostics (thus no need for diag_buffer). // pr = process (cpath, - args.data (), + args, 0, -1, -2, nullptr, // CWD env.empty () ? nullptr : env.data ()); @@ -4895,7 +5400,7 @@ namespace build2 fdstream_mode::binary | fdstream_mode::skip); parser p; - p.parse (is, path_name (*sp), tu); + p.parse (is, path_name (*sp), tu, cid); is.close (); @@ -4910,7 +5415,9 @@ namespace build2 if (!modules) { if (ut != unit_type::non_modular || !mi.imports.empty ()) - fail << "modules support required by " << src; + fail << "modules support required by " << src << + info << "consider enabling modules with " + << x << ".features.modules=true in root.build"; } else { @@ -4935,18 +5442,6 @@ namespace build2 ut = md.type; mi.name = src.path ().string (); } - - // Prior to 15.5 (19.12) VC was not using the 'export module M;' - // syntax so we use the preprequisite type to distinguish - // between interface and implementation units. - // - // @@ TMP: probably outdated. - // - if (ctype == compiler_type::msvc && cmaj == 19 && cmin <= 11) - { - if (ut == unit_type::module_impl && src.is_a (*x_mod)) - ut = unit_type::module_intf; - } } // If we were forced to reprocess, assume the checksum is not @@ -4992,7 +5487,7 @@ namespace build2 info << "then run failing command to display compiler diagnostics"; } else - run_finish (args, pr); // Throws. + run_finish (args, pr, 2 /* verbosity */); // Throws. } catch (const process_error& e) { @@ -5161,6 +5656,9 @@ namespace build2 { tracer trace (x, "compile_rule::search_modules"); + context& ctx (bs.ctx); + const scope& rs (*bs.root_scope ()); + // NOTE: currently we don't see header unit imports (they are handled by // extract_headers() and are not in imports). @@ -5196,7 +5694,7 @@ namespace build2 // So, the fuzzy match: the idea is that each match gets a score, the // number of characters in the module name that got matched. A match // with the highest score is used. And we use the (length + 1) for a - // match against an actual module name. + // match against an actual (extracted) module name. // // Actually, the scoring system is a bit more elaborate than that. // Consider module name core.window and two files, window.mxx and @@ -5224,10 +5722,10 @@ namespace build2 // module (or partition) component. Failed that, we will match `format` // to `print` because the last character (`t`) is the same. // - // For std.* modules we only accept non-fuzzy matches (think std.core vs - // some core.mxx). And if such a module is unresolved, then we assume it - // is pre-built and will be found by some other means (e.g., VC's - // IFCPATH). + // For std.* modules we only accept non-fuzzy matches (think std.compat + // vs some compat.mxx). And if such a module is unresolved, then we + // assume it is pre-built and will be found by some other means (e.g., + // VC's IFCPATH). // // Note also that we handle module partitions the same as submodules. In // other words, for matching, `.` and `:` are treated the same. @@ -5240,7 +5738,7 @@ namespace build2 // PPPPABBBB // // Where PPPP is the primary score, A is the A) score, and BBBB is - // the B) scope described above. Zero signifies no match. + // the B) score described above. Zero signifies no match. // // We use decimal instead of binary packing to make it easier for the // human to separate fields in the trace messages, during debugging, @@ -5346,6 +5844,31 @@ namespace build2 if (!match) return 0; + // Here is another corner case, the module is async_simple:IOExecutor + // and the file names are: + // + // IOExecutor.mxx + // SimpleIOExecutor.mxx + // + // The above implementation treats the latter as better because + // `Simple` in SimpleIOExecutor matches `simple` in async_simple. It's + // unclear what we can do about it without potentially breaking other + // legitimate cases (think Boost_Simple:IOExecutor). Maybe we could + // boost the exact partition name match score, similar to the exact + // module match, as some sort of a heuristics? Let's try. + // + if (fi == 0 && mi != 0 && m[mi - 1] == ':') + { + // Pretend we matched one short of the next module component. This + // way AsyncSimpleIOExecutor.mxx would still be a better match. + // + while (--mi != 0 && m[mi - 1] != '.') + ; + + msep = (mi != 0); // For uncount logic below. + mi++; // One short. + } + // "Uncount" real separators. // if (fsep) fi++; @@ -5374,6 +5897,20 @@ namespace build2 return ps * 100000 + as * 10000 + bs; }; +#if 0 + assert (match ("IOExecutor", "async_simple:IOExecutor") > + match ("SimpleIOExecutor", "async_simple:IOExecutor")); + + assert (match ("IOExecutor", "async_simple:IOExecutor") < + match ("AsyncSimpleIOExecutor", "async_simple:IOExecutor")); + + assert (match ("IOExecutor", "x.async_simple:IOExecutor") > + match ("SimpleIOExecutor", "x.async_simple:IOExecutor")); + + assert (match ("IOExecutor", "x.async_simple:IOExecutor") < + match ("AsyncSimpleIOExecutor", "x.async_simple:IOExecutor")); +#endif + auto& pts (t.prerequisite_targets[a]); size_t start (pts.size ()); // Index of the first to be added. @@ -5388,7 +5925,7 @@ namespace build2 // promise. It has to do with module re-exporting (export import M;). // In this case (currently) all implementations simply treat it as a // shallow (from the BMI's point of view) reference to the module (or an - // implicit import, if you will). Do you see where it's going? Nowever + // implicit import, if you will). Do you see where it's going? Nowhere // good, that's right. This shallow reference means that the compiler // should be able to find BMIs for all the re-exported modules, // recursively. The good news is we are actually in a pretty good shape @@ -5434,6 +5971,7 @@ namespace build2 // so we actually don't need to pass any extra options (unless things // get moved) but they still need access to the BMIs (and things will // most likely have to be done differenly for distributed compilation). + // @@ Note: no longer the case for Clang either. // // So the revised plan: on the off chance that some implementation will // do it differently we will continue maintaing the imported/re-exported @@ -5527,6 +6065,8 @@ namespace build2 continue; // Scan the rest to detect if all done. } } + else + assert (name != m.name); // No duplicates. done = false; } @@ -5554,10 +6094,18 @@ namespace build2 // if (pt->is_a<bmix> ()) { - const string& n (cast<string> (pt->state[a].vars[c_module_name])); - - if (const target** p = check_exact (n)) - *p = pt; + // If the extraction of the module information for this BMI failed + // and we have deferred failure to compiler diagnostics, then + // there will be no module name assigned. It would have been + // better to make sure that's the cause, but that won't be easy. + // + const string* n (cast_null<string> ( + pt->state[a].vars[c_module_name])); + if (n != nullptr) + { + if (const target** p = check_exact (*n)) + *p = pt; + } } else if (pt->is_a (*x_mod)) { @@ -5566,7 +6114,8 @@ namespace build2 // rule puts them into prerequisite_targets for us). // // The module names should be specified but if not assume - // something else is going on and ignore. + // something else is going on (like a deferred failure) and + // ignore. // // Note also that besides modules, prerequisite_targets may // contain libraries which are interface dependencies of this @@ -5579,7 +6128,15 @@ namespace build2 continue; if (const target** p = check_exact (*n)) - *p = &this->make_module_sidebuild (a, bs, l, *pt, *n); // GCC 4.9 + { + // It seems natural to build a BMI type that corresponds to the + // library type. After all, this is where the object file part + // of the BMI is going to come from (unless it's a module + // interface-only library). + // + *p = &this->make_module_sidebuild ( + a, bs, &l, link_type (l).type, *pt, *n).first; // GCC 4.9 + } } // Note that in prerequisite targets we will have the libux{} // members, not the group. @@ -5594,112 +6151,300 @@ namespace build2 } }; - for (prerequisite_member p: group_prerequisite_members (a, t)) - { - if (include (a, t, p) != include_type::normal) // Excluded/ad hoc. - continue; + // Pre-resolve standard library modules (std and std.compat) in an ad + // hoc way. + // - const target* pt (p.load ()); // Should be cached for libraries. + // Similar logic to check_exact() above. + // + done = true; - if (pt != nullptr) - { - const file* lt (nullptr); + for (size_t i (0); i != n; ++i) + { + module_import& m (imports[i]); - if (const libx* l = pt->is_a<libx> ()) - lt = link_member (*l, a, li); - else if (pt->is_a<liba> () || pt->is_a<libs> () || pt->is_a<libux> ()) - lt = &pt->as<file> (); + if (m.name == "std" || m.name == "std.compat") + { + otype ot (otype::e); + const target* mt (nullptr); - // If this is a library, check its bmi{}s and mxx{}s. - // - if (lt != nullptr) + switch (ctype) { - find (*lt, find); + case compiler_type::clang: + { + // @@ 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"; + + if (cmaj < 18) + fail << "standard library module '" << m.name << "' is " + << "only supported in Clang 18 or later"; + + // 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; - if (done) 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"))) + { + p->make_directory (); // Strip vcruntime.h. + if (p->leaf () == path ("include")) // Sanity check. + { + modules = path_cast<dir_path> (move (p->make_directory ())); + modules /= "modules"; + } + } - continue; + 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; + + // 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"}; + + 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. + + 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)); + + if (tl.second.owns_lock ()) + { + // Special compile options for the std modules. + // + if (ctype == compiler_type::clang) + { + value& v (tl.first.append_locked (x_coptions)); + + if (v.null) + v = strings {}; + + strings& cops (v.as<strings> ()); + + switch (ctype) + { + 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 (); } - // Fall through. + pts[start + i].target = &tl.first; + m.score = match_max (m.name) + 1; + continue; // Scan the rest to detect if all done. } - // While it would have been even better not to search for a target, we - // need to get hold of the corresponding mxx{} (unlikely but possible - // for bmi{} to have a different name). - // - // While we want to use group_prerequisite_members() below, we cannot - // call resolve_group() since we will be doing it "speculatively" for - // modules that we may use but also for modules that may use us. This - // quickly leads to deadlocks. So instead we are going to perform an - // ad hoc group resolution. - // - const target* pg; - if (p.is_a<bmi> ()) - { - pg = pt != nullptr ? pt : &p.search (t); - pt = &search (t, btt, p.key ()); // Same logic as in picking obj*{}. - } - else if (p.is_a (btt)) - { - pg = &search (t, bmi::static_type, p.key ()); - if (pt == nullptr) pt = &p.search (t); - } - else - continue; + done = false; + } - // Find the mxx{} prerequisite and extract its "file name" for the - // fuzzy match unless the user specified the module name explicitly. - // - for (prerequisite_member p: - prerequisite_members (a, t, group_prerequisites (*pt, pg))) + // Go over prerequisites and try to resolve imported modules with them. + // + if (!done) + { + for (prerequisite_member p: group_prerequisite_members (a, t)) { if (include (a, t, p) != include_type::normal) // Excluded/ad hoc. continue; - if (p.is_a (*x_mod)) + const target* pt (p.load ()); // Should be cached for libraries. + + if (pt != nullptr) { - // Check for an explicit module name. Only look for an existing - // target (which means the name can only be specified on the - // target itself, not target type/pattern-spec). + const file* lt (nullptr); + + if (const libx* l = pt->is_a<libx> ()) + lt = link_member (*l, a, li); + else if (pt->is_a<liba> () || + pt->is_a<libs> () || + pt->is_a<libux> ()) + lt = &pt->as<file> (); + + // If this is a library, check its bmi{}s and mxx{}s. // - const target* t (p.search_existing ()); - const string* n (t != nullptr - ? cast_null<string> (t->vars[c_module_name]) - : nullptr); - if (n != nullptr) + if (lt != nullptr) { - if (const target** p = check_exact (*n)) - *p = pt; + find (*lt, find); + + if (done) + break; + + continue; } - else + + // Fall through. + } + + // While it would have been even better not to search for a target, + // we need to get hold of the corresponding mxx{} (unlikely but + // possible for bmi{} to have a different name). + // + // While we want to use group_prerequisite_members() below, we + // cannot call resolve_group() since we will be doing it + // "speculatively" for modules that we may use but also for modules + // that may use us. This quickly leads to deadlocks. So instead we + // are going to perform an ad hoc group resolution. + // + const target* pg; + if (p.is_a<bmi> ()) + { + pg = pt != nullptr ? pt : &p.search (t); + pt = &search (t, btt, p.key ()); // Same logic as in picking obj*{}. + } + else if (p.is_a (btt)) + { + pg = &search (t, bmi::static_type, p.key ()); + if (pt == nullptr) pt = &p.search (t); + } + else + continue; + + // Find the mxx{} prerequisite and extract its "file name" for the + // fuzzy match unless the user specified the module name explicitly. + // + for (prerequisite_member p: + prerequisite_members (a, t, group_prerequisites (*pt, pg))) + { + if (include (a, t, p) != include_type::normal) // Excluded/ad hoc. + continue; + + if (p.is_a (*x_mod)) { - // Fuzzy match. + // Check for an explicit module name. Only look for an existing + // target (which means the name can only be specified on the + // target itself, not target type/pattern-spec). // - string f; + const target* mt (p.search_existing ()); + const string* n (mt != nullptr + ? cast_null<string> (mt->vars[c_module_name]) + : nullptr); + if (n != nullptr) + { + if (const target** p = check_exact (*n)) + *p = pt; + } + else + { + // Fuzzy match. + // + string f; - // Add the directory part if it is relative. The idea is to - // include it into the module match, say hello.core vs - // hello/mxx{core}. - // - // @@ MOD: Why not for absolute? Good question. What if it - // contains special components, say, ../mxx{core}? - // - const dir_path& d (p.dir ()); + // Add the directory part if it is relative. The idea is to + // include it into the module match, say hello.core vs + // hello/mxx{core}. + // + // @@ MOD: Why not for absolute? Good question. What if it + // contains special components, say, ../mxx{core}? + // + const dir_path& d (p.dir ()); - if (!d.empty () && d.relative ()) - f = d.representation (); // Includes trailing slash. + if (!d.empty () && d.relative ()) + f = d.representation (); // Includes trailing slash. - f += p.name (); - check_fuzzy (pt, f); + f += p.name (); + check_fuzzy (pt, f); + } + break; } - break; } - } - if (done) - break; + if (done) + break; + } } // Diagnose unresolved modules. @@ -5769,9 +6514,12 @@ namespace build2 if (m.score <= match_max (in)) { - const string& mn (cast<string> (bt->state[a].vars[c_module_name])); + // As above (deffered failure). + // + const string* mn ( + cast_null<string> (bt->state[a].vars[c_module_name])); - if (in != mn) + if (mn != nullptr && in != *mn) { // Note: matched, so the group should be resolved. // @@ -5785,7 +6533,7 @@ namespace build2 fail (relative (src)) << "failed to correctly guess module name from " << p << info << "guessed: " << in << - info << "actual: " << mn << + info << "actual: " << *mn << info << "consider adjusting module interface file names or" << info << "consider specifying module name with " << x << ".module_name"; @@ -5813,12 +6561,15 @@ namespace build2 if (et == nullptr) continue; // Unresolved (std.*). - const string& mn (cast<string> (et->state[a].vars[c_module_name])); + // As above (deferred failure). + // + const string* mn (cast_null<string> (et->state[a].vars[c_module_name])); - if (find_if (imports.begin (), imports.end (), - [&mn] (const module_import& i) + if (mn != nullptr && + find_if (imports.begin (), imports.end (), + [mn] (const module_import& i) { - return i.name == mn; + return i.name == *mn; }) == imports.end ()) { pts.push_back (et); @@ -5829,10 +6580,10 @@ namespace build2 // but it's probably not worth it if we have a small string // optimization. // - import_type t (mn.find (':') != string::npos + import_type t (mn->find (':') != string::npos ? import_type::module_part : import_type::module_intf); - imports.push_back (module_import {t, mn, true, 0}); + imports.push_back (module_import {t, *mn, true, 0}); } } } @@ -5852,6 +6603,10 @@ namespace build2 // Find or create a modules sidebuild subproject returning its root // directory. // + // @@ Could we omit creating a subproject if the sidebuild scope is the + // project scope itself? This would speed up simple examples (and + // potentially direct compilation that we may support). + // pair<dir_path, const scope&> compile_rule:: find_modules_sidebuild (const scope& rs) const { @@ -5922,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'; @@ -5956,13 +6720,18 @@ namespace build2 return pair<dir_path, const scope&> (move (pd), *as); } - // Synthesize a dependency for building a module binary interface on - // the side. + // Synthesize a dependency for building a module binary interface of a + // library on the side. If library is missing, then assume it's some + // ad hoc/system library case (in which case we assume it's binless, + // for now). // - const file& compile_rule:: + // The return value semantics is as in target_set::insert_locked(). + // + pair<target&, ulock> compile_rule:: make_module_sidebuild (action a, const scope& bs, - const file& lt, + const file* lt, + otype ot, const target& mt, const string& mn) const { @@ -5983,24 +6752,20 @@ namespace build2 back_inserter (mf), [] (char c) {return c == '.' ? '-' : c == ':' ? '+' : c;}); - // It seems natural to build a BMI type that corresponds to the library - // type. After all, this is where the object file part of the BMI is - // going to come from (unless it's a module interface-only library). - // - const target_type& tt (compile_types (link_type (lt).type).bmi); + const target_type& tt (compile_types (ot).bmi); // Store the BMI target in the subproject root. If the target already // exists then we assume all this is already done (otherwise why would // someone have created such a target). // - if (const file* bt = bs.ctx.targets.find<file> ( + if (const target* bt = bs.ctx.targets.find ( tt, pd, dir_path (), // Always in the out tree. mf, nullopt, // Use default extension. trace)) - return *bt; + return pair<target&, ulock> (const_cast<target&> (*bt), ulock ()); prerequisites ps; ps.push_back (prerequisite (mt)); @@ -6013,19 +6778,22 @@ namespace build2 // // Note: lt is matched and so the group is resolved. // - ps.push_back (prerequisite (lt)); - for (prerequisite_member p: group_prerequisite_members (a, lt)) + if (lt != nullptr) { - // Ignore update=match. - // - lookup l; - if (include (a, lt, p, &l) != include_type::normal) // Excluded/ad hoc. - continue; - - if (p.is_a<libx> () || - p.is_a<liba> () || p.is_a<libs> () || p.is_a<libux> ()) + ps.push_back (prerequisite (*lt)); + for (prerequisite_member p: group_prerequisite_members (a, *lt)) { - ps.push_back (p.as_prerequisite ()); + // Ignore update=match. + // + lookup l; + if (include (a, *lt, p, &l) != include_type::normal) // Excluded/ad hoc. + continue; + + if (p.is_a<libx> () || + p.is_a<liba> () || p.is_a<libs> () || p.is_a<libux> ()) + { + ps.push_back (p.as_prerequisite ()); + } } } @@ -6038,22 +6806,22 @@ namespace build2 target_decl::implied, trace, true /* skip_find */)); - file& bt (p.first.as<file> ()); // Note that this is racy and someone might have created this target // while we were preparing the prerequisite list. // if (p.second) { - bt.prerequisites (move (ps)); + p.first.prerequisites (move (ps)); // Unless this is a binless library, we don't need the object file // (see config_data::b_binless for details). // - bt.vars.assign (b_binless) = (lt.mtime () == timestamp_unreal); + p.first.vars.assign (b_binless) = (lt == nullptr || + lt->mtime () == timestamp_unreal); } - return bt; + return p; } // Synthesize a dependency for building a header unit binary interface on @@ -6317,7 +7085,7 @@ namespace build2 // Filter cl.exe noise (msvc.cxx). // void - msvc_filter_cl (ifdstream&, const path& src); + msvc_filter_cl (diag_buffer&, const path& src); // Append header unit-related options. // @@ -6368,7 +7136,7 @@ namespace build2 // options). // void compile_rule:: - append_module_options (environment& env, + append_module_options (environment&, cstrings& args, small_vector<string, 2>& stor, action a, @@ -6379,8 +7147,6 @@ namespace build2 unit_type ut (md.type); const module_positions& ms (md.modules); - dir_path stdifc; // See the VC case below. - switch (ctype) { case compiler_type::gcc: @@ -6409,15 +7175,12 @@ namespace build2 if (ms.start == 0) return; - // Clang embeds module file references so we only need to specify - // our direct imports. - // - // If/when we get the ability to specify the mapping in a file, we - // will pass the whole list. + // If/when we get the ability to specify the mapping in a file. // #if 0 // In Clang the module implementation's unit .pcm is special and - // must be "loaded". + // must be "loaded". Note: not anymore, not from Clang 16 and is + // deprecated in 17. // if (ut == unit_type::module_impl) { @@ -6434,10 +7197,7 @@ namespace build2 stor.push_back (move (s)); #else auto& pts (t.prerequisite_targets[a]); - for (size_t i (ms.start), - n (ms.copied != 0 ? ms.copied : pts.size ()); - i != n; - ++i) + for (size_t i (ms.start), n (pts.size ()); i != n; ++i) { const target* pt (pts[i]); @@ -6450,17 +7210,9 @@ namespace build2 const file& f (pt->as<file> ()); string s (relative (f.path ()).string ()); - // In Clang the module implementation's unit .pcm is special and - // must be "loaded". - // - if (ut == unit_type::module_impl && i == ms.start) - s.insert (0, "-fmodule-file="); - else - { - s.insert (0, 1, '='); - s.insert (0, cast<string> (f.state[a].vars[c_module_name])); - s.insert (0, "-fmodule-file="); - } + s.insert (0, 1, '='); + s.insert (0, cast<string> (f.state[a].vars[c_module_name])); + s.insert (0, "-fmodule-file="); stor.push_back (move (s)); } @@ -6472,10 +7224,11 @@ namespace build2 if (ms.start == 0) return; + // MSVC requires a transitive set of interfaces, including + // implementation partitions. + // auto& pts (t.prerequisite_targets[a]); - for (size_t i (ms.start), n (pts.size ()); - i != n; - ++i) + for (size_t i (ms.start), n (pts.size ()); i != n; ++i) { const target* pt (pts[i]); @@ -6486,34 +7239,14 @@ namespace build2 // of these are bmi's. // const file& f (pt->as<file> ()); + string s (relative (f.path ()).string ()); - // In VC std.* modules can only come from a single directory - // specified with the IFCPATH environment variable or the - // /module:stdIfcDir option. - // - if (std_module (cast<string> (f.state[a].vars[c_module_name]))) - { - dir_path d (f.path ().directory ()); + s.insert (0, 1, '='); + s.insert (0, cast<string> (f.state[a].vars[c_module_name])); - if (stdifc.empty ()) - { - // Go one directory up since /module:stdIfcDir will look in - // either Release or Debug subdirectories. Keeping the result - // absolute feels right. - // - stor.push_back ("/module:stdIfcDir"); - stor.push_back (d.directory ().string ()); - stdifc = move (d); - } - else if (d != stdifc) // Absolute and normalized. - fail << "multiple std.* modules in different directories"; - } - else - { - stor.push_back ("/module:reference"); - stor.push_back (relative (f.path ()).string ()); - } + stor.push_back (move (s)); } + break; } case compiler_type::icc: @@ -6524,25 +7257,11 @@ namespace build2 // into storage? Because of potential reallocations. // for (const string& a: stor) - args.push_back (a.c_str ()); - - if (getenv ("IFCPATH")) { - // VC's IFCPATH takes precedence over /module:stdIfcDir so unset it if - // we are using our own std modules. Note: IFCPATH saved in guess.cxx. - // - if (!stdifc.empty ()) - env.push_back ("IFCPATH"); - } - else if (stdifc.empty ()) - { - // Add the VC's default directory (should be only one). - // - if (sys_mod_dirs != nullptr && !sys_mod_dirs->empty ()) - { - args.push_back ("/module:stdIfcDir"); - args.push_back (sys_mod_dirs->front ().string ().c_str ()); - } + if (ctype == compiler_type::msvc) + args.push_back ("/reference"); + + args.push_back (a.c_str ()); } } @@ -6615,7 +7334,8 @@ namespace build2 // If we are building a module interface or partition, then the target // is bmi*{} and it may have an ad hoc obj*{} member. For header units // there is no obj*{} (see the corresponding add_adhoc_member() call in - // apply()). + // apply()). For named modules there may be no obj*{} if this is a + // sidebuild (obj*{} is already in the library binary). // path relm; path relo; @@ -6663,9 +7383,6 @@ namespace build2 small_vector<string, 2> header_args; // Header unit options storage. small_vector<string, 2> module_args; // Module options storage. - size_t out_i (0); // Index of the -o option. - size_t lang_n (0); // Number of lang options. - switch (cclass) { case compiler_class::msvc: @@ -6685,14 +7402,20 @@ namespace build2 if (md.pp != preprocessed::all) append_sys_hdr_options (args); // Extra system header dirs (last). + // Note: could be overridden in mode. + // + append_diag_color_options (args); + // Set source/execution charsets to UTF-8 unless a custom charset // is specified. // // Note that clang-cl supports /utf-8 and /*-charset. // { - bool sc (find_option_prefix ("/source-charset:", args)); - bool ec (find_option_prefix ("/execution-charset:", args)); + bool sc (find_option_prefixes ( + {"/source-charset:", "-source-charset:"}, args)); + bool ec (find_option_prefixes ( + {"/execution-charset:", "-execution-charset:"}, args)); if (!sc && !ec) args.push_back ("/utf-8"); @@ -6711,8 +7434,8 @@ namespace build2 // if (cvariant != "clang" && isystem (*this)) { - if (find_option_prefix ("/external:I", args) && - !find_option_prefix ("/external:W", args)) + if (find_option_prefixes ({"/external:I", "-external:I"}, args) && + !find_option_prefixes ({"/external:W", "-external:W"}, args)) args.push_back ("/external:W0"); } @@ -6726,7 +7449,9 @@ namespace build2 // For C looks like no /EH* (exceptions supported but no C++ objects // destroyed) is a reasonable default. // - if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) + + if (x_lang == lang::cxx && + !find_option_prefixes ({"/EH", "-EH"}, args)) args.push_back ("/EHsc"); // The runtime is a bit more interesting. At first it may seem like @@ -6748,7 +7473,7 @@ namespace build2 // unreasonable thing to do). So by default we will always use the // release runtime. // - if (!find_option_prefixes ({"/MD", "/MT"}, args)) + if (!find_option_prefixes ({"/MD", "/MT", "-MD", "-MT"}, args)) args.push_back ("/MD"); msvc_sanitize_cl (args); @@ -6771,9 +7496,8 @@ namespace build2 // Note also that what we are doing here appears to be incompatible // with PCH (/Y* options) and /Gm (minimal rebuild). // - // @@ MOD: TODO deal with absent relo. - // - if (find_options ({"/Zi", "/ZI"}, args)) + if (!relo.empty () && + find_options ({"/Zi", "/ZI", "-Zi", "-ZI"}, args)) { if (fc) args.push_back ("/Fd:"); @@ -6786,27 +7510,38 @@ namespace build2 args.push_back (out1.c_str ()); } - if (fc) - { - args.push_back ("/Fo:"); - args.push_back (relo.string ().c_str ()); - } - else + if (ut == unit_type::module_intf || + ut == unit_type::module_intf_part || + ut == unit_type::module_impl_part || + ut == unit_type::module_header) { - out = "/Fo" + relo.string (); - args.push_back (out.c_str ()); - } + assert (ut != unit_type::module_header); // @@ MODHDR - // @@ MODHDR MSVC - // @@ MODPART MSVC - // - if (ut == unit_type::module_intf) - { relm = relative (tp); - args.push_back ("/module:interface"); - args.push_back ("/module:output"); + args.push_back ("/ifcOutput"); args.push_back (relm.string ().c_str ()); + + if (relo.empty ()) + args.push_back ("/ifcOnly"); + else + { + args.push_back ("/Fo:"); + args.push_back (relo.string ().c_str ()); + } + } + else + { + if (fc) + { + args.push_back ("/Fo:"); + args.push_back (relo.string ().c_str ()); + } + else + { + out = "/Fo" + relo.string (); + args.push_back (out.c_str ()); + } } // Note: no way to indicate that the source if already preprocessed. @@ -6821,9 +7556,53 @@ namespace build2 { append_options (args, cmode); + // Clang 15 introduced the unqualified-std-cast-call warning which + // warns about unqualified calls to std::move() and std::forward() + // (because they can be "hijacked" via ADL). Surprisingly, this + // warning is enabled by default, as opposed to with -Wextra or at + // least -Wall. It has also proven to be quite disruptive, causing a + // large number of warnings in a large number of packages. So we are + // going to "remap" it to -Wextra for now and in the future may + // "relax" it to -Wall and potentially to being enabled by default. + // See GitHub issue #259 for background and details. + // + if (x_lang == lang::cxx && + ctype == compiler_type::clang && + cmaj >= 15) + { + bool w (false); // Seen -W[no-]unqualified-std-cast-call + optional<bool> extra; // Seen -W[no-]extra + + for (const char* s: reverse_iterate (args)) + { + if (s != nullptr) + { + if (strcmp (s, "-Wunqualified-std-cast-call") == 0 || + strcmp (s, "-Wno-unqualified-std-cast-call") == 0) + { + w = true; + break; + } + + if (!extra) // Last seen option wins. + { + if (strcmp (s, "-Wextra") == 0) extra = true; + else if (strcmp (s, "-Wno-extra") == 0) extra = false; + } + } + } + + if (!w && (!extra || !*extra)) + args.push_back ("-Wno-unqualified-std-cast-call"); + } + if (md.pp != preprocessed::all) append_sys_hdr_options (args); // Extra system header dirs (last). + // Note: could be overridden in mode. + // + append_diag_color_options (args); + // Set the input charset to UTF-8 unless a custom one is specified. // // Note that the execution charset (-fexec-charset) is UTF-8 by @@ -6877,8 +7656,7 @@ namespace build2 // either -nostdlib or -nostartfiles is specified. Let's do // the same. // - initializer_list<const char*> os {"-nostdlib", "-nostartfiles"}; - if (!find_options (os, cmode) && !find_options (os, args)) + if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) { args.push_back ("-D_MT"); args.push_back ("-D_DLL"); @@ -6940,10 +7718,6 @@ namespace build2 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. - // - out_i = args.size (); // Index of the -o option. - if (ut == unit_type::module_intf || ut == unit_type::module_intf_part || ut == unit_type::module_impl_part || @@ -6982,21 +7756,35 @@ namespace build2 } case compiler_type::clang: { - // @@ MOD TODO: deal with absent relo. + assert (ut != unit_type::module_header); // @@ MODHDR relm = relative (tp); - args.push_back ("-o"); - args.push_back (relm.string ().c_str ()); - args.push_back ("--precompile"); - // Without this option Clang's .pcm will reference source - // files. In our case this file may be transient (.ii). Plus, + // files. In our case this file may be transient (.ii). Plus, // it won't play nice with distributed compilation. // + // Note that this sort of appears to be the default from Clang + // 17, but not quite, see llvm-project issued #72383. + // args.push_back ("-Xclang"); args.push_back ("-fmodules-embed-all-files"); + if (relo.empty ()) + { + args.push_back ("-o"); + args.push_back (relm.string ().c_str ()); + args.push_back ("--precompile"); + } + else + { + out1 = "-fmodule-output=" + relm.string (); + args.push_back (out1.c_str ()); + args.push_back ("-o"); + args.push_back (relo.string ().c_str ()); + args.push_back ("-c"); + } + break; } case compiler_type::msvc: @@ -7011,7 +7799,7 @@ namespace build2 args.push_back ("-c"); } - lang_n = append_lang_options (args, md); + append_lang_options (args, md); if (md.pp == preprocessed::all) { @@ -7056,23 +7844,44 @@ namespace build2 if (!env.empty ()) env.push_back (nullptr); + // We have no choice but to serialize early if we want the command line + // printed shortly before actually executing the compiler. Failed that, + // it may look like we are still executing in parallel. + // + scheduler::alloc_guard jobs_ag; + if (!ctx.dry_run && cast_false<bool> (t[c_serialize])) + jobs_ag = scheduler::alloc_guard (*ctx.sched, phase_unlock (nullptr)); + // With verbosity level 2 print the command line as if we are compiling // the source file, not its preprocessed version (so that it's easy to // copy and re-run, etc). Only at level 3 and above print the real deal. // + // @@ TODO: why don't we print env (here and/or below)? Also link rule. + // if (verb == 1) - text << x_name << ' ' << s; + { + const char* name (x_assembler_cpp (s) ? "as-cpp" : + x_objective (s) ? x_obj_name : + x_name); + + print_diag (name, s, t); + } else if (verb == 2) print_process (args); // If we have the (partially) preprocessed output, switch to that. // - bool psrc (md.psrc); + // But we remember the original source/position to restore later. + // + bool psrc (md.psrc); // Note: false if cc.reprocess. bool ptmp (psrc && md.psrc.temporary); + pair<size_t, const char*> osrc; if (psrc) { args.pop_back (); // nullptr + osrc.second = args.back (); args.pop_back (); // sp + osrc.first = args.size (); sp = &md.psrc.path (); @@ -7082,25 +7891,40 @@ namespace build2 { case compiler_type::gcc: { - // The -fpreprocessed is implied by .i/.ii. But not when compiling - // a header unit (there is no .hi/.hii). + // -fpreprocessed is implied by .i/.ii unless compiling a header + // unit (there is no .hi/.hii). Also, we would need to pop -x + // since it takes precedence over the extension, which would mess + // up our osrc logic. So in the end it feels like always passing + // explicit -fpreprocessed is the way to go. // - if (ut == unit_type::module_header) - args.push_back ("-fpreprocessed"); - else - // Pop -x since it takes precedence over the extension. - // - // @@ I wonder why bother and not just add -fpreprocessed? Are - // we trying to save an option or does something break? - // - for (; lang_n != 0; --lang_n) - args.pop_back (); - + // Also note that similarly there is no .Si for .S files. + // + args.push_back ("-fpreprocessed"); args.push_back ("-fdirectives-only"); break; } case compiler_type::clang: { + // Clang 15 and later with -pedantic warns about GNU-style line + // markers that it wrote itself in the -frewrite-includes output + // (llvm-project issue 63284). So we suppress this warning unless + // compiling from source. + // + // In Apple Clang this warning/option are absent in 14.0.3 (which + // is said to be based on vanilla Clang 15.0.5) for some reason + // (let's hope it's because they patched it out rather than due to + // a misleading _LIBCPP_VERSION value). + // + if (ctype == compiler_type::clang && + cmaj >= (cvariant != "apple" ? 15 : 16)) + { + if (find_options ({"-pedantic", "-pedantic-errors", + "-Wpedantic", "-Werror=pedantic"}, args)) + { + args.push_back ("-Wno-gnu-line-marker"); + } + } + // Note that without -x Clang will treat .i/.ii as fully // preprocessed. // @@ -7149,45 +7973,38 @@ namespace build2 file_cache::read psrcr (psrc ? md.psrc.open () : file_cache::read ()); // VC cl.exe sends diagnostics to stdout. It also prints the file - // name being compiled as the first line. So for cl.exe we redirect - // stdout to a pipe, filter that noise out, and send the rest to - // stderr. + // name being compiled as the first line. So for cl.exe we filter + // that noise out. // - // For other compilers redirect stdout to stderr, in case any of - // them tries to pull off something similar. For sane compilers this - // should be harmless. + // For other compilers also redirect stdout to stderr, in case any + // of them tries to pull off something similar. For sane compilers + // this should be harmless. // bool filter (ctype == compiler_type::msvc); process pr (cpath, - args.data (), - 0, (filter ? -1 : 2), 2, + args, + 0, 2, diag_buffer::pipe (ctx, filter /* force */), nullptr, // CWD env.empty () ? nullptr : env.data ()); - if (filter) - { - try - { - ifdstream is ( - move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit); + diag_buffer dbuf (ctx, args[0], pr); - msvc_filter_cl (is, *sp); + if (filter) + msvc_filter_cl (dbuf, *sp); - // If anything remains in the stream, send it all to stderr. - // Note that the eof check is important: if the stream is at - // eof, this and all subsequent writes to the diagnostics stream - // will fail (and you won't see a thing). - // - if (is.peek () != ifdstream::traits_type::eof ()) - diag_stream_lock () << is.rdbuf (); + dbuf.read (); - is.close (); - } - catch (const io_error&) {} // Assume exits with error. + // Restore the original source if we switched to preprocessed. + // + if (psrc) + { + args.resize (osrc.first); + args.push_back (osrc.second); + args.push_back (nullptr); } - run_finish (args, pr); + run_finish (dbuf, args, pr, 1 /* verbosity */); } catch (const process_error& e) { @@ -7199,6 +8016,8 @@ namespace build2 throw failed (); } + jobs_ag.deallocate (); + if (md.deferred_failure) fail << "expected error exit status from " << x_lang << " compiler"; } @@ -7208,57 +8027,6 @@ namespace build2 if (ptmp && verb >= 3) md.psrc.temporary = true; - // Clang's module compilation requires two separate compiler - // invocations. - // - // @@ MODPART: Clang (all of this is probably outdated). - // - if (ctype == compiler_type::clang && ut == unit_type::module_intf) - { - // Adjust the command line. First discard everything after -o then - // build the new "tail". - // - args.resize (out_i + 1); - args.push_back (relo.string ().c_str ()); // Produce .o. - args.push_back ("-c"); // By compiling .pcm. - args.push_back ("-Wno-unused-command-line-argument"); - args.push_back (relm.string ().c_str ()); - args.push_back (nullptr); - - if (verb >= 2) - print_process (args); - - if (!ctx.dry_run) - { - // Remove the target file if this fails. If we don't do that, we - // will end up with a broken build that is up-to-date. - // - auto_rmfile rm (relm); - - try - { - process pr (cpath, - args.data (), - 0, 2, 2, - nullptr, // CWD - env.empty () ? nullptr : env.data ()); - - run_finish (args, pr); - } - catch (const process_error& e) - { - error << "unable to execute " << args[0] << ": " << e; - - if (e.child) - exit (1); - - throw failed (); - } - - rm.cancel (); - } - } - timestamp now (system_clock::now ()); if (!ctx.dry_run) @@ -7274,25 +8042,27 @@ namespace build2 } target_state compile_rule:: - perform_clean (action a, const target& xt) const + perform_clean (action a, const target& xt, const target_type& srct) const { const file& t (xt.as<file> ()); + // Preprocessed file extension. + // + const char* pext (x_assembler_cpp (srct) ? ".Si" : + x_objective (srct) ? x_obj_pext : + x_pext); + // Compressed preprocessed file extension. // - auto cpext = [this, &t, s = string ()] () mutable -> const char* - { - return (s = t.ctx.fcache.compressed_extension (x_pext)).c_str (); - }; + string cpext (t.ctx.fcache->compressed_extension (pext)); clean_extras extras; - switch (ctype) { - case compiler_type::gcc: extras = {".d", x_pext, cpext (), ".t"}; break; - case compiler_type::clang: extras = {".d", x_pext, cpext ()}; break; - case compiler_type::msvc: extras = {".d", x_pext, cpext (), ".idb", ".pdb"};break; - case compiler_type::icc: extras = {".d"}; break; + case compiler_type::gcc: extras = {".d", pext, cpext.c_str (), ".t"}; break; + case compiler_type::clang: extras = {".d", pext, cpext.c_str ()}; break; + case compiler_type::msvc: extras = {".d", pext, cpext.c_str (), ".idb", ".pdb"}; break; + case compiler_type::icc: extras = {".d"}; break; } return perform_clean_extra (a, t, extras); diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx index 95734e0..0886b4b 100644 --- a/libbuild2/cc/compile-rule.hxx +++ b/libbuild2/cc/compile-rule.hxx @@ -58,7 +58,7 @@ namespace build2 perform_update (action, const target&, match_data&) const; target_state - perform_clean (action, const target&) const; + perform_clean (action, const target&, const target_type&) const; public: using appended_libraries = small_vector<const target*, 256>; @@ -113,12 +113,12 @@ namespace build2 prefix_map build_prefix_map (const scope&, action, const target&, linfo) const; - struct module_mapper_state; + struct gcc_module_mapper_state; - bool - gcc_module_mapper (module_mapper_state&, + optional<bool> + gcc_module_mapper (gcc_module_mapper_state&, action, const scope&, file&, linfo, - ifdstream&, ofdstream&, + const string&, ofdstream&, depdb&, bool&, bool&, optional<prefix_map>&, srcout_map&) const; @@ -130,10 +130,11 @@ namespace build2 optional<bool> inject_header (action, file&, const file&, timestamp, bool) const; - pair<file_cache::entry, bool> + void extract_headers (action, const scope&, file&, linfo, const file&, match_data&, - depdb&, bool&, timestamp, module_imports&) const; + depdb&, bool&, timestamp, module_imports&, + pair<file_cache::entry, bool>&) const; string parse_unit (action, file&, linfo, @@ -155,8 +156,9 @@ namespace build2 pair<dir_path, const scope&> find_modules_sidebuild (const scope&) const; - const file& - make_module_sidebuild (action, const scope&, const file&, + pair<target&, ulock> + make_module_sidebuild (action, const scope&, + const file*, otype, const target&, const string&) const; const file& diff --git a/libbuild2/cc/functions.cxx b/libbuild2/cc/functions.cxx index 94900ee..9d408af 100644 --- a/libbuild2/cc/functions.cxx +++ b/libbuild2/cc/functions.cxx @@ -52,7 +52,7 @@ namespace build2 // if (bs->ctx.phase != run_phase::match && bs->ctx.phase != run_phase::execute) - fail << f.name << " can only be called during execution"; + fail << f.name << " can only be called from recipe"; const module* m (rs->find_module<module> (d.x)); @@ -131,7 +131,7 @@ namespace build2 if (bs->ctx.phase != run_phase::match && // See above. bs->ctx.phase != run_phase::execute) - fail << f.name << " can only be called during execution"; + fail << f.name << " can only be called from recipe"; const module* m (rs->find_module<module> (d.x)); diff --git a/libbuild2/cc/gcc.cxx b/libbuild2/cc/gcc.cxx index 7999c82..286ba10 100644 --- a/libbuild2/cc/gcc.cxx +++ b/libbuild2/cc/gcc.cxx @@ -45,6 +45,13 @@ namespace build2 d = dir_path (o, 2, string::npos); else continue; + + // Ignore relative paths. Or maybe we should warn? + // + if (d.relative ()) + continue; + + d.normalize (); } catch (const invalid_path& e) { @@ -52,10 +59,7 @@ namespace build2 << o << "'"; } - // Ignore relative paths. Or maybe we should warn? - // - if (!d.relative ()) - r.push_back (move (d)); + r.push_back (move (d)); } } @@ -78,6 +82,71 @@ namespace build2 } #endif + // Parse color/semicolon-separated list of search directories (from + // -print-search-dirs output, environment variables). + // + static void + parse_search_dirs (const string& v, dir_paths& r, + const char* what, const char* what2 = "") + { + // Now the fun part: figuring out which delimiter is used. Normally it + // is ':' but on Windows it is ';' (or can be; who knows for sure). Also + // note that these paths are absolute (or should be). So here is what we + // are going to do: first look for ';'. If found, then that's the + // delimiter. If not found, then there are two cases: it is either a + // single Windows path or the delimiter is ':'. To distinguish these two + // cases we check if the path starts with a Windows drive. + // + char d (';'); + string::size_type e (v.find (d)); + + if (e == string::npos && + (v.size () < 2 || v[0] == '/' || v[1] != ':')) + { + d = ':'; + e = v.find (d); + } + + // Now chop it up. We already have the position of the first delimiter + // (if any). + // + for (string::size_type b (0);; e = v.find (d, (b = e + 1))) + { + dir_path d; + try + { + string ds (v, b, (e != string::npos ? e - b : e)); + + // Skip empty entries (sometimes found in random MinGW toolchains). + // + if (!ds.empty ()) + { +#ifdef _WIN32 + if (path_traits::is_separator (ds[0])) + add_current_drive (ds); +#endif + d = dir_path (move (ds)); + + if (d.relative ()) + throw invalid_path (move (d).string ()); + + d.normalize (); + } + } + catch (const invalid_path& e) + { + fail << "invalid directory '" << e.path << "'" << " in " + << what << what2; + } + + if (!d.empty () && find (r.begin (), r.end (), d) == r.end ()) + r.push_back (move (d)); + + if (e == string::npos) + break; + } + } + // Extract system header search paths from GCC (gcc/g++) or compatible // (Clang, Intel) using the `-v -E </dev/null` method. // @@ -88,14 +157,15 @@ namespace build2 // do this is to run the compiler twice. // pair<dir_paths, size_t> config_module:: - gcc_header_search_dirs (const process_path& xc, scope& rs) const + gcc_header_search_dirs (const compiler_info& xi, scope& rs) const { dir_paths r; // Note also that any -I and similar that we may specify on the command - // line are factored into the output. + // line are factored into the output. As well as the CPATH, etc., + // environment variable values. // - cstrings args {xc.recall_string ()}; + cstrings args {xi.path.recall_string ()}; append_options (args, rs, x_mode); // Compile as. @@ -119,7 +189,7 @@ namespace build2 args.push_back ("-"); args.push_back (nullptr); - process_env env (xc); + process_env env (xi.path); // For now let's assume that all the platforms other than Windows // recognize LC_ALL. @@ -132,119 +202,109 @@ namespace build2 if (verb >= 3) print_process (env, args); + bool found_q (false); // Found `#include "..." ...` marker. + bool found_b (false); // Found `#include <...> ...` marker. + + // Open pipe to stderr, redirect stdin and stdout to /dev/null. + // + process pr (run_start ( + env, + args, + -2, /* stdin */ + -2, /* stdout */ + -1 /* stderr */)); try { - //@@ TODO: why don't we use run_start() here? Because it's unable to - // open pipe for stderr and we need to change it first, for example, - // making the err parameter a file descriptor rather than a flag. - // + ifdstream is ( + move (pr.in_efd), fdstream_mode::skip, ifdstream::badbit); - // Open pipe to stderr, redirect stdin and stdout to /dev/null. + // Normally the system header paths appear between the following + // lines: // - process pr (xc, - args.data (), - -2, /* stdin */ - -2, /* stdout */ - -1, /* stderr */ - nullptr /* cwd */, - env.vars); - - try + // #include <...> search starts here: + // End of search list. + // + // The exact text depends on the current locale. What we can rely on + // is the presence of the "#include <...>" marker in the "opening" + // line and the fact that the paths are indented with a single space + // character, unlike the "closing" line. + // + // Note that on Mac OS we will also see some framework paths among + // system header paths, followed with a comment. For example: + // + // /Library/Frameworks (framework directory) + // + // For now we ignore framework paths and to filter them out we will + // only consider valid paths to existing directories, skipping those + // which we fail to normalize or stat. @@ Maybe this is a bit too + // loose, especially compared to gcc_library_search_dirs()? + // + // Note that when there are no paths (e.g., because of -nostdinc), + // then GCC prints both #include markers while Clang -- only "...". + // + for (string s; getline (is, s); ) { - ifdstream is ( - move (pr.in_efd), fdstream_mode::skip, ifdstream::badbit); - - // Normally the system header paths appear between the following - // lines: - // - // #include <...> search starts here: - // End of search list. - // - // The exact text depends on the current locale. What we can rely on - // is the presence of the "#include <...>" substring in the - // "opening" line and the fact that the paths are indented with a - // single space character, unlike the "closing" line. - // - // Note that on Mac OS we will also see some framework paths among - // system header paths, followed with a comment. For example: - // - // /Library/Frameworks (framework directory) - // - // For now we ignore framework paths and to filter them out we will - // only consider valid paths to existing directories, skipping those - // which we fail to normalize or stat. @@ Maybe this is a bit too - // loose, especially compared to gcc_library_search_dirs()? - // - string s; - for (bool found (false); getline (is, s); ) + if (!found_q) + found_q = s.find ("#include \"...\"") != string::npos; + else if (!found_b) + found_b = s.find ("#include <...>") != string::npos; + else { - if (!found) - found = s.find ("#include <...>") != string::npos; - else - { - if (s[0] != ' ') - break; + if (s[0] != ' ') + break; - dir_path d; - try - { - string ds (s, 1, s.size () - 1); + dir_path d; + try + { + string ds (s, 1, s.size () - 1); #ifdef _WIN32 - if (path_traits::is_separator (ds[0])) - add_current_drive (ds); + if (path_traits::is_separator (ds[0])) + add_current_drive (ds); #endif - d = dir_path (move (ds)); - - if (d.relative () || !exists (d, true)) - continue; + d = dir_path (move (ds)); - d.normalize (); - } - catch (const invalid_path&) - { + if (d.relative () || !exists (d, true)) continue; - } - if (find (r.begin (), r.end (), d) == r.end ()) - r.emplace_back (move (d)); + d.normalize (); } + catch (const invalid_path&) + { + continue; + } + + if (find (r.begin (), r.end (), d) == r.end ()) + r.emplace_back (move (d)); } + } - is.close (); // Don't block. + is.close (); // Don't block. - if (!pr.wait ()) - { - // We have read stderr so better print some diagnostics. - // - diag_record dr (fail); + if (!run_wait (args, pr)) + { + // We have read stderr so better print some diagnostics. + // + diag_record dr (fail); - dr << "failed to extract " << x_lang << " header search paths" << - info << "command line: "; + dr << "failed to extract " << x_lang << " header search paths" << + info << "command line: "; - print_process (dr, args); - } - } - catch (const io_error&) - { - pr.wait (); - fail << "error reading " << x_lang << " compiler -v -E output"; + print_process (dr, args); } } - catch (const process_error& e) + catch (const io_error&) { - error << "unable to execute " << args[0] << ": " << e; - - if (e.child) - exit (1); - - throw failed (); + run_wait (args, pr); + fail << "error reading " << x_lang << " compiler -v -E output"; } - // It's highly unlikely not to have any system directories. More likely - // we misinterpreted the compiler output. + // Note that it's possible that we will have no system directories, for + // example, if the user specified -nostdinc. But we must have still seen + // at least one marker. Failed that we assume we misinterpreted the + // compiler output. // - if (r.empty ()) + if (!found_b && !found_q) fail << "unable to extract " << x_lang << " compiler system header " << "search paths"; @@ -255,7 +315,7 @@ namespace build2 // (Clang, Intel) using the -print-search-dirs option. // pair<dir_paths, size_t> config_module:: - gcc_library_search_dirs (const process_path& xc, scope& rs) const + gcc_library_search_dirs (const compiler_info& xi, scope& rs) const { // The output of -print-search-dirs are a bunch of lines that start with // "<name>: =" where name can be "install", "programs", or "libraries". @@ -282,12 +342,12 @@ namespace build2 gcc_extract_library_search_dirs (cast<strings> (rs[x_mode]), r); size_t rn (r.size ()); - cstrings args {xc.recall_string ()}; + cstrings args {xi.path.recall_string ()}; append_options (args, rs, x_mode); args.push_back ("-print-search-dirs"); args.push_back (nullptr); - process_env env (xc); + process_env env (xi.path); // For now let's assume that all the platforms other than Windows // recognize LC_ALL. @@ -302,6 +362,9 @@ namespace build2 // Open pipe to stdout. // + // Note: this function is called in the serial load phase and so no + // diagnostics buffering is needed. + // process pr (run_start (env, args, 0, /* stdin */ @@ -336,68 +399,22 @@ namespace build2 // by that and let run_finish() deal with it. } - run_finish (args, pr); + run_finish (args, pr, 2 /* verbosity */); if (l.empty ()) fail << "unable to extract " << x_lang << " compiler system library " << "search paths"; - // Now the fun part: figuring out which delimiter is used. Normally it - // is ':' but on Windows it is ';' (or can be; who knows for sure). Also - // note that these paths are absolute (or should be). So here is what we - // are going to do: first look for ';'. If found, then that's the - // delimiter. If not found, then there are two cases: it is either a - // single Windows path or the delimiter is ':'. To distinguish these two - // cases we check if the path starts with a Windows drive. - // - char d (';'); - string::size_type e (l.find (d)); + parse_search_dirs (l, r, args[0], " -print-search-dirs output"); - if (e == string::npos && - (l.size () < 2 || l[0] == '/' || l[1] != ':')) - { - d = ':'; - e = l.find (d); - } - - // Now chop it up. We already have the position of the first delimiter - // (if any). + // While GCC incorporates the LIBRARY_PATH environment variable value + // into the -print-search-dirs output, Clang does not. Also, unlike GCC, + // it appears to consider such paths last. // - for (string::size_type b (0);; e = l.find (d, (b = e + 1))) + if (xi.id.type == compiler_type::clang) { - dir_path d; - try - { - string ds (l, b, (e != string::npos ? e - b : e)); - - // Skip empty entries (sometimes found in random MinGW toolchains). - // - if (!ds.empty ()) - { -#ifdef _WIN32 - if (path_traits::is_separator (ds[0])) - add_current_drive (ds); -#endif - - d = dir_path (move (ds)); - - if (d.relative ()) - throw invalid_path (move (d).string ()); - - d.normalize (); - } - } - catch (const invalid_path& e) - { - fail << "invalid directory '" << e.path << "'" << " in " - << args[0] << " -print-search-dirs output"; - } - - if (!d.empty () && find (r.begin (), r.end (), d) == r.end ()) - r.emplace_back (move (d)); - - if (e == string::npos) - break; + if (optional<string> v = getenv ("LIBRARY_PATH")) + parse_search_dirs (*v, r, "LIBRARY_PATH environment variable"); } return make_pair (move (r), rn); diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index 13b60aa..5ae6fb2 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -106,7 +106,7 @@ namespace build2 else if (id.compare (0, p, "icc" ) == 0) type = compiler_type::icc; else throw invalid_argument ( - "invalid compiler type '" + string (id, 0, p) + "'"); + "invalid compiler type '" + string (id, 0, p) + '\''); if (p != string::npos) { @@ -181,12 +181,12 @@ namespace build2 // could also be because there is something wrong with the compiler or // options but that we simply leave to blow up later). // - process pr (run_start (3 /* verbosity */, + process pr (run_start (3 /* verbosity */, xp, args, - -1 /* stdin */, - -1 /* stdout */, - false /* error */)); + -1 /* stdin */, + -1 /* stdout */, + 1 /* stderr (to stdout) */)); string l, r; try { @@ -222,7 +222,7 @@ namespace build2 // that. } - if (!run_finish_code (args.data (), pr, l)) + if (!run_finish_code (args.data (), pr, l, 2 /* verbosity */)) r = "none"; if (r.empty ()) @@ -412,6 +412,8 @@ namespace build2 // // Note that Visual Studio versions prior to 15.0 are not supported. // + // Note also the directories are absolute and normalized. + // struct msvc_info { dir_path msvc_dir; // VC tools directory (...\Tools\MSVC\<ver>\). @@ -759,7 +761,7 @@ namespace build2 // for (const dir_entry& de: dir_iterator (r.psdk_dir / dir_path ("Include"), - false /* ignore_dangling */)) + dir_iterator::no_follow)) { if (de.type () == entry_type::directory) { @@ -777,6 +779,16 @@ namespace build2 return nullopt; } + try + { + r.msvc_dir.normalize (); + r.psdk_dir.normalize (); + } + catch (const invalid_path&) + { + return nullopt; + } + return r; } #endif @@ -817,7 +829,8 @@ namespace build2 // Note: allowed to change pre if succeeds. // static guess_result - guess (const char* xm, + guess (context& ctx, + const char* xm, lang xl, const path& xc, const strings& x_mo, @@ -1009,7 +1022,7 @@ namespace build2 #endif string cache; - auto run = [&cs, &env, &args, &cache] ( + auto run = [&ctx, &cs, &env, &args, &cache] ( const char* o, auto&& f, bool checksum = false) -> guess_result @@ -1017,9 +1030,10 @@ namespace build2 args[args.size () - 2] = o; cache.clear (); return build2::run<guess_result> ( + ctx, 3 /* verbosity */, env, - args.data (), + args, forward<decltype (f)> (f), false /* error */, false /* ignore_exit */, @@ -1066,7 +1080,7 @@ namespace build2 // The gcc -v output will have a last line in the form: // - // "gcc version X.Y[.Z][...] ..." + // "gcc version X[.Y[.Z]][...] ..." // // The "version" word can probably be translated. For example: // @@ -1078,6 +1092,7 @@ namespace build2 // gcc version 5.1.0 (Ubuntu 5.1.0-0ubuntu11~14.04.1) // gcc version 6.0.0 20160131 (experimental) (GCC) // gcc version 9.3-win32 20200320 (GCC) + // gcc version 10-win32 20220324 (GCC) // if (cache.empty ()) { @@ -1317,7 +1332,11 @@ namespace build2 // const char* evars[] = {"CL=", "_CL_=", nullptr}; - r = build2::run<guess_result> (3, process_env (xp, evars), f, false); + r = build2::run<guess_result> (ctx, + 3, + process_env (xp, evars), + f, + false); if (r.empty ()) { @@ -1530,6 +1549,8 @@ namespace build2 msvc_extract_header_search_dirs (mo, r); size_t rn (r.size ()); + // Note: the resulting directories are normalized by construction. + // r.push_back (dir_path (mi.msvc_dir) /= "include"); // This path structure only appeared in Platform SDK 10 (if anyone wants @@ -1579,6 +1600,8 @@ namespace build2 msvc_extract_library_search_dirs (mo, r); size_t rn (r.size ()); + // Note: the resulting directories are normalized by construction. + // r.push_back ((dir_path (mi.msvc_dir) /= "lib") /= cpu); // This path structure only appeared in Platform SDK 10 (if anyone wants @@ -1633,7 +1656,8 @@ namespace build2 "LIB", "LINK", "_LINK_", nullptr}; static compiler_info - guess_msvc (const char* xm, + guess_msvc (context&, + const char* xm, lang xl, const path& xc, const string* xv, @@ -1904,7 +1928,8 @@ namespace build2 "SDKROOT", "MACOSX_DEPLOYMENT_TARGET", nullptr}; static compiler_info - guess_gcc (const char* xm, + guess_gcc (context& ctx, + const char* xm, lang xl, const path& xc, const string* xv, @@ -1923,7 +1948,7 @@ namespace build2 // though language words can be translated and even rearranged (see // examples above). // - // "gcc version X.Y[.Z][...]" + // "gcc version X[.Y[.Z]][...]" // compiler_version ver; { @@ -1962,7 +1987,10 @@ namespace build2 // try { - semantic_version v (string (s, b, e - b), ".-+"); + semantic_version v (string (s, b, e - b), + semantic_version::allow_omit_minor | + semantic_version::allow_build, + ".-+"); ver.major = v.major; ver.minor = v.minor; ver.patch = v.patch; @@ -2014,7 +2042,7 @@ namespace build2 // auto f = [] (string& l, bool) {return move (l);}; - t = run<string> (3, xp, args.data (), f, false); + t = run<string> (ctx, 3, xp, args, f, false); if (t.empty ()) { @@ -2022,7 +2050,7 @@ namespace build2 << "falling back to -dumpmachine";}); args[args.size () - 2] = "-dumpmachine"; - t = run<string> (3, xp, args.data (), f, false); + t = run<string> (ctx, 3, xp, args, f, false); } if (t.empty ()) @@ -2165,9 +2193,9 @@ namespace build2 process pr (run_start (3 /* verbosity */, xp, args, - -2 /* stdin (/dev/null) */, - -1 /* stdout */, - false /* error (2>&1) */)); + -2 /* stdin (to /dev/null) */, + -1 /* stdout */, + 1 /* stderr (to stdout) */)); clang_msvc_info r; @@ -2319,7 +2347,7 @@ namespace build2 // that. } - if (!run_finish_code (args.data (), pr, l)) + if (!run_finish_code (args.data (), pr, l, 2 /* verbosity */)) fail << "unable to extract MSVC information from " << xp; if (const char* w = ( @@ -2337,23 +2365,27 @@ namespace build2 // These are derived from gcc_* plus the sparse documentation (clang(1)) // and source code. // + // Note that for now for Clang targeting MSVC we use msvc_env but should + // probably use a combined list. + // // See also the note on environment and caching below if adding any new // variables. // static const char* clang_c_env[] = { - "CPATH", "C_INCLUDE_PATH", + "CPATH", "C_INCLUDE_PATH", "CCC_OVERRIDE_OPTIONS", "LIBRARY_PATH", "LD_RUN_PATH", "COMPILER_PATH", nullptr}; static const char* clang_cxx_env[] = { - "CPATH", "CPLUS_INCLUDE_PATH", + "CPATH", "CPLUS_INCLUDE_PATH", "CCC_OVERRIDE_OPTIONS", "LIBRARY_PATH", "LD_RUN_PATH", "COMPILER_PATH", nullptr}; static compiler_info - guess_clang (const char* xm, + guess_clang (context& ctx, + const char* xm, lang xl, const path& xc, const string* xv, @@ -2392,6 +2424,12 @@ namespace build2 // // emcc (...) 2.0.8 // + // Pre-releases of the vanilla Clang append `rc` or `git` to the + // version, unfortunately without a separator. So we will handle these + // ad hoc. For example: + // + // FreeBSD clang version 18.1.0rc (https://github.com/llvm/llvm-project.git llvmorg-18-init-18361-g22683463740e) + // auto extract_version = [] (const string& s, bool patch, const char* what) -> compiler_version { @@ -2406,8 +2444,28 @@ namespace build2 // end of the word position (first space). In fact, we can just // check if it is >= e. // - if (s.find_first_not_of ("1234567890.", b, 11) >= e) + size_t p (s.find_first_not_of ("1234567890.", b, 11)); + if (p >= e) break; + + // Handle the unseparated `rc` and `git` suffixes. + // + if (p != string::npos) + { + if (p + 2 == e && (e - b) > 2 && + s[p] == 'r' && s[p + 1] == 'c') + { + e -= 2; + break; + } + + if (p + 3 == e && (e - b) > 3 && + s[p] == 'g' && s[p + 1] == 'i' && s[p + 2] == 't') + { + e -= 3; + break; + } + } } if (b == e) @@ -2443,7 +2501,14 @@ namespace build2 ver.patch = next ("patch", patch); if (e != s.size ()) - ver.build.assign (s, e + 1, string::npos); + { + // Skip the separator (it could also be unseparated `rc` or `git`). + // + if (s[e] == ' ' || s[e] == '-') + e++; + + ver.build.assign (s, e, string::npos); + } return ver; }; @@ -2467,7 +2532,10 @@ namespace build2 // Some overrides for testing. // + //string s (xv != nullptr ? *xv : ""); + // //s = "clang version 3.7.0 (tags/RELEASE_370/final)"; + //s = "FreeBSD clang version 18.1.0rc (https://github.com/llvm/llvm-project.git llvmorg-18-init-18361-g22683463740e)"; // //gr.id.variant = "apple"; //s = "Apple LLVM version 7.3.0 (clang-703.0.16.1)"; @@ -2495,59 +2563,77 @@ namespace build2 // // Specifically, we now look in the libc++'s __config file for the // _LIBCPP_VERSION and use the previous version as a conservative - // estimate (NOTE that there could be multiple __config files with + // estimate (NOTE: that there could be multiple __config files with // potentially different versions so compile with -v to see which one // gets picked up). // + // Also, lately, we started seeing _LIBCPP_VERSION values like 15.0.6 + // or 16.0.2 which would suggest the base is 15.0.5 or 16.0.1. But + // that assumption did not check out with the actual usage. For + // example, vanilla Clang 16 should no longer require -fmodules-ts but + // the Apple's version (that is presumably based on it) still does. So + // the theory here is that Apple upgrades to newer libc++ while + // keeping the old compiler. Which means we must be more conservative + // and assume something like 15.0.6 is still 14-based. But then you + // get -Wunqualified-std-cast-call in 14, which was supposedly only + // introduced in Clang 15. So maybe not. + // // Note that this is Apple Clang version and not XCode version. // - // 4.2 -> 3.2svn - // 5.0 -> 3.3svn - // 5.1 -> 3.4svn - // 6.0 -> 3.5svn - // 6.1.0 -> 3.6svn - // 7.0.0 -> 3.7 - // 7.3.0 -> 3.8 - // 8.0.0 -> 3.9 - // 8.1.0 -> ? - // 9.0.0 -> 4.0 - // 9.1.0 -> 5.0 - // 10.0.0 -> 6.0 - // 11.0.0 -> 7.0 - // 11.0.3 -> 8.0 (yes, seriously!) - // 12.0.0 -> 9.0 - // 12.0.5 -> 10.0 (yes, seriously!) - // 13.0.0 -> 11.0 - // 13.1.6 -> 12.0 + // 4.2 -> 3.2svn + // 5.0 -> 3.3svn + // 5.1 -> 3.4svn + // 6.0 -> 3.5svn + // 6.1.0 -> 3.6svn + // 7.0.0 -> 3.7 + // 7.3.0 -> 3.8 + // 8.0.0 -> 3.9 + // 8.1.0 -> ? + // 9.0.0 -> 4.0 + // 9.1.0 -> 5.0 + // 10.0.0 -> 6.0 + // 11.0.0 -> 7.0 + // 11.0.3 -> 8.0 (yes, seriously!) + // 12.0.0 -> 9.0 + // 12.0.5 -> 10.0 (yes, seriously!) + // 13.0.0 -> 11.0 + // 13.1.6 -> 12.0 + // 14.0.0 -> 12.0 (_LIBCPP_VERSION=130000) + // 14.0.3 -> 15.0 (_LIBCPP_VERSION=150006) + // 15.0.0.0 -> 16.0 (_LIBCPP_VERSION=160002) + // 15.0.0.1 -> 16.0 (_LIBCPP_VERSION=160006) + // 15.0.0.3 -> 16.0 (_LIBCPP_VERSION=170006) // uint64_t mj (var_ver->major); uint64_t mi (var_ver->minor); uint64_t pa (var_ver->patch); - if (mj > 13 || (mj == 13 && mi >= 1)) {mj = 12; mi = 0;} - else if (mj == 13) {mj = 11; mi = 0;} - else if (mj == 12 && (mi > 0 || pa >= 5)) {mj = 10; mi = 0;} - else if (mj == 12) {mj = 9; mi = 0;} - else if (mj == 11 && (mi > 0 || pa >= 3)) {mj = 8; mi = 0;} - else if (mj == 11) {mj = 7; mi = 0;} - else if (mj == 10) {mj = 6; mi = 0;} - else if (mj == 9 && mi >= 1) {mj = 5; mi = 0;} - else if (mj == 9) {mj = 4; mi = 0;} - else if (mj == 8) {mj = 3; mi = 9;} - else if (mj == 7 && mi >= 3) {mj = 3; mi = 8;} - else if (mj == 7) {mj = 3; mi = 7;} - else if (mj == 6 && mi >= 1) {mj = 3; mi = 5;} - else if (mj == 6) {mj = 3; mi = 4;} - else if (mj == 5 && mi >= 1) {mj = 3; mi = 3;} - else if (mj == 5) {mj = 3; mi = 2;} - else if (mj == 4 && mi >= 2) {mj = 3; mi = 1;} - else {mj = 3; mi = 0;} + if (mj >= 15) {mj = 16; mi = 0; pa = 0;} + else if (mj == 14 && (mi > 0 || pa >= 3)) {mj = 15; mi = 0; pa = 0;} + else if (mj == 14 || (mj == 13 && mi >= 1)) {mj = 12; mi = 0; pa = 0;} + else if (mj == 13) {mj = 11; mi = 0; pa = 0;} + else if (mj == 12 && (mi > 0 || pa >= 5)) {mj = 10; mi = 0; pa = 0;} + else if (mj == 12) {mj = 9; mi = 0; pa = 0;} + else if (mj == 11 && (mi > 0 || pa >= 3)) {mj = 8; mi = 0; pa = 0;} + else if (mj == 11) {mj = 7; mi = 0; pa = 0;} + else if (mj == 10) {mj = 6; mi = 0; pa = 0;} + else if (mj == 9 && mi >= 1) {mj = 5; mi = 0; pa = 0;} + else if (mj == 9) {mj = 4; mi = 0; pa = 0;} + else if (mj == 8) {mj = 3; mi = 9; pa = 0;} + else if (mj == 7 && mi >= 3) {mj = 3; mi = 8; pa = 0;} + else if (mj == 7) {mj = 3; mi = 7; pa = 0;} + else if (mj == 6 && mi >= 1) {mj = 3; mi = 5; pa = 0;} + else if (mj == 6) {mj = 3; mi = 4; pa = 0;} + else if (mj == 5 && mi >= 1) {mj = 3; mi = 3; pa = 0;} + else if (mj == 5) {mj = 3; mi = 2; pa = 0;} + else if (mj == 4 && mi >= 2) {mj = 3; mi = 1; pa = 0;} + else {mj = 3; mi = 0; pa = 0;} ver = compiler_version { - to_string (mj) + '.' + to_string (mi) + ".0", + to_string (mj) + '.' + to_string (mi) + '.' + to_string (pa), mj, mi, - 0, + pa, ""}; } else if (emscr) @@ -2600,7 +2686,7 @@ namespace build2 // for LC_ALL. // auto f = [] (string& l, bool) {return move (l);}; - t = run<string> (3, xp, args.data (), f, false); + t = run<string> (ctx, 3, xp, args, f, false); if (t.empty ()) fail << "unable to extract target architecture from " << xc @@ -2660,7 +2746,7 @@ namespace build2 const char* cpu (msvc_cpu (tt.cpu)); // Come up with the system library search paths. Ideally we would want - // to extract this from Clang and -print-search-paths would have been + // to extract this from Clang and -print-search-dirs would have been // the natural way for Clang to report it. But no luck. // lib_dirs = msvc_lib (mi, x_mo, cpu); @@ -2828,7 +2914,8 @@ namespace build2 } static compiler_info - guess_icc (const char* xm, + guess_icc (context& ctx, + const char* xm, lang xl, const path& xc, const string* xv, @@ -2892,7 +2979,7 @@ namespace build2 // // @@ TODO: running without the mode options. // - s = run<string> (3, env, "-V", f, false); + s = run<string> (ctx, 3, env, "-V", f, false); if (s.empty ()) fail << "unable to extract signature from " << xc << " -V output"; @@ -3018,7 +3105,7 @@ namespace build2 // The -V output is sent to STDERR. // - t = run<string> (3, env, args.data (), f, false); + t = run<string> (ctx, 3, env, args, f, false); if (t.empty ()) fail << "unable to extract target architecture from " << xc @@ -3069,7 +3156,7 @@ namespace build2 // { auto f = [] (string& l, bool) {return move (l);}; - t = run<string> (3, xp, "-dumpmachine", f); + t = run<string> (ctx, 3, xp, "-dumpmachine", f); } if (t.empty ()) @@ -3150,7 +3237,8 @@ namespace build2 static global_cache<compiler_info> cache; const compiler_info& - guess (const char* xm, + guess (context& ctx, + const char* xm, lang xl, const string& ec, const path& xc, @@ -3224,7 +3312,7 @@ namespace build2 if (pre.type != invalid_compiler_type) { - gr = guess (xm, xl, xc, x_mo, xi, pre, cs); + gr = guess (ctx, xm, xl, xc, x_mo, xi, pre, cs); if (gr.empty ()) { @@ -3240,13 +3328,14 @@ namespace build2 } if (gr.empty ()) - gr = guess (xm, xl, xc, x_mo, xi, pre, cs); + gr = guess (ctx, xm, xl, xc, x_mo, xi, pre, cs); if (gr.empty ()) fail << "unable to guess " << xl << " compiler type of " << xc << info << "use config." << xm << ".id to specify explicitly"; compiler_info (*gf) ( + context&, const char*, lang, const path&, const string*, const string*, const strings&, const strings*, const strings*, @@ -3266,7 +3355,8 @@ namespace build2 case compiler_type::icc: gf = &guess_icc; break; } - compiler_info r (gf (xm, xl, xc, xv, xt, + compiler_info r (gf (ctx, + xm, xl, xc, xv, xt, x_mo, c_po, x_po, c_co, x_co, c_lo, x_lo, move (gr), cs)); @@ -3424,6 +3514,7 @@ namespace build2 // In the future we will probably have to maintain per-standard additions. // static const char* std_importable[] = { + "<initializer_list>", // Note: keep first (present in freestanding). "<algorithm>", "<any>", "<array>", @@ -3448,7 +3539,6 @@ namespace build2 "<fstream>", "<functional>", "<future>", - "<initializer_list>", "<iomanip>", "<ios>", "<iosfwd>", @@ -3547,6 +3637,9 @@ namespace build2 // is currently not provided by GCC. Though entering missing headers // should be harmless. // + // Plus, a freestanding implementation may only have a subset of such + // headers (see [compliance]). + // pair<const path, importable_headers::groups>* p; auto add_groups = [&p] (bool imp) { @@ -3568,29 +3661,39 @@ namespace build2 } else { + // While according to [compliance] a freestanding implementation + // should provide a subset of headers, including <initializer_list>, + // there seem to be cases where no headers are provided at all (see GH + // issue #219). So if we cannot find <initializer_list>, we just skip + // the whole thing. + // p = hs.insert_angle (sys_hdr_dirs, std_importable[0]); - assert (p != nullptr); - add_groups (true); + if (p != nullptr) + { + assert (p != nullptr); - dir_path d (p->first.directory ()); + add_groups (true); - auto add_header = [&hs, &d, &p, add_groups] (const char* f, bool imp) - { - path fp (d); - fp.combine (f + 1, strlen (f) - 2, '\0'); // Assuming simple. + dir_path d (p->first.directory ()); - p = &hs.insert_angle (move (fp), f); - add_groups (imp); - }; + auto add_header = [&hs, &d, &p, add_groups] (const char* f, bool imp) + { + path fp (d); + fp.combine (f + 1, strlen (f) - 2, '\0'); // Assuming simple. - for (size_t i (1); - i != sizeof (std_importable) / sizeof (std_importable[0]); - ++i) - add_header (std_importable[i], true); + p = &hs.insert_angle (move (fp), f); + add_groups (imp); + }; - for (const char* f: std_non_importable) - add_header (f, false); + for (size_t i (1); + i != sizeof (std_importable) / sizeof (std_importable[0]); + ++i) + add_header (std_importable[i], true); + + for (const char* f: std_non_importable) + add_header (f, false); + } } } } diff --git a/libbuild2/cc/guess.hxx b/libbuild2/cc/guess.hxx index 53acc15..7cbbd87 100644 --- a/libbuild2/cc/guess.hxx +++ b/libbuild2/cc/guess.hxx @@ -253,7 +253,8 @@ 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 var names in diagnostics). + guess (context&, + const char* xm, // Module (for var names in diagnostics). lang xl, // Language. const string& ec, // Environment checksum. const path& xc, // Compiler path. diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx index 062e750..e124450 100644 --- a/libbuild2/cc/init.cxx +++ b/libbuild2/cc/init.cxx @@ -86,7 +86,10 @@ namespace build2 // Enter variables. // - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); auto v_t (variable_visibility::target); @@ -97,13 +100,19 @@ 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<string> ("config.cc.internal.scope"); + + vp.insert<bool> ("config.cc.reprocess"); // See cc.preprocess below. + + vp.insert<abs_dir_path> ("config.cc.pkgconfig.sysroot"); 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.internal.libs"); @@ -117,8 +126,8 @@ namespace build2 // files instead of the default install.{include,lib}. Relative paths // are resolved as install paths. // - vp.insert<dir_paths> ("cc.pkconfig.include"); - vp.insert<dir_paths> ("cc.pkconfig.lib"); + vp.insert<dir_paths> ("cc.pkgconfig.include"); + vp.insert<dir_paths> ("cc.pkgconfig.lib"); // Hint variables (not overridable). // @@ -174,9 +183,15 @@ namespace build2 // Ability to disable using preprocessed output for compilation. // - vp.insert<bool> ("config.cc.reprocess"); vp.insert<bool> ("cc.reprocess"); + // Execute serially with regards to any other recipe. This is primarily + // useful when compiling large translation units or linking large + // binaries that require so much memory that doing that in parallel with + // other compilation/linking jobs is likely to summon the OOM killer. + // + vp.insert<bool> ("cc.serialize"); + // Register scope operation callback. // // It feels natural to clean up sidebuilds as a post operation but that @@ -334,14 +349,24 @@ namespace build2 if (lookup l = lookup_config (rs, "config.cc.reprocess")) rs.assign ("cc.reprocess") = *l; + // config.cc.pkgconfig.sysroot + // + // Let's look it up instead of just marking for saving to make sure the + // path is valid. + // + // Note: save omitted. + // + lookup_config (rs, "config.cc.pkgconfig.sysroot"); + // Load the bin.config module. // if (!cast_false<bool> (rs["bin.config.loaded"])) { - // Prepare configuration hints. They are only used on the first load - // of bin.config so we only populate them on our first load. + // Prepare configuration hints (pretend it belongs to root scope). + // They are only used on the first load of bin.config so we only + // populate them on our first load. // - variable_map h (rs.ctx); + variable_map h (rs); if (first) { diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx index 0b4d1e1..6758e03 100644 --- a/libbuild2/cc/install-rule.cxx +++ b/libbuild2/cc/install-rule.cxx @@ -18,20 +18,67 @@ namespace build2 { using namespace bin; + using posthoc_prerequisite_target = + context::posthoc_target::prerequisite_target; + // install_rule // install_rule:: install_rule (data&& d, const link_rule& l) : common (move (d)), link_ (l) {} - const target* install_rule:: + // Wrap the file_rule's recipe into a data-carrying recipe. + // + struct install_match_data + { + build2::recipe recipe; + uint64_t options; // Match options. + link_rule::libs_paths libs_paths; + + target_state + operator() (action a, const target& t) + { + return recipe (a, t); + } + }; + + bool install_rule:: + filter (action a, const target& t, const target& m) const + { + if (!t.is_a<exe> ()) + { + // If runtime-only, filter out all known buildtime target types. + // + const auto& md (t.data<install_match_data> (a)); + + if ((md.options & lib::option_install_buildtime) == 0) + { + if (m.is_a<liba> () || // Staic library. + m.is_a<pc> () || // pkg-config file. + m.is_a<libi> ()) // Import library. + return false; + } + } + + return true; + } + + pair<const target*, uint64_t> install_rule:: filter (const scope* is, - action a, const target& t, prerequisite_iterator& i) const + action a, const target& t, prerequisite_iterator& i, + match_extra& me) const { // NOTE: see libux_install_rule::filter() if changing anything here. const prerequisite& p (i->prerequisite); + uint64_t options (match_extra::all_options); + + otype ot (link_type (t).type); + + // @@ TMP: drop eventually. + // +#if 0 // If this is a shared library prerequisite, install it as long as it is // in the installation scope. // @@ -43,10 +90,14 @@ namespace build2 // // Note: we install ad hoc prerequisites by default. // - otype ot (link_type (t).type); + // Note: at least one must be true since we only register this rule for + // exe{}, and lib[as]{} (this makes sure the following if-condition will + // always be true for libx{}). + // bool st (t.is_a<exe> () || t.is_a<libs> ()); // Target needs shared. bool at (t.is_a<liba> () || t.is_a<libs> ()); // Target needs static. + assert (st || at); if ((st && (p.is_a<libx> () || p.is_a<libs> ())) || (at && (p.is_a<libx> () || p.is_a<liba> ()))) @@ -59,26 +110,115 @@ namespace build2 if (const libx* l = pt->is_a<libx> ()) pt = link_member (*l, a, link_info (t.base_scope (), ot)); - // Note: not redundant since we are returning a member. + // Note: not redundant since we could be returning a member. // if ((st && pt->is_a<libs> ()) || (at && pt->is_a<liba> ())) - return is == nullptr || pt->in (*is) ? pt : nullptr; + { + // Adjust match options. + // + if (a.operation () != update_id) + { + if (t.is_a<exe> ()) + options = lib::option_install_runtime; + else + { + // This is a library prerequisite of a library target and + // runtime-only begets runtime-only. + // + if (me.cur_options == lib::option_install_runtime) + options = lib::option_install_runtime; + } + } + + return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr, + options); + } // See through to libu*{} members. Note that we are always in the same // project (and thus amalgamation). // if (pt->is_a<libux> ()) - return pt; + { + // Adjust match options (similar to above). + // + if (a.operation () != update_id && !pt->is_a<libue> ()) + { + if (t.is_a<exe> ()) + options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + options = lib::option_install_runtime; + } + } + + return make_pair (pt, options); + } + } +#else + // Note that at first it may seem like we don't need to install static + // library prerequisites of executables. But such libraries may still + // have prerequisites that are needed at runtime (say, some data files). + // So we install all libraries as long as they are in the installation + // scope and deal with runtime vs buildtime distiction using match + // options. + // + // Note: for now we assume these prerequisites never come from see- + // through groups. + // + // Note: we install ad hoc prerequisites by default. + // + if (p.is_a<libx> () || p.is_a<libs> () || p.is_a<liba> ()) + { + const target* pt (&search (t, p)); + + // If this is the lib{}/libu*{} group, pick a member which we would + // link. For libu*{} we want the "see through" logic. + // + if (const libx* l = pt->is_a<libx> ()) + pt = link_member (*l, a, link_info (t.base_scope (), ot)); + + // Adjust match options. + // + if (a.operation () != update_id) + { + if (t.is_a<exe> ()) + options = lib::option_install_runtime; + else + { + // This is a library prerequisite of a library target and + // runtime-only begets runtime-only. + // + if (me.cur_options == lib::option_install_runtime) + options = lib::option_install_runtime; + } + } + + // Note: not redundant since we could be returning a member. + // + if (pt->is_a<libs> () || pt->is_a<liba> ()) + { + return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr, + options); + } + else // libua{} or libus{} + { + // See through to libu*{} members. Note that we are always in the + // same project (and thus amalgamation). + // + return make_pair (pt, options); + } } +#endif // The rest of the tests only succeed if the base filter() succeeds. // - const target* pt (file_rule::filter (is, a, t, p)); + const target* pt (file_rule::filter (is, a, t, p, me).first); if (pt == nullptr) - return pt; + return make_pair (pt, options); - // Don't install executable's prerequisite headers and module - // interfaces. + // Don't install executable's or runtime-only library's prerequisite + // headers and module interfaces. // // Note that if they come from a group, then we assume the entire // group is not to be installed. @@ -88,12 +228,18 @@ namespace build2 // auto header_source = [this] (const auto& p) { - return (x_header (p) || - p.is_a (x_src) || - (x_mod != nullptr && p.is_a (*x_mod))); + return (x_header (p) || + p.is_a (x_src) || + p.is_a (c::static_type) || + p.is_a (S::static_type) || + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && (p.is_a (*x_obj) || + p.is_a (m::static_type)))); }; - if (t.is_a<exe> ()) + if (t.is_a<exe> () || + (a.operation () != update_id && + me.cur_options == lib::option_install_runtime)) { if (header_source (p)) pt = nullptr; @@ -108,7 +254,7 @@ namespace build2 } if (pt == nullptr) - return pt; + return make_pair (pt, options); } // Here is a problem: if the user spells the obj*/bmi*{} targets @@ -138,16 +284,16 @@ namespace build2 { pt = t.is_a<exe> () ? nullptr - : file_rule::filter (is, a, *pt, pm.prerequisite); + : file_rule::filter (is, a, *pt, pm.prerequisite, me).first; break; } } if (pt == nullptr) - return pt; + return make_pair (pt, options); } - return pt; + return make_pair (pt, options); } bool install_rule:: @@ -160,27 +306,34 @@ namespace build2 file_rule::match (a, t); } - // Wrap the file_rule's recipe into a data-carrying recipe. - // - struct install_match_data + recipe install_rule:: + apply (action a, target& t, match_extra& me) const { - build2::recipe recipe; - link_rule::libs_paths libs_paths; - - target_state - operator() (action a, const target& t) + // Handle match options. + // + // Do it before calling apply_impl() since we need this information + // in the filter() callbacks. + // + if (a.operation () != update_id) { - return recipe (a, t); + if (!t.is_a<exe> ()) + { + if (me.new_options == 0) + me.new_options = lib::option_install_runtime; // Minimum we can do. + + me.cur_options = me.new_options; + } } - }; - recipe install_rule:: - apply (action a, target& t) const - { - recipe r (file_rule::apply_impl (a, t)); + recipe r (file_rule::apply_impl ( + a, t, me, + me.cur_options != match_extra::all_options /* reapply */)); if (r == nullptr) + { + me.cur_options = match_extra::all_options; // Noop for all options. return noop_recipe; + } if (a.operation () == update_id) { @@ -202,29 +355,109 @@ namespace build2 } else // install or uninstall { - // Derive shared library paths and cache them in the target's aux - // storage if we are un/installing (used in the *_extra() functions - // below). - // - if (file* f = t.is_a<libs> ()) + file* ls; + if ((ls = t.is_a<libs> ()) || t.is_a<liba> ()) { - if (!f->path ().empty ()) // Not binless. + // Derive shared library paths and cache them in the target's aux + // storage if we are un/installing (used in the *_extra() functions + // below). + // + link_rule::libs_paths lsp; + if (ls != nullptr && !ls->path ().empty ()) // Not binless. { const string* p (cast_null<string> (t["bin.lib.prefix"])); const string* s (cast_null<string> (t["bin.lib.suffix"])); - return install_match_data { - move (r), - link_.derive_libs_paths (*f, - p != nullptr ? p->c_str (): nullptr, - s != nullptr ? s->c_str (): nullptr)}; + lsp = link_.derive_libs_paths (*ls, + p != nullptr ? p->c_str (): nullptr, + s != nullptr ? s->c_str (): nullptr); } + + return install_match_data {move (r), me.cur_options, move (lsp)}; } } return r; } + void install_rule:: + apply_posthoc (action a, target& t, match_extra& me) const + { + // Similar semantics to filter() above for shared libraries specified as + // post hoc prerequisites (e.g., plugins). + // + if (a.operation () != update_id) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + if (t.is_a<exe> ()) + p.match_options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + p.match_options = lib::option_install_runtime; + } + } + } + } + } + + void install_rule:: + reapply (action a, target& t, match_extra& me) const + { + tracer trace ("cc::install_rule::reapply"); + + assert (a.operation () != update_id && !t.is_a<exe> ()); + + l6 ([&]{trace << "rematching " << t + << ", current options " << me.cur_options + << ", new options " << me.new_options;}); + + me.cur_options |= me.new_options; + + // We also need to update options in install_match_data. + // + t.data<install_match_data> (a).options = me.cur_options; + + if ((me.new_options & lib::option_install_buildtime) != 0) + { + // If we are rematched with the buildtime option, propagate it to our + // prerequisite libraries. + // + for (const target* pt: t.prerequisite_targets[a]) + { + if (pt != nullptr && (pt->is_a<liba> () || pt->is_a<libs> () || + pt->is_a<libua> () || pt->is_a<libus> ())) + { + // Go for all options instead of just install_buildtime to avoid + // any further relocking/reapply (we only support runtime-only or + // everything). + // + rematch_sync (a, *pt, match_extra::all_options); + } + } + + // Also to post hoc. + // + if (me.posthoc_prerequisite_targets != nullptr) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + p.match_options = match_extra::all_options; + } + } + } + + // Also match any additional prerequisites (e.g., headers). + // + file_rule::reapply_impl (a, t, me); + } + } + bool install_rule:: install_extra (const file& t, const install_dir& id) const { @@ -232,14 +465,19 @@ namespace build2 if (t.is_a<libs> ()) { + const auto& md (t.data<install_match_data> (perform_install_id)); + // Here we may have a bunch of symlinks that we need to install. // + // Note that for runtime-only install we only omit the name that is + // used for linking (e.g., libfoo.so). + // const scope& rs (t.root_scope ()); - auto& lp (t.data<install_match_data> (perform_install_id).libs_paths); + const link_rule::libs_paths& lp (md.libs_paths); - auto ln = [&rs, &id] (const path& f, const path& l) + auto ln = [&t, &rs, &id] (const path& f, const path& l) { - install_l (rs, id, f.leaf (), l.leaf (), 2 /* verbosity */); + install_l (rs, id, l.leaf (), t, f.leaf (), 2 /* verbosity */); return true; }; @@ -253,7 +491,10 @@ namespace build2 if (!in.empty ()) {r = ln (*f, in) || r; f = ∈} if (!so.empty ()) {r = ln (*f, so) || r; f = &so;} if (!ld.empty ()) {r = ln (*f, ld) || r; f = &ld;} - if (!lk.empty ()) {r = ln (*f, lk) || r; } + if ((md.options & lib::option_install_buildtime) != 0) + { + if (!lk.empty ()) {r = ln (*f, lk) || r;} + } } return r; @@ -266,14 +507,16 @@ namespace build2 if (t.is_a<libs> ()) { + const auto& md (t.data<install_match_data> (perform_uninstall_id)); + // Here we may have a bunch of symlinks that we need to uninstall. // const scope& rs (t.root_scope ()); - auto& lp (t.data<install_match_data> (perform_uninstall_id).libs_paths); + const link_rule::libs_paths& lp (md.libs_paths); - auto rm = [&rs, &id] (const path& l) + auto rm = [&rs, &id] (const path& f, const path& l) { - return uninstall_f (rs, id, nullptr, l.leaf (), 2 /* verbosity */); + return uninstall_l (rs, id, l.leaf (), f.leaf (), 2 /* verbosity */); }; const path& lk (lp.link); @@ -281,10 +524,15 @@ namespace build2 const path& so (lp.soname); const path& in (lp.interm); - if (!lk.empty ()) r = rm (lk) || r; - if (!ld.empty ()) r = rm (ld) || r; - if (!so.empty ()) r = rm (so) || r; - if (!in.empty ()) r = rm (in) || r; + const path* f (lp.real); + + if (!in.empty ()) {r = rm (*f, in) || r; f = ∈} + if (!so.empty ()) {r = rm (*f, so) || r; f = &so;} + if (!ld.empty ()) {r = rm (*f, ld) || r; f = &ld;} + if ((md.options & lib::option_install_buildtime) != 0) + { + if (!lk.empty ()) {r = rm (*f, lk) || r;} + } } return r; @@ -296,22 +544,30 @@ namespace build2 libux_install_rule (data&& d, const link_rule& l) : common (move (d)), link_ (l) {} - const target* libux_install_rule:: + pair<const target*, uint64_t> libux_install_rule:: filter (const scope* is, - action a, const target& t, prerequisite_iterator& i) const + action a, const target& t, prerequisite_iterator& i, + match_extra& me) const { using file_rule = install::file_rule; const prerequisite& p (i->prerequisite); + uint64_t options (match_extra::all_options); + + otype ot (link_type (t).type); + // The "see through" semantics that should be parallel to install_rule // above. In particular, here we use libue/libua/libus{} as proxies for // exe/liba/libs{} there. // - otype ot (link_type (t).type); + // @@ TMP: drop eventually. + // +#if 0 bool st (t.is_a<libue> () || t.is_a<libus> ()); // Target needs shared. bool at (t.is_a<libua> () || t.is_a<libus> ()); // Target needs static. + assert (st || at); if ((st && (p.is_a<libx> () || p.is_a<libs> ())) || (at && (p.is_a<libx> () || p.is_a<liba> ()))) @@ -322,24 +578,85 @@ namespace build2 pt = link_member (*l, a, link_info (t.base_scope (), ot)); if ((st && pt->is_a<libs> ()) || (at && pt->is_a<liba> ())) - return is == nullptr || pt->in (*is) ? pt : nullptr; + { + if (a.operation () != update_id) + { + if (t.is_a<libue> ()) + options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + options = lib::option_install_runtime; + } + } + + return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr, + options); + } if (pt->is_a<libux> ()) - return pt; + { + if (a.operation () != update_id && !pt->is_a<libue> ()) + { + if (t.is_a<libue> ()) + options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + options = lib::option_install_runtime; + } + } + + return make_pair (pt, options); + } } +#else + if (p.is_a<libx> () || p.is_a<libs> () || p.is_a<liba> ()) + { + const target* pt (&search (t, p)); - const target* pt (file_rule::instance.filter (is, a, t, p)); + if (const libx* l = pt->is_a<libx> ()) + pt = link_member (*l, a, link_info (t.base_scope (), ot)); + + if (a.operation () != update_id) + { + if (t.is_a<libue> ()) + options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + options = lib::option_install_runtime; + } + } + + if (pt->is_a<libs> () || pt->is_a<liba> ()) + { + return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr, + options); + } + else + return make_pair (pt, options); + } +#endif + + const target* pt (file_rule::instance.filter (is, a, t, p, me).first); if (pt == nullptr) - return pt; + return make_pair (pt, options); auto header_source = [this] (const auto& p) { - return (x_header (p) || - p.is_a (x_src) || - (x_mod != nullptr && p.is_a (*x_mod))); + return (x_header (p) || + p.is_a (x_src) || + p.is_a (c::static_type) || + p.is_a (S::static_type) || + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && (p.is_a (*x_obj) || + p.is_a (m::static_type)))); }; - if (t.is_a<libue> ()) + if (t.is_a<libue> () || + (a.operation () != update_id && + me.cur_options == lib::option_install_runtime)) { if (header_source (p)) pt = nullptr; @@ -354,7 +671,7 @@ namespace build2 } if (pt == nullptr) - return pt; + return make_pair (pt, options); } bool g (false); @@ -370,16 +687,17 @@ namespace build2 { pt = t.is_a<libue> () ? nullptr - : file_rule::instance.filter (is, a, *pt, pm.prerequisite); + : file_rule::instance.filter ( + is, a, *pt, pm.prerequisite, me).first; break; } } if (pt == nullptr) - return pt; + return make_pair (pt, options); } - return pt; + return make_pair (pt, options); } bool libux_install_rule:: @@ -391,5 +709,81 @@ namespace build2 return link_.sub_match (x_link, update_id, a, t, me) && alias_rule::match (a, t); } + + recipe libux_install_rule:: + apply (action a, target& t, match_extra& me) const + { + if (a.operation () != update_id) + { + if (!t.is_a<libue> ()) + { + if (me.new_options == 0) + me.new_options = lib::option_install_runtime; + + me.cur_options = me.new_options; + } + } + + return alias_rule::apply_impl ( + a, t, me, me.cur_options != match_extra::all_options /* reapply */); + } + + void libux_install_rule:: + apply_posthoc (action a, target& t, match_extra& me) const + { + if (a.operation () != update_id) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + if (t.is_a<libue> ()) + p.match_options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + p.match_options = lib::option_install_runtime; + } + } + } + } + } + + void libux_install_rule:: + reapply (action a, target& t, match_extra& me) const + { + tracer trace ("cc::linux_install_rule::reapply"); + + assert (a.operation () != update_id && !t.is_a<libue> ()); + + l6 ([&]{trace << "rematching " << t + << ", current options " << me.cur_options + << ", new options " << me.new_options;}); + + me.cur_options |= me.new_options; + + if ((me.new_options & lib::option_install_buildtime) != 0) + { + for (const target* pt: t.prerequisite_targets[a]) + { + if (pt != nullptr && (pt->is_a<liba> () || pt->is_a<libs> () || + pt->is_a<libua> () || pt->is_a<libus> ())) + rematch_sync (a, *pt, match_extra::all_options); + } + + if (me.posthoc_prerequisite_targets != nullptr) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + p.match_options = match_extra::all_options; + } + } + } + + alias_rule::reapply_impl (a, t, me); + } + } } } diff --git a/libbuild2/cc/install-rule.hxx b/libbuild2/cc/install-rule.hxx index 6998d63..771c33b 100644 --- a/libbuild2/cc/install-rule.hxx +++ b/libbuild2/cc/install-rule.hxx @@ -20,7 +20,7 @@ namespace build2 { class link_rule; - // Installation rule for exe{} and lib*{}. Here we do: + // Installation rule for exe{} and lib[as]{}. Here we do: // // 1. Signal to the link rule that this is update for install. // @@ -28,17 +28,23 @@ namespace build2 // // 3. Extra un/installation (e.g., libs{} symlinks). // + // 4. Handling runtime/buildtime match options for lib[as]{}. + // class LIBBUILD2_CC_SYMEXPORT install_rule: public install::file_rule, virtual common { public: install_rule (data&&, const link_rule&); - virtual const target* + virtual bool + filter (action, const target&, const target&) const override; + + virtual pair<const target*, uint64_t> filter (const scope*, - action, const target&, prerequisite_iterator&) const override; + action, const target&, prerequisite_iterator&, + match_extra&) const override; - // Note: rule::match() override. + // Note: rule::match() override (with hint and match_extra). // virtual bool match (action, target&, const string&, match_extra&) const override; @@ -46,7 +52,13 @@ namespace build2 using file_rule::match; // Make Clang happy. virtual recipe - apply (action, target&) const override; + apply (action, target&, match_extra&) const override; + + virtual void + apply_posthoc (action, target&, match_extra&) const override; + + virtual void + reapply (action, target&, match_extra&) const override; virtual bool install_extra (const file&, const install_dir&) const override; @@ -58,22 +70,24 @@ namespace build2 const link_rule& link_; }; - // Installation rule for libu*{}. + // Installation rule for libu[eas]{}. // // While libu*{} members themselves are not installable, we need to see // through them in case they depend on stuff that we need to install // (e.g., headers). Note that we use the alias_rule as a base. // - class LIBBUILD2_CC_SYMEXPORT libux_install_rule: - public install::alias_rule, - virtual common + class LIBBUILD2_CC_SYMEXPORT libux_install_rule: public install::alias_rule, + virtual common { public: libux_install_rule (data&&, const link_rule&); - virtual const target* + // Note: utility libraries currently have no ad hoc members. + + virtual pair<const target*, uint64_t> filter (const scope*, - action, const target&, prerequisite_iterator&) const override; + action, const target&, prerequisite_iterator&, + match_extra&) const override; // Note: rule::match() override. // @@ -82,6 +96,15 @@ namespace build2 using alias_rule::match; // Make Clang happy. + virtual recipe + apply (action, target&, match_extra&) const override; + + virtual void + apply_posthoc (action, target&, match_extra&) const override; + + virtual void + reapply (action, target&, match_extra&) const override; + private: const link_rule& link_; }; diff --git a/libbuild2/cc/lexer+comment.test.testscript b/libbuild2/cc/lexer+comment.test.testscript index 358865c..381e479 100644 --- a/libbuild2/cc/lexer+comment.test.testscript +++ b/libbuild2/cc/lexer+comment.test.testscript @@ -16,6 +16,11 @@ four /** six /* */ +/* */ +/* + +*/ +/**/ EOI : cxx-comment diff --git a/libbuild2/cc/lexer+raw-string-literal.test.testscript b/libbuild2/cc/lexer+raw-string-literal.test.testscript index bca489a..a6455eb 100644 --- a/libbuild2/cc/lexer+raw-string-literal.test.testscript +++ b/libbuild2/cc/lexer+raw-string-literal.test.testscript @@ -16,6 +16,7 @@ R"X(a b)X" R"X(a\ b)X" +R""(a)"" EOI <string literal> <string literal> @@ -24,6 +25,7 @@ EOI <string literal> <string literal> <string literal> +<string literal> EOO : prefix diff --git a/libbuild2/cc/lexer.cxx b/libbuild2/cc/lexer.cxx index beeb970..d20e0dc 100644 --- a/libbuild2/cc/lexer.cxx +++ b/libbuild2/cc/lexer.cxx @@ -214,7 +214,7 @@ namespace build2 // #line <integer> [<string literal>] ... // # <integer> [<string literal>] ... // - // Also diagnose #include while at it. + // Also diagnose #include while at it if preprocessed. // if (!(c >= '0' && c <= '9')) { @@ -222,10 +222,13 @@ namespace build2 if (t.type == type::identifier) { - if (t.value == "include") - fail (l) << "unexpected #include directive"; - else if (t.value != "line") + if (t.value != "line") + { + if (preprocessed_ && t.value == "include") + fail (l) << "unexpected #include directive"; + continue; + } } else continue; @@ -734,8 +737,8 @@ namespace build2 // R"<delimiter>(<raw_characters>)<delimiter>" // // Where <delimiter> is a potentially-empty character sequence made of - // any source character but parentheses, backslash and spaces. It can be - // at most 16 characters long. + // any source character but parentheses, backslash, and spaces (in + // particular, it can be `"`). It can be at most 16 characters long. // // Note that the <raw_characters> are not processed in any way, not even // for line continuations. @@ -750,7 +753,7 @@ namespace build2 { c = geth (); - if (eos (c) || c == '\"' || c == ')' || c == '\\' || c == ' ') + if (eos (c) || c == ')' || c == '\\' || c == ' ') fail (l) << "invalid raw string literal"; if (c == '(') @@ -1108,21 +1111,18 @@ namespace build2 if (eos (c)) fail (p) << "unterminated comment"; - if (c == '*' && (c = peek ()) == '/') + if (c == '*') { - get (c); - break; + if ((c = peek ()) == '/') + { + get (c); + break; + } } - - if (c != '*' && c != '\\') + else { // Direct buffer scan. // - // Note that we should call get() prior to the direct buffer - // scan (see butl::char_scanner for details). - // - get (c); - const char* b (gptr_); const char* e (egptr_); const char* p (b); diff --git a/libbuild2/cc/lexer.hxx b/libbuild2/cc/lexer.hxx index 81e0d97..17d706b 100644 --- a/libbuild2/cc/lexer.hxx +++ b/libbuild2/cc/lexer.hxx @@ -12,6 +12,8 @@ #include <libbuild2/diagnostics.hxx> +#include <libbuild2/cc/export.hxx> + namespace build2 { namespace cc @@ -20,13 +22,15 @@ namespace build2 // sequence of tokens returned is similar to what a real C/C++ compiler // would see from its preprocessor. // - // The input is a (partially-)preprocessed translation unit that may still - // contain comments, line continuations, and preprocessor directives such - // as #line, #pragma, but not #include (which is diagnosed). Currently, - // all preprocessor directives except #line are ignored and no values are - // saved from literals. The #line directive (and its shorthand notation) - // is recognized to provide the logical token location. Note that the - // modules-related pseudo-directives are not recognized or handled. + // The input is a potentially (partially-)preprocessed translation unit + // that may still contain comments, line continuations, and preprocessor + // directives such as #line and #pragma. If the input is said to be + // (partially-)preprocessed then #include directives are diagnosed. + // Currently, all preprocessor directives except #line are ignored and no + // values are saved from literals. The #line directive (and its shorthand + // notation) is recognized to provide the logical token location. Note + // that the modules-related pseudo-directives are not recognized or + // handled. // // While at it we also calculate the checksum of the input ignoring // comments, whitespaces, etc. This is used to detect changes that do not @@ -80,15 +84,19 @@ namespace build2 // Output the token value in a format suitable for diagnostics. // - ostream& + LIBBUILD2_CC_SYMEXPORT ostream& operator<< (ostream&, const token&); - class lexer: protected butl::char_scanner<> + class LIBBUILD2_CC_SYMEXPORT lexer: protected butl::char_scanner<> { public: - lexer (ifdstream& is, const path_name& name) + // If preprocessed is true, then assume the input is at least partially + // preprocessed and therefore should not contain #include directives. + // + lexer (ifdstream& is, const path_name& name, bool preprocessed) : char_scanner (is, false /* crlf */), name_ (name), + preprocessed_ (preprocessed), fail ("error", &name_), log_file_ (name) { @@ -173,6 +181,8 @@ namespace build2 private: const path_name& name_; + bool preprocessed_; + const fail_mark fail; // Logical file and line as set by the #line directives. Note that the diff --git a/libbuild2/cc/lexer.test.cxx b/libbuild2/cc/lexer.test.cxx index 39e4279..82163fe 100644 --- a/libbuild2/cc/lexer.test.cxx +++ b/libbuild2/cc/lexer.test.cxx @@ -65,7 +65,7 @@ namespace build2 is.open (fddup (stdin_fd ())); } - lexer l (is, in); + lexer l (is, in, true /* preprocessed */); // No use printing eos since we will either get it or loop forever. // diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 8bc073a..417cba5 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -20,6 +20,8 @@ #include <libbuild2/bin/target.hxx> #include <libbuild2/bin/utility.hxx> +#include <libbuild2/install/utility.hxx> + #include <libbuild2/cc/target.hxx> // c, pc* #include <libbuild2/cc/utility.hxx> @@ -94,7 +96,7 @@ namespace build2 return false; } - if (const target* t = search_existing (n, bs, dir_path () /* out */)) + if (const target* t = search_existing (n, bs)) { // The same logic as in process_libraries(). // @@ -290,12 +292,15 @@ namespace build2 if (p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)) || + (x_asp != nullptr && p.is_a (*x_asp)) || + (x_obj != nullptr && p.is_a (*x_obj)) || // Header-only X library (or library with C source and X header). (library && x_header (p, false /* c_hdr */))) { r.seen_x = true; } - else if (p.is_a<c> () || + else if (p.is_a<c> () || p.is_a<S> () || + (x_obj != nullptr && p.is_a<m> ()) || // Header-only C library. (library && p.is_a<h> ())) { @@ -431,9 +436,12 @@ namespace build2 r.seen_lib = true; } // Some other c-common header/source (say C++ in a C rule) other than - // a C header (we assume everyone can hanle that). + // a C header (we assume everyone can hanle that) or some other + // #include'able target. // - else if (p.is_a<cc> () && !(x_header (p, true /* c_hdr */))) + else if (p.is_a<cc> () && + !(x_header (p, true /* c_hdr */)) && + !p.is_a (x_inc) && !p.is_a<c_inc> ()) { r.seen_cc = true; break; @@ -840,6 +848,9 @@ namespace build2 // If not, then we may need the same in recursive-binless logic. // #if 0 + // @@ TMP hm, this hasn't actually been enabled. So may actually + // enable and see if it trips up (do git-blame for good measure). + // assert (false); // @@ TMP (remove before 0.16.0 release) #endif ux = &link_member (*ul, a, li)->as<libux> (); @@ -901,7 +912,7 @@ namespace build2 // for binless libraries since there could be other output (e.g., .pc // files). // - inject_fsdir (a, t); + const fsdir* dir (inject_fsdir (a, t)); // Process prerequisites, pass 1: search and match prerequisite // libraries, search obj/bmi{} targets, and search targets we do rule @@ -995,11 +1006,13 @@ namespace build2 // By default update ad hoc headers/sources during match (see // above). // +#if 1 if (!um) - um = (p.is_a (x_src) || - p.is_a<c> () || - (x_mod != nullptr && p.is_a (*x_mod)) || + um = (p.is_a (x_src) || p.is_a<c> () || p.is_a<S> () || + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a<m> ())) || x_header (p, true)); +#endif if (*um) { @@ -1021,12 +1034,14 @@ namespace build2 // 2 - mod // 3 - obj/bmi and also lib not to be cleaned (and other stuff) // - uint8_t m (0); + uint8_t mk (0); bool mod (x_mod != nullptr && p.is_a (*x_mod)); bool hdr (false); - if (mod || p.is_a (x_src) || p.is_a<c> ()) + if (mod || + p.is_a (x_src) || p.is_a<c> () || p.is_a<S> () || + (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a<m> ()))) { binless = binless && (mod ? user_binless : false); @@ -1114,7 +1129,7 @@ namespace build2 } pt = &r.first; - m = mod ? 2 : 1; + mk = mod ? 2 : 1; } else if (p.is_a<libx> () || p.is_a<liba> () || @@ -1132,7 +1147,7 @@ namespace build2 pt = &p.search (t); if (skip (*pt)) - m = 3; // Mark so it is not matched. + mk = 3; // Mark so it is not matched. // If this is the lib{}/libul{} group, then pick the appropriate // member. Also note this in prerequisite_target::include (used @@ -1186,6 +1201,12 @@ namespace build2 } pt = &p.search (t); + + if (pt == dir) + { + pt = nullptr; + continue; + } } if (skip (*pt)) @@ -1204,7 +1225,7 @@ namespace build2 !pt->is_a<hbmix> () && cast_false<bool> ((*pt)[b_binless]))); - m = 3; + mk = 3; } if (user_binless && !binless) @@ -1217,23 +1238,25 @@ namespace build2 { // By default update headers during match (see above). // +#if 1 if (!um) um = hdr; +#endif if (*um) { - if (m != 3) + if (mk != 3) fail << "unable to update during match prerequisite " << p << info << "updating this type of prerequisites during match is " << "not supported by this rule"; - m = 0; + mk = 0; pto.include |= prerequisite_target::include_udm; update_match = true; } } - mark (pt, m); + mark (pt, mk); } // Match lib{} first and then update during match (the only unmarked) in @@ -1529,6 +1552,11 @@ namespace build2 if (wasm.path ().empty ()) wasm.derive_path (); + // We don't want to print this member at level 1 diagnostics. + // + wasm.state[a].assign (ctx.var_backlink) = names { + name ("group"), name ("false")}; + // If we have -pthread then we get additional .worker.js file // which is used for thread startup. In a somewhat hackish way we // represent it as an exe{} member to make sure it gets installed @@ -1552,6 +1580,11 @@ namespace build2 if (worker.path ().empty ()) worker.derive_path (); + + // We don't want to print this member at level 1 diagnostics. + // + worker.state[a].assign (ctx.var_backlink) = names { + name ("group"), name ("false")}; } } @@ -1579,6 +1612,11 @@ namespace build2 // if (pdb.path ().empty ()) pdb.derive_path (t.path ()); + + // We don't want to print this member at level 1 diagnostics. + // + pdb.state[a].assign (ctx.var_backlink) = names { + name ("group"), name ("false")}; } } } @@ -1658,14 +1696,13 @@ namespace build2 // exists (windows_rpath_assembly() does take care to clean it up // if not used). // -#ifdef _WIN32 - target& dir = -#endif + target& dir ( add_adhoc_member (t, fsdir::static_type, path_cast<dir_path> (t.path () + ".dlls"), t.out, - string () /* name */); + string () /* name */, + nullopt /* ext */)); // By default our backlinking logic will try to symlink the // directory and it can even be done on Windows using junctions. @@ -1679,9 +1716,15 @@ namespace build2 // Wine. So we only resort to copy-link'ing if we are running on // Windows. // + // We also don't want to print this member at level 1 diagnostics. + // + dir.state[a].assign (ctx.var_backlink) = names { #ifdef _WIN32 - dir.state[a].assign (ctx.var_backlink) = "copy"; + name ("copy"), name ("false") +#else + name ("group"), name ("false") #endif + }; } } } @@ -1707,20 +1750,20 @@ namespace build2 // 1 - completion // 2 - verification // - uint8_t m (unmark (pt)); + uint8_t mk (unmark (pt)); - if (m == 3) // obj/bmi or lib not to be cleaned + if (mk == 3) // obj/bmi or lib not to be cleaned { - m = 1; // Just completion. + mk = 1; // Just completion. // Note that if this is a library not to be cleaned, we keep it // marked for completion (see the next phase). } - else if (m == 1 || m == 2) // Source/module chain. + else if (mk == 1 || mk == 2) // Source/module chain. { - bool mod (m == 2); + bool mod (mk == 2); // p is_a x_mod - m = 1; + mk = 1; const target& rt (*pt); bool group (!p.prerequisite.belongs (t)); // Group's prerequisite. @@ -1871,7 +1914,10 @@ namespace build2 // Most of the time we will have just a single source so fast- // path that case. // - if (p1.is_a (mod ? *x_mod : x_src) || p1.is_a<c> ()) + if (mod + ? p1.is_a (*x_mod) + : (p1.is_a (x_src) || p1.is_a<c> () || p1.is_a<S> () || + (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a<m> ())))) { src = true; continue; // Check the rest of the prerequisites. @@ -1884,8 +1930,12 @@ namespace build2 p1.is_a<libx> () || p1.is_a<liba> () || p1.is_a<libs> () || p1.is_a<libux> () || p1.is_a<bmi> () || p1.is_a<bmix> () || - (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || - (p.is_a<c> () && p1.is_a<h> ())) + ((mod || + p.is_a (x_src) || + (x_asp != nullptr && p.is_a (*x_asp)) || + (x_obj != nullptr && p.is_a (*x_obj))) && x_header (p1)) || + ((p.is_a<c> () || p.is_a<S> () || + (x_obj != nullptr && p.is_a<m> ())) && p1.is_a<h> ())) continue; fail << "synthesized dependency for prerequisite " << p @@ -1898,11 +1948,11 @@ namespace build2 if (!src) fail << "synthesized dependency for prerequisite " << p << " would be incompatible with existing target " << *pt << - info << "no existing c/" << x_name << " source prerequisite" << + info << "no existing C/" << x_lang << " source prerequisite" << info << "specify corresponding " << rtt.name << "{} " << "dependency explicitly"; - m = 2; // Needs verification. + mk = 2; // Needs verification. } } else // lib*{} or update during match @@ -1914,6 +1964,8 @@ namespace build2 bool u; if ((u = pt->is_a<libux> ()) || pt->is_a<liba> ()) { + // Note: go straight for the public variable pool. + // const variable& var (ctx.var_pool["bin.whole"]); // @@ Cache. // See the bin module for the lookup semantics discussion. Note @@ -1942,7 +1994,7 @@ namespace build2 } } - mark (pt, m); + mark (pt, mk); } // Process prerequisites, pass 3: match everything and verify chains. @@ -1958,7 +2010,7 @@ namespace build2 bool adhoc (pts[i].adhoc ()); const target*& pt (pts[i++]); - uint8_t m; + uint8_t mk; if (pt == nullptr) { @@ -1968,13 +2020,13 @@ namespace build2 continue; pt = &p.search (t); - m = 1; // Mark for completion. + mk = 1; // Mark for completion. } else { - m = unmark (pt); + mk = unmark (pt); - if (m == 0) + if (mk == 0) continue; // Already matched. // If this is a library not to be cleaned, we can finally blank it @@ -1988,7 +2040,7 @@ namespace build2 } match_async (a, *pt, ctx.count_busy (), t[a].task_count); - mark (pt, m); + mark (pt, mk); } wg.wait (); @@ -2003,15 +2055,15 @@ namespace build2 // Skipped or not marked for completion. // - uint8_t m; - if (pt == nullptr || (m = unmark (pt)) == 0) + uint8_t mk; + if (pt == nullptr || (mk = unmark (pt)) == 0) continue; match_complete (a, *pt); // Nothing else to do if not marked for verification. // - if (m == 1) + if (mk == 1) continue; // Finish verifying the existing dependency (which is now matched) @@ -2023,7 +2075,10 @@ namespace build2 for (prerequisite_member p1: group_prerequisite_members (a, *pt)) { - if (p1.is_a (mod ? *x_mod : x_src) || p1.is_a<c> ()) + if (mod + ? p1.is_a (*x_mod) + : (p1.is_a (x_src) || p1.is_a<c> () || p1.is_a<S> () || + (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a<m> ())))) { // Searching our own prerequisite is ok, p1 must already be // resolved. @@ -2206,17 +2261,47 @@ namespace build2 *type != "cc" && type->compare (0, 3, "cc,") != 0) { - auto& md (l->data<link_rule::match_data> (d.a)); - assert (md.for_install); // Must have been executed. + auto* md (l->try_data<link_rule::match_data> (d.a)); + + if (md == nullptr) + fail << "library " << *l << " is not built with cc module-based " + << "link rule" << + info << "mark it as generic with cc.type=cc target-specific " + << "variable"; + + assert (md->for_install); // Must have been executed. // The user will get the target name from the context info. // - if (*md.for_install != *d.for_install) + if (*md->for_install != *d.for_install) fail << "incompatible " << *l << " build" << - info << "library is built " << (*md.for_install ? "" : "not ") + info << "library is built " << (*md->for_install ? "" : "not ") << "for install"; } + auto newer = [&d, l] () + { + // @@ Work around the unexecuted member for installed libraries + // issue (see search_library() for details). + // + // Note that the member may not even be matched, let alone + // executed, so we have to go through the group to detect this + // case (if the group is not matched, then the member got to be). + // +#if 0 + return l->newer (d.mt); +#else + const target* g (l->group); + target_state s (g != nullptr && + g->matched (d.a, memory_order_acquire) && + g->state[d.a].rule == &file_rule::rule_match + ? target_state::unchanged + : l->executed_state (d.a)); + + return l->newer (d.mt, s); +#endif + }; + if (d.li.type == otype::a) { // Linking a utility library to a static library. @@ -2244,7 +2329,7 @@ namespace build2 // Check if this library renders us out of date. // if (d.update != nullptr) - *d.update = *d.update || l->newer (d.mt); + *d.update = *d.update || newer (); for (const target* pt: l->prerequisite_targets[d.a]) { @@ -2283,7 +2368,7 @@ namespace build2 // Check if this library renders us out of date. // if (d.update != nullptr) - *d.update = *d.update || l->newer (d.mt); + *d.update = *d.update || newer (); // On Windows a shared library is a DLL with the import library as // an ad hoc group member. MinGW though can link directly to DLLs @@ -2364,6 +2449,8 @@ namespace build2 // if (const target* g = exp && l.is_a<libs> () ? l.group : &l) { + // Note: go straight for the public variable pool. + // const variable& var ( com ? (exp ? c_export_loptions : c_loptions) @@ -2498,6 +2585,24 @@ namespace build2 // We don't rpath system libraries. Why, you may ask? There are many // good reasons and I have them written on a napkin somewhere... // + // Well, the main reason is that we naturally assume the dynamic + // linker searches there by default and so there is no need for rpath. + // Plus, rpath would prevent "overriding" distribution-system + // (/usr/lib) libraries with user-system (/usr/local/lib). + // + // Note, however, that some operating systems don't search in + // /usr/local/lib by default (for example, Fedora, RHEL, Mac OS since + // version 13). In a sense, on these platforms /usr/local is + // "half-system" in that the system compiler by default searches in + // /usr/local/include and/or /usr/local/lib (see config_module::init() + // for background) but the dynamic linker does not. While we could + // hack this test for such platforms and add rpath for /usr/local/lib, + // this is still feels wrong (the user can always "fix" such an + // operating system by instructing the dynamic linker to search in + // /usr/local/lib, as many, including ourselves, do). So for now we + // are not going to do anything. In the end, the user can always add + // an rpath for /usr/local/lib manually. + // // We also assume system libraries can only depend on other system // libraries and so can prune the traversal. // @@ -2509,18 +2614,26 @@ namespace build2 size_t p (path::traits_type::rfind_separator (f)); assert (p != string::npos); + // For good measure, also suppress duplicates at the options level. + // This will take care of different libraries built in the same + // directory, system-installed, etc. + if (d.rpath) { string o ("-Wl,-rpath,"); o.append (f, 0, (p != 0 ? p : 1)); // Don't include trailing slash. - d.args.push_back (move (o)); + + if (find (d.args.begin (), d.args.end (), o) == d.args.end ()) + d.args.push_back (move (o)); } if (d.rpath_link) { string o ("-Wl,-rpath-link,"); o.append (f, 0, (p != 0 ? p : 1)); - d.args.push_back (move (o)); + + if (find (d.args.begin (), d.args.end (), o) == d.args.end ()) + d.args.push_back (move (o)); } }; @@ -2573,7 +2686,9 @@ namespace build2 if ((c ? f.compare (p, string::npos, e) : icasecmp (f.c_str () + p, e)) == 0) + { append (f); + } } } @@ -2584,13 +2699,22 @@ namespace build2 { // Top-level shared library dependency. // + // As above, suppress duplicates. + // + if (find (d.ls.begin (), d.ls.end (), &l) != d.ls.end ()) + return; + if (!l.path ().empty ()) // Not binless. { // It is either matched or imported so should be a cc library. // if (!cast_false<bool> (l.vars[c_system])) { - args.push_back ("-Wl,-rpath," + l.path ().directory ().string ()); + string o ("-Wl,-rpath," + l.path ().directory ().string ()); + + if (find (args.begin (), args.end (), o) == args.end ()) + args.push_back (move (o)); + ls.push_back (&l); } } @@ -2663,7 +2787,7 @@ namespace build2 // Filter link.exe noise (msvc.cxx). // void - msvc_filter_link (ifdstream&, const file&, otype); + msvc_filter_link (diag_buffer&, const file&, otype); // Translate target CPU to the link.exe/lib.exe /MACHINE option. // @@ -2751,7 +2875,7 @@ namespace build2 // those that don't match. Note that we have to do it after updating // prerequisites to keep the dependency counts straight. // - if (const variable* var_fi = ctx.var_pool.find ("for_install")) + if (const variable* var_fi = rs.var_pool ().find ("for_install")) { // Parallel prerequisites/prerequisite_targets loop. // @@ -2777,7 +2901,7 @@ namespace build2 // (Re)generate pkg-config's .pc file. While the target itself might be // up-to-date from a previous run, there is no guarantee that .pc exists // or also up-to-date. So to keep things simple we just regenerate it - // unconditionally (and avoid doing so on uninstall; see pkconfig_save() + // unconditionally (and avoid doing so on uninstall; see pkgconfig_save() // for details). // // Also, if you are wondering why don't we just always produce this .pc, @@ -2787,7 +2911,7 @@ namespace build2 // There is a further complication: we may have no intention of // installing the library but still need to update it for install (see // install_scope() for background). In which case we may still not have - // the installation directories. We handle this in pkconfig_save() by + // the installation directories. We handle this in pkgconfig_save() by // skipping the generation of .pc files (and letting the install rule // complain if we do end up trying to install them). // @@ -2921,14 +3045,19 @@ namespace build2 try { + // We assume that what we write to stdin is small enough to + // fit into the pipe's buffer without blocking. + // process pr (rc, args, - -1 /* stdin */, - 1 /* stdout */, - 2 /* stderr */, - nullptr /* cwd */, + -1 /* stdin */, + 1 /* stdout */, + diag_buffer::pipe (ctx) /* stderr */, + nullptr /* cwd */, env_ptrs.empty () ? nullptr : env_ptrs.data ()); + diag_buffer dbuf (ctx, args[0], pr); + try { ofdstream os (move (pr.out_fd)); @@ -2952,7 +3081,8 @@ namespace build2 // was caused by that and let run_finish() deal with it. } - run_finish (args, pr); + dbuf.read (); + run_finish (dbuf, args, pr, 2 /* verbosity */); } catch (const process_error& e) { @@ -3007,6 +3137,8 @@ namespace build2 { // For VC we use link.exe directly. // + // Note: go straight for the public variable pool. + // const string& cs ( cast<string> ( rs[tsys == "win32-msvc" @@ -3216,10 +3348,76 @@ namespace build2 rpath_libraries (sargs, bs, a, t, li, for_install /* link */); lookup l; - if ((l = t["bin.rpath"]) && !l->empty ()) + { + // See if we need to make the specified paths relative using the + // $ORIGIN (Linux, BSD) or @loader_path (Mac OS) mechanisms. + // + optional<dir_path> origin; + if (for_install && cast_false<bool> (rs["install.relocatable"])) + { + // Note that both $ORIGIN and @loader_path will be expanded to + // the path of the binary that we are building (executable or + // shared library) as opposed to top-level executable. + // + path p (install::resolve_file (t)); + + // If the file is not installable then the install.relocatable + // semantics does not apply, naturally. + // + if (!p.empty ()) + origin = p.directory (); + } + + // Note: suppress duplicates at the options level, similar to + // rpath_libraries(). + + bool origin_used (false); for (const dir_path& p: cast<dir_paths> (l)) - sargs.push_back ("-Wl,-rpath," + p.string ()); + { + string o ("-Wl,-rpath,"); + + // Note that we only rewrite absolute paths so if the user + // specified $ORIGIN or @loader_path manually, we will pass it + // through as is. + // + if (origin && p.absolute ()) + { + dir_path l; + try + { + l = p.relative (*origin); + } + catch (const invalid_path&) + { + fail << "unable to make rpath " << p << " relative to " + << *origin << + info << "required for relocatable installation"; + } + + o += (tclass == "macos" ? "@loader_path" : "$ORIGIN"); + + if (!l.empty ()) + { + o += path_traits::directory_separator; + o += l.string (); + } + + origin_used = true; + } + else + o += p.string (); + + if (find (sargs.begin (), sargs.end (), o) == sargs.end ()) + sargs.push_back (move (o)); + } + + // According to the Internet, `-Wl,-z,origin` is not needed except + // potentially for older BSDs. + // + if (origin_used && tclass == "bsd") + sargs.push_back ("-Wl,-z,origin"); + } if ((l = t["bin.rpath_link"]) && !l->empty ()) { @@ -3229,7 +3427,12 @@ namespace build2 fail << ctgt << " does not support rpath-link"; for (const dir_path& p: cast<dir_paths> (l)) - sargs.push_back ("-Wl,-rpath-link," + p.string ()); + { + string o ("-Wl,-rpath-link," + p.string ()); + + if (find (sargs.begin (), sargs.end (), o) == sargs.end ()) + sargs.push_back (move (o)); + } } } @@ -3253,37 +3456,42 @@ namespace build2 // Extra system library dirs (last). // - assert (sys_lib_dirs_extra <= sys_lib_dirs.size ()); + assert (sys_lib_dirs_mode + sys_lib_dirs_extra <= sys_lib_dirs.size ()); + + // Note that the mode options are added as part of cmode. + // + auto b (sys_lib_dirs.begin () + sys_lib_dirs_mode); + auto x (b + sys_lib_dirs_extra); if (tsys == "win32-msvc") { // If we have no LIB environment variable set, then we add all of // them. But we want extras to come first. // - // Note that the mode options are added as part of cmode. - // - auto b (sys_lib_dirs.begin () + sys_lib_dirs_mode); - auto m (sys_lib_dirs.begin () + sys_lib_dirs_extra); - auto e (sys_lib_dirs.end ()); - - for (auto i (m); i != e; ++i) + for (auto i (b); i != x; ++i) sargs1.push_back ("/LIBPATH:" + i->string ()); if (!getenv ("LIB")) { - for (auto i (b); i != m; ++i) + for (auto i (x), e (sys_lib_dirs.end ()); i != e; ++i) sargs1.push_back ("/LIBPATH:" + i->string ()); } append_args (sargs1); } - else + else if (b != x) { - append_option_values ( - args, + // Use the more canonical combined form (-L/usr/local/lib) even + // though it's less efficient (the split one is just too much of an + // eye-sore in the logs). + // + append_combined_option_values ( + sargs1, "-L", - sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (), - [] (const dir_path& d) {return d.string ().c_str ();}); + b, x, + [] (const dir_path& d) -> const string& {return d.string ();}); + + append_args (sargs1); } } @@ -3378,7 +3586,7 @@ namespace build2 &cs, &update, mt, bs, a, *f, la, p.data, li, for_install, true, true, &lc); - f = nullptr; // Timestamp checked by hash_libraries(). + f = nullptr; // Timestamp checked by append_libraries(). } else { @@ -3467,6 +3675,10 @@ namespace build2 // path relt (relative (tp)); + path reli; // Import library. + if (lt.shared_library () && (tsys == "win32-msvc" || tsys == "mingw32")) + reli = relative (find_adhoc_member<libi> (t)->path ()); + const process_path* ld (nullptr); if (lt.static_library ()) { @@ -3598,7 +3810,7 @@ namespace build2 // derived from the import library by changing the extension. // Lucky for us -- there is no option to name it. // - out2 += relative (find_adhoc_member<libi> (t)->path ()).string (); + out2 += reli.string (); } else { @@ -3635,6 +3847,8 @@ namespace build2 { ld = &cpath; + append_diag_color_options (args); + // Add the option that triggers building a shared library and // take care of any extras (e.g., import library). // @@ -3650,8 +3864,7 @@ namespace build2 // On Windows libs{} is the DLL and an ad hoc group member // is the import library. // - const file& imp (*find_adhoc_member<libi> (t)); - out = "-Wl,--out-implib=" + relative (imp.path ()).string (); + out = "-Wl,--out-implib=" + reli.string (); args.push_back (out.c_str ()); } } @@ -3808,17 +4021,43 @@ namespace build2 try_rmfile (relt, true); } + // We have no choice but to serialize early if we want the command line + // printed shortly before actually executing the linker. Failed that, it + // may look like we are still executing in parallel. + // + scheduler::alloc_guard jobs_ag; + if (!ctx.dry_run && cast_false<bool> (t[c_serialize])) + jobs_ag = scheduler::alloc_guard (*ctx.sched, phase_unlock (nullptr)); + if (verb == 1) - text << (lt.static_library () ? "ar " : "ld ") << t; + print_diag (lt.static_library () ? "ar" : "ld", t); else if (verb == 2) print_process (args); + // Do any necessary fixups to the command line to make it runnable. + // + // Notice the split in the diagnostics: at verbosity level 1 we print + // the "logical" command line while at level 2 and above -- what we are + // actually executing. + // + // We also need to save the original for the diag_buffer::close() call + // below if at verbosity level 1. + // + cstrings oargs; + // Adjust linker parallelism. // + // Note that we are not going to bother with oargs for this. + // + // Note also that we now have scheduler::serialize() which allows us to + // block until full parallelism is available (this mode can currently + // be forced with cc.serialize=true; maybe we should invent something + // like config.cc.link_serialize or some such which can be used when + // LTO is enabled). + // string jobs_arg; - scheduler::alloc_guard jobs_extra; - if (!lt.static_library ()) + if (!ctx.dry_run && !lt.static_library ()) { switch (ctype) { @@ -3834,8 +4073,10 @@ namespace build2 auto i (find_option_prefix ("-flto", args.rbegin (), args.rend ())); if (i != args.rend () && strcmp (*i, "-flto=auto") == 0) { - jobs_extra = scheduler::alloc_guard (ctx.sched, 0); - jobs_arg = "-flto=" + to_string (1 + jobs_extra.n); + if (jobs_ag.n == 0) // Might already have (see above). + jobs_ag = scheduler::alloc_guard (*ctx.sched, 0); + + jobs_arg = "-flto=" + to_string (1 + jobs_ag.n); *i = jobs_arg.c_str (); } break; @@ -3853,8 +4094,10 @@ namespace build2 strcmp (*i, "-flto=thin") == 0 && !find_option_prefix ("-flto-jobs=", args)) { - jobs_extra = scheduler::alloc_guard (ctx.sched, 0); - jobs_arg = "-flto-jobs=" + to_string (1 + jobs_extra.n); + if (jobs_ag.n == 0) // Might already have (see above). + jobs_ag = scheduler::alloc_guard (*ctx.sched, 0); + + jobs_arg = "-flto-jobs=" + to_string (1 + jobs_ag.n); args.insert (i.base (), jobs_arg.c_str ()); // After -flto=thin. } break; @@ -3865,12 +4108,6 @@ namespace build2 } } - // Do any necessary fixups to the command line to make it runnable. - // - // Notice the split in the diagnostics: at verbosity level 1 we print - // the "logical" command line while at level 2 and above -- what we are - // actually executing. - // // On Windows we need to deal with the command line length limit. The // best workaround seems to be passing (part of) the command line in an // "options file" ("response file" in Microsoft's terminology). Both @@ -3956,19 +4193,20 @@ namespace build2 fail << "unable to write to " << f << ": " << e; } + if (verb == 1) + oargs = args; + // Replace input arguments with @file. // targ = '@' + f.string (); args.resize (args_input); args.push_back (targ.c_str()); args.push_back (nullptr); - - //@@ TODO: leave .t file if linker failed and verb > 2? } } #endif - if (verb > 2) + if (verb >= 3) print_process (args); // Remove the target file if any of the subsequent (after the linker) @@ -3986,52 +4224,51 @@ namespace build2 { // VC tools (both lib.exe and link.exe) send diagnostics to stdout. // Also, link.exe likes to print various gratuitous messages. So for - // link.exe we redirect stdout to a pipe, filter that noise out, and - // send the rest to stderr. + // link.exe we filter that noise out. // // For lib.exe (and any other insane linker that may try to pull off // something like this) we are going to redirect stdout to stderr. // For sane compilers this should be harmless. // // Note that we don't need this for LLD's link.exe replacement which - // is quiet. + // is thankfully quiet. // bool filter (tsys == "win32-msvc" && !lt.static_library () && cast<string> (rs["bin.ld.id"]) != "msvc-lld"); process pr (*ld, - args.data (), - 0 /* stdin */, - (filter ? -1 : 2) /* stdout */, - 2 /* stderr */, - nullptr /* cwd */, + args, + 0 /* stdin */, + 2 /* stdout */, + diag_buffer::pipe (ctx, filter /* force */) /* stderr */, + nullptr /* cwd */, env_ptrs.empty () ? nullptr : env_ptrs.data ()); + diag_buffer dbuf (ctx, args[0], pr); + if (filter) + msvc_filter_link (dbuf, t, ot); + + dbuf.read (); + { - try - { - ifdstream is ( - move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit); + bool e (pr.wait ()); - msvc_filter_link (is, t, ot); +#ifdef _WIN32 + // Keep the options file if we have shown it. + // + if (!e && verb >= 3) + trm.cancel (); +#endif - // If anything remains in the stream, send it all to stderr. - // Note that the eof check is important: if the stream is at - // eof, this and all subsequent writes to the diagnostics stream - // will fail (and you won't see a thing). - // - if (is.peek () != ifdstream::traits_type::eof ()) - diag_stream_lock () << is.rdbuf (); + dbuf.close (oargs.empty () ? args : oargs, + *pr.exit, + 1 /* verbosity */); - is.close (); - } - catch (const io_error&) {} // Assume exits with error. + if (!e) + throw failed (); } - - run_finish (args, pr); - jobs_extra.deallocate (); } catch (const process_error& e) { @@ -4053,12 +4290,24 @@ namespace build2 throw failed (); } - // Clean up executable's import library (see above for details). + // Clean up executable's import library (see above for details). And + // make sure we have an import library for a shared library. // - if (lt.executable () && tsys == "win32-msvc") + if (tsys == "win32-msvc") { - try_rmfile (relt + ".lib", true /* ignore_errors */); - try_rmfile (relt + ".exp", true /* ignore_errors */); + if (lt.executable ()) + { + try_rmfile (relt + ".lib", true /* ignore_errors */); + try_rmfile (relt + ".exp", true /* ignore_errors */); + } + else if (lt.shared_library ()) + { + if (!file_exists (reli, + false /* follow_symlinks */, + true /* ignore_error */)) + fail << "linker did not produce import library " << reli << + info << "perhaps this library does not export any symbols?"; + } } // Set executable bit on the .js file so that it can be run with a @@ -4090,12 +4339,17 @@ namespace build2 print_process (args); if (!ctx.dry_run) - run (rl, + { + run (ctx, + rl, args, - dir_path () /* cwd */, + 1 /* finish_verbosity */, env_ptrs.empty () ? nullptr : env_ptrs.data ()); + } } + jobs_ag.deallocate (); + // For Windows generate (or clean up) rpath-emulating assembly. // if (tclass == "windows") diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index c930d49..cf6c6e4 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -11,10 +11,7 @@ #include <libbuild2/bin/target.hxx> -#include <libbuild2/cc/target.hxx> // pc* - #include <libbuild2/config/utility.hxx> -#include <libbuild2/install/utility.hxx> #include <libbuild2/cc/guess.hxx> @@ -30,6 +27,8 @@ namespace build2 { tracer trace (x, "guess_init"); + context& ctx (rs.ctx); + bool cc_loaded (cast_false<bool> (rs["cc.core.guess.loaded"])); // Adjust module priority (compiler). Also order cc module before us @@ -41,7 +40,10 @@ namespace build2 config::save_module (rs, x, 250); - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); // Must already exist. // @@ -55,7 +57,7 @@ namespace build2 // config.x // - strings mode; + strings omode; // Original mode. { // Normally we will have a persistent configuration and computing the // default value every time will be a waste. So try without a default @@ -139,21 +141,34 @@ namespace build2 fail << "invalid path '" << s << "' in " << config_x; } - mode.assign (++v.begin (), v.end ()); + omode.assign (++v.begin (), v.end ()); // Save original path/mode in *.config.path/mode. // rs.assign (x_c_path) = xc; - rs.assign (x_c_mode) = mode; + rs.assign (x_c_mode) = omode; + + // Merge the configured mode options into user-specified (which must + // be done before loading the *.guess module). + // + // In particular, this ability to specify the compiler mode in a + // buildfile is useful in embedded development where the project may + // need to hardcode things like -target, -nostdinc, etc. + // + const strings& mode (cast<strings> (rs.assign (x_mode) += omode)); // 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). Note that - // we are now folding *.std options into mode options. + // we are now adding *.std options into mode options. + // + // @@ But can't the language standard options alter things like search + // directories? // x_info = &build2::cc::guess ( + ctx, x, x_lang, rs.root_extra->environment_checksum, move (xc), @@ -180,7 +195,8 @@ namespace build2 if (config_sub) { - ct = run<string> (3, + ct = run<string> (ctx, + 3, *config_sub, xi.target.c_str (), [] (string& l, bool) {return move (l);}); @@ -218,9 +234,10 @@ namespace build2 // Assign values to variables that describe the compiler. // + // Note: x_mode is dealt with above. + // rs.assign (x_path) = process_path_ex ( xi.path, x_name, xi.checksum, env_checksum); - const strings& xm (cast<strings> (rs.assign (x_mode) = move (mode))); rs.assign (x_id) = xi.id.string (); rs.assign (x_id_type) = to_string (xi.id.type); @@ -265,9 +282,9 @@ namespace build2 // if (!cc_loaded) { - // Prepare configuration hints. + // Prepare configuration hints (pretend it belongs to root scope). // - variable_map h (rs.ctx); + variable_map h (rs); // Note that all these variables have already been registered. // @@ -278,8 +295,8 @@ namespace build2 if (!xi.pattern.empty ()) h.assign ("config.cc.pattern") = xi.pattern; - if (!xm.empty ()) - h.assign ("config.cc.mode") = xm; + if (!omode.empty ()) + h.assign ("config.cc.mode") = move (omode); h.assign (c_runtime) = xi.runtime; h.assign (c_stdlib) = xi.c_stdlib; @@ -350,6 +367,8 @@ namespace build2 # ifdef __APPLE__ static const dir_path a_usr_inc ( "/Library/Developer/CommandLineTools/SDKs/MacOSX*.sdk/usr/include"); + static const dir_path a_usr_lib ( + "/Library/Developer/CommandLineTools/SDKs/MacOSX*.sdk/usr/lib"); # endif #endif @@ -376,7 +395,9 @@ namespace build2 // if (!cast_false<bool> (rs["cc.core.config.loaded"])) { - variable_map h (rs.ctx); + // Prepare configuration hints (pretend it belongs to root scope). + // + variable_map h (rs); if (!xi.bin_pattern.empty ()) h.assign ("config.bin.pattern") = xi.bin_pattern; @@ -602,10 +623,10 @@ namespace build2 switch (xi.class_) { case compiler_class::gcc: - lib_dirs = gcc_library_search_dirs (xi.path, rs); + lib_dirs = gcc_library_search_dirs (xi, rs); break; case compiler_class::msvc: - lib_dirs = msvc_library_search_dirs (xi.path, rs); + lib_dirs = msvc_library_search_dirs (xi, rs); break; } } @@ -619,10 +640,10 @@ namespace build2 switch (xi.class_) { case compiler_class::gcc: - hdr_dirs = gcc_header_search_dirs (xi.path, rs); + hdr_dirs = gcc_header_search_dirs (xi, rs); break; case compiler_class::msvc: - hdr_dirs = msvc_header_search_dirs (xi.path, rs); + hdr_dirs = msvc_header_search_dirs (xi, rs); break; } } @@ -640,8 +661,8 @@ namespace build2 sys_hdr_dirs_mode = hdr_dirs.second; sys_mod_dirs_mode = mod_dirs ? mod_dirs->second : 0; - sys_lib_dirs_extra = lib_dirs.first.size (); - sys_hdr_dirs_extra = hdr_dirs.first.size (); + sys_lib_dirs_extra = 0; + sys_hdr_dirs_extra = 0; #ifndef _WIN32 // Add /usr/local/{include,lib}. We definitely shouldn't do this if we @@ -657,11 +678,11 @@ namespace build2 // on the next invocation. // { - auto& is (hdr_dirs.first); + auto& hs (hdr_dirs.first); auto& ls (lib_dirs.first); - bool ui (find (is.begin (), is.end (), usr_inc) != is.end ()); - bool uli (find (is.begin (), is.end (), usr_loc_inc) != is.end ()); + bool ui (find (hs.begin (), hs.end (), usr_inc) != hs.end ()); + bool uli (find (hs.begin (), hs.end (), usr_loc_inc) != hs.end ()); #ifdef __APPLE__ // On Mac OS starting from 10.14 there is no longer /usr/include. @@ -684,15 +705,28 @@ namespace build2 // // Is Apple's /usr/include. // - if (!ui && !uli) + // Also, it appears neither Clang nor GCC report MacOSX*.sdk/usr/lib + // with -print-search-dirs but they do search in there. So we add it + // to our list if we see MacOSX*.sdk/usr/include. + // + auto aui (find_if (hs.begin (), hs.end (), + [] (const dir_path& d) + { + return path_match (d, a_usr_inc); + })); + + if (aui != hs.end ()) { - for (const dir_path& d: is) + if (!ui) + ui = true; + + if (find_if (ls.begin (), ls.end (), + [] (const dir_path& d) + { + return path_match (d, a_usr_lib); + }) == ls.end ()) { - if (path_match (d, a_usr_inc)) - { - ui = true; - break; - } + ls.push_back (aui->directory () /= "lib"); } } #endif @@ -700,18 +734,29 @@ namespace build2 { bool ull (find (ls.begin (), ls.end (), usr_loc_lib) != ls.end ()); - // Many platforms don't search in /usr/local/lib by default (but do - // for headers in /usr/local/include). So add it as the last option. + // Many platforms don't search in /usr/local/lib by default but do + // for headers in /usr/local/include. + // + // Note that customarily /usr/local/include is searched before + // /usr/include so we add /usr/local/lib before built-in entries + // (there isn't really a way to add it after since all we can do is + // specify it with -L). // if (!ull && exists (usr_loc_lib, true /* ignore_error */)) - ls.push_back (usr_loc_lib); + { + ls.insert (ls.begin () + sys_lib_dirs_mode, usr_loc_lib); + ++sys_lib_dirs_extra; + } // FreeBSD is at least consistent: it searches in neither. Quoting // its wiki: "FreeBSD can't even find libraries that it installed." // So let's help it a bit. // if (!uli && exists (usr_loc_inc, true /* ignore_error */)) - is.push_back (usr_loc_inc); + { + hs.insert (hs.begin () + sys_hdr_dirs_mode, usr_loc_inc); + ++sys_hdr_dirs_extra; + } } } #endif @@ -815,8 +860,11 @@ namespace build2 dr << "\n hdr dirs"; for (size_t i (0); i != incs.size (); ++i) { - if (i == sys_hdr_dirs_extra) + if ((sys_hdr_dirs_mode != 0 && i == sys_hdr_dirs_mode) || + (sys_hdr_dirs_extra != 0 && + i == sys_hdr_dirs_extra + sys_hdr_dirs_mode)) dr << "\n --"; + dr << "\n " << incs[i]; } } @@ -826,8 +874,11 @@ namespace build2 dr << "\n lib dirs"; for (size_t i (0); i != libs.size (); ++i) { - if (i == sys_lib_dirs_extra) + if ((sys_lib_dirs_mode != 0 && i == sys_lib_dirs_mode) || + (sys_lib_dirs_extra != 0 && + i == sys_lib_dirs_extra + sys_lib_dirs_mode)) dr << "\n --"; + dr << "\n " << libs[i]; } } @@ -948,40 +999,7 @@ namespace build2 // Register target types and configure their "installability". // - bool install_loaded (cast_false<bool> (rs["install.loaded"])); - - { - using namespace install; - - rs.insert_target_type (x_src); - - auto insert_hdr = [&rs, install_loaded] (const target_type& tt) - { - rs.insert_target_type (tt); - - // Install headers into install.include. - // - if (install_loaded) - install_path (rs, tt, dir_path ("include")); - }; - - // Note: module (x_mod) is in x_hdr. - // - for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht) - insert_hdr (**ht); - - // Also register the C header for C-derived languages. - // - if (*x_hdr != &h::static_type) - insert_hdr (h::static_type); - - rs.insert_target_type<pc> (); - rs.insert_target_type<pca> (); - rs.insert_target_type<pcs> (); - - if (install_loaded) - install_path<pc> (rs, dir_path ("pkgconfig")); - } + load_module (rs, rs, (string (x) += ".types"), loc); // Register rules. // @@ -1079,8 +1097,11 @@ namespace build2 // them in case they depend on stuff that we need to install (see the // install rule implementations for details). // - if (install_loaded) + if (cast_false<bool> (rs["install.loaded"])) { + // Note: we rely quite heavily in these rule implementations that + // these are the only target types they are registered for. + const install_rule& ir (*this); r.insert<exe> (perform_install_id, x_install, ir); diff --git a/libbuild2/cc/module.hxx b/libbuild2/cc/module.hxx index 2a8611b..4213516 100644 --- a/libbuild2/cc/module.hxx +++ b/libbuild2/cc/module.hxx @@ -17,6 +17,7 @@ #include <libbuild2/cc/compile-rule.hxx> #include <libbuild2/cc/link-rule.hxx> #include <libbuild2/cc/install-rule.hxx> +#include <libbuild2/cc/predefs-rule.hxx> #include <libbuild2/cc/export.hxx> @@ -115,18 +116,18 @@ namespace build2 // Defined in gcc.cxx. // pair<dir_paths, size_t> - gcc_header_search_dirs (const process_path&, scope&) const; + gcc_header_search_dirs (const compiler_info&, scope&) const; pair<dir_paths, size_t> - gcc_library_search_dirs (const process_path&, scope&) const; + gcc_library_search_dirs (const compiler_info&, scope&) const; // Defined in msvc.cxx. // pair<dir_paths, size_t> - msvc_header_search_dirs (const process_path&, scope&) const; + msvc_header_search_dirs (const compiler_info&, scope&) const; pair<dir_paths, size_t> - msvc_library_search_dirs (const process_path&, scope&) const; + msvc_library_search_dirs (const compiler_info&, scope&) const; }; class LIBBUILD2_CC_SYMEXPORT module: public build2::module, @@ -134,7 +135,8 @@ namespace build2 public link_rule, public compile_rule, public install_rule, - public libux_install_rule + public libux_install_rule, + public predefs_rule { public: explicit @@ -143,7 +145,8 @@ namespace build2 link_rule (move (d)), compile_rule (move (d), rs), install_rule (move (d), *this), - libux_install_rule (move (d), *this) {} + libux_install_rule (move (d), *this), + predefs_rule (move (d)) {} void init (scope&, diff --git a/libbuild2/cc/msvc.cxx b/libbuild2/cc/msvc.cxx index f95cab0..d21969c 100644 --- a/libbuild2/cc/msvc.cxx +++ b/libbuild2/cc/msvc.cxx @@ -164,18 +164,21 @@ namespace build2 // Filter cl.exe and link.exe noise. // + // Note: must be followed with the dbuf.read() call. + // void - msvc_filter_cl (ifdstream& is, const path& src) + msvc_filter_cl (diag_buffer& dbuf, const path& src) + try { // While it appears VC always prints the source name (event if the // file does not exist), let's do a sanity check. Also handle the // command line errors/warnings which come before the file name. // - for (string l; !eof (getline (is, l)); ) + for (string l; !eof (getline (dbuf.is, l)); ) { if (l != src.leaf ().string ()) { - diag_stream_lock () << l << endl; + dbuf.write (l, true /* newline */); if (msvc_sense_diag (l, 'D').first != string::npos) continue; @@ -184,14 +187,19 @@ namespace build2 break; } } + catch (const io_error& e) + { + fail << "unable to read from " << dbuf.args0 << " stderr: " << e; + } void - msvc_filter_link (ifdstream& is, const file& t, otype lt) + msvc_filter_link (diag_buffer& dbuf, const file& t, otype lt) + try { // Filter lines until we encounter something we don't recognize. We also // have to assume the messages can be translated. // - for (string l; getline (is, l); ) + for (string l; getline (dbuf.is, l); ) { // " Creating library foo\foo.dll.lib and object foo\foo.dll.exp" // @@ -216,12 +224,15 @@ namespace build2 // /INCREMENTAL causes linker to sometimes issue messages but now I // can't quite reproduce it. - // - diag_stream_lock () << l << endl; + dbuf.write (l, true /* newline */); break; } } + catch (const io_error& e) + { + fail << "unable to read from " << dbuf.args0 << " stderr: " << e; + } void msvc_extract_header_search_dirs (const strings& v, dir_paths& r) @@ -253,6 +264,13 @@ namespace build2 } else continue; + + // Ignore relative paths. Or maybe we should warn? + // + if (d.relative ()) + continue; + + d.normalize (); } catch (const invalid_path& e) { @@ -260,10 +278,7 @@ namespace build2 << o << "'"; } - // Ignore relative paths. Or maybe we should warn? - // - if (!d.relative ()) - r.push_back (move (d)); + r.push_back (move (d)); } } @@ -284,6 +299,13 @@ namespace build2 d = dir_path (o, 9, string::npos); else continue; + + // Ignore relative paths. Or maybe we should warn? + // + if (d.relative ()) + continue; + + d.normalize (); } catch (const invalid_path& e) { @@ -291,10 +313,7 @@ namespace build2 << o << "'"; } - // Ignore relative paths. Or maybe we should warn? - // - if (!d.relative ()) - r.push_back (move (d)); + r.push_back (move (d)); } } @@ -313,7 +332,7 @@ namespace build2 { try { - r.push_back (dir_path (move (d))); + r.push_back (dir_path (move (d)).normalize ()); } catch (const invalid_path&) { @@ -326,7 +345,7 @@ namespace build2 // Extract system header search paths from MSVC. // pair<dir_paths, size_t> config_module:: - msvc_header_search_dirs (const process_path&, scope& rs) const + msvc_header_search_dirs (const compiler_info&, scope& rs) const { // MSVC doesn't have any built-in paths and all of them either come from // the INCLUDE environment variable or are specified explicitly on the @@ -354,7 +373,7 @@ namespace build2 // Extract system library search paths from MSVC. // pair<dir_paths, size_t> config_module:: - msvc_library_search_dirs (const process_path&, scope& rs) const + msvc_library_search_dirs (const compiler_info&, scope& rs) const { // MSVC doesn't seem to have any built-in paths and all of them either // come from the LIB environment variable or are specified explicitly on @@ -379,9 +398,22 @@ namespace build2 // Inspect the file and determine if it is static or import library. // Return otype::e if it is neither (which we quietly ignore). // + static global_cache<otype> library_type_cache; + static otype library_type (const process_path& ld, const path& l) { + string key; + { + sha256 cs; + cs.append (ld.effect_string ()); + cs.append (l.string ()); + key = cs.string (); + + if (const otype* r = library_type_cache.find (key)) + return *r; + } + // The are several reasonably reliable methods to tell whether it is a // static or import library. One is lib.exe /LIST -- if there aren't any // .obj members, then it is most likely an import library (it can also @@ -422,9 +454,9 @@ namespace build2 // process pr (run_start (ld, args, - 0 /* stdin */, - -1 /* stdout */, - false /* error */)); + 0 /* stdin */, + -1 /* stdout */, + 1 /* stderr (to stdout) */)); bool obj (false), dll (false); string s; @@ -447,14 +479,11 @@ namespace build2 // libhello\hello.lib.obj // hello-0.1.0-a.0.19700101000000.dll // - // Archive member name at 746: [...]hello.dll[/][ ]* - // Archive member name at 8C70: [...]hello.lib.obj[/][ ]* - // size_t n (s.size ()); for (; n != 0 && s[n - 1] == ' '; --n) ; // Skip trailing spaces. - if (n >= 7) // At least ": X.obj" or ": X.dll". + if (n >= 5) // At least "X.obj" or "X.dll". { n -= 4; // Beginning of extension. @@ -480,7 +509,7 @@ namespace build2 io = true; } - if (!run_finish_code (args, pr, s) || io) + if (!run_finish_code (args, pr, s, 2 /* verbosity */) || io) { diag_record dr; dr << warn << "unable to detect " << l << " library type, ignoring" << @@ -489,23 +518,25 @@ namespace build2 return otype::e; } - if (obj && dll) + otype r; + if (obj != dll) + r = obj ? otype::a : otype::s; + else { - warn << l << " looks like hybrid static/import library, ignoring"; - return otype::e; - } + if (obj && dll) + warn << l << " looks like hybrid static/import library, ignoring"; - if (!obj && !dll) - { - warn << l << " looks like empty static or import library, ignoring"; - return otype::e; + if (!obj && !dll) + warn << l << " looks like empty static or import library, ignoring"; + + r = otype::e; } - return obj ? otype::a : otype::s; + return library_type_cache.insert (move (key), r); } template <typename T> - static T* + static pair<T*, bool> msvc_search_library (const process_path& ld, const dir_path& d, const prerequisite_key& p, @@ -551,20 +582,26 @@ namespace build2 // timestamp mt (mtime (f)); - if (mt != timestamp_nonexistent && library_type (ld, f) == lt) + pair<T*, bool> r (nullptr, true); + + if (mt != timestamp_nonexistent) { - // Enter the target. - // - T* t; - common::insert_library (p.scope->ctx, t, name, d, ld, e, exist, trace); - t->path_mtime (move (f), mt); - return t; + if (library_type (ld, f) == lt) + { + // Enter the target. + // + common::insert_library ( + p.scope->ctx, r.first, name, d, ld, e, exist, trace); + r.first->path_mtime (move (f), mt); + } + else + r.second = false; // Don't search for binless. } - return nullptr; + return r; } - liba* common:: + pair<bin::liba*, bool> common:: msvc_search_static (const process_path& ld, const dir_path& d, const prerequisite_key& p, @@ -572,14 +609,21 @@ namespace build2 { tracer trace (x, "msvc_search_static"); - liba* r (nullptr); + liba* a (nullptr); + bool b (true); - auto search = [&r, &ld, &d, &p, exist, &trace] ( + auto search = [&a, &b, &ld, &d, &p, exist, &trace] ( const char* pf, const char* sf) -> bool { - r = msvc_search_library<liba> ( - ld, d, p, otype::a, pf, sf, exist, trace); - return r != nullptr; + pair<liba*, bool> r (msvc_search_library<liba> ( + ld, d, p, otype::a, pf, sf, exist, trace)); + + if (r.first != nullptr) + a = r.first; + else if (!r.second) + b = false; + + return a != nullptr; }; // Try: @@ -592,10 +636,10 @@ namespace build2 search ("", "") || search ("lib", "") || search ("", "lib") || - search ("", "_static") ? r : nullptr; + search ("", "_static") ? make_pair (a, true) : make_pair (nullptr, b); } - libs* common:: + pair<bin::libs*, bool> common:: msvc_search_shared (const process_path& ld, const dir_path& d, const prerequisite_key& pk, @@ -606,12 +650,14 @@ namespace build2 assert (pk.scope != nullptr); libs* s (nullptr); + bool b (true); - auto search = [&s, &ld, &d, &pk, exist, &trace] ( + auto search = [&s, &b, &ld, &d, &pk, exist, &trace] ( const char* pf, const char* sf) -> bool { - if (libi* i = msvc_search_library<libi> ( - ld, d, pk, otype::s, pf, sf, exist, trace)) + pair<libi*, bool> r (msvc_search_library<libi> ( + ld, d, pk, otype::s, pf, sf, exist, trace)); + if (r.first != nullptr) { ulock l ( insert_library ( @@ -619,6 +665,8 @@ namespace build2 if (!exist) { + libi* i (r.first); + if (l.owns_lock ()) { s->adhoc_member = i; // We are first. @@ -632,6 +680,8 @@ namespace build2 s->path_mtime (path (), i->mtime ()); } } + else if (!r.second) + b = false; return s != nullptr; }; @@ -644,7 +694,7 @@ namespace build2 return search ("", "") || search ("lib", "") || - search ("", "dll") ? s : nullptr; + search ("", "dll") ? make_pair (s, true) : make_pair (nullptr, b); } } } diff --git a/libbuild2/cc/parser.cxx b/libbuild2/cc/parser.cxx index dc5093f..f62847e 100644 --- a/libbuild2/cc/parser.cxx +++ b/libbuild2/cc/parser.cxx @@ -15,9 +15,11 @@ namespace build2 using type = token_type; void parser:: - parse (ifdstream& is, const path_name& in, unit& u) + parse (ifdstream& is, const path_name& in, unit& u, const compiler_id& cid) { - lexer l (is, in); + cid_ = &cid; + + lexer l (is, in, true /* preprocessed */); l_ = &l; u_ = &u; @@ -82,6 +84,12 @@ namespace build2 // to call it __import) or it can have a special attribute (GCC // currently marks it with [[__translated]]). // + // Similarly, MSVC drops the `module;` marker and replaces all + // other `module` keywords with `__preprocessed_module`. + // + // Clang doesn't appear to rewrite anything, at least as of + // version 18. + // if (bb == 0 && t.first) { const string& id (t.value); // Note: tracks t. @@ -102,7 +110,9 @@ namespace build2 // Fall through. } - if (id == "module") + if (id == "module" || + (cid_->type == compiler_type::msvc && + id == "__preprocessed_module")) { location_value l (get_location (t)); l_->next (t); @@ -113,7 +123,9 @@ namespace build2 else n = false; } - else if (id == "import" /*|| id == "__import"*/) + else if (id == "import" /* || + (cid_->type == compiler_type::gcc && + id == "__import")*/) { l_->next (t); @@ -181,7 +193,7 @@ namespace build2 // pair<string, bool> np (parse_module_name (t, true /* partition */)); - // Should be {}-balanced. + // Skip attributes (should be {}-balanced). // for (; t.type != type::eos && t.type != type::semi && !t.first; @@ -262,7 +274,7 @@ namespace build2 return; } - // Should be {}-balanced. + // Skip attributes (should be {}-balanced). // for (; t.type != type::eos && t.type != type::semi && !t.first; diff --git a/libbuild2/cc/parser.hxx b/libbuild2/cc/parser.hxx index 1fbf1a3..0c2eb2d 100644 --- a/libbuild2/cc/parser.hxx +++ b/libbuild2/cc/parser.hxx @@ -10,6 +10,7 @@ #include <libbuild2/diagnostics.hxx> #include <libbuild2/cc/types.hxx> +#include <libbuild2/cc/guess.hxx> // compiler_id namespace build2 { @@ -23,16 +24,19 @@ namespace build2 class parser { public: + // The compiler_id argument should identify the compiler that has done + // the preprocessing. + // unit - parse (ifdstream& is, const path_name& n) + parse (ifdstream& is, const path_name& n, const compiler_id& cid) { unit r; - parse (is, n, r); + parse (is, n, r, cid); return r; } void - parse (ifdstream&, const path_name&, unit&); + parse (ifdstream&, const path_name&, unit&, const compiler_id&); private: void @@ -54,6 +58,7 @@ namespace build2 string checksum; // Translation unit checksum. private: + const compiler_id* cid_; lexer* l_; unit* u_; diff --git a/libbuild2/cc/parser.test.cxx b/libbuild2/cc/parser.test.cxx index 1d5930a..2270d32 100644 --- a/libbuild2/cc/parser.test.cxx +++ b/libbuild2/cc/parser.test.cxx @@ -44,7 +44,7 @@ namespace build2 } parser p; - unit u (p.parse (is, in)); + unit u (p.parse (is, in, compiler_id (compiler_type::gcc, ""))); switch (u.type) { diff --git a/libbuild2/cc/pkgconfig-libpkgconf.cxx b/libbuild2/cc/pkgconfig-libpkgconf.cxx index 81a96c3..f3754d3 100644 --- a/libbuild2/cc/pkgconfig-libpkgconf.cxx +++ b/libbuild2/cc/pkgconfig-libpkgconf.cxx @@ -81,10 +81,17 @@ namespace build2 #endif ; +#if defined(LIBPKGCONF_VERSION) && LIBPKGCONF_VERSION >= 10900 + static bool + pkgconf_error_handler (const char* msg, + const pkgconf_client_t*, + void*) +#else static bool pkgconf_error_handler (const char* msg, const pkgconf_client_t*, const void*) +#endif { error << runtime_error (msg); // Sanitize the message (trailing dot). return true; diff --git a/libbuild2/cc/pkgconfig.cxx b/libbuild2/cc/pkgconfig.cxx index 5efab0d..046fbc8 100644 --- a/libbuild2/cc/pkgconfig.cxx +++ b/libbuild2/cc/pkgconfig.cxx @@ -34,6 +34,9 @@ namespace build2 // // @@ TODO: handle empty values (save as ''?) // + // Note: may contain variable expansions (e.g, ${pcfiledir}) so unclear + // if can use quoting. + // static string escape (const string& s) { @@ -161,6 +164,8 @@ namespace build2 const string& stem, bool common) const { + tracer trace (x, "pkgconfig_search"); + // When it comes to looking for .pc files we have to decide where to // search (which directory(ies)) as well as what to search for (which // names). Suffix is our ".shared" or ".static" extension. @@ -182,28 +187,36 @@ namespace build2 // then you get something like zlib which calls it zlib.pc. So let's // just do it. // - f = dir; - f /= "lib"; - f += stem; - f += sfx; - f += ".pc"; - if (exists (f)) - return f; + // And as you think you've covered all the bases, someone decides to + // play with the case (libXau.* vs xau.pc). So let's also try the + // lower-case versions of the stem unless we are on a case-insensitive + // filesystem. + // + auto check = [&dir, & sfx, &f] (const string& n) + { + f = dir; + f /= n; + f += sfx; + f += ".pc"; + return exists (f); + }; - f = dir; - f /= stem; - f += sfx; - f += ".pc"; - if (exists (f)) + if (check ("lib" + stem) || check (stem)) return f; +#ifndef _WIN32 + string lstem (lcase (stem)); + + if (lstem != stem) + { + if (check ("lib" + lstem) || check (lstem)) + return f; + } +#endif + if (proj) { - f = dir; - f /= proj->string (); - f += sfx; - f += ".pc"; - if (exists (f)) + if (check (proj->string ())) return f; } @@ -243,12 +256,15 @@ namespace build2 if (pkgconfig_derive (libd, check)) { + l6 ([&]{trace << "found " << libd << stem << " in " + << (d.a.empty () ? d.a : d.s).directory ();}); + r.first = move (d.a); r.second = move (d.s); } return r; - }; + } bool common:: pkgconfig_load (optional<action> act, @@ -300,23 +316,120 @@ namespace build2 assert (!ap.empty () || !sp.empty ()); - // Extract --cflags and set them as lib?{}:export.poptions.. + const scope& rs (*s.root_scope ()); + + const dir_path* sysroot ( + cast_null<abs_dir_path> (rs["config.cc.pkgconfig.sysroot"])); + + // Append -I<dir> or -L<dir> option suppressing duplicates. Also handle + // the sysroot rewrite. // - auto parse_cflags = [&trace, this] (target& t, - const pkgconfig& pc, - bool la) + auto append_dir = [sysroot] (strings& ops, string&& o) { + char c (o[1]); + + // @@ Should we normalize the path for good measure? But on the other + // hand, most of the time when it's not normalized, it will likely + // be "consistently-relative", e.g., something like + // ${prefix}/lib/../include. I guess let's wait and see for some + // real-world examples. + // + // Well, we now support generating relocatable .pc files that have + // a bunch of -I${pcfiledir}/../../include and -L${pcfiledir}/.. . + // + // On the other hand, there could be symlinks involved and just + // normalize() may not be correct. + // + // Note that we do normalize -L paths in the usrd logic later + // (but not when setting as *.export.loptions). + + if (sysroot != nullptr) + { + // Notes: + // + // - The path might not be absolute (we only rewrite absolute ones). + // + // - Do this before duplicate suppression since options in ops + // already have the sysroot rewritten. + // + // - Check if the path already starts with sysroot since some .pc + // files might already be in a good shape (e.g., because they use + // ${pcfiledir} to support relocation properly). + // + const char* op (o.c_str () + 2); + size_t on (o.size () - 2); + + if (path_traits::absolute (op, on)) + { + const string& s (sysroot->string ()); + + const char* sp (s.c_str ()); + size_t sn (s.size ()); + + if (!path_traits::sub (op, on, sp, sn)) // Already in sysroot. + { + // Find the first directory seperator that seperates the root + // component from the rest of the path (think /usr/include, + // c:\install\include). We need to replace the root component + // with sysroot. If there is no separator (say, -Ic:) or the + // path after the separator is empty (say, -I/), then we replace + // the entire path. + // + size_t p (path_traits::find_separator (o, 2)); + if (p == string::npos || p + 1 == o.size ()) + p = o.size (); + + o.replace (2, p - 2, s); + } + } + } + + for (const string& x: ops) + { + if (x.size () > 2 && x[0] == '-' && x[1] == c) + { + if (path_traits::compare (x.c_str () + 2, x.size () - 2, + o.c_str () + 2, o.size () - 2) == 0) + return; // Duplicate. + } + } + + ops.push_back (move (o)); + }; + + // Extract --cflags and set them as lib?{}:export.poptions returing the + // pointer to the set value. If [as]pops are not NULL, then only keep + // options that are present in both. + // + auto parse_cflags =[&trace, + this, + &append_dir] (target& t, + const pkgconfig& pc, + bool la, + const strings* apops = nullptr, + const strings* spops = nullptr) + -> const strings* + { + // Note that we normalize `-[IDU] <arg>` to `-[IDU]<arg>`. + // strings pops; - bool arg (false); - for (auto& o: pc.cflags (la)) + char arg ('\0'); // Option with pending argument. + for (string& o: pc.cflags (la)) { if (arg) { // Can only be an argument for -I, -D, -U options. // - pops.push_back (move (o)); - arg = false; + o.insert (0, 1, arg); + o.insert (0, 1, '-'); + + if (arg == 'I') + append_dir (pops, move (o)); + else + pops.push_back (move (o)); + + arg = '\0'; continue; } @@ -327,8 +440,15 @@ namespace build2 if (n >= 2 && o[0] == '-' && (o[1] == 'I' || o[1] == 'D' || o[1] == 'U')) { - pops.push_back (move (o)); - arg = (n == 2); + if (n > 2) + { + if (o[1] == 'I') + append_dir (pops, move (o)); + else + pops.push_back (move (o)); + } + else + arg = o[1]; continue; } @@ -337,7 +457,7 @@ namespace build2 } if (arg) - fail << "argument expected after " << pops.back () << + fail << "argument expected after -" << arg << info << "while parsing pkg-config --cflags " << pc.path; if (!pops.empty ()) @@ -350,19 +470,45 @@ namespace build2 // export stub and we shouldn't touch them. // if (p.second) + { + // If required, only keep common stuff. While removing the entries + // is not the most efficient way, it is simple. + // + if (apops != nullptr || spops != nullptr) + { + for (auto i (pops.begin ()); i != pops.end (); ) + { + if ((apops != nullptr && find ( + apops->begin (), apops->end (), *i) == apops->end ()) || + (spops != nullptr && find ( + spops->begin (), spops->end (), *i) == spops->end ())) + i = pops.erase (i); + else + ++i; + } + } + p.first = move (pops); + return &p.first.as<strings> (); + } } + + return nullptr; }; // Parse --libs into loptions/libs (interface and implementation). If // ps is not NULL, add each resolved library target as a prerequisite. // - auto parse_libs = [this, act, &s, top_sysd] (target& t, - bool binless, - const pkgconfig& pc, - bool la, - prerequisites* ps) + auto parse_libs = [this, + &append_dir, + act, &s, top_sysd] (target& t, + bool binless, + const pkgconfig& pc, + bool la, + prerequisites* ps) { + // Note that we normalize `-L <arg>` to `-L<arg>`. + // strings lops; vector<name> libs; @@ -379,15 +525,21 @@ namespace build2 // library. What we do at the moment is stop recognizing just library // names (without -l) after seeing an unknown option. // - bool arg (false), first (true), known (true), have_L; - for (auto& o: pc.libs (la)) + bool first (true), known (true), have_L (false); + + string self; // The library itself (-l of just name/path). + + char arg ('\0'); // Option with pending argument. + for (string& o: pc.libs (la)) { if (arg) { - // Can only be an argument for an loption. + // Can only be an argument for an -L option. // - lops.push_back (move (o)); - arg = false; + o.insert (0, 1, arg); + o.insert (0, 1, '-'); + append_dir (lops, move (o)); + arg = '\0'; continue; } @@ -397,15 +549,17 @@ namespace build2 // if (n >= 2 && o[0] == '-' && o[1] == 'L') { + if (n > 2) + append_dir (lops, move (o)); + else + arg = o[1]; have_L = true; - lops.push_back (move (o)); - arg = (n == 2); continue; } // See if that's -l, -pthread, or just the library name/path. // - if ((known && o[0] != '-') || + if ((known && n != 0 && o[0] != '-') || (n > 2 && o[0] == '-' && (o[1] == 'l' || o == "-pthread"))) { // Unless binless, the first one is the library itself, which we @@ -413,19 +567,11 @@ namespace build2 // be some other library, but we haven't encountered such a beast // yet. // - if (first) - { - first = false; - - if (!binless) - continue; - } - - // @@ If for some reason this is the library itself (doesn't go - // first or libpkg-config parsed libs in some bizarre way) we - // will have a dependency cycle by trying to lock its target - // inside search_library() as by now it is already locked. To - // be safe we probably shouldn't rely on the position and + // What we have enountered (e.g., in the Magick++ library) is the + // library itself repeated in Libs.private. So now we save it and + // filter all its subsequent occurences. + // + // @@ To be safe we probably shouldn't rely on the position and // filter out all occurrences of the library itself (by name?) // and complain if none were encountered. // @@ -435,6 +581,22 @@ namespace build2 // frame around the call to search_library() to help diagnose // such situations. // + if (first) + { + first = false; + + if (!binless) + { + self = move (o); + continue; + } + } + else + { + if (!binless && o == self) + continue; + } + libs.push_back (name (move (o))); continue; } @@ -446,7 +608,7 @@ namespace build2 } if (arg) - fail << "argument expected after " << lops.back () << + fail << "argument expected after -" << arg << info << "while parsing pkg-config --libs " << pc.path; // Space-separated list of escaped library flags. @@ -454,7 +616,7 @@ namespace build2 auto lflags = [&pc, la] () -> string { string r; - for (const auto& o: pc.libs (la)) + for (const string& o: pc.libs (la)) { if (!r.empty ()) r += ' '; @@ -463,7 +625,7 @@ namespace build2 return r; }; - if (first && !binless) + if (!binless && self.empty ()) fail << "library expected in '" << lflags () << "'" << info << "while parsing pkg-config --libs " << pc.path; @@ -504,12 +666,15 @@ namespace build2 if (l[0] != '-') // e.g., just shell32.lib continue; else if (cmp ("advapi32") || + cmp ("authz") || cmp ("bcrypt") || + cmp ("comdlg32") || cmp ("crypt32") || - cmp ("dbgeng") || - cmp ("dbghelp") || cmp ("d2d1") || cmp ("d3d", 3) || // d3d* + cmp ("dbgeng") || + cmp ("dbghelp") || + cmp ("dnsapi") || cmp ("dwmapi") || cmp ("dwrite") || cmp ("dxgi") || @@ -522,6 +687,7 @@ namespace build2 cmp ("kernel32") || cmp ("mincore") || cmp ("mpr") || + cmp ("msimg32") || cmp ("mswsock") || cmp ("msxml", 5) || // msxml* cmp ("netapi32") || @@ -534,6 +700,7 @@ namespace build2 cmp ("psapi") || cmp ("rpcrt4") || cmp ("secur32") || + cmp ("setupapi") || cmp ("shell32") || cmp ("shlwapi") || cmp ("synchronization") || @@ -541,6 +708,7 @@ namespace build2 cmp ("userenv") || cmp ("uuid") || cmp ("version") || + cmp ("windowscodecs") || cmp ("winhttp") || cmp ("winmm") || cmp ("winspool") || @@ -591,7 +759,11 @@ namespace build2 } else if (tclass == "macos") { - if (l == "-lSystem") + // Note that Mac OS has libiconv in /usr/lib/ which only comes + // in the shared variant. So we treat it as system. + // + if (l == "-lSystem" || + l == "-liconv") continue; } else if (tclass == "bsd") @@ -608,18 +780,13 @@ namespace build2 { usrd = dir_paths (); - for (auto i (lops.begin ()); i != lops.end (); ++i) + for (const string& o: lops) { - const string& o (*i); - - if (o.size () >= 2 && o[0] == '-' && o[1] == 'L') + // Note: always in the -L<dir> form (see above). + // + if (o.size () > 2 && o[0] == '-' && o[1] == 'L') { - string p; - - if (o.size () == 2) - p = *++i; // We've verified it's there. - else - p = string (o, 2); + string p (o, 2); try { @@ -630,6 +797,7 @@ namespace build2 << lflags () << "'" << info << "while parsing pkg-config --libs " << pc.path; + d.normalize (); usrd->push_back (move (d)); } catch (const invalid_path& e) @@ -709,24 +877,16 @@ namespace build2 { // Translate -L to /LIBPATH. // - for (auto i (lops.begin ()); i != lops.end (); ) + for (string& o: lops) { - string& o (*i); size_t n (o.size ()); - if (n >= 2 && o[0] == '-' && o[1] == 'L') + // Note: always in the -L<dir> form (see above). + // + if (n > 2 && o[0] == '-' && o[1] == 'L') { o.replace (0, 2, "/LIBPATH:"); - - if (n == 2) - { - o += *++i; // We've verified it's there. - i = lops.erase (i); - continue; - } } - - ++i; } } @@ -805,6 +965,8 @@ namespace build2 optional<uint64_t> ver; optional<string> pfx; + variable_pool* vp (nullptr); // Resolve lazily. + string s; for (size_t b (0), e (0); !(s = next (md, b, e)).empty (); ) { @@ -865,8 +1027,13 @@ namespace build2 : name (move (s))); } - auto& vp (ctx.var_pool.rw ()); // Load phase. - const variable& var (vp.insert (move (vn))); + // These should be public (qualified) variables so go straight for + // the public variable pool. + // + if (vp == nullptr) + vp = &ctx.var_pool.rw (); // Load phase if user==true. + + const variable& var (vp->insert (move (vn))); value& v (t.assign (var)); v.assign (move (ns), &var); @@ -913,6 +1080,14 @@ namespace build2 string mn (m, 0, p); path mp (m, p + 1, string::npos); + + // Must be absolute but may not be normalized due to a relocatable + // .pc file. We assume there are no symlink shenanigans that would + // require realize(). + // + if (!mp.normalized ()) + mp.normalize (); + path mf (mp.leaf ()); // Extract module properties, if any. @@ -937,7 +1112,7 @@ namespace build2 target_decl::implied, trace)); - target& mt (tl.first); + file& mt (tl.first.as<file> ()); // If the target already exists, then setting its variables is not // MT-safe. So currently we only do it if we have the lock (and thus @@ -955,6 +1130,7 @@ namespace build2 // if (tl.second.owns_lock ()) { + mt.path (move (mp)); mt.vars.assign (c_module_name) = move (mn); // Set module properties. Note that if unspecified we should still @@ -1005,6 +1181,14 @@ namespace build2 for (size_t b (0), e (0); !(h = next (*val, b, e)).empty (); ) { path hp (move (h)); + + // Must be absolute but may not be normalized due to a relocatable + // .pc file. We assume there are no symlink shenanigans that would + // require realize(). + // + if (!hp.normalized ()) + hp.normalize (); + path hf (hp.leaf ()); auto tl ( @@ -1017,7 +1201,7 @@ namespace build2 target_decl::implied, trace)); - target& ht (tl.first); + file& ht (tl.first.as<file> ()); // If the target already exists, then setting its variables is not // MT-safe. So currently we only do it if we have the lock (and thus @@ -1026,6 +1210,7 @@ namespace build2 // if (tl.second.owns_lock ()) { + ht.path (move (hp)); ht.vars.assign (c_importable) = true; tl.second.unlock (); } @@ -1053,9 +1238,16 @@ namespace build2 // Note that we rely on the "small function object" optimization here. // - auto add_pc_dir = [&pc_dirs] (dir_path&& d) -> bool + auto add_pc_dir = [&trace, &pc_dirs] (dir_path&& d) -> bool { - pc_dirs.emplace_back (move (d)); + // Suppress duplicated. + // + if (find (pc_dirs.begin (), pc_dirs.end (), d) == pc_dirs.end ()) + { + l6 ([&]{trace << "search path " << d;}); + pc_dirs.emplace_back (move (d)); + } + return false; }; @@ -1182,14 +1374,26 @@ namespace build2 false, &prs); + const strings* apops (nullptr); if (pa) { - parse_cflags (*at, apc, true); + apops = parse_cflags (*at, apc, true); parse_libs (*at, at->path ().empty (), apc, true, nullptr); } + const strings* spops (nullptr); if (ps) - parse_cflags (*st, spc, false); + spops = parse_cflags (*st, spc, false); + + // Also set common poptions for the group. In particular, this makes + // sure $lib_poptions() in the "common interface" mode works for the + // installed libraries. + // + // Note that if there are no poptions set for either, then we cannot + // possibly have a common subset. + // + if (apops != nullptr || spops != nullptr) + parse_cflags (lt, ipc, false, apops, spops); // @@ TODO: we can now load cc.type if there is metadata (but need to // return this rather than set, see search_library() for @@ -1221,7 +1425,7 @@ namespace build2 // We treat headers outside of any project as C headers (see // enter_header() for details). // - parse_headers (ipc, h::static_type /* **x_hdr */, x, prs); + parse_headers (ipc, h::static_type /* **x_hdrs */, x, prs); parse_headers (ipc, h::static_type, "c", prs); } @@ -1341,7 +1545,7 @@ namespace build2 { bool f (ldirs.empty ()); - ldirs.push_back (resolve_dir (g, d, !f /* fail_unknown */)); + ldirs.push_back (resolve_dir (g, d, {}, !f /* fail_unknown */)); if (f && ldirs.back ().empty ()) break; @@ -1350,6 +1554,7 @@ namespace build2 else ldirs.push_back (resolve_dir (g, cast<dir_path> (g["install.lib"]), + {}, false /* fail_unknown */)); if (!ldirs.empty () && ldirs.front ().empty ()) @@ -1372,14 +1577,79 @@ namespace build2 // Note that generation can take some time if we have a large number of // prerequisite libraries. // - if (verb) - text << "pc " << *t; - else if (verb >= 2) + if (verb >= 2) text << "cat >" << p; + else if (verb) + print_diag ("pc", g, *t); if (ctx.dry_run) return; + // See if we should be generating a relocatable .pc file and if so get + // its installation location. The plan is to make all absolute paths + // that we write relative to this location and prefix them with the + // built-in ${pcfiledir} variable (which supported by everybody: the + // original pkg-config, pkgconf, and our libpkg-config library). + // + dir_path rel_base; + if (cast_false<bool> (rs["install.relocatable"])) + { + path f (install::resolve_file (*t)); + if (!f.empty ()) // Shouldn't happen but who knows. + rel_base = f.directory (); + } + + // Note: reloc_*path() expect absolute and normalized paths. + // + // Note also that reloc_path() can be used on dir_path to get the path + // without the trailing slash. + // + auto reloc_path = [&rel_base, + s = string ()] (const path& p, + const char* what) mutable + -> const string& + { + if (rel_base.empty ()) + return p.string (); + + try + { + s = p.relative (rel_base).string (); + } + catch (const invalid_path&) + { + fail << "unable to make " << what << " path " << p << " relative to " + << rel_base; + } + + if (!s.empty ()) s.insert (0, 1, path_traits::directory_separator); + s.insert (0, "${pcfiledir}"); + return s; + }; + + auto reloc_dir_path = [&rel_base, + s = string ()] (const dir_path& p, + const char* what) mutable + -> const string& + { + if (rel_base.empty ()) + return (s = p.representation ()); + + try + { + s = p.relative (rel_base).representation (); + } + catch (const invalid_path&) + { + fail << "unable to make " << what << " path " << p << " relative to " + << rel_base; + } + + if (!s.empty ()) s.insert (0, 1, path_traits::directory_separator); + s.insert (0, "${pcfiledir}"); + return s; + }; + auto_rmfile arm (p); try @@ -1531,7 +1801,7 @@ namespace build2 // os << "Cflags:"; for (const dir_path& d: idirs) - os << " -I" << escape (d.string ()); + os << " -I" << escape (reloc_path (d, "header search")); save_poptions (x_export_poptions); save_poptions (c_export_poptions); os << endl; @@ -1551,7 +1821,7 @@ namespace build2 // necessary to resolve its binful dependencies. // for (const dir_path& d: ldirs) - os << " -L" << escape (d.string ()); + os << " -L" << escape (reloc_path (d, "library search")); // Now process ourselves as if we were being linked to something (so // pretty similar to link_rule::append_libraries()). We also reuse @@ -1644,7 +1914,7 @@ namespace build2 //@@ TODO: should we filter -L similar to -I? //@@ TODO: how will the Libs/Libs.private work? - //@@ TODO: remember to use escape() + //@@ TODO: remember to use reloc_*() and escape(). if (d.pls != nullptr && d.pls->find (l) != nullptr) return true; @@ -1738,7 +2008,8 @@ namespace build2 } catch (const invalid_argument& e) { - fail << "invalid metadata version in library " << g << ": " << e; + fail << "invalid metadata version in library " << g << ": " << e + << endf; } if (ver != 1) @@ -1896,16 +2167,43 @@ namespace build2 const value& val (*b.val); names ns; - names_view nv (reverse (val, ns)); + names_view nv (reverse (val, ns, true /* reduce */)); os << *b.name << " ="; - auto append = [&l, &var, &s] (const name& v) + auto append = [&rel_base, + &reloc_path, + &reloc_dir_path, + &l, &var, &val, &s] (const name& v) { + // If this is absolute path or dir_path, then attempt to + // relocate. Without that the result will not be relocatable. + // if (v.simple ()) - s += v.value; + { + path p; + if (!rel_base.empty () && + val.type != nullptr && + (val.type->is_a<path> () || val.type->is_a<paths> ()) && + (p = path (v.value)).absolute ()) + { + p.normalize (); + s += reloc_path (p, var.name.c_str ()); + } + else + s += v.value; + } else if (v.directory ()) - s += v.dir.representation (); + { + if (!rel_base.empty () && v.dir.absolute ()) + { + dir_path p (v.dir); + p.normalize (); + s += reloc_dir_path (p, var.name.c_str ()); + } + else + s += v.dir.representation (); + } else // It seems like we shouldn't end up here due to the type // check but let's keep it for good measure. @@ -1974,6 +2272,8 @@ namespace build2 // if (la) { + // Note: go straight for the public variable pool. + // if (cast_false<bool> (l.lookup_original ( ctx.var_pool["bin.whole"], true /* target_only */).first)) @@ -2061,7 +2361,7 @@ namespace build2 move (pp), symexport}); } - else if (pt->is_a (**x_hdr) || pt->is_a<h> ()) + else if (pt->is_a (**this->x_hdrs) || pt->is_a<h> ()) { if (cast_false<bool> ((*pt)[c_importable])) { @@ -2104,7 +2404,8 @@ namespace build2 // Module names shouldn't require escaping. // os << (n != 1 ? " \\\n" : " ") - << m.name << '=' << escape (m.file.string ()); + << m.name << '=' + << escape (reloc_path (m.file, "module interface")); } os << endl; @@ -2130,7 +2431,8 @@ namespace build2 << "c.importable_headers ="; for (const path& h: c_hdrs) - os << (n != 1 ? " \\\n" : " ") << escape (h.string ()); + os << (n != 1 ? " \\\n" : " ") + << escape (reloc_path (h, "header unit")); os << endl; } @@ -2141,7 +2443,8 @@ namespace build2 << x << ".importable_headers ="; for (const path& h: x_hdrs) - os << (n != 1 ? " \\\n" : " ") << escape (h.string ()); + os << (n != 1 ? " \\\n" : " ") + << escape (reloc_path (h, "header unit")); os << endl; } diff --git a/libbuild2/cc/pkgconfig.hxx b/libbuild2/cc/pkgconfig.hxx index 7959da1..a1bcdee 100644 --- a/libbuild2/cc/pkgconfig.hxx +++ b/libbuild2/cc/pkgconfig.hxx @@ -56,8 +56,8 @@ namespace build2 // Movable-only type. // - pkgconfig (pkgconfig&&); - pkgconfig& operator= (pkgconfig&&); + pkgconfig (pkgconfig&&) noexcept; + pkgconfig& operator= (pkgconfig&&) noexcept; pkgconfig (const pkgconfig&) = delete; pkgconfig& operator= (const pkgconfig&) = delete; @@ -95,7 +95,7 @@ namespace build2 } inline pkgconfig:: - pkgconfig (pkgconfig&& p) + pkgconfig (pkgconfig&& p) noexcept : path (move (p.path)), client_ (p.client_), pkg_ (p.pkg_) @@ -105,7 +105,7 @@ namespace build2 } inline pkgconfig& pkgconfig:: - operator= (pkgconfig&& p) + operator= (pkgconfig&& p) noexcept { if (this != &p) { diff --git a/libbuild2/cc/predefs-rule.cxx b/libbuild2/cc/predefs-rule.cxx new file mode 100644 index 0000000..606db06 --- /dev/null +++ b/libbuild2/cc/predefs-rule.cxx @@ -0,0 +1,379 @@ +// file : libbuild2/cc/predefs-rule.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include <libbuild2/cc/predefs-rule.hxx> + +#include <libbuild2/depdb.hxx> +#include <libbuild2/target.hxx> +#include <libbuild2/context.hxx> +#include <libbuild2/algorithm.hxx> +#include <libbuild2/filesystem.hxx> +#include <libbuild2/diagnostics.hxx> + +namespace build2 +{ + namespace cc + { + predefs_rule:: + predefs_rule (data&& d) + : common (move (d)), + rule_name (string (x) += ".predefs"), + rule_id (rule_name + " 1") + { + } + + bool predefs_rule:: + match (action, target&, const string& hint, match_extra&) const + { + tracer trace (x, "predefs_rule::match"); + + // We only match with an explicit hint (failed that, we will turn every + // header into predefs). + // + if (hint == rule_name) + { + // Don't match if unsupported compiler. In particular, this allows the + // user to provide a fallback rule. + // + switch (cclass) + { + case compiler_class::gcc: return true; + case compiler_class::msvc: + { + // Only MSVC 19.20 or later. Not tested with clang-cl. + // + if (cvariant.empty () && (cmaj > 19 || (cmaj == 19 && cmin >= 20))) + return true; + + l4 ([&]{trace << "unsupported compiler/version";}); + break; + } + } + } + + return false; + } + + recipe predefs_rule:: + apply (action a, target& xt, match_extra&) const + { + file& t (xt.as<file> ()); + t.derive_path (); + + // Inject dependency on the output directory. + // + inject_fsdir (a, t); + + if (a == perform_update_id) + { + return [this] (action a, const target& xt) + { + return perform_update (a, xt); + }; + } + else if (a == perform_clean_id) + { + return [] (action a, const target& t) + { + // Also remove the temporary input source file in case it wasn't + // removed at the end of the update. + // + return perform_clean_extra (a, t.as<file> (), {".d", ".t"}); + }; + } + else + return noop_recipe; // Configure update. + } + + // Filter noise, sanitize options (msvc.cxx). + // + void + msvc_filter_cl (diag_buffer&, const path& src); + + void + msvc_sanitize_cl (cstrings&); + + target_state predefs_rule:: + perform_update (action a, const target& xt) const + { + tracer trace (x, "predefs_rule::perform_update"); + + const file& t (xt.as<file> ()); + const path& tp (t.path ()); + + context& ctx (t.ctx); + + const scope& rs (t.root_scope ()); + + // Execute prerequisites (the output directory being the only one thus + // not mtime checking). + // + execute_prerequisites (a, t); + + // Use depdb to track changes to options, compiler, etc (similar to + // the compile_rule). + // + depdb dd (tp + ".d"); + { + // First should come the rule name/version. + // + if (dd.expect (rule_id) != nullptr) + l4 ([&]{trace << "rule mismatch forcing update of " << t;}); + + // Then the compiler checksum. + // + if (dd.expect (cast<string> (rs[x_checksum])) != nullptr) + l4 ([&]{trace << "compiler mismatch forcing update of " << t;}); + + // Then the compiler environment checksum. + // + if (dd.expect (env_checksum) != nullptr) + l4 ([&]{trace << "environment mismatch forcing update of " << t;}); + + // Finally the options checksum (as below). + // + { + sha256 cs; + append_options (cs, t, c_coptions); + append_options (cs, t, x_coptions); + append_options (cs, cmode); + + if (dd.expect (cs.string ()) != nullptr) + l4 ([&]{trace << "options mismatch forcing update of " << t;}); + } + } + + // Update if depdb mismatch. + // + bool update (dd.writing () || dd.mtime > t.load_mtime ()); + + dd.close (); + + if (!update) + return target_state::unchanged; // No mtime-based prerequisites. + + // Prepare the compiler command-line. + // + cstrings args {cpath.recall_string ()}; + + // Append compile options. + // + // Note that any command line macros that we specify with -D will end up + // in the predefs, which is something we don't want. So no poptions. + // + append_options (args, t, c_coptions); + append_options (args, t, x_coptions); + append_options (args, cmode); + + // The output and input paths, relative to the working directory for + // easier to read diagnostics. + // + path relo (relative (tp)); + path reli; + + // Add compiler-specific command-line arguments. + // + switch (cclass) + { + case compiler_class::gcc: + { + // Add implied options which may affect predefs, similar to the + // compile rule. + // + if (!find_option_prefix ("-finput-charset=", args)) + args.push_back ("-finput-charset=UTF-8"); + + if (ctype == compiler_type::clang && tsys == "win32-msvc") + { + if (!find_options ({"-nostdlib", "-nostartfiles"}, args)) + { + args.push_back ("-D_MT"); + args.push_back ("-D_DLL"); + } + } + + if (ctype == compiler_type::clang && cvariant == "emscripten") + { + if (x_lang == lang::cxx) + { + if (!find_option_prefix ("DISABLE_EXCEPTION_CATCHING=", args)) + { + args.push_back ("-s"); + args.push_back ("DISABLE_EXCEPTION_CATCHING=0"); + } + } + } + + args.push_back ("-E"); // Stop after the preprocessing stage. + args.push_back ("-dM"); // Generate #define directives. + + // Output. + // + args.push_back ("-o"); + args.push_back (relo.string ().c_str ()); + + // Input. + // + args.push_back ("-x"); + switch (x_lang) + { + case lang::c: args.push_back ("c"); break; + case lang::cxx: args.push_back ("c++"); break; + } + + // With GCC and Clang we can compile /dev/null as stdin by + // specifying `-` and thus omitting the temporary file. + // + args.push_back ("-"); + + break; + } + case compiler_class::msvc: + { + // Add implied options which may affect predefs, similar to the + // compile rule. + // + { + // Note: these affect the _MSVC_EXECUTION_CHARACTER_SET, _UTF8 + // macros. + // + bool sc (find_option_prefixes ( + {"/source-charset:", "-source-charset:"}, args)); + bool ec (find_option_prefixes ( + {"/execution-charset:", "-execution-charset:"}, args)); + + if (!sc && !ec) + args.push_back ("/utf-8"); + else + { + if (!sc) + args.push_back ("/source-charset:UTF-8"); + + if (!ec) + args.push_back ("/execution-charset:UTF-8"); + } + } + + if (x_lang == lang::cxx) + { + if (!find_option_prefixes ({"/EH", "-EH"}, args)) + args.push_back ("/EHsc"); + } + + if (!find_option_prefixes ({"/MD", "/MT", "-MD", "-MT"}, args)) + args.push_back ("/MD"); + + msvc_sanitize_cl (args); + + args.push_back ("/nologo"); + + // /EP may seem like it contradicts /P but it's the recommended + // way to suppress `#line`s from the output of the /P option (see + // /P in the "MSVC Compiler Options" documentation). + // + args.push_back ("/P"); // Write preprocessor output to a file. + args.push_back ("/EP"); // Preprocess to stdout without `#line`s. + + args.push_back ("/PD"); // Print all macro definitions. + args.push_back ("/Zc:preprocessor"); // Preproc. conformance mode. + + // Output (note that while the /Fi: variant is only availbale + // starting with VS2013, /Zc:preprocessor is only available + // starting from VS2019). + // + args.push_back ("/Fi:"); + args.push_back (relo.string ().c_str ()); + + // Input. + // + switch (x_lang) + { + case lang::c: args.push_back ("/TC"); break; + case lang::cxx: args.push_back ("/TP"); break; + } + + // Input path. + // + // Note that with MSVC we have to use a temporary file. In + // particular compiling `nul` does not work. + // + reli = relo + ".t"; + args.push_back (reli.string ().c_str ()); + + break; + } + } + + args.push_back (nullptr); + + // Run the compiler. + // + if (verb >= 2) + print_process (args); + else if (verb) + print_diag ((string (x_name) + "-predefs").c_str (), t); + + if (!ctx.dry_run) + { + // Create an empty temporary input source file, if necessary. + // + auto_rmfile rmi; + if (!reli.empty ()) + { + rmi = auto_rmfile (reli); + + if (exists (reli, false /* follow_symlinks */)) + rmfile (ctx, reli, 3 /* verbosity */); + + touch (ctx, reli, true /* create */, 3 /* verbosity */); + } + + try + { + // VC cl.exe sends diagnostics to stdout. It also prints the file + // name being compiled as the first line. So for cl.exe we filter + // that noise out. + // + // For other compilers also redirect stdout to stderr, in case any + // of them tries to pull off something similar. For sane compilers + // this should be harmless. + // + // We also redirect stdin to /dev/null in case that's used instead + // of the temporary file. + // + // Note: similar logic as in compile_rule. + // + bool filter (ctype == compiler_type::msvc); + + process pr (cpath, + args, + -2, /* stdin */ + 2, /* stdout */ + diag_buffer::pipe (ctx, filter /* force */) /* stderr */); + + diag_buffer dbuf (ctx, args[0], pr); + + if (filter) + msvc_filter_cl (dbuf, reli); + + dbuf.read (); + + run_finish (dbuf, args, pr, 1 /* verbosity */); + dd.check_mtime (tp); + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e; + + if (e.child) + exit (1); + + throw failed (); + } + } + + t.mtime (system_clock::now ()); + return target_state::changed; + } + } +} diff --git a/libbuild2/cc/predefs-rule.hxx b/libbuild2/cc/predefs-rule.hxx new file mode 100644 index 0000000..60aa063 --- /dev/null +++ b/libbuild2/cc/predefs-rule.hxx @@ -0,0 +1,45 @@ +// file : libbuild2/cc/predefs-rule.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_CC_PREDEFS_RULE_HXX +#define LIBBUILD2_CC_PREDEFS_RULE_HXX + +#include <libbuild2/types.hxx> +#include <libbuild2/utility.hxx> + +#include <libbuild2/rule.hxx> + +#include <libbuild2/cc/types.hxx> +#include <libbuild2/cc/common.hxx> + +#include <libbuild2/cc/export.hxx> + +namespace build2 +{ + namespace cc + { + class LIBBUILD2_CC_SYMEXPORT predefs_rule: public rule, + virtual common + { + public: + const string rule_name; + + explicit + predefs_rule (data&&); + + virtual bool + match (action, target&, const string&, match_extra&) const override; + + virtual recipe + apply (action, target&, match_extra&) const override; + + target_state + perform_update (action, const target&) const; + + private: + const string rule_id; + }; + } +} + +#endif // LIBBUILD2_CC_PREDEFS_RULE_HXX diff --git a/libbuild2/cc/std.compat.cppm b/libbuild2/cc/std.compat.cppm new file mode 100644 index 0000000..2668b30 --- /dev/null +++ b/libbuild2/cc/std.compat.cppm @@ -0,0 +1,996 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// WARNING, this entire header is generated by +// utils/generate_libcxx_cppm_in.py +// DO NOT MODIFY! + +module; + +#include <__config> + +#if _LIBCPP_VERSION < 180000 +#error libc++ version 18.0.0 or later required +#endif + +// The headers of Table 24: C++ library headers [tab:headers.cpp] +// and the headers of Table 25: C++ headers for C library facilities [tab:headers.cpp.c] +#include <cassert> +#include <cctype> +#include <cerrno> +#include <cfenv> +#include <cfloat> +#include <cinttypes> +#include <climits> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <clocale> +#endif +#include <cmath> +#include <csetjmp> +#include <csignal> +#include <cstdarg> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <cuchar> +#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +# include <cwchar> +#endif +#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +# include <cwctype> +#endif + +#if 0 +// *** Headers not yet available *** +#if __has_include(<debugging>) +# error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<debugging>) +#if __has_include(<flat_map>) +# error "please update the header information for <flat_map> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<flat_map>) +#if __has_include(<flat_set>) +# error "please update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<flat_set>) +#if __has_include(<generator>) +# error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<generator>) +#if __has_include(<hazard_pointer>) +# error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<hazard_pointer>) +#if __has_include(<linalg>) +# error "please update the header information for <linalg> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<linalg>) +#if __has_include(<rcu>) +# error "please update the header information for <rcu> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<rcu>) +#if __has_include(<spanstream>) +# error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<spanstream>) +#if __has_include(<stacktrace>) +# error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<stacktrace>) +#if __has_include(<stdfloat>) +# error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<stdfloat>) +#if __has_include(<text_encoding>) +# error "please update the header information for <text_encoding> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<text_encoding>) +#endif + +export module std.compat; +export import std; + +// cassert.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // This module exports nothing. +} // export + +// cctype.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::isalnum; + using ::isalpha; + using ::isblank; + using ::iscntrl; + using ::isdigit; + using ::isgraph; + using ::islower; + using ::isprint; + using ::ispunct; + using ::isspace; + using ::isupper; + using ::isxdigit; + using ::tolower; + using ::toupper; +} // export + +// cerrno.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // This module exports nothing. +} // export + +// cfenv.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // types + using ::fenv_t; + using ::fexcept_t; + + // functions + using ::feclearexcept; + using ::fegetexceptflag; + using ::feraiseexcept; + using ::fesetexceptflag; + using ::fetestexcept; + + using ::fegetround; + using ::fesetround; + + using ::fegetenv; + using ::feholdexcept; + using ::fesetenv; + using ::feupdateenv; +} // export + +// cfloat.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // This module exports nothing. +} // export + +// cinttypes.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::imaxdiv_t; + + using ::imaxabs; + using ::imaxdiv; + using ::strtoimax; + using ::strtoumax; + using ::wcstoimax; + using ::wcstoumax; + + // abs is conditionally here, but always present in cmath.cppm. To avoid + // conflicing declarations omit the using here. + + // div is conditionally here, but always present in cstdlib.cppm. To avoid + // conflicing declarations omit the using here. +} // export + +// climits.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // This module exports nothing. +} // export + +// clocale.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using ::lconv; + + using ::localeconv; + using ::setlocale; +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // export + +// cmath.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::double_t; + using ::float_t; + + using ::acos; + using ::acosf; + using ::acosl; + + using ::asin; + using ::asinf; + using ::asinl; + + using ::atan; + using ::atanf; + using ::atanl; + + using ::atan2; + using ::atan2f; + using ::atan2l; + + using ::cos; + using ::cosf; + using ::cosl; + + using ::sin; + using ::sinf; + using ::sinl; + + using ::tan; + using ::tanf; + using ::tanl; + + using ::acosh; + using ::acoshf; + using ::acoshl; + + using ::asinh; + using ::asinhf; + using ::asinhl; + + using ::atanh; + using ::atanhf; + using ::atanhl; + + using ::cosh; + using ::coshf; + using ::coshl; + + using ::sinh; + using ::sinhf; + using ::sinhl; + + using ::tanh; + using ::tanhf; + using ::tanhl; + + using ::exp; + using ::expf; + using ::expl; + + using ::exp2; + using ::exp2f; + using ::exp2l; + + using ::expm1; + using ::expm1f; + using ::expm1l; + + using ::frexp; + using ::frexpf; + using ::frexpl; + + using ::ilogb; + using ::ilogbf; + using ::ilogbl; + + using ::ldexp; + using ::ldexpf; + using ::ldexpl; + + using ::log; + using ::logf; + using ::logl; + + using ::log10; + using ::log10f; + using ::log10l; + + using ::log1p; + using ::log1pf; + using ::log1pl; + + using ::log2; + using ::log2f; + using ::log2l; + + using ::logb; + using ::logbf; + using ::logbl; + + using ::modf; + using ::modff; + using ::modfl; + + using ::scalbn; + using ::scalbnf; + using ::scalbnl; + + using ::scalbln; + using ::scalblnf; + using ::scalblnl; + + using ::cbrt; + using ::cbrtf; + using ::cbrtl; + + // [c.math.abs], absolute values + using ::abs; + + using ::fabs; + using ::fabsf; + using ::fabsl; + + using ::hypot; + using ::hypotf; + using ::hypotl; + + // [c.math.hypot3], three-dimensional hypotenuse + + using ::pow; + using ::powf; + using ::powl; + + using ::sqrt; + using ::sqrtf; + using ::sqrtl; + + using ::erf; + using ::erff; + using ::erfl; + + using ::erfc; + using ::erfcf; + using ::erfcl; + + using ::lgamma; + using ::lgammaf; + using ::lgammal; + + using ::tgamma; + using ::tgammaf; + using ::tgammal; + + using ::ceil; + using ::ceilf; + using ::ceill; + + using ::floor; + using ::floorf; + using ::floorl; + + using ::nearbyint; + using ::nearbyintf; + using ::nearbyintl; + + using ::rint; + using ::rintf; + using ::rintl; + + using ::lrint; + using ::lrintf; + using ::lrintl; + + using ::llrint; + using ::llrintf; + using ::llrintl; + + using ::round; + using ::roundf; + using ::roundl; + + using ::lround; + using ::lroundf; + using ::lroundl; + + using ::llround; + using ::llroundf; + using ::llroundl; + + using ::trunc; + using ::truncf; + using ::truncl; + + using ::fmod; + using ::fmodf; + using ::fmodl; + + using ::remainder; + using ::remainderf; + using ::remainderl; + + using ::remquo; + using ::remquof; + using ::remquol; + + using ::copysign; + using ::copysignf; + using ::copysignl; + + using ::nan; + using ::nanf; + using ::nanl; + + using ::nextafter; + using ::nextafterf; + using ::nextafterl; + + using ::nexttoward; + using ::nexttowardf; + using ::nexttowardl; + + using ::fdim; + using ::fdimf; + using ::fdiml; + + using ::fmax; + using ::fmaxf; + using ::fmaxl; + + using ::fmin; + using ::fminf; + using ::fminl; + + using ::fma; + using ::fmaf; + using ::fmal; + + // [c.math.lerp], linear interpolation + // [support.c.headers.other]/1 + // ... placed within the global namespace scope, except for the functions + // described in [sf.cmath], the std::lerp function overloads ([c.math.lerp]) + // ... + + // [c.math.fpclass], classification / comparison functions + using ::fpclassify; + using ::isfinite; + using ::isgreater; + using ::isgreaterequal; + using ::isinf; + using ::isless; + using ::islessequal; + using ::islessgreater; + using ::isnan; + using ::isnormal; + using ::isunordered; + using ::signbit; + + // [sf.cmath], mathematical special functions +} // export + +// csetjmp.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::jmp_buf; + using ::longjmp; +} // export + +// csignal.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::sig_atomic_t; + + // [support.signal], signal handlers + using ::signal; + + using ::raise; +} // export + +// cstdarg.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { using ::va_list; } // export + +// cstddef.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::max_align_t; + using ::nullptr_t; + using ::ptrdiff_t; + using ::size_t; + + // [support.c.headers]/1 + // ... placed within the global namespace scope, except for ... the + // declaration of std::byte ([cstddef.syn]), and the functions and + // function templates described in [support.types.byteops]. ... + + // [support.types.byteops], byte type operations +} // export + +// cstdint.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // signed + using ::int8_t _LIBCPP_USING_IF_EXISTS; + using ::int16_t _LIBCPP_USING_IF_EXISTS; + using ::int32_t _LIBCPP_USING_IF_EXISTS; + using ::int64_t _LIBCPP_USING_IF_EXISTS; + + using ::int_fast16_t; + using ::int_fast32_t; + using ::int_fast64_t; + using ::int_fast8_t; + + using ::int_least16_t; + using ::int_least32_t; + using ::int_least64_t; + using ::int_least8_t; + + using ::intmax_t; + + using ::intptr_t _LIBCPP_USING_IF_EXISTS; + + // unsigned + using ::uint8_t _LIBCPP_USING_IF_EXISTS; + using ::uint16_t _LIBCPP_USING_IF_EXISTS; + using ::uint32_t _LIBCPP_USING_IF_EXISTS; + using ::uint64_t _LIBCPP_USING_IF_EXISTS; + + using ::uint_fast16_t; + using ::uint_fast32_t; + using ::uint_fast64_t; + using ::uint_fast8_t; + + using ::uint_least16_t; + using ::uint_least32_t; + using ::uint_least64_t; + using ::uint_least8_t; + + using ::uintmax_t; + + using ::uintptr_t _LIBCPP_USING_IF_EXISTS; +} // export + +// cstdio.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::FILE; + using ::fpos_t; + using ::size_t; + + using ::clearerr; + using ::fclose; + using ::feof; + using ::ferror; + using ::fflush; + using ::fgetc; + using ::fgetpos; + using ::fgets; + using ::fopen; + using ::fprintf; + using ::fputc; + using ::fputs; + using ::fread; + using ::freopen; + using ::fscanf; + using ::fseek; + using ::fsetpos; + using ::ftell; + using ::fwrite; + using ::getc; + using ::getchar; + using ::perror; + using ::printf; + using ::putc; + using ::putchar; + using ::puts; + using ::remove; + using ::rename; + using ::rewind; + using ::scanf; + using ::setbuf; + using ::setvbuf; + using ::snprintf; + using ::sprintf; + using ::sscanf; + using ::tmpfile; + using ::tmpnam; + using ::ungetc; + using ::vfprintf; + using ::vfscanf; + using ::vprintf; + using ::vscanf; + using ::vsnprintf; + using ::vsprintf; + using ::vsscanf; + +} // export + +// cstdlib.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::div_t; + using ::ldiv_t; + using ::lldiv_t; + using ::size_t; + + // [support.start.term], start and termination + using ::_Exit; + using ::abort; + using ::at_quick_exit _LIBCPP_USING_IF_EXISTS; + using ::atexit; + using ::exit; + using ::quick_exit _LIBCPP_USING_IF_EXISTS; + + using ::getenv; + using ::system; + + // [c.malloc], C library memory allocation + using ::aligned_alloc _LIBCPP_USING_IF_EXISTS; + using ::calloc; + using ::free; + using ::malloc; + using ::realloc; + + using ::atof; + using ::atoi; + using ::atol; + using ::atoll; + using ::strtod; + using ::strtof; + using ::strtol; + using ::strtold; + using ::strtoll; + using ::strtoul; + using ::strtoull; + + // [c.mb.wcs], multibyte / wide string and character conversion functions + using ::mblen; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using ::mbstowcs; + using ::mbtowc; + using ::wcstombs; + using ::wctomb; +#endif + // [alg.c.library], C standard library algorithms + using ::bsearch; + using ::qsort; + + // [c.math.rand], low-quality random number generation + using ::rand; + using ::srand; + + // [c.math.abs], absolute values + using ::abs; + + using ::labs; + using ::llabs; + + using ::div; + using ::ldiv; + using ::lldiv; + +} // export + +// cstring.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::size_t; + + using ::memchr; + using ::memcmp; + using ::memcpy; + using ::memmove; + using ::memset; + using ::strcat; + using ::strchr; + using ::strcmp; + using ::strcoll; + using ::strcpy; + using ::strcspn; + using ::strerror; + using ::strlen; + using ::strncat; + using ::strncmp; + using ::strncpy; + using ::strpbrk; + using ::strrchr; + using ::strspn; + using ::strstr; + using ::strtok; + using ::strxfrm; + +} // export + +// ctime.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + using ::clock_t; + using ::size_t; + using ::time_t; + + using ::timespec; + using ::tm; + + using ::asctime; + using ::clock; + using ::ctime; + using ::difftime; + using ::gmtime; + using ::localtime; + using ::mktime; + using ::strftime; + using ::time; + using ::timespec_get _LIBCPP_USING_IF_EXISTS; +} // export + +// cuchar.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { + // Note the Standard does not mark these symbols optional, but libc++'s header + // does. So this seems strictly not to be conforming. + + // mbstate_t is conditionally here, but always present in cwchar.cppm. To avoid + // conflicing declarations omit the using here. + + // size_t is conditionally here, but always present in cstddef.cppm. To avoid + // conflicing declarations omit the using here. + +#if !defined(_LIBCPP_HAS_NO_C8RTOMB_MBRTOC8) + using ::mbrtoc8 _LIBCPP_USING_IF_EXISTS; + using ::c8rtomb _LIBCPP_USING_IF_EXISTS; +#endif + using ::mbrtoc16 _LIBCPP_USING_IF_EXISTS; + using ::c16rtomb _LIBCPP_USING_IF_EXISTS; + using ::mbrtoc32 _LIBCPP_USING_IF_EXISTS; + using ::c32rtomb _LIBCPP_USING_IF_EXISTS; +} // export + +// cwchar.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using ::mbstate_t; + using ::size_t; + using ::wint_t; + + using ::tm; + + using ::btowc; + using ::fgetwc; + using ::fgetws; + using ::fputwc; + using ::fputws; + using ::fwide; + using ::fwprintf; + using ::fwscanf; + using ::getwc; + using ::getwchar; + using ::putwc; + using ::putwchar; + using ::swprintf; + using ::swscanf; + using ::ungetwc; + using ::vfwprintf; + using ::vfwscanf; + using ::vswprintf; + using ::vswscanf; + using ::vwprintf; + using ::vwscanf; + using ::wcscat; + using ::wcschr; + using ::wcscmp; + using ::wcscoll; + using ::wcscpy; + using ::wcscspn; + using ::wcsftime; + using ::wcslen; + using ::wcsncat; + using ::wcsncmp; + using ::wcsncpy; + using ::wcspbrk; + using ::wcsrchr; + using ::wcsspn; + using ::wcsstr; + using ::wcstod; + using ::wcstof; + using ::wcstok; + using ::wcstol; + using ::wcstold; + using ::wcstoll; + using ::wcstoul; + using ::wcstoull; + using ::wcsxfrm; + using ::wctob; + using ::wmemchr; + using ::wmemcmp; + using ::wmemcpy; + using ::wmemmove; + using ::wmemset; + using ::wprintf; + using ::wscanf; + + // [c.mb.wcs], multibyte / wide string and character conversion functions + using ::mbrlen; + using ::mbrtowc; + using ::mbsinit; + using ::mbsrtowcs; + using ::wcrtomb; + using ::wcsrtombs; +#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +} // export + +// cwctype.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export { +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using ::wctrans_t; + using ::wctype_t; + using ::wint_t; + + using ::iswalnum; + using ::iswalpha; + using ::iswblank; + using ::iswcntrl; + using ::iswctype; + using ::iswdigit; + using ::iswgraph; + using ::iswlower; + using ::iswprint; + using ::iswpunct; + using ::iswspace; + using ::iswupper; + using ::iswxdigit; + using ::towctrans; + using ::towlower; + using ::towupper; + using ::wctrans; + using ::wctype; +#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +} // export diff --git a/libbuild2/cc/std.cppm b/libbuild2/cc/std.cppm new file mode 100644 index 0000000..575e6a4 --- /dev/null +++ b/libbuild2/cc/std.cppm @@ -0,0 +1,6795 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// WARNING, this entire header is generated by +// utils/generate_libcxx_cppm_in.py +// DO NOT MODIFY! + +module; + +#include <__config> + +#if _LIBCPP_VERSION < 180000 +#error libc++ version 18.0.0 or later required +#endif + +// The headers of Table 24: C++ library headers [tab:headers.cpp] +// and the headers of Table 25: C++ headers for C library facilities [tab:headers.cpp.c] +#include <algorithm> +#include <any> +#include <array> +#if !defined(_LIBCPP_HAS_NO_ATOMIC_HEADER) +# include <atomic> +#endif +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <barrier> +#endif +#include <bit> +#include <bitset> +#include <cassert> +#include <cctype> +#include <cerrno> +#include <cfenv> +#include <cfloat> +#include <charconv> +#include <chrono> +#include <cinttypes> +#include <climits> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <clocale> +#endif +#include <cmath> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <codecvt> +#endif +#include <compare> +#include <complex> +#include <concepts> +#include <condition_variable> +#include <coroutine> +#include <csetjmp> +#include <csignal> +#include <cstdarg> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <cuchar> +#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +# include <cwchar> +#endif +#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +# include <cwctype> +#endif +#include <deque> +#include <exception> +#include <execution> +#include <expected> +#include <filesystem> +#include <format> +#include <forward_list> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <fstream> +#endif +#include <functional> +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <future> +#endif +#include <initializer_list> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <iomanip> +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <ios> +#endif +#include <iosfwd> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <iostream> +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <istream> +#endif +#include <iterator> +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <latch> +#endif +#include <limits> +#include <list> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <locale> +#endif +#include <map> +#include <mdspan> +#include <memory> +#include <memory_resource> +#include <mutex> +#include <new> +#include <numbers> +#include <numeric> +#include <optional> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <ostream> +#endif +#include <print> +#include <queue> +#include <random> +#include <ranges> +#include <ratio> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <regex> +#endif +#include <scoped_allocator> +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <semaphore> +#endif +#include <set> +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <shared_mutex> +#endif +#include <source_location> +#include <span> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <sstream> +#endif +#include <stack> +#include <stdexcept> +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <stop_token> +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <streambuf> +#endif +#include <string> +#include <string_view> +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <strstream> +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <syncstream> +#endif +#include <system_error> +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include <thread> +#endif +#include <tuple> +#include <type_traits> +#include <typeindex> +#include <typeinfo> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <valarray> +#include <variant> +#include <vector> +#include <version> + +#if 0 +// *** Headers not yet available *** +#if __has_include(<debugging>) +# error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<debugging>) +#if __has_include(<flat_map>) +# error "please update the header information for <flat_map> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<flat_map>) +#if __has_include(<flat_set>) +# error "please update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<flat_set>) +#if __has_include(<generator>) +# error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<generator>) +#if __has_include(<hazard_pointer>) +# error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<hazard_pointer>) +#if __has_include(<linalg>) +# error "please update the header information for <linalg> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<linalg>) +#if __has_include(<rcu>) +# error "please update the header information for <rcu> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<rcu>) +#if __has_include(<spanstream>) +# error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<spanstream>) +#if __has_include(<stacktrace>) +# error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<stacktrace>) +#if __has_include(<stdfloat>) +# error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<stdfloat>) +#if __has_include(<text_encoding>) +# error "please update the header information for <text_encoding> in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include(<text_encoding>) +#endif + +export module std; + +// algorithm.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + namespace ranges { + // [algorithms.results], algorithm result types + using std::ranges::in_found_result; + using std::ranges::in_fun_result; + using std::ranges::in_in_out_result; + using std::ranges::in_in_result; + using std::ranges::in_out_out_result; + using std::ranges::in_out_result; +#if _LIBCPP_STD_VER >= 23 + using std::ranges::in_value_result; +#endif + using std::ranges::min_max_result; + // using std::ranges::out_value_result; + } // namespace ranges + + // [alg.nonmodifying], non-modifying sequence operations + // [alg.all.of], all of + using std::all_of; + namespace ranges { + using std::ranges::all_of; + } + + // [alg.any.of], any of + using std::any_of; + namespace ranges { + using std::ranges::any_of; + } + + // [alg.none.of], none of + using std::none_of; + namespace ranges { + using std::ranges::none_of; + } + +#if _LIBCPP_STD_VER >= 23 + // [alg.contains], contains + namespace ranges { + using std::ranges::contains; +#if 0 + using std::ranges::contains_subrange; +#endif + } // namespace ranges +#endif // _LIBCPP_STD_VER >= 23 + + // [alg.foreach], for each + using std::for_each; + + namespace ranges { + using std::ranges::for_each; + using std::ranges::for_each_result; + } // namespace ranges + + using std::for_each_n; + + namespace ranges { + using std::ranges::for_each_n_result; + + using std::ranges::for_each_n; + } // namespace ranges + + // [alg.find], find + using std::find; + using std::find_if; + using std::find_if_not; + + namespace ranges { + using std::ranges::find; + using std::ranges::find_if; + using std::ranges::find_if_not; + } // namespace ranges + + namespace ranges { +#if 0 + using std::ranges::find_last; + using std::ranges::find_last_if; + using std::ranges::find_last_if_not; +#endif + } // namespace ranges + + // [alg.find.end], find end + using std::find_end; + + namespace ranges { + using std::ranges::find_end; + } + + // [alg.find.first.of], find first + using std::find_first_of; + + namespace ranges { + using std::ranges::find_first_of; + } + + // [alg.adjacent.find], adjacent find + using std::adjacent_find; + + namespace ranges { + using std::ranges::adjacent_find; + } + + // [alg.count], count + using std::count; + using std::count_if; + + namespace ranges { + using std::ranges::count; + using std::ranges::count_if; + } // namespace ranges + + // [mismatch], mismatch + using std::mismatch; + + namespace ranges { + using std::ranges::mismatch_result; + + using std::ranges::mismatch; + } // namespace ranges + + // [alg.equal], equal + using std::equal; + + namespace ranges { + using std::ranges::equal; + } + + // [alg.is.permutation], is permutation + using std::is_permutation; + + namespace ranges { + using std::ranges::is_permutation; + } + + // [alg.search], search + using std::search; + + namespace ranges { + using std::ranges::search; + } + + using std::search_n; + + namespace ranges { + using std::ranges::search_n; + } + + namespace ranges { +#if _LIBCPP_STD_VER >= 23 + // [alg.starts.with], starts with + using std::ranges::starts_with; + + // [alg.ends.with], ends with + using std::ranges::ends_with; + + // [alg.fold], fold + using std::ranges::fold_left; + using std::ranges::fold_left_with_iter; + using std::ranges::fold_left_with_iter_result; +# if 0 + using std::ranges::fold_left_first; + using std::ranges::fold_right; + using std::ranges::fold_right_last; + using std::ranges::fold_left_with_iter; + using std::ranges::fold_left_first_with_iter; + using std::ranges::fold_left_first_with_iter; +# endif +#endif // _LIBCPP_STD_VER >= 23 + } // namespace ranges + + // [alg.modifying.operations], mutating sequence operations + // [alg.copy], copy + using std::copy; + + namespace ranges { + using std::ranges::copy; + using std::ranges::copy_result; + } // namespace ranges + + using std::copy_n; + + namespace ranges { + using std::ranges::copy_n; + using std::ranges::copy_n_result; + } // namespace ranges + + using std::copy_if; + + namespace ranges { + using std::ranges::copy_if; + using std::ranges::copy_if_result; + } // namespace ranges + + using std::copy_backward; + + namespace ranges { + using std::ranges::copy_backward; + using std::ranges::copy_backward_result; + } // namespace ranges + + // [alg.move], move + using std::move; + + namespace ranges { + using std::ranges::move; + using std::ranges::move_result; + } // namespace ranges + + using std::move_backward; + + namespace ranges { + using std::ranges::move_backward; + using std::ranges::move_backward_result; + } // namespace ranges + + // [alg.swap], swap + using std::swap_ranges; + + namespace ranges { + using std::ranges::swap_ranges; + using std::ranges::swap_ranges_result; + } // namespace ranges + + using std::iter_swap; + + // [alg.transform], transform + using std::transform; + + namespace ranges { + using std::ranges::binary_transform_result; + using std::ranges::unary_transform_result; + + using std::ranges::transform; + + } // namespace ranges + + using std::replace; + using std::replace_if; + + namespace ranges { + using std::ranges::replace; + using std::ranges::replace_if; + } // namespace ranges + + using std::replace_copy; + using std::replace_copy_if; + + namespace ranges { + using std::ranges::replace_copy; + using std::ranges::replace_copy_if; + using std::ranges::replace_copy_if_result; + using std::ranges::replace_copy_result; + } // namespace ranges + + // [alg.fill], fill + using std::fill; + using std::fill_n; + + namespace ranges { + using std::ranges::fill; + using std::ranges::fill_n; + } // namespace ranges + + // [alg.generate], generate + using std::generate; + using std::generate_n; + + namespace ranges { + using std::ranges::generate; + using std::ranges::generate_n; + } // namespace ranges + + // [alg.remove], remove + using std::remove; + using std::remove_if; + + namespace ranges { + using std::ranges::remove; + using std::ranges::remove_if; + } // namespace ranges + + using std::remove_copy; + using std::remove_copy_if; + namespace ranges { + using std::ranges::remove_copy; + using std::ranges::remove_copy_if; + using std::ranges::remove_copy_if_result; + using std::ranges::remove_copy_result; + } // namespace ranges + + // [alg.unique], unique + using std::unique; + + namespace ranges { + using std::ranges::unique; + } + + using std::unique_copy; + + namespace ranges { + using std::ranges::unique_copy; + using std::ranges::unique_copy_result; + } // namespace ranges + + // [alg.reverse], reverse + using std::reverse; + + namespace ranges { + using std::ranges::reverse; + } + + using std::reverse_copy; + + namespace ranges { + using std::ranges::reverse_copy; + using std::ranges::reverse_copy_result; + } // namespace ranges + + // [alg.rotate], rotate + using std::rotate; + + namespace ranges { + using std::ranges::rotate; + } + + using std::rotate_copy; + + namespace ranges { + using std::ranges::rotate_copy; + using std::ranges::rotate_copy_result; + } // namespace ranges + + // [alg.random.sample], sample + using std::sample; + + namespace ranges { + using std::ranges::sample; + } + + // [alg.random.shuffle], shuffle + using std::shuffle; + + namespace ranges { + using std::ranges::shuffle; + } + + // [alg.shift], shift + using std::shift_left; + + namespace ranges { + // using std::ranges::shift_left; + } + + using std::shift_right; + + namespace ranges { + // using std::ranges::shift_right; + } + + // [alg.sorting], sorting and related operations + // [alg.sort], sorting + using std::sort; + + namespace ranges { + using std::ranges::sort; + } + + using std::stable_sort; + + namespace ranges { + using std::ranges::stable_sort; + } + + using std::partial_sort; + + namespace ranges { + using std::ranges::partial_sort; + } + using std::partial_sort_copy; + + namespace ranges { + using std::ranges::partial_sort_copy; + using std::ranges::partial_sort_copy_result; + } // namespace ranges + + using std::is_sorted; + using std::is_sorted_until; + + namespace ranges { + using std::ranges::is_sorted; + using std::ranges::is_sorted_until; + } // namespace ranges + + // [alg.nth.element], Nth element + using std::nth_element; + + namespace ranges { + using std::ranges::nth_element; + } + + // [alg.binary.search], binary search + using std::lower_bound; + + namespace ranges { + using std::ranges::lower_bound; + } + + using std::upper_bound; + + namespace ranges { + using std::ranges::upper_bound; + } + + using std::equal_range; + + namespace ranges { + using std::ranges::equal_range; + } + + using std::binary_search; + + namespace ranges { + using std::ranges::binary_search; + } + + // [alg.partitions], partitions + using std::is_partitioned; + + namespace ranges { + using std::ranges::is_partitioned; + } + + using std::partition; + + namespace ranges { + using std::ranges::partition; + } + + using std::stable_partition; + + namespace ranges { + using std::ranges::stable_partition; + } + + using std::partition_copy; + + namespace ranges { + using std::ranges::partition_copy; + using std::ranges::partition_copy_result; + } // namespace ranges + + using std::partition_point; + + namespace ranges { + using std::ranges::partition_point; + } + // [alg.merge], merge + using std::merge; + namespace ranges { + using std::ranges::merge; + using std::ranges::merge_result; + } // namespace ranges + + using std::inplace_merge; + + namespace ranges { + using std::ranges::inplace_merge; + } + + // [alg.set.operations], set operations + using std::includes; + namespace ranges { + using std::ranges::includes; + } + + using std::set_union; + + namespace ranges { + using std::ranges::set_union; + using std::ranges::set_union_result; + } // namespace ranges + + using std::set_intersection; + namespace ranges { + using std::ranges::set_intersection; + using std::ranges::set_intersection_result; + } // namespace ranges + + using std::set_difference; + + namespace ranges { + using std::ranges::set_difference; + using std::ranges::set_difference_result; + } // namespace ranges + + using std::set_symmetric_difference; + + namespace ranges { + using std::ranges::set_symmetric_difference_result; + + using std::ranges::set_symmetric_difference; + } // namespace ranges + + // [alg.heap.operations], heap operations + using std::push_heap; + + namespace ranges { + using std::ranges::push_heap; + } + + using std::pop_heap; + + namespace ranges { + using std::ranges::pop_heap; + } + + using std::make_heap; + + namespace ranges { + using std::ranges::make_heap; + } + + using std::sort_heap; + + namespace ranges { + using std::ranges::sort_heap; + } + + using std::is_heap; + + namespace ranges { + using std::ranges::is_heap; + } + + using std::is_heap_until; + + namespace ranges { + using std::ranges::is_heap_until; + } + + // [alg.min.max], minimum and maximum + using std::min; + + namespace ranges { + using std::ranges::min; + } + + using std::max; + + namespace ranges { + using std::ranges::max; + } + + using std::minmax; + + namespace ranges { + using std::ranges::minmax_result; + + using std::ranges::minmax; + } // namespace ranges + + using std::min_element; + + namespace ranges { + using std::ranges::min_element; + } + + using std::max_element; + + namespace ranges { + using std::ranges::max_element; + } + + using std::minmax_element; + + namespace ranges { + using std::ranges::minmax_element_result; + + using std::ranges::minmax_element; + } // namespace ranges + // [alg.clamp], bounded value + using std::clamp; + + namespace ranges { + using std::ranges::clamp; + } + + // [alg.lex.comparison], lexicographical comparison + using std::lexicographical_compare; + + namespace ranges { + using std::ranges::lexicographical_compare; + } + + // [alg.three.way], three-way comparison algorithms + using std::lexicographical_compare_three_way; + + // [alg.permutation.generators], permutations + using std::next_permutation; + + namespace ranges { + using std::ranges::next_permutation_result; + + using std::ranges::next_permutation; + } // namespace ranges + + using std::prev_permutation; + + namespace ranges { + using std::ranges::prev_permutation_result; + + using std::ranges::prev_permutation; + } // namespace ranges + +} // namespace std + +// any.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [any.bad.any.cast], class bad_any_cast + using std::bad_any_cast; + + // [any.class], class any + using std::any; + + // [any.nonmembers], non-member functions + using std::any_cast; + using std::make_any; + using std::swap; + +} // namespace std + +// array.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [array], class template array + using std::array; + + using std::operator==; + using std::operator<=>; + + // [array.special], specialized algorithms + using std::swap; + + // [array.creation], array creation functions + using std::to_array; + + // [array.tuple], tuple interface + using std::get; + using std::tuple_element; + using std::tuple_size; + +} // namespace std + +// atomic.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [atomics.order], order and consistency + using std::memory_order; + using std::memory_order_acq_rel; + using std::memory_order_acquire; + using std::memory_order_consume; + using std::memory_order_relaxed; + using std::memory_order_release; + using std::memory_order_seq_cst; + + using std::kill_dependency; + + // [atomics.ref.generic], class template atomic_ref + // [atomics.ref.pointer], partial specialization for pointers + // using std::atomic_ref; + + // [atomics.types.generic], class template atomic + using std::atomic; + + // [atomics.nonmembers], non-member functions + using std::atomic_compare_exchange_strong; + using std::atomic_compare_exchange_strong_explicit; + using std::atomic_compare_exchange_weak; + using std::atomic_compare_exchange_weak_explicit; + using std::atomic_exchange; + using std::atomic_exchange_explicit; + using std::atomic_is_lock_free; + using std::atomic_load; + using std::atomic_load_explicit; + using std::atomic_store; + using std::atomic_store_explicit; + + using std::atomic_fetch_add; + using std::atomic_fetch_add_explicit; + using std::atomic_fetch_and; + using std::atomic_fetch_and_explicit; + using std::atomic_fetch_or; + using std::atomic_fetch_or_explicit; + using std::atomic_fetch_sub; + using std::atomic_fetch_sub_explicit; + using std::atomic_fetch_xor; + using std::atomic_fetch_xor_explicit; + using std::atomic_notify_all; + using std::atomic_notify_one; + using std::atomic_wait; + using std::atomic_wait_explicit; + + // [atomics.alias], type aliases + using std::atomic_bool; + using std::atomic_char; + using std::atomic_char16_t; + using std::atomic_char32_t; +#ifndef _LIBCPP_HAS_NO_CHAR8_T + using std::atomic_char8_t; +#endif + using std::atomic_int; + using std::atomic_llong; + using std::atomic_long; + using std::atomic_schar; + using std::atomic_short; + using std::atomic_uchar; + using std::atomic_uint; + using std::atomic_ullong; + using std::atomic_ulong; + using std::atomic_ushort; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::atomic_wchar_t; +#endif + + using std::atomic_int16_t; + using std::atomic_int32_t; + using std::atomic_int64_t; + using std::atomic_int8_t; + using std::atomic_uint16_t; + using std::atomic_uint32_t; + using std::atomic_uint64_t; + using std::atomic_uint8_t; + + using std::atomic_int_least16_t; + using std::atomic_int_least32_t; + using std::atomic_int_least64_t; + using std::atomic_int_least8_t; + using std::atomic_uint_least16_t; + using std::atomic_uint_least32_t; + using std::atomic_uint_least64_t; + using std::atomic_uint_least8_t; + + using std::atomic_int_fast16_t; + using std::atomic_int_fast32_t; + using std::atomic_int_fast64_t; + using std::atomic_int_fast8_t; + using std::atomic_uint_fast16_t; + using std::atomic_uint_fast32_t; + using std::atomic_uint_fast64_t; + using std::atomic_uint_fast8_t; + + using std::atomic_intmax_t; + using std::atomic_intptr_t; + using std::atomic_ptrdiff_t; + using std::atomic_size_t; + using std::atomic_uintmax_t; + using std::atomic_uintptr_t; + + using std::atomic_signed_lock_free; + using std::atomic_unsigned_lock_free; + + // [atomics.flag], flag type and operations + using std::atomic_flag; + + using std::atomic_flag_clear; + using std::atomic_flag_clear_explicit; + using std::atomic_flag_test; + using std::atomic_flag_test_and_set; + using std::atomic_flag_test_and_set_explicit; + using std::atomic_flag_test_explicit; + + using std::atomic_flag_notify_all; + using std::atomic_flag_notify_one; + using std::atomic_flag_wait; + using std::atomic_flag_wait_explicit; + + // [atomics.fences], fences + using std::atomic_signal_fence; + using std::atomic_thread_fence; + + // [depr.atomics.nonmembers] + using std::atomic_init; + +} // namespace std + +// barrier.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + using std::barrier; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// bit.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [bit.cast], bit_cast + using std::bit_cast; + +#if _LIBCPP_STD_VER >= 23 + // [bit.byteswap], byteswap + using std::byteswap; +#endif + + // [bit.pow.two], integral powers of 2 + using std::bit_ceil; + using std::bit_floor; + using std::bit_width; + using std::has_single_bit; + + // [bit.rotate], rotating + using std::rotl; + using std::rotr; + + // [bit.count], counting + using std::countl_one; + using std::countl_zero; + using std::countr_one; + using std::countr_zero; + using std::popcount; + + // [bit.endian], endian + using std::endian; +} // namespace std + +// bitset.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::bitset; + + // [bitset.operators], bitset operators + using std::operator&; + using std::operator|; + using std::operator^; + using std::operator>>; + using std::operator<<; + + // [bitset.hash], hash support + using std::hash; + +} // namespace std + +// cassert.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // This module exports nothing. +} // namespace std + +// cctype.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::isalnum; + using std::isalpha; + using std::isblank; + using std::iscntrl; + using std::isdigit; + using std::isgraph; + using std::islower; + using std::isprint; + using std::ispunct; + using std::isspace; + using std::isupper; + using std::isxdigit; + using std::tolower; + using std::toupper; +} // namespace std + +// cerrno.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // This module exports nothing. +} // namespace std + +// cfenv.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // types + using std::fenv_t; + using std::fexcept_t; + + // functions + using std::feclearexcept; + using std::fegetexceptflag; + using std::feraiseexcept; + using std::fesetexceptflag; + using std::fetestexcept; + + using std::fegetround; + using std::fesetround; + + using std::fegetenv; + using std::feholdexcept; + using std::fesetenv; + using std::feupdateenv; + +} // namespace std + +// cfloat.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // This module exports nothing. +} // namespace std + +// charconv.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // floating-point format for primitive numerical conversion + using std::chars_format; + + // chars_format is a bitmask type. + // [bitmask.types] specified operators + using std::operator&; + using std::operator&=; + using std::operator^; + using std::operator^=; + using std::operator|; + using std::operator|=; + using std::operator~; + + // [charconv.to.chars], primitive numerical output conversion + using std::to_chars_result; + + using std::to_chars; + + // [charconv.from.chars], primitive numerical input conversion + using std::from_chars_result; + + using std::from_chars; +} // namespace std + +// chrono.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + namespace chrono { + using std::chrono::duration; + using std::chrono::time_point; + + } // namespace chrono + + using std::common_type; + + namespace chrono { + + // [time.traits], customization traits + using std::chrono::treat_as_floating_point; + using std::chrono::treat_as_floating_point_v; + + using std::chrono::duration_values; + + // using std::chrono::is_clock; + // using std::chrono::is_clock_v; + + // [time.duration.nonmember], duration arithmetic + using std::chrono::operator+; + using std::chrono::operator-; + using std::chrono::operator*; + using std::chrono::operator/; + using std::chrono::operator%; + + // [time.duration.comparisons], duration comparisons + using std::chrono::operator==; + using std::chrono::operator!=; + using std::chrono::operator<; + using std::chrono::operator>; + using std::chrono::operator<=; + using std::chrono::operator>=; + using std::chrono::operator<=>; + + // [time.duration.cast], conversions + using std::chrono::ceil; + using std::chrono::duration_cast; + using std::chrono::floor; + using std::chrono::round; + + // [time.duration.io], duration I/O +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::chrono::operator<<; +#endif + // using std::chrono::from_stream; + + // convenience typedefs + using std::chrono::days; + using std::chrono::hours; + using std::chrono::microseconds; + using std::chrono::milliseconds; + using std::chrono::minutes; + using std::chrono::months; + using std::chrono::nanoseconds; + using std::chrono::seconds; + using std::chrono::weeks; + using std::chrono::years; + + // [time.point.nonmember], time_point arithmetic + + // [time.point.comparisons], time_point comparisons + + // [time.point.cast], conversions + using std::chrono::time_point_cast; + + // [time.duration.alg], specialized algorithms + using std::chrono::abs; + + // [time.clock.system], class system_clock + using std::chrono::system_clock; + + using std::chrono::sys_days; + using std::chrono::sys_seconds; + using std::chrono::sys_time; + +#if 0 + // [time.clock.utc], class utc_clock + using std::chrono::utc_clock; + + using std::chrono::utc_seconds; + using std::chrono::utc_time; + + using std::chrono::leap_second_info; + + using std::chrono::get_leap_second_info; + // [time.clock.tai], class tai_clock + using std::chrono::tai_clock; + + using std::chrono::tai_seconds; + using std::chrono::tai_time; + + // [time.clock.gps], class gps_clock + using std::chrono::gps_clock; + + using std::chrono::gps_seconds; + using std::chrono::gps_time; +#endif + // [time.clock.file], type file_clock + using std::chrono::file_clock; + + using std::chrono::file_time; + +#ifndef _LIBCPP_HAS_NO_MONOTONIC_CLOCK + // [time.clock.steady], class steady_clock + using std::chrono::steady_clock; +#endif + + // [time.clock.hires], class high_resolution_clock + using std::chrono::high_resolution_clock; + + // [time.clock.local], local time + using std::chrono::local_days; + using std::chrono::local_seconds; + using std::chrono::local_t; + using std::chrono::local_time; + + // [time.clock.cast], time_point conversions + // using std::chrono::clock_time_conversion; + + // using std::chrono::clock_cast; + + // [time.cal.last], class last_spec + using std::chrono::last_spec; + + // [time.cal.day], class day + using std::chrono::day; + + // [time.cal.month], class month + using std::chrono::month; + + // [time.cal.year], class year + using std::chrono::year; + + // [time.cal.wd], class weekday + using std::chrono::weekday; + + // [time.cal.wdidx], class weekday_indexed + using std::chrono::weekday_indexed; + + // [time.cal.wdlast], class weekday_last + using std::chrono::weekday_last; + + // [time.cal.md], class month_day + using std::chrono::month_day; + + // [time.cal.mdlast], class month_day_last + using std::chrono::month_day_last; + + // [time.cal.mwd], class month_weekday + using std::chrono::month_weekday; + + // [time.cal.mwdlast], class month_weekday_last + using std::chrono::month_weekday_last; + + // [time.cal.ym], class year_month + using std::chrono::year_month; + + // [time.cal.ymd], class year_month_day + using std::chrono::year_month_day; + + // [time.cal.ymdlast], class year_month_day_last + using std::chrono::year_month_day_last; + + // [time.cal.ymwd], class year_month_weekday + using std::chrono::year_month_weekday; + + // [time.cal.ymwdlast], class year_month_weekday_last + using std::chrono::year_month_weekday_last; + + // [time.cal.operators], civil calendar conventional syntax operators + + // [time.hms], class template hh_mm_ss + using std::chrono::hh_mm_ss; + + // [time.12], 12/24 hour functions + using std::chrono::is_am; + using std::chrono::is_pm; + using std::chrono::make12; + using std::chrono::make24; + +#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ + !defined(_LIBCPP_HAS_NO_LOCALIZATION) + +# ifdef _LIBCPP_ENABLE_EXPERIMENTAL + // [time.zone.db], time zone database + using std::chrono::tzdb; + using std::chrono::tzdb_list; + + // [time.zone.db.access], time zone database access + // using std::chrono::current_zone; + using std::chrono::get_tzdb; + using std::chrono::get_tzdb_list; + // using std::chrono::locate_zone; + + // [time.zone.db.remote], remote time zone database support + using std::chrono::reload_tzdb; + using std::chrono::remote_version; + +# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && + // !defined(_LIBCPP_HAS_NO_LOCALIZATION) + +# if 0 + // [time.zone.exception], exception classes + using std::chrono::ambiguous_local_time; + using std::chrono::nonexistent_local_time; + + // [time.zone.info], information classes + using std::chrono::sys_info; + + // [time.zone.timezone], class time_zone + using std::chrono::choose; + using std::chrono::time_zone; + + // [time.zone.zonedtraits], class template zoned_traits + using std::chrono::zoned_traits; + + // [time.zone.zonedtime], class template zoned_time + using std::chrono::zoned_time; + + using std::chrono::zoned_seconds; + + // [time.zone.leap], leap second support + using std::chrono::leap_second; + + // [time.zone.link], class time_zone_link + using std::chrono::time_zone_link; + + // [time.format], formatting + using std::chrono::local_time_format; +# endif +#endif // _LIBCPP_ENABLE_EXPERIMENTAL + } // namespace chrono + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::formatter; +#endif // _LIBCPP_HAS_NO_LOCALIZATION + + namespace chrono { + // using std::chrono::parse; + + // calendrical constants + using std::chrono::last; + + using std::chrono::Friday; + using std::chrono::Monday; + using std::chrono::Saturday; + using std::chrono::Sunday; + using std::chrono::Thursday; + using std::chrono::Tuesday; + using std::chrono::Wednesday; + + using std::chrono::April; + using std::chrono::August; + using std::chrono::December; + using std::chrono::February; + using std::chrono::January; + using std::chrono::July; + using std::chrono::June; + using std::chrono::March; + using std::chrono::May; + using std::chrono::November; + using std::chrono::October; + using std::chrono::September; + + } // namespace chrono + +} // namespace std +export namespace std::inline literals::inline chrono_literals { + // [time.duration.literals], suffixes for duration literals + using std::literals::chrono_literals::operator""h; + using std::literals::chrono_literals::operator""min; + using std::literals::chrono_literals::operator""s; + using std::literals::chrono_literals::operator""ms; + using std::literals::chrono_literals::operator""us; + using std::literals::chrono_literals::operator""ns; + + // [using std::literals::chrono_literals::.cal.day.nonmembers], non-member functions + using std::literals::chrono_literals::operator""d; + + // [using std::literals::chrono_literals::.cal.year.nonmembers], non-member functions + using std::literals::chrono_literals::operator""y; +} // namespace std::inline literals::inline chrono_literals + +// cinttypes.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::imaxdiv_t; + + using std::imaxabs; + using std::imaxdiv; + using std::strtoimax; + using std::strtoumax; + using std::wcstoimax; + using std::wcstoumax; + + // abs is conditionally here, but always present in cmath.cppm. To avoid + // conflicing declarations omit the using here. + + // div is conditionally here, but always present in cstdlib.cppm. To avoid + // conflicing declarations omit the using here. +} // namespace std + +// climits.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // This module exports nothing. +} // namespace std + +// clocale.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::lconv; + + using std::localeconv; + using std::setlocale; +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// cmath.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + using std::double_t; + using std::float_t; + + using std::acos; + using std::acosf; + using std::acosl; + + using std::asin; + using std::asinf; + using std::asinl; + + using std::atan; + using std::atanf; + using std::atanl; + + using std::atan2; + using std::atan2f; + using std::atan2l; + + using std::cos; + using std::cosf; + using std::cosl; + + using std::sin; + using std::sinf; + using std::sinl; + + using std::tan; + using std::tanf; + using std::tanl; + + using std::acosh; + using std::acoshf; + using std::acoshl; + + using std::asinh; + using std::asinhf; + using std::asinhl; + + using std::atanh; + using std::atanhf; + using std::atanhl; + + using std::cosh; + using std::coshf; + using std::coshl; + + using std::sinh; + using std::sinhf; + using std::sinhl; + + using std::tanh; + using std::tanhf; + using std::tanhl; + + using std::exp; + using std::expf; + using std::expl; + + using std::exp2; + using std::exp2f; + using std::exp2l; + + using std::expm1; + using std::expm1f; + using std::expm1l; + + using std::frexp; + using std::frexpf; + using std::frexpl; + + using std::ilogb; + using std::ilogbf; + using std::ilogbl; + + using std::ldexp; + using std::ldexpf; + using std::ldexpl; + + using std::log; + using std::logf; + using std::logl; + + using std::log10; + using std::log10f; + using std::log10l; + + using std::log1p; + using std::log1pf; + using std::log1pl; + + using std::log2; + using std::log2f; + using std::log2l; + + using std::logb; + using std::logbf; + using std::logbl; + + using std::modf; + using std::modff; + using std::modfl; + + using std::scalbn; + using std::scalbnf; + using std::scalbnl; + + using std::scalbln; + using std::scalblnf; + using std::scalblnl; + + using std::cbrt; + using std::cbrtf; + using std::cbrtl; + + // [c.math.abs], absolute values + using std::abs; + + using std::fabs; + using std::fabsf; + using std::fabsl; + + using std::hypot; + using std::hypotf; + using std::hypotl; + + // [c.math.hypot3], three-dimensional hypotenuse + + using std::pow; + using std::powf; + using std::powl; + + using std::sqrt; + using std::sqrtf; + using std::sqrtl; + + using std::erf; + using std::erff; + using std::erfl; + + using std::erfc; + using std::erfcf; + using std::erfcl; + + using std::lgamma; + using std::lgammaf; + using std::lgammal; + + using std::tgamma; + using std::tgammaf; + using std::tgammal; + + using std::ceil; + using std::ceilf; + using std::ceill; + + using std::floor; + using std::floorf; + using std::floorl; + + using std::nearbyint; + using std::nearbyintf; + using std::nearbyintl; + + using std::rint; + using std::rintf; + using std::rintl; + + using std::lrint; + using std::lrintf; + using std::lrintl; + + using std::llrint; + using std::llrintf; + using std::llrintl; + + using std::round; + using std::roundf; + using std::roundl; + + using std::lround; + using std::lroundf; + using std::lroundl; + + using std::llround; + using std::llroundf; + using std::llroundl; + + using std::trunc; + using std::truncf; + using std::truncl; + + using std::fmod; + using std::fmodf; + using std::fmodl; + + using std::remainder; + using std::remainderf; + using std::remainderl; + + using std::remquo; + using std::remquof; + using std::remquol; + + using std::copysign; + using std::copysignf; + using std::copysignl; + + using std::nan; + using std::nanf; + using std::nanl; + + using std::nextafter; + using std::nextafterf; + using std::nextafterl; + + using std::nexttoward; + using std::nexttowardf; + using std::nexttowardl; + + using std::fdim; + using std::fdimf; + using std::fdiml; + + using std::fmax; + using std::fmaxf; + using std::fmaxl; + + using std::fmin; + using std::fminf; + using std::fminl; + + using std::fma; + using std::fmaf; + using std::fmal; + + // [c.math.lerp], linear interpolation + using std::lerp; + + // [c.math.fpclass], classification / comparison functions + using std::fpclassify; + using std::isfinite; + using std::isgreater; + using std::isgreaterequal; + using std::isinf; + using std::isless; + using std::islessequal; + using std::islessgreater; + using std::isnan; + using std::isnormal; + using std::isunordered; + using std::signbit; + + // [sf.cmath], mathematical special functions +#if 0 + // [sf.cmath.assoc.laguerre], associated Laguerre polynomials + using std::assoc_laguerre; + using std::assoc_laguerref; + using std::assoc_laguerrel; + + // [sf.cmath.assoc.legendre], associated Legendre functions + using std::assoc_legendre; + using std::assoc_legendref; + using std::assoc_legendrel; + + // [sf.cmath.beta], beta function + using std::beta; + using std::betaf; + using std::betal; + + // [sf.cmath.comp.ellint.1], complete elliptic integral of the first kind + using std::comp_ellint_1; + using std::comp_ellint_1f; + using std::comp_ellint_1l; + + // [sf.cmath.comp.ellint.2], complete elliptic integral of the second kind + using std::comp_ellint_2; + using std::comp_ellint_2f; + using std::comp_ellint_2l; + + // [sf.cmath.comp.ellint.3], complete elliptic integral of the third kind + using std::comp_ellint_3; + using std::comp_ellint_3f; + using std::comp_ellint_3l; + + // [sf.cmath.cyl.bessel.i], regular modified cylindrical Bessel functions + using std::cyl_bessel_i; + using std::cyl_bessel_if; + using std::cyl_bessel_il; + + // [sf.cmath.cyl.bessel.j], cylindrical Bessel functions of the first kind + using std::cyl_bessel_j; + using std::cyl_bessel_jf; + using std::cyl_bessel_jl; + + // [sf.cmath.cyl.bessel.k], irregular modified cylindrical Bessel functions + using std::cyl_bessel_k; + using std::cyl_bessel_kf; + using std::cyl_bessel_kl; + + // [sf.cmath.cyl.neumann], cylindrical Neumann functions + // cylindrical Bessel functions of the second kind + using std::cyl_neumann; + using std::cyl_neumannf; + using std::cyl_neumannl; + + // [sf.cmath.ellint.1], incomplete elliptic integral of the first kind + using std::ellint_1; + using std::ellint_1f; + using std::ellint_1l; + + // [sf.cmath.ellint.2], incomplete elliptic integral of the second kind + using std::ellint_2; + using std::ellint_2f; + using std::ellint_2l; + + // [sf.cmath.ellint.3], incomplete elliptic integral of the third kind + using std::ellint_3; + using std::ellint_3f; + using std::ellint_3l; + + // [sf.cmath.expint], exponential integral + using std::expint; + using std::expintf; + using std::expintl; + + // [sf.cmath.hermite], Hermite polynomials + using std::hermite; + using std::hermitef; + using std::hermitel; + + // [sf.cmath.laguerre], Laguerre polynomials + using std::laguerre; + using std::laguerref; + using std::laguerrel; + + // [sf.cmath.legendre], Legendre polynomials + using std::legendre; + using std::legendref; + using std::legendrel; + + // [sf.cmath.riemann.zeta], Riemann zeta function + using std::riemann_zeta; + using std::riemann_zetaf; + using std::riemann_zetal; + + // [sf.cmath.sph.bessel], spherical Bessel functions of the first kind + using std::sph_bessel; + using std::sph_besself; + using std::sph_bessell; + + // [sf.cmath.sph.legendre], spherical associated Legendre functions + using std::sph_legendre; + using std::sph_legendref; + using std::sph_legendrel; + + // [sf.cmath.sph.neumann], spherical Neumann functions; + // spherical Bessel functions of the second kind + using std::sph_neumann; + using std::sph_neumannf; + using std::sph_neumannl; +#endif +} // namespace std + +// codecvt.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +# if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_CODECVT) + using std::codecvt_mode; + + using std::codecvt_utf16; + using std::codecvt_utf8; + using std::codecvt_utf8_utf16; +# endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_CODECVT) +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// compare.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [cmp.categories], comparison category types + using std::partial_ordering; + using std::strong_ordering; + using std::weak_ordering; + + // named comparison functions + using std::is_eq; + using std::is_gt; + using std::is_gteq; + using std::is_lt; + using std::is_lteq; + using std::is_neq; + + // [cmp.common], common comparison category type + using std::common_comparison_category; + using std::common_comparison_category_t; + + // [cmp.concept], concept three_way_comparable + using std::three_way_comparable; + using std::three_way_comparable_with; + + // [cmp.result], result of three-way comparison + using std::compare_three_way_result; + + using std::compare_three_way_result_t; + + // [comparisons.three.way], class compare_three_way + using std::compare_three_way; + + // [cmp.alg], comparison algorithms + inline namespace __cpo { + using std::__cpo::compare_partial_order_fallback; + using std::__cpo::compare_strong_order_fallback; + using std::__cpo::compare_weak_order_fallback; + using std::__cpo::partial_order; + using std::__cpo::strong_order; + using std::__cpo::weak_order; + } // namespace __cpo + +} // namespace std + +// complex.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [complex], class template complex + using std::complex; + + // [complex.ops], operators + using std::operator+; + using std::operator-; + using std::operator*; + using std::operator/; + + using std::operator==; +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::operator>>; + using std::operator<<; +#endif // _LIBCPP_HAS_NO_LOCALIZATION + + // [complex.value.ops], values + using std::imag; + using std::real; + + using std::abs; + using std::arg; + using std::norm; + + using std::conj; + using std::polar; + using std::proj; + + // [complex.transcendentals], transcendentals + using std::acos; + using std::asin; + using std::atan; + + using std::acosh; + using std::asinh; + using std::atanh; + + using std::cos; + using std::cosh; + using std::exp; + using std::log; + using std::log10; + + using std::pow; + + using std::sin; + using std::sinh; + using std::sqrt; + using std::tan; + using std::tanh; + + // [complex.literals], complex literals + inline namespace literals { + inline namespace complex_literals { + using std::operator""il; + using std::operator""i; + using std::operator""if; + } // namespace complex_literals + } // namespace literals + +} // namespace std + +// concepts.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [concepts.lang], language-related concepts + // [concept.same], concept same_as + using std::same_as; + + // [concept.derived], concept derived_from + using std::derived_from; + + // [concept.convertible], concept convertible_to + using std::convertible_to; + + // [concept.commonref], concept common_reference_with + using std::common_reference_with; + + // [concept.common], concept common_with + using std::common_with; + + // [concepts.arithmetic], arithmetic concepts + using std::floating_point; + using std::integral; + using std::signed_integral; + using std::unsigned_integral; + + // [concept.assignable], concept assignable_from + using std::assignable_from; + + // [concept.swappable], concept swappable + namespace ranges { + inline namespace __cpo { + using std::ranges::__cpo::swap; + } + } // namespace ranges + + using std::swappable; + using std::swappable_with; + + // [concept.destructible], concept destructible + using std::destructible; + + // [concept.constructible], concept constructible_from + using std::constructible_from; + + // [concept.default.init], concept default_initializable + using std::default_initializable; + + // [concept.moveconstructible], concept move_constructible + using std::move_constructible; + + // [concept.copyconstructible], concept copy_constructible + using std::copy_constructible; + + // [concepts.compare], comparison concepts + // [concept.equalitycomparable], concept equality_comparable + using std::equality_comparable; + using std::equality_comparable_with; + + // [concept.totallyordered], concept totally_ordered + using std::totally_ordered; + using std::totally_ordered_with; + + // [concepts.object], object concepts + using std::copyable; + using std::movable; + using std::regular; + using std::semiregular; + + // [concepts.callable], callable concepts + // [concept.invocable], concept invocable + using std::invocable; + + // [concept.regularinvocable], concept regular_invocable + using std::regular_invocable; + + // [concept.predicate], concept predicate + using std::predicate; + + // [concept.relation], concept relation + using std::relation; + + // [concept.equiv], concept equivalence_relation + using std::equivalence_relation; + + // [concept.strictweakorder], concept strict_weak_order + using std::strict_weak_order; + +} // namespace std + +// condition_variable.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + // [thread.condition.condvar], class condition_variable + using std::condition_variable; + // [thread.condition.condvarany], class condition_variable_any + using std::condition_variable_any; + + // [thread.condition.nonmember], non-member functions + using std::notify_all_at_thread_exit; + + using std::cv_status; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// coroutine.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + + // [coroutine.traits], coroutine traits + using std::coroutine_traits; + + // [coroutine.handle], coroutine handle + using std::coroutine_handle; + + // [coroutine.handle.compare], comparison operators + using std::operator==; + using std::operator<=>; + + // [coroutine.handle.hash], hash support + using std::hash; + + // [coroutine.noop], no-op coroutines + using std::noop_coroutine; + using std::noop_coroutine_handle; + using std::noop_coroutine_promise; + + // [coroutine.trivial.awaitables], trivial awaitables + using std::suspend_always; + using std::suspend_never; +} // namespace std + +// csetjmp.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::jmp_buf; + using std::longjmp; +} // namespace std + +// csignal.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::sig_atomic_t; + + // [support.signal], signal handlers + using std::signal; + + using std::raise; + +} // namespace std + +// cstdarg.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::va_list; +} // namespace std + +// cstddef.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::max_align_t; + using std::nullptr_t; + using std::ptrdiff_t; + using std::size_t; + + using std::byte; + + // [support.types.byteops], byte type operations + using std::operator<<=; + using std::operator<<; + using std::operator>>=; + using std::operator>>; + using std::operator|=; + using std::operator|; + using std::operator&=; + using std::operator&; + using std::operator^=; + using std::operator^; + using std::operator~; + using std::to_integer; +} // namespace std + +// cstdint.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // signed + using std::int8_t _LIBCPP_USING_IF_EXISTS; + using std::int16_t _LIBCPP_USING_IF_EXISTS; + using std::int32_t _LIBCPP_USING_IF_EXISTS; + using std::int64_t _LIBCPP_USING_IF_EXISTS; + + using std::int_fast16_t; + using std::int_fast32_t; + using std::int_fast64_t; + using std::int_fast8_t; + + using std::int_least16_t; + using std::int_least32_t; + using std::int_least64_t; + using std::int_least8_t; + + using std::intmax_t; + + using std::intptr_t _LIBCPP_USING_IF_EXISTS; + + // unsigned + using std::uint8_t _LIBCPP_USING_IF_EXISTS; + using std::uint16_t _LIBCPP_USING_IF_EXISTS; + using std::uint32_t _LIBCPP_USING_IF_EXISTS; + using std::uint64_t _LIBCPP_USING_IF_EXISTS; + + using std::uint_fast16_t; + using std::uint_fast32_t; + using std::uint_fast64_t; + using std::uint_fast8_t; + + using std::uint_least16_t; + using std::uint_least32_t; + using std::uint_least64_t; + using std::uint_least8_t; + + using std::uintmax_t; + + using std::uintptr_t _LIBCPP_USING_IF_EXISTS; +} // namespace std + +// cstdio.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::FILE; + using std::fpos_t; + using std::size_t; + + using std::clearerr; + using std::fclose; + using std::feof; + using std::ferror; + using std::fflush; + using std::fgetc; + using std::fgetpos; + using std::fgets; + using std::fopen; + using std::fprintf; + using std::fputc; + using std::fputs; + using std::fread; + using std::freopen; + using std::fscanf; + using std::fseek; + using std::fsetpos; + using std::ftell; + using std::fwrite; + using std::getc; + using std::getchar; + using std::perror; + using std::printf; + using std::putc; + using std::putchar; + using std::puts; + using std::remove; + using std::rename; + using std::rewind; + using std::scanf; + using std::setbuf; + using std::setvbuf; + using std::snprintf; + using std::sprintf; + using std::sscanf; + using std::tmpfile; + using std::tmpnam; + using std::ungetc; + using std::vfprintf; + using std::vfscanf; + using std::vprintf; + using std::vscanf; + using std::vsnprintf; + using std::vsprintf; + using std::vsscanf; +} // namespace std + +// cstdlib.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::div_t; + using std::ldiv_t; + using std::lldiv_t; + using std::size_t; + + // [support.start.term], start and termination + using std::_Exit; + using std::abort; + using std::at_quick_exit; + using std::atexit; + using std::exit; + using std::quick_exit; + + using std::getenv; + using std::system; + + // [c.malloc], C library memory allocation + using std::aligned_alloc; + using std::calloc; + using std::free; + using std::malloc; + using std::realloc; + + using std::atof; + using std::atoi; + using std::atol; + using std::atoll; + using std::strtod; + using std::strtof; + using std::strtol; + using std::strtold; + using std::strtoll; + using std::strtoul; + using std::strtoull; + + // [c.mb.wcs], multibyte / wide string and character conversion functions + using std::mblen; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::mbstowcs; + using std::mbtowc; + using std::wcstombs; + using std::wctomb; +#endif + // [alg.c.library], C standard library algorithms + using std::bsearch; + using std::qsort; + + // [c.math.rand], low-quality random number generation + using std::rand; + using std::srand; + + // [c.math.abs], absolute values + using std::abs; + + using std::labs; + using std::llabs; + + using std::div; + using std::ldiv; + using std::lldiv; +} // namespace std + +// cstring.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::size_t; + + using std::memchr; + using std::memcmp; + using std::memcpy; + using std::memmove; + using std::memset; + using std::strcat; + using std::strchr; + using std::strcmp; + using std::strcoll; + using std::strcpy; + using std::strcspn; + using std::strerror; + using std::strlen; + using std::strncat; + using std::strncmp; + using std::strncpy; + using std::strpbrk; + using std::strrchr; + using std::strspn; + using std::strstr; + using std::strtok; + using std::strxfrm; +} // namespace std + +// ctime.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::clock_t; + using std::size_t; + using std::time_t; + + using std::timespec; + using std::tm; + + using std::asctime; + using std::clock; + using std::ctime; + using std::difftime; + using std::gmtime; + using std::localtime; + using std::mktime; + using std::strftime; + using std::time; + using std::timespec_get _LIBCPP_USING_IF_EXISTS; +} // namespace std + +// cuchar.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // Note the Standard does not mark these symbols optional, but libc++'s header + // does. So this seems strictly not to be conforming. + + // mbstate_t is conditionally here, but always present in cwchar.cppm. To avoid + // conflicing declarations omit the using here. + + // size_t is conditionally here, but always present in cstddef.cppm. To avoid + // conflicing declarations omit the using here. + +#if !defined(_LIBCPP_HAS_NO_C8RTOMB_MBRTOC8) + using std::mbrtoc8 _LIBCPP_USING_IF_EXISTS; + using std::c8rtomb _LIBCPP_USING_IF_EXISTS; +#endif + using std::mbrtoc16 _LIBCPP_USING_IF_EXISTS; + using std::c16rtomb _LIBCPP_USING_IF_EXISTS; + using std::mbrtoc32 _LIBCPP_USING_IF_EXISTS; + using std::c32rtomb _LIBCPP_USING_IF_EXISTS; +} // namespace std + +// cwchar.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::mbstate_t; + using std::size_t; + using std::wint_t; + + using std::tm; + + using std::btowc; + using std::fgetwc; + using std::fgetws; + using std::fputwc; + using std::fputws; + using std::fwide; + using std::fwprintf; + using std::fwscanf; + using std::getwc; + using std::getwchar; + using std::putwc; + using std::putwchar; + using std::swprintf; + using std::swscanf; + using std::ungetwc; + using std::vfwprintf; + using std::vfwscanf; + using std::vswprintf; + using std::vswscanf; + using std::vwprintf; + using std::vwscanf; + using std::wcscat; + using std::wcschr; + using std::wcscmp; + using std::wcscoll; + using std::wcscpy; + using std::wcscspn; + using std::wcsftime; + using std::wcslen; + using std::wcsncat; + using std::wcsncmp; + using std::wcsncpy; + using std::wcspbrk; + using std::wcsrchr; + using std::wcsspn; + using std::wcsstr; + using std::wcstod; + using std::wcstof; + using std::wcstok; + using std::wcstol; + using std::wcstold; + using std::wcstoll; + using std::wcstoul; + using std::wcstoull; + using std::wcsxfrm; + using std::wctob; + using std::wmemchr; + using std::wmemcmp; + using std::wmemcpy; + using std::wmemmove; + using std::wmemset; + using std::wprintf; + using std::wscanf; + + // [c.mb.wcs], multibyte / wide string and character conversion functions + using std::mbrlen; + using std::mbrtowc; + using std::mbsinit; + using std::mbsrtowcs; + using std::wcrtomb; + using std::wcsrtombs; +#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +} // namespace std + +// cwctype.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wctrans_t; + using std::wctype_t; + using std::wint_t; + + using std::iswalnum; + using std::iswalpha; + using std::iswblank; + using std::iswcntrl; + using std::iswctype; + using std::iswdigit; + using std::iswgraph; + using std::iswlower; + using std::iswprint; + using std::iswpunct; + using std::iswspace; + using std::iswupper; + using std::iswxdigit; + using std::towctrans; + using std::towlower; + using std::towupper; + using std::wctrans; + using std::wctype; +#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +} // namespace std + +// deque.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [deque], class template deque + using std::deque; + + using std::operator==; + using std::operator<=>; + + using std::swap; + + // [deque.erasure], erasure + using std::erase; + using std::erase_if; + + namespace pmr { + using std::pmr::deque; + } +} // namespace std + +// exception.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::bad_exception; + using std::current_exception; + using std::exception; + using std::exception_ptr; + using std::get_terminate; + using std::make_exception_ptr; + using std::nested_exception; + using std::rethrow_exception; + using std::rethrow_if_nested; + using std::set_terminate; + using std::terminate; + using std::terminate_handler; + using std::throw_with_nested; + using std::uncaught_exception; + using std::uncaught_exceptions; +} // namespace std + +// execution.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifdef _LIBCPP_ENABLE_EXPERIMENTAL +export namespace std { + // [execpol.type], execution policy type trait + using std::is_execution_policy; + using std::is_execution_policy_v; +} // namespace std + +export namespace std::execution { + // [execpol.seq], sequenced execution policy + using std::execution::sequenced_policy; + + // [execpol.par], parallel execution policy + using std::execution::parallel_policy; + + // [execpol.parunseq], parallel and unsequenced execution policy + using std::execution::parallel_unsequenced_policy; + + // [execpol.unseq], unsequenced execution policy + using std::execution::unsequenced_policy; + + // [execpol.objects], execution policy objects + using std::execution::par; + using std::execution::par_unseq; + using std::execution::seq; + using std::execution::unseq; +} // namespace std::execution +#endif // _LIBCPP_ENABLE_EXPERIMENTAL + +// expected.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if _LIBCPP_STD_VER >= 23 + // [expected.unexpected], class template unexpected + using std::unexpected; + + // [expected.bad], class template bad_expected_access + using std::bad_expected_access; + + // in-place construction of unexpected values + using std::unexpect; + using std::unexpect_t; + + // [expected.expected], class template expected + using std::expected; +#endif // _LIBCPP_STD_VER >= 23 +} // namespace std + +// filesystem.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std::filesystem { + // [fs.class.path], paths + using std::filesystem::path; + + // [fs.path.nonmember], path non-member functions + using std::filesystem::hash_value; + using std::filesystem::swap; + + // [fs.class.filesystem.error], filesystem errors + using std::filesystem::filesystem_error; + +#ifndef _LIBCPP_HAS_NO_FILESYSTEM + // [fs.class.directory.entry], directory entries + using std::filesystem::directory_entry; + + // [fs.class.directory.iterator], directory iterators + using std::filesystem::directory_iterator; + + // [fs.dir.itr.nonmembers], range access for directory iterators + using std::filesystem::begin; + using std::filesystem::end; + + // [fs.class.rec.dir.itr], recursive directory iterators + using std::filesystem::recursive_directory_iterator; +#endif // _LIBCPP_HAS_NO_FILESYSTEM + + // [fs.rec.dir.itr.nonmembers], range access for recursive directory iterators + + // [fs.class.file.status], file status + using std::filesystem::file_status; + using std::filesystem::space_info; + + // [fs.enum], enumerations + using std::filesystem::copy_options; + using std::filesystem::directory_options; + using std::filesystem::file_type; + using std::filesystem::perm_options; + using std::filesystem::perms; + + using std::filesystem::file_time_type; + + // several of these enums are a bitmask type. + // [bitmask.types] specified operators + using std::filesystem::operator&; + using std::filesystem::operator&=; + using std::filesystem::operator^; + using std::filesystem::operator^=; + using std::filesystem::operator|; + using std::filesystem::operator|=; + using std::filesystem::operator~; + +#ifndef _LIBCPP_HAS_NO_FILESYSTEM + // [fs.op.funcs], filesystem operations + using std::filesystem::absolute; + using std::filesystem::canonical; + using std::filesystem::copy; + using std::filesystem::copy_file; + using std::filesystem::copy_symlink; + using std::filesystem::create_directories; + using std::filesystem::create_directory; + using std::filesystem::create_directory_symlink; + using std::filesystem::create_hard_link; + using std::filesystem::create_symlink; + using std::filesystem::current_path; + using std::filesystem::equivalent; + using std::filesystem::exists; + using std::filesystem::file_size; + using std::filesystem::hard_link_count; + + using std::filesystem::is_block_file; + using std::filesystem::is_character_file; + using std::filesystem::is_directory; + using std::filesystem::is_empty; + using std::filesystem::is_fifo; + using std::filesystem::is_other; + using std::filesystem::is_regular_file; + using std::filesystem::is_socket; + using std::filesystem::is_symlink; + + using std::filesystem::last_write_time; + using std::filesystem::permissions; + using std::filesystem::proximate; + using std::filesystem::read_symlink; + using std::filesystem::relative; + using std::filesystem::remove; + + using std::filesystem::remove_all; + using std::filesystem::rename; + using std::filesystem::resize_file; + using std::filesystem::space; + using std::filesystem::status; + using std::filesystem::status_known; + using std::filesystem::symlink_status; + using std::filesystem::temp_directory_path; + using std::filesystem::weakly_canonical; +#endif // _LIBCPP_HAS_NO_FILESYSTEM + + // [depr.fs.path.factory] + using std::filesystem::u8path; +} // namespace std::filesystem + +// [fs.path.hash], hash support +export namespace std { + using std::hash; +} + +export namespace std::ranges { +#ifndef _LIBCPP_HAS_NO_FILESYSTEM + using std::ranges::enable_borrowed_range; + using std::ranges::enable_view; +#endif // _LIBCPP_HAS_NO_FILESYSTEM +} // namespace std::ranges + +// flat_map.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 + // [flat.map], class template flat_map + using std::flat_map; + + using std::sorted_unique; + using std::sorted_unique_t; + + using std::uses_allocator; + + // [flat.map.erasure], erasure for flat_map + using std::erase_if; + + // [flat.multimap], class template flat_multimap + using std::flat_multimap; + + using std::sorted_equivalent; + using std::sorted_equivalent_t; + + // [flat.multimap.erasure], erasure for flat_multimap +#endif +} // namespace std + +// flat_set.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 + // [flat.set], class template flat_set + using std::flat_set; + + using std::sorted_unique; + using std::sorted_unique_t; + + using std::uses_allocator; + + // [flat.set.erasure], erasure for flat_set + using std::erase_if; + + // [flat.multiset], class template flat_multiset + using std::flat_multiset; + + using std::sorted_equivalent; + using std::sorted_equivalent_t; +#endif +} // namespace std + +// format.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [format.context], class template basic_format_context + using std::basic_format_context; + using std::format_context; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wformat_context; +#endif + + // [format.args], class template basic_format_args + using std::basic_format_args; + using std::format_args; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wformat_args; +#endif + + // [format.fmt.string], class template basic_format_string + using std::basic_format_string; + using std::format_string; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wformat_string; +#endif +#if _LIBCPP_STD_VER >= 26 + using std::runtime_format; +#endif //_LIBCPP_STD_VER >= 26 + + // [format.functions], formatting functions + using std::format; + using std::format_to; + using std::vformat; + using std::vformat_to; + + using std::format_to_n; + using std::format_to_n_result; + using std::formatted_size; + + // [format.formatter], formatter + using std::formatter; + +#if _LIBCPP_STD_VER >= 23 + // [format.formattable], concept formattable + using std::formattable; +#endif + + // [format.parse.ctx], class template basic_format_parse_context + using std::basic_format_parse_context; + using std::format_parse_context; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wformat_parse_context; +#endif + +#if _LIBCPP_STD_VER >= 23 + // [format.range], formatting of ranges + // [format.range.fmtkind], variable template format_kind + using std::format_kind; + using std::range_format; + + // [format.range.formatter], class template range_formatter + using std::range_formatter; +#endif // _LIBCPP_STD_VER >= 23 + + // [format.arg], class template basic_format_arg + using std::basic_format_arg; + using std::visit_format_arg; + + // [format.arg.store], class template format-arg-store + using std::make_format_args; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::make_wformat_args; +#endif + + // [format.error], class format_error + using std::format_error; +} // namespace std + +// forward_list.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [forward.list], class template forward_list + using std::forward_list; + + using std::operator==; + using std::operator<=>; + + using std::swap; + + // [forward.list.erasure], erasure + using std::erase; + using std::erase_if; + + namespace pmr { + using std::pmr::forward_list; + } +} // namespace std + +// fstream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::basic_filebuf; + +# ifndef _LIBCPP_HAS_NO_FILESYSTEM + using std::swap; +# endif + + using std::filebuf; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wfilebuf; +# endif + + using std::basic_ifstream; + + using std::ifstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wifstream; +# endif + + using std::basic_ofstream; + + using std::ofstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wofstream; +# endif + + using std::basic_fstream; + + using std::fstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wfstream; +# endif +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// functional.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [func.invoke], invoke + using std::invoke; +#if _LIBCPP_STD_VER >= 23 + using std::invoke_r; +#endif + + // [refwrap], reference_wrapper + using std::reference_wrapper; + + using std::cref; + using std::ref; + + // [arithmetic.operations], arithmetic operations + using std::divides; + using std::minus; + using std::modulus; + using std::multiplies; + using std::negate; + using std::plus; + // [comparisons], comparisons + using std::equal_to; + using std::greater; + using std::greater_equal; + using std::less; + using std::less_equal; + using std::not_equal_to; + + // [comparisons.three.way], class compare_three_way + using std::compare_three_way; + + // [logical.operations], logical operations + using std::logical_and; + using std::logical_not; + using std::logical_or; + + // [bitwise.operations], bitwise operations + using std::bit_and; + using std::bit_not; + using std::bit_or; + using std::bit_xor; + + // [func.identity], identity + using std::identity; + + // [func.not.fn], function template not_fn + using std::not_fn; + + // [func.bind.partial], function templates bind_front and bind_back + // using std::bind_back; + using std::bind_front; + + // [func.bind], bind + using std::is_bind_expression; + using std::is_bind_expression_v; + using std::is_placeholder; + using std::is_placeholder_v; + + using std::bind; + + namespace placeholders { + // M is the implementation-defined number of placeholders + using std::placeholders::_1; + using std::placeholders::_10; + using std::placeholders::_2; + using std::placeholders::_3; + using std::placeholders::_4; + using std::placeholders::_5; + using std::placeholders::_6; + using std::placeholders::_7; + using std::placeholders::_8; + using std::placeholders::_9; + } // namespace placeholders + + // [func.memfn], member function adaptors + using std::mem_fn; + + // [func.wrap], polymorphic function wrappers + using std::bad_function_call; + + using std::function; + + using std::swap; + + using std::operator==; + + // [func.wrap.move], move only wrapper + // using std::move_only_function; + + // [func.search], searchers + using std::default_searcher; + + using std::boyer_moore_searcher; + + using std::boyer_moore_horspool_searcher; + + // [unord.hash], class template hash + using std::hash; + + namespace ranges { + // [range.cmp], concept-constrained comparisons + using std::ranges::equal_to; + using std::ranges::greater; + using std::ranges::greater_equal; + using std::ranges::less; + using std::ranges::less_equal; + using std::ranges::not_equal_to; + } // namespace ranges +} // namespace std + +// future.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + using std::future_errc; + using std::future_status; + using std::launch; + + // launch is a bitmask type. + // [bitmask.types] specified operators + using std::operator&; + using std::operator&=; + using std::operator^; + using std::operator^=; + using std::operator|; + using std::operator|=; + using std::operator~; + + // [futures.errors], error handling + using std::is_error_code_enum; + using std::make_error_code; + using std::make_error_condition; + + using std::future_category; + + // [futures.future.error], class future_error + using std::future_error; + + // [futures.promise], class template promise + using std::promise; + + using std::swap; + + using std::uses_allocator; + + // [futures.unique.future], class template future + using std::future; + + // [futures.shared.future], class template shared_future + using std::shared_future; + + // [futures.task], class template packaged_task + using std::packaged_task; + + // [futures.async], function template async + using std::async; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// generator.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 + using std::generator; +#endif +} // namespace std + +// hazard_pointer.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 +# if _LIBCPP_STD_VER >= 23 + // 4.1.3, class template hazard_pointer_obj_base + using std::hazard_pointer_obj_base; + // 4.1.4, class hazard_pointer + using std::hazard_pointer; + // 4.1.5, Construct non-empty hazard_pointer + using std::make_hazard_pointer; + // 4.1.6, Hazard pointer swap + using std::swap; +# endif // _LIBCPP_STD_VER >= 23 +#endif +} // namespace std + +// initializer_list.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::initializer_list; + + // [support.initlist.range], initializer list range access + using std::begin; + using std::end; +} // namespace std + +// iomanip.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::get_money; + using std::get_time; + using std::put_money; + using std::put_time; + using std::resetiosflags; + using std::setbase; + using std::setfill; + using std::setiosflags; + using std::setprecision; + using std::setw; + + using std::quoted; +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// ios.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::fpos; + // based on [tab:fpos.operations] + using std::operator!=; // Note not affected by P1614, seems like a bug. + using std::operator-; + using std::operator==; + + using std::streamoff; + using std::streamsize; + + using std::basic_ios; + using std::ios_base; + + // [std.ios.manip], manipulators + using std::boolalpha; + using std::noboolalpha; + + using std::noshowbase; + using std::showbase; + + using std::noshowpoint; + using std::showpoint; + + using std::noshowpos; + using std::showpos; + + using std::noskipws; + using std::skipws; + + using std::nouppercase; + using std::uppercase; + + using std::nounitbuf; + using std::unitbuf; + + // [adjustfield.manip], adjustfield + using std::internal; + using std::left; + using std::right; + + // [basefield.manip], basefield + using std::dec; + using std::hex; + using std::oct; + + // [floatfield.manip], floatfield + using std::defaultfloat; + using std::fixed; + using std::hexfloat; + using std::scientific; + + // [error.reporting], error reporting + using std::io_errc; + + using std::iostream_category; + using std::is_error_code_enum; + using std::make_error_code; + using std::make_error_condition; + + // [iosfwd.syn] + using std::ios; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wios; +# endif +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// iosfwd.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::streampos; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wstreampos; +#endif + using std::u16streampos; + using std::u32streampos; +#ifndef _LIBCPP_HAS_NO_CHAR8_T + using std::u8streampos; +#endif + +#ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM + using std::basic_osyncstream; + using std::basic_syncbuf; +#endif + + using std::istreambuf_iterator; + using std::ostreambuf_iterator; + +#ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM + using std::osyncstream; + using std::syncbuf; + +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wosyncstream; + using std::wsyncbuf; +#endif +#endif + + using std::fpos; +} // namespace std + +// iostream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::cerr; + using std::cin; + using std::clog; + using std::cout; + +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wcerr; + using std::wcin; + using std::wclog; + using std::wcout; +# endif +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// istream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::basic_istream; + + using std::istream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wistream; +# endif + + using std::basic_iostream; + + using std::iostream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wiostream; +# endif + + using std::ws; + + using std::operator>>; +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// iterator.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [iterator.assoc.types], associated types + // [incrementable.traits], incrementable traits + using std::incrementable_traits; + using std::iter_difference_t; + + using std::indirectly_readable_traits; + using std::iter_value_t; + + // [iterator.traits], iterator traits + using std::iterator_traits; + + using std::iter_reference_t; + + namespace ranges { + // [iterator.cust], customization point objects + inline namespace __cpo { + // [iterator.cust.move], ranges::iter_move + using std::ranges::__cpo::iter_move; + + // [iterator.cust.swap], ranges::iter_swap + using std::ranges::__cpo::iter_swap; + } // namespace __cpo + } // namespace ranges + + using std::iter_rvalue_reference_t; + + // [iterator.concepts], iterator concepts + // [iterator.concept.readable], concept indirectly_readable + using std::indirectly_readable; + + using std::iter_common_reference_t; + + // [iterator.concept.writable], concept indirectly_writable + using std::indirectly_writable; + + // [iterator.concept.winc], concept weakly_incrementable + using std::weakly_incrementable; + + // [iterator.concept.inc], concept incrementable + using std::incrementable; + + // [iterator.concept.iterator], concept input_or_output_iterator + using std::input_or_output_iterator; + + // [iterator.concept.sentinel], concept sentinel_for + using std::sentinel_for; + + // [iterator.concept.sizedsentinel], concept sized_sentinel_for + using std::disable_sized_sentinel_for; + + using std::sized_sentinel_for; + + // [iterator.concept.input], concept input_iterator + using std::input_iterator; + + // [iterator.concept.output], concept output_iterator + using std::output_iterator; + + // [iterator.concept.forward], concept forward_iterator + using std::forward_iterator; + + // [iterator.concept.bidir], concept bidirectional_iterator + using std::bidirectional_iterator; + + // [iterator.concept.random.access], concept random_access_iterator + using std::random_access_iterator; + + // [iterator.concept.contiguous], concept contiguous_iterator + using std::contiguous_iterator; + + // [indirectcallable], indirect callable requirements + // [indirectcallable.indirectinvocable], indirect callables + using std::indirectly_unary_invocable; + + using std::indirectly_regular_unary_invocable; + + using std::indirect_unary_predicate; + + using std::indirect_binary_predicate; + + using std::indirect_equivalence_relation; + + using std::indirect_strict_weak_order; + + using std::indirect_result_t; + + // [projected], projected + using std::projected; + + // [alg.req], common algorithm requirements + // [alg.req.ind.move], concept indirectly_movable + using std::indirectly_movable; + + using std::indirectly_movable_storable; + + // [alg.req.ind.copy], concept indirectly_copyable + using std::indirectly_copyable; + + using std::indirectly_copyable_storable; + + // [alg.req.ind.swap], concept indirectly_swappable + using std::indirectly_swappable; + + // [alg.req.ind.cmp], concept indirectly_comparable + using std::indirectly_comparable; + + // [alg.req.permutable], concept permutable + using std::permutable; + + // [alg.req.mergeable], concept mergeable + using std::mergeable; + + // [alg.req.sortable], concept sortable + using std::sortable; + + // [iterator.primitives], primitives + // [std.iterator.tags], iterator tags + using std::bidirectional_iterator_tag; + using std::contiguous_iterator_tag; + using std::forward_iterator_tag; + using std::input_iterator_tag; + using std::output_iterator_tag; + using std::random_access_iterator_tag; + + // [iterator.operations], iterator operations + using std::advance; + using std::distance; + using std::next; + using std::prev; + + // [range.iter.ops], range iterator operations + namespace ranges { + // [range.iter.op.advance], ranges::advance + using std::ranges::advance; + + // [range.iter.op.distance], ranges::distance + using std::ranges::distance; + + // [range.iter.op.next], ranges::next + using std::ranges::next; + + // [range.iter.op.prev], ranges::prev + using std::ranges::prev; + } // namespace ranges + + // [predef.iterators], predefined iterators and sentinels + // [reverse.iterators], reverse iterators + using std::reverse_iterator; + + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + using std::operator<=>; + + using std::operator-; + using std::operator+; + + using std::make_reverse_iterator; + + // using std::disable_sized_sentinel_for; + + // [insert.iterators], insert iterators + using std::back_insert_iterator; + using std::back_inserter; + + using std::front_insert_iterator; + using std::front_inserter; + + using std::insert_iterator; + using std::inserter; + + // [const.iterators], constant iterators and sentinels + // [const.iterators.alias], alias templates + // using std::const_iterator; + // using std::const_sentinel; + // using std::iter_const_reference_t; + + // [const.iterators.iterator], class template basic_const_iterator + // using std::basic_const_iterator; + + // using std::common_type; + + // using std::make_const_iterator; + + // [move.iterators], move iterators and sentinels + using std::move_iterator; + + using std::make_move_iterator; + + using std::move_sentinel; + + using std::common_iterator; + + // [default.sentinel], default sentinel + using std::default_sentinel; + using std::default_sentinel_t; + + // [iterators.counted], counted iterators + using std::counted_iterator; + + // [unreachable.sentinel], unreachable sentinel + using std::unreachable_sentinel; + using std::unreachable_sentinel_t; + + // [stream.iterators], stream iterators + using std::istream_iterator; + + using std::ostream_iterator; + + using std::istreambuf_iterator; + using std::ostreambuf_iterator; + + // [iterator.range], range access + using std::begin; + using std::cbegin; + using std::cend; + using std::crbegin; + using std::crend; + using std::end; + using std::rbegin; + using std::rend; + + using std::empty; + using std::size; + using std::ssize; + + using std::data; + + // [depr.iterator] + using std::iterator; +} // namespace std + +// latch.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + using std::latch; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// limits.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [fp.style], floating-point type properties + using std::float_denorm_style; + using std::float_round_style; + + // [numeric.limits], class template numeric_limits + using std::numeric_limits; +} // namespace std + +// list.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [list], class template list + using std::list; + + using std::operator==; + using std::operator<=>; + + using std::swap; + + // [list.erasure], erasure + using std::erase; + using std::erase_if; + + namespace pmr { + using std::pmr::list; + } +} // namespace std + +// locale.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + // [locale], locale + using std::has_facet; + using std::locale; + using std::use_facet; + + // [locale.convenience], convenience interfaces + using std::isalnum; + using std::isalpha; + using std::isblank; + using std::iscntrl; + using std::isdigit; + using std::isgraph; + using std::islower; + using std::isprint; + using std::ispunct; + using std::isspace; + using std::isupper; + using std::isxdigit; + using std::tolower; + using std::toupper; + + // [category.ctype], ctype + using std::codecvt; + using std::codecvt_base; + using std::codecvt_byname; + using std::ctype; + using std::ctype_base; + using std::ctype_byname; + + // [category.numeric], numeric + using std::num_get; + using std::num_put; + using std::numpunct; + using std::numpunct_byname; + + // [category.collate], collation + using std::collate; + using std::collate_byname; + + // [category.time], date and time + using std::time_base; + using std::time_get; + using std::time_get_byname; + using std::time_put; + using std::time_put_byname; + + // [category.monetary], money + using std::money_base; + using std::money_get; + using std::money_put; + using std::moneypunct; + using std::moneypunct_byname; + + // [category.messages], message retrieval + using std::messages; + using std::messages_base; + using std::messages_byname; + + // [depr.conversions.buffer] + using std::wbuffer_convert; + + // [depr.conversions.string] + using std::wstring_convert; +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// map.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [map], class template map + using std::map; + + using std::operator==; + using std::operator<=>; + + using std::swap; + + // [map.erasure], erasure for map + using std::erase_if; + + // [multimap], class template multimap + using std::multimap; + + namespace pmr { + using std::pmr::map; + using std::pmr::multimap; + } // namespace pmr +} // namespace std + +// mdspan.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if _LIBCPP_STD_VER >= 23 + // [mdspan.extents], class template extents + using std::extents; + + // [mdspan.extents.dextents], alias template dextents + using std::dextents; + + // [mdspan.layout], layout mapping + using std::layout_left; + using std::layout_right; + using std::layout_stride; + + // [mdspan.accessor.default], class template default_accessor + using std::default_accessor; + + // [mdspan.mdspan], class template mdspan + using std::mdspan; +#endif // _LIBCPP_STD_VER >= 23 +} // namespace std + +// memory.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [pointer.traits], pointer traits + using std::pointer_traits; + + // [pointer.conversion], pointer conversion + using std::to_address; + + // [ptr.align], pointer alignment + using std::align; + using std::assume_aligned; + + // [obj.lifetime], explicit lifetime management + // using std::start_lifetime_as; + // using std::start_lifetime_as_array; + + // [allocator.tag], allocator argument tag + using std::allocator_arg; + using std::allocator_arg_t; + + // [allocator.uses], uses_allocator + using std::uses_allocator; + + // [allocator.uses.trait], uses_allocator + using std::uses_allocator_v; + + // [allocator.uses.construction], uses-allocator construction + using std::uses_allocator_construction_args; + + using std::make_obj_using_allocator; + using std::uninitialized_construct_using_allocator; + + // [allocator.traits], allocator traits + using std::allocator_traits; + +#if _LIBCPP_STD_VER >= 23 + using std::allocation_result; + + // Note: no longer in Clang 19. + // + //using std::allocate_at_least; +#endif + + // [default.allocator], the default allocator + using std::allocator; + using std::operator==; + + // [specialized.addressof], addressof + using std::addressof; + + // [specialized.algorithms], specialized algorithms + // [special.mem.concepts], special memory concepts + + using std::uninitialized_default_construct; + using std::uninitialized_default_construct_n; + + namespace ranges { + using std::ranges::uninitialized_default_construct; + using std::ranges::uninitialized_default_construct_n; + } // namespace ranges + + using std::uninitialized_value_construct; + using std::uninitialized_value_construct_n; + + namespace ranges { + using std::ranges::uninitialized_value_construct; + using std::ranges::uninitialized_value_construct_n; + } // namespace ranges + + using std::uninitialized_copy; + using std::uninitialized_copy_n; + + namespace ranges { + using std::ranges::uninitialized_copy; + using std::ranges::uninitialized_copy_result; + + using std::ranges::uninitialized_copy_n; + using std::ranges::uninitialized_copy_n_result; + } // namespace ranges + + using std::uninitialized_move; + using std::uninitialized_move_n; + + namespace ranges { + using std::ranges::uninitialized_move; + using std::ranges::uninitialized_move_result; + + using std::ranges::uninitialized_move_n; + using std::ranges::uninitialized_move_n_result; + } // namespace ranges + + using std::uninitialized_fill; + using std::uninitialized_fill_n; + + namespace ranges { + using std::ranges::uninitialized_fill; + using std::ranges::uninitialized_fill_n; + } // namespace ranges + + // [specialized.construct], construct_at + using std::construct_at; + + namespace ranges { + using std::ranges::construct_at; + } + // [specialized.destroy], destroy + using std::destroy; + using std::destroy_at; + using std::destroy_n; + + namespace ranges { + using std::ranges::destroy; + using std::ranges::destroy_at; + using std::ranges::destroy_n; + } // namespace ranges + + // [unique.ptr], class template unique_ptr + using std::default_delete; + using std::unique_ptr; + + using std::make_unique; + using std::make_unique_for_overwrite; + + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + using std::operator<=>; + + using std::operator<<; + + // [util.smartptr.weak.bad], class bad_weak_ptr + using std::bad_weak_ptr; + + // [util.smartptr.shared], class template shared_ptr + using std::shared_ptr; + + // [util.smartptr.shared.create], shared_ptr creation + using std::allocate_shared; + using std::allocate_shared_for_overwrite; + using std::make_shared; + using std::make_shared_for_overwrite; + + // [util.smartptr.shared.spec], shared_ptr specialized algorithms + using std::swap; + + // [util.smartptr.shared.cast], shared_ptr casts + using std::const_pointer_cast; + using std::dynamic_pointer_cast; + using std::reinterpret_pointer_cast; + using std::static_pointer_cast; + +#ifndef _LIBCPP_HAS_NO_RTTI + using std::get_deleter; +#endif // _LIBCPP_HAS_NO_RTTI + + // [util.smartptr.shared.io], shared_ptr I/O + + // [util.smartptr.weak], class template weak_ptr + using std::weak_ptr; + + // [util.smartptr.weak.spec], weak_ptr specialized algorithms + + // [util.smartptr.ownerless], class template owner_less + using std::owner_less; + + // [util.smartptr.enab], class template enable_shared_from_this + using std::enable_shared_from_this; + + // [util.smartptr.hash], hash support + using std::hash; + + // [util.smartptr.atomic], atomic smart pointers + // using std::atomic; + + // [out.ptr.t], class template out_ptr_t + // using std::out_ptr_t; + + // [out.ptr], function template out_ptr + // using std::out_ptr; + + // [inout.ptr.t], class template inout_ptr_t + // using std::inout_ptr_t; + + // [inout.ptr], function template inout_ptr + // using std::inout_ptr; + +#ifndef _LIBCPP_HAS_NO_THREADS + // [depr.util.smartptr.shared.atomic] + using std::atomic_is_lock_free; + + using std::atomic_load; + using std::atomic_load_explicit; + + using std::atomic_store; + using std::atomic_store_explicit; + + using std::atomic_exchange; + using std::atomic_exchange_explicit; + + using std::atomic_compare_exchange_strong; + using std::atomic_compare_exchange_strong_explicit; + using std::atomic_compare_exchange_weak; + using std::atomic_compare_exchange_weak_explicit; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// memory_resource.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std::pmr { + // [mem.res.class], class memory_resource + using std::pmr::memory_resource; + + using std::pmr::operator==; + + // [mem.poly.allocator.class], class template polymorphic_allocator + using std::pmr::polymorphic_allocator; + + // [mem.res.global], global memory resources + using std::pmr::get_default_resource; + using std::pmr::new_delete_resource; + using std::pmr::null_memory_resource; + using std::pmr::set_default_resource; + + // [mem.res.pool], pool resource classes + using std::pmr::monotonic_buffer_resource; + using std::pmr::pool_options; + using std::pmr::synchronized_pool_resource; + using std::pmr::unsynchronized_pool_resource; +} // namespace std::pmr + +// mutex.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + // [thread.mutex.class], class mutex + using std::mutex; + // [thread.mutex.recursive], class recursive_mutex + using std::recursive_mutex; + // [thread.timedmutex.class] class timed_mutex + using std::timed_mutex; + // [thread.timedmutex.recursive], class recursive_timed_mutex + using std::recursive_timed_mutex; + + using std::adopt_lock_t; + using std::defer_lock_t; + using std::try_to_lock_t; + + using std::adopt_lock; + using std::defer_lock; + using std::try_to_lock; + + // [thread.lock], locks + using std::lock_guard; + using std::scoped_lock; + using std::unique_lock; + + using std::swap; + + // [thread.lock.algorithm], generic locking algorithms + using std::lock; + using std::try_lock; +#endif // _LIBCPP_HAS_NO_THREADS + + using std::once_flag; + + using std::call_once; +} // namespace std + +// new.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [alloc.errors], storage allocation errors + using std::bad_alloc; + using std::bad_array_new_length; + + using std::destroying_delete; + using std::destroying_delete_t; + + // global operator new control + using std::align_val_t; + + using std::nothrow; + using std::nothrow_t; + + using std::get_new_handler; + using std::new_handler; + using std::set_new_handler; + + // [ptr.launder], pointer optimization barrier + using std::launder; +#if 0 + // [hardware.interference], hardware interference size + using std::hardware_constructive_interference_size; + using std::hardware_destructive_interference_size; +#endif +} // namespace std + +export { + using ::operator new; + using ::operator delete; + using ::operator new[]; + using ::operator delete[]; +} // export + +// numbers.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std::numbers { + using std::numbers::e_v; + using std::numbers::egamma_v; + using std::numbers::inv_pi_v; + using std::numbers::inv_sqrt3_v; + using std::numbers::inv_sqrtpi_v; + using std::numbers::ln10_v; + using std::numbers::ln2_v; + using std::numbers::log10e_v; + using std::numbers::log2e_v; + using std::numbers::phi_v; + using std::numbers::pi_v; + using std::numbers::sqrt2_v; + using std::numbers::sqrt3_v; + + using std::numbers::e; + using std::numbers::egamma; + using std::numbers::inv_pi; + using std::numbers::inv_sqrt3; + using std::numbers::inv_sqrtpi; + using std::numbers::ln10; + using std::numbers::ln2; + using std::numbers::log10e; + using std::numbers::log2e; + using std::numbers::phi; + using std::numbers::pi; + using std::numbers::sqrt2; + using std::numbers::sqrt3; +} // namespace std::numbers + +// numeric.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [accumulate], accumulate + using std::accumulate; + + // [reduce], reduce + using std::reduce; + + // [inner.product], inner product + using std::inner_product; + + // [transform.reduce], transform reduce + using std::transform_reduce; + + // [partial.sum], partial sum + using std::partial_sum; + + // [exclusive.scan], exclusive scan + using std::exclusive_scan; + + // [inclusive.scan], inclusive scan + using std::inclusive_scan; + + // [transform.exclusive.scan], transform exclusive scan + using std::transform_exclusive_scan; + + // [transform.inclusive.scan], transform inclusive scan + using std::transform_inclusive_scan; + + // [adjacent.difference], adjacent difference + using std::adjacent_difference; + + // [numeric.iota], iota + using std::iota; + + namespace ranges { + // using std::ranges::iota_result; + // using std::ranges::iota; + } // namespace ranges + + // [numeric.ops.gcd], greatest common divisor + using std::gcd; + + // [numeric.ops.lcm], least common multiple + using std::lcm; + + // [numeric.ops.midpoint], midpoint + using std::midpoint; + +#if _LIBCPP_STD_VER >= 26 + // [numeric.sat], saturation arithmetic + using std::add_sat; + using std::div_sat; + using std::mul_sat; + using std::saturate_cast; + using std::sub_sat; +#endif + +} // namespace std + +// optional.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [optional.optional], class template optional + using std::optional; + + // [optional.nullopt], no-value state indicator + using std::nullopt; + using std::nullopt_t; + + // [optional.bad.access], class bad_optional_access + using std::bad_optional_access; + + // [optional.relops], relational operators + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + using std::operator<=>; + + // [optional.specalg], specialized algorithms + using std::swap; + + using std::make_optional; + + // [optional.hash], hash support + using std::hash; +} // namespace std + +// ostream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::basic_ostream; + + using std::ostream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wostream; +# endif + + using std::endl; + using std::ends; + using std::flush; + +# if 0 + using std::emit_on_flush; + using std::flush_emit; + using std::noemit_on_flush; +# endif + using std::operator<<; + +# if _LIBCPP_STD_VER >= 23 + // [ostream.formatted.print], print functions + using std::print; + using std::println; + + using std::vprint_nonunicode; +# ifndef _LIBCPP_HAS_NO_UNICODE + using std::vprint_unicode; +# endif // _LIBCPP_HAS_NO_UNICODE +# endif // _LIBCPP_STD_VER >= 23 + +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// print.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if _LIBCPP_STD_VER >= 23 + // [print.fun], print functions + using std::print; + using std::println; + + using std::vprint_nonunicode; +# ifndef _LIBCPP_HAS_NO_UNICODE + using std::vprint_unicode; +# endif // _LIBCPP_HAS_NO_UNICODE +#endif // _LIBCPP_STD_VER >= 23 +} // namespace std + +// queue.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [queue], class template queue + using std::queue; + + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + using std::operator<=>; + + using std::swap; + using std::uses_allocator; + + // [priority.queue], class template priority_queue + using std::priority_queue; +} // namespace std + +// random.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [rand.req.urng], uniform random bit generator requirements + using std::uniform_random_bit_generator; + + // [rand.eng.lcong], class template linear_congruential_engine + using std::linear_congruential_engine; + + // [rand.eng.mers], class template mersenne_twister_engine + using std::mersenne_twister_engine; + + // [rand.eng.sub], class template subtract_with_carry_engine + using std::subtract_with_carry_engine; + + // [rand.adapt.disc], class template discard_block_engine + using std::discard_block_engine; + + // [rand.adapt.ibits], class template independent_bits_engine + using std::independent_bits_engine; + + // [rand.adapt.shuf], class template shuffle_order_engine + using std::shuffle_order_engine; + + // [rand.predef], engines and engine adaptors with predefined parameters + using std::knuth_b; + using std::minstd_rand; + using std::minstd_rand0; + using std::mt19937; + using std::mt19937_64; + using std::ranlux24; + using std::ranlux24_base; + using std::ranlux48; + using std::ranlux48_base; + + using std::default_random_engine; + +#ifndef _LIBCPP_HAS_NO_RANDOM_DEVICE + // [rand.device], class random_device + using std::random_device; +#endif + + // [rand.util.seedseq], class seed_seq + using std::seed_seq; + + // [rand.util.canonical], function template generate_canonical + using std::generate_canonical; + + // [rand.dist.uni.int], class template uniform_int_distribution + using std::uniform_int_distribution; + + // [rand.dist.uni.real], class template uniform_real_distribution + using std::uniform_real_distribution; + + // [rand.dist.bern.bernoulli], class bernoulli_distribution + using std::bernoulli_distribution; + + // [rand.dist.bern.bin], class template binomial_distribution + using std::binomial_distribution; + + // [rand.dist.bern.geo], class template geometric_distribution + using std::geometric_distribution; + + // [rand.dist.bern.negbin], class template negative_binomial_distribution + using std::negative_binomial_distribution; + + // [rand.dist.pois.poisson], class template poisson_distribution + using std::poisson_distribution; + + // [rand.dist.pois.exp], class template exponential_distribution + using std::exponential_distribution; + + // [rand.dist.pois.gamma], class template gamma_distribution + using std::gamma_distribution; + + // [rand.dist.pois.weibull], class template weibull_distribution + using std::weibull_distribution; + + // [rand.dist.pois.extreme], class template extreme_value_distribution + using std::extreme_value_distribution; + + // [rand.dist.norm.normal], class template normal_distribution + using std::normal_distribution; + + // [rand.dist.norm.lognormal], class template lognormal_distribution + using std::lognormal_distribution; + + // [rand.dist.norm.chisq], class template chi_squared_distribution + using std::chi_squared_distribution; + + // [rand.dist.norm.cauchy], class template cauchy_distribution + using std::cauchy_distribution; + + // [rand.dist.norm.f], class template fisher_f_distribution + using std::fisher_f_distribution; + + // [rand.dist.norm.t], class template student_t_distribution + using std::student_t_distribution; + + // [rand.dist.samp.discrete], class template discrete_distribution + using std::discrete_distribution; + + // [rand.dist.samp.pconst], class template piecewise_constant_distribution + using std::piecewise_constant_distribution; + + // [rand.dist.samp.plinear], class template piecewise_linear_distribution + using std::piecewise_linear_distribution; +} // namespace std + +// ranges.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + namespace ranges { + inline namespace __cpo { + // [range.access], range access + using std::ranges::__cpo::begin; + using std::ranges::__cpo::cbegin; + using std::ranges::__cpo::cend; + using std::ranges::__cpo::crbegin; + using std::ranges::__cpo::crend; + using std::ranges::__cpo::end; + using std::ranges::__cpo::rbegin; + using std::ranges::__cpo::rend; + + using std::ranges::__cpo::cdata; + using std::ranges::__cpo::data; + using std::ranges::__cpo::empty; + using std::ranges::__cpo::size; + using std::ranges::__cpo::ssize; + } // namespace __cpo + + // [range.range], ranges + using std::ranges::range; + + using std::ranges::enable_borrowed_range; + + using std::ranges::borrowed_range; + + // using std::ranges::const_iterator_t; + // using std::ranges::const_sentinel_t; + using std::ranges::iterator_t; + // using std::ranges::range_const_reference_t; + using std::ranges::range_common_reference_t; + using std::ranges::range_difference_t; + using std::ranges::range_reference_t; + using std::ranges::range_rvalue_reference_t; + using std::ranges::range_size_t; + using std::ranges::range_value_t; + using std::ranges::sentinel_t; + + // [range.sized], sized ranges + using std::ranges::disable_sized_range; + using std::ranges::sized_range; + + // [range.view], views + using std::ranges::enable_view; + using std::ranges::view; + using std::ranges::view_base; + + // [range.refinements], other range refinements + using std::ranges::bidirectional_range; + using std::ranges::common_range; + // using std::ranges::constant_range; + using std::ranges::contiguous_range; + using std::ranges::forward_range; + using std::ranges::input_range; + using std::ranges::output_range; + using std::ranges::random_access_range; + using std::ranges::viewable_range; + + // [view.interface], class template view_interface + using std::ranges::view_interface; + + // [range.subrange], sub-ranges + using std::ranges::subrange; + using std::ranges::subrange_kind; + + using std::ranges::get; + } // namespace ranges + + using std::ranges::get; + + namespace ranges { + + // [range.dangling], dangling iterator handling + using std::ranges::dangling; + + // [range.elementsof], class template elements_of + // using std::ranges::elements_of; + + using std::ranges::borrowed_iterator_t; + + using std::ranges::borrowed_subrange_t; + +#if _LIBCPP_STD_VER >= 23 + // [range.utility.conv], range conversions + using std::ranges::to; +#endif + + // [range.empty], empty view + using std::ranges::empty_view; + + namespace views { + using std::ranges::views::empty; + } + + // [range.single], single view + using std::ranges::single_view; + + namespace views { + using std::ranges::views::single; + } // namespace views + + // [range.iota], iota view + using std::ranges::iota_view; + + namespace views { + using std::ranges::views::iota; + } // namespace views + +#if _LIBCPP_STD_VER >= 23 + // [range.repeat], repeat view + using std::ranges::repeat_view; + + namespace views { + using std::ranges::views::repeat; + } // namespace views +#endif // _LIBCPP_STD_VER >= 23 + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + // [range.istream], istream view + using std::ranges::basic_istream_view; + using std::ranges::istream_view; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::ranges::wistream_view; +# endif + + namespace views { + using std::ranges::views::istream; + } +#endif // _LIBCPP_HAS_NO_LOCALIZATION + + // [range.adaptor.object], range adaptor objects + // using std::ranges::range_adaptor_closure; + + // [range.all], all view + namespace views { + using std::ranges::views::all; + using std::ranges::views::all_t; + } // namespace views + + // [range.ref.view], ref view + using std::ranges::ref_view; + + // [range.owning.view], owning view + using std::ranges::owning_view; + +#if _LIBCPP_STD_VER >= 23 + // [range.as.rvalue], as rvalue view + using std::ranges::as_rvalue_view; + + namespace views { + using std::ranges::views::as_rvalue; + } // namespace views +#endif // _LIBCPP_STD_VER >= 23 + + // [range.filter], filter view + using std::ranges::filter_view; + + namespace views { + using std::ranges::views::filter; + } // namespace views + + // [range.transform], transform view + using std::ranges::transform_view; + + namespace views { + using std::ranges::views::transform; + } // namespace views + + // [range.take], take view + using std::ranges::take_view; + + namespace views { + using std::ranges::views::take; + } // namespace views + + // [range.take.while], take while view + using std::ranges::take_while_view; + + namespace views { + using std::ranges::views::take_while; + } // namespace views + + // [range.drop], drop view + using std::ranges::drop_view; + + namespace views { + using std::ranges::views::drop; + } // namespace views + + // [range.drop.while], drop while view + using std::ranges::drop_while_view; + + namespace views { + using std::ranges::views::drop_while; + } // namespace views + + using std::ranges::join_view; + + namespace views { + using std::ranges::views::join; + } // namespace views +#if 0 + using std::ranges::join_with_view; + + namespace views { + using std::ranges::views::join_with; + } // namespace views +#endif + using std::ranges::lazy_split_view; + + // [range.split], split view + using std::ranges::split_view; + + namespace views { + using std::ranges::views::lazy_split; + using std::ranges::views::split; + } // namespace views + + // [range.counted], counted view + namespace views { + using std::ranges::views::counted; + } // namespace views + + // [range.common], common view + using std::ranges::common_view; + + namespace views { + using std::ranges::views::common; + } // namespace views + + // [range.reverse], reverse view + using std::ranges::reverse_view; + + namespace views { + using std::ranges::views::reverse; + } // namespace views + + // [range.as.const], as const view +#if 0 + using std::ranges::as_const_view; + + namespace views { + using std::ranges::views::as_const; + } // namespace views +#endif + // [range.elements], elements view + using std::ranges::elements_view; + + using std::ranges::keys_view; + using std::ranges::values_view; + + namespace views { + using std::ranges::views::elements; + using std::ranges::views::keys; + using std::ranges::views::values; + } // namespace views + +#if _LIBCPP_STD_VER >= 23 + // [range.zip], zip view + using std::ranges::zip_view; + + namespace views { + using std::ranges::views::zip; + } // namespace views +#endif // _LIBCPP_STD_VER >= 23 + +#if 0 + // [range.zip.transform], zip transform view + using std::ranges::zip_transform_view; + + namespace views { + using std::ranges::views::zip_transform; + } + + using std::ranges::adjacent_view; + + namespace views { + using std::ranges::views::adjacent; + using std::ranges::views::pairwise; + } // namespace views + + using std::ranges::adjacent_transform_view; + + namespace views { + using std::ranges::views::adjacent_transform; + using std::ranges::views::pairwise_transform; + } // namespace views + + using std::ranges::chunk_view; + + using std::ranges::chunk_view<V>; + + namespace views { + using std::ranges::views::chunk; + } + + using std::ranges::slide_view; + + namespace views { + using std::ranges::views::slide; + } +#endif + +#if _LIBCPP_STD_VER >= 23 + // [range.chunk.by], chunk by view + using std::ranges::chunk_by_view; + + namespace views { + using std::ranges::views::chunk_by; + } +#endif // _LIBCPP_STD_VER >= 23 + +#if 0 + // [range.stride], stride view + using std::ranges::stride_view; + + namespace views { + using std::ranges::views::stride; + } + + using std::ranges::cartesian_product_view; + + namespace views { + using std::ranges::views::cartesian_product; + } +#endif + } // namespace ranges + + namespace views = ranges::views; + + using std::tuple_element; + using std::tuple_size; + +#if _LIBCPP_STD_VER >= 23 + using std::from_range; + using std::from_range_t; +#endif // _LIBCPP_STD_VER >= 23 +} // namespace std + +// ratio.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [ratio.ratio], class template ratio + using std::ratio; + + // [ratio.arithmetic], ratio arithmetic + using std::ratio_add; + using std::ratio_divide; + using std::ratio_multiply; + using std::ratio_subtract; + + // [ratio.comparison], ratio comparison + using std::ratio_equal; + using std::ratio_greater; + using std::ratio_greater_equal; + using std::ratio_less; + using std::ratio_less_equal; + using std::ratio_not_equal; + + using std::ratio_equal_v; + using std::ratio_greater_equal_v; + using std::ratio_greater_v; + using std::ratio_less_equal_v; + using std::ratio_less_v; + using std::ratio_not_equal_v; + + // [ratio.si], convenience SI typedefs + using std::atto; + using std::centi; + using std::deca; + using std::deci; + using std::exa; + using std::femto; + using std::giga; + using std::hecto; + using std::kilo; + using std::mega; + using std::micro; + using std::milli; + using std::nano; + using std::peta; + using std::pico; + using std::tera; + + // These are not supported by libc++, due to the range of intmax_t + // using std::yocto; + // using std::yotta; + // using std::zepto; + // using std::zetta +} // namespace std + +// rcu.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 +# if _LIBCPP_STD_VER >= 23 + // 2.2.3, class template rcu_obj_base using std::rcu_obj_base; + // 2.2.4, class rcu_domain + using std::rcu_domain; + using std::rcu_default_domain(); + using std::rcu_barrier; + using std::rcu_retire; + using std::rcu_synchronize; +# endif // _LIBCPP_STD_VER >= 23 +#endif +} // namespace std + +// regex.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + // [re.const], regex constants + namespace regex_constants { + using std::regex_constants::error_type; + using std::regex_constants::match_flag_type; + using std::regex_constants::syntax_option_type; + + // regex_constants is a bitmask type. + // [bitmask.types] specified operators + using std::regex_constants::operator&; + using std::regex_constants::operator&=; + using std::regex_constants::operator^; + using std::regex_constants::operator^=; + using std::regex_constants::operator|; + using std::regex_constants::operator|=; + using std::regex_constants::operator~; + + } // namespace regex_constants + + // [re.badexp], class regex_error + using std::regex_error; + + // [re.traits], class template regex_traits + using std::regex_traits; + + // [re.regex], class template basic_regex + using std::basic_regex; + + using std::regex; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wregex; +# endif + + // [re.regex.swap], basic_regex swap + using std::swap; + + // [re.submatch], class template sub_match + using std::sub_match; + + using std::csub_match; + using std::ssub_match; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wcsub_match; + using std::wssub_match; +# endif + + // [re.submatch.op], sub_match non-member operators + using std::operator==; + using std::operator<=>; + + using std::operator<<; + + // [re.results], class template match_results + using std::match_results; + + using std::cmatch; + using std::smatch; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wcmatch; + using std::wsmatch; +# endif + + // match_results comparisons + + // [re.results.swap], match_results swap + + // [re.alg.match], function template regex_match + using std::regex_match; + + // [re.alg.search], function template regex_search + using std::regex_search; + + // [re.alg.replace], function template regex_replace + using std::regex_replace; + + // [re.regiter], class template regex_iterator + using std::regex_iterator; + + using std::cregex_iterator; + using std::sregex_iterator; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wcregex_iterator; + using std::wsregex_iterator; +# endif + + // [re.tokiter], class template regex_token_iterator + using std::regex_token_iterator; + + using std::cregex_token_iterator; + using std::sregex_token_iterator; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wcregex_token_iterator; + using std::wsregex_token_iterator; +# endif + + namespace pmr { + using std::pmr::match_results; + + using std::pmr::cmatch; + using std::pmr::smatch; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::pmr::wcmatch; + using std::pmr::wsmatch; +# endif + } // namespace pmr +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// scoped_allocator.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // class template scoped_allocator_adaptor + using std::scoped_allocator_adaptor; + + // [scoped.adaptor.operators], scoped allocator operators + using std::operator==; + +} // namespace std + +// semaphore.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + // [thread.sema.cnt], class template counting_semaphore + using std::counting_semaphore; + + using std::binary_semaphore; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// set.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [set], class template set + using std::set; + + using std::operator==; + using std::operator<=>; + + using std::swap; + + // [set.erasure], erasure for set + using std::erase_if; + + // [multiset], class template multiset + using std::multiset; + + namespace pmr { + using std::pmr::multiset; + using std::pmr::set; + } // namespace pmr +} // namespace std + +// shared_mutex.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + // [thread.sharedmutex.class], class shared_mutex + using std::shared_mutex; + // [thread.sharedtimedmutex.class], class shared_timed_mutex + using std::shared_timed_mutex; + // [thread.lock.shared], class template shared_lock + using std::shared_lock; + using std::swap; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// source_location.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::source_location; +} // namespace std + +// span.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // constants + using std::dynamic_extent; + + // [views.span], class template span + using std::span; + + namespace ranges { + using std::ranges::enable_borrowed_range; + using std::ranges::enable_view; + } // namespace ranges + + // [span.objectrep], views of object representation + using std::as_bytes; + + using std::as_writable_bytes; +} // namespace std + +// spanstream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 + using std::basic_spanbuf; + + using std::swap; + + using std::spanbuf; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wspanbuf; +# endif + + using std::basic_ispanstream; + + using std::ispanstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wispanstream; +# endif + + using std::basic_ospanstream; + + using std::ospanstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wospanstream; +# endif + + using std::basic_spanstream; + + using std::spanstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wspanstream; +# endif +#endif +} // namespace std + +// sstream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::basic_stringbuf; + + using std::swap; + + using std::stringbuf; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wstringbuf; +# endif + + using std::basic_istringstream; + + using std::istringstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wistringstream; +# endif + + using std::basic_ostringstream; + + using std::ostringstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wostringstream; +# endif + + using std::basic_stringstream; + + using std::stringstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wstringstream; +# endif +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// stack.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [stack], class template stack + using std::stack; + + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + using std::operator<=>; + + using std::swap; + using std::uses_allocator; +} // namespace std + +// stacktrace.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 + // [stacktrace.entry], class stacktrace_entry + using std::stacktrace_entry; + + // [stacktrace.basic], class template basic_stacktrace + using std::basic_stacktrace; + + // basic_stacktrace typedef-names + using std::stacktrace; + + // [stacktrace.basic.nonmem], non-member functions + using std::swap; + + using std::to_string; + + using std::operator<<; + + namespace pmr { + using std::pmr::stacktrace; + } + + // [stacktrace.basic.hash], hash support + using std::hash; +#endif +} // namespace std + +// stdexcept.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::domain_error; + using std::invalid_argument; + using std::length_error; + using std::logic_error; + using std::out_of_range; + using std::overflow_error; + using std::range_error; + using std::runtime_error; + using std::underflow_error; +} // namespace std + +// stdfloat.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if defined(__STDCPP_FLOAT16_T__) + using std::float16_t; +#endif +#if defined(__STDCPP_FLOAT32_T__) + using std::float32_t; +#endif +#if defined(__STDCPP_FLOAT64_T__) + using std::float64_t; +#endif +#if defined(__STDCPP_FLOAT128_T__) + using std::float128_t; +#endif +#if defined(__STDCPP_BFLOAT16_T__) + using std::bfloat16_t; +#endif +} // namespace std + +// stop_token.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS +# ifdef _LIBCPP_ENABLE_EXPERIMENTAL + // [stoptoken], class stop_token + using std::stop_token; + + // [stopsource], class stop_source + using std::stop_source; + + // no-shared-stop-state indicator + using std::nostopstate; + using std::nostopstate_t; + + // [stopcallback], class template stop_callback + using std::stop_callback; +# endif // _LIBCPP_ENABLE_EXPERIMENTAL +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// streambuf.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::basic_streambuf; + using std::streambuf; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wstreambuf; +# endif +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// string.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [char.traits], character traits + using std::char_traits; + + // [basic.string], basic_string + using std::basic_string; + + using std::operator+; + using std::operator==; + using std::operator<=>; + + // [string.special], swap + using std::swap; + + // [string.io], inserters and extractors + using std::operator>>; + using std::operator<<; + using std::getline; + + // [string.erasure], erasure + using std::erase; + using std::erase_if; + + // basic_string typedef-names + using std::string; + using std::u16string; + using std::u32string; +#ifndef _LIBCPP_HAS_NO_CHAR8_T + using std::u8string; +#endif +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wstring; +#endif + + // [string.conversions], numeric conversions + using std::stod; + using std::stof; + using std::stoi; + using std::stol; + using std::stold; + using std::stoll; + using std::stoul; + using std::stoull; + using std::to_string; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::to_wstring; +#endif + + namespace pmr { + using std::pmr::basic_string; + using std::pmr::string; + using std::pmr::u16string; + using std::pmr::u32string; +#ifndef _LIBCPP_HAS_NO_CHAR8_T + using std::pmr::u8string; +#endif +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::pmr::wstring; +#endif + } // namespace pmr + + // [basic.string.hash], hash support + using std::hash; + + inline namespace literals { + inline namespace string_literals { + // [basic.string.literals], suffix for basic_string literals + using std::literals::string_literals::operator""s; + } // namespace string_literals + } // namespace literals +} // namespace std + +// string_view.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [string.view.template], class template basic_string_view + using std::basic_string_view; + + namespace ranges { + using std::ranges::enable_borrowed_range; + using std::ranges::enable_view; + } // namespace ranges + + // [string.view.comparison], non-member comparison functions + using std::operator==; + using std::operator<=>; + + // [string.view.io], inserters and extractors + using std::operator<<; + + // basic_string_view typedef-names + using std::string_view; + using std::u16string_view; + using std::u32string_view; +#ifndef _LIBCPP_HAS_NO_CHAR8_T + using std::u8string_view; +#endif +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wstring_view; +#endif + + // [string.view.hash], hash support + using std::hash; + + inline namespace literals { + inline namespace string_view_literals { + // [string.view.literals], suffix for basic_string_view literals + using std::literals::string_view_literals::operator""sv; + } // namespace string_view_literals + } // namespace literals +} // namespace std + +// strstream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::istrstream; + using std::ostrstream; + using std::strstream; + using std::strstreambuf; +#endif // _LIBCPP_HAS_NO_LOCALIZATION +} // namespace std + +// syncstream.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) + using std::basic_syncbuf; + + // [syncstream.syncbuf.special], specialized algorithms + using std::swap; + + using std::syncbuf; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wsyncbuf; +# endif + using std::basic_osyncstream; + + using std::osyncstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wosyncstream; +# endif +#endif // !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) +} // namespace std + +// system_error.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::error_category; + using std::generic_category; + using std::system_category; + + using std::error_code; + using std::error_condition; + using std::system_error; + + using std::is_error_code_enum; + using std::is_error_condition_enum; + + using std::errc; + + // [syserr.errcode.nonmembers], non-member functions + using std::make_error_code; + + using std::operator<<; + + // [syserr.errcondition.nonmembers], non-member functions + using std::make_error_condition; + + // [syserr.compare], comparison operator functions + using std::operator==; + using std::operator<=>; + + // [syserr.hash], hash support + using std::hash; + + // [syserr], system error support + using std::is_error_code_enum_v; + using std::is_error_condition_enum_v; +} // namespace std + +// text_encoding.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#if 0 +# if _LIBCPP_STD_VER >= 23 + using std::text_encoding; + + // hash support + using std::hash; +# endif // _LIBCPP_STD_VER >= 23 +#endif +} // namespace std + +// thread.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { +#ifndef _LIBCPP_HAS_NO_THREADS + // [thread.thread.class], class thread + using std::thread; + + using std::swap; + + // [thread.jthread.class], class jthread +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) + using std::jthread; +# endif + + // [thread.thread.this], namespace this_thread + namespace this_thread { + using std::this_thread::get_id; + + using std::this_thread::sleep_for; + using std::this_thread::sleep_until; + using std::this_thread::yield; + } // namespace this_thread + + // [thread.thread.id] + using std::operator==; + using std::operator<=>; +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + using std::operator<<; +# endif // _LIBCPP_HAS_NO_LOCALIZATION + +# if _LIBCPP_STD_VER >= 23 + using std::formatter; +# endif + + using std::hash; +#endif // _LIBCPP_HAS_NO_THREADS +} // namespace std + +// tuple.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [tuple.tuple], class template tuple + using std::tuple; + + // [tuple.like], concept tuple-like + +#if _LIBCPP_STD_VER >= 23 + // [tuple.common.ref], common_reference related specializations + using std::basic_common_reference; + using std::common_type; +#endif + + // [tuple.creation], tuple creation functions + using std::ignore; + + using std::forward_as_tuple; + using std::make_tuple; + using std::tie; + using std::tuple_cat; + + // [tuple.apply], calling a function with a tuple of arguments + using std::apply; + + using std::make_from_tuple; + + // [tuple.helper], tuple helper classes + using std::tuple_element; + using std::tuple_size; + + // [tuple.elem], element access + using std::get; + using std::tuple_element_t; + + // [tuple.rel], relational operators + using std::operator==; + using std::operator<=>; + + // [tuple.traits], allocator-related traits + using std::uses_allocator; + + // [tuple.special], specialized algorithms + using std::swap; + + // [tuple.helper], tuple helper classes + using std::tuple_size_v; +} // namespace std + +// type_traits.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [meta.help], helper class + using std::integral_constant; + + using std::bool_constant; + using std::false_type; + using std::true_type; + + // [meta.unary.cat], primary type categories + using std::is_array; + using std::is_class; + using std::is_enum; + using std::is_floating_point; + using std::is_function; + using std::is_integral; + using std::is_lvalue_reference; + using std::is_member_function_pointer; + using std::is_member_object_pointer; + using std::is_null_pointer; + using std::is_pointer; + using std::is_rvalue_reference; + using std::is_union; + using std::is_void; + + // [meta.unary.comp], composite type categories + using std::is_arithmetic; + using std::is_compound; + using std::is_fundamental; + using std::is_member_pointer; + using std::is_object; + using std::is_reference; + using std::is_scalar; + + // [meta.unary.prop], type properties + using std::is_abstract; + using std::is_aggregate; + using std::is_const; + using std::is_empty; + using std::is_final; + using std::is_polymorphic; + using std::is_standard_layout; + using std::is_trivial; + using std::is_trivially_copyable; + using std::is_volatile; + + using std::is_bounded_array; +#if _LIBCPP_STD_VER >= 23 + using std::is_scoped_enum; +#endif + using std::is_signed; + using std::is_unbounded_array; + using std::is_unsigned; + + using std::is_constructible; + using std::is_copy_constructible; + using std::is_default_constructible; + using std::is_move_constructible; + + using std::is_assignable; + using std::is_copy_assignable; + using std::is_move_assignable; + + using std::is_swappable; + using std::is_swappable_with; + + using std::is_destructible; + + using std::is_trivially_constructible; + using std::is_trivially_copy_constructible; + using std::is_trivially_default_constructible; + using std::is_trivially_move_constructible; + + using std::is_trivially_assignable; + using std::is_trivially_copy_assignable; + using std::is_trivially_destructible; + using std::is_trivially_move_assignable; + + using std::is_nothrow_constructible; + using std::is_nothrow_copy_constructible; + using std::is_nothrow_default_constructible; + using std::is_nothrow_move_constructible; + + using std::is_nothrow_assignable; + using std::is_nothrow_copy_assignable; + using std::is_nothrow_move_assignable; + + using std::is_nothrow_swappable; + using std::is_nothrow_swappable_with; + + using std::is_nothrow_destructible; + + // using std::is_implicit_lifetime; + + using std::has_virtual_destructor; + + using std::has_unique_object_representations; + + // using std::reference_constructs_from_temporary; + // using std::reference_converts_from_temporary; + + // [meta.unary.prop.query], type property queries + using std::alignment_of; + using std::extent; + using std::rank; + + // [meta.rel], type relations + using std::is_base_of; + using std::is_convertible; + // using std::is_layout_compatible; + using std::is_nothrow_convertible; + // using std::is_pointer_interconvertible_base_of; + using std::is_same; + + using std::is_invocable; + using std::is_invocable_r; + + using std::is_nothrow_invocable; + using std::is_nothrow_invocable_r; + + // [meta.trans.cv], const-volatile modifications + using std::add_const; + using std::add_cv; + using std::add_volatile; + using std::remove_const; + using std::remove_cv; + using std::remove_volatile; + + using std::add_const_t; + using std::add_cv_t; + using std::add_volatile_t; + using std::remove_const_t; + using std::remove_cv_t; + using std::remove_volatile_t; + + // [meta.trans.ref], reference modifications + using std::add_lvalue_reference; + using std::add_rvalue_reference; + using std::remove_reference; + + using std::add_lvalue_reference_t; + using std::add_rvalue_reference_t; + using std::remove_reference_t; + + // [meta.trans.sign], sign modifications + using std::make_signed; + using std::make_unsigned; + + using std::make_signed_t; + using std::make_unsigned_t; + + // [meta.trans.arr], array modifications + using std::remove_all_extents; + using std::remove_extent; + + using std::remove_all_extents_t; + using std::remove_extent_t; + + // [meta.trans.ptr], pointer modifications + using std::add_pointer; + using std::remove_pointer; + + using std::add_pointer_t; + using std::remove_pointer_t; + + // [meta.trans.other], other transformations + using std::basic_common_reference; + using std::common_reference; + using std::common_type; + using std::conditional; + using std::decay; + using std::enable_if; + using std::invoke_result; + using std::remove_cvref; + using std::type_identity; + using std::underlying_type; + using std::unwrap_ref_decay; + using std::unwrap_reference; + + using std::common_reference_t; + using std::common_type_t; + using std::conditional_t; + using std::decay_t; + using std::enable_if_t; + using std::invoke_result_t; + using std::remove_cvref_t; + using std::type_identity_t; + using std::underlying_type_t; + using std::unwrap_ref_decay_t; + using std::unwrap_reference_t; + using std::void_t; + + // [meta.logical], logical operator traits + using std::conjunction; + using std::disjunction; + using std::negation; + + // [meta.unary.cat], primary type categories + using std::is_array_v; + using std::is_class_v; + using std::is_enum_v; + using std::is_floating_point_v; + using std::is_function_v; + using std::is_integral_v; + using std::is_lvalue_reference_v; + using std::is_member_function_pointer_v; + using std::is_member_object_pointer_v; + using std::is_null_pointer_v; + using std::is_pointer_v; + using std::is_rvalue_reference_v; + using std::is_union_v; + using std::is_void_v; + + // [meta.unary.comp], composite type categories + using std::is_arithmetic_v; + using std::is_compound_v; + using std::is_fundamental_v; + using std::is_member_pointer_v; + using std::is_object_v; + using std::is_reference_v; + using std::is_scalar_v; + + // [meta.unary.prop], type properties + using std::has_unique_object_representations_v; + using std::has_virtual_destructor_v; + using std::is_abstract_v; + using std::is_aggregate_v; + using std::is_assignable_v; + using std::is_bounded_array_v; + using std::is_const_v; + using std::is_constructible_v; + using std::is_copy_assignable_v; + using std::is_copy_constructible_v; + using std::is_default_constructible_v; + using std::is_destructible_v; + using std::is_empty_v; + using std::is_final_v; + // using std::is_implicit_lifetime_v; + using std::is_move_assignable_v; + using std::is_move_constructible_v; + using std::is_nothrow_assignable_v; + using std::is_nothrow_constructible_v; + using std::is_nothrow_copy_assignable_v; + using std::is_nothrow_copy_constructible_v; + using std::is_nothrow_default_constructible_v; + using std::is_nothrow_destructible_v; + using std::is_nothrow_move_assignable_v; + using std::is_nothrow_move_constructible_v; + using std::is_nothrow_swappable_v; + using std::is_nothrow_swappable_with_v; + using std::is_polymorphic_v; +#if _LIBCPP_STD_VER >= 23 + using std::is_scoped_enum_v; +#endif + using std::is_signed_v; + using std::is_standard_layout_v; + using std::is_swappable_v; + using std::is_swappable_with_v; + using std::is_trivial_v; + using std::is_trivially_assignable_v; + using std::is_trivially_constructible_v; + using std::is_trivially_copy_assignable_v; + using std::is_trivially_copy_constructible_v; + using std::is_trivially_copyable_v; + using std::is_trivially_default_constructible_v; + using std::is_trivially_destructible_v; + using std::is_trivially_move_assignable_v; + using std::is_trivially_move_constructible_v; + using std::is_unbounded_array_v; + using std::is_unsigned_v; + using std::is_volatile_v; + // using std::reference_constructs_from_temporary_v; + // using std::reference_converts_from_temporary_v; + + // [meta.unary.prop.query], type property queries + using std::alignment_of_v; + using std::extent_v; + using std::rank_v; + + // [meta.rel], type relations + using std::is_base_of_v; + using std::is_convertible_v; + using std::is_invocable_r_v; + using std::is_invocable_v; + // using std::is_layout_compatible_v; + using std::is_nothrow_convertible_v; + using std::is_nothrow_invocable_r_v; + using std::is_nothrow_invocable_v; + // using std::is_pointer_interconvertible_base_of_v; + using std::is_same_v; + + // [meta.logical], logical operator traits + using std::conjunction_v; + using std::disjunction_v; + using std::negation_v; + + // [meta.member], member relationships + // using std::is_corresponding_member; + // using std::is_pointer_interconvertible_with_class; + + // [meta.const.eval], constant evaluation context + using std::is_constant_evaluated; + + // [depr.meta.types] + using std::aligned_storage; + using std::aligned_storage_t; + using std::aligned_union; + using std::aligned_union_t; + using std::is_pod; + using std::is_pod_v; +} // namespace std + +// typeindex.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::hash; + using std::type_index; +} // namespace std + +// typeinfo.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::bad_cast; + using std::bad_typeid; + using std::type_info; +} // namespace std + +// unordered_map.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [unord.map], class template unordered_map + using std::unordered_map; + + // [unord.multimap], class template unordered_multimap + using std::unordered_multimap; + + using std::operator==; + + using std::swap; + + // [unord.map.erasure], erasure for unordered_map + using std::erase_if; + + namespace pmr { + using std::pmr::unordered_map; + using std::pmr::unordered_multimap; + } // namespace pmr +} // namespace std + +// unordered_set.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [unord.set], class template unordered_set + using std::unordered_set; + + // [unord.multiset], class template unordered_multiset + using std::unordered_multiset; + + using std::operator==; + + using std::swap; + + // [unord.set.erasure], erasure for unordered_set + using std::erase_if; + + namespace pmr { + using std::pmr::unordered_multiset; + using std::pmr::unordered_set; + } // namespace pmr +} // namespace std + +// utility.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [utility.swap], swap + using std::swap; + + // [utility.exchange], exchange + using std::exchange; + + // [forward], forward/move + using std::forward; +#if _LIBCPP_STD_VER >= 23 + using std::forward_like; +#endif + using std::move; + using std::move_if_noexcept; + + // [utility.as.const], as_const + using std::as_const; + + // [declval], declval + using std::declval; + + // [utility.intcmp], integer comparison functions + using std::cmp_equal; + using std::cmp_not_equal; + + using std::cmp_greater; + using std::cmp_greater_equal; + using std::cmp_less; + using std::cmp_less_equal; + + using std::in_range; + +#if _LIBCPP_STD_VER >= 23 + // [utility.underlying], to_underlying + using std::to_underlying; + + // [utility.unreachable], unreachable + using std::unreachable; +#endif // _LIBCPP_STD_VER >= 23 + + // [intseq], compile-time integer sequences + using std::index_sequence; + using std::integer_sequence; + + using std::make_index_sequence; + using std::make_integer_sequence; + + using std::index_sequence_for; + + // [pairs], class template pair + using std::pair; + +#if _LIBCPP_STD_VER >= 23 + using std::basic_common_reference; + using std::common_type; +#endif + // [pairs.spec], pair specialized algorithms + using std::operator==; + using std::operator<=>; + + using std::make_pair; + + // [pair.astuple], tuple-like access to pair + using std::tuple_element; + using std::tuple_size; + + using std::get; + + // [pair.piecewise], pair piecewise construction + using std::piecewise_construct; + using std::piecewise_construct_t; + + // in-place construction + using std::in_place; + using std::in_place_t; + + using std::in_place_type; + using std::in_place_type_t; + + using std::in_place_index; + using std::in_place_index_t; + + // [depr.relops] + namespace rel_ops { + using rel_ops::operator!=; + using rel_ops::operator>; + using rel_ops::operator<=; + using rel_ops::operator>=; + } // namespace rel_ops +} // namespace std + +// valarray.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + using std::gslice; + using std::gslice_array; + using std::indirect_array; + using std::mask_array; + using std::slice; + using std::slice_array; + using std::valarray; + + using std::swap; + + using std::operator*; + using std::operator/; + using std::operator%; + using std::operator+; + using std::operator-; + + using std::operator^; + using std::operator&; + using std::operator|; + + using std::operator<<; + using std::operator>>; + + using std::operator&&; + using std::operator||; + + using std::operator==; + using std::operator!=; + + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + + using std::abs; + using std::acos; + using std::asin; + using std::atan; + + using std::atan2; + + using std::cos; + using std::cosh; + using std::exp; + using std::log; + using std::log10; + + using std::pow; + + using std::sin; + using std::sinh; + using std::sqrt; + using std::tan; + using std::tanh; + + using std::begin; + using std::end; +} // namespace std + +// variant.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [variant.variant], class template variant + using std::variant; + + // [variant.helper], variant helper classes + using std::variant_alternative; + using std::variant_npos; + using std::variant_size; + using std::variant_size_v; + + // [variant.get], value access + using std::get; + using std::get_if; + using std::holds_alternative; + using std::variant_alternative_t; + + // [variant.relops], relational operators + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator>; + using std::operator<=; + using std::operator>=; + using std::operator<=>; + + // [variant.visit], visitation + using std::visit; + + // [variant.monostate], class monostate + using std::monostate; + + // [variant.specalg], specialized algorithms + using std::swap; + + // [variant.bad.access], class bad_variant_access + using std::bad_variant_access; + + // [variant.hash], hash support + using std::hash; +} // namespace std + +// vector.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // [vector], class template vector + using std::vector; + + using std::operator==; + using std::operator<=>; + + using std::swap; + + // [vector.erasure], erasure + using std::erase; + using std::erase_if; + + namespace pmr { + using std::pmr::vector; + } + + // hash support + using std::hash; + +#if _LIBCPP_STD_VER >= 23 + // [vector.bool.fmt], formatter specialization for vector<bool> + using std::formatter; +#endif +} // namespace std + +// version.inc +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +export namespace std { + // This module exports nothing. +} // namespace std diff --git a/libbuild2/cc/target.cxx b/libbuild2/cc/target.cxx index 3f71eb1..6a518dd 100644 --- a/libbuild2/cc/target.cxx +++ b/libbuild2/cc/target.cxx @@ -25,7 +25,6 @@ namespace build2 }; extern const char h_ext_def[] = "h"; - const target_type h::static_type { "h", @@ -40,7 +39,6 @@ namespace build2 }; extern const char c_ext_def[] = "c"; - const target_type c::static_type { "c", @@ -54,8 +52,48 @@ namespace build2 target_type::flag::none }; - extern const char pc_ext[] = "pc"; // VC14 rejects constexpr. + extern const char m_ext_def[] = "m"; + const target_type m::static_type + { + "m", + &cc::static_type, + &target_factory<m>, + nullptr, /* fixed_extension */ + &target_extension_var<m_ext_def>, + &target_pattern_var<m_ext_def>, + nullptr, + &file_search, + target_type::flag::none + }; + + extern const char S_ext_def[] = "S"; + const target_type S::static_type + { + "S", + &cc::static_type, + &target_factory<S>, + nullptr, /* fixed_extension */ + &target_extension_var<S_ext_def>, + &target_pattern_var<S_ext_def>, + nullptr, + &file_search, + target_type::flag::none + }; + + const target_type c_inc::static_type + { + "c_inc", + &cc::static_type, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + target_type::flag::none + }; + extern const char pc_ext[] = "pc"; // VC14 rejects constexpr. const target_type pc::static_type { "pc", @@ -70,7 +108,6 @@ namespace build2 }; extern const char pca_ext[] = "static.pc"; // VC14 rejects constexpr. - const target_type pca::static_type { "pca", @@ -85,7 +122,6 @@ namespace build2 }; extern const char pcs_ext[] = "shared.pc"; // VC14 rejects constexpr. - const target_type pcs::static_type { "pcs", diff --git a/libbuild2/cc/target.hxx b/libbuild2/cc/target.hxx index fbac790..01f2d6e 100644 --- a/libbuild2/cc/target.hxx +++ b/libbuild2/cc/target.hxx @@ -68,6 +68,57 @@ namespace build2 static const target_type static_type; }; + // Objective-C source file (the same rationale for having it here as for + // c{} above). + // + class LIBBUILD2_CC_SYMEXPORT m: public cc + { + public: + m (context& c, dir_path d, dir_path o, string n) + : cc (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } + + public: + static const target_type static_type; + }; + + // Assembler with C preprocessor source file (the same rationale for + // having it here as for c{} above). + // + class LIBBUILD2_CC_SYMEXPORT S: public cc + { + public: + S (context& c, dir_path d, dir_path o, string n) + : cc (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } + + public: + static const target_type static_type; + }; + + // This is an abstract base target for deriving additional targets that + // can be #include'd in C translation units (the same rationale for having + // it here as for c{} above). In particular, only such targets will be + // considered to reverse-lookup extensions to target types (see + // dyndep_rule::map_extension() for background). + // + class LIBBUILD2_CC_SYMEXPORT c_inc: public cc + { + public: + c_inc (context& c, dir_path d, dir_path o, string n) + : cc (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } + + public: + static const target_type static_type; + }; + // pkg-config file targets. // class LIBBUILD2_CC_SYMEXPORT pc: public file // .pc (common) diff --git a/libbuild2/cc/types.cxx b/libbuild2/cc/types.cxx index 8ee4fa9..c6cfae9 100644 --- a/libbuild2/cc/types.cxx +++ b/libbuild2/cc/types.cxx @@ -6,6 +6,7 @@ #include <libbuild2/cc/utility.hxx> using namespace std; +using namespace butl; namespace build2 { @@ -123,6 +124,8 @@ namespace build2 size_t importable_headers:: insert_angle_pattern (const dir_paths& sys_hdr_dirs, const string& pat) { + tracer trace ("importable_headers::insert_angle_pattern"); + assert (pat.front () == '<' && pat.back () == '>' && path_pattern (pat)); // First see if it has already been inserted. @@ -172,7 +175,17 @@ namespace build2 try { - path_search (f, process, dir); + path_search ( + f, + process, + dir, + path_match_flags::follow_symlinks, + [&trace] (const dir_entry& de) + { + l5 ([&]{trace << "skipping inaccessible/dangling entry " + << de.base () / de.path ();}); + return true; + }); } catch (const system_error& e) { diff --git a/libbuild2/cc/windows-rpath.cxx b/libbuild2/cc/windows-rpath.cxx index 9387078..eb62ad1 100644 --- a/libbuild2/cc/windows-rpath.cxx +++ b/libbuild2/cc/windows-rpath.cxx @@ -45,6 +45,8 @@ namespace build2 // Return the greatest (newest) timestamp of all the DLLs that we will be // adding to the assembly or timestamp_nonexistent if there aren't any. // + // Note: called during the execute phase. + // timestamp link_rule:: windows_rpath_timestamp (const file& t, const scope& bs, @@ -88,7 +90,18 @@ namespace build2 // if (l->is_a<libs> () && !l->path ().empty ()) // Also covers binless. { - timestamp t (l->load_mtime ()); + // Handle the case where the library is a member of a group (for + // example, people are trying to hack something up with pre-built + // libraries; see GH issue #366). + // + timestamp t; + if (l->group_state (action () /* inner */)) + { + t = l->group->is_a<mtime_target> ()->mtime (); + assert (t != timestamp_unknown); + } + else + t = l->load_mtime (); if (t > r) r = t; @@ -128,7 +141,9 @@ namespace build2 library_cache lib_cache; for (const prerequisite_target& pt: t.prerequisite_targets[a]) { - if (pt == nullptr || pt.adhoc ()) + // Note: during execute so check for ad hoc first to avoid data races. + // + if (pt.adhoc () || pt == nullptr) continue; bool la; @@ -255,7 +270,9 @@ namespace build2 library_cache lib_cache; for (const prerequisite_target& pt: t.prerequisite_targets[a]) { - if (pt == nullptr || pt.adhoc ()) + // Note: during execute so check for ad hoc first to avoid data races. + // + if (pt.adhoc () || pt == nullptr) continue; bool la; |