[libc++] Make __config_site modular (#134699)

This patch makes the __config_site header modular, which solves various
problems with non-modular headers. This requires going back to
generating the modulemap file, since we only know how to make
__config_site modular when we're not using the per-target runtime dir.

The patch also adds a test that we support
-Wnon-modular-include-in-module, which warns about non-modular includes
from modules.

---------

Co-authored-by: Konstantin Varlamov <varconst@apple.com>
This commit is contained in:
Louis Dionne
2025-04-18 22:06:25 +09:00
committed by GitHub
parent 622765f976
commit 860e88411d
7 changed files with 41 additions and 6 deletions

View File

@@ -116,7 +116,7 @@ sure you don't forget anything:
- Did you add all new named declarations to the ``std`` module?
- If you added a header:
- Did you add it to ``include/module.modulemap``?
- Did you add it to ``include/module.modulemap.in``?
- Did you add it to ``include/CMakeLists.txt``?
- If it's a public header, did you update ``utils/libcxx/header_information.py``?

View File

@@ -1027,7 +1027,6 @@ set(files
mdspan
memory
memory_resource
module.modulemap
mutex
new
numbers
@@ -1667,8 +1666,16 @@ 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)
set(LIBCXX_CONFIG_SITE_MODULE_ENTRY "textual header \"__config_site\"")
endif()
configure_file("module.modulemap.in" "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap" @ONLY)
set(_all_includes "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/__config_site"
"${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler")
"${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler"
"${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap")
foreach(f ${files})
set(src "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
set(dst "${LIBCXX_GENERATED_INCLUDE_DIR}/${f}")
@@ -1726,6 +1733,12 @@ if (LIBCXX_INSTALL_HEADERS)
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
COMPONENT cxx-headers)
# Install the generated modulemap file to the generic include dir.
install(FILES "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap"
DESTINATION "${LIBCXX_INSTALL_INCLUDE_DIR}"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
COMPONENT cxx-headers)
if (NOT CMAKE_CONFIGURATION_TYPES)
add_custom_target(install-cxx-headers
DEPENDS cxx-headers

View File

@@ -1,6 +1,7 @@
// This module contains headers related to the configuration of the library. These headers
// are free of any dependency on the rest of libc++.
module std_config [system] {
@LIBCXX_CONFIG_SITE_MODULE_ENTRY@ // generated via CMake
textual header "__config"
textual header "__configuration/abi.h"
textual header "__configuration/availability.h"

View File

@@ -0,0 +1,21 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: target={{.*}}-apple-{{.*}}
// UNSUPPORTED: c++03
// This test ensures that libc++ supports being compiled with modules enabled and with
// -Wnon-modular-include-in-module. This effectively checks that we don't include any
// non-modular header from the library.
//
// Since most underlying platforms are not modularized properly, this test currently only
// works on Apple platforms.
// ADDITIONAL_COMPILE_FLAGS: -Wnon-modular-include-in-module -Wsystem-headers-in-module=std -fmodules -fcxx-modules
#include <vector>

View File

@@ -4,7 +4,7 @@ import sys
sys.path.append(sys.argv[1])
from libcxx.header_information import all_headers, libcxx_include
with open(libcxx_include / "module.modulemap") as f:
with open(libcxx_include / "module.modulemap.in") as f:
modulemap = f.read()
isHeaderMissing = False

View File

@@ -11,7 +11,7 @@ import re
def exclude_from_consideration(path):
return (
path.endswith(".txt")
or path.endswith(".modulemap")
or path.endswith(".modulemap.in")
or os.path.basename(path) == "__config"
or os.path.basename(path) == "__config_site.in"
or os.path.basename(path) == "libcxx.imp"

View File

@@ -15,7 +15,7 @@ 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",
"module.modulemap.in",
"CMakeLists.txt",
"libcxx.imp",
"__config_site.in",