[libc++][modules] Adds the C++23 std module.

The patch is based on D144994.

D151030 added the module definitions for the module std.
This patch wires in the module and enables the basic testing.

Some notable features are missing:
- There is no test that libc++ can be fully imported as a module.
- This lacks the parts for the std.compat module.
- The module is not shipped with libc++.

Implements parts of
- P2465R3 Standard Library Modules std and std.compat

Reviewed By: ldionne, aaronmondal, #libc

Differential Revision: https://reviews.llvm.org/D151814
This commit is contained in:
Mark de Wever 2023-02-28 20:29:26 +01:00
parent 6551e59802
commit 9b08c8a225
24 changed files with 1132 additions and 12 deletions

View File

@ -97,6 +97,12 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
the shared library they shipped should turn this on and see `include/__availability`
for more details." OFF)
option(LIBCXX_ENABLE_CLANG_TIDY "Whether to compile and run clang-tidy checks" OFF)
# TODO MODULES Remove this option and test for the requirements (CMake/Clang) instead.
option(LIBCXX_ENABLE_STD_MODULES
"Whether to enable the building the C++23 `std` module. This feature is
experimental and has additional dependencies. Only enable this when
interested in testing or developing this module. See
https://libcxx.llvm.org/Modules.html for more information." OFF)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
@ -410,6 +416,7 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE})
set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1")
set(LIBCXX_GENERATED_MODULE_DIR "${LLVM_BINARY_DIR}/modules/c++/v1")
set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LLVM_BINARY_DIR}/include/${LLVM_DEFAULT_TARGET_TRIPLE}/c++/v1")
set(LIBCXX_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE} CACHE PATH
"Path where built libc++ libraries should be installed.")
@ -423,9 +430,11 @@ else()
if(LLVM_LIBRARY_OUTPUT_INTDIR)
set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR})
set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1")
set(LIBCXX_GENERATED_MODULE_DIR "${LLVM_BINARY_DIR}/modules/c++/v1")
else()
set(LIBCXX_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXX_LIBDIR_SUFFIX})
set(LIBCXX_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/c++/v1")
set(LIBCXX_GENERATED_MODULE_DIR "${CMAKE_BINARY_DIR}/modules/c++/v1")
endif()
set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LIBCXX_GENERATED_INCLUDE_DIR}")
set(LIBCXX_INSTALL_LIBRARY_DIR lib${LIBCXX_LIBDIR_SUFFIX} CACHE PATH
@ -856,6 +865,9 @@ endfunction()
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(utils)
if (LIBCXX_ENABLE_STD_MODULES)
add_subdirectory(modules)
endif()
set(LIBCXX_TEST_DEPS "cxx_experimental")
@ -867,6 +879,10 @@ if (LIBCXX_ENABLE_CLANG_TIDY)
list(APPEND LIBCXX_TEST_DEPS cxx-tidy)
endif()
if (LIBCXX_ENABLE_STD_MODULES)
list(APPEND LIBCXX_TEST_DEPS generate-cxx-modules generate-test-module-std)
endif()
if (LIBCXX_INCLUDE_BENCHMARKS)
add_subdirectory(benchmarks)
endif()

View File

@ -0,0 +1,4 @@
set(LIBCXX_ENABLE_STD_MODULES ON CACHE BOOL "")
set(LIBCXX_TEST_PARAMS "enable_modules=std;std=c++23" CACHE STRING "")
set(LIBCXXABI_TEST_PARAMS "std=c++23" CACHE STRING "")

View File

@ -1,2 +1,2 @@
set(LIBCXX_TEST_PARAMS "enable_modules=True;enable_modules_lsv=True" CACHE STRING "")
set(LIBCXX_TEST_PARAMS "enable_modules=clang;enable_modules_lsv=True" CACHE STRING "")
set(LIBCXXABI_TEST_PARAMS "${LIBCXX_TEST_PARAMS}" CACHE STRING "")

View File

@ -1,2 +1,2 @@
set(LIBCXX_TEST_PARAMS "enable_modules=True" CACHE STRING "")
set(LIBCXX_TEST_PARAMS "enable_modules=clang" CACHE STRING "")
set(LIBCXXABI_TEST_PARAMS "${LIBCXX_TEST_PARAMS}" CACHE STRING "")

View File

@ -44,6 +44,7 @@ sure you don't forget anything:
- Did you update the synopsis of the relevant headers?
- Did you update the relevant files to track implementation status (in ``docs/Status/``)?
- Did you mark all functions and type declarations with the :ref:`proper visibility macro <visibility-macros>`?
- Did you add all new named declarations to the ``std`` module?
- If you added a header:
- Did you add it to ``include/module.modulemap.in``?

226
libcxx/docs/Modules.rst Normal file
View File

@ -0,0 +1,226 @@
.. _ModulesInLibcxx:
=================
Modules in libc++
=================
.. warning:: Modules are an experimental feature. It has additional build
requirements and not all libc++ configurations are supported yet.
The work is still in an early developement state and not
considered stable nor complete
This page contains information regarding C++23 module support in libc++.
There are two kinds of modules available in Clang
* `Clang specific modules <https://clang.llvm.org/docs/Modules.html>`_
* `C++ modules <https://clang.llvm.org/docs/StandardCPlusPlusModules.html>`_
This page mainly discusses the C++ modules. In C++20 there are also header units,
these are not part of this document.
Overview
========
The module sources are stored in ``.cppm`` files. Modules need to be available
as BMIs, which are ``.pcm`` files for Clang. BMIs are not portable, they depend
on the compiler used and its compilation flags. Therefore there needs to be a
way to distribute the ``.cppm`` files to the user and offer a way for them to
build and use the ``.pcm`` files. It is expected this will be done by build
systems in the future. To aid early adaptor and build system vendors libc++
currently ships a CMake project to aid building modules.
.. note:: This CMake file is intended to be a temporary solution and will
be removed in the future. The timeline for the removal depends
on the availability of build systems with proper module support.
What works
~~~~~~~~~~
* Building BMIs
* Running tests using the ``std`` module
* Using the ``std`` module in external projects
* The following "parts disabled" configuration options are supported
* ``LIBCXX_ENABLE_LOCALIZATION``
* ``LIBCXX_ENABLE_WIDE_CHARACTERS``
Some of the current limitations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* There is no official build system support, libc++ has experimental CMake support
* Requires CMake 3.26
* Requires Ninja 1.11
* Requires a recent Clang 17
* The path to the compiler may not be a symlink, ``clang-scan-deps`` does
not handle that case properly
* Only C++23 is tested
* Libc++ is not tested with modules instead of headers
* The module ``.cppm`` files are not installed
* The experimental ``PSTL`` library is not supported
* Clang supports modules using GNU extensions, but libc++ does not work using
GNU extensions.
* Clang:
* Including headers after importing the ``std`` module may fail. This is
hard to solve and there is a work-around by first including all headers
`bug report <https://github.com/llvm/llvm-project/issues/61465>`__.
Blockers
~~~~~~~~
* libc++
* Currently the tests only test with modules enabled, but do not import
modules instead of headers. When converting tests to using modules there
are still failures. These are under investigation.
* It has not been determined how to fully test libc++ with modules instead
of headers.
* Clang
* Some concepts do not work properly
`bug report <https://github.com/llvm/llvm-project/issues/62943>`__.
Using in external projects
==========================
Users need to be able to build their own BMI files.
.. note:: The requirements for users to build their own BMI files will remain
true for the forseeable future. For now this needs to be done manually.
Once libc++'s implementation is more mature we will reach out to build
system vendors, with the goal that building the BMI files is done by
the build system.
Currently this requires a local build of libc++ with modules enabled. Since
modules are not part of the installation yet, they are used from the build
directory. First libc++ needs to be build with module support enabled.
.. code-block:: bash
$ git clone https://github.com/llvm/llvm-project.git
$ cd llvm-project
$ mkdir build
$ cmake -G Ninja -S runtimes -B build -DLIBCXX_ENABLE_STD_MODULES=ON -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind"
$ ninja -C build
The above ``build`` directory will be referred to as ``<build>`` in the
rest of these instructions.
This is a small sample program that uses the module ``std``. It consists of a
``CMakeLists.txt`` and a ``main.cpp`` file.
.. code-block:: cpp
import std;
int main() { std::cout << "Hello modular world\n"; }
.. code-block:: cmake
cmake_minimum_required(VERSION 3.26.0 FATAL_ERROR)
project("module"
LANGUAGES CXX
)
#
# Set language version used
#
# At the moment only C++23 is tested.
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
# Libc++ doesn't support compiler extensions for modules.
set(CMAKE_CXX_EXTENSIONS OFF)
#
# Enable modules in CMake
#
# This is required to write your own modules in your project.
if(CMAKE_VERSION VERSION_LESS "3.27.0")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
else()
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
endif()
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
#
# Import the modules from libc++
#
include(FetchContent)
FetchContent_Declare(
std
URL "file://${LIBCXX_BUILD}/modules/c++/v1/"
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_GetProperties(std)
if(NOT std_POPULATED)
FetchContent_Populate(std)
add_subdirectory(${std_SOURCE_DIR} ${std_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
#
# Adjust project compiler flags
#
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fprebuilt-module-path=${CMAKE_BINARY_DIR}/_deps/std-build/CMakeFiles/std.dir/>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-nostdinc++>)
# The include path needs to be set to be able to use macros from headers.
# For example from, the headers <cassert> and <version>.
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-isystem>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${LIBCXX_BUILD}/include/c++/v1>)
#
# Adjust project linker flags
#
add_link_options($<$<COMPILE_LANGUAGE:CXX>:-nostdlib++>)
add_link_options($<$<COMPILE_LANGUAGE:CXX>:-L${LIBCXX_BUILD}/lib>)
add_link_options($<$<COMPILE_LANGUAGE:CXX>:-Wl,-rpath,${LIBCXX_BUILD}/lib>)
# Linking against std is required for CMake to get the proper dependencies
link_libraries(std c++)
#
# Add the project
#
add_executable(main)
target_sources(main
PRIVATE
main.cpp
)
Building this project is done with the following steps, assuming the files
``main.cpp`` and ``CMakeLists.txt`` are copied in the current directory.
.. code-block:: bash
$ mkdir build
$ cmake -G Ninja -S . -B build -DCMAKE_CXX_COMPILER=<path-to-compiler> -DLIBCXX_BUILD=<build>
$ ninja -C build
$ build/main
.. warning:: ``<path-to-compiler>`` should point point to the real binary and
not to a symlink.
.. warning:: When using these examples in your own projects make sure the
compilation flags are the same for the ``std`` module and your
project. Some flags will affect the generated code, when these
are different the module cannot be used. For example using
``-pthread`` in your project and not in the module will give
errors like
``error: POSIX thread support was disabled in PCH file but is currently enabled``
``error: module file _deps/std-build/CMakeFiles/std.dir/std.pcm cannot be loaded due to a configuration mismatch with the current compilation [-Wmodule-file-config-mismatch]``
If you have questions about modules free free to ask them in the ``#libcxx``
channel on `LLVM's Discord server <https://discord.gg/jzUbyP26tQ>`__.
If you think you've found a bug please it using the `LLVM bug tracker
<https://github.com/llvm/llvm-project/issues>`_. Please make sure the issue
you found is not one of the known bugs or limitations on this page.

View File

@ -35,6 +35,9 @@ see the `releases page <https://llvm.org/releases/>`_.
What's New in Libc++ 17.0.0?
============================
There is an experimental implementation of the C++23 ``std`` module. See
:ref:`ModulesInLibcxx` for more information.
Implemented Papers
------------------
- P2520R0 - ``move_iterator<T*>`` should be a random access iterator
@ -161,3 +164,9 @@ Build System Changes
- ``LIBCXX_ENABLE_FSTREAM`` is not supported anymore, please use ``LIBCXX_ENABLE_FILESYSTEM=OFF`` if your
platform does not have support for a filesystem.
- The lit test parameter ``enable_modules`` changed from a Boolean to an enum. The changes are
- ``False`` became ``none``. This option does not test with modules enabled.
- ``True`` became ``clang``. This option tests using Clang modules.
- ``std`` is a new optional and tests with the experimental C++23 ``std`` module.

View File

@ -39,6 +39,7 @@ Getting Started with libc++
BuildingLibcxx
TestingLibcxx
Contributing
Modules
ReleaseProcedure
Status/Cxx14
Status/Cxx17

View File

@ -0,0 +1,3 @@
BasedOnStyle: InheritParentConfig
NamespaceIndentation: All

View File

@ -0,0 +1,146 @@
if (CMAKE_VERSION VERSION_LESS 3.26)
message(WARNING "The libc++ modules won't be available because the CMake version is too old. Update to CMake 3.26 or later.")
return()
endif()
# The headers of Table 24: C++ library headers[tab:headers.cpp]
# and the headers of Table 25: C++ headers for C library facilities[tab:headers.cpp.c]
set(LIBCXX_SOURCES_MODULE_STD
std.cppm
std/algorithm.cppm
std/any.cppm
std/array.cppm
std/atomic.cppm
std/barrier.cppm
std/bit.cppm
std/bitset.cppm
std/cassert.cppm
std/cctype.cppm
std/cerrno.cppm
std/cfenv.cppm
std/cfloat.cppm
std/charconv.cppm
std/chrono.cppm
std/cinttypes.cppm
std/climits.cppm
std/clocale.cppm
std/cmath.cppm
std/codecvt.cppm
std/compare.cppm
std/complex.cppm
std/concepts.cppm
std/condition_variable.cppm
std/coroutine.cppm
std/csetjmp.cppm
std/csignal.cppm
std/cstdarg.cppm
std/cstddef.cppm
std/cstdint.cppm
std/cstdio.cppm
std/cstdlib.cppm
std/cstring.cppm
std/ctime.cppm
std/cuchar.cppm
std/cwchar.cppm
std/cwctype.cppm
std/deque.cppm
std/exception.cppm
std/execution.cppm
std/expected.cppm
std/filesystem.cppm
std/flat_map.cppm
std/flat_set.cppm
std/format.cppm
std/forward_list.cppm
std/fstream.cppm
std/functional.cppm
std/future.cppm
std/generator.cppm
std/initializer_list.cppm
std/iomanip.cppm
std/ios.cppm
std/iosfwd.cppm
std/iostream.cppm
std/iostream.cppm
std/istream.cppm
std/iterator.cppm
std/latch.cppm
std/limits.cppm
std/list.cppm
std/locale.cppm
std/map.cppm
std/mdspan.cppm
std/memory.cppm
std/memory_resource.cppm
std/mutex.cppm
std/new.cppm
std/numbers.cppm
std/numeric.cppm
std/optional.cppm
std/ostream.cppm
std/print.cppm
std/queue.cppm
std/random.cppm
std/ranges.cppm
std/ratio.cppm
std/regex.cppm
std/scoped_allocator.cppm
std/semaphore.cppm
std/set.cppm
std/shared_mutex.cppm
std/source_location.cppm
std/span.cppm
std/spanstream.cppm
std/sstream.cppm
std/stack.cppm
std/stacktrace.cppm
std/stdexcept.cppm
std/stdexcept.cppm
std/stdfloat.cppm
std/stop_token.cppm
std/streambuf.cppm
std/string.cppm
std/string_view.cppm
std/strstream.cppm
std/syncstream.cppm
std/system_error.cppm
std/thread.cppm
std/tuple.cppm
std/type_traits.cppm
std/typeindex.cppm
std/typeinfo.cppm
std/unordered_map.cppm
std/unordered_set.cppm
std/utility.cppm
std/valarray.cppm
std/variant.cppm
std/vector.cppm
std/version.cppm
)
# TODO MODULES the CMakeLists.txt in the install directory is only temporary
# When that is removed the configured file can use the substitution
# LIBCXX_GENERATED_INCLUDE_TARGET_DIR avoiding this set.
# Also clean up the parts needed to generate the install version.
set(LIBCXX_CONFIGURED_INCLUDE_DIR ${LIBCXX_GENERATED_INCLUDE_TARGET_DIR})
configure_file(
"CMakeLists.txt.in"
"${LIBCXX_GENERATED_MODULE_DIR}/CMakeLists.txt"
@ONLY
)
set(_all_modules "${LIBCXX_GENERATED_MODULE_DIR}/CMakeLists.txt")
foreach(file ${LIBCXX_SOURCES_MODULE_STD})
set(src "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
set(dst "${LIBCXX_GENERATED_MODULE_DIR}/${file}")
add_custom_command(OUTPUT ${dst}
DEPENDS ${src}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}
COMMENT "Copying CXX module ${file}")
list(APPEND _all_modules "${dst}")
endforeach()
add_custom_target(generate-cxx-modules
ALL DEPENDS
${_all_modules}
)

View File

@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.26)
project(libc++-modules LANGUAGES CXX)
# Enable CMake's module support
if(CMAKE_VERSION VERSION_LESS "3.27.0")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
else()
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
endif()
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
# Default to C++ extensions being off. Libc++'s modules support have trouble
# with extensions right now.
set(CMAKE_CXX_EXTENSIONS OFF)
# Propagates the CMake options to the modules.
#
# This uses the std module hard-coded since the std.compat module does not
# depend on these flags.
macro(compile_define_if_not condition def)
if (NOT ${condition})
target_compile_definitions(std PRIVATE ${def})
endif()
endmacro()
macro(compile_define_if condition def)
if (${condition})
target_compile_definitions(std PRIVATE ${def})
endif()
endmacro()
if(NOT @LIBCXX_ENABLE_THREADS@ OR NOT @LIBCXXABI_ENABLE_THREADS@ OR NOT @LIBCXX_ENABLE_MONOTONIC_CLOCK@)
message(FATAL_ERROR "Modules without thread support is not yet implemented.")
endif()
if(NOT @LIBCXX_ENABLE_FILESYSTEM@)
message(FATAL_ERROR "Modules without filesystem support is not yet implemented.")
endif()
if(NOT @LIBCXX_ENABLE_RANDOM_DEVICE@)
message(FATAL_ERROR "Modules without randome device support is not yet implemented.")
endif()
if(NOT @LIBCXX_ENABLE_UNICODE@)
message(FATAL_ERROR "Modules without Unicode support is not yet implemented.")
endif()
if(NOT @LIBCXX_ENABLE_EXCEPTIONS@ OR NOT @LIBCXXABI_ENABLE_EXCEPTIONS@)
message(FATAL_ERROR "Modules without exception support is not yet implemented.")
endif()
add_library(std)
target_sources(std
PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES
@LIBCXX_SOURCES_MODULE_STD@
)
target_compile_definitions(std PRIVATE _LIBCPP_ENABLE_EXPERIMENTAL)
target_include_directories(std SYSTEM PRIVATE @LIBCXX_CONFIGURED_INCLUDE_DIR@)
target_compile_options(std
PUBLIC
-nostdinc++
-Wno-reserved-module-identifier
-Wno-reserved-user-defined-literal
@LIBCXX_COMPILE_FLAGS@
)
set_target_properties(std
PROPERTIES
OUTPUT_NAME "c++std"
)

View File

@ -71,10 +71,15 @@ export namespace std {
// [basic.string.hash], hash support
using std::hash;
// TODO MODULES is this a bug?
#if 1
using std::operator""s;
#else
inline namespace literals {
inline namespace string_literals {
// [basic.string.literals], suffix for basic_string literals
using std::literals::string_literals::operator""s;
} // namespace string_literals
} // namespace literals
#endif
} // namespace std

View File

@ -68,6 +68,32 @@ if (LIBCXX_INCLUDE_TESTS)
"Running libcxx tests"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS cxx-test-depends)
if(LIBCXX_ENABLE_STD_MODULES)
# Generates the modules used in the test.
# Note the test will regenerate this with the proper setting
# - the right DCMAKE_CXX_STANDARD
# - the right test compilation flags
# Since modules depend on these flags there currently is no way to
# avoid generating these for the tests. The advantage of the
# pre generation is that less build information needs to be shared
# in the bridge.
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/test/__config_module__/CMakeCache.txt"
COMMAND
${CMAKE_COMMAND}
"-G${CMAKE_GENERATOR}"
"-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}"
"-B${CMAKE_BINARY_DIR}/test/__config_module__"
"-H${LIBCXX_GENERATED_MODULE_DIR}"
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_STANDARD=23"
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
)
add_custom_target(generate-test-module-std
DEPENDS "${CMAKE_BINARY_DIR}/test/__config_module__/CMakeCache.txt"
COMMENT "Builds generic module std.")
endif()
endif()
if (LIBCXX_GENERATE_COVERAGE)

