
When some headers are not available because we removed features like localization or threads, the compiler should not try to include these headers when building modules. To avoid that from happening, add a requires-declaration that is never satisfied when the configuration in use doesn't support a header. rdar://93777687 Differential Revision: https://reviews.llvm.org/D127127
151 lines
6.9 KiB
Python
Executable File
151 lines
6.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import contextlib
|
|
import glob
|
|
import io
|
|
import os
|
|
import pathlib
|
|
import re
|
|
|
|
header_restrictions = {
|
|
"barrier": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"future": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"latch": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"semaphore": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"shared_mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"stdatomic.h": "__cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)",
|
|
"thread": "!defined(_LIBCPP_HAS_NO_THREADS)",
|
|
|
|
"filesystem": "!defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)",
|
|
|
|
"clocale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"codecvt": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"fstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"iomanip": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"ios": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"iostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"istream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"locale.h": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"locale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"ostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"sstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"streambuf": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
"strstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
|
|
|
|
"wctype.h": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
|
|
"cwctype": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
|
|
"cwchar": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
|
|
"wchar.h": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
|
|
|
|
"experimental/algorithm": "__cplusplus >= 201103L",
|
|
"experimental/coroutine": "__cplusplus >= 201103L && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES)",
|
|
"experimental/deque": "__cplusplus >= 201103L",
|
|
"experimental/forward_list": "__cplusplus >= 201103L",
|
|
"experimental/functional": "__cplusplus >= 201103L",
|
|
"experimental/iterator": "__cplusplus >= 201103L",
|
|
"experimental/list": "__cplusplus >= 201103L",
|
|
"experimental/map": "__cplusplus >= 201103L",
|
|
"experimental/memory_resource": "__cplusplus >= 201103L",
|
|
"experimental/propagate_const": "__cplusplus >= 201103L",
|
|
"experimental/regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L",
|
|
"experimental/set": "__cplusplus >= 201103L",
|
|
"experimental/simd": "__cplusplus >= 201103L",
|
|
"experimental/span": "__cplusplus >= 201103L",
|
|
"experimental/string": "__cplusplus >= 201103L",
|
|
"experimental/type_traits": "__cplusplus >= 201103L",
|
|
"experimental/unordered_map": "__cplusplus >= 201103L",
|
|
"experimental/unordered_set": "__cplusplus >= 201103L",
|
|
"experimental/utility": "__cplusplus >= 201103L",
|
|
"experimental/vector": "__cplusplus >= 201103L",
|
|
}
|
|
|
|
private_headers_still_public_in_modules = [
|
|
'__assert', '__bsd_locale_defaults.h', '__bsd_locale_fallbacks.h', '__config',
|
|
'__config_site.in', '__debug', '__hash_table',
|
|
'__threading_support', '__tree', '__undef_macros'
|
|
]
|
|
|
|
def find_script(file):
|
|
"""Finds the script used to generate a file inside the file itself. The script is delimited by
|
|
BEGIN-SCRIPT and END-SCRIPT markers.
|
|
"""
|
|
with open(file, 'r') as f:
|
|
content = f.read()
|
|
|
|
match = re.search(r'^BEGIN-SCRIPT$(.+)^END-SCRIPT$', content, flags=re.MULTILINE | re.DOTALL)
|
|
if not match:
|
|
raise RuntimeError("Was unable to find a script delimited with BEGIN-SCRIPT/END-SCRIPT markers in {}".format(test_file))
|
|
return match.group(1)
|
|
|
|
def execute_script(script, variables):
|
|
"""Executes the provided Mako template with the given variables available during the
|
|
evaluation of the script, and returns the result.
|
|
"""
|
|
code = compile(script, 'fake-filename', 'exec')
|
|
output = io.StringIO()
|
|
with contextlib.redirect_stdout(output):
|
|
exec(code, variables)
|
|
output = output.getvalue()
|
|
return output
|
|
|
|
def generate_new_file(file, new_content):
|
|
"""Generates the new content of the file by inserting the new content in-between
|
|
two '// GENERATED-MARKER' markers located in the file.
|
|
"""
|
|
with open(file, 'r') as f:
|
|
old_content = f.read()
|
|
|
|
try:
|
|
before, begin_marker, _, end_marker, after = re.split(r'(// GENERATED-MARKER\n)', old_content, flags=re.MULTILINE | re.DOTALL)
|
|
except ValueError:
|
|
raise RuntimeError("Failed to split {} based on markers, please make sure the file has exactly two '// GENERATED-MARKER' occurrences".format(file))
|
|
|
|
return before + begin_marker + new_content + end_marker + after
|
|
|
|
def produce(test_file, variables):
|
|
script = find_script(test_file)
|
|
result = execute_script(script, variables)
|
|
new_content = generate_new_file(test_file, result)
|
|
with open(test_file, 'w', newline='\n') as f:
|
|
f.write(new_content)
|
|
|
|
def is_header(file):
|
|
"""Returns whether the given file is a header (i.e. not a directory or the modulemap file)."""
|
|
return not file.is_dir() and not file.name == 'module.modulemap.in'
|
|
|
|
def main():
|
|
monorepo_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
include = pathlib.Path(os.path.join(monorepo_root, 'libcxx', 'include'))
|
|
test = pathlib.Path(os.path.join(monorepo_root, 'libcxx', 'test'))
|
|
assert(monorepo_root.exists())
|
|
|
|
toplevel_headers = sorted(str(p.relative_to(include)) for p in include.glob('[a-z]*') if is_header(p))
|
|
experimental_headers = sorted(str(p.relative_to(include)) for p in include.glob('experimental/[a-z]*') if is_header(p))
|
|
extended_headers = sorted(str(p.relative_to(include)) for p in include.glob('ext/[a-z]*') if is_header(p))
|
|
public_headers = toplevel_headers + experimental_headers + extended_headers
|
|
private_headers = sorted(str(p.relative_to(include)) for p in include.rglob('*') if is_header(p) and str(p.relative_to(include)).startswith('__'))
|
|
variables = {
|
|
'toplevel_headers': toplevel_headers,
|
|
'experimental_headers': experimental_headers,
|
|
'extended_headers': extended_headers,
|
|
'public_headers': public_headers,
|
|
'private_headers': private_headers,
|
|
'header_restrictions': header_restrictions,
|
|
'private_headers_still_public_in_modules': private_headers_still_public_in_modules
|
|
}
|
|
|
|
produce(test.joinpath('libcxx/assertions/headers_declare_assertion_handler.sh.cpp'), variables)
|
|
produce(test.joinpath('libcxx/clang_tidy.sh.cpp'), variables)
|
|
produce(test.joinpath('libcxx/double_include.sh.cpp'), variables)
|
|
produce(test.joinpath('libcxx/min_max_macros.compile.pass.cpp'), variables)
|
|
produce(test.joinpath('libcxx/modules_include.sh.cpp'), variables)
|
|
produce(test.joinpath('libcxx/nasty_macros.compile.pass.cpp'), variables)
|
|
produce(test.joinpath('libcxx/no_assert_include.compile.pass.cpp'), variables)
|
|
produce(test.joinpath('libcxx/private_headers.verify.cpp'), variables)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|