xref: /llvm-project/libcxx/utils/libcxx/header_information.py (revision 30feb35c1ebc6f09d85df814f8aac57e8eb6fcc2)
1# ===----------------------------------------------------------------------===##
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7# ===----------------------------------------------------------------------===##
8
9import pathlib, functools
10
11libcxx_root = pathlib.Path(__file__).resolve().parent.parent.parent
12libcxx_include = libcxx_root / "include"
13assert libcxx_root.exists()
14
15def _is_header_file(file):
16    """Returns whether the given file is a header file, i.e. not a directory or the modulemap file."""
17    return not file.is_dir() and not file.name in [
18        "module.modulemap",
19        "CMakeLists.txt",
20        "libcxx.imp",
21        "__config_site.in",
22    ]
23
24@functools.total_ordering
25class Header:
26    _name: str
27    """Relative path from the root of libcxx/include"""
28
29    def __init__(self, name: str):
30        """Create a Header.
31
32        name: The path of the header relative to libc++'s include directory.
33              For example '__algorithm/find.h' or 'coroutine'.
34        """
35        self._name = name
36
37    def is_public(self) -> bool:
38        """Returns whether the header is a public libc++ API header."""
39        return "__" not in self._name and not self._name.startswith("ext/")
40
41    def is_internal(self) -> bool:
42        """Returns whether the header is an internal implementation detail of the library."""
43        return not self.is_public()
44
45    def is_C_compatibility(self) -> bool:
46        """
47        Returns whether the header is a C compatibility header (headers ending in .h like stdlib.h).
48
49        Note that headers like <cstdlib> are not considered C compatibility headers.
50        """
51        return self.is_public() and self._name.endswith(".h")
52
53    def is_cstd(self) -> bool:
54        """Returns whether the header is a C 'std' header, like <cstddef>, <cerrno>, etc."""
55        return self._name in [
56            "cassert",
57            "ccomplex",
58            "cctype",
59            "cerrno",
60            "cfenv",
61            "cfloat",
62            "cinttypes",
63            "ciso646",
64            "climits",
65            "clocale",
66            "cmath",
67            "csetjmp",
68            "csignal",
69            "cstdalign",
70            "cstdarg",
71            "cstdbool",
72            "cstddef",
73            "cstdint",
74            "cstdio",
75            "cstdlib",
76            "cstring",
77            "ctgmath",
78            "ctime",
79            "cuchar",
80            "cwchar",
81            "cwctype",
82        ]
83
84    def is_experimental(self) -> bool:
85        """Returns whether the header is a public experimental header."""
86        return self.is_public() and self._name.startswith("experimental/")
87
88    def has_cxx20_module(self) -> bool:
89        """
90        Returns whether the header is in the std and std.compat C++20 modules.
91
92        These headers are all C++23-and-later headers, excluding C compatibility headers and
93        experimental headers.
94        """
95        # These headers have been removed in C++20 so are never part of a module.
96        removed_in_20 = ["ccomplex", "ciso646", "cstdalign", "cstdbool", "ctgmath"]
97        return self.is_public() and not self.is_experimental() and not self.is_C_compatibility() and not self._name in removed_in_20
98
99    def is_cxx03_frozen_header(self) -> bool:
100        """Returns whether the header is a frozen C++03 support header."""
101        return self._name.startswith("__cxx03/")
102
103    def is_in_modulemap(self) -> bool:
104        """Returns whether a header should be listed in the modulemap."""
105        # TODO: Should `__config_site` be in the modulemap?
106        if self._name == "__config_site":
107            return False
108
109        if self._name == "__assertion_handler":
110            return False
111
112        # exclude libc++abi files
113        if self._name in ["cxxabi.h", "__cxxabi_config.h"]:
114            return False
115
116        # exclude headers in __support/ - these aren't supposed to work everywhere,
117        # so they shouldn't be included in general
118        if self._name.startswith("__support/"):
119            return False
120
121        # exclude ext/ headers - these are non-standard extensions and are barely
122        # maintained. People should migrate away from these and we don't need to
123        # burden ourself with maintaining them in any way.
124        if self._name.startswith("ext/"):
125            return False
126
127        # TODO: Frozen C++03 headers should probably be in the modulemap as well
128        if self.is_cxx03_frozen_header():
129            return False
130
131        return True
132
133    def __str__(self) -> str:
134        return self._name
135
136    def __repr__(self) -> str:
137        return repr(self._name)
138
139    def __eq__(self, other) -> bool:
140        if isinstance(other, str):
141            return self._name == other
142        return self._name == other._name
143
144    def __lt__(self, other) -> bool:
145        if isinstance(other, str):
146            return self._name < other
147        return self._name < other._name
148
149    def __hash__(self) -> int:
150        return hash(self._name)
151
152
153# Commonly-used sets of headers
154all_headers = [Header(p.relative_to(libcxx_include).as_posix()) for p in libcxx_include.rglob("[_a-z]*") if _is_header_file(p)]
155all_headers += [Header("__config_site"), Header("__assertion_handler")] # Headers generated during the build process
156public_headers = [h for h in all_headers if h.is_public()]
157module_headers = [h for h in all_headers if h.has_cxx20_module()]
158module_c_headers = [h for h in all_headers if h.has_cxx20_module() and h.is_cstd()]
159
160# These headers are not yet implemented in libc++
161#
162# These headers are required by the latest (draft) Standard but have not been
163# implemented yet. They are used in the generated module input. The C++23 standard
164# modules will fail to build if a header is added but this list is not updated.
165headers_not_available = list(map(Header, [
166    "debugging",
167    "flat_set",
168    "generator",
169    "hazard_pointer",
170    "inplace_vector",
171    "linalg",
172    "rcu",
173    "spanstream",
174    "stacktrace",
175    "stdfloat",
176    "text_encoding",
177]))
178
179header_restrictions = {
180    # headers with #error directives
181    "atomic": "_LIBCPP_HAS_ATOMIC_HEADER",
182    "stdatomic.h": "_LIBCPP_HAS_ATOMIC_HEADER",
183
184    # headers with #error directives
185    "ios": "_LIBCPP_HAS_LOCALIZATION",
186    # transitive includers of the above headers
187    "clocale": "_LIBCPP_HAS_LOCALIZATION",
188    "codecvt": "_LIBCPP_HAS_LOCALIZATION",
189    "fstream": "_LIBCPP_HAS_LOCALIZATION",
190    "iomanip": "_LIBCPP_HAS_LOCALIZATION",
191    "iostream": "_LIBCPP_HAS_LOCALIZATION",
192    "istream": "_LIBCPP_HAS_LOCALIZATION",
193    "locale": "_LIBCPP_HAS_LOCALIZATION",
194    "ostream": "_LIBCPP_HAS_LOCALIZATION",
195    "regex": "_LIBCPP_HAS_LOCALIZATION",
196    "sstream": "_LIBCPP_HAS_LOCALIZATION",
197    "streambuf": "_LIBCPP_HAS_LOCALIZATION",
198    "strstream": "_LIBCPP_HAS_LOCALIZATION",
199    "syncstream": "_LIBCPP_HAS_LOCALIZATION",
200}
201
202lit_header_restrictions = {
203    "barrier": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
204    "clocale": "// UNSUPPORTED: no-localization",
205    "codecvt": "// UNSUPPORTED: no-localization",
206    "coroutine": "// UNSUPPORTED: c++03, c++11, c++14, c++17",
207    "cwchar": "// UNSUPPORTED: no-wide-characters",
208    "cwctype": "// UNSUPPORTED: no-wide-characters",
209    "experimental/iterator": "// UNSUPPORTED: c++03",
210    "experimental/propagate_const": "// UNSUPPORTED: c++03",
211    "experimental/simd": "// UNSUPPORTED: c++03",
212    "experimental/type_traits": "// UNSUPPORTED: c++03",
213    "experimental/utility": "// UNSUPPORTED: c++03",
214    "filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
215    "fstream": "// UNSUPPORTED: no-localization, no-filesystem",
216    "future": "// UNSUPPORTED: no-threads, c++03",
217    "iomanip": "// UNSUPPORTED: no-localization",
218    "ios": "// UNSUPPORTED: no-localization",
219    "iostream": "// UNSUPPORTED: no-localization",
220    "istream": "// UNSUPPORTED: no-localization",
221    "latch": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
222    "locale": "// UNSUPPORTED: no-localization",
223    "mutex": "// UNSUPPORTED: no-threads, c++03",
224    "ostream": "// UNSUPPORTED: no-localization",
225    "print": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14, c++17, c++20, availability-fp_to_chars-missing", # TODO PRINT investigate
226    "regex": "// UNSUPPORTED: no-localization",
227    "semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
228    "shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
229    "sstream": "// UNSUPPORTED: no-localization",
230    "stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
231    "stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
232    "streambuf": "// UNSUPPORTED: no-localization",
233    "strstream": "// UNSUPPORTED: no-localization",
234    "syncstream": "// UNSUPPORTED: no-localization",
235    "thread": "// UNSUPPORTED: no-threads, c++03",
236    "wchar.h": "// UNSUPPORTED: no-wide-characters",
237    "wctype.h": "// UNSUPPORTED: no-wide-characters",
238}
239
240# Undeprecate headers that are deprecated in C++17 and removed in C++20.
241lit_header_undeprecations = {
242    "ccomplex": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
243    "ciso646": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
244    "cstdalign": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
245    "cstdbool": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
246    "ctgmath": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
247}
248
249# This table was produced manually, by grepping the TeX source of the Standard's
250# library clauses for the string "#include". Each header's synopsis contains
251# explicit "#include" directives for its mandatory inclusions.
252# For example, [algorithm.syn] contains "#include <initializer_list>".
253mandatory_inclusions = {
254    "algorithm": ["initializer_list"],
255    "array": ["compare", "initializer_list"],
256    "bitset": ["iosfwd", "string"],
257    "chrono": ["compare"],
258    "cinttypes": ["cstdint"],
259    "complex.h": ["complex"],
260    "coroutine": ["compare"],
261    "deque": ["compare", "initializer_list"],
262    "filesystem": ["compare"],
263    "flat_map": ["compare", "initializer_list"],
264    "forward_list": ["compare", "initializer_list"],
265    "ios": ["iosfwd"],
266    "iostream": ["ios", "istream", "ostream", "streambuf"],
267    "iterator": ["compare", "concepts"],
268    "list": ["compare", "initializer_list"],
269    "map": ["compare", "initializer_list"],
270    "memory": ["compare"],
271    "optional": ["compare"],
272    "queue": ["compare", "initializer_list"],
273    "random": ["initializer_list"],
274    "ranges": ["compare", "initializer_list", "iterator"],
275    "regex": ["compare", "initializer_list"],
276    "set": ["compare", "initializer_list"],
277    "stack": ["compare", "initializer_list"],
278    "string_view": ["compare"],
279    "string": ["compare", "initializer_list"],
280    "syncstream": ["ostream"],
281    "system_error": ["compare"],
282    "tgmath.h": ["cmath", "complex"],
283    "thread": ["compare"],
284    "tuple": ["compare"],
285    "typeindex": ["compare"],
286    "unordered_map": ["compare", "initializer_list"],
287    "unordered_set": ["compare", "initializer_list"],
288    "utility": ["compare", "initializer_list"],
289    "valarray": ["initializer_list"],
290    "variant": ["compare"],
291    "vector": ["compare", "initializer_list"],
292}
293