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
120 changes: 29 additions & 91 deletions cmake/HPX_CXXModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# 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)
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.29+ with FILE_SET CXX_MODULES handles BMI generation and
# -fmodule-output/-fprebuilt-module-path flags automatically. No manual compiler
# flags are needed here.
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
# 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()
9 changes: 4 additions & 5 deletions cmake/HPX_CollectStdHeaders.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,10 @@ 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 standard library headers (with or without extensions, e.g.
# <vector>, <link.h>, <dlfcn.h>).
Comment on lines +138 to +139
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The comment mentions capturing headers like <link.h>/<dlfcn.h>, but those are not present in STANDARD_LIBRARY_HEADERS above. As written, they will never be captured by the IN_LIST STANDARD_LIBRARY_HEADERS check. Either add the intended headers to STANDARD_LIBRARY_HEADERS (if you want them treated as “standard” for this purpose), or adjust the comment to match the actual list being checked.

Suggested change
# Capture standard library headers (with or without extensions, e.g.
# <vector>, <link.h>, <dlfcn.h>).
# Capture only headers explicitly listed in STANDARD_LIBRARY_HEADERS
# (for example, <vector>).

Copilot uses AI. Check for mistakes.
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.

This may be a valid comment to address.

list(APPEND found_includes "${filename}")
endif()
endforeach()

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

# Export HPXInternalTargets in the build directory
# CXX_MODULES_DIRECTORY was added in CMake 3.28
set(_cxx_modules_directory_arg)
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
set(_cxx_modules_directory_arg CXX_MODULES_DIRECTORY cxx-modules)
endif()
Comment on lines +22 to +26
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

_cxx_modules_directory_arg is applied to the build-tree export() calls, but the corresponding install(EXPORT ...) blocks for HPXInternalTargets and HPXTargets do not pass CXX_MODULES_DIRECTORY. If install(EXPORT) is missing this argument, the installed package may not install/generate the per-target C++ module metadata directory, breaking downstream find_package(HPX) consumption of modules from an install tree. Consider forwarding the same ${_cxx_modules_directory_arg} (guarded by the same CMake version check) to both install(EXPORT ...) calls as well.

Copilot uses AI. Check for mistakes.
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.

@arpittkhandelwal Is this a valid comment?


# Export HPXInternalTargets in the build directory. Use the EXPORT signature so
# CMake also generates the per-target C++ module metadata files.
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 +43,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
)
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