diff options
454 files changed, 8612 insertions, 2089 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8f26508..f321d33 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -275,6 +275,26 @@ t:hip4.2-radeon: variables: CMAKE_CI_NO_MR: "true" +t:linux-gcc-cxx-modules-ninja: + extends: + - .gcc_cxx_modules_ninja + - .cmake_test_linux_release + - .linux_builder_tags + - .run_dependent + - .needs_centos6_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:linux-gcc-cxx-modules-ninja-multi: + extends: + - .gcc_cxx_modules_ninja_multi + - .cmake_test_linux_release + - .linux_builder_tags + - .run_dependent + - .needs_centos6_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + b:fedora36-ninja: extends: - .fedora36_ninja diff --git a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake index bbccbcf..08c1a1a 100644 --- a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake +++ b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake @@ -44,6 +44,7 @@ set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "") set(CMake_TEST_FindMPI "ON" CACHE BOOL "") set(CMake_TEST_FindODBC "ON" CACHE BOOL "") set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") diff --git a/.gitlab/ci/configure_debian10_ninja.cmake b/.gitlab/ci/configure_debian10_ninja.cmake index 2fcff7a..12564fa 100644 --- a/.gitlab/ci/configure_debian10_ninja.cmake +++ b/.gitlab/ci/configure_debian10_ninja.cmake @@ -48,6 +48,7 @@ set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "") set(CMake_TEST_FindMPI "ON" CACHE BOOL "") set(CMake_TEST_FindODBC "ON" CACHE BOOL "") set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") diff --git a/.gitlab/ci/configure_fedora36_makefiles.cmake b/.gitlab/ci/configure_fedora36_makefiles.cmake index c5b5190..7abc269 100644 --- a/.gitlab/ci/configure_fedora36_makefiles.cmake +++ b/.gitlab/ci/configure_fedora36_makefiles.cmake @@ -47,6 +47,7 @@ set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "") set(CMake_TEST_FindMPI "ON" CACHE BOOL "") set(CMake_TEST_FindODBC "ON" CACHE BOOL "") set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake new file mode 100644 index 0000000..bf990c8 --- /dev/null +++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake @@ -0,0 +1,4 @@ +set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake new file mode 100644 index 0000000..bf990c8 --- /dev/null +++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake @@ -0,0 +1,4 @@ +set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/cxx_modules_rules_gcc.cmake b/.gitlab/ci/cxx_modules_rules_gcc.cmake new file mode 100644 index 0000000..d800099 --- /dev/null +++ b/.gitlab/ci/cxx_modules_rules_gcc.cmake @@ -0,0 +1,10 @@ +set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3") + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE + "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>" + " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>" + " -fmodules-ts -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT> -fdep-format=trtbd" + " -o <PREPROCESSED_SOURCE>") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "-fmodules-ts -fmodule-mapper=<MODULE_MAP_FILE> -fdep-format=trtbd -x c++") diff --git a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile new file mode 100644 index 0000000..e0af0b9 --- /dev/null +++ b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile @@ -0,0 +1,9 @@ +FROM fedora:36 +MAINTAINER Ben Boeckel <ben.boeckel@kitware.com> + +# Install build dependencies for packages. +COPY install_deps.sh /root/install_deps.sh +RUN sh /root/install_deps.sh + +COPY install_gcc.sh /root/install_gcc.sh +RUN sh /root/install_gcc.sh diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh new file mode 100755 index 0000000..b8b706b --- /dev/null +++ b/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +dnf install -y --setopt=install_weak_deps=False \ + gcc-c++ mpfr-devel libmpc-devel isl-devel flex bison file findutils diffutils git-core +dnf clean all diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh new file mode 100755 index 0000000..20ea35f --- /dev/null +++ b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -e + +readonly revision="p1689r5-cmake-ci-20220614" # 3075e510e3d29583f8886b95aff044c0474c84a5 +readonly tarball="https://github.com/mathstuf/gcc/archive/$revision.tar.gz" + +readonly workdir="$HOME/gcc" +readonly srcdir="$workdir/gcc" +readonly builddir="$workdir/build" +readonly njobs="$( nproc )" + +mkdir -p "$workdir" +cd "$workdir" +curl -L "$tarball" > "gcc-$revision.tar.gz" +tar xf "gcc-$revision.tar.gz" +mv "gcc-$revision" "$srcdir" +mkdir -p "$builddir" +cd "$builddir" +"$srcdir/configure" \ + --disable-multilib \ + --enable-languages=c,c++ \ + --prefix="/opt/gcc-p1689" +make "-j$njobs" +make "-j$njobs" install-strip +rm -rf "$workdir" diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 12fbc1e..25d5365 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -299,6 +299,30 @@ CMAKE_CONFIGURATION: hip4.2_radeon CMAKE_GENERATOR: "Ninja Multi-Config" +### C++ modules + +.gcc_cxx_modules_x86_64: + image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2022-06-21" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + CMAKE_ARCH: x86_64 + CC: "/opt/gcc-p1689/bin/gcc" + CXX: "/opt/gcc-p1689/bin/g++" + +.gcc_cxx_modules_ninja: + extends: .gcc_cxx_modules_x86_64 + + variables: + CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja + +.gcc_cxx_modules_ninja_multi: + extends: .gcc_cxx_modules_x86_64 + + variables: + CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja_multi + CMAKE_GENERATOR: "Ninja Multi-Config" + ## Tags .linux_builder_tags: diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el index 8224d9e..2de9b98 100644 --- a/Auxiliary/cmake-mode.el +++ b/Auxiliary/cmake-mode.el @@ -288,6 +288,39 @@ This puts the mark at the end, and point at the beginning." ;------------------------------------------------------------------------------ +(defun cmake--syntax-propertize-until-bracket-close (syntax) + ;; This function assumes that a previous search has matched the + ;; beginning of a bracket_comment or bracket_argument and that the + ;; second capture group has matched the equal signs between the two + ;; opening brackets + (let* ((mb (match-beginning 2)) + (me (match-end 2)) + (cb (format "]%s]" (buffer-substring mb me)))) + (save-match-data + (if (search-forward cb end 'move) + (progn + (setq me (match-end 0)) + (put-text-property + (1- me) + me + 'syntax-table + (string-to-syntax syntax))) + (setq me end))) + (put-text-property + (match-beginning 1) + me + 'syntax-multiline + t))) + +(defconst cmake--syntax-propertize-function + (syntax-propertize-rules + ("\\(#\\)\\[\\(=*\\)\\[" + (1 + (prog1 "!" (cmake--syntax-propertize-until-bracket-close "!")))) + ("\\(\\[\\)\\(=*\\)\\[" + (1 + (prog1 "|" (cmake--syntax-propertize-until-bracket-close "|")))))) + ;; Syntax table for this mode. (defvar cmake-mode-syntax-table nil "Syntax table for CMake mode.") @@ -318,7 +351,10 @@ This puts the mark at the end, and point at the beginning." ; Setup indentation function. (set (make-local-variable 'indent-line-function) 'cmake-indent) ; Setup comment syntax. - (set (make-local-variable 'comment-start) "#")) + (set (make-local-variable 'comment-start) "#") + ;; Setup syntax propertization + (set (make-local-variable 'syntax-propertize-function) cmake--syntax-propertize-function) + (add-hook 'syntax-propertize-extend-region-functions #'syntax-propertize-multiline nil t)) ;; Default cmake-mode key bindings (define-key cmake-mode-map "\e\C-a" #'cmake-beginning-of-defun) diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index e1a2885..1273c00 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -436,6 +436,7 @@ syn keyword cmakeProperty contained \ XCODE_SCHEME_ENVIRONMENT \ XCODE_SCHEME_EXECUTABLE \ XCODE_SCHEME_GUARD_MALLOC + \ XCODE_SCHEME_LAUNCH_MODE \ XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP \ XCODE_SCHEME_MALLOC_GUARD_EDGES \ XCODE_SCHEME_MALLOC_SCRIBBLE @@ -1537,6 +1538,7 @@ syn keyword cmakeVariable contained \ CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE \ CMAKE_XCODE_SCHEME_ENVIRONMENT \ CMAKE_XCODE_SCHEME_GUARD_MALLOC + \ CMAKE_XCODE_SCHEME_LAUNCH_MODE \ CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP \ CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES \ CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE diff --git a/CMakeLists.txt b/CMakeLists.txt index 9de5338..4d9d3ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION 3.13...3.22 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.23 FATAL_ERROR) set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake) diff --git a/CompileFlags.cmake b/CompileFlags.cmake index e6fb20b..5641de4 100644 --- a/CompileFlags.cmake +++ b/CompileFlags.cmake @@ -136,3 +136,12 @@ OFF to disable /MP completely." ) endif() endif() endif() + +# Get rid of excess -Wunused-but-set-variable on release builds with LCC >= 1.26 +foreach(l C CXX) + if(CMAKE_${l}_COMPILER_ID STREQUAL "LCC" AND NOT CMAKE_${l}_COMPILER_VERSION VERSION_LESS 1.26) + foreach(c MINSIZEREL RELEASE RELWITHDEBINFO) + string(APPEND "CMAKE_${l}_FLAGS_${c}" " -Wno-unused-but-set-variable") + endforeach() + endif() +endforeach() diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt index 6683edb..e5e7496 100644 --- a/Help/command/FIND_XXX.txt +++ b/Help/command/FIND_XXX.txt @@ -15,6 +15,7 @@ The general signature is: [PATHS [path | ENV var]... ] [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)] [PATH_SUFFIXES suffix1 [suffix2 ...]] + [VALIDATOR function] [DOC "cache documentation string"] [NO_CACHE] [REQUIRED] @@ -66,6 +67,26 @@ Options include: Specify additional subdirectories to check below each directory location otherwise considered. +``VALIDATOR`` + .. versionadded:: 3.25 + + Specify a :command:`function` (a :command:`macro` is not an acceptable + choice) which will be called for each found item. The search ends when + the validation function returns a successful status. + + The validation function expects two arguments: output variable name and item + value. By default, the output variable name already holds a ``TRUE`` value. + + .. parsed-literal:: + + function (MY_CHECK output_status item) + if (NOT item MATCHES ...) + set(${output_status} FALSE PARENT_SCOPE) + endif() + endfunction() + + |FIND_XXX| (result NAMES ... VALIDATOR my_check) + ``DOC`` Specify the documentation string for the ``<VAR>`` cache entry. diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst index cb8d60b..377e42d 100644 --- a/Help/command/cmake_language.rst +++ b/Help/command/cmake_language.rst @@ -14,6 +14,7 @@ Synopsis cmake_language(`EVAL`_ CODE <code>...) cmake_language(`DEFER`_ <options>... CALL <command> [<arg>...]) cmake_language(`SET_DEPENDENCY_PROVIDER`_ <command> SUPPORTED_METHODS <methods>...) + cmake_language(`GET_MESSAGE_LOG_LEVEL`_ <out-var>) Introduction ^^^^^^^^^^^^ @@ -491,3 +492,28 @@ calling the provider command recursively for the same dependency. SET_DEPENDENCY_PROVIDER mycomp_provide_dependency SUPPORTED_METHODS FIND_PACKAGE ) + +Getting current message log level +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.25 + +.. _GET_MESSAGE_LOG_LEVEL: +.. _query_message_log_level: + +.. code-block:: cmake + + cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>) + +Writes the current :command:`message` logging level +into the given ``<output_variable>``. + +See :command:`message` for the possible logging levels. + +The current message logging level can be set either using the ``--log-level`` +command line option of the :manual:`cmake(1)` program or using +the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable. + +If both the command line option and the variable are set, the command line +option takes precedence. If neither are set, the default logging level +is returned. diff --git a/Help/command/enable_language.rst b/Help/command/enable_language.rst index d2acbc8..d9103b8 100644 --- a/Help/command/enable_language.rst +++ b/Help/command/enable_language.rst @@ -1,13 +1,14 @@ enable_language --------------- -Enable a language (CXX/C/OBJC/OBJCXX/Fortran/etc) + +Enable languages (CXX/C/OBJC/OBJCXX/Fortran/etc) .. code-block:: cmake - enable_language(<lang> [OPTIONAL] ) + enable_language(<lang>... [OPTIONAL]) -Enables support for the named language in CMake. This is -the same as the :command:`project` command but does not create any of the extra +Enables support for the named languages in CMake. This is the same as +the :command:`project` command but does not create any of the extra variables that are created by the project command. Example languages are ``CXX``, ``C``, ``CUDA``, ``OBJC``, ``OBJCXX``, ``Fortran``, ``HIP``, ``ISPC``, and ``ASM``. diff --git a/Help/command/export.rst b/Help/command/export.rst index dc69645..6785b05 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst @@ -25,7 +25,8 @@ Exporting Targets .. code-block:: cmake export(TARGETS <target>... [NAMESPACE <namespace>] - [APPEND] FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES]) + [APPEND] FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES] + [CXX_MODULES_DIRECTORY <directory>]) Creates a file ``<filename>`` that may be included by outside projects to import targets named by ``<target>...`` from the current project's build tree. @@ -52,6 +53,16 @@ The options are: in the export, even when policy :policy:`CMP0022` is NEW. This is useful to support consumers using CMake versions older than 2.8.12. +``CXX_MODULES_DIRECTORY <directory>`` + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + + Export C++ module properties to files under the given directory. Each file + will be named according to the target's export name (without any namespace). + These files will automatically be included from the export file. + This signature requires all targets to be listed explicitly. If a library target is included in the export, but a target to which it links is not included, the behavior is unspecified. See the `export(EXPORT)`_ signature @@ -95,7 +106,8 @@ Exporting Targets matching install(EXPORT) .. code-block:: cmake - export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>]) + export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>] + [CXX_MODULES_DIRECTORY <directory>]) Creates a file ``<filename>`` that may be included by outside projects to import targets from the current project's build tree. This is the same diff --git a/Help/command/install.rst b/Help/command/install.rst index 973aa31..5aa3a4b 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -132,7 +132,7 @@ Installing Targets install(TARGETS targets... [EXPORT <export-name>] [RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>] [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE| - PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>] + PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>|CXX_MODULES_BMI] [DESTINATION <dir>] [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] @@ -215,11 +215,21 @@ that may be installed: ``/blah/include/myproj/here.h`` with a base directory ``/blah/include`` would be installed to ``myproj/here.h`` below the destination. +``CXX_MODULES_BMI`` + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + + Any module files from C++ modules from ``PUBLIC`` sources in a file set of + type ``CXX_MODULES`` will be installed to the given ``DESTINATION``. All + modules are placed directly in the destination as no directory structure is + derived from the names of the modules. An empty ``DESTINATION`` may be used + to suppress installing these files (for use in generic code). + For each of these arguments given, the arguments following them only apply to the target or file type specified in the argument. If none is given, the -installation properties apply to all target types. If only one is given then -only targets of that type will be installed (which can be used to install -just a DLL or just an import library.) +installation properties apply to all target types. For regular executables, static libraries and shared libraries, the ``DESTINATION`` argument is not required. For these target types, when @@ -233,6 +243,14 @@ Apple bundles and frameworks. A destination can be omitted for interface and object libraries, but they are handled differently (see the discussion of this topic toward the end of this section). +For shared libraries on DLL platforms, if neither ``RUNTIME`` nor ``ARCHIVE`` +destinations are specified, both the ``RUNTIME`` and ``ARCHIVE`` components are +installed to their default destinations. If either a ``RUNTIME`` or ``ARCHIVE`` +destination is specified, the component is installed to that destination, and +the other component is not installed. If both ``RUNTIME`` and ``ARCHIVE`` +destinations are specified, then both components are installed to their +respective destinations. + The following table shows the target types with their associated variables and built-in defaults that apply when no destination is given: @@ -778,9 +796,10 @@ Installing Exports .. code-block:: cmake install(EXPORT <export-name> DESTINATION <dir> - [NAMESPACE <namespace>] [[FILE <name>.cmake]| + [NAMESPACE <namespace>] [FILE <name>.cmake] [PERMISSIONS permissions...] - [CONFIGURATIONS [Debug|Release|...]] + [CONFIGURATIONS [Debug|Release|...] + [CXX_MODULES_DIRECTORY <directory>] [EXPORT_LINK_INTERFACE_LIBRARIES] [COMPONENT <component>] [EXCLUDE_FROM_ALL]) @@ -836,6 +855,18 @@ library is always installed if the headers and CMake export file are present. to an ndk build system complete with transitive dependencies, include flags and defines required to use the libraries. +``CXX_MODULES_DIRECTORY`` + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + + Specify a subdirectory to store C++ module information for targets in the + export set. This directory will be populated with files which add the + necessary target property information to the relevant targets. Note that + without this information, none of the C++ modules which are part of the + targets in the export set will support being imported in consuming targets. + The ``EXPORT`` form is useful to help outside projects use targets built and installed by the current project. For example, the code diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst index 1ad6c37..a079307 100644 --- a/Help/command/target_sources.rst +++ b/Help/command/target_sources.rst @@ -52,10 +52,10 @@ expressions to ensure the sources are correctly assigned to the target. .. code-block:: cmake # WRONG: starts with generator expression, but relative path used - target_sources(MyTarget "$<$<CONFIG:Debug>:dbgsrc.cpp>") + target_sources(MyTarget PRIVATE "$<$<CONFIG:Debug>:dbgsrc.cpp>") # CORRECT: absolute path used inside the generator expression - target_sources(MyTarget "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/dbgsrc.cpp>") + target_sources(MyTarget PRIVATE "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/dbgsrc.cpp>") See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. @@ -75,9 +75,33 @@ File Sets Adds a file set to a target, or adds files to an existing file set. Targets have zero or more named file sets. Each file set has a name, a type, a scope of ``INTERFACE``, ``PUBLIC``, or ``PRIVATE``, one or more base directories, and -files within those directories. The only acceptable type is ``HEADERS``. The -optional default file sets are named after their type. The target may not be a -custom target or :prop_tgt:`FRAMEWORK` target. +files within those directories. The acceptable types include: + +``HEADERS`` + + Sources intended to be used via a language's ``#include`` mechanism. + +``CXX_MODULES`` + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + + Sources which contain C++ interface module or partition units (i.e., those + using the ``export`` keyword). This file set type may not have an + ``INTERFACE`` scope except on ``IMPORTED`` targets. + +``CXX_MODULE_HEADER_UNITS`` + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + + C++ header sources which may be imported by other C++ source code. This file + set type may not have an ``INTERFACE`` scope except on ``IMPORTED`` targets. + +The optional default file sets are named after their type. The target may not +be a custom target or :prop_tgt:`FRAMEWORK` target. Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets @@ -93,16 +117,17 @@ Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, o The name of the file set to create or add to. It must contain only letters, numbers and underscores. Names starting with a capital letter are reserved - for built-in file sets predefined by CMake. The only predefined set name is - ``HEADERS``. All other set names must not start with a capital letter or + for built-in file sets predefined by CMake. The only predefined set names + are those matching the acceptable types. All other set names must not start + with a capital letter or underscore. ``TYPE <type>`` - Every file set is associated with a particular type of file. ``HEADERS`` - is currently the only defined type and it is an error to specify anything - else. As a special case, if the name of the file set is ``HEADERS``, the - type does not need to be specified and the ``TYPE <type>`` arguments can be + Every file set is associated with a particular type of file. Only types + specified above may be used and it is an error to specify anything else. As + a special case, if the name of the file set is one of the types, the type + does not need to be specified and the ``TYPE <type>`` arguments can be omitted. For all other file set names, ``TYPE`` is required. ``BASE_DIRS <dirs>...`` @@ -134,6 +159,8 @@ Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, o The following target properties are set by ``target_sources(FILE_SET)``, but they should not generally be manipulated directly: +For file sets of type ``HEADERS``: + * :prop_tgt:`HEADER_SETS` * :prop_tgt:`INTERFACE_HEADER_SETS` * :prop_tgt:`HEADER_SET` @@ -141,17 +168,37 @@ but they should not generally be manipulated directly: * :prop_tgt:`HEADER_DIRS` * :prop_tgt:`HEADER_DIRS_<NAME>` +For file sets of type ``CXX_MODULES``: + +* :prop_tgt:`CXX_MODULE_SETS` +* :prop_tgt:`INTERFACE_CXX_MODULE_SETS` +* :prop_tgt:`CXX_MODULE_SET` +* :prop_tgt:`CXX_MODULE_SET_<NAME>` +* :prop_tgt:`CXX_MODULE_DIRS` +* :prop_tgt:`CXX_MODULE_DIRS_<NAME>` + +For file sets of type ``CXX_MODULE_HEADER_UNITS``: + +* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` +* :prop_tgt:`INTERFACE_CXX_MODULE_HEADER_UNIT_SETS` +* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` +* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>` +* :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS` +* :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS_<NAME>` + Target properties related to include directories are also modified by ``target_sources(FILE_SET)`` as follows: :prop_tgt:`INCLUDE_DIRECTORIES` - If the ``TYPE`` is ``HEADERS``, and the scope of the file set is ``PRIVATE`` - or ``PUBLIC``, all of the ``BASE_DIRS`` of the file set are wrapped in - :genex:`$<BUILD_INTERFACE>` and appended to this property. + If the ``TYPE`` is ``HEADERS`` or ``CXX_MODULE_HEADER_UNITS``, and the scope + of the file set is ``PRIVATE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of + the file set are wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this + property. :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` - If the ``TYPE`` is ``HEADERS``, and the scope of the file set is - ``INTERFACE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of the file set are - wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this property. + If the ``TYPE`` is ``HEADERS`` or ``CXX_MODULE_HEADER_UNITS``, and the scope + of the file set is ``INTERFACE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of + the file set are wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this + property. diff --git a/Help/cpack_gen/freebsd.rst b/Help/cpack_gen/freebsd.rst index f429bc5..faf8c74 100644 --- a/Help/cpack_gen/freebsd.rst +++ b/Help/cpack_gen/freebsd.rst @@ -62,8 +62,6 @@ the RPM information (e.g. package license). - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` (this is always set by CPack itself, if nothing else sets it explicitly). - - :variable:`PROJECT_DESCRIPTION` (this can be set with the DESCRIPTION - parameter for :command:`project`). .. variable:: CPACK_FREEBSD_PACKAGE_DESCRIPTION @@ -75,6 +73,10 @@ the RPM information (e.g. package license). - :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` (this may be set already for Debian packaging, so it is used as a fallback). + - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` (this is always set + by CPack itself, if nothing else sets it explicitly). + - :variable:`PROJECT_DESCRIPTION` (this can be set with the DESCRIPTION + parameter for :command:`project`). .. variable:: CPACK_FREEBSD_PACKAGE_WWW @@ -85,12 +87,12 @@ the RPM information (e.g. package license). * Mandatory: YES * Default: - - :variable:`CMAKE_PROJECT_HOMEPAGE_URL`, or if that is not set, - :variable:`CPACK_DEBIAN_PACKAGE_HOMEPAGE` (this may be set already + - :variable:`CPACK_PACKAGE_HOMEPAGE_URL`, or if that is not set, + - :variable:`CPACK_DEBIAN_PACKAGE_HOMEPAGE` (this may be set already for Debian packaging, so it is used as a fallback). .. versionadded:: 3.12 - The ``CMAKE_PROJECT_HOMEPAGE_URL`` variable. + The ``CPACK_PACKAGE_HOMEPAGE_URL`` variable. .. variable:: CPACK_FREEBSD_PACKAGE_LICENSE diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index 7638d22..b8d681c 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -7,6 +7,23 @@ See documentation on `CMake Development`_ for more information. .. _`CMake Development`: README.rst +Features are gated behind ``CMAKE_EXPERIMENTAL_`` variables which must be set +to specific values in order to enable their gated behaviors. Note that the +specific values will change over time to reinforce their experimental nature. +When used, a warning will be generated to indicate that an experimental +feature is in use and that the affected behavior in the project is not part of +CMake's stability guarantees. + +C++20 Module APIs +================= + +Variable: ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` +Value: ``3c375311-a3c9-4396-a187-3227ef642046`` + +In order to support C++20 modules, there are a number of behaviors that have +CMake APIs to provide the required features to build and export them from a +project. + C++20 Module Dependencies ========================= @@ -40,11 +57,6 @@ dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder. The ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT`` file may be set to ``msvc`` for scandep rules which use ``msvc``-style dependency reporting. -For tools which need to know the file set the source belongs to, the -``CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_<FILE_SET_TYPE>`` flag may -be provided so that different source types can be distinguished prior to -scanning. - The module dependencies should be written in the format described by the `P1689r4`_ paper. diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 25e581a..9949772 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -11,40 +11,189 @@ Introduction ============ Generator expressions are evaluated during build system generation to produce -information specific to each build configuration. +information specific to each build configuration. They have the form +``$<...>``. For example: + +.. code-block:: cmake + + target_include_directories(tgt PRIVATE /opt/include/$<CXX_COMPILER_ID>) + +This would expand to ``/opt/include/GNU``, ``/opt/include/Clang``, etc. +depending on the C++ compiler used. Generator expressions are allowed in the context of many target properties, such as :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_DEFINITIONS` and others. They may also be used when using commands to populate those properties, such as :command:`target_link_libraries`, :command:`target_include_directories`, :command:`target_compile_definitions` -and others. +and others. They enable conditional linking, conditional definitions used when +compiling, conditional include directories, and more. The conditions may be +based on the build configuration, target properties, platform information, +or any other queryable information. -They enable conditional linking, conditional definitions used when compiling, -conditional include directories, and more. The conditions may be based on -the build configuration, target properties, platform information or any other -queryable information. +Generator expressions can be nested: -Generator expressions have the form ``$<...>``. To avoid confusion, this page -deviates from most of the CMake documentation in that it omits angular brackets -``<...>`` around placeholders like ``condition``, ``string``, ``target``, -among others. +.. code-block:: cmake -Generator expressions can be nested, as shown in most of the examples below. + target_compile_definitions(tgt PRIVATE + $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,4.2.0>:OLD_COMPILER> + ) -.. _`Boolean Generator Expressions`: +The above would expand to ``OLD_COMPILER`` if the +:variable:`CMAKE_CXX_COMPILER_VERSION <CMAKE_<LANG>_COMPILER_VERSION>` is less +than 4.2.0. -Boolean Generator Expressions -============================= +Whitespace And Quoting +====================== -Boolean expressions evaluate to either ``0`` or ``1``. -They are typically used to construct the condition in a :ref:`conditional -generator expression<Conditional Generator Expressions>`. +Generator expressions are typically parsed after command arguments. +If a generator expression contains spaces, new lines, semicolons or +other characters that may be interpreted as command argument separators, +the whole expression should be surrounded by quotes when passed to a +command. Failure to do so may result in the expression being split and +it may no longer be recognized as a generator expression. -Available boolean expressions are: +When using :command:`add_custom_command` or :command:`add_custom_target`, +use the ``VERBATIM`` and ``COMMAND_EXPAND_LISTS`` options to obtain robust +argument splitting and quoting. -Logical Operators ------------------ +.. code-block:: cmake + + # WRONG: Embedded space will be treated as an argument separator. + # This ends up not being seen as a generator expression at all. + add_custom_target(run_some_tool + COMMAND some_tool -I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I> + VERBATIM + ) + +.. code-block:: cmake + + # Better, but still not robust. Quotes prevent the space from splitting the + # expression. However, the tool will receive the expanded value as a single + # argument. + add_custom_target(run_some_tool + COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>" + VERBATIM + ) + +.. code-block:: cmake + + # Nearly correct. Using a semicolon to separate arguments and adding the + # COMMAND_EXPAND_LISTS option means that paths with spaces will be handled + # correctly. Quoting the whole expression ensures it is seen as a generator + # expression. But if the target property is empty, we will get a bare -I + # with nothing after it. + add_custom_target(run_some_tool + COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>,;-I>" + COMMAND_EXPAND_LISTS + VERBATIM + ) + +Using variables to build up a more complex generator expression is also a +good way to reduce errors and improve readability. The above example can be +improved further like so: + +.. code-block:: cmake + + # The $<BOOL:...> check prevents adding anything if the property is empty, + # assuming the property value cannot be one of CMake's false constants. + set(prop "$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>") + add_custom_target(run_some_tool + COMMAND some_tool "$<$<BOOL:${prop}>:-I$<JOIN:${prop},;-I>>" + COMMAND_EXPAND_LISTS + VERBATIM + ) + +A common mistake is to try to split a generator expression across multiple +lines with indenting: + +.. code-block:: cmake + + # WRONG: New lines and spaces all treated as argument separators, so the + # generator expression is split and not recognized correctly. + target_compile_definitions(tgt PRIVATE + $<$<AND: + $<CXX_COMPILER_ID:GNU>, + $<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5> + >:HAVE_5_OR_LATER> + ) + +Again, use helper variables with well-chosen names to build up a readable +expression instead: + +.. code-block:: cmake + + set(is_gnu "$<CXX_COMPILER_ID:GNU>") + set(v5_or_later "$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>") + set(meet_requirements "$<AND:${is_gnu},${v5_or_later}>") + target_compile_definitions(tgt PRIVATE + "$<${meet_requirements}:HAVE_5_OR_LATER>" + ) + +Debugging +========= + +Since generator expressions are evaluated during generation of the buildsystem, +and not during processing of ``CMakeLists.txt`` files, it is not possible to +inspect their result with the :command:`message()` command. One possible way +to generate debug messages is to add a custom target: + +.. code-block:: cmake + + add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<...>") + +After running ``cmake``, you can then build the ``genexdebug`` target to print +the result of the ``$<...>`` expression (i.e. run the command +``cmake --build ... --target genexdebug``). + +Another way is to write debug messages to a file with :command:`file(GENERATE)`: + +.. code-block:: cmake + + file(GENERATE OUTPUT filename CONTENT "$<...>") + +Generator Expression Reference +============================== + +.. note:: + + This reference deviates from most of the CMake documentation in that it + omits angular brackets ``<...>`` around placeholders like ``condition``, + ``string``, ``target``, etc. This is to prevent an opportunity for those + placeholders to be misinterpreted as generator expressions. + +.. _`Conditional Generator Expressions`: + +Conditional Expressions +----------------------- + +A fundamental category of generator expressions relates to conditional logic. +Two forms of conditional generator expressions are supported: + +.. genex:: $<condition:true_string> + + Evaluates to ``true_string`` if ``condition`` is ``1``, or an empty string + if ``condition`` evaluates to ``0``. Any other value for ``condition`` + results in an error. + +.. genex:: $<IF:condition,true_string,false_string> + + .. versionadded:: 3.8 + + Evaluates to ``true_string`` if ``condition`` is ``1``, or ``false_string`` + if ``condition`` is ``0``. Any other value for ``condition`` results in an + error. + +Typically, the ``condition`` is itself a generator expression. For instance, +the following expression expands to ``DEBUG_MODE`` when the ``Debug`` +configuration is used, and the empty string for all other configurations: + +.. code-block:: cmake + + $<$<CONFIG:Debug>:DEBUG_MODE> + +Boolean-like ``condition`` values other than ``1`` or ``0`` can be handled +by wrapping them with the ``$<BOOL:...>`` generator expression: .. genex:: $<BOOL:string> @@ -58,49 +207,71 @@ Logical Operators Otherwise evaluates to ``1``. +The ``$<BOOL:...>`` generator expression is often used when a ``condition`` +is provided by a CMake variable: + +.. code-block:: cmake + + $<$<BOOL:${HAVE_SOME_FEATURE}>:-DENABLE_SOME_FEATURE> + + +.. _`Boolean Generator Expressions`: + +Logical Operators +----------------- + +The common boolean logic operators are supported: + .. genex:: $<AND:conditions> - where ``conditions`` is a comma-separated list of boolean expressions. - Evaluates to ``1`` if all conditions are ``1``. - Otherwise evaluates to ``0``. + where ``conditions`` is a comma-separated list of boolean expressions, + all of which must evaluate to either ``1`` or ``0``. The whole expression + evaluates to ``1`` if all conditions are ``1``. If any condition is ``0``, + the whole expression evaluates to ``0``. .. genex:: $<OR:conditions> where ``conditions`` is a comma-separated list of boolean expressions. - Evaluates to ``1`` if at least one of the conditions is ``1``. - Otherwise evaluates to ``0``. + all of which must evaluate to either ``1`` or ``0``. The whole expression + evaluates to ``1`` if at least one of the ``conditions`` is ``1``. If all + ``conditions`` evaluate to ``0``, the whole expression evaluates to ``0``. .. genex:: $<NOT:condition> + ``condition`` must be ``0`` or ``1``. The result of the expression is ``0`` if ``condition`` is ``1``, else ``1``. +.. _`Comparison Expressions`: + +Primary Comparison Expressions +------------------------------ + +CMake supports a variety of generator expressions that compare things. +This section covers the primary and most widely used comparison types. +Other more specific comparison types are documented in their own separate +sections further below. + String Comparisons ------------------- +^^^^^^^^^^^^^^^^^^ .. genex:: $<STREQUAL:string1,string2> ``1`` if ``string1`` and ``string2`` are equal, else ``0``. The comparison is case-sensitive. For a case-insensitive comparison, combine with a :ref:`string transforming generator expression - <String Transforming Generator Expressions>`, + <String Transforming Generator Expressions>`. For example, the following + evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc. .. code-block:: cmake - $<STREQUAL:$<UPPER_CASE:${foo}>,"BAR"> # "1" if ${foo} is any of "BAR", "Bar", "bar", ... + $<STREQUAL:$<UPPER_CASE:${foo}>,BAR> .. genex:: $<EQUAL:value1,value2> ``1`` if ``value1`` and ``value2`` are numerically equal, else ``0``. -.. genex:: $<IN_LIST:string,list> - - .. versionadded:: 3.12 - - ``1`` if ``string`` is member of the semicolon-separated ``list``, else ``0``. - Uses case-sensitive comparisons. - Version Comparisons -------------------- +^^^^^^^^^^^^^^^^^^^ .. genex:: $<VERSION_LESS:v1,v2> @@ -126,9 +297,70 @@ Version Comparisons ``1`` if ``v1`` is a version greater than or equal to ``v2``, else ``0``. -Path Comparisons +.. _`String Transforming Generator Expressions`: + +String Transformations +---------------------- + +.. genex:: $<LOWER_CASE:string> + + Content of ``string`` converted to lower case. + +.. genex:: $<UPPER_CASE:string> + + Content of ``string`` converted to upper case. + +.. genex:: $<MAKE_C_IDENTIFIER:...> + + Content of ``...`` converted to a C identifier. The conversion follows the + same behavior as :command:`string(MAKE_C_IDENTIFIER)`. + +List Expressions ---------------- +.. genex:: $<IN_LIST:string,list> + + .. versionadded:: 3.12 + + ``1`` if ``string`` is an item in the semicolon-separated ``list``, else ``0``. + It uses case-sensitive comparisons. + +.. genex:: $<JOIN:list,string> + + Joins the list with the content of ``string`` inserted between each item. + +.. genex:: $<REMOVE_DUPLICATES:list> + + .. versionadded:: 3.15 + + Removes duplicated items in the given ``list``. The relative order of items + is preserved, but if duplicates are encountered, only the first instance is + preserved. + +.. genex:: $<FILTER:list,INCLUDE|EXCLUDE,regex> + + .. versionadded:: 3.15 + + Includes or removes items from ``list`` that match the regular expression + ``regex``. + +Path Expressions +---------------- + +Most of the expressions in this section are closely associated with the +:command:`cmake_path` command, providing the same capabilities, but in +the form of a generator expression. + +For all generator expressions in this section, paths are expected to be in +cmake-style format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` +generator expression can be used to convert a native path to a cmake-style +one. + +.. _GenEx Path Comparisons: + +Path Comparisons +^^^^^^^^^^^^^^^^ + .. genex:: $<PATH_EQUAL:path1,path2> .. versionadded:: 3.24 @@ -142,18 +374,11 @@ Path Comparisons .. _GenEx Path Queries: Path Queries ------------- +^^^^^^^^^^^^ -The ``$<PATH>`` generator expression offers the same capabilities as the -:command:`cmake_path` command, for the :ref:`Query <Path Query>` options. - -For all ``$<PATH>`` generator expressions, paths are expected in cmake-style -format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator -expression can be used to convert a native path to a cmake-style one. - -The ``$<PATH>`` generator expression can also be used for path -:ref:`Decomposition <GenEx Path Decomposition>` and -:ref:`Transformations <GenEx Path Transformations>`. +These expressions provide the generation-time capabilities equivalent to the +:ref:`Query <Path Query>` options of the :command:`cmake_path` command. +All paths are expected to be in cmake-style format. .. genex:: $<PATH:HAS_*,path> @@ -199,211 +424,421 @@ The ``$<PATH>`` generator expression can also be used for path .. versionadded:: 3.24 - Returns ``1`` if ``path`` is the prefix of ``input``,``0`` otherwise. + Returns ``1`` if ``path`` is the prefix of ``input``, ``0`` otherwise. When the ``NORMALIZE`` option is specified, ``path`` and ``input`` are :ref:`normalized <Normalization>` before the check. -Variable Queries ----------------- +.. _GenEx Path Decomposition: -.. genex:: $<TARGET_EXISTS:target> +Path Decomposition +^^^^^^^^^^^^^^^^^^ - .. versionadded:: 3.12 +These expressions provide the generation-time capabilities equivalent to the +:ref:`Decomposition <Path Decomposition>` options of the :command:`cmake_path` +command. All paths are expected to be in cmake-style format. - ``1`` if ``target`` exists, else ``0``. +.. genex:: $<PATH:GET_*,...> -.. genex:: $<CONFIG:cfgs> + .. versionadded:: 3.24 - ``1`` if config is any one of the entries in comma-separated list - ``cfgs``, else ``0``. This is a case-insensitive comparison. The mapping in - :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by this - expression when it is evaluated on a property on an :prop_tgt:`IMPORTED` - target. + The following operations retrieve a different component or group of + components from a path. See :ref:`Path Structure And Terminology` for the + meaning of each path component. -.. genex:: $<PLATFORM_ID:platform_ids> + :: - where ``platform_ids`` is a comma-separated list. - ``1`` if the CMake's platform id matches any one of the entries in - ``platform_ids``, otherwise ``0``. - See also the :variable:`CMAKE_SYSTEM_NAME` variable. + $<PATH:GET_ROOT_NAME,path> + $<PATH:GET_ROOT_DIRECTORY,path> + $<PATH:GET_ROOT_PATH,path> + $<PATH:GET_FILENAME,path> + $<PATH:GET_EXTENSION[,LAST_ONLY],path> + $<PATH:GET_STEM[,LAST_ONLY],path> + $<PATH:GET_RELATIVE_PART,path> + $<PATH:GET_PARENT_PATH,path> -.. genex:: $<C_COMPILER_ID:compiler_ids> + If a requested component is not present in the path, an empty string is + returned. - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the C compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +.. _GenEx Path Transformations: -.. genex:: $<CXX_COMPILER_ID:compiler_ids> +Path Transformations +^^^^^^^^^^^^^^^^^^^^ - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the CXX compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +These expressions provide the generation-time capabilities equivalent to the +:ref:`Modification <Path Modification>` and :ref:`Generation <Path Generation>` +options of the :command:`cmake_path` command. All paths are expected to be +in cmake-style format. -.. genex:: $<CUDA_COMPILER_ID:compiler_ids> +.. _GenEx PATH-CMAKE_PATH: - .. versionadded:: 3.15 +.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path> - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the CUDA compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + .. versionadded:: 3.24 -.. genex:: $<OBJC_COMPILER_ID:compiler_ids> + Returns ``path``. If ``path`` is a native path, it is converted into a + cmake-style path with forward-slashes (``/``). On Windows, the long filename + marker is taken into account. - .. versionadded:: 3.16 + When the ``NORMALIZE`` option is specified, the path is :ref:`normalized + <Normalization>` after the conversion. - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the Objective-C compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +.. genex:: $<PATH:APPEND,path,input,...> -.. genex:: $<OBJCXX_COMPILER_ID:compiler_ids> + .. versionadded:: 3.24 - .. versionadded:: 3.16 + Returns all the ``input`` arguments appended to ``path`` using ``/`` as the + ``directory-separator``. Depending on the ``input``, the value of ``path`` + may be discarded. - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the Objective-C++ compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + See :ref:`cmake_path(APPEND) <APPEND>` for more details. -.. genex:: $<Fortran_COMPILER_ID:compiler_ids> +.. genex:: $<PATH:REMOVE_FILENAME,path> - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the Fortran compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + .. versionadded:: 3.24 -.. genex:: $<HIP_COMPILER_ID:compiler_ids> + Returns ``path`` with filename component (as returned by + ``$<PATH:GET_FILENAME>``) removed. After removal, any trailing + ``directory-separator`` is left alone, if present. - .. versionadded:: 3.21 + See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details. - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the HIP compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +.. genex:: $<PATH:REPLACE_FILENAME,path,input> -.. genex:: $<ISPC_COMPILER_ID:compiler_ids> + .. versionadded:: 3.24 - .. versionadded:: 3.19 + Returns ``path`` with the filename component replaced by ``input``. If + ``path`` has no filename component (i.e. ``$<PATH:HAS_FILENAME>`` returns + ``0``), ``path`` is unchanged. - where ``compiler_ids`` is a comma-separated list. - ``1`` if the CMake's compiler id of the ISPC compiler matches any one - of the entries in ``compiler_ids``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details. + +.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path> + + .. versionadded:: 3.24 + + Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` removed, if any. + + See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details. + +.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path> + + .. versionadded:: 3.24 + + Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` replaced by + ``input``, if any. + + See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details. + +.. genex:: $<PATH:NORMAL_PATH,path> + + .. versionadded:: 3.24 + + Returns ``path`` normalized according to the steps described in + :ref:`Normalization`. + +.. genex:: $<PATH:RELATIVE_PATH,path,base_directory> + + .. versionadded:: 3.24 + + Returns ``path``, modified to make it relative to the ``base_directory`` + argument. + + See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more + details. + +.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory> + + .. versionadded:: 3.24 + + Returns ``path`` as absolute. If ``path`` is a relative path + (``$<PATH:IS_RELATIVE>`` returns ``1``), it is evaluated relative to the + given base directory specified by ``base_directory`` argument. + + When the ``NORMALIZE`` option is specified, the path is + :ref:`normalized <Normalization>` after the path computation. + + See :ref:`cmake_path(ABSOLUTE_PATH) <ABSOLUTE_PATH>` for more details. + +Shell Paths +^^^^^^^^^^^ + +.. genex:: $<SHELL_PATH:...> + + .. versionadded:: 3.4 + + Content of ``...`` converted to shell path style. For example, slashes are + converted to backslashes in Windows shells and drive letters are converted + to posix paths in MSYS shells. The ``...`` must be an absolute path. + + .. versionadded:: 3.14 + The ``...`` may be a :ref:`semicolon-separated list <CMake Language Lists>` + of paths, in which case each path is converted individually and a result + list is generated using the shell path separator (``:`` on POSIX and + ``;`` on Windows). Be sure to enclose the argument containing this genex + in double quotes in CMake source code so that ``;`` does not split arguments. + +Configuration Expressions +------------------------- + +.. genex:: $<CONFIG> + + Configuration name. Use this instead of the deprecated :genex:`CONFIGURATION` + generator expression. + +.. genex:: $<CONFIG:cfgs> + + ``1`` if config is any one of the entries in comma-separated list + ``cfgs``, else ``0``. This is a case-insensitive comparison. The mapping in + :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by this + expression when it is evaluated on a property of an :prop_tgt:`IMPORTED` + target. + +.. genex:: $<OUTPUT_CONFIG:...> + + .. versionadded:: 3.20 + + Only valid in :command:`add_custom_command` and :command:`add_custom_target` + as the outer-most generator expression in an argument. + With the :generator:`Ninja Multi-Config` generator, generator expressions + in ``...`` are evaluated using the custom command's "output config". + With other generators, the content of ``...`` is evaluated normally. + +.. genex:: $<COMMAND_CONFIG:...> + + .. versionadded:: 3.20 + + Only valid in :command:`add_custom_command` and :command:`add_custom_target` + as the outer-most generator expression in an argument. + With the :generator:`Ninja Multi-Config` generator, generator expressions + in ``...`` are evaluated using the custom command's "command config". + With other generators, the content of ``...`` is evaluated normally. + +Toolchain And Language Expressions +---------------------------------- + +Platform +^^^^^^^^ + +.. genex:: $<PLATFORM_ID> + + The current system's CMake platform id. + See also the :variable:`CMAKE_SYSTEM_NAME` variable. + +.. genex:: $<PLATFORM_ID:platform_ids> + + where ``platform_ids`` is a comma-separated list. + ``1`` if CMake's platform id matches any one of the entries in + ``platform_ids``, otherwise ``0``. + See also the :variable:`CMAKE_SYSTEM_NAME` variable. + +Compiler Version +^^^^^^^^^^^^^^^^ + +See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable, which is +closely related to the expressions in this sub-section. + +.. genex:: $<C_COMPILER_VERSION> + + The version of the C compiler used. .. genex:: $<C_COMPILER_VERSION:version> ``1`` if the version of the C compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<CXX_COMPILER_VERSION> + + The version of the CXX compiler used. .. genex:: $<CXX_COMPILER_VERSION:version> ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<CUDA_COMPILER_VERSION> + + .. versionadded:: 3.15 + + The version of the CUDA compiler used. .. genex:: $<CUDA_COMPILER_VERSION:version> .. versionadded:: 3.15 ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<OBJC_COMPILER_VERSION> + + .. versionadded:: 3.16 + + The version of the OBJC compiler used. .. genex:: $<OBJC_COMPILER_VERSION:version> .. versionadded:: 3.16 ``1`` if the version of the OBJC compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<OBJCXX_COMPILER_VERSION> + + .. versionadded:: 3.16 + + The version of the OBJCXX compiler used. .. genex:: $<OBJCXX_COMPILER_VERSION:version> .. versionadded:: 3.16 ``1`` if the version of the OBJCXX compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<Fortran_COMPILER_VERSION> + + The version of the Fortran compiler used. .. genex:: $<Fortran_COMPILER_VERSION:version> ``1`` if the version of the Fortran compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<HIP_COMPILER_VERSION> + + .. versionadded:: 3.21 + + The version of the HIP compiler used. .. genex:: $<HIP_COMPILER_VERSION:version> .. versionadded:: 3.21 ``1`` if the version of the HIP compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + +.. genex:: $<ISPC_COMPILER_VERSION> + + .. versionadded:: 3.19 + + The version of the ISPC compiler used. .. genex:: $<ISPC_COMPILER_VERSION:version> .. versionadded:: 3.19 ``1`` if the version of the ISPC compiler matches ``version``, otherwise ``0``. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. -.. genex:: $<TARGET_POLICY:policy> +Compiler Language And ID +^^^^^^^^^^^^^^^^^^^^^^^^ - ``1`` if the ``policy`` was NEW when the 'head' target was created, - else ``0``. If the ``policy`` was not set, the warning message for the policy - will be emitted. This generator expression only works for a subset of - policies. +See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable, which is closely +related to most of the expressions in this sub-section. -.. genex:: $<COMPILE_FEATURES:features> +.. genex:: $<C_COMPILER_ID> - .. versionadded:: 3.1 + CMake's compiler id of the C compiler used. - where ``features`` is a comma-spearated list. - Evaluates to ``1`` if all of the ``features`` are available for the 'head' - target, and ``0`` otherwise. If this expression is used while evaluating - the link implementation of a target and if any dependency transitively - increases the required :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD` - for the 'head' target, an error is reported. See the - :manual:`cmake-compile-features(7)` manual for information on - compile features and a list of supported compilers. +.. genex:: $<C_COMPILER_ID:compiler_ids> -.. _`Boolean COMPILE_LANGUAGE Generator Expression`: + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the C compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. -.. genex:: $<COMPILE_LANG_AND_ID:language,compiler_ids> +.. genex:: $<CXX_COMPILER_ID> + + CMake's compiler id of the CXX compiler used. + +.. genex:: $<CXX_COMPILER_ID:compiler_ids> + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the CXX compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<CUDA_COMPILER_ID> .. versionadded:: 3.15 - ``1`` when the language used for compilation unit matches ``language`` and - the CMake's compiler id of the language compiler matches any one of the - entries in ``compiler_ids``, otherwise ``0``. This expression is a short form - for the combination of ``$<COMPILE_LANGUAGE:language>`` and - ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify - compile options, compile definitions, and include directories for source files of a - particular language and compiler combination in a target. For example: + CMake's compiler id of the CUDA compiler used. - .. code-block:: cmake +.. genex:: $<CUDA_COMPILER_ID:compiler_ids> - add_executable(myapp main.cpp foo.c bar.cpp zot.cu) - target_compile_definitions(myapp - PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:COMPILING_CXX_WITH_CLANG> - $<$<COMPILE_LANG_AND_ID:CXX,Intel>:COMPILING_CXX_WITH_INTEL> - $<$<COMPILE_LANG_AND_ID:C,Clang>:COMPILING_C_WITH_CLANG> - ) + .. versionadded:: 3.15 - This specifies the use of different compile definitions based on both - the compiler id and compilation language. This example will have a - ``COMPILING_CXX_WITH_CLANG`` compile definition when Clang is the CXX - compiler, and ``COMPILING_CXX_WITH_INTEL`` when Intel is the CXX compiler. - Likewise when the C compiler is Clang it will only see the ``COMPILING_C_WITH_CLANG`` - definition. + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the CUDA compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. - Without the ``COMPILE_LANG_AND_ID`` generator expression the same logic - would be expressed as: +.. genex:: $<OBJC_COMPILER_ID> - .. code-block:: cmake + .. versionadded:: 3.16 - target_compile_definitions(myapp - PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:COMPILING_CXX_WITH_CLANG> - $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Intel>>:COMPILING_CXX_WITH_INTEL> - $<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:Clang>>:COMPILING_C_WITH_CLANG> - ) + CMake's compiler id of the OBJC compiler used. + +.. genex:: $<OBJC_COMPILER_ID:compiler_ids> + + .. versionadded:: 3.16 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the Objective-C compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<OBJCXX_COMPILER_ID> + + .. versionadded:: 3.16 + + CMake's compiler id of the OBJCXX compiler used. + +.. genex:: $<OBJCXX_COMPILER_ID:compiler_ids> + + .. versionadded:: 3.16 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the Objective-C++ compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<Fortran_COMPILER_ID> + + CMake's compiler id of the Fortran compiler used. + +.. genex:: $<Fortran_COMPILER_ID:compiler_ids> + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the Fortran compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<HIP_COMPILER_ID> + + .. versionadded:: 3.21 + + CMake's compiler id of the HIP compiler used. + +.. genex:: $<HIP_COMPILER_ID:compiler_ids> + + .. versionadded:: 3.21 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the HIP compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<ISPC_COMPILER_ID> + + .. versionadded:: 3.19 + + CMake's compiler id of the ISPC compiler used. + +.. genex:: $<ISPC_COMPILER_ID:compiler_ids> + + .. versionadded:: 3.19 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler id of the ISPC compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<COMPILE_LANGUAGE> + + .. versionadded:: 3.3 + + The compile language of source files when evaluating compile options. + See :ref:`the related boolean expression + <Boolean COMPILE_LANGUAGE Generator Expression>` + ``$<COMPILE_LANGUAGE:language>`` + for notes about the portability of this generator expression. + +.. _`Boolean COMPILE_LANGUAGE Generator Expression`: .. genex:: $<COMPILE_LANGUAGE:languages> @@ -411,8 +846,8 @@ Variable Queries ``1`` when the language used for compilation unit matches any of the entries in ``languages``, otherwise ``0``. This expression may be used to specify - compile options, compile definitions, and include directories for source files of a - particular language in a target. For example: + compile options, compile definitions, and include directories for source + files of a particular language in a target. For example: .. code-block:: cmake @@ -451,49 +886,82 @@ Variable Queries add_executable(myapp main.cpp) target_link_libraries(myapp myapp_c myapp_cxx) -.. _`Boolean LINK_LANGUAGE Generator Expression`: - -.. genex:: $<LINK_LANG_AND_ID:language,compiler_ids> +.. genex:: $<COMPILE_LANG_AND_ID:language,compiler_ids> - .. versionadded:: 3.18 + .. versionadded:: 3.15 - ``1`` when the language used for link step matches ``language`` and the - CMake's compiler id of the language linker matches any one of the entries - in ``compiler_ids``, otherwise ``0``. This expression is a short form for the - combination of ``$<LINK_LANGUAGE:language>`` and + ``1`` when the language used for compilation unit matches ``language`` and + CMake's compiler id of the ``language`` compiler matches any one of the + entries in ``compiler_ids``, otherwise ``0``. This expression is a short form + for the combination of ``$<COMPILE_LANGUAGE:language>`` and ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify - link libraries, link options, link directories and link dependencies of a - particular language and linker combination in a target. For example: + compile options, compile definitions, and include directories for source + files of a particular language and compiler combination in a target. + For example: .. code-block:: cmake - add_library(libC_Clang ...) - add_library(libCXX_Clang ...) - add_library(libC_Intel ...) - add_library(libCXX_Intel ...) + add_executable(myapp main.cpp foo.c bar.cpp zot.cu) + target_compile_definitions(myapp + PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:COMPILING_CXX_WITH_CLANG> + $<$<COMPILE_LANG_AND_ID:CXX,Intel>:COMPILING_CXX_WITH_INTEL> + $<$<COMPILE_LANG_AND_ID:C,Clang>:COMPILING_C_WITH_CLANG> + ) - add_executable(myapp main.c) - if (CXX_CONFIG) - target_sources(myapp PRIVATE file.cxx) - endif() - target_link_libraries(myapp - PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang> - $<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang> - $<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel> - $<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>) + This specifies the use of different compile definitions based on both + the compiler id and compilation language. This example will have a + ``COMPILING_CXX_WITH_CLANG`` compile definition when Clang is the CXX + compiler, and ``COMPILING_CXX_WITH_INTEL`` when Intel is the CXX compiler. + Likewise, when the C compiler is Clang, it will only see the + ``COMPILING_C_WITH_CLANG`` definition. - This specifies the use of different link libraries based on both the - compiler id and link language. This example will have target ``libCXX_Clang`` - as link dependency when ``Clang`` or ``AppleClang`` is the ``CXX`` - linker, and ``libCXX_Intel`` when ``Intel`` is the ``CXX`` linker. - Likewise when the ``C`` linker is ``Clang`` or ``AppleClang``, target - ``libC_Clang`` will be added as link dependency and ``libC_Intel`` when - ``Intel`` is the ``C`` linker. + Without the ``COMPILE_LANG_AND_ID`` generator expression, the same logic + would be expressed as: - See :ref:`the note related to - <Constraints LINK_LANGUAGE Generator Expression>` - ``$<LINK_LANGUAGE:language>`` for constraints about the usage of this - generator expression. + .. code-block:: cmake + + target_compile_definitions(myapp + PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:COMPILING_CXX_WITH_CLANG> + $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Intel>>:COMPILING_CXX_WITH_INTEL> + $<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:Clang>>:COMPILING_C_WITH_CLANG> + ) + +Compile Features +^^^^^^^^^^^^^^^^ + +.. genex:: $<COMPILE_FEATURES:features> + + .. versionadded:: 3.1 + + where ``features`` is a comma-separated list. + Evaluates to ``1`` if all of the ``features`` are available for the 'head' + target, and ``0`` otherwise. If this expression is used while evaluating + the link implementation of a target and if any dependency transitively + increases the required :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD` + for the 'head' target, an error is reported. See the + :manual:`cmake-compile-features(7)` manual for information on + compile features and a list of supported compilers. + +Linker Language And ID +^^^^^^^^^^^^^^^^^^^^^^ + +.. genex:: $<LINK_LANGUAGE> + + .. versionadded:: 3.18 + + The link language of the target when evaluating link options. + See :ref:`the related boolean expression + <Boolean LINK_LANGUAGE Generator Expression>` ``$<LINK_LANGUAGE:languages>`` + for notes about the portability of this generator expression. + + .. note:: + + This generator expression is not supported by the link libraries + properties to avoid side-effects due to the double evaluation of + these properties. + + +.. _`Boolean LINK_LANGUAGE Generator Expression`: .. genex:: $<LINK_LANGUAGE:languages> @@ -509,7 +977,7 @@ Variable Queries add_library(api_C ...) add_library(api_CXX ...) add_library(api INTERFACE) - target_link_options(api INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c> + target_link_options(api INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c> $<$<LINK_LANGUAGE:CXX>:-opt_cxx>) target_link_libraries(api INTERFACE $<$<LINK_LANGUAGE:C>:api_C> $<$<LINK_LANGUAGE:CXX>:api_CXX>) @@ -560,485 +1028,391 @@ Variable Queries evaluation will give ``C`` as link language, so the second pass will correctly add target ``libother`` as link dependency. -String-Valued Generator Expressions -=================================== - -These expressions expand to some string. -For example, - -.. code-block:: cmake - - include_directories(/usr/include/$<CXX_COMPILER_ID>/) - -expands to ``/usr/include/GNU/`` or ``/usr/include/Clang/`` etc, depending on -the compiler identifier. - -String-valued expressions may also be combined with other expressions. -Here an example for a string-valued expression within a boolean expressions -within a conditional expression: - -.. code-block:: cmake - - $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,4.2.0>:OLD_COMPILER> - -expands to ``OLD_COMPILER`` if the -:variable:`CMAKE_CXX_COMPILER_VERSION <CMAKE_<LANG>_COMPILER_VERSION>` is less -than 4.2.0. - -And here two nested string-valued expressions: - -.. code-block:: cmake - - -I$<JOIN:$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>, -I> - -generates a string of the entries in the :prop_tgt:`INCLUDE_DIRECTORIES` target -property with each entry preceded by ``-I``. - -Expanding on the previous example, if one first wants to check if the -``INCLUDE_DIRECTORIES`` property is non-empty, then it is advisable to -introduce a helper variable to keep the code readable: - -.. code-block:: cmake - - set(prop "$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>") # helper variable - $<$<BOOL:${prop}>:-I$<JOIN:${prop}, -I>> - -The following string-valued generator expressions are available: - -Escaped Characters ------------------- - -String literals to escape the special meaning a character would otherwise have: - -.. genex:: $<ANGLE-R> - - A literal ``>``. Used for example to compare strings that contain a ``>``. - -.. genex:: $<COMMA> - - A literal ``,``. Used for example to compare strings which contain a ``,``. - -.. genex:: $<SEMICOLON> - - A literal ``;``. Used to prevent list expansion on an argument with ``;``. - -.. _`Conditional Generator Expressions`: - -Conditional Expressions ------------------------ - -Conditional generator expressions depend on a boolean condition -that must be ``0`` or ``1``. - -.. genex:: $<condition:true_string> - - Evaluates to ``true_string`` if ``condition`` is ``1``. - Otherwise evaluates to the empty string. - -.. genex:: $<IF:condition,true_string,false_string> - - .. versionadded:: 3.8 - - Evaluates to ``true_string`` if ``condition`` is ``1``. - Otherwise evaluates to ``false_string``. - -Typically, the ``condition`` is a :ref:`boolean generator expression -<Boolean Generator Expressions>`. For instance, - -.. code-block:: cmake - - $<$<CONFIG:Debug>:DEBUG_MODE> - -expands to ``DEBUG_MODE`` when the ``Debug`` configuration is used, and -otherwise expands to the empty string. - -.. _`String Transforming Generator Expressions`: - -String Transformations ----------------------- - -.. genex:: $<JOIN:list,string> - - Joins the list with the content of ``string``. - -.. genex:: $<REMOVE_DUPLICATES:list> - - .. versionadded:: 3.15 - - Removes duplicated items in the given ``list``. The relative order of items - is preserved, but if duplicates are encountered, only the first instance is - preserved. - -.. genex:: $<FILTER:list,INCLUDE|EXCLUDE,regex> - - .. versionadded:: 3.15 - - Includes or removes items from ``list`` that match the regular expression ``regex``. - -.. genex:: $<LOWER_CASE:string> - - Content of ``string`` converted to lower case. - -.. genex:: $<UPPER_CASE:string> - - Content of ``string`` converted to upper case. - -.. genex:: $<GENEX_EVAL:expr> - - .. versionadded:: 3.12 - - Content of ``expr`` evaluated as a generator expression in the current - context. This enables consumption of generator expressions whose - evaluation results itself in generator expressions. - -.. genex:: $<TARGET_GENEX_EVAL:tgt,expr> - - .. versionadded:: 3.12 - - Content of ``expr`` evaluated as a generator expression in the context of - ``tgt`` target. This enables consumption of custom target properties that - themselves contain generator expressions. - - Having the capability to evaluate generator expressions is very useful when - you want to manage custom properties supporting generator expressions. - For example: - - .. code-block:: cmake - - add_library(foo ...) - - set_property(TARGET foo PROPERTY - CUSTOM_KEYS $<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS> - ) - - add_custom_target(printFooKeys - COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_PROPERTY:foo,CUSTOM_KEYS> - ) +.. genex:: $<LINK_LANG_AND_ID:language,compiler_ids> - This naive implementation of the ``printFooKeys`` custom command is wrong - because ``CUSTOM_KEYS`` target property is not evaluated and the content - is passed as is (i.e. ``$<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>``). + .. versionadded:: 3.18 - To have the expected result (i.e. ``FOO_EXTRA_THINGS`` if config is - ``Debug``), it is required to evaluate the output of - ``$<TARGET_PROPERTY:foo,CUSTOM_KEYS>``: + ``1`` when the language used for link step matches ``language`` and the + CMake's compiler id of the language linker matches any one of the entries + in ``compiler_ids``, otherwise ``0``. This expression is a short form for the + combination of ``$<LINK_LANGUAGE:language>`` and + ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify + link libraries, link options, link directories and link dependencies of a + particular language and linker combination in a target. For example: .. code-block:: cmake - add_custom_target(printFooKeys - COMMAND ${CMAKE_COMMAND} -E - echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>> - ) - -.. _GenEx Path Decomposition: - -Path Decomposition ------------------- - -The ``$<PATH>`` generator expression offers the same capabilities as the -:command:`cmake_path` command, for the -:ref:`Decomposition <Path Decomposition>` options. - -For all ``$<PATH>`` generator expressions, paths are expected in cmake-style -format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator -expression can be used to convert a native path to a cmake-style one. - -The ``$<PATH>`` generator expression can also be used for path -:ref:`Queries <GenEx Path Queries>` and -:ref:`Transformations <GenEx Path Transformations>`. - -.. genex:: $<PATH:GET_*,...> - - .. versionadded:: 3.24 - - The following operations retrieve a different component or group of - components from a path. See :ref:`Path Structure And Terminology` for the - meaning of each path component. - - :: - - $<PATH:GET_ROOT_NAME,path> - $<PATH:GET_ROOT_DIRECTORY,path> - $<PATH:GET_ROOT_PATH,path> - $<PATH:GET_FILENAME,path> - $<PATH:GET_EXTENSION[,LAST_ONLY],path> - $<PATH:GET_STEM[,LAST_ONLY],path> - $<PATH:GET_RELATIVE_PART,path> - $<PATH:GET_PARENT_PATH,path> - - If a requested component is not present in the path, an empty string is - returned. - -.. _GenEx Path Transformations: - -Path Transformations --------------------- + add_library(libC_Clang ...) + add_library(libCXX_Clang ...) + add_library(libC_Intel ...) + add_library(libCXX_Intel ...) -The ``$<PATH>`` generator expression offers the same capabilities as the -:command:`cmake_path` command, for the -:ref:`Modification <Path Modification>` and -:ref:`Generation <Path Generation>` options. + add_executable(myapp main.c) + if (CXX_CONFIG) + target_sources(myapp PRIVATE file.cxx) + endif() + target_link_libraries(myapp + PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang> + $<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang> + $<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel> + $<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>) -For all ``$<PATH>`` generator expressions, paths are expected in cmake-style -format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator -expression can be used to convert a native path to a cmake-style one. + This specifies the use of different link libraries based on both the + compiler id and link language. This example will have target ``libCXX_Clang`` + as link dependency when ``Clang`` or ``AppleClang`` is the ``CXX`` + linker, and ``libCXX_Intel`` when ``Intel`` is the ``CXX`` linker. + Likewise when the ``C`` linker is ``Clang`` or ``AppleClang``, target + ``libC_Clang`` will be added as link dependency and ``libC_Intel`` when + ``Intel`` is the ``C`` linker. -The ``$<PATH>`` generator expression can also be used for path -:ref:`Queries <GenEx Path Queries>` and -:ref:`Decomposition <GenEx Path Decomposition>`. + See :ref:`the note related to + <Constraints LINK_LANGUAGE Generator Expression>` + ``$<LINK_LANGUAGE:language>`` for constraints about the usage of this + generator expression. -.. _GenEx PATH-CMAKE_PATH: +Link Features +^^^^^^^^^^^^^ -.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path> +.. genex:: $<LINK_LIBRARY:feature,library-list> .. versionadded:: 3.24 - Returns ``path``. If ``path`` is a native path, it is converted into a - cmake-style path with forward-slashes (``/``). On Windows, the long filename - marker is taken into account. - - When the ``NORMALIZE`` option is specified, the path is :ref:`normalized - <Normalization>` after the conversion. + Specify a set of libraries to link to a target, along with a ``feature`` + which provides details about *how* they should be linked. For example: -.. genex:: $<PATH:APPEND,path,input,...> + .. code-block:: cmake - .. versionadded:: 3.24 + add_library(lib1 STATIC ...) + add_library(lib2 ...) + target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,lib1>") - Returns all the ``input`` arguments appended to ``path`` using ``/`` as the - ``directory-separator``. Depending on the ``input``, the value of ``path`` - may be discarded. + This specifies that ``lib2`` should link to ``lib1`` and use the + ``WHOLE_ARCHIVE`` feature when doing so. - See :ref:`cmake_path(APPEND) <APPEND>` for more details. + Feature names are case-sensitive and may only contain letters, numbers and + underscores. Feature names defined in all uppercase are reserved for CMake's + own built-in features. The pre-defined built-in library features are: -.. genex:: $<PATH:REMOVE_FILENAME,path> + .. include:: ../variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt - .. versionadded:: 3.24 + Built-in and custom library features are defined in terms of the following + variables: - Returns ``path`` with filename component (as returned by - ``$<PATH:GET_FILENAME>``) removed. After removal, any trailing - ``directory-separator`` is left alone, if present. + * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` + * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` + * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` + * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` - See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details. + The value used for each of these variables is the value as set at the end of + the directory scope in which the target was created. The usage is as follows: -.. genex:: $<PATH:REPLACE_FILENAME,path,input> + 1. If the language-specific + :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable + is true, the ``feature`` must be defined by the corresponding + :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` variable. + 2. If no language-specific ``feature`` is supported, then the + :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable must be + true and the ``feature`` must be defined by the corresponding + :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variable. - .. versionadded:: 3.24 + The following limitations should be noted: - Returns ``path`` with the filename component replaced by ``input``. If - ``path`` has no filename component (i.e. ``$<PATH:HAS_FILENAME>`` returns - ``0``), ``path`` is unchanged. + * The ``library-list`` can specify CMake targets or libraries. + Any CMake target of type :ref:`OBJECT <Object Libraries>` + or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect + of the expression and instead be linked in the standard way. - See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details. + * The ``$<LINK_LIBRARY:...>`` generator expression can only be used to + specify link libraries. In practice, this means it can appear in the + :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`, and + :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be + specified in :command:`target_link_libraries` and :command:`link_libraries` + commands. -.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path> + * If a ``$<LINK_LIBRARY:...>`` generator expression appears in the + :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be + included in the imported target generated by a :command:`install(EXPORT)` + command. It is the responsibility of the environment consuming this + import to define the link feature used by this expression. - .. versionadded:: 3.24 + * Each target or library involved in the link step must have at most only + one kind of library feature. The absence of a feature is also incompatible + with all other features. For example: - Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` removed, if any. + .. code-block:: cmake - See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details. + add_library(lib1 ...) + add_library(lib2 ...) + add_library(lib3 ...) -.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path> + # lib1 will be associated with feature1 + target_link_libraries(lib2 PUBLIC "$<LINK_LIBRARY:feature1,lib1>") - .. versionadded:: 3.24 + # lib1 is being linked with no feature here. This conflicts with the + # use of feature1 in the line above and would result in an error. + target_link_libraries(lib3 PRIVATE lib1 lib2) - Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` replaced by - ``input``, if any. + Where it isn't possible to use the same feature throughout a build for a + given target or library, the :prop_tgt:`LINK_LIBRARY_OVERRIDE` and + :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties can be + used to resolve such incompatibilities. - See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details. + * The ``$<LINK_LIBRARY:...>`` generator expression does not guarantee + that the list of specified targets and libraries will be kept grouped + together. To manage constructs like ``--start-group`` and ``--end-group``, + as supported by the GNU ``ld`` linker, use the :genex:`LINK_GROUP` + generator expression instead. -.. genex:: $<PATH:NORMAL_PATH,path> +.. genex:: $<LINK_GROUP:feature,library-list> .. versionadded:: 3.24 - Returns ``path`` normalized according to the steps described in - :ref:`Normalization`. - -.. genex:: $<PATH:RELATIVE_PATH,path,base_directory> + Specify a group of libraries to link to a target, along with a ``feature`` + which defines how that group should be linked. For example: - .. versionadded:: 3.24 + .. code-block:: cmake - Returns ``path``, modified to make it relative to the ``base_directory`` - argument. + add_library(lib1 STATIC ...) + add_library(lib2 ...) + target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:RESCAN,lib1,external>") - See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more - details. + This specifies that ``lib2`` should link to ``lib1`` and ``external``, and + that both of those two libraries should be included on the linker command + line according to the definition of the ``RESCAN`` feature. -.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory> + Feature names are case-sensitive and may only contain letters, numbers and + underscores. Feature names defined in all uppercase are reserved for CMake's + own built-in features. Currently, there is only one pre-defined built-in + group feature: - .. versionadded:: 3.24 + .. include:: ../variable/LINK_GROUP_PREDEFINED_FEATURES.txt - Returns ``path`` as absolute. If ``path`` is a relative path - (``$<PATH:IS_RELATIVE>`` returns ``1``), it is evaluated relative to the - given base directory specified by ``base_directory`` argument. + Built-in and custom group features are defined in terms of the following + variables: - When the ``NORMALIZE`` option is specified, the path is - :ref:`normalized <Normalization>` after the path computation. + * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` + * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` + * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` + * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` - See :ref:`cmake_path(ABSOLUTE_PATH) <ABSOLUTE_PATH>` for more details. + The value used for each of these variables is the value as set at the end of + the directory scope in which the target was created. The usage is as follows: -Variable Queries ----------------- + 1. If the language-specific + :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable + is true, the ``feature`` must be defined by the corresponding + :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` variable. + 2. If no language-specific ``feature`` is supported, then the + :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable must be + true and the ``feature`` must be defined by the corresponding + :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` variable. -.. genex:: $<CONFIG> + The ``LINK_GROUP`` generator expression is compatible with the + :genex:`LINK_LIBRARY` generator expression. The libraries involved in a + group can be specified using the :genex:`LINK_LIBRARY` generator expression. - Configuration name. + Each target or external library involved in the link step is allowed to be + part of multiple groups, but only if all the groups involved specify the + same ``feature``. Such groups will not be merged on the linker command line, + the individual groups will still be preserved. Mixing different group + features for the same target or library is forbidden. -.. genex:: $<CONFIGURATION> + .. code-block:: cmake - Configuration name. Deprecated since CMake 3.0. Use ``CONFIG`` instead. + add_library(lib1 ...) + add_library(lib2 ...) + add_library(lib3 ...) + add_library(lib4 ...) + add_library(lib5 ...) -.. genex:: $<PLATFORM_ID> + target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>") + target_link_libraries(lib4 PRIVATE "$<LINK_GROUP:feature1,lib1,lib3>") + # lib4 will be linked with the groups {lib1,lib2} and {lib1,lib3}. + # Both groups specify the same feature, so this is fine. - The current system's CMake platform id. - See also the :variable:`CMAKE_SYSTEM_NAME` variable. + target_link_libraries(lib5 PRIVATE "$<LINK_GROUP:feature2,lib1,lib3>") + # An error will be raised here because both lib1 and lib3 are part of two + # groups with different features. -.. genex:: $<C_COMPILER_ID> + When a target or an external library is involved in the link step as part of + a group and also as not part of any group, any occurrence of the non-group + link item will be replaced by the groups it belongs to. - The CMake's compiler id of the C compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + .. code-block:: cmake -.. genex:: $<CXX_COMPILER_ID> + add_library(lib1 ...) + add_library(lib2 ...) + add_library(lib3 ...) + add_library(lib4 ...) - The CMake's compiler id of the CXX compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + target_link_libraries(lib3 PUBLIC lib1) -.. genex:: $<CUDA_COMPILER_ID> + target_link_libraries(lib4 PRIVATE lib3 "$<LINK_GROUP:feature1,lib1,lib2>") + # lib4 will only be linked with lib3 and the group {lib1,lib2} - The CMake's compiler id of the CUDA compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + Because ``lib1`` is part of the group defined for ``lib4``, that group then + gets applied back to the use of ``lib1`` for ``lib3``. The end result will + be as though the linking relationship for ``lib3`` had been specified as: -.. genex:: $<OBJC_COMPILER_ID> + .. code-block:: cmake - .. versionadded:: 3.16 + target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>") - The CMake's compiler id of the OBJC compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + Be aware that the precedence of the group over the non-group link item can + result in circular dependencies between groups. If this occurs, a fatal + error is raised because circular dependencies are not allowed for groups. -.. genex:: $<OBJCXX_COMPILER_ID> + .. code-block:: cmake - .. versionadded:: 3.16 + add_library(lib1A ...) + add_library(lib1B ...) + add_library(lib2A ...) + add_library(lib2B ...) + add_library(lib3 ...) - The CMake's compiler id of the OBJCXX compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + # Non-group linking relationships, these are non-circular so far + target_link_libraries(lib1A PUBLIC lib2A) + target_link_libraries(lib2B PUBLIC lib1B) -.. genex:: $<Fortran_COMPILER_ID> + # The addition of these groups creates circular dependencies + target_link_libraries(lib3 PRIVATE + "$<LINK_GROUP:feat,lib1A,lib1B>" + "$<LINK_GROUP:feat,lib2A,lib2B>" + ) - The CMake's compiler id of the Fortran compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + Because of the groups defined for ``lib3``, the linking relationships for + ``lib1A`` and ``lib2B`` effectively get expanded to the equivalent of: -.. genex:: $<HIP_COMPILER_ID> + .. code-block:: cmake - .. versionadded:: 3.21 + target_link_libraries(lib1A PUBLIC "$<LINK_GROUP:feat,lib2A,lib2B>") + target_link_libraries(lib2B PUBLIC "$<LINK_GROUP:feat,lib1A,lib1B>") - The CMake's compiler id of the HIP compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + This creates a circular dependency between groups: + ``lib1A --> lib2B --> lib1A``. -.. genex:: $<ISPC_COMPILER_ID> + The following limitations should also be noted: - .. versionadded:: 3.19 + * The ``library-list`` can specify CMake targets or libraries. + Any CMake target of type :ref:`OBJECT <Object Libraries>` + or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect + of the expression and instead be linked in the standard way. - The CMake's compiler id of the ISPC compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. + * The ``$<LINK_GROUP:...>`` generator expression can only be used to + specify link libraries. In practice, this means it can appear in the + :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`,and + :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be + specified in :command:`target_link_libraries` and :command:`link_libraries` + commands. -.. genex:: $<C_COMPILER_VERSION> + * If a ``$<LINK_GROUP:...>`` generator expression appears in the + :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be + included in the imported target generated by a :command:`install(EXPORT)` + command. It is the responsibility of the environment consuming this + import to define the link feature used by this expression. - The version of the C compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +Link Context +^^^^^^^^^^^^ -.. genex:: $<CXX_COMPILER_VERSION> +.. genex:: $<LINK_ONLY:...> - The version of the CXX compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + .. versionadded:: 3.1 -.. genex:: $<CUDA_COMPILER_VERSION> + Content of ``...``, except while collecting :ref:`Target Usage Requirements`, + in which case it is the empty string. This is intended for use in an + :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated + via the :command:`target_link_libraries` command, to specify private link + dependencies without other usage requirements. - The version of the CUDA compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + .. versionadded:: 3.24 + ``LINK_ONLY`` may also be used in a :prop_tgt:`LINK_LIBRARIES` target + property. See policy :policy:`CMP0131`. -.. genex:: $<OBJC_COMPILER_VERSION> +.. genex:: $<DEVICE_LINK:list> - .. versionadded:: 3.16 + .. versionadded:: 3.18 - The version of the OBJC compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + Returns the list if it is the device link step, an empty list otherwise. + The device link step is controlled by :prop_tgt:`CUDA_SEPARABLE_COMPILATION` + and :prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` properties and + policy :policy:`CMP0105`. This expression can only be used to specify link + options. -.. genex:: $<OBJCXX_COMPILER_VERSION> +.. genex:: $<HOST_LINK:list> - .. versionadded:: 3.16 + .. versionadded:: 3.18 - The version of the OBJCXX compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + Returns the list if it is the normal link step, an empty list otherwise. + This expression is mainly useful when a device link step is also involved + (see :genex:`$<DEVICE_LINK:list>` generator expression). This expression can + only be used to specify link options. -.. genex:: $<Fortran_COMPILER_VERSION> - The version of the Fortran compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +.. _`Target-Dependent Queries`: -.. genex:: $<HIP_COMPILER_VERSION> +Target-Dependent Expressions +---------------------------- - .. versionadded:: 3.21 +These queries refer to a target ``tgt``. Unless otherwise stated, this can +be any runtime artifact, namely: - The version of the HIP compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +* An executable target created by :command:`add_executable`. +* A shared library target (``.so``, ``.dll`` but not their ``.lib`` import + library) created by :command:`add_library`. +* A static library target created by :command:`add_library`. -.. genex:: $<ISPC_COMPILER_VERSION> +In the following, the phrase "the ``tgt`` filename" means the name of the +``tgt`` binary file. This has to be distinguished from the phrase +"the target name", which is just the string ``tgt``. - .. versionadded:: 3.19 +.. genex:: $<TARGET_EXISTS:tgt> - The version of the ISPC compiler used. - See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. + .. versionadded:: 3.12 -.. genex:: $<COMPILE_LANGUAGE> + ``1`` if ``tgt`` exists as a CMake target, else ``0``. - .. versionadded:: 3.3 +.. genex:: $<TARGET_NAME_IF_EXISTS:tgt> - The compile language of source files when evaluating compile options. - See :ref:`the related boolean expression - <Boolean COMPILE_LANGUAGE Generator Expression>` - ``$<COMPILE_LANGUAGE:language>`` - for notes about the portability of this generator expression. + .. versionadded:: 3.12 -.. genex:: $<LINK_LANGUAGE> + The target name ``tgt`` if the target exists, an empty string otherwise. - .. versionadded:: 3.18 + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. - The link language of target when evaluating link options. - See :ref:`the related boolean expression - <Boolean LINK_LANGUAGE Generator Expression>` ``$<LINK_LANGUAGE:language>`` - for notes about the portability of this generator expression. +.. genex:: $<TARGET_NAME:...> - .. note:: + Marks ``...`` as being the name of a target. This is required if exporting + targets to multiple dependent export sets. The ``...`` must be a literal + name of a target, it may not contain generator expressions. - This generator expression is not supported by the link libraries - properties to avoid side-effects due to the double evaluation of - these properties. +.. genex:: $<TARGET_PROPERTY:tgt,prop> -.. _`Target-Dependent Queries`: + Value of the property ``prop`` on the target ``tgt``. -Target-Dependent Queries ------------------------- + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. -These queries refer to a target ``tgt``. This can be any runtime artifact, -namely: +.. genex:: $<TARGET_PROPERTY:prop> -* an executable target created by :command:`add_executable` -* a shared library target (``.so``, ``.dll`` but not their ``.lib`` import library) - created by :command:`add_library` -* a static library target created by :command:`add_library` + Value of the property ``prop`` on the target for which the expression + is being evaluated. Note that for generator expressions in + :ref:`Target Usage Requirements` this is the consuming target rather + than the target specifying the requirement. -In the following, "the ``tgt`` filename" means the name of the ``tgt`` -binary file. This has to be distinguished from "the target name", -which is just the string ``tgt``. +.. genex:: $<TARGET_OBJECTS:tgt> -.. genex:: $<TARGET_NAME_IF_EXISTS:tgt> + .. versionadded:: 3.1 - .. versionadded:: 3.12 + List of objects resulting from building ``tgt``. This would typically be + used on :ref:`object library <Object Libraries>` targets. - The target name ``tgt`` if the target exists, an empty string otherwise. +.. genex:: $<TARGET_POLICY:policy> - Note that ``tgt`` is not added as a dependency of the target this - expression is evaluated on. + ``1`` if the ``policy`` was ``NEW`` when the 'head' target was created, + else ``0``. If the ``policy`` was not set, the warning message for the policy + will be emitted. This generator expression only works for a subset of + policies. .. genex:: $<TARGET_FILE:tgt> @@ -1273,20 +1647,6 @@ which is just the string ``tgt``. Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on (see policy :policy:`CMP0112`). -.. genex:: $<TARGET_PROPERTY:tgt,prop> - - Value of the property ``prop`` on the target ``tgt``. - - Note that ``tgt`` is not added as a dependency of the target this - expression is evaluated on. - -.. genex:: $<TARGET_PROPERTY:prop> - - Value of the property ``prop`` on the target for which the expression - is being evaluated. Note that for generator expressions in - :ref:`Target Usage Requirements` this is the consuming target rather - than the target specifying the requirement. - .. genex:: $<TARGET_RUNTIME_DLLS:tgt> .. versionadded:: 3.21 @@ -1294,8 +1654,8 @@ which is just the string ``tgt``. List of DLLs that the target depends on at runtime. This is determined by the locations of all the ``SHARED`` targets in the target's transitive dependencies. Using this generator expression on targets other than - executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error. On - non-DLL platforms, it evaluates to an empty string. + executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error. + **On non-DLL platforms, this expression always evaluates to an empty string**. This generator expression can be used to copy all of the DLLs that a target depends on into its output directory in a ``POST_BUILD`` custom command. For @@ -1310,7 +1670,7 @@ which is just the string ``tgt``. add_custom_command(TARGET exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe> COMMAND_EXPAND_LISTS - ) + ) .. note:: @@ -1321,368 +1681,100 @@ which is just the string ``tgt``. section for details. Many :ref:`Find Modules` produce imported targets with the ``UNKNOWN`` type and therefore will be ignored. -.. genex:: $<INSTALL_PREFIX> - - Content of the install prefix when the target is exported via - :command:`install(EXPORT)`, or when evaluated in the - :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of - :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise. - -Output-Related Expressions --------------------------- - -.. genex:: $<TARGET_NAME:...> - - Marks ``...`` as being the name of a target. This is required if exporting - targets to multiple dependent export sets. The ``...`` must be a literal - name of a target- it may not contain generator expressions. - -.. genex:: $<LINK_ONLY:...> - - .. versionadded:: 3.1 - - Content of ``...``, except while collecting :ref:`Target Usage Requirements`, - in which case it is the empty string. This is intended for use in an - :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated - via the :command:`target_link_libraries` command, to specify private link - dependencies without other usage requirements. - - .. versionadded:: 3.24 - ``LINK_ONLY`` may also be used in a :prop_tgt:`LINK_LIBRARIES` target - property. See policy :policy:`CMP0131`. - -.. genex:: $<DEVICE_LINK:list> - - .. versionadded:: 3.18 - - Returns the list if it is the device link step, an empty list otherwise. - The device link step is controlled by :prop_tgt:`CUDA_SEPARABLE_COMPILATION` - and :prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` properties and - policy :policy:`CMP0105`. This expression can only be used to specify link - options. - -.. genex:: $<HOST_LINK:list> - - .. versionadded:: 3.18 - - Returns the list if it is the normal link step, an empty list otherwise. - This expression is mainly useful when a device link step is also involved - (see :genex:`$<DEVICE_LINK:list>` generator expression). This expression can - only be used to specify link options. - -.. genex:: $<LINK_LIBRARY:feature,library-list> - - .. versionadded:: 3.24 - - Specify a set of libraries to link to a target, along with a ``feature`` - which provides details about *how* they should be linked. For example: - - .. code-block:: cmake - - add_library(lib1 STATIC ...) - add_library(lib2 ...) - target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,lib1>") - - This specifies that ``lib2`` should link to ``lib1`` and use the - ``WHOLE_ARCHIVE`` feature when doing so. - Feature names are case-sensitive and may only contain letters, numbers and - underscores. Feature names defined in all uppercase are reserved for CMake's - own built-in features. The pre-defined built-in library features are: - - .. include:: ../variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt - - Built-in and custom library features are defined in terms of the following - variables: - - * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` - * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` - * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` - * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` - - The value used for each of these variables is the value as set at the end of - the directory scope in which the target was created. The usage is as follows: - - 1. If the language-specific - :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable - is true, the ``feature`` must be defined by the corresponding - :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` variable. - 2. If no language-specific ``feature`` is supported, then the - :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable must be - true and the ``feature`` must be defined by the corresponding - :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variable. +Export And Install Expressions +------------------------------ - The following limitations should be noted: - - * The ``library-list`` can specify CMake targets or libraries. - Any CMake target of type :ref:`OBJECT <Object Libraries>` - or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect - of the expression and instead be linked in the standard way. - - * The ``$<LINK_LIBRARY:...>`` generator expression can only be used to - specify link libraries. In practice, this means it can appear in the - :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`, and - :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be - specified in :command:`target_link_libraries` and :command:`link_libraries` - commands. - - * If a ``$<LINK_LIBRARY:...>`` generator expression appears in the - :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be - included in the imported target generated by a :command:`install(EXPORT)` - command. It is the responsibility of the environment consuming this - import to define the link feature used by this expression. - - * Each target or library involved in the link step must have at most only - one kind of library feature. The absence of a feature is also incompatible - with all other features. For example: - - .. code-block:: cmake - - add_library(lib1 ...) - add_library(lib2 ...) - add_library(lib3 ...) - - # lib1 will be associated with feature1 - target_link_libraries(lib2 PUBLIC "$<LINK_LIBRARY:feature1,lib1>") - - # lib1 is being linked with no feature here. This conflicts with the - # use of feature1 in the line above and would result in an error. - target_link_libraries(lib3 PRIVATE lib1 lib2) - - Where it isn't possible to use the same feature throughout a build for a - given target or library, the :prop_tgt:`LINK_LIBRARY_OVERRIDE` and - :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties can be - used to resolve such incompatibilities. - - * The ``$<LINK_LIBRARY:...>`` generator expression does not guarantee - that the list of specified targets and libraries will be kept grouped - together. To manage constructs like ``--start-group`` and ``--end-group``, - as supported by the GNU ``ld`` linker, use the :genex:`LINK_GROUP` - generator expression instead. - -.. genex:: $<LINK_GROUP:feature,library-list> - - .. versionadded:: 3.24 - - Specify a group of libraries to link to a target, along with a ``feature`` - which defines how that group should be linked. For example: - - .. code-block:: cmake - - add_library(lib1 STATIC ...) - add_library(lib2 ...) - target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:RESCAN,lib1,external>") +.. genex:: $<INSTALL_INTERFACE:...> - This specifies that ``lib2`` should link to ``lib1`` and ``external``, and - that both of those two libraries should be included on the linker command - line according to the definition of the ``RESCAN`` feature. + Content of ``...`` when the property is exported using + :command:`install(EXPORT)`, and empty otherwise. - Feature names are case-sensitive and may only contain letters, numbers and - underscores. Feature names defined in all uppercase are reserved for CMake's - own built-in features. Currently, there is only one pre-defined built-in - group feature: +.. genex:: $<BUILD_INTERFACE:...> - .. include:: ../variable/LINK_GROUP_PREDEFINED_FEATURES.txt + Content of ``...`` when the property is exported using :command:`export`, or + when the target is used by another target in the same buildsystem. Expands to + the empty string otherwise. - Built-in and custom group features are defined in terms of the following - variables: +.. genex:: $<INSTALL_PREFIX> - * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` - * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` - * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` - * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` + Content of the install prefix when the target is exported via + :command:`install(EXPORT)`, or when evaluated in the + :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of + :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise. - The value used for each of these variables is the value as set at the end of - the directory scope in which the target was created. The usage is as follows: +Multi-level Expression Evaluation +--------------------------------- - 1. If the language-specific - :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable - is true, the ``feature`` must be defined by the corresponding - :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` variable. - 2. If no language-specific ``feature`` is supported, then the - :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable must be - true and the ``feature`` must be defined by the corresponding - :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` variable. - - The ``LINK_GROUP`` generator expression is compatible with the - :genex:`LINK_LIBRARY` generator expression. The libraries involved in a - group can be specified using the :genex:`LINK_LIBRARY` generator expression. +.. genex:: $<GENEX_EVAL:expr> - Each target or external library involved in the link step is allowed to be - part of multiple groups, but only if all the groups involved specify the - same ``feature``. Such groups will not be merged on the linker command line, - the individual groups will still be preserved. Mixing different group - features for the same target or library is forbidden. + .. versionadded:: 3.12 - .. code-block:: cmake + Content of ``expr`` evaluated as a generator expression in the current + context. This enables consumption of generator expressions whose + evaluation results itself in generator expressions. - add_library(lib1 ...) - add_library(lib2 ...) - add_library(lib3 ...) - add_library(lib4 ...) - add_library(lib5 ...) +.. genex:: $<TARGET_GENEX_EVAL:tgt,expr> - target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>") - target_link_libraries(lib4 PRIVATE "$<LINK_GROUP:feature1,lib1,lib3>") - # lib4 will be linked with the groups {lib1,lib2} and {lib1,lib3}. - # Both groups specify the same feature, so this is fine. + .. versionadded:: 3.12 - target_link_libraries(lib5 PRIVATE "$<LINK_GROUP:feature2,lib1,lib3>") - # An error will be raised here because both lib1 and lib3 are part of two - # groups with different features. + Content of ``expr`` evaluated as a generator expression in the context of + ``tgt`` target. This enables consumption of custom target properties that + themselves contain generator expressions. - When a target or an external library is involved in the link step as part of - a group and also as not part of any group, any occurrence of the non-group - link item will be replaced by the groups it belongs to. + Having the capability to evaluate generator expressions is very useful when + you want to manage custom properties supporting generator expressions. + For example: .. code-block:: cmake - add_library(lib1 ...) - add_library(lib2 ...) - add_library(lib3 ...) - add_library(lib4 ...) - - target_link_libraries(lib3 PUBLIC lib1) - - target_link_libraries(lib4 PRIVATE lib3 "$<LINK_GROUP:feature1,lib1,lib2>") - # lib4 will only be linked with lib3 and the group {lib1,lib2} + add_library(foo ...) - Because ``lib1`` is part of the group defined for ``lib4``, that group then - gets applied back to the use of ``lib1`` for ``lib3``. The end result will - be as though the linking relationship for ``lib3`` had been specified as: + set_property(TARGET foo PROPERTY + CUSTOM_KEYS $<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS> + ) - .. code-block:: cmake + add_custom_target(printFooKeys + COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_PROPERTY:foo,CUSTOM_KEYS> + ) - target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>") + This naive implementation of the ``printFooKeys`` custom command is wrong + because ``CUSTOM_KEYS`` target property is not evaluated and the content + is passed as is (i.e. ``$<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>``). - Be aware that the precedence of the group over the non-group link item can - result in circular dependencies between groups. If this occurs, a fatal - error is raised because circular dependencies are not allowed for groups. + To have the expected result (i.e. ``FOO_EXTRA_THINGS`` if config is + ``Debug``), it is required to evaluate the output of + ``$<TARGET_PROPERTY:foo,CUSTOM_KEYS>``: .. code-block:: cmake - add_library(lib1A ...) - add_library(lib1B ...) - add_library(lib2A ...) - add_library(lib2B ...) - add_library(lib3 ...) - - # Non-group linking relationships, these are non-circular so far - target_link_libraries(lib1A PUBLIC lib2A) - target_link_libraries(lib2B PUBLIC lib1B) - - # The addition of these groups creates circular dependencies - target_link_libraries(lib3 PRIVATE - "$<LINK_GROUP:feat,lib1A,lib1B>" - "$<LINK_GROUP:feat,lib2A,lib2B>" + add_custom_target(printFooKeys + COMMAND ${CMAKE_COMMAND} -E + echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>> ) - Because of the groups defined for ``lib3``, the linking relationships for - ``lib1A`` and ``lib2B`` effectively get expanded to the equivalent of: - - .. code-block:: cmake - - target_link_libraries(lib1A PUBLIC "$<LINK_GROUP:feat,lib2A,lib2B>") - target_link_libraries(lib2B PUBLIC "$<LINK_GROUP:feat,lib1A,lib1B>") - - This creates a circular dependency between groups: - ``lib1A --> lib2B --> lib1A``. - - The following limitations should also be noted: - - * The ``library-list`` can specify CMake targets or libraries. - Any CMake target of type :ref:`OBJECT <Object Libraries>` - or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect - of the expression and instead be linked in the standard way. - - * The ``$<LINK_GROUP:...>`` generator expression can only be used to - specify link libraries. In practice, this means it can appear in the - :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`,and - :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be - specified in :command:`target_link_libraries` and :command:`link_libraries` - commands. - - * If a ``$<LINK_GROUP:...>`` generator expression appears in the - :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be - included in the imported target generated by a :command:`install(EXPORT)` - command. It is the responsibility of the environment consuming this - import to define the link feature used by this expression. - -.. genex:: $<INSTALL_INTERFACE:...> - - Content of ``...`` when the property is exported using :command:`install(EXPORT)`, - and empty otherwise. - -.. genex:: $<BUILD_INTERFACE:...> - - Content of ``...`` when the property is exported using :command:`export`, or - when the target is used by another target in the same buildsystem. Expands to - the empty string otherwise. - -.. genex:: $<MAKE_C_IDENTIFIER:...> - - Content of ``...`` converted to a C identifier. The conversion follows the - same behavior as :command:`string(MAKE_C_IDENTIFIER)`. - -.. genex:: $<TARGET_OBJECTS:objLib> - - .. versionadded:: 3.1 - - List of objects resulting from build of ``objLib``. - -.. genex:: $<SHELL_PATH:...> - - .. versionadded:: 3.4 - - Content of ``...`` converted to shell path style. For example, slashes are - converted to backslashes in Windows shells and drive letters are converted - to posix paths in MSYS shells. The ``...`` must be an absolute path. - - .. versionadded:: 3.14 - The ``...`` may be a :ref:`semicolon-separated list <CMake Language Lists>` - of paths, in which case each path is converted individually and a result - list is generated using the shell path separator (``:`` on POSIX and - ``;`` on Windows). Be sure to enclose the argument containing this genex - in double quotes in CMake source code so that ``;`` does not split arguments. - -.. genex:: $<OUTPUT_CONFIG:...> - - .. versionadded:: 3.20 - - Only valid in :command:`add_custom_command` and :command:`add_custom_target` - as the outer-most generator expression in an argument. - With the :generator:`Ninja Multi-Config` generator, generator expressions - in ``...`` are evaluated using the custom command's "output config". - With other generators, the content of ``...`` is evaluated normally. - -.. genex:: $<COMMAND_CONFIG:...> - - .. versionadded:: 3.20 +Escaped Characters +------------------ - Only valid in :command:`add_custom_command` and :command:`add_custom_target` - as the outer-most generator expression in an argument. - With the :generator:`Ninja Multi-Config` generator, generator expressions - in ``...`` are evaluated using the custom command's "command config". - With other generators, the content of ``...`` is evaluated normally. +These expressions evaluate to specific string literals. Use them in place of +the actual string literal where you need to prevent them from having their +special meaning. -Debugging -========= +.. genex:: $<ANGLE-R> -Since generator expressions are evaluated during generation of the buildsystem, -and not during processing of ``CMakeLists.txt`` files, it is not possible to -inspect their result with the :command:`message()` command. + A literal ``>``. Used for example to compare strings that contain a ``>``. -One possible way to generate debug messages is to add a custom target, +.. genex:: $<COMMA> -.. code-block:: cmake + A literal ``,``. Used for example to compare strings which contain a ``,``. - add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<...>") +.. genex:: $<SEMICOLON> -The shell command ``make genexdebug`` (invoked after execution of ``cmake``) -would then print the result of ``$<...>``. + A literal ``;``. Used to prevent list expansion on an argument with ``;``. -Another way is to write debug messages to a file: +Deprecated Expressions +---------------------- -.. code-block:: cmake +.. genex:: $<CONFIGURATION> - file(GENERATE OUTPUT filename CONTENT "$<...>") + Configuration name. Deprecated since CMake 3.0. Use :genex:`CONFIG` instead. diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index d88322c..9fb46be 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -184,6 +184,16 @@ Properties on Targets /prop_tgt/CUDA_STANDARD /prop_tgt/CUDA_STANDARD_REQUIRED /prop_tgt/CXX_EXTENSIONS + /prop_tgt/CXX_MODULE_DIRS + /prop_tgt/CXX_MODULE_DIRS_NAME + /prop_tgt/CXX_MODULE_SET + /prop_tgt/CXX_MODULE_SET_NAME + /prop_tgt/CXX_MODULE_SETS + /prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS + /prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME + /prop_tgt/CXX_MODULE_HEADER_UNIT_SET + /prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME + /prop_tgt/CXX_MODULE_HEADER_UNIT_SETS /prop_tgt/CXX_STANDARD /prop_tgt/CXX_STANDARD_REQUIRED /prop_tgt/DEBUG_POSTFIX @@ -202,6 +212,7 @@ Properties on Targets /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG /prop_tgt/EXPORT_COMPILE_COMMANDS /prop_tgt/EXPORT_NAME + /prop_tgt/EXPORT_NO_SYSTEM /prop_tgt/EXPORT_PROPERTIES /prop_tgt/FOLDER /prop_tgt/Fortran_BUILDING_INSTRINSIC_MODULES @@ -262,6 +273,8 @@ Properties on Targets /prop_tgt/INTERFACE_COMPILE_DEFINITIONS /prop_tgt/INTERFACE_COMPILE_FEATURES /prop_tgt/INTERFACE_COMPILE_OPTIONS + /prop_tgt/INTERFACE_CXX_MODULE_SETS + /prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS /prop_tgt/INTERFACE_HEADER_SETS /prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES @@ -375,6 +388,7 @@ Properties on Targets /prop_tgt/Swift_LANGUAGE_VERSION /prop_tgt/Swift_MODULE_DIRECTORY /prop_tgt/Swift_MODULE_NAME + /prop_tgt/SYSTEM /prop_tgt/TYPE /prop_tgt/UNITY_BUILD /prop_tgt/UNITY_BUILD_BATCH_SIZE @@ -451,6 +465,7 @@ Properties on Targets /prop_tgt/XCODE_SCHEME_ENVIRONMENT /prop_tgt/XCODE_SCHEME_EXECUTABLE /prop_tgt/XCODE_SCHEME_GUARD_MALLOC + /prop_tgt/XCODE_SCHEME_LAUNCH_MODE /prop_tgt/XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP /prop_tgt/XCODE_SCHEME_MALLOC_GUARD_EDGES /prop_tgt/XCODE_SCHEME_MALLOC_SCRIBBLE diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 7c8a7fa..ce65aee 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -278,6 +278,7 @@ Variables that Change Behavior /variable/CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE /variable/CMAKE_XCODE_SCHEME_ENVIRONMENT /variable/CMAKE_XCODE_SCHEME_GUARD_MALLOC + /variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE /variable/CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP /variable/CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES /variable/CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 38105dd..2726f13 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -249,6 +249,11 @@ Options For backward compatibility reasons, ``--loglevel`` is also accepted as a synonym for this option. + .. versionadded:: 3.25 + See the :command:`cmake_language` + :ref:`cmake_language <query_message_log_level>` command for a way to query + the current message logging level. + ``--log-context`` Enable the :command:`message` command outputting context attached to each message. diff --git a/Help/prop_tgt/CXX_MODULE_DIRS.rst b/Help/prop_tgt/CXX_MODULE_DIRS.rst new file mode 100644 index 0000000..fdf3831 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_DIRS.rst @@ -0,0 +1,17 @@ +CXX_MODULE_DIRS +--------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of base directories of the target's default +C++ module set (i.e. the file set with name and type ``CXX_MODULES``). The +property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_DIRS_<NAME>` for the list of base directories in +other C++ module sets. diff --git a/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst new file mode 100644 index 0000000..8c27d45 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst @@ -0,0 +1,17 @@ +CXX_MODULE_DIRS_<NAME> +---------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of base directories of the target's ``<NAME>`` C++ +module set, which has the set type ``CXX_MODULES``. The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_DIRS` for the list of base directories in the +default C++ module set. See :prop_tgt:`CXX_MODULE_SETS` for the file set names +of all C++ module sets. diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst new file mode 100644 index 0000000..17e5cf0 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst @@ -0,0 +1,17 @@ +CXX_MODULE_HEADER_UNIT_DIRS +--------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of base directories of the target's default C++ +module header set (i.e. the file set with name and type +``CXX_MODULE_HEADER_UNITS``). The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS_<NAME>` for the list of base directories +in other C++ module header sets. diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst new file mode 100644 index 0000000..ca30f23 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst @@ -0,0 +1,19 @@ +CXX_MODULE_HEADER_UNIT_DIRS_<NAME> +---------------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of base directories of the target's ``<NAME>`` C++ +module header set, which has the set type ``CXX_MODULE_HEADER_UNITS``. The +property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS` for the list of base directories +in the default C++ module header set. See +:prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` for the file set names of all C++ +module header sets. diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst new file mode 100644 index 0000000..f67a848 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst @@ -0,0 +1,18 @@ +CXX_MODULE_HEADER_UNIT_SET +-------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of files in the target's default C++ module header +set, (i.e. the file set with name and type ``CXX_MODULE_HEADER_UNITS``). If +any of the paths are relative, they are computed relative to the target's +source directory. The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>` for the list of files in +other C++ module header sets. diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst new file mode 100644 index 0000000..7b4bd3f --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst @@ -0,0 +1,18 @@ +CXX_MODULE_HEADER_UNIT_SETS +--------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module header +sets (i.e. all file sets with the type ``CXX_MODULE_HEADER_UNITS``). Files +listed in these file sets are treated as source files for the purpose of IDE +integration. + +C++ module header sets may be defined using the :command:`target_sources` +command ``FILE_SET`` option with type ``CXX_MODULE_HEADER_UNITS``. + +See also :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>`, +:prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` and +:prop_tgt:`INTERFACE_CXX_MODULE_HEADER_UNIT_SETS`. diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst new file mode 100644 index 0000000..d328950 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst @@ -0,0 +1,19 @@ +CXX_MODULE_HEADER_UNIT_SET_<NAME> +--------------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of files in the target's ``<NAME>`` C++ module header +set, which has the set type ``CXX_MODULE_HEADER_UNITS``. If any of the paths +are relative, they are computed relative to the target's source directory. The +property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` for the list of files in the +default C++ module header set. See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` for +the file set names of all C++ module header sets. diff --git a/Help/prop_tgt/CXX_MODULE_SET.rst b/Help/prop_tgt/CXX_MODULE_SET.rst new file mode 100644 index 0000000..ae9000e --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_SET.rst @@ -0,0 +1,18 @@ +CXX_MODULE_SET +-------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of files in the target's default C++ module set, +(i.e. the file set with name and type ``CXX_MODULES``). If any of the paths +are relative, they are computed relative to the target's source directory. The +property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_SET_<NAME>` for the list of files in other C++ +module sets. diff --git a/Help/prop_tgt/CXX_MODULE_SETS.rst b/Help/prop_tgt/CXX_MODULE_SETS.rst new file mode 100644 index 0000000..c03df39 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_SETS.rst @@ -0,0 +1,16 @@ +CXX_MODULE_SETS +--------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module sets (i.e. +all file sets with the type ``CXX_MODULES``). Files listed in these file sets are +treated as source files for the purpose of IDE integration. + +C++ module sets may be defined using the :command:`target_sources` command +``FILE_SET`` option with type ``CXX_MODULES``. + +See also :prop_tgt:`CXX_MODULE_SET_<NAME>`, :prop_tgt:`CXX_MODULE_SET` and +:prop_tgt:`INTERFACE_CXX_MODULE_SETS`. diff --git a/Help/prop_tgt/CXX_MODULE_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_SET_NAME.rst new file mode 100644 index 0000000..27c88f3 --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_SET_NAME.rst @@ -0,0 +1,18 @@ +CXX_MODULE_SET_<NAME> +--------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Semicolon-separated list of files in the target's ``<NAME>`` C++ module set, +which has the set type ``CXX_MODULES``. If any of the paths are relative, they +are computed relative to the target's source directory. The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`CXX_MODULE_SET` for the list of files in the default C++ module +set. See :prop_tgt:`CXX_MODULE_SETS` for the file set names of all C++ module +sets. diff --git a/Help/prop_tgt/EXPORT_NO_SYSTEM.rst b/Help/prop_tgt/EXPORT_NO_SYSTEM.rst new file mode 100644 index 0000000..c93d1a5 --- /dev/null +++ b/Help/prop_tgt/EXPORT_NO_SYSTEM.rst @@ -0,0 +1,11 @@ +EXPORT_NO_SYSTEM +---------------- + +.. versionadded:: 3.25 + +Specifies that :command:`install(EXPORT)` and :command:`export` commands will +generate a imported target with :prop_tgt:`SYSTEM` property `OFF`. + +See the :prop_tgt:`NO_SYSTEM_FROM_IMPORTED` target property to set this +behavior on the target consuming the include directories rather than +providing them. diff --git a/Help/prop_tgt/IMPORTED_NO_SYSTEM.rst b/Help/prop_tgt/IMPORTED_NO_SYSTEM.rst index ee22d6f..913d9f2 100644 --- a/Help/prop_tgt/IMPORTED_NO_SYSTEM.rst +++ b/Help/prop_tgt/IMPORTED_NO_SYSTEM.rst @@ -3,11 +3,21 @@ IMPORTED_NO_SYSTEM .. versionadded:: 3.23 +.. deprecated:: 3.25 + + ``IMPORTED_NO_SYSTEM`` is deprecated. Set :prop_tgt:`SYSTEM` to `OFF` + instead if you don't want target's include directories to be ``SYSTEM`` + when compiling consumers. Set :prop_tgt:`EXPORT_NO_SYSTEM` to `ON` instead + if you don't want the include directories of the imported target generated + by :command:`install(EXPORT)` and :command:`export` commands to be + ``SYSTEM`` when compiling consumers. + Specifies that an :ref:`Imported Target <Imported Targets>` is not a ``SYSTEM`` library. This has the following effects: * Entries of :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` are not treated - as ``SYSTEM`` include directories when compiling consumers, as they + as ``SYSTEM`` include directories when compiling consumers (regardless of + the value of the consumed target's :prop_tgt:`SYSTEM` property), as they would be by default. Entries of :prop_tgt:`INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` are not affected, and will always be treated as ``SYSTEM`` include directories. diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst new file mode 100644 index 0000000..eb3a9ff --- /dev/null +++ b/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst @@ -0,0 +1,16 @@ +INTERFACE_CXX_MODULE_HEADER_UNIT_SETS +------------------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Read-only list of the target's ``PUBLIC`` C++ module header sets (i.e. all +file sets with the type ``CXX_MODULE_HEADER_UNITS``). Files listed in these +C++ module header sets can be installed with :command:`install(TARGETS)` and +exported with :command:`install(EXPORT)` and :command:`export`. + +C++ module header sets may be defined using the :command:`target_sources` +command ``FILE_SET`` option with type ``CXX_MODULE_HEADER_UNITS``. + +See also :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS`. diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst new file mode 100644 index 0000000..cc30386 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst @@ -0,0 +1,16 @@ +INTERFACE_CXX_MODULE_SETS +------------------------- + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Read-only list of the target's ``PUBLIC`` C++ module sets (i.e. all file sets +with the type ``CXX_MODULES``). Files listed in these C++ module sets can be +installed with :command:`install(TARGETS)` and exported with +:command:`install(EXPORT)` and :command:`export`. + +C++ module sets may be defined using the :command:`target_sources` command +``FILE_SET`` option with type ``CXX_MODULES``. + +See also :prop_tgt:`CXX_MODULE_SETS`. diff --git a/Help/prop_tgt/SYSTEM.rst b/Help/prop_tgt/SYSTEM.rst new file mode 100644 index 0000000..2db6aed --- /dev/null +++ b/Help/prop_tgt/SYSTEM.rst @@ -0,0 +1,16 @@ +SYSTEM +------ + +.. versionadded:: 3.25 + +Specifies that a target is a ``SYSTEM`` library. This has the following effects: + +* Entries of :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` are treated as ``SYSTEM`` + include directories when compiling consumers. + Entries of :prop_tgt:`INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` are not affected, + and will always be treated as ``SYSTEM`` include directories. + +For imported targets, this property has a default value `ON`, which means that their +`INTERFACE_INCLUDE_DIRECTORIES` are treated as ``SYSTEM`` by default. If their +`SYSTEM` property is `OFF`, then their `INTERFACE_INCLUDE_DIRECTORIES` will not be +treated as ``SYSTEM``, regardless of the value of `IMPORTED_NO_SYSTEM` property. diff --git a/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst b/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst index 8f46d2f..eceddc1 100644 --- a/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst +++ b/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst @@ -41,4 +41,5 @@ The following target properties will be applied on the - :prop_tgt:`XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE` - :prop_tgt:`XCODE_SCHEME_ENVIRONMENT` - :prop_tgt:`XCODE_SCHEME_EXECUTABLE` +- :prop_tgt:`XCODE_SCHEME_LAUNCH_MODE` - :prop_tgt:`XCODE_SCHEME_WORKING_DIRECTORY` diff --git a/Help/prop_tgt/XCODE_SCHEME_LAUNCH_MODE.rst b/Help/prop_tgt/XCODE_SCHEME_LAUNCH_MODE.rst new file mode 100644 index 0000000..df5ae07 --- /dev/null +++ b/Help/prop_tgt/XCODE_SCHEME_LAUNCH_MODE.rst @@ -0,0 +1,22 @@ +XCODE_SCHEME_LAUNCH_MODE +------------------------ + +.. versionadded:: 3.25 + +Property value for ``Launch`` in the Info section of the generated Xcode +scheme. + +Possible values are: + +``AUTO`` + Launch automatically. This is the default. + +``WAIT`` + Wait for the executable to be launched. + +This property is initialized by the value of the variable +:variable:`CMAKE_XCODE_SCHEME_LAUNCH_MODE` if it is set when a target is +created. + +Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property +documentation to see all Xcode schema related properties. diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst new file mode 100644 index 0000000..e4cc01e --- /dev/null +++ b/Help/release/dev/0-sample-topic.rst @@ -0,0 +1,7 @@ +0-sample-topic +-------------- + +* This is a sample release note for the change in a topic. + Developers should add similar notes for each topic branch + making a noteworthy change. Each document should be named + and titled to match the topic name to avoid merge conflicts. diff --git a/Help/release/dev/cmake_language_GET_MESSAGE_LOG_LEVEL.rst b/Help/release/dev/cmake_language_GET_MESSAGE_LOG_LEVEL.rst new file mode 100644 index 0000000..6e99a05 --- /dev/null +++ b/Help/release/dev/cmake_language_GET_MESSAGE_LOG_LEVEL.rst @@ -0,0 +1,6 @@ +cmake-language_GET_MESSAGE_LOG_LEVEL +------------------------------------ + +* The :command:`cmake_language` command gained a new + ``GET_MESSAGE_LOG_LEVEL`` sub-command. It can be used to + query the current message logging level. diff --git a/Help/release/dev/find_item-VALIDATOR.rst b/Help/release/dev/find_item-VALIDATOR.rst new file mode 100644 index 0000000..2cda421 --- /dev/null +++ b/Help/release/dev/find_item-VALIDATOR.rst @@ -0,0 +1,6 @@ +find_item-VALIDATOR +------------------- + +* :command:`find_file`, :command:`find_path`, :command:`find_library`, and + :command:`find_program` commands gain the capability to specify a function + which will be called for each found item to validate it. diff --git a/Help/release/dev/finddoxygen-better-version-checking.rst b/Help/release/dev/finddoxygen-better-version-checking.rst new file mode 100644 index 0000000..3c2215d --- /dev/null +++ b/Help/release/dev/finddoxygen-better-version-checking.rst @@ -0,0 +1,11 @@ +finddoxygen-better-version-checking +----------------------------------- + +* The :module:`FindDoxygen` module now evaluates as many candidate + Doxygen installs as are necessary to satisfy version constraints, + with the package considered to be not found if none are available. + +* The :module:`FindDoxygen` module now handles version ranges. + +* The :module:`FindDoxygen` module now ignores non-semantic portions + of the output from Doxygen's `--version` option. diff --git a/Help/release/dev/findopenal-add-import-library.rst b/Help/release/dev/findopenal-add-import-library.rst new file mode 100644 index 0000000..6c9c93f --- /dev/null +++ b/Help/release/dev/findopenal-add-import-library.rst @@ -0,0 +1,4 @@ +findopenal-add-import-library +----------------------------- + +* The :module:`FindOpenAL` module now provides an imported target. diff --git a/Help/release/dev/findvulkan-volk.rst b/Help/release/dev/findvulkan-volk.rst new file mode 100644 index 0000000..cb77078 --- /dev/null +++ b/Help/release/dev/findvulkan-volk.rst @@ -0,0 +1,5 @@ +findvulkan-volk +--------------- + +* The :module:`FindVulkan` module now includes a ``volk`` component + for the Volk open source vulkan meta-loader. diff --git a/Help/release/dev/system.rst b/Help/release/dev/system.rst new file mode 100644 index 0000000..7cc841e --- /dev/null +++ b/Help/release/dev/system.rst @@ -0,0 +1,15 @@ +system +------ + +* The :prop_tgt:`SYSTEM` target property was added to specify + that a target should be treated as a system library (i.e. + its include directories are automatically ``SYSTEM`` when + compiling consumers). + +* The :prop_tgt:`EXPORT_NO_SYSTEM` target property was added to + specify that :command:`install(EXPORT)` and :command:`export` + commands will generate a imported target with + :prop_tgt:`SYSTEM` property `OFF`. + +* The :prop_tgt:`IMPORTED_NO_SYSTEM` target property was deprecated + in favor of :prop_tgt:`SYSTEM` and :prop_tgt:`EXPORT_NO_SYSTEM`. diff --git a/Help/release/dev/xcode-launch-mode.rst b/Help/release/dev/xcode-launch-mode.rst new file mode 100644 index 0000000..32b9ee6 --- /dev/null +++ b/Help/release/dev/xcode-launch-mode.rst @@ -0,0 +1,7 @@ +xcode-launch-mode +----------------- + +* The :variable:`CMAKE_XCODE_SCHEME_LAUNCH_MODE` variable and corresponding + :prop_tgt:`XCODE_SCHEME_LAUNCH_MODE` target property were added to tell + the :generator:`Xcode` generator what to put in the scheme's "Launch" + mode setting. diff --git a/Help/release/index.rst b/Help/release/index.rst index 4dfac8a..11d5a11 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst @@ -7,6 +7,8 @@ CMake Release Notes This file should include the adjacent "dev.txt" file in development versions but not in release versions. +.. include:: dev.txt + Releases ======== diff --git a/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst b/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst index 3b45d1d..54ea6a4 100644 --- a/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst +++ b/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst @@ -15,3 +15,8 @@ subsequent CMake runs will continue to use the chosen log level. Projects should not set this variable, it is intended for users so that they may control the log level according to their own needs. + +.. versionadded:: 3.25 + See the :command:`cmake_language` + :ref:`cmake_language <query_message_log_level>` command for a way to query + the current message logging level. diff --git a/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE.rst b/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE.rst new file mode 100644 index 0000000..c15b1ea --- /dev/null +++ b/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE.rst @@ -0,0 +1,13 @@ +CMAKE_XCODE_SCHEME_LAUNCH_MODE +------------------------------ + +.. versionadded:: 3.25 + +Property value for ``Launch`` in the Info section of the generated Xcode +scheme. + +This variable initializes the :prop_tgt:`XCODE_SCHEME_LAUNCH_MODE` property on +all targets. + +Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property +documentation to see all Xcode schema related properties. diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake index 2e3ae2c..33509ac 100644 --- a/Modules/Compiler/NVIDIA-CUDA.cmake +++ b/Modules/Compiler/NVIDIA-CUDA.cmake @@ -132,15 +132,19 @@ else() endif() -# FIXME: investigate use of --options-file. -# Tell Makefile generator that nvcc does not support @<rspfile> syntax. -set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0) -set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0) -set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0) - if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0") set(CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG "--options-file ") set(CMAKE_CUDA_RESPONSE_FILE_FLAG "--options-file ") endif() +if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0") + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 1) + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 1) + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 1) +else() + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0) + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0) + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0) +endif() + __compiler_check_default_language_standard(CUDA 6.0 03) diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index 7a381af..d02c589 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake @@ -35,6 +35,12 @@ The following variables may be set to influence this module's behavior: if set ``pkg-config`` will be used to search for a BLAS library first and if one is found that is preferred +``BLA_PKGCONFIG_BLAS`` + .. versionadded:: 3.25 + + If set, the ``pkg-config`` method will look for this module name instead of + just ``blas``. + ``BLA_SIZEOF_INTEGER`` .. versionadded:: 3.22 @@ -273,8 +279,11 @@ endif() include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) if(BLA_PREFER_PKGCONFIG) + if(NOT BLA_PKGCONFIG_BLAS) + set(BLA_PKGCONFIG_BLAS "blas") + endif() find_package(PkgConfig QUIET) - pkg_check_modules(PKGC_BLAS QUIET blas) + pkg_check_modules(PKGC_BLAS QUIET ${BLA_PKGCONFIG_BLAS}) if(PKGC_BLAS_FOUND) set(BLAS_FOUND ${PKGC_BLAS_FOUND}) set(BLAS_LIBRARIES "${PKGC_BLAS_LINK_LIBRARIES}") diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index af5f798..470111f 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake @@ -799,7 +799,9 @@ if(NOT "${CUDA_TOOLKIT_ROOT_DIR}" STREQUAL "${CUDA_TOOLKIT_ROOT_DIR_INTERNAL}") unset(CUDA_VERSION CACHE) endif() -if(NOT "${CUDA_TOOLKIT_TARGET_DIR}" STREQUAL "${CUDA_TOOLKIT_TARGET_DIR_INTERNAL}") +# If CUDA_TOOLKIT_TARGET_DIR exists, check if it has changed. +if(DEFINED CUDA_TOOLKIT_TARGET_DIR + AND NOT "${CUDA_TOOLKIT_TARGET_DIR}" STREQUAL "${CUDA_TOOLKIT_TARGET_DIR_INTERNAL}") cuda_unset_include_and_libraries() endif() diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake index e37d225..279e263 100644 --- a/Modules/FindCURL.cmake +++ b/Modules/FindCURL.cmake @@ -72,6 +72,8 @@ if(NOT CURL_NO_CURL_CMAKE) # can print what we found and return. if(CURL_FOUND) find_package_handle_standard_args(CURL HANDLE_COMPONENTS CONFIG_MODE) + # The upstream curl package sets CURL_VERSION, not CURL_VERSION_STRING. + set(CURL_VERSION_STRING "${CURL_VERSION}") return() endif() endif() diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake index 4a16e31..23b7107 100644 --- a/Modules/FindDoxygen.cmake +++ b/Modules/FindDoxygen.cmake @@ -432,9 +432,44 @@ endif() # or use something like homebrew. # ============== End OSX stuff ================ +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) + # # Find Doxygen... # +function(_Doxygen_get_version doxy_version result_var doxy_path) + execute_process( + COMMAND "${doxy_path}" --version + OUTPUT_VARIABLE full_doxygen_version + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE version_result + ) + + # Ignore any commit hashes, etc. + string(REGEX MATCH [[^[0-9]+\.[0-9]+\.[0-9]+]] sem_doxygen_version "${full_doxygen_version}") + + set(${result_var} ${version_result} PARENT_SCOPE) + set(${doxy_version} ${sem_doxygen_version} PARENT_SCOPE) +endfunction() + +function(_Doxygen_version_validator version_match doxy_path) + if(NOT DEFINED Doxygen_FIND_VERSION) + set(${is_valid_version} TRUE PARENT_SCOPE) + else() + _Doxygen_get_version(candidate_version version_result "${doxy_path}") + + if(version_result) + message(DEBUG "Unable to determine candidate doxygen version at ${doxy_path}: ${version_result}") + endif() + + find_package_check_version("${candidate_version}" valid_doxy_version + HANDLE_VERSION_RANGE + ) + + set(${version_match} "${valid_doxy_version}" PARENT_SCOPE) + endif() +endfunction() + macro(_Doxygen_find_doxygen) find_program( DOXYGEN_EXECUTABLE @@ -446,16 +481,13 @@ macro(_Doxygen_find_doxygen) /Applications/Utilities/Doxygen.app/Contents/Resources /Applications/Utilities/Doxygen.app/Contents/MacOS DOC "Doxygen documentation generation tool (http://www.doxygen.org)" + VALIDATOR _Doxygen_version_validator ) mark_as_advanced(DOXYGEN_EXECUTABLE) if(DOXYGEN_EXECUTABLE) - execute_process( - COMMAND "${DOXYGEN_EXECUTABLE}" --version - OUTPUT_VARIABLE DOXYGEN_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE _Doxygen_version_result - ) + _Doxygen_get_version(DOXYGEN_VERSION _Doxygen_version_result "${DOXYGEN_EXECUTABLE}") + if(_Doxygen_version_result) message(WARNING "Unable to determine doxygen version: ${_Doxygen_version_result}") endif() @@ -642,11 +674,11 @@ endforeach() unset(_comp) # Verify find results -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args( Doxygen REQUIRED_VARS DOXYGEN_EXECUTABLE VERSION_VAR DOXYGEN_VERSION + HANDLE_VERSION_RANGE HANDLE_COMPONENTS ) diff --git a/Modules/FindGLEW.cmake b/Modules/FindGLEW.cmake index b9ebe08..bfde40b 100644 --- a/Modules/FindGLEW.cmake +++ b/Modules/FindGLEW.cmake @@ -63,11 +63,36 @@ This module defines the following variables: #]=======================================================================] include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) find_package(GLEW CONFIG QUIET) if(GLEW_FOUND) find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_CONFIG) + get_target_property(GLEW_INCLUDE_DIRS GLEW::GLEW INTERFACE_INCLUDE_DIRECTORIES) + set(GLEW_INCLUDE_DIR ${GLEW_INCLUDE_DIRS}) + get_target_property(_GLEW_DEFS GLEW::GLEW INTERFACE_COMPILE_DEFINITIONS) + if("${_GLEW_DEFS}" MATCHES "GLEW_STATIC") + get_target_property(GLEW_LIBRARY_DEBUG GLEW::GLEW IMPORTED_LOCATION_DEBUG) + get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_LOCATION_RELEASE) + else() + get_target_property(GLEW_LIBRARY_DEBUG GLEW::GLEW IMPORTED_IMPLIB_DEBUG) + get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_IMPLIB_RELEASE) + endif() + get_target_property(_GLEW_LINK_INTERFACE GLEW::GLEW IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE) # same for debug and release + list(APPEND GLEW_LIBRARIES ${_GLEW_LINK_INTERFACE}) + list(APPEND GLEW_LIBRARY ${_GLEW_LINK_INTERFACE}) + select_library_configurations(GLEW) + if("${_GLEW_DEFS}" MATCHES "GLEW_STATIC") + set(GLEW_STATIC_LIBRARIES ${GLEW_LIBRARIES}) + else() + set(GLEW_SHARED_LIBRARIES ${GLEW_LIBRARIES}) + endif() + unset(_GLEW_DEFS) + unset(_GLEW_LINK_INTERFACE) + unset(GLEW_LIBRARY) + unset(GLEW_LIBRARY_DEBUG) + unset(GLEW_LIBRARY_RELEASE) return() endif() @@ -171,8 +196,6 @@ find_library(GLEW_STATIC_LIBRARY_DEBUG set(CMAKE_FIND_LIBRARY_SUFFIXES ${__GLEW_CURRENT_FIND_LIBRARY_SUFFIXES}) unset(__GLEW_CURRENT_FIND_LIBRARY_SUFFIXES) -include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) - select_library_configurations(GLEW_SHARED) select_library_configurations(GLEW_STATIC) diff --git a/Modules/FindICU.cmake b/Modules/FindICU.cmake index 91dcba5..b4f4d71 100644 --- a/Modules/FindICU.cmake +++ b/Modules/FindICU.cmake @@ -254,13 +254,6 @@ function(_ICU_FIND) set("${component_found}" ON) set("${component_found_compat}" ON) list(APPEND ICU_LIBRARY "${${component_cache}}") - endif() - mark_as_advanced("${component_found}") - mark_as_advanced("${component_found_compat}") - set("${component_cache}" "${${component_cache}}" PARENT_SCOPE) - set("${component_found}" "${${component_found}}" PARENT_SCOPE) - set("${component_found_compat}" "${${component_found_compat}}" PARENT_SCOPE) - if(component_found OR component_found_compat) if (ICU_FIND_REQUIRED_${component}) list(APPEND ICU_LIBS_FOUND "${component} (required): ${${component_cache}}") else() @@ -274,6 +267,11 @@ function(_ICU_FIND) list(APPEND ICU_LIBS_NOTFOUND "${component} (optional)") endif() endif() + mark_as_advanced("${component_found}") + mark_as_advanced("${component_found_compat}") + set("${component_cache}" "${${component_cache}}" PARENT_SCOPE) + set("${component_found}" "${${component_found}}" PARENT_SCOPE) + set("${component_found_compat}" "${${component_found_compat}}" PARENT_SCOPE) endforeach() set(_ICU_REQUIRED_LIBS_FOUND "${ICU_REQUIRED_LIBS_FOUND}" PARENT_SCOPE) set(ICU_LIBRARY "${ICU_LIBRARY}" PARENT_SCOPE) diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index b08b359..4151116 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -35,6 +35,13 @@ The following variables may be set to influence this module's behavior: if set ``pkg-config`` will be used to search for a LAPACK library first and if one is found that is preferred +``BLA_PKGCONFIG_LAPACK`` + .. versionadded:: 3.25 + + If set, the ``pkg-config`` method will look for this module name instead of + just ``lapack``. + + ``BLA_SIZEOF_INTEGER`` .. versionadded:: 3.22 @@ -278,8 +285,11 @@ endif() # Search with pkg-config if specified if(BLA_PREFER_PKGCONFIG) + if(NOT BLA_PKGCONFIG_LAPACK) + set(BLA_PKGCONFIG_LAPACK "lapack") + endif() find_package(PkgConfig QUIET) - pkg_check_modules(PKGC_LAPACK QUIET lapack) + pkg_check_modules(PKGC_LAPACK QUIET ${BLA_PKGCONFIG_LAPACK}) if(PKGC_LAPACK_FOUND) set(LAPACK_FOUND TRUE) set(LAPACK_LIBRARIES "${PKGC_LAPACK_LINK_LIBRARIES}") diff --git a/Modules/FindOpenAL.cmake b/Modules/FindOpenAL.cmake index b5b92c5..53aafdc 100644 --- a/Modules/FindOpenAL.cmake +++ b/Modules/FindOpenAL.cmake @@ -29,6 +29,16 @@ OpenAL is searched in the following order: 5. Manually compiled framework: ``/Library/Frameworks``. 6. Add-on package: ``/opt``. +IMPORTED Targets +^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.25 + +This module defines the :prop_tgt:`IMPORTED` target: + +``OpenAL::OpenAL`` + The OpenAL library, if found. + Result Variables ^^^^^^^^^^^^^^^^ @@ -94,3 +104,19 @@ find_package_handle_standard_args( ) mark_as_advanced(OPENAL_LIBRARY OPENAL_INCLUDE_DIR) + +if(OPENAL_INCLUDE_DIR AND OPENAL_LIBRARY) + if(NOT TARGET OpenAL::OpenAL) + if(EXISTS "${OPENAL_LIBRARY}") + add_library(OpenAL::OpenAL UNKNOWN IMPORTED) + set_target_properties(OpenAL::OpenAL PROPERTIES + IMPORTED_LOCATION "${OPENAL_LIBRARY}") + else() + add_library(OpenAL::OpenAL INTERFACE IMPORTED) + set_target_properties(OpenAL::OpenAL PROPERTIES + IMPORTED_LIBNAME "${OPENAL_LIBRARY}") + endif() + set_target_properties(OpenAL::OpenAL PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${OPENAL_INCLUDE_DIR}") + endif() +endif() diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index ecfb7f9..d19624e 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -243,6 +243,23 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Parsed ${LANG} OpenMP implicit link information from above output:\n${OpenMP_${LANG}_LOG_VAR}\n\n") + # For LCC we should additionally alanyze -print-search-dirs output + # to check for additional implicit_dirs. + # Note: This won't work if CMP0129 policy is set to OLD! + if("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "LCC") + execute_process( + COMMAND ${CMAKE_${LANG}_COMPILER} -print-search-dirs + OUTPUT_VARIABLE output_lines + COMMAND_ERROR_IS_FATAL ANY + ERROR_QUIET) + if("${output_lines}" MATCHES ".*\nlibraries:[ \t]+(.*:)\n.*") + string(REPLACE ":" ";" implicit_dirs_addon "${CMAKE_MATCH_1}") + list(PREPEND OpenMP_${LANG}_IMPLICIT_LINK_DIRS ${implicit_dirs_addon}) + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + " Extended OpenMP library search paths: [${implicit_dirs}]\n") + endif() + endif() + unset(_OPENMP_LIB_NAMES) foreach(_OPENMP_IMPLICIT_LIB IN LISTS OpenMP_${LANG}_IMPLICIT_LIBRARIES) get_filename_component(_OPENMP_IMPLICIT_LIB_DIR "${_OPENMP_IMPLICIT_LIB}" DIRECTORY) diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index 9278566..352c4cc 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake @@ -90,13 +90,25 @@ This module will set the following variables in your project: Hints ^^^^^ -Set ``OPENSSL_ROOT_DIR`` to the root directory of an OpenSSL installation. +The following variables may be set to control search behavior: -.. versionadded:: 3.4 - Set ``OPENSSL_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries. +``OPENSSL_ROOT_DIR`` + Set to the root directory of an OpenSSL installation. + +``OPENSSL_USE_STATIC_LIBS`` + .. versionadded:: 3.4 + + Set to ``TRUE`` to look for static libraries. + +``OPENSSL_MSVC_STATIC_RT`` + .. versionadded:: 3.5 + + Set to ``TRUE`` to choose the MT version of the lib. -.. versionadded:: 3.5 - Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib. +``ENV{PKG_CONFIG_PATH}`` + On UNIX-like systems, ``pkg-config`` is used to locate the system OpenSSL. + Set the ``PKG_CONFIG_PATH`` environment varialbe to look in alternate + locations. Useful on multi-lib systems. #]=======================================================================] macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library) diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index c4fae6b..53519ba 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake @@ -14,6 +14,8 @@ cmake_policy(PUSH) cmake_policy (SET CMP0012 NEW) # IN_LIST operator cmake_policy (SET CMP0057 NEW) +# registry view behavior +cmake_policy (SET CMP0134 NEW) if (NOT DEFINED _PYTHON_PREFIX) message (FATAL_ERROR "FindPython: INTERNAL ERROR") @@ -175,30 +177,31 @@ function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS) foreach (version IN LISTS _PGR_VERSION) string (REPLACE "." "" version_no_dots ${version}) list (APPEND registries - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]) + [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] + [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath]) if (version VERSION_GREATER_EQUAL "3.5") + # cmake_host_system_information is not usable in bootstrap get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${version};SysArchitecture]" NAME) if (arch MATCHES "(${_${_PYTHON_PREFIX}_ARCH}|${_${_PYTHON_PREFIX}_ARCH2})bit") list (APPEND registries - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath]) + [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) endif() else() list (APPEND registries - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath]) + [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) endif() list (APPEND registries - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]) + [HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] + [HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath] + [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] + [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath] + [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}/InstallPath] + [HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] + [HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath]) endforeach() elseif (implementation STREQUAL "IronPython") foreach (version IN LISTS _PGR_VERSION) - list (APPEND registries [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${version}\\InstallPath]) + list (APPEND registries [HKEY_LOCAL_MACHINE/SOFTWARE/IronPython/${version}/InstallPath]) endforeach() endif() endforeach() @@ -711,7 +714,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) if (_PVI_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_EXECUTABLE}") # interpreter does not exist anymore - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() @@ -732,7 +735,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) endif() if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() @@ -748,7 +751,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) OUTPUT_STRIP_TRAILING_WHITESPACE) if (result) # interpreter is not usable - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() @@ -773,7 +776,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) if (_PVI_EXACT AND NOT version VERSION_EQUAL _PVI_VERSION) # interpreter has wrong version - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() else() @@ -782,7 +785,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVI_VERSION}") if (NOT major_version VERSION_EQUAL expected_major_version OR NOT version VERSION_GREATER_EQUAL _PVI_VERSION) - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() @@ -794,7 +797,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # interpreter has invalid version - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() @@ -813,9 +816,9 @@ function (_PYTHON_VALIDATE_INTERPRETER) if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # interpreter not usable or has wrong major version if (result) - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") else() - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() @@ -836,9 +839,9 @@ function (_PYTHON_VALIDATE_INTERPRETER) if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P) # interpreter not usable or has wrong architecture if (result) - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") else() - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() @@ -846,6 +849,14 @@ function (_PYTHON_VALIDATE_INTERPRETER) endif() endfunction() +function(_python_validate_find_interpreter status interpreter) + set(_${_PYTHON_PREFIX}_EXECUTABLE "${interpreter}" CACHE FILEPATH "" FORCE) + _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + set (${status} FALSE PARENT_SCOPE) + endif() +endfunction() + function (_PYTHON_VALIDATE_COMPILER) if (NOT _${_PYTHON_PREFIX}_COMPILER) @@ -856,7 +867,7 @@ function (_PYTHON_VALIDATE_COMPILER) if (_PVC_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_COMPILER}") # Compiler does not exist anymore - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() @@ -883,7 +894,7 @@ function (_PYTHON_VALIDATE_COMPILER) file (REMOVE_RECURSE "${working_dir}") if (result) # compiler is not usable - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() @@ -909,7 +920,7 @@ function (_PYTHON_VALIDATE_COMPILER) if (_PVC_EXACT AND NOT version VERSION_EQUAL _PVC_VERSION) # interpreter has wrong version - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() else() @@ -918,7 +929,7 @@ function (_PYTHON_VALIDATE_COMPILER) string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVC_VERSION}") if (NOT major_version VERSION_EQUAL expected_major_version OR NOT version VERSION_GREATER_EQUAL _PVC_VERSION) - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() @@ -930,7 +941,7 @@ function (_PYTHON_VALIDATE_COMPILER) find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # interpreter has invalid version - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() @@ -939,13 +950,21 @@ function (_PYTHON_VALIDATE_COMPILER) string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") if (NOT major_version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # Compiler has wrong major version - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() endif() endfunction() +function(_python_validate_find_compiler status compiler) + set(_${_PYTHON_PREFIX}_COMPILER "${compiler}" CACHE FILEPATH "" FORCE) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (NOT _${_PYTHON_PREFIX}_COMPILER) + set (${status} FALSE PARENT_SCOPE) + endif() +endfunction() + function (_PYTHON_VALIDATE_LIBRARY) if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) @@ -957,7 +976,7 @@ function (_PYTHON_VALIDATE_LIBRARY) if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") # library does not exist anymore - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") if (WIN32) set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") @@ -971,7 +990,7 @@ function (_PYTHON_VALIDATE_LIBRARY) if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") else() if (_PVL_VERSION OR _PVL_IN_RANGE) @@ -980,7 +999,7 @@ function (_PYTHON_VALIDATE_LIBRARY) string (REGEX MATCH "[0-9](\\.[0-9]+)?" version "${_PVL_VERSION}") if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL version) OR (lib_VERSION VERSION_LESS version)) # library has wrong version - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() endif() @@ -990,14 +1009,14 @@ function (_PYTHON_VALIDATE_LIBRARY) find_package_check_version ("${lib_VERSION}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # library has wrong version - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() endif() else() if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # library has wrong major version - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() endif() @@ -1023,7 +1042,7 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR) if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") # include file does not exist anymore - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") return() endif() @@ -1033,14 +1052,14 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR) if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") else() if (_PVID_VERSION OR _PVID_IN_RANGE) if (_PVID_VERSION) if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version)) # include dir has wrong version - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endif() @@ -1050,14 +1069,14 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR) find_package_check_version ("${inc_VERSION}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # include dir has wrong version - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endif() else() if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # include dir has wrong major version - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endif() @@ -1247,12 +1266,14 @@ if (DEFINED ${_PYTHON_PREFIX}_FIND_STRATEGY) endif() # Python and Anaconda distributions: define which architectures can be used +unset (_${_PYTHON_PREFIX}_REGISTRY_VIEW) if (CMAKE_SIZEOF_VOID_P) math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) # In this case, search only for 64bit or 32bit set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) + set (_${_PYTHON_PREFIX}_REGISTRY_VIEW REGISTRY_VIEW ${_${_PYTHON_PREFIX}_ARCH}) else() if (_${_PYTHON_PREFIX}_ARCH EQUAL "32") set (_${_PYTHON_PREFIX}_ARCH2 64) @@ -1506,9 +1527,13 @@ endfunction() unset (_${_PYTHON_PREFIX}_REQUIRED_VARS) unset (_${_PYTHON_PREFIX}_CACHED_VARS) unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE INTERNAL "Interpreter reason failure") unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE INTERNAL "Compiler reason failure") unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE INTERNAL "Development reason failure") unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE INTERNAL "NumPy reason failure") # preamble @@ -1590,9 +1615,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - - _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1612,8 +1636,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1626,9 +1650,10 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1641,8 +1666,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1650,8 +1675,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) - _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1663,8 +1688,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) - _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1676,8 +1701,9 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) - _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1687,9 +1713,9 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) endwhile() else() # look-up for various versions and locations - set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS EXACT) + set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) - list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE) endif() foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) @@ -1698,6 +1724,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS}) # Virtual environments handling if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") @@ -1710,8 +1737,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1731,7 +1758,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) endif() # Windows registry @@ -1742,11 +1770,12 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) endif() - _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1758,21 +1787,18 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() + # try using standard paths. - # NAMES_PER_DIR is not defined on purpose to have a chance to find - # expected version. - # For example, typical systems have 'python' for version 2.* and 'python3' - # for version 3.*. So looking for names per dir will find, potentially, - # systematically 'python' (i.e. version 2) even if version 3 is searched. find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) - _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NAMES_PER_DIR + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_interpreter) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1784,7 +1810,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) endif() # Windows registry @@ -1794,10 +1821,11 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) endif() - _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() @@ -1806,15 +1834,12 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) if (NOT _${_PYTHON_PREFIX}_EXECUTABLE AND NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") # No specific version found. Retry with generic names and standard paths. - # NAMES_PER_DIR is not defined on purpose to have a chance to find - # expected version. - # For example, typical systems have 'python' for version 2.* and 'python3' - # for version 3.*. So looking for names per dir will find, potentially, - # systematically 'python' (i.e. version 2) even if version 3 is searched. _python_get_names (_${_PYTHON_PREFIX}_NAMES POSIX INTERPRETER) + unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS) find_program (_${_PYTHON_PREFIX}_EXECUTABLE - NAMES ${_${_PYTHON_PREFIX}_NAMES}) - _python_validate_interpreter () + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + VALIDATOR _python_validate_find_interpreter) endif() endif() endif() @@ -1836,7 +1861,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) # Interpreter is not usable set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE FALSE) unset (${_PYTHON_PREFIX}_VERSION) - set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() endif() @@ -1884,7 +1909,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) endif() if (${_PYTHON_PREFIX}_Interpreter_FOUND) - unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE) + unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE) # compute and save interpreter signature string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") @@ -2058,8 +2083,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2072,9 +2097,10 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2087,8 +2113,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2097,8 +2123,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) - _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2110,12 +2136,13 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) - _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() + # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_COMPILER @@ -2123,8 +2150,9 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) - _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2134,9 +2162,9 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) endwhile() else() # try using root dir and registry - set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS EXACT) + set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) - list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE) endif() foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) @@ -2157,6 +2185,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS}) + # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_COMPILER @@ -2168,8 +2198,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2182,9 +2212,10 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2197,8 +2228,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2210,8 +2241,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) - _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2223,8 +2254,9 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} - NO_DEFAULT_PATH) - _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() @@ -2240,11 +2272,13 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} COMPILER) + unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS) find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) - _python_validate_compiler () + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_compiler) endif() endif() @@ -2285,7 +2319,7 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) else() # compiler not usable set (_${_PYTHON_PREFIX}_COMPILER_USABLE FALSE) - set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") endif() file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") endif() @@ -2304,7 +2338,7 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) endif() if (${_PYTHON_PREFIX}_Compiler_FOUND) - unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE) + unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE) # compute and save compiler signature string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") @@ -2786,7 +2820,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() @@ -2939,7 +2973,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") - set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() @@ -3033,7 +3067,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development.Embed_FOUND) OR (NOT "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development.Module_FOUND)) - unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE) + unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE) endif() if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS @@ -3144,7 +3178,7 @@ if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Inte set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") if(_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") - set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_NumPy_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR-NOTFOUND") endif() @@ -3166,7 +3200,7 @@ if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Inte endif() if (${_PYTHON_PREFIX}_NumPy_FOUND) - unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE) + unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE) # compute and save numpy signature string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${${_PYTHON_PREFIX}_NumPyINCLUDE_DIR}") @@ -3196,7 +3230,7 @@ unset (_${_PYTHON_PREFIX}_REASON_FAILURE) foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy) if (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE) string (APPEND _${_PYTHON_PREFIX}_REASON_FAILURE "\n ${_${_PYTHON_PREFIX}_COMPONENT}: ${_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE}") - unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE) + unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE CACHE) endif() endforeach() diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake index a80758d..b14349f 100644 --- a/Modules/FindRuby.cmake +++ b/Modules/FindRuby.cmake @@ -369,56 +369,17 @@ if(Ruby_EXECUTABLE AND NOT Ruby_VERSION_MAJOR) set(Ruby_VERSION_MAJOR 1) set(Ruby_VERSION_MINOR 9) endif() - # check whether we found 2.0.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?0") + # check whether we found 2.[0-7].x + if(${Ruby_EXECUTABLE} MATCHES "ruby2") set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 0) + string(REGEX_REPLACE ${Ruby_EXECUTABLE} "ruby2\\.?([0-7])" "\\1" Ruby_VERSION_MINOR) endif() - # check whether we found 2.1.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?1") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 1) - endif() - # check whether we found 2.2.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?2") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 2) - endif() - # check whether we found 2.3.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?3") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 3) - endif() - # check whether we found 2.4.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?4") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 4) - endif() - # check whether we found 2.5.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?5") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 5) - endif() - # check whether we found 2.6.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?6") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 6) - endif() - # check whether we found 2.7.x - if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?7") - set(Ruby_VERSION_MAJOR 2) - set(Ruby_VERSION_MINOR 7) - endif() - # check whether we found 3.0.x - if(${Ruby_EXECUTABLE} MATCHES "ruby3\\.?0") - set(Ruby_VERSION_MAJOR 3) - set(Ruby_VERSION_MINOR 0) - endif() - # check whether we found 3.1.x - if(${Ruby_EXECUTABLE} MATCHES "ruby3\\.?1") + # check whether we found 3.[0-1].x + if(${Ruby_EXECUTABLE} MATCHES "ruby3") set(Ruby_VERSION_MAJOR 3) - set(Ruby_VERSION_MINOR 1) + string(REGEX_REPLACE ${Ruby_EXECUTABLE} "ruby3\\.?([0-1])" "\\1" Ruby_VERSION_MINOR) endif() + endif() if(Ruby_VERSION_MAJOR) diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake index 8b322ed..78b07c4 100644 --- a/Modules/FindVulkan.cmake +++ b/Modules/FindVulkan.cmake @@ -70,6 +70,11 @@ This module defines :prop_tgt:`IMPORTED` targets if Vulkan has been found: Defined if SDK has the Khronos library which implement a subset of Vulkan API over Apple Metal graphics framework. (MoltenVK). +``Vulkan::volk`` + .. versionadded:: 3.25 + + Defined if SDK has the Vulkan meta-loader (volk). + Result Variables ^^^^^^^^^^^^^^^^ @@ -109,6 +114,10 @@ This module defines the following variables: .. versionadded:: 3.24 True, if the SDK has the MoltenVK library. +``Vulkan_volk_FOUND`` + .. versionadded:: 3.25 + + True, if the SDK has the volk library. The module will also defines these cache variables: @@ -137,6 +146,11 @@ The module will also defines these cache variables: Path to the MoltenVK library. +``Vulkan_volk_LIBRARY`` + .. versionadded:: 3.25 + + Path to the volk library. + Hints ^^^^^ @@ -374,6 +388,13 @@ if(MoltenVK IN_LIST Vulkan_FIND_COMPONENTS) ) mark_as_advanced(Vulkan_MoltenVK_INCLUDE_DIR) endif() +if(volk IN_LIST Vulkan_FIND_COMPONENTS) + find_library(Vulkan_volk_LIBRARY + NAMES volk + HINTS + ${_Vulkan_hint_library_search_paths}) + mark_as_advanced(Vulkan_Volk_LIBRARY) +endif() if(Vulkan_GLSLC_EXECUTABLE) set(Vulkan_glslc_FOUND TRUE) @@ -437,6 +458,7 @@ _Vulkan_set_library_component_found(glslang glslang-genericcodegen) _Vulkan_set_library_component_found(shaderc_combined) _Vulkan_set_library_component_found(SPIRV-Tools) +_Vulkan_set_library_component_found(volk) if(Vulkan_MoltenVK_INCLUDE_DIR AND Vulkan_MoltenVK_LIBRARY) set(Vulkan_MoltenVK_FOUND TRUE) @@ -723,6 +745,25 @@ if(Vulkan_FOUND) IMPORTED_LOCATION_DEBUG "${Vulkan_SPIRV-Tools_DEBUG_LIBRARY}") endif() endif() + + if(Vulkan_volk_LIBRARY AND NOT TARGET Vulkan::volk) + add_library(Vulkan::volk STATIC IMPORTED) + set_property(TARGET Vulkan::volk + PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}") + set_property(TARGET Vulkan::volk APPEND + PROPERTY + IMPORTED_CONFIGURATIONS Release) + set_property(TARGET Vulkan::volk APPEND + PROPERTY + IMPORTED_LOCATION_RELEASE "${Vulkan_volk_LIBRARY}") + + if (NOT WIN32) + set_property(TARGET Vulkan::volk APPEND + PROPERTY + IMPORTED_LINK_INTERFACE_LIBRARIES dl) + endif() + endif() endif() if(Vulkan_MoltenVK_FOUND) diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index 5a532c7..a9aa8e0 100644 --- a/Modules/Platform/AIX-GNU.cmake +++ b/Modules/Platform/AIX-GNU.cmake @@ -23,11 +23,11 @@ macro(__aix_compiler_gnu lang) # Construct the export list ourselves to pass only the object files so # that we export only the symbols actually provided by the sources. set(CMAKE_${lang}_CREATE_SHARED_LIBRARY - "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS> <OBJECTS>" + "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp -c <CMAKE_${lang}_COMPILER> <AIX_EXPORTS> <OBJECTS>" "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS - "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>" + "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -c <CMAKE_${lang}_COMPILER> -l . <AIX_EXPORTS> <OBJECTS>" "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>") endmacro() diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake index 2a8c159..902cbb3 100644 --- a/Modules/Platform/AIX-XL.cmake +++ b/Modules/Platform/AIX-XL.cmake @@ -29,12 +29,12 @@ macro(__aix_compiler_xl lang) # Construct the export list ourselves to pass only the object files so # that we export only the symbols actually provided by the sources. set(CMAKE_${lang}_CREATE_SHARED_LIBRARY - "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS>${_OBJECTS}" + "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp -c <CMAKE_${lang}_COMPILER> <AIX_EXPORTS>${_OBJECTS}" "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS - "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>" + "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -c <CMAKE_${lang}_COMPILER> -l . <AIX_EXPORTS> <OBJECTS>" "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>") unset(_OBJECTS) diff --git a/Modules/Platform/AIX/ExportImportList b/Modules/Platform/AIX/ExportImportList index 891bce7..5e16fcb 100755 --- a/Modules/Platform/AIX/ExportImportList +++ b/Modules/Platform/AIX/ExportImportList @@ -5,7 +5,7 @@ # This script is internal to CMake and meant only to be # invoked by CMake-generated build systems on AIX. -usage='usage: ExportImportList -o <out-file> [-l <lib>] [-n] [--] <objects>...' +usage='usage: ExportImportList -o <out-file> -c <compiler> [-l <lib>] [-n] [--] <objects>...' die() { echo "$@" 1>&2; exit 1 @@ -15,11 +15,13 @@ die() { out='' lib='' no_objects='' +compiler='' while test "$#" != 0; do case "$1" in -l) shift; lib="$1" ;; -o) shift; out="$1" ;; -n) no_objects='1' ;; + -c) shift; compiler="$1" ;; --) shift; break ;; -*) die "$usage" ;; *) break ;; @@ -27,27 +29,47 @@ while test "$#" != 0; do shift done test -n "$out" || die "$usage" +# We need the compiler executable to resolve where the ibm-llvm-nm executable is +test -n "$compiler" || die "$usage" # Build a temporary file that atomically replaces the output later. out_tmp="$out.tmp$$" trap 'rm -f "$out_tmp"' EXIT INT TERM > "$out_tmp" +# If IPA was enabled and a compiler from the IBMClang family is used, then +# the object files contain LLVM bitcode[0] rather than XCOFF objects and so +# need to be handled differently. +# +# [0]: https://www.ibm.com/docs/en/openxl-c-and-cpp-aix/17.1.0?topic=compatibility-link-time-optimization-lto +NM="$(dirname "$compiler")/../libexec/ibm-llvm-nm" + +function IsBitcode { + # N4 = first 4 bytes, -tx = output in hexadecimal, -An = don't display offset + # cut: trim off the preceding whitespace where the offset would be + # 4243code is the hexadecimal magic number for LLVM bitcode + [ "$(od -N4 -tx -An $1 | cut -d ' ' -f 2)" == "4243c0de" ]; +} + # Collect symbols exported from all object files. if test -z "$no_objects"; then for f in "$@"; do - dump -tov -X 32_64 "$f" | - awk ' - BEGIN { - V["EXPORTED"]=" export" - V["PROTECTED"]=" protected" - } - /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / { - if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) { - print $NF V[$(NF-1)] + if IsBitcode "$f"; then + "$NM" "$f" --defined-only --extern-only --just-symbol-name 2>/dev/null + else + dump -tov -X 32_64 "$f" | + awk ' + BEGIN { + V["EXPORTED"]=" export" + V["PROTECTED"]=" protected" + } + /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / { + if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) { + print $NF V[$(NF-1)] + } } - } - ' + ' + fi done >> "$out_tmp" fi diff --git a/Modules/Platform/SerenityOS-Clang-ASM.cmake b/Modules/Platform/SerenityOS-Clang-ASM.cmake new file mode 100644 index 0000000..ba1e18c --- /dev/null +++ b/Modules/Platform/SerenityOS-Clang-ASM.cmake @@ -0,0 +1,2 @@ +include(Platform/SerenityOS-GNU) +__serenity_compiler_gnu(ASM) diff --git a/Modules/Platform/SerenityOS-Clang-C.cmake b/Modules/Platform/SerenityOS-Clang-C.cmake new file mode 100644 index 0000000..791a197 --- /dev/null +++ b/Modules/Platform/SerenityOS-Clang-C.cmake @@ -0,0 +1,2 @@ +include(Platform/SerenityOS-GNU) +__serenity_compiler_gnu(C) diff --git a/Modules/Platform/SerenityOS-Clang-CXX.cmake b/Modules/Platform/SerenityOS-Clang-CXX.cmake new file mode 100644 index 0000000..084e319 --- /dev/null +++ b/Modules/Platform/SerenityOS-Clang-CXX.cmake @@ -0,0 +1,2 @@ +include(Platform/SerenityOS-GNU) +__serenity_compiler_gnu(CXX) diff --git a/Modules/Platform/SerenityOS-GNU-ASM.cmake b/Modules/Platform/SerenityOS-GNU-ASM.cmake new file mode 100644 index 0000000..ba1e18c --- /dev/null +++ b/Modules/Platform/SerenityOS-GNU-ASM.cmake @@ -0,0 +1,2 @@ +include(Platform/SerenityOS-GNU) +__serenity_compiler_gnu(ASM) diff --git a/Modules/Platform/SerenityOS-GNU-C.cmake b/Modules/Platform/SerenityOS-GNU-C.cmake new file mode 100644 index 0000000..791a197 --- /dev/null +++ b/Modules/Platform/SerenityOS-GNU-C.cmake @@ -0,0 +1,2 @@ +include(Platform/SerenityOS-GNU) +__serenity_compiler_gnu(C) diff --git a/Modules/Platform/SerenityOS-GNU-CXX.cmake b/Modules/Platform/SerenityOS-GNU-CXX.cmake new file mode 100644 index 0000000..084e319 --- /dev/null +++ b/Modules/Platform/SerenityOS-GNU-CXX.cmake @@ -0,0 +1,2 @@ +include(Platform/SerenityOS-GNU) +__serenity_compiler_gnu(CXX) diff --git a/Modules/Platform/SerenityOS-GNU.cmake b/Modules/Platform/SerenityOS-GNU.cmake new file mode 100644 index 0000000..ed39477 --- /dev/null +++ b/Modules/Platform/SerenityOS-GNU.cmake @@ -0,0 +1,24 @@ +# This module is shared by multiple languages; use include blocker. +include_guard() + +set(CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--hash-style=gnu,-z,relro,-z,now,-z,noexecstack,-z,separate-code,-z,max-page-size=0x1000") + +macro(__serenity_compiler_gnu lang) + set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG "-Wl,-rpath,") + set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ":") + set(CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG "-Wl,-rpath-link,") + set(CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG "-Wl,-soname,") + set(CMAKE_EXE_EXPORTS_${lang}_FLAG "-Wl,--export-dynamic") + + set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared -Wl,--hash-style=gnu,-z,relro,-z,now,-z,noexecstack,-z,separate-code") + + # Initialize link type selection flags. These flags are used when + # building a shared library, shared module, or executable that links + # to other libraries to select whether to use the static or shared + # versions of the libraries. + foreach(type SHARED_LIBRARY SHARED_MODULE EXE) + set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS "-Wl,-Bstatic") + set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic") + endforeach() + +endmacro() diff --git a/Modules/Platform/SerenityOS.cmake b/Modules/Platform/SerenityOS.cmake new file mode 100644 index 0000000..541620c --- /dev/null +++ b/Modules/Platform/SerenityOS.cmake @@ -0,0 +1,12 @@ + +set(SERENITYOS 1) + +set(CMAKE_DL_LIBS "-ldl") +set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") + +# Shared libraries with no builtin soname may not be linked safely by +# specifying the file path. +set(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1) + +include(Platform/UnixPaths) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 95b07cb..fe92716 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -197,6 +197,8 @@ set(SRCS cmCustomCommandLines.cxx cmCustomCommandLines.h cmCustomCommandTypes.h + cmCxxModuleMapper.cxx + cmCxxModuleMapper.h cmDefinitions.cxx cmDefinitions.h cmDependencyProvider.h @@ -543,6 +545,8 @@ set(SRCS cmExecuteProcessCommand.h cmExpandedCommandArgument.cxx cmExpandedCommandArgument.h + cmExperimental.cxx + cmExperimental.h cmExportCommand.cxx cmExportCommand.h cmExportLibraryDependenciesCommand.cxx @@ -603,6 +607,8 @@ set(SRCS cmInstallCommand.h cmInstallCommandArguments.cxx cmInstallCommandArguments.h + cmInstallCxxModuleBmiGenerator.cxx + cmInstallCxxModuleBmiGenerator.h cmInstallFilesCommand.cxx cmInstallFilesCommand.h cmInstallProgramsCommand.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 0e29ef1..82e1f62 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,8 +1,8 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 24) -set(CMake_VERSION_PATCH 0) -set(CMake_VERSION_RC 3) +set(CMake_VERSION_PATCH 20220720) +#set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) # Start with the full version number used in tags. It has no dev info. diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx index 607d797..162dfc7 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.cxx +++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx @@ -23,8 +23,6 @@ // Suffix used to tell libpkg what compression to use static const char FreeBSDPackageCompression[] = "txz"; -// Resulting package file-suffix, for < 1.17 and >= 1.17 versions of libpkg -static const char FreeBSDPackageSuffix_10[] = ".txz"; static const char FreeBSDPackageSuffix_17[] = ".pkg"; cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator() @@ -83,6 +81,14 @@ public: { if (!isValid()) return false; + // The API in the FreeBSD sources (the header has no documentation), + // is as follows: + // + // int pkg_create(struct pkg_create *pc, const char *metadata, const char + // *plist, bool hash) + // + // We let the plist be determined from what is installed, and all + // the rest comes from the manifest data. int r = pkg_create(d, manifest.c_str(), nullptr, false); return r == 0; } @@ -402,7 +408,8 @@ int cmCPackFreeBSDGenerator::PackageFiles() return 0; } - std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel); + const std::string output_dir = + cmSystemTools::CollapseFullPath("../", toplevel); PkgCreate package(output_dir, toplevel, manifestname); if (package.isValid()) { if (!package.Create()) { @@ -416,40 +423,33 @@ int cmCPackFreeBSDGenerator::PackageFiles() return 0; } - // Specifically looking for packages suffixed with the TAG, either extension - std::string broken_suffix_10 = - cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_10); + // Specifically looking for packages suffixed with the TAG std::string broken_suffix_17 = cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_17); for (std::string& name : packageFileNames) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl); - if (cmHasSuffix(name, broken_suffix_10)) { - name.replace(name.size() - broken_suffix_10.size(), std::string::npos, - FreeBSDPackageSuffix_10); - break; - } if (cmHasSuffix(name, broken_suffix_17)) { name.replace(name.size() - broken_suffix_17.size(), std::string::npos, FreeBSDPackageSuffix_17); break; } } - // If the name uses a *new* style name, which doesn't exist, but there - // is an *old* style name, then use that instead. This indicates we used - // an older libpkg, which still creates .txz instead of .pkg files. - for (std::string& name : packageFileNames) { - if (cmHasSuffix(name, FreeBSDPackageSuffix_17) && - !cmSystemTools::FileExists(name)) { - const std::string badSuffix(FreeBSDPackageSuffix_17); - const std::string goodSuffix(FreeBSDPackageSuffix_10); - std::string repairedName(name); - repairedName.replace(repairedName.size() - badSuffix.size(), - std::string::npos, goodSuffix); - if (cmSystemTools::FileExists(repairedName)) { - name = repairedName; - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "Repaired packagefile " << name << std::endl); - } + + const std::string packageFileName = + var_lookup("CPACK_PACKAGE_FILE_NAME") + FreeBSDPackageSuffix_17; + if (packageFileNames.size() == 1 && !packageFileName.empty() && + packageFileNames[0] != packageFileName) { + // Since libpkg always writes <name>-<version>.<suffix>, + // if there is a CPACK_PACKAGE_FILE_NAME set, we need to + // rename, and then re-set the name. + const std::string sourceFile = packageFileNames[0]; + const std::string packageSubDirectory = + cmSystemTools::GetParentDirectory(sourceFile); + const std::string targetFileName = + packageSubDirectory + '/' + packageFileName; + if (cmSystemTools::RenameFile(sourceFile, targetFileName)) { + this->packageFileNames.clear(); + this->packageFileNames.emplace_back(targetFileName); } } diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx index 7432d08..97b0a89 100644 --- a/Source/CTest/cmCTestCoverageCommand.cxx +++ b/Source/CTest/cmCTestCoverageCommand.cxx @@ -4,7 +4,6 @@ #include <set> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmCTest.h" @@ -18,13 +17,6 @@ void cmCTestCoverageCommand::BindArguments() this->Bind("LABELS"_s, this->Labels); } -void cmCTestCoverageCommand::CheckArguments( - std::vector<std::string> const& keywords) -{ - this->LabelsMentioned = - !this->Labels.empty() || cm::contains(keywords, "LABELS"); -} - cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() { this->CTest->SetCTestConfigurationFromCMakeVariable( @@ -36,9 +28,9 @@ cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() handler->Initialize(); // If a LABELS option was given, select only files with the labels. - if (this->LabelsMentioned) { + if (this->Labels) { handler->SetLabelFilter( - std::set<std::string>(this->Labels.begin(), this->Labels.end())); + std::set<std::string>(this->Labels->begin(), this->Labels->end())); } handler->SetQuiet(this->Quiet); diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h index 9344852..55c68b2 100644 --- a/Source/CTest/cmCTestCoverageCommand.h +++ b/Source/CTest/cmCTestCoverageCommand.h @@ -9,7 +9,9 @@ #include <vector> #include <cm/memory> +#include <cm/optional> +#include "cmArgumentParserTypes.h" // IWYU pragma: keep #include "cmCTestHandlerCommand.h" #include "cmCommand.h" @@ -41,9 +43,7 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const& keywords) override; cmCTestGenericHandler* InitializeHandler() override; - bool LabelsMentioned; - std::vector<std::string> Labels; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Labels; }; diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 5494d20..af365ad 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -7,6 +7,7 @@ #include <cstring> #include <sstream> +#include <cm/string_view> #include <cmext/string_view> #include "cmCTest.h" @@ -81,11 +82,10 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // Process input arguments. std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - std::vector<std::string> parsedKeywords; - this->Parse(args, &unparsedArguments, &keywordsMissingValue, + std::vector<cm::string_view> parsedKeywords; + this->Parse(args, &unparsedArguments, /*keywordsMissingValue=*/nullptr, &parsedKeywords); - this->CheckArguments(keywordsMissingValue); + this->CheckArguments(); std::sort(parsedKeywords.begin(), parsedKeywords.end()); auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end()); @@ -242,6 +242,6 @@ void cmCTestHandlerCommand::BindArguments() this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex); } -void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&) +void cmCTestHandlerCommand::CheckArguments() { } diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index 756952d..31942c8 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -42,7 +42,7 @@ protected: // Command argument handling. virtual void BindArguments(); - virtual void CheckArguments(std::vector<std::string> const& keywords); + virtual void CheckArguments(); bool Append = false; bool Quiet = false; diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index a2dc615..a1933cc 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx @@ -8,7 +8,6 @@ #include <cm/memory> #include <cm/vector> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmCTest.h" @@ -87,7 +86,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // If FILES are given, but not PARTS, only the FILES are submitted // and *no* PARTS are submitted. // (This is why we select the empty "noParts" set in the - // FilesMentioned block below...) + // if(this->Files) block below...) // // If PARTS are given, only the selected PARTS are submitted. // @@ -96,7 +95,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // If given explicit FILES to submit, pass them to the handler. // - if (this->FilesMentioned) { + if (this->Files) { // Intentionally select *no* PARTS. (Pass an empty set.) If PARTS // were also explicitly mentioned, they will be selected below... // But FILES with no PARTS mentioned should just submit the FILES @@ -104,14 +103,14 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // handler->SelectParts(std::set<cmCTest::Part>()); handler->SelectFiles( - std::set<std::string>(this->Files.begin(), this->Files.end())); + std::set<std::string>(this->Files->begin(), this->Files->end())); } // If a PARTS option was given, select only the named parts for submission. // - if (this->PartsMentioned) { + if (this->Parts) { auto parts = - cmMakeRange(this->Parts).transform([this](std::string const& arg) { + cmMakeRange(*(this->Parts)).transform([this](std::string const& arg) { return this->CTest->GetPartFromName(arg); }); handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end())); @@ -172,33 +171,31 @@ void cmCTestSubmitCommand::BindArguments() this->cmCTestHandlerCommand::BindArguments(); } -void cmCTestSubmitCommand::CheckArguments( - std::vector<std::string> const& keywords) +void cmCTestSubmitCommand::CheckArguments() { - this->PartsMentioned = - !this->Parts.empty() || cm::contains(keywords, "PARTS"); - this->FilesMentioned = - !this->Files.empty() || cm::contains(keywords, "FILES"); - - cm::erase_if(this->Parts, [this](std::string const& arg) -> bool { - cmCTest::Part p = this->CTest->GetPartFromName(arg); - if (p == cmCTest::PartCount) { - std::ostringstream e; - e << "Part name \"" << arg << "\" is invalid."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return true; - } - return false; - }); - - cm::erase_if(this->Files, [this](std::string const& arg) -> bool { - if (!cmSystemTools::FileExists(arg)) { - std::ostringstream e; - e << "File \"" << arg << "\" does not exist. Cannot submit " - << "a non-existent file."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return true; - } - return false; - }); + if (this->Parts) { + cm::erase_if(*(this->Parts), [this](std::string const& arg) -> bool { + cmCTest::Part p = this->CTest->GetPartFromName(arg); + if (p == cmCTest::PartCount) { + std::ostringstream e; + e << "Part name \"" << arg << "\" is invalid."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return true; + } + return false; + }); + } + + if (this->Files) { + cm::erase_if(*(this->Files), [this](std::string const& arg) -> bool { + if (!cmSystemTools::FileExists(arg)) { + std::ostringstream e; + e << "File \"" << arg << "\" does not exist. Cannot submit " + << "a non-existent file."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return true; + } + return false; + }); + } } diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index c5d11df..b67f182 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -8,6 +8,9 @@ #include <string> #include <vector> +#include <cm/optional> + +#include "cmArgumentParserTypes.h" #include "cmCTestHandlerCommand.h" class cmCommand; @@ -35,13 +38,11 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const& keywords) override; + void CheckArguments() override; cmCTestGenericHandler* InitializeHandler() override; bool CDashUpload = false; - bool FilesMentioned = false; bool InternalTest = false; - bool PartsMentioned = false; std::string BuildID; std::string CDashUploadFile; @@ -50,7 +51,7 @@ protected: std::string RetryDelay; std::string SubmitURL; - std::vector<std::string> Files; - std::vector<std::string> HttpHeaders; - std::vector<std::string> Parts; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> HttpHeaders; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Parts; }; diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx index f86ee0d..2ed671c 100644 --- a/Source/CTest/cmCTestUploadCommand.cxx +++ b/Source/CTest/cmCTestUploadCommand.cxx @@ -21,7 +21,7 @@ void cmCTestUploadCommand::BindArguments() this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError); } -void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&) +void cmCTestUploadCommand::CheckArguments() { cm::erase_if(this->Files, [this](std::string const& arg) -> bool { if (!cmSystemTools::FileExists(arg)) { diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index fe155f6..a9d1dd2 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -10,6 +10,7 @@ #include <cm/memory> +#include "cmArgumentParserTypes.h" #include "cmCTestHandlerCommand.h" #include "cmCommand.h" @@ -42,8 +43,8 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const&) override; + void CheckArguments() override; cmCTestGenericHandler* InitializeHandler() override; - std::vector<std::string> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Files; }; diff --git a/Source/CursesDialog/cmCursesLongMessageForm.cxx b/Source/CursesDialog/cmCursesLongMessageForm.cxx index b14a751..6e6f0d6 100644 --- a/Source/CursesDialog/cmCursesLongMessageForm.cxx +++ b/Source/CursesDialog/cmCursesLongMessageForm.cxx @@ -85,7 +85,7 @@ void cmCursesLongMessageForm::UpdateStatusBar() for (size_t i = 0; i < sideSpace; i++) { version[i] = ' '; } - sprintf(version + sideSpace, "%s", vertmp); + snprintf(version + sideSpace, sizeof(version) - sideSpace, "%s", vertmp); version[width] = '\0'; char fmt_s[] = "%s"; diff --git a/Source/CursesDialog/form/.gitattributes b/Source/CursesDialog/form/.gitattributes index 12ede74..6dfa627 100644 --- a/Source/CursesDialog/form/.gitattributes +++ b/Source/CursesDialog/form/.gitattributes @@ -1 +1,2 @@ +* -whitespace * -format.clang-format-6.0 diff --git a/Source/CursesDialog/form/fty_int.c b/Source/CursesDialog/form/fty_int.c index 7107fcc..7aeb4b8 100644 --- a/Source/CursesDialog/form/fty_int.c +++ b/Source/CursesDialog/form/fty_int.c @@ -117,7 +117,7 @@ static bool Check_Integer_Field(FIELD * field, const void * argp) { if (val<low || val>high) return FALSE; } - sprintf(buf,"%.*ld",(prec>0?prec:0),val); + snprintf(buf,sizeof(buf),"%.*ld",(prec>0?prec:0),val); set_field_buffer(field,0,buf); return TRUE; } diff --git a/Source/CursesDialog/form/fty_num.c b/Source/CursesDialog/form/fty_num.c index 7809599..4109b6f 100644 --- a/Source/CursesDialog/form/fty_num.c +++ b/Source/CursesDialog/form/fty_num.c @@ -140,7 +140,7 @@ static bool Check_Numeric_Field(FIELD * field, const void * argp) { if (val<low || val>high) return FALSE; } - sprintf(buf,"%.*f",(prec>0?prec:0),val); + snprintf(buf,sizeof(buf),"%.*f",(prec>0?prec:0),val); set_field_buffer(field,0,buf); return TRUE; } diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index 4624f1c..f0c4cfc 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -4,6 +4,8 @@ #include <algorithm> +#include "cmArgumentParserTypes.h" + namespace ArgumentParser { auto ActionMap::Emplace(cm::string_view name, Action action) @@ -44,14 +46,28 @@ void Instance::Bind(std::string& val) this->ExpectValue = true; } -void Instance::Bind(StringList& val) +void Instance::Bind(Maybe<std::string>& val) +{ + this->CurrentString = &val; + this->CurrentList = nullptr; + this->ExpectValue = false; +} + +void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val) +{ + this->CurrentString = nullptr; + this->CurrentList = &val; + this->ExpectValue = false; +} + +void Instance::Bind(NonEmpty<std::vector<std::string>>& val) { this->CurrentString = nullptr; this->CurrentList = &val; this->ExpectValue = true; } -void Instance::Bind(MultiStringList& val) +void Instance::Bind(std::vector<std::vector<std::string>>& val) { this->CurrentString = nullptr; this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back()); @@ -60,17 +76,17 @@ void Instance::Bind(MultiStringList& val) void Instance::Consume(cm::string_view arg, void* result, std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue, - std::vector<std::string>* parsedKeywords) + std::vector<cm::string_view>* keywordsMissingValue, + std::vector<cm::string_view>* parsedKeywords) { auto const it = this->Bindings.Find(arg); if (it != this->Bindings.end()) { if (parsedKeywords != nullptr) { - parsedKeywords->emplace_back(arg); + parsedKeywords->emplace_back(it->first); } it->second(*this, result); if (this->ExpectValue && keywordsMissingValue != nullptr) { - keywordsMissingValue->emplace_back(arg); + keywordsMissingValue->emplace_back(it->first); } return; } diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index 71ed844..26148d9 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -10,13 +10,13 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> -namespace ArgumentParser { +#include "cmArgumentParserTypes.h" // IWYU pragma: keep -using StringList = std::vector<std::string>; -using MultiStringList = std::vector<StringList>; +namespace ArgumentParser { class Instance; using Action = std::function<void(Instance&, void*)>; @@ -39,18 +39,30 @@ public: void Bind(bool& val); void Bind(std::string& val); - void Bind(StringList& val); - void Bind(MultiStringList& val); + void Bind(Maybe<std::string>& val); + void Bind(MaybeEmpty<std::vector<std::string>>& val); + void Bind(NonEmpty<std::vector<std::string>>& val); + void Bind(std::vector<std::vector<std::string>>& val); + + // cm::optional<> records the presence the keyword to which it binds. + template <typename T> + void Bind(cm::optional<T>& optVal) + { + if (!optVal) { + optVal.emplace(); + } + this->Bind(*optVal); + } void Consume(cm::string_view arg, void* result, std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue, - std::vector<std::string>* parsedKeywords); + std::vector<cm::string_view>* keywordsMissingValue, + std::vector<cm::string_view>* parsedKeywords); private: ActionMap const& Bindings; std::string* CurrentString = nullptr; - StringList* CurrentList = nullptr; + std::vector<std::string>* CurrentList = nullptr; bool ExpectValue = false; }; @@ -78,9 +90,9 @@ public: template <typename Range> void Parse(Result& result, Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + std::vector<std::string>* unparsedArguments, + std::vector<cm::string_view>* keywordsMissingValue = nullptr, + std::vector<cm::string_view>* parsedKeywords = nullptr) const { ArgumentParser::Instance instance(this->Bindings); for (cm::string_view arg : args) { @@ -90,10 +102,9 @@ public: } template <typename Range> - Result Parse(Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + Result Parse(Range const& args, std::vector<std::string>* unparsedArguments, + std::vector<cm::string_view>* keywordsMissingValue = nullptr, + std::vector<cm::string_view>* parsedKeywords = nullptr) const { Result result; this->Parse(result, args, unparsedArguments, keywordsMissingValue, @@ -118,10 +129,9 @@ public: } template <typename Range> - void Parse(Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + void Parse(Range const& args, std::vector<std::string>* unparsedArguments, + std::vector<cm::string_view>* keywordsMissingValue = nullptr, + std::vector<cm::string_view>* parsedKeywords = nullptr) const { ArgumentParser::Instance instance(this->Bindings); for (cm::string_view arg : args) { diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h new file mode 100644 index 0000000..9afa5c7 --- /dev/null +++ b/Source/cmArgumentParserTypes.h @@ -0,0 +1,24 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +namespace ArgumentParser { + +template <typename T> +struct Maybe : public T +{ +}; + +template <typename T> +struct MaybeEmpty : public T +{ +}; + +template <typename T> +struct NonEmpty : public T +{ +}; + +} // namespace ArgumentParser diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 0750eea..033dd6d 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -491,7 +491,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, .Bind("SEPARATOR"_s, &Arguments::Separator) .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); std::vector<std::string> invalidArgs; - std::vector<std::string> keywordsMissingValue; + std::vector<cm::string_view> keywordsMissingValue; Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue); diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index a2aaa2a..76561c3 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -14,15 +14,18 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmDependencyProvider.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmRange.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmake.h" namespace { @@ -235,7 +238,7 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( struct SetProviderArgs { std::string Command; - std::vector<std::string> Methods; + ArgumentParser::NonEmpty<std::vector<std::string>> Methods; }; auto const ArgsParser = @@ -303,6 +306,27 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( return true; } + +bool cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL( + std::vector<cmListFileArgument> const& args, cmExecutionStatus& status) +{ + cmMakefile& makefile = status.GetMakefile(); + std::vector<std::string> expandedArgs; + makefile.ExpandArguments(args, expandedArgs); + + if (args.size() < 2 || expandedArgs.size() > 2) { + return FatalError( + status, + "sub-command GET_MESSAGE_LOG_LEVEL expects exactly one argument"); + } + + Message::LogLevel logLevel = makefile.GetCurrentLogLevel(); + std::string outputValue = cmake::LogLevelToString(logLevel); + + const std::string& outputVariable = expandedArgs[1]; + makefile.AddDefinition(outputVariable, outputValue); + return true; +} } bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, @@ -451,5 +475,9 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, return cmCMakeLanguageCommandEVAL(args, status); } + if (expArgs[expArg] == "GET_MESSAGE_LOG_LEVEL") { + return cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(args, status); + } + return FatalError(status, "called with unknown meta-operation"); } diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index bf94c2d..adcffa9 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -44,8 +44,8 @@ public: template <int Advance = 2> Result Parse(std::vector<std::string> const& args, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + std::vector<cm::string_view>* keywordsMissingValue = nullptr, + std::vector<cm::string_view>* parsedKeywords = nullptr) const { this->Inputs.clear(); @@ -89,11 +89,11 @@ public: args, &this->KeywordsMissingValue, &this->ParsedKeywords); } - const std::vector<std::string>& GetKeywordsMissingValue() const + const std::vector<cm::string_view>& GetKeywordsMissingValue() const { return this->KeywordsMissingValue; } - const std::vector<std::string>& GetParsedKeywords() const + const std::vector<cm::string_view>& GetParsedKeywords() const { return this->ParsedKeywords; } @@ -121,8 +121,8 @@ public: } private: - mutable std::vector<std::string> KeywordsMissingValue; - mutable std::vector<std::string> ParsedKeywords; + mutable std::vector<cm::string_view> KeywordsMissingValue; + mutable std::vector<cm::string_view> ParsedKeywords; }; struct OutputVariable diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index ba95168..5fe6756 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -9,6 +9,7 @@ #include "cmComputeLinkInformation.h" #include "cmGeneratorTarget.h" #include "cmGlobalCommonGenerator.h" +#include "cmGlobalGenerator.h" #include "cmLocalCommonGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -175,6 +176,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories( cmLocalGenerator* lg = linkee->GetLocalGenerator(); std::string di = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', lg->GetTargetDirectory(linkee)); + if (lg->GetGlobalGenerator()->IsMultiConfig()) { + di = cmStrCat(di, '/', config); + } dirs.push_back(std::move(di)); } } diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in index 6a419f6..31d03da 100644 --- a/Source/cmConfigure.cmake.h.in +++ b/Source/cmConfigure.cmake.h.in @@ -14,6 +14,10 @@ #pragma warning(disable : 1572) /* floating-point equality test */ #endif +#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123) +#pragma diag_suppress 2910 /* excess -Wunused-function in 1.23.x */ +#endif + #cmakedefine HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE #cmakedefine HAVE_UNSETENV #cmakedefine CMake_USE_MACH_PARSER diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 5418e7c..45afdd1 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -605,6 +605,15 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, // does not include any system headers anyway. fprintf(fout, "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n"); + + // The link and compile lines for ABI detection step need to not use + // response files so we can extract implicit includes given to + // the underlying host compiler + if (testLangs.find("CUDA") != testLangs.end()) { + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n"); + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n"); + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n"); + } } fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n"); for (std::string const& li : testLangs) { @@ -1117,7 +1126,8 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName, searchDirs.emplace_back(std::move(tmp)); } searchDirs.emplace_back("/Debug"); -#if defined(__APPLE__) + + // handle app-bundles (for targeting apple-platforms) std::string app = "/" + targetName + ".app"; if (cmNonempty(config)) { std::string tmp = cmStrCat('/', *config, app); @@ -1126,7 +1136,7 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName, std::string tmp = "/Debug" + app; searchDirs.emplace_back(std::move(tmp)); searchDirs.emplace_back(std::move(app)); -#endif + searchDirs.emplace_back("/Development"); for (std::string const& sdir : searchDirs) { diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx new file mode 100644 index 0000000..94ad721 --- /dev/null +++ b/Source/cmCxxModuleMapper.cxx @@ -0,0 +1,76 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCxxModuleMapper.h" + +#include <cassert> +#include <sstream> +#include <vector> + +#include "cmScanDepFormat.h" + +cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule( + std::string const& logical_name) const +{ + if (auto l = this->BmiLocationForModule(logical_name)) { + return this->PathForGenerator(*l); + } + return {}; +} + +namespace { + +std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc, + cmScanDepInfo const& obj) +{ + std::stringstream mm; + + // Documented in GCC's documentation. The format is a series of + // lines with a module name and the associated filename separated + // by spaces. The first line may use `$root` as the module name + // to specify a "repository root". That is used to anchor any + // relative paths present in the file (CMake should never + // generate any). + + // Write the root directory to use for module paths. + mm << "$root " << loc.RootDirectory << "\n"; + + for (auto const& p : obj.Provides) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + mm << p.LogicalName << ' ' << *bmi_loc << '\n'; + } + } + for (auto const& r : obj.Requires) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) { + mm << r.LogicalName << ' ' << *bmi_loc << '\n'; + } + } + + return mm.str(); +} +} + +cm::static_string_view CxxModuleMapExtension( + cm::optional<CxxModuleMapFormat> format) +{ + if (format) { + switch (*format) { + case CxxModuleMapFormat::Gcc: + return ".gcm"_s; + } + } + + return ".bmi"_s; +} + +std::string CxxModuleMapContent(CxxModuleMapFormat format, + CxxModuleLocations const& loc, + cmScanDepInfo const& obj) +{ + switch (format) { + case CxxModuleMapFormat::Gcc: + return CxxModuleMapContentGcc(loc, obj); + } + + assert(false); + return {}; +} diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h new file mode 100644 index 0000000..99384c9 --- /dev/null +++ b/Source/cmCxxModuleMapper.h @@ -0,0 +1,48 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <functional> +#include <string> + +#include <cm/optional> +#include <cmext/string_view> + +struct cmScanDepInfo; + +enum class CxxModuleMapFormat +{ + Gcc, +}; + +struct CxxModuleLocations +{ + // The path from which all relative paths should be computed. If + // this is relative, it is relative to the compiler's working + // directory. + std::string RootDirectory; + + // A function to convert a full path to a path for the generator. + std::function<std::string(std::string const&)> PathForGenerator; + + // Lookup the BMI location of a logical module name. + std::function<cm::optional<std::string>(std::string const&)> + BmiLocationForModule; + + // Returns the generator path (if known) for the BMI given a + // logical module name. + cm::optional<std::string> BmiGeneratorPathForModule( + std::string const& logical_name) const; +}; + +// Return the extension to use for a given modulemap format. +cm::static_string_view CxxModuleMapExtension( + cm::optional<CxxModuleMapFormat> format); + +// Return the contents of the module map in the given format for the +// object file. +std::string CxxModuleMapContent(CxxModuleMapFormat format, + CxxModuleLocations const& loc, + cmScanDepInfo const& obj); diff --git a/Source/cmDefinePropertyCommand.cxx b/Source/cmDefinePropertyCommand.cxx index faefcb8..31ee665 100644 --- a/Source/cmDefinePropertyCommand.cxx +++ b/Source/cmDefinePropertyCommand.cxx @@ -8,6 +8,7 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmProperty.h" @@ -51,8 +52,8 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args, // Parse remaining arguments. bool inherited = false; std::string PropertyName; - std::vector<std::string> BriefDocs; - std::vector<std::string> FullDocs; + ArgumentParser::NonEmpty<std::vector<std::string>> BriefDocs; + ArgumentParser::NonEmpty<std::vector<std::string>> FullDocs; std::string initializeFromVariable; cmArgumentParser<void> parser; diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 222ea80..af56e2d 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -95,13 +95,13 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; + std::vector<cm::string_view> keywordsMissingValue; Arguments const arguments = parser.Parse(args, &unparsedArguments, &keywordsMissingValue); if (!keywordsMissingValue.empty()) { - status.SetError(" called with no value for " + - keywordsMissingValue.front() + "."); + status.SetError(cmStrCat(" called with no value for ", + keywordsMissingValue.front(), ".")); return false; } if (!unparsedArguments.empty()) { diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx new file mode 100644 index 0000000..922b53f --- /dev/null +++ b/Source/cmExperimental.cxx @@ -0,0 +1,63 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmExperimental.h" + +#include <cassert> +#include <cstddef> +#include <string> + +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmValue.h" + +namespace { + +/* + * The `Uuid` fields of these objects should change periodically. + * Search for other instances to keep the documentation and test suite + * up-to-date. + */ + +struct FeatureData +{ + std::string const Uuid; + std::string const Variable; + std::string const Description; + bool Warned; +} LookupTable[] = { + // CxxModuleCMakeApi + { "3c375311-a3c9-4396-a187-3227ef642046", + "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API", + "CMake's C++ module support is experimental. It is meant only for " + "experimentation and feedback to CMake developers.", + false }, +}; +static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == + static_cast<size_t>(cmExperimental::Feature::Sentinel), + "Experimental feature lookup table mismatch"); + +FeatureData& DataForFeature(cmExperimental::Feature f) +{ + assert(f != cmExperimental::Feature::Sentinel); + return LookupTable[static_cast<size_t>(f)]; +} +} + +bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f) +{ + bool enabled = false; + auto& data = DataForFeature(f); + + auto value = mf.GetDefinition(data.Variable); + if (value == data.Uuid) { + enabled = true; + } + + if (enabled && !data.Warned) { + mf.IssueMessage(MessageType::AUTHOR_WARNING, data.Description); + data.Warned = true; + } + + return enabled; +} diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h new file mode 100644 index 0000000..26e0d17 --- /dev/null +++ b/Source/cmExperimental.h @@ -0,0 +1,21 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +class cmMakefile; + +class cmExperimental +{ +public: + enum class Feature + { + CxxModuleCMakeApi, + + Sentinel, + }; + + static bool HasSupportEnabled(cmMakefile const& mf, Feature f); +}; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 6ce0c98..ed199ea 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -9,10 +9,13 @@ #include <sstream> #include <utility> +#include <cm/string_view> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmExportSet.h" #include "cmFileSet.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -23,6 +26,7 @@ #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" #include "cmValue.h" @@ -139,11 +143,18 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->GenerateTargetFileSets(gte, os); } + this->GenerateCxxModuleInformation(os); + // Generate import file content for each configuration. for (std::string const& c : this->Configurations) { this->GenerateImportConfig(os, c); } + // Generate import file content for each configuration. + for (std::string const& c : this->Configurations) { + this->GenerateImportCxxModuleConfigTargetInclusion(c); + } + this->GenerateMissingTargetsCheckCode(os); return true; @@ -382,6 +393,21 @@ std::string cmExportBuildFileGenerator::GetFileSetDirectories( std::any_of(directoryEntries.begin(), directoryEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base directory entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& directory : directories) { auto dest = cmOutputConverter::EscapeForCMake( directory, cmOutputConverter::WrapQuotes::NoWrap); @@ -427,6 +453,21 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, std::any_of(fileEntries.begin(), fileEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& it : files) { for (auto const& filename : it.second) { auto escapedFile = cmOutputConverter::EscapeForCMake( @@ -447,3 +488,60 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, return cmJoin(resultVector, " "); } + +std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const +{ + return this->CxxModulesDirectory; +} + +void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation( + std::ostream& os) const +{ + const char* opt = ""; + if (this->Configurations.size() > 1) { + // With more than one configuration, each individual file is optional. + opt = " OPTIONAL"; + } + + // Generate import file content for each configuration. + for (std::string c : this->Configurations) { + if (c.empty()) { + c = "noconfig"; + } + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << c << ".cmake\"" + << opt << ")\n"; + } +} + +bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion( + std::string config) const +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + if (config.empty()) { + config = "noconfig"; + } + + std::string fileName = cmStrCat(this->FileDir, '/', cxx_modules_dirname, + "/cxx-modules-", config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + for (auto const* tgt : this->ExportedTargets) { + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" << tgt->GetExportName() + << '-' << config << ".cmake\")\n"; + } + + return true; +} diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 5681e8f..4636196 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -47,6 +47,16 @@ public: } void SetExportSet(cmExportSet*); + /** Set the name of the C++ module directory. */ + void SetCxxModuleDirectory(std::string cxx_module_dir) + { + this->CxxModulesDirectory = std::move(cxx_module_dir); + } + const std::string& GetCxxModuleDirectory() const + { + return this->CxxModulesDirectory; + } + /** Set whether to append generated code to the output file. */ void SetAppendMode(bool append) { this->AppendMode = append; } @@ -81,6 +91,10 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string) const; + std::pair<std::vector<std::string>, std::string> FindBuildExportInfo( cmGlobalGenerator* gg, const std::string& name); @@ -88,4 +102,6 @@ protected: cmExportSet* ExportSet; std::vector<cmGeneratorTarget*> Exports; cmLocalGenerator* LG; + // The directory for C++ module information. + std::string CxxModulesDirectory; }; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 63440a3..a58f2b7 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -7,13 +7,15 @@ #include <utility> #include <cm/memory> -#include <cmext/algorithm> +#include <cm/optional> #include <cmext/string_view> #include "cmsys/RegularExpression.hxx" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" +#include "cmExperimental.h" #include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" @@ -57,10 +59,11 @@ bool cmExportCommand(std::vector<std::string> const& args, struct Arguments { std::string ExportSetName; - std::vector<std::string> Targets; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets; std::string Namespace; std::string Filename; std::string AndroidMKFile; + std::string CxxModulesDirectory; bool Append = false; bool ExportOld = false; }; @@ -69,6 +72,12 @@ bool cmExportCommand(std::vector<std::string> const& args, .Bind("NAMESPACE"_s, &Arguments::Namespace) .Bind("FILE"_s, &Arguments::Filename); + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + status.GetMakefile(), cmExperimental::Feature::CxxModuleCMakeApi); + if (supportCxx20FileSetTypes) { + parser.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory); + } + if (args[0] == "EXPORT") { parser.Bind("EXPORT"_s, &Arguments::ExportSetName); } else { @@ -79,9 +88,7 @@ bool cmExportCommand(std::vector<std::string> const& args, } std::vector<std::string> unknownArgs; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unknownArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); @@ -145,9 +152,8 @@ bool cmExportCommand(std::vector<std::string> const& args, return false; } exportSet = &it->second; - } else if (!arguments.Targets.empty() || - cm::contains(keywordsMissingValue, "TARGETS")) { - for (std::string const& currentTarget : arguments.Targets) { + } else if (arguments.Targets) { + for (std::string const& currentTarget : *arguments.Targets) { if (mf.IsAlias(currentTarget)) { std::ostringstream e; e << "given ALIAS target \"" << currentTarget @@ -214,6 +220,7 @@ bool cmExportCommand(std::vector<std::string> const& args, } ebfg->SetExportFile(fname.c_str()); ebfg->SetNamespace(arguments.Namespace); + ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory); ebfg->SetAppendMode(arguments.Append); if (exportSet != nullptr) { ebfg->SetExportSet(exportSet); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 452eb99..23b5690 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -932,13 +932,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) // Isolate the file policy level. // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.22 (this upper limit may be reviewed + // policy settings for up to CMake 3.23 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. /* clang-format off */ os << "cmake_policy(PUSH)\n" - << "cmake_policy(VERSION 2.8.3...3.22)\n"; + << "cmake_policy(VERSION 2.8.3...3.23)\n"; /* clang-format on */ } @@ -1088,6 +1088,10 @@ void cmExportFileGenerator::GenerateImportTargetCode( << " PROPERTY IMPORTED_NO_SYSTEM 1)\n"; } + if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) { + os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n"; + } + os << "\n"; } @@ -1301,3 +1305,28 @@ void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte, os << " )\nendif()\n\n"; } } + +void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os) +{ + auto const cxx_module_dirname = this->GetCxxModulesDirectory(); + if (cxx_module_dirname.empty()) { + return; + } + + // Write the include. + os << "# Include C++ module properties\n" + << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname + << "/cxx-modules.cmake\")\n\n"; + + // Get the path to the file we're going to write. + std::string path = this->MainImportFile; + path = cmSystemTools::GetFilenamePath(path); + auto trampoline_path = + cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules.cmake"); + + // Include all configuration-specific include files. + cmGeneratedFileStream ap(trampoline_path, true); + ap.SetCopyIfDifferent(true); + + this->GenerateCxxModuleConfigInformation(ap); +} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index d27a555..fdda878 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -182,6 +182,8 @@ protected: void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, cmTargetExport* te = nullptr); + void GenerateCxxModuleInformation(std::ostream& os); + virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) = 0; @@ -226,4 +228,7 @@ private: virtual std::string InstallNameDir(cmGeneratorTarget const* target, const std::string& config) = 0; + + virtual std::string GetCxxModulesDirectory() const = 0; + virtual void GenerateCxxModuleConfigInformation(std::ostream& os) const = 0; }; diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index 4e4f8a1..d53254d 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -35,8 +35,7 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( for (size_t n = 0; n < numDotDot; n++) { path += "/.."; } - os << "_IMPORT_PREFIX := " - << "$(LOCAL_PATH)" << path << "\n\n"; + os << "_IMPORT_PREFIX := $(LOCAL_PATH)" << path << "\n\n"; for (std::unique_ptr<cmTargetExport> const& te : this->IEGen->GetExportSet()->GetTargetExports()) { // Collect import properties for this target. diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index adccdfe..7d8572d 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -7,6 +7,9 @@ #include <sstream> #include <utility> +#include <cm/string_view> +#include <cmext/string_view> + #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratedFileStream.h" @@ -18,6 +21,7 @@ #include "cmInstallTargetGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmStateTypes.h" @@ -162,10 +166,20 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->LoadConfigFiles(os); + bool result = true; + + this->GenerateCxxModuleInformation(os); + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) { + result = false; + } + } + } + this->CleanupTemporaryVariables(os); this->GenerateImportedFileCheckLoop(os); - bool result = true; // Generate an import file for each configuration. // Don't do this if we only export INTERFACE_LIBRARY targets. if (requiresConfigFiles) { @@ -562,6 +576,21 @@ std::string cmExportInstallFileGenerator::GetFileSetDirectories( cge->Evaluate(gte->LocalGenerator, config, gte), cmOutputConverter::WrapQuotes::NoWrap)); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (cge->GetHadContextSensitiveCondition() && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { resultVector.push_back( cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); @@ -610,6 +639,21 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles( std::any_of(fileEntries.begin(), fileEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& it : files) { auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); for (auto const& filename : it.second) { @@ -635,3 +679,65 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles( return cmJoin(resultVector, " "); } + +std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const +{ + return IEGen->GetCxxModuleDirectory(); +} + +void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation( + std::ostream& os) const +{ + // Now load per-configuration properties for them. + /* clang-format off */ + os << "# Load information for each installed configuration.\n" + "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-*.cmake\")\n" + "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n" + " include(\"${_cmake_cxx_module_include}\")\n" + "endforeach()\n" + "unset(_cmake_cxx_module_include)\n" + "unset(_cmake_cxx_module_includes)\n"; + /* clang-format on */ +} + +bool cmExportInstallFileGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& config) +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + std::string filename_config = config; + if (filename_config.empty()) { + filename_config = "noconfig"; + } + + std::string const dest = + cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); + std::string fileName = + cmStrCat(dest, "cxx-modules-", filename_config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + // Record this per-config import file. + this->ConfigCxxModuleFiles[config] = fileName; + + auto& prop_files = this->ConfigCxxModuleTargetFiles[config]; + for (auto const* tgt : this->ExportedTargets) { + auto prop_filename = cmStrCat("target-", tgt->GetExportName(), '-', + filename_config, ".cmake"); + prop_files.emplace_back(cmStrCat(dest, prop_filename)); + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + } + + return true; +} diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 86fb505..e073a31 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -50,6 +50,23 @@ public: return this->ConfigImportFiles; } + /** Get the per-config C++ module file generated for each configuration. + This maps from the configuration name to the file temporary location + for installation. */ + std::map<std::string, std::string> const& GetConfigCxxModuleFiles() + { + return this->ConfigCxxModuleFiles; + } + + /** Get the per-config C++ module file generated for each configuration. + This maps from the configuration name to the file temporary location + for installation for each target in the export set. */ + std::map<std::string, std::vector<std::string>> const& + GetConfigCxxModuleTargetFiles() + { + return this->ConfigCxxModuleTargetFiles; + } + /** Compute the globbing expression used to load per-config import files from the main file. */ std::string GetConfigImportFileGlob(); @@ -100,8 +117,16 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&); + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. std::map<std::string, std::string> ConfigImportFiles; + // The C++ module property file generated for each configuration. + std::map<std::string, std::string> ConfigCxxModuleFiles; + // The C++ module property target files generated for each configuration. + std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles; }; diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 1dd8a20..5c34fad 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -55,6 +55,9 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override { return {}; } + void GenerateCxxModuleConfigInformation(std::ostream&) const override {} + private: std::string FindTargets(const std::string& prop, const cmGeneratorTarget* tgt, diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index dd0540c..0581802 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -27,6 +27,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -1092,6 +1093,21 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) if (installFileSet->GetOptional()) { installer["isOptional"] = true; } + } else if (auto* cxxModuleBmi = + dynamic_cast<cmInstallCxxModuleBmiGenerator*>(gen)) { + installer["type"] = "cxxModuleBmi"; + installer["destination"] = cxxModuleBmi->GetDestination(this->Config); + + auto const* target = cxxModuleBmi->GetTarget(); + installer["cxxModuleBmiTarget"] = Json::objectValue; + installer["cxxModuleBmiTarget"]["id"] = TargetId(target, this->TopBuild); + installer["cxxModuleBmiTarget"]["index"] = this->TargetIndexMap[target]; + + // FIXME: Parse FilePermissions. + // FIXME: Parse MessageLevel. + if (cxxModuleBmi->GetOptional()) { + installer["isOptional"] = true; + } } // Add fields common to all install generators. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 7d05347..d2aa63c 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -28,8 +28,8 @@ #include "cm_sys_stat.h" -#include "cmAlgorithms.h" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmCryptoHash.h" #include "cmELF.h" @@ -172,7 +172,8 @@ bool HandleReadCommand(std::vector<std::string> const& args, .Bind("LIMIT"_s, &Arguments::Limit) .Bind("HEX"_s, &Arguments::Hex); - Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3)); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3), + /*unparsedArguments=*/nullptr); std::string fileName = fileNameArg; if (!cmsys::SystemTools::FileIsFullPath(fileName)) { @@ -958,8 +959,8 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, bool removeEnvironmentRPath = false; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; + std::vector<cm::string_view> missingArgs; + std::vector<cm::string_view> parsedArgs; parser.Bind("FILE"_s, file) .Bind("OLD_RPATH"_s, oldRPath) .Bind("NEW_RPATH"_s, newRPath) @@ -1028,8 +1029,8 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, std::string newRPath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; + std::vector<cm::string_view> missingArgs; + std::vector<cm::string_view> parsedArgs; parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath); parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, &parsedArgs); @@ -1087,7 +1088,7 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args, std::string file; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; + std::vector<cm::string_view> missingArgs; parser.Bind("FILE"_s, file); parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs); if (!unknownArgs.empty()) { @@ -1138,8 +1139,8 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, std::string rpath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; + std::vector<cm::string_view> missingArgs; + std::vector<cm::string_view> parsedArgs; parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath); parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, &parsedArgs); @@ -1197,7 +1198,8 @@ bool HandleReadElfCommand(std::vector<std::string> const& args, .Bind("RPATH"_s, &Arguments::RPath) .Bind("RUNPATH"_s, &Arguments::RunPath) .Bind("CAPTURE_ERROR"_s, &Arguments::Error); - Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2)); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2), + /*unparsedArguments=*/nullptr); if (!cmSystemTools::FileExists(fileNameArg, true)) { status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg, @@ -1252,7 +1254,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, struct Arguments { - std::string BaseDirectory; + cm::optional<std::string> BaseDirectory; bool ExpandTilde = false; }; static auto const parser = @@ -1261,11 +1263,9 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - std::vector<std::string> parsedKeywords; - auto arguments = - parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments, - &keywordsMissingValue, &parsedKeywords); + std::vector<cm::string_view> keywordsMissingValue; + auto arguments = parser.Parse(cmMakeRange(args).advance(3), + &unparsedArguments, &keywordsMissingValue); if (!unparsedArguments.empty()) { status.SetError("REAL_PATH called with unexpected arguments"); @@ -1276,7 +1276,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, return false; } - if (parsedKeywords.empty()) { + if (!arguments.BaseDirectory) { arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } @@ -1295,7 +1295,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, } cmCMakePath path(input, cmCMakePath::auto_format); - path = path.Absolute(arguments.BaseDirectory).Normal(); + path = path.Absolute(*arguments.BaseDirectory).Normal(); auto realPath = cmSystemTools::GetRealPath(path.GenericString()); status.GetMakefile().AddDefinition(args[2], realPath); @@ -2497,15 +2497,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, struct Arguments { - std::string Output; - std::string Input; - std::string Content; - std::string Condition; - std::string Target; - std::string NewLineStyle; + cm::optional<std::string> Output; + cm::optional<std::string> Input; + cm::optional<std::string> Content; + cm::optional<std::string> Condition; + cm::optional<std::string> Target; + cm::optional<std::string> NewLineStyle; bool NoSourcePermissions = false; bool UseSourcePermissions = false; - std::vector<std::string> FilePermissions; + ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; }; static auto const parser = @@ -2521,14 +2521,16 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValues; - std::vector<std::string> parsedKeywords; + std::vector<cm::string_view> keywordsMissingValues; + std::vector<cm::string_view> parsedKeywords; Arguments const arguments = parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments, &keywordsMissingValues, &parsedKeywords); if (!keywordsMissingValues.empty()) { - status.SetError("Incorrect arguments to GENERATE subcommand."); + status.SetError( + cmStrCat("GENERATE keywords missing values:\n ", + cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); return false; } @@ -2537,56 +2539,41 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - bool mandatoryOptionsSpecified = false; - if (parsedKeywords.size() > 1) { - const bool outputOprionSpecified = parsedKeywords[0] == "OUTPUT"_s; - const bool inputOrContentSpecified = - parsedKeywords[1] == "INPUT"_s || parsedKeywords[1] == "CONTENT"_s; - if (outputOprionSpecified && inputOrContentSpecified) { - mandatoryOptionsSpecified = true; - } + if (!arguments.Output || parsedKeywords[0] != "OUTPUT"_s) { + status.SetError("GENERATE requires OUTPUT as first option."); + return false; } - if (!mandatoryOptionsSpecified) { - status.SetError("Incorrect arguments to GENERATE subcommand."); + std::string const& output = *arguments.Output; + + if (!arguments.Input && !arguments.Content) { + status.SetError("GENERATE requires INPUT or CONTENT option."); return false; } + const bool inputIsContent = parsedKeywords[1] == "CONTENT"_s; + if (!inputIsContent && parsedKeywords[1] == "INPUT") { + status.SetError("Unknown argument to GENERATE subcommand."); + } + std::string const& input = + inputIsContent ? *arguments.Content : *arguments.Input; - const bool conditionOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "CONDITION"_s) != - parsedKeywords.end(); - if (conditionOptionSpecified && arguments.Condition.empty()) { + if (arguments.Condition && arguments.Condition->empty()) { status.SetError("CONDITION of sub-command GENERATE must not be empty " "if specified."); return false; } + std::string const& condition = + arguments.Condition ? *arguments.Condition : std::string(); - const bool targetOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "TARGET"_s) != - parsedKeywords.end(); - if (targetOptionSpecified && arguments.Target.empty()) { + if (arguments.Target && arguments.Target->empty()) { status.SetError("TARGET of sub-command GENERATE must not be empty " "if specified."); return false; } + std::string const& target = + arguments.Target ? *arguments.Target : std::string(); - const bool outputOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "OUTPUT"_s) != - parsedKeywords.end(); - if (outputOptionSpecified && parsedKeywords[0] != "OUTPUT"_s) { - status.SetError("Incorrect arguments to GENERATE subcommand."); - return false; - } - - const bool inputIsContent = parsedKeywords[1] != "INPUT"_s; - if (inputIsContent && parsedKeywords[1] != "CONTENT") { - status.SetError("Unknown argument to GENERATE subcommand."); - } - - const bool newLineStyleSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), - "NEWLINE_STYLE"_s) != parsedKeywords.end(); cmNewLineStyle newLineStyle; - if (newLineStyleSpecified) { + if (arguments.NewLineStyle) { std::string errorMessage; if (!newLineStyle.ReadFromArguments(args, errorMessage)) { status.SetError(cmStrCat("GENERATE ", errorMessage)); @@ -2594,11 +2581,6 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } } - std::string input = arguments.Input; - if (inputIsContent) { - input = arguments.Content; - } - if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) { status.SetError("given both NO_SOURCE_PERMISSIONS and " "USE_SOURCE_PERMISSIONS. Only one option allowed."); @@ -2656,8 +2638,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } } - AddEvaluationFile(input, arguments.Target, arguments.Output, - arguments.Condition, inputIsContent, + AddEvaluationFile(input, target, output, condition, inputIsContent, newLineStyle.GetCharacters(), permissions, status); return true; } @@ -2836,7 +2817,11 @@ bool HandleTimestampCommand(std::vector<std::string> const& args, unsigned int argsIndex = 1; - const std::string& filename = args[argsIndex++]; + std::string filename = args[argsIndex++]; + if (!cmsys::SystemTools::FileIsFullPath(filename)) { + filename = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', + filename); + } const std::string& outputVariable = args[argsIndex++]; @@ -3067,17 +3052,18 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, std::string ConflictingDependenciesPrefix; std::string RPathPrefix; std::string BundleExecutable; - std::vector<std::string> Executables; - std::vector<std::string> Libraries; - std::vector<std::string> Directories; - std::vector<std::string> Modules; - std::vector<std::string> PreIncludeRegexes; - std::vector<std::string> PreExcludeRegexes; - std::vector<std::string> PostIncludeRegexes; - std::vector<std::string> PostExcludeRegexes; - std::vector<std::string> PostIncludeFiles; - std::vector<std::string> PostExcludeFiles; - std::vector<std::string> PostExcludeFilesStrict; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> + PostExcludeFilesStrict; }; static auto const parser = @@ -3102,7 +3088,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, &keywordsMissingValues); @@ -3113,24 +3099,10 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { - "DIRECTORIES", - "EXECUTABLES", - "LIBRARIES", - "MODULES", - "POST_EXCLUDE_FILES", - "POST_EXCLUDE_FILES_STRICT", - "POST_EXCLUDE_REGEXES", - "POST_INCLUDE_FILES", - "POST_INCLUDE_REGEXES", - "PRE_EXCLUDE_REGEXES", - "PRE_INCLUDE_REGEXES", - }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (!keywordsMissingValues.empty()) { + status.SetError( + cmStrCat("Keywords missing values:\n ", + cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); cmSystemTools::SetFatalErrorOccurred(); return false; } @@ -3234,11 +3206,12 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, { struct Arguments { - std::string Output; - std::string Content; + cm::optional<std::string> Output; + cm::optional<std::string> Content; bool EscapeQuotes = false; bool AtOnly = false; - std::string NewlineStyle; + // "NEWLINE_STYLE" requires one value, but we use a custom check below. + ArgumentParser::Maybe<std::string> NewlineStyle; }; static auto const parser = @@ -3250,11 +3223,10 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingArguments; - std::vector<std::string> parsedKeywords; + std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingArguments, &parsedKeywords); + &keywordsMissingValues); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { @@ -3264,28 +3236,23 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, return false; } - std::vector<std::string> mandatoryOptions{ "OUTPUT", "CONTENT" }; - for (auto const& e : mandatoryOptions) { - const bool optionHasNoValue = - std::find(keywordsMissingArguments.begin(), - keywordsMissingArguments.end(), - e) != keywordsMissingArguments.end(); - if (optionHasNoValue) { - status.SetError(cmStrCat("CONFIGURE ", e, " option needs a value.")); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } + if (!keywordsMissingValues.empty()) { + status.SetError( + cmStrCat("CONFIGURE keywords missing values:\n ", + cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + cmSystemTools::SetFatalErrorOccurred(); + return false; } - for (auto const& e : mandatoryOptions) { - const bool optionGiven = - std::find(parsedKeywords.begin(), parsedKeywords.end(), e) != - parsedKeywords.end(); - if (!optionGiven) { - status.SetError(cmStrCat("CONFIGURE ", e, " option is mandatory.")); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } + if (!parsedArgs.Output) { + status.SetError("CONFIGURE OUTPUT option is mandatory."); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + if (!parsedArgs.Content) { + status.SetError("CONFIGURE CONTENT option is mandatory."); + cmSystemTools::SetFatalErrorOccurred(); + return false; } std::string errorMessage; @@ -3297,7 +3264,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, // Check for generator expressions std::string outputFile = cmSystemTools::CollapseFullPath( - parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory()); + *parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory()); std::string::size_type pos = outputFile.find_first_of("<>"); if (pos != std::string::npos) { @@ -3346,7 +3313,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, fout.SetCopyIfDifferent(true); // copy input to output and expand variables from input at the same time - std::stringstream sin(parsedArgs.Content, std::ios::in); + std::stringstream sin(*parsedArgs.Content, std::ios::in); std::string inLine; std::string outLine; bool hasNewLine = false; @@ -3375,9 +3342,13 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, std::string Format; std::string Compression; std::string CompressionLevel; - std::string MTime; + // "MTIME" should require one value, but it has long been accidentally + // accepted without one and treated as if an empty value were given. + // Fixing this would require a policy. + ArgumentParser::Maybe<std::string> MTime; bool Verbose = false; - std::vector<std::string> Paths; + // "PATHS" requires at least one value, but use a custom check below. + ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths; }; static auto const parser = @@ -3391,7 +3362,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, .Bind("PATHS"_s, &Arguments::Paths); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, &keywordsMissingValues); @@ -3402,14 +3373,10 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { - "OUTPUT", "FORMAT", "COMPRESSION", "COMPRESSION_LEVEL", "MTIME", "PATHS" - }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (!keywordsMissingValues.empty()) { + status.SetError( + cmStrCat("Keywords missing values:\n ", + cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); cmSystemTools::SetFatalErrorOccurred(); return false; } @@ -3506,7 +3473,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, bool Verbose = false; bool ListOnly = false; std::string Destination; - std::vector<std::string> Patterns; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns; bool Touch = false; }; @@ -3519,7 +3486,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, .Bind("TOUCH"_s, &Arguments::Touch); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, &keywordsMissingValues); @@ -3530,13 +3497,10 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { "INPUT", "DESTINATION", - "PATTERNS" }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (!keywordsMissingValues.empty()) { + status.SetError( + cmStrCat("Keywords missing values:\n ", + cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); cmSystemTools::SetFatalErrorOccurred(); return false; } @@ -3630,9 +3594,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, struct Arguments { - std::vector<std::string> Permissions; - std::vector<std::string> FilePermissions; - std::vector<std::string> DirectoryPermissions; + ArgumentParser::NonEmpty<std::vector<std::string>> Permissions; + ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; + ArgumentParser::NonEmpty<std::vector<std::string>> DirectoryPermissions; }; static auto const parser = @@ -3642,7 +3606,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions); std::vector<std::string> pathEntries; - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1), &pathEntries, &keywordsMissingValues); @@ -3666,7 +3630,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, if (!keywordsMissingValues.empty()) { for (const auto& i : keywordsMissingValues) { - status.SetError(i + " is not given any arguments"); + status.SetError(cmStrCat(i, " is not given any arguments")); cmSystemTools::SetFatalErrorOccurred(); } return false; diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 4dfbef9..91efc66 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -2,15 +2,20 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmFindBase.h" +#include <algorithm> #include <cstddef> #include <deque> +#include <functional> #include <map> #include <utility> #include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmCMakePath.h" +#include "cmExecutionStatus.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -24,8 +29,6 @@ #include "cmWindowsRegistry.h" #include "cmake.h" -class cmExecutionStatus; - cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status) : cmFindCommon(status) , FindCommandName(std::move(findCommandName)) @@ -138,6 +141,31 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j])); return false; } + } else if (args[j] == "VALIDATOR") { + if (++j == args.size()) { + this->SetError("missing required argument for \"VALIDATOR\""); + return false; + } + auto command = this->Makefile->GetState()->GetCommand(args[j]); + if (command == nullptr) { + this->SetError(cmStrCat( + "command specified for \"VALIDATOR\" is undefined: ", args[j], '.')); + return false; + } + // ensure a macro is not specified as validator + const auto& validatorName = args[j]; + auto macros = cmExpandedList(this->Makefile->GetProperty("MACROS")); + if (std::find_if(macros.begin(), macros.end(), + [&validatorName](const std::string& item) { + return cmSystemTools::Strucmp(validatorName.c_str(), + item.c_str()) == 0; + }) != macros.end()) { + this->SetError(cmStrCat( + "command specified for \"VALIDATOR\" is not a function: ", args[j], + '.')); + return false; + } + this->ValidatorName = args[j]; } else if (this->CheckCommonArgument(args[j])) { doing = DoingNone; } else { @@ -188,6 +216,36 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) return true; } +bool cmFindBase::Validate(const std::string& path) const +{ + if (this->ValidatorName.empty()) { + return true; + } + + // The validator command will be executed in an isolated scope. + cmMakefile::ScopePushPop varScope(this->Makefile); + cmMakefile::PolicyPushPop polScope(this->Makefile); + static_cast<void>(varScope); + static_cast<void>(polScope); + + auto resultName = + cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName), + "_VALIDATOR_STATUS"_s); + + this->Makefile->AddDefinitionBool(resultName, true); + + cmListFileFunction validator( + this->ValidatorName, 0, 0, + { cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0), + cmListFileArgument(path, cmListFileArgument::Quoted, 0) }); + cmExecutionStatus status(*this->Makefile); + + if (this->Makefile->ExecuteCommand(validator, status)) { + return this->Makefile->GetDefinition(resultName).IsOn(); + } + return false; +} + void cmFindBase::ExpandPaths() { if (!this->NoDefaultPath) { diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h index d197424..75d9a6d 100644 --- a/Source/cmFindBase.h +++ b/Source/cmFindBase.h @@ -31,6 +31,11 @@ public: */ virtual bool ParseArguments(std::vector<std::string> const& args); + /** + * To check validity of a found path using user's validator, if any + */ + bool Validate(const std::string& path) const; + protected: friend class cmFindBaseDebugState; void ExpandPaths(); @@ -63,6 +68,8 @@ protected: bool Required = false; + std::string ValidatorName; + private: // Add pieces of the search. void FillPackageRootPath(); diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index 1c4039b..6296a60 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -192,6 +192,7 @@ struct cmFindLibraryHelper // Context information. cmMakefile* Makefile; + cmFindBase const* FindBase; cmGlobalGenerator* GG; // List of valid prefixes and suffixes. @@ -239,6 +240,11 @@ struct cmFindLibraryHelper bool CheckDirectory(std::string const& path); bool CheckDirectoryForName(std::string const& path, Name& name); + bool Validate(const std::string& path) const + { + return this->FindBase->Validate(path); + } + cmFindBaseDebugState DebugSearches; void DebugLibraryFailed(std::string const& name, std::string const& path) @@ -291,6 +297,7 @@ std::string const& get_suffixes(cmMakefile* mf) cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf, cmFindBase const* base) : Makefile(mf) + , FindBase(base) , DebugMode(base->DebugModeEnabled()) , DebugSearches(std::move(debugName), base) { @@ -416,10 +423,13 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path, if (!exists) { this->DebugLibraryFailed(name.Raw, path); } else { - this->DebugLibraryFound(name.Raw, path); - this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath); - cmSystemTools::ConvertToUnixSlashes(this->BestPath); - return true; + auto testPath = cmSystemTools::CollapseFullPath(this->TestPath); + if (this->Validate(testPath)) { + this->DebugLibraryFound(name.Raw, path); + this->BestPath = testPath; + return true; + } + this->DebugLibraryFailed(name.Raw, path); } } @@ -443,8 +453,11 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path, this->TestPath = cmStrCat(path, origName); // Make sure the path is readable and is not a directory. if (cmSystemTools::FileExists(this->TestPath, true)) { - this->DebugLibraryFound(name.Raw, dir); + if (!this->Validate(cmSystemTools::CollapseFullPath(this->TestPath))) { + continue; + } + this->DebugLibraryFound(name.Raw, dir); // This is a matching file. Check if it is better than the // best name found so far. Earlier prefixes are preferred, // followed by earlier suffixes. For OpenBSD, shared library @@ -541,7 +554,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryNamesPerDir() for (std::string const& n : this->Names) { fwPath = cmStrCat(d, n, ".framework"); if (cmSystemTools::FileIsDirectory(fwPath)) { - return cmSystemTools::CollapseFullPath(fwPath); + auto finalPath = cmSystemTools::CollapseFullPath(fwPath); + if (this->Validate(finalPath)) { + return finalPath; + } } } } @@ -558,7 +574,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryDirsPerName() for (std::string const& d : this->SearchPaths) { fwPath = cmStrCat(d, n, ".framework"); if (cmSystemTools::FileIsDirectory(fwPath)) { - return cmSystemTools::CollapseFullPath(fwPath); + auto finalPath = cmSystemTools::CollapseFullPath(fwPath); + if (this->Validate(finalPath)) { + return finalPath; + } } } } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 8c6a0aa..4ad9124 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -822,13 +822,13 @@ void cmFindPackageCommand::SetVersionVariables( char buf[64]; snprintf(buf, sizeof(buf), "%u", major); addDefinition(prefix + "_MAJOR", buf); - sprintf(buf, "%u", minor); + snprintf(buf, sizeof(buf), "%u", minor); addDefinition(prefix + "_MINOR", buf); - sprintf(buf, "%u", patch); + snprintf(buf, sizeof(buf), "%u", patch); addDefinition(prefix + "_PATCH", buf); - sprintf(buf, "%u", tweak); + snprintf(buf, sizeof(buf), "%u", tweak); addDefinition(prefix + "_TWEAK", buf); - sprintf(buf, "%u", count); + snprintf(buf, sizeof(buf), "%u", count); addDefinition(prefix + "_COUNT", buf); } diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx index 27074ff..74a69d8 100644 --- a/Source/cmFindPathCommand.cxx +++ b/Source/cmFindPathCommand.cxx @@ -88,7 +88,8 @@ std::string cmFindPathCommand::FindHeaderInFramework( if (!frameWorkName.empty()) { std::string fpath = cmStrCat(dir, frameWorkName, ".framework"); std::string intPath = cmStrCat(fpath, "/Headers/", fileName); - if (cmSystemTools::FileExists(intPath)) { + if (cmSystemTools::FileExists(intPath) && + this->Validate(this->IncludeFileInPath ? intPath : fpath)) { debug.FoundAt(intPath); if (this->IncludeFileInPath) { return intPath; @@ -124,7 +125,8 @@ std::string cmFindPathCommand::FindNormalHeader(cmFindBaseDebugState& debug) for (std::string const& n : this->Names) { for (std::string const& sp : this->SearchPaths) { tryPath = cmStrCat(sp, n); - if (cmSystemTools::FileExists(tryPath)) { + if (cmSystemTools::FileExists(tryPath) && + this->Validate(this->IncludeFileInPath ? tryPath : sp)) { debug.FoundAt(tryPath); if (this->IncludeFileInPath) { return tryPath; diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index a64e0e4..8a2a69e 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -27,6 +27,7 @@ struct cmFindProgramHelper cmFindBase const* base) : DebugSearches(std::move(debugName), base) , Makefile(makefile) + , FindBase(base) , PolicyCMP0109(makefile->GetPolicyStatus(cmPolicies::CMP0109)) { #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) @@ -56,6 +57,7 @@ struct cmFindProgramHelper // Debug state cmFindBaseDebugState DebugSearches; cmMakefile* Makefile; + cmFindBase const* FindBase; cmPolicies::PolicyStatus PolicyCMP0109; @@ -94,7 +96,7 @@ struct cmFindProgramHelper this->TestNameExt = cmStrCat(name, ext); this->TestPath = cmSystemTools::CollapseFullPath( this->TestNameExt, path); - bool exists = this->FileIsExecutable(this->TestPath); + bool exists = this->FileIsValid(this->TestPath); exists ? this->DebugSearches.FoundAt(this->TestPath) : this->DebugSearches.FailedAt(this->TestPath); if (exists) { @@ -104,12 +106,12 @@ struct cmFindProgramHelper return false; }); } - bool FileIsExecutable(std::string const& file) const + bool FileIsValid(std::string const& file) const { -#ifdef _WIN32 if (!this->FileIsExecutableCMP0109(file)) { return false; } +#ifdef _WIN32 // Pretend the Windows "python" app installer alias does not exist. if (cmSystemTools::LowerCase(file).find("/windowsapps/python") != std::string::npos) { @@ -119,10 +121,8 @@ struct cmFindProgramHelper return false; } } - return true; -#else - return this->FileIsExecutableCMP0109(file); #endif + return this->FindBase->Validate(file); } bool FileIsExecutableCMP0109(std::string const& file) const { diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 8a7215b..433c1d5 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -807,11 +807,16 @@ void handleSystemIncludesDep(cmLocalGenerator* lg, dagChecker, depTgt, language), result); } - if (!depTgt->IsImported() || excludeImported) { + if (!depTgt->GetPropertyAsBool("SYSTEM")) { return; } - if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { - return; + if (depTgt->IsImported()) { + if (excludeImported) { + return; + } + if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { + return; + } } if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) { @@ -1707,7 +1712,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget, } } if (!found) { - if (fileSet->GetType() == "HEADERS"_s) { + if (fileSet->GetType() == "HEADERS"_s || + fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) { headTarget->Makefile->GetOrCreateSourceGroup("Header Files") ->AddGroupFile(path); } @@ -1728,6 +1734,20 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget, addFileSetEntry(headTarget, config, dagChecker, headerSet, entries); } } + for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) { + for (auto const& name : cmExpandedList(entry.Value)) { + auto const* cxxModuleSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries); + } + } + for (auto const& entry : + headTarget->Target->GetCxxModuleHeaderSetsEntries()) { + for (auto const& name : cmExpandedList(entry.Value)) { + auto const* cxxModuleHeaderSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleHeaderSet, + entries); + } + } } bool processSources(cmGeneratorTarget const* tgt, @@ -8700,3 +8720,76 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile( return filename; } + +bool cmGeneratorTarget::HaveCxx20ModuleSources() const +{ + auto const& fs_names = this->Target->GetAllFileSetNames(); + return std::any_of(fs_names.begin(), fs_names.end(), + [this](std::string const& name) -> bool { + auto const* file_set = this->Target->GetFileSet(name); + if (!file_set) { + this->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", this->Target->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + return false; + } + + auto const& fs_type = file_set->GetType(); + return fs_type == "CXX_MODULES"_s || + fs_type == "CXX_MODULE_HEADER_UNITS"_s; + }); +} + +cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( + std::string const& config) const +{ + auto const* state = this->Makefile->GetState(); + if (!state->GetLanguageEnabled("CXX")) { + return Cxx20SupportLevel::MissingCxx; + } + cmStandardLevelResolver standardResolver(this->Makefile); + if (!standardResolver.HaveStandardAvailable(this, "CXX", config, + "cxx_std_20")) { + return Cxx20SupportLevel::NoCxx20; + } + if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { + return Cxx20SupportLevel::MissingExperimentalFlag; + } + return Cxx20SupportLevel::Supported; +} + +void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const +{ + // Check for `CXX_MODULE*` file sets and a lack of support. + if (this->HaveCxx20ModuleSources()) { + switch (this->HaveCxxModuleSupport(config)) { + case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GetName(), + "\" target has C++ module sources but the \"CXX\" language " + "has not been enabled")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GetName(), + "\" target has C++ module sources but its experimental " + "support has not been requested")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "The \"", this->GetName(), + "\" target has C++ module sources but is not using at least " + "\"cxx_std_20\"")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::Supported: + // All is well. + break; + } + } +} diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 6bce7d2..349afa7 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -1196,4 +1196,34 @@ public: bool operator()(cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const; }; + + // C++20 module support queries. + + /** + * Query whether the target expects C++20 module support. + * + * This will inspect the target itself to see if C++20 module + * support is expected to work based on its sources. + */ + bool HaveCxx20ModuleSources() const; + + enum class Cxx20SupportLevel + { + // C++ is not available. + MissingCxx, + // The experimental feature is not available. + MissingExperimentalFlag, + // The target does not require at least C++20. + NoCxx20, + // C++20 modules are available and working. + Supported, + }; + /** + * Query whether the target has C++20 module support available (regardless of + * whether it is required or not). + */ + Cxx20SupportLevel HaveCxxModuleSupport(std::string const& config) const; + + // Check C++ module status for the target. + void CheckCxxModuleStatus(std::string const& config) const; }; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index acaed36..b4d5746 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <cctype> #include <cstdio> +#include <functional> #include <sstream> #include <utility> @@ -14,6 +15,7 @@ #include <cm/string_view> #include <cmext/algorithm> #include <cmext/memory> +#include <cmext/string_view> #include <cm3p/json/reader.h> #include <cm3p/json/value.h> @@ -21,7 +23,9 @@ #include "cmsys/FStream.hxx" +#include "cmCxxModuleMapper.h" #include "cmDocumentationEntry.h" +#include "cmFileSet.h" #include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" @@ -2480,13 +2484,53 @@ cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( } } +struct CxxModuleFileSet +{ + std::string Name; + std::string RelativeDirectory; + std::string SourcePath; + std::string Type; + cmFileSetVisibility Visibility; + cm::optional<std::string> Destination; +}; + +struct CxxModuleBmiInstall +{ + std::string Component; + std::string Destination; + bool ExcludeFromAll; + bool Optional; + std::string Permissions; + std::string MessageLevel; + std::string ScriptLocation; +}; + +struct CxxModuleExport +{ + std::string Name; + std::string Destination; + std::string Prefix; + std::string CxxModuleInfoDir; + std::string Namespace; + bool Install; +}; + +struct cmGlobalNinjaGenerator::CxxModuleExportInfo +{ + std::map<std::string, CxxModuleFileSet> ObjectToFileSet; + cm::optional<CxxModuleBmiInstall> BmiInstallation; + std::vector<CxxModuleExport> Exports; + std::string Config; +}; + bool cmGlobalNinjaGenerator::WriteDyndepFile( std::string const& dir_top_src, std::string const& dir_top_bld, std::string const& dir_cur_src, std::string const& dir_cur_bld, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt) + std::string const& arg_lang, std::string const& arg_modmapfmt, + CxxModuleExportInfo const& export_info) { // Setup path conversions. { @@ -2517,7 +2561,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // Populate the module map with those provided by linked targets first. for (std::string const& linked_target_dir : linked_target_dirs) { std::string const ltmn = - cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json"); + cmStrCat(linked_target_dir, '/', arg_lang, "Modules.json"); Json::Value ltm; cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; @@ -2534,11 +2578,20 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( } } - const char* module_ext = ""; - if (arg_modmapfmt == "gcc") { - module_ext = ".gcm"; + cm::optional<CxxModuleMapFormat> modmap_fmt; + if (arg_modmapfmt.empty()) { + // nothing to do. + } else if (arg_modmapfmt == "gcc") { + modmap_fmt = CxxModuleMapFormat::Gcc; + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep does not understand the ", arg_modmapfmt, + " module map format")); + return false; } + auto module_ext = CxxModuleMapExtension(modmap_fmt); + // Extend the module map with those provided by this target. // We do this after loading the modules provided by linked targets // in case we have one of the same name that must be preferred. @@ -2555,7 +2608,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( } } else { // Assume the module file path matches the logical module name. - std::string safe_logical_name = p.LogicalName; + std::string safe_logical_name = + p.LogicalName; // TODO: needs fixing for header units cmSystemTools::ReplaceString(safe_logical_name, ":", "-"); mod = cmStrCat(module_dir, safe_logical_name, module_ext); } @@ -2568,6 +2622,20 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( ddf << "ninja_dyndep_version = 1.0\n"; { + CxxModuleLocations locs; + locs.RootDirectory = "."; + locs.PathForGenerator = [this](std::string const& path) -> std::string { + return this->ConvertToNinjaPath(path); + }; + locs.BmiLocationForModule = + [&mod_files](std::string const& logical) -> cm::optional<std::string> { + auto m = mod_files.find(logical); + if (m != mod_files.end()) { + return m->second; + } + return {}; + }; + cmNinjaBuild build("dyndep"); build.Outputs.emplace_back(""); for (cmScanDepInfo const& object : objects) { @@ -2589,46 +2657,14 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( build.Variables.emplace("restat", "1"); } - if (arg_modmapfmt.empty()) { - // nothing to do. - } else { - std::stringstream mm; - if (arg_modmapfmt == "gcc") { - // Documented in GCC's documentation. The format is a series of lines - // with a module name and the associated filename separated by - // spaces. The first line may use `$root` as the module name to - // specify a "repository root". That is used to anchor any relative - // paths present in the file (CMake should never generate any). - - // Write the root directory to use for module paths. - mm << "$root .\n"; - - for (auto const& l : object.Provides) { - auto m = mod_files.find(l.LogicalName); - if (m != mod_files.end()) { - mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second) - << "\n"; - } - } - for (auto const& r : object.Requires) { - auto m = mod_files.find(r.LogicalName); - if (m != mod_files.end()) { - mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second) - << "\n"; - } - } - } else { - cmSystemTools::Error( - cmStrCat("-E cmake_ninja_dyndep does not understand the ", - arg_modmapfmt, " module map format")); - return false; - } + if (modmap_fmt) { + auto mm = CxxModuleMapContent(*modmap_fmt, locs, object); // XXX(modmap): If changing this path construction, change // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the // corresponding file path. cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap")); - mmf << mm.str(); + mmf << mm; } this->WriteBuild(ddf, build); @@ -2642,7 +2678,279 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( cmGeneratedFileStream tmf(target_mods_file); tmf << tm; - return true; + bool result = true; + + // Fortran doesn't support any of the file-set or BMI installation considered + // below. + if (arg_lang != "Fortran"_s) { + // Prepare the export information blocks. + std::string const config_upper = + cmSystemTools::UpperCase(export_info.Config); + std::vector<std::pair<std::unique_ptr<cmGeneratedFileStream>, + CxxModuleExport const*>> + exports; + for (auto const& exp : export_info.Exports) { + std::unique_ptr<cmGeneratedFileStream> properties; + + std::string const export_dir = + cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/'); + std::string const property_file_path = cmStrCat( + export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake"); + properties = cm::make_unique<cmGeneratedFileStream>(property_file_path); + + // Set up the preamble. + *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name + << "\"\n" + << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper + << '\n'; + + exports.emplace_back(std::move(properties), &exp); + } + + std::unique_ptr<cmGeneratedFileStream> bmi_install_script; + if (export_info.BmiInstallation) { + bmi_install_script = cm::make_unique<cmGeneratedFileStream>( + export_info.BmiInstallation->ScriptLocation); + } + + auto cmEscape = [](cm::string_view str) { + return cmOutputConverter::EscapeForCMake( + str, cmOutputConverter::WrapQuotes::NoWrap); + }; + auto install_destination = + [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> { + if (cmSystemTools::FileIsFullPath(dest)) { + return std::make_pair(true, cmEscape(dest)); + } + return std::make_pair(false, + cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest))); + }; + + // public/private requirement tracking. + std::set<std::string> private_modules; + std::map<std::string, std::set<std::string>> public_source_requires; + + for (cmScanDepInfo const& object : objects) { + // Convert to forward slashes. + auto output_path = object.PrimaryOutput; +# ifdef _WIN32 + cmSystemTools::ConvertToUnixSlashes(output_path); +# endif + // Find the fileset for this object. + auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path); + bool const has_provides = !object.Provides.empty(); + if (fileset_info_itr == export_info.ObjectToFileSet.end()) { + // If it provides anything, it should have a `CXX_MODULES` or + // `CXX_MODULE_INTERNAL_PARTITIONS` type and be present. + if (has_provides) { + // Take the first module provided to provide context. + auto const& provides = object.Provides[0]; + char const* ok_types = "`CXX_MODULES`"; + if (provides.LogicalName.find(':') != std::string::npos) { + ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " + "it is not `export`ed)"; + } + cmSystemTools::Error( + cmStrCat("Output ", object.PrimaryOutput, " provides the `", + provides.LogicalName, + "` module but it is not found in a `FILE_SET` of type ", + ok_types)); + result = false; + } + + // This object file does not provide anything, so nothing more needs to + // be done. + continue; + } + + auto const& file_set = fileset_info_itr->second; + + // Verify the fileset type for the object. + if (file_set.Type == "CXX_MODULES"_s) { + if (!has_provides) { + cmSystemTools::Error(cmStrCat( + "Output ", object.PrimaryOutput, + " is of type `CXX_MODULES` but does not provide a module")); + result = false; + continue; + } + } else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) { + if (!has_provides) { + cmSystemTools::Error(cmStrCat( + "Source ", file_set.SourcePath, + " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " + "provide a module")); + result = false; + continue; + } + auto const& provides = object.Provides[0]; + if (provides.LogicalName.find(':') == std::string::npos) { + cmSystemTools::Error(cmStrCat( + "Source ", file_set.SourcePath, + " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " + "provide a module partition")); + result = false; + continue; + } + } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) { + // TODO. + } else { + if (has_provides) { + auto const& provides = object.Provides[0]; + char const* ok_types = "`CXX_MODULES`"; + if (provides.LogicalName.find(':') != std::string::npos) { + ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " + "it is not `export`ed)"; + } + cmSystemTools::Error(cmStrCat( + "Source ", file_set.SourcePath, " provides the `", + provides.LogicalName, "` C++ module but is of type `", + file_set.Type, "` module but must be of type ", ok_types)); + result = false; + } + + // Not a C++ module; ignore. + continue; + } + + if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) { + // Nothing needs to be conveyed about non-`PUBLIC` modules. + for (auto const& p : object.Provides) { + private_modules.insert(p.LogicalName); + } + continue; + } + + // The module is public. Record what it directly requires. + { + auto& reqs = public_source_requires[file_set.SourcePath]; + for (auto const& r : object.Requires) { + reqs.insert(r.LogicalName); + } + } + + // Write out properties and install rules for any exports. + for (auto const& p : object.Provides) { + bool bmi_dest_is_abs = false; + std::string bmi_destination; + if (export_info.BmiInstallation) { + auto dest = + install_destination(export_info.BmiInstallation->Destination); + bmi_dest_is_abs = dest.first; + bmi_destination = cmStrCat(dest.second, '/'); + } + + std::string install_bmi_path; + std::string build_bmi_path; + auto m = mod_files.find(p.LogicalName); + if (m != mod_files.end()) { + install_bmi_path = + cmStrCat(bmi_destination, + cmEscape(cmSystemTools::GetFilenameName(m->second))); + build_bmi_path = cmEscape(m->second); + } + + for (auto const& exp : exports) { + std::string iface_source; + if (exp.second->Install && file_set.Destination) { + auto dest = install_destination(*file_set.Destination); + iface_source = cmStrCat( + dest.second, '/', cmEscape(file_set.RelativeDirectory), + cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath))); + } else { + iface_source = cmEscape(file_set.SourcePath); + } + + std::string bmi_path; + if (exp.second->Install && export_info.BmiInstallation) { + bmi_path = install_bmi_path; + } else if (!exp.second->Install) { + bmi_path = build_bmi_path; + } + + if (iface_source.empty()) { + // No destination for the C++ module source; ignore this property + // value. + continue; + } + + *exp.first << " \"" << cmEscape(p.LogicalName) << '=' + << iface_source; + if (!bmi_path.empty()) { + *exp.first << ',' << bmi_path; + } + *exp.first << "\"\n"; + } + + if (bmi_install_script) { + auto const& bmi_install = *export_info.BmiInstallation; + + *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \"" + << cmEscape(bmi_install.Component) << '\"'; + if (!bmi_install.ExcludeFromAll) { + *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT"; + } + *bmi_install_script << ")\n"; + *bmi_install_script << " file(INSTALL\n" + " DESTINATION \""; + if (!bmi_dest_is_abs) { + *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/"; + } + *bmi_install_script << cmEscape(bmi_install.Destination) + << "\"\n" + " TYPE FILE\n"; + if (bmi_install.Optional) { + *bmi_install_script << " OPTIONAL\n"; + } + if (!bmi_install.MessageLevel.empty()) { + *bmi_install_script << " " << bmi_install.MessageLevel << "\n"; + } + if (!bmi_install.Permissions.empty()) { + *bmi_install_script << " PERMISSIONS" << bmi_install.Permissions + << "\n"; + } + *bmi_install_script << " FILES \"" << m->second << "\")\n"; + if (bmi_dest_is_abs) { + *bmi_install_script + << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n" + " \"" + << cmEscape(cmSystemTools::GetFilenameName(m->second)) + << "\")\n" + " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n" + " message(WARNING\n" + " \"ABSOLUTE path INSTALL DESTINATION : " + "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" + " endif ()\n" + " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n" + " message(FATAL_ERROR\n" + " \"ABSOLUTE path INSTALL DESTINATION forbidden (by " + "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" + " endif ()\n"; + } + *bmi_install_script << "endif ()\n"; + } + } + } + + // Add trailing parenthesis for the `set_property` call. + for (auto const& exp : exports) { + *exp.first << ")\n"; + } + + // Check that public sources only require public modules. + for (auto const& pub_reqs : public_source_requires) { + for (auto const& req : pub_reqs.second) { + if (private_modules.count(req)) { + cmSystemTools::Error(cmStrCat( + "Public C++ module source `", pub_reqs.first, "` requires the `", + req, "` C++ module which is provided by a private source")); + result = false; + } + } + } + } + + return result; } int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -2716,6 +3024,59 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, } } + cmGlobalNinjaGenerator::CxxModuleExportInfo export_info; + export_info.Config = tdi["config"].asString(); + if (export_info.Config.empty()) { + export_info.Config = "noconfig"; + } + Json::Value const& tdi_exports = tdi["exports"]; + if (tdi_exports.isArray()) { + for (auto const& tdi_export : tdi_exports) { + CxxModuleExport exp; + exp.Install = tdi_export["install"].asBool(); + exp.Name = tdi_export["export-name"].asString(); + exp.Destination = tdi_export["destination"].asString(); + exp.Prefix = tdi_export["export-prefix"].asString(); + exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString(); + exp.Namespace = tdi_export["namespace"].asString(); + + export_info.Exports.push_back(exp); + } + } + auto const& bmi_installation = tdi["bmi-installation"]; + if (bmi_installation.isObject()) { + CxxModuleBmiInstall bmi_install; + + bmi_install.Component = bmi_installation["component"].asString(); + bmi_install.Destination = bmi_installation["destination"].asString(); + bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool(); + bmi_install.Optional = bmi_installation["optional"].asBool(); + bmi_install.Permissions = bmi_installation["permissions"].asString(); + bmi_install.MessageLevel = bmi_installation["message-level"].asString(); + bmi_install.ScriptLocation = + bmi_installation["script-location"].asString(); + + export_info.BmiInstallation = bmi_install; + } + Json::Value const& tdi_cxx_modules = tdi["cxx-modules"]; + if (tdi_cxx_modules.isObject()) { + for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) { + CxxModuleFileSet& fsi = export_info.ObjectToFileSet[i.key().asString()]; + auto const& tdi_cxx_module_info = *i; + fsi.Name = tdi_cxx_module_info["name"].asString(); + fsi.RelativeDirectory = + tdi_cxx_module_info["relative-directory"].asString(); + fsi.SourcePath = tdi_cxx_module_info["source"].asString(); + fsi.Type = tdi_cxx_module_info["type"].asString(); + fsi.Visibility = cmFileSetVisibilityFromName( + tdi_cxx_module_info["visibility"].asString(), nullptr); + auto const& tdi_fs_dest = tdi_cxx_module_info["destination"]; + if (tdi_fs_dest.isString()) { + fsi.Destination = tdi_fs_dest.asString(); + } + } + } + cmake cm(cmake::RoleInternal, cmState::Unknown); cm.SetHomeDirectory(dir_top_src); cm.SetHomeOutputDirectory(dir_top_bld); @@ -2723,7 +3084,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, if (!ggd || !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile( dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis, - module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) { + module_dir, linked_target_dirs, arg_lang, arg_modmapfmt, + export_info)) { return 1; } return 0; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index aa2df4d..dc4f444 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -417,13 +417,15 @@ public: bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } void StripNinjaOutputPathPrefixAsSuffix(std::string& path); + struct CxxModuleExportInfo; bool WriteDyndepFile( std::string const& dir_top_src, std::string const& dir_top_bld, std::string const& dir_cur_src, std::string const& dir_cur_bld, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt); + std::string const& arg_lang, std::string const& arg_modmapfmt, + CxxModuleExportInfo const& export_info); virtual std::string BuildAlias(const std::string& alias, const std::string& /*config*/) const diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index f7f7317..e53ae8e 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -395,12 +395,27 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( { VisualStudioFolders.clear(); + std::vector<std::string> configs = + root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (cmGeneratorTarget const* target : projectTargets) { if (!this->IsInSolution(target)) { continue; } bool written = false; + for (auto const& c : configs) { + target->CheckCxxModuleStatus(c); + } + + if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) { + root->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", target->GetName(), + "\" target contains C++ module sources which are not " + "supported by the generator")); + } + // handle external vc project files cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); if (expath) { diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index a55cf45..288069c 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -157,6 +157,8 @@ protected: cmValue typeGuid, const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0; + virtual bool SupportsCxxModuleDyndep() const { return false; } + std::string ConvertToSolutionPath(const std::string& path); std::set<std::string> IsPartOfDefaultBuild( diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index d5783ef..456f5bc 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -468,6 +468,10 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir, } CFRelease(cfStr); } +#else + (void)bindir; + (void)projectName; + (void)dryRun; #endif return ret; @@ -1372,6 +1376,18 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( return true; } + for (std::string const& configName : this->CurrentConfigurationTypes) { + gtgt->CheckCxxModuleStatus(configName); + } + + if (gtgt->HaveCxx20ModuleSources()) { + gtgt->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", gtgt->GetName(), + "\" target contains C++ module sources which are not " + "supported by the generator")); + } + auto& gtgt_visited = this->CommandsVisited[gtgt]; auto& deps = this->GetTargetDirectDepends(gtgt); for (auto& d : deps) { @@ -2407,6 +2423,18 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->AppendDefines(ppDefs, targetDefines); buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS", ppDefs.CreateList()); + if (languages.count("Swift")) { + if (this->XcodeVersion < 80) { + std::string defineString; + std::set<std::string> defines(targetDefines.begin(), + targetDefines.end()); + this->CurrentLocalGenerator->JoinDefines(defines, defineString, "Swift"); + cflags["Swift"] += " " + defineString; + } else { + buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS", + ppDefs.CreateList()); + } + } std::string extraLinkOptionsVar; std::string extraLinkOptions; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 7ca5b23..b1044a8 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -12,18 +12,22 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmsys/Glob.hxx" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" +#include "cmExperimental.h" #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmInstallCommandArguments.h" +#include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -54,13 +58,13 @@ namespace { struct RuntimeDependenciesArgs { - std::vector<std::string> Directories; - std::vector<std::string> PreIncludeRegexes; - std::vector<std::string> PreExcludeRegexes; - std::vector<std::string> PostIncludeRegexes; - std::vector<std::string> PostExcludeRegexes; - std::vector<std::string> PostIncludeFiles; - std::vector<std::string> PostExcludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles; }; auto const RuntimeDependenciesArgHelper = @@ -109,6 +113,8 @@ public: const cmInstallCommandArguments* args) const; std::string GetLibraryDestination( const cmInstallCommandArguments* args) const; + std::string GetCxxModulesBmiDestination( + const cmInstallCommandArguments* args) const; std::string GetIncludeDestination( const cmInstallCommandArguments* args) const; std::string GetSysconfDestination( @@ -401,16 +407,17 @@ bool HandleTargetsMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Archive; - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Object; - std::vector<std::string> Framework; - std::vector<std::string> Bundle; - std::vector<std::string> Includes; - std::vector<std::string> PrivateHeader; - std::vector<std::string> PublicHeader; - std::vector<std::string> Resource; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Archive; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Object; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Includes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PrivateHeader; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PublicHeader; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Resource; + ArgumentParser::MaybeEmpty<std::vector<std::string>> CxxModulesBmi; std::vector<std::vector<std::string>> FileSets; }; @@ -426,7 +433,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader) .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader) .Bind("RESOURCE"_s, &ArgVectors::Resource) - .Bind("FILE_SET"_s, &ArgVectors::FileSets); + .Bind("FILE_SET"_s, &ArgVectors::FileSets) + .Bind("CXX_MODULES_BMI"_s, &ArgVectors::CxxModulesBmi); std::vector<std::string> genericArgVector; ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector); @@ -434,26 +442,25 @@ bool HandleTargetsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY/ // ARCHIVE, RUNTIME etc. (see above) // These generic args also contain the targets and the export stuff - std::vector<std::string> targetList; + ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList; std::string exports; - std::vector<std::string> runtimeDependenciesArgVector; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> + runtimeDependenciesArgVector; std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; - std::vector<std::string> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &unknownArgs, nullptr, &parsedArgs); + genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); - bool withRuntimeDependencies = - std::find(parsedArgs.begin(), parsedArgs.end(), "RUNTIME_DEPENDENCIES") != - parsedArgs.end(); RuntimeDependenciesArgs runtimeDependenciesArgs = - RuntimeDependenciesArgHelper.Parse(runtimeDependenciesArgVector, - &unknownArgs); + runtimeDependenciesArgVector + ? RuntimeDependenciesArgHelper.Parse(*runtimeDependenciesArgVector, + &unknownArgs) + : RuntimeDependenciesArgs(); cmInstallCommandArguments archiveArgs(helper.DefaultComponentName); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); @@ -467,6 +474,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, cmInstallCommandIncludesArgument includesArgs; std::vector<cmInstallCommandFileSetArguments> fileSetArgs( argVectors.FileSets.size(), { helper.DefaultComponentName }); + cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -490,6 +498,15 @@ bool HandleTargetsMode(std::vector<std::string> const& args, fileSetArgs[i] = std::move(fileSetArg); } + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + if (!supportCxx20FileSetTypes) { + std::copy(argVectors.CxxModulesBmi.begin(), argVectors.CxxModulesBmi.end(), + std::back_inserter(unknownArgs)); + } else { + cxxModuleBmiArgs.Parse(argVectors.CxxModulesBmi, &unknownArgs); + } + if (!unknownArgs.empty()) { // Unknown argument. status.SetError( @@ -510,6 +527,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& fileSetArg : fileSetArgs) { fileSetArg.SetGenericArguments(&genericArgs); } + cxxModuleBmiArgs.SetGenericArguments(&genericArgs); success = success && archiveArgs.Finalize(); success = success && libraryArgs.Finalize(); @@ -523,6 +541,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& fileSetArg : fileSetArgs) { success = success && fileSetArg.Finalize(); } + if (supportCxx20FileSetTypes) { + success = success && cxxModuleBmiArgs.Finalize(); + } if (!success) { return false; @@ -536,7 +557,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.GetNamelinkOnly(); })) { + -> bool { return fileSetArg.GetNamelinkOnly(); }) || + cxxModuleBmiArgs.GetNamelinkOnly()) { status.SetError( "TARGETS given NAMELINK_ONLY option not in LIBRARY group. " "The NAMELINK_ONLY option may be specified only following LIBRARY."); @@ -548,7 +570,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.GetNamelinkSkip(); })) { + -> bool { return fileSetArg.GetNamelinkSkip(); }) || + cxxModuleBmiArgs.GetNamelinkSkip()) { status.SetError( "TARGETS given NAMELINK_SKIP option not in LIBRARY group. " "The NAMELINK_SKIP option may be specified only following LIBRARY."); @@ -564,7 +587,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, resourceArgs.HasNamelinkComponent() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.HasNamelinkComponent(); })) { + -> bool { return fileSetArg.HasNamelinkComponent(); }) || + cxxModuleBmiArgs.HasNamelinkComponent()) { status.SetError( "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " "The NAMELINK_COMPONENT option may be specified only following " @@ -583,7 +607,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return !fileSetArg.GetType().empty(); })) { + -> bool { return !fileSetArg.GetType().empty(); }) || + !cxxModuleBmiArgs.GetType().empty()) { status.SetError( "TARGETS given TYPE option. The TYPE option may only be specified in " " install(FILES) and install(DIRECTORIES)."); @@ -597,7 +622,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; - if (withRuntimeDependencies) { + if (runtimeDependenciesArgVector) { if (!runtimeDependencySetArg.empty()) { status.SetError("TARGETS cannot have both RUNTIME_DEPENDENCIES and " "RUNTIME_DEPENDENCY_SET."); @@ -706,6 +731,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, bool installsPublicHeader = false; bool installsResource = false; std::vector<bool> installsFileSet(fileSetArgs.size(), false); + bool installsCxxModuleBmi = false; // Generate install script code to install the given targets. for (cmTarget* ti : targets) { @@ -722,6 +748,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator; std::unique_ptr<cmInstallFilesGenerator> resourceGenerator; std::vector<std::unique_ptr<cmInstallFileSetGenerator>> fileSetGenerators; + std::unique_ptr<cmInstallCxxModuleBmiGenerator> cxxModuleBmiGenerator; // Avoid selecting default destinations for PUBLIC_HEADER and // PRIVATE_HEADER if any artifacts are specified. @@ -760,6 +787,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto const& gen : fileSetGenerators) { te->FileSetGenerators[gen->GetFileSet()] = gen.get(); } + te->CxxModuleBmiGenerator = cxxModuleBmiGenerator.get(); target.AddInstallIncludeDirectories( *te, cmMakeRange(includesArgs.GetIncludeDirs())); te->NamelinkOnly = namelinkOnly; @@ -1105,6 +1133,19 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } } + if (supportCxx20FileSetTypes && + !cxxModuleBmiArgs.GetDestination().empty()) { + cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>( + target.GetName(), + helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs), + cxxModuleBmiArgs.GetPermissions(), + cxxModuleBmiArgs.GetConfigurations(), cxxModuleBmiArgs.GetComponent(), + cmInstallGenerator::SelectMessageLevel(target.GetMakefile()), + cxxModuleBmiArgs.GetExcludeFromAll(), cxxModuleBmiArgs.GetOptional(), + helper.Makefile->GetBacktrace()); + target.SetHaveInstallRule(true); + } + // Add this install rule to an export if one was specified. if (!addTargetExport()) { return false; @@ -1121,6 +1162,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, installsPrivateHeader = installsPrivateHeader || privateHeaderGenerator; installsPublicHeader = installsPublicHeader || publicHeaderGenerator; installsResource = installsResource || resourceGenerator; + installsCxxModuleBmi = installsCxxModuleBmi || cxxModuleBmiGenerator; helper.Makefile->AddInstallGenerator(std::move(archiveGenerator)); helper.Makefile->AddInstallGenerator(std::move(libraryGenerator)); @@ -1135,9 +1177,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& gen : fileSetGenerators) { helper.Makefile->AddInstallGenerator(std::move(gen)); } + helper.Makefile->AddInstallGenerator(std::move(cxxModuleBmiGenerator)); } - if (withRuntimeDependencies && !runtimeDependencySet->Empty()) { + if (runtimeDependenciesArgVector && !runtimeDependencySet->Empty()) { AddInstallRuntimeDependenciesGenerator( helper, runtimeDependencySet, runtimeArgs, libraryArgs, frameworkArgs, std::move(runtimeDependenciesArgs), installsRuntime, installsLibrary, @@ -1192,6 +1235,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, fileSetArgs[i].GetComponent()); } } + if (installsCxxModuleBmi) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + cxxModuleBmiArgs.GetComponent()); + } return true; } @@ -1206,10 +1253,10 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Framework; - std::vector<std::string> Bundle; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle; }; static auto const argHelper = cmArgumentParser<ArgVectors>{} @@ -1223,7 +1270,7 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY, // RUNTIME etc. (see above) - std::vector<std::string> targetList; + ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList; std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); @@ -1464,7 +1511,7 @@ bool HandleFilesMode(std::vector<std::string> const& args, // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); cmInstallCommandArguments ica(helper.DefaultComponentName); - std::vector<std::string> files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> files; ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -1950,7 +1997,7 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, exportOld, true, + ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, helper.Makefile->GetBacktrace())); return true; @@ -1973,12 +2020,19 @@ bool HandleExportMode(std::vector<std::string> const& args, std::string name_space; bool exportOld = false; std::string filename; + std::string cxx_modules_directory; ica.Bind("EXPORT"_s, exp); ica.Bind("NAMESPACE"_s, name_space); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + if (supportCxx20FileSetTypes) { + ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory); + } + std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -2064,8 +2118,8 @@ bool HandleExportMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, exportOld, false, - helper.Makefile->GetBacktrace())); + ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, + exportOld, false, helper.Makefile->GetBacktrace())); return true; } @@ -2088,9 +2142,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; }; static auto const argHelper = cmArgumentParser<ArgVectors>{} @@ -2106,7 +2160,7 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, // These generic args also contain the runtime dependency set std::string runtimeDependencySetArg; std::vector<std::string> runtimeDependencyArgVector; - std::vector<std::string> parsedArgs; + std::vector<cm::string_view> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr, @@ -2280,6 +2334,15 @@ std::string Helper::GetLibraryDestination( return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); } +std::string Helper::GetCxxModulesBmiDestination( + const cmInstallCommandArguments* args) const +{ + if (args) { + return args->GetDestination(); + } + return {}; +} + std::string Helper::GetIncludeDestination( const cmInstallCommandArguments* args) const { diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 79bd945..6e46aac 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -8,6 +8,7 @@ #include <vector> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" class cmInstallCommandArguments : public cmArgumentParser<void> { @@ -44,8 +45,8 @@ private: std::string NamelinkComponent; bool ExcludeFromAll = false; std::string Rename; - std::vector<std::string> Permissions; - std::vector<std::string> Configurations; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Permissions; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations; bool Optional = false; bool NamelinkOnly = false; bool NamelinkSkip = false; diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx new file mode 100644 index 0000000..1ef1eaa --- /dev/null +++ b/Source/cmInstallCxxModuleBmiGenerator.cxx @@ -0,0 +1,75 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallCxxModuleBmiGenerator.h" + +#include <ostream> +#include <utility> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmOutputConverter.h" +#include "cmStringAlgorithms.h" + +cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator( + std::string target, std::string const& dest, std::string file_permissions, + std::vector<std::string> const& configurations, std::string const& component, + MessageLevel message, bool exclude_from_all, bool optional, + cmListFileBacktrace backtrace) + : cmInstallGenerator(dest, configurations, component, message, + exclude_from_all, false, std::move(backtrace)) + , TargetName(std::move(target)) + , FilePermissions(std::move(file_permissions)) + , Optional(optional) +{ + this->ActionsPerConfig = true; +} + +cmInstallCxxModuleBmiGenerator::~cmInstallCxxModuleBmiGenerator() = default; + +bool cmInstallCxxModuleBmiGenerator::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + + this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName); + if (!this->Target) { + // If no local target has been found, find it in the global scope. + this->Target = + lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName); + } + + return true; +} + +std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation( + std::string const& config) const +{ + char const* config_name = config.c_str(); + if (config.empty()) { + config_name = "noconfig"; + } + return cmStrCat(this->Target->GetSupportDirectory(), + "/install-cxx-module-bmi-", config_name, ".cmake"); +} + +std::string cmInstallCxxModuleBmiGenerator::GetDestination( + std::string const& config) const +{ + return cmGeneratorExpression::Evaluate(this->Destination, + this->LocalGenerator, config); +} + +void cmInstallCxxModuleBmiGenerator::GenerateScriptForConfig( + std::ostream& os, const std::string& config, Indent indent) +{ + auto const& loc = this->GetScriptLocation(config); + if (loc.empty()) { + return; + } + os << indent << "include(\"" + << cmOutputConverter::EscapeForCMake( + loc, cmOutputConverter::WrapQuotes::NoWrap) + << "\" OPTIONAL)\n"; +} diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h new file mode 100644 index 0000000..21edb2e --- /dev/null +++ b/Source/cmInstallCxxModuleBmiGenerator.h @@ -0,0 +1,52 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <string> +#include <vector> + +#include "cmInstallGenerator.h" +#include "cmScriptGenerator.h" + +class cmGeneratorTarget; +class cmListFileBacktrace; +class cmLocalGenerator; + +/** \class cmInstallCxxModuleBmiGenerator + * \brief Generate C++ module BMI installation rules. + */ +class cmInstallCxxModuleBmiGenerator : public cmInstallGenerator +{ +public: + cmInstallCxxModuleBmiGenerator( + std::string target, std::string const& dest, std::string file_permissions, + std::vector<std::string> const& configurations, + std::string const& component, MessageLevel message, bool exclude_from_all, + bool optional, cmListFileBacktrace backtrace); + ~cmInstallCxxModuleBmiGenerator() override; + + bool Compute(cmLocalGenerator* lg) override; + + std::string const& GetFilePermissions() const + { + return this->FilePermissions; + } + std::string GetDestination(std::string const& config) const; + std::string GetScriptLocation(std::string const& config) const; + cmGeneratorTarget const* GetTarget() const { return this->Target; } + bool GetOptional() const { return this->Optional; } + MessageLevel GetMessageLevel() const { return this->Message; } + +protected: + void GenerateScriptForConfig(std::ostream& os, const std::string& config, + Indent indent) override; + + std::string const TargetName; + cmGeneratorTarget const* Target = nullptr; + cmLocalGenerator* LocalGenerator = nullptr; + std::string const FilePermissions; + bool const Optional; +}; diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index b80437d..1d81b0b 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -23,7 +23,8 @@ cmInstallExportGenerator::cmInstallExportGenerator( cmExportSet* exportSet, std::string const& destination, std::string file_permissions, std::vector<std::string> const& configurations, std::string const& component, MessageLevel message, bool exclude_from_all, - std::string filename, std::string name_space, bool exportOld, bool android, + std::string filename, std::string name_space, + std::string cxx_modules_directory, bool exportOld, bool android, cmListFileBacktrace backtrace) : cmInstallGenerator(destination, configurations, component, message, exclude_from_all, false, std::move(backtrace)) @@ -31,6 +32,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( , FilePermissions(std::move(file_permissions)) , FileName(std::move(filename)) , Namespace(std::move(name_space)) + , CxxModulesDirectory(std::move(cxx_modules_directory)) , ExportOld(exportOld) { if (android) { @@ -141,6 +143,75 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, os << indent << "endif()\n"; files.clear(); } + + // Now create a configuration-specific install rule for the C++ module import + // property file of each configuration. + auto cxx_module_dest = + cmStrCat(this->Destination, '/', this->CxxModulesDirectory); + std::string config_file_example; + for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { + config_file_example = i.second; + break; + } + if (!config_file_example.empty()) { + // Remove old per-configuration export files if the main changes. + std::string installedDir = cmStrCat( + "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/'); + std::string installedFile = cmStrCat(installedDir, "/cxx-modules.cmake"); + std::string toInstallFile = + cmStrCat(cmSystemTools::GetFilenamePath(config_file_example), + "/cxx-modules.cmake"); + os << indent << "if(EXISTS \"" << installedFile << "\")\n"; + Indent indentN = indent.Next(); + Indent indentNN = indentN.Next(); + Indent indentNNN = indentNN.Next(); + /* clang-format off */ + os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" + << indentN << " \"" << installedFile << "\"\n" + << indentN << " \"" << toInstallFile << "\")\n"; + os << indentN << "if(_cmake_export_file_changed)\n"; + os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir + << this->EFGen->GetConfigImportFileGlob() << "\")\n"; + os << indentNN << "if(_cmake_old_config_files)\n"; + os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n"; + os << indentNNN << R"(message(STATUS "Old C++ module export file \")" << installedFile + << "\\\" will be replaced. Removing files [${_cmake_old_config_files_text}].\")\n"; + os << indentNNN << "unset(_cmake_old_config_files_text)\n"; + os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n"; + os << indentNN << "endif()\n"; + os << indentNN << "unset(_cmake_old_config_files)\n"; + os << indentN << "endif()\n"; + os << indentN << "unset(_cmake_export_file_changed)\n"; + os << indent << "endif()\n"; + /* clang-format on */ + + // All of these files are siblings; get its location to know where the + // "anchor" file is. + files.push_back(toInstallFile); + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent); + files.clear(); + } + for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { + files.push_back(i.second); + std::string config_test = this->CreateConfigTest(i.first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } + for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) { + std::string config_test = this->CreateConfigTest(i.first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, i.second, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } } void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 02fe1fa..346ca67 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -28,7 +28,8 @@ public: const std::vector<std::string>& configurations, std::string const& component, MessageLevel message, bool exclude_from_all, std::string filename, - std::string name_space, bool exportOld, + std::string name_space, + std::string cxx_modules_directory, bool exportOld, bool android, cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -50,6 +51,10 @@ public: std::string GetDestinationFile() const; std::string GetFileName() const { return this->FileName; } std::string GetTempDir() const; + std::string GetCxxModuleDirectory() const + { + return this->CxxModulesDirectory; + } protected: void GenerateScript(std::ostream& os) override; @@ -64,6 +69,7 @@ protected: std::string const FilePermissions; std::string const FileName; std::string const Namespace; + std::string const CxxModulesDirectory; bool const ExportOld; cmLocalGenerator* LocalGenerator = nullptr; diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index e125470..de1d3cd 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1412,13 +1412,16 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) { // The dependencies must be regenerated. - std::string targetName = cmSystemTools::GetFilenameName(targetDir); - targetName = targetName.substr(0, targetName.length() - 4); - std::string message = - cmStrCat("Scanning dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); + if (verbose) { + std::string targetName = cmSystemTools::GetFilenameName(targetDir); + targetName = targetName.substr(0, targetName.length() - 4); + std::string message = + cmStrCat("Scanning dependencies of target ", targetName); + cmSystemTools::MakefileColorEcho( + cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + } status = this->ScanDependencies(targetDir, dependFile, internalDependFile, validDependencies); @@ -1447,13 +1450,19 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( this->GetBinaryDirectory()) : std::function<bool(const std::string&)>())) { // regenerate dependencies files - std::string targetName = - cmCMakePath(targetDir).GetFileName().RemoveExtension().GenericString(); - auto message = cmStrCat( - "Consolidate compiler generated dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); + if (verbose) { + std::string targetName = cmCMakePath(targetDir) + .GetFileName() + .RemoveExtension() + .GenericString(); + auto message = + cmStrCat("Consolidate compiler generated dependencies of target ", + targetName); + cmSystemTools::MakefileColorEcho( + cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + } // Open the make depends file. This should be copy-if-different // because the make tool may try to reload it needlessly otherwise. diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 628eb1d..208d907 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -149,6 +149,29 @@ void cmMakefile::IssueMessage(MessageType t, std::string const& text) const this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace); } +Message::LogLevel cmMakefile::GetCurrentLogLevel() const +{ + const cmake* cmakeInstance = this->GetCMakeInstance(); + + const Message::LogLevel logLevelCliOrDefault = cmakeInstance->GetLogLevel(); + assert("Expected a valid log level here" && + logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel result = logLevelCliOrDefault; + + // If the log-level was set via the command line option, it takes precedence + // over the CMAKE_MESSAGE_LOG_LEVEL variable. + if (!cmakeInstance->WasLogLevelSetViaCLI()) { + const Message::LogLevel logLevelFromVar = cmake::StringToLogLevel( + this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); + if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) { + result = logLevelFromVar; + } + } + + return result; +} + bool cmMakefile::CheckCMP0037(std::string const& targetName, cmStateEnums::TargetType targetType) const { @@ -3456,7 +3479,7 @@ void cmMakefile::AddTargetObject(std::string const& tgtName, #endif } -void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, +void cmMakefile::EnableLanguage(std::vector<std::string> const& languages, bool optional) { if (this->DeferRunning) { @@ -3468,24 +3491,48 @@ void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { this->AddDefinition("CMAKE_CFG_INTDIR", def); } + + std::vector<std::string> unique_languages; + { + std::vector<std::string> duplicate_languages; + for (std::string const& language : languages) { + if (!cm::contains(unique_languages, language)) { + unique_languages.push_back(language); + } else if (!cm::contains(duplicate_languages, language)) { + duplicate_languages.push_back(language); + } + } + if (!duplicate_languages.empty()) { + auto quantity = duplicate_languages.size() == 1 ? std::string(" has") + : std::string("s have"); + this->IssueMessage(MessageType::AUTHOR_WARNING, + "Languages to be enabled may not be specified more " + "than once at the same time. The following language" + + quantity + " been specified multiple times: " + + cmJoin(duplicate_languages, ", ")); + } + } + // If RC is explicitly listed we need to do it after other languages. // On some platforms we enable RC implicitly while enabling others. // Do not let that look like recursive enable_language(RC). - std::vector<std::string> langs; - std::vector<std::string> langsRC; - langs.reserve(lang.size()); - for (std::string const& i : lang) { - if (i == "RC") { - langsRC.push_back(i); + std::vector<std::string> languages_without_RC; + std::vector<std::string> languages_for_RC; + languages_without_RC.reserve(unique_languages.size()); + for (std::string const& language : unique_languages) { + if (language == "RC") { + languages_for_RC.push_back(language); } else { - langs.push_back(i); + languages_without_RC.push_back(language); } } - if (!langs.empty()) { - this->GetGlobalGenerator()->EnableLanguage(langs, this, optional); + if (!languages_without_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_without_RC, this, + optional); } - if (!langsRC.empty()) { - this->GetGlobalGenerator()->EnableLanguage(langsRC, this, optional); + if (!languages_for_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_for_RC, this, + optional); } } @@ -4431,7 +4478,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0102 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index e7b9716..df40c82 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -925,6 +925,7 @@ public: }; void IssueMessage(MessageType t, std::string const& text) const; + Message::LogLevel GetCurrentLogLevel() const; /** Set whether or not to report a CMP0000 violation. */ void SetCheckCMP0000(bool b) { this->CheckCMP0000 = b; } diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 3849c6f..74574f7 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -182,14 +182,16 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( // Collect up flags to link in needed libraries. std::string linkLibs; - this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + this->CreateLinkLibs( + linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; - this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects, - buildObjs, depends, false); + this->CreateObjectLists( + useLinkScript, false, useResponseFileForObjects, buildObjs, depends, + false, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); cmRulePlaceholderExpander::RuleVariables vars; std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index f30ec27..3f7d87d 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -322,15 +322,17 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( linkLineComputer->SetForResponse(useResponseFileForLibs); linkLineComputer->SetRelink(relink); - this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + this->CreateLinkLibs( + linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; - this->CreateObjectLists(useLinkScript, false, // useArchiveRules - useResponseFileForObjects, buildObjs, depends, - false); + this->CreateObjectLists( + useLinkScript, false, // useArchiveRules + useResponseFileForObjects, buildObjs, depends, false, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); objectDir = this->LocalGenerator->ConvertToOutputFormat( diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index aec6577..1e1df79 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -21,6 +21,7 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" +#include "cmFileSet.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -46,6 +47,7 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTarget.h" #include "cmValue.h" #include "cmake.h" @@ -107,7 +109,7 @@ std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New( return result; } -std::string cmMakefileTargetGenerator::GetConfigName() +std::string cmMakefileTargetGenerator::GetConfigName() const { auto const& configNames = this->LocalGenerator->GetConfigNames(); assert(configNames.size() == 1); @@ -190,6 +192,16 @@ void cmMakefileTargetGenerator::CreateRuleFile() void cmMakefileTargetGenerator::WriteTargetBuildRules() { + this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName()); + + if (this->GeneratorTarget->HaveCxx20ModuleSources()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GeneratorTarget->GetName(), + "\" target contains C++ module sources which are not supported " + "by the generator")); + } + // -- Write the custom commands for this target // Evaluates generator expressions and expands prop_value @@ -302,6 +314,40 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() } } + std::map<std::string, std::string> file_set_map; + + auto const* tgt = this->GeneratorTarget->Target; + for (auto const& name : tgt->GetAllFileSetNames()) { + auto const* file_set = tgt->GetFileSet(name); + if (!file_set) { + this->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + continue; + } + + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->LocalGenerator, this->GetConfigName(), + this->GeneratorTarget); + + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files, entry, + this->LocalGenerator, this->GetConfigName(), + this->GeneratorTarget); + } + + for (auto const& it : files) { + for (auto const& filename : it.second) { + file_set_map[filename] = file_set->GetType(); + } + } + } + std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, this->GetConfigName()); @@ -314,6 +360,25 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() this->WriteObjectRuleFiles(*sf); } } + + for (cmSourceFile const* sf : objectSources) { + auto const& path = sf->GetFullPath(); + auto const it = file_set_map.find(path); + if (it != file_set_map.end()) { + auto const& file_set_type = it->second; + if (file_set_type == "CXX_MODULES"_s || + file_set_type == "CXX_MODULE_HEADER_UNITS"_s) { + if (sf->GetLanguage() != "CXX"_s) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", tgt->GetName(), "\" contains the source\n ", path, + "\nin a file set of type \"", file_set_type, + R"(" but the source is not classified as a "CXX" source.)")); + } + } + } + } } void cmMakefileTargetGenerator::WriteCommonCodeRules() @@ -2080,7 +2145,7 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( } std::string cmMakefileTargetGenerator::CreateResponseFile( - const char* name, std::string const& options, + const std::string& name, std::string const& options, std::vector<std::string>& makefile_depends) { // FIXME: Find a better way to determine the response file encoding, @@ -2126,7 +2191,8 @@ cmMakefileTargetGenerator::CreateLinkLineComputer( void cmMakefileTargetGenerator::CreateLinkLibs( cmLinkLineComputer* linkLineComputer, std::string& linkLibs, - bool useResponseFile, std::vector<std::string>& makefile_depends) + bool useResponseFile, std::vector<std::string>& makefile_depends, + ResponseFlagFor responseMode) { std::string frameworkPath; std::string linkPath; @@ -2139,20 +2205,13 @@ void cmMakefileTargetGenerator::CreateLinkLibs( if (useResponseFile && linkLibs.find_first_not_of(' ') != std::string::npos) { // Lookup the response file reference flag. - std::string responseFlagVar = - cmStrCat("CMAKE_", - this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), - "_RESPONSE_FILE_LINK_FLAG"); - std::string responseFlag; - if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { - responseFlag = *p; - } else { - responseFlag = "@"; - } + std::string responseFlag = this->GetResponseFlag(responseMode); // Create this response file. + std::string responseFileName = + (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp"; std::string link_rsp = - this->CreateResponseFile("linklibs.rsp", linkLibs, makefile_depends); + this->CreateResponseFile(responseFileName, linkLibs, makefile_depends); // Reference the response file. linkLibs = cmStrCat(responseFlag, @@ -2164,7 +2223,7 @@ void cmMakefileTargetGenerator::CreateLinkLibs( void cmMakefileTargetGenerator::CreateObjectLists( bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote) + bool useWatcomQuote, ResponseFlagFor responseMode) { std::string variableName; std::string variableNameExternal; @@ -2179,27 +2238,19 @@ void cmMakefileTargetGenerator::CreateObjectLists( this->WriteObjectsStrings(object_strings, responseFileLimit); // Lookup the response file reference flag. - std::string responseFlagVar = - cmStrCat("CMAKE_", - this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), - "_RESPONSE_FILE_LINK_FLAG"); - std::string responseFlag; - if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { - responseFlag = *p; - } else { - responseFlag = "@"; - } + std::string responseFlag = this->GetResponseFlag(responseMode); // Write a response file for each string. const char* sep = ""; for (unsigned int i = 0; i < object_strings.size(); ++i) { // Number the response files. - char rsp[32]; - snprintf(rsp, sizeof(rsp), "objects%u.rsp", i + 1); + std::string responseFileName = + (responseMode == Link) ? "objects" : "deviceObjects"; + responseFileName += std::to_string(i + 1); // Create this response file. - std::string objects_rsp = - this->CreateResponseFile(rsp, object_strings[i], makefile_depends); + std::string objects_rsp = this->CreateResponseFile( + responseFileName, object_strings[i], makefile_depends); // Separate from previous response file references. buildObjs += sep; @@ -2251,7 +2302,7 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, } std::string name = cmStrCat("includes_", lang, ".rsp"); std::string arg = std::move(responseFlag) + - this->CreateResponseFile(name.c_str(), includeFlags, + this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang]); this->LocalGenerator->AppendFlags(flags, arg); } else { @@ -2304,3 +2355,22 @@ void cmMakefileTargetGenerator::GenDefFile( fout << src->GetFullPath() << "\n"; } } + +std::string cmMakefileTargetGenerator::GetResponseFlag( + ResponseFlagFor mode) const +{ + std::string responseFlag = "@"; + std::string responseFlagVar; + + auto lang = this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()); + if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) { + responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); + } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) { + responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"; + } + + if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { + responseFlag = *p; + } + return responseFlag; +} diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index cb804e0..dafa650 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -56,7 +56,7 @@ public: cmGeneratorTarget* GetGeneratorTarget() { return this->GeneratorTarget; } - std::string GetConfigName(); + std::string GetConfigName() const; protected: void GetDeviceLinkFlags(std::string& linkFlags, @@ -157,22 +157,31 @@ protected: /** Create a response file with the given set of options. Returns the relative path from the target build working directory to the response file name. */ - std::string CreateResponseFile(const char* name, std::string const& options, + std::string CreateResponseFile(const std::string& name, + std::string const& options, std::vector<std::string>& makefile_depends); bool CheckUseResponseFileForObjects(std::string const& l) const; bool CheckUseResponseFileForLibraries(std::string const& l) const; + enum ResponseFlagFor + { + Link, + DeviceLink + }; + /** Create list of flags for link libraries. */ void CreateLinkLibs(cmLinkLineComputer* linkLineComputer, std::string& linkLibs, bool useResponseFile, - std::vector<std::string>& makefile_depends); + std::vector<std::string>& makefile_depends, + ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Create lists of object files for linking and cleaning. */ void CreateObjectLists(bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote); + bool useWatcomQuote, + ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Add commands for generate def files */ void GenDefFile(std::vector<std::string>& real_link_commands); @@ -180,6 +189,9 @@ protected: void AddIncludeFlags(std::string& flags, const std::string& lang, const std::string& config) override; + /** Return the response flag for the given configuration */ + std::string GetResponseFlag(ResponseFlagFor mode) const; + virtual void CloseFileStreams(); cmLocalUnixMakefileGenerator3* LocalGenerator; cmGlobalUnixMakefileGenerator3* GlobalGenerator; diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index cd57600..fa29ec9 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -81,94 +81,83 @@ bool cmMessageCommand(std::vector<std::string> const& args, auto type = MessageType::MESSAGE; auto fatal = false; - auto level = cmake::LogLevel::LOG_UNDEFINED; + auto level = Message::LogLevel::LOG_UNDEFINED; auto checkingType = CheckingType::UNDEFINED; if (*i == "SEND_ERROR") { type = MessageType::FATAL_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; ++i; } else if (*i == "FATAL_ERROR") { fatal = true; type = MessageType::FATAL_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; ++i; } else if (*i == "WARNING") { type = MessageType::WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { fatal = true; type = MessageType::AUTHOR_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { type = MessageType::AUTHOR_WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "CHECK_START") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_START; ++i; } else if (*i == "CHECK_PASS") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_PASS; ++i; } else if (*i == "CHECK_FAIL") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_FAIL; ++i; } else if (*i == "STATUS") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; ++i; } else if (*i == "VERBOSE") { - level = cmake::LogLevel::LOG_VERBOSE; + level = Message::LogLevel::LOG_VERBOSE; ++i; } else if (*i == "DEBUG") { - level = cmake::LogLevel::LOG_DEBUG; + level = Message::LogLevel::LOG_DEBUG; ++i; } else if (*i == "TRACE") { - level = cmake::LogLevel::LOG_TRACE; + level = Message::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) { fatal = true; type = MessageType::DEPRECATION_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") || mf.IsOn("CMAKE_WARN_DEPRECATED")) { type = MessageType::DEPRECATION_WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "NOTICE") { // `NOTICE` message type is going to be output to stderr - level = cmake::LogLevel::LOG_NOTICE; + level = Message::LogLevel::LOG_NOTICE; ++i; } else { // Messages w/o any type are `NOTICE`s - level = cmake::LogLevel::LOG_NOTICE; + level = Message::LogLevel::LOG_NOTICE; } assert("Message log level expected to be set" && - level != cmake::LogLevel::LOG_UNDEFINED); - - auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel(); - assert("Expected a valid log level here" && - desiredLevel != cmake::LogLevel::LOG_UNDEFINED); - - // Command line option takes precedence over the cache variable - if (!mf.GetCMakeInstance()->WasLogLevelSetViaCLI()) { - const auto desiredLevelFromCache = - cmake::StringToLogLevel(mf.GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); - if (desiredLevelFromCache != cmake::LogLevel::LOG_UNDEFINED) { - desiredLevel = desiredLevelFromCache; - } - } + level != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel desiredLevel = mf.GetCurrentLogLevel(); if (desiredLevel < level) { // Suppress the message @@ -178,17 +167,17 @@ bool cmMessageCommand(std::vector<std::string> const& args, auto message = cmJoin(cmMakeRange(i, args.cend()), ""); switch (level) { - case cmake::LogLevel::LOG_ERROR: - case cmake::LogLevel::LOG_WARNING: + case Message::LogLevel::LOG_ERROR: + case Message::LogLevel::LOG_WARNING: // we've overridden the message type, above, so display it directly mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace()); break; - case cmake::LogLevel::LOG_NOTICE: + case Message::LogLevel::LOG_NOTICE: cmSystemTools::Message(IndentText(message, mf)); break; - case cmake::LogLevel::LOG_STATUS: + case Message::LogLevel::LOG_STATUS: switch (checkingType) { case CheckingType::CHECK_START: mf.DisplayStatus(IndentText(message, mf), -1); @@ -209,9 +198,9 @@ bool cmMessageCommand(std::vector<std::string> const& args, } break; - case cmake::LogLevel::LOG_VERBOSE: - case cmake::LogLevel::LOG_DEBUG: - case cmake::LogLevel::LOG_TRACE: + case Message::LogLevel::LOG_VERBOSE: + case Message::LogLevel::LOG_DEBUG: + case Message::LogLevel::LOG_TRACE: mf.DisplayStatus(IndentText(message, mf), -1); break; diff --git a/Source/cmMessageType.h b/Source/cmMessageType.h index 44de429..decb4b3 100644 --- a/Source/cmMessageType.h +++ b/Source/cmMessageType.h @@ -16,3 +16,19 @@ enum class MessageType DEPRECATION_ERROR, DEPRECATION_WARNING }; + +namespace Message { + +/** \brief Define log level constants. */ +enum class LogLevel +{ + LOG_UNDEFINED, + LOG_ERROR, + LOG_WARNING, + LOG_NOTICE, + LOG_STATUS, + LOG_VERBOSE, + LOG_DEBUG, + LOG_TRACE +}; +} diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 3fac7f5..a4080d8 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -21,11 +21,17 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" +#include "cmExportBuildFileGenerator.h" +#include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalNinjaGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.h" +#include "cmInstallGenerator.h" #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" @@ -36,12 +42,12 @@ #include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" -#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetExport.h" #include "cmValue.h" #include "cmake.h" @@ -153,17 +159,12 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule( bool cmNinjaTargetGenerator::NeedCxxModuleSupport( std::string const& lang, std::string const& config) const { - if (lang != "CXX") { + if (lang != "CXX"_s) { return false; } - if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { - return false; - } - cmGeneratorTarget const* tgt = this->GetGeneratorTarget(); - cmStandardLevelResolver standardResolver(this->Makefile); - bool const uses_cxx20 = - standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20"); - return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport(); + return this->GetGeneratorTarget()->HaveCxxModuleSupport(config) == + cmGeneratorTarget::Cxx20SupportLevel::Supported && + this->GetGlobalGenerator()->CheckCxxModuleSupport(); } bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang, @@ -255,51 +256,53 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); } - if (this->NeedCxxModuleSupport(language, config)) { - auto const& path = source->GetFullPath(); - auto const* tgt = this->GeneratorTarget->Target; + auto const& path = source->GetFullPath(); + auto const* tgt = this->GeneratorTarget->Target; - std::string file_set_type; + std::string file_set_type; - for (auto const& name : tgt->GetAllFileSetNames()) { - auto const* file_set = tgt->GetFileSet(name); - if (!file_set) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target `", tgt->GetName(), - "` is tracked to have file set `", name, - "`, but it was not found.")); - continue; - } + for (auto const& name : tgt->GetAllFileSetNames()) { + auto const* file_set = tgt->GetFileSet(name); + if (!file_set) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + continue; + } - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - auto directories = file_set->EvaluateDirectoryEntries( - directoryEntries, this->LocalGenerator, config, this->GeneratorTarget); + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->LocalGenerator, config, this->GeneratorTarget); - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files, entry, - this->LocalGenerator, config, - this->GeneratorTarget); - } + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files, entry, + this->LocalGenerator, config, + this->GeneratorTarget); + } - for (auto const& it : files) { - for (auto const& filename : it.second) { - if (filename == path) { - file_set_type = file_set->GetType(); - break; - } + for (auto const& it : files) { + for (auto const& filename : it.second) { + if (filename == path) { + file_set_type = file_set->GetType(); + break; } } + } - if (!file_set_type.empty()) { - std::string source_type_var = cmStrCat( - "CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_", file_set_type); - cmMakefile* mf = this->GetMakefile(); - if (cmValue source_type_flag = mf->GetDefinition(source_type_var)) { - this->LocalGenerator->AppendFlags(flags, *source_type_flag); - } + if (file_set_type == "CXX_MODULES"_s || + file_set_type == "CXX_MODULE_HEADER_UNITS"_s) { + if (source->GetLanguage() != "CXX"_s) { + this->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", tgt->GetName(), "\" contains the source\n ", path, + "\nin a file set of type \"", file_set_type, + R"(" but the source is not classified as a "CXX" source.)")); + continue; } } } @@ -1038,6 +1041,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( const std::string& config, const std::string& fileConfig, bool firstForConfig) { + this->GeneratorTarget->CheckCxxModuleStatus(config); + // Write comments. cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig)); this->GetImplFileStream(fileConfig) @@ -1338,9 +1343,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } - this->ExportObjectCompileCommand( - language, sourceFilePath, objectDir, objectFileName, objectFileDir, - vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, objectFileName, objectFileDir, + vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + } objBuild.Outputs.push_back(objectFileName); if (firstForConfig) { @@ -1616,8 +1623,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( this->Makefile->GetHomeOutputDirectory()); } else if (lang == "CXX") { - mod_dir = - cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory); + mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir( + cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory), + config); } if (mod_dir.empty()) { mod_dir = this->Makefile->GetCurrentBinaryDirectory(); @@ -1654,6 +1662,215 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, tdi_linked_target_dirs.append(l); } + cmTarget* tgt = this->GeneratorTarget->Target; + auto all_file_sets = tgt->GetAllFileSetNames(); + Json::Value& tdi_cxx_module_info = tdi["cxx-modules"] = Json::objectValue; + for (auto const& file_set_name : all_file_sets) { + auto* file_set = tgt->GetFileSet(file_set_name); + if (!file_set) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", file_set_name, + "\", but it was not found.")); + continue; + } + auto fs_type = file_set->GetType(); + // We only care about C++ module sources here. + if (fs_type != "CXX_MODULES"_s) { + continue; + } + + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->GeneratorTarget->LocalGenerator, config, + this->GeneratorTarget); + std::map<std::string, std::vector<std::string>> files_per_dirs; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files_per_dirs, entry, + this->GeneratorTarget->LocalGenerator, + config, this->GeneratorTarget); + } + + std::map<std::string, cmSourceFile const*> sf_map; + { + std::vector<cmSourceFile const*> objectSources; + this->GeneratorTarget->GetObjectSources(objectSources, config); + for (auto const* sf : objectSources) { + auto full_path = sf->GetFullPath(); + if (full_path.empty()) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" has a full path-less source file.")); + continue; + } + sf_map[full_path] = sf; + } + } + + Json::Value fs_dest = Json::nullValue; + for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) { + if (auto const* fsg = + dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) { + if (fsg->GetTarget() == this->GeneratorTarget && + fsg->GetFileSet() == file_set) { + fs_dest = fsg->GetDestination(config); + continue; + } + } + } + + for (auto const& files_per_dir : files_per_dirs) { + for (auto const& file : files_per_dir.second) { + auto lookup = sf_map.find(file); + if (lookup == sf_map.end()) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + file, + R"(" which is not in any of its "FILE_SET BASE_DIRS".)")); + continue; + } + + auto const* sf = lookup->second; + + if (!sf) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + file, "\" which has not been tracked properly.")); + continue; + } + + auto obj_path = this->GetObjectFilePath(sf, config); + Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] = + Json::objectValue; + + tdi_module_info["source"] = file; + tdi_module_info["relative-directory"] = files_per_dir.first; + tdi_module_info["name"] = file_set->GetName(); + tdi_module_info["type"] = file_set->GetType(); + tdi_module_info["visibility"] = + std::string(cmFileSetVisibilityToName(file_set->GetVisibility())); + tdi_module_info["destination"] = fs_dest; + } + } + } + + tdi["config"] = config; + + // Add information about the export sets that this target is a member of. + Json::Value& tdi_exports = tdi["exports"] = Json::arrayValue; + std::string export_name = this->GeneratorTarget->GetExportName(); + + cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr; + for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) { + if (auto const* bmig = + dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) { + if (bmig->GetTarget() == this->GeneratorTarget) { + bmi_gen = bmig; + continue; + } + } + } + if (bmi_gen) { + Json::Value tdi_bmi_info = Json::objectValue; + + tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions(); + tdi_bmi_info["destination"] = bmi_gen->GetDestination(config); + const char* msg_level = ""; + switch (bmi_gen->GetMessageLevel()) { + case cmInstallGenerator::MessageDefault: + break; + case cmInstallGenerator::MessageAlways: + msg_level = "MESSAGE_ALWAYS"; + break; + case cmInstallGenerator::MessageLazy: + msg_level = "MESSAGE_LAZY"; + break; + case cmInstallGenerator::MessageNever: + msg_level = "MESSAGE_NEVER"; + break; + } + tdi_bmi_info["message-level"] = msg_level; + tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config); + + tdi["bmi-installation"] = tdi_bmi_info; + } else { + tdi["bmi-installation"] = Json::nullValue; + } + + auto const& all_install_exports = + this->GetGlobalGenerator()->GetExportSets(); + for (auto const& exp : all_install_exports) { + // Ignore exports sets which are not for this target. + auto const& targets = exp.second.GetTargetExports(); + auto tgt_export = + std::find_if(targets.begin(), targets.end(), + [this](std::unique_ptr<cmTargetExport> const& te) { + return te->Target == this->GeneratorTarget; + }); + if (tgt_export == targets.end()) { + continue; + } + + auto const* installs = exp.second.GetInstallations(); + for (auto const* install : *installs) { + Json::Value tdi_export_info = Json::objectValue; + + auto const& ns = install->GetNamespace(); + auto const& dest = install->GetDestination(); + auto const& cxxm_dir = install->GetCxxModuleDirectory(); + auto const& export_prefix = install->GetTempDir(); + + tdi_export_info["namespace"] = ns; + tdi_export_info["export-name"] = export_name; + tdi_export_info["destination"] = dest; + tdi_export_info["cxx-module-info-dir"] = cxxm_dir; + tdi_export_info["export-prefix"] = export_prefix; + tdi_export_info["install"] = true; + + tdi_exports.append(tdi_export_info); + } + } + + auto const& all_build_exports = + this->GetMakefile()->GetExportBuildFileGenerators(); + for (auto const& exp : all_build_exports) { + std::vector<std::string> targets; + exp->GetTargets(targets); + + // Ignore exports sets which are not for this target. + auto const& name = this->GeneratorTarget->GetName(); + bool has_current_target = + std::any_of(targets.begin(), targets.end(), + [name](std::string const& tname) { return tname == name; }); + if (!has_current_target) { + continue; + } + + Json::Value tdi_export_info = Json::objectValue; + + auto const& ns = exp->GetNamespace(); + auto const& main_fn = exp->GetMainExportFileName(); + auto const& cxxm_dir = exp->GetCxxModuleDirectory(); + auto dest = cmsys::SystemTools::GetParentDirectory(main_fn); + auto const& export_prefix = + cmSystemTools::GetFilenamePath(exp->GetMainExportFileName()); + + tdi_export_info["namespace"] = ns; + tdi_export_info["export-name"] = export_name; + tdi_export_info["destination"] = dest; + tdi_export_info["cxx-module-info-dir"] = cxxm_dir; + tdi_export_info["export-prefix"] = export_prefix; + tdi_export_info["install"] = false; + + tdi_exports.append(tdi_export_info); + } + std::string const tdin = this->GetTargetDependInfoPath(lang, config); cmGeneratedFileStream tdif(tdin); tdif << tdi; diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index 95f3e7e..31538b6 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -10,6 +10,7 @@ #include <cm/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -41,8 +42,9 @@ namespace { using options_map = std::map<std::string, bool>; using single_map = std::map<std::string, std::string>; -using multi_map = std::map<std::string, std::vector<std::string>>; -using options_set = std::set<std::string>; +using multi_map = + std::map<std::string, ArgumentParser::NonEmpty<std::vector<std::string>>>; +using options_set = std::set<cm::string_view>; struct UserArgumentParser : public cmArgumentParser<void> { @@ -208,7 +210,7 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, } } - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; parser.Parse(list, &unparsed, &keywordsMissingValues); diff --git a/Source/cmScriptGenerator.cxx b/Source/cmScriptGenerator.cxx index 166ee56..32f9bec 100644 --- a/Source/cmScriptGenerator.cxx +++ b/Source/cmScriptGenerator.cxx @@ -133,7 +133,7 @@ void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os, std::string config_test = this->CreateConfigTest(this->Configurations); os << indent << "if(" << config_test << ")\n"; this->GenerateScriptActions(os, indent.Next()); - os << indent << "endif(" << config_test << ")\n"; + os << indent << "endif()\n"; } } diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index c3ee695..fe311d1 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -143,7 +143,8 @@ bool HandleHexCommand(std::vector<std::string> const& args, std::string::size_type hexIndex = 0; for (auto const& c : instr) { - sprintf(&output[hexIndex], "%.2x", static_cast<unsigned char>(c) & 0xFF); + snprintf(&output[hexIndex], 3, "%.2x", + static_cast<unsigned char>(c) & 0xFF); hexIndex += 2; } diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 351386a..3de45bc 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -1218,7 +1218,7 @@ std::string cmSystemTools::ComputeCertificateThumbprint( certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) { for (DWORD i = 0; i < hashLength; i++) { // Convert each byte to hexadecimal - sprintf(pHashPrint, "%02X", hashData[i]); + snprintf(pHashPrint, 3, "%02X", hashData[i]); pHashPrint += 2; } *pHashPrint = '\0'; @@ -1501,8 +1501,7 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top, bool cmSystemTools::UnsetEnv(const char* value) { # if !defined(HAVE_UNSETENV) - std::string var = cmStrCat(value, '='); - return cmSystemTools::PutEnv(var); + return cmSystemTools::UnPutEnv(value); # else unsetenv(value); return true; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index a8cd619..c76e2cb 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -272,6 +272,8 @@ public: cmListFileBacktrace Backtrace; FileSetType HeadersFileSets; + FileSetType CxxModulesFileSets; + FileSetType CxxModuleHeadersFileSets; cmTargetInternals(); @@ -301,6 +303,19 @@ cmTargetInternals::cmTargetInternals() "The default header set"_s, "Header set"_s, FileSetEntries("HEADER_SETS"_s), FileSetEntries("INTERFACE_HEADER_SETS"_s)) + , CxxModulesFileSets("CXX_MODULES"_s, "CXX_MODULE_DIRS"_s, + "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s, + "CXX_MODULE_SET_"_s, "C++ module"_s, + "The default C++ module set"_s, "C++ module set"_s, + FileSetEntries("CXX_MODULE_SETS"_s), + FileSetEntries("INTERFACE_CXX_MODULE_SETS"_s)) + , CxxModuleHeadersFileSets( + "CXX_MODULE_HEADER_UNITS"_s, "CXX_MODULE_HEADER_UNIT_DIRS"_s, + "CXX_MODULE_HEADER_UNIT_SET"_s, "CXX_MODULE_HEADER_UNIT_DIRS_"_s, + "CXX_MODULE_HEADER_UNIT_SET_"_s, "C++ module header"_s, + "The default C++ module header set"_s, "C++ module header set"_s, + FileSetEntries("CXX_MODULE_HEADER_UNIT_SETS"_s), + FileSetEntries("INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"_s)) { } @@ -611,6 +626,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("XCODE_SCHEME_MALLOC_SCRIBBLE"); initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES"); initProp("XCODE_SCHEME_GUARD_MALLOC"); + initProp("XCODE_SCHEME_LAUNCH_MODE"); initProp("XCODE_SCHEME_ZOMBIE_OBJECTS"); initProp("XCODE_SCHEME_MALLOC_STACK"); initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); @@ -760,6 +776,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } } + if (this->IsImported()) { + this->SetProperty("SYSTEM", "ON"); + } + for (auto const& prop : mf->GetState()->GetPropertyDefinitions().GetMap()) { if (prop.first.second == cmProperty::TARGET && !prop.second.GetInitializeFromVariable().empty()) { @@ -1367,11 +1387,32 @@ cmBTStringRange cmTarget::GetHeaderSetsEntries() const return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries); } +cmBTStringRange cmTarget::GetCxxModuleSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModulesFileSets.SelfEntries.Entries); +} + +cmBTStringRange cmTarget::GetCxxModuleHeaderSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModuleHeadersFileSets.SelfEntries.Entries); +} + cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const { return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries); } +cmBTStringRange cmTarget::GetInterfaceCxxModuleSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); +} + +cmBTStringRange cmTarget::GetInterfaceCxxModuleHeaderSetsEntries() const +{ + return cmMakeRange( + this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries); +} + namespace { #define MAKE_PROP(PROP) const std::string prop##PROP = #PROP MAKE_PROP(C_STANDARD); @@ -1631,6 +1672,12 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value) } else if (this->impl->HeadersFileSets.WriteProperties( this, this->impl.get(), prop, value, true)) { /* Handled in the `if` condition. */ + } else if (this->impl->CxxModulesFileSets.WriteProperties( + this, this->impl.get(), prop, value, true)) { + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties( + this, this->impl.get(), prop, value, true)) { + /* Handled in the `if` condition. */ } else { this->impl->Properties.SetProperty(prop, value); } @@ -1742,6 +1789,13 @@ void cmTarget::AppendProperty(const std::string& prop, this->impl->Makefile->IssueMessage( MessageType::FATAL_ERROR, prop + " property may not be appended."); } else if (this->impl->HeadersFileSets.WriteProperties( + this, this->impl.get(), prop, value, + false)) { // NOLINT(bugprone-branch-clone) + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModulesFileSets.WriteProperties( + this, this->impl.get(), prop, value, false)) { + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties( this, this->impl.get(), prop, value, false)) { /* Handled in the `if` condition. */ } else { @@ -2250,6 +2304,17 @@ cmValue cmTarget::GetProperty(const std::string& prop) const if (headers.first) { return headers.second; } + auto cxx_modules = this->impl->CxxModulesFileSets.ReadProperties( + this, this->impl.get(), prop); + if (cxx_modules.first) { + return cxx_modules.second; + } + auto cxx_module_headers = + this->impl->CxxModuleHeadersFileSets.ReadProperties( + this, this->impl.get(), prop); + if (cxx_module_headers.first) { + return cxx_module_headers.second; + } } cmValue retVal = this->impl->Properties.GetPropertyValue(prop); @@ -2527,6 +2592,11 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet( auto bt = this->impl->Makefile->GetBacktrace(); if (type == this->impl->HeadersFileSets.TypeName) { this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt)); + } else if (type == this->impl->CxxModulesFileSets.TypeName) { + this->impl->CxxModulesFileSets.AddFileSet(name, vis, std::move(bt)); + } else if (type == this->impl->CxxModuleHeadersFileSets.TypeName) { + this->impl->CxxModuleHeadersFileSets.AddFileSet(name, vis, + std::move(bt)); } } return std::make_pair(&result.first->second, result.second); @@ -2537,6 +2607,12 @@ std::string cmTarget::GetFileSetsPropertyName(const std::string& type) if (type == "HEADERS") { return "HEADER_SETS"; } + if (type == "CXX_MODULES") { + return "CXX_MODULE_SETS"; + } + if (type == "CXX_MODULE_HEADER_UNITS") { + return "CXX_MODULE_HEADER_UNIT_SETS"; + } return ""; } @@ -2545,6 +2621,12 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type) if (type == "HEADERS") { return "INTERFACE_HEADER_SETS"; } + if (type == "CXX_MODULES") { + return "INTERFACE_CXX_MODULE_SETS"; + } + if (type == "CXX_MODULE_HEADER_UNITS") { + return "INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"; + } return ""; } @@ -2572,6 +2654,8 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const }; appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries); + appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); + appendEntries(this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries); return result; } diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 5ed018e..94d6688 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -275,8 +275,12 @@ public: cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; cmBTStringRange GetHeaderSetsEntries() const; + cmBTStringRange GetCxxModuleSetsEntries() const; + cmBTStringRange GetCxxModuleHeaderSetsEntries() const; cmBTStringRange GetInterfaceHeaderSetsEntries() const; + cmBTStringRange GetInterfaceCxxModuleSetsEntries() const; + cmBTStringRange GetInterfaceCxxModuleHeaderSetsEntries() const; std::string ImportedGetFullPath(const std::string& config, cmStateEnums::ArtifactType artifact) const; diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h index 885ac74..1cef888 100644 --- a/Source/cmTargetExport.h +++ b/Source/cmTargetExport.h @@ -8,6 +8,7 @@ class cmFileSet; class cmGeneratorTarget; +class cmInstallCxxModuleBmiGenerator; class cmInstallFileSetGenerator; class cmInstallFilesGenerator; class cmInstallTargetGenerator; @@ -32,6 +33,7 @@ public: cmInstallTargetGenerator* BundleGenerator; cmInstallFilesGenerator* HeaderGenerator; std::map<cmFileSet*, cmInstallFileSetGenerator*> FileSetGenerators; + cmInstallCxxModuleBmiGenerator* CxxModuleBmiGenerator; ///@} bool NamelinkOnly = false; diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index b1367e1..e2b0213 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -9,6 +9,8 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExperimental.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmListFileCache.h" @@ -27,8 +29,8 @@ struct FileSetArgs { std::string Type; std::string FileSet; - std::vector<std::string> BaseDirs; - std::vector<std::string> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> BaseDirs; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Files; }; auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>() @@ -196,7 +198,7 @@ std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent( bool TargetSourcesImpl::HandleFileSetMode( const std::string& scope, const std::vector<std::string>& content) { - auto args = FileSetsArgsParser.Parse(content); + auto args = FileSetsArgsParser.Parse(content, /*unparsedArguments=*/nullptr); for (auto& argList : args.FileSets) { argList.emplace(argList.begin(), "FILE_SET"_s); @@ -256,9 +258,31 @@ bool TargetSourcesImpl::HandleOneFileSet( this->SetError("Must specify a TYPE when creating file set"); return false; } - if (type != "HEADERS"_s) { - this->SetError("File set TYPE may only be \"HEADERS\""); - return false; + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + + if (supportCxx20FileSetTypes) { + if (type != "HEADERS"_s && type != "CXX_MODULES"_s && + type != "CXX_MODULE_HEADER_UNITS"_s) { + this->SetError( + R"(File set TYPE may only be "HEADERS", "CXX_MODULES", or "CXX_MODULE_HEADER_UNITS")"); + return false; + } + + if (cmFileSetVisibilityIsForInterface(visibility) && + !cmFileSetVisibilityIsForSelf(visibility) && + !this->Target->IsImported()) { + if (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s) { + this->SetError( + R"(File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" may not have "INTERFACE" visibility)"); + return false; + } + } + } else { + if (type != "HEADERS"_s) { + this->SetError("File set TYPE may only be \"HEADERS\""); + return false; + } } if (args.BaseDirs.empty()) { @@ -294,7 +318,7 @@ bool TargetSourcesImpl::HandleOneFileSet( if (!baseDirectories.empty()) { fileSet.first->AddDirectoryEntry( BT<std::string>(baseDirectories, this->Makefile->GetBacktrace())); - if (type == "HEADERS"_s) { + if (type == "HEADERS"_s || type == "CXX_MODULE_HEADER_UNITS"_s) { for (auto const& dir : cmExpandedList(baseDirectories)) { auto interfaceDirectoriesGenex = cmStrCat("$<BUILD_INTERFACE:", dir, ">"); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index c79331c..2356869 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -347,6 +347,18 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString( void cmVisualStudio10TargetGenerator::Generate() { + for (std::string const& config : this->Configurations) { + this->GeneratorTarget->CheckCxxModuleStatus(config); + } + + if (this->GeneratorTarget->HaveCxx20ModuleSources()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GeneratorTarget->GetName(), + "\" target contains C++ module sources which are not supported " + "by the generator")); + } + this->ProjectType = this->ComputeProjectType(this->GeneratorTarget); this->Managed = this->ProjectType == VsProjectType::csproj; const std::string ProjectFileExtension = @@ -952,6 +964,10 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/"; ConvertToWindowsSlash(outDir); e1.Element("OutputPath", outDir); + + Options& o = *(this->ClOptions[config]); + OptionsHelper oh(o, e1); + oh.OutputFlagMap(); } this->WriteDotNetDocumentationFile(e0); @@ -2914,7 +2930,7 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( Elem& e0) { cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType(); - if (ttype > cmStateEnums::GLOBAL_TARGET) { + if (ttype > cmStateEnums::INTERFACE_LIBRARY) { return; } if (this->ProjectType == VsProjectType::csproj) { @@ -3852,21 +3868,28 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( this->GeneratorTarget->GetLinkOptions(linkOpts, configName, "CUDA"); // LINK_OPTIONS are escaped. this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts); + + cmComputeLinkInformation* pcli = + this->GeneratorTarget->GetLinkInformation(configName); + if (doDeviceLinking && pcli) { + + cmLinkLineDeviceComputer computer( + this->LocalGenerator, + this->LocalGenerator->GetStateSnapshot().GetDirectory()); + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags(computer, configName, ignored_, + linkFlags, ignored_, ignored_, + this->GeneratorTarget); + + this->LocalGenerator->AddLanguageFlagsForLinking( + linkFlags, this->GeneratorTarget, "CUDA", configName); + } cudaLinkOptions.AppendFlagString("AdditionalOptions", linkFlags); // For static libraries that have device linking enabled compute // the libraries if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY && doDeviceLinking) { - cmComputeLinkInformation* pcli = - this->GeneratorTarget->GetLinkInformation(configName); - if (!pcli) { - cmSystemTools::Error( - "CMake can not compute cmComputeLinkInformation for target: " + - this->Name); - return false; - } - cmComputeLinkInformation& cli = *pcli; cmLinkLineDeviceComputer computer( this->LocalGenerator, diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 00c65ed..16584f5 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -161,71 +161,12 @@ bool cmVisualStudioGeneratorOptions::UsingSBCS() const void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() { - // Extract temporary values stored by our flag table. - FlagValue arch = this->TakeFlag("cmake-temp-arch"); - FlagValue code = this->TakeFlag("cmake-temp-code"); - FlagValue gencode = this->TakeFlag("cmake-temp-gencode"); - - // No -code allowed without -arch. - if (arch.empty()) { - code.clear(); - } - - // Create a CodeGeneration field with [arch],[code] syntax in each entry. - // CUDA will convert it to `-gencode=arch=[arch],code="[code],[arch]"`. - FlagValue& result = this->FlagMap["CodeGeneration"]; - - // If there are no flags, leave the CodeGeneration field empty. - if (arch.empty() && gencode.empty()) { - return; - } - - // First entries for the -arch=<arch> [-code=<code>,...] pair. - if (!arch.empty()) { - std::string arch_name = arch[0]; - if (arch_name == "all" || arch_name == "all-major" || - arch_name == "native") { - AppendFlagString("AdditionalOptions", "-arch=" + arch_name); - return; - } - std::vector<std::string> codes; - if (!code.empty()) { - codes = cmTokenize(code[0], ","); - } - if (codes.empty()) { - codes.push_back(arch_name); - // nvcc -arch=<arch> has a special case that allows a real - // architecture to be specified instead of a virtual arch. - // It translates to -arch=<virtual> -code=<real>. - cmSystemTools::ReplaceString(arch_name, "sm_", "compute_"); - } - for (std::string const& c : codes) { - std::string entry = arch_name + "," + c; - result.push_back(entry); - } - } - - // Now add entries for the following signatures: - // -gencode=<arch>,<code> - // -gencode=<arch>,[<code1>,<code2>] - // -gencode=<arch>,"<code1>,<code2>" - for (std::string const& e : gencode) { - std::string entry = e; - cmSystemTools::ReplaceString(entry, "arch=", ""); - cmSystemTools::ReplaceString(entry, "code=", ""); - cmSystemTools::ReplaceString(entry, "[", ""); - cmSystemTools::ReplaceString(entry, "]", ""); - cmSystemTools::ReplaceString(entry, "\"", ""); - - std::vector<std::string> codes = cmTokenize(entry, ","); - if (codes.size() >= 2) { - auto gencode_arch = cm::cbegin(codes); - for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) { - std::string code_entry = *gencode_arch + "," + *ci; - result.push_back(code_entry); - } - } - } + // Create an empty CodeGeneration field, and pass the the actual + // compile flags via additional options so that we have consistent + // behavior and avoid issues with MSBuild extensions injecting + // virtual code when we request real only. + FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"]; + code_gen_flag = ""; } void cmVisualStudioGeneratorOptions::FixManifestUACFlags() diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx index adc500a..a62015f 100644 --- a/Source/cmXCodeScheme.cxx +++ b/Source/cmXCodeScheme.cxx @@ -147,7 +147,15 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout, "Xcode.DebuggerFoundation.Debugger.LLDB"); xout.Attribute("selectedLauncherIdentifier", "Xcode.DebuggerFoundation.Launcher.LLDB"); - xout.Attribute("launchStyle", "0"); + { + cmValue launchMode = + this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE"); + std::string value = "0"; // == 'AUTO' + if (launchMode && *launchMode == "WAIT") { + value = "1"; + } + xout.Attribute("launchStyle", value); + } WriteCustomWorkingDirectory(xout, configuration); xout.Attribute("ignoresPersistentStateOnLaunch", "NO"); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index e6fa219..efb2520 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -3,6 +3,7 @@ #include "cmake.h" #include <algorithm> +#include <array> #include <cstdio> #include <cstdlib> #include <cstring> @@ -955,7 +956,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { const auto logLevel = StringToLogLevel(value); - if (logLevel == LogLevel::LOG_UNDEFINED) { + if (logLevel == Message::LogLevel::LOG_UNDEFINED) { cmSystemTools::Error( "Invalid level specified for --log-level"); return false; @@ -971,7 +972,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { const auto logLevel = StringToLogLevel(value); - if (logLevel == LogLevel::LOG_UNDEFINED) { + if (logLevel == Message::LogLevel::LOG_UNDEFINED) { cmSystemTools::Error( "Invalid level specified for --loglevel"); return false; @@ -1402,23 +1403,52 @@ void cmake::SetArgs(const std::vector<std::string>& args) #endif } -cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) -{ - using LevelsPair = std::pair<std::string, LogLevel>; - static const std::vector<LevelsPair> levels = { - { "error", LogLevel::LOG_ERROR }, { "warning", LogLevel::LOG_WARNING }, - { "notice", LogLevel::LOG_NOTICE }, { "status", LogLevel::LOG_STATUS }, - { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG }, - { "trace", LogLevel::LOG_TRACE } +namespace { +using LevelsPair = std::pair<cm::string_view, Message::LogLevel>; +using LevelsPairArray = std::array<LevelsPair, 7>; +const LevelsPairArray& getStringToLogLevelPairs() +{ + static const LevelsPairArray levels = { + { { "error", Message::LogLevel::LOG_ERROR }, + { "warning", Message::LogLevel::LOG_WARNING }, + { "notice", Message::LogLevel::LOG_NOTICE }, + { "status", Message::LogLevel::LOG_STATUS }, + { "verbose", Message::LogLevel::LOG_VERBOSE }, + { "debug", Message::LogLevel::LOG_DEBUG }, + { "trace", Message::LogLevel::LOG_TRACE } } }; + return levels; +} +} // namespace + +Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr) +{ + const LevelsPairArray& levels = getStringToLogLevelPairs(); - const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr); + const auto levelStrLowCase = + cmSystemTools::LowerCase(std::string{ levelStr }); + // NOLINTNEXTLINE(readability-qualified-auto) const auto it = std::find_if(levels.cbegin(), levels.cend(), [&levelStrLowCase](const LevelsPair& p) { return p.first == levelStrLowCase; }); - return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED; + return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED; +} + +std::string cmake::LogLevelToString(Message::LogLevel level) +{ + const LevelsPairArray& levels = getStringToLogLevelPairs(); + + // NOLINTNEXTLINE(readability-qualified-auto) + const auto it = + std::find_if(levels.cbegin(), levels.cend(), + [&level](const LevelsPair& p) { return p.second == level; }); + const cm::string_view levelStrLowerCase = + (it != levels.cend()) ? it->first : "undefined"; + std::string levelStrUpperCase = + cmSystemTools::UpperCase(std::string{ levelStrLowerCase }); + return levelStrUpperCase; } cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr) diff --git a/Source/cmake.h b/Source/cmake.h index 3c6af17..a631647 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -119,19 +119,6 @@ public: FIND_PACKAGE_MODE }; - /** \brief Define log level constants. */ - enum LogLevel - { - LOG_UNDEFINED, - LOG_ERROR, - LOG_WARNING, - LOG_NOTICE, - LOG_STATUS, - LOG_VERBOSE, - LOG_DEBUG, - LOG_TRACE - }; - /** \brief Define supported trace formats **/ enum TraceFormat { @@ -469,9 +456,10 @@ public: bool WasLogLevelSetViaCLI() const { return this->LogLevelWasSetViaCLI; } //! Get the selected log level for `message()` commands during the cmake run. - LogLevel GetLogLevel() const { return this->MessageLogLevel; } - void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; } - static LogLevel StringToLogLevel(const std::string& levelStr); + Message::LogLevel GetLogLevel() const { return this->MessageLogLevel; } + void SetLogLevel(Message::LogLevel level) { this->MessageLogLevel = level; } + static Message::LogLevel StringToLogLevel(cm::string_view levelStr); + static std::string LogLevelToString(Message::LogLevel level); static TraceFormat StringToTraceFormat(const std::string& levelStr); bool HasCheckInProgress() const @@ -732,7 +720,7 @@ private: std::set<std::string> DebugFindPkgs; std::set<std::string> DebugFindVars; - LogLevel MessageLogLevel = LogLevel::LOG_STATUS; + Message::LogLevel MessageLogLevel = Message::LogLevel::LOG_STATUS; bool LogLevelWasSetViaCLI = false; bool LogContext = false; diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 5889a4b..89a3cc7 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -3067,17 +3067,14 @@ std::string SystemTools::GetRealPath(const std::string& path, return ret; } -bool SystemTools::FileIsDirectory(const std::string& inName) +// Remove any trailing slash from the name except in a root component. +static const char* RemoveTrailingSlashes( + const std::string& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH], + std::string& string_buffer) { - if (inName.empty()) { - return false; - } size_t length = inName.size(); const char* name = inName.c_str(); - // Remove any trailing slash from the name except in a root component. - char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; - std::string string_buffer; size_t last = length - 1; if (last > 0 && (name[last] == '/' || name[last] == '\\') && strcmp(name, "/") != 0 && name[last - 1] != ':') { @@ -3091,6 +3088,19 @@ bool SystemTools::FileIsDirectory(const std::string& inName) } } + return name; +} + +bool SystemTools::FileIsDirectory(const std::string& inName) +{ + if (inName.empty()) { + return false; + } + + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer); + // Now check the file node type. #if defined(_WIN32) DWORD attr = @@ -3107,9 +3117,21 @@ bool SystemTools::FileIsDirectory(const std::string& inName) } } -bool SystemTools::FileIsExecutable(const std::string& name) +bool SystemTools::FileIsExecutable(const std::string& inName) { - return !FileIsDirectory(name) && TestFileAccess(name, TEST_FILE_EXECUTE); +#ifdef _WIN32 + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer); + const auto attr = + GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str()); + + // On Windows any file that exists and is not a directory is considered + // readable and therefore also executable: + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +#else + return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE); +#endif } #if defined(_WIN32) diff --git a/Templates/MSBuild/FlagTables/v10_CSharp.json b/Templates/MSBuild/FlagTables/v10_CSharp.json index 9f21d9a..5341841 100644 --- a/Templates/MSBuild/FlagTables/v10_CSharp.json +++ b/Templates/MSBuild/FlagTables/v10_CSharp.json @@ -201,6 +201,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v10_Cuda.json b/Templates/MSBuild/FlagTables/v10_Cuda.json index b3230ac..b0765f5 100644 --- a/Templates/MSBuild/FlagTables/v10_Cuda.json +++ b/Templates/MSBuild/FlagTables/v10_Cuda.json @@ -70,118 +70,6 @@ ] }, { - "name": "cmake-temp-gencode", - "switch": "gencode=", - "comment": "", - "value": "", - "flags": [ - "UserValue", - "SemicolonAppendable" - ] - }, - { - "name": "cmake-temp-gencode", - "switch": "gencode", - "comment": "", - "value": "", - "flags": [ - "UserFollowing", - "SemicolonAppendable" - ] - }, - { - "name": "cmake-temp-gencode", - "switch": "-generate-code=", - "comment": "", - "value": "", - "flags": [ - "UserValue", - "SemicolonAppendable" - ] - }, - { - "name": "cmake-temp-gencode", - "switch": "-generate-code", - "comment": "", - "value": "", - "flags": [ - "UserFollowing", - "SemicolonAppendable" - ] - }, - { - "name": "cmake-temp-code", - "switch": "code=", - "comment": "", - "value": "", - "flags": [ - "UserValue" - ] - }, - { - "name": "cmake-temp-code", - "switch": "code", - "comment": "", - "value": "", - "flags": [ - "UserFollowing" - ] - }, - { - "name": "cmake-temp-code", - "switch": "-gpu-code=", - "comment": "", - "value": "", - "flags": [ - "UserValue" - ] - }, - { - "name": "cmake-temp-code", - "switch": "-gpu-code", - "comment": "", - "value": "", - "flags": [ - "UserFollowing" - ] - }, - { - "name": "cmake-temp-arch", - "switch": "arch=", - "comment": "", - "value": "", - "flags": [ - "UserValue" - ] - }, - { - "name": "cmake-temp-arch", - "switch": "arch", - "comment": "", - "value": "", - "flags": [ - "UserFollowing" - ] - }, - { - "name": "cmake-temp-arch", - "switch": "-gpu-architecture=", - "comment": "", - "value": "", - "flags": [ - "UserValue" - ] - }, - { - "name": "cmake-temp-arch", - "switch": "-gpu-architecture", - "comment": "", - "value": "", - "flags": [ - "UserFollowing" - ] - }, - { "name": "FastMath", "switch": "use_fast_math", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v11_CSharp.json b/Templates/MSBuild/FlagTables/v11_CSharp.json index 9f21d9a..5341841 100644 --- a/Templates/MSBuild/FlagTables/v11_CSharp.json +++ b/Templates/MSBuild/FlagTables/v11_CSharp.json @@ -201,6 +201,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v12_CSharp.json b/Templates/MSBuild/FlagTables/v12_CSharp.json index 9f21d9a..5341841 100644 --- a/Templates/MSBuild/FlagTables/v12_CSharp.json +++ b/Templates/MSBuild/FlagTables/v12_CSharp.json @@ -201,6 +201,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v140_CSharp.json b/Templates/MSBuild/FlagTables/v140_CSharp.json index 9f21d9a..5341841 100644 --- a/Templates/MSBuild/FlagTables/v140_CSharp.json +++ b/Templates/MSBuild/FlagTables/v140_CSharp.json @@ -201,6 +201,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v141_CSharp.json b/Templates/MSBuild/FlagTables/v141_CSharp.json index 9f21d9a..5341841 100644 --- a/Templates/MSBuild/FlagTables/v141_CSharp.json +++ b/Templates/MSBuild/FlagTables/v141_CSharp.json @@ -201,6 +201,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v142_CSharp.json b/Templates/MSBuild/FlagTables/v142_CSharp.json index ae1bd47..9ea8f4b 100644 --- a/Templates/MSBuild/FlagTables/v142_CSharp.json +++ b/Templates/MSBuild/FlagTables/v142_CSharp.json @@ -208,6 +208,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Templates/MSBuild/FlagTables/v143_CSharp.json b/Templates/MSBuild/FlagTables/v143_CSharp.json index ae1bd47..9ea8f4b 100644 --- a/Templates/MSBuild/FlagTables/v143_CSharp.json +++ b/Templates/MSBuild/FlagTables/v143_CSharp.json @@ -208,6 +208,13 @@ "flags": [] }, { + "name": "DebugType", + "switch": "debug:embedded", + "comment": "", + "value": "embedded", + "flags": [] + }, + { "name": "Optimize", "switch": "optimize", "comment": "", diff --git a/Tests/Assembler/CMakeLists.txt b/Tests/Assembler/CMakeLists.txt index 1b7e57d..8f6b355 100644 --- a/Tests/Assembler/CMakeLists.txt +++ b/Tests/Assembler/CMakeLists.txt @@ -24,6 +24,12 @@ if("${CMAKE_GENERATOR}" MATCHES "Makefile|Xcode|Ninja" AND elseif("${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64") list(APPEND C_FLAGS -arch arm64) endif() + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + # Just in case the user is passing -flto, we need to pass -fno-lto to + # clang when generating the assembly file, or else clang will generate + # LLVM IR instead of assembly. + list(APPEND C_FLAGS -fno-lto) + endif() # Clang on OS X, and perhaps other compilers, do not support -g # for both generating and assembling, so drop it from generating. list(REMOVE_ITEM C_FLAGS -g) diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx index 965690c..ecfc5fc 100644 --- a/Tests/CMakeLib/testArgumentParser.cxx +++ b/Tests/CMakeLib/testArgumentParser.cxx @@ -6,10 +6,12 @@ #include <string> #include <vector> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" namespace { @@ -19,39 +21,54 @@ struct Result bool Option2 = false; std::string String1; - std::string String2; + cm::optional<std::string> String2; + cm::optional<std::string> String3; + ArgumentParser::Maybe<std::string> String4; - std::vector<std::string> List1; - std::vector<std::string> List2; - std::vector<std::string> List3; + ArgumentParser::NonEmpty<std::vector<std::string>> List1; + ArgumentParser::NonEmpty<std::vector<std::string>> List2; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List3; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List4; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List5; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> List6; std::vector<std::vector<std::string>> Multi1; std::vector<std::vector<std::string>> Multi2; - std::vector<std::vector<std::string>> Multi3; + cm::optional<std::vector<std::vector<std::string>>> Multi3; + cm::optional<std::vector<std::vector<std::string>>> Multi4; }; std::initializer_list<cm::string_view> const args = { /* clang-format off */ "OPTION_1", // option + // "OPTION_2", // option that is not present "STRING_1", // string arg missing value - "STRING_2", "foo", "bar", // string arg + unparsed value + "STRING_2", "foo", "bar", // string arg + unparsed value, presence captured + // "STRING_3", // string arg that is not present + "STRING_4", // string arg allowed to be missing value "LIST_1", // list arg missing values "LIST_2", "foo", "bar", // list arg with 2 elems "LIST_3", "bar", // list arg ... "LIST_3", "foo", // ... with continuation + "LIST_4", // list arg missing values, presence captured + // "LIST_5", // list arg that is not present + "LIST_6", // list arg allowed to be empty "MULTI_2", // multi list with 0 lists "MULTI_3", "foo", "bar", // multi list with first list with two elems "MULTI_3", "bar", "foo", // multi list with second list with two elems + // "MULTI_4", // multi list arg that is not present /* clang-format on */ }; bool verifyResult(Result const& result, std::vector<std::string> const& unparsedArguments, - std::vector<std::string> const& keywordsMissingValue) + std::vector<cm::string_view> const& keywordsMissingValue) { static std::vector<std::string> const foobar = { "foo", "bar" }; static std::vector<std::string> const barfoo = { "bar", "foo" }; - static std::vector<std::string> const missing = { "STRING_1", "LIST_1" }; + static std::vector<cm::string_view> const missing = { "STRING_1"_s, + "LIST_1"_s, + "LIST_4"_s }; #define ASSERT_TRUE(x) \ do { \ @@ -65,18 +82,29 @@ bool verifyResult(Result const& result, ASSERT_TRUE(!result.Option2); ASSERT_TRUE(result.String1.empty()); - ASSERT_TRUE(result.String2 == "foo"); + ASSERT_TRUE(result.String2); + ASSERT_TRUE(*result.String2 == "foo"); + ASSERT_TRUE(!result.String3); + ASSERT_TRUE(result.String4.empty()); ASSERT_TRUE(result.List1.empty()); ASSERT_TRUE(result.List2 == foobar); - ASSERT_TRUE(result.List3 == barfoo); + ASSERT_TRUE(result.List3); + ASSERT_TRUE(*result.List3 == barfoo); + ASSERT_TRUE(result.List4); + ASSERT_TRUE(result.List4->empty()); + ASSERT_TRUE(!result.List5); + ASSERT_TRUE(result.List6); + ASSERT_TRUE(result.List6->empty()); ASSERT_TRUE(result.Multi1.empty()); ASSERT_TRUE(result.Multi2.size() == 1); ASSERT_TRUE(result.Multi2[0].empty()); - ASSERT_TRUE(result.Multi3.size() == 2); - ASSERT_TRUE(result.Multi3[0] == foobar); - ASSERT_TRUE(result.Multi3[1] == barfoo); + ASSERT_TRUE(result.Multi3); + ASSERT_TRUE((*result.Multi3).size() == 2); + ASSERT_TRUE((*result.Multi3)[0] == foobar); + ASSERT_TRUE((*result.Multi3)[1] == barfoo); + ASSERT_TRUE(!result.Multi4); ASSERT_TRUE(unparsedArguments.size() == 1); ASSERT_TRUE(unparsedArguments[0] == "bar"); @@ -89,19 +117,25 @@ bool testArgumentParserDynamic() { Result result; std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; + std::vector<cm::string_view> keywordsMissingValue; cmArgumentParser<void>{} .Bind("OPTION_1"_s, result.Option1) .Bind("OPTION_2"_s, result.Option2) .Bind("STRING_1"_s, result.String1) .Bind("STRING_2"_s, result.String2) + .Bind("STRING_3"_s, result.String3) + .Bind("STRING_4"_s, result.String4) .Bind("LIST_1"_s, result.List1) .Bind("LIST_2"_s, result.List2) .Bind("LIST_3"_s, result.List3) + .Bind("LIST_4"_s, result.List4) + .Bind("LIST_5"_s, result.List5) + .Bind("LIST_6"_s, result.List6) .Bind("MULTI_1"_s, result.Multi1) .Bind("MULTI_2"_s, result.Multi2) .Bind("MULTI_3"_s, result.Multi3) + .Bind("MULTI_4"_s, result.Multi4) .Parse(args, &unparsedArguments, &keywordsMissingValue); return verifyResult(result, unparsedArguments, keywordsMissingValue); @@ -115,15 +149,21 @@ bool testArgumentParserStatic() .Bind("OPTION_2"_s, &Result::Option2) .Bind("STRING_1"_s, &Result::String1) .Bind("STRING_2"_s, &Result::String2) + .Bind("STRING_3"_s, &Result::String3) + .Bind("STRING_4"_s, &Result::String4) .Bind("LIST_1"_s, &Result::List1) .Bind("LIST_2"_s, &Result::List2) .Bind("LIST_3"_s, &Result::List3) + .Bind("LIST_4"_s, &Result::List4) + .Bind("LIST_5"_s, &Result::List5) + .Bind("LIST_6"_s, &Result::List6) .Bind("MULTI_1"_s, &Result::Multi1) .Bind("MULTI_2"_s, &Result::Multi2) - .Bind("MULTI_3"_s, &Result::Multi3); + .Bind("MULTI_3"_s, &Result::Multi3) + .Bind("MULTI_4"_s, &Result::Multi4); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; + std::vector<cm::string_view> keywordsMissingValue; Result const result = parser.Parse(args, &unparsedArguments, &keywordsMissingValue); diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 24e98f4..d2ded37 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -431,7 +431,7 @@ if(BUILD_TESTING) if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])") ADD_TEST_MACRO(CSharpOnly CSharpOnly) - if(NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") ADD_TEST_MACRO(CSharpLinkToCxx CSharpLinkToCxx) ADD_TEST_MACRO(CSharpLinkFromCxx CSharpLinkFromCxx) endif() @@ -1056,8 +1056,11 @@ if(BUILD_TESTING) endif() endif() if(NSIS_MAKENSIS_EXECUTABLE) - set(CPackComponents_BUILD_OPTIONS ${CPackComponents_BUILD_OPTIONS} - -DCPACK_BINARY_NSIS:BOOL=ON) + execute_process(COMMAND ${NSIS_MAKENSIS_EXECUTABLE} "-VERSION" ERROR_QUIET OUTPUT_QUIET RESULT_VARIABLE NSIS_OK) + if("${NSIS_OK}" STREQUAL "0") + set(CPackComponents_BUILD_OPTIONS ${CPackComponents_BUILD_OPTIONS} + -DCPACK_BINARY_NSIS:BOOL=ON) + endif() endif() add_test(CPackComponents ${CMAKE_CTEST_COMMAND} @@ -1153,7 +1156,6 @@ if(BUILD_TESTING) "components-source" "components-shlibdeps1" "components-depend1" - "components-depend2" "compression") # Run additional tests if dpkg-shlibdeps is available (and is new enough version) find_program(SHLIBDEPS_EXECUTABLE NAMES dpkg-shlibdeps) @@ -1173,6 +1175,11 @@ if(BUILD_TESTING) list(APPEND DEB_CONFIGURATIONS_TO_TEST "shlibdeps-with-private-lib-failure" "shlibdeps-with-private-lib-success") endif() + # Check if distro has symbols or shlibs data + file(GLOB SHLIBS_FILES_EXIST "/var/lib/dpkg/info/*.shlibs" "/var/lib/dpkg/info/*.symbols") + if(SHLIBS_FILES_EXIST) + list(APPEND DEB_CONFIGURATIONS_TO_TEST "components-depend2") + endif() endif() set(CPackGen "DEB") @@ -1460,6 +1467,7 @@ if(BUILD_TESTING) LTTngUST ODBC OpenACC + OpenAL OpenCL OpenGL OpenMP @@ -2115,7 +2123,7 @@ if(BUILD_TESTING) if(MSVC AND NOT MSVC_VERSION LESS 1310 AND (NOT CMAKE_GENERATOR MATCHES "Visual Studio 9 " OR CMAKE_SIZEOF_VOID_P EQUAL 4) - AND (NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + AND (NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") ) ADD_TEST_MACRO(VSMASM VSMASM) endif() @@ -2127,7 +2135,7 @@ if(BUILD_TESTING) if(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio 9 " AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90" - AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") ADD_TEST_MACRO(VSWindowsFormsResx VSWindowsFormsResx) ADD_TEST_MACRO(VSManagedCustomCommand) endif() @@ -2665,7 +2673,10 @@ if(BUILD_TESTING) endif() if(NOT DEFINED CMake_TEST_CTestUpdate_HG AND HG_EXECUTABLE AND (UNIX OR NOT "${HG_EXECUTABLE}" MATCHES "cygwin")) - set(CMake_TEST_CTestUpdate_HG 1) + execute_process(COMMAND "${HG_EXECUTABLE}" --version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE HG_RV) + if(HG_RV EQUAL 0) + set(CMake_TEST_CTestUpdate_HG 1) + endif() endif() if(CMake_TEST_CTestUpdate_HG) if(NOT HG_EXECUTABLE) @@ -3367,56 +3378,70 @@ if(BUILD_TESTING) set(JavaExportImport_BUILD_OPTIONS -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM}) ADD_TEST_MACRO(JavaExportImport JavaExportImport) - get_filename_component(JNIPATH ${Java_JAVAC_EXECUTABLE} PATH) + get_filename_component(JAVACPATH ${Java_JAVAC_EXECUTABLE} REALPATH) + get_filename_component(JNIPATH ${JAVACPATH} PATH) find_file(JNI_H jni.h "${JNIPATH}/../include" "${JNIPATH}/../java/include") if(JNI_H AND EXISTS "${JNI_H}") # in case jni.h is a broken symlink file(READ "${JNI_H}" JNI_FILE) if("${JNI_FILE}" MATCHES "JDK1_2") - add_test(NAME Java.Jar COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/Java" - "${CMake_BINARY_DIR}/Tests/JavaJar" - ${build_generator_args} - --build-project hello - --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJar/" - --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJar") - # For next tests, java tool must have same architecture as toolchain - math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8") execute_process( COMMAND "${Java_JAVA_EXECUTABLE}" -version OUTPUT_VARIABLE _version ERROR_VARIABLE _version RESULT_VARIABLE _result ) - if(_result EQUAL 0 AND _version MATCHES "${_object_mode}-Bit") - ## next test is valid only if Java version is less than 1.10 - if ("${Java_VERSION}" VERSION_LESS 1.10) - add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/JavaJavah" - "${CMake_BINARY_DIR}/Tests/JavaJavah" - ${build_generator_args} - --build-project helloJavah - --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/" - --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah") + + # E2K has broken Java RVM before 3.5.2 + if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "e2k" AND _result EQUAL 0) + string(REGEX MATCH "RVM ([0-9.]+)" RVMVER "${_version}") + # Consider empty match a broken version too + if("${CMAKE_MATCH_1}" VERSION_LESS "3.5.2") + set(BROKEN_RVM TRUE) endif() - ## next test is valid only if Java is, at least, version 1.8 - if (NOT "${Java_VERSION}" VERSION_LESS 1.8) - add_test(NAME Java.NativeHeaders COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/JavaNativeHeaders" - "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders" - ${build_generator_args} - --build-project helloJavaNativeHeaders - --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/" - --build-target install - --build-options - "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/Install" - --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders") + endif() + + if(NOT BROKEN_RVM) + add_test(NAME Java.Jar COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Java" + "${CMake_BINARY_DIR}/Tests/JavaJar" + ${build_generator_args} + --build-project hello + --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJar/" + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJar") + + # For next tests, java tool must have same architecture as toolchain + math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8") + if(_result EQUAL 0 AND _version MATCHES "${_object_mode}-Bit") + ## next test is valid only if Java version is less than 1.10 + if ("${Java_VERSION}" VERSION_LESS 1.10) + add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/JavaJavah" + "${CMake_BINARY_DIR}/Tests/JavaJavah" + ${build_generator_args} + --build-project helloJavah + --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/" + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah") + endif() + ## next test is valid only if Java is, at least, version 1.8 + if (NOT "${Java_VERSION}" VERSION_LESS 1.8) + add_test(NAME Java.NativeHeaders COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/JavaNativeHeaders" + "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders" + ${build_generator_args} + --build-project helloJavaNativeHeaders + --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/" + --build-target install + --build-options + "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/Install" + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders") + endif() endif() endif() endif() diff --git a/Tests/CMakeOnly/AllFindModules/CMakeLists.txt b/Tests/CMakeOnly/AllFindModules/CMakeLists.txt index 49a4041..0907d03 100644 --- a/Tests/CMakeOnly/AllFindModules/CMakeLists.txt +++ b/Tests/CMakeOnly/AllFindModules/CMakeLists.txt @@ -56,6 +56,15 @@ macro(check_version_string MODULE_NAME VERSION_VAR) if (NOT _exclude_pos EQUAL -1) message(STATUS "excluding check of ${VERSION_VAR}='${${VERSION_VAR}}' due to local configuration") elseif (${MODULE_NAME}_FOUND) + + unset(SKIP_CHECK) + if(${MODULE_NAME} STREQUAL "HG") + execute_process(COMMAND "${HG_EXECUTABLE}" --version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE HG_RV) + if(NOT HG_RV EQUAL 0) + message(WARNING "Broken HG executable detected, skipping") + set(SKIP_CHECK TRUE) + endif() + endif() if (DEFINED ${VERSION_VAR}) message(STATUS "${VERSION_VAR}='${${VERSION_VAR}}'") if (NOT ${VERSION_VAR} MATCHES "^[0-9]") @@ -71,7 +80,9 @@ macro(check_version_string MODULE_NAME VERSION_VAR) message(SEND_ERROR "unexpected: ${VERSION_VAR} is NOT VERSION_GREATER 0") endif() else() - message(SEND_ERROR "${MODULE_NAME}_FOUND is set but version number variable ${VERSION_VAR} is NOT DEFINED") + if(NOT SKIP_CHECK) + message(SEND_ERROR "${MODULE_NAME}_FOUND is set but version number variable ${VERSION_VAR} is NOT DEFINED") + endif() endif() endif () endmacro() diff --git a/Tests/CTestTestFdSetSize/sleep.c b/Tests/CTestTestFdSetSize/sleep.c index 2fb6490..d55cec6 100644 --- a/Tests/CTestTestFdSetSize/sleep.c +++ b/Tests/CTestTestFdSetSize/sleep.c @@ -1,7 +1,11 @@ #if defined(_WIN32) # include <windows.h> -#else +#elif _XOPEN_SOURCE >= 500 || defined(_ALL_SOURCE) # include <unistd.h> +#else +# include <time.h> + +# include <sys/select.h> #endif /* sleeps for 0.1 second */ @@ -9,8 +13,14 @@ int main(int argc, char** argv) { #if defined(_WIN32) Sleep(100); -#else +#elif _XOPEN_SOURCE >= 500 || defined(_ALL_SOURCE) usleep(100 * 1000); +#else + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + + select(0, NULL, NULL, NULL, &tv); #endif return 0; } diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index c9e41f5..6f19c13 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -155,6 +155,15 @@ target_include_directories(testInterfaceIncludeUser "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/testInterfaceIncludeUser>" ) set_property(TARGET testInterfaceIncludeUser PROPERTY IMPORTED_NO_SYSTEM 1) + +add_library(testInterfaceIncludeUser2 INTERFACE) +target_include_directories(testInterfaceIncludeUser2 + INTERFACE + "$<INSTALL_INTERFACE:include/testInterfaceIncludeUser>" + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/testInterfaceIncludeUser>" +) +set_property(TARGET testInterfaceIncludeUser2 PROPERTY EXPORT_NO_SYSTEM 1) + install( FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/testInterfaceIncludeUser/testInterfaceInclude.h" @@ -562,6 +571,7 @@ install( TopDirLib SubDirLinkA systemlib testInterfaceIncludeUser + testInterfaceIncludeUser2 EXPORT exp RUNTIME DESTINATION $<1:bin>$<0:/wrong> LIBRARY DESTINATION $<1:lib>$<0:/wrong> NAMELINK_SKIP @@ -622,6 +632,7 @@ export(TARGETS testExe1 testLib1 testLib2 testLib3 TopDirLib SubDirLinkA systemlib testInterfaceIncludeUser + testInterfaceIncludeUser2 NAMESPACE bld_ FILE ExportBuildTree.cmake ) diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 272c7a9..5bafdf8 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -75,6 +75,10 @@ add_library(imp_testInterfaceInclude1 STATIC imp_testInterfaceInclude1.c) target_include_directories(imp_testInterfaceInclude1 SYSTEM PRIVATE testInterfaceIncludeSystem) target_link_libraries(imp_testInterfaceInclude1 PRIVATE exp_testInterfaceIncludeUser) +add_library(imp_testInterfaceInclude1b STATIC imp_testInterfaceInclude1.c) +target_include_directories(imp_testInterfaceInclude1b SYSTEM PRIVATE testInterfaceIncludeSystem) +target_link_libraries(imp_testInterfaceInclude1b PRIVATE exp_testInterfaceIncludeUser2) + add_executable(imp_UseSharedLibWithHelper1 ../../../InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c) target_link_libraries(imp_UseSharedLibWithHelper1 PRIVATE exp_testSharedLibWithHelper testSharedLibHelperExclude) @@ -122,9 +126,13 @@ target_link_libraries(imp_testExe1b bld_testStaticLibWithPlugin ) -add_library(imp_testInterfaceInclude1b STATIC imp_testInterfaceInclude1.c) -target_include_directories(imp_testInterfaceInclude1b SYSTEM PRIVATE testInterfaceIncludeSystem) -target_link_libraries(imp_testInterfaceInclude1b PRIVATE bld_testInterfaceIncludeUser) +add_library(imp_testInterfaceInclude1c STATIC imp_testInterfaceInclude1.c) +target_include_directories(imp_testInterfaceInclude1c SYSTEM PRIVATE testInterfaceIncludeSystem) +target_link_libraries(imp_testInterfaceInclude1c PRIVATE bld_testInterfaceIncludeUser) + +add_library(imp_testInterfaceInclude1d STATIC imp_testInterfaceInclude1.c) +target_include_directories(imp_testInterfaceInclude1d SYSTEM PRIVATE testInterfaceIncludeSystem) +target_link_libraries(imp_testInterfaceInclude1d PRIVATE bld_testInterfaceIncludeUser2) add_custom_target(check_testLib1_genex ALL COMMAND ${CMAKE_COMMAND} -DtestLib1=$<TARGET_FILE:exp_testLib1> diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt index e4c6c66..81d31e7 100644 --- a/Tests/ExternalProject/CMakeLists.txt +++ b/Tests/ExternalProject/CMakeLists.txt @@ -45,7 +45,11 @@ if(NOT DEFINED EP_TEST_HG OR EP_TEST_HG) find_package(Hg) endif() if(NOT DEFINED EP_TEST_HG AND Hg_FOUND) - set(EP_TEST_HG 1) + # Check if hg executable is working + execute_process(COMMAND "${HG_EXECUTABLE}" --version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE HG_RV) + if(HG_RV EQUAL 0) + set(EP_TEST_HG 1) + endif() endif() message(STATUS "EP_TEST_CVS='${EP_TEST_CVS}' CVS_EXECUTABLE='${CVS_EXECUTABLE}'") diff --git a/Tests/FindOpenAL/CMakeLists.txt b/Tests/FindOpenAL/CMakeLists.txt new file mode 100644 index 0000000..fc4803e --- /dev/null +++ b/Tests/FindOpenAL/CMakeLists.txt @@ -0,0 +1,10 @@ +add_test(NAME FindOpenAL.Test COMMAND + ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindOpenAL/Test" + "${CMake_BINARY_DIR}/Tests/FindOpenAL/Test" + ${build_generator_args} + --build-project TestFindOpenAL + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> + ) diff --git a/Tests/FindOpenAL/Test/CMakeLists.txt b/Tests/FindOpenAL/Test/CMakeLists.txt new file mode 100644 index 0000000..fa3e263 --- /dev/null +++ b/Tests/FindOpenAL/Test/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.21) +project(TestFindOpenAL CXX) +include(CTest) + +find_package(OpenAL REQUIRED) + +add_executable(test_tgt main.cxx) +target_link_libraries(test_tgt OpenAL::OpenAL) +add_test(NAME test_tgt COMMAND test_tgt) + +add_executable(test_var main.cxx) +target_include_directories(test_var PRIVATE ${OPENAL_INCLUDE_DIR}) +target_link_libraries(test_var PRIVATE ${OPENAL_LIBRARY}) +add_test(NAME test_var COMMAND test_var) diff --git a/Tests/FindOpenAL/Test/main.cxx b/Tests/FindOpenAL/Test/main.cxx new file mode 100644 index 0000000..bb45faf --- /dev/null +++ b/Tests/FindOpenAL/Test/main.cxx @@ -0,0 +1,13 @@ +#include <AL/al.h> +#include <AL/alc.h> +#include <stdio.h> + +int main() +{ + /* Reference an AL symbol without requiring a context at runtime. */ + printf("&alGetString = %p\n", &alGetString); + + /* Reference an ALC symbol without requiring a context at runtime. */ + printf("&alcGetString = %p\n", &alcGetString); + return 0; +} diff --git a/Tests/FindVulkan/Test/CMakeLists.txt b/Tests/FindVulkan/Test/CMakeLists.txt index 42543ac..727e1ce 100644 --- a/Tests/FindVulkan/Test/CMakeLists.txt +++ b/Tests/FindVulkan/Test/CMakeLists.txt @@ -7,6 +7,7 @@ set(components glslang shaderc_combined SPIRV-Tools + volk ) if(APPLE) list(APPEND components MoltenVK) @@ -75,6 +76,10 @@ if(APPLE) add_test(NAME test_tgt_MoltenVK COMMAND test_tgt_MoltenVK) endif() +add_executable(test_tgt_volk main-volk.cxx) +target_link_libraries(test_tgt_volk Vulkan::volk) +add_test(NAME test_tgt_volk COMMAND test_tgt_volk) + if(Vulkan_GLSLC_EXECUTABLE) add_test(NAME test_glslc COMMAND ${CMAKE_COMMAND} diff --git a/Tests/FindVulkan/Test/main-volk.cxx b/Tests/FindVulkan/Test/main-volk.cxx new file mode 100644 index 0000000..2ec9fb4 --- /dev/null +++ b/Tests/FindVulkan/Test/main-volk.cxx @@ -0,0 +1,14 @@ +#include <iostream> + +#include <volk/volk.h> + +int main() +{ + if (volkInitialize() != VK_SUCCESS) { + std::cout << "volk initialization success!" << std::endl; + } else { + std::cout << "volk initialization failure!" << std::endl; + } + + return 0; +} diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt index bb4b92c..e82cea2 100644 --- a/Tests/IncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/CMakeLists.txt @@ -121,12 +121,66 @@ add_library(ordertest ordertest.cpp) target_include_directories(ordertest SYSTEM PUBLIC SystemIncludeDirectories/systemlib) target_include_directories(ordertest PUBLIC SystemIncludeDirectories/userlib) +# Test "IMPORTED_NO_SYSTEM" property and its interaction with "SYSTEM" add_library(ordertest2 ordertest.cpp) target_include_directories(ordertest2 SYSTEM PRIVATE SystemIncludeDirectories/systemlib) target_link_libraries(ordertest2 PRIVATE ordertest2_userlib) add_library(ordertest2_userlib INTERFACE IMPORTED) target_include_directories(ordertest2_userlib INTERFACE SystemIncludeDirectories/userlib) set_property(TARGET ordertest2_userlib PROPERTY IMPORTED_NO_SYSTEM 1) +get_property(system_prop_value TARGET ordertest2_userlib PROPERTY SYSTEM) +if (NOT system_prop_value) + message(SEND_ERROR "ordertest2_userlib SYSTEM property should be ON.") +endif() + +# Test "SYSTEM" property of non-imported libraries +add_library(ordertest3_systemlib INTERFACE) +target_include_directories(ordertest3_systemlib INTERFACE SystemIncludeDirectories/systemlib) +set_property(TARGET ordertest3_systemlib PROPERTY SYSTEM 1) +add_library(ordertest3_userlib INTERFACE) +target_include_directories(ordertest3_userlib INTERFACE SystemIncludeDirectories/userlib) +add_library(ordertest3 ordertest.cpp) +target_link_libraries(ordertest3 PRIVATE ordertest3_systemlib ordertest3_userlib) + +# Test "SYSTEM" property of imported libraries and its interaction with "IMPORTED_NO_SYSTEM" +add_library(ordertest4 ordertest.cpp) +target_include_directories(ordertest4 SYSTEM PRIVATE SystemIncludeDirectories/systemlib) +target_link_libraries(ordertest4 PRIVATE ordertest4_userlib) +add_library(ordertest4_userlib INTERFACE IMPORTED) +target_include_directories(ordertest4_userlib INTERFACE SystemIncludeDirectories/userlib) +set_property(TARGET ordertest4_userlib PROPERTY SYSTEM 0) +get_property(imported_no_system_prop_value TARGET ordertest4_userlib PROPERTY IMPORTED_NO_SYSTEM) +if (imported_no_system_prop_value) + message(SEND_ERROR "ordertest4_userlib IMPORTED_NO_SYSTEM property should be OFF.") +endif() + +# Test the interaction between "SYSTEM" and "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES" +add_library(ordertest5_systemlib INTERFACE) +target_include_directories(ordertest5_systemlib SYSTEM INTERFACE SystemIncludeDirectories/systemlib) +# The default value of `SYSTEM` is already `OFF`. Here we explicitly set it again. +set_property(TARGET ordertest5_systemlib PROPERTY SYSTEM 0) +add_library(ordertest5_userlib INTERFACE) +target_include_directories(ordertest5_userlib INTERFACE SystemIncludeDirectories/userlib) +add_library(ordertest5 ordertest.cpp) +target_link_libraries(ordertest5 PRIVATE ordertest5_systemlib ordertest5_userlib) + +# Test that the include of imported executable is treated as system by default. +add_executable(ordertest6_systemexe IMPORTED) +target_include_directories(ordertest6_systemexe INTERFACE SystemIncludeDirectories/systemlib) +set_property(TARGET ordertest6_systemexe PROPERTY ENABLE_EXPORTS 1) +add_library(ordertest6_userlib INTERFACE) +target_include_directories(ordertest6_userlib INTERFACE SystemIncludeDirectories/userlib) +add_library(ordertest6 ordertest.cpp) +target_link_libraries(ordertest6 PRIVATE ordertest6_systemexe ordertest6_userlib) + +# Test that the include of imported executable is not treated as system if "SYSTEM" property is OFF. +add_library(ordertest7 ordertest.cpp) +target_include_directories(ordertest7 SYSTEM PRIVATE SystemIncludeDirectories/systemlib) +target_link_libraries(ordertest7 PRIVATE ordertest7_userexe) +add_library(ordertest7_userexe INTERFACE IMPORTED) +target_include_directories(ordertest7_userexe INTERFACE SystemIncludeDirectories/userlib) +set_property(TARGET ordertest7_userexe PROPERTY ENABLE_EXPORTS 1) +set_property(TARGET ordertest7_userexe PROPERTY SYSTEM 0) add_subdirectory(StandardIncludeDirectories) add_subdirectory(TargetIncludeDirectories) diff --git a/Tests/LoadCommand/CMakeCommands/cmTestCommand.c b/Tests/LoadCommand/CMakeCommands/cmTestCommand.c index af7b092..7176ebe 100644 --- a/Tests/LoadCommand/CMakeCommands/cmTestCommand.c +++ b/Tests/LoadCommand/CMakeCommands/cmTestCommand.c @@ -75,10 +75,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) info->CAPI->DisplaySatus(mf, info->CAPI->GetStartOutputDirectory(mf)); info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentDirectory(mf)); info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentOutputDirectory(mf)); - sprintf(buffer, "Cache version: %d.%d, CMake version: %d.%d", - info->CAPI->GetCacheMajorVersion(mf), - info->CAPI->GetCacheMinorVersion(mf), - info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf)); + snprintf( + buffer, sizeof(buffer), "Cache version: %d.%d, CMake version: %d.%d", + info->CAPI->GetCacheMajorVersion(mf), info->CAPI->GetCacheMinorVersion(mf), + info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf)); info->CAPI->DisplaySatus(mf, buffer); if (info->CAPI->CommandExists(mf, "SET")) { info->CAPI->DisplaySatus(mf, "Command SET exists"); @@ -91,10 +91,12 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) source_file = info->CAPI->CreateNewSourceFile(mf); cstr = info->CAPI->SourceFileGetSourceName(source_file); - sprintf(buffer, "Should be empty (source file name): [%s]", cstr); + snprintf(buffer, sizeof(buffer), "Should be empty (source file name): [%s]", + cstr); info->CAPI->DisplaySatus(mf, buffer); cstr = info->CAPI->SourceFileGetFullPath(source_file); - sprintf(buffer, "Should be empty (source file full path): [%s]", cstr); + snprintf(buffer, sizeof(buffer), + "Should be empty (source file full path): [%s]", cstr); info->CAPI->DisplaySatus(mf, buffer); info->CAPI->DefineSourceFileProperty(mf, "SOME_PROPERTY", "unused old prop", "This property is no longer used", 0); @@ -106,7 +108,8 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) "This property is for testing.", 0); info->CAPI->SourceFileSetProperty(source_file, "SOME_PROPERTY2", "HERE"); cstr = info->CAPI->SourceFileGetProperty(source_file, "ABSTRACT"); - sprintf(buffer, "Should be 0 (source file abstract property): [%p]", cstr); + snprintf(buffer, sizeof(buffer), + "Should be 0 (source file abstract property): [%p]", cstr); info->CAPI->DisplaySatus(mf, buffer); info->CAPI->DestroySourceFile(source_file); diff --git a/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c b/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c index af7b092..7176ebe 100644 --- a/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c +++ b/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c @@ -75,10 +75,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) info->CAPI->DisplaySatus(mf, info->CAPI->GetStartOutputDirectory(mf)); info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentDirectory(mf)); info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentOutputDirectory(mf)); - sprintf(buffer, "Cache version: %d.%d, CMake version: %d.%d", - info->CAPI->GetCacheMajorVersion(mf), - info->CAPI->GetCacheMinorVersion(mf), - info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf)); + snprintf( + buffer, sizeof(buffer), "Cache version: %d.%d, CMake version: %d.%d", + info->CAPI->GetCacheMajorVersion(mf), info->CAPI->GetCacheMinorVersion(mf), + info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf)); info->CAPI->DisplaySatus(mf, buffer); if (info->CAPI->CommandExists(mf, "SET")) { info->CAPI->DisplaySatus(mf, "Command SET exists"); @@ -91,10 +91,12 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) source_file = info->CAPI->CreateNewSourceFile(mf); cstr = info->CAPI->SourceFileGetSourceName(source_file); - sprintf(buffer, "Should be empty (source file name): [%s]", cstr); + snprintf(buffer, sizeof(buffer), "Should be empty (source file name): [%s]", + cstr); info->CAPI->DisplaySatus(mf, buffer); cstr = info->CAPI->SourceFileGetFullPath(source_file); - sprintf(buffer, "Should be empty (source file full path): [%s]", cstr); + snprintf(buffer, sizeof(buffer), + "Should be empty (source file full path): [%s]", cstr); info->CAPI->DisplaySatus(mf, buffer); info->CAPI->DefineSourceFileProperty(mf, "SOME_PROPERTY", "unused old prop", "This property is no longer used", 0); @@ -106,7 +108,8 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) "This property is for testing.", 0); info->CAPI->SourceFileSetProperty(source_file, "SOME_PROPERTY2", "HERE"); cstr = info->CAPI->SourceFileGetProperty(source_file, "ABSTRACT"); - sprintf(buffer, "Should be 0 (source file abstract property): [%p]", cstr); + snprintf(buffer, sizeof(buffer), + "Should be 0 (source file abstract property): [%p]", cstr); info->CAPI->DisplaySatus(mf, buffer); info->CAPI->DestroySourceFile(source_file); diff --git a/Tests/RunCMake/AutoExportDll/AutoExport.cmake b/Tests/RunCMake/AutoExportDll/AutoExport.cmake index dbcf4b8..fe57d56 100644 --- a/Tests/RunCMake/AutoExportDll/AutoExport.cmake +++ b/Tests/RunCMake/AutoExportDll/AutoExport.cmake @@ -7,7 +7,7 @@ set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1) add_library(autoexport SHARED hello.cxx world.cxx foo.c $<TARGET_OBJECTS:objlib>) add_library(autoexport3 SHARED cppCLI.cxx) if(MSVC AND NOT MSVC_VERSION VERSION_LESS 1600 - AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") set_property(TARGET autoexport3 PROPERTY COMMON_LANGUAGE_RUNTIME "") endif() @@ -17,7 +17,7 @@ if(MSVC) add_library(autoexport_for_exec SHARED hello2.c) target_link_libraries(autoexport_for_exec say) if(NOT MSVC_VERSION VERSION_LESS 1600 AND - NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") enable_language(ASM_MASM) target_sources(autoexport PRIVATE nop.asm) set_property(SOURCE nop.asm PROPERTY COMPILE_FLAGS /safeseh) diff --git a/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt b/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt new file mode 100644 index 0000000..5d09fcf --- /dev/null +++ b/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0102-OLD.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0102 will be removed from a future version + of CMake. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 69def6d..9549af9 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -565,6 +565,9 @@ if(CMake_TEST_CUDA) endif() add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER}) +# Add C++ Module tests. +add_RunCMake_test(CXXModules -DCMake_TEST_MODULE_COMPILATION=${CMake_TEST_MODULE_COMPILATION} -DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}) + # ctresalloc links against CMakeLib and CTestLib, which means it can't be built # if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.) # So, it has to be provided in the original build tree. @@ -966,7 +969,7 @@ endif() if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])") add_RunCMake_test(CSharpCustomCommand) - if(NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") add_RunCMake_test(CSharpReferenceImport) endif() endif() diff --git a/Tests/RunCMake/CTestTimeout/TestTimeout.c b/Tests/RunCMake/CTestTimeout/TestTimeout.c index 5a008a7..0d534fc 100644 --- a/Tests/RunCMake/CTestTimeout/TestTimeout.c +++ b/Tests/RunCMake/CTestTimeout/TestTimeout.c @@ -1,6 +1,7 @@ #if defined(_WIN32) # include <windows.h> #else +# include <sched.h> # include <unistd.h> #endif diff --git a/Tests/RunCMake/CXXModules/CMakeLists.txt b/Tests/RunCMake/CXXModules/CMakeLists.txt new file mode 100644 index 0000000..708d92c --- /dev/null +++ b/Tests/RunCMake/CXXModules/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.23) +project(${RunCMake_TEST} NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-check.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-check.cmake new file mode 100644 index 0000000..cb6f6bd --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-check.cmake @@ -0,0 +1,40 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/export-modules-targets.cmake" export_script) + +if (NOT export_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules/cxx-modules\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property script inclusion") +endif () + +file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules.cmake" trampoline_script) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + if (NOT trampoline_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-[^.]*\.cmake" OPTIONAL\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property per-config script inclusion(s)") + endif () +else () + if (NOT trampoline_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-[^.]*\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property per-config script inclusion(s)") + endif () +endif () + +set(any_exists 0) +foreach (config IN ITEMS noconfig Debug Release RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-${config}.cmake") + continue () + endif () + set(any_exists 1) + + file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-${config}.cmake" config_script) + + if (NOT config_script MATCHES "include\\(\"\\\${CMAKE_CURRENT_LIST_DIR}/target-export-name-${config}\\.cmake\"\\)") + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module per-target property script inclusion") + endif () +endforeach () + +if (NOT any_exists) + list(APPEND RunCMake_TEST_FAILED + "No per-configuration target files exist.") +endif () diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt new file mode 100644 index 0000000..c05b0b4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at ExportBuildCxxModules.cmake:6 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake new file mode 100644 index 0000000..850f8dc --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake @@ -0,0 +1,22 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(export-modules) +target_sources(export-modules + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(export-modules + PRIVATE + cxx_std_20) +set_property(TARGET export-modules + PROPERTY EXPORT_NAME export-name) + +install(TARGETS export-modules + EXPORT exp + FILE_SET fs DESTINATION "include/cxx/export-modules") + +export(EXPORT exp + FILE "${CMAKE_BINARY_DIR}/lib/cmake/export-modules/export-modules-targets.cmake" + CXX_MODULES_DIRECTORY "cxx-modules") diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-check.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-check.cmake new file mode 100644 index 0000000..9e83fd8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-check.cmake @@ -0,0 +1,35 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/exp.cmake" export_script) + +if (NOT export_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules/cxx-modules\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property script inclusion") +endif () + +file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules.cmake" trampoline_script) + +if (NOT trampoline_script MATCHES [[file\(GLOB _cmake_cxx_module_includes "\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-\*\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property per-config script inclusion(s)") +endif () + +set(any_exists 0) +foreach (config IN ITEMS noconfig Debug Release RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-${config}.cmake") + continue () + endif () + set(any_exists 1) + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-${config}.cmake" config_script) + + if (NOT config_script MATCHES "include\\(\"\\\${CMAKE_CURRENT_LIST_DIR}/target-export-name-${config}\\.cmake\"\\)") + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module per-target property script inclusion") + endif () +endforeach () + +if (NOT any_exists) + list(APPEND RunCMake_TEST_FAILED + "No per-configuration target files exist.") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt new file mode 100644 index 0000000..4fe27a9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at ExportInstallCxxModules.cmake:6 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake new file mode 100644 index 0000000..234a4b5 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake @@ -0,0 +1,22 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(export-modules) +target_sources(export-modules + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(export-modules + PRIVATE + cxx_std_20) +set_property(TARGET export-modules + PROPERTY EXPORT_NAME export-name) + +install(TARGETS export-modules + EXPORT exp + FILE_SET fs DESTINATION "include/cxx/export-modules") + +install(EXPORT exp + DESTINATION "lib/cmake/export-modules" + CXX_MODULES_DIRECTORY "cxx-modules") diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt new file mode 100644 index 0000000..d573a02 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt @@ -0,0 +1,12 @@ +CMake Warning \(dev\) at FileSetModuleHeaderUnitsInterface.cmake:2 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error at FileSetModuleHeaderUnitsInterface.cmake:2 \(target_sources\): + target_sources File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" + may not have "INTERFACE" visibility +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake new file mode 100644 index 0000000..03ca17e --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake @@ -0,0 +1,8 @@ +add_library(module-header) +target_sources(module-header + INTERFACE + FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES + sources/module-header.h) +target_compile_features(module-header + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported-stderr.txt new file mode 100644 index 0000000..1b4ba5d --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported-stderr.txt @@ -0,0 +1,6 @@ +CMake Warning \(dev\) at FileSetModuleHeaderUnitsInterfaceImported.cmake:2 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported.cmake new file mode 100644 index 0000000..9ff5606 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported.cmake @@ -0,0 +1,8 @@ +add_library(module-header SHARED IMPORTED) +target_sources(module-header + INTERFACE + FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES + sources/module-header.h) +target_compile_features(module-header + INTERFACE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt new file mode 100644 index 0000000..a7ac88e --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at FileSetModuleHeaderUnitsPrivate.cmake:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake new file mode 100644 index 0000000..ebf9853 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake @@ -0,0 +1,13 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(module-header + sources/cxx-anchor.cxx) +target_sources(module-header + PRIVATE + FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES + sources/module-header.h) +target_compile_features(module-header + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt new file mode 100644 index 0000000..a5b4ede --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at FileSetModuleHeaderUnitsPublic.cmake:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake new file mode 100644 index 0000000..3dfccbb5 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake @@ -0,0 +1,13 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(module-header + sources/cxx-anchor.cxx) +target_sources(module-header + PUBLIC + FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES + sources/module-header.h) +target_compile_features(module-header + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt new file mode 100644 index 0000000..81a35e8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt @@ -0,0 +1,12 @@ +CMake Warning \(dev\) at FileSetModulesInterface.cmake:2 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error at FileSetModulesInterface.cmake:2 \(target_sources\): + target_sources File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" + may not have "INTERFACE" visibility +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake b/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake new file mode 100644 index 0000000..24cec3e --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake @@ -0,0 +1,8 @@ +add_library(module) +target_sources(module + INTERFACE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(module + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt new file mode 100644 index 0000000..4420bbc --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt @@ -0,0 +1,6 @@ +CMake Warning \(dev\) at FileSetModulesInterfaceImported.cmake:2 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported.cmake b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported.cmake new file mode 100644 index 0000000..6640ae9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported.cmake @@ -0,0 +1,8 @@ +add_library(module SHARED IMPORTED) +target_sources(module + INTERFACE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(module + INTERFACE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt new file mode 100644 index 0000000..03e06cc --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at FileSetModulesPrivate.cmake:6 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake new file mode 100644 index 0000000..ca18982 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake @@ -0,0 +1,12 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(module) +target_sources(module + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(module + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt new file mode 100644 index 0000000..0c110c3 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at FileSetModulesPublic.cmake:6 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake new file mode 100644 index 0000000..58de174 --- /dev/null +++ b/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake @@ -0,0 +1,12 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(module) +target_sources(module + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(module + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/InstallBMI-check.cmake b/Tests/RunCMake/CXXModules/InstallBMI-check.cmake new file mode 100644 index 0000000..f891c80 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMI-check.cmake @@ -0,0 +1,24 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script) + +if (NOT install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi" OR NOT CMAKE_INSTALL_COMPONENT\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find BMI install script component for `bmi`") +endif () + +if (NOT install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find BMI install script inclusion") +endif () + +if (NOT install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi-optional"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find BMI install script component for `bmi-optional`") +endif () + +if (NOT install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi-only-debug" OR NOT CMAKE_INSTALL_COMPONENT\) + if\(CMAKE_INSTALL_CONFIG_NAME MATCHES "\^\(\[Dd\]\[Ee\]\[Bb\]\[Uu\]\[Gg\]\)\$"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find BMI install script component for `bmi-only-debug`") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt new file mode 100644 index 0000000..fc3c7db --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt @@ -0,0 +1,6 @@ +CMake Warning \(dev\) at InstallBMI.cmake:8 \(install\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/InstallBMI.cmake b/Tests/RunCMake/CXXModules/InstallBMI.cmake new file mode 100644 index 0000000..f0947b4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMI.cmake @@ -0,0 +1,23 @@ +enable_language(CXX) + +add_library(install-bmi) +target_sources(install-bmi + PRIVATE + sources/cxx-anchor.cxx) + +install(TARGETS install-bmi + CXX_MODULES_BMI + DESTINATION "lib/bmi" + COMPONENT "bmi") + +install(TARGETS install-bmi + CXX_MODULES_BMI + DESTINATION "lib/bmi" + EXCLUDE_FROM_ALL + COMPONENT "bmi-optional") + +install(TARGETS install-bmi + CXX_MODULES_BMI + DESTINATION "lib/bmi" + CONFIGURATIONS Debug + COMPONENT "bmi-only-debug") diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-check.cmake b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-check.cmake new file mode 100644 index 0000000..32a37ad --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-check.cmake @@ -0,0 +1,8 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script) + +if (NOT install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi-generic-args\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find BMI install script inclusion") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt new file mode 100644 index 0000000..44c961f --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt @@ -0,0 +1,6 @@ +CMake Warning \(dev\) at InstallBMIGenericArgs.cmake:8 \(install\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs.cmake b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs.cmake new file mode 100644 index 0000000..8f17143 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs.cmake @@ -0,0 +1,9 @@ +enable_language(CXX) + +add_library(install-bmi-generic-args) +target_sources(install-bmi-generic-args + PRIVATE + sources/cxx-anchor.cxx) + +install(TARGETS install-bmi-generic-args + DESTINATION "bin") diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore-check.cmake b/Tests/RunCMake/CXXModules/InstallBMIIgnore-check.cmake new file mode 100644 index 0000000..7d13ef0 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIIgnore-check.cmake @@ -0,0 +1,13 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script) + +if (install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi" OR NOT CMAKE_INSTALL_COMPONENT\)]]) + list(APPEND RunCMake_TEST_FAILED + "Found BMI install script component for `bmi`") +endif () + +if (install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi-ignore\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]]) + list(APPEND RunCMake_TEST_FAILED + "Found BMI install script inclusion") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt new file mode 100644 index 0000000..d9d2c2d --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt @@ -0,0 +1,6 @@ +CMake Warning \(dev\) at InstallBMIIgnore.cmake:5 \(install\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore.cmake b/Tests/RunCMake/CXXModules/InstallBMIIgnore.cmake new file mode 100644 index 0000000..f339511 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIIgnore.cmake @@ -0,0 +1,9 @@ +enable_language(CXX) + +add_library(install-bmi-ignore INTERFACE) + +install(TARGETS install-bmi-ignore + CXX_MODULES_BMI + # An empty destination ignores BMI installation. + DESTINATION "" + COMPONENT "bmi") diff --git a/Tests/RunCMake/CXXModules/InstallBMINoGenericArgs-check.cmake b/Tests/RunCMake/CXXModules/InstallBMINoGenericArgs-check.cmake new file mode 100644 index 0000000..412e260 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMINoGenericArgs-check.cmake @@ -0,0 +1,8 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script) + +if (install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi-generic-args\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]]) + list(APPEND RunCMake_TEST_FAILED + "Found BMI install script inclusion") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake new file mode 100644 index 0000000..0d08c44 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake @@ -0,0 +1,34 @@ +include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(have_file 0) + foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json") + continue () + endif () + set(have_file 1) + + set(CMAKE_BUILD_TYPE "${config}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + endforeach () + + if (NOT have_file) + list(APPEND RunCMake_TEST_FAILED + "No recognized build configurations found.") + endif () +else () + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") +endif () diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt new file mode 100644 index 0000000..ebf7be5 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at NinjaDependInfoBMIInstall.cmake:14 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake new file mode 100644 index 0000000..32dc42d --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake @@ -0,0 +1,76 @@ +# Fake out that we have dyndep; we only need to generate, not actually build +# here. +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +enable_language(CXX) + +if (NOT CMAKE_GENERATOR MATCHES "Ninja") + message(FATAL_ERROR + "This test requires a 'Ninja' generator to be used.") +endif () + +add_library(ninja-bmi-install-public) +target_sources(ninja-bmi-install-public + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.cxx + sources/module-use.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/sources" + FILES + sources/module.cxx + sources/module-part.cxx + FILE_SET internal_partitions TYPE CXX_MODULES FILES + sources/module-internal-part.cxx) +target_compile_features(ninja-bmi-install-public + PRIVATE + cxx_std_20) +set_property(TARGET ninja-bmi-install-public + PROPERTY EXPORT_NAME "with-public") + +install(TARGETS ninja-bmi-install-public + FILE_SET modules + DESTINATION "lib/cxx" + COMPONENT "modules" + FILE_SET internal_partitions + DESTINATION "lib/cxx/internals" + COMPONENT "modules-internal" + CXX_MODULES_BMI + DESTINATION "lib/cxx/modules/$<CONFIG>" + COMPONENT "bmi") + +add_library(ninja-bmi-install-private) +target_sources(ninja-bmi-install-private + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.cxx + sources/module-use.cxx + PRIVATE + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/sources" + FILES + sources/module.cxx + sources/module-part.cxx + FILE_SET internal_partitions TYPE CXX_MODULES FILES + sources/module-internal-part.cxx) +target_compile_features(ninja-bmi-install-private + PRIVATE + cxx_std_20) +set_property(TARGET ninja-bmi-install-private + PROPERTY EXPORT_NAME "with-private") + +set(CMAKE_INSTALL_MESSAGE LAZY) +install(TARGETS ninja-bmi-install-private + CXX_MODULES_BMI + DESTINATION "lib/cxx/modules/private/$<CONFIG>" + PERMISSIONS + OWNER_READ OWNER_WRITE + GROUP_READ + WORLD_READ + COMPONENT "bmi") diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake new file mode 100644 index 0000000..7720257 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake @@ -0,0 +1,34 @@ +include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(have_file 0) + foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json") + continue () + endif () + set(have_file 1) + + set(CMAKE_BUILD_TYPE "${config}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + endforeach () + + if (NOT have_file) + list(APPEND RunCMake_TEST_FAILED + "No recognized build configurations found.") + endif () +else () + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") +endif () diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt new file mode 100644 index 0000000..e328223 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at NinjaDependInfoExport.cmake:14 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake new file mode 100644 index 0000000..05e7ef7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake @@ -0,0 +1,85 @@ +# Fake out that we have dyndep; we only need to generate, not actually build +# here. +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +enable_language(CXX) + +if (NOT CMAKE_GENERATOR MATCHES "Ninja") + message(FATAL_ERROR + "This test requires a 'Ninja' generator to be used.") +endif () + +add_library(ninja-exports-public) +target_sources(ninja-exports-public + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.cxx + sources/module-use.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/sources" + FILES + sources/module.cxx + sources/module-part.cxx + FILE_SET internal_partitions TYPE CXX_MODULES FILES + sources/module-internal-part.cxx) +target_compile_features(ninja-exports-public + PRIVATE + cxx_std_20) +set_property(TARGET ninja-exports-public + PROPERTY EXPORT_NAME "with-public") + +install(TARGETS ninja-exports-public + EXPORT exp + FILE_SET modules + DESTINATION "lib/cxx" + COMPONENT "modules" + FILE_SET internal_partitions + DESTINATION "lib/cxx/internals" + COMPONENT "modules-internal") + +add_library(ninja-exports-private) +target_sources(ninja-exports-private + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.cxx + sources/module-use.cxx + PRIVATE + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/sources" + FILES + sources/module.cxx + sources/module-part.cxx + FILE_SET internal_partitions TYPE CXX_MODULES FILES + sources/module-internal-part.cxx) +target_compile_features(ninja-exports-private + PRIVATE + cxx_std_20) +set_property(TARGET ninja-exports-private + PROPERTY EXPORT_NAME "with-private") + +install(TARGETS ninja-exports-private + EXPORT exp) + +# Test multiple build exports. +export(EXPORT exp + FILE "${CMAKE_BINARY_DIR}/lib/cmake/export1/export1-targets.cmake" + NAMESPACE export1:: + CXX_MODULES_DIRECTORY "cxx-modules") +export(EXPORT exp + FILE "${CMAKE_BINARY_DIR}/lib/cmake/export2/export2-targets.cmake" + CXX_MODULES_DIRECTORY "cxx-modules") + +# Test multiple install exports. +install(EXPORT exp + DESTINATION "lib/cmake/export1" + NAMESPACE export1:: + CXX_MODULES_DIRECTORY "cxx-modules") +install(EXPORT exp + DESTINATION "lib/cmake/export2" + CXX_MODULES_DIRECTORY "cxx-modules") diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake new file mode 100644 index 0000000..b9a1315 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake @@ -0,0 +1,34 @@ +include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(have_file 0) + foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json") + continue () + endif () + set(have_file 1) + + set(CMAKE_BUILD_TYPE "${config}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + endforeach () + + if (NOT have_file) + list(APPEND RunCMake_TEST_FAILED + "No recognized build configurations found.") + endif () +else () + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") +endif () diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt new file mode 100644 index 0000000..ca430cc --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt @@ -0,0 +1,11 @@ +CMake Warning \(dev\) at NinjaDependInfoFileSet.cmake:14 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake new file mode 100644 index 0000000..74e729e --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake @@ -0,0 +1,59 @@ +# Fake out that we have dyndep; we only need to generate, not actually build +# here. +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +enable_language(CXX) + +if (NOT CMAKE_GENERATOR MATCHES "Ninja") + message(FATAL_ERROR + "This test requires a 'Ninja' generator to be used.") +endif () + +add_library(ninja-file-sets-public) +target_sources(ninja-file-sets-public + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.cxx + sources/module-use.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/sources" + FILES + sources/module.cxx + sources/module-part.cxx + FILE_SET internal_partitions TYPE CXX_MODULES FILES + sources/module-internal-part.cxx) +target_compile_features(ninja-file-sets-public + PRIVATE + cxx_std_20) + +install(TARGETS ninja-file-sets-public + FILE_SET modules + DESTINATION "lib/cxx" + COMPONENT "modules" + FILE_SET internal_partitions + DESTINATION "lib/cxx/internals" + COMPONENT "modules-internal") + +add_library(ninja-file-sets-private) +target_sources(ninja-file-sets-private + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.cxx + sources/module-use.cxx + PRIVATE + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/sources" + FILES + sources/module.cxx + sources/module-part.cxx + FILE_SET internal_partitions TYPE CXX_MODULES FILES + sources/module-internal-part.cxx) +target_compile_features(ninja-file-sets-private + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/NoCXX-result.txt b/Tests/RunCMake/CXXModules/NoCXX-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt new file mode 100644 index 0000000..aa7f406 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt @@ -0,0 +1,20 @@ +CMake Warning \(dev\) at NoCXX.cmake:4 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error in CMakeLists.txt: + The "nocxx" target has C\+\+ module sources but the "CXX" language has not + been enabled + +( +CMake Error in CMakeLists.txt: +( The "nocxx" target has C\+\+ module sources but the "CXX" language has not + been enabled +| The "nocxx" target contains C\+\+ module sources which are not supported by + the generator +) +)* +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoCXX.cmake b/Tests/RunCMake/CXXModules/NoCXX.cmake new file mode 100644 index 0000000..3c46f9d --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX.cmake @@ -0,0 +1,9 @@ +enable_language(C) + +add_library(nocxx) +target_sources(nocxx + PRIVATE + sources/c-anchor.c + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) diff --git a/Tests/RunCMake/CXXModules/NoCXX20-result.txt b/Tests/RunCMake/CXXModules/NoCXX20-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX20-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt new file mode 100644 index 0000000..95d73b1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt @@ -0,0 +1,20 @@ +CMake Warning \(dev\) at NoCXX20.cmake:4 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error in CMakeLists.txt: + The "nocxx20" target has C\+\+ module sources but is not using at least + "cxx_std_20" + +( +CMake Error in CMakeLists.txt: +( The "nocxx20" target has C\+\+ module sources but is not using at least + "cxx_std_20" +| The "nocxx20" target contains C\+\+ module sources which are not supported by + the generator +) +)* +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoCXX20.cmake b/Tests/RunCMake/CXXModules/NoCXX20.cmake new file mode 100644 index 0000000..b7372e8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX20.cmake @@ -0,0 +1,11 @@ +enable_language(CXX) + +add_library(nocxx20) +target_sources(nocxx20 + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +set_target_properties(nocxx20 + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON) diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt new file mode 100644 index 0000000..5f90ec8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt @@ -0,0 +1,20 @@ +CMake Warning \(dev\) at NoCXX20ModuleFlag.cmake:4 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error in CMakeLists.txt: + The "noexperimentalflag" target has C\+\+ module sources but its experimental + support has not been requested + +( +CMake Error in CMakeLists.txt: +( The "noexperimentalflag" target has C\+\+ module sources but its experimental + support has not been requested +| The "noexperimentalflag" target contains C\+\+ module sources which are not + supported by the generator +) +)* +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake new file mode 100644 index 0000000..5f896f9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake @@ -0,0 +1,10 @@ +enable_language(CXX) + +add_library(noexperimentalflag) +target_sources(noexperimentalflag + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(noexperimentalflag + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt new file mode 100644 index 0000000..52f781f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt @@ -0,0 +1,30 @@ +CMake Warning \(dev\) at NoDyndepSupport.cmake:10 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +(CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error: + The Ninja generator does not support C\+\+20 modules using Ninja version + + .* + + due to lack of required features. Ninja 1.10 or higher is required. + +|CMake Error in CMakeLists.txt: + The "nodyndep" target contains C\+\+ module sources which are not supported + by the generator + +( +CMake Error in CMakeLists.txt: + The "nodyndep" target contains C\+\+ module sources which are not supported + by the generator + +)*) +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake b/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake new file mode 100644 index 0000000..0954400 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake @@ -0,0 +1,16 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +if (NOT CMAKE_CXX_STANDARD_DEFAULT) + set(CMAKE_CXX_STANDARD_DEFAULT "11") +endif () + +add_library(nodyndep) +target_sources(nodyndep + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nodyndep + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt new file mode 100644 index 0000000..a93eb40 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt @@ -0,0 +1,22 @@ +CMake Warning \(dev\) at NotCXXSourceModuleHeaderUnits.cmake:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error in CMakeLists.txt: + Target "not-cxx-source" contains the source + + .*/Tests/RunCMake/CXXModules/sources/c-anchor.c + + in a file set of type "CXX_MODULE_HEADER_UNITS" but the source is not + classified as a "CXX" source. + + +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake new file mode 100644 index 0000000..af4ddac --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake @@ -0,0 +1,15 @@ +enable_language(C) +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(not-cxx-source) +target_sources(not-cxx-source + PRIVATE + sources/cxx-anchor.cxx + PUBLIC + FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES + sources/c-anchor.c) +target_compile_features(not-cxx-source + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt new file mode 100644 index 0000000..d341c1f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt @@ -0,0 +1,17 @@ +CMake Warning \(dev\) at NotCXXSourceModules.cmake:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:6 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error in CMakeLists.txt: + Target "not-cxx-source" contains the source + + .*/Tests/RunCMake/CXXModules/sources/c-anchor.c + + in a file set of type "CXX_MODULES" but the source is not classified as a + "CXX" source. + + +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake new file mode 100644 index 0000000..f7a6060 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake @@ -0,0 +1,13 @@ +enable_language(C) +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(not-cxx-source) +target_sources(not-cxx-source + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/c-anchor.c) +target_compile_features(not-cxx-source + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake new file mode 100644 index 0000000..ca1bc81 --- /dev/null +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake @@ -0,0 +1,169 @@ +include(RunCMake) + +# For `if (IN_LIST)` +cmake_policy(SET CMP0057 NEW) + +run_cmake(compiler_introspection) +include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake") + +# Test negative cases where C++20 modules do not work. +run_cmake(NoCXX) +if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + # This test requires that the compiler be told to compile in an older-than-20 + # standard. If the compiler forces a standard to be used, skip it. + if (NOT forced_cxx_standard) + run_cmake(NoCXX20) + endif () + + # This test uses C++20, but another prerequisite is missing, so forced + # standards don't matter. + run_cmake(NoCXX20ModuleFlag) +endif () + +if (RunCMake_GENERATOR MATCHES "Ninja") + execute_process( + COMMAND "${CMAKE_MAKE_PROGRAM}" --version + RESULT_VARIABLE res + OUTPUT_VARIABLE ninja_version + ERROR_VARIABLE err + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE) + + if (res) + message(WARNING + "Failed to determine `ninja` version: ${err}") + set(ninja_version "0") + endif () +endif () + +# Test behavior when the generator does not support C++20 modules. +if (NOT RunCMake_GENERATOR MATCHES "Ninja" OR + ninja_version VERSION_LESS "1.10" OR + NOT "cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + run_cmake(NoDyndepSupport) + endif () + + # Bail; the remaining tests require the generator to successfully generate + # with C++20 modules in the source list. + return () +endif () + +set(fileset_types + Modules + ModuleHeaderUnits) +set(scopes + Interface + Private + Public) +foreach (fileset_type IN LISTS fileset_types) + foreach (scope IN LISTS scopes) + run_cmake("FileSet${fileset_type}${scope}") + endforeach () + run_cmake("FileSet${fileset_type}InterfaceImported") + + # Test the error message when a non-C++ source file is found in the source + # list. + run_cmake("NotCXXSource${fileset_type}") +endforeach () + +run_cmake(InstallBMI) +run_cmake(InstallBMIGenericArgs) +run_cmake(InstallBMIIgnore) + +run_cmake(ExportBuildCxxModules) +run_cmake(ExportInstallCxxModules) + +# Generator-specific tests. +if (RunCMake_GENERATOR MATCHES "Ninja") + run_cmake(NinjaDependInfoFileSet) + run_cmake(NinjaDependInfoExport) + run_cmake(NinjaDependInfoBMIInstall) +else () + message(FATAL_ERROR + "Please add 'DependInfo' tests for the '${RunCMake_GENERATOR}' generator.") +endif () + +# Actual compilation tests. +if (NOT CMake_TEST_MODULE_COMPILATION) + return () +endif () + +function (run_cxx_module_test directory) + set(test_name "${directory}") + if (NOT ARGN STREQUAL "") + list(POP_FRONT ARGN test_name) + endif () + + set(RunCMake_TEST_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/examples/${directory}") + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/examples/${test_name}-build") + + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) + else () + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif () + + if (RunCMake_CXXModules_INSTALL) + set(prefix "${RunCMake_BINARY_DIR}/examples/${test_name}-install") + file(REMOVE_RECURSE "${prefix}") + list(APPEND RunCMake_TEST_OPTIONS + "-DCMAKE_INSTALL_PREFIX=${prefix}") + endif () + + list(APPEND RunCMake_TEST_OPTIONS + "-DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}" + ${ARGN}) + run_cmake("examples/${test_name}") + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command("examples/${test_name}-build" "${CMAKE_COMMAND}" --build . --config Debug) + if (RunCMake_CXXModules_INSTALL) + run_cmake_command("examples/${test_name}-install" "${CMAKE_COMMAND}" --build . --target install --config Debug) + endif () + run_cmake_command("examples/${test_name}-test" "${CMAKE_CTEST_COMMAND}" -C Debug --output-on-failure) +endfunction () + +string(REPLACE "," ";" CMake_TEST_MODULE_COMPILATION "${CMake_TEST_MODULE_COMPILATION}") + +# Tests which use named modules. +if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(simple) + run_cxx_module_test(library library-static -DBUILD_SHARED_LIBS=OFF) + run_cxx_module_test(generated) + run_cxx_module_test(public-req-private) +endif () + +# Tests which use named modules in shared libraries. +if ("shared" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(library library-shared -DBUILD_SHARED_LIBS=ON) +endif () + +# Tests which use partitions. +if ("partitions" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(partitions) +endif () + +# Tests which use internal partitions. +if ("internal_partitions" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(internal-partitions) +endif () + +# Tests which install BMIs +if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(export-interface-build) + run_cxx_module_test(export-bmi-and-interface-build) +endif () + +# All of the following tests perform installation. +set(RunCMake_CXXModules_INSTALL 1) + +# Tests which install BMIs +if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(install-bmi) + run_cxx_module_test(install-bmi-and-interfaces) + + if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(export-interface-install) + run_cxx_module_test(export-bmi-and-interface-install) + endif () +endif () diff --git a/Tests/RunCMake/CXXModules/check-json.cmake b/Tests/RunCMake/CXXModules/check-json.cmake new file mode 100644 index 0000000..19d0c8a --- /dev/null +++ b/Tests/RunCMake/CXXModules/check-json.cmake @@ -0,0 +1,160 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) + +function (json_placeholders in out) + string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" in "${in}") + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + string(REPLACE "<CONFIG_DIR>" "${CMAKE_BUILD_TYPE}/" in "${in}") + else () + string(REPLACE "<CONFIG_DIR>" "" in "${in}") + endif () + if (CMAKE_BUILD_TYPE) + string(REPLACE "<CONFIG_FORCE>" "${CMAKE_BUILD_TYPE}" in "${in}") + else () + string(REPLACE "<CONFIG_FORCE>" "noconfig" in "${in}") + endif () + string(REPLACE "<SOURCE_DIR>" "${RunCMake_SOURCE_DIR}" in "${in}") + string(REPLACE "<BINARY_DIR>" "${RunCMake_TEST_BINARY_DIR}" in "${in}") + set("${out}" "${in}" PARENT_SCOPE) +endfunction () + +function (check_json_value path actual_type expect_type actual_value expect_value) + if (NOT actual_type STREQUAL expect_type) + list(APPEND RunCMake_TEST_FAILED + "Type mismatch at ${path}: ${actual_type} vs. ${expect_type}") + return () + endif () + + if (actual_type STREQUAL NULL) + # Nothing to check + elseif (actual_type STREQUAL BOOLEAN) + if (NOT actual_value STREQUAL expect_value) + list(APPEND RunCMake_TEST_FAILED + "Boolean mismatch at ${path}: ${actual_value} vs. ${expect_value}") + endif () + elseif (actual_type STREQUAL NUMBER) + if (NOT actual_value EQUAL expect_value) + list(APPEND RunCMake_TEST_FAILED + "Number mismatch at ${path}: ${actual_value} vs. ${expect_value}") + endif () + elseif (actual_type STREQUAL STRING) + # Allow some values to be ignored. + if (expect_value STREQUAL "<IGNORE>") + return () + endif () + + json_placeholders("${expect_value}" expect_value_expanded) + if (NOT actual_value STREQUAL expect_value_expanded) + list(APPEND RunCMake_TEST_FAILED + "String mismatch at ${path}: ${actual_value} vs. ${expect_value_expanded}") + endif () + elseif (actual_type STREQUAL ARRAY) + check_json_array("${path}" "${actual_value}" "${expect_value}") + elseif (actual_type STREQUAL OBJECT) + check_json_object("${path}" "${actual_value}" "${expect_value}") + endif () +endfunction () + +# Check that two arrays are the same. +function (check_json_array path actual expect) + string(JSON actual_len LENGTH "${actual}") + string(JSON expect_len LENGTH "${expect}") + + set(iter_len "${actual_len}") + if (actual_len LESS expect_len) + list(APPEND RunCMake_TEST_FAILED + "Missing array items at ${path}") + elseif (expect_len LESS actual_len) + list(APPEND RunCMake_TEST_FAILED + "Extra array items at ${path}") + set(iter_len "${expect_len}") + endif () + + foreach (idx RANGE "${iter_len}") + if (idx EQUAL iter_len) + break () + endif () + + set(new_path "${path}[${idx}]") + string(JSON actual_type TYPE "${actual}" "${idx}") + string(JSON expect_type TYPE "${expect}" "${idx}") + string(JSON actual_value GET "${actual}" "${idx}") + string(JSON expect_value GET "${expect}" "${idx}") + check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}") + endforeach () +endfunction () + +# Check that two inner objects are the same. +function (check_json_object path actual expect) + string(JSON actual_len LENGTH "${actual}") + string(JSON expect_len LENGTH "${expect}") + + set(actual_keys "") + set(expect_keys "") + foreach (idx RANGE "${actual_len}") + if (idx EQUAL actual_len) + break () + endif () + + string(JSON actual_key MEMBER "${actual}" "${idx}") + list(APPEND actual_keys "${actual_key}") + endforeach () + foreach (idx RANGE "${expect_len}") + if (idx EQUAL expect_len) + break () + endif () + + string(JSON expect_key MEMBER "${expect}" "${idx}") + list(APPEND expect_keys "${expect_key}") + endforeach () + + json_placeholders("${expect_keys}" expect_keys_expanded) + + set(actual_keys_missed "${actual_keys}") + set(expect_keys_missed "${expect_keys}") + + set(common_keys "") + set(expect_keys_stack "${expect_keys}") + while (expect_keys_stack) + list(POP_BACK expect_keys_stack expect_key) + json_placeholders("${expect_key}" expect_key_expanded) + + if (expect_key_expanded IN_LIST actual_keys_missed AND + expect_key IN_LIST expect_keys_missed) + list(APPEND common_keys "${expect_key}") + endif () + + list(REMOVE_ITEM actual_keys_missed "${expect_key_expanded}") + list(REMOVE_ITEM expect_keys_missed "${expect_key}") + endwhile () + + if (actual_keys_missed) + string(REPLACE ";" ", " actual_keys_missed_text "${actual_keys_missed}") + list(APPEND RunCMake_TEST_FAILED + "Missing expected members at ${path}: ${actual_keys_missed_text}") + endif () + if (expect_keys_missed) + string(REPLACE ";" ", " expect_keys_missed_text "${expect_keys_missed}") + list(APPEND RunCMake_TEST_FAILED + "Extra unexpected members at ${path}: ${expect_keys_missed_text}") + endif () + + foreach (key IN LISTS common_keys) + json_placeholders("${key}" key_expanded) + set(new_path "${path}.${key_expanded}") + string(JSON actual_type TYPE "${actual}" "${key_expanded}") + string(JSON expect_type TYPE "${expect}" "${key}") + string(JSON actual_value GET "${actual}" "${key_expanded}") + string(JSON expect_value GET "${expect}" "${key}") + check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}") + endforeach () +endfunction () + +# Check that two JSON objects are the same. +function (check_json actual expect) + check_json_object("" "${actual}" "${expect}") +endfunction () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") + +cmake_policy(POP) diff --git a/Tests/RunCMake/CXXModules/compiler_introspection.cmake b/Tests/RunCMake/CXXModules/compiler_introspection.cmake new file mode 100644 index 0000000..7a2df3d --- /dev/null +++ b/Tests/RunCMake/CXXModules/compiler_introspection.cmake @@ -0,0 +1,25 @@ +enable_language(CXX) + +set(info "") + +# See `Modules/Compiler/MSVC-CXX.cmake` for this. If there is explicitly no +# default, the feature list is populated to be everything. +if (DEFINED CMAKE_CXX_STANDARD_DEFAULT AND + CMAKE_CXX_STANDARD_DEFAULT STREQUAL "") + set(CMAKE_CXX_COMPILE_FEATURES "") +endif () + +# Detect if the environment forces a C++ standard, let the test selection know. +set(forced_cxx_standard 0) +if (CMAKE_CXX_FLAGS MATCHES "-std=") + set(forced_cxx_standard 1) +endif () + +# Forward information about the C++ compile features. +string(APPEND info "\ +set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\") +set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\") +set(forced_cxx_standard \"${forced_cxx_standard}\") +") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi-and-interfaces.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi-and-interfaces.cmake new file mode 100644 index 0000000..f99455b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi-and-interfaces.cmake @@ -0,0 +1,22 @@ +function (check_for_bmi prefix destination name) + set(found 0) + foreach (ext IN ITEMS gcm) + if (EXISTS "${prefix}/${destination}/${name}.${ext}") + set(found 1) + break () + endif () + endforeach () + + if (NOT found) + message(SEND_ERROR + "Failed to find the ${name} BMI") + endif () +endfunction () + +function (check_for_interface prefix destination subdir name) + set(found 0) + if (NOT EXISTS "${prefix}/${destination}/${subdir}/${name}") + message(SEND_ERROR + "Failed to find the ${name} module interface") + endif () +endfunction () diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake new file mode 100644 index 0000000..8efe508 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake @@ -0,0 +1,27 @@ +function (check_for_bmi prefix destination name) + set(found 0) + foreach (ext IN ITEMS gcm) + if (EXISTS "${prefix}/${destination}/${name}.${ext}") + set(found 1) + break () + endif () + endforeach () + + if (NOT found) + message(SEND_ERROR + "Failed to find the ${name} BMI") + endif () +endfunction () + +function (check_for_interface prefix destination subdir name) + set(found 0) + if (NOT EXISTS "${prefix}/${destination}/${subdir}/${name}") + message(SEND_ERROR + "Failed to find the ${name} module interface") + endif () +endfunction () + +function (report_dirs prefix destination) + message("prefix: ${prefix}") + message("destination: ${destination}") +endfunction () diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake new file mode 100644 index 0000000..381094e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake @@ -0,0 +1,18 @@ +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +if (NOT EXISTS "${CMake_TEST_MODULE_COMPILATION_RULES}") + message(FATAL_ERROR + "The `CMake_TEST_MODULE_COMPILATION_RULES` file must be specified " + "for these tests to operate.") +endif () + +include("${CMake_TEST_MODULE_COMPILATION_RULES}") + +if (NOT CMake_TEST_CXXModules_UUID STREQUAL "a246741c-d067-4019-a8fb-3d16b0c9d1d3") + message(FATAL_ERROR + "The compilation rule file needs updated for changes in the test " + "suite. Please see the history for what needs to be updated.") +endif () + +include(CTest) +enable_testing() diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt new file mode 100644 index 0000000..a450b7e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_bmi_and_interfaces CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_bmi_and_interfaces STATIC) +target_sources(export_bmi_and_interfaces + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_bmi_and_interfaces PUBLIC cxx_std_20) + +install(TARGETS export_bmi_and_interfaces + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu" + CXX_MODULES_BMI DESTINATION "lib/cxx/bmi") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-targets.cmake" + CXX_MODULES_DIRECTORY "export_bmi_and_interfaces-cxx-modules") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_bmi_and_interfaces-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_bmi_and_interfaces_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_source_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dexpected_binary_dir=${CMAKE_CURRENT_BINARY_DIR}" + "-Dexport_bmi_and_interfaces_DIR=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/importable.cxx new file mode 100644 index 0000000..e0b1872 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/importable.cxx @@ -0,0 +1,8 @@ +export module importable; + +int forwarding(); + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt new file mode 100644 index 0000000..b814b3b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +find_package(export_bmi_and_interfaces REQUIRED) + +if (NOT TARGET CXXModules::export_bmi_and_interfaces) + message(FATAL_ERROR + "Missing imported target") +endif () + +get_property(file_sets TARGET CXXModules::export_bmi_and_interfaces + PROPERTY INTERFACE_CXX_MODULE_SETS) +if (NOT file_sets STREQUAL "modules") + message(FATAL_ERROR + "Incorrect exported file sets in `CXXModules::export_bmi_and_interfaces`: `${file_sets}`") +endif () + +get_property(file_set_files TARGET CXXModules::export_bmi_and_interfaces + PROPERTY CXX_MODULE_SET_modules) +if (NOT file_set_files STREQUAL "${expected_source_dir}/importable.cxx") + message(FATAL_ERROR + "Incorrect exported file set paths in CXXModules::export_bmi_and_interfaces`: `${file_set_files}`") +endif () + +get_property(imported_modules TARGET CXXModules::export_bmi_and_interfaces + PROPERTY IMPORTED_CXX_MODULES_DEBUG) +if (NOT imported_modules MATCHES "importable=${expected_source_dir}/importable.cxx,${expected_binary_dir}/CMakeFiles/export_bmi_and_interfaces.dir(/Debug)?/importable.(gcm|pcm|ifc)") + message(FATAL_ERROR + "Incorrect exported modules in CXXModules::export_bmi_and_interfaces`: `${imported_modules}`") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt new file mode 100644 index 0000000..a5574fe --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_bmi_and_interfaces CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_bmi_and_interfaces STATIC) +target_sources(export_bmi_and_interfaces + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_bmi_and_interfaces PUBLIC cxx_std_20) + +install(TARGETS export_bmi_and_interfaces + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu" + CXX_MODULES_BMI DESTINATION "lib/cxx/bmi") +install(EXPORT CXXModules + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/export_bmi_and_interfaces" + FILE "export_bmi_and_interfaces-targets.cmake" + CXX_MODULES_DIRECTORY "export_bmi_and_interfaces-cxx-modules") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_bmi_and_interfaces-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-config.cmake" + DESTINATION "lib/cmake/export_bmi_and_interfaces") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_bmi_and_interfaces_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_source_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dexpected_binary_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/bmi" + "-Dexport_bmi_and_interfaces_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_bmi_and_interfaces" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/importable.cxx new file mode 100644 index 0000000..e0b1872 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/importable.cxx @@ -0,0 +1,8 @@ +export module importable; + +int forwarding(); + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt new file mode 100644 index 0000000..db0484d --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +find_package(export_bmi_and_interfaces REQUIRED) + +if (NOT TARGET CXXModules::export_bmi_and_interfaces) + message(FATAL_ERROR + "Missing imported target") +endif () + +get_property(file_sets TARGET CXXModules::export_bmi_and_interfaces + PROPERTY INTERFACE_CXX_MODULE_SETS) +if (NOT file_sets STREQUAL "modules") + message(FATAL_ERROR + "Incorrect exported file sets in `CXXModules::export_bmi_and_interfaces`: `${file_sets}`") +endif () + +get_property(file_set_files TARGET CXXModules::export_bmi_and_interfaces + PROPERTY CXX_MODULE_SET_modules) +if (NOT file_set_files STREQUAL "${expected_source_dir}/importable.cxx") + message(FATAL_ERROR + "Incorrect exported file set paths in CXXModules::export_bmi_and_interfaces`: `${file_set_files}`") +endif () + +get_property(imported_modules TARGET CXXModules::export_bmi_and_interfaces + PROPERTY IMPORTED_CXX_MODULES_DEBUG) +if (NOT imported_modules MATCHES "importable=${expected_source_dir}/importable.cxx,${expected_binary_dir}/importable.(gcm|pcm|ifc)") + message(FATAL_ERROR + "Incorrect exported modules in CXXModules::export_bmi_and_interfaces`: `${imported_modules}`") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt new file mode 100644 index 0000000..80ddaf8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_interfaces CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_interfaces STATIC) +target_sources(export_interfaces + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_interfaces PUBLIC cxx_std_20) + +install(TARGETS export_interfaces + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_interfaces-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_interfaces_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dexport_interfaces_DIR=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-build/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-build/importable.cxx new file mode 100644 index 0000000..e0b1872 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/importable.cxx @@ -0,0 +1,8 @@ +export module importable; + +int forwarding(); + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-build/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt new file mode 100644 index 0000000..6145210 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +find_package(export_interfaces REQUIRED) + +if (NOT TARGET CXXModules::export_interfaces) + message(FATAL_ERROR + "Missing imported target") +endif () + +get_property(file_sets TARGET CXXModules::export_interfaces + PROPERTY INTERFACE_CXX_MODULE_SETS) +if (NOT file_sets STREQUAL "modules") + message(FATAL_ERROR + "Incorrect exported file sets in `CXXModules::export_interfaces`: `${file_sets}`") +endif () + +get_property(file_set_files TARGET CXXModules::export_interfaces + PROPERTY CXX_MODULE_SET_modules) +if (NOT file_set_files STREQUAL "${expected_dir}/importable.cxx") + message(FATAL_ERROR + "Incorrect exported file set paths in CXXModules::export_interfaces`: `${file_set_files}`") +endif () + +get_property(imported_modules_set TARGET CXXModules::export_interfaces + PROPERTY IMPORTED_CXX_MODULES_DEBUG SET) +if (imported_modules_set) + message(FATAL_ERROR + "Unexpected C++ modules specified.") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt new file mode 100644 index 0000000..1dfb6da --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_interfaces CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_interfaces STATIC) +target_sources(export_interfaces + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_interfaces PUBLIC cxx_std_20) + +install(TARGETS export_interfaces + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +install(EXPORT CXXModules + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/export_interfaces" + FILE "export_interfaces-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_interfaces-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-config.cmake" + DESTINATION "lib/cmake/export_interfaces") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_interfaces_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dexport_interfaces_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_interfaces" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-install/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-install/importable.cxx new file mode 100644 index 0000000..e0b1872 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/importable.cxx @@ -0,0 +1,8 @@ +export module importable; + +int forwarding(); + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-install/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt new file mode 100644 index 0000000..6145210 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +find_package(export_interfaces REQUIRED) + +if (NOT TARGET CXXModules::export_interfaces) + message(FATAL_ERROR + "Missing imported target") +endif () + +get_property(file_sets TARGET CXXModules::export_interfaces + PROPERTY INTERFACE_CXX_MODULE_SETS) +if (NOT file_sets STREQUAL "modules") + message(FATAL_ERROR + "Incorrect exported file sets in `CXXModules::export_interfaces`: `${file_sets}`") +endif () + +get_property(file_set_files TARGET CXXModules::export_interfaces + PROPERTY CXX_MODULE_SET_modules) +if (NOT file_set_files STREQUAL "${expected_dir}/importable.cxx") + message(FATAL_ERROR + "Incorrect exported file set paths in CXXModules::export_interfaces`: `${file_set_files}`") +endif () + +get_property(imported_modules_set TARGET CXXModules::export_interfaces + PROPERTY IMPORTED_CXX_MODULES_DEBUG SET) +if (imported_modules_set) + message(FATAL_ERROR + "Unexpected C++ modules specified.") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt new file mode 100644 index 0000000..b9bbf34 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:12 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt new file mode 100644 index 0000000..73f7ff7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_generated CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx.in" + "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx" + COPYONLY) + +add_executable(generated) +target_sources(generated + PRIVATE + main.cxx + PRIVATE + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_BINARY_DIR}" + FILES + "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx") +target_compile_features(generated PUBLIC cxx_std_20) + +add_test(NAME generated COMMAND generated) diff --git a/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in b/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in new file mode 100644 index 0000000..a9287d7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in @@ -0,0 +1,5 @@ +export module importable; + +export int from_import() { + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/generated/main.cxx b/Tests/RunCMake/CXXModules/examples/generated/main.cxx new file mode 100644 index 0000000..feb38d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/generated/main.cxx @@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import(); +} diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt new file mode 100644 index 0000000..efaca0e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_install_bmi_and_interfaces CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(install_bmi_and_interfaces STATIC) +target_sources(install_bmi_and_interfaces + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(install_bmi_and_interfaces PUBLIC cxx_std_20) + +install(TARGETS install_bmi_and_interfaces + ARCHIVE DESTINATION "lib" + CXX_MODULES_BMI DESTINATION "lib/cxx/bmi" + FILE_SET CXX_MODULES DESTINATION "lib/cxx/miu") + +add_test(NAME check-for-bmi + COMMAND + "${CMAKE_COMMAND}" + "-Dprefix=${CMAKE_INSTALL_PREFIX}" + "-Dbmi_destination=lib/cxx/bmi" + "-Dfs_destination=lib/cxx/miu" + -P "${CMAKE_CURRENT_SOURCE_DIR}/check-for-bmi.cmake") diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/check-for-bmi.cmake b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/check-for-bmi.cmake new file mode 100644 index 0000000..a8ff1ad --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/check-for-bmi.cmake @@ -0,0 +1,7 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cxx-modules-find-bmi.cmake") + +report_dirs("${prefix}" "${bmi_destination}") +check_for_bmi("${prefix}" "${bmi_destination}" importable) + +report_dirs("${prefix}" "${fs_destination}") +check_for_interface("${prefix}" "${fs_destination}" "" importable.cxx) diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/importable.cxx b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/importable.cxx new file mode 100644 index 0000000..607680a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/importable.cxx @@ -0,0 +1,6 @@ +export module importable; + +export int from_import() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt new file mode 100644 index 0000000..4e039f9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_install_bmi CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(install_bmi STATIC) +target_sources(install_bmi + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(install_bmi PUBLIC cxx_std_20) + +install(TARGETS install_bmi + ARCHIVE DESTINATION "lib" + CXX_MODULES_BMI DESTINATION "lib/cxx/bmi") + +add_test(NAME check-for-bmi + COMMAND + "${CMAKE_COMMAND}" + "-Dprefix=${CMAKE_INSTALL_PREFIX}" + "-Ddestination=lib/cxx/bmi" + -P "${CMAKE_CURRENT_SOURCE_DIR}/check-for-bmi.cmake") diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/check-for-bmi.cmake b/Tests/RunCMake/CXXModules/examples/install-bmi/check-for-bmi.cmake new file mode 100644 index 0000000..ff84ed6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi/check-for-bmi.cmake @@ -0,0 +1,4 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cxx-modules-find-bmi.cmake") + +report_dirs("${prefix}" "${destination}") +check_for_bmi("${prefix}" "${destination}" importable) diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/importable.cxx b/Tests/RunCMake/CXXModules/examples/install-bmi/importable.cxx new file mode 100644 index 0000000..607680a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/install-bmi/importable.cxx @@ -0,0 +1,6 @@ +export module importable; + +export int from_import() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt new file mode 100644 index 0000000..4652aec --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt new file mode 100644 index 0000000..f5e9d94 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_internal_partitions CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +include(GenerateExportHeader) + +add_library(internal-partitions) +generate_export_header(internal-partitions) +target_sources(internal-partitions + PUBLIC + FILE_SET HEADERS + BASE_DIRS + "${CMAKE_CURRENT_BINARY_DIR}" + FILES + "${CMAKE_CURRENT_BINARY_DIR}/internal-partitions_export.h" + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx + partition.cxx) +target_compile_features(internal-partitions PUBLIC cxx_std_20) + +add_executable(exe) +target_link_libraries(exe PRIVATE internal-partitions) +target_sources(exe + PRIVATE + main.cxx) + +add_test(NAME exe COMMAND exe) diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx new file mode 100644 index 0000000..8be521b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx @@ -0,0 +1,9 @@ +export module importable; +import importable : internal_partition; + +#include "internal-partitions_export.h" + +export INTERNAL_PARTITIONS_EXPORT int from_import() +{ + return from_partition(); +} diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx new file mode 100644 index 0000000..feb38d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx @@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import(); +} diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx new file mode 100644 index 0000000..b15f53c --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx @@ -0,0 +1,6 @@ +module importable : internal_partition; + +int from_partition() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt new file mode 100644 index 0000000..4652aec --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt new file mode 100644 index 0000000..4652aec --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt new file mode 100644 index 0000000..27fd94f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +include(GenerateExportHeader) + +add_library(library) +generate_export_header(library) +target_sources(library + PUBLIC + FILE_SET HEADERS + BASE_DIRS + "${CMAKE_CURRENT_BINARY_DIR}" + FILES + "${CMAKE_CURRENT_BINARY_DIR}/library_export.h" + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(library PUBLIC cxx_std_20) + +add_executable(exe) +target_link_libraries(exe PRIVATE library) +target_sources(exe + PRIVATE + main.cxx) + +add_test(NAME exe COMMAND exe) diff --git a/Tests/RunCMake/CXXModules/examples/library/importable.cxx b/Tests/RunCMake/CXXModules/examples/library/importable.cxx new file mode 100644 index 0000000..72ed0df --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/library/importable.cxx @@ -0,0 +1,8 @@ +export module importable; + +#include "library_export.h" + +export LIBRARY_EXPORT int from_import() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/library/main.cxx b/Tests/RunCMake/CXXModules/examples/library/main.cxx new file mode 100644 index 0000000..feb38d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/library/main.cxx @@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import(); +} diff --git a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt new file mode 100644 index 0000000..4652aec --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt new file mode 100644 index 0000000..3a7b0d4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_partitions CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +include(GenerateExportHeader) + +add_library(partitions) +generate_export_header(partitions) +target_sources(partitions + PUBLIC + FILE_SET HEADERS + BASE_DIRS + "${CMAKE_CURRENT_BINARY_DIR}" + FILES + "${CMAKE_CURRENT_BINARY_DIR}/partitions_export.h" + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx + partition.cxx) +target_compile_features(partitions PUBLIC cxx_std_20) + +add_executable(exe) +target_link_libraries(exe PRIVATE partitions) +target_sources(exe + PRIVATE + main.cxx) + +add_test(NAME exe COMMAND exe) diff --git a/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx new file mode 100644 index 0000000..1e81aaf --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx @@ -0,0 +1,9 @@ +export module importable; +export import importable : partition; + +#include "partitions_export.h" + +export PARTITIONS_EXPORT int from_import() +{ + return from_partition(); +} diff --git a/Tests/RunCMake/CXXModules/examples/partitions/main.cxx b/Tests/RunCMake/CXXModules/examples/partitions/main.cxx new file mode 100644 index 0000000..c5b78c9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/partitions/main.cxx @@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import() + from_partition(); +} diff --git a/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx b/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx new file mode 100644 index 0000000..a47a4fd --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx @@ -0,0 +1,8 @@ +export module importable : partition; + +#include "partitions_export.h" + +export PARTITIONS_EXPORT int from_partition() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-build-result.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-build-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/public-req-private-build-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-build-stdout.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-build-stdout.txt new file mode 100644 index 0000000..b5f1c55 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/public-req-private-build-stdout.txt @@ -0,0 +1 @@ +CMake Error: Public C\+\+ module source `.*/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx` requires the `priv` C\+\+ module which is provided by a private source diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt new file mode 100644 index 0000000..600fec4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_public_req_private CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(public_req_private) +target_sources(public_req_private + PRIVATE + FILE_SET private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + priv.cxx + PUBLIC + FILE_SET public TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + pub.cxx) +target_compile_features(public_req_private PUBLIC cxx_std_20) + +add_test(NAME cmake-version COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/priv.cxx b/Tests/RunCMake/CXXModules/examples/public-req-private/priv.cxx new file mode 100644 index 0000000..7c000b7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/public-req-private/priv.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int g() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx b/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx new file mode 100644 index 0000000..6ff2d23 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx @@ -0,0 +1,8 @@ +export module pub; + +import priv; + +export int f() +{ + return g(); +} diff --git a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support 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. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt new file mode 100644 index 0000000..442e425 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_simple CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_executable(simple) +target_sources(simple + PRIVATE + main.cxx + PRIVATE + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(simple PUBLIC cxx_std_20) + +add_test(NAME simple COMMAND simple) diff --git a/Tests/RunCMake/CXXModules/examples/simple/importable.cxx b/Tests/RunCMake/CXXModules/examples/simple/importable.cxx new file mode 100644 index 0000000..607680a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/simple/importable.cxx @@ -0,0 +1,6 @@ +export module importable; + +export int from_import() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/simple/main.cxx b/Tests/RunCMake/CXXModules/examples/simple/main.cxx new file mode 100644 index 0000000..feb38d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/simple/main.cxx @@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import(); +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json new file mode 100644 index 0000000..65f0759 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json @@ -0,0 +1,45 @@ +{ + "bmi-installation": { + "destination": "lib/cxx/modules/private/<CONFIG>", + "message-level": "MESSAGE_LAZY", + "permissions": " OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ", + "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir/install-cxx-module-bmi-<CONFIG_FORCE>.cmake" + }, + "compiler-id": "<IGNORE>", + "config": "<CONFIG>", + "cxx-modules": { + "CMakeFiles/ninja-bmi-install-private.dir/sources/module-internal-part.cxx.o": { + "destination": null, + "name": "internal_partitions", + "relative-directory": "sources", + "source": "<SOURCE_DIR>/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-bmi-install-private.dir/sources/module-part.cxx.o": { + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-bmi-install-private.dir/sources/module.cxx.o": { + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + } + }, + "dir-cur-bld": "<BINARY_DIR>", + "dir-cur-src": "<SOURCE_DIR>", + "dir-top-bld": "<BINARY_DIR>", + "dir-top-src": "<SOURCE_DIR>", + "exports": [], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir" +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json new file mode 100644 index 0000000..9c8a895 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json @@ -0,0 +1,45 @@ +{ + "bmi-installation": { + "destination": "lib/cxx/modules/<CONFIG>", + "message-level": "", + "permissions": "", + "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir/install-cxx-module-bmi-noconfig.cmake" + }, + "compiler-id": "<IGNORE>", + "config": "<CONFIG>", + "cxx-modules": { + "CMakeFiles/ninja-bmi-install-public.dir/sources/module-internal-part.cxx.o": { + "destination": "lib/cxx/internals", + "name": "internal_partitions", + "relative-directory": "sources", + "source": "<SOURCE_DIR>/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-bmi-install-public.dir/sources/module-part.cxx.o": { + "destination": "lib/cxx", + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-bmi-install-public.dir/sources/module.cxx.o": { + "destination": "lib/cxx", + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + } + }, + "dir-cur-bld": "<BINARY_DIR>", + "dir-cur-src": "<SOURCE_DIR>", + "dir-top-bld": "<BINARY_DIR>", + "dir-top-src": "<SOURCE_DIR>", + "exports": [], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir" +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json new file mode 100644 index 0000000..0545981 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json @@ -0,0 +1,73 @@ +{ + "bmi-installation": null, + "compiler-id": "<IGNORE>", + "config": "<CONFIG>", + "cxx-modules": { + "CMakeFiles/ninja-exports-private.dir/sources/module-internal-part.cxx.o": { + "destination": null, + "name": "internal_partitions", + "relative-directory": "sources", + "source": "<SOURCE_DIR>/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-exports-private.dir/sources/module-part.cxx.o": { + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-exports-private.dir/sources/module.cxx.o": { + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + } + }, + "dir-cur-bld": "<BINARY_DIR>", + "dir-cur-src": "<SOURCE_DIR>", + "dir-top-bld": "<BINARY_DIR>", + "dir-top-src": "<SOURCE_DIR>", + "exports": [ + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "lib/cmake/export1", + "export-name" : "with-private", + "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/d2e2673818fd2bd8c45c0e3ed0e38fcd", + "install" : true, + "namespace" : "export1::" + }, + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "lib/cmake/export2", + "export-name" : "with-private", + "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/28cd47cb4c96ad5cadaa3fb1b0201ae8", + "install" : true, + "namespace" : "" + }, + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "<BINARY_DIR>/lib/cmake/export1", + "export-name" : "with-private", + "export-prefix" : "<BINARY_DIR>/lib/cmake/export1", + "install" : false, + "namespace" : "export1::" + }, + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "<BINARY_DIR>/lib/cmake/export2", + "export-name" : "with-private", + "export-prefix" : "<BINARY_DIR>/lib/cmake/export2", + "install" : false, + "namespace" : "" + } + ], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-private.dir" +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json new file mode 100644 index 0000000..adc3ae3 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json @@ -0,0 +1,73 @@ +{ + "bmi-installation": null, + "compiler-id": "<IGNORE>", + "config": "<CONFIG>", + "cxx-modules": { + "CMakeFiles/ninja-exports-public.dir/sources/module-internal-part.cxx.o": { + "destination": "lib/cxx/internals", + "name": "internal_partitions", + "relative-directory": "sources", + "source": "<SOURCE_DIR>/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-exports-public.dir/sources/module-part.cxx.o": { + "destination": "lib/cxx", + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-exports-public.dir/sources/module.cxx.o": { + "destination": "lib/cxx", + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + } + }, + "dir-cur-bld": "<BINARY_DIR>", + "dir-cur-src": "<SOURCE_DIR>", + "dir-top-bld": "<BINARY_DIR>", + "dir-top-src": "<SOURCE_DIR>", + "exports": [ + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "lib/cmake/export1", + "export-name" : "with-public", + "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/d2e2673818fd2bd8c45c0e3ed0e38fcd", + "install" : true, + "namespace" : "export1::" + }, + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "lib/cmake/export2", + "export-name" : "with-public", + "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/28cd47cb4c96ad5cadaa3fb1b0201ae8", + "install" : true, + "namespace" : "" + }, + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "<BINARY_DIR>/lib/cmake/export1", + "export-name" : "with-public", + "export-prefix" : "<BINARY_DIR>/lib/cmake/export1", + "install" : false, + "namespace" : "export1::" + }, + { + "cxx-module-info-dir" : "cxx-modules", + "destination" : "<BINARY_DIR>/lib/cmake/export2", + "export-name" : "with-public", + "export-prefix" : "<BINARY_DIR>/lib/cmake/export2", + "install" : false, + "namespace" : "" + } + ], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-public.dir" +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json new file mode 100644 index 0000000..9ba6568 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json @@ -0,0 +1,40 @@ +{ + "bmi-installation": null, + "compiler-id": "<IGNORE>", + "config": "<CONFIG>", + "cxx-modules": { + "CMakeFiles/ninja-file-sets-private.dir/sources/module-internal-part.cxx.o": { + "destination": null, + "name": "internal_partitions", + "relative-directory": "sources", + "source": "<SOURCE_DIR>/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-file-sets-private.dir/sources/module-part.cxx.o": { + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-file-sets-private.dir/sources/module.cxx.o": { + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + } + }, + "dir-cur-bld": "<BINARY_DIR>", + "dir-cur-src": "<SOURCE_DIR>", + "dir-top-bld": "<BINARY_DIR>", + "dir-top-src": "<SOURCE_DIR>", + "exports": [], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-private.dir" +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json new file mode 100644 index 0000000..46e2cbf --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json @@ -0,0 +1,40 @@ +{ + "bmi-installation": null, + "compiler-id": "<IGNORE>", + "config": "<CONFIG>", + "cxx-modules": { + "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-internal-part.cxx.o": { + "destination": "lib/cxx/internals", + "name": "internal_partitions", + "relative-directory": "sources", + "source": "<SOURCE_DIR>/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-part.cxx.o": { + "destination": "lib/cxx", + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module.cxx.o": { + "destination": "lib/cxx", + "name": "modules", + "relative-directory": "", + "source": "<SOURCE_DIR>/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + } + }, + "dir-cur-bld": "<BINARY_DIR>", + "dir-cur-src": "<SOURCE_DIR>", + "dir-top-bld": "<BINARY_DIR>", + "dir-top-src": "<SOURCE_DIR>", + "exports": [], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-public.dir" +} diff --git a/Tests/RunCMake/CXXModules/sources/c-anchor.c b/Tests/RunCMake/CXXModules/sources/c-anchor.c new file mode 100644 index 0000000..c782188 --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/c-anchor.c @@ -0,0 +1,4 @@ +int c_anchor() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx b/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx new file mode 100644 index 0000000..9c94ec1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx @@ -0,0 +1,4 @@ +int cxx_anchor() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/sources/module-header.h b/Tests/RunCMake/CXXModules/sources/module-header.h new file mode 100644 index 0000000..982617e --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-header.h @@ -0,0 +1,9 @@ +#ifndef module_header_h +#define module_header_h + +inline int h() +{ + return 0; +} + +#endif diff --git a/Tests/RunCMake/CXXModules/sources/module-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-impl.cxx new file mode 100644 index 0000000..4718999 --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-impl.cxx @@ -0,0 +1,6 @@ +module M; + +int f() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx new file mode 100644 index 0000000..be77b0d --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx @@ -0,0 +1,11 @@ +#ifdef _MSC_VER +// Only MSVC supports this pattern. +module M : internal_part; +#else +module M; +#endif + +int i() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx b/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx new file mode 100644 index 0000000..fa82afb --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx @@ -0,0 +1,3 @@ +module M : internal_part; + +int i(); diff --git a/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx new file mode 100644 index 0000000..46d5d9f --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx @@ -0,0 +1,13 @@ +#ifdef _MSC_VER +// Only MSVC supports this pattern. +module M : part; +#else +module M; +#endif + +import M : internal_part; + +int p() +{ + return i(); +} diff --git a/Tests/RunCMake/CXXModules/sources/module-part.cxx b/Tests/RunCMake/CXXModules/sources/module-part.cxx new file mode 100644 index 0000000..137c16f --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-part.cxx @@ -0,0 +1,3 @@ +export module M : part; + +int p(); diff --git a/Tests/RunCMake/CXXModules/sources/module-use.cxx b/Tests/RunCMake/CXXModules/sources/module-use.cxx new file mode 100644 index 0000000..2d060cd --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module-use.cxx @@ -0,0 +1,6 @@ +import M; + +int main(int argc, char* argv[]) +{ + return f() + p(); +} diff --git a/Tests/RunCMake/CXXModules/sources/module.cxx b/Tests/RunCMake/CXXModules/sources/module.cxx new file mode 100644 index 0000000..a631354 --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/module.cxx @@ -0,0 +1,5 @@ +export module M; +export import M : part; +import M : internal_part; + +int f(); diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index c8234ec..80a7c69 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -940,8 +940,8 @@ set(CMAKE_DEPENDS_CHECK_C set(CMAKE_RELATIVE_PATH_TOP_SOURCE \"${RunCMake_TEST_SOURCE_DIR}\") set(CMAKE_RELATIVE_PATH_TOP_BINARY \"${RunCMake_TEST_BINARY_DIR}\") ") - run_cmake_command(cmake_depends ${CMAKE_COMMAND} -E cmake_depends - "Unix Makefiles" + run_cmake_command(cmake_depends ${CMAKE_COMMAND} -E env VERBOSE=1 + ${CMAKE_COMMAND} -E cmake_depends "Unix Makefiles" ${RunCMake_TEST_SOURCE_DIR} ${RunCMake_TEST_SOURCE_DIR} ${RunCMake_TEST_BINARY_DIR} ${RunCMake_TEST_BINARY_DIR} ${RunCMake_TEST_BINARY_DIR}/CMakeFiles/DepTarget.dir/DependInfo.cmake diff --git a/Tests/RunCMake/CommandLine/cmake_depends-stdout.txt b/Tests/RunCMake/CommandLine/cmake_depends-stdout.txt index 8fe092b..cf972a8 100644 --- a/Tests/RunCMake/CommandLine/cmake_depends-stdout.txt +++ b/Tests/RunCMake/CommandLine/cmake_depends-stdout.txt @@ -1 +1 @@ -^Scanning dependencies of target DepTarget$ +Scanning dependencies of target DepTarget$ diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index d5f596e..01a004c 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -213,6 +213,16 @@ def check_directory(c): assert is_int(at["index"]) assert c["targets"][at["index"]]["name"] == et["index"] + if e.get("cxxModuleBmiTarget", None) is not None: + expected_keys.append("cxxModuleBmiTarget") + et = e["cxxModuleBmiTarget"] + at = a["cxxModuleBmiTarget"] + assert is_dict(at) + assert sorted(at.keys()) == ["id", "index"] + assert matches(at["id"], et["id"]) + assert is_int(at["index"]) + assert c["targets"][at["index"]]["name"] == et["index"] + if e["backtrace"] is not None: expected_keys.append("backtrace") check_backtrace(d, a["backtrace"], e["backtrace"]) diff --git a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake index 3908f42..dad0dd3 100644 --- a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake +++ b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake @@ -13,6 +13,9 @@ run_cmake(zip) # Extracting only selected files or directories run_cmake(zip-filtered) +run_cmake(create-missing-args) +run_cmake(extract-missing-args) + run_cmake(unsupported-format) run_cmake(zip-with-bad-compression) run_cmake(7zip-with-bad-compression) diff --git a/Tests/RunCMake/File_Archive/create-missing-args-result.txt b/Tests/RunCMake/File_Archive/create-missing-args-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/File_Archive/create-missing-args-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt new file mode 100644 index 0000000..ecfe401 --- /dev/null +++ b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt @@ -0,0 +1,9 @@ +^CMake Error at create-missing-args.cmake:[0-9]+ \(file\): + file Keywords missing values: + + OUTPUT + FORMAT + COMPRESSION + COMPRESSION_LEVEL +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/File_Archive/create-missing-args.cmake b/Tests/RunCMake/File_Archive/create-missing-args.cmake new file mode 100644 index 0000000..a0c84d2 --- /dev/null +++ b/Tests/RunCMake/File_Archive/create-missing-args.cmake @@ -0,0 +1,8 @@ +file(ARCHIVE_CREATE + OUTPUT # missing output path + FORMAT # missing output format + COMPRESSION # missing compression type + COMPRESSION_LEVEL # missing compression level + MTIME # missing modification time + PATHS # no paths + ) diff --git a/Tests/RunCMake/File_Archive/extract-missing-args-result.txt b/Tests/RunCMake/File_Archive/extract-missing-args-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/File_Archive/extract-missing-args-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt new file mode 100644 index 0000000..96c779f --- /dev/null +++ b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at extract-missing-args.cmake:[0-9]+ \(file\): + file Keywords missing values: + + INPUT + DESTINATION +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/File_Archive/extract-missing-args.cmake b/Tests/RunCMake/File_Archive/extract-missing-args.cmake new file mode 100644 index 0000000..21c5d99 --- /dev/null +++ b/Tests/RunCMake/File_Archive/extract-missing-args.cmake @@ -0,0 +1,5 @@ +file(ARCHIVE_EXTRACT + INPUT # missing input + DESTINATION # missing destination + PATTERNS # no patterns + ) diff --git a/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt b/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt index a6ea314..dd6a2e7 100644 --- a/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt +++ b/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt @@ -1,4 +1,6 @@ CMake Error at BadArgContent.cmake:[0-9]+ \(file\): - file CONFIGURE CONTENT option needs a value. + file CONFIGURE keywords missing values: + + CONTENT Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt b/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt index b5a924c..e1bf7b4 100644 --- a/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt +++ b/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt @@ -1,4 +1,6 @@ CMake Error at BadArgOutput.cmake:[0-9]+ \(file\): - file CONFIGURE OUTPUT option needs a value. + file CONFIGURE keywords missing values: + + OUTPUT Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/File_Configure/NoArgContent-result.txt b/Tests/RunCMake/File_Configure/NoArgContent-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/File_Configure/NoArgContent-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/File_Configure/NoArgContent-stderr.txt b/Tests/RunCMake/File_Configure/NoArgContent-stderr.txt new file mode 100644 index 0000000..2e8dd9a --- /dev/null +++ b/Tests/RunCMake/File_Configure/NoArgContent-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at NoArgContent.cmake:[0-9]+ \(file\): + file CONFIGURE CONTENT option is mandatory. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/File_Configure/NoArgContent.cmake b/Tests/RunCMake/File_Configure/NoArgContent.cmake new file mode 100644 index 0000000..cf52c46 --- /dev/null +++ b/Tests/RunCMake/File_Configure/NoArgContent.cmake @@ -0,0 +1 @@ +file(CONFIGURE OUTPUT "") diff --git a/Tests/RunCMake/File_Configure/NoArgOutput-result.txt b/Tests/RunCMake/File_Configure/NoArgOutput-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/File_Configure/NoArgOutput-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/File_Configure/NoArgOutput-stderr.txt b/Tests/RunCMake/File_Configure/NoArgOutput-stderr.txt new file mode 100644 index 0000000..53de48b --- /dev/null +++ b/Tests/RunCMake/File_Configure/NoArgOutput-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at NoArgOutput.cmake:[0-9]+ \(file\): + file CONFIGURE OUTPUT option is mandatory. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/File_Configure/NoArgOutput.cmake b/Tests/RunCMake/File_Configure/NoArgOutput.cmake new file mode 100644 index 0000000..77e9cdc --- /dev/null +++ b/Tests/RunCMake/File_Configure/NoArgOutput.cmake @@ -0,0 +1 @@ +file(CONFIGURE CONTENT "") diff --git a/Tests/RunCMake/File_Configure/RunCMakeTest.cmake b/Tests/RunCMake/File_Configure/RunCMakeTest.cmake index 5022985..008ce67 100644 --- a/Tests/RunCMake/File_Configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/File_Configure/RunCMakeTest.cmake @@ -9,6 +9,8 @@ run_cmake(DirOutput) run_cmake(NewLineStyle-NoArg) run_cmake(NewLineStyle-ValidArg) run_cmake(NewLineStyle-WrongArg) +run_cmake(NoArgOutput) +run_cmake(NoArgContent) run_cmake(SubDir) run_cmake(AtOnly) run_cmake(EscapeQuotes) diff --git a/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt b/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt index e823b25..4278bce 100644 --- a/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt +++ b/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt @@ -1,4 +1,6 @@ CMake Error at EmptyCondition1.cmake:2 \(file\): - file Incorrect arguments to GENERATE subcommand. + file GENERATE keywords missing values: + + CONDITION Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/File_Generate/InputAndContent-check.cmake b/Tests/RunCMake/File_Generate/InputAndContent-check.cmake new file mode 100644 index 0000000..5c9b803 --- /dev/null +++ b/Tests/RunCMake/File_Generate/InputAndContent-check.cmake @@ -0,0 +1,8 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/output-INPUT.txt" input) +if(NOT input MATCHES "INPUT file") + string(APPEND RunCMake_TEST_FAILED "INPUT incorrectly overridden by CONTENT") +endif() +file(READ "${RunCMake_TEST_BINARY_DIR}/output-CONTENT.txt" content) +if(NOT content MATCHES "CONTENT argument") + string(APPEND RunCMake_TEST_FAILED "CONTENT incorrectly overridden by INPUT") +endif() diff --git a/Tests/RunCMake/File_Generate/InputAndContent-input.txt b/Tests/RunCMake/File_Generate/InputAndContent-input.txt new file mode 100644 index 0000000..73f162b --- /dev/null +++ b/Tests/RunCMake/File_Generate/InputAndContent-input.txt @@ -0,0 +1 @@ +INPUT file diff --git a/Tests/RunCMake/File_Generate/InputAndContent.cmake b/Tests/RunCMake/File_Generate/InputAndContent.cmake new file mode 100644 index 0000000..9c3977a --- /dev/null +++ b/Tests/RunCMake/File_Generate/InputAndContent.cmake @@ -0,0 +1,10 @@ +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-INPUT.txt" + INPUT "${CMAKE_CURRENT_SOURCE_DIR}/InputAndContent-input.txt" + CONTENT "CONTENT argument" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-CONTENT.txt" + CONTENT "CONTENT argument" + INPUT "${CMAKE_CURRENT_SOURCE_DIR}/InputAndContent-input.txt" +) diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt index bc71f2f..4059bf8 100644 --- a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt +++ b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt @@ -1,4 +1,6 @@ CMake Error at NewLineStyle-NoArg.cmake:[0-9]+ \(file\): - file Incorrect arguments to GENERATE subcommand. + file GENERATE keywords missing values: + + NEWLINE_STYLE Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/File_Generate/RunCMakeTest.cmake b/Tests/RunCMake/File_Generate/RunCMakeTest.cmake index be3bf04..5a670ae 100644 --- a/Tests/RunCMake/File_Generate/RunCMakeTest.cmake +++ b/Tests/RunCMake/File_Generate/RunCMakeTest.cmake @@ -17,6 +17,7 @@ run_cmake(EmptyCondition2) run_cmake(BadCondition) run_cmake(DebugEvaluate) run_cmake(GenerateSource) +run_cmake(InputAndContent) run_cmake(OutputNameMatchesSources) run_cmake(OutputNameMatchesObjects) run_cmake(OutputNameMatchesOtherSources) diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake index f479dcf..661ae3f 100644 --- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake @@ -24,6 +24,27 @@ endif() # We need a real pkg-config to run the test for get_variable. find_package(PkgConfig) if (PKG_CONFIG_FOUND) + string(FIND "${CMAKE_CURRENT_BINARY_DIR}" " " IS_SPACES_IN_PATH) + if(IS_SPACES_IN_PATH GREATER -1) + string(REPLACE " " "\\ " ESCAPED_ROOT "${CMAKE_CURRENT_BINARY_DIR}") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_spaces.pc" " +libdir=${ESCAPED_ROOT} +Name: test_spaces.pc +Version: 0.0 +Description: test spaces +Libs: -L\${libdir} +") + set(PKG_CONFIG_PATH_SAVED "$ENV{PKG_CONFIG_PATH}") + set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_BINARY_DIR}") + execute_process(COMMAND "${PKG_CONFIG_EXECUTABLE}" --libs test_spaces + ERROR_QUIET COMMAND_ERROR_IS_FATAL ANY + OUTPUT_VARIABLE test_spaces_LIBS) + set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH_SAVED}") + string(STRIP "${test_spaces_LIBS}" test_spaces_LIBS_STRIPPED) + if(NOT "${test_spaces_LIBS_STRIPPED}" STREQUAL "-L${ESCAPED_ROOT}") + set(PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH TRUE) + endif() + endif() run_cmake(FindPkgConfig_GET_VARIABLE) run_cmake(FindPkgConfig_GET_VARIABLE_PREFIX_PATH) run_cmake(FindPkgConfig_GET_VARIABLE_PKGCONFIG_PATH) @@ -32,5 +53,7 @@ if (PKG_CONFIG_FOUND) run_cmake(FindPkgConfig_VERSION_OPERATORS) run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME) run_cmake(FindPkgConfig_empty_target) - run_cmake(FindPkgConfig_LIBRARY_PATH) + if(NOT PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH) + run_cmake(FindPkgConfig_LIBRARY_PATH) + endif() endif () diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake new file mode 100644 index 0000000..a1ae6ac --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake @@ -0,0 +1,28 @@ +set(expected_compile_commands +[==[^\[ +{ + "directory": "[^ +]*(/Tests/RunCMake/NinjaMultiConfig/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\CompileCommands-build)", + "command": "[^ +]*Debug[^ +]*", + "file": "[^ +]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)" +}, +{ + "directory": "[^ +]*(/Tests/RunCMake/NinjaMultiConfig/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\CompileCommands-build)", + "command": "[^ +]*Release[^ +]*", + "file": "[^ +]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)" +} +]$]==]) + +file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" actual_compile_commands) +if(NOT actual_compile_commands MATCHES "${expected_compile_commands}") + string(REPLACE "\n" "\n " expected_compile_commands_formatted "${expected_compile_commands}") + string(REPLACE "\n" "\n " actual_compile_commands_formatted "${actual_compile_commands}") + string(APPEND RunCMake_TEST_FAILED "Expected compile_commands.json to match:\n ${expected_compile_commands_formatted}\nActual compile_commands.json:\n ${actual_compile_commands_formatted}\n") +endif() diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake new file mode 100644 index 0000000..fc44d5a --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake @@ -0,0 +1,3 @@ +enable_language(C) + +add_executable(exe main.c) diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index 738bc6c..c040e8f 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake @@ -453,6 +453,11 @@ run_cmake_command(NoUnusedVariables ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR} "-DCMAKE_DEFAULT_BUILD_TYPE=Debug" "-DCMAKE_DEFAULT_CONFIGS=all" ) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release;-DCMAKE_CROSS_CONFIGS=all;-DCMAKE_EXPORT_COMPILE_COMMANDS=ON") +run_cmake(CompileCommands) +unset(RunCMake_TEST_OPTIONS) # CudaSimple uses separable compilation, which is currently only supported on NVCC. if(CMake_TEST_CUDA) diff --git a/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake b/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake index bcdc101..1701a36 100644 --- a/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake +++ b/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake @@ -5,6 +5,7 @@ if(NOT EXISTS "${vcProjectFile}") endif() set(found_iface_h 0) +set(found_int_dir 0) file(STRINGS "${vcProjectFile}" lines) foreach(line IN LISTS lines) if(line MATCHES "<([A-Za-z0-9_]+) +Include=.*iface\\.h") @@ -19,7 +20,15 @@ foreach(line IN LISTS lines) endif() set(found_iface_h 1) endif() + if(line MATCHES "^ *<IntDir [^<>]+>[^<>]+</IntDir>") + set(found_int_dir 1) + endif() endforeach() if(NOT found_iface_h) set(RunCMake_TEST_FAILED "iface.h not referenced in\n ${vcProjectFile}") + return() +endif() +if(NOT found_int_dir) + set(RunCMake_TEST_FAILED "No references to IntDir in\n ${vcProjectFile}") + return() endif() diff --git a/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake b/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake index 9c9074e..22e2bb3 100644 --- a/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake +++ b/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake @@ -4,6 +4,7 @@ include(RunCMake) run_cmake(VsDotnetSdkCustomCommandsTarget) run_cmake(VsDotnetSdkCustomCommandsSource) run_cmake(VsDotnetSdkStartupObject) +run_cmake(VsDotnetSdkDefines) run_cmake(DotnetSdkVariables) function(run_VsDotnetSdk) diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines-check.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines-check.cmake new file mode 100644 index 0000000..eaeba24 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines-check.cmake @@ -0,0 +1,64 @@ +# +# Check C# VS project for required elements. +# +set(csProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.csproj") +if(NOT EXISTS "${csProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${csProjectFile} does not exist.") + return() +endif() + + +set(inDebug FALSE) +set(inRelease FALSE) +set(debugOK FALSE) +set(releaseOK FALSE) + + +file(STRINGS "${csProjectFile}" lines) +foreach(line IN LISTS lines) + #message(STATUS ${line}) + if(line MATCHES "^ *<PropertyGroup .*Debug.*") + set(inDebug TRUE) + elseif(line MATCHES "^ *<PropertyGroup .*Release.*") + set(inRelease TRUE) + elseif(line MATCHES "^ *</PropertyGroup> *$") + set(inRelease FALSE) + set(inDebug FALSE) + elseif(inDebug AND + (line MATCHES "^ *<DefineConstants>.*MY_FOO_DEFINE.*</DefineConstants> *$") AND + (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_DEBUG.*</DefineConstants> *$") AND + (NOT (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_RELEASE.*</DefineConstants> *$")) AND + (NOT (line MATCHES "^ *<DefineConstants>.*MY_BAR_ASSIGNMENT=bar.*</DefineConstants> *$")) + ) + set(debugOK TRUE) + elseif(inRelease AND + (line MATCHES "^ *<DefineConstants>.*MY_FOO_DEFINE.*</DefineConstants> *$") AND + (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_RELEASE.*</DefineConstants> *$") AND + (NOT (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_DEBUG.*</DefineConstants> *$")) AND + (NOT (line MATCHES "^ *<DefineConstants>.*MY_BAR_ASSIGNMENT=bar.*</DefineConstants> *$")) + ) + set(releaseOK TRUE) + endif() +endforeach() + +function(print_csprojfile) + file(STRINGS "${csProjectFile}" lines) + foreach(line IN LISTS lines) + message(STATUS ${line}) + endforeach() +endfunction() + + +if(NOT debugOK) + message(STATUS "Failed to set Debug configuration defines correctly.") + set(RunCMake_TEST_FAILED "Failed to set Debug configuration defines correctly.") + print_csprojfile() + return() +endif() + +if(NOT releaseOK) + message(STATUS "Failed to set Release configuration defines correctly.") + set(RunCMake_TEST_FAILED "Failed to set Release configuration defines correctly.") + print_csprojfile() + return() +endif() diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines.cmake new file mode 100644 index 0000000..d89f19b --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines.cmake @@ -0,0 +1,19 @@ +enable_language(CSharp) +if(NOT CMAKE_CSharp_COMPILER) + return() +endif() + +set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") +set(CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION "net5.0") + +add_executable(foo csharponly.cs lib1.cs) + +# Issue 23376 +target_compile_definitions( + foo + PUBLIC + MY_FOO_DEFINE + "MY_BAR_ASSIGNMENT=bar" + $<$<CONFIG:Debug>:DEFINE_ONLY_FOR_DEBUG> + $<$<CONFIG:Release>:DEFINE_ONLY_FOR_RELEASE> +) diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake index be44ecd..efd70cf 100644 --- a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake @@ -43,6 +43,8 @@ check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED" "enableGPUFrameCaptureMo check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_METAL" "enableGPUFrameCaptureMode=\"1\"") check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED_MIXED_CASE" "enableGPUFrameCaptureMode=\"3\"") check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_METAL_MIXED_CASE" "enableGPUFrameCaptureMode=\"1\"") +check_property("LAUNCH_MODE_AUTO" "launchStyle=\"0\"") +check_property("LAUNCH_MODE_WAIT" "launchStyle=\"1\"") check_property("EXECUTABLE" "myExecutable") check_property("ARGUMENTS" [=["--foo"]=]) diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake index 126a9fc..5edbc89 100644 --- a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake @@ -38,6 +38,8 @@ create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED ENABLE_GPU_FRA create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_METAL ENABLE_GPU_FRAME_CAPTURE_MODE Metal) create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED_MIXED_CASE ENABLE_GPU_FRAME_CAPTURE_MODE DISAbled) create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_METAL_MIXED_CASE ENABLE_GPU_FRAME_CAPTURE_MODE METal) +create_scheme_for_property(LAUNCH_MODE_AUTO LAUNCH_MODE AUTO) +create_scheme_for_property(LAUNCH_MODE_WAIT LAUNCH_MODE WAIT) create_scheme_for_property(EXECUTABLE EXECUTABLE myExecutable) create_scheme_for_property(ARGUMENTS ARGUMENTS "--foo;--bar=baz") create_scheme_for_property(ENVIRONMENT ENVIRONMENT "FOO=foo;BAR=bar") diff --git a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake index 6480b2e..03a15fc 100644 --- a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake @@ -82,3 +82,61 @@ run_cmake(defer_get_call_id_var) run_cmake(defer_missing_arg) run_cmake(defer_missing_call) run_cmake(defer_unknown_option) + +# Default log level +run_cmake_command( + get_message_log_level_none + ${CMAKE_COMMAND} + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) + +# Log level from cache +run_cmake_command( + get_message_log_level_cache + ${CMAKE_COMMAND} + -DCMAKE_MESSAGE_LOG_LEVEL=TRACE + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) + +# Log level from regular variable +run_cmake_command( + get_message_log_level_var + ${CMAKE_COMMAND} + -DNEW_LOG_LEVEL=TRACE + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) + +# Log level from command line +run_cmake_command( + get_message_log_level_cli + ${CMAKE_COMMAND} + --log-level=DEBUG + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) + +# Log level from command line, it has higher priority over a cache variable +run_cmake_command( + get_message_log_level_cli_and_cache + ${CMAKE_COMMAND} + --log-level=DEBUG + -DCMAKE_MESSAGE_LOG_LEVEL=TRACE + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) + +# Log level from command line, it has higher priority over a regular variable +run_cmake_command( + get_message_log_level_cli_and_var + ${CMAKE_COMMAND} + --log-level=DEBUG + -DNEW_LOG_LEVEL=TRACE + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) + +# Log level from variable, it has higher priority over a cache variable +run_cmake_command( + get_message_log_level_var_and_cache + ${CMAKE_COMMAND} + -DNEW_LOG_LEVEL=DEBUG + -DCMAKE_MESSAGE_LOG_LEVEL=TRACE + -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake + ) diff --git a/Tests/RunCMake/cmake_language/get_message_log_level.cmake b/Tests/RunCMake/cmake_language/get_message_log_level.cmake new file mode 100644 index 0000000..1740c1f --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level.cmake @@ -0,0 +1,5 @@ +if(NEW_LOG_LEVEL) + set(CMAKE_MESSAGE_LOG_LEVEL "${NEW_LOG_LEVEL}") +endif() +cmake_language(GET_MESSAGE_LOG_LEVEL log_level) +message(STATUS "log level is: ${log_level}") diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt new file mode 100644 index 0000000..cf1cd7b --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt @@ -0,0 +1 @@ +log level is: TRACE diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt new file mode 100644 index 0000000..4d6e1eb --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt @@ -0,0 +1 @@ +log level is: DEBUG diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt new file mode 100644 index 0000000..4d6e1eb --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt @@ -0,0 +1 @@ +log level is: DEBUG diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt new file mode 100644 index 0000000..4d6e1eb --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt @@ -0,0 +1 @@ +log level is: DEBUG diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt new file mode 100644 index 0000000..92ffd34 --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt @@ -0,0 +1 @@ +log level is: STATUS diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt new file mode 100644 index 0000000..cf1cd7b --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt @@ -0,0 +1 @@ +log level is: TRACE diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt new file mode 100644 index 0000000..4d6e1eb --- /dev/null +++ b/Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt @@ -0,0 +1 @@ +log level is: DEBUG diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index aff4735..db88956 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -47,6 +47,7 @@ run_cmake(GLOB_RECURSE) run_cmake(GLOB_RECURSE-noexp-FOLLOW_SYMLINKS) run_cmake(SIZE) run_cmake(SIZE-error-does-not-exist) +run_cmake(TIMESTAMP) run_cmake(REMOVE-empty) diff --git a/Tests/RunCMake/file/TIMESTAMP-stdout.txt b/Tests/RunCMake/file/TIMESTAMP-stdout.txt new file mode 100644 index 0000000..42be6ba --- /dev/null +++ b/Tests/RunCMake/file/TIMESTAMP-stdout.txt @@ -0,0 +1 @@ +-- '[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]Z' diff --git a/Tests/RunCMake/file/TIMESTAMP.cmake b/Tests/RunCMake/file/TIMESTAMP.cmake new file mode 100644 index 0000000..2bd2577 --- /dev/null +++ b/Tests/RunCMake/file/TIMESTAMP.cmake @@ -0,0 +1,2 @@ +file(TIMESTAMP "TIMESTAMP.cmake" output UTC) +message(STATUS "'${output}'") diff --git a/Tests/RunCMake/find_file/RunCMakeTest.cmake b/Tests/RunCMake/find_file/RunCMakeTest.cmake index 23765d4..296bb71 100644 --- a/Tests/RunCMake/find_file/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_file/RunCMakeTest.cmake @@ -7,6 +7,10 @@ run_cmake(Required) run_cmake(NO_CACHE) run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) +run_cmake(VALIDATOR-no-function) +run_cmake(VALIDATOR-undefined-function) +run_cmake(VALIDATOR-specify-macro) +run_cmake(VALIDATOR) run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=PrefixInPATH_File) diff --git a/Tests/RunCMake/find_file/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_file/VALIDATOR-no-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-no-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_file/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-no-function-stderr.txt new file mode 100644 index 0000000..4d49649 --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-no-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_file\): + find_file missing required argument for "VALIDATOR" +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_file/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_file/VALIDATOR-no-function.cmake new file mode 100644 index 0000000..4800fe9 --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-no-function.cmake @@ -0,0 +1,2 @@ + +find_file(result NAMES input.txt VALIDATOR) diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt new file mode 100644 index 0000000..7e0eda0 --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_file\): + find_file command specified for "VALIDATOR" is not a function: check. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake new file mode 100644 index 0000000..ec0ce9a --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake @@ -0,0 +1,5 @@ + +macro(CHECK result path) +endmacro() + +find_file(result NAMES input.txt VALIDATOR check) diff --git a/Tests/RunCMake/find_file/VALIDATOR-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-stderr.txt new file mode 100644 index 0000000..6c8159b --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-stderr.txt @@ -0,0 +1,3 @@ +CHECK='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h' +CHECK='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h' +CHECK='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h' diff --git a/Tests/RunCMake/find_file/VALIDATOR-stdout.txt b/Tests/RunCMake/find_file/VALIDATOR-stdout.txt new file mode 100644 index 0000000..44fa77c --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-stdout.txt @@ -0,0 +1,3 @@ +-- FILE='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- FILE='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- FILE='FILE-NOTFOUND' diff --git a/Tests/RunCMake/find_file/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_file/VALIDATOR-undefined-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-undefined-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_file/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-undefined-function-stderr.txt new file mode 100644 index 0000000..b4125e6 --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-undefined-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_file\): + find_file command specified for "VALIDATOR" is undefined: undefined. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_file/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_file/VALIDATOR-undefined-function.cmake new file mode 100644 index 0000000..c465887 --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR-undefined-function.cmake @@ -0,0 +1,2 @@ + +find_file(result NAMES input.txt VALIDATOR undefined) diff --git a/Tests/RunCMake/find_file/VALIDATOR.cmake b/Tests/RunCMake/find_file/VALIDATOR.cmake new file mode 100644 index 0000000..72dd8f9 --- /dev/null +++ b/Tests/RunCMake/find_file/VALIDATOR.cmake @@ -0,0 +1,39 @@ + +function(CHECK_DEFAULT result filename) + message("CHECK='${filename}'") +endfunction() + +function(CHECK_OK result filename) + message("CHECK='${filename}'") + set(${result} TRUE PARENT_SCOPE) +endfunction() + +function(CHECK_KO result filename) + message("CHECK='${filename}'") + set(${result} FALSE PARENT_SCOPE) +endfunction() + + +find_file(FILE + NAMES PrefixInPATH.h + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include + VALIDATOR check_default + ) +message(STATUS "FILE='${FILE}'") +unset(FILE CACHE) + +find_file(FILE + NAMES PrefixInPATH.h + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include + VALIDATOR check_ok + ) +message(STATUS "FILE='${FILE}'") +unset(FILE CACHE) + +find_file(FILE + NAMES PrefixInPATH.h + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include + VALIDATOR check_ko + ) +message(STATUS "FILE='${FILE}'") +unset(FILE CACHE) diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake index de0ee14..cc005d0 100644 --- a/Tests/RunCMake/find_library/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake @@ -13,6 +13,10 @@ run_cmake(Required) run_cmake(NO_CACHE) run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) +run_cmake(VALIDATOR-no-function) +run_cmake(VALIDATOR-undefined-function) +run_cmake(VALIDATOR-specify-macro) +run_cmake(VALIDATOR) run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp") diff --git a/Tests/RunCMake/find_library/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_library/VALIDATOR-no-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-no-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_library/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-no-function-stderr.txt new file mode 100644 index 0000000..6c04aa4 --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-no-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_library\): + find_library missing required argument for "VALIDATOR" +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_library/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_library/VALIDATOR-no-function.cmake new file mode 100644 index 0000000..516e54e --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-no-function.cmake @@ -0,0 +1,2 @@ + +find_library(result NAMES input.txt VALIDATOR) diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt new file mode 100644 index 0000000..03e7df9 --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_library\): + find_library command specified for "VALIDATOR" is not a function: check. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake new file mode 100644 index 0000000..fa90d72 --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake @@ -0,0 +1,5 @@ + +macro(CHECK result path) +endmacro() + +find_library(result NAMES input.txt VALIDATOR check) diff --git a/Tests/RunCMake/find_library/VALIDATOR-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-stderr.txt new file mode 100644 index 0000000..7211450 --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-stderr.txt @@ -0,0 +1,3 @@ +CHECK='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +CHECK='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +CHECK='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' diff --git a/Tests/RunCMake/find_library/VALIDATOR-stdout.txt b/Tests/RunCMake/find_library/VALIDATOR-stdout.txt new file mode 100644 index 0000000..bd2549b --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-stdout.txt @@ -0,0 +1,3 @@ +-- LIB='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- LIB='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- LIB='LIB-NOTFOUND' diff --git a/Tests/RunCMake/find_library/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_library/VALIDATOR-undefined-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-undefined-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_library/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-undefined-function-stderr.txt new file mode 100644 index 0000000..dc7ad9e --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-undefined-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_library\): + find_library command specified for "VALIDATOR" is undefined: undefined. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_library/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_library/VALIDATOR-undefined-function.cmake new file mode 100644 index 0000000..9ac7ee0 --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR-undefined-function.cmake @@ -0,0 +1,2 @@ + +find_library(result NAMES input.txt VALIDATOR undefined) diff --git a/Tests/RunCMake/find_library/VALIDATOR.cmake b/Tests/RunCMake/find_library/VALIDATOR.cmake new file mode 100644 index 0000000..7593941 --- /dev/null +++ b/Tests/RunCMake/find_library/VALIDATOR.cmake @@ -0,0 +1,41 @@ +list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib) +list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a) + +function(CHECK_DEFAULT result filename) + message("CHECK='${filename}'") +endfunction() + +function(CHECK_OK result filename) + message("CHECK='${filename}'") + set(${result} TRUE PARENT_SCOPE) +endfunction() + +function(CHECK_KO result filename) + message("CHECK='${filename}'") + set(${result} FALSE PARENT_SCOPE) +endfunction() + + +find_library(LIB + NAMES PrefixInPATH + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib + VALIDATOR check_default + ) +message(STATUS "LIB='${LIB}'") +unset(LIB CACHE) + +find_library(LIB + NAMES PrefixInPATH + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib + VALIDATOR check_ok + ) +message(STATUS "LIB='${LIB}'") +unset(LIB CACHE) + +find_library(LIB + NAMES PrefixInPATH + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib + VALIDATOR check_ko + ) +message(STATUS "LIB='${LIB}'") +unset(LIB CACHE) diff --git a/Tests/RunCMake/find_path/RunCMakeTest.cmake b/Tests/RunCMake/find_path/RunCMakeTest.cmake index 63cadc2..9c76f2e 100644 --- a/Tests/RunCMake/find_path/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_path/RunCMakeTest.cmake @@ -7,6 +7,10 @@ run_cmake(Required) run_cmake(NO_CACHE) run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) +run_cmake(VALIDATOR-no-function) +run_cmake(VALIDATOR-undefined-function) +run_cmake(VALIDATOR-specify-macro) +run_cmake(VALIDATOR) if(APPLE) run_cmake(FrameworksWithSubdirs) diff --git a/Tests/RunCMake/find_path/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_path/VALIDATOR-no-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-no-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_path/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-no-function-stderr.txt new file mode 100644 index 0000000..1dfd064 --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-no-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_path\): + find_path missing required argument for "VALIDATOR" +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_path/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_path/VALIDATOR-no-function.cmake new file mode 100644 index 0000000..bac2752 --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-no-function.cmake @@ -0,0 +1,2 @@ + +find_path(result NAMES input.txt VALIDATOR) diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt new file mode 100644 index 0000000..92ee17e --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_path\): + find_path command specified for "VALIDATOR" is not a function: check. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake new file mode 100644 index 0000000..62b44ed --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake @@ -0,0 +1,5 @@ + +macro(CHECK result path) +endmacro() + +find_path(result NAMES input.txt VALIDATOR check) diff --git a/Tests/RunCMake/find_path/VALIDATOR-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-stderr.txt new file mode 100644 index 0000000..851353d --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-stderr.txt @@ -0,0 +1,3 @@ +CHECK='[^']+/Tests/RunCMake/find_path/include/' +CHECK='[^']+/Tests/RunCMake/find_path/include/' +CHECK='[^']+/Tests/RunCMake/find_path/include/' diff --git a/Tests/RunCMake/find_path/VALIDATOR-stdout.txt b/Tests/RunCMake/find_path/VALIDATOR-stdout.txt new file mode 100644 index 0000000..3fce030 --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-stdout.txt @@ -0,0 +1,3 @@ +-- DIR='[^']+/Tests/RunCMake/find_path/include' +-- DIR='[^']+/Tests/RunCMake/find_path/include' +-- DIR='DIR-NOTFOUND' diff --git a/Tests/RunCMake/find_path/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_path/VALIDATOR-undefined-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-undefined-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_path/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-undefined-function-stderr.txt new file mode 100644 index 0000000..d3e0517 --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-undefined-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_path\): + find_path command specified for "VALIDATOR" is undefined: undefined. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_path/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_path/VALIDATOR-undefined-function.cmake new file mode 100644 index 0000000..3fb55c5 --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR-undefined-function.cmake @@ -0,0 +1,2 @@ + +find_path(result NAMES input.txt VALIDATOR undefined) diff --git a/Tests/RunCMake/find_path/VALIDATOR.cmake b/Tests/RunCMake/find_path/VALIDATOR.cmake new file mode 100644 index 0000000..1e01250 --- /dev/null +++ b/Tests/RunCMake/find_path/VALIDATOR.cmake @@ -0,0 +1,39 @@ + +function(CHECK_DEFAULT result filename) + message("CHECK='${filename}'") +endfunction() + +function(CHECK_OK result filename) + message("CHECK='${filename}'") + set(${result} TRUE PARENT_SCOPE) +endfunction() + +function(CHECK_KO result filename) + message("CHECK='${filename}'") + set(${result} FALSE PARENT_SCOPE) +endfunction() + + +find_path(DIR + NAMES PrefixInPATH.h + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include + VALIDATOR check_default + ) +message(STATUS "DIR='${DIR}'") +unset(DIR CACHE) + +find_path(DIR + NAMES PrefixInPATH.h + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include + VALIDATOR check_ok + ) +message(STATUS "DIR='${DIR}'") +unset(DIR CACHE) + +find_path(DIR + NAMES PrefixInPATH.h + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include + VALIDATOR check_ko + ) +message(STATUS "DIR='${DIR}'") +unset(DIR CACHE) diff --git a/Tests/RunCMake/find_program/RunCMakeTest.cmake b/Tests/RunCMake/find_program/RunCMakeTest.cmake index d0ce8fc..f8ecb8f 100644 --- a/Tests/RunCMake/find_program/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_program/RunCMakeTest.cmake @@ -9,6 +9,10 @@ run_cmake(NO_CACHE) run_cmake(IgnorePrefixPath) run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) +run_cmake(VALIDATOR-no-function) +run_cmake(VALIDATOR-undefined-function) +run_cmake(VALIDATOR-specify-macro) +run_cmake(VALIDATOR) if(CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN|MSYS)$") run_cmake(WindowsCom) diff --git a/Tests/RunCMake/find_program/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_program/VALIDATOR-no-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-no-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_program/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-no-function-stderr.txt new file mode 100644 index 0000000..b8868af --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-no-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_program\): + find_program missing required argument for "VALIDATOR" +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_program/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_program/VALIDATOR-no-function.cmake new file mode 100644 index 0000000..0d6a74f --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-no-function.cmake @@ -0,0 +1,2 @@ + +find_program(result NAMES input.txt VALIDATOR) diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt new file mode 100644 index 0000000..1ae5148 --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_program\): + find_program command specified for "VALIDATOR" is not a function: check. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake new file mode 100644 index 0000000..43bb9ec --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake @@ -0,0 +1,5 @@ + +macro(CHECK result path) +endmacro() + +find_program(result NAMES input.txt VALIDATOR check) diff --git a/Tests/RunCMake/find_program/VALIDATOR-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-stderr.txt new file mode 100644 index 0000000..6704ed1 --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-stderr.txt @@ -0,0 +1,3 @@ +CHECK='[^']+/Tests/RunCMake/find_program/A/testA' +CHECK='[^']+/Tests/RunCMake/find_program/A/testA' +CHECK='[^']+/Tests/RunCMake/find_program/A/testA' diff --git a/Tests/RunCMake/find_program/VALIDATOR-stdout.txt b/Tests/RunCMake/find_program/VALIDATOR-stdout.txt new file mode 100644 index 0000000..d434742 --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-stdout.txt @@ -0,0 +1,3 @@ +-- PROG='[^']+/Tests/RunCMake/find_program/A/testA' +-- PROG='[^']+/Tests/RunCMake/find_program/A/testA' +-- PROG='PROG-NOTFOUND' diff --git a/Tests/RunCMake/find_program/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_program/VALIDATOR-undefined-function-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-undefined-function-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_program/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-undefined-function-stderr.txt new file mode 100644 index 0000000..a89ffa0 --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-undefined-function-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_program\): + find_program command specified for "VALIDATOR" is undefined: undefined. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_program/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_program/VALIDATOR-undefined-function.cmake new file mode 100644 index 0000000..96be5fd --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR-undefined-function.cmake @@ -0,0 +1,2 @@ + +find_program(result NAMES input.txt VALIDATOR undefined) diff --git a/Tests/RunCMake/find_program/VALIDATOR.cmake b/Tests/RunCMake/find_program/VALIDATOR.cmake new file mode 100644 index 0000000..8be2b39 --- /dev/null +++ b/Tests/RunCMake/find_program/VALIDATOR.cmake @@ -0,0 +1,39 @@ + +function(CHECK_DEFAULT result filename) + message("CHECK='${filename}'") +endfunction() + +function(CHECK_OK result filename) + message("CHECK='${filename}'") + set(${result} TRUE PARENT_SCOPE) +endfunction() + +function(CHECK_KO result filename) + message("CHECK='${filename}'") + set(${result} FALSE PARENT_SCOPE) +endfunction() + + +find_program(PROG + NAMES testA + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A + VALIDATOR check_default + ) +message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) + +find_program(PROG + NAMES testA + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A + VALIDATOR check_ok + ) +message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) + +find_program(PROG + NAMES testA + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A + VALIDATOR check_ko + ) +message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) diff --git a/Tests/RunCMake/project/LanguagesDuplicate-check.cmake b/Tests/RunCMake/project/LanguagesDuplicate-check.cmake new file mode 100644 index 0000000..31c7da3 --- /dev/null +++ b/Tests/RunCMake/project/LanguagesDuplicate-check.cmake @@ -0,0 +1,10 @@ +if(NOT actual_stderr MATCHES "The following language has been specified multiple times: C\n") + set(RunCMake_TEST_FAILED "'LANGUAGES C C C' should report only 'C' and only once.") +endif() + +if(NOT actual_stderr MATCHES "The following languages have been specified multiple times: C, CXX\n") + if(RunCMake_TEST_FAILED) + string(APPEND RunCMake_TEST_FAILED "\n") + endif() + string(APPEND RunCMake_TEST_FAILED "'LANGUAGES C C CXX CXX' should report 'C' and 'CXX'.") +endif() diff --git a/Tests/RunCMake/project/LanguagesDuplicate.cmake b/Tests/RunCMake/project/LanguagesDuplicate.cmake new file mode 100644 index 0000000..97a79d0 --- /dev/null +++ b/Tests/RunCMake/project/LanguagesDuplicate.cmake @@ -0,0 +1,11 @@ +cmake_policy(SET CMP0057 NEW) + +project(ProjectA C C C) +project(ProjectB C C CXX CXX) + +get_property(langs GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(lang C CXX) + if(NOT lang IN_LIST langs) + message(FATAL_ERROR "Expected language '${lang}' to be enabled.") + endif() +endforeach() diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index 945d9ed..6d9f52f 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake @@ -12,6 +12,11 @@ run_cmake_with_options(CodeInjection if(CMake_TEST_RESOURCES) run_cmake(ExplicitRC) endif() + +set(RunCMake_DEFAULT_stderr .) +run_cmake(LanguagesDuplicate) +unset(RunCMake_DEFAULT_stderr) + run_cmake(LanguagesImplicit) run_cmake(LanguagesEmpty) run_cmake(LanguagesNONE) diff --git a/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt b/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt new file mode 100644 index 0000000..f04e43f --- /dev/null +++ b/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0101-BEFORE_keyword.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0101 will be removed from a future version + of CMake. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt new file mode 100644 index 0000000..042d67d --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt @@ -0,0 +1,12 @@ +^CMake Warning \(dev\) at FileSetDefaultWrongTypeExperimental.cmake:6 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error at FileSetDefaultWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\): + target_sources File set TYPE may only be "HEADERS", "CXX_MODULES", or + "CXX_MODULE_HEADER_UNITS" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake new file mode 100644 index 0000000..5ade637 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake @@ -0,0 +1,6 @@ +enable_language(C) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +add_library(lib1 STATIC empty.c) +target_sources(lib1 PRIVATE FILE_SET UNKNOWN) diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt new file mode 100644 index 0000000..a1b784f --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt @@ -0,0 +1,12 @@ +^CMake Warning \(dev\) at FileSetWrongTypeExperimental.cmake:6 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Error at FileSetWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\): + target_sources File set TYPE may only be "HEADERS", "CXX_MODULES", or + "CXX_MODULE_HEADER_UNITS" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake new file mode 100644 index 0000000..332441c --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake @@ -0,0 +1,6 @@ +enable_language(C) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") + +add_library(lib1 STATIC empty.c) +target_sources(lib1 PRIVATE FILE_SET a TYPE UNKNOWN) diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake index 6a3c7b9..7c67c3f 100644 --- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake @@ -26,6 +26,8 @@ run_cmake(FileSetProperties) run_cmake(FileSetNoType) run_cmake(FileSetWrongType) run_cmake(FileSetDefaultWrongType) +run_cmake(FileSetWrongTypeExperimental) +run_cmake(FileSetDefaultWrongTypeExperimental) run_cmake(FileSetChangeScope) run_cmake(FileSetChangeType) run_cmake(FileSetWrongBaseDirs) diff --git a/Tests/RunCMake/try_compile/CxxStandard-stderr.txt b/Tests/RunCMake/try_compile/CxxStandard-stderr.txt index ec7245f..cee1b44 100644 --- a/Tests/RunCMake/try_compile/CxxStandard-stderr.txt +++ b/Tests/RunCMake/try_compile/CxxStandard-stderr.txt @@ -1,6 +1,17 @@ -^CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\): +^(CMake Error in .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt: + The CXX_STANDARD property on target "cmTC_[0-9a-f]*" contained an invalid + value: "3". + + +)?CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\): CXX_STANDARD is set to invalid value '3' -+ + +( +CMake Error in .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt: + The CXX_STANDARD property on target "cmTC_[0-9a-f]*" contained an invalid + value: "3". + +)? CMake Error at CxxStandard.cmake:[0-9]+ \(try_compile\): Failed to generate test project build system. Call Stack \(most recent call first\): diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index 01c2222..e92e544 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt @@ -25,6 +25,7 @@ endif() set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift) add_executable(SwiftOnly main.swift) +target_compile_definitions(SwiftOnly PRIVATE SWIFTONLY) add_library(L L.swift) diff --git a/Tests/SwiftOnly/main.swift b/Tests/SwiftOnly/main.swift index 28560d0..a3f1a2c 100644 --- a/Tests/SwiftOnly/main.swift +++ b/Tests/SwiftOnly/main.swift @@ -1 +1,7 @@ dump("SwiftOnly") + +#if SWIFTONLY +dump("SWIFTONLY defined") +#else +fatalError("SWIFTONLY NOT defined") +#endif diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index b712c27..bc16350 100644 --- a/Utilities/Doxygen/CMakeLists.txt +++ b/Utilities/Doxygen/CMakeLists.txt @@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeDeveloperReference_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...3.22 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.23 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index fbce1c8..58c0573 100644 --- a/Utilities/Sphinx/CMakeLists.txt +++ b/Utilities/Sphinx/CMakeLists.txt @@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeHelp_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...3.22 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.23 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 635e9c1..5d1203b 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -217,8 +217,10 @@ * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30) * LibreSSL: since 2.5.3 (April 12, 2017) */ -#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \ - defined(OPENSSL_IS_BORINGSSL) +#if ((OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20503000L)) || \ + defined(OPENSSL_IS_BORINGSSL) #define HAVE_SSL_CTX_SET_EC_CURVES #endif @@ -2282,6 +2284,14 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, # define HAS_NPN 1 #endif +/* Check for OpenSSL 1.1.0 which has set_{min,max}_proto_version(). */ +#undef HAS_MODERN_SET_PROTO_VER +#if OPENSSL_VERSION_NUMBER >= 0x10100000L \ + && !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20600000L) +# define HAS_MODERN_SET_PROTO_VER 1 +#endif + #ifdef HAS_NPN /* @@ -2340,7 +2350,7 @@ select_next_proto_cb(SSL *ssl, } #endif /* HAS_NPN */ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ +#ifdef HAS_MODERN_SET_PROTO_VER static CURLcode set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) { @@ -2424,7 +2434,7 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) return CURLE_OK; } -#endif +#endif /* HAS_MODERN_SET_PROTO_VER */ #ifdef OPENSSL_IS_BORINGSSL typedef uint32_t ctx_option_t; @@ -2434,7 +2444,7 @@ typedef uint64_t ctx_option_t; typedef long ctx_option_t; #endif -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ +#if !defined(HAS_MODERN_SET_PROTO_VER) static CURLcode set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, struct Curl_easy *data, @@ -2509,7 +2519,7 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, } return CURLE_OK; } -#endif +#endif /* ! HAS_MODERN_SET_PROTO_VER */ /* The "new session" callback must return zero if the session can be removed * or non-zero if the session has been put into the session cache. @@ -2813,7 +2823,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3; -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ +#if HAS_MODERN_SET_PROTO_VER /* 1.1.0 */ result = set_ssl_version_min_max(backend->ctx, conn); #else result = set_ssl_version_min_max_legacy(&ctx_options, data, conn, @@ -328,6 +328,7 @@ CMAKE_CXX_SOURCES="\ cmCustomCommand \ cmCustomCommandGenerator \ cmCustomCommandLines \ + cmCxxModuleMapper \ cmDefinePropertyCommand \ cmDefinitions \ cmDocumentationFormatter \ @@ -337,6 +338,7 @@ CMAKE_CXX_SOURCES="\ cmExecProgramCommand \ cmExecuteProcessCommand \ cmExpandedCommandArgument \ + cmExperimental \ cmExportBuildFileGenerator \ cmExportFileGenerator \ cmExportInstallFileGenerator \ @@ -391,6 +393,7 @@ CMAKE_CXX_SOURCES="\ cmIncludeRegularExpressionCommand \ cmInstallCommand \ cmInstallCommandArguments \ + cmInstallCxxModuleBmiGenerator \ cmInstallDirectoryGenerator \ cmInstallExportGenerator \ cmInstallFileSetGenerator \ |