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")
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.")
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
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(utils)
+if (LIBCXX_ENABLE_STD_MODULES)
+ add_subdirectory(modules)
+endif()
set(LIBCXX_TEST_DEPS "cxx_experimental")
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()
--- /dev/null
+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 "")
+
-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 "")
-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 "")
- 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``?
--- /dev/null
+.. _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.
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
- ``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.
BuildingLibcxx
TestingLibcxx
Contributing
+ Modules
ReleaseProcedure
Status/Cxx14
Status/Cxx17
--- /dev/null
+BasedOnStyle: InheritParentConfig
+
+NamespaceIndentation: All
--- /dev/null
+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}
+)
--- /dev/null
+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"
+)
// [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
"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)
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@'))
--- /dev/null
+# ===----------------------------------------------------------------------===##
+#
+# 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")
//--- {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
set(SOURCES
abi_tag_on_virtual.cpp
+ header_exportable_declarations.cpp
hide_from_abi.cpp
proper_version_checks.cpp
qualify_declval.cpp
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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
#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"
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");
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:
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.
# 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}"
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
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"
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
#
import platform
import re
import shutil
+import subprocess
import tempfile
import libcxx.test.format
}
+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]
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):
"""
"-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",
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",
),
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(