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
19 changes: 19 additions & 0 deletions cmake/Modules/Platform/Emscripten.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,22 @@ 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_LIBRARY_DIR "${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_LIBRARY_DIR}")
set(LIBCXX_GENERATED_MODULE_DIR "${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_MODULES_DIR}")

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

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 this should be referring to the installed version in the emscripten cache/sysroot not the version in source control.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah this implies relative paths to CMAKE_INSTALL_PREFIX = cache/sysroot.

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 -print-file-name is already sysroot relative.

It looks like cmake expects clang -print-file-name=libc++.modules.json to print the file within the sysroot, like it does for libs.

It looks like libc++.modules.json is supposed to live in cache/sysroot/lib/wasm32-emscripten/ .. and the current code should then find it.

You might need to update install_system_headers so that the json files go in the right place.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes it prints the correct path when installed in cache/sysroot/lib/wasm32-emscripten but CMake seems not to invoke the underlying function from Clang-CXX-CXXImportStd automatically for emscripten:

[cmake] CMake Error in CMakeLists.txt:
[cmake]   "The "CXX_MODULE_STD" property on target "HelloWorld" requires
[cmake]   CMAKE_CXX_STDLIB_MODULES_JSON be set, but it was not provided by the
[cmake]   toolchain.

So I guess the only alternative for CMake>3.30 would be using our own copy of the older Clang-CXX-CXXImportStd since they changed the underlying function.

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'm not sure how Clang-CXX-CXXImportStd is supposed to be called.

Why don't we land this change (to add the new files) and then followup with another change to add test and figure out the Clang-CXX-CXXImportStd thing?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm not sure how Clang-CXX-CXXImportStd is supposed to be called.

It can be explicitly called like:

include(Compiler/Clang-CXX-CXXImportStd)
_cmake_cxx_find_modules_json()     # >4.2 Sets CMAKE_CXX_STDLIB_MODULES_JSON
_cmake_cxx_import_std(23 to_eval)  # <4.2 Sets targets

But yeah lets figure that out later


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