diff --git a/libcxx/docs/Contributing.rst b/libcxx/docs/Contributing.rst --- a/libcxx/docs/Contributing.rst +++ b/libcxx/docs/Contributing.rst @@ -49,7 +49,7 @@ - Did you add it to ``include/module.modulemap.in``? - Did you add it to ``include/CMakeLists.txt``? - - If it's a public header, did you update ``utils/libcxx/test/header_information.py``? + - If it's a public header, did you update ``utils/libcxx/header_information.py``? - Did you add the relevant feature test macro(s) for your feature? Did you update the ``generate_feature_test_macro_components.py`` script with it? - Did you run the ``libcxx-generate-files`` target and verify its output? diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -674,7 +674,6 @@ __ranges/views.h __ranges/zip_view.h __split_buffer - __std_clang_module __std_mbstate_t.h __stop_token/atomic_unique_lock.h __stop_token/intrusive_list_view.h @@ -1053,6 +1052,17 @@ list(APPEND _all_includes "${dst}") endforeach() +add_custom_command( + OUTPUT "${LIBCXX_GENERATED_INCLUDE_DIR}/__std_clang_module" + # Run this command on every build in case a public header was added or removed. + "${LIBCXX_GENERATED_INCLUDE_DIR}/__std_clang_module_run_every_build" + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/generate_std_clang_module_header.py" + --libcxx-python-module "${CMAKE_CURRENT_SOURCE_DIR}/../utils" + --libcxx-include-directory "${LIBCXX_GENERATED_INCLUDE_DIR}" + COMMENT "Generating CXX header __std_clang_module") +list(APPEND _all_includes "${LIBCXX_GENERATED_INCLUDE_DIR}/__std_clang_module" + "${LIBCXX_GENERATED_INCLUDE_DIR}/__std_clang_module_run_every_build") + add_custom_target(generate-cxx-headers ALL DEPENDS ${_all_includes}) add_library(cxx-headers INTERFACE) diff --git a/libcxx/include/__std_clang_module b/libcxx/include/__std_clang_module deleted file mode 100644 --- a/libcxx/include/__std_clang_module +++ /dev/null @@ -1,212 +0,0 @@ -// -*- 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 -// -//===----------------------------------------------------------------------===// - -// This header should not be directly included, it's exclusively to import all -// of the libc++ public clang modules for the `std` clang module to export. In -// other words, it's to facilitate `@import std;` in Objective-C++ and `import std` -// in Swift to expose all of the libc++ interfaces. This is generally not -// recommended, however there are some clients that need to import all of libc++ -// without knowing what "all" is. -#if !__building_module(std) -# error "Do not include this header directly, include individual headers instead" -#endif - -#include <__availability> -#include <__config> - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -# pragma GCC system_header -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -# ifndef _LIBCPP_HAS_NO_FILESYSTEM -# include -# endif -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# include - -# include -#endif - -#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS -# include -# include - -# include -# include -#endif - -#ifdef _LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT -# include -#endif - -#ifndef _LIBCPP_CXX03_LANG -# ifndef _LIBCPP_HAS_NO_THREADS -# include -# include -# include -# endif - -# include -# include -# include -# include -# include -# include -# include -# ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -# endif -# include -# include -# include -# include -# include -# include -# include -# include -#endif - -#if _LIBCPP_STD_VER >= 14 -# ifndef _LIBCPP_HAS_NO_THREADS -# include -# endif -#endif - -#if _LIBCPP_STD_VER >= 17 -# ifndef _LIBCPP_HAS_NO_FILESYSTEM -# include -# endif -#endif - -#if _LIBCPP_STD_VER >= 20 -# include - -# ifndef _LIBCPP_HAS_NO_THREADS -# include -# include -# include -# include -# endif -#endif - -#if _LIBCPP_STD_VER >= 23 -# ifndef _LIBCPP_HAS_NO_THREADS -# include -# endif -#endif diff --git a/libcxx/include/generate_std_clang_module_header.py b/libcxx/include/generate_std_clang_module_header.py new file mode 100644 --- /dev/null +++ b/libcxx/include/generate_std_clang_module_header.py @@ -0,0 +1,72 @@ +import argparse +import operator +import os.path +import sys + +parser = argparse.ArgumentParser() +parser.add_argument('--libcxx-python-module', required=True) +parser.add_argument('--libcxx-include-directory', required=True) +args = parser.parse_args() + +sys.path.append(args.libcxx_python_module) + +import libcxx.header_information + +public_headers = libcxx.header_information.public_headers +header_include_requirements = libcxx.header_information.header_include_requirements +always_available_headers = frozenset(public_headers).difference(*header_include_requirements.values()) + +with open(os.path.join(args.libcxx_include_directory, '__std_clang_module'), 'w') as std_clang_module_header: + std_clang_module_header.write('''\ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +// This header should not be directly included, it's exclusively to import all +// of the libc++ public clang modules for the `std` clang module to export. In +// other words, it's to facilitate `@import std;` in Objective-C++ and `import std` +// in Swift to expose all of the libc++ interfaces. This is generally not +// recommended, however there are some clients that need to import all of libc++ +// without knowing what "all" is. +#if !__building_module(std) +# error "Do not include this header directly, include individual headers instead" +#endif + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +''') + for header in sorted(always_available_headers): + std_clang_module_header.write('#include <') + std_clang_module_header.write(header) + std_clang_module_header.write('>\n') + std_clang_module_header.write('\n') + + for requirements, headers in sorted(header_include_requirements.items(), key=operator.itemgetter(0)): + if len(requirements) == 1: + std_clang_module_header.write('#ifndef ') + std_clang_module_header.write(requirements[0]) + else: + std_clang_module_header.write('#if') + for index, requirement in enumerate(requirements): + if index > 0: + std_clang_module_header.write(' &&') + std_clang_module_header.write(' !defined(') + std_clang_module_header.write(requirement) + std_clang_module_header.write(')') + std_clang_module_header.write('\n') + + for header in sorted(headers): + std_clang_module_header.write('# include <') + std_clang_module_header.write(header) + std_clang_module_header.write('>\n') + + std_clang_module_header.write('#endif\n\n') diff --git a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.gen.py b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.gen.py --- a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.gen.py +++ b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.gen.py @@ -14,7 +14,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers for header in public_headers: # Skip C compatibility headers. diff --git a/libcxx/test/libcxx/clang_tidy.gen.py b/libcxx/test/libcxx/clang_tidy.gen.py --- a/libcxx/test/libcxx/clang_tidy.gen.py +++ b/libcxx/test/libcxx/clang_tidy.gen.py @@ -12,7 +12,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers for header in public_headers: BLOCKLIT = '' # block Lit from interpreting a RUN/XFAIL/etc inside the generation script diff --git a/libcxx/test/libcxx/double_include.gen.py b/libcxx/test/libcxx/double_include.gen.py --- a/libcxx/test/libcxx/double_include.gen.py +++ b/libcxx/test/libcxx/double_include.gen.py @@ -12,7 +12,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers for header in public_headers: BLOCKLIT = '' # block Lit from interpreting a RUN/XFAIL/etc inside the generation script diff --git a/libcxx/test/libcxx/header_inclusions.gen.py b/libcxx/test/libcxx/header_inclusions.gen.py --- a/libcxx/test/libcxx/header_inclusions.gen.py +++ b/libcxx/test/libcxx/header_inclusions.gen.py @@ -13,7 +13,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers, mandatory_inclusions +from libcxx.header_information import lit_header_restrictions, public_headers, mandatory_inclusions for header in public_headers: header_guard = lambda h: f"_LIBCPP_{h.upper().replace('.', '_').replace('/', '_')}" diff --git a/libcxx/test/libcxx/libcpp_version.gen.py b/libcxx/test/libcxx/libcpp_version.gen.py --- a/libcxx/test/libcxx/libcpp_version.gen.py +++ b/libcxx/test/libcxx/libcpp_version.gen.py @@ -12,7 +12,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers for header in public_headers: print(f"""\ diff --git a/libcxx/test/libcxx/lint/lint_headers.sh.py b/libcxx/test/libcxx/lint/lint_headers.sh.py --- a/libcxx/test/libcxx/lint/lint_headers.sh.py +++ b/libcxx/test/libcxx/lint/lint_headers.sh.py @@ -10,7 +10,8 @@ def exclude_from_consideration(path): return ( - path.endswith(".txt") + path.endswith(".py") + or path.endswith(".txt") or path.endswith(".modulemap.in") or os.path.basename(path) == "__config" or os.path.basename(path) == "__config_site.in" diff --git a/libcxx/test/libcxx/module_std.gen.py b/libcxx/test/libcxx/module_std.gen.py --- a/libcxx/test/libcxx/module_std.gen.py +++ b/libcxx/test/libcxx/module_std.gen.py @@ -21,7 +21,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import toplevel_headers +from libcxx.header_information import toplevel_headers BLOCKLIT = ( "" # block Lit from interpreting a RUN/XFAIL/etc inside the generation script diff --git a/libcxx/test/libcxx/modules_include.gen.py b/libcxx/test/libcxx/modules_include.gen.py --- a/libcxx/test/libcxx/modules_include.gen.py +++ b/libcxx/test/libcxx/modules_include.gen.py @@ -14,10 +14,11 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers + +BLOCKLIT = '' # block Lit from interpreting a RUN/XFAIL/etc inside the generation script for header in public_headers: - BLOCKLIT = '' # block Lit from interpreting a RUN/XFAIL/etc inside the generation script print(f"""\ //--- {header}.compile.pass.cpp // RUN{BLOCKLIT}: %{{cxx}} %s %{{flags}} %{{compile_flags}} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only @@ -45,7 +46,7 @@ #include <{header}> """) -print(f""" +print(f"""\ //--- __std_clang_module.compile.pass.mm // RUN{BLOCKLIT}: %{{cxx}} %s %{{flags}} %{{compile_flags}} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only @@ -68,10 +69,6 @@ // TODO: Investigate this failure // UNSUPPORTED{BLOCKLIT}: LIBCXX-FREEBSD-FIXME -// Lit seems to compile this twice: once with the default flags and once with with -// the flags specified in the RUN directive. Guard the first compile from failing. -#if __has_feature(modules) @import std; -#endif """) diff --git a/libcxx/test/libcxx/no_assert_include.gen.py b/libcxx/test/libcxx/no_assert_include.gen.py --- a/libcxx/test/libcxx/no_assert_include.gen.py +++ b/libcxx/test/libcxx/no_assert_include.gen.py @@ -13,7 +13,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers for header in public_headers: if header == 'cassert': diff --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py --- a/libcxx/test/libcxx/system_reserved_names.gen.py +++ b/libcxx/test/libcxx/system_reserved_names.gen.py @@ -13,7 +13,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers for header in public_headers: print(f"""\ diff --git a/libcxx/test/libcxx/transitive_includes.gen.py b/libcxx/test/libcxx/transitive_includes.gen.py --- a/libcxx/test/libcxx/transitive_includes.gen.py +++ b/libcxx/test/libcxx/transitive_includes.gen.py @@ -20,7 +20,7 @@ import sys sys.path.append(sys.argv[1]) -from libcxx.test.header_information import lit_header_restrictions, public_headers +from libcxx.header_information import lit_header_restrictions, public_headers import re diff --git a/libcxx/utils/libcxx/test/header_information.py b/libcxx/utils/libcxx/header_information.py rename from libcxx/utils/libcxx/test/header_information.py rename to libcxx/utils/libcxx/header_information.py --- a/libcxx/utils/libcxx/test/header_information.py +++ b/libcxx/utils/libcxx/header_information.py @@ -15,10 +15,8 @@ "coroutine": "// UNSUPPORTED: c++03, c++11, c++14, c++17", "cwchar": "// UNSUPPORTED: no-wide-characters", "cwctype": "// UNSUPPORTED: no-wide-characters", - "experimental/algorithm": "// UNSUPPORTED: c++03", "experimental/deque": "// UNSUPPORTED: c++03", "experimental/forward_list": "// UNSUPPORTED: c++03", - "experimental/functional": "// UNSUPPORTED: c++03", "experimental/iterator": "// UNSUPPORTED: c++03", "experimental/list": "// UNSUPPORTED: c++03", "experimental/map": "// UNSUPPORTED: c++03", @@ -59,16 +57,23 @@ "wctype.h": "// UNSUPPORTED: no-wide-characters", } -private_headers_still_public_in_modules = [ - "__assert", - "__config", - "__config_site.in", - "__hash_table", - "__threading_support", - "__tree", - "__undef_macros", - "__verbose_abort", -] +header_include_requirements = { + # headers with #error directives + ("_LIBCPP_HAS_NO_ATOMIC_HEADER",): ("atomic", + # transitive includers of the above headers + "stdatomic.h"), + # headers with #error directives + ("_LIBCPP_HAS_NO_LOCALIZATION",): ("ios", "locale.h", + # transitive includers of the above headers + "clocale", "codecvt", "experimental/regex", "fstream", "iomanip", "iostream", "istream", + "locale", "ostream", "regex", "sstream", "streambuf", "strstream"), + # headers with #error directives + ("_LIBCPP_HAS_NO_THREADS",): ("barrier", "future", "latch", "semaphore", "shared_mutex", "stop_token", "thread"), + # headers with #error directives + ("_LIBCPP_HAS_NO_WIDE_CHARACTERS",): ("wchar.h", "wctype.h", + # transitive includers of the above headers + "cwchar", "cwctype") +} # Headers that can't be included on their own. Most of these are conceptually # part of another header that were split out just for organization, but aren't @@ -133,10 +138,11 @@ not file.is_dir() and not file.name == "module.modulemap.in" and not file.name == "CMakeLists.txt" + and not file.name == "generate_std_clang_module_header.py" and file.name != "libcxx.imp" ) -libcxx_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) +libcxx_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) include = pathlib.Path(os.path.join(libcxx_root, "include")) test = pathlib.Path(os.path.join(libcxx_root, "test")) assert libcxx_root.exists()