diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index d6b9c64f..351cb387 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -40,9 +40,9 @@ jobs: "tests": [ { "stdlibs": ["libstdc++"], "tests": [ - "Debug.Default", "Release.Default", - "Release.MaxSan", "Debug.Werror", - "Debug.Dynamic", "Debug.Coverage" + "Debug.Default", "Debug.Coverage", + "Debug.MaxSan", "Debug.Werror", + "Release.Default", "Release.Dynamic" ] } ] @@ -67,8 +67,8 @@ jobs: "tests": [ { "stdlibs": ["libc++"], "tests": [ - "Debug.Default", "Release.Default", "Release.MaxSan", - "Debug.Dynamic" + "Debug.Default", "Debug.MaxSan", + "Release.Default", "Release.Dynamic" ] } ] @@ -96,7 +96,10 @@ jobs: { "cxxversions": ["c++23"], "tests": [ { "stdlibs": ["stl"], - "tests": ["Debug.Default", "Release.Default", "Release.MaxSan"] + "tests": [ + "Debug.Default", "Debug.MaxSan", + "Release.Default", "Release.Dynamic" + ] } ] } diff --git a/CMakeLists.txt b/CMakeLists.txt index 095c67d2..5907ee0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,33 +26,32 @@ if(BEMAN_USE_MODULES) # CMake requires the language standard to be specified as compile feature # when a target provides C++23 modules and the target will be installed - add_library(beman.execution STATIC) - add_library(beman::execution ALIAS beman.execution) + add_library(${TARGET_PREFIX} STATIC) + add_library(beman::execution ALIAS ${TARGET_PREFIX}) target_compile_features( - beman.execution + ${TARGET_PREFIX} PUBLIC cxx_std_${CMAKE_CXX_STANDARD} ) include(GenerateExportHeader) - generate_export_header( - beman.execution - BASE_NAME beman.execution + ${TARGET_PREFIX} + BASE_NAME ${TARGET_PREFIX} EXPORT_FILE_NAME beman/execution/modules_export.hpp ) target_sources( - beman.execution + ${TARGET_PREFIX} PUBLIC FILE_SET HEADERS BASE_DIRS include ${CMAKE_CURRENT_BINARY_DIR} FILES ${CMAKE_CURRENT_BINARY_DIR}/beman/execution/modules_export.hpp ) - # FIXME: target_compile_definitions(beman.execution PUBLIC BEMAN_HAS_MODULES) + target_compile_definitions(${TARGET_PREFIX} PUBLIC BEMAN_HAS_MODULES) endif() if(BEMAN_USE_MODULES AND CMAKE_CXX_MODULE_STD) - target_compile_definitions(beman.execution PUBLIC BEMAN_HAS_IMPORT_STD) + target_compile_definitions(${TARGET_PREFIX} PUBLIC BEMAN_HAS_IMPORT_STD) else() message(WARNING "Missing support for CMAKE_CXX_MODULE_STD!") endif() @@ -72,13 +71,18 @@ option( add_subdirectory(src/beman/execution) +#=============================================================================== # NOTE: this must be done before tests! CK -include(./cmake/beman-install-library-config.cmake) -beman_install_library(${TARGET_PREFIX} headers) +include(./cmake/beman-install-library.cmake) +beman_install_library(${TARGET_PREFIX} TARGETS ${TARGET_NAME} ${TARGET_PREFIX} + # FIXME: DEPENDENCIES [===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt + # TODO(CK): XXX OR XXX DEPENDENCIES "beman.inplace_vector 1.0.0;beman.scope 0.0.1 EXACT" +) +#=============================================================================== -if(BEMAN_EXECUTION_ENABLE_TESTING) - enable_testing() +enable_testing() +if(BEMAN_EXECUTION_ENABLE_TESTING) add_subdirectory(tests/beman/execution) endif() diff --git a/Makefile b/Makefile index 6549b0d8..23dddc01 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ SANITIZERS := run # SANITIZERS += asan # TODO: tsan msan # endif -.PHONY: default release debug doc run update check ce todo distclean clean codespell clang-tidy build test install all format unstage $(SANITIZERS) module build-module test-module +.PHONY: default release debug doc run update check ce todo distclean clean codespell clang-tidy build test install all format unstage $(SANITIZERS) module build-module test-module build-interface SYSROOT ?= TOOLCHAIN ?= @@ -99,7 +99,7 @@ endif # TODO: beman.execution.examples.modules # FIXME: beman.execution.execution-module.test beman.execution.stop-token-module.test -default: release +default: module all: $(SANITIZERS) @@ -116,7 +116,7 @@ doc: # ========================================================== # NOTE: cmake configure to only test without modules! CK # ========================================================== -build: +build build-interface: cmake -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \ -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \ -D CMAKE_SKIP_INSTALL_RULES=ON \ @@ -126,9 +126,11 @@ build: -D CMAKE_CXX_SCAN_FOR_MODULES=OFF \ -D BEMAN_USE_MODULES=OFF \ -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_SKIP_TEST_ALL_DEPENDENCY=OFF \ -D CMAKE_CXX_COMPILER=$(CXX) --log-level=VERBOSE - cmake --build $(BUILD) -# XXX --fresh -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)" + # XXX --fresh -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)" + cmake --build $(BUILD) --target all_verify_interface_header_sets + cmake --build $(BUILD) --target all # NOTE: without install, see CMAKE_SKIP_INSTALL_RULES! CK test: build @@ -137,13 +139,15 @@ test: build module build-module: cmake -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \ -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -D CMAKE_SKIP_INSTALL_RULES=ON \ + -D CMAKE_SKIP_INSTALL_RULES=OFF \ -D CMAKE_CXX_STANDARD=23 \ -D CMAKE_CXX_EXTENSIONS=ON \ -D CMAKE_CXX_STANDARD_REQUIRED=ON \ -D CMAKE_CXX_SCAN_FOR_MODULES=ON \ -D BEMAN_USE_MODULES=ON \ -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_MESSAGE=LAZY \ + -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_COMPILER=$(CXX) --log-level=VERBOSE cmake --build $(BUILD) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in new file mode 100644 index 00000000..6a965772 --- /dev/null +++ b/cmake/Config.cmake.in @@ -0,0 +1,10 @@ +# cmake/Config.cmake.in -*-makefile-*- +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(CMakeFindDependencyMacro) + +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake) + +check_required_components(@PROJECT_NAME@) diff --git a/cmake/README.md b/cmake/README.md new file mode 100644 index 00000000..954e3e3d --- /dev/null +++ b/cmake/README.md @@ -0,0 +1,306 @@ + + +# Create and install an interface library and the `CXX_MODULE` if possible + +To be able to compile a project with and without MODULES, the project needs at least 2 targets: + +- an interface library (header only) with one `PUBLIC FILE_SET HEADERS` +- a library (static and/or dynamic) with one `PUBLIC FILE_SET CXX_MODULE` + + +## NEW CMAKE modules intended to be in infra/cmake: + +- prelude.cmake +- cxx-modules-rules.cmake +- beman-install-library.cmake +- Config.cmake.in + + +## NEW added related CMake options: + + * BEMAN_USE_MODULES:BOOL=ON + * BEMAN_USE_STD_MODULE:BOOL=OFF + * BEMAN_HAS_IMPORT_STD::BOOL=${CMAKE_CXX_SCAN_FOR_MODULES} # only if toolchain supports it. + +### The recommended usage in CMake code + +```cmake +if(CMAKE_CXX_STANDARD GREATER_EQUAL 20) + option(BEMAN_USE_MODULES "Build CXX_MODULES" ${CMAKE_CXX_SCAN_FOR_MODULES}) +endif() +if(BEMAN_USE_MODULES) + target_compile_definitions(beman.execution PUBLIC BEMAN_HAS_MODULES) +endif() + +BEMAN_USE_STD_MODULE:BOOL=ON +# -> "Check if 'import std;' is possible with the toolchain?" + +if(BEMAN_USE_MODULES AND CMAKE_CXX_MODULE_STD) + target_compile_definitions(beman.execution PUBLIC BEMAN_HAS_IMPORT_STD) +endif() +``` + + +### beman_install_library + +Installs a library (or set of targets) along with headers, C++ modules, +and optional CMake package configuration files. + +#### Usage: + + beman_install_library( + TARGETS target1 [target2 ...] + DEPENDENCIES dependency1 [dependency2 ...] + [NAMESPACE ] + [EXPORT_NAME ] + [DESTINATION ] + ) + +#### Arguments: + +name + Logical package name (e.g. "beman.utility"). + Used to derive config file names and cache variable prefixes. + +TARGETS (required) + List of CMake targets to install. + +DEPENDENCIES (optional) + Semicolon-separated list, one dependency per entry. + Each entry is a valid find_dependency() argument list. + Note: you must use the bracket form for quoting if not only a package name is used! + "[===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt" + +NAMESPACE (optional) + Namespace for exported targets. + Defaults to "beman::". + +EXPORT_NAME (optional) + Name of the CMake export set. + Defaults to "-targets". + +DESTINATION (optional) + The install destination for CXX_MODULES. + Defaults to CMAKE_INSTALL_INCLUDEDIR/beman/modules. + +#### Brief + +This function installs the specified project TARGETS and its FILE_SET +HEADERS to the default CMAKE install Destination. + +It also handles the installation of the CMake config package files if +needed. If the given targets has FILE_SET CXX_MODULE, it will also +installed to the given DESTINATION + +#### Cache variables: + +BEMAN_INSTALL_CONFIG_FILE_PACKAGES + List of package names for which config files should be installed. + +_INSTALL_CONFIG_FILE_PACKAGE + Per-package override to enable/disable config file installation. + is the uppercased package name with dots replaced by underscores. + + +## The possible usage in CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.30...4.2) + +include(./cmake/prelude.cmake) +project(beman.execution VERSION 0.2.0 LANGUAGES CXX) +include(./cmake/cxx-modules-rules.cmake) + +set(TARGET_PREFIX ${PROJECT_NAME}) + +#=============================================================================== +if(BEMAN_USE_MODULES) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) + + # CMake requires the language standard to be specified as compile feature + # when a target provides C++23 modules and the target will be installed + add_library(${TARGET_PREFIX} STATIC) + add_library(beman::execution ALIAS ${TARGET_PREFIX}) + target_compile_features( + ${TARGET_PREFIX} + PUBLIC cxx_std_${CMAKE_CXX_STANDARD} + ) + + include(GenerateExportHeader) + generate_export_header( + ${TARGET_PREFIX} + BASE_NAME ${TARGET_PREFIX} + EXPORT_FILE_NAME beman/execution/modules_export.hpp + ) + target_sources( + ${TARGET_PREFIX} + PUBLIC + FILE_SET HEADERS + BASE_DIRS include ${CMAKE_CURRENT_BINARY_DIR} + FILES + ${CMAKE_CURRENT_BINARY_DIR}/beman/execution/modules_export.hpp + ) + target_compile_definitions(${TARGET_PREFIX} PUBLIC BEMAN_HAS_MODULES) +endif() + +if(BEMAN_USE_MODULES AND CMAKE_CXX_MODULE_STD) + target_compile_definitions(${TARGET_PREFIX} PUBLIC BEMAN_HAS_IMPORT_STD) +else() + message(WARNING "Missing support for CMAKE_CXX_MODULE_STD!") +endif() +#=============================================================================== + +# ... + +# NOTE: this must be done before tests! CK +include(beman-install-library) +beman_install_library(${PROJECT_NAME} TARGETS ${TARGET_PREFIX} beman.exemplar_headers # + # TODO(add): DEPENDENCIES [===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt +) +``` + + +## Possible cmake config output + +```bash +bash-5.3$ make test-module +cmake -G Ninja -S /Users/clausklein/Workspace/cpp/beman-project/execution26 -B build/Darwin/default \ + -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -D CMAKE_SKIP_INSTALL_RULES=OFF \ + -D CMAKE_CXX_STANDARD=23 \ + -D CMAKE_CXX_EXTENSIONS=ON \ + -D CMAKE_CXX_STANDARD_REQUIRED=ON \ + -D CMAKE_CXX_SCAN_FOR_MODULES=ON \ + -D BEMAN_USE_MODULES=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_MESSAGE=LAZY \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_COMPILER=clang++ --log-level=VERBOSE +-- use ccache +-- CXXFLAGS=-stdlib=libc++ +'brew' '--prefix' 'llvm' +-- LLVM_DIR=/usr/local/Cellar/llvm/21.1.8 +-- CMAKE_CXX_STDLIB_MODULES_JSON=/usr/local/Cellar/llvm/21.1.8/lib/c++/libc++.modules.json +-- CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=/usr/local/Cellar/llvm/21.1.8/include/c++/v1;/usr/local/Cellar/llvm/21.1.8/lib/clang/21/include;/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/usr/include +-- CMAKE_CXX_STDLIB_MODULES_JSON=/usr/local/Cellar/llvm/21.1.8/lib/c++/libc++.modules.json +-- BEMAN_USE_STD_MODULE=ON +-- CMAKE_CXX_COMPILER_IMPORT_STD=23;26 +-- CMAKE_CXX_MODULE_STD=ON +-- BEMAN_HAS_IMPORT_STD=ON +-- BEMAN_USE_MODULES=ON +-- CMAKE_CXX_SCAN_FOR_MODULES=ON +-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY +-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY - Success +-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY +-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY - Success +-- Performing Test COMPILER_HAS_DEPRECATED_ATTR +-- Performing Test COMPILER_HAS_DEPRECATED_ATTR - Success +-- beman_install_library(beman.execution): COMPONENT execution_headers for TARGET 'beman.execution_headers' +-- beman-install-library(beman.execution): 'beman.execution_headers' has INTERFACE_HEADER_SETS=public_headers +-- beman_install_library(beman.execution): COMPONENT execution for TARGET 'beman.execution' +-- beman-install-library(beman.execution): 'beman.execution' has INTERFACE_HEADER_SETS=HEADERS +-- beman-install-library(beman.execution): 'beman.execution' has CXX_MODULE_SETS=CXX_MODULES +-- Configuring done (3.1s) +CMake Warning (dev) in CMakeLists.txt: + CMake's support for `import std;` in C++23 and newer is experimental. It + is meant only for experimentation and feedback to CMake developers. +This warning is for project developers. Use -Wno-dev to suppress it. + +-- Generating done (0.4s) +-- Build files have been written to: /Users/clausklein/Workspace/cpp/beman-project/execution26/build/Darwin/default +bash-5.3$ +``` + + +## Possible cmake export config package + +```bash +cmake --install build/Darwin/default --prefix $PWD/stagedir + +bash-5.3$ cd stagedir/ +bash-5.3$ tree lib/ +lib/ +├── cmake +│   └── beman.execution +│   ├── beman.execution-config-version.cmake +│   ├── beman.execution-config.cmake +│   ├── beman.execution-targets-debug.cmake +│   ├── beman.execution-targets-relwithdebinfo.cmake +│   ├── beman.execution-targets.cmake +│   ├── bmi-GNU_Debug +│   │   └── beman.execution.gcm +│   ├── bmi-GNU_RelWithDebInfo +│   │   └── beman.execution.gcm +│   ├── cxx-modules +│   │   ├── cxx-modules-beman.execution-Debug.cmake +│   │   ├── cxx-modules-beman.execution-RelWithDebInfo.cmake +│   │   ├── cxx-modules-beman.execution.cmake +│   │   ├── target-execution-Debug.cmake +│   │   └── target-execution-RelWithDebInfo.cmake +│   └── modules +│   └── execution.cppm +├── libbeman.execution.a +└── libbeman.execution_d.a + +7 directories, 15 files +bash-5.3$ +``` + + +## The recommended usage in implementation + +```cpp +// identity.cppm +module; + +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#endif + +export module beman.exemplar; + +export namespace beman::exemplar { +struct __is_transparent; + +struct identity { + template + constexpr T&& operator()(T&& t) const noexcept { + return std::forward(t); + } + + using is_transparent = __is_transparent; +}; +} // namespace beman::exemplar +``` + + +## The possible usage in user code + +```cpp +// example-usage.cpp + +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#endif + +#ifdef BEMAN_HAS_MODULES +import beman.exemplar; +#else +#include +#endif + +int main() { + beman::exemplar::identity id; + + int x = 42; + int y = id(x); // y == 42 + + std::cout << y << '\n'; + return 0; +} +``` diff --git a/cmake/beman-install-library-config.cmake b/cmake/beman-install-library-config.cmake deleted file mode 100644 index 4ad27ff2..00000000 --- a/cmake/beman-install-library-config.cmake +++ /dev/null @@ -1,251 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -include_guard(GLOBAL) - -# This file defines the function `beman_install_library` which is used to -# install a library target and its headers, along with optional CMake -# configuration files. -# -# The function is designed to be reusable across different Beman libraries. - -function(beman_install_library name interface) - # Usage - # ----- - # - # beman_install_library(NAME INTERFACE) - # - # Brief - # ----- - # - # This function installs the specified library target and its headers. - # It also handles the installation of the CMake configuration files if needed. - # - # CMake variables - # --------------- - # - # Note that configuration of the installation is generally controlled by CMake - # cache variables so that they can be controlled by the user or tool running the - # `cmake` command. Neither `CMakeLists.txt` nor `*.cmake` files should set these - # variables directly. - # - # - BEMAN_INSTALL_CONFIG_FILE_PACKAGES: - # List of packages that require config file installation. - # If the package name is in this list, it will install the config file. - # - # - _INSTALL_CONFIG_FILE_PACKAGE: - # Boolean to control config file installation for the specific library. - # The prefix `` is the uppercased name of the library with dots - # replaced by underscores. - # - - # if(NOT TARGET "${name}") - # message(FATAL_ERROR "Target '${name}' does not exist.") - # endif() - - # if(NOT ARGN STREQUAL "") - # message( - # FATAL_ERROR - # "beman_install_library does not accept extra arguments: ${ARGN}" - # ) - # endif() - - # Given foo.bar, the component name is bar - string(REPLACE "." ";" name_parts "${name}") - # fail if the name doesn't look like foo.bar - list(LENGTH name_parts name_parts_length) - if(NOT name_parts_length EQUAL 2) - message( - FATAL_ERROR - "beman_install_library expects a name of the form 'beman.', got '${name}'" - ) - endif() - - set(target_name "${name}") - - # COMPONENT - # Specify an installation component name with which the install rule is associated, - # such as Runtime or Development. - set(install_component_name "${name}") # TODO(CK): this is not common name! - - set(export_name "${name}") - set(package_name "${name}") - list(GET name_parts -1 component_name) - - include(GNUInstallDirs) - - set(package_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}") - - set(target_list) - if(TARGET "${target_name}") - set_target_properties( - "${target_name}" - PROPERTIES EXPORT_NAME "${component_name}" - ) - message( - VERBOSE - "beman-install-library: COMPONENT ${component_name} for TARGET '${target_name}'" - ) - list(APPEND target_list "${target_name}") - - get_target_property( - INTERFACE_CXX_MODULE_SETS - ${target_name} - INTERFACE_CXX_MODULE_SETS - ) - if(INTERFACE_CXX_MODULE_SETS) - message( - VERBOSE - "beman-install-library: '${target_name}' has INTERFACE_CXX_MODULE_SETS=${INTERFACE_CXX_MODULE_SETS}" - ) - set(__INSTALL_CXX_MODULES - FILE_SET - ${INTERFACE_CXX_MODULE_SETS} - DESTINATION - ${package_install_dir}/modules - ) - endif() - endif() - - if(interface AND TARGET "${target_name}_${interface}") - set_target_properties( - "${target_name}_${interface}" - PROPERTIES EXPORT_NAME "${component_name}_${interface}" - ) - message( - VERBOSE - "beman-install-library: COMPONENT ${component_name} for TARGET '${name}_${interface}'" - ) - list(APPEND target_list "${target_name}_${interface}") - - get_target_property( - INTERFACE_HEADER_SETS - ${target_name}_${interface} - INTERFACE_HEADER_SETS - ) - if(INTERFACE_HEADER_SETS) - message( - VERBOSE - "beman-install-library: '${target_name}_${interface}' has INTERFACE_HEADER_SETS=${INTERFACE_HEADER_SETS}" - ) - set(__INSTALL_HEADER_SETS FILE_SET ${INTERFACE_HEADER_SETS}) - endif() - endif() - - if(CMAKE_SKIP_INSTALL_RULES) - message( - WARNING - "beman-install-library: not installing targets '${target_list}' due to CMAKE_SKIP_INSTALL_RULES" - ) - return() - endif() - - # ============================================================ - install( - TARGETS ${target_list} - COMPONENT "${install_component_name}" - EXPORT "${export_name}" - FILE_SET HEADERS - ${__INSTALL_HEADER_SETS} - # FILE_SET CXX_MODULES - # DESTINATION ${package_install_dir}/modules - ${__INSTALL_CXX_MODULES} - # There's currently no convention for this location - CXX_MODULES_BMI - DESTINATION - ${package_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ - ) - # ============================================================ - - # Determine the prefix for project-specific variables - string(TOUPPER "${name}" project_prefix) - string(REPLACE "." "_" project_prefix "${project_prefix}") - - option( - ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE - "Enable creating and installing a CMake config-file package. Default: ON. Values: { ON, OFF }." - ON - ) - - # By default, install the config package - set(install_config_package ON) - - # Turn OFF installation of config package by default if, - # in order of precedence: - # 1. The specific package variable is set to OFF - # 2. The package name is not in the list of packages to install config files - if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES) - if( - NOT "${install_component_name}" - IN_LIST - BEMAN_INSTALL_CONFIG_FILE_PACKAGES - ) - set(install_config_package OFF) - endif() - endif() - if(DEFINED ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE) - set(install_config_package - ${${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE} - ) - endif() - - if(install_config_package) - message( - VERBOSE - "beman-install-library: Export cmake config to ${package_install_dir} for '${name}'" - ) - - include(CMakePackageConfigHelpers) - - find_file( - config_file_template - NAMES "${package_name}-config.cmake.in" - PATHS "${PROJECT_SOURCE_DIR}/cmake" - NO_DEFAULT_PATH - NO_CACHE - REQUIRED - ) - set(config_package_file - "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-config.cmake" - ) - configure_package_config_file( - "${config_file_template}" - "${config_package_file}" - INSTALL_DESTINATION "${package_install_dir}" - PATH_VARS PROJECT_NAME PROJECT_VERSION - ) - - set(config_version_file - "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-config-version.cmake" - ) - write_basic_package_version_file( - "${config_version_file}" - VERSION "${PROJECT_VERSION}" - COMPATIBILITY ExactVersion - ) - - install( - FILES "${config_package_file}" "${config_version_file}" - DESTINATION "${package_install_dir}" - COMPONENT "${install_component_name}" - ) - - # NOTE: must be same value as ${TARGETS_EXPORT_NAME}.cmake! CK - set(config_targets_file "${package_name}-targets.cmake") - install( - EXPORT "${export_name}" - DESTINATION "${package_install_dir}" - NAMESPACE beman:: - FILE "${config_targets_file}" - CXX_MODULES_DIRECTORY - cxx-modules - COMPONENT "${install_component_name}" - ) - else() - message( - WARNING - "beman-install-library: Not installing a config package for '${name}'" - ) - endif() -endfunction() - -set(CPACK_GENERATOR TGZ) -include(CPack) diff --git a/cmake/beman-install-library.cmake b/cmake/beman-install-library.cmake new file mode 100644 index 00000000..a73f4fc7 --- /dev/null +++ b/cmake/beman-install-library.cmake @@ -0,0 +1,289 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.30) + +include_guard(GLOBAL) + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +# beman_install_library +# ===================== +# +# Installs a library (or set of targets) along with headers, C++ modules, +# and optional CMake package configuration files. +# +# Usage: +# ------ +# beman_install_library( +# TARGETS target1 [target2 ...] +# DEPENDENCIES dependency1 [dependency2 ...] +# [NAMESPACE ] +# [EXPORT_NAME ] +# [DESTINATION ] +# ) +# +# Arguments: +# ---------- +# name +# Logical package name (e.g. "beman.utility"). +# Used to derive config file names and cache variable prefixes. +# +# TARGETS (required) +# List of CMake targets to install. +# +# DEPENDENCIES (optional) +# Semicolon-separated list, one dependency per entry. +# Each entry is a valid find_dependency() argument list. +# Note: you must use the bracket form for quoting if not only a package name is used! +# "[===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt" +# +# NAMESPACE (optional) +# Namespace for exported targets. +# Defaults to "beman::". +# +# EXPORT_NAME (optional) +# Name of the CMake export set. +# Defaults to "-targets". +# +# DESTINATION (optional) +# The install destination for CXX_MODULES. +# Defaults to CMAKE_INSTALL_INCLUDEDIR/beman/modules. +# +# Brief +# ----- +# +# This function installs the specified project TARGETS and its FILE_SET +# HEADERS to the default CMAKE install Destination. +# +# It also handles the installation of the CMake config package files if +# needed. If the given targets has FILE_SET CXX_MODULE, it will also +# installed to the given DESTINATION +# +# Cache variables: +# ---------------- +# BEMAN_INSTALL_CONFIG_FILE_PACKAGES +# List of package names for which config files should be installed. +# +# _INSTALL_CONFIG_FILE_PACKAGE +# Per-package override to enable/disable config file installation. +# is the uppercased package name with dots replaced by underscores. +# +function(beman_install_library name) + # ---------------------------- + # Argument parsing + # ---------------------------- + set(options) + set(oneValueArgs NAMESPACE EXPORT_NAME DESTINATION) + set(multiValueArgs TARGETS DEPENDENCIES) + + cmake_parse_arguments( + BEMAN + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + + if(NOT BEMAN_TARGETS) + message( + FATAL_ERROR + "beman_install_library(${name}): TARGETS must be specified" + ) + endif() + + if(CMAKE_SKIP_INSTALL_RULES) + message( + WARNING + "beman_install_library(${name}): not installing targets '${BEMAN_TARGETS}' due to CMAKE_SKIP_INSTALL_RULES" + ) + return() + endif() + + set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}") + + # ---------------------------- + # Defaults + # ---------------------------- + if(NOT BEMAN_NAMESPACE) + set(BEMAN_NAMESPACE "beman::") + endif() + + if(NOT BEMAN_EXPORT_NAME) + set(BEMAN_EXPORT_NAME "${name}-targets") + endif() + + if(NOT BEMAN_DESTINATION) + # XXX set(BEMAN_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/beman/modules") + set(BEMAN_DESTINATION "${_config_install_dir}/modules") + endif() + + string(REPLACE "beman." "" install_component_name "${name}") + + # -------------------------------------------------- + # Install each target with all of its file sets + # -------------------------------------------------- + foreach(_tgt IN LISTS BEMAN_TARGETS) + if(NOT TARGET "${_tgt}") + message( + WARNING + "beman_install_library(${name}): '${_tgt}' is not a target" + ) + continue() + endif() + + # Given foo.bar, the component name is bar + string(REPLACE "." ";" name_parts "${_tgt}") + # fail if the name doesn't look like foo.bar + list(LENGTH name_parts name_parts_length) + if(NOT name_parts_length EQUAL 2) + message( + FATAL_ERROR + "beman_install_library(${name}): expects a name of the form 'beman.', got '${_tgt}'" + ) + endif() + list(GET name_parts -1 component_name) + set_target_properties( + "${_tgt}" + PROPERTIES EXPORT_NAME "${component_name}" + ) + message( + VERBOSE + "beman_install_library(${name}): COMPONENT ${component_name} for TARGET '${_tgt}'" + ) + + # Get the list of interface header sets, exact one expected! + get_target_property(_header_sets ${_tgt} INTERFACE_HEADER_SETS) + if(_header_sets) + message( + VERBOSE + "beman-install-library(${name}): '${_tgt}' has INTERFACE_HEADER_SETS=${_header_sets}" + ) + # XXX set(__INSTALL_HEADER_SETS FILE_SET ${_header_sets}) + else() + set(_header_sets HEADERS) # Note: empty FILE_SET in this case! CK + endif() + + # Detect presence of C++ module file sets, exact one expected! + get_target_property(_module_sets "${_tgt}" CXX_MODULE_SETS) + if(_module_sets) + message( + VERBOSE + "beman-install-library(${name}): '${_tgt}' has CXX_MODULE_SETS=${_module_sets}" + ) + install( + TARGETS "${_tgt}" + COMPONENT "${install_component_name}" + EXPORT ${BEMAN_EXPORT_NAME} + ARCHIVE # DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY # DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME # DESTINATION ${CMAKE_INSTALL_BINDIR} + FILE_SET + ${_header_sets} # DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILE_SET CXX_MODULES DESTINATION "${BEMAN_DESTINATION}" + # NOTE: There's currently no convention for this location! CK + CXX_MODULES_BMI + # TODO(CK): DESTINATION ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ + ) + else() + install( + TARGETS "${_tgt}" + COMPONENT "${install_component_name}" + EXPORT ${BEMAN_EXPORT_NAME} + ARCHIVE # DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY # DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME # DESTINATION ${CMAKE_INSTALL_BINDIR} + FILE_SET + ${_header_sets} # DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + endif() + endforeach() + + # -------------------------------------------------- + # Export targets + # -------------------------------------------------- + install( + EXPORT ${BEMAN_EXPORT_NAME} + NAMESPACE ${BEMAN_NAMESPACE} + CXX_MODULES_DIRECTORY + cxx-modules + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${name} + COMPONENT "${install_component_name}" + ) + + # ---------------------------------------- + # Config file installation logic + # ---------------------------------------- + string(TOUPPER "${name}" _pkg_upper) + string(REPLACE "." "_" _pkg_prefix "${_pkg_upper}") + + option( + ${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE + "Enable creating and installing a CMake config-file package. Default: ON. Values: { ON, OFF }." + ON + ) + + set(_pkg_var "${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE") + + if(NOT DEFINED ${_pkg_var}) + set(${_pkg_var} + OFF + CACHE BOOL + "Install CMake package config files for ${name}" + ) + endif() + + set(_install_config OFF) + + if(${_pkg_var}) + set(_install_config ON) + elseif(BEMAN_INSTALL_CONFIG_FILE_PACKAGES) + list(FIND BEMAN_INSTALL_CONFIG_FILE_PACKAGES "${name}" _idx) + if(NOT _idx EQUAL -1) + set(_install_config ON) + endif() + endif() + + # ---------------------------------------- + # expand dependencies + # ---------------------------------------- + set(_beman_find_deps "") + foreach(dep IN ITEMS ${BEMAN_DEPENDENCIES}) + message(VERBOSE "Add find_dependency(${dep})") + string(APPEND _beman_find_deps "find_dependency(${dep})\n") + endforeach() + set(BEMAN_FIND_DEPENDENCIES "${_beman_find_deps}") + + # ---------------------------------------- + # Generate + install config files + # ---------------------------------------- + if(_install_config) + configure_package_config_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config.cmake" + INSTALL_DESTINATION ${_config_install_dir} + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config-version.cmake" + DESTINATION ${_config_install_dir} + COMPONENT "${install_component_name}" + ) + else() + message( + WARNING + "beman-install-library(${name}): Not installing a config package for '${name}'" + ) + endif() +endfunction() + +set(CPACK_GENERATOR TGZ) +include(CPack) diff --git a/cmake/beman.execution-config.cmake.in b/cmake/beman.execution-config.cmake.in deleted file mode 100644 index 40463a38..00000000 --- a/cmake/beman.execution-config.cmake.in +++ /dev/null @@ -1,7 +0,0 @@ -# cmake/Config.cmake.in -*-makefile-*- -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@TARGET_LIBRARY@") diff --git a/cmake/cxx-modules-rules.cmake b/cmake/cxx-modules-rules.cmake index 498131d1..23a0ec2e 100644 --- a/cmake/cxx-modules-rules.cmake +++ b/cmake/cxx-modules-rules.cmake @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # A CMake language file to be included as the last step of all project() command calls. # This file must be included/used as CMAKE_PROJECT_INCLUDE -> after project() @@ -92,7 +93,7 @@ endif() option( BEMAN_USE_STD_MODULE "Check if 'import std;' is possible with the toolchain?" - OFF + ON ) message(STATUS "BEMAN_USE_STD_MODULE=${BEMAN_USE_STD_MODULE}") diff --git a/cmake/prelude.cmake b/cmake/prelude.cmake index c7b86338..36c4fc68 100644 --- a/cmake/prelude.cmake +++ b/cmake/prelude.cmake @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # This file must be included/used as CMAKE_PROJECT_TOP_LEVEL_INCLUDES -> before project() is called! # diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c68e6e17..958b6df0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -43,7 +43,6 @@ foreach(EXAMPLE ${EXAMPLES}) add_executable(${EXAMPLE_TARGET}) target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp) if(BEMAN_USE_MODULES) - # target_compile_definitions(${EXAMPLE_TARGET} PUBLIC BEMAN_HAS_MODULES) target_link_libraries(${EXAMPLE_TARGET} PRIVATE beman::execution) else() target_link_libraries( diff --git a/infra/cmake/appleclang-toolchain.cmake b/infra/cmake/appleclang-toolchain.cmake index 70ef548e..451c86b6 100644 --- a/infra/cmake/appleclang-toolchain.cmake +++ b/infra/cmake/appleclang-toolchain.cmake @@ -32,7 +32,7 @@ endif() set(CMAKE_C_FLAGS_DEBUG_INIT "${SANITIZER_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG_INIT "${SANITIZER_FLAGS}") -set(RELEASE_FLAGS "-O3 ${SANITIZER_FLAGS}") +set(RELEASE_FLAGS "-O3") set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") @@ -41,4 +41,4 @@ set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") # Add this dir to the module path so that `find_package(beman-install-library)` works -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") +# NO! list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/infra/cmake/gnu-toolchain.cmake b/infra/cmake/gnu-toolchain.cmake index d3b9f92d..a946349d 100644 --- a/infra/cmake/gnu-toolchain.cmake +++ b/infra/cmake/gnu-toolchain.cmake @@ -29,7 +29,7 @@ endif() set(CMAKE_C_FLAGS_DEBUG_INIT "${SANITIZER_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG_INIT "${SANITIZER_FLAGS}") -set(RELEASE_FLAGS "-O3 ${SANITIZER_FLAGS}") +set(RELEASE_FLAGS "-O3") set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") @@ -38,4 +38,4 @@ set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") # Add this dir to the module path so that `find_package(beman-install-library)` works -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") +# NO! list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/infra/cmake/llvm-toolchain.cmake b/infra/cmake/llvm-toolchain.cmake index f1623b7e..b0ca3bdb 100644 --- a/infra/cmake/llvm-toolchain.cmake +++ b/infra/cmake/llvm-toolchain.cmake @@ -29,7 +29,7 @@ endif() set(CMAKE_C_FLAGS_DEBUG_INIT "${SANITIZER_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG_INIT "${SANITIZER_FLAGS}") -set(RELEASE_FLAGS "-O3 ${SANITIZER_FLAGS}") +set(RELEASE_FLAGS "-O3") set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") @@ -38,4 +38,4 @@ set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") # Add this dir to the module path so that `find_package(beman-install-library)` works -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") +# NO! list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/infra/cmake/msvc-toolchain.cmake b/infra/cmake/msvc-toolchain.cmake index bdc24de9..7bd82279 100644 --- a/infra/cmake/msvc-toolchain.cmake +++ b/infra/cmake/msvc-toolchain.cmake @@ -29,7 +29,7 @@ endif() set(CMAKE_CXX_FLAGS_DEBUG_INIT "/EHsc /permissive- ${SANITIZER_FLAGS}") set(CMAKE_C_FLAGS_DEBUG_INIT "/EHsc /permissive- ${SANITIZER_FLAGS}") -set(RELEASE_FLAGS "/EHsc /permissive- /O2 ${SANITIZER_FLAGS}") +set(RELEASE_FLAGS "/EHsc /permissive- /O2") set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") @@ -38,4 +38,4 @@ set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") # Add this dir to the module path so that `find_package(beman-install-library)` works -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") +# NO! list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/src/beman/execution/CMakeLists.txt b/src/beman/execution/CMakeLists.txt index 10774bac..1558ca95 100644 --- a/src/beman/execution/CMakeLists.txt +++ b/src/beman/execution/CMakeLists.txt @@ -200,8 +200,8 @@ if(BEMAN_USE_MODULES) execution.cpp # TODO(CK): check if really needed! PUBLIC FILE_SET CXX_MODULES - BASE_DIRS ${PROJECT_SOURCE_DIR}/src/beman/execution - FILES ${PROJECT_SOURCE_DIR}/src/beman/execution/execution.cppm + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES execution.cppm ) target_link_libraries(${TARGET_PREFIX} PUBLIC ${TARGET_NAME}) endif() diff --git a/tests/beman/execution/CMakeLists.txt b/tests/beman/execution/CMakeLists.txt index 65238e5c..35b07dea 100644 --- a/tests/beman/execution/CMakeLists.txt +++ b/tests/beman/execution/CMakeLists.txt @@ -122,6 +122,13 @@ foreach(test ${execution_tests}) add_test(NAME ${TEST_EXE} COMMAND $) endforeach() +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + return() +endif() + +#====================================================================== +# NOTE: Only for Release build with enabled install rules useful! CK +#====================================================================== if(BEMAN_EXECUTION_INSTALL_CONFIG_FILE_PACKAGE AND NOT CMAKE_SKIP_INSTALL_RULES) # test if the targets are usable from the install directory add_test(