Skip to content

Expose version module using C++20 modules #6761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions .cmake-format.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@
'EXCLUDE_FROM_GLOBAL_HEADER': '+',
'ADD_TO_GLOBAL_HEADER': '+',
'GLOBAL_HEADER_GEN': 1,
'GLOBAL_HEADER_MODULE_GEN': 1,
'HEADERS': '+',
'OBJECTS': '+',
'MODULE_DEPENDENCIES': '+',
Expand Down
19 changes: 19 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

cmake_minimum_required(VERSION 3.18 FATAL_ERROR)

if(HPX_WITH_CXX_MODULES)
cmake_minimum_required(VERSION 3.29...3.29.99 FATAL_ERROR)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't it work with newer versions (than 3.29) as well?

Suggested change
cmake_minimum_required(VERSION 3.29...3.29.99 FATAL_ERROR)
if (NOT (CMAKE_VERSION VERSION_GREATER_EQUAL "3.29"))
hpx_fatal("Please use a version of CMake newer than V3.28 in order to enable C++ module support for HPX")
endif()

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the catch! I will switch from the fixed range to a VERSION_GREATER_EQUAL 3.29 check. I will keep it inside if(HPX_WITH_CXX_MODULES) so non‑module builds still work with CMake ≥ 3.18.

endif()

# Overrides must go before the project() statement, otherwise they are ignored.

# ##############################################################################
Expand Down Expand Up @@ -312,6 +316,21 @@ if(HPX_WITH_UNITY_BUILD)
set(HPX_WITH_UNITY_BUILD_OPTION UNITY_BUILD)
endif()

# ##############################################################################
# C++20 modules configuration
# ##############################################################################
hpx_option(
HPX_WITH_CXX_MODULES BOOL "Enable C++20 modules mode (default: OFF)." OFF
ADVANCED
)
if(HPX_WITH_CXX_MODULES)
hpx_info("HPX will build and install C++20 module interface units for downstream users.")
hpx_info("Note: HPX itself will still be built using traditional headers.")
add_compile_definitions(HPX_HAVE_CXX_MODULES)
Copy link
Member

Choose a reason for hiding this comment

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

Generally, all preprocessor macros are put into a special file (<hpx/config/defines.hpp>) instead of being added to the compiler's command line. This ensures that all targets that depend on HPX (even external ones) will see a consistent set of preprocessor definitions.

Suggested change
add_compile_definitions(HPX_HAVE_CXX_MODULES)
hpx_add_config_define(HPX_HAVE_CXX_MODULES)

else()
hpx_info("HPX will be built and used with traditional headers only.")
endif()

