blob: 7d88b73d0331bb1707ab03c037ca7c750234ce2e [file] [log] [blame]
# ===----------------------------------------------------------------------===##
#
# 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
#
# ===----------------------------------------------------------------------===##
import os, pathlib, functools
libcxx_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
libcxx_include = libcxx_root / "include"
assert libcxx_root.exists()
def _is_header_file(file):
"""Returns whether the given file is a header file, i.e. not a directory or the modulemap file."""
return not file.is_dir() and not file.name in [
"module.modulemap",
"CMakeLists.txt",
"libcxx.imp",
"__config_site.in",
]
@functools.total_ordering
class Header:
_name: str
"""Relative path from the root of libcxx/include"""
def __init__(self, name: str):
"""Create a Header.
name: The path of the header relative to libc++'s include directory.
For example '__algorithm/find.h' or 'coroutine'.
"""
self._name = name
def is_public(self) -> bool:
"""Returns whether the header is a public libc++ API header."""
return "__" not in self._name and not self._name.startswith("ext/")
def is_internal(self) -> bool:
"""Returns whether the header is an internal implementation detail of the library."""
return not self.is_public()
def is_C_compatibility(self) -> bool:
"""
Returns whether the header is a C compatibility header (headers ending in .h like stdlib.h).
Note that headers like <cstdlib> are not considered C compatibility headers.
"""
return self.is_public() and self._name.endswith(".h")
def is_cstd(self) -> bool:
"""Returns whether the header is a C 'std' header, like <cstddef>, <cerrno>, etc."""
return self._name in [
"cassert",
"ccomplex",
"cctype",
"cerrno",
"cfenv",
"cfloat",
"cinttypes",
"ciso646",
"climits",
"clocale",
"cmath",
"csetjmp",
"csignal",
"cstdalign",
"cstdarg",
"cstdbool",
"cstddef",
"cstdint",
"cstdio",
"cstdlib",
"cstring",
"ctgmath",
"ctime",
"cuchar",
"cwchar",
"cwctype",
]
def is_experimental(self) -> bool:
"""Returns whether the header is a public experimental header."""
return self.is_public() and self._name.startswith("experimental/")
def has_cxx20_module(self) -> bool:
"""
Returns whether the header is in the std and std.compat C++20 modules.
These headers are all C++23-and-later headers, excluding C compatibility headers and
experimental headers.
"""
# These headers have been removed in C++20 so are never part of a module.
removed_in_20 = ["ccomplex", "ciso646", "cstdalign", "cstdbool", "ctgmath"]
return self.is_public() and not self.is_experimental() and not self.is_C_compatibility() and not self._name in removed_in_20
def is_cxx03_frozen_header(self) -> bool:
"""Returns whether the header is a frozen C++03 support header."""
return self._name.startswith("__cxx03/")
def is_in_modulemap(self) -> bool:
"""Returns whether a header should be listed in the modulemap."""
# TODO: Should `__config_site` be in the modulemap?
if self._name == "__config_site":
return False
if self._name == "__assertion_handler":
return False
# exclude libc++abi files
if self._name in ["cxxabi.h", "__cxxabi_config.h"]:
return False
# exclude headers in __support/ - these aren't supposed to work everywhere,
# so they shouldn't be included in general
if self._name.startswith("__support/"):
return False
# exclude ext/ headers - these are non-standard extensions and are barely
# maintained. People should migrate away from these and we don't need to
# burden ourself with maintaining them in any way.
if self._name.startswith("ext/"):
return False
# TODO: Frozen C++03 headers should probably be in the modulemap as well
if self.is_cxx03_frozen_header():
return False
return True
def __str__(self) -> str:
return self._name
def __repr__(self) -> str:
return repr(self._name)
def __eq__(self, other) -> bool:
if isinstance(other, str):
return self._name == other
return self._name == other._name
def __lt__(self, other) -> bool:
if isinstance(other, str):
return self._name < other
return self._name < other._name
def __hash__(self) -> int:
return hash(self._name)
# Commonly-used sets of headers
all_headers = [Header(p.relative_to(libcxx_include).as_posix()) for p in libcxx_include.rglob("[_a-z]*") if _is_header_file(p)]
all_headers += [Header("__config_site"), Header("__assertion_handler")] # Headers generated during the build process
public_headers = [h for h in all_headers if h.is_public()]
module_headers = [h for h in all_headers if h.has_cxx20_module()]
module_c_headers = [h for h in all_headers if h.has_cxx20_module() and h.is_cstd()]
# These headers are not yet implemented in libc++
#
# These headers are required by the latest (draft) Standard but have not been
# implemented yet. They are used in the generated module input. The C++23 standard
# modules will fail to build if a header is added but this list is not updated.
headers_not_available = list(map(Header, [
"debugging",
"flat_set",
"generator",
"hazard_pointer",
"inplace_vector",
"linalg",
"rcu",
"spanstream",
"stacktrace",
"stdfloat",
"text_encoding",
]))
header_restrictions = {
# headers with #error directives
"atomic": "_LIBCPP_HAS_ATOMIC_HEADER",
"stdatomic.h": "_LIBCPP_HAS_ATOMIC_HEADER",
# headers with #error directives
"ios": "_LIBCPP_HAS_LOCALIZATION",
# transitive includers of the above headers
"clocale": "_LIBCPP_HAS_LOCALIZATION",
"codecvt": "_LIBCPP_HAS_LOCALIZATION",
"fstream": "_LIBCPP_HAS_LOCALIZATION",
"iomanip": "_LIBCPP_HAS_LOCALIZATION",
"iostream": "_LIBCPP_HAS_LOCALIZATION",
"istream": "_LIBCPP_HAS_LOCALIZATION",
"locale": "_LIBCPP_HAS_LOCALIZATION",
"ostream": "_LIBCPP_HAS_LOCALIZATION",
"regex": "_LIBCPP_HAS_LOCALIZATION",
"sstream": "_LIBCPP_HAS_LOCALIZATION",
"streambuf": "_LIBCPP_HAS_LOCALIZATION",
"strstream": "_LIBCPP_HAS_LOCALIZATION",
"syncstream": "_LIBCPP_HAS_LOCALIZATION",
}
lit_header_restrictions = {
"barrier": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"clocale": "// UNSUPPORTED: no-localization",
"codecvt": "// UNSUPPORTED: no-localization",
"coroutine": "// UNSUPPORTED: c++03, c++11, c++14, c++17",
"cwchar": "// UNSUPPORTED: no-wide-characters",
"cwctype": "// UNSUPPORTED: no-wide-characters",
"experimental/iterator": "// UNSUPPORTED: c++03",
"experimental/propagate_const": "// UNSUPPORTED: c++03",
"experimental/simd": "// UNSUPPORTED: c++03",
"experimental/type_traits": "// UNSUPPORTED: c++03",
"experimental/utility": "// UNSUPPORTED: c++03",
"filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
"fstream": "// UNSUPPORTED: no-localization, no-filesystem",
"future": "// UNSUPPORTED: no-threads, c++03",
"iomanip": "// UNSUPPORTED: no-localization",
"ios": "// UNSUPPORTED: no-localization",
"iostream": "// UNSUPPORTED: no-localization",
"istream": "// UNSUPPORTED: no-localization",
"latch": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"locale": "// UNSUPPORTED: no-localization",
"mutex": "// UNSUPPORTED: no-threads, c++03",
"ostream": "// UNSUPPORTED: no-localization",
"print": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14, c++17, c++20, availability-fp_to_chars-missing", # TODO PRINT investigate
"regex": "// UNSUPPORTED: no-localization",
"semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
"sstream": "// UNSUPPORTED: no-localization",
"stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
"stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"streambuf": "// UNSUPPORTED: no-localization",
"strstream": "// UNSUPPORTED: no-localization",
"syncstream": "// UNSUPPORTED: no-localization",
"thread": "// UNSUPPORTED: no-threads, c++03",
"wchar.h": "// UNSUPPORTED: no-wide-characters",
"wctype.h": "// UNSUPPORTED: no-wide-characters",
}
# Undeprecate headers that are deprecated in C++17 and removed in C++20.
lit_header_undeprecations = {
"ccomplex": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
"ciso646": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
"cstdalign": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
"cstdbool": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
"ctgmath": "// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
}
# This table was produced manually, by grepping the TeX source of the Standard's
# library clauses for the string "#include". Each header's synopsis contains
# explicit "#include" directives for its mandatory inclusions.
# For example, [algorithm.syn] contains "#include <initializer_list>".
mandatory_inclusions = {
"algorithm": ["initializer_list"],
"array": ["compare", "initializer_list"],
"bitset": ["iosfwd", "string"],
"chrono": ["compare"],
"cinttypes": ["cstdint"],
"complex.h": ["complex"],
"coroutine": ["compare"],
"deque": ["compare", "initializer_list"],
"filesystem": ["compare"],
"flat_map": ["compare", "initializer_list"],
"forward_list": ["compare", "initializer_list"],
"ios": ["iosfwd"],
"iostream": ["ios", "istream", "ostream", "streambuf"],
"iterator": ["compare", "concepts"],
"list": ["compare", "initializer_list"],
"map": ["compare", "initializer_list"],
"memory": ["compare"],
"optional": ["compare"],
"queue": ["compare", "initializer_list"],
"random": ["initializer_list"],
"ranges": ["compare", "initializer_list", "iterator"],
"regex": ["compare", "initializer_list"],
"set": ["compare", "initializer_list"],
"stack": ["compare", "initializer_list"],
"string_view": ["compare"],
"string": ["compare", "initializer_list"],
"syncstream": ["ostream"],
"system_error": ["compare"],
"tgmath.h": ["cmath", "complex"],
"thread": ["compare"],
"tuple": ["compare"],
"typeindex": ["compare"],
"unordered_map": ["compare", "initializer_list"],
"unordered_set": ["compare", "initializer_list"],
"utility": ["compare", "initializer_list"],
"valarray": ["initializer_list"],
"variant": ["compare"],
"vector": ["compare", "initializer_list"],
}