View File

@ -29,5 +29,13 @@ config.substitutions.append(('%{libcxx}', '@LIBCXX_SOURCE_DIR@'))
config.substitutions.append(('%{include}', '@LIBCXX_GENERATED_INCLUDE_DIR@'))
config.substitutions.append(('%{target-include}', '@LIBCXX_GENERATED_INCLUDE_TARGET_DIR@'))
config.substitutions.append(('%{lib}', '@LIBCXX_LIBRARY_DIR@'))
config.substitutions.append(('%{module}', '@LIBCXX_GENERATED_MODULE_DIR@'))
config.substitutions.append(('%{executor}', '@LIBCXX_EXECUTOR@'))
config.substitutions.append(('%{test-tools}', '@LIBCXX_TEST_TOOLS_PATH@'))
# The test needs to manually rebuild the module. The compiler flags used in the
# test need to be the same as the compiler flags used to generate the module.
# In the future, when CMake can generated modules this may no longer be
# necessary.
# TODO MODULES whether it's possible to remove this substitution.
config.substitutions.append(('%{cmake}', '@CMAKE_COMMAND@'))

View File

@ -0,0 +1,227 @@
# ===----------------------------------------------------------------------===##
#
# 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
#
# ===----------------------------------------------------------------------===##
# Test that all named declarations with external linkage match the
# exported declarations in their associated module partition.
# Then it tests the sum of the exported declarations in the module
# partitions matches the export of the std module.
# Note the test of the std module requires all partitions to be tested
# first. Since lit tests have no dependencies, this means the test needs
# to be one monolitic test. Since the test doesn't take very long it's
# not a huge issue.
# RUN: %{python} %s %{libcxx}/utils
import sys
sys.path.append(sys.argv[1])
from libcxx.test.header_information import toplevel_headers
BLOCKLIT = (
"" # block Lit from interpreting a RUN/XFAIL/etc inside the generation script
)
### Remove the headers that have no module associated with them
# Note all C-headers using .h are filtered in the loop.
# These headers are not available in C++23, but in older language Standards.
toplevel_headers.remove("ccomplex")
toplevel_headers.remove("ciso646")
toplevel_headers.remove("cstdbool")
toplevel_headers.remove("ctgmath")
# Ignore several declarations found in the includes.
#
# Part of these items are bugs other are not yet implemented features.
SkipDeclarations = dict()
# See comment in the header.
SkipDeclarations["cuchar"] = ["std::mbstate_t", "std::size_t"]
# Not in the synopsis.
SkipDeclarations["cwchar"] = ["std::FILE"]
# The operators are added for private types like __iom_t10.
SkipDeclarations["iomanip"] = ["std::operator<<", "std::operator>>"]
SkipDeclarations["iosfwd"] = ["std::ios_base", "std::vector"]
# This header also provides declarations in the namespace that might be
# an error.
SkipDeclarations["filesystem"] = [
"std::filesystem::operator==",
"std::filesystem::operator!=",
]
# This is a specialization for a private type
SkipDeclarations["iterator"] = ["std::pointer_traits"]
# TODO MODULES
# This definition is declared in string and defined in istream
# This declaration should be part of string
SkipDeclarations["istream"] = ["std::getline"]
# P1614 (at many places) and LWG3519 too.
SkipDeclarations["random"] = [
"std::operator!=",
# LWG3519 makes these hidden friends.
# Note the older versions had the requirement of these operations but not in
# the synopsis.
"std::operator<<",
"std::operator>>",
"std::operator==",
]
# Declared in the forward header since std::string uses std::allocator
SkipDeclarations["string"] = ["std::allocator"]
# TODO MODULES remove zombie names
# https://libcxx.llvm.org/Status/Cxx20.html#note-p0619
SkipDeclarations["memory"] = [
"std::return_temporary_buffer",
"std::get_temporary_buffer",
]
# TODO MODULES this should be part of ios instead
SkipDeclarations["streambuf"] = ["std::basic_ios"]
# include/__type_traits/is_swappable.h
SkipDeclarations["type_traits"] = [
"std::swap",
# TODO MODULES gotten through __functional/unwrap_ref.h
"std::reference_wrapper",
]
# Add declarations in headers.
#
# Some headers have their defines in a different header, which may have
# additional declarations.
ExtraDeclarations = dict()
# This declaration is in the ostream header.
ExtraDeclarations["system_error"] = ["std::operator<<"]
# Adds an extra header file to scan
#
#
ExtraHeader = dict()
# locale has a file and not a subdirectory
ExtraHeader["locale"] = "v1/__locale$"
ExtraHeader["thread"] = "v1/__threading_support$"
ExtraHeader["ranges"] = "v1/__fwd/subrange.h$"
# The extra header is needed since two headers are required to provide the
# same definition.
ExtraHeader["functional"] = "v1/__compare/compare_three_way.h$"
# Create empty file with all parts.
print(
f"""\
//--- module_std.sh.cpp
// UNSUPPORTED{BLOCKLIT}: c++03, c++11, c++14, c++17, c++20
// REQUIRES{BLOCKLIT}: has-clang-tidy
// REQUIRES{BLOCKLIT}: use_module_std
// The GCC compiler flags are not always compatible with clang-tidy.
// UNSUPPORTED{BLOCKLIT}: gcc
// RUN{BLOCKLIT}: echo -n > %t.all_partitions
"""
)
# Validate all module parts.
for header in toplevel_headers:
if header.endswith(".h"): # Skip C compatibility headers
continue
# Dump the information as found in the module's cppm file.
print(
f"// RUN{BLOCKLIT}: %{{clang-tidy}} %{{module}}/std/{header}.cppm "
" --checks='-*,libcpp-header-exportable-declarations' "
" -config='{CheckOptions: [ "
" {"
" key: libcpp-header-exportable-declarations.Filename, "
f" value: {header}.cppm"
" }, {"
" key: libcpp-header-exportable-declarations.FileType, "
" value: ModulePartition"
" }, "
" ]}' "
" --load=%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin "
" -- %{flags} %{compile_flags} "
f"| sort > %t.{header}.module"
)
print(f"// RUN{BLOCKLIT}: cat %t.{header}.module >> %t.all_partitions")
# Dump the information as found in the module by using the header file(s).
skip_declarations = " ".join(SkipDeclarations.get(header, []))
if skip_declarations:
skip_declarations = (
"{"
" key: libcpp-header-exportable-declarations.SkipDeclarations, "
f' value: "{skip_declarations}" '
"}, "
)
extra_declarations = " ".join(ExtraDeclarations.get(header, []))
if extra_declarations:
extra_declarations = (
" {"
" key: libcpp-header-exportable-declarations.ExtraDeclarations, "
f' value: "{extra_declarations}" '
"}, "
)
extra_header = ExtraHeader.get(header, "")
if extra_header:
extra_header = (
"{"
" key: libcpp-header-exportable-declarations.ExtraHeader, "
f' value: "{extra_header}" '
"}, "
)
# Clang-tidy needs a file input
print(f'// RUN{BLOCKLIT}: echo "#include <{header}>" > %t.{header}.cpp')
print(
f"// RUN{BLOCKLIT}: %{{clang-tidy}} %t.{header}.cpp "
" --checks='-*,libcpp-header-exportable-declarations' "
" -config='{CheckOptions: [ "
f" {{key: libcpp-header-exportable-declarations.Filename, value: {header}}}, "
" {key: libcpp-header-exportable-declarations.FileType, value: Header}, "
f" {skip_declarations} {extra_declarations} {extra_header}, "
" ]}' "
" --load=%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin "
" -- %{flags} %{compile_flags} "
f" | sort > %t.{header}.include"
)
# Compare the cppm and header file(s) return the same results.
print(f"// RUN{BLOCKLIT}: diff -u %t.{header}.module %t.{header}.include")
# Merge the data of the parts
print(f"// RUN{BLOCKLIT}: sort -u -o %t.all_partitions %t.all_partitions")
# Dump the information as found in std.cppm.
print(
f"// RUN{BLOCKLIT}: %{{clang-tidy}} %{{module}}/std.cppm "
" --checks='-*,libcpp-header-exportable-declarations' "
" -config='{CheckOptions: [ "
" {key: libcpp-header-exportable-declarations.Header, value: std.cppm}, "
" {key: libcpp-header-exportable-declarations.FileType, value: Module}, "
" ]}' "
f" --load=%{{test-tools}}/clang_tidy_checks/libcxx-tidy.plugin "
" -- %{flags} %{compile_flags} "
" | sort > %t.module"
)
# Compare the sum of the parts with the main module.
print(f"// RUN{BLOCKLIT}: diff -u %t.all_partitions %t.module")

