Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cmake/Modules/Platform/Emscripten.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,23 @@ endif()
# complain about unused CMake variable.
if (CMAKE_CROSSCOMPILING_EMULATOR)
endif()

# C++23 stl module
if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_CXX_MODULE_STD AND NOT DEFINED CMAKE_CXX_STDLIB_MODULES_JSON)
set(LIBCXX_INSTALL_LIBRARY_DIR "/lib")
set(LIBCXX_INSTALL_MODULES_DIR "/share/libc++/v1")

set(LIBCXX_BINARY_DIR libcxx)
set(LIBCXX_LIBRARY_DIR "${CMAKE_BINARY_DIR}/${LIBCXX_BINARY_DIR}/${LIBCXX_INSTALL_LIBRARY_DIR}")
set(LIBCXX_GENERATED_MODULE_DIR "${CMAKE_BINARY_DIR}/${LIBCXX_BINARY_DIR}/${LIBCXX_INSTALL_MODULES_DIR}")

add_subdirectory("${EMSCRIPTEN_ROOT_PATH}/system/lib/libcxx/modules" ${LIBCXX_BINARY_DIR})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would imagine we can avoid referencing system/lib/ completely no? Shouldn't we just refer to the installed files in the sysroot? What is this line required for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we install them there some other way? These lines are meant to generate the libc++.modules.json and the module interfaces from there in the first place.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see.. so the libc++.modules.json is not checked in but generated automatically on first use?

How does this work in normal/desktop systems?

I would have through we would install the modules directory as part of the install_system_headers step and then point to them there? (install_system_headers is a little misnames since it installs all kind of things really).

@Diyou Diyou Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These files usually get shipped together with libc++. Older llvm versions might need to set a flag to build them but looks like they are on by default here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so that means we should "install" them into the sysroot, no? Since that is our version of "shipping" here I think.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(We don't use libc++'s install logic but reproduce it instead in install_system_headers)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to build them in the purview of install_system_headers instead of Emscripten.cmake we'd need to add a little cmake shim project I suppose.
For the cmake side of things this wouldn't strictly be necessary though since we can just set CMAKE_CXX_STDLIB_MODULES_JSON for CMake > 4.2


set_property(SOURCE
"${LIBCXX_GENERATED_MODULE_DIR}/std.cppm"
"${LIBCXX_GENERATED_MODULE_DIR}/std.compat.cppm"
PROPERTY
COMPILE_FLAGS -Wno-reserved-module-identifier
)
set(CMAKE_CXX_STDLIB_MODULES_JSON "${LIBCXX_LIBRARY_DIR}/libc++.modules.json")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if you install libc++.modules.json in the correct place in the sysroot then CMake should be able to find it without setting CMAKE_CXX_STDLIB_MODULES_JSON like this ?

endif()
3 changes: 3 additions & 0 deletions system/lib/libcxx/modules/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BasedOnStyle: InheritParentConfig

NamespaceIndentation: All
269 changes: 269 additions & 0 deletions system/lib/libcxx/modules/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# 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_MODULE_STD_SOURCES
std/algorithm.inc
std/any.inc
std/array.inc
std/atomic.inc
std/barrier.inc
std/bit.inc
std/bitset.inc
std/cassert.inc
std/cctype.inc
std/cerrno.inc
std/cfenv.inc
std/cfloat.inc
std/charconv.inc
std/chrono.inc
std/cinttypes.inc
std/climits.inc
std/clocale.inc
std/cmath.inc
std/codecvt.inc
std/compare.inc
std/complex.inc
std/concepts.inc
std/condition_variable.inc
std/coroutine.inc
std/csetjmp.inc
std/csignal.inc
std/cstdarg.inc
std/cstddef.inc
std/cstdint.inc
std/cstdio.inc
std/cstdlib.inc
std/cstring.inc
std/ctime.inc
std/cuchar.inc
std/cwchar.inc
std/cwctype.inc
std/deque.inc
std/exception.inc
std/execution.inc
std/expected.inc
std/filesystem.inc
std/flat_map.inc
std/flat_set.inc
std/format.inc
std/forward_list.inc
std/fstream.inc
std/functional.inc
std/future.inc
std/generator.inc
std/hazard_pointer.inc
std/initializer_list.inc
std/iomanip.inc
std/ios.inc
std/iosfwd.inc
std/iostream.inc
std/istream.inc
std/iterator.inc
std/latch.inc
std/limits.inc
std/list.inc
std/locale.inc
std/map.inc
std/mdspan.inc
std/memory.inc
std/memory_resource.inc
std/mutex.inc
std/new.inc
std/numbers.inc
std/numeric.inc
std/optional.inc
std/ostream.inc
std/print.inc
std/queue.inc
std/random.inc
std/ranges.inc
std/ratio.inc
std/rcu.inc
std/regex.inc
std/scoped_allocator.inc
std/semaphore.inc
std/set.inc
std/shared_mutex.inc
std/source_location.inc
std/span.inc
std/spanstream.inc
std/sstream.inc
std/stack.inc
std/stacktrace.inc
std/stdexcept.inc
std/stdfloat.inc
std/stop_token.inc
std/streambuf.inc
std/string.inc
std/string_view.inc
std/strstream.inc
std/syncstream.inc
std/system_error.inc
std/text_encoding.inc
std/thread.inc
std/tuple.inc
std/type_traits.inc
std/typeindex.inc
std/typeinfo.inc
std/unordered_map.inc
std/unordered_set.inc
std/utility.inc
std/valarray.inc
std/variant.inc
std/vector.inc
std/version.inc
)