hpx_option(
HPX_WITH_PRECOMPILED_HEADERS
BOOL
Expand Down
25 changes: 21 additions & 4 deletions cmake/HPX_AddModule.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ include(HPX_PrintSummary)
function(add_hpx_module libname modulename)
# Retrieve arguments
set(options CUDA CONFIG_FILES NO_CONFIG_IN_GENERATED_HEADERS)
set(one_value_args GLOBAL_HEADER_GEN)
set(one_value_args GLOBAL_HEADER_GEN GLOBAL_HEADER_MODULE_GEN)
set(multi_value_args
SOURCES
HEADERS
Expand Down Expand Up @@ -154,10 +154,27 @@ function(add_hpx_module libname modulename)
if(NOT ${modulename}_NO_CONFIG_IN_GENERATED_HEADERS)
set(module_headers "${module_headers}#endif\n")
endif()
configure_file(
"${PROJECT_SOURCE_DIR}/cmake/templates/global_module_header.hpp.in"
"${global_header}"

# Decide output path and template file
if(DEFINED ${modulename}_GLOBAL_HEADER_MODULE_GEN
AND ${modulename}_GLOBAL_HEADER_MODULE_GEN
)
set(global_header
"${CMAKE_CURRENT_BINARY_DIR}/include/hpx/global_module.hpp"
)
set(template_file
"${HPX_SOURCE_DIR}/cmake/templates/global_module_header_modules.hpp.in"
)
else()
set(global_header
"${CMAKE_CURRENT_BINARY_DIR}/include/hpx/modules/${modulename}.hpp"
)
set(template_file
"${PROJECT_SOURCE_DIR}/cmake/templates/global_module_header.hpp.in"
)
endif()
Comment on lines +159 to +175
Copy link
Member

Choose a reason for hiding this comment

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

The name of the generated file should be the same in both cases:

Suggested change
if(DEFINED ${modulename}_GLOBAL_HEADER_MODULE_GEN
AND ${modulename}_GLOBAL_HEADER_MODULE_GEN
)
set(global_header
"${CMAKE_CURRENT_BINARY_DIR}/include/hpx/global_module.hpp"
)
set(template_file
"${HPX_SOURCE_DIR}/cmake/templates/global_module_header_modules.hpp.in"
)
else()
set(global_header
"${CMAKE_CURRENT_BINARY_DIR}/include/hpx/modules/${modulename}.hpp"
)
set(template_file
"${PROJECT_SOURCE_DIR}/cmake/templates/global_module_header.hpp.in"
)
endif()
set(global_header
"${CMAKE_CURRENT_BINARY_DIR}/include/hpx/modules/${modulename}.hpp"
)
if(${modulename}_GLOBAL_HEADER_MODULE_GEN)
set(template_file
"${HPX_SOURCE_DIR}/cmake/templates/global_module_header_modules.hpp.in"
)
else()
set(template_file
"${PROJECT_SOURCE_DIR}/cmake/templates/global_module_header.hpp.in"
)
endif()


configure_file(${template_file} ${global_header} @ONLY)
set(generated_headers ${global_header})
endif()

Expand Down
16 changes: 16 additions & 0 deletions cmake/templates/global_module_header_modules.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2019-@HPX_COPYRIGHT_YEAR@ STE||AR Group
//
// SPDX-License-Identifier: BSL-1.0
// 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)

// Do not edit this file! It has been generated by the CMake configuration step.

#pragma once

#if defined(HPX_HAVE_CXX_MODULES)
import HPX.Core;
#else
#include <hpx/version.hpp>
Copy link
Member

Choose a reason for hiding this comment

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

For all HPX modules to be converted in the future, the generated module header should still include all necessary files as before:

Suggested change
#include <hpx/version.hpp>
@module_headers@

#endif
17 changes: 16 additions & 1 deletion libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,29 @@ foreach(lib ${HPX_LIBS})
add_library(hpx_${lib} ${hpx_library_link_mode} src/empty.cpp)
target_compile_definitions(hpx_${lib} PRIVATE HPX_${uppercase_lib}_EXPORTS)

if(HPX_WITH_CXX_MODULES)
target_sources(
hpx_${lib} PUBLIC FILE_SET hpx_${lib}_public_sources TYPE CXX_MODULES
FILES ${CMAKE_CURRENT_SOURCE_DIR}/core/hpx_core.ixx
)
endif()

set_target_properties(hpx_${lib} PROPERTIES FOLDER "Core")

if(lib STREQUAL "core" AND HPX_WITH_CXX_MODULES)
set(module_installation FILE_SET hpx_core_public_sources DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}
)
endif()

install(
TARGETS hpx_${lib}
EXPORT HPXInternalTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT runtime
${module_installation}
)

# install PDB if needed
Expand Down
12 changes: 12 additions & 0 deletions libs/core/config/include/hpx/config/export_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@
# define HPX_CORE_EXPORT HPX_SYMBOL_IMPORT
#endif

///////////////////////////////////////////////////////////////////////////////
// C++20 module export definitions
#if defined(HPX_BUILD_MODULE)
#undef HPX_CORE_EXPORT
# define HPX_CORE_EXPORT /* empty */
# define HPX_MODULE_EXPORT export
# define HPX_MODULE_EXTERN_CORE extern "C++" export
#else
# define HPX_MODULE_EXPORT /* empty */
# define HPX_MODULE_EXTERN_CORE HPX_CORE_EXPORT
#endif

///////////////////////////////////////////////////////////////////////////////
#if defined(HPX_EXPORTS) || defined(HPX_FULL_EXPORTS)
# define HPX_EXPORT HPX_SYMBOL_EXPORT
Expand Down
21 changes: 21 additions & 0 deletions libs/core/hpx_core.ixx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2025 Haokun Wu
//
// SPDX-License-Identifier: BSL-1.0
// 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)

module;

// Include all system headers in global module fragment to prevent ODR violations
#include <algorithm>
#include <cstdint>
#include <sstream>
#include <string>

// Define module-specific macros before including config
#define HPX_BUILD_MODULE
#include "config/include/hpx/config.hpp"
Copy link
Member

@hkaiser hkaiser Aug 9, 2025

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:

Suggested change
#include "config/include/hpx/config.hpp"
#include <hpx/config.hpp>

If you would like to minimize the #included content, this could be:

Suggested change
#include "config/include/hpx/config.hpp"
#include <hpx/config/defines.hpp>

which contains all the HPX_HAVE_XXX macro definitions.


export module HPX.Core;

#include "version/include/hpx/version.hpp"
Copy link
Member

@hkaiser hkaiser Aug 9, 2025

Choose a reason for hiding this comment

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

Now that we're generating the module headers correctly, shouldn't we add them here (one by one as we adapt the HPX modules):

Suggested change
#include "version/include/hpx/version.hpp"
#include <hpx/modules/version.hpp>

3 changes: 2 additions & 1 deletion libs/core/version/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ endif()
include(HPX_AddModule)
add_hpx_module(
core version
GLOBAL_HEADER_GEN OFF
GLOBAL_HEADER_GEN ON
GLOBAL_HEADER_MODULE_GEN TRUE
Copy link
Member

Choose a reason for hiding this comment

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

Just for consistency:

Suggested change
GLOBAL_HEADER_MODULE_GEN TRUE
GLOBAL_HEADER_MODULE_GEN ON

SOURCES ${version_sources}
HEADERS ${version_headers}
MODULE_DEPENDENCIES hpx_config hpx_config_registry hpx_format hpx_prefix
Expand Down
36 changes: 18 additions & 18 deletions libs/core/version/include/hpx/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,56 +17,56 @@
namespace hpx {

// Returns the major HPX version.
[[nodiscard]] HPX_CORE_EXPORT std::uint8_t major_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::uint8_t major_version();
Copy link
Member

Choose a reason for hiding this comment

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

I think this has to be in a reverse sequence, it may generate compilation errors otherwise.

Suggested change
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::uint8_t major_version();
[[nodiscard]] HPX_MODULE_EXTERN_CORE std::uint8_t major_version();


// Returns the minor HPX version.
[[nodiscard]] HPX_CORE_EXPORT std::uint8_t minor_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::uint8_t minor_version();

// Returns the sub-minor/patch-level HPX version.
[[nodiscard]] HPX_CORE_EXPORT std::uint8_t subminor_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::uint8_t subminor_version();

// Returns the full HPX version.
[[nodiscard]] HPX_CORE_EXPORT std::uint32_t full_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::uint32_t full_version();

// Returns the full HPX version.
[[nodiscard]] HPX_CORE_EXPORT std::string full_version_as_string();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string full_version_as_string();

// Returns the AGAS subsystem version.
[[nodiscard]] HPX_CORE_EXPORT std::uint8_t agas_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::uint8_t agas_version();

// Returns the tag.
[[nodiscard]] HPX_CORE_EXPORT std::string tag();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string tag();

// Returns the HPX full build information string.
[[nodiscard]] HPX_CORE_EXPORT std::string full_build_string();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string full_build_string();

// Returns the HPX version string.
[[nodiscard]] HPX_CORE_EXPORT std::string build_string();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string build_string();

// Returns the Boost version string.
[[nodiscard]] HPX_CORE_EXPORT std::string boost_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string boost_version();

// Returns the Boost platform string.
[[nodiscard]] HPX_CORE_EXPORT std::string boost_platform();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string boost_platform();

// Returns the Boost compiler string.
[[nodiscard]] HPX_CORE_EXPORT std::string boost_compiler();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string boost_compiler();

// Returns the Boost standard library string.
[[nodiscard]] HPX_CORE_EXPORT std::string boost_stdlib();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string boost_stdlib();

// Returns the copyright string.
[[nodiscard]] HPX_CORE_EXPORT std::string copyright();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string copyright();

// Returns the full version string.
[[nodiscard]] HPX_CORE_EXPORT std::string complete_version();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string complete_version();

// Returns the HPX build type ('Debug', 'Release', etc.)
[[nodiscard]] HPX_CORE_EXPORT std::string build_type();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string build_type();

// Returns the HPX build date and time
[[nodiscard]] HPX_CORE_EXPORT std::string build_date_time();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string build_date_time();

// Return the HPX configuration information
[[nodiscard]] HPX_CORE_EXPORT std::string configuration_string();
HPX_MODULE_EXTERN_CORE [[nodiscard]] std::string configuration_string();
} // namespace hpx
2 changes: 1 addition & 1 deletion libs/core/version/tests/unit/version_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// 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/global_module.hpp>
Copy link
Member

@hkaiser hkaiser Aug 9, 2025

Choose a reason for hiding this comment

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

Let's stick with the conventions:

Suggested change
#include <hpx/global_module.hpp>
#include <hpx/modules/version.hpp>

Alternatively (to avoid having to touch all files that currently #include <hpx/version.hpp>), we could add #include <hpx/modules/version.hpp> in that file.

Not sure if this wouldn't create a circular #include sequence, though. In this case, we need to reshuffle the content slightly:

#include <hpx/modules/testing.hpp>
#include <hpx/version.hpp>

#include <cstdint>

Expand Down
Loading