View File

@ -22,6 +22,8 @@ for header in public_headers:
//--- {header}.compile.pass.cpp
// RUN{BLOCKLIT}: %{{cxx}} %s %{{flags}} %{{compile_flags}} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only
// UNSUPPORTED{BLOCKLIT}: use_module_std
// GCC doesn't support -fcxx-modules
// UNSUPPORTED{BLOCKLIT}: gcc

View File

@ -16,6 +16,7 @@ endif()
set(SOURCES
abi_tag_on_virtual.cpp
header_exportable_declarations.cpp
hide_from_abi.cpp
proper_version_checks.cpp
qualify_declval.cpp

View File

@ -0,0 +1,240 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "clang-tidy/ClangTidyCheck.h"
#include "clang-tidy/ClangTidyModuleRegistry.h"
#include "clang/Basic/Module.h"
#include "llvm/ADT/ArrayRef.h"
#include "header_exportable_declarations.hpp"
#include <iostream>
#include <iterator>
#include <ranges>
#include <algorithm>
template <>
struct clang::tidy::OptionEnumMapping<libcpp::header_exportable_declarations::FileType> {
static llvm::ArrayRef<std::pair<libcpp::header_exportable_declarations::FileType, llvm::StringRef>> getEnumMapping() {
static constexpr std::pair<libcpp::header_exportable_declarations::FileType, llvm::StringRef> Mapping[] = {
{libcpp::header_exportable_declarations::FileType::Header, "Header"},
{libcpp::header_exportable_declarations::FileType::ModulePartition, "ModulePartition"},
{libcpp::header_exportable_declarations::FileType::Module, "Module"}};
return ArrayRef(Mapping);
}
};
namespace libcpp {
header_exportable_declarations::header_exportable_declarations(
llvm::StringRef name, clang::tidy::ClangTidyContext* context)
: clang::tidy::ClangTidyCheck(name, context),
filename_(Options.get("Filename", "")),
file_type_(Options.get("FileType", header_exportable_declarations::FileType::Unknown)),
extra_header_(Options.get("ExtraHeader", "")) {
if (filename_.empty())
llvm::errs() << "No filename is provided.\n";
switch (file_type_) {
case header_exportable_declarations::FileType::Header:
/* DO NOTHING */
break;
case header_exportable_declarations::FileType::Module:
case header_exportable_declarations::FileType::ModulePartition:
if (!extra_header_.empty())
llvm::errs() << "Extra headers are not allowed for modules.\n";
if (Options.get("SkipDeclarations"))
llvm::errs() << "Modules may not skip declarations.\n";
if (Options.get("ExtraDeclarations"))
llvm::errs() << "Modules may not have extra declarations.\n";
break;
case header_exportable_declarations::FileType::Unknown:
llvm::errs() << "No file type is provided.\n";
break;
}
std::optional<llvm::StringRef> list = Options.get("SkipDeclarations");
// TODO(LLVM-17) Remove clang 15 work-around.
#if defined(__clang_major__) && __clang_major__ < 16
if (list) {
std::string_view s = *list;
auto b = s.begin();
auto e = std::find(b, s.end(), ' ');
while (b != e) {
decls_.emplace(b, e);
if (e == s.end())
break;
b = e + 1;
e = std::find(b, s.end(), ' ');
}
}
#else // defined(__clang_major__) && __clang_major__ < 16
if (list)
for (auto decl : std::views::split(*list, ' ')) {
std::string s;
std::ranges::copy(decl, std::back_inserter(s)); // use range based constructor
decls_.emplace(std::move(s));
}
#endif // defined(__clang_major__) && __clang_major__ < 16
list = Options.get("ExtraDeclarations");
// TODO(LLVM-17) Remove clang 15 work-around.
#if defined(__clang_major__) && __clang_major__ < 16
if (list) {
std::string_view s = *list;
auto b = s.begin();
auto e = std::find(b, s.end(), ' ');
while (b != e) {
std::cout << "using " << std::string_view{b, e} << ";\n";
if (e == s.end())
break;
b = e + 1;
e = std::find(b, s.end(), ' ');
}
}
#else // defined(__clang_major__) && __clang_major__ < 16
if (list)
for (auto decl : std::views::split(*list, ' '))
std::cout << "using " << std::string_view{decl.data(), decl.size()} << ";\n";
#endif // defined(__clang_major__) && __clang_major__ < 16
}
void header_exportable_declarations::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
// there are no public names in the Standard starting with an underscore, so
// no need to check the strict rules.
using namespace clang::ast_matchers;
switch (file_type_) {
case FileType::Header:
finder->addMatcher(
namedDecl(
// Looks at the common locations where headers store their data
// * header
// * __header/*.h
// * __fwd/header.h
anyOf(isExpansionInFileMatching(("v1/__" + filename_ + "/").str()),
isExpansionInFileMatching(extra_header_),
isExpansionInFileMatching(("v1/__fwd/" + filename_ + "\\.h$").str()),
isExpansionInFileMatching(("v1/" + filename_ + "$").str())),
unless(hasAncestor(friendDecl())))
.bind("header_exportable_declarations"),
this);
break;
case FileType::ModulePartition:
finder->addMatcher(namedDecl(isExpansionInFileMatching(filename_)).bind("header_exportable_declarations"), this);
break;
case FileType::Module:
finder->addMatcher(namedDecl().bind("header_exportable_declarations"), this);
break;
case header_exportable_declarations::FileType::Unknown:
llvm::errs() << "This should be unreachable.\n";
break;
}
}
/// Returns the qualified name of a declaration.
///
/// There is a small issue with qualified names. Typically the name returned is
/// in the namespace \c std instead of the namespace \c std::__1. Except when a
/// name is declared both in the namespace \c std and in the namespace
/// \c std::__1. In that case the returned value will adjust the name to use
/// the namespace \c std.
///
/// The reason this happens is due to some parts of libc++ using
/// \code namespace std \endcode instead of
/// \code _LIBCPP_BEGIN_NAMESPACE_STD \endcode
/// Some examples
/// * cstddef has bitwise operators for the type \c byte
/// * exception has equality operators for the type \c exception_ptr
/// * initializer_list has the functions \c begin and \c end
static std::string get_qualified_name(const clang::NamedDecl& decl) {
std::string result = decl.getQualifiedNameAsString();
if (result.starts_with("std::__1::"))
result.erase(5, 5);
return result;
}
static bool is_viable_declaration(const clang::NamedDecl* decl) {
// Declarations nested in records are automatically exported with the record itself.
if (!decl->getDeclContext()->isNamespace())
return false;
// Declarations that are a subobject of a friend Declaration are automatically exported with the record itself.
if (decl->getFriendObjectKind() != clang::Decl::FOK_None)
return false;
// *** Function declarations ***
if (clang::CXXMethodDecl::classof(decl))
return false;
if (clang::CXXDeductionGuideDecl::classof(decl))
return false;
if (clang::FunctionDecl::classof(decl))
return true;
if (clang::CXXConstructorDecl::classof(decl))
return false;
// implicit constructors disallowed
if (const auto* r = llvm::dyn_cast_or_null<clang::RecordDecl>(decl))
return !r->isLambda() && !r->isImplicit();
// *** Unconditionally accepted declarations ***
return llvm::isa<clang::EnumDecl, clang::VarDecl, clang::ConceptDecl, clang::TypedefNameDecl, clang::UsingDecl>(decl);
}
/// Returns the name is a reserved name.
///
/// Detected reserved names are names starting with __ or _[A-Z].
/// These names can be in the namespace std or any namespace inside std. For
/// example std::ranges contains reserved names to implement the Niebloids.
///
/// This test misses 2 candidates which are not used in libc++
/// * any identifier with two underscores not at the start
/// * a name with a leading underscore in the global namespace
bool is_reserved_name(const std::string& name) {
std::size_t pos = name.find("::_");
if (pos == std::string::npos)
return false;
if (pos + 3 > name.size())
return false;
return name[pos + 3] == '_' || std::isupper(name[pos + 3]);
}
void header_exportable_declarations::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
if (const auto* decl = result.Nodes.getNodeAs<clang::NamedDecl>("header_exportable_declarations"); decl != nullptr) {
if (!is_viable_declaration(decl))
return;
std::string name = get_qualified_name(*decl);
if (is_reserved_name(name))
return;
// For modules (std, std.compat) only take the declarations exported from the partitions.
// Making sure no declatations of headers are compared.
if (file_type_ == FileType::Module)
if (clang::Module* M = decl->getOwningModule(); M && M->Kind != clang::Module::ModulePartitionInterface)
return;
if (decls_.contains(name))
return;
std::cout << "using " << std::string{name} << ";\n";
decls_.insert(name);
}
}
} // namespace libcpp

