[NFC][Py Reformat] Reformat python files in libcxx/libcxxabi
[llvm-project.git] / libcxx / utils / generate_header_tests.py
blob905e7a4cec4f4af8403509ed53884f25a57ea9ad
1 #!/usr/bin/env python
3 import contextlib
4 import glob
5 import io
6 import os
7 import pathlib
8 import re
10 header_restrictions = {
11 "barrier": "!defined(_LIBCPP_HAS_NO_THREADS)",
12 "future": "!defined(_LIBCPP_HAS_NO_THREADS)",
13 "latch": "!defined(_LIBCPP_HAS_NO_THREADS)",
14 "mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
15 "semaphore": "!defined(_LIBCPP_HAS_NO_THREADS)",
16 "shared_mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
17 "stdatomic.h": "__cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)",
18 "thread": "!defined(_LIBCPP_HAS_NO_THREADS)",
19 "filesystem": "!defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)",
20 # TODO(LLVM-17): simplify this to __cplusplus >= 202002L
21 "coroutine": "(defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L) || (defined(__cpp_coroutines) && __cpp_coroutines >= 201703L)",
22 "clocale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
23 "codecvt": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
24 "fstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)",
25 "iomanip": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
26 "ios": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
27 "iostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
28 "istream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
29 "locale.h": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
30 "locale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
31 "ostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
32 "regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
33 "sstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
34 "streambuf": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
35 "strstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
36 "wctype.h": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
37 "cwctype": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
38 "cwchar": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
39 "wchar.h": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
40 "experimental/algorithm": "__cplusplus >= 201103L",
41 "experimental/deque": "__cplusplus >= 201103L",
42 "experimental/forward_list": "__cplusplus >= 201103L",
43 "experimental/functional": "__cplusplus >= 201103L",
44 "experimental/iterator": "__cplusplus >= 201103L",
45 "experimental/list": "__cplusplus >= 201103L",
46 "experimental/map": "__cplusplus >= 201103L",
47 "experimental/memory_resource": "__cplusplus >= 201103L",
48 "experimental/propagate_const": "__cplusplus >= 201103L",
49 "experimental/regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L",
50 "experimental/set": "__cplusplus >= 201103L",
51 "experimental/simd": "__cplusplus >= 201103L",
52 "experimental/span": "__cplusplus >= 201103L",
53 "experimental/string": "__cplusplus >= 201103L",
54 "experimental/type_traits": "__cplusplus >= 201103L",
55 "experimental/unordered_map": "__cplusplus >= 201103L",
56 "experimental/unordered_set": "__cplusplus >= 201103L",
57 "experimental/utility": "__cplusplus >= 201103L",
58 "experimental/vector": "__cplusplus >= 201103L",
61 private_headers_still_public_in_modules = [
62 "__assert",
63 "__config",
64 "__config_site.in",
65 "__debug",
66 "__hash_table",
67 "__threading_support",
68 "__tree",
69 "__undef_macros",
70 "__verbose_abort",
74 def find_script(file):
75 """Finds the script used to generate a file inside the file itself. The script is delimited by
76 BEGIN-SCRIPT and END-SCRIPT markers.
77 """
78 with open(file, "r") as f:
79 content = f.read()
81 match = re.search(
82 r"^BEGIN-SCRIPT$(.+)^END-SCRIPT$", content, flags=re.MULTILINE | re.DOTALL
84 if not match:
85 raise RuntimeError(
86 "Was unable to find a script delimited with BEGIN-SCRIPT/END-SCRIPT markers in {}".format(
87 test_file
90 return match.group(1)
93 def execute_script(script, variables):
94 """Executes the provided Mako template with the given variables available during the
95 evaluation of the script, and returns the result.
96 """
97 code = compile(script, "fake-filename", "exec")
98 output = io.StringIO()
99 with contextlib.redirect_stdout(output):
100 exec(code, variables)
101 output = output.getvalue()
102 return output
105 def generate_new_file(file, new_content):
106 """Generates the new content of the file by inserting the new content in-between
107 two '// GENERATED-MARKER' markers located in the file.
109 with open(file, "r") as f:
110 old_content = f.read()
112 try:
113 before, begin_marker, _, end_marker, after = re.split(
114 r"(// GENERATED-MARKER\n)", old_content, flags=re.MULTILINE | re.DOTALL
116 except ValueError:
117 raise RuntimeError(
118 "Failed to split {} based on markers, please make sure the file has exactly two '// GENERATED-MARKER' occurrences".format(
119 file
123 return before + begin_marker + new_content + end_marker + after
126 def produce(test_file, variables):
127 script = find_script(test_file)
128 result = execute_script(script, variables)
129 new_content = generate_new_file(test_file, result)
130 with open(test_file, "w", newline="\n") as f:
131 f.write(new_content)
134 def is_header(file):
135 """Returns whether the given file is a header (i.e. not a directory or the modulemap file)."""
136 return (
137 not file.is_dir()
138 and not file.name == "module.modulemap.in"
139 and file.name != "libcxx.imp"
143 def main():
144 monorepo_root = pathlib.Path(
145 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
147 include = pathlib.Path(os.path.join(monorepo_root, "libcxx", "include"))
148 test = pathlib.Path(os.path.join(monorepo_root, "libcxx", "test"))
149 assert monorepo_root.exists()
151 toplevel_headers = sorted(
152 str(p.relative_to(include)) for p in include.glob("[a-z]*") if is_header(p)
154 experimental_headers = sorted(
155 str(p.relative_to(include))
156 for p in include.glob("experimental/[a-z]*")
157 if is_header(p)
159 public_headers = toplevel_headers + experimental_headers
160 private_headers = sorted(
161 str(p.relative_to(include))
162 for p in include.rglob("*")
163 if is_header(p)
164 and str(p.relative_to(include)).startswith("__")
165 and not p.name.startswith("pstl")
167 variables = {
168 "toplevel_headers": toplevel_headers,
169 "experimental_headers": experimental_headers,
170 "public_headers": public_headers,
171 "private_headers": private_headers,
172 "header_restrictions": header_restrictions,
173 "private_headers_still_public_in_modules": private_headers_still_public_in_modules,
176 produce(
177 test.joinpath("libcxx/assertions/headers_declare_verbose_abort.sh.cpp"),
178 variables,
180 produce(test.joinpath("libcxx/clang_tidy.sh.cpp"), variables)
181 produce(test.joinpath("libcxx/double_include.sh.cpp"), variables)
182 produce(test.joinpath("libcxx/min_max_macros.compile.pass.cpp"), variables)
183 produce(test.joinpath("libcxx/modules_include.sh.cpp"), variables)
184 produce(test.joinpath("libcxx/nasty_macros.compile.pass.cpp"), variables)
185 produce(test.joinpath("libcxx/no_assert_include.compile.pass.cpp"), variables)
186 produce(test.joinpath("libcxx/private_headers.verify.cpp"), variables)
187 produce(test.joinpath("libcxx/transitive_includes.sh.cpp"), variables)
190 if __name__ == "__main__":
191 main()