Skip to content
Open
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
2 changes: 1 addition & 1 deletion .cmake-format.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@
'INCLUDE_DIRS': '+',
'FOUND_HEADERS': 1},
'pargs': { 'flags': [], 'nargs': '1+'}},
'hpx_configure_module_producer': { 'kwargs': { 'MODULE_OUT_DIR': 1},
'hpx_configure_module_producer': { 'kwargs': {},
'pargs': { 'flags': [], 'nargs': '1+'}}
}

Expand Down
124 changes: 31 additions & 93 deletions cmake/HPX_CXXModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

include(HPX_AddCompileFlag)
include(HPX_Message)

macro(hpx_check_cxx_modules_support)
if(HPX_WITH_CXX_MODULES)
if(NOT (CMAKE_VERSION VERSION_GREATER_EQUAL "3.29"))
if(NOT (CMAKE_VERSION VERSION_GREATER_EQUAL "3.28"))
hpx_fatal(
"Please use a version of CMake newer than V3.28 in order to enable C++ module support for HPX"
"Please use a version of CMake 3.28 or newer in order to enable C++ module support for HPX"
)
endif()

Expand Down Expand Up @@ -65,79 +64,39 @@ if(NOT HPX_WITH_CXX_MODULES)
return()
endif()

# hpx_configure_module_producer(<producer> [MODULE_OUT_DIR <dir>])
# hpx_configure_module_producer(<producer>)
#
# * Ensures a stable module output dir for producer target
# * Adds compiler flags to write module cache there (Clang/GCC)
# * Creates an interface target '<producer>_if' for consumers to link to
# * Creates an interface target '<producer>_if' for consumers to link to.
# * Sets INTERFACE_CXX_SCAN_FOR_MODULES ON so that CMake's native module
# dependency tracking propagates to all consumers.
#
# CMake 3.28+ with FILE_SET CXX_MODULES handles BMI generation and
# -fmodule-output/-fprebuilt-module-path flags automatically. No manual compiler
# flags are needed here.
Comment thread
arpittkhandelwal marked this conversation as resolved.
function(hpx_configure_module_producer producer)
if(NOT TARGET ${producer})
hpx_error("hpx_configure_module_producer: target '${producer}' not found")
endif()

# parse optional args
set(options)
set(one_value_args MODULE_OUT_DIR)
set(multi_value_args)
cmake_parse_arguments(
_args "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}
)

if(_args_MODULE_OUT_DIR)
set(_moddir "${_args_MODULE_OUT_DIR}")
else()
set(_moddir "$<TARGET_FILE_DIR:${producer}>")
endif()

set(_iface "${producer}_if")
if(NOT TARGET ${_iface})
add_library(${_iface} INTERFACE)
target_link_libraries(${_iface} INTERFACE ${producer})
endif()

# Set a property so consumers can query the BMI directory via
# get_target_property.
set_target_properties(
${_iface} PROPERTIES INTERFACE_EXPORT_MODULE_DIR "${_moddir}"
)

# Make sure consumers scan for the BMI
set_target_properties(${_iface} PROPERTIES INTERFACE_CXX_SCAN_FOR_MODULES On)

if(MSVC)
# MSVC: CMake/MSVC handle IFCs automatically; create a target for
# convenience, consumers can link to this to get ordering and include info
return()
endif()

