diff options
Diffstat (limited to 'libbuild2')
-rw-r--r-- | libbuild2/buildfile | 4 | ||||
-rw-r--r-- | libbuild2/c/init.cxx | 26 | ||||
-rw-r--r-- | libbuild2/cc/buildfile | 8 | ||||
-rw-r--r-- | libbuild2/cc/common.cxx | 14 | ||||
-rw-r--r-- | libbuild2/cc/compile-rule.cxx | 302 | ||||
-rw-r--r-- | libbuild2/cc/guess.cxx | 44 | ||||
-rw-r--r-- | libbuild2/cc/link-rule.cxx | 70 | ||||
-rw-r--r-- | libbuild2/cc/predefs-rule.cxx | 2 | ||||
-rw-r--r-- | libbuild2/cc/std.compat.cppm | 996 | ||||
-rw-r--r-- | libbuild2/cc/std.cppm | 134 | ||||
-rw-r--r-- | libbuild2/cxx/init.cxx | 51 | ||||
-rw-r--r-- | libbuild2/diagnostics.cxx | 36 | ||||
-rw-r--r-- | libbuild2/functions-filesystem.cxx | 51 | ||||
-rw-r--r-- | libbuild2/functions-path.cxx | 438 | ||||
-rw-r--r-- | libbuild2/functions-regex.cxx | 3 | ||||
-rw-r--r-- | libbuild2/functions-string.cxx | 246 | ||||
-rw-r--r-- | libbuild2/scheduler.cxx | 12 | ||||
-rw-r--r-- | libbuild2/test/script/parser.cxx | 11 | ||||
-rw-r--r-- | libbuild2/utility.hxx | 28 | ||||
-rw-r--r-- | libbuild2/utility.txx | 21 | ||||
-rw-r--r-- | libbuild2/variable.hxx | 15 | ||||
-rw-r--r-- | libbuild2/variable.txx | 35 |
22 files changed, 2092 insertions, 455 deletions
diff --git a/libbuild2/buildfile b/libbuild2/buildfile index 3518d93..d9711e6 100644 --- a/libbuild2/buildfile +++ b/libbuild2/buildfile @@ -293,7 +293,7 @@ if ($install.root != [null]) if ($cxx.target.class != 'windows') { - libul{build2}: cxx.libs += -lpthread + libul{build2}: cxx.libs += -pthread # Note: only linking libdl in shared build. # @@ -323,7 +323,7 @@ lib{build2}: # needed for some std::thread implementations (like libstdc++). # if ($cxx.target.class != 'windows') - lib{build2}: cxx.export.libs += -lpthread + lib{build2}: cxx.export.libs += -pthread liba{build2}: cxx.export.poptions += -DLIBBUILD2_STATIC libs{build2}: cxx.export.poptions += -DLIBBUILD2_SHARED diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index 8bc2f7d..f100abc 100644 --- a/libbuild2/c/init.cxx +++ b/libbuild2/c/init.cxx @@ -111,23 +111,31 @@ namespace build2 // From version 16.8 VC now supports /std:c11 and /std:c17 options // which enable C11/17 conformance. However, as of version 16.10, // neither SDK nor CRT can be compiled in these modes (see the /std - // option documentation for details/updates). + // option documentation for details/updates). There is also now + // /std:clatest which can be used to enable C23 typeof as of MSVC + // 17.9. So let's map C23 to that. // if (v == nullptr) ; else if (!stdcmp ("90")) { - uint64_t cver (ci.version.major); - - if ((stdcmp ("99") && cver < 16) || // Since VS2010/10.0. - ((stdcmp ("11") || - stdcmp ("17") || - stdcmp ("18")) && cver < 18) || // Since VS????/11.0. - (stdcmp ("23", "2x") )) + uint64_t mj (ci.version.major); + uint64_t mi (ci.version.minor); + + if (stdcmp ("99") && mj >= 16) // Since VS2010/10.0. + ; + else if ((stdcmp ("11") || + stdcmp ("17") || + stdcmp ("18")) && mj >= 18) // Since VS????/11.0. + ; + else if (stdcmp ("23", "2x") && + (mj > 19 || (mj == 19 && mi >= 39))) // Since 17.9. { + mode.insert (mode.begin (), "/std:clatest"); + } + else fail << "C " << *v << " is not supported by " << ci.signature << info << "required by " << project (rs) << '@' << rs; - } } break; } diff --git a/libbuild2/cc/buildfile b/libbuild2/cc/buildfile index 7dcd811..05e4c8c 100644 --- a/libbuild2/cc/buildfile +++ b/libbuild2/cc/buildfile @@ -11,7 +11,7 @@ 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} @@ -25,14 +25,14 @@ libul{build2-cc}: cxx{pkgconfig-libpkg-config}: include = (!$libpkgconf) libul{build2-cc}: $intf_libs $impl_libs -# libc++ std module interface translation unit. +# 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} -file{std.cppm}@./: install = data/libbuild2/cc/ +lib{build2-cc}: file{std.cppm std.compat.cppm} +file{std.cppm}@./ file{std.compat.cppm}@./: install = data/libbuild2/cc/ # Unit tests. # diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx index 2a8bc50..9a4a07c 100644 --- a/libbuild2/cc/common.cxx +++ b/libbuild2/cc/common.cxx @@ -1691,6 +1691,10 @@ namespace build2 // 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 () && @@ -1716,7 +1720,8 @@ namespace build2 // // 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. + // GCC's spelling. Note that to enable color on Windows Clang also + // needs -fansi-escape-codes. // if ( #ifndef _WIN32 @@ -1742,7 +1747,14 @@ namespace build2 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 + } } } diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 2e4775e..7629ed5 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -6151,180 +6151,185 @@ namespace build2 } }; - // Pre-resolve std modules in an ad hoc way for certain compilers. + // Pre-resolve standard library modules (std and std.compat) in an ad + // hoc way. // - // @@ TODO: cache x_stdlib value. + + // Similar logic to check_exact() above. // - if ((ctype == compiler_type::msvc) || - (ctype == compiler_type::clang && - cmaj >= 17 && - cast<string> (rs[x_stdlib]) == "libc++")) + done = true; + + for (size_t i (0); i != n; ++i) { - // Similar logic to check_exact() above. - // - done = true; + module_import& m (imports[i]); - for (size_t i (0); i != n; ++i) + if (m.name == "std" || m.name == "std.compat") { - module_import& m (imports[i]); + otype ot (otype::e); + const target* mt (nullptr); - if (m.name == "std" || m.name == "std.compat") + switch (ctype) { - otype ot (otype::e); - const target* mt (nullptr); - - switch (ctype) + case compiler_type::clang: { - case compiler_type::clang: - { - if (m.name != "std") - fail << "module " << m.name << " not yet provided by libc++"; + // @@ TODO: cache x_stdlib value. + // + if (cast<string> (rs[x_stdlib]) != "libc++") + fail << "standard library module '" << m.name << "' is " + << "currently only supported in libc++" << + info << "try adding -stdlib=libc++ as compiler mode option"; - // Find or insert std.cppm (similar code to pkgconfig.cxx). - // - // Note: build_install_data is absolute and normalized. - // - mt = &ctx.targets.insert_locked ( - *x_mod, - (dir_path (build_install_data) /= "libbuild2") /= "cc", - dir_path (), - "std", - string ("cppm"), // For C++14 during bootstrap. - target_decl::implied, - trace).first; - - // Which output type should we use, static or shared? The - // correct way would be to detect whether static or shared - // version of libc++ is to be linked and use the corresponding - // type. And we could do that by looking for -static-libstdc++ - // in loption (and no, it's not -static-libc++). - // - // But, looking at the object file produced from std.cppm, it - // only contains one symbol, the static object initializer. - // And this is unlikely to change since all other non-inline - // or template symbols should be in libc++. So feels like it's - // not worth the trouble and one variant should be good enough - // for both cases. Let's use the shared one for less - // surprising diagnostics (as in, "why are you linking obje{} - // to a shared library?") - // - // (Of course, theoretically, std.cppm could detect via a - // macro whether it's being compiled with -fPIC or not and do - // things differently, but this seems far-fetched). - // - ot = otype::s; + if (cmaj < 18) + fail << "standard library module '" << m.name << "' is " + << "only supported in Clang 18 or later"; - break; - } - case compiler_type::msvc: + // Find or insert std*.cppm (similar code to pkgconfig.cxx). + // + // Note: build_install_data is absolute and normalized. + // + mt = &ctx.targets.insert_locked ( + *x_mod, + (dir_path (build_install_data) /= "libbuild2") /= "cc", + dir_path (), + m.name, + string ("cppm"), // For C++14 during bootstrap. + target_decl::implied, + trace).first; + + // Which output type should we use, static or shared? The + // correct way would be to detect whether static or shared + // version of libc++ is to be linked and use the corresponding + // type. And we could do that by looking for -static-libstdc++ + // in loption (and no, it's not -static-libc++). + // + // But, looking at the object file produced from std*.cppm, they + // only contain one symbol, the static object initializer. And + // this is unlikely to change since all other non-inline or + // template symbols should be in libc++. So feels like it's not + // worth the trouble and one variant should be good enough for + // both cases. Let's use the shared one for less surprising + // diagnostics (as in, "why are you linking obje{} to a shared + // library?") + // + // (Of course, theoretically, std*.cppm could detect via a macro + // whether they are being compiled with -fPIC or not and do + // things differently, but this seems far-fetched). + // + ot = otype::s; + + break; + } + case compiler_type::msvc: + { + // For MSVC, the source files std.ixx and std.compat.ixx are + // found in the modules/ subdirectory which is a sibling of + // include/ in the MSVC toolset (and "that is a contract with + // customers" to quote one of the developers). + // + // The problem of course is that there are multiple system + // header search directories (for example, as specified in the + // INCLUDE environment variable) and which one of them is for + // the MSVC toolset is not specified. So what we are going to do + // is search for one of the well-known standard C++ headers and + // assume that the directory where we found it is the one we are + // looking for. Or we could look for something MSVC-specific + // like vcruntime.h. + // + dir_path modules; + if (optional<path> p = find_system_header (path ("vcruntime.h"))) { - // For MSVC, the source files std.ixx and std.compat.ixx are - // found in the modules/ subdirectory which is a sibling of - // include/ in the MSVC toolset (and "that is a contract with - // customers" to quote one of the developers). - // - // The problem of course is that there are multiple system - // header search directories (for example, as specified in the - // INCLUDE environment variable) and which one of them is for - // the MSVC toolset is not specified. So what we are going to - // do is search for one of the well-known standard C++ headers - // and assume that the directory where we found it is the one - // we are looking for. Or we could look for something - // MSVC-specific like vcruntime.h. - // - dir_path modules; - if (optional<path> p = find_system_header (path ("vcruntime.h"))) + p->make_directory (); // Strip vcruntime.h. + if (p->leaf () == path ("include")) // Sanity check. { - p->make_directory (); // Strip vcruntime.h. - if (p->leaf () == path ("include")) // Sanity check. - { - modules = path_cast<dir_path> (move (p->make_directory ())); - modules /= "modules"; - } + modules = path_cast<dir_path> (move (p->make_directory ())); + modules /= "modules"; } + } - if (modules.empty ()) - fail << "unable to locate MSVC standard modules directory"; - - mt = &ctx.targets.insert_locked ( - *x_mod, - move (modules), - dir_path (), - m.name, - string ("ixx"), // For C++14 during bootstrap. - target_decl::implied, - trace).first; + if (modules.empty ()) + fail << "unable to locate MSVC standard modules directory"; - // For MSVC it's easier to detect the runtime being used since - // it's specified with the compile options (/MT[d], /MD[d]). - // - // Similar semantics as in extract_headers() except here we - // use options visible from the root scope. Note that - // find_option_prefixes() looks in reverse, so look in the - // cmode, x_coptions, c_coptions order. - // - initializer_list<const char*> os {"/MD", "/MT", "-MD", "-MT"}; + mt = &ctx.targets.insert_locked ( + *x_mod, + move (modules), + dir_path (), + m.name, + string ("ixx"), // For C++14 during bootstrap. + target_decl::implied, + trace).first; - const string* o; - if ((o = find_option_prefixes (os, cmode)) != nullptr || - (o = find_option_prefixes (os, rs, x_coptions)) != nullptr || - (o = find_option_prefixes (os, rs, c_coptions)) != nullptr) - { - ot = (*o)[2] == 'D' ? otype::s : otype::a; - } - else - ot = otype::s; // The default is /MD. + // For MSVC it's easier to detect the runtime being used since + // it's specified with the compile options (/MT[d], /MD[d]). + // + // Similar semantics as in extract_headers() except here we use + // options visible from the root scope. Note that + // find_option_prefixes() looks in reverse, so look in the + // cmode, x_coptions, c_coptions order. + // + initializer_list<const char*> os {"/MD", "/MT", "-MD", "-MT"}; - break; + const string* o; + if ((o = find_option_prefixes (os, cmode)) != nullptr || + (o = find_option_prefixes (os, rs, x_coptions)) != nullptr || + (o = find_option_prefixes (os, rs, c_coptions)) != nullptr) + { + ot = (*o)[2] == 'D' ? otype::s : otype::a; } - case compiler_type::gcc: - case compiler_type::icc: - assert (false); - }; + else + ot = otype::s; // The default is /MD. + + break; + } + case compiler_type::gcc: + case compiler_type::icc: + { + fail << "standard library module '" << m.name << "' is " + << "not yet supported in this compiler"; + } + }; - pair<target&, ulock> tl ( - this->make_module_sidebuild ( // GCC 4.9 - a, bs, nullptr, ot, *mt, m.name)); + pair<target&, ulock> tl ( + this->make_module_sidebuild ( // GCC 4.9 + a, bs, nullptr, ot, *mt, m.name)); - if (tl.second.owns_lock ()) + if (tl.second.owns_lock ()) + { + // Special compile options for the std modules. + // + if (ctype == compiler_type::clang) { - // Special compile options for the std modules. - // - if (ctype == compiler_type::clang) - { - value& v (tl.first.append_locked (x_coptions)); + value& v (tl.first.append_locked (x_coptions)); - if (v.null) - v = strings {}; + if (v.null) + v = strings {}; - strings& cops (v.as<strings> ()); + strings& cops (v.as<strings> ()); - switch (ctype) + switch (ctype) + { + case compiler_type::clang: { - case compiler_type::clang: - { - cops.push_back ("-Wno-reserved-module-identifier"); - break; - } - case compiler_type::msvc: - // It appears nothing special is needed to compile MSVC - // standard modules. - case compiler_type::gcc: - case compiler_type::icc: - assert (false); - }; - } - - tl.second.unlock (); + cops.push_back ("-Wno-reserved-module-identifier"); + break; + } + case compiler_type::msvc: + // It appears nothing special is needed to compile MSVC + // standard modules. + case compiler_type::gcc: + case compiler_type::icc: + assert (false); + }; } - pts[start + i].target = &tl.first; - m.score = match_max (m.name) + 1; - continue; // Scan the rest to detect if all done. + tl.second.unlock (); } - done = false; + pts[start + i].target = &tl.first; + m.score = match_max (m.name) + 1; + continue; // Scan the rest to detect if all done. } + + done = false; } // Go over prerequisites and try to resolve imported modules with them. @@ -6672,6 +6677,15 @@ namespace build2 // string extra; + // @@ What happens if different projects used different standards? + // Specifically, how do we detect this and what can the user do + // about it? For the latter question, forcing the same standard + // with config.cxx.std seems like the only sensible option. For + // the former, we could read the value of cxx.std using our + // buildfile first-line peeking mechanism. But doing that for + // every module interface feels inefficient so we will probably + // need to cache it on the per-project basis. Maybe/later. + // if (const string* std = cast_null<string> (rs[x_std])) extra += string (x) + ".std = " + *std + '\n'; diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index d7e9c63..5ae6fb2 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -2580,27 +2580,29 @@ namespace build2 // // 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 - // 14.0.0 -> 12.0 (_LIBCPP_VERSION=130000) - // 14.0.3 -> 15.0 (_LIBCPP_VERSION=150006) - // 15.0.0 -> 16.0 (_LIBCPP_VERSION=160002) + // 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); diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 08a60b9..417cba5 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -2585,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. // @@ -2596,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)); } }; @@ -2660,7 +2686,9 @@ namespace build2 if ((c ? f.compare (p, string::npos, e) : icasecmp (f.c_str () + p, e)) == 0) + { append (f); + } } } @@ -2671,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); } } @@ -3332,6 +3369,9 @@ namespace build2 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)) { @@ -3368,7 +3408,8 @@ namespace build2 else o += p.string (); - sargs.push_back (move (o)); + 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 @@ -3386,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)); + } } } @@ -3433,13 +3479,19 @@ namespace build2 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", b, x, - [] (const dir_path& d) {return d.string ().c_str ();}); + [] (const dir_path& d) -> const string& {return d.string ();}); + + append_args (sargs1); } } diff --git a/libbuild2/cc/predefs-rule.cxx b/libbuild2/cc/predefs-rule.cxx index e74192d..606db06 100644 --- a/libbuild2/cc/predefs-rule.cxx +++ b/libbuild2/cc/predefs-rule.cxx @@ -278,7 +278,7 @@ namespace build2 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 in + // starting with VS2013, /Zc:preprocessor is only available // starting from VS2019). // args.push_back ("/Fi:"); 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 index 5368d1c..575e6a4 100644 --- a/libbuild2/cc/std.cppm +++ b/libbuild2/cc/std.cppm @@ -8,15 +8,15 @@ //===----------------------------------------------------------------------===// // WARNING, this entire header is generated by -// utils/generate_std_cppm_in.py +// utils/generate_libcxx_cppm_in.py // DO NOT MODIFY! module; #include <__config> -#if _LIBCPP_VERSION < 170000 -#error libc++ version 17.0.0 or later required +#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] @@ -153,11 +153,8 @@ module; # include <strstream> #endif #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) -#if __has_include(<syncstream>) -# define _LIPCPP_HAS_YES_SYNCSTREAM # include <syncstream> #endif -#endif #include <system_error> #if !defined(_LIBCPP_HAS_NO_THREADS) # include <thread> @@ -177,38 +174,38 @@ module; #if 0 // *** Headers not yet available *** #if __has_include(<debugging>) -# error "update the header information for <debugging> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <flat_map> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <flat_set> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <generator> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <hazard_pointer> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <linalg> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <rcu> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <spanstream> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <stacktrace> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <stdfloat> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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 "update the header information for <text_encoding> in libcxx/utils/generate_std_cppm_in.py" -#endif // __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; @@ -232,7 +229,9 @@ export namespace std { using std::ranges::in_in_result; using std::ranges::in_out_out_result; using std::ranges::in_out_result; - // using std::ranges::in_value_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 @@ -256,13 +255,15 @@ export namespace std { using std::ranges::none_of; } +#if _LIBCPP_STD_VER >= 23 // [alg.contains], contains -#if 0 namespace ranges { using std::ranges::contains; +#if 0 using std::ranges::contains_subrange; - } // namespace ranges #endif + } // namespace ranges +#endif // _LIBCPP_STD_VER >= 23 // [alg.foreach], for each using std::for_each; @@ -370,20 +371,18 @@ export namespace std { // [alg.starts.with], starts with using std::ranges::starts_with; -#if _LIBCPP_VERSION >= 180000 // [alg.ends.with], ends with using std::ranges::ends_with; -#endif -# if 0 // [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_with_iter_result; - using std::ranges::fold_left_with_iter; using std::ranges::fold_left_first_with_iter; using std::ranges::fold_left_first_with_iter; # endif @@ -955,7 +954,9 @@ export namespace std { 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; @@ -1993,11 +1994,13 @@ export namespace std { 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 @@ -2617,7 +2620,7 @@ export namespace std { using std::mktime; using std::strftime; using std::time; - using std::timespec_get; + using std::timespec_get _LIBCPP_USING_IF_EXISTS; } // namespace std // cuchar.inc @@ -3108,6 +3111,9 @@ export namespace std { #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; @@ -3590,9 +3596,11 @@ export namespace std { #endif using std::u16streampos; using std::u32streampos; +#ifndef _LIBCPP_HAS_NO_CHAR8_T using std::u8streampos; +#endif -#ifdef _LIBCPP_HAS_YES_SYNCSTREAM +#ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM using std::basic_osyncstream; using std::basic_syncbuf; #endif @@ -3600,13 +3608,11 @@ export namespace std { using std::istreambuf_iterator; using std::ostreambuf_iterator; -#ifdef _LIBCPP_HAS_YES_SYNCSTREAM +#ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM using std::osyncstream; using std::syncbuf; -#endif #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS -#ifdef _LIBCPP_HAS_YES_SYNCSTREAM using std::wosyncstream; using std::wsyncbuf; #endif @@ -4112,9 +4118,7 @@ export namespace std { // [mdspan.layout], layout mapping using std::layout_left; using std::layout_right; -#if _LIBCPP_VERSION >= 180000 using std::layout_stride; -#endif // [mdspan.accessor.default], class template default_accessor using std::default_accessor; @@ -4171,7 +4175,9 @@ export namespace std { #if _LIBCPP_STD_VER >= 23 using std::allocation_result; - using std::allocate_at_least; + // Note: no longer in Clang 19. + // + //using std::allocate_at_least; #endif // [default.allocator], the default allocator @@ -4283,7 +4289,9 @@ export namespace std { 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 @@ -4555,6 +4563,16 @@ export namespace std { // [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 @@ -4626,14 +4644,17 @@ export namespace std { # endif using std::operator<<; -# if 0 +# 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 +# endif // _LIBCPP_HAS_NO_UNICODE +# endif // _LIBCPP_STD_VER >= 23 + #endif // _LIBCPP_HAS_NO_LOCALIZATION } // namespace std @@ -5013,13 +5034,11 @@ export namespace std { using std::ranges::views::drop_while; } // namespace views -#ifdef _LIBCPP_ENABLE_EXPERIMENTAL using std::ranges::join_view; namespace views { using std::ranges::views::join; } // namespace views -#endif // _LIBCPP_ENABLE_EXPERIMENTAL #if 0 using std::ranges::join_with_view; @@ -5123,14 +5142,12 @@ export namespace std { #endif #if _LIBCPP_STD_VER >= 23 -#if _LIBCPP_VERSION >= 180000 // [range.chunk.by], chunk by view using std::ranges::chunk_by_view; namespace views { using std::ranges::views::chunk_by; } -#endif #endif // _LIBCPP_STD_VER >= 23 #if 0 @@ -5783,7 +5800,9 @@ export namespace std { 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 @@ -5807,7 +5826,9 @@ export namespace std { 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 @@ -5816,17 +5837,12 @@ export namespace std { // [basic.string.hash], hash support using std::hash; - // TODO MODULES is this a bug? -#if _LIBCPP_STD_VER >= 23 - using std::operator""s; -#else 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 -#endif + } // namespace literals } // namespace std // string_view.inc @@ -5859,7 +5875,9 @@ export namespace std { 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 @@ -5904,8 +5922,6 @@ export namespace std { // //===----------------------------------------------------------------------===// -#ifdef _LIBCPP_HAS_YES_SYNCSTREAM - export namespace std { #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) using std::basic_syncbuf; @@ -5926,8 +5942,6 @@ export namespace std { #endif // !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) } // namespace std -#endif - // system_error.inc // -*- C++ -*- //===----------------------------------------------------------------------===// diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx index 8159d18..3185eaa 100644 --- a/libbuild2/cxx/init.cxx +++ b/libbuild2/cxx/init.cxx @@ -335,7 +335,12 @@ namespace build2 { case compiler_type::gcc: { - if (mj >= 11) + if (mj >= 14) + { + o = "-std=c++26"; + cplusplus = 202400; + } + else if (mj >= 11) { o = "-std=c++23"; cplusplus = 202302; @@ -367,25 +372,29 @@ namespace build2 } case compiler_type::clang: { - // Clang 10.0.0 targeting MSVC 16.4 and 16.5 (preview) in the - // c++2a mode uncovers some Concepts-related bugs in MSVC STL - // (LLVM bug #44956). So in this case we map `latest` to - // c++17. - // - // While reportedly this has been fixed in the later versions - // of MSVC, instead of somehow passing the version of MSVC - // Clang is targeting, we will just assume that Clang 11 - // and later are used with a sufficiently new version of - // MSVC. - // - - if (mj >= 13) + if (mj >= 18) + { + o = "-std=c++26"; + cplusplus = 202400; + } + else if (mj >= 13) { o = "-std=c++2b"; cplusplus = 202302; } else if (mj == 10 && latest && tt.system == "win32-msvc") { + // Clang 10.0.0 targeting MSVC 16.4 and 16.5 (preview) in + // the c++2a mode uncovers some Concepts-related bugs in + // MSVC STL (LLVM bug #44956). So in this case we map + // `latest` to c++17. + // + // While reportedly this has been fixed in the later + // versions of MSVC, instead of somehow passing the version + // of MSVC Clang is targeting, we will just assume that + // Clang 11 and later are used with a sufficiently new + // version of MSVC. + // o = "-std=c++17"; cplusplus = 201703; } @@ -446,7 +455,7 @@ namespace build2 // 26 to 2c for compatibility with older versions of the // compilers. // - // @@ TMP: update C++26 __cplusplus value once known. + // @@ TMP: update C++26 __cplusplus value once known (and above). // o = "-std="; @@ -481,6 +490,12 @@ namespace build2 { case compiler_type::msvc: { + // Let's enable the new preprocessor in this mode. For background, + // see MSVC issue 10537317. + // + if (mj > 19 || (mj == 19 && mi >= 39)) + prepend ("/Zc:preprocessor"); + // Starting with 15.5 (19.12) Visual Studio-created projects // default to the strict mode. However, this flag currently tends // to trigger too many compiler bugs. So for now we leave it to @@ -498,8 +513,8 @@ namespace build2 // Unless disabled by the user, try to enable C++ modules. // - // NOTE: see also diagnostics about modules support required in compile - // rule. + // NOTE: see also diagnostics about modules support required (if + // attempting to use) in compile rule. // if (!modules.value || *modules.value) { @@ -580,7 +595,7 @@ namespace build2 // around Clang 16 so we don't support anything earlier than // that (it's not practically usable anyway). // - // Clang enable modules by default in c++20 or later but they + // Clang enables modules by default in c++20 or later but they // don't yet (as of Clang 18) define __cpp_modules. When they // do, we can consider enabling modules by default on our side. // For now, we only enable modules if forced with explicit diff --git a/libbuild2/diagnostics.cxx b/libbuild2/diagnostics.cxx index 4a46756..c795e44 100644 --- a/libbuild2/diagnostics.cxx +++ b/libbuild2/diagnostics.cxx @@ -55,31 +55,31 @@ namespace build2 if (st) { - // @@ TMP: eventually we want to enable on Windows by default. + // Only attempt to enable if explicitly requested by the user. Note that + // while we may enable color for our process, who knows if this gets + // inherited by other processes we start (e.g., compilers) and/or + // whether they will do something sensible about any of this. // -#ifdef _WIN32 - if (c && *c) + try { -#endif - stderr_term_color = fdterm_color (stderr_fd (), !c || *c /* enable */); + stderr_term_color = fdterm_color (stderr_fd (), c && *c /* enable */); + } + catch (const io_error& e) + { + fail << "unable to query terminal color support for stderr: " << e; + } - // If the user specified --diag-color on POSIX we will trust the color - // is supported (e.g., wrong TERM value, etc). - // - if (!stderr_term_color && c && *c) - { + // If the user specified --diag-color on POSIX we will trust the color + // is supported (e.g., wrong TERM value, etc). + // + if (!stderr_term_color && c && *c) + { #ifdef _WIN32 - fail << "unable to enable diagnostics color support for stderr"; + fail << "unable to enable diagnostics color support for stderr"; #else - stderr_term_color = true; + stderr_term_color = true; #endif - } - -#ifdef _WIN32 } - else - stderr_term_color = false; -#endif } else stderr_term_color = false; diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx index 665a0f3..340c2bc 100644 --- a/libbuild2/functions-filesystem.cxx +++ b/libbuild2/functions-filesystem.cxx @@ -5,6 +5,7 @@ #include <libbuild2/function.hxx> #include <libbuild2/variable.hxx> +#include <libbuild2/filesystem.hxx> using namespace std; using namespace butl; @@ -95,14 +96,60 @@ namespace build2 return r; } + static bool + file_exists (path&& f) + { + if (f.relative () && path_traits::thread_current_directory () != nullptr) + f.complete (); + + return exists (f); + } + + static bool + directory_exists (dir_path&& d) + { + if (d.relative () && path_traits::thread_current_directory () != nullptr) + d.complete (); + + return exists (d); + } + void filesystem_functions (function_map& m) { - // @@ Maybe we should have the ability to mark the whole family as not - // pure? + // NOTE: anything that depends on relative path must handle the + // thread-specific curren directory override explicitly. function_family f (m, "filesystem"); + // $file_exists(<path>) + // + // Return true if a filesystem entry at the specified path exists and is a + // regular file (or is a symlink to a regular file) and false otherwise. + // + // Note that this function is not pure. + // + { + auto e (f.insert ("file_exists", false)); + + e += [](path f) {return file_exists (move (f));}; + e += [](names ns) {return file_exists (convert<path> (move (ns)));}; + } + + // $directory_exists(<path>) + // + // Return true if a filesystem entry at the specified path exists and is a + // directory (or is a symlink to a directory) and false otherwise. + // + // Note that this function is not pure. + // + { + auto e (f.insert ("directory_exists", false)); + + e += [](path f) {return directory_exists (path_cast<dir_path> (move (f)));}; + e += [](names ns) {return directory_exists (convert<dir_path> (move (ns)));}; + } + // $path_search(<pattern>[, <start-dir>]) // // Return filesystem paths that match the shell-like wildcard pattern. If diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx index 4b114f5..2f7f159 100644 --- a/libbuild2/functions-path.cxx +++ b/libbuild2/functions-path.cxx @@ -193,6 +193,35 @@ namespace build2 #endif } + template <typename P> + static bool + try_normalize (P& p) + { + try + { + p.normalize (); + return true; + } + catch (const invalid_path&) {} + + return false; + } + + template <typename P> + static bool + try_actualize (P& p) + { + try + { + p.normalize (true); + return true; + } + catch (const invalid_path&) {} + catch (const system_error&) {} + + return false; + } + void path_functions (function_map& m) { @@ -344,138 +373,76 @@ namespace build2 return ns; }; - // $canonicalize(<paths>) - // $path.canonicalize(<untyped>) + // $absolute(<path>) + // $path.absolute(<untyped>) // - // Canonicalize the path (or list of paths) by converting all the - // directory separators to the canonical form for the host platform. Note - // that multiple directory separators are not collapsed. - // - - // @@ TODO: add ability to specify alternative separator. + // Return true if the path is absolute and false otherwise. // - f["canonicalize"] += [](path p) {p.canonicalize (); return p;}; - f["canonicalize"] += [](dir_path p) {p.canonicalize (); return p;}; - - f["canonicalize"] += [](paths v) - { - for (auto& p: v) - p.canonicalize (); - return v; - }; - - f["canonicalize"] += [](dir_paths v) + f["absolute"] += [](path p) { - for (auto& p: v) - p.canonicalize (); - return v; + return p.absolute (); }; - f[".canonicalize"] += [](names ns) + f[".absolute"] += [](names ns) { - // For each path decide based on the presence of a trailing slash - // whether it is a directory. Return as untyped list of (potentially - // mixed) paths. - // - for (name& n: ns) - { - if (n.directory ()) - n.dir.canonicalize (); - else - n.value = convert<path> (move (n)).canonicalize ().string (); - } - return ns; + return convert<path> (move (ns)).absolute (); }; - // $normalize(<paths>) - // $path.normalize(<untyped>) + // $simple(<path>) + // $path.simple(<untyped>) // - // Normalize the path (or list of paths) by collapsing the `.` and `..` - // components if possible, collapsing multiple directory separators, and - // converting all the directory separators to the canonical form for the - // host platform. + // Return true if the path is simple, that is, has no direcrory component, + // and false otherwise. // - f["normalize"] += [](path p) {p.normalize (); return p;}; - f["normalize"] += [](dir_path p) {p.normalize (); return p;}; + // Note that on POSIX `/foo` is not a simple path (it is `foo` in the root + // directory) while `/` is (it is the root directory). + // + f["simple"] += [](path p) + { + return p.simple (); + }; - f["normalize"] += [](paths v) + f[".simple"] += [](names ns) { - for (auto& p: v) - p.normalize (); - return v; + return convert<path> (move (ns)).simple (); }; - f["normalize"] += [](dir_paths v) + // $sub_path(<path>, <path>) + // $path.sub_path(<untyped>, <untyped>) + // + // Return true if the path specified as the first argument is a sub-path + // of the one specified as the second argument (in other words, the second + // argument is a prefix of the first) and false otherwise. Both paths are + // expected to be normalized. Note that this function returns true if the + // paths are equal. Empty path is considered a prefix of any path. + // + f["sub_path"] += [](path p, value v) { - for (auto& p: v) - p.normalize (); - return v; + return p.sub (convert_to_base<path> (move (v))); }; - f[".normalize"] += [](names ns) + f[".sub_path"] += [](names ns, value v) { - // For each path decide based on the presence of a trailing slash - // whether it is a directory. Return as untyped list of (potentially - // mixed) paths. - // - for (name& n: ns) - { - if (n.directory ()) - n.dir.normalize (); - else - n.value = convert<path> (move (n)).normalize ().string (); - } - return ns; + return convert<path> (move (ns)).sub (convert_to_base<path> (move (v))); }; - // $actualize(<paths>) - // $path.actualize(<untyped>) - // - // Actualize the path (or list of paths) by first normalizing it and then - // for host platforms with case-insensitive filesystems obtaining the - // actual spelling of the path. + // $super_path(<path>, <path>) + // $path.super_path(<untyped>, <untyped>) // - // Note that only an absolute path can be actualized. If a path component - // does not exist, then its (and all subsequent) spelling is - // unchanged. This is a potentially expensive operation. - // - // Note that this function is not pure. + // Return true if the path specified as the first argument is a super-path + // of the one specified as the second argument (in other words, the second + // argument is a suffix of the first) and false otherwise. Both paths are + // expected to be normalized. Note that this function returns true if the + // paths are equal. Empty path is considered a suffix of any path. // + f["super_path"] += [](path p, value v) { - auto e (f.insert ("actualize", false)); - - e += [](path p) {p.normalize (true); return p;}; - e += [](dir_path p) {p.normalize (true); return p;}; - - e += [](paths v) - { - for (auto& p: v) - p.normalize (true); - return v; - }; - - e += [](dir_paths v) - { - for (auto& p: v) - p.normalize (true); - return v; - }; - } + return p.sup (convert_to_base<path> (move (v))); + }; - f.insert (".actualize", false) += [](names ns) + f[".super_path"] += [](names ns, value v) { - // For each path decide based on the presence of a trailing slash - // whether it is a directory. Return as untyped list of (potentially - // mixed) paths. - // - for (name& n: ns) - { - if (n.directory ()) - n.dir.normalize (true); - else - n.value = convert<path> (move (n)).normalize (true).string (); - } - return ns; + return convert<path> (move (ns)).sup (convert_to_base<path> (move (v))); }; // $directory(<paths>) @@ -615,6 +582,8 @@ namespace build2 // specified paths). Issue diagnostics and fail if a relative path cannot // be derived (for example, paths are on different drives on Windows). // + // Note: to check if a path if relative, use `$path.absolute()`. + // f["relative"] += [](path p, dir_path d) { return relative (p, d); @@ -701,6 +670,261 @@ namespace build2 return extension (convert<path> (move (ns))); }; + // $complete(<paths>) + // $path.complete(<untyped>) + // + // Complete the path (or list of paths) by prepending the current working + // directory unless the path is already absolute. + // + f["complete"] += [](path p) {p.complete (); return p;}; + f["complete"] += [](dir_path p) {p.complete (); return p;}; + + f["complete"] += [](paths v) + { + for (auto& p: v) + p.complete (); + return v; + }; + + f["complete"] += [](dir_paths v) + { + for (auto& p: v) + p.complete (); + return v; + }; + + f[".complete"] += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of (potentially + // mixed) paths. + // + for (name& n: ns) + { + if (n.directory ()) + n.dir.complete (); + else + n.value = convert<path> (move (n)).complete ().string (); + } + return ns; + }; + + // $canonicalize(<paths>) + // $path.canonicalize(<untyped>) + // + // Canonicalize the path (or list of paths) by converting all the + // directory separators to the canonical form for the host platform. Note + // that multiple directory separators are not collapsed. + // + f["canonicalize"] += [](path p) {p.canonicalize (); return p;}; + f["canonicalize"] += [](dir_path p) {p.canonicalize (); return p;}; + + f["canonicalize"] += [](paths v) + { + for (auto& p: v) + p.canonicalize (); + return v; + }; + + f["canonicalize"] += [](dir_paths v) + { + for (auto& p: v) + p.canonicalize (); + return v; + }; + + f[".canonicalize"] += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of (potentially + // mixed) paths. + // + for (name& n: ns) + { + if (n.directory ()) + n.dir.canonicalize (); + else + n.value = convert<path> (move (n)).canonicalize ().string (); + } + return ns; + }; + + // $normalize(<paths>) + // $path.normalize(<untyped>) + // $try_normalize(<path>) + // $path.try_normalize(<untyped>) + // + // Normalize the path (or list of paths) by collapsing the `.` and `..` + // components if possible, collapsing multiple directory separators, and + // converting all the directory separators to the canonical form for the + // host platform. + // + // If the resulting path would be invalid, the `$normalize()` version + // issues diagnostics and fails while the `$try_normalize()` version + // returns `null`. Note that `$try_normalize()` only accepts a single + // path. + // + f["normalize"] += [](path p) {p.normalize (); return p;}; + f["normalize"] += [](dir_path p) {p.normalize (); return p;}; + + f["normalize"] += [](paths v) + { + for (auto& p: v) + p.normalize (); + return v; + }; + + f["normalize"] += [](dir_paths v) + { + for (auto& p: v) + p.normalize (); + return v; + }; + + f[".normalize"] += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of (potentially + // mixed) paths. + // + for (name& n: ns) + { + if (n.directory ()) + n.dir.normalize (); + else + n.value = convert<path> (move (n)).normalize ().string (); + } + return ns; + }; + + f["try_normalize"] += [](path p) + { + return try_normalize (p) ? value (move (p)) : value (nullptr); + }; + + f["try_normalize"] += [](dir_path p) + { + return try_normalize (p) ? value (move (p)) : value (nullptr); + }; + + f[".try_normalize"] += [](names ns) + { + if (ns.size () != 1) + throw invalid_argument ("multiple paths"); + + name& n (ns.front ()); + + bool r; + if (n.directory ()) + r = try_normalize (n.dir); + else + { + path p (convert<path> (move (n))); + if ((r = try_normalize (p))) + n.value = move (p).string (); + } + + return r ? value (move (ns)) : value (nullptr); + }; + + // $actualize(<paths>) + // $path.actualize(<untyped>) + // $try_actualize(<path>) + // $path.try_actualize(<untyped>) + // + // Actualize the path (or list of paths) by first normalizing it and then + // for host platforms with case-insensitive filesystems obtaining the + // actual spelling of the path. + // + // Only an absolute path can be actualized. If a path component does not + // exist, then its (and all subsequent) spelling is unchanged. Note that + // this is a potentially expensive operation. + // + // If the resulting path would be invalid or in case of filesystem errors + // (other than non-existent component), the `$actualize()` version issues + // diagnostics and fails while the `$try_actualize()` version returns + // `null`. Note that `$try_actualize()` only accepts a single path. + // + // Note that this function is not pure. + // + { + auto e (f.insert ("actualize", false)); + + e += [](path p) {p.normalize (true); return p;}; + e += [](dir_path p) {p.normalize (true); return p;}; + + e += [](paths v) + { + for (auto& p: v) + p.normalize (true); + return v; + }; + + e += [](dir_paths v) + { + for (auto& p: v) + p.normalize (true); + return v; + }; + } + + f.insert (".actualize", false) += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of (potentially + // mixed) paths. + // + for (name& n: ns) + { + if (n.directory ()) + n.dir.normalize (true); + else + n.value = convert<path> (move (n)).normalize (true).string (); + } + return ns; + }; + + { + auto e (f.insert ("try_actualize", false)); + + e += [](path p) + { + return try_actualize (p) ? value (move (p)) : value (nullptr); + }; + + e += [](dir_path p) + { + return try_actualize (p) ? value (move (p)) : value (nullptr); + }; + } + + f.insert (".try_actualize", false) += [](names ns) + { + if (ns.size () != 1) + throw invalid_argument ("multiple paths"); + + name& n (ns.front ()); + + bool r; + if (n.directory ()) + r = try_actualize (n.dir); + else + { + path p (convert<path> (move (n))); + if ((r = try_actualize (p))) + n.value = move (p).string (); + } + + return r ? value (move (ns)) : value (nullptr); + }; + + + // Note that we currently do not expose realize(). For one, it might be + // tricky to handle CWD overrides (on POSIX we just call realize(3)). + // Also, our implementation for Windows currently does not handle + // symlinks. + + // $size(<paths>) // $size(<path>) // diff --git a/libbuild2/functions-regex.cxx b/libbuild2/functions-regex.cxx index cf3ffd0..c46f6f5 100644 --- a/libbuild2/functions-regex.cxx +++ b/libbuild2/functions-regex.cxx @@ -688,6 +688,9 @@ namespace build2 // If both `return_match` and `return_subs` flags are specified then the // sub-string that matches the whole regular expression comes first. // + // See also `$string.contains()`, `$string.starts_with()`, + // `$string.ends_with()`. + // f[".search"] += [](value v, string re, optional<names> flags) { return search (move (v), re, move (flags)); diff --git a/libbuild2/functions-string.cxx b/libbuild2/functions-string.cxx index b7e0a17..eccc6c7 100644 --- a/libbuild2/functions-string.cxx +++ b/libbuild2/functions-string.cxx @@ -8,6 +8,136 @@ using namespace std; namespace build2 { + // Look for the substring forwards in the [p, n) range. + // + static inline size_t + find (const string& s, size_t p, const string& ss, bool ic) + { + size_t sn (ss.size ()); + + for (size_t n (s.size ()); p != n; ++p) + { + if (n - p >= sn && + (ic + ? icasecmp (ss, s.c_str () + p, sn) + : s.compare (p, sn, ss)) == 0) + return p; + } + + return string::npos; + } + + // Look for the substring backwards in the [0, n) range. + // + static inline size_t + rfind (const string& s, size_t n, const string& ss, bool ic) + { + size_t sn (ss.size ()); + + if (n >= sn) + { + n -= sn; // Don't consider characters out of range. + + for (size_t p (n);; ) + { + if ((ic + ? icasecmp (ss, s.c_str () + p, sn) + : s.compare (p, sn, ss)) == 0) + return p; + + if (--p == 0) + break; + } + } + + return string::npos; + } + + static bool + contains (const string& s, value&& ssv, optional<names>&& fs) + { + bool ic (false), once (false); + if (fs) + { + for (name& f: *fs) + { + string s (convert<string> (move (f))); + + if (s == "icase") + ic = true; + else if (s == "once") + once = true; + else + throw invalid_argument ("invalid flag '" + s + '\''); + } + } + + const string ss (convert<string> (move (ssv))); + + if (ss.empty ()) + throw invalid_argument ("empty substring"); + + size_t p (find (s, 0, ss, ic)); + + if (once && p != string::npos && p != rfind (s, s.size (), ss, ic)) + p = string::npos; + + return p != string::npos; + } + + static bool + starts_with (const string& s, value&& pfv, optional<names>&& fs) + { + bool ic (false); + if (fs) + { + for (name& f: *fs) + { + string s (convert<string> (move (f))); + + if (s == "icase") + ic = true; + else + throw invalid_argument ("invalid flag '" + s + '\''); + } + } + + const string pf (convert<string> (move (pfv))); + + if (pf.empty ()) + throw invalid_argument ("empty prefix"); + + return find (s, 0, pf, ic) == 0; + } + + static bool + ends_with (const string& s, value&& sfv, optional<names>&& fs) + { + bool ic (false); + if (fs) + { + for (name& f: *fs) + { + string s (convert<string> (move (f))); + + if (s == "icase") + ic = true; + else + throw invalid_argument ("invalid flag '" + s + '\''); + } + } + + const string sf (convert<string> (move (sfv))); + + if (sf.empty ()) + throw invalid_argument ("empty suffix"); + + size_t n (s.size ()); + size_t p (rfind (s, n, sf, ic)); + + return p != string::npos && p + sf.size () == n; + } + static string replace (string&& s, value&& fv, value&& tv, optional<names>&& fs) { @@ -43,52 +173,13 @@ namespace build2 size_t fn (f.size ()); - // Look for the substring forward in the [p, n) range. - // - auto find = [&s, &f, fn, ic] (size_t p) -> size_t - { - for (size_t n (s.size ()); p != n; ++p) - { - if (n - p >= fn && - (ic - ? icasecmp (f, s.c_str () + p, fn) - : s.compare (p, fn, f)) == 0) - return p; - } - - return string::npos; - }; - - // Look for the substring backard in the [0, n) range. - // - auto rfind = [&s, &f, fn, ic] (size_t n) -> size_t - { - if (n >= fn) - { - n -= fn; // Don't consider characters out of range. - - for (size_t p (n);; ) - { - if ((ic - ? icasecmp (f, s.c_str () + p, fn) - : s.compare (p, fn, f)) == 0) - return p; - - if (--p == 0) - break; - } - } - - return string::npos; - }; - if (fo || lo) { - size_t p (lo ? rfind (s.size ()) : find (0)); + size_t p (lo ? rfind (s, s.size (), f, ic) : find (s, 0, f, ic)); if (fo && lo && p != string::npos) { - if (p != find (0)) + if (p != find (s, 0, f, ic)) p = string::npos; } @@ -97,7 +188,9 @@ namespace build2 } else { - for (size_t p (0); (p = find (0)) != string::npos; p += fn) + size_t tn (t.size ()); + + for (size_t p (0); (p = find (s, p, f, ic)) != string::npos; p += tn) s.replace (p, fn, t); } } @@ -173,6 +266,75 @@ namespace build2 convert<string> (move (y))) == 0; }; + // $string.contains(<untyped>, <untyped>[, <flags>]) + // $contains(<string>, <string>[, <flags>]) + // + // Check if the string (first argument) contains the given substring + // (second argument). The substring must not be empty. + // + // The following flags are supported: + // + // icase - compare ignoring case + // + // once - check if the substring occurs exactly once + // + // See also `$string.starts_with()`, `$string.ends_with()`, + // `$regex.search()`. + // + f["contains"] += [](string s, value ss, optional<names> fs) + { + return contains (move (s), move (ss), move (fs)); + }; + + f[".contains"] += [](names s, value ss, optional<names> fs) + { + return contains (convert<string> (move (s)), move (ss), move (fs)); + }; + + // $string.starts_with(<untyped>, <untyped>[, <flags>]) + // $starts_with(<string>, <string>[, <flags>]) + // + // Check if the string (first argument) begins with the given prefix + // (second argument). The prefix must not be empty. + // + // The following flags are supported: + // + // icase - compare ignoring case + // + // See also `$string.contains()`. + // + f["starts_with"] += [](string s, value pf, optional<names> fs) + { + return starts_with (move (s), move (pf), move (fs)); + }; + + f[".starts_with"] += [](names s, value pf, optional<names> fs) + { + return starts_with (convert<string> (move (s)), move (pf), move (fs)); + }; + + // $string.ends_with(<untyped>, <untyped>[, <flags>]) + // $ends_with(<string>, <string>[, <flags>]) + // + // Check if the string (first argument) ends with the given suffix (second + // argument). The suffix must not be empty. + // + // The following flags are supported: + // + // icase - compare ignoring case + // + // See also `$string.contains()`. + // + f["ends_with"] += [](string s, value sf, optional<names> fs) + { + return ends_with (move (s), move (sf), move (fs)); + }; + + f[".ends_with"] += [](names s, value sf, optional<names> fs) + { + return ends_with (convert<string> (move (s)), move (sf), move (fs)); + }; + // $string.replace(<untyped>, <from>, <to> [, <flags>]) // $replace(<string>, <from>, <to> [, <flags>]) // diff --git a/libbuild2/scheduler.cxx b/libbuild2/scheduler.cxx index e3fbcc1..69673e6 100644 --- a/libbuild2/scheduler.cxx +++ b/libbuild2/scheduler.cxx @@ -97,6 +97,10 @@ namespace build2 { // Note: assume non-serial execution. + // Note: increment progress before/after every wait. + // + progress_.fetch_add (1, memory_order_relaxed); + lock l (move (rl)); // Make sure unlocked on exception. active_--; @@ -135,6 +139,10 @@ namespace build2 { // Note: assume non-serial execution. + // Note: increment progress before/after every wait. + // + progress_.fetch_add (1, memory_order_relaxed); + lock l (mutex_); if (collision) @@ -1053,7 +1061,9 @@ namespace build2 // Re-check active/external counts for good measure (in case we were // spinning too fast). // - if (np == op && s.active_ == 0 && s.external_ == 0 && !s.shutdown_) + if (np == op && + s.active_ == 0 && s.external_ == 0 && !s.shutdown_ && + s.progress_.load (memory_order_consume) == op) { // Shutting things down cleanly is tricky: we could have handled it // in the scheduler (e.g., by setting a flag and then waking diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx index b712c21..337b162 100644 --- a/libbuild2/test/script/parser.cxx +++ b/libbuild2/test/script/parser.cxx @@ -1609,6 +1609,17 @@ namespace build2 { runner_->enter (*scope_, scope_->start_loc_); + // Set thread-specific current directory override. In particular, this + // makes sure functions like $path.complete() work correctly. + // + auto wdg = make_guard ( + [old = path_traits::thread_current_directory ()] () + { + path_traits::thread_current_directory (old); + }); + + path_traits::thread_current_directory (&scope_->work_dir.path->string ()); + // Note that we rely on "small function object" optimization for the // exec_*() lambdas. // diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index b534f41..151b409 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -877,17 +877,29 @@ namespace build2 // template <typename I, typename F> void - append_option_values (cstrings&, - const char* opt, - I begin, I end, - F&& get = [] (const string& s) {return s.c_str ();}); + append_option_values ( + cstrings&, + const char* opt, + I begin, I end, + F&& get = [] (const string& s) {return s.c_str ();}); template <typename I, typename F> void - append_option_values (sha256&, - const char* opt, - I begin, I end, - F&& get = [] (const string& s) {return s;}); + append_option_values ( + sha256&, + const char* opt, + I begin, I end, + F&& get = [] (const string& s) -> const string& {return s;}); + + // As above but in a combined form (e.g., -L/usr/local/lib). + // + template <typename I, typename F> + void + append_combined_option_values ( + strings&, + const char* opt, + I begin, I end, + F&& get = [] (const string& s) -> const string& {return s;}); // As above but append a single option (used for append/hash uniformity). // diff --git a/libbuild2/utility.txx b/libbuild2/utility.txx index d2fc29c..cdf510f 100644 --- a/libbuild2/utility.txx +++ b/libbuild2/utility.txx @@ -5,16 +5,16 @@ namespace build2 { template <typename I, typename F> void - append_option_values (cstrings& args, const char* o, I b, I e, F&& get) + append_option_values (cstrings& ss, const char* o, I b, I e, F&& get) { if (b != e) { - args.reserve (args.size () + (e - b)); + ss.reserve (ss.size () + (e - b)); for (; b != e; ++b) { - args.push_back (o); - args.push_back (get (*b)); + ss.push_back (o); + ss.push_back (get (*b)); } } } @@ -30,6 +30,19 @@ namespace build2 } } + template <typename I, typename F> + void + append_combined_option_values (strings& ss, const char* o, I b, I e, F&& get) + { + if (b != e) + { + ss.reserve (ss.size () + (e - b)); + + for (; b != e; ++b) + ss.push_back (string (o) += get (*b)); + } + } + template <typename K> basic_path<char, K> relative (const basic_path<char, K>& p) diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index aed3350..6dfbbc6 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -92,6 +92,9 @@ namespace build2 // static_cast to const T*. If it is NULL, then cast data_ directly. Note // that this function is used for both const and non-const values. // + // @@ This is currently ignored by as<T>() which is now used in quite a + // few places (in particular, grep for as<T>). + // const void* (*const cast) (const value&, const value_type*); // If NULL, then the types are compared as PODs using memcmp(). @@ -702,14 +705,18 @@ namespace build2 template <typename T> T convert (names&&); // Convert value to T. If value is already of type T, then simply cast it. - // Otherwise call convert(names) above. If value is NULL, then throw - // invalid_argument (with an appropriate message). + // Otherwise call convert(names) above. If the value is NULL, then throw + // invalid_argument (with an appropriate message). See also + // convert_to_base() below. // template <typename T> T convert (value&&); + template <typename T> T convert (const value&); - // As above but preserving the value. + // As above but also allow the derived-to-base conversions (where T is + // base). Note that this call may potentially slice the value. // - template <typename T> T convert (const value&); + template <typename T> T convert_to_base (value&&); + template <typename T> T convert_to_base (const value&); // Default implementations of the dtor/copy_ctor/copy_assing callbacks for // types that are stored directly in value::data_ and the provide all the diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx index 0b831e9..a1ee340 100644 --- a/libbuild2/variable.txx +++ b/libbuild2/variable.txx @@ -38,6 +38,11 @@ namespace build2 { if (v.type == nullptr) return convert<T> (move (v).as<names> ()); + // + // Note that while it may be tempting to use is_a() here like in cast(), + // the implications are unclear (i.e., we may end up relaxing things we + // don't want to). So we have the convert_to_base() variants instead. + // else if (v.type == &value_traits<T>::value_type) return move (v).as<T> (); } @@ -61,6 +66,36 @@ namespace build2 } template <typename T> + T + convert_to_base (value&& v) + { + if (v) + { + if (v.type == nullptr) + return convert<T> (move (v).as<names> ()); + else if (v.type->is_a<T> ()) + return move (v).as<T> (); + } + + convert_throw (v ? v.type : nullptr, value_traits<T>::value_type); + } + + template <typename T> + T + convert_to_base (const value& v) + { + if (v) + { + if (v.type == nullptr) + return convert<T> (names (v.as<names> ())); + else if (v.type->is_a<T> ()) + return v.as<T> (); + } + + convert_throw (v ? v.type : nullptr, value_traits<T>::value_type); + } + + template <typename T> void default_dtor (value& v) { |