diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index c86fedb9..8e764926 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -13,10 +13,10 @@ on: jobs: beman-submodule-check: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.1.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.2.1 preset-test: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.1.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.2.1 with: matrix_config: > [ @@ -24,12 +24,14 @@ jobs: {"preset": "gcc-release", "image": "ghcr.io/bemanproject/infra-containers-gcc:latest"}, {"preset": "llvm-debug", "image": "ghcr.io/bemanproject/infra-containers-clang:latest"}, {"preset": "llvm-release", "image": "ghcr.io/bemanproject/infra-containers-clang:latest"}, + {"preset": "appleclang-debug", "runner": "macos-latest"}, + {"preset": "appleclang-release", "runner": "macos-latest"}, {"preset": "msvc-debug", "runner": "windows-latest"}, {"preset": "msvc-release", "runner": "windows-latest"} ] build-and-test: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.1.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.2.1 with: matrix_config: > { @@ -40,7 +42,8 @@ jobs: "tests": [ { "stdlibs": ["libstdc++"], "tests": [ - "Debug.Default", "Release.Default", "Release.MaxSan", + "Debug.Default", "Release.Default", "Release.TSan", + "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", "Debug.Coverage" ] } @@ -51,7 +54,7 @@ jobs: } ] }, - { "versions": ["14", "13"], + { "versions": ["14"], "tests": [ { "cxxversions": ["c++26", "c++23"], "tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}] @@ -60,42 +63,51 @@ jobs: } ], "clang": [ - { "versions": ["20"], + { "versions": ["21"], "tests": [ {"cxxversions": ["c++26"], "tests": [ - { "stdlibs": ["libstdc++", "libc++"], + { "stdlibs": ["libc++"], "tests": [ - "Debug.Default", "Release.Default", "Release.MaxSan", - "Debug.Dynamic" + "Debug.Default", "Release.Default", "Release.TSan", + "Release.MaxSan", "Debug.Werror" ] } ] }, { "cxxversions": ["c++23"], "tests": [ - {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]} + {"stdlibs": ["libc++"], "tests": ["Release.Default"]} ] } ] }, - { "versions": ["19"], + { "versions": ["20", "19"], "tests": [ { "cxxversions": ["c++26", "c++23"], "tests": [ - {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]} + {"stdlibs": ["libc++"], "tests": ["Release.Default"]} ] } ] } ], + "appleclang": [ + { "versions": ["latest"], + "tests": [ + { "cxxversions": ["c++23"], + "tests": [{ "stdlibs": ["libc++"], "tests": ["Release.Default"]}] + } + ] + } + ], "msvc": [ { "versions": ["latest"], "tests": [ { "cxxversions": ["c++23"], "tests": [ { "stdlibs": ["stl"], - "tests": ["Debug.Default", "Release.Default"] + "tests": ["Debug.Default", "Release.Default", "Release.MaxSan"] } ] } @@ -107,4 +119,4 @@ jobs: create-issue-when-fault: needs: [preset-test, build-and-test] if: failure() && github.event_name == 'schedule' - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.1.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.2.1 diff --git a/.github/workflows/pre-commit-check.yml b/.github/workflows/pre-commit-check.yml new file mode 100644 index 00000000..57493433 --- /dev/null +++ b/.github/workflows/pre-commit-check.yml @@ -0,0 +1,13 @@ +name: Lint Check (pre-commit) + +on: + # We have to use pull_request_target here as pull_request does not grant + # enough permission for reviewdog + pull_request_target: + push: + branches: + - main + +jobs: + pre-commit: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.2.1 diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml new file mode 100644 index 00000000..9261dbf0 --- /dev/null +++ b/.github/workflows/pre-commit-update.yml @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: Weekly pre-commit autoupdate + +on: + workflow_dispatch: + schedule: + - cron: "0 16 * * 0" + +jobs: + auto-update-pre-commit: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-update-pre-commit.yml@1.2.1 + secrets: + APP_ID: ${{ secrets.AUTO_PR_BOT_APP_ID }} + PRIVATE_KEY: ${{ secrets.AUTO_PR_BOT_PRIVATE_KEY }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5baf30b5..9636e830 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: # See also: https://github.com/ssciwr/clang-format-wheel # Config file: .clang-format - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v21.1.7 + rev: v21.1.8 hooks: - id: clang-format types_or: [c++, c, json] @@ -32,7 +32,7 @@ repos: # CMake linting and formatting - repo: https://github.com/BlankSpruce/gersemi - rev: 0.23.2 + rev: 0.25.3 hooks: - id: gersemi name: CMake linting @@ -55,7 +55,7 @@ repos: # Config file: pyproject.toml # second Python code formatting - repo: https://github.com/psf/black - rev: 25.11.0 + rev: 26.1.0 hooks: - id: black diff --git a/CMakeLists.txt b/CMakeLists.txt index 409e197c..8080b5b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,76 +3,36 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # gersemi: on -cmake_minimum_required(VERSION 3.25...4.2) - -#========================== pre project settings =============================== -# gersemi: off -if(CMAKE_VERSION VERSION_EQUAL 4.2) - set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") - - if(CMAKE_CXX_STDLIB_MODULES_JSON) - message( - STATUS - "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}" - ) - endif() -endif() -# gersemi: on -#=============================================================================== +cmake_minimum_required(VERSION 3.30...4.2) +include(cmake/prelude.cmake) #=================================================== -project(beman_execution VERSION 0.0.1 LANGUAGES CXX) +project(beman.execution VERSION 0.0.1 LANGUAGES CXX) #=================================================== -if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) - message(FATAL_ERROR "In-source builds are not allowed!") -endif() +# Modules opt in only on compilers that support it: msvc, g++-15 and clang-20+ +include(cxx-modules-rules) +# TODO(CK): check this if is confirm to beman standard! set(TARGET_NAME execution) set(TARGET_NAMESPACE beman) set(TARGET_PREFIX ${TARGET_NAMESPACE}.${TARGET_NAME}) -set(TARGET_LIBRARY ${PROJECT_NAME}) +set(TARGET_LIBRARY ${TARGET_NAME}) set(TARGET_ALIAS ${TARGET_NAMESPACE}::${TARGET_NAME}) set(TARGET_PACKAGE_NAME ${PROJECT_NAME}-config) -set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-config-targets) - -#========================== post project settings ============================== -# Tell CMake that we explicitly want `import std`. -# This will initialize the property on all targets declared after this to 1 -message(STATUS "CMAKE_CXX_COMPILER_IMPORT_STD=${CMAKE_CXX_COMPILER_IMPORT_STD}") -if(${CMAKE_CXX_STANDARD} IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD) - set(CMAKE_CXX_MODULE_STD ON) - message(STATUS "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}") -endif() +set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-targets) -if(CMAKE_CXX_SCAN_FOR_MODULES AND ${CMAKE_GENERATOR} STREQUAL Ninja) - set(BEMAN_USE_MODULES ON) - message(STATUS "BEMAN_USE_MODULES=${BEMAN_USE_MODULES}") -else() - message(WARNING "Missing support for CMAKE_CXX_SCAN_FOR_MODULES!") -endif() +add_library(${TARGET_LIBRARY} STATIC) +add_library(${TARGET_ALIAS} ALIAS ${TARGET_LIBRARY}) -# gersemi: off -if(CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) - message( - STATUS - "CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}" +if(BEMAN_HAS_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 + target_compile_features( + ${TARGET_LIBRARY} + PUBLIC cxx_std_${CMAKE_CXX_STANDARD} ) endif() -# gersemi: on - -# 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_NAME} STATIC) -target_compile_features(${TARGET_NAME} PUBLIC cxx_std_23) - -if(BEMAN_USE_MODULES AND CMAKE_CXX_MODULE_STD) - target_compile_definitions(${TARGET_NAME} PUBLIC BEMAN_HAS_IMPORT_STD) -else() - message(WARNING "Missing support for CMAKE_CXX_MODULE_STD!") -endif() -#=============================================================================== option( BEMAN_EXECUTION_ENABLE_TESTING @@ -97,9 +57,9 @@ set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) add_subdirectory(src/beman/execution) -if(BEMAN_EXECUTION_ENABLE_TESTING) - enable_testing() +enable_testing() +if(BEMAN_EXECUTION_ENABLE_TESTING) add_subdirectory(tests/beman/execution) endif() @@ -112,6 +72,9 @@ if(NOT BEMAN_EXECUTION_ENABLE_INSTALL OR CMAKE_SKIP_INSTALL_RULES) return() endif() +include(beman-install-library-config) +# FIXME(CK): beman_install_library(beman.execution) + include(CMakePackageConfigHelpers) write_basic_package_version_file( diff --git a/CMakePresets.json b/CMakePresets.json index b56d96e6..8f7d2bde 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 9, "configurePresets": [ { "name": "_root-config", @@ -61,7 +61,7 @@ "_debug-base" ], "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-libc++-toolchain.cmake" } }, { @@ -72,7 +72,7 @@ "_release-base" ], "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-libc++-toolchain.cmake" } }, { diff --git a/Makefile b/Makefile index 825c9daf..b4c65be7 100644 --- a/Makefile +++ b/Makefile @@ -31,38 +31,39 @@ endif LDFLAGS ?= SAN_FLAGS ?= CXX_FLAGS ?= -g -# TODO: SANITIZER := release -SANITIZER ?= default +# Note: disabled while working on CXX_MODULES! CK +# XXX: NO! SANITIZER := release +SANITIZER ?= RelWithDebInfo SOURCEDIR = $(CURDIR) BUILDROOT = build export hostSystemName:=$(shell uname -s) -# TODO BUILD := $(BUILDROOT)/$(SANITIZER) +BUILD := $(BUILDROOT)/$(SANITIZER) BUILD ?= $(BUILDROOT)/$(hostSystemName)/$(SANITIZER) EXAMPLE = beman.execution.examples.stop_token ################################################ ifeq (${hostSystemName},Darwin) - export LLVM_PREFIX:=$(shell brew --prefix llvm) + export LLVM_PREFIX:=$(shell brew --prefix llvm) export LLVM_DIR:=$(shell realpath ${LLVM_PREFIX}) export PATH:=${LLVM_DIR}/bin:${PATH} - # export CMAKE_CXX_STDLIB_MODULES_JSON=${LLVM_DIR}/lib/c++/libc++.modules.json - # export CXX=clang++ - # export LDFLAGS=-L$(LLVM_DIR)/lib/c++ -lc++abi -lc++ # -lc++experimental - # export GCOV="llvm-cov gcov" + export CMAKE_CXX_STDLIB_MODULES_JSON=${LLVM_DIR}/lib/c++/libc++.modules.json + export CXX=clang++ + export LDFLAGS=-L$(LLVM_DIR)/lib/c++ -lc++abi # NO! -lc++ -lc++experimental + export GCOV="llvm-cov gcov" ### TODO: to test g++-15: export GCC_PREFIX:=$(shell brew --prefix gcc) export GCC_DIR:=$(shell realpath ${GCC_PREFIX}) - export CMAKE_CXX_STDLIB_MODULES_JSON=${GCC_DIR}/lib/gcc/current/libstdc++.modules.json + # export CMAKE_CXX_STDLIB_MODULES_JSON=${GCC_DIR}/lib/gcc/current/libstdc++.modules.json # export CXX:=g++-15 - # export CXXFLAGS:=-stdlib=libstdc++ - export GCOV="gcov" + # export CXXFLAGS:=-stdlib=libstdc++ + # export GCOV="gcov" else ifeq (${hostSystemName},Linux) - export LLVM_DIR=/usr/lib/llvm-20 + export LLVM_DIR=/usr/lib/llvm-22 export PATH:=${LLVM_DIR}/bin:${PATH} - # export CXX=clang++-20 + export CXX=clang++-22 else export CXX=$(COMPILER) endif @@ -94,12 +95,25 @@ ifeq ($(SANITIZER),lsan) LDFLAGS = $(SAN_FLAGS) endif -# TODO: beman.execution.examples.modules -# FIXME: beman.execution.execution-module.test beman.execution.stop-token-module.test +.PHONY: help FIXME TODO +help: + @echo "Use one of the folling targets:" + @echo "" + @echo "build -> use CXX_MODULES if possible" + @echo "test -> build and test " + @echo "debug -> preset with 'import std;'" + @echo "release -> preset with 'import std;'" + @echo "doc" + @echo "clean" + @echo "format" + @echo "distclean" -default: test +# TODO: beman.execution.examples.modules +FIXME: beman.execution.execution-module.test beman.execution.stop-token-module.test -all: $(SANITIZERS) +# Note: disabled while working on CXX_MODULES! CK +# XXX: NO! $(SANITIZER): test +# XXX all: $(SANITIZERS) run: test ./$(BUILD)/examples/$(EXAMPLE) @@ -108,20 +122,24 @@ doc: ./bin/mk-doc.py docs/*.mds doxygen docs/Doxyfile +# Note: disabled while working on CXX_MODULES! CK # $(SANITIZERS): # $(MAKE) SANITIZER=$@ build: - cmake --fresh -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \ + cmake -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \ -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \ -D CMAKE_SKIP_INSTALL_RULES=ON \ -D CMAKE_CXX_STANDARD=23 \ -D CMAKE_CXX_EXTENSIONS=ON \ -D CMAKE_CXX_STANDARD_REQUIRED=ON \ - -D CMAKE_CXX_COMPILER=$(CXX) # XXX -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)" + -D CMAKE_CXX_SCAN_FOR_MODULES=ON \ + -D CMAKE_EXPERIMENTAL_CXX_IMPORT_STD=OFF \ + -D CMAKE_BUILD_TYPE=$(SANITIZER) \ + -D CMAKE_CXX_COMPILER=$(CXX) # XXX --fresh -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)" cmake --build $(BUILD) -# NOTE: without install, see CMAKE_SKIP_INSTALL_RULES! CK +# NOTE: may w/o enabled install, see CMAKE_SKIP_INSTALL_RULES! CK test: build ctest --test-dir $(BUILD) --rerun-failed --output-on-failure @@ -166,6 +184,7 @@ codespell: pre-commit run $@ format: + pre-commit autoupdate pre-commit run --all cmake-format: diff --git a/cmake/CMakeUserPresets.json b/cmake/CMakeUserPresets.json index 331c063e..9e051014 100644 --- a/cmake/CMakeUserPresets.json +++ b/cmake/CMakeUserPresets.json @@ -22,9 +22,16 @@ "configurePreset": "release", "configuration": "Release", "targets": [ - "all_verify_interface_header_sets", "all" ] + }, + { + "name": "verify", + "configurePreset": "release", + "configuration": "Release", + "targets": [ + "all_verify_interface_header_sets" + ] } ], "testPresets": [ diff --git a/cmake/beman-install-library-config.cmake b/cmake/beman-install-library-config.cmake new file mode 100644 index 00000000..da4173e1 --- /dev/null +++ b/cmake/beman-install-library-config.cmake @@ -0,0 +1,173 @@ +# 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) + # Usage + # ----- + # + # beman_install_library(NAME) + # + # 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}") + set(install_component_name "${name}") + set(export_name "${name}") + set(package_name "${name}") + list(GET name_parts -1 component_name) + + include(GNUInstallDirs) + + install( + TARGETS "${target_name}" + COMPONENT "${install_component_name}" + EXPORT "${export_name}" + FILE_SET HEADERS + FILE_SET CXX_MODULES + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}/modules + ) + + set_target_properties( + "${target_name}" + PROPERTIES EXPORT_NAME "${component_name}" + ) + + # 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( + DEBUG + "beman-install-library: Installing a config package 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" + ) + set(package_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}") + 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}" + ) + + 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( + DEBUG + "beman-install-library: Not installing a config package for '${name}'" + ) + endif() +endfunction() diff --git a/cmake/beman.execution-config.cmake.in b/cmake/beman.execution-config.cmake.in new file mode 100644 index 00000000..1c526a56 --- /dev/null +++ b/cmake/beman.execution-config.cmake.in @@ -0,0 +1,11 @@ +include(CMakeFindDependencyMacro) + +# TODO(CK): set if needed! find_dependency(beman.inplace_vector) + +set(BEMAN_EXECUTION_VERSION @PROJECT_VERSION@) + +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake) + +check_required_components(@PROJECT_NAME@) diff --git a/cmake/cxx-modules-rules.cmake b/cmake/cxx-modules-rules.cmake new file mode 100644 index 00000000..36dc73e9 --- /dev/null +++ b/cmake/cxx-modules-rules.cmake @@ -0,0 +1,146 @@ +# +# 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() +# + +# ---- The include guard applies within the current directory and below ---- +include_guard(DIRECTORY) + +if(NOT PROJECT_NAME) + message( + FATAL_ERROR + "This CMake file has to be included as the last step of all project() command calls!" + ) +endif() + +# Use modules? default NO! +if(NOT DEFINED CMAKE_CXX_SCAN_FOR_MODULES) + set(CMAKE_CXX_SCAN_FOR_MODULES OFF) +endif() + +# Control whether the test target depends on the all target. +set(CMAKE_SKIP_TEST_ALL_DEPENDENCY OFF) + +# gersemi: off +option(CMAKE_EXPORT_COMPILE_COMMANDS "Prepare run-clang-tidy" ${PROJECT_IS_TOP_LEVEL}) +if(CMAKE_EXPORT_COMPILE_COMMANDS) + message( + STATUS + "CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}" + ) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() +# gersemi: on + +# Ensure non-empty default build type for single-config +get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(NOT isMultiConfig) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type") +endif() +set(CMAKE_DEBUG_POSTFIX _d) + +# ------------------------------------------------------------------------------ +# This property setting also needs to be consistent between the installed shared +# library and its consumer, otherwise most toolchains will once again reject the +# consumer's generated BMI. +# ------------------------------------------------------------------------------ +if(NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 23) +endif() + +# Neither of these two are technically needed, but they make the expectation clear +set(CMAKE_CXX_EXTENSIONS ON) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# NOTE: only with Ninja generator install of bmi files works yet! +if(CMAKE_GENERATOR MATCHES "Ninja") + if( + CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0 + ) + set(CMAKE_CXX_SCAN_FOR_MODULES ON) + + if(NOT LINUX) + string(APPEND CMAKE_CXX_MODULE_MAP_FLAG " -fmodules-reduced-bmi") + endif() + + add_compile_options($ENV{CXXFLAGS}) + add_link_options($ENV{CXXFLAGS}) + elseif( + CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0 + ) + set(CMAKE_CXX_SCAN_FOR_MODULES ON) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_SCAN_FOR_MODULES ON) + else() + set(CMAKE_CXX_SCAN_FOR_MODULES OFF) + endif() +endif() + +if(CMAKE_CXX_STDLIB_MODULES_JSON) + message( + STATUS + "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}" + ) +endif() + +if(NOT DEFINED CMAKE_CXX_MODULE_STD) + set(CMAKE_CXX_MODULE_STD OFF) +endif() + +option( + BEMAN_USE_STD_MODULE + "Check if 'import std;' is possible with the toolchain" + OFF +) +if(BEMAN_USE_STD_MODULE) + # ------------------------------------------------------------------------- + # Tell CMake that we explicitly want `import std`. + # This will initialize the property on all targets declared after this to 1 + # ------------------------------------------------------------------------- + message( + STATUS + "CMAKE_CXX_COMPILER_IMPORT_STD=${CMAKE_CXX_COMPILER_IMPORT_STD}" + ) + if(${CMAKE_CXX_STANDARD} IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD) + set(CMAKE_CXX_MODULE_STD ON) + option( + BEMAN_HAS_IMPORT_STD + "Build with import std; possible" + ${CMAKE_CXX_MODULE_STD} + ) + message(STATUS "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}") + else() + set(CMAKE_CXX_MODULE_STD OFF) + message(WARNING "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}") + endif() +endif() + +message(STATUS "CMAKE_CXX_SCAN_FOR_MODULES=${CMAKE_CXX_SCAN_FOR_MODULES}") + +if(CMAKE_CXX_STANDARD GREATER_EQUAL 20) + option(BEMAN_HAS_MODULES "Build CXX_MODULES" ${CMAKE_CXX_SCAN_FOR_MODULES}) +endif() + +# ------------------------------------------------------------------------------ +# Avoid creating CMAKE_..._OUTPUT_DIRECTORY as cache variables, they should not +# be under the control of the developer. They should be controlled by the +# project because parts of the project may make assumptions about the relative +# layout of the binaries. More importantly, leaving them as ordinary variables +# also means they can be unset within subdirectories where test executables are +# defined, allowing them to avoid being collected with the other main binaries +# and cluttering up that area. +# ------------------------------------------------------------------------------ +set(stageDir ${CMAKE_CURRENT_BINARY_DIR}/stagedir) +include(GNUInstallDirs) + +if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_BINDIR}) +endif() +if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_LIBDIR}) +endif() +if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_LIBDIR}) +endif() diff --git a/cmake/prelude.cmake b/cmake/prelude.cmake new file mode 100644 index 00000000..3f42470f --- /dev/null +++ b/cmake/prelude.cmake @@ -0,0 +1,113 @@ +# +# This file must be included/used as CMAKE_PROJECT_TOP_LEVEL_INCLUDES -> before project() is called! +# + +# ---- The include guard applies globally to the whole build ---- +include_guard(GLOBAL) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds are not supported. " + "Please read the BUILDING document before trying to build this project. " + "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first." + ) +endif() + +if(PROJECT_NAME) + message( + FATAL_ERROR + "This CMake file has to be included before first project() command call!" + ) +endif() + +# gersemi: off +# --------------------------------------------------------------------------- +# use ccache if found +# --------------------------------------------------------------------------- +find_program(CCACHE_EXECUTABLE "ccache" HINTS /usr/local/bin /opt/local/bin) +if(CCACHE_EXECUTABLE) + message(STATUS "use ccache") + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache") + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache") +endif() + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +# --------------------------------------------------------------------------- +# check if import std; is supported by CMAKE_CXX_COMPILER +# --------------------------------------------------------------------------- +if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2) + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") +endif() +# gersemi: on + +if("$ENV{CXX}" STREQUAL "" AND CMAKE_CXX_COMPILER) + message(WARNING "\$CXX is not set") + set(ENV{CXX} ${CMAKE_CXX_COMPILER}) +endif() + +# --------------------------------------------------------------------------- +# Workaround needed for CMAKE and clang++ to find the libc++.modules.json file +# --------------------------------------------------------------------------- +if( + CMAKE_VERSION VERSION_GREATER 4.2 + AND ("$ENV{CXX}" MATCHES "clang" OR CMAKE_CXX_COMPILER MATCHES "clang") +) + # NOTE: Always use libc++ + # see https://releases.llvm.org/19.1.0/projects/libcxx/docs/index.html + set(ENV{CXXFLAGS} -stdlib=libc++) + message(STATUS "CXXFLAGS=-stdlib=libc++") + + if(APPLE) + execute_process( + OUTPUT_VARIABLE LLVM_PREFIX + COMMAND brew --prefix llvm + COMMAND_ECHO STDOUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + file(REAL_PATH ${LLVM_PREFIX} LLVM_DIR) + set(LLVM_DIR ${LLVM_DIR} CACHE FILEPATH "") + + message(STATUS "LLVM_DIR=${LLVM_DIR}") + add_link_options(-L${LLVM_DIR}/lib/c++) + include_directories(SYSTEM ${LLVM_DIR}/include) + + set(CMAKE_CXX_STDLIB_MODULES_JSON + ${LLVM_DIR}/lib/c++/libc++.modules.json + ) + elseif(LINUX) + execute_process( + OUTPUT_VARIABLE LLVM_MODULES + COMMAND clang++ -print-file-name=c++/libc++.modules.json + COMMAND_ECHO STDOUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT CMAKE_CXX_STDLIB_MODULES_JSON) + set(CMAKE_CXX_STDLIB_MODULES_JSON ${LLVM_MODULES}) + endif() + message( + STATUS + "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}" + ) + endif() + + if(EXISTS ${CMAKE_CXX_STDLIB_MODULES_JSON}) + message( + STATUS + "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}" + ) + # gersemi: off + set(CACHE{CMAKE_CXX_STDLIB_MODULES_JSON} + TYPE FILEPATH + HELP "Result of: clang++ -print-file-name=c++/libc++.modules.json" + VALUE ${CMAKE_CXX_STDLIB_MODULES_JSON} + ) + # gersemi: on + else() + message( + WARNING + "File does NOT EXISTS! ${CMAKE_CXX_STDLIB_MODULES_JSON}" + ) + endif() +endif() diff --git a/cmake/presets/CMakeDarwinPresets.json b/cmake/presets/CMakeDarwinPresets.json index 04528268..e635705f 100644 --- a/cmake/presets/CMakeDarwinPresets.json +++ b/cmake/presets/CMakeDarwinPresets.json @@ -22,7 +22,7 @@ "hidden": true, "cacheVariables": { "CMAKE_CXX_STDLIB_MODULES_JSON": "$env{CMAKE_CXX_STDLIB_MODULES_JSON}", - "CMAKE_BUILD_TYPE": "RelWithDebInfo" + "CMAKE_BUILD_TYPE": "Release" }, "condition": { "type": "equals", diff --git a/cmake/presets/CMakeGenericPresets.json b/cmake/presets/CMakeGenericPresets.json index 826484cc..d222b74b 100644 --- a/cmake/presets/CMakeGenericPresets.json +++ b/cmake/presets/CMakeGenericPresets.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 9, "configurePresets": [ { "name": "root-config", diff --git a/cmake/presets/CMakeLinuxPresets.json b/cmake/presets/CMakeLinuxPresets.json index 61f9b24a..4861d788 100644 --- a/cmake/presets/CMakeLinuxPresets.json +++ b/cmake/presets/CMakeLinuxPresets.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 9, "include": [ "CMakeGenericPresets.json" ], @@ -22,7 +22,7 @@ "hidden": true, "cacheVariables": { "CMAKE_CXX_STDLIB_MODULES_JSON": "$env{CMAKE_CXX_STDLIB_MODULES_JSON}", - "CMAKE_BUILD_TYPE": "RelWithDebInfo" + "CMAKE_BUILD_TYPE": "Release" }, "condition": { "type": "equals", diff --git a/cmake/presets/CMakeWindowsPresets.json b/cmake/presets/CMakeWindowsPresets.json index d8834f2c..6ab8f1cf 100644 --- a/cmake/presets/CMakeWindowsPresets.json +++ b/cmake/presets/CMakeWindowsPresets.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 9, "include": [ "CMakeGenericPresets.json" ], diff --git a/docs/code/CMakeLists.txt b/docs/code/CMakeLists.txt index 2d2752d3..0008a6be 100644 --- a/docs/code/CMakeLists.txt +++ b/docs/code/CMakeLists.txt @@ -3,11 +3,11 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # gersemi: on -list(APPEND EXAMPLES) +set(EXAMPLES) -if(BEMAN_USE_MODULES) - list(APPEND EXAMPLES modules) # modules.cpp -endif() +# if(BEMAN_USE_MODULES) +# list(APPEND EXAMPLES modules) # TODO(CK): missing modules.cpp? +# endif() foreach(EXAMPLE ${EXAMPLES}) set(EXAMPLE_TARGET ${TARGET_PREFIX}.tutorial.${EXAMPLE}) diff --git a/docs/cxx_modules_cheatsheeet.md b/docs/cxx_modules_cheatsheeet.md new file mode 100644 index 00000000..a4fbc66d --- /dev/null +++ b/docs/cxx_modules_cheatsheeet.md @@ -0,0 +1,279 @@ + +# C++20/23 Modules – Quick Reference Cheat Sheet + +This cheat sheet summarizes **the correct order of `#include`, `import`, and `export module`** in C++20/23 modules. + +--- + +## 1️⃣ Module File Zones +``` +[Zone A] Global Module Fragment → before `export module` +[Zone B] Module Interface → after `export module` +[Zone C] Module Implementation → optional/private +``` + +--- + +## 2️⃣ Global Module Fragment (`module;`) +- Must appear **before** `export module` +- Allowed here: + - `#include` legacy headers + - macros + - conditional compilation + - rare `import` +- Example: +```cpp +module; +#include +#define INTERNAL_MACRO 1 +``` + +--- + +## 3️⃣ Module Declaration +```cpp +export module my.module; +``` +- Marks start of module interface +- Everything after is part of the module +- Prefer `import` instead of `#include` inside + +--- + +## 4️⃣ Import Rules +- ✅ After module: `import std;`, `import bar;` +- ✅ Rarely allowed in global fragment +- ❌ Forbidden before module line: +```cpp +import std; +export module foo; // ❌ +``` + +--- + +## 5️⃣ Include Rules +- ✅ Before `export module`: allowed (legacy headers) +- ✅ After `export module`: content becomes part of module +- ❌ System headers inside module body: may cause errors +Use `import std;` instead + +--- + +## 6️⃣ Recommended Layout +```cpp +module; // Global fragment +#include // OK +#define HELPER 1 // OK + +export module my.module; // Module interface begins +import std; // Prefer imports + +export int foo(); +#include "public_api.hpp" // Exported +``` + +--- + +## 7️⃣ Header-Only Library Example + +### the boost/any/modules/boost_any.cppm + +```cpp +// Copyright (c) 2016-2025 Antony Polukhin +// +// 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 +#include +#include + +// FIXME(CK): #include +import boost.type_index; + +#ifdef BOOST_ANY_USE_STD_MODULE +import std; +#else +#include +#include +#include +#include +#include +#endif + +#define BOOST_ANY_INTERFACE_UNIT + +export module boost.any; + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Winclude-angled-in-module-purview" +#endif + +#include +#include +#include + +``` + +### the boost/any.hpp + +```cpp +// See http://www.boost.org/libs/any for Documentation. + +#ifndef BOOST_ANY_INCLUDED +#define BOOST_ANY_INCLUDED + +#include + +#if !defined(BOOST_USE_MODULES) || defined(BOOST_ANY_INTERFACE_UNIT) + +#ifndef BOOST_ANY_INTERFACE_UNIT +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include // for std::addressof +#include + +#include +#include + +#endif // #ifndef BOOST_ANY_INTERFACE_UNIT + +#include +#include +#include + +namespace boost { + +BOOST_ANY_BEGIN_MODULE_EXPORT + + /// \brief A class whose instances can hold instances of any + /// type that satisfies \forcedlink{ValueType} requirements. + class any + { + public: + + /// \post this->empty() is true. + constexpr any() noexcept + : content(0) + { + } + + /// Makes a copy of `value`, so + + /// ... #### more code ### + + private: // representation + template + friend ValueType * unsafe_any_cast(any *) noexcept; + + friend class boost::anys::unique_any; + + placeholder * content; + /// @endcond + }; + + /// Exchange of the contents of `lhs` and `rhs`. + /// \throws Nothing. + inline void swap(any & lhs, any & rhs) noexcept + { + lhs.swap(rhs); + } + + /// ... ### more code ### + + /// \returns ValueType stored in `operand` + /// \throws boost::bad_any_cast if `operand` does not contain + /// specified ValueType. + template + ValueType any_cast(any & operand) + { + using nonref = typename std::remove_reference::type; + + nonref * result = boost::any_cast(std::addressof(operand)); + if(!result) + boost::throw_exception(bad_any_cast()); + + // Attempt to avoid construction of a temporary object in cases when + // `ValueType` is not a reference. Example: + // `static_cast(*result);` + // which is equal to `std::string(*result);` + typedef typename std::conditional< + std::is_reference::value, + ValueType, + typename std::add_lvalue_reference::type + >::type ref_type; + +#ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable: 4172) // "returning address of local variable or temporary" but *result is not local! +#endif + return static_cast(*result); +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + } + + /// \returns `ValueType` stored in `operand` + /// \throws boost::bad_any_cast if `operand` does not contain + /// specified `ValueType`. + template + inline ValueType any_cast(const any & operand) + { + using nonref = typename std::remove_reference::type; + return boost::any_cast(const_cast(operand)); + } + + /// \returns `ValueType` stored in `operand`, leaving the `operand` empty. + /// \throws boost::bad_any_cast if `operand` does not contain + /// specified `ValueType`. + template + inline ValueType any_cast(any&& operand) + { + static_assert( + std::is_rvalue_reference::value /*true if ValueType is rvalue or just a value*/ + || std::is_const< typename std::remove_reference::type >::value, + "boost::any_cast shall not be used for getting nonconst references to temporary objects" + ); + return boost::any_cast(operand); + } + +BOOST_ANY_END_MODULE_EXPORT + +} + +// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. +// Copyright Antony Polukhin, 2013-2025. +// +// 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) + +#endif // #if !defined(BOOST_USE_MODULES) || defined(BOOST_ANY_INTERFACE_UNIT) + +#endif + +``` +--- + +## 8️⃣ Quick Rules Summary + +| Phase | Allowed | +|----------------------------|---------------------------------------| +| Before `export module` | `#include`, macros, conditional, rare `import` | +| After `export module` | `import`, includes become part of module interface | +| Never | `import` before module line, system headers inside module body, macros leaking into exported API | + +**Rule of Thumb:** +1. `module;` → legacy code +2. `export module X;` → module interface +3. `import` → after module +4. Include only when you want it exported + +--- + +**Tip:** Follow this layout for Beman, Boost, fmt, JSON, or other header-only libraries to wrap them safely as modules. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index baa1019e..ddb35695 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,6 +3,24 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # gersemi: on +cmake_minimum_required(VERSION 3.30...4.2) + +include(../cmake/prelude.cmake) + +project(beman.execution.example LANGUAGES CXX) + +if(PROJECT_IS_TOP_LEVEL) + include(../cmake/cxx-modules-rules.cmake) + + if(NOT DEFINED BEMAN_HAS_MODULES) + set(CMAKE_CXX_SCAN_FOR_MODULES OFF) + endif() + + find_package(beman.execution REQUIRED) + + enable_testing() +endif() + list( APPEND EXAMPLES stackoverflow @@ -26,11 +44,11 @@ if(BEMAN_USE_MODULES) endif() foreach(EXAMPLE ${EXAMPLES}) - set(EXAMPLE_TARGET ${TARGET_PREFIX}.examples.${EXAMPLE}) + set(EXAMPLE_TARGET beman.execution.examples.${EXAMPLE}) add_executable(${EXAMPLE_TARGET}) target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp) - target_link_libraries( - ${EXAMPLE_TARGET} - PRIVATE ${TARGET_NAMESPACE}::${TARGET_NAME} - ) + target_link_libraries(${EXAMPLE_TARGET} PRIVATE beman::execution) + if(NOT ${EXAMPLE} STREQUAL stackoverflow) + add_test(NAME ${EXAMPLE_TARGET} COMMAND ${EXAMPLE_TARGET}) + endif() endforeach() diff --git a/examples/modules.cpp b/examples/modules.cpp index b3c245c9..dfbff855 100644 --- a/examples/modules.cpp +++ b/examples/modules.cpp @@ -4,24 +4,24 @@ #ifdef BEMAN_HAS_IMPORT_STD import std; #else - #include #include #include - #endif -#if __cpp_modules < 201907L -#include -#else import beman_execution; -#endif namespace ex = beman::execution; int main() { +#ifdef USE_THIS_CODE auto [result] = ex::sync_wait(ex::when_all(ex::just(std::string("hello, ")), ex::just(std::string("world"))) | ex::then([](const auto& s1, const auto& s2) { return s1 + s2; })) .value_or(std::tuple(std::string("oops"))); std::cout << "result='" << result << "'\n"; +#elif defined(BEMAN_HAS_IMPORT_STD) + std::println("ex::version = {}", ex::version); +#else + std::cout << "ex::version = " << ex::version << "\n"; +#endif } diff --git a/infra/cmake/beman-install-library-config.cmake b/infra/cmake/beman-install-library-config.cmake index e7fd0ad3..c40959dd 100644 --- a/infra/cmake/beman-install-library-config.cmake +++ b/infra/cmake/beman-install-library-config.cmake @@ -84,8 +84,8 @@ function(beman_install_library name) option( ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE - "Enable building examples. Default: ${PROJECT_IS_TOP_LEVEL}. Values: { ON, OFF }." - ${PROJECT_IS_TOP_LEVEL} + "Enable creating and installing a CMake config-file package. Default: ON. Values: { ON, OFF }." + ON ) # By default, install the config package @@ -121,7 +121,7 @@ function(beman_install_library name) find_file( config_file_template NAMES "${package_name}-config.cmake.in" - PATHS "${CMAKE_CURRENT_SOURCE_DIR}" + PATHS "${PROJECT_SOURCE_DIR}/cmake" NO_DEFAULT_PATH NO_CACHE REQUIRED diff --git a/infra/cmake/use-fetch-content.cmake b/infra/cmake/use-fetch-content.cmake index 4ed48397..d78669b4 100644 --- a/infra/cmake/use-fetch-content.cmake +++ b/infra/cmake/use-fetch-content.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 3.30) include(FetchContent) @@ -165,9 +165,8 @@ function(BemanExemplar_provideDependency method package_name) "${BemanExemplar_name}" GIT_REPOSITORY "${BemanExemplar_repo}" GIT_TAG "${BemanExemplar_tag}" - EXCLUDE_FROM_ALL + # NO! EXCLUDE_FROM_ALL ) - set(INSTALL_GTEST OFF) # Disable GoogleTest installation FetchContent_MakeAvailable("${BemanExemplar_name}") # Important! _FOUND tells CMake that `find_package` is diff --git a/requirements.txt b/requirements.txt index b5f68daf..8c9a52c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,11 +14,11 @@ pyaml ### cmake build context bump2version>=1.0.1 -check-jsonschema>=0.35.0 +check-jsonschema>=0.36.1 cmake>=4.2 codespell>=2.4.1 -# conan>=2.7.0 -gersemi>=0.23.2 -gcovr>=8.2 +# conan>=2.24.0 +gersemi>=0.25.3 +gcovr>=8.6 ninja>=1.13 -yamllint>=1.35 +yamllint>=1.38 diff --git a/src/beman/execution/CMakeLists.txt b/src/beman/execution/CMakeLists.txt index 0a3e3e34..1eedb41f 100644 --- a/src/beman/execution/CMakeLists.txt +++ b/src/beman/execution/CMakeLists.txt @@ -3,13 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # gersemi: on -if(NOT TARGET ${TARGET_NAME}) - add_library(${TARGET_NAME} STATIC) -endif() -add_library(${TARGET_ALIAS} ALIAS ${TARGET_NAME}) - target_sources( - ${TARGET_NAME} + ${TARGET_LIBRARY} PRIVATE execution.cpp PUBLIC FILE_SET ${TARGET_NAME}_public_headers @@ -196,45 +191,46 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/write_env.hpp ) -if(BEMAN_USE_MODULES) +if(BEMAN_HAS_MODULES) target_sources( - ${TARGET_NAME} - PUBLIC - FILE_SET CXX_MODULES - BASE_DIRS ${PROJECT_SOURCE_DIR}/src/beman/execution - FILES ${PROJECT_SOURCE_DIR}/src/beman/execution/execution.cppm + ${TARGET_LIBRARY} + PUBLIC FILE_SET CXX_MODULES FILES execution.cppm + ) +else() + set_target_properties( + ${TARGET_LIBRARY} + PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON ) endif() get_property( DETAIL_HEADER_FILES - TARGET ${TARGET_NAME} + TARGET ${TARGET_LIBRARY} PROPERTY HEADER_SET_${TARGET_NAME}_detail_headers ) source_group("Header Files\\detail" FILES ${DETAIL_HEADER_FILES}) -set_target_properties(${TARGET_NAME} PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) - if(NOT BEMAN_EXECUTION_ENABLE_INSTALL OR CMAKE_SKIP_INSTALL_RULES) return() endif() install( - TARGETS ${TARGET_NAME} - EXPORT ${TARGETS_EXPORT_NAME}1 + TARGETS ${TARGET_LIBRARY} + EXPORT ${TARGETS_EXPORT_NAME} ARCHIVE DESTINATION lib/$ + # TODO(CK): check this if is confirm to beman standard! FILE_SET ${TARGET_NAME}_public_headers FILE_SET ${TARGET_NAME}_detail_headers - FILE_SET CXX_MODULES DESTINATION ${INSTALL_CONFIGDIR}/module + FILE_SET CXX_MODULES DESTINATION ${INSTALL_CONFIGDIR}/cxx-modules # There's currently no convention for this location CXX_MODULES_BMI DESTINATION ${INSTALL_CONFIGDIR}/bmi-${CMAKE_CXX_COMPILER_ID}_$ ) install( - EXPORT ${TARGETS_EXPORT_NAME}1 + EXPORT ${TARGETS_EXPORT_NAME} FILE ${TARGETS_EXPORT_NAME}.cmake - DESTINATION "${INSTALL_CONFIGDIR}" + DESTINATION ${INSTALL_CONFIGDIR} NAMESPACE ${TARGET_NAMESPACE}:: CXX_MODULES_DIRECTORY . diff --git a/src/beman/execution/execution.cppm b/src/beman/execution/execution.cppm index b8c57614..6cffc25e 100644 --- a/src/beman/execution/execution.cppm +++ b/src/beman/execution/execution.cppm @@ -3,15 +3,24 @@ // ---------------------------------------------------------------------------- module; + +#ifndef BEMAN_HAS_IMPORT_STD #include #include -#include +#endif + +// FIXME: #include #include export module beman_execution; +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#endif + namespace beman::execution { export int version(0); + // [stoptoken.concepts], stop token concepts export using ::beman::execution::stoppable_token; export using ::beman::execution::unstoppable_token; @@ -19,6 +28,7 @@ export using ::beman::execution::unstoppable_token; // [stoptoken], class stop_token export using ::beman::execution::stop_token; +#ifdef USE_THIS_CODE // [stopsource], class stop_source export using ::beman::execution::stop_source; @@ -218,5 +228,6 @@ export using ::beman::execution::sync_wait; // [exec.with.awaitable.senders] //-dk:TODO export using ::beman::execution::with_awaitable_senders; +#endif } // namespace beman::execution diff --git a/tests/beman/execution/CMakeLists.txt b/tests/beman/execution/CMakeLists.txt index 4f4fefc7..0bcfdbb9 100644 --- a/tests/beman/execution/CMakeLists.txt +++ b/tests/beman/execution/CMakeLists.txt @@ -1,12 +1,5 @@ # src/beman/execution/tests/CMakeLists.txt # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required(VERSION 3.25...4.2) - -project(beman_execution_tests LANGUAGES CXX) - -if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) - message(FATAL_ERROR "In-source builds are not allowed!") -endif() list(APPEND todo exec-associate.test) @@ -16,109 +9,106 @@ if(BEMAN_USE_MODULES) execution-module.test # execution-module.test.cpp stop-token-module.test # stop-token-module.test.cpp ) +else() + list( + APPEND execution_tests + issue-174.test + issue-186.test + exec-scope-counting.test + exec-spawn.test + exec-stop-when.test + exec-prop.test + exec-scope-simple-counting.test + exec-spawn-future.test + exec-scope-concepts.test + issue-144.test + exec-on.test + notify.test + exec-awaitable.test + allocator-requirements-general.test + exec-connect.test + exec-continues-on.test + exec-domain-default.test + exec-fwd-env.test + exec-general.test + exec-get-allocator.test + exec-get-compl-sched.test + exec-get-delegation-scheduler.test + exec-get-domain.test + exec-get-env.test + exec-get-scheduler.test + exec-get-stop-token.test + exec-getcomplsigs.test + exec-into-variant.test + exec-just.test + exec-let.test + exec-opstate-start.test + exec-opstate.test + exec-read-env.test + exec-recv-concepts.test + exec-recv.test + exec-run-loop-general.test + exec-run-loop-types.test + exec-sched.test + exec-schedule-from.test + exec-schedule.test + exec-set-error.test + exec-set-stopped.test + exec-set-value.test + exec-snd-apply.test + exec-snd-concepts.test + exec-snd-expos.test + exec-snd-transform.test + exec-starts-on.test + exec-split.test + exec-sync-wait.test + exec-then.test + exec-utils-cmplsigs.test + exec-when-all.test + exec-with-awaitable-senders.test + execution-queryable-concept.test + exec-bulk.test + execution-syn.test + forward-like.test + function-objects.test + functional-syn.test + meta-combine.test + meta-contains.test + meta-filter.test + meta-prepend.test + meta-transform.test + meta-unique.test + stopcallback-cons.test + stopcallback-general.test + stopcallback-inplace-cons.test + stopcallback-inplace-general.test + stopcallback-inplace.test + stopcallback.test + stopsource-cons.test + stopsource-general.test + stopsource-inplace-cons.test + stopsource-inplace-general.test + stopsource-inplace-mem.test + stopsource-inplace.test + stopsource-mem.test + stopsource.test + stoptoken-concepts.test + stoptoken-general.test + stoptoken-inplace-general.test + stoptoken-inplace-members.test + stoptoken-inplace.test + stoptoken-mem.test + stoptoken-never-general.test + stoptoken-never.test + stoptoken.test + thread-stoptoken-intro.test + thread-stoptoken-syn.compile.test + thread-stoptoken.test + thread.test + utilities.test + ) endif() -list( - APPEND execution_tests - exec-affine-on.test - issue-174.test - issue-186.test - exec-scope-counting.test - exec-spawn.test - exec-stop-when.test - non_assignable.test - exec-prop.test - exec-env.test - exec-scope-simple-counting.test - exec-spawn-future.test - exec-scope-concepts.test - issue-144.test - exec-on.test - notify.test - exec-awaitable.test - allocator-requirements-general.test - exec-connect.test - exec-continues-on.test - exec-domain-default.test - exec-fwd-env.test - exec-general.test - exec-get-allocator.test - exec-get-compl-sched.test - exec-get-delegation-scheduler.test - exec-get-domain.test - exec-get-env.test - exec-get-scheduler.test - exec-get-stop-token.test - exec-getcomplsigs.test - exec-into-variant.test - exec-just.test - exec-let.test - exec-opstate-start.test - exec-opstate.test - exec-read-env.test - exec-recv-concepts.test - exec-recv.test - exec-run-loop-general.test - exec-run-loop-types.test - exec-sched.test - exec-schedule-from.test - exec-schedule.test - exec-set-error.test - exec-set-stopped.test - exec-set-value.test - exec-snd-apply.test - exec-snd-concepts.test - exec-snd-expos.test - exec-snd-transform.test - exec-starts-on.test - exec-split.test - exec-sync-wait.test - exec-then.test - exec-utils-cmplsigs.test - exec-when-all.test - exec-with-awaitable-senders.test - execution-queryable-concept.test - exec-bulk.test - execution-syn.test - forward-like.test - function-objects.test - functional-syn.test - meta-combine.test - meta-contains.test - meta-filter.test - meta-prepend.test - meta-transform.test - meta-unique.test - stopcallback-cons.test - stopcallback-general.test - stopcallback-inplace-cons.test - stopcallback-inplace-general.test - stopcallback-inplace.test - stopcallback.test - stopsource-cons.test - stopsource-general.test - stopsource-inplace-cons.test - stopsource-inplace-general.test - stopsource-inplace-mem.test - stopsource-inplace.test - stopsource-mem.test - stopsource.test - stoptoken-concepts.test - stoptoken-general.test - stoptoken-inplace-general.test - stoptoken-inplace-members.test - stoptoken-inplace.test - stoptoken-mem.test - stoptoken-never-general.test - stoptoken-never.test - stoptoken.test - thread-stoptoken-intro.test - thread-stoptoken-syn.compile.test - thread-stoptoken.test - thread.test - utilities.test -) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) foreach(test ${execution_tests}) @@ -127,3 +117,33 @@ foreach(test ${execution_tests}) target_link_libraries(${TEST_EXE} PRIVATE beman::execution) add_test(NAME ${TEST_EXE} COMMAND $) endforeach() + +if(BEMAN_EXECUTION_ENABLE_INSTALL) + # test if the targets are usable from the install directory + add_test( + NAME install-to-stagedir + COMMAND + ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix + ${CMAKE_BINARY_DIR}/stagedir --config $ + ) + add_test( + NAME find-package-test + COMMAND + ${CMAKE_CTEST_COMMAND} # --verbose + --output-on-failure -C $ # + --build-and-test "${CMAKE_SOURCE_DIR}/examples" + "${CMAKE_CURRENT_BINARY_DIR}/find-package-test" # + --build-generator ${CMAKE_GENERATOR} # + --build-makeprogram ${CMAKE_MAKE_PROGRAM} # + --build-options # + "-D BEMAN_HAS_MODULES=${BEMAN_HAS_MODULES}" + "-D CMAKE_BUILD_TYPE=$" + "-D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-D CMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}" + "-D CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}" + "-D CMAKE_CXX_SCAN_FOR_MODULES=${CMAKE_CXX_SCAN_FOR_MODULES}" + "-D CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-D CMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}" + "-D CMAKE_PREFIX_PATH=${CMAKE_BINARY_DIR}/stagedir" + ) +endif() diff --git a/tests/beman/execution/execution-module.test.cpp b/tests/beman/execution/execution-module.test.cpp index 6645b50f..34b5d217 100644 --- a/tests/beman/execution/execution-module.test.cpp +++ b/tests/beman/execution/execution-module.test.cpp @@ -2,12 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#if 1 -#include + +#ifdef BEMAN_HAS_IMPORT_STD +import std; #else -import beman_execution; +#include #endif +import beman_execution; + // ---------------------------------------------------------------------------- namespace { @@ -22,11 +25,11 @@ using error_types_of_t = test_stdex::error_types_of_t; TEST(execution_modules) { #if 0 - //-dk:TOD enable execution policies - test::use_type(); - test::use_type(); - test::use_type(); - test::use_type(); + //-dk:TODO enable execution policies + test::check_type(); + test::check_type(); + test::check_type(); + test::check_type(); test::use(test_std::seq); test::use(test_std::par); diff --git a/tests/beman/execution/include/test/execution.hpp b/tests/beman/execution/include/test/execution.hpp index f030b621..26d84f3f 100644 --- a/tests/beman/execution/include/test/execution.hpp +++ b/tests/beman/execution/include/test/execution.hpp @@ -4,7 +4,10 @@ #ifndef INCLUDED_TEST_EXECUTION #define INCLUDED_TEST_EXECUTION +// #ifndef BEMAN_USE_MODULES #include +// #endif + #include #include #ifndef _MSC_VER @@ -23,6 +26,7 @@ #define TEST(name) auto main() -> int namespace beman::execution {} +namespace beman::execution::detail {} namespace test_std = ::beman::execution; namespace test_detail = ::beman::execution::detail; diff --git a/tests/beman/execution/stop-token-module.test.cpp b/tests/beman/execution/stop-token-module.test.cpp index e58ee690..bbf68011 100644 --- a/tests/beman/execution/stop-token-module.test.cpp +++ b/tests/beman/execution/stop-token-module.test.cpp @@ -2,12 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#if 0 -#include + +#ifdef BEMAN_HAS_IMPORT_STD +import std; #else -import beman_execution; +#include #endif +import beman_execution; + // ---------------------------------------------------------------------------- TEST(stop_token_modules) {