diff --git a/.gitignore b/.gitignore index d293e3b..0d7e3bf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ /.cache /compile_commands.json -/build +build/ # ignore emacs temp files *~ diff --git a/CMakeLists.txt b/CMakeLists.txt index 26ae1da..01c7ffa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required(VERSION 3.28...4.2) +cmake_minimum_required(VERSION 3.30...4.3) + +include(./cmake/prelude.cmake) project( beman.transform_view # CMake Project Name, which is also the name of the top-level @@ -10,6 +12,9 @@ project( VERSION 0.1.0 ) +# Modules opt in only on compilers that support it: msvc, g++-15 and clang-20+ +include(./cmake/cxx-modules-rules.cmake) + # [CMAKE.SKIP_TESTS] option( BEMAN_TRANSFORM_VIEW_BUILD_TESTS @@ -30,24 +35,51 @@ include(infra/cmake/beman-install-library.cmake) add_library(beman.transform_view INTERFACE) add_library(beman::transform_view ALIAS beman.transform_view) -target_sources( - beman.transform_view - PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" -) +target_sources(beman.transform_view PUBLIC FILE_SET HEADERS BASE_DIRS include) set_target_properties( beman.transform_view - PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON + PROPERTIES VERIFY_INTERFACE_HEADER_SETS ${PROJECT_IS_TOP_LEVEL} ) add_subdirectory(include/beman/transform_view) -include(CTest) +if(BEMAN_USE_MODULES) + add_library(beman.transform_view_module STATIC) + add_library(beman::transform_view_module ALIAS beman.transform_view_module) + target_compile_features( + beman.transform_view_module + PUBLIC cxx_std_${CMAKE_CXX_STANDARD} + ) + if(BEMAN_HAS_IMPORT_STD) + target_compile_definitions( + beman.transform_view_module + PUBLIC BEMAN_HAS_IMPORT_STD + ) + set_target_properties( + beman.transform_view_module + PROPERTIES CXX_MODULE_STD ON + ) + endif() + + target_sources( + beman.transform_view_module + PUBLIC + FILE_SET HEADERS + BASE_DIRS include + FILES include/beman/transform_view/transform_view.hpp + ) + + add_subdirectory(src/beman/transform_view) +endif() + +# NOTE: CTest.cmake creates a lot of unused target here! CK +# NOT needed! include(CTest) +enable_testing() -beman_install_library(beman.transform_view TARGETS beman.transform_view) +beman_install_library(beman.transform_view TARGETS beman.transform_view beman.transform_view_module) if(BEMAN_TRANSFORM_VIEW_BUILD_TESTS) - enable_testing() add_subdirectory(tests/beman/transform_view) endif() diff --git a/CMakePresets.json b/CMakePresets.json index bd35911..f216563 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,14 +7,31 @@ "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { + "BEMAN_USE_MODULES": true, + "BEMAN_USE_STD_MODULE": true, "CMAKE_CXX_STANDARD": "23", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "./infra/cmake/use-fetch-content.cmake" + "CMAKE_CXX_EXTENSIONS": true, + "CMAKE_CXX_STANDARD_REQUIRED": true, + "CMAKE_EXPORT_COMPILE_COMMANDS": true, + "CMAKE_INSTALL_MESSAGE": "LAZY", + "CMAKE_SKIP_TEST_ALL_DEPENDENCY": false, + "CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "infra/cmake/use-fetch-content.cmake" } }, { "name": "_debug-base", "hidden": true, + "warnings": { + "dev": true, + "deprecated": true, + "uninitialized": true, + "unusedCli": true, + "systemVars": false + }, + "errors": { + "dev": false, + "deprecated": false + }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "BEMAN_BUILDSYS_SANITIZER": "MaxSan" @@ -57,7 +74,11 @@ "_debug-base" ], "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-libc++-toolchain.cmake" + }, + "environment": { + "CXX": "clang++", + "CMAKE_CXX_FLAGS": "-stdlib=libc++" } }, { @@ -68,7 +89,11 @@ "_release-base" ], "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-libc++-toolchain.cmake" + }, + "environment": { + "CXX": "clang++", + "CMAKE_CXX_FLAGS": "-stdlib=libc++" } }, { @@ -79,6 +104,8 @@ "_debug-base" ], "cacheVariables": { + "BEMAN_USE_STD_MODULE": false, + "BEMAN_USE_MODULES": false, "CMAKE_TOOLCHAIN_FILE": "infra/cmake/appleclang-toolchain.cmake" } }, @@ -90,6 +117,8 @@ "_release-base" ], "cacheVariables": { + "BEMAN_USE_STD_MODULE": false, + "BEMAN_USE_MODULES": false, "CMAKE_TOOLCHAIN_FILE": "infra/cmake/appleclang-toolchain.cmake" } }, @@ -102,6 +131,11 @@ ], "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "infra/cmake/msvc-toolchain.cmake" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" } }, { @@ -113,6 +147,11 @@ ], "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "infra/cmake/msvc-toolchain.cmake" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" } } ], diff --git a/cmake/cxx-modules-rules.cmake b/cmake/cxx-modules-rules.cmake new file mode 100644 index 0000000..458b3d6 --- /dev/null +++ b/cmake/cxx-modules-rules.cmake @@ -0,0 +1,149 @@ +# 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() +# + +# ---- 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(CMAKE_CXX_STANDARD GREATER_EQUAL 20) + option(BEMAN_USE_MODULES "Build CXX_MODULES" ${CMAKE_CXX_SCAN_FOR_MODULES}) +endif() +message(STATUS "BEMAN_USE_MODULES=${BEMAN_USE_MODULES}") + +option( + BEMAN_USE_STD_MODULE + "Check if 'import std;' is possible with the toolchain?" + OFF +) +message(STATUS "BEMAN_USE_STD_MODULE=${BEMAN_USE_STD_MODULE}") + +if(BEMAN_USE_MODULES AND 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_SCAN_FOR_MODULES ON) + option( + BEMAN_HAS_IMPORT_STD + "Build with import std; is possible and used!" + ${BEMAN_USE_STD_MODULE} + ) + else() + set(BEMAN_HAS_IMPORT_STD OFF) + set(CMAKE_CXX_MODULE_STD OFF) + message(WARNING "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}") + endif() +endif() + +if(NOT BEMAN_USE_MODULES) + set(BEMAN_HAS_IMPORT_STD OFF) +endif() +message(STATUS "BEMAN_HAS_IMPORT_STD=${BEMAN_HAS_IMPORT_STD}") + +# ------------------------------------------------------------------------------ +# 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 0000000..e1572d4 --- /dev/null +++ b/cmake/prelude.cmake @@ -0,0 +1,122 @@ +# 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! +# + +# ---- 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() + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +# --------------------------------------------------------------------------- +if(NOT BEMAN_USE_STD_MODULE) + return() +endif() +# --------------------------------------------------------------------------- + +if(PROJECT_NAME) + message( + WARNING + "This CMake file has to be included before first project() command call!" + ) +endif() + +# gersemi: off +# --------------------------------------------------------------------------- +# check if import std; is supported by CMAKE_CXX_COMPILER +# --------------------------------------------------------------------------- +if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.3 AND CMAKE_VERSION VERSION_LESS 4.4) + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") +elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_VERSION VERSION_LESS 4.3) + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") +endif() +# gersemi: on + +# --------------------------------------------------------------------------- +# TODO(CK): Do we need this HACK still for linux too? +# --------------------------------------------------------------------------- +if(NOT APPLE) + return() +endif() + +# FIXME: clang++ we still needs to export CXX=clang++ +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_EQUAL 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) + + # /usr/local/Cellar/llvm/21.1.8_1/lib/c++/libc++.modules.json + # "/usr/local/Cellar/llvm/21.1.8_1/share/libc++/v1/std.cppm", + 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=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( + FATAL_ERROR + "File does NOT EXISTS! ${CMAKE_CXX_STDLIB_MODULES_JSON}" + ) + endif() +endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 112e633..4b0c783 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,19 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +cmake_minimum_required(VERSION 3.30...4.3) + +include(../cmake/prelude.cmake) + +project(beman.transform_view.example LANGUAGES CXX) + +if(PROJECT_IS_TOP_LEVEL) + include(../cmake/cxx-modules-rules.cmake) + + find_package(beman.transform_view 0.1.0 EXACT REQUIRED) + + enable_testing() +endif() + set(ALL_EXAMPLES transform_view_direct_usage) message("Examples to be built: ${ALL_EXAMPLES}") @@ -10,8 +24,29 @@ foreach(example ${ALL_EXAMPLES}) beman.transform_view.examples.${example} PRIVATE ${example}.cpp ) - target_link_libraries( - beman.transform_view.examples.${example} - PRIVATE beman::transform_view + if(BEMAN_USE_MODULES) + target_compile_features( + beman.transform_view.examples.${example} + PUBLIC cxx_std_${CMAKE_CXX_STANDARD} + ) + if(BEMAN_HAS_IMPORT_STD) + set_target_properties( + beman.transform_view.examples.${example} + PROPERTIES CXX_MODULE_STD ON + ) + endif() + target_link_libraries( + beman.transform_view.examples.${example} + PRIVATE beman::transform_view_module + ) + else() + target_link_libraries( + beman.transform_view.examples.${example} + PRIVATE beman::transform_view + ) + endif() + add_test( + NAME beman.transform_view.examples.${example} + COMMAND beman.transform_view.examples.${example} ) endforeach() diff --git a/examples/transform_view_direct_usage.cpp b/examples/transform_view_direct_usage.cpp index ddb7f00..43564ae 100644 --- a/examples/transform_view_direct_usage.cpp +++ b/examples/transform_view_direct_usage.cpp @@ -1,12 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include - +#ifdef BEMAN_HAS_STD_MODULE +import std; +#else #include +#include +#include +#endif + +#ifdef BEMAN_HAS_MODULES +import beman.transform_view; +#else +#include +#endif namespace tv26 = beman::transform_view; int main() { + // TODO(CK): warning: C-style casts are discouraged; use static_cast auto to_lower = [](char c) { return char(c + 0x20); }; const std::string upper_str = "LOWER"; diff --git a/include/beman/transform_view/transform_view.hpp b/include/beman/transform_view/transform_view.hpp index a159176..c551820 100644 --- a/include/beman/transform_view/transform_view.hpp +++ b/include/beman/transform_view/transform_view.hpp @@ -3,10 +3,12 @@ #ifndef BEMAN_TRANSFORM_VIEW_HPP #define BEMAN_TRANSFORM_VIEW_HPP +#ifndef BEMAN_HAS_STD_MODULE #include #include #include #include +#endif namespace beman::transform_view { @@ -504,7 +506,7 @@ struct bind_back_t { private: using indices = std::index_sequence_for; - template + template static constexpr decltype(auto) call_impl(T&& this_, std::index_sequence, Args&&... args) { return ((T&&)this_) diff --git a/infra/cmake/beman-install-library.cmake b/infra/cmake/beman-install-library.cmake index f8673f4..c5e3686 100644 --- a/infra/cmake/beman-install-library.cmake +++ b/infra/cmake/beman-install-library.cmake @@ -1,5 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +cmake_minimum_required(VERSION 3.30) + include_guard(GLOBAL) include(CMakePackageConfigHelpers) @@ -15,11 +17,11 @@ include(GNUInstallDirs) # ------ # beman_install_library( # TARGETS [ ...] -# [DEPENDENCY [] [EXACT]] -# [DEPENDENCY ...] +# [DEPENDENCIES [ ...]] # [NAMESPACE ] # [EXPORT_NAME ] # [DESTINATION ] +# [VERSION_SUFFIX] # ) # # Arguments: @@ -32,16 +34,11 @@ include(GNUInstallDirs) # TARGETS (required) # List of CMake targets to install. # -# DEPENDENCY (optional, repeatable) -# A dependency to add as a find_dependency() call in the generated config -# file. Each DEPENDENCY keyword starts a new entry; all tokens up to the -# next keyword are passed as arguments to find_dependency(). May be -# specified multiple times. -# -# Example: -# DEPENDENCY beman.inplace_vector 1.0.0 -# DEPENDENCY beman.scope 0.0.1 EXACT -# DEPENDENCY fmt +# 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. @@ -55,6 +52,9 @@ include(GNUInstallDirs) # The install destination for CXX_MODULES. # Defaults to ${CMAKE_INSTALL_LIBDIR}/cmake/${name}/modules. # +# VERSION_SUFFIX (optional) +# option to enable the versioning of install destinations +# # Brief # ----- # @@ -88,59 +88,16 @@ function(beman_install_library name) # ---------------------------- # Argument parsing # ---------------------------- - - # Pre-process ARGN to extract repeated DEPENDENCY entries, since - # cmake_parse_arguments does not support repeated keywords. - set(_known_keywords - TARGETS DEPENDENCY NAMESPACE EXPORT_NAME DESTINATION - ) - set(_dependencies "") - set(_filtered_args "") - set(_in_dep FALSE) - set(_current_dep "") - - foreach(_token ${ARGN}) - if(_token STREQUAL "DEPENDENCY") - # Flush previous dependency if any - if(_in_dep AND NOT "${_current_dep}" STREQUAL "") - list(APPEND _dependencies "${_current_dep}") - endif() - set(_in_dep TRUE) - set(_current_dep "") - elseif(_in_dep AND "${_token}" IN_LIST _known_keywords) - # Hit another keyword; flush current dep and pass token through - if(NOT "${_current_dep}" STREQUAL "") - list(APPEND _dependencies "${_current_dep}") - endif() - set(_in_dep FALSE) - set(_current_dep "") - list(APPEND _filtered_args "${_token}") - elseif(_in_dep) - # Accumulate tokens as space-separated find_dependency() args - if("${_current_dep}" STREQUAL "") - set(_current_dep "${_token}") - else() - string(APPEND _current_dep " ${_token}") - endif() - else() - list(APPEND _filtered_args "${_token}") - endif() - endforeach() - # Flush final dependency - if(_in_dep AND NOT "${_current_dep}" STREQUAL "") - list(APPEND _dependencies "${_current_dep}") - endif() - - set(options "") + set(options VERSION_SUFFIX) set(oneValueArgs NAMESPACE EXPORT_NAME DESTINATION) - set(multiValueArgs TARGETS) + set(multiValueArgs TARGETS DEPENDENCIES) cmake_parse_arguments( BEMAN "${options}" "${oneValueArgs}" "${multiValueArgs}" - ${_filtered_args} + ${ARGN} ) if(NOT BEMAN_TARGETS) @@ -158,7 +115,20 @@ function(beman_install_library name) return() endif() - set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}") + # gersemi: off + set(_version_suffix) + set(_include_install_dir) + set(_lib_install_dir) + set(_bin_install_dir) + # NOTE: If one of this variables is not set, the default DESTINATION is used! CK + if(BEMAN_VERSION_SUFFIX) + set(_version_suffix "-${PROJECT_VERSION}") + set(_include_install_dir DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/beman${_version_suffix}) + # set(_lib_install_dir DESTINATION ${CMAKE_INSTALL_LIBDIR}/beman${_version_suffix}) + # set(_bin_install_dir DESTINATION ${CMAKE_INSTALL_BINDIR}/beman${_version_suffix}) + endif() + set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}${_version_suffix}") + # gersemi: on # ---------------------------- # Defaults @@ -227,10 +197,10 @@ function(beman_install_library name) ) foreach(_install_header_set IN LISTS _available_header_sets) list( - APPEND - _install_header_set_args + APPEND _install_header_set_args FILE_SET "${_install_header_set}" + ${_include_install_dir} COMPONENT "${install_component_name}_Development" ) @@ -250,17 +220,22 @@ function(beman_install_library name) TARGETS "${_tgt}" EXPORT ${BEMAN_EXPORT_NAME} ARCHIVE + ${_lib_install_dir} COMPONENT "${install_component_name}_Development" LIBRARY + ${_lib_install_dir} COMPONENT "${install_component_name}_Runtime" NAMELINK_COMPONENT "${install_component_name}_Development" RUNTIME + ${_bin_install_dir} COMPONENT "${install_component_name}_Runtime" ${_install_header_set_args} FILE_SET ${_module_sets} DESTINATION "${BEMAN_DESTINATION}" COMPONENT "${install_component_name}_Development" + # NOTE: There's currently no convention for this location! CK CXX_MODULES_BMI + DESTINATION ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ COMPONENT "${install_component_name}_Development" ) else() @@ -268,11 +243,14 @@ function(beman_install_library name) TARGETS "${_tgt}" EXPORT ${BEMAN_EXPORT_NAME} ARCHIVE + ${_lib_install_dir} COMPONENT "${install_component_name}_Development" LIBRARY + ${_lib_install_dir} COMPONENT "${install_component_name}_Runtime" NAMELINK_COMPONENT "${install_component_name}_Development" RUNTIME + ${_bin_install_dir} COMPONENT "${install_component_name}_Runtime" ${_install_header_set_args} ) @@ -294,42 +272,47 @@ function(beman_install_library name) # ---------------------------------------- # Config file installation logic - # - # Precedence (highest to lowest): - # 1. Per-package variable _INSTALL_CONFIG_FILE_PACKAGE - # 2. Allow-list BEMAN_INSTALL_CONFIG_FILE_PACKAGES (if defined) - # 3. Default: ON # ---------------------------------------- string(TOUPPER "${name}" _pkg_upper) string(REPLACE "." "_" _pkg_prefix "${_pkg_upper}") - set(_pkg_var "${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE") + option( + ${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE + "Enable creating and installing a CMake config-file package. Default: ON. Values: { ON, OFF }." + ON + ) - # Default: install config files - set(_install_config ON) + set(_pkg_var "${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE") - # If the allow-list is defined, only install for packages in the list - if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES) - if(NOT "${name}" IN_LIST BEMAN_INSTALL_CONFIG_FILE_PACKAGES) - set(_install_config OFF) - endif() + if(NOT DEFINED ${_pkg_var}) + set(${_pkg_var} + OFF + CACHE BOOL + "Install CMake package config files for ${name}" + ) endif() - # Per-package override takes highest precedence - if(DEFINED ${_pkg_var}) - set(_install_config ${${_pkg_var}}) + 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 LISTS _dependencies) + foreach(dep IN LISTS BEMAN_DEPENDENCIES) message( VERBOSE - "beman-install-library(${name}): Add find_dependency(${_dep})" + "beman-install-library(${name}): Add find_dependency(${dep})" ) - string(APPEND _beman_find_deps "find_dependency(${_dep})\n") + string(APPEND _beman_find_deps "find_dependency(${dep})\n") endforeach() set(BEMAN_FIND_DEPENDENCIES "${_beman_find_deps}") diff --git a/src/beman/transform_view/CMakeLists.txt b/src/beman/transform_view/CMakeLists.txt new file mode 100644 index 0000000..663ccfb --- /dev/null +++ b/src/beman/transform_view/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +target_sources( + beman.transform_view_module + PUBLIC FILE_SET CXX_MODULES FILES transform_view.cppm +) + +target_compile_definitions(beman.transform_view_module PUBLIC BEMAN_HAS_MODULES) diff --git a/src/beman/transform_view/transform_view.cppm b/src/beman/transform_view/transform_view.cppm new file mode 100644 index 0000000..d0c403e --- /dev/null +++ b/src/beman/transform_view/transform_view.cppm @@ -0,0 +1,18 @@ +module; + +#ifndef BEMAN_HAS_STD_MODULE +#include +#include +#include +#include +#else +import std; +#endif + +export module beman.transform_view; + +extern "C++" { +export { +#include +} +} diff --git a/tests/beman/transform_view/CMakeLists.txt b/tests/beman/transform_view/CMakeLists.txt index 0747c5b..c6f80ec 100644 --- a/tests/beman/transform_view/CMakeLists.txt +++ b/tests/beman/transform_view/CMakeLists.txt @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -find_package(GTest REQUIRED) +find_package(GTest CONFIG REQUIRED) add_executable(beman.transform_view.tests.transform_view) target_sources( @@ -12,5 +12,58 @@ target_link_libraries( PRIVATE beman::transform_view GTest::gtest GTest::gtest_main ) +if(BEMAN_USE_MODULES) + add_executable(beman.transform_view.tests.module_smoke_test) + target_sources( + beman.transform_view.tests.module_smoke_test + PRIVATE module_smoke_test.test.cpp + ) + target_link_libraries( + beman.transform_view.tests.module_smoke_test + PRIVATE beman::transform_view_module + ) +endif() + include(GoogleTest) gtest_discover_tests(beman.transform_view.tests.transform_view) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + return() +endif() + +#====================================================================== +# NOTE: Only for Release build with enabled install rules useful! CK +#====================================================================== +if( + BEMAN_TRANSFORM_VIEW_INSTALL_CONFIG_FILE_PACKAGE + AND NOT CMAKE_SKIP_INSTALL_RULES +) + # 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_USE_MODULES=${BEMAN_USE_MODULES}" + "-D BEMAN_USE_STD_MODULE=${BEMAN_USE_STD_MODULE}" + "-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_STANDARD=${CMAKE_CXX_STANDARD}" + "-D CMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}" + "-D CMAKE_PREFIX_PATH=${CMAKE_BINARY_DIR}/stagedir" + "-D CMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" + ) +endif() diff --git a/tests/beman/transform_view/module_smoke_test.test.cpp b/tests/beman/transform_view/module_smoke_test.test.cpp new file mode 100644 index 0000000..50c4830 --- /dev/null +++ b/tests/beman/transform_view/module_smoke_test.test.cpp @@ -0,0 +1,3 @@ +import beman.transform_view; + +int main() {}