set(LIBCXX_MODULE_STD_COMPAT_SOURCES
std.compat/cassert.inc
std.compat/cctype.inc
std.compat/cerrno.inc
std.compat/cfenv.inc
std.compat/cfloat.inc
std.compat/cinttypes.inc
std.compat/climits.inc
std.compat/clocale.inc
std.compat/cmath.inc
std.compat/csetjmp.inc
std.compat/csignal.inc
std.compat/cstdarg.inc
std.compat/cstddef.inc
std.compat/cstdint.inc
std.compat/cstdio.inc
std.compat/cstdlib.inc
std.compat/cstring.inc
std.compat/ctime.inc
std.compat/cuchar.inc
std.compat/cwchar.inc
std.compat/cwctype.inc
)

# TODO MODULES the CMakeLists.txt in the build directory is only temporary.
# This allows using as available in the build directory. Once build systems
# have proper support for the installed files this will be removed.
if ("${LIBCXX_GENERATED_INCLUDE_DIR}" STREQUAL "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}")
# This typically happens when the target is not installed.
set(LIBCXX_CONFIGURED_INCLUDE_DIRS "${LIBCXX_GENERATED_INCLUDE_DIR}")
else()
# It's important that the arch directory be included first so that its header files
# which interpose on the default include dir be included instead of the default ones.
set(LIBCXX_CONFIGURED_INCLUDE_DIRS
"${LIBCXX_GENERATED_INCLUDE_TARGET_DIR};${LIBCXX_GENERATED_INCLUDE_DIR}"
)
endif()
configure_file(
"CMakeLists.txt.in"
"${LIBCXX_GENERATED_MODULE_DIR}/CMakeLists.txt"
@ONLY
)

set(LIBCXX_MODULE_STD_INCLUDE_SOURCES)
foreach(file ${LIBCXX_MODULE_STD_SOURCES})
set(
LIBCXX_MODULE_STD_INCLUDE_SOURCES
"${LIBCXX_MODULE_STD_INCLUDE_SOURCES}#include \"${file}\"\n"
)
endforeach()

configure_file(
"std.cppm.in"
"${LIBCXX_GENERATED_MODULE_DIR}/std.cppm"
@ONLY
)

set(LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES)
foreach(file ${LIBCXX_MODULE_STD_COMPAT_SOURCES})
set(
LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES
"${LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES}#include \"${file}\"\n"
)
endforeach()

configure_file(
"std.compat.cppm.in"
"${LIBCXX_GENERATED_MODULE_DIR}/std.compat.cppm"
@ONLY
)

set(_all_modules)
list(APPEND _all_modules "${LIBCXX_GENERATED_MODULE_DIR}/CMakeLists.txt")
list(APPEND _all_modules "${LIBCXX_GENERATED_MODULE_DIR}/std.cppm")
list(APPEND _all_modules "${LIBCXX_GENERATED_MODULE_DIR}/std.compat.cppm")
foreach(file ${LIBCXX_MODULE_STD_SOURCES} ${LIBCXX_MODULE_STD_COMPAT_SOURCES})
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}
)