View File

@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "clang-tidy/ClangTidyCheck.h"
#include "llvm/ADT/StringRef.h"
#include <fstream>
#include <set>
#include <string>
namespace libcpp {
class header_exportable_declarations : public clang::tidy::ClangTidyCheck {
public:
explicit header_exportable_declarations(llvm::StringRef, clang::tidy::ClangTidyContext*);
void registerMatchers(clang::ast_matchers::MatchFinder*) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult&) override;
enum class FileType { Header, ModulePartition, Module, Unknown };
private:
llvm::StringRef filename_;
FileType file_type_;
llvm::StringRef extra_header_;
std::set<std::string> decls_;
};
} // namespace libcpp

View File

@ -10,6 +10,7 @@
#include "clang-tidy/ClangTidyModuleRegistry.h"
#include "abi_tag_on_virtual.hpp"
#include "header_exportable_declarations.hpp"
#include "hide_from_abi.hpp"
#include "proper_version_checks.hpp"
#include "qualify_declval.hpp"
@ -21,6 +22,7 @@ class LibcxxTestModule : public clang::tidy::ClangTidyModule {
public:
void addCheckFactories(clang::tidy::ClangTidyCheckFactories& check_factories) override {
check_factories.registerCheck<libcpp::abi_tag_on_virtual>("libcpp-avoid-abi-tag-on-virtual");
check_factories.registerCheck<libcpp::header_exportable_declarations>("libcpp-header-exportable-declarations");
check_factories.registerCheck<libcpp::hide_from_abi>("libcpp-hide-from-abi");
check_factories.registerCheck<libcpp::proper_version_checks>("libcpp-cpp-version-check");
check_factories.registerCheck<libcpp::robust_against_adl_check>("libcpp-robust-against-adl");

View File

@ -146,6 +146,27 @@ steps:
limit: 2
timeout_in_minutes: 120
- label: "C++23 Module std"
command: "libcxx/utils/ci/run-buildbot generic-module-std-cxx23"
artifact_paths:
- "**/test-results.xml"
- "**/*.abilist"
env:
# Note modules require and absolute path for clang-scan-deps
# https://github.com/llvm/llvm-project/issues/61006
CC: "/usr/lib/llvm-${LLVM_HEAD_VERSION}/bin/clang"
CXX: "/usr/lib/llvm-${LLVM_HEAD_VERSION}/bin/clang++"
CMAKE: "/opt/bin/cmake"
ENABLE_CLANG_TIDY: "On"
agents:
queue: "libcxx-builders"
os: "linux"
retry:
automatic:
- exit_status: -1 # Agent was lost
limit: 2
timeout_in_minutes: 120
- label: "C++11"
command: "libcxx/utils/ci/run-buildbot generic-cxx11"
artifact_paths:

View File

@ -35,9 +35,12 @@ ${PROGNAME} [options] <BUILDER>
Environment variables
CC The C compiler to use, this value is used by CMake. This
variable is optional.
CXX The C++ compiler to use, this value is used by CMake. This
variable is optional.
CMAKE The CMake binary to use. This variable is optional.
CLANG_FORMAT The clang-format binary to use when generating the format
ignore list.
@ -89,8 +92,24 @@ INSTALL_DIR="${BUILD_DIR}/install"
# version will generally work with the Clang shipped in Xcode (e.g. if Clang
# knows about -std=c++20, the CMake bundled in Xcode will probably know about
# that flag too).
if xcrun --find ninja &>/dev/null; then NINJA="$(xcrun --find ninja)"; else NINJA="ninja"; fi
if xcrun --find cmake &>/dev/null; then CMAKE="$(xcrun --find cmake)"; else CMAKE="cmake"; fi
if xcrun --find ninja &>/dev/null; then
NINJA="$(xcrun --find ninja)"
elif which ninja &>/dev/null; then
# The current implementation of modules needs the absolute path to the ninja
# binary.
# TODO MODULES Is this still needed when CMake has libc++ module support?
NINJA="$(which ninja)"
else
NINJA="ninja"
fi
if [ -z "${CMAKE}" ]; then
if xcrun --find cmake &>/dev/null; then
CMAKE="$(xcrun --find cmake)"
else
CMAKE="cmake"
fi
fi
function clean() {
rm -rf "${BUILD_DIR}"
@ -178,8 +197,8 @@ check-format)
fi
${GIT_CLANG_FORMAT} \
--diff \
--extensions ',h,hpp,c,cpp,inc,ipp' HEAD~1 \
-- $(find libcxx/{benchmarks,include,src}/ -type f | grep -vf libcxx/utils/data/ignore_format.txt) \
--extensions ',h,hpp,c,cpp,cppm,inc,ipp' HEAD~1 \
-- $(find libcxx/{benchmarks,include,modules,src}/ -type f | grep -vf libcxx/utils/data/ignore_format.txt) \
| tee ${BUILD_DIR}/clang-format.patch
# Check if the diff is empty, fail otherwise.
! grep -q '^--- a' ${BUILD_DIR}/clang-format.patch
@ -379,6 +398,9 @@ generic-with_llvm_unwinder)
generate-cmake -DLIBCXXABI_USE_LLVM_UNWINDER=ON
check-runtimes
;;
#
# Module builds
#
generic-modules)
clean
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-modules.cmake"
@ -391,6 +413,12 @@ generic-modules-lsv)
check-runtimes
check-abi-list
;;
generic-module-std-cxx23)
clean
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-module-std-cxx23.cmake"
check-runtimes
check-abi-list
;;
#
# Parts removed
#

View File

@ -12,6 +12,7 @@ import pipes
import platform
import re
import shutil
import subprocess
import tempfile
import libcxx.test.format
@ -338,6 +339,12 @@ def featureTestMacros(config, flags=""):
}
def _getSubstitution(substitution, config):
for (orig, replacement) in config.substitutions:
if orig == substitution:
return replacement
raise ValueError('Substitution {} is not in the config.'.format(substitution))
def _appendToSubstitution(substitutions, key, value):
return [(k, v + " " + value) if k == key else (k, v) for (k, v) in substitutions]
@ -430,6 +437,30 @@ class AddFlag(ConfigAction):
def pretty(self, config, litParams):
return "add {} to %{{flags}}".format(self._getFlag(config))
class BuildStdModule(ConfigAction):
def applyTo(self, config):
build = os.path.join(config.test_exec_root, '__config_module__')
std = _getSubstitution('%{cxx_std}', config)
if std == 'cxx26':
# This fails to work properly. It might be due to
# CMAKE_CXX_STANDARD 26
# does not work in CMake 3.26, it requires the upcomming CMake 3.27.
# TODO MODULES test whether this is fixed with CMake 3.27.
std = '17'
elif std == 'cxx23':
std = '23'
else:
std = '17' # Not allowed for modules
flags = _getSubstitution('%{flags}', config)
cmake = _getSubstitution('%{cmake}', config)
subprocess.check_call([cmake, "-DCMAKE_CXX_STANDARD=" + std, f"-DCMAKE_CXX_FLAGS={flags}", build], env={})
subprocess.check_call([cmake, "--build", build], env={})
def pretty(self, config, litParams):
return "building std module with flags {}".format(_getSubstitution('%{flags}', config))
class AddFlagIfSupported(ConfigAction):
"""

View File

@ -24,6 +24,7 @@ _warningFlags = [
"-Wno-noexcept-type",
"-Wno-aligned-allocation-unavailable",
"-Wno-atomic-alignment",
"-Wno-reserved-module-identifier",
# GCC warns about places where we might want to add sized allocation/deallocation
# functions, but we know better what we're doing/testing in the test suite.
"-Wno-sized-deallocation",
@ -73,6 +74,15 @@ def getStdFlag(cfg, std):
return None
_allModules = ["none", "clang", "std"]
def getModuleFlag(cfg, enable_modules):
if enable_modules in _allModules:
return enable_modules
return None
DEFAULT_PARAMETERS = [
Parameter(
name="target_triple",
@ -104,18 +114,32 @@ DEFAULT_PARAMETERS = [
),
Parameter(
name="enable_modules",
choices=[True, False],
type=bool,
default=False,
help="Whether to build the test suite with Clang modules enabled.",
actions=lambda modules: [
choices=_allModules,
type=str,
help="Whether to build the test suite with modules enabled. Select "
"`clang` for Clang modules and `std` for C++23 std module",
default=lambda cfg: next(s for s in _allModules if getModuleFlag(cfg, s)),
actions=lambda enable_modules: [
AddFeature("modules-build"),
AddCompileFlag("-fmodules"),
AddCompileFlag(
"-fcxx-modules"
), # AppleClang disregards -fmodules entirely when compiling C++. This enables modules for C++.
]
if modules
if enable_modules == "clang"
else [
AddFeature("use_module_std"),
AddCompileFlag("-DTEST_USE_MODULE"),
AddCompileFlag("-DTEST_USE_MODULE_STD"),
AddCompileFlag(
lambda cfg: "-fprebuilt-module-path="
+ os.path.join(
cfg.test_exec_root, "__config_module__/CMakeFiles/std.dir"
)
),
BuildStdModule(),
]
if enable_modules == "std"
else [],
),
Parameter(