[libcxx-commits] [libcxx] [libc++] Fix issue with running transitive includes tests on Docker-in-Docker (PR #110554)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Oct 16 16:56:45 PDT 2024
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/110554
>From 162bef91efd7f0385eeb1ffaa4fcf3283560d91e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 16 Oct 2024 19:09:34 -0400
Subject: [PATCH] [libc++] Rewrite the transitive header checking machinery
Since we don't generate a full dependency graph of headers, we can
greatly simplify the script that parses the result of --trace-includes.
At the same time, we also unify the mechanism for detecting whether a
header is a public/C compat/internal/etc header with the existing
mechanism in header_information.py.
As a drive-by this fixes the headers_in_modulemap.sh.py test which had
been disabled by mistake because it used its own way of determining
the list of libc++ headers. By consistently using header_information.py
to get that information, problems like this shouldn't happen anymore.
---
libcxx/test/libcxx/header_inclusions.gen.py | 2 +-
libcxx/test/libcxx/headers_in_modulemap.sh.py | 21 +-
libcxx/test/libcxx/transitive_includes.gen.py | 12 +-
.../test/libcxx/transitive_includes/to_csv.py | 120 ++++++++
.../test/libcxx/transitive_includes_to_csv.py | 147 ----------
libcxx/utils/generate_iwyu_mapping.py | 2 +-
libcxx/utils/generate_libcxx_cppm_in.py | 14 +-
libcxx/utils/libcxx/header_information.py | 277 ++++++++++--------
8 files changed, 298 insertions(+), 297 deletions(-)
create mode 100755 libcxx/test/libcxx/transitive_includes/to_csv.py
delete mode 100755 libcxx/test/libcxx/transitive_includes_to_csv.py
diff --git a/libcxx/test/libcxx/header_inclusions.gen.py b/libcxx/test/libcxx/header_inclusions.gen.py
index 2ecc47cbb1891a..e5def1ad4cb70d 100644
--- a/libcxx/test/libcxx/header_inclusions.gen.py
+++ b/libcxx/test/libcxx/header_inclusions.gen.py
@@ -16,7 +16,7 @@
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('/', '_')}"
+ header_guard = lambda h: f"_LIBCPP_{str(h).upper().replace('.', '_').replace('/', '_')}"
# <cassert> has no header guards
if header == 'cassert':
diff --git a/libcxx/test/libcxx/headers_in_modulemap.sh.py b/libcxx/test/libcxx/headers_in_modulemap.sh.py
index 237b006115ccdf..0302278edc7d6e 100644
--- a/libcxx/test/libcxx/headers_in_modulemap.sh.py
+++ b/libcxx/test/libcxx/headers_in_modulemap.sh.py
@@ -1,25 +1,16 @@
-# RUN: %{python} %s %{libcxx-dir}/utils %{include-dir}
+# RUN: %{python} %s %{libcxx-dir}/utils
import sys
-
sys.path.append(sys.argv[1])
-
import pathlib
-import sys
-from libcxx.header_information import is_modulemap_header, is_header
+from libcxx.header_information import all_headers, libcxx_include
-headers = list(pathlib.Path(sys.argv[2]).rglob("*"))
-modulemap = open(f"{sys.argv[2]}/module.modulemap").read()
+with open(libcxx_include / "module.modulemap") as f:
+ modulemap = f.read()
isHeaderMissing = False
-
-for header in headers:
- if not is_header(header):
- continue
-
- header = header.relative_to(pathlib.Path(sys.argv[2])).as_posix()
-
- if not is_modulemap_header(header):
+for header in all_headers:
+ if not header.is_in_modulemap():
continue
if not str(header) in modulemap:
diff --git a/libcxx/test/libcxx/transitive_includes.gen.py b/libcxx/test/libcxx/transitive_includes.gen.py
index 22075364bf1b79..2693617bcb0e5b 100644
--- a/libcxx/test/libcxx/transitive_includes.gen.py
+++ b/libcxx/test/libcxx/transitive_includes.gen.py
@@ -42,10 +42,10 @@
all_traces = []
for header in sorted(public_headers):
- if header.endswith(".h"): # Skip C compatibility or detail headers
+ if header.is_C_compatibility() or header.is_internal():
continue
- normalized_header = re.sub("/", "_", header)
+ normalized_header = re.sub("/", "_", str(header))
print(
f"""\
// RUN: echo "#include <{header}>" | %{{cxx}} -xc++ - %{{flags}} %{{compile_flags}} --trace-includes -fshow-skipped-includes --preprocess > /dev/null 2> %t/trace-includes.{normalized_header}.txt
@@ -55,17 +55,17 @@
print(
f"""\
-// RUN: %{{python}} %{{libcxx-dir}}/test/libcxx/transitive_includes_to_csv.py {' '.join(all_traces)} > %{{libcxx-dir}}/test/libcxx/transitive_includes/%{{cxx_std}}.csv
+// RUN: %{{python}} %{{libcxx-dir}}/test/libcxx/transitive_includes/to_csv.py {' '.join(all_traces)} > %{{libcxx-dir}}/test/libcxx/transitive_includes/%{{cxx_std}}.csv
"""
)
else:
for header in public_headers:
- if header.endswith(".h"): # Skip C compatibility or detail headers
+ if header.is_C_compatibility() or header.is_internal():
continue
# Escape slashes for the awk command below
- escaped_header = header.replace("/", "\\/")
+ escaped_header = str(header).replace("/", "\\/")
print(
f"""\
@@ -92,7 +92,7 @@
// RUN: mkdir %t
// RUN: %{{cxx}} %s %{{flags}} %{{compile_flags}} --trace-includes -fshow-skipped-includes --preprocess > /dev/null 2> %t/trace-includes.txt
-// RUN: %{{python}} %{{libcxx-dir}}/test/libcxx/transitive_includes_to_csv.py %t/trace-includes.txt > %t/actual_transitive_includes.csv
+// RUN: %{{python}} %{{libcxx-dir}}/test/libcxx/transitive_includes/to_csv.py %t/trace-includes.txt > %t/actual_transitive_includes.csv
// RUN: cat %{{libcxx-dir}}/test/libcxx/transitive_includes/%{{cxx_std}}.csv | awk '/^{escaped_header} / {{ print }}' > %t/expected_transitive_includes.csv
// RUN: diff -w %t/expected_transitive_includes.csv %t/actual_transitive_includes.csv
#include <{header}>
diff --git a/libcxx/test/libcxx/transitive_includes/to_csv.py b/libcxx/test/libcxx/transitive_includes/to_csv.py
new file mode 100755
index 00000000000000..69d94deedf6f50
--- /dev/null
+++ b/libcxx/test/libcxx/transitive_includes/to_csv.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+from typing import List, Tuple, Optional
+import argparse
+import io
+import itertools
+import os
+import pathlib
+import re
+import sys
+
+libcxx_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+sys.path.append(os.path.join(libcxx_root, "utils"))
+from libcxx.header_information import Header
+
+def parse_line(line: str) -> Tuple[int, str]:
+ """
+ Parse a single line of --trace-includes output.
+
+ Returns the inclusion level and the raw file name being included.
+ """
+ match = re.match(r"(\.+) (.+)", line)
+ if not match:
+ raise ArgumentError(f"Line {line} contains invalid data.")
+
+ # The number of periods in front of the header name is the nesting level of
+ # that header.
+ return (len(match.group(1)), match.group(2))
+
+def make_cxx_v1_relative(header: str) -> Optional[str]:
+ """
+ Returns the path of the header as relative to <whatever>/c++/v1, or None if the path
+ doesn't contain c++/v1.
+
+ We use that heuristic to figure out which headers are libc++ headers.
+ """
+ # On Windows, the path separators can either be forward slash or backslash.
+ # If it is a backslash, Clang prints it escaped as two consecutive
+ # backslashes, and they need to be escaped in the RE. (Use a raw string for
+ # the pattern to avoid needing another level of escaping on the Python string
+ # literal level.)
+ pathsep = r"(?:/|\\\\)"
+ CXX_V1_REGEX = r"^.*c\+\+" + pathsep + r"v[0-9]+" + pathsep + r"(.+)$"
+ match = re.match(CXX_V1_REGEX, header)
+ if not match:
+ return None
+ else:
+ return match.group(1)
+
+def parse_file(file: io.TextIOBase) -> List[Tuple[Header, Header]]:
+ """
+ Parse a file containing --trace-includes output to generate a list of the
+ transitive includes contained in it.
+ """
+ result = []
+ includer = None
+ for line in file.readlines():
+ (level, header) = parse_line(line)
+ relative = make_cxx_v1_relative(header)
+
+ # Not a libc++ header
+ if relative is None:
+ continue
+
+ # If we're at the first level, remember this header as being the one who includes other headers.
+ # There's usually exactly one, except if the compiler is passed a file with `-include`.
+ if level == 1:
+ includer = Header(relative)
+ continue
+
+ # Otherwise, take note that this header is being included by the top-level includer.
+ else:
+ assert includer is not None
+ result.append((includer, Header(relative)))
+ return result
+
+def print_csv(includes: List[Tuple[Header, Header]]) -> None:
+ """
+ Print the transitive includes as space-delimited CSV.
+
+ This function only prints public libc++ headers that are not C compatibility headers.
+ """
+ # Sort and group by includer
+ by_includer = lambda t: t[0]
+ includes = itertools.groupby(sorted(includes, key=by_includer), key=by_includer)
+
+ for (includer, includees) in includes:
+ includees = map(lambda t: t[1], includees)
+ for h in sorted(set(includees)):
+ if h.is_public() and not h.is_C_compatibility():
+ print(f"{includer} {h}")
+
+def main(argv):
+ parser = argparse.ArgumentParser(
+ description="""
+ Given a list of headers produced by --trace-includes, produce a list of libc++ headers in that output.
+
+ Note that -fshow-skipped-includes must also be passed to the compiler in order to get sufficient
+ information for this script to run.
+
+ The output of this script is provided in space-delimited CSV format where each line contains:
+
+ <header performing inclusion> <header being included>
+ """)
+ parser.add_argument("inputs", type=argparse.FileType("r"), nargs='+', default=None,
+ help="One or more files containing the result of --trace-includes")
+ args = parser.parse_args(argv)
+
+ includes = [line for file in args.inputs for line in parse_file(file)]
+ print_csv(includes)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/libcxx/test/libcxx/transitive_includes_to_csv.py b/libcxx/test/libcxx/transitive_includes_to_csv.py
deleted file mode 100755
index 73571fb404ae7b..00000000000000
--- a/libcxx/test/libcxx/transitive_includes_to_csv.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/env python
-# ===----------------------------------------------------------------------===##
-#
-# 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
-#
-# ===----------------------------------------------------------------------===##
-
-from dataclasses import dataclass
-from typing import List # Needed for python 3.8 compatibility.
-import argparse
-import pathlib
-import re
-import sys
-
-
- at dataclass
-class header:
- name: str = None
- level: int = -1
-
-
-def parse_line(line: str) -> header:
- """
- Parse an output line from --trace-includes into a `header`.
- """
- match = re.match(r"(\.+) (.+)", line)
- if not match:
- sys.exit(f"Line {line} contains invalid data.")
-
- # The number of periods in front of the header name is the nesting level of
- # that header.
- return header(match.group(2), len(match.group(1)))
-
-
-# On Windows, the path separators can either be forward slash or backslash.
-# If it is a backslash, Clang prints it escaped as two consecutive
-# backslashes, and they need to be escaped in the RE. (Use a raw string for
-# the pattern to avoid needing another level of escaping on the Python string
-# literal level.)
-LIBCXX_HEADER_REGEX = r".*c\+\+(?:/|\\\\)v[0-9]+(?:/|\\\\)(.+)"
-
-def is_libcxx_header(header: str) -> bool:
- """
- Returns whether a header is a libc++ header, excluding the C-compatibility headers.
- """
- # Only keep files in the c++/vN directory.
- match = re.match(LIBCXX_HEADER_REGEX, header)
- if not match:
- return False
-
- # Skip C compatibility headers (in particular, make sure not to skip libc++ detail headers).
- relative = match.group(1)
- if relative.endswith(".h") and not (
- relative.startswith("__") or re.search(r"(/|\\\\)__", relative)
- ):
- return False
-
- return True
-
-
-def parse_file(file: pathlib.Path) -> List[str]:
- """
- Parse a file containing --trace-includes output to generate a list of the top-level C++ includes
- contained in it.
-
- This effectively generates the list of public libc++ headers of the header whose --trace-includes it is.
- In order to get the expected result of --trace-includes, the -fshow-skipped-includes flag also needs to
- be passed.
- """
- result = list()
- with file.open(encoding="utf-8") as f:
- for line in f.readlines():
- header = parse_line(line)
-
- # Skip non-libc++ headers
- if not is_libcxx_header(header.name):
- continue
-
- # Include top-level headers in the output. There's usually exactly one,
- # except if the compiler is passed a file with `-include`. Top-level
- # headers are transparent, in the sense that we want to go look at
- # transitive includes underneath.
- if header.level == 1:
- level = 999
- result.append(header)
- continue
-
- # Detail headers are transparent too: we attribute all includes of public libc++
- # headers under a detail header to the last public libc++ header that included it.
- if header.name.startswith("__") or re.search(r"(/|\\\\)__", header.name):
- level = 999
- continue
-
- # Add the non-detail libc++ header to the list.
- level = header.level
- result.append(header)
- return result
-
-
-def create_include_graph(trace_includes: List[pathlib.Path]) -> List[str]:
- result = list()
- for file in trace_includes:
- headers = parse_file(file)
-
- # Get actual filenames relative to libc++'s installation directory instead of full paths
- relative = lambda h: re.match(LIBCXX_HEADER_REGEX, h).group(1)
-
- top_level = relative(
- next(h.name for h in headers if h.level == 1)
- ) # There should be only one top-level header
- includes = [relative(h.name) for h in headers if h.level != 1]
-
- # Remove duplicates in all includes.
- includes = list(set(includes))
-
- if len(includes) != 0:
- result.append([top_level] + includes)
- return result
-
-
-def print_csv(graph: List[str]) -> None:
- for includes in graph:
- header = includes[0]
- for include in sorted(includes[1:]):
- if header == include:
- sys.exit(f"Cycle detected: header {header} includes itself.")
- print(f"{header} {include}")
-
-
-if __name__ == "__main__":
- parser = argparse.ArgumentParser(
- description="""Produce a dependency graph of libc++ headers, in CSV format.
-This script is normally executed by libcxx/test/libcxx/transitive_includes.gen.py""",
- formatter_class=argparse.RawDescriptionHelpFormatter,
- )
- parser.add_argument(
- "inputs",
- default=None,
- metavar="FILE",
- nargs='+',
- help="One or more files containing the result of --trace-includes on the headers one wishes to graph.",
- )
- options = parser.parse_args()
-
- print_csv(create_include_graph(map(pathlib.Path, options.inputs)))
diff --git a/libcxx/utils/generate_iwyu_mapping.py b/libcxx/utils/generate_iwyu_mapping.py
index 599201808bb79b..7976f4afc24fa8 100644
--- a/libcxx/utils/generate_iwyu_mapping.py
+++ b/libcxx/utils/generate_iwyu_mapping.py
@@ -71,7 +71,7 @@ def main(argv: typing.List[str]):
mappings = [] # Pairs of (header, public_header)
for header in libcxx.header_information.all_headers:
- public_headers = IWYU_mapping(header)
+ public_headers = IWYU_mapping(str(header))
if public_headers is not None:
mappings.extend((header, public) for public in public_headers)
diff --git a/libcxx/utils/generate_libcxx_cppm_in.py b/libcxx/utils/generate_libcxx_cppm_in.py
index e98ac1b2433144..39076a61b55b84 100644
--- a/libcxx/utils/generate_libcxx_cppm_in.py
+++ b/libcxx/utils/generate_libcxx_cppm_in.py
@@ -9,19 +9,11 @@
import os.path
import sys
-from libcxx.header_information import module_c_headers
-from libcxx.header_information import module_headers
-from libcxx.header_information import header_restrictions
-from libcxx.header_information import headers_not_available
+from libcxx.header_information import module_c_headers, module_headers, header_restrictions, headers_not_available, libcxx_root
def write_file(module):
- libcxx_module_directory = os.path.join(
- os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "modules"
- )
- with open(
- os.path.join(libcxx_module_directory, f"{module}.cppm.in"), "w"
- ) as module_cpp_in:
+ with open(libcxx_root / "modules" / f"{module}.cppm.in", "w") as module_cpp_in:
module_cpp_in.write(
"""\
// -*- C++ -*-
@@ -45,7 +37,7 @@ def write_file(module):
// and the headers of Table 25: C++ headers for C library facilitiesā[tab:headers.cpp.c]
"""
)
- for header in module_headers if module == "std" else module_c_headers:
+ for header in sorted(module_headers if module == "std" else module_c_headers):
if header in header_restrictions:
module_cpp_in.write(
f"""\
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 6bebf3302ffae9..64637c73a57b71 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -6,7 +6,167 @@
#
# ===----------------------------------------------------------------------===##
-import os, pathlib
+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",
+ ]
+
+ at 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",
+ "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", "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_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
+ 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
+experimental_headers = [h for h in all_headers if h.is_experimental()]
+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_map",
+ "flat_set",
+ "generator",
+ "hazard_pointer",
+ "inplace_vector",
+ "linalg",
+ "rcu",
+ "spanstream",
+ "stacktrace",
+ "stdfloat",
+ "text_encoding",
+]))
header_restrictions = {
# headers with #error directives
@@ -114,118 +274,3 @@
"variant": ["compare"],
"vector": ["compare", "initializer_list"],
}
-
-
-# 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 = [
- "debugging",
- "flat_map",
- "flat_set",
- "generator",
- "hazard_pointer",
- "inplace_vector",
- "linalg",
- "rcu",
- "spanstream",
- "stacktrace",
- "stdfloat",
- "text_encoding",
-]
-
-
-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 in [
- "module.modulemap",
- "CMakeLists.txt",
- "libcxx.imp",
- ]
-
-
-def is_public_header(header):
- return "__" not in header and not header.startswith("ext/")
-
-
-def is_modulemap_header(header):
- """Returns whether a header should be listed in the modulemap"""
- # TODO: Should `__config_site` be in the modulemap?
- if header == "__config_site":
- return False
-
- if header == "__assertion_handler":
- return False
-
- # exclude libc++abi files
- if header 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 header.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 header.startswith("ext/"):
- return False
- return True
-
-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()
-
-all_headers = sorted(
- p.relative_to(include).as_posix() for p in include.rglob("[_a-z]*") if is_header(p)
-)
-toplevel_headers = sorted(
- p.relative_to(include).as_posix() for p in include.glob("[_a-z]*") if is_header(p)
-)
-experimental_headers = sorted(
- p.relative_to(include).as_posix()
- for p in include.glob("experimental/[a-z]*")
- if is_header(p)
-)
-
-public_headers = [p for p in all_headers if is_public_header(p)]
-
-# The headers used in the std and std.compat modules.
-#
-# This is the set of all C++23-and-later headers, excluding C compatibility headers.
-module_headers = [
- header
- for header in toplevel_headers
- if not header.endswith(".h") and is_public_header(header)
- # These headers have been removed in C++20 so are never part of a module.
- and not header in ["ccomplex", "ciso646", "cstdbool", "ctgmath"]
-]
-
-# The C headers used in the std and std.compat modules.
-module_c_headers = [
- "cassert",
- "cctype",
- "cerrno",
- "cfenv",
- "cfloat",
- "cinttypes",
- "climits",
- "clocale",
- "cmath",
- "csetjmp",
- "csignal",
- "cstdarg",
- "cstddef",
- "cstdint",
- "cstdio",
- "cstdlib",
- "cstring",
- "ctime",
- "cuchar",
- "cwchar",
- "cwctype",
-]
More information about the libcxx-commits
mailing list