# Configure the modules manifest.
# Use the relative path between the installation and the module in the json
# file. This allows moving the entire installation to a different location.
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "")
set(BASE_DIRECTORY "/")
else()
set(BASE_DIRECTORY ${CMAKE_INSTALL_PREFIX})
endif()
cmake_path(ABSOLUTE_PATH LIBCXX_INSTALL_LIBRARY_DIR
BASE_DIRECTORY ${BASE_DIRECTORY}
OUTPUT_VARIABLE ABS_LIBRARY_DIR)
cmake_path(ABSOLUTE_PATH LIBCXX_INSTALL_MODULES_DIR
BASE_DIRECTORY ${BASE_DIRECTORY}
OUTPUT_VARIABLE ABS_MODULES_DIR)
file(RELATIVE_PATH LIBCXX_MODULE_RELATIVE_PATH
${ABS_LIBRARY_DIR}
${ABS_MODULES_DIR})
configure_file(
"modules.json.in"
"${LIBCXX_LIBRARY_DIR}/libc++.modules.json"
@ONLY
)

# Dummy library to make modules an installation component.
add_library(cxx-modules INTERFACE)
add_dependencies(cxx-modules generate-cxx-modules)

if (LIBCXX_INSTALL_MODULES)
foreach(file ${LIBCXX_MODULE_STD_SOURCES} ${LIBCXX_MODULE_STD_COMPAT_SOURCES})
get_filename_component(dir ${file} DIRECTORY)
install(FILES ${file}
DESTINATION "${LIBCXX_INSTALL_MODULES_DIR}/${dir}"
COMPONENT cxx-modules
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)
endforeach()

# Install the generated module files.
install(FILES
"${LIBCXX_GENERATED_MODULE_DIR}/std.cppm"
"${LIBCXX_GENERATED_MODULE_DIR}/std.compat.cppm"
DESTINATION "${LIBCXX_INSTALL_MODULES_DIR}"
COMPONENT cxx-modules
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)

# Install the module manifest.
install(FILES
"${LIBCXX_LIBRARY_DIR}/libc++.modules.json"
DESTINATION "${LIBCXX_INSTALL_LIBRARY_DIR}"
COMPONENT cxx-modules
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)

if (NOT CMAKE_CONFIGURATION_TYPES)
add_custom_target(install-cxx-modules
DEPENDS cxx-modules
COMMAND "${CMAKE_COMMAND}"
-DCMAKE_INSTALL_COMPONENT=cxx-modules
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
# Stripping is a no-op for modules
add_custom_target(install-cxx-modules-stripped DEPENDS install-cxx-modules)
endif()
endif()
96 changes: 96 additions & 0 deletions system/lib/libcxx/modules/CMakeLists.txt.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
cmake_minimum_required(VERSION 3.26)

project(libc++-modules LANGUAGES CXX)

# Enable CMake's module support
if(CMAKE_VERSION VERSION_LESS "3.28.0")
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)
else()
cmake_policy(VERSION 3.28)
endif()

# 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()

### STD

add_library(std)
target_sources(std
PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES
std.cppm
)

target_include_directories(std SYSTEM PUBLIC @LIBCXX_CONFIGURED_INCLUDE_DIRS@)

if (NOT @LIBCXX_ENABLE_EXCEPTIONS@)
target_compile_options(std PUBLIC -fno-exceptions)
endif()

target_compile_options(std
PUBLIC
-nostdinc++
@LIBCXX_COMPILE_FLAGS@
)
target_compile_options(std
PRIVATE
-Wno-reserved-module-identifier
-Wno-reserved-user-defined-literal
)
target_link_options(std PUBLIC -nostdlib++ -Wl,-rpath,@LIBCXX_LIBRARY_DIR@ -L@LIBCXX_LIBRARY_DIR@)
target_link_libraries(std c++)
set_target_properties(std
PROPERTIES
OUTPUT_NAME "c++std"
)

### STD.COMPAT

add_library(std.compat)
target_sources(std.compat
PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES
std.compat.cppm
)

target_include_directories(std.compat SYSTEM PUBLIC @LIBCXX_CONFIGURED_INCLUDE_DIRS@)

if (NOT @LIBCXX_ENABLE_EXCEPTIONS@)
target_compile_options(std.compat PUBLIC -fno-exceptions)
endif()

target_compile_options(std.compat
PUBLIC
-nostdinc++
@LIBCXX_COMPILE_FLAGS@
)
target_compile_options(std.compat
PRIVATE
-Wno-reserved-module-identifier
-Wno-reserved-user-defined-literal
)
set_target_properties(std.compat
PROPERTIES
OUTPUT_NAME "c++std.compat"
)
add_dependencies(std.compat std)
target_link_libraries(std.compat PUBLIC std c++)
Loading