# Compiler-specific flags to instruct where to write module cache
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES
"AppleClang"
)
# Clang common flags
target_compile_options(${producer} PRIVATE "-fmodule-output=${_moddir}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# GCC: modern flags
hpx_add_target_compile_option_if_available(
${producer} PRIVATE "-fmodule-output=${_moddir}" RESULT ok
)
if(NOT ok)
hpx_error(
"hpx_configure_module_producer: the used version of gcc does not support '-fmodule-output'"
)
endif()
else()
hpx_warn(
"hpx_configure_module_producer: unknown compiler '${CMAKE_CXX_COMPILER_ID}'; "
"exposing EXPORT_MODULE_DIR='${_moddir}' for manual handling"
)
endif()
# Propagate scanning requirement to consumers via the interface target. CMake
# uses this to enable its native module dependency scanning for any target
# that links to this interface.
set_target_properties(${_iface} PROPERTIES INTERFACE_CXX_SCAN_FOR_MODULES ON)
Comment thread
arpittkhandelwal marked this conversation as resolved.
endfunction()

# hpx_configure_module_consumer(<consumer> <producer>])
# hpx_configure_module_consumer(<consumer> <producer>)
#
# * Links the consumer to the producer interface target.
# * Enables CMake's native module scanning on the consumer.
#
# * propagates module-related properties from producer interface target
# * sets necessary consumer compiler flags for clang and gcc
# CMake 3.29+ automatically resolves the BMI location from the FILE_SET
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be:

Suggested change
# CMake 3.29+ automatically resolves the BMI location from the FILE_SET
# CMake 3.28+ automatically resolves the BMI location from the FILE_SET

# CXX_MODULES declared on the producer. No -fprebuilt-module-path needed.
function(hpx_configure_module_consumer consumer producer)
if(NOT TARGET ${consumer})
hpx_error("hpx_configure_module_consumer: target '${consumer}' not found")
Expand All @@ -146,41 +105,20 @@ function(hpx_configure_module_consumer consumer producer)
hpx_error("hpx_configure_module_consumer: target '${producer}' not found")
endif()

# Imported module metadata is only picked up from direct link dependencies.
# Link the underlying module target directly when the producer follows the
# '<module>_if' wrapper pattern.
if(producer MATCHES "_if$")
string(REGEX REPLACE "_if$" "" _producer_target "${producer}")
if(TARGET ${_producer_target})
target_link_libraries(${consumer} PRIVATE ${_producer_target})
endif()
endif()

target_link_libraries(${consumer} PRIVATE ${producer})

get_target_property(_scan ${producer} INTERFACE_CXX_SCAN_FOR_MODULES)
if(_scan)
set_target_properties(${consumer} PROPERTIES CXX_SCAN_FOR_MODULES ${_scan})
endif()

get_target_property(_module_dir ${producer} INTERFACE_EXPORT_MODULE_DIR)
if(_module_dir)
if(MSVC)
return()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID
MATCHES "AppleClang"
)
target_compile_options(
${consumer} PRIVATE "-fprebuilt-module-path=${_module_dir}"
)
get_target_property(_type ${consumer} TYPE)
if((_type STREQUAL "SHARED_LIBRARY") OR (_type STREQUAL "EXECUTABLE"))
target_link_options(${consumer} PRIVATE "-fuse-ld=lld")
target_link_options(${consumer} PRIVATE "-Wl,--error-limit=0")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
hpx_add_target_compile_option_if_available(
${consumer} PRIVATE "-fprebuilt-module-path=${_module_dir}" RESULT ok
)
if(NOT ok)
hpx_error(
"hpx_configure_module_consumer: the used version of gcc does not "
"support '-fprebuilt-module-path='"
)
endif()
else()
hpx_warn(
"hpx_configure_module_consumer: unknown compiler '${CMAKE_CXX_COMPILER_ID}'"
)
endif()
endif()
endfunction()
8 changes: 3 additions & 5 deletions cmake/HPX_CollectStdHeaders.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,9 @@ function(hpx_extract_includes_from_file module)
foreach(include ${includes})
string(REGEX REPLACE "#include (<[^>]+>)" "\\1" filename ${include})

if(NOT filename MATCHES "\\.|/")
# Check if the include is a standard library header
if(${filename} IN_LIST STANDARD_LIBRARY_HEADERS)
list(APPEND found_includes ${filename})
endif()
if("${filename}" IN_LIST STANDARD_LIBRARY_HEADERS)
# Capture only headers explicitly listed in STANDARD_LIBRARY_HEADERS.
list(APPEND found_includes "${filename}")
endif()
endforeach()

Expand Down
12 changes: 10 additions & 2 deletions cmake/HPX_GeneratePackage.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ write_basic_package_version_file(
COMPATIBILITY AnyNewerVersion
)

# Export HPXInternalTargets in the build directory
# Export HPXInternalTargets in the build directory. Use the EXPORT signature so
# CMake also generates the per-target C++ module metadata files.
set(_cxx_modules_directory_arg)
if(HPX_WITH_CXX_MODULES)
set(_cxx_modules_directory_arg CXX_MODULES_DIRECTORY cxx-modules)
endif()
export(
TARGETS ${HPX_EXPORT_INTERNAL_TARGETS}
NAMESPACE HPXInternal::
FILE "${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/${HPX_PACKAGE_NAME}/HPXInternalTargets.cmake"
${_cxx_modules_directory_arg}
)

# Export HPXInternalTargets in the install directory
Expand All @@ -35,11 +41,13 @@ install(
COMPONENT cmake
Comment thread
arpittkhandelwal marked this conversation as resolved.
)

# Export HPXTargets in the build directory
# Export HPXTargets in the build directory. Use the EXPORT signature so CMake
# also generates the per-target C++ module metadata files.
export(
TARGETS ${HPX_EXPORT_TARGETS}
NAMESPACE HPX::
FILE "${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/${HPX_PACKAGE_NAME}/HPXTargets.cmake"
${_cxx_modules_directory_arg}
)
Comment thread
arpittkhandelwal marked this conversation as resolved.

# Add aliases with the namespace for use within HPX
Expand Down
26 changes: 23 additions & 3 deletions cmake/HPX_SetupTarget.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function(hpx_setup_target target)
HPX_PREFIX
HEADER_ROOT
SCAN_FOR_MODULES
CXX_STANDARD
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You may want to add this new argument option to .cmake-format.py. That may require re-formatting the CMake files that use it, though.

)
set(multi_value_args DEPENDENCIES COMPONENT_DEPENDENCIES COMPILE_FLAGS
LINK_FLAGS INSTALL_FLAGS INSTALL_PDB
Expand Down Expand Up @@ -87,6 +88,13 @@ function(hpx_setup_target target)
hpx_debug("setup_target.${target}" "LINK_FLAGS: ${target_LINK_FLAGS}")
endif()

if(target_CXX_STANDARD)
set_target_properties(
${target} PROPERTIES CXX_STANDARD ${target_CXX_STANDARD}
)
hpx_debug("setup_target.${target}" "CXX_STANDARD: ${target_CXX_STANDARD}")
endif()

if(target_NAME)
set(name "${target_NAME}")
else()
Expand Down Expand Up @@ -232,9 +240,21 @@ function(hpx_setup_target target)
if(HPX_WITH_CXX_MODULES AND target_SCAN_FOR_MODULES)
hpx_debug("setup_target.${target} SCAN_FOR_MODULES: ON")

hpx_configure_module_consumer(${target} hpx_core_module_if)
if(TARGET hpx_core_module_if)
hpx_configure_module_consumer(${target} hpx_core_module_if)
Comment thread
arpittkhandelwal marked this conversation as resolved.
elseif(TARGET HPXInternal::hpx_core_module_if)
hpx_configure_module_consumer(${target} HPXInternal::hpx_core_module_if)
else()
hpx_error(
"setup_target.${target}: C++ modules scanning is enabled, but neither "
"hpx_core_module_if nor HPXInternal::hpx_core_module_if exists"
)
endif()

if(TARGET hpx_full_module_if)
hpx_configure_module_consumer(${target} hpx_full_module_if)
elseif(TARGET HPXInternal::hpx_full_module_if)
hpx_configure_module_consumer(${target} HPXInternal::hpx_full_module_if)
Comment thread
arpittkhandelwal marked this conversation as resolved.
Comment thread
arpittkhandelwal marked this conversation as resolved.
endif()
else()
hpx_debug("setup_target.${target} SCAN_FOR_MODULES: OFF")
Expand All @@ -247,8 +267,8 @@ function(hpx_setup_target target)

# If modules are enabled, Clang emits DWARF v5, which requires using lld
# instead of ld.
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES
"AppleClang"
if((NOT MSVC) AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang"
OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")
)
Comment thread
arpittkhandelwal marked this conversation as resolved.
get_target_property(_type ${target} TYPE)
if((_type STREQUAL "SHARED_LIBRARY") OR (_type STREQUAL "EXECUTABLE"))
Expand Down
22 changes: 21 additions & 1 deletion examples/gtest_emulation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,23 @@ if(EXISTS "${HPX_DIR}")

if(HPX_WITH_CXX_MODULES)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
# CMake only propagates imported C++ module metadata from targets that are
# linked directly by the consumer. The exported HPXInternal module targets
# are therefore linked explicitly for the external build tests.
set(hpx_cxx_module_targets)
if(TARGET HPXInternal::hpx_core_module)
list(APPEND hpx_cxx_module_targets HPXInternal::hpx_core_module)
endif()
if(TARGET HPXInternal::hpx_full_module)
list(APPEND hpx_cxx_module_targets HPXInternal::hpx_full_module)
endif()
endif()

# Add a static library which contains a main to emulate gtest_main
add_library(static_main_lib STATIC static_main.cpp)
set_target_properties(
static_main_lib PROPERTIES CXX_STANDARD ${HPX_CXX_STANDARD}
)

# /!\ This helper interface is needed to keep the right linking order
add_library(hpx_helper_interface INTERFACE)
Expand All @@ -27,7 +40,14 @@ if(EXISTS "${HPX_DIR}")

# Test with the main function in a separate static library
add_executable(hpx_main_ext_main hpx_main_ext_main.cpp)
target_link_libraries(hpx_main_ext_main PRIVATE hpx_helper_interface)
# Keep the helper interface for link order, but link module targets directly
# to the executable so CMake can see the imported BMI metadata.
target_link_libraries(
hpx_main_ext_main PRIVATE hpx_helper_interface ${hpx_cxx_module_targets}
)
set_target_properties(
hpx_main_ext_main PROPERTIES CXX_STANDARD ${HPX_CXX_STANDARD}
)

enable_testing()
add_test(hello_world_test hpx_main_ext_main)
Expand Down
39 changes: 32 additions & 7 deletions examples/hello_world_component/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@ project(hello_world_client CXX)
if(EXISTS "${HPX_DIR}")
find_package(HPX REQUIRED)

if(HPX_WITH_DISTRIBUTED_RUNTIME)
add_library(hello_world_component SHARED hello_world_component.cpp)
endif()

if(HPX_WITH_CXX_MODULES)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
# CMake only propagates imported C++ module metadata from targets that are
# linked directly by the consumer. The exported HPXInternal module targets
# are therefore linked explicitly for the external build tests.
set(hpx_cxx_module_targets)
if(TARGET HPXInternal::hpx_core_module)
list(APPEND hpx_cxx_module_targets HPXInternal::hpx_core_module)
endif()
if(TARGET HPXInternal::hpx_full_module)
list(APPEND hpx_cxx_module_targets HPXInternal::hpx_full_module)
endif()
endif()

if(HPX_WITH_DISTRIBUTED_RUNTIME)
add_library(hello_world_component SHARED hello_world_component.cpp)
endif()

add_executable(hello_world_client hello_world_client.cpp)
Expand All @@ -26,11 +36,15 @@ if(EXISTS "${HPX_DIR}")
if(HPX_WITH_DISTRIBUTED_RUNTIME)
target_link_libraries(
hello_world_component PUBLIC HPX::hpx HPX::iostreams_component
${hpx_cxx_module_targets}
)
target_link_libraries(hello_world_component PRIVATE HPX::component)
target_link_libraries(hello_world_client PRIVATE hello_world_component)
endif()
target_link_libraries(hello_world_client PRIVATE HPX::hpx HPX::wrap_main)
target_link_libraries(
hello_world_client PRIVATE HPX::hpx HPX::wrap_main
${hpx_cxx_module_targets}
)

# We still support not linking to HPX::wrap_main when
# HPX_WITH_DYNAMIC_HPX_MAIN=OFF for legacy use. This can only be done using
Expand All @@ -44,6 +58,7 @@ if(EXISTS "${HPX_DIR}")

target_link_libraries(
hello_world_client_only_hpx_init PRIVATE hello_world_component
${hpx_cxx_module_targets}
)
endif()
elseif("${SETUP_TYPE}" STREQUAL "MACROS")
Expand All @@ -53,10 +68,20 @@ if(EXISTS "${HPX_DIR}")
COMPONENT_DEPENDENCIES iostreams
DEPENDENCIES HPX::wrap_main
TYPE COMPONENT
SCAN_FOR_MODULES ${HPX_WITH_CXX_MODULES} CXX_STANDARD
${HPX_CXX_STANDARD}
)
hpx_setup_target(
hello_world_client
DEPENDENCIES hello_world_component
SCAN_FOR_MODULES ${HPX_WITH_CXX_MODULES} CXX_STANDARD
${HPX_CXX_STANDARD}
)
hpx_setup_target(hello_world_client DEPENDENCIES hello_world_component)
else()
hpx_setup_target(hello_world_client)
hpx_setup_target(
hello_world_client SCAN_FOR_MODULES ${HPX_WITH_CXX_MODULES}
CXX_STANDARD ${HPX_CXX_STANDARD}
)
endif()
else()
message(FATAL_ERROR "Unknown SETUP_TYPE=\"${SETUP_TYPE}\"")
Expand Down
Loading
Loading