[libcxx-commits] [libcxx] [libc++] Test existence of generated headers in the modulemap (PR #209312)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 4 08:27:34 PDT 2026
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209312
>From 717e8c1ec35426a2c411421427b38c3e279a42b0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 9 Apr 2025 21:35:27 -0400
Subject: [PATCH 1/5] [libc++] Test existence of generated headers in the
modulemap
The __config_site and __assertion_handler headers were previously
excluded from the modulemap test, but since we now include them in
the modulemap despite being generated, we can test it.
---
libcxx/test/libcxx/headers_in_modulemap.sh.py | 8 +++++---
libcxx/utils/libcxx/header_information.py | 9 +--------
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/libcxx/test/libcxx/headers_in_modulemap.sh.py b/libcxx/test/libcxx/headers_in_modulemap.sh.py
index d4a18a20c8926..f7670365bc521 100644
--- a/libcxx/test/libcxx/headers_in_modulemap.sh.py
+++ b/libcxx/test/libcxx/headers_in_modulemap.sh.py
@@ -1,10 +1,12 @@
-# RUN: %{python} %s %{libcxx-dir}/utils
+# RUN: %{python} %s %{libcxx-dir}/utils %{include-dir}
+import pathlib
import sys
sys.path.append(sys.argv[1])
-from libcxx.header_information import all_headers, libcxx_include
+from libcxx.header_information import all_headers
-with open(libcxx_include / "module.modulemap.in") as f:
+include = pathlib.Path(sys.argv[2])
+with open(include / "module.modulemap") as f:
modulemap = f.read()
isHeaderMissing = False
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index a505d37b65b81..bc8c586c6980a 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -9,7 +9,6 @@
import pathlib, functools
libcxx_root = pathlib.Path(__file__).resolve().parent.parent.parent
-libcxx_include = libcxx_root / "include"
assert libcxx_root.exists()
def _is_header_file(file):
@@ -102,13 +101,6 @@ def is_cxx03_frozen_header(self) -> bool:
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
@@ -151,6 +143,7 @@ def __hash__(self) -> int:
# Commonly-used sets of headers
+libcxx_include = libcxx_root / "include"
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()]
>From 02662c25923f8c1166835acfc2a41267858ee0a2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 3 Aug 2026 12:42:47 -0400
Subject: [PATCH 2/5] Create per-target modulemap
---
libcxx/include/CMakeLists.txt | 19 ++++++++++++++++---
libcxx/include/target.modulemap.in | 3 +++
libcxx/test/libcxx/headers_in_modulemap.sh.py | 7 ++++++-
libcxx/utils/libcxx/header_information.py | 1 +
4 files changed, 26 insertions(+), 4 deletions(-)
create mode 100644 libcxx/include/target.modulemap.in
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8ac94b1b00751..fb77defb92ac2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1694,9 +1694,11 @@ set(files
configure_file("__config_site.in" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/__config_site" @ONLY)
configure_file("${LIBCXX_ASSERTION_HANDLER_FILE}" "${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler" COPYONLY)
-# We generate the modulemap file so that we can include __config_site in it. For now, we don't know how to
-# make __config_site modular when per-target runtime directories are used.
-if (NOT LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
+# We generate the modulemap file so that we can include __config_site in it. When using a target-specific include
+# directory, give it its own modulemap.
+if (LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
+ configure_file("target.modulemap.in" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/module.modulemap" COPYONLY)
+else()
set(LIBCXX_CONFIG_SITE_MODULE_ENTRY "textual header \"__config_site\"")
endif()
configure_file("module.modulemap.in" "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap" @ONLY)
@@ -1704,6 +1706,9 @@ configure_file("module.modulemap.in" "${LIBCXX_GENERATED_INCLUDE_DIR}/module.mod
set(_all_includes "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/__config_site"
"${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler"
"${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap")
+if (LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
+ list(APPEND _all_includes "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/module.modulemap")
+endif()
foreach(f ${files})
set(src "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
set(dst "${LIBCXX_GENERATED_INCLUDE_DIR}/${f}")
@@ -1772,6 +1777,14 @@ if (LIBCXX_INSTALL_HEADERS)
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
COMPONENT cxx-headers)
+ # Install the per-target modulemap when we use per-target include dirs.
+ if (LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
+ install(FILES "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/module.modulemap"
+ DESTINATION "${LIBCXX_INSTALL_INCLUDE_TARGET_DIR}"
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
+ COMPONENT cxx-headers)
+ endif()
+
if (NOT CMAKE_CONFIGURATION_TYPES)
add_custom_target(install-cxx-headers
DEPENDS cxx-headers
diff --git a/libcxx/include/target.modulemap.in b/libcxx/include/target.modulemap.in
new file mode 100644
index 0000000000000..77735e5ad7564
--- /dev/null
+++ b/libcxx/include/target.modulemap.in
@@ -0,0 +1,3 @@
+module std_config_site {
+ textual header "__config_site"
+}
diff --git a/libcxx/test/libcxx/headers_in_modulemap.sh.py b/libcxx/test/libcxx/headers_in_modulemap.sh.py
index f7670365bc521..e44937b49409f 100644
--- a/libcxx/test/libcxx/headers_in_modulemap.sh.py
+++ b/libcxx/test/libcxx/headers_in_modulemap.sh.py
@@ -1,13 +1,18 @@
-# RUN: %{python} %s %{libcxx-dir}/utils %{include-dir}
+# RUN: %{python} %s %{libcxx-dir}/utils %{include-dir} %{target-include-dir}
import pathlib
import sys
sys.path.append(sys.argv[1])
from libcxx.header_information import all_headers
+# Check both the generic modulemap and the target-specific modulemap, if there is one.
include = pathlib.Path(sys.argv[2])
+target_include = pathlib.Path(sys.argv[3])
with open(include / "module.modulemap") as f:
modulemap = f.read()
+if target_include.resolve() != include.resolve():
+ with open(target_include / "module.modulemap") as f:
+ modulemap += f.read()
isHeaderMissing = False
for header in all_headers:
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 841e1149d464e..7fc1cb6d46366 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -15,6 +15,7 @@ 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.in",
+ "target.modulemap.in",
"CMakeLists.txt",
"libcxx.imp",
"__config_site.in",
>From eb89bb5f35cfee3e16f9603f20835cb079e38a1e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 4 Aug 2026 09:03:34 -0400
Subject: [PATCH 3/5] Don't generate target modulemap
---
libcxx/include/CMakeLists.txt | 6 +++---
libcxx/include/{target.modulemap.in => target.modulemap} | 0
libcxx/utils/libcxx/header_information.py | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
rename libcxx/include/{target.modulemap.in => target.modulemap} (100%)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index fb77defb92ac2..d446eb7b894e9 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1694,10 +1694,10 @@ set(files
configure_file("__config_site.in" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/__config_site" @ONLY)
configure_file("${LIBCXX_ASSERTION_HANDLER_FILE}" "${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler" COPYONLY)
-# We generate the modulemap file so that we can include __config_site in it. When using a target-specific include
-# directory, give it its own modulemap.
+# We generate the main modulemap file so that we can include __config_site in it. When using a target-specific
+# include directory, give it its own modulemap.
if (LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
- configure_file("target.modulemap.in" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/module.modulemap" COPYONLY)
+ configure_file("target.modulemap" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/module.modulemap" COPYONLY)
else()
set(LIBCXX_CONFIG_SITE_MODULE_ENTRY "textual header \"__config_site\"")
endif()
diff --git a/libcxx/include/target.modulemap.in b/libcxx/include/target.modulemap
similarity index 100%
rename from libcxx/include/target.modulemap.in
rename to libcxx/include/target.modulemap
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 7fc1cb6d46366..ee067d5eae264 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -15,7 +15,7 @@ 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.in",
- "target.modulemap.in",
+ "target.modulemap",
"CMakeLists.txt",
"libcxx.imp",
"__config_site.in",
>From cb79ca769a6664ec67cae8ca37c6bb0fdec4a62b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 4 Aug 2026 11:26:47 -0400
Subject: [PATCH 4/5] Handle target modulemap in lint headers
---
libcxx/test/libcxx/lint/lint_headers.sh.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/test/libcxx/lint/lint_headers.sh.py b/libcxx/test/libcxx/lint/lint_headers.sh.py
index ab237c968da7e..34eb5c000d237 100644
--- a/libcxx/test/libcxx/lint/lint_headers.sh.py
+++ b/libcxx/test/libcxx/lint/lint_headers.sh.py
@@ -12,6 +12,7 @@ def exclude_from_consideration(path):
return (
path.endswith(".txt")
or path.endswith(".modulemap.in")
+ or os.path.basename(path) == "target.modulemap"
or os.path.basename(path) == "__config"
or os.path.basename(path) == "__config_site.in"
or os.path.basename(path) == "libcxx.imp"
>From a6c1b4a51955b13add6c4d5704a1329ee10c4ca0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 4 Aug 2026 11:27:18 -0400
Subject: [PATCH 5/5] Frozen c++03 headers too
---
libcxx/test/libcxx-03/lint/lint_headers.sh.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/test/libcxx-03/lint/lint_headers.sh.py b/libcxx/test/libcxx-03/lint/lint_headers.sh.py
index ab237c968da7e..34eb5c000d237 100644
--- a/libcxx/test/libcxx-03/lint/lint_headers.sh.py
+++ b/libcxx/test/libcxx-03/lint/lint_headers.sh.py
@@ -12,6 +12,7 @@ def exclude_from_consideration(path):
return (
path.endswith(".txt")
or path.endswith(".modulemap.in")
+ or os.path.basename(path) == "target.modulemap"
or os.path.basename(path) == "__config"
or os.path.basename(path) == "__config_site.in"
or os.path.basename(path) == "libcxx.imp"
More information about the libcxx-commits
mailing list