diff options
47 files changed, 933 insertions, 179 deletions
diff --git a/Help/command/install.rst b/Help/command/install.rst index bd8da39..35207f4 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -473,6 +473,11 @@ this advice while installing headers to a project-specific subdirectory: use "generator expressions" with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual for available expressions. +.. versionadded:: 3.20 + An install rename given as a ``RENAME`` argument may + use "generator expressions" with the syntax ``$<...>``. See the + :manual:`cmake-generator-expressions(7)` manual for available expressions. + Installing Directories ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst index 00fa39a..94753d5 100644 --- a/Help/guide/tutorial/index.rst +++ b/Help/guide/tutorial/index.rst @@ -414,27 +414,23 @@ tutorial assume that they are not common. If the platform has ``log`` and ``exp`` then we will use them to compute the square root in the ``mysqrt`` function. We first test for the availability of -these functions using the :module:`CheckSymbolExists` module in the top-level -``CMakeLists.txt``. On some platforms, we will need to link to the m library. -If ``log`` and ``exp`` are not initially found, require the m library and try -again. - -We're going to use the new defines in ``TutorialConfig.h.in``, so be sure to -set them before that file is configured. +these functions using the :module:`CheckSymbolExists` module in +``MathFunctions/CMakeLists.txt``. On some platforms, we will need to link to +the m library. If ``log`` and ``exp`` are not initially found, require the m +library and try again. .. literalinclude:: Step6/MathFunctions/CMakeLists.txt :language: cmake :start-after: # does this system provide the log and exp functions? :end-before: # add compile definitions -Now let's add these defines to ``TutorialConfig.h.in`` so that we can use them -from ``mysqrt.cxx``: - -.. code-block:: console +If available, use :command:`target_compile_definitions` to specify +``HAVE_LOG`` and ``HAVE_EXP`` as ``PRIVATE`` compile definitions. - // does the platform provide exp and log functions? - #cmakedefine HAVE_LOG - #cmakedefine HAVE_EXP +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # add compile definitions + :end-before: # install rules If ``log`` and ``exp`` are available on the system, then we will use them to compute the square root in the ``mysqrt`` function. Add the following code to @@ -456,51 +452,8 @@ Run the :manual:`cmake <cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it with your chosen build tool and run the Tutorial executable. -You will notice that we're not using ``log`` and ``exp``, even if we think they -should be available. We should realize quickly that we have forgotten to -include ``TutorialConfig.h`` in ``mysqrt.cxx``. - -We will also need to update ``MathFunctions/CMakeLists.txt`` so ``mysqrt.cxx`` -knows where this file is located: - -.. code-block:: cmake - - target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_BINARY_DIR} - ) - -After making this update, go ahead and build the project again and run the -built Tutorial executable. If ``log`` and ``exp`` are still not being used, -open the generated ``TutorialConfig.h`` file from the build directory. Maybe -they aren't available on the current system? - Which function gives better results now, sqrt or mysqrt? -Specify Compile Definition --------------------------- - -Is there a better place for us to save the ``HAVE_LOG`` and ``HAVE_EXP`` values -other than in ``TutorialConfig.h``? Let's try to use -:command:`target_compile_definitions`. - -First, remove the defines from ``TutorialConfig.h.in``. We no longer need to -include ``TutorialConfig.h`` from ``mysqrt.cxx`` or the extra include in -``MathFunctions/CMakeLists.txt``. - -Next, we can move the check for ``HAVE_LOG`` and ``HAVE_EXP`` to -``MathFunctions/CMakeLists.txt`` and then specify those values as ``PRIVATE`` -compile definitions. - -.. literalinclude:: Step6/MathFunctions/CMakeLists.txt - :language: cmake - :start-after: # does this system provide the log and exp functions? - :end-before: # install rules - -After making these updates, go ahead and build the project again. Run the -built Tutorial executable and verify that the results are same as earlier in -this step. - Adding a Custom Command and Generated File (Step 6) =================================================== diff --git a/Help/manual/cmake-developer.7.rst b/Help/manual/cmake-developer.7.rst index 85ed935..af9a8ab 100644 --- a/Help/manual/cmake-developer.7.rst +++ b/Help/manual/cmake-developer.7.rst @@ -23,15 +23,14 @@ in turn link to developer guides for CMake itself. Find Modules ============ -A "find module" is a ``Find<PackageName>.cmake`` file to be loaded -by the :command:`find_package` command when invoked for ``<PackageName>``. +A "find module" is a ``Find<PackageName>.cmake`` file to be loaded by the +:command:`find_package` command when invoked for ``<PackageName>``. -The primary task of a find module is to determine whether a package -exists on the system, set the ``<PackageName>_FOUND`` variable to reflect -this and provide any variables, macros and imported targets required to -use the package. A find module is useful in cases where an upstream -library does not provide a -:ref:`config file package <Config File Packages>`. +The primary task of a find module is to determine whether a package is +available, set the ``<PackageName>_FOUND`` variable to reflect this and +provide any variables, macros and imported targets required to use the +package. A find module is useful in cases where an upstream library does +not provide a :ref:`config file package <Config File Packages>`. The traditional approach is to use variables for everything, including libraries and executables: see the `Standard Variable Names`_ section @@ -91,55 +90,92 @@ Standard Variable Names For a ``FindXxx.cmake`` module that takes the approach of setting variables (either instead of or in addition to creating imported targets), the following variable names should be used to keep things -consistent between find modules. Note that all variables start with -``Xxx_`` to make sure they do not interfere with other find modules; the -same consideration applies to macros, functions and imported targets. +consistent between Find modules. Note that all variables start with +``Xxx_``, which (unless otherwise noted) must match exactly the name +of the ``FindXxx.cmake`` file, including upper/lowercase. +This prefix on the variable names ensures that they do not conflict with +variables of other Find modules. The same pattern should also be followed +for any macros, functions and imported targets defined by the Find module. ``Xxx_INCLUDE_DIRS`` The final set of include directories listed in one variable for use by - client code. This should not be a cache entry. + client code. This should not be a cache entry (note that this also means + this variable should not be used as the result variable of a + :command:`find_path` command - see ``Xxx_INCLUDE_DIR`` below for that). ``Xxx_LIBRARIES`` - The libraries to link against to use Xxx. These should include full - paths. This should not be a cache entry. + The libraries to use with the module. These may be CMake targets, full + absolute paths to a library binary or the name of a library that the + linker must find in its search path. This should not be a cache entry + (note that this also means this variable should not be used as the + result variable of a :command:`find_library` command - see + ``Xxx_LIBRARY`` below for that). ``Xxx_DEFINITIONS`` - Definitions to use when compiling code that uses Xxx. This really - shouldn't include options such as ``-DHAS_JPEG`` that a client + The compile definitions to use when compiling code that uses the module. + This really shouldn't include options such as ``-DHAS_JPEG`` that a client source-code file uses to decide whether to ``#include <jpeg.h>`` ``Xxx_EXECUTABLE`` - Where to find the Xxx tool. - -``Xxx_Yyy_EXECUTABLE`` - Where to find the Yyy tool that comes with Xxx. + The full absolute path to an executable. In this case, ``Xxx`` might not + be the name of the module, it might be the name of the tool (usually + converted to all uppercase), assuming that tool has such a well-known name + that it is unlikely that another tool with the same name exists. It would + be appropriate to use this as the result variable of a + :command:`find_program` command. + +``Xxx_YYY_EXECUTABLE`` + Similar to ``Xxx_EXECUTABLE`` except here the ``Xxx`` is always the module + name and ``YYY`` is the tool name (again, usually fully uppercase). + Prefer this form if the tool name is not very widely known or has the + potential to clash with another tool. For greater consistency, also + prefer this form if the module provides more than one executable. ``Xxx_LIBRARY_DIRS`` Optionally, the final set of library directories listed in one - variable for use by client code. This should not be a cache entry. + variable for use by client code. This should not be a cache entry. ``Xxx_ROOT_DIR`` - Where to find the base directory of Xxx. - -``Xxx_VERSION_Yy`` - Expect Version Yy if true. Make sure at most one of these is ever true. - -``Xxx_WRAP_Yy`` - If False, do not try to use the relevant CMake wrapping command. + Where to find the base directory of the module. + +``Xxx_VERSION_VV`` + Variables of this form specify whether the ``Xxx`` module being provided + is version ``VV`` of the module. There should not be more than one + variable of this form set to true for a given module. For example, a + module ``Barry`` might have evolved over many years and gone through a + number of different major versions. Version 3 of the ``Barry`` module + might set the variable ``Barry_VERSION_3`` to true, whereas an older + version of the module might set ``Barry_VERSION_2`` to true instead. + It would be an error for both ``Barry_VERSION_3`` and ``Barry_VERSION_2`` + to both be set to true. + +``Xxx_WRAP_YY`` + When a variable of this form is set to false, it indicates that the + relevant wrapping command should not be used. The wrapping command + depends on the module, it may be implied by the module name or it might + be specified by the ``YY`` part of the variable. ``Xxx_Yy_FOUND`` - If False, optional Yy part of Xxx system is not available. + For variables of this form, ``Yy`` is the name of a component for the + module. It should match exactly one of the valid component names that + may be passed to the :command:`find_package` command for the module. + If a variable of this form is set to false, it means that the ``Yy`` + component of module ``Xxx`` was not found or is not available. + Variables of this form would typically be used for optional components + so that the caller can check whether an optional component is available. ``Xxx_FOUND`` - Set to false, or undefined, if we haven't found, or don't want to use - Xxx. + When the :command:`find_package` command returns to the caller, this + variable will be set to true if the module was deemed to have been found + successfully. ``Xxx_NOT_FOUND_MESSAGE`` Should be set by config-files in the case that it has set ``Xxx_FOUND`` to FALSE. The contained message will be printed by the :command:`find_package` command and by - ``find_package_handle_standard_args()`` to inform the user about the - problem. + :command:`find_package_handle_standard_args` to inform the user about the + problem. Use this instead of calling :command:`message` directly to + report a reason for failing to find the module or package. ``Xxx_RUNTIME_LIBRARY_DIRS`` Optionally, the runtime library search path for use when running an @@ -160,23 +196,36 @@ same consideration applies to macros, functions and imported targets. ``Xxx_VERSION_PATCH`` The patch version of the package found, if any. -The following names should not usually be used in CMakeLists.txt files, but -are typically cache variables for users to edit and control the -behaviour of find modules (like entering the path to a library manually) +The following names should not usually be used in ``CMakeLists.txt`` files. +They are intended for use by Find modules to specify and cache the locations +of specific files or directories. Users are typically able to set and edit +these variables to control the behavior of Find modules (like entering the +path to a library manually): ``Xxx_LIBRARY`` - The path of the Xxx library (as used with :command:`find_library`, for - example). + The path of the library. Use this form only when the module provides a + single library. It is appropriate to use this as the result variable + in a :command:`find_library` command. ``Xxx_Yy_LIBRARY`` - The path of the Yy library that is part of the Xxx system. It may or - may not be required to use Xxx. + The path of library ``Yy`` provided by the module ``Xxx``. Use this form + when the module provides more than one library or where other modules may + also provide a library of the same name. It is also appropriate to use + this form as the result variable in a :command:`find_library` command. ``Xxx_INCLUDE_DIR`` - Where to find headers for using the Xxx library. + When the module provides only a single library, this variable can be used + to specify where to find headers for using the library (or more accurately, + the path that consumers of the library should add to their header search + path). It would be appropriate to use this as the result variable in a + :command:`find_path` command. ``Xxx_Yy_INCLUDE_DIR`` - Where to find headers for using the Yy library of the Xxx system. + If the module provides more than one library or where other modules may + also provide a library of the same name, this form is recommended for + specifying where to find headers for using library ``Yy`` provided by + the module. Again, it would be appropriate to use this as the result + variable in a :command:`find_path` command. To prevent users being overwhelmed with settings to configure, try to keep as many options as possible out of the cache, leaving at least one @@ -185,7 +234,8 @@ not-found library (e.g. ``Xxx_ROOT_DIR``). For the same reason, mark most cache options as advanced. For packages which provide both debug and release binaries, it is common to create cache variables with a ``_LIBRARY_<CONFIG>`` suffix, such as ``Foo_LIBRARY_RELEASE`` and -``Foo_LIBRARY_DEBUG``. +``Foo_LIBRARY_DEBUG``. The :module:`SelectLibraryConfigurations` module +can be helpful for such cases. While these are the standard variable names, you should provide backwards compatibility for any old names that were actually in use. diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index 6876e1c..89739b7 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst @@ -1154,3 +1154,160 @@ The members specific to ``cmakeFiles`` objects are: ``isCMake`` Optional member that is present with boolean value ``true`` if the path specifies a file in the CMake installation. + +Object Kind "toolchains" +------------------------ + +The ``toolchains`` object kind lists properties of the toolchains used during +the build. These include the language, compiler path, ID, and version. + +There is only one ``toolchains`` object major version, version 1. + +"toolchains" version 1 +^^^^^^^^^^^^^^^^^^^^^^ + +``toolchains`` object version 1 is a JSON object: + +.. code-block:: json + + { + "kind": "toolchains", + "version": { "major": 1, "minor": 0 }, + "toolchains": [ + { + "language": "C", + "compiler": { + "path": "/usr/bin/cc", + "id": "GNU", + "version": "9.3.0", + "implicit": { + "includeDirectories": [ + "/usr/lib/gcc/x86_64-linux-gnu/9/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "/usr/include" + ], + "linkDirectories": [ + "/usr/lib/gcc/x86_64-linux-gnu/9", + "/usr/lib/x86_64-linux-gnu", + "/usr/lib", + "/lib/x86_64-linux-gnu", + "/lib" + ], + "linkFrameworkDirectories": [], + "linkLibraries": [ "gcc", "gcc_s", "c", "gcc", "gcc_s" ] + } + }, + "sourceFileExtensions": [ "c", "m" ] + }, + { + "language": "CXX", + "compiler": { + "path": "/usr/bin/c++", + "id": "GNU", + "version": "9.3.0", + "implicit": { + "includeDirectories": [ + "/usr/include/c++/9", + "/usr/include/x86_64-linux-gnu/c++/9", + "/usr/include/c++/9/backward", + "/usr/lib/gcc/x86_64-linux-gnu/9/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "/usr/include" + ], + "linkDirectories": [ + "/usr/lib/gcc/x86_64-linux-gnu/9", + "/usr/lib/x86_64-linux-gnu", + "/usr/lib", + "/lib/x86_64-linux-gnu", + "/lib" + ], + "linkFrameworkDirectories": [], + "linkLibraries": [ + "stdc++", "m", "gcc_s", "gcc", "c", "gcc_s", "gcc" + ] + } + }, + "sourceFileExtensions": [ + "C", "M", "c++", "cc", "cpp", "cxx", "mm", "CPP" + ] + } + ] + } + +The members specific to ``toolchains`` objects are: + +``toolchains`` + A JSON array whose entries are each a JSON object specifying a toolchain + associated with a particular language. The members of each entry are: + + ``language`` + A JSON string specifying the toolchain language, like C or CXX. Language + names are the same as langauge names that can be passed to the + :command:`project` command. Because CMake only supports a single toolchain + per language, this field can be used as a key. + + ``compiler`` + A JSON object containing members: + + ``path`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER` variable is defined for the current + language. Its value is a JSON string holding the path to the compiler. + + ``id`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER_ID` variable is defined for the current + language. Its value is a JSON string holding the ID (GNU, MSVC, etc.) of + the compiler. + + ``version`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable is defined for the + current language. Its value is a JSON string holding the version of the + compiler. + + ``target`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_COMPILER_TARGET` variable is defined for the + current language. Its value is a JSON string holding the cross-compiling + target of the compiler. + + ``implicit`` + A JSON object containing members: + + ``includeDirectories`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES` variable is + defined for the current language. Its value is a JSON array of JSON + strings where each string holds a path to an implicit include + directory for the compiler. + + ``linkDirectories`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES` variable is + defined for the current language. Its value is a JSON array of JSON + strings where each string holds a path to an implicit link directory + for the compiler. + + ``linkFrameworkDirectories`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES` variable + is defined for the current language. Its value is a JSON array of JSON + strings where each string holds a path to an implicit link framework + directory for the compiler. + + ``linkLibraries`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES` variable is defined + for the current language. Its value is a JSON array of JSON strings + where each string holds a path to an implicit link library for the + compiler. + + ``sourceFileExtensions`` + Optional member that is present when the + :variable:`CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS` variable is defined for + the current language. Its value is a JSON array of JSON strings where each + each string holds a file extension (without the leading dot) for the + language. diff --git a/Help/release/dev/external-project-configure-handled-by-build.rst b/Help/release/dev/external-project-configure-handled-by-build.rst new file mode 100644 index 0000000..4a1fac8 --- /dev/null +++ b/Help/release/dev/external-project-configure-handled-by-build.rst @@ -0,0 +1,8 @@ +external-project-configure-handled-by-build +------------------------------------------- + +* The :module:`ExternalProject` function ``ExternalProject_Add`` learned a new + ``CONFIGURE_HANDLED_BY_BUILD`` option to have subsequent runs of the configure + step be triggered by the build step when an external project dependency + rebuilds instead of always rerunning the configure step when an external + project dependency rebuilds. diff --git a/Help/release/dev/install-files-rename-genex.rst b/Help/release/dev/install-files-rename-genex.rst new file mode 100644 index 0000000..f735e24 --- /dev/null +++ b/Help/release/dev/install-files-rename-genex.rst @@ -0,0 +1,5 @@ +install-files-rename-genex +-------------------------- + +* The :command:`install(FILES)` command ``RENAME`` option learned to + support :manual:`generator expressions <cmake-generator-expressions(7)>`. diff --git a/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst b/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst index 6cd51fa..b6fee2e 100644 --- a/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst +++ b/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst @@ -5,3 +5,8 @@ CTEST_MEMORYCHECK_SANITIZER_OPTIONS Specify the CTest ``MemoryCheckSanitizerOptions`` setting in a :manual:`ctest(1)` dashboard client script. + +CTest prepends correct sanitizer options ``*_OPTIONS`` +environment variable to executed command. CTests adds +its own ``log_path`` to sanitizer options, don't provide your +own ``log_path``. diff --git a/Modules/CMakeDetermineCCompiler.cmake b/Modules/CMakeDetermineCCompiler.cmake index ae3abe9..ab33b40 100644 --- a/Modules/CMakeDetermineCCompiler.cmake +++ b/Modules/CMakeDetermineCCompiler.cmake @@ -161,9 +161,10 @@ if (NOT _CMAKE_TOOLCHAIN_PREFIX) if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|QCC") get_filename_component(COMPILER_BASENAME "${CMAKE_C_COMPILER}" NAME) - if (COMPILER_BASENAME MATCHES "^(.+-)(clang|g?cc)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") + if (COMPILER_BASENAME MATCHES "^(.+-)?(clang|g?cc)(-cl)?(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) - set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5}) + set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_4}) + set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_6}) elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") if(CMAKE_C_COMPILER_TARGET) set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_C_COMPILER_TARGET}-) diff --git a/Modules/CMakeDetermineCXXCompiler.cmake b/Modules/CMakeDetermineCXXCompiler.cmake index 905eb25..7283bc2 100644 --- a/Modules/CMakeDetermineCXXCompiler.cmake +++ b/Modules/CMakeDetermineCXXCompiler.cmake @@ -160,8 +160,9 @@ if (NOT _CMAKE_TOOLCHAIN_PREFIX) if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|QCC") get_filename_component(COMPILER_BASENAME "${CMAKE_CXX_COMPILER}" NAME) - if (COMPILER_BASENAME MATCHES "^(.+-)(clan)?[gc]\\+\\+(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") + if (COMPILER_BASENAME MATCHES "^(.+-)?(clang\\+\\+|g\\+\\+|clang-cl)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) + set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_3}) set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5}) elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if(CMAKE_CXX_COMPILER_TARGET) diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index d81fd11..ff178f6 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -70,17 +70,18 @@ if(("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC" AND OR (CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "Tegra-Android")) + set(_CMAKE_LINKER_NAMES "link") + set(_CMAKE_AR_NAMES "lib") + set(_CMAKE_MT_NAMES "mt") if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang") - find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm llvm-nm HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - set(_CMAKE_ADDITIONAL_LINKER_NAMES "lld-link") - set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-lib") + set(_CMAKE_NM_NAMES "llvm-nm" "nm") + list(APPEND _CMAKE_AR_NAMES "lib" "llvm-lib") + list(APPEND _CMAKE_MT_NAMES "mt" "llvm-mt") + list(APPEND _CMAKE_LINKER_NAMES "lld-link") + list(APPEND _CMAKE_TOOL_VARS NM) endif() - find_program(CMAKE_LINKER NAMES ${_CMAKE_ADDITIONAL_LINKER_NAMES} link HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_AR NAMES ${_CMAKE_ADDITIONAL_AR_NAMES} lib HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_MT NAMES mt HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - - list(APPEND _CMAKE_TOOL_VARS LINKER MT) + list(APPEND _CMAKE_TOOL_VARS LINKER MT AR) # in all other cases search for ar, ranlib, etc. else() @@ -92,55 +93,54 @@ else() endif() if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang) - set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-ar") - set(_CMAKE_ADDITIONAL_RANLIB_NAMES "llvm-ranlib") - set(_CMAKE_ADDITIONAL_STRIP_NAMES "llvm-strip") if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC") - set(_CMAKE_ADDITIONAL_LINKER_NAMES "lld-link") + set(_CMAKE_LINKER_NAMES "lld-link") else() - set(_CMAKE_ADDITIONAL_LINKER_NAMES "ld.lld") + set(_CMAKE_LINKER_NAMES "ld.lld") endif() - set(_CMAKE_ADDITIONAL_NM_NAMES "llvm-nm") - set(_CMAKE_ADDITIONAL_OBJDUMP_NAMES "llvm-objdump") - set(_CMAKE_ADDITIONAL_OBJCOPY_NAMES "llvm-objcopy") - set(_CMAKE_ADDITIONAL_READELF_NAMES "llvm-readelf") - set(_CMAKE_ADDITIONAL_DLLTOOL_NAMES "llvm-dlltool") - set(_CMAKE_ADDITIONAL_ADDR2LINE_NAMES "llvm-addr2line") + list(APPEND _CMAKE_AR_NAMES "llvm-ar") + list(APPEND _CMAKE_NM_NAMES "llvm-nm") + list(APPEND _CMAKE_OBJDUMP_NAMES "llvm-objdump") + list(APPEND _CMAKE_OBJCOPY_NAMES "llvm-objcopy") + list(APPEND _CMAKE_READELF_NAMES "llvm-readelf") + list(APPEND _CMAKE_DLLTOOL_NAMES "llvm-dlltool") + list(APPEND _CMAKE_ADDR2LINE_NAMES "llvm-addr2line") endif() - if(NOT CMAKE_CROSSCOMPILING AND NOT "${_CMAKE_TOOLCHAIN_PREFIX}" STREQUAL "") - list(APPEND _CMAKE_ADDITIONAL_AR_NAMES "ar") - list(APPEND _CMAKE_ADDITIONAL_RANLIB_NAMES "ranlib") - list(APPEND _CMAKE_ADDITIONAL_STRIP_NAMES "strip") - list(APPEND _CMAKE_ADDITIONAL_LINKER_NAMES "ld") - list(APPEND _CMAKE_ADDITIONAL_NM_NAMES "nm") - list(APPEND _CMAKE_ADDITIONAL_OBJDUMP_NAMES "objdump") - list(APPEND _CMAKE_ADDITIONAL_OBJCOPY_NAMES "objcopy") - list(APPEND _CMAKE_ADDITIONAL_READELF_NAMES "readelf") - list(APPEND _CMAKE_ADDITIONAL_DLLTOOL_NAMES "dlltool") - list(APPEND _CMAKE_ADDITIONAL_ADDR2LINE_NAMES "addr2line") - endif() + list(APPEND _CMAKE_AR_NAMES "ar") + list(APPEND _CMAKE_RANLIB_NAMES "ranlib") + list(APPEND _CMAKE_STRIP_NAMES "strip") + list(APPEND _CMAKE_LINKER_NAMES "ld") + list(APPEND _CMAKE_NM_NAMES "nm") + list(APPEND _CMAKE_OBJDUMP_NAMES "objdump") + list(APPEND _CMAKE_OBJCOPY_NAMES "objcopy") + list(APPEND _CMAKE_READELF_NAMES "readelf") + list(APPEND _CMAKE_DLLTOOL_NAMES "dlltool") + list(APPEND _CMAKE_ADDR2LINE_NAMES "addr2line") + + list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE) +endif() - find_program(CMAKE_AR NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_AR_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) +foreach(TOOL IN LISTS _CMAKE_TOOL_VARS) + foreach(NAME IN LISTS _CMAKE_${TOOL}_NAMES) + if(NOT _CMAKE_TOOLCHAIN_PREFIX STREQUAL "") + if(NOT _CMAKE_TOOLCHAIN_SUFFIX STREQUAL "") + list(PREPEND _CMAKE_${TOOL}_NAMES ${NAME}${_CMAKE_TOOLCHAIN_SUFFIX}) + endif() + list(PREPEND _CMAKE_${TOOL}_NAMES ${_CMAKE_TOOLCHAIN_PREFIX}${NAME}) + endif() + if(NOT _CMAKE_TOOLCHAIN_SUFFIX STREQUAL "") + list(PREPEND _CMAKE_${TOOL}_NAMES ${_CMAKE_TOOLCHAIN_PREFIX}${NAME}${_CMAKE_TOOLCHAIN_SUFFIX}) + endif() + endforeach() + find_program(CMAKE_${TOOL} NAMES ${_CMAKE_${TOOL}_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) +endforeach() - find_program(CMAKE_RANLIB NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${_CMAKE_ADDITIONAL_RANLIB_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - if(NOT CMAKE_RANLIB) +if(NOT CMAKE_RANLIB) set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib") - endif() - - - find_program(CMAKE_STRIP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_STRIP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_LINKER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_OBJDUMP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_READELF NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_DLLTOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_ADDR2LINE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}addr2line ${_CMAKE_ADDITIONAL_ADDR2LINE_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - - list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE) endif() + if(CMAKE_PLATFORM_HAS_INSTALLNAME) find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) @@ -157,7 +157,7 @@ foreach(var IN LISTS _CMAKE_TOOL_VARS) if(_CMAKE_TOOL_CACHED) mark_as_advanced(CMAKE_${var}) endif() - unset(_CMAKE_ADDITIONAL_${var}_NAMES) + unset(_CMAKE_${var}_NAMES) endforeach() unset(_CMAKE_TOOL_VARS) unset(_CMAKE_TOOL_CACHED) diff --git a/Modules/Compiler/Clang-FindBinUtils.cmake b/Modules/Compiler/Clang-FindBinUtils.cmake index b852660..e6c469a 100644 --- a/Modules/Compiler/Clang-FindBinUtils.cmake +++ b/Modules/Compiler/Clang-FindBinUtils.cmake @@ -2,6 +2,12 @@ if(NOT DEFINED _CMAKE_PROCESSING_LANGUAGE OR _CMAKE_PROCESSING_LANGUAGE STREQUAL message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set") endif() +# Ubuntu: +# * /usr/bin/llvm-ar-9 +# * /usr/bin/llvm-ranlib-9 +string(REGEX MATCH "^([0-9]+)" __version_x + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}") + # Debian: # * /usr/bin/llvm-ar-4.0 # * /usr/bin/llvm-ranlib-4.0 @@ -19,6 +25,7 @@ set(__clang_hints ${__clang_hint_1} ${__clang_hint_2}) # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ar.1.html find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x_y}" + "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x}" "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar" HINTS ${__clang_hints} DOC "LLVM archiver" @@ -28,6 +35,7 @@ mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR) # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ranlib.1.html find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x_y}" + "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x}" "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib" HINTS ${__clang_hints} DOC "Generate index for LLVM archive" diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 28d21c1..3307bc6 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -543,6 +543,18 @@ External Project Definition When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND`` is used to point to an alternative directory within the source tree. + ``CONFIGURE_HANDLED_BY_BUILD <bool>`` + .. versionadded:: 3.20 + + Enabling this option relaxes the dependencies of the configure step on + other external projects to order-only. This means the configure step will + be executed after its external project dependencies are built but it will + not be marked dirty when one of its external project dependencies is + rebuilt. This option can be enabled when the build step is smart enough + to figure out if the configure step needs to be rerun. CMake and Meson are + examples of build systems whose build step is smart enough to know if the + configure step needs to be rerun. + **Build Step Options:** If the configure step assumed the external project uses CMake as its build system, the build step will also. Otherwise, the build step will assume a @@ -3083,6 +3095,23 @@ function(_ep_add_patch_command name) ) endfunction() +function(_ep_get_file_deps var name) + set(file_deps) + + get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) + foreach(dep IN LISTS deps) + get_property(dep_type TARGET ${dep} PROPERTY TYPE) + if(dep_type STREQUAL "UTILITY") + get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT) + if(is_ep) + _ep_get_step_stampfile(${dep} "done" done_stamp_file) + list(APPEND file_deps ${done_stamp_file}) + endif() + endif() + endforeach() + + set("${var}" "${file_deps}" PARENT_SCOPE) +endfunction() function(_ep_extract_configure_command var name) get_property(cmd_set TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND SET) @@ -3191,19 +3220,13 @@ endfunction() function(_ep_add_configure_command name) ExternalProject_Get_Property(${name} binary_dir tmp_dir) - # Depend on other external projects (file-level). set(file_deps) - get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) - foreach(dep IN LISTS deps) - get_property(dep_type TARGET ${dep} PROPERTY TYPE) - if(dep_type STREQUAL "UTILITY") - get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT) - if(is_ep) - _ep_get_step_stampfile(${dep} "done" done_stamp_file) - list(APPEND file_deps ${done_stamp_file}) - endif() - endif() - endforeach() + get_property(configure_handled_by_build TARGET ${name} + PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD) + if(NOT configure_handled_by_build) + # Depend on other external projects (file-level) + _ep_get_file_deps(file_deps ${name}) + endif() _ep_extract_configure_command(cmd ${name}) @@ -3254,6 +3277,14 @@ endfunction() function(_ep_add_build_command name) ExternalProject_Get_Property(${name} binary_dir) + set(file_deps) + get_property(configure_handled_by_build TARGET ${name} + PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD) + if(configure_handled_by_build) + # Depend on other external projects (file-level) + _ep_get_file_deps(file_deps ${name}) + endif() + get_property(cmd_set TARGET ${name} PROPERTY _EP_BUILD_COMMAND SET) if(cmd_set) get_property(cmd TARGET ${name} PROPERTY _EP_BUILD_COMMAND) @@ -3296,6 +3327,7 @@ function(_ep_add_build_command name) BYPRODUCTS \${build_byproducts} WORKING_DIRECTORY \${binary_dir} DEPENDEES configure + DEPENDS \${file_deps} ALWAYS \${always} ${log} ${uses_terminal} diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index c5b67c0..dca94ee 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -268,6 +268,8 @@ set(SRCS cmFileAPICodemodel.h cmFileAPICMakeFiles.cxx cmFileAPICMakeFiles.h + cmFileAPIToolchains.cxx + cmFileAPIToolchains.h cmFileCopier.cxx cmFileCopier.h cmFileInstaller.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index d439eeb..edd70bd 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 19) -set(CMake_VERSION_PATCH 20210113) +set(CMake_VERSION_PATCH 20210115) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index c2ab2f1..d2a9bec 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -18,6 +18,7 @@ #include "cmFileAPICMakeFiles.h" #include "cmFileAPICache.h" #include "cmFileAPICodemodel.h" +#include "cmFileAPIToolchains.h" #include "cmGlobalGenerator.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -262,6 +263,17 @@ bool cmFileAPI::ReadQuery(std::string const& query, objects.push_back(o); return true; } + if (kindName == ObjectKindName(ObjectKind::Toolchains)) { + Object o; + o.Kind = ObjectKind::Toolchains; + if (verStr == "v1") { + o.Version = 1; + } else { + return false; + } + objects.push_back(o); + return true; + } if (kindName == ObjectKindName(ObjectKind::InternalTest)) { Object o; o.Kind = ObjectKind::InternalTest; @@ -402,6 +414,7 @@ const char* cmFileAPI::ObjectKindName(ObjectKind kind) "codemodel", // "cache", // "cmakeFiles", // + "toolchains", // "__test" // }; return objectKindNames[size_t(kind)]; @@ -435,6 +448,9 @@ Json::Value cmFileAPI::BuildObject(Object const& object) case ObjectKind::CMakeFiles: value = this->BuildCMakeFiles(object); break; + case ObjectKind::Toolchains: + value = this->BuildToolchains(object); + break; case ObjectKind::InternalTest: value = this->BuildInternalTest(object); break; @@ -491,6 +507,8 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest( r.Kind = ObjectKind::Cache; } else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) { r.Kind = ObjectKind::CMakeFiles; + } else if (kindName == this->ObjectKindName(ObjectKind::Toolchains)) { + r.Kind = ObjectKind::Toolchains; } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) { r.Kind = ObjectKind::InternalTest; } else { @@ -518,6 +536,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest( case ObjectKind::CMakeFiles: this->BuildClientRequestCMakeFiles(r, versions); break; + case ObjectKind::Toolchains: + this->BuildClientRequestToolchains(r, versions); + break; case ObjectKind::InternalTest: this->BuildClientRequestInternalTest(r, versions); break; @@ -765,6 +786,40 @@ Json::Value cmFileAPI::BuildCMakeFiles(Object const& object) return cmakeFiles; } +// The "toolchains" object kind. + +static unsigned int const ToolchainsV1Minor = 0; + +void cmFileAPI::BuildClientRequestToolchains( + ClientRequest& r, std::vector<RequestVersion> const& versions) +{ + // Select a known version from those requested. + for (RequestVersion const& v : versions) { + if ((v.Major == 1 && v.Minor <= ToolchainsV1Minor)) { + r.Version = v.Major; + break; + } + } + if (!r.Version) { + r.Error = NoSupportedVersion(versions); + } +} + +Json::Value cmFileAPI::BuildToolchains(Object const& object) +{ + Json::Value toolchains = cmFileAPIToolchainsDump(*this, object.Version); + toolchains["kind"] = this->ObjectKindName(object.Kind); + + Json::Value& version = toolchains["version"]; + if (object.Version == 1) { + version = BuildVersion(1, ToolchainsV1Minor); + } else { + return toolchains; // should be unreachable + } + + return toolchains; +} + // The "__test" object kind is for internal testing of CMake. static unsigned int const InternalTestV1Minor = 3; @@ -828,5 +883,13 @@ Json::Value cmFileAPI::ReportCapabilities() requests.append(std::move(request)); // NOLINT(*) } + { + Json::Value request = Json::objectValue; + request["kind"] = ObjectKindName(ObjectKind::Toolchains); + Json::Value& versions = request["version"] = Json::arrayValue; + versions.append(BuildVersion(1, ToolchainsV1Minor)); + requests.append(std::move(request)); // NOLINT(*) + } + return capabilities; } diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h index 086a92a..22302b4 100644 --- a/Source/cmFileAPI.h +++ b/Source/cmFileAPI.h @@ -56,6 +56,7 @@ private: CodeModel, Cache, CMakeFiles, + Toolchains, InternalTest }; @@ -200,6 +201,10 @@ private: ClientRequest& r, std::vector<RequestVersion> const& versions); Json::Value BuildCMakeFiles(Object const& object); + void BuildClientRequestToolchains( + ClientRequest& r, std::vector<RequestVersion> const& versions); + Json::Value BuildToolchains(Object const& object); + void BuildClientRequestInternalTest( ClientRequest& r, std::vector<RequestVersion> const& versions); Json::Value BuildInternalTest(Object const& object); diff --git a/Source/cmFileAPIToolchains.cxx b/Source/cmFileAPIToolchains.cxx new file mode 100644 index 0000000..722c114 --- /dev/null +++ b/Source/cmFileAPIToolchains.cxx @@ -0,0 +1,151 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPIToolchains.h" + +#include <memory> +#include <string> +#include <vector> + +#include <cm3p/json/value.h> + +#include "cmFileAPI.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmProperty.h" +#include "cmState.h" +#include "cmStringAlgorithms.h" +#include "cmake.h" + +namespace { + +struct ToolchainVariable +{ + std::string ObjectKey; + std::string VariableSuffix; + bool IsList; +}; + +class Toolchains +{ + cmFileAPI& FileAPI; + unsigned long Version; + + static const std::vector<ToolchainVariable> CompilerVariables; + static const std::vector<ToolchainVariable> CompilerImplicitVariables; + static const ToolchainVariable SourceFileExtensionsVariable; + + Json::Value DumpToolchains(); + Json::Value DumpToolchain(std::string const& lang); + Json::Value DumpToolchainVariables( + cmMakefile const* mf, std::string const& lang, + std::vector<ToolchainVariable> const& variables); + void DumpToolchainVariable(cmMakefile const* mf, Json::Value& object, + std::string const& lang, + ToolchainVariable const& variable); + +public: + Toolchains(cmFileAPI& fileAPI, unsigned long version); + Json::Value Dump(); +}; + +const std::vector<ToolchainVariable> Toolchains::CompilerVariables{ + { "path", "COMPILER", false }, + { "id", "COMPILER_ID", false }, + { "version", "COMPILER_VERSION", false }, + { "target", "COMPILER_TARGET", false }, +}; + +const std::vector<ToolchainVariable> Toolchains::CompilerImplicitVariables{ + { "includeDirectories", "IMPLICIT_INCLUDE_DIRECTORIES", true }, + { "linkDirectories", "IMPLICIT_LINK_DIRECTORIES", true }, + { "linkFrameworkDirectories", "IMPLICIT_LINK_FRAMEWORK_DIRECTORIES", true }, + { "linkLibraries", "IMPLICIT_LINK_LIBRARIES", true }, +}; + +const ToolchainVariable Toolchains::SourceFileExtensionsVariable{ + "sourceFileExtensions", "SOURCE_FILE_EXTENSIONS", true +}; + +Toolchains::Toolchains(cmFileAPI& fileAPI, unsigned long version) + : FileAPI(fileAPI) + , Version(version) +{ + static_cast<void>(this->Version); +} + +Json::Value Toolchains::Dump() +{ + Json::Value toolchains = Json::objectValue; + toolchains["toolchains"] = this->DumpToolchains(); + return toolchains; +} + +Json::Value Toolchains::DumpToolchains() +{ + Json::Value toolchains = Json::arrayValue; + + for (std::string const& lang : + this->FileAPI.GetCMakeInstance()->GetState()->GetEnabledLanguages()) { + toolchains.append(this->DumpToolchain(lang)); + } + + return toolchains; +} + +Json::Value Toolchains::DumpToolchain(std::string const& lang) +{ + const auto& mf = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator()->GetMakefiles()[0]; + Json::Value toolchain = Json::objectValue; + toolchain["language"] = lang; + toolchain["compiler"] = + this->DumpToolchainVariables(mf.get(), lang, CompilerVariables); + toolchain["compiler"]["implicit"] = + this->DumpToolchainVariables(mf.get(), lang, CompilerImplicitVariables); + this->DumpToolchainVariable(mf.get(), toolchain, lang, + SourceFileExtensionsVariable); + return toolchain; +} + +Json::Value Toolchains::DumpToolchainVariables( + cmMakefile const* mf, std::string const& lang, + std::vector<ToolchainVariable> const& variables) +{ + Json::Value object = Json::objectValue; + for (const auto& variable : variables) { + this->DumpToolchainVariable(mf, object, lang, variable); + } + return object; +} + +void Toolchains::DumpToolchainVariable(cmMakefile const* mf, + Json::Value& object, + std::string const& lang, + ToolchainVariable const& variable) +{ + std::string const variableName = + cmStrCat("CMAKE_", lang, "_", variable.VariableSuffix); + + if (variable.IsList) { + std::vector<std::string> values; + if (mf->GetDefExpandList(variableName, values)) { + Json::Value jsonArray = Json::arrayValue; + for (std::string const& value : values) { + jsonArray.append(value); + } + object[variable.ObjectKey] = jsonArray; + } + } else { + cmProp def = mf->GetDefinition(variableName); + if (def) { + object[variable.ObjectKey] = *def; + } + } +} +} + +Json::Value cmFileAPIToolchainsDump(cmFileAPI& fileAPI, unsigned long version) +{ + Toolchains toolchains(fileAPI, version); + return toolchains.Dump(); +} diff --git a/Source/cmFileAPIToolchains.h b/Source/cmFileAPIToolchains.h new file mode 100644 index 0000000..c188807 --- /dev/null +++ b/Source/cmFileAPIToolchains.h @@ -0,0 +1,12 @@ +/* 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 <cm3p/json/value.h> + +class cmFileAPI; + +extern Json::Value cmFileAPIToolchainsDump(cmFileAPI& fileAPI, + unsigned long version); diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx index c4bec23..36f583f 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.cxx +++ b/Source/cmGlobalNMakeMakefileGenerator.cxx @@ -21,6 +21,8 @@ cmGlobalNMakeMakefileGenerator::cmGlobalNMakeMakefileGenerator(cmake* cm) this->PassMakeflags = true; this->UnixCD = false; this->MakeSilentFlag = "/nologo"; + // nmake breaks on '!' in long-line dependencies + this->ToolSupportsLongLineDependencies = false; } void cmGlobalNMakeMakefileGenerator::EnableLanguage( diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index c45000d..0a353e4 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx @@ -25,10 +25,14 @@ cmInstallFilesGenerator::cmInstallFilesGenerator( , Programs(programs) , Optional(optional) { - // We need per-config actions if the destination has generator expressions. + // We need per-config actions if the destination and rename have generator + // expressions. if (cmGeneratorExpression::Find(this->Destination) != std::string::npos) { this->ActionsPerConfig = true; } + if (cmGeneratorExpression::Find(this->Rename) != std::string::npos) { + this->ActionsPerConfig = true; + } // We need per-config actions if any directories have generator expressions. if (!this->ActionsPerConfig) { @@ -56,6 +60,12 @@ std::string cmInstallFilesGenerator::GetDestination( this->LocalGenerator, config); } +std::string cmInstallFilesGenerator::GetRename(std::string const& config) const +{ + return cmGeneratorExpression::Evaluate(this->Rename, this->LocalGenerator, + config); +} + void cmInstallFilesGenerator::AddFilesInstallRule( std::ostream& os, std::string const& config, Indent indent, std::vector<std::string> const& files) @@ -66,7 +76,7 @@ void cmInstallFilesGenerator::AddFilesInstallRule( os, this->GetDestination(config), (this->Programs ? cmInstallType_PROGRAMS : cmInstallType_FILES), files, this->Optional, this->FilePermissions.c_str(), no_dir_permissions, - this->Rename.c_str(), nullptr, indent); + this->GetRename(config).c_str(), nullptr, indent); } void cmInstallFilesGenerator::GenerateScriptActions(std::ostream& os, diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h index b5a1ef4..66145f4 100644 --- a/Source/cmInstallFilesGenerator.h +++ b/Source/cmInstallFilesGenerator.h @@ -31,6 +31,7 @@ public: bool Compute(cmLocalGenerator* lg) override; std::string GetDestination(std::string const& config) const; + std::string GetRename(std::string const& config) const; protected: void GenerateScriptActions(std::ostream& os, Indent indent) override; diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index 53c871b..fdddb45 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -422,9 +422,10 @@ bool HandleJoinCommand(std::vector<std::string> const& args, bool HandleRemoveItemCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - if (args.size() < 3) { - status.SetError("sub-command REMOVE_ITEM requires two or more arguments."); - return false; + assert(args.size() >= 2); + + if (args.size() == 2) { + return true; } const std::string& listName = args[1]; diff --git a/Tests/CMakeTests/ListTest.cmake.in b/Tests/CMakeTests/ListTest.cmake.in index 785f41d..76737e5 100644 --- a/Tests/CMakeTests/ListTest.cmake.in +++ b/Tests/CMakeTests/ListTest.cmake.in @@ -142,9 +142,8 @@ set(Find-List-Only-STDERR "three") set(Insert-List-Only-STDERR "at least three") set(Length-List-Only-STDERR "two") set(Remove_At-List-Only-STDERR "at least two") -set(Remove_Item-List-Only-STDERR "two or more") -foreach(cmd IN ITEMS Find Get Insert Length Remove_At Remove_Item) +foreach(cmd IN ITEMS Find Get Insert Length Remove_At) string(TOUPPER ${cmd} cmd_upper) set(${cmd}-List-Only-RESULT 1) set(${cmd}-List-Only-STDERR ".*CMake Error at List-${cmd}-List-Only.cmake:1 \\(list\\):.*list sub-command ${cmd_upper} requires ${${cmd}-List-Only-STDERR} arguments.*") diff --git a/Tests/Fuzzing/README.rst b/Tests/Fuzzing/README.rst new file mode 100644 index 0000000..a869f9c --- /dev/null +++ b/Tests/Fuzzing/README.rst @@ -0,0 +1,8 @@ +The fuzzers in this directory are run continuously through OSS-fuzz. +All fuzzers are implemented by way of the `libFuzzer engine`_. + +The link to the OSS-fuzz integration can be found here: (pending) +All email addresses in the `project.yaml` file on OSS-fuzz will have access +to detailed bug reports and will be notified via email if/when bugs are found. + +.. _`libFuzzer Engine`: https://llvm.org/docs/LibFuzzer.html diff --git a/Tests/Fuzzing/xml_parser_fuzzer.cc b/Tests/Fuzzing/xml_parser_fuzzer.cc new file mode 100644 index 0000000..1faa918 --- /dev/null +++ b/Tests/Fuzzing/xml_parser_fuzzer.cc @@ -0,0 +1,27 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "cmXMLParser.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + char test_file[] = "libfuzzer.xml"; + + FILE* fp = fopen(test_file, "wb"); + if (!fp) + return 0; + fwrite(data, size, 1, fp); + fclose(fp); + + cmXMLParser parser; + if (!parser.ParseFile(test_file)) { + return 1; + } + + remove(test_file); + return 0; +} diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt index d980a52..d4c19c7 100644 --- a/Tests/IncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/CMakeLists.txt @@ -67,13 +67,7 @@ else() endif() # Test escaping of special characters in include directory paths. -set(special_chars "~@&{}()'") -if(NOT (CMAKE_GENERATOR STREQUAL "NMake Makefiles" AND - "x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC" AND - "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS 13.0)) - # NMake from VS 6 mistakes '!' in a path after a line continuation for a directive. - string(APPEND special_chars "!") -endif() +set(special_chars "~@&{}()!'") if(NOT CMAKE_GENERATOR MATCHES "(Unix|MinGW|MSYS) Makefiles") # when compiler is used for dependencies, special characters for make are not escaped string(APPEND special_chars "%") diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index a8b6584..c76c92d 100644 --- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt @@ -1 +1 @@ -^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$ +^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$ diff --git a/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake new file mode 100644 index 0000000..887da0f --- /dev/null +++ b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake @@ -0,0 +1,19 @@ +file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_AFTER "%s") +# When BUILD_ALWAYS is set, the build stamp is never created. +file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_AFTER "%s") +file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_AFTER "%s") + +if(NOT PROJ1_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ1_CONFIGURE_TIMESTAMP_AFTER) + set(RunCMake_TEST_FAILED "Unexpected rebuild of proj1 configure step (${PROJ1_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ1_CONFIGURE_TIMESTAMP_AFTER})") + return() +endif() + +if(NOT PROJ2_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ2_CONFIGURE_TIMESTAMP_AFTER) + set(RunCMake_TEST_FAILED "Unexpected rebuild of proj2 configure step (${PROJ2_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ2_CONFIGURE_TIMESTAMP_AFTER})") + return() +endif() + +if(PROJ2_BUILD_TIMESTAMP_BEFORE EQUAL PROJ2_BUILD_TIMESTAMP_AFTER) + set(RunCMake_TEST_FAILED "proj2 build step did not rebuild (${PROJ2_BUILD_TIMESTAMP_BEFORE} != ${PROJ2_BUILD_TIMESTAMP_AFTER})") + return() +endif() diff --git a/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake new file mode 100644 index 0000000..c86a60e --- /dev/null +++ b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake @@ -0,0 +1,28 @@ +include(ExternalProject) + +# Given this setup, on the first build, both configure steps and both build +# steps will run. On a noop rebuild, only the build steps will run. Without +# CONFIGURE_HANDLED_BY_BUILD, the configure step of proj2 would also run on a +# noop rebuild. + +ExternalProject_Add(proj1 + DOWNLOAD_COMMAND "" + SOURCE_DIR "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something" + # file(TIMESTAMP) gives back the timestamp in seconds so we sleep a second to + # make sure we get a different timestamp on the stamp file + BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1 + INSTALL_COMMAND "" + BUILD_ALWAYS ON + STAMP_DIR "stamp" +) +ExternalProject_Add(proj2 + DOWNLOAD_COMMAND "" + SOURCE_DIR "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something" + BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1 + INSTALL_COMMAND "" + CONFIGURE_HANDLED_BY_BUILD ON + DEPENDS proj1 + STAMP_DIR "stamp" +) diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake index 598671f..22b8d24 100644 --- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake @@ -151,3 +151,33 @@ endif() if(doSubstitutionTest) __ep_test_with_build(Substitutions) endif() + +function(__ep_test_CONFIGURE_HANDLED_BY_BUILD) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CONFIGURE_HANDLED_BY_BUILD-build) + run_cmake(CONFIGURE_HANDLED_BY_BUILD) + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(BUILD_CONFIG --config Debug) + set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp/Debug") + else() + set(BUILD_CONFIG "") + set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp") + endif() + + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-build ${CMAKE_COMMAND} --build . ${BUILD_CONFIG}) + + # Calculate timestamps before rebuilding so we can compare before and after in + # CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake + + file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_BEFORE "%s") + # When BUILD_ALWAYS is set, the build stamp is never created. + file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_BEFORE "%s") + file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_BEFORE "%s") + + run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-rebuild ${CMAKE_COMMAND} --build . ${BUILD_CONFIG}) +endfunction() + +if(NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 ") + __ep_test_CONFIGURE_HANDLED_BY_BUILD() +endif() diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake index 2bb2765..ae3d179 100644 --- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake +++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake @@ -24,6 +24,7 @@ function(check_python case) file(GLOB index ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/index-*.json) execute_process( COMMAND ${PYTHON_EXECUTABLE} "${RunCMake_SOURCE_DIR}/${case}-check.py" "${index}" "${CMAKE_CXX_COMPILER_ID}" + "${RunCMake_TEST_BINARY_DIR}" RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE output @@ -62,3 +63,4 @@ endfunction() run_object(codemodel-v2) run_object(cache-v2) run_object(cmakeFiles-v1) +run_object(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-check.cmake new file mode 100644 index 0000000..ce38461 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/query.json + reply + reply/index-[0-9.T-]+.json + reply/toolchains-v1-[0-9a-f]+.json + ) +check_api("^${expect}$") + +check_python(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-prep.cmake new file mode 100644 index 0000000..ca62edf --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateful-prep.cmake @@ -0,0 +1,4 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[ +{ "requests": [ { "kind": "toolchains", "version" : 1 } ] } +]]) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-check.cmake new file mode 100644 index 0000000..4676dd8 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/toolchains-v1 + reply + reply/index-[0-9.T-]+.json + reply/toolchains-v1-[0-9a-f]+.json + ) +check_api("^${expect}$") + +check_python(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-prep.cmake new file mode 100644 index 0000000..7edff93 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-ClientStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/toolchains-v1" "") diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-check.cmake new file mode 100644 index 0000000..8e83758 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-check.cmake @@ -0,0 +1,10 @@ +set(expect + query + query/toolchains-v1 + reply + reply/index-[0-9.T-]+.json + reply/toolchains-v1-[0-9a-f]+.json + ) +check_api("^${expect}$") + +check_python(toolchains-v1) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-prep.cmake new file mode 100644 index 0000000..2db73a1 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-SharedStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/toolchains-v1" "") diff --git a/Tests/RunCMake/FileAPI/toolchains-v1-check.py b/Tests/RunCMake/FileAPI/toolchains-v1-check.py new file mode 100644 index 0000000..a0e50c2 --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1-check.py @@ -0,0 +1,86 @@ +from check_index import * +import os + +class ExpectedVar(object): + def __init__(self, name): + self.name = name + +class ExpectedList(object): + def __init__(self, name): + self.name = name + +EXPECTED_TOOLCHAIN = { + "language": "CXX", + "compiler": { + "path": ExpectedVar("CMAKE_CXX_COMPILER"), + "id": ExpectedVar("CMAKE_CXX_COMPILER_ID"), + "version": ExpectedVar("CMAKE_CXX_COMPILER_VERSION"), + "target": ExpectedVar("CMAKE_CXX_COMPILER_TARGET"), + "implicit": { + "includeDirectories": \ + ExpectedList("CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES"), + "linkDirectories": \ + ExpectedList("CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES"), + "linkFrameworkDirectories": \ + ExpectedList( + "CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES"), + "linkLibraries": \ + ExpectedList("CMAKE_CXX_IMPLICIT_LINK_LIBRARIES"), + } + }, + "sourceFileExtensions": \ + ExpectedList("CMAKE_CXX_SOURCE_FILE_EXTENSIONS"), +} + +def check_objects(o): + assert is_list(o) + assert len(o) == 1 + check_index_object(o[0], "toolchains", 1, 0, check_object_toolchains) + +def check_object_toolchains(o): + assert sorted(o.keys()) == ["kind", "toolchains", "version"] + # The "kind" and "version" members are handled by check_index_object. + toolchains = o["toolchains"] + assert is_list(toolchains) + + # Other platform-specific toolchains may exist (like RC on Windows). + has_cxx_toolchain = False + for toolchain in toolchains: + assert is_dict(toolchain) + assert "language" in toolchain + if toolchain["language"] == "CXX": + check_object_toolchain(toolchain, EXPECTED_TOOLCHAIN) + has_cxx_toolchain = True + + assert has_cxx_toolchain + +def check_object_toolchain(o, expected): + expected_keys = [ + key for (key, value) in expected.items() + if is_string(value) or is_dict(value) + or (type(value) in (ExpectedVar, ExpectedList) + and variables[value.name]["defined"])] + assert sorted(o.keys()) == sorted(expected_keys) + + for key in expected_keys: + value = expected[key] + if is_string(value): + assert o[key] == value + elif is_dict(value): + check_object_toolchain(o[key], value) + elif type(value) == ExpectedVar: + assert o[key] == variables[value.name]["value"] + elif type(value) == ExpectedList: + expected_items = filter( + None, variables[value.name]["value"].split(";")) + check_list_match(lambda a, b: a == b, o[key], expected_items) + else: + assert False + +with open(os.path.join(sys.argv[3], "toolchain_variables.json")) as f: + variables = json.load(f) + +assert is_dict(variables) +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/toolchains-v1.cmake b/Tests/RunCMake/FileAPI/toolchains-v1.cmake new file mode 100644 index 0000000..367aade --- /dev/null +++ b/Tests/RunCMake/FileAPI/toolchains-v1.cmake @@ -0,0 +1,22 @@ +enable_language(CXX) + +set(variable_suffixes + COMPILER COMPILER_ID COMPILER_VERSION COMPILER_TARGET + IMPLICIT_INCLUDE_DIRECTORIES IMPLICIT_LINK_DIRECTORIES + IMPLICIT_LINK_FRAMEWORK_DIRECTORIES IMPLICIT_LINK_LIBRARIES + SOURCE_FILE_EXTENSIONS) +set(language CXX) +set(json "{}") + +foreach(variable_suffix ${variable_suffixes}) + set(variable "CMAKE_${language}_${variable_suffix}") + string(JSON json SET "${json}" "${variable}" "{}") + if(DEFINED "${variable}") + string(JSON json SET "${json}" "${variable}" "defined" "true") + string(JSON json SET "${json}" "${variable}" "value" "\"${${variable}}\"") + else() + string(JSON json SET "${json}" "${variable}" "defined" "false") + endif() +endforeach() + +file(WRITE ${CMAKE_BINARY_DIR}/toolchain_variables.json "${json}") diff --git a/Tests/RunCMake/install/FILES-RENAME-all-check.cmake b/Tests/RunCMake/install/FILES-RENAME-all-check.cmake new file mode 100644 index 0000000..7e9b103 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-all-check.cmake @@ -0,0 +1 @@ +check_installed([[^src;src/script_Debug\.ps]]) diff --git a/Tests/RunCMake/install/FILES-RENAME-bad-result.txt b/Tests/RunCMake/install/FILES-RENAME-bad-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-bad-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt b/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt new file mode 100644 index 0000000..9844158 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt @@ -0,0 +1,6 @@ +CMake Error: + Error evaluating generator expression: + + \$<NOTAGENEX> + + Expression did not evaluate to a known generator expression diff --git a/Tests/RunCMake/install/FILES-RENAME-bad.cmake b/Tests/RunCMake/install/FILES-RENAME-bad.cmake new file mode 100644 index 0000000..5be0bb2 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME-bad.cmake @@ -0,0 +1,4 @@ +install(FILES empty.c + DESTINATION mybin + RENAME $<NOTAGENEX> + ) diff --git a/Tests/RunCMake/install/FILES-RENAME.cmake b/Tests/RunCMake/install/FILES-RENAME.cmake new file mode 100644 index 0000000..5896e64 --- /dev/null +++ b/Tests/RunCMake/install/FILES-RENAME.cmake @@ -0,0 +1,4 @@ +install(FILES script.bat + DESTINATION src + RENAME script_$<CONFIG>.ps + ) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index d64d88b..b067b3a 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -74,6 +74,7 @@ run_cmake(SkipInstallRulesNoWarning2) run_cmake(DIRECTORY-DIRECTORY-bad) run_cmake(DIRECTORY-DESTINATION-bad) run_cmake(FILES-DESTINATION-bad) +run_cmake(FILES-RENAME-bad) run_cmake(TARGETS-DESTINATION-bad) run_cmake(EXPORT-OldIFace) run_cmake(EXPORT-UnknownExport) @@ -91,6 +92,10 @@ run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc) run_cmake(FILES-DESTINATION-TYPE) run_cmake(DIRECTORY-DESTINATION-TYPE) +set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug") +run_install_test(FILES-RENAME) +unset(RunCMake_TEST_OPTIONS) + if(APPLE) run_cmake(TARGETS-Apple-Defaults) endif() diff --git a/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake b/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake new file mode 100644 index 0000000..f69c024 --- /dev/null +++ b/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake @@ -0,0 +1,5 @@ +set(ls "a" "b" "c") +list(REMOVE_ITEM ls alpha) +if (NOT ls STREQUAL "a;b;c") + message(FATAL_ERROR "list(REMOVE_ITEM) modified for empty item") +endif () diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake index b4a91bc..c11891c 100644 --- a/Tests/RunCMake/list/RunCMakeTest.cmake +++ b/Tests/RunCMake/list/RunCMakeTest.cmake @@ -30,6 +30,7 @@ run_cmake(FILTER-NotList) run_cmake(REMOVE_AT-NotList) run_cmake(REMOVE_DUPLICATES-NotList) run_cmake(REMOVE_ITEM-NotList) +run_cmake(REMOVE_ITEM-NoItemArg) run_cmake(REVERSE-NotList) run_cmake(SORT-NotList) |