diff options
270 files changed, 4465 insertions, 2949 deletions
diff --git a/.gitlab/ci/wix.ps1 b/.gitlab/ci/wix.ps1 index a69053303e..b7cb3f3402 100755 --- a/.gitlab/ci/wix.ps1 +++ b/.gitlab/ci/wix.ps1 @@ -2,13 +2,14 @@ $erroractionpreference = "stop" $release = "v3.14.0.6526" $sha256sum = "4C89898DF3BCAB13E12F7CA54399C35AD273475AD2CB6284611D00AE2D063C2C" -$filename = "wix314-binaries" +$filename = "wix-3.14.0.6526-win-i386" $tarball = "$filename.zip" $outdir = $pwd.Path $outdir = "$outdir\.gitlab" $ProgressPreference = 'SilentlyContinue' -Invoke-WebRequest -Uri "https://wixtoolset.org/downloads/$release/$tarball" -OutFile "$outdir\$tarball" +#Invoke-WebRequest -Uri "https://wixtoolset.org/downloads/$release/$tarball" -OutFile "$outdir\$tarball" +Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/$tarball" -OutFile "$outdir\$tarball" $hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 if ($hash.Hash -ne $sha256sum) { exit 1 diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 3183bf7ff9..5632f6df3a 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -7,7 +7,7 @@ GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci ext/$CI_CONCURRENT_ID" # TODO: Factor this out so that each job selects the Xcode version to # use so that different versions can be tested in a single pipeline. - DEVELOPER_DIR: "/Applications/Xcode-13.3.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode-13.4.app/Contents/Developer" # Avoid conflicting with other projects running on the same machine. SCCACHE_SERVER_PORT: 4227 @@ -95,7 +95,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos - shell - - xcode-13.3 + - xcode-13.4 - nonconcurrent .macos_x86_64_builder_tags_package: @@ -103,7 +103,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos - shell - - xcode-13.3 + - xcode-13.4 - nonconcurrent - finder @@ -112,7 +112,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos - shell - - xcode-13.3 + - xcode-13.4 - concurrent .macos_arm64_builder_tags: @@ -120,7 +120,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-13.3 + - xcode-13.4 - nonconcurrent .macos_arm64_builder_ext_tags: @@ -128,7 +128,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-13.3 + - xcode-13.4 - concurrent ## macOS-specific scripts diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index 49026a33d1..967e1bfbe0 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in @@ -74,6 +74,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*rand.*may return deterministic values" "cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*rand.*isn.*t random" # we do not do crypto "cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*srand.*seed choices are.*poor" # we do not do crypto + "cmFindPackageCommand.cxx.*: warning #177-D: parameter .* was declared but never referenced" "IPA warning: function.*multiply defined in" "LICENSE WARNING" # PGI license expiry. Not useful in nightly testing. diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst index 6fd3cf6888..eb7da07e38 100644 --- a/Help/command/cmake_path.rst +++ b/Help/command/cmake_path.rst @@ -482,8 +482,9 @@ are :ref:`normalized <Normalization>` before the check. cmake_path(COMPARE <input1> NOT_EQUAL <input2> <out-var>) Compares the lexical representations of two paths provided as string literals. -No normalization is performed on either path. Equality is determined -according to the following pseudo-code logic: +No normalization is performed on either path, except multiple consecutive +directory separators are effectively collapsed into a single separator. +Equality is determined according to the following pseudo-code logic: :: diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index 82fcd46a51..d4ba4654db 100644 --- a/Help/command/execute_process.rst +++ b/Help/command/execute_process.rst @@ -37,8 +37,15 @@ Options: ``COMMAND`` A child process command line. - CMake executes the child process using operating system APIs directly. - All arguments are passed VERBATIM to the child process. + CMake executes the child process using operating system APIs directly: + + * On POSIX platforms, the command line is passed to the + child process in an ``argv[]`` style array. + + * On Windows platforms, the command line is encoded as a string such + that child processes using ``CommandLineToArgvW`` will decode the + original arguments. + No intermediate shell is used, so shell operators such as ``>`` are treated as normal arguments. (Use the ``INPUT_*``, ``OUTPUT_*``, and ``ERROR_*`` options to diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index 35e3c51a92..ee52c4189c 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst @@ -103,8 +103,9 @@ Basic Signature [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)] + [GLOBAL] [NO_POLICY_SCOPE] - [GLOBAL]) + [BYPASS_PROVIDER]) The basic signature is supported by both Module and Config modes. The ``MODULE`` keyword implies that only Module mode can be used to find @@ -181,6 +182,14 @@ only take the single version at the lower end of the range into account. See the :command:`cmake_policy` command documentation for discussion of the ``NO_POLICY_SCOPE`` option. +.. versionadded:: 3.24 + The ``BYPASS_PROVIDER`` keyword is only allowed when ``find_package()`` is + being called by a :ref:`dependency provider <dependency_providers>`. + It can be used by providers to call the built-in ``find_package()`` + implementation directly and prevent that call from being re-routed back to + itself. Future versions of CMake may detect attempts to use this keyword + from places other than a dependency provider and halt with a fatal error. + .. _`full signature`: Full Signature @@ -192,8 +201,9 @@ Full Signature [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [CONFIG|NO_MODULE] - [NO_POLICY_SCOPE] [GLOBAL] + [NO_POLICY_SCOPE] + [BYPASS_PROVIDER] [NAMES name1 [name2 ...]] [CONFIGS config1 [config2 ...]] [HINTS path1 [path2 ... ]] diff --git a/Help/command/foreach.rst b/Help/command/foreach.rst index d9f54ca750..ddf8dfa84f 100644 --- a/Help/command/foreach.rst +++ b/Help/command/foreach.rst @@ -130,3 +130,11 @@ yields -- en=two, ba=dua -- en=three, ba=tiga -- en=four, ba= + +See Also +^^^^^^^^ + +* :command:`break` +* :command:`continue` +* :command:`endforeach` +* :command:`while` diff --git a/Help/command/function.rst b/Help/command/function.rst index 3d25aa45f0..fc55c034ff 100644 --- a/Help/command/function.rst +++ b/Help/command/function.rst @@ -73,3 +73,9 @@ argument. Referencing to ``ARGV#`` arguments beyond ``ARGC`` have undefined behavior. Checking that ``ARGC`` is greater than ``#`` is the only way to ensure that ``ARGV#`` was passed to the function as an extra argument. + +See Also +^^^^^^^^ + +* :command:`endfunction` +* :command:`return` diff --git a/Help/command/if.rst b/Help/command/if.rst index c096725236..b72769f6d1 100644 --- a/Help/command/if.rst +++ b/Help/command/if.rst @@ -320,13 +320,18 @@ Path Comparisons .. _PATH_EQUAL: ``if(<variable|string> PATH_EQUAL <variable|string>)`` - .. versionadded:: 3.24 - Compares the lexical representations of two paths provided as string - literals or variables. No normalization is performed on either path. + .. versionadded:: 3.24 - Lexical comparison has the advantage over string comparison to have a - knowledge of the structure of the path. So, the following comparison is - ``TRUE`` using ``PATH_EQUAL`` operator, but ``FALSE`` with ``STREQUAL``: + Compares the two paths component-by-component. Only if every component of + both paths match will the two paths compare equal. Multiple path separators + are effectively collapsed into a single separator, but note that backslashes + are not converted to forward slashes. No other + :ref:`path normalization <Normalization>` is performed. + + Component-wise comparison is superior to string-based comparison due to the + handling of multiple path separators. In the following example, the + expression evaluates to true using ``PATH_EQUAL``, but false with + ``STREQUAL``: .. code-block:: cmake @@ -419,3 +424,10 @@ There is no automatic evaluation for environment or cache :ref:`Variable References`. Their values must be referenced as ``$ENV{<name>}`` or ``$CACHE{<name>}`` wherever the above-documented condition syntax accepts ``<variable|string>``. + +See also +^^^^^^^^ + + * :command:`else` + * :command:`elseif` + * :command:`endif` diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst index 4699a08574..a079307508 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. diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst index fc41cdd7b7..125b0acb32 100644 --- a/Help/command/try_run.rst +++ b/Help/command/try_run.rst @@ -19,6 +19,8 @@ Try Compiling and Running Source Files [LINK_LIBRARIES <libs>...] [COMPILE_OUTPUT_VARIABLE <var>] [RUN_OUTPUT_VARIABLE <var>] + [RUN_OUTPUT_STDOUT_VARIABLE <var>] + [RUN_OUTPUT_STDERR_VARIABLE <var>] [OUTPUT_VARIABLE <var>] [WORKING_DIRECTORY <var>] [ARGS <args>...]) @@ -70,6 +72,16 @@ The options are: ``RUN_OUTPUT_VARIABLE <var>`` Report the output from running the executable in a given variable. +``RUN_OUTPUT_STDOUT_VARIABLE <var>`` + .. versionadded:: 3.25 + + Report the output of stdout from running the executable in a given variable. + +``RUN_OUTPUT_STDERR_VARIABLE <var>`` + .. versionadded:: 3.25 + + Report the output of stderr from running the executable in a given variable. + ``WORKING_DIRECTORY <var>`` .. versionadded:: 3.20 @@ -110,6 +122,7 @@ These cache entries are: In order to make cross compiling your project easier, use ``try_run`` only if really required. If you use ``try_run``, use the +``RUN_OUTPUT_STDOUT_VARIABLE``, ``RUN_OUTPUT_STDERR_VARIABLE``, ``RUN_OUTPUT_VARIABLE`` or ``OUTPUT_VARIABLE`` options only if really required. Using them will require that when cross-compiling, the cache variables will have to be set manually to the output of the executable. diff --git a/Help/command/while.rst b/Help/command/while.rst index a4957c1dbc..0bafae5ada 100644 --- a/Help/command/while.rst +++ b/Help/command/while.rst @@ -23,3 +23,11 @@ Per legacy, the :command:`endwhile` command admits an optional ``<condition>`` argument. If used, it must be a verbatim repeat of the argument of the opening ``while`` command. + +See Also +^^^^^^^^ + + * :command:`break` + * :command:`continue` + * :command:`foreach` + * :command:`endwhile` diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst index cf1ec01860..d57cc358b7 100644 --- a/Help/guide/tutorial/A Basic Starting Point.rst +++ b/Help/guide/tutorial/A Basic Starting Point.rst @@ -19,8 +19,19 @@ required. This will be the starting point for our tutorial. Create a add_executable(Tutorial tutorial.cxx) -Note that this example uses lower case commands in the ``CMakeLists.txt`` file. -Upper, lower, and mixed case commands are supported by CMake. The source +Any project's top most ``CMakeLists.txt`` must start by specifying +a minimum CMake version using :command:`cmake_minimum_required`. This ensures +that the later CMake functions are run with a compatible version of CMake. + +To start a project, we use :command:`project` to set the project name. This +call is required with every project and should be called soon after +:command:`cmake_minimum_required`. + +Lastly, we use :command:`add_executable` to specify we want an executable +named Tutorial generated using ``tutorial.cxx`` as the source. + +Note that this example uses lower case commands in the ``CMakeLists.txt`` +file. Upper, lower, and mixed case commands are supported by CMake. The source code for ``tutorial.cxx`` is provided in the ``Step1`` directory and can be used to compute the square root of a number. @@ -79,7 +90,7 @@ to set the project name and version number. :language: cmake :end-before: # specify the C++ standard -Then, configure a header file to pass the version number to the source +Then use :command:`configure_file` to pass the version number to the source code: .. literalinclude:: Step2/CMakeLists.txt @@ -91,7 +102,8 @@ code: Since the configured file will be written into the binary tree, we must add that directory to the list of paths to search for include -files. Add the following lines to the end of the ``CMakeLists.txt`` file: +files. Use :command:`target_include_directories` to add the following lines to +the end of the ``CMakeLists.txt`` file: .. literalinclude:: Step2/CMakeLists.txt :caption: CMakeLists.txt @@ -107,9 +119,9 @@ directory with the following contents: :name: TutorialConfig.h.in :language: c++ -When CMake configures this header file the values for +When CMake configures this header file, the values for ``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will be -replaced. +replaced with the corresponding version numbers from the project. Next modify ``tutorial.cxx`` to include the configured header file, ``TutorialConfig.h``. @@ -141,7 +153,7 @@ Next let's add some C++11 features to our project by replacing ``atof`` with We will need to explicitly state in the CMake code that it should use the correct flags. The easiest way to enable support for a specific C++ standard in CMake is by using the :variable:`CMAKE_CXX_STANDARD` variable. For this -tutorial, set the :variable:`CMAKE_CXX_STANDARD` variable in the +tutorial, :command:`set` the :variable:`CMAKE_CXX_STANDARD` variable in the ``CMakeLists.txt`` file to ``11`` and :variable:`CMAKE_CXX_STANDARD_REQUIRED` to ``True``. Make sure to add the ``CMAKE_CXX_STANDARD`` declarations above the call to ``add_executable``. diff --git a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst index c6e0fd0b3b..9ec195c8f7 100644 --- a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst +++ b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst @@ -1,4 +1,4 @@ -Step 8: Adding Support for a Testing Dashboard +Step 5: Adding Support for a Testing Dashboard ============================================== Adding support for submitting our test results to a dashboard is simple. We @@ -9,21 +9,21 @@ we include the :module:`CTest` module in our top-level ``CMakeLists.txt``. Replace: -.. code-block:: cmake +.. literalinclude:: Step5/CMakeLists.txt :caption: CMakeLists.txt :name: CMakeLists.txt-enable_testing-remove - - # enable testing - enable_testing() + :language: cmake + :start-after: # enable testing + :end-before: # does the application run With: -.. code-block:: cmake +.. literalinclude:: Step6/CMakeLists.txt :caption: CMakeLists.txt :name: CMakeLists.txt-include-CTest - - # enable dashboard scripting - include(CTest) + :language: cmake + :start-after: # enable testing + :end-before: # does the application run The :module:`CTest` module will automatically call ``enable_testing()``, so we can remove it from our CMake files. @@ -46,7 +46,7 @@ downloaded from the ``Settings`` page of the project on the CDash instance that will host and display the test results. Once downloaded from CDash, the file should not be modified locally. -.. literalinclude:: Step9/CTestConfig.cmake +.. literalinclude:: Step6/CTestConfig.cmake :caption: CTestConfig.cmake :name: CTestConfig.cmake :language: cmake diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst index 8db0cb8f33..710e9793af 100644 --- a/Help/guide/tutorial/Adding System Introspection.rst +++ b/Help/guide/tutorial/Adding System Introspection.rst @@ -1,4 +1,4 @@ -Step 5: Adding System Introspection +Step 6: Adding System Introspection =================================== Let us consider adding some code to our project that depends on features the @@ -15,7 +15,7 @@ these functions using the :module:`CheckCXXSourceCompiles` module in Add the checks for ``log`` and ``exp`` to ``MathFunctions/CMakeLists.txt``, after the call to :command:`target_include_directories`: -.. literalinclude:: Step6/MathFunctions/CMakeLists.txt +.. literalinclude:: Step7/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-check_cxx_source_compiles :language: cmake @@ -25,7 +25,7 @@ after the call to :command:`target_include_directories`: If available, use :command:`target_compile_definitions` to specify ``HAVE_LOG`` and ``HAVE_EXP`` as ``PRIVATE`` compile definitions. -.. literalinclude:: Step6/MathFunctions/CMakeLists.txt +.. literalinclude:: Step7/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-target_compile_definitions :language: cmake @@ -37,7 +37,7 @@ compute the square root in the ``mysqrt`` function. Add the following code to the ``mysqrt`` function in ``MathFunctions/mysqrt.cxx`` (don't forget the ``#endif`` before returning the result!): -.. literalinclude:: Step6/MathFunctions/mysqrt.cxx +.. literalinclude:: Step7/MathFunctions/mysqrt.cxx :caption: MathFunctions/mysqrt.cxx :name: MathFunctions/mysqrt.cxx-ifdef :language: c++ @@ -46,7 +46,7 @@ the ``mysqrt`` function in ``MathFunctions/mysqrt.cxx`` (don't forget the We will also need to modify ``mysqrt.cxx`` to include ``cmath``. -.. literalinclude:: Step6/MathFunctions/mysqrt.cxx +.. literalinclude:: Step7/MathFunctions/mysqrt.cxx :caption: MathFunctions/mysqrt.cxx :name: MathFunctions/mysqrt.cxx-include-cmath :language: c++ diff --git a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst index 70c6695623..6dc47617f1 100644 --- a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst +++ b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst @@ -1,4 +1,4 @@ -Step 6: Adding a Custom Command and Generated File +Step 7: Adding a Custom Command and Generated File ================================================== Suppose, for the purpose of this tutorial, we decide that we never want to use @@ -26,7 +26,7 @@ accomplish this. First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for ``MakeTable`` is added as any other executable would be added. -.. literalinclude:: Step7/MathFunctions/CMakeLists.txt +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-add_executable-MakeTable :language: cmake @@ -36,7 +36,7 @@ First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for Then we add a custom command that specifies how to produce ``Table.h`` by running MakeTable. -.. literalinclude:: Step7/MathFunctions/CMakeLists.txt +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-add_custom_command-Table.h :language: cmake @@ -47,7 +47,7 @@ Next we have to let CMake know that ``mysqrt.cxx`` depends on the generated file ``Table.h``. This is done by adding the generated ``Table.h`` to the list of sources for the library MathFunctions. -.. literalinclude:: Step7/MathFunctions/CMakeLists.txt +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-add_library-Table.h :language: cmake @@ -57,7 +57,7 @@ of sources for the library MathFunctions. We also have to add the current binary directory to the list of include directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``. -.. literalinclude:: Step7/MathFunctions/CMakeLists.txt +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-target_include_directories-Table.h :language: cmake @@ -67,7 +67,7 @@ directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``. Now let's use the generated table. First, modify ``mysqrt.cxx`` to include ``Table.h``. Next, we can rewrite the ``mysqrt`` function to use the table: -.. literalinclude:: Step7/MathFunctions/mysqrt.cxx +.. literalinclude:: Step8/MathFunctions/mysqrt.cxx :caption: MathFunctions/mysqrt.cxx :name: MathFunctions/mysqrt.cxx :language: c++ diff --git a/Help/guide/tutorial/Packaging an Installer.rst b/Help/guide/tutorial/Packaging an Installer.rst index 0ee5db2aca..5f0b27a174 100644 --- a/Help/guide/tutorial/Packaging an Installer.rst +++ b/Help/guide/tutorial/Packaging an Installer.rst @@ -1,4 +1,4 @@ -Step 7: Packaging an Installer +Step 8: Packaging an Installer ============================== Next suppose that we want to distribute our project to other people so that @@ -11,7 +11,7 @@ installations and package management features. To accomplish this we will use CPack to create platform specific installers. Specifically we need to add a few lines to the bottom of our top-level ``CMakeLists.txt`` file. -.. literalinclude:: Step8/CMakeLists.txt +.. literalinclude:: Step9/CMakeLists.txt :caption: CMakeLists.txt :name: CMakeLists.txt-include-CPack :language: cmake diff --git a/Help/guide/tutorial/Step5/CTestConfig.cmake b/Help/guide/tutorial/Step5/CTestConfig.cmake new file mode 100644 index 0000000000..73efdb1f6e --- /dev/null +++ b/Help/guide/tutorial/Step5/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt index 82d00c8d47..3ac5cd675a 100644 --- a/Help/guide/tutorial/Step6/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/CMakeLists.txt @@ -37,7 +37,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" ) # enable testing -enable_testing() +include(CTest) # does the application run add_test(NAME Runs COMMAND Tutorial 25) diff --git a/Help/guide/tutorial/Step6/CTestConfig.cmake b/Help/guide/tutorial/Step6/CTestConfig.cmake new file mode 100644 index 0000000000..73efdb1f6e --- /dev/null +++ b/Help/guide/tutorial/Step6/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt index 42e098af80..b12f27d94a 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt @@ -6,29 +6,6 @@ target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) -# does this system provide the log and exp functions? -include(CheckCXXSourceCompiles) -check_cxx_source_compiles(" - #include <cmath> - int main() { - std::log(1.0); - return 0; - } -" HAVE_LOG) -check_cxx_source_compiles(" - #include <cmath> - int main() { - std::exp(1.0); - return 0; - } -" HAVE_EXP) - -# add compile definitions -if(HAVE_LOG AND HAVE_EXP) - target_compile_definitions(MathFunctions - PRIVATE "HAVE_LOG" "HAVE_EXP") -endif() - # install rules install(TARGETS MathFunctions DESTINATION lib) install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx deleted file mode 100644 index ee585568cb..0000000000 --- a/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include <cmath> -#include <fstream> -#include <iostream> - -int main(int argc, char* argv[]) -{ - // make sure we have enough arguments - if (argc < 2) { - return 1; - } - - std::ofstream fout(argv[1], std::ios_base::out); - const bool fileOpen = fout.is_open(); - if (fileOpen) { - fout << "double sqrtTable[] = {" << std::endl; - for (int i = 0; i < 10; ++i) { - fout << sqrt(static_cast<double>(i)) << "," << std::endl; - } - // close the table with a zero - fout << "0};" << std::endl; - fout.close(); - } - return fileOpen ? 0 : 1; // return 0 if wrote the file -} diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx index 7eecd26b5d..abe767d5ae 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx @@ -1,4 +1,3 @@ -#include <cmath> #include <iostream> #include "MathFunctions.h" @@ -10,12 +9,6 @@ double mysqrt(double x) return 0; } - // if we have both log and exp then use them -#if defined(HAVE_LOG) && defined(HAVE_EXP) - double result = std::exp(std::log(x) * 0.5); - std::cout << "Computing sqrt of " << x << " to be " << result - << " using log and exp" << std::endl; -#else double result = x; // do ten iterations @@ -27,6 +20,5 @@ double mysqrt(double x) result = result + 0.5 * delta / result; std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; } -#endif return result; } diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt index 82d00c8d47..3ac5cd675a 100644 --- a/Help/guide/tutorial/Step7/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/CMakeLists.txt @@ -37,7 +37,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" ) # enable testing -enable_testing() +include(CTest) # does the application run add_test(NAME Runs COMMAND Tutorial 25) diff --git a/Help/guide/tutorial/Step7/CTestConfig.cmake b/Help/guide/tutorial/Step7/CTestConfig.cmake new file mode 100644 index 0000000000..73efdb1f6e --- /dev/null +++ b/Help/guide/tutorial/Step7/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step7/License.txt b/Help/guide/tutorial/Step7/License.txt deleted file mode 100644 index c62d00b9ea..0000000000 --- a/Help/guide/tutorial/Step7/License.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is the open source License.txt file introduced in -CMake/Tutorial/Step7... diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt index 9ede4b327c..42e098af80 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt @@ -1,29 +1,34 @@ -# first we add the executable that generates the table -add_executable(MakeTable MakeTable.cxx) - -# add the command to generate the source code -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h - COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h - DEPENDS MakeTable - ) - -# add the main library -add_library(MathFunctions - mysqrt.cxx - ${CMAKE_CURRENT_BINARY_DIR}/Table.h - ) +add_library(MathFunctions mysqrt.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. -# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the -# TutorialConfig.h include is an implementation detail -# state that we depend on our binary dir to find Table.h target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ) +# does this system provide the log and exp functions? +include(CheckCXXSourceCompiles) +check_cxx_source_compiles(" + #include <cmath> + int main() { + std::log(1.0); + return 0; + } +" HAVE_LOG) +check_cxx_source_compiles(" + #include <cmath> + int main() { + std::exp(1.0); + return 0; + } +" HAVE_EXP) + +# add compile definitions +if(HAVE_LOG AND HAVE_EXP) + target_compile_definitions(MathFunctions + PRIVATE "HAVE_LOG" "HAVE_EXP") +endif() + # install rules install(TARGETS MathFunctions DESTINATION lib) install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx index 7d80ee9648..7eecd26b5d 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx @@ -1,10 +1,8 @@ +#include <cmath> #include <iostream> #include "MathFunctions.h" -// include the generated table -#include "Table.h" - // a hack square root calculation using simple operations double mysqrt(double x) { @@ -12,12 +10,13 @@ double mysqrt(double x) return 0; } - // use the table to help find an initial value + // if we have both log and exp then use them +#if defined(HAVE_LOG) && defined(HAVE_EXP) + double result = std::exp(std::log(x) * 0.5); + std::cout << "Computing sqrt of " << x << " to be " << result + << " using log and exp" << std::endl; +#else double result = x; - if (x >= 1 && x < 10) { - std::cout << "Use the table to help find an initial value " << std::endl; - result = sqrtTable[static_cast<int>(x)]; - } // do ten iterations for (int i = 0; i < 10; ++i) { @@ -28,6 +27,6 @@ double mysqrt(double x) result = result + 0.5 * delta / result; std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; } - +#endif return result; } diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt index 4c78b94c0b..3ac5cd675a 100644 --- a/Help/guide/tutorial/Step8/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/CMakeLists.txt @@ -37,7 +37,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" ) # enable testing -enable_testing() +include(CTest) # does the application run add_test(NAME Runs COMMAND Tutorial 25) @@ -64,11 +64,3 @@ do_test(Tutorial 7 "7 is 2.645") do_test(Tutorial 25 "25 is 5") do_test(Tutorial -25 "-25 is (-nan|nan|0)") do_test(Tutorial 0.0001 "0.0001 is 0.01") - -# setup installer -include(InstallRequiredSystemLibraries) -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") -set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") -set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") -set(CPACK_SOURCE_GENERATOR "TGZ") -include(CPack) diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt index 6bae26e42b..b13a662837 100644 --- a/Help/guide/tutorial/Step9/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/CMakeLists.txt @@ -65,6 +65,7 @@ do_test(Tutorial 25 "25 is 5") do_test(Tutorial -25 "-25 is (-nan|nan|0)") do_test(Tutorial 0.0001 "0.0001 is 0.01") +# setup installer include(InstallRequiredSystemLibraries) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst index 09553cb28f..65d4829f8d 100644 --- a/Help/guide/tutorial/index.rst +++ b/Help/guide/tutorial/index.rst @@ -25,10 +25,10 @@ provides the complete solution for the previous step. Adding a Library Adding Usage Requirements for a Library Installing and Testing + Adding Support for a Testing Dashboard Adding System Introspection Adding a Custom Command and Generated File Packaging an Installer - Adding Support for a Testing Dashboard Selecting Static or Shared Libraries Adding Generator Expressions Adding Export Configuration diff --git a/Help/guide/using-dependencies/index.rst b/Help/guide/using-dependencies/index.rst index 6cab7ef479..bb519ad135 100644 --- a/Help/guide/using-dependencies/index.rst +++ b/Help/guide/using-dependencies/index.rst @@ -388,7 +388,7 @@ can still be built out-of-the-box. The developer, on the other hand, may be much more interested in controlling *how* a dependency is provided to the project. You might want to use a -particular version of a package that you built themself. You might want +particular version of a package that you built yourself. You might want to use a third party package manager. You might want to redirect some requests to a different URL on a system you control for security or performance reasons. CMake supports these sort of scenarios through diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 25e581ad21..7a6188aafe 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,input> + + .. 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. - - 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: +Export And Install Expressions +------------------------------ - .. 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: - - .. include:: ../variable/LINK_GROUP_PREDEFINED_FEATURES.txt +.. genex:: $<BUILD_INTERFACE:...> - Built-in and custom group features are defined in terms of the following - variables: + 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. - * :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>` +.. genex:: $<INSTALL_PREFIX> - 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: + 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. - 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. +Multi-level Expression Evaluation +--------------------------------- - 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-modules.7.rst b/Help/manual/cmake-modules.7.rst index 93beea9f8d..9dd623ace9 100644 --- a/Help/manual/cmake-modules.7.rst +++ b/Help/manual/cmake-modules.7.rst @@ -236,6 +236,7 @@ They are normally called through the :command:`find_package` command. /module/FindRuby /module/FindSDL /module/FindSDL_image + /module/FindSDL_gfx /module/FindSDL_mixer /module/FindSDL_net /module/FindSDL_sound diff --git a/Help/module/FindSDL_gfx.rst b/Help/module/FindSDL_gfx.rst new file mode 100644 index 0000000000..e05d661ea6 --- /dev/null +++ b/Help/module/FindSDL_gfx.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/FindSDL_gfx.cmake diff --git a/Help/policy/CMP0097.rst b/Help/policy/CMP0097.rst index 2240874e0f..24957d08d4 100644 --- a/Help/policy/CMP0097.rst +++ b/Help/policy/CMP0097.rst @@ -4,9 +4,11 @@ CMP0097 .. versionadded:: 3.16 :command:`ExternalProject_Add` with ``GIT_SUBMODULES ""`` initializes no -submodules. +submodules. The policy also applies to :command:`FetchContent_Declare`, +which uses the same download and update features as +:command:`ExternalProject_Add`. -The module provides a ``GIT_SUBMODULES`` option which controls what submodules +The commands provide a ``GIT_SUBMODULES`` option which controls what submodules to initialize and update. Starting with CMake 3.16, explicitly setting ``GIT_SUBMODULES`` to an empty string means no submodules will be initialized or updated. diff --git a/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst b/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst index a9af74d4bd..0749c6fb18 100644 --- a/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst +++ b/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst @@ -8,6 +8,8 @@ Enforce that link items that can be target names are actually existing targets. Set this property to a true value to enable additional checks on the contents of the :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES` target properties, typically populated by :command:`target_link_libraries`. +Checks are also applied to libraries added to a target through the +:prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` properties of its dependencies. CMake will verify that link items that might be target names actually name existing targets. An item is considered a possible target name if: diff --git a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst index 30c02f5b00..da461a79cd 100644 --- a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst +++ b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst @@ -7,13 +7,13 @@ Used to verify that all headers in a target's ``PUBLIC`` and ``INTERFACE`` header sets can be included on their own. When this property is set to true, and the target is an object library, static -library, shared library, or executable with exports enabled, and the target -has one or more ``PUBLIC`` or ``INTERFACE`` header sets, an object library -target named ``<target_name>_verify_interface_header_sets`` is created. This -verification target has one source file per header in the ``PUBLIC`` and -``INTERFACE`` header sets. Each source file only includes its associated -header file. The verification target links against the original target to get -all of its usage requirements. The verification target has its +library, shared library, interface library, or executable with exports enabled, +and the target has one or more ``PUBLIC`` or ``INTERFACE`` header sets, an +object library target named ``<target_name>_verify_interface_header_sets`` is +created. This verification target has one source file per header in the +``PUBLIC`` and ``INTERFACE`` header sets. Each source file only includes its +associated header file. The verification target links against the original +target to get all of its usage requirements. The verification target has its :prop_tgt:`EXCLUDE_FROM_ALL` and :prop_tgt:`DISABLE_PRECOMPILE_HEADERS` properties set to true, and its :prop_tgt:`AUTOMOC`, :prop_tgt:`AUTORCC`, :prop_tgt:`AUTOUIC`, and :prop_tgt:`UNITY_BUILD` properties set to false. @@ -22,7 +22,13 @@ If the header's :prop_sf:`LANGUAGE` property is set, the value of that property is used to determine the language with which to compile the header file. Otherwise, if the target has any C++ sources, the header is compiled as C++. Otherwise, if the target has any C sources, the header is compiled as C. -Otherwise, the header file is not compiled. +Otherwise, if C++ is enabled globally, the header is compiled as C++. +Otherwise, if C is enabled globally, the header is compiled as C. Otherwise, +the header file is not compiled. + +This property is initialized by the value of the +:variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` variable if it is set when +a target is created. If the project wishes to control which header sets are verified by this -property, you can set :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`. +property, it can set :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`. diff --git a/Help/release/3.22.rst b/Help/release/3.22.rst index 5ae3b574fb..eba5d66c39 100644 --- a/Help/release/3.22.rst +++ b/Help/release/3.22.rst @@ -170,8 +170,8 @@ Changes made since CMake 3.22.0 include the following. compatibility. The fix may be restored in a future version of CMake via a policy. -3.22.4, 3.22.5 --------------- +3.22.4, 3.22.5, 3.22.6 +---------------------- * These versions made no changes to documented features or interfaces. Some implementation updates were made to support ecosystem changes diff --git a/Help/release/3.23.rst b/Help/release/3.23.rst index 70a6175dab..69898e9574 100644 --- a/Help/release/3.23.rst +++ b/Help/release/3.23.rst @@ -309,3 +309,10 @@ Changes made since CMake 3.23.0 include the following. expected the ``CPACK_PACKAGEMAKER_CHOICES`` variable to be defined. The old ``CPACK_PACKAGEMAKER_CHOICES`` variable is now also set to the same content as it was before, but it is formally deprecated. + +3.23.3 +------ + +* This version made no changes to documented features or interfaces. + Some implementation updates were made to support ecosystem changes + and/or fix regressions. diff --git a/Help/release/3.24.rst b/Help/release/3.24.rst index f9e66b64e1..02252e0c59 100644 --- a/Help/release/3.24.rst +++ b/Help/release/3.24.rst @@ -220,6 +220,10 @@ Modules gained a ``NO_IMPLICIT_LINK_TO_MATLAB_LIBRARIES`` option to disable automatic linking of MATLAB libraries. +* The :module:`FindVulkan` module now supports components to select which + VulkanSDK tool and libraries to find in addition to the Vulkan SDK headers + and library. + * The :module:`FindZLIB` gained a new ``ZLIB_USE_STATIC_LIBS`` variable to search only for static libraries. diff --git a/Help/release/dev/FindVulkan-dxc.rst b/Help/release/dev/FindVulkan-dxc.rst new file mode 100644 index 0000000000..e22f016f37 --- /dev/null +++ b/Help/release/dev/FindVulkan-dxc.rst @@ -0,0 +1,5 @@ +FindVulkan-dxc +-------------- + +* The :module:`FindVulkan` module gained support for a DirectX Shader Compiler + component, ``dxc``. diff --git a/Help/release/dev/try_run_split_output.rst b/Help/release/dev/try_run_split_output.rst new file mode 100644 index 0000000000..98aedd64f6 --- /dev/null +++ b/Help/release/dev/try_run_split_output.rst @@ -0,0 +1,6 @@ +try_run_split_output +-------------------- + +* The :command:`try_run` command gained ``RUN_OUTPUT_STDOUT_VARIABLE`` + and ``RUN_OUTPUT_STDERR_VARIABLE`` options to capture stdout and stderr + separately from the output of the compiled program. diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index df40c859d4..27070c05a4 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake @@ -1189,19 +1189,18 @@ function(FetchContent_Declare contentName) ) endif() - set(options "") + # Because we are only looking for a subset of the supported keywords, we + # cannot check for multi-value arguments with this method. We will have to + # handle the URL keyword differently. set(oneValueArgs SVN_REPOSITORY DOWNLOAD_NO_EXTRACT DOWNLOAD_EXTRACT_TIMESTAMP - URL BINARY_DIR SOURCE_DIR ) - set(multiValueArgs "") - cmake_parse_arguments(PARSE_ARGV 1 ARG - "${options}" "${oneValueArgs}" "${multiValueArgs}") + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "${oneValueArgs}" "") string(TOLOWER ${contentName} contentNameLower) @@ -1230,31 +1229,45 @@ function(FetchContent_Declare contentName) # explicitly set the relevant option if not already provided. The condition # here is essentially an abbreviated version of the logic in # ExternalProject's _ep_add_download_command() function. - if(ARG_URL AND - NOT IS_DIRECTORY "${ARG_URL}" AND - NOT ARG_DOWNLOAD_NO_EXTRACT AND + if(NOT ARG_DOWNLOAD_NO_EXTRACT AND NOT DEFINED ARG_DOWNLOAD_EXTRACT_TIMESTAMP) - cmake_policy(GET CMP0135 _FETCHCONTENT_CMP0135 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - if(_FETCHCONTENT_CMP0135 STREQUAL "") - message(AUTHOR_WARNING - "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy " - "CMP0135 is not set. The policy's OLD behavior will be used. " - "When using a URL download, the timestamps of extracted files " - "should preferably be that of the time of extraction, otherwise " - "code that depends on the extracted contents might not be " - "rebuilt if the URL changes. The OLD behavior preserves the " - "timestamps from the archive instead, but this is usually not " - "what you want. Update your project to the NEW behavior or " - "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of " - "true to avoid this robustness issue." - ) - set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE) - elseif(_FETCHCONTENT_CMP0135 STREQUAL "NEW") - set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP FALSE) - else() - set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE) + list(FIND ARGN URL urlIndex) + if(urlIndex GREATER_EQUAL 0) + math(EXPR urlIndex "${urlIndex} + 1") + list(LENGTH ARGN numArgs) + if(urlIndex GREATER_EQUAL numArgs) + message(FATAL_ERROR + "URL keyword needs to be followed by at least one URL" + ) + endif() + # If we have multiple URLs, none of them are allowed to be local paths. + # Therefore, we can test just the first URL, and if it is non-local, so + # will be the others if there are more. + list(GET ARGN ${urlIndex} firstUrl) + if(NOT IS_DIRECTORY "${firstUrl}") + cmake_policy(GET CMP0135 _FETCHCONTENT_CMP0135 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(_FETCHCONTENT_CMP0135 STREQUAL "") + message(AUTHOR_WARNING + "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy " + "CMP0135 is not set. The policy's OLD behavior will be used. " + "When using a URL download, the timestamps of extracted files " + "should preferably be that of the time of extraction, otherwise " + "code that depends on the extracted contents might not be " + "rebuilt if the URL changes. The OLD behavior preserves the " + "timestamps from the archive instead, but this is usually not " + "what you want. Update your project to the NEW behavior or " + "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of " + "true to avoid this robustness issue." + ) + set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE) + elseif(_FETCHCONTENT_CMP0135 STREQUAL "NEW") + set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP FALSE) + else() + set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE) + endif() + endif() endif() endif() diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake index 279e2634c4..acb87dc605 100644 --- a/Modules/FindCURL.cmake +++ b/Modules/FindCURL.cmake @@ -82,7 +82,6 @@ find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(PC_CURL QUIET libcurl) if(PC_CURL_FOUND) - set(CURL_VERSION_STRING ${PC_CURL_VERSION}) pkg_get_variable(CURL_SUPPORTED_PROTOCOLS libcurl supported_protocols) pkg_get_variable(CURL_SUPPORTED_FEATURES libcurl supported_features) endif() @@ -122,7 +121,7 @@ if(NOT CURL_LIBRARY) select_library_configurations(CURL) endif() -if(CURL_INCLUDE_DIR AND NOT CURL_VERSION_STRING) +if(CURL_INCLUDE_DIR) foreach(_curl_version_header curlver.h curl.h) if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}") file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"") diff --git a/Modules/FindCoin3D.cmake b/Modules/FindCoin3D.cmake index 301e70bd6e..5910ad1e47 100644 --- a/Modules/FindCoin3D.cmake +++ b/Modules/FindCoin3D.cmake @@ -31,11 +31,11 @@ if (WIN32) "[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/include" ) - find_library(COIN3D_LIBRARY_DEBUG coin2d + find_library(COIN3D_LIBRARY_DEBUG NAMES coin2d coin4d "[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/lib" ) - find_library(COIN3D_LIBRARY_RELEASE coin2 + find_library(COIN3D_LIBRARY_RELEASE NAMES coin2 coin4 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/lib" ) diff --git a/Modules/FindGTest.cmake b/Modules/FindGTest.cmake index 60bb401032..92334e44e8 100644 --- a/Modules/FindGTest.cmake +++ b/Modules/FindGTest.cmake @@ -314,7 +314,7 @@ if(GTest_FOUND) __gtest_define_backwards_compatible_library_targets() endif() -if(GMock_FOUND) +if(GMock_FOUND AND GTest_FOUND) if(NOT TARGET GTest::gmock) __gtest_determine_library_type(GMOCK_LIBRARY) add_library(GTest::gmock ${GMOCK_LIBRARY_TYPE} IMPORTED) diff --git a/Modules/FindOpenACC.cmake b/Modules/FindOpenACC.cmake index cf58f3b807..71bd84ff0e 100644 --- a/Modules/FindOpenACC.cmake +++ b/Modules/FindOpenACC.cmake @@ -27,6 +27,13 @@ The module provides :prop_tgt:`IMPORTED` targets: Variables ^^^^^^^^^ +The module defines the following variables: + +``OpenACC_FOUND`` + .. versionadded:: 3.25 + + Variable indicating that OpenACC flags for at least one languages have been found. + This module will set the following variables per language in your project, where ``<lang>`` is one of C, CXX, or Fortran: @@ -270,6 +277,9 @@ foreach (LANG IN ITEMS C CXX Fortran) REQUIRED_VARS OpenACC_${LANG}_FLAGS VERSION_VAR OpenACC_${LANG}_VERSION ) + if(OpenACC_${LANG}_FOUND) + set(OpenACC_FOUND TRUE) + endif() endif() endforeach() diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index d19624e3a3..844ceb36a4 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -279,7 +279,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) DOC "Path to the ${_OPENMP_IMPLICIT_LIB_PLAIN} library for OpenMP" HINTS ${OpenMP_${LANG}_IMPLICIT_LINK_DIRS} CMAKE_FIND_ROOT_PATH_BOTH - NO_DEFAULT_PATH + NO_PACKAGE_ROOT_PATH + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH ) endif() mark_as_advanced(OpenMP_${_OPENMP_IMPLICIT_LIB_PLAIN}_LIBRARY) @@ -477,10 +479,14 @@ foreach(LANG IN ITEMS C CXX) if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND" OR NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND") _OPENMP_GET_FLAGS("${LANG}" "${LANG}" OpenMP_${LANG}_FLAGS_WORK OpenMP_${LANG}_LIB_NAMES_WORK) - set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}" - CACHE STRING "${LANG} compiler flags for OpenMP parallelization" FORCE) - set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}" - CACHE STRING "${LANG} compiler libraries for OpenMP parallelization" FORCE) + if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND") + set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}" + CACHE STRING "${LANG} compiler flags for OpenMP parallelization" FORCE) + endif() + if(NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND") + set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}" + CACHE STRING "${LANG} compiler libraries for OpenMP parallelization" FORCE) + endif() mark_as_advanced(OpenMP_${LANG}_FLAGS OpenMP_${LANG}_LIB_NAMES) endif() endif() @@ -496,10 +502,14 @@ if(CMAKE_Fortran_COMPILER_LOADED) set(OpenMP_Fortran_HAVE_OMPLIB_MODULE TRUE CACHE BOOL INTERNAL "") endif() - set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}" - CACHE STRING "Fortran compiler flags for OpenMP parallelization") - set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}" - CACHE STRING "Fortran compiler libraries for OpenMP parallelization") + if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND") + set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}" + CACHE STRING "Fortran compiler flags for OpenMP parallelization" FORCE) + endif() + if(NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND") + set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}" + CACHE STRING "Fortran compiler libraries for OpenMP parallelization" FORCE) + endif() mark_as_advanced(OpenMP_Fortran_FLAGS OpenMP_Fortran_LIB_NAMES) endif() @@ -512,11 +522,14 @@ if(CMAKE_Fortran_COMPILER_LOADED) set(OpenMP_Fortran_HAVE_OMPLIB_HEADER TRUE CACHE BOOL INTERNAL "") endif() - set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}" - CACHE STRING "Fortran compiler flags for OpenMP parallelization") - - set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES}" - CACHE STRING "Fortran compiler libraries for OpenMP parallelization") + if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND") + set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}" + CACHE STRING "Fortran compiler flags for OpenMP parallelization" FORCE) + endif() + if(NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND") + set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}" + CACHE STRING "Fortran compiler libraries for OpenMP parallelization" FORCE) + endif() endif() if(OpenMP_Fortran_HAVE_OMPLIB_MODULE) diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index 53519ba65f..7a127e4b40 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake @@ -2871,7 +2871,15 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) - if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS + set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE) + foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module Embed) + string (TOUPPER "${_${_PYTHON_PREFIX}_COMPONENT}" _${_PYTHON_PREFIX}_ID) + if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND NOT "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS) + set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE) + endif() + endforeach() + if (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) # Don't search for include dir if no library was founded break() diff --git a/Modules/FindSDL_gfx.cmake b/Modules/FindSDL_gfx.cmake new file mode 100644 index 0000000000..2dd96e98fd --- /dev/null +++ b/Modules/FindSDL_gfx.cmake @@ -0,0 +1,86 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindSDL_gfx +----------- + +.. versionadded:: 3.25 + +Locate SDL_gfx library + +This module defines: + +:: + + SDL::SDL_gfx, the name of the target to use with target_*() commands + SDL_GFX_LIBRARIES, the name of the library to link against + SDL_GFX_INCLUDE_DIRS, where to find the headers + SDL_GFX_FOUND, if false, do not try to link against + SDL_GFX_VERSION_STRING - human-readable string containing the + version of SDL_gfx + +``$SDLDIR`` is an environment variable that would correspond to the +``./configure --prefix=$SDLDIR`` used in building SDL. +#]=======================================================================] + +find_path(SDL_GFX_INCLUDE_DIRS + NAMES + SDL_framerate.h + SDL_gfxBlitFunc.h + SDL_gfxPrimitives.h + SDL_gfxPrimitives_font.h + SDL_imageFilter.h + SDL_rotozoom.h + HINTS + ENV SDLGFXDIR + ENV SDLDIR + PATH_SUFFIXES SDL + # path suffixes to search inside ENV{SDLDIR} + include/SDL include/SDL12 include/SDL11 include +) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +find_library(SDL_GFX_LIBRARIES + NAMES SDL_gfx + HINTS + ENV SDLGFXDIR + ENV SDLDIR + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} +) + +if(SDL_GFX_INCLUDE_DIRS AND EXISTS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h") + file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MAJOR[ \t]+[0-9]+$") + file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MINOR[ \t]+[0-9]+$") + file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MICRO[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_MAJOR "${SDL_GFX_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MINOR[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_MINOR "${SDL_GFX_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MICRO[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_PATCH "${SDL_GFX_VERSION_PATCH_LINE}") + set(SDL_GFX_VERSION_STRING ${SDL_GFX_VERSION_MAJOR}.${SDL_GFX_VERSION_MINOR}.${SDL_GFX_VERSION_PATCH}) + unset(SDL_GFX_VERSION_MAJOR_LINE) + unset(SDL_GFX_VERSION_MINOR_LINE) + unset(SDL_GFX_VERSION_PATCH_LINE) + unset(SDL_GFX_VERSION_MAJOR) + unset(SDL_GFX_VERSION_MINOR) + unset(SDL_GFX_VERSION_PATCH) +endif() + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_gfx + REQUIRED_VARS SDL_GFX_LIBRARIES SDL_GFX_INCLUDE_DIRS + VERSION_VAR SDL_GFX_VERSION_STRING) + +if(SDL_gfx_FOUND) + if(NOT TARGET SDL::SDL_gfx) + add_library(SDL::SDL_gfx INTERFACE IMPORTED) + set_target_properties(SDL::SDL_gfx PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SDL_GFX_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${SDL_GFX_LIBRARIES}") + endif() +endif() diff --git a/Modules/FindSDL_image.cmake b/Modules/FindSDL_image.cmake index e687b49836..324fef5d29 100644 --- a/Modules/FindSDL_image.cmake +++ b/Modules/FindSDL_image.cmake @@ -31,10 +31,6 @@ For backward compatibility the following variables are also set: $SDLDIR is an environment variable that would correspond to the ./configure --prefix=$SDLDIR used in building SDL. - -Created by Eric Wing. This was influenced by the FindSDL.cmake -module, but with modifications to recognize OS X frameworks and -additional Unix paths (FreeBSD, etc). #]=======================================================================] if(NOT SDL_IMAGE_INCLUDE_DIR AND SDLIMAGE_INCLUDE_DIR) diff --git a/Modules/FindSDL_mixer.cmake b/Modules/FindSDL_mixer.cmake index 315400a30e..8ed3cb4ba5 100644 --- a/Modules/FindSDL_mixer.cmake +++ b/Modules/FindSDL_mixer.cmake @@ -31,10 +31,6 @@ For backward compatibility the following variables are also set: $SDLDIR is an environment variable that would correspond to the ./configure --prefix=$SDLDIR used in building SDL. - -Created by Eric Wing. This was influenced by the FindSDL.cmake -module, but with modifications to recognize OS X frameworks and -additional Unix paths (FreeBSD, etc). #]=======================================================================] if(NOT SDL_MIXER_INCLUDE_DIR AND SDLMIXER_INCLUDE_DIR) diff --git a/Modules/FindSDL_net.cmake b/Modules/FindSDL_net.cmake index 28cb4d6c21..639e5bd78f 100644 --- a/Modules/FindSDL_net.cmake +++ b/Modules/FindSDL_net.cmake @@ -30,10 +30,6 @@ For backward compatibility the following variables are also set: $SDLDIR is an environment variable that would correspond to the ./configure --prefix=$SDLDIR used in building SDL. - -Created by Eric Wing. This was influenced by the FindSDL.cmake -module, but with modifications to recognize OS X frameworks and -additional Unix paths (FreeBSD, etc). #]=======================================================================] if(NOT SDL_NET_INCLUDE_DIR AND SDLNET_INCLUDE_DIR) diff --git a/Modules/FindSDL_sound.cmake b/Modules/FindSDL_sound.cmake index 8d2f9f8fc4..68075229e6 100644 --- a/Modules/FindSDL_sound.cmake +++ b/Modules/FindSDL_sound.cmake @@ -54,7 +54,19 @@ Typically, you should not use these variables directly, and you should use SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other audio libraries (if needed) to successfully compile on your system. -Created by Eric Wing. This module is a bit more complicated than the +Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that +would correspond to the ./configure --prefix=$SDLDIR used in building +SDL. + +On OSX, this will prefer the Framework version (if found) over others. +People will have to manually change the cache values of SDL_LIBRARY to +override this selectionor set the CMake environment CMAKE_INCLUDE_PATH +to modify the search paths. +#]=======================================================================] + + +#[[ +This module is a bit more complicated than the other FindSDL* family modules. The reason is that SDL_sound can be compiled in a large variety of different ways which are independent of platform. SDL_sound may dynamically link against other 3rd party @@ -70,16 +82,7 @@ This module uses a brute force approach to create a test program that uses SDL_sound, and then tries to build it. If the build fails, it parses the error output for known symbol names to figure out which libraries are needed. - -Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that -would correspond to the ./configure --prefix=$SDLDIR used in building -SDL. - -On OSX, this will prefer the Framework version (if found) over others. -People will have to manually change the cache values of SDL_LIBRARY to -override this selectionor set the CMake environment CMAKE_INCLUDE_PATH -to modify the search paths. -#]=======================================================================] +#]] set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags") mark_as_advanced(SDL_SOUND_EXTRAS) diff --git a/Modules/FindSDL_ttf.cmake b/Modules/FindSDL_ttf.cmake index d5721da0a7..d67c08930e 100644 --- a/Modules/FindSDL_ttf.cmake +++ b/Modules/FindSDL_ttf.cmake @@ -30,10 +30,6 @@ For backward compatibility the following variables are also set: $SDLDIR is an environment variable that would correspond to the ./configure --prefix=$SDLDIR used in building SDL. - -Created by Eric Wing. This was influenced by the FindSDL.cmake -module, but with modifications to recognize OS X frameworks and -additional Unix paths (FreeBSD, etc). #]=======================================================================] if(NOT SDL_TTF_INCLUDE_DIR AND SDLTTF_INCLUDE_DIR) diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake index 78b07c41d7..2f69234c7b 100644 --- a/Modules/FindVulkan.cmake +++ b/Modules/FindVulkan.cmake @@ -15,10 +15,34 @@ Optional COMPONENTS .. versionadded:: 3.24 -This module respects several optional COMPONENTS: ``glslc``, -``glslangValidator``, ``glslang``, ``shaderc_combined`` and ``SPIRV-Tools``. -On macOS, an additional component ``MoltenVK`` is available. -There are corresponding import targets for each of these flags. +This module respects several optional COMPONENTS. +There are corresponding imported targets for each of these. + +``glslc`` + The SPIR-V compiler. + +``glslangValidator`` + The ``glslangValidator`` tool. + +``glslang`` + The SPIR-V generator library. + +``shaderc_combined`` + The static library for Vulkan shader compilation. + +``SPIRV-Tools`` + Tools to process SPIR-V modules. + +``MoltenVK`` + On macOS, an additional component ``MoltenVK`` is available. + +``dxc`` + .. versionadded:: 3.25 + + The DirectX Shader Compiler. + +The ``glslc`` and ``glslangValidator`` components are provided even +if not explicitly requested (for backward compatibility). IMPORTED Targets ^^^^^^^^^^^^^^^^ @@ -75,6 +99,16 @@ This module defines :prop_tgt:`IMPORTED` targets if Vulkan has been found: Defined if SDK has the Vulkan meta-loader (volk). +``Vulkan::dxc_lib`` + .. versionadded:: 3.25 + + Defined if SDK has the DirectX shader compiler library. + +``Vulkan::dxc_exe`` + .. versionadded:: 3.25 + + Defined if SDK has the DirectX shader compiler CLI tool. + Result Variables ^^^^^^^^^^^^^^^^ @@ -119,6 +153,17 @@ This module defines the following variables: True, if the SDK has the volk library. +``Vulkan_dxc_lib_FOUND`` + .. versionadded:: 3.25 + + True, if the SDK has the DirectX shader compiler library. + +``Vulkan_dxc_exe_FOUND`` + .. versionadded:: 3.25 + + True, if the SDK has the DirectX shader compiler CLI tool. + + The module will also defines these cache variables: ``Vulkan_INCLUDE_DIR`` @@ -151,6 +196,16 @@ The module will also defines these cache variables: Path to the volk library. +``Vulkan_dxc_LIBRARY`` + .. versionadded:: 3.25 + + Path to the DirectX shader compiler library. + +``Vulkan_dxc_EXECUTABLE`` + .. versionadded:: 3.25 + + Path to the DirectX shader compiler CLI tool. + Hints ^^^^^ @@ -396,6 +451,20 @@ if(volk IN_LIST Vulkan_FIND_COMPONENTS) mark_as_advanced(Vulkan_Volk_LIBRARY) endif() +if (dxc IN_LIST Vulkan_FIND_COMPONENTS) + find_library(Vulkan_dxc_LIBRARY + NAMES dxcompiler + HINTS + ${_Vulkan_hint_library_search_paths}) + mark_as_advanced(Vulkan_dxc_LIBRARY) + + find_program(Vulkan_dxc_EXECUTABLE + NAMES dxc + HINTS + ${_Vulkan_hint_executable_search_paths}) + mark_as_advanced(Vulkan_dxc_EXECUTABLE) +endif() + if(Vulkan_GLSLC_EXECUTABLE) set(Vulkan_glslc_FOUND TRUE) else() @@ -408,6 +477,12 @@ else() set(Vulkan_glslangValidator_FOUND FALSE) endif() +if (Vulkan_dxc_EXECUTABLE) + set(Vulkan_dxc_exe_FOUND TRUE) +else() + set(Vulkan_dxc_exe_FOUND FALSE) +endif() + function(_Vulkan_set_library_component_found component) cmake_parse_arguments(PARSE_ARGV 1 _ARG "NO_WARNING" @@ -459,6 +534,7 @@ _Vulkan_set_library_component_found(glslang _Vulkan_set_library_component_found(shaderc_combined) _Vulkan_set_library_component_found(SPIRV-Tools) _Vulkan_set_library_component_found(volk) +_Vulkan_set_library_component_found(dxc) if(Vulkan_MoltenVK_INCLUDE_DIR AND Vulkan_MoltenVK_LIBRARY) set(Vulkan_MoltenVK_FOUND TRUE) @@ -764,6 +840,25 @@ if(Vulkan_FOUND) IMPORTED_LINK_INTERFACE_LIBRARIES dl) endif() endif() + + if (Vulkan_dxc_LIBRARY AND NOT TARGET Vulkan::dxc_lib) + add_library(Vulkan::dxc_lib STATIC IMPORTED) + set_property(TARGET Vulkan::dxc_lib + PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}") + set_property(TARGET Vulkan::dxc_lib APPEND + PROPERTY + IMPORTED_CONFIGURATIONS Release) + set_property(TARGET Vulkan::dxc_lib APPEND + PROPERTY + IMPORTED_LOCATION_RELEASE "${Vulkan_dxc_LIBRARY}") + endif() + + if(Vulkan_dxc_EXECUTABLE AND NOT TARGET Vulkan::dxc_exe) + add_executable(Vulkan::dxc_exe IMPORTED) + set_property(TARGET Vulkan::dxc_exe PROPERTY IMPORTED_LOCATION "${Vulkan_dxc_EXECUTABLE}") + endif() + endif() if(Vulkan_MoltenVK_FOUND) diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index 5a532c7ccb..a9aa8e0220 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 2a8c159afb..902cbb3aab 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 891bce76d9..5e16fcb4c6 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/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 0862ef7a41..5190ab1d51 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 24) -set(CMake_VERSION_PATCH 20220713) +set(CMake_VERSION_PATCH 20220803) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index af365ad729..be952cd26d 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -82,14 +82,13 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // Process input arguments. std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> parsedKeywords; - this->Parse(args, &unparsedArguments, /*keywordsMissingValue=*/nullptr, - &parsedKeywords); + this->Parse(args, &unparsedArguments); this->CheckArguments(); - std::sort(parsedKeywords.begin(), parsedKeywords.end()); - auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end()); - if (it != parsedKeywords.end()) { + std::sort(this->ParsedKeywords.begin(), this->ParsedKeywords.end()); + auto it = std::adjacent_find(this->ParsedKeywords.begin(), + this->ParsedKeywords.end()); + if (it != this->ParsedKeywords.end()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Called with more than one value for ", *it)); @@ -233,6 +232,7 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*) void cmCTestHandlerCommand::BindArguments() { + this->BindParsedKeywords(this->ParsedKeywords); this->Bind("APPEND"_s, this->Append); this->Bind("QUIET"_s, this->Quiet); this->Bind("RETURN_VALUE"_s, this->ReturnValue); diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index 31942c8feb..ed6d9af581 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmArgumentParser.h" #include "cmCTestCommand.h" @@ -44,6 +46,7 @@ protected: virtual void BindArguments(); virtual void CheckArguments(); + std::vector<cm::string_view> ParsedKeywords; bool Append = false; bool Quiet = false; std::string CaptureCMakeError; diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index f0c4cfcbe0..614d00d680 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -5,10 +5,13 @@ #include <algorithm> #include "cmArgumentParserTypes.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" namespace ArgumentParser { -auto ActionMap::Emplace(cm::string_view name, Action action) +auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action) -> std::pair<iterator, bool> { auto const it = @@ -21,7 +24,7 @@ auto ActionMap::Emplace(cm::string_view name, Action action) : std::make_pair(this->emplace(it, name, std::move(action)), true); } -auto ActionMap::Find(cm::string_view name) const -> const_iterator +auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator { auto const it = std::lower_bound(this->begin(), this->end(), name, @@ -31,82 +34,168 @@ auto ActionMap::Find(cm::string_view name) const -> const_iterator return (it != this->end() && it->first == name) ? it : this->end(); } +auto PositionActionMap::Emplace(std::size_t pos, PositionAction action) + -> std::pair<iterator, bool> +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) + ? std::make_pair(it, false) + : std::make_pair(this->emplace(it, pos, std::move(action)), true); +} + +auto PositionActionMap::Find(std::size_t pos) const -> const_iterator +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) ? it : this->end(); +} + +void Instance::Bind(std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect) +{ + this->KeywordValueFunc = std::move(f); + this->KeywordValuesExpected = expect.Count; +} + void Instance::Bind(bool& val) { val = true; - this->CurrentString = nullptr; - this->CurrentList = nullptr; - this->ExpectValue = false; + this->Bind(nullptr, ExpectAtLeast{ 0 }); } void Instance::Bind(std::string& val) { - this->CurrentString = &val; - this->CurrentList = nullptr; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 1 }); +} + +void Instance::Bind(NonEmpty<std::string>& val) +{ + this->Bind( + [this, &val](cm::string_view arg) -> Continue { + if (arg.empty() && this->ParseResults) { + this->ParseResults->AddKeywordError(this->Keyword, + " empty string not allowed\n"); + } + val.assign(std::string(arg)); + return Continue::No; + }, + ExpectAtLeast{ 1 }); } void Instance::Bind(Maybe<std::string>& val) { - this->CurrentString = &val; - this->CurrentList = nullptr; - this->ExpectValue = false; + this->Bind( + [&val](cm::string_view arg) -> Continue { + static_cast<std::string&>(val) = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 0 }); } void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val) { - this->CurrentString = nullptr; - this->CurrentList = &val; - this->ExpectValue = false; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); } void Instance::Bind(NonEmpty<std::vector<std::string>>& val) { - this->CurrentString = nullptr; - this->CurrentList = &val; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 1 }); } -void Instance::Bind(std::vector<std::vector<std::string>>& val) +void Instance::Bind(std::vector<std::vector<std::string>>& multiVal) { - this->CurrentString = nullptr; - this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back()); - this->ExpectValue = false; + multiVal.emplace_back(); + std::vector<std::string>& val = multiVal.back(); + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); } -void Instance::Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue, - std::vector<cm::string_view>* parsedKeywords) +void Instance::Consume(std::size_t pos, cm::string_view arg) { - auto const it = this->Bindings.Find(arg); - if (it != this->Bindings.end()) { - if (parsedKeywords != nullptr) { - parsedKeywords->emplace_back(it->first); + auto const it = this->Bindings.Keywords.Find(arg); + if (it != this->Bindings.Keywords.end()) { + this->FinishKeyword(); + this->Keyword = it->first; + this->KeywordValuesSeen = 0; + if (this->Bindings.ParsedKeyword) { + this->Bindings.ParsedKeyword(*this, it->first); } - it->second(*this, result); - if (this->ExpectValue && keywordsMissingValue != nullptr) { - keywordsMissingValue->emplace_back(it->first); + it->second(*this); + return; + } + + if (this->KeywordValueFunc) { + switch (this->KeywordValueFunc(arg)) { + case Continue::Yes: + break; + case Continue::No: + this->KeywordValueFunc = nullptr; + break; } + ++this->KeywordValuesSeen; + return; + } + + auto const pit = this->Bindings.Positions.Find(pos); + if (pit != this->Bindings.Positions.end()) { + pit->second(*this, pos, arg); return; } - if (this->CurrentString != nullptr) { - this->CurrentString->assign(std::string(arg)); - this->CurrentString = nullptr; - this->CurrentList = nullptr; - } else if (this->CurrentList != nullptr) { - this->CurrentList->emplace_back(arg); - } else if (unparsedArguments != nullptr) { - unparsedArguments->emplace_back(arg); + if (this->UnparsedArguments != nullptr) { + this->UnparsedArguments->emplace_back(arg); } +} - if (this->ExpectValue) { - if (keywordsMissingValue != nullptr) { - keywordsMissingValue->pop_back(); +void Instance::FinishKeyword() +{ + if (this->Keyword.empty()) { + return; + } + if (this->KeywordValuesSeen < this->KeywordValuesExpected) { + if (this->ParseResults != nullptr) { + this->ParseResults->AddKeywordError(this->Keyword, + " missing required value\n"); + } + if (this->Bindings.KeywordMissingValue) { + this->Bindings.KeywordMissingValue(*this, this->Keyword); } - this->ExpectValue = false; } } +bool ParseResult::MaybeReportError(cmMakefile& mf) const +{ + if (*this) { + return false; + } + std::string e; + for (auto const& ke : this->KeywordErrors) { + e = cmStrCat(e, "Error after keyword \"", ke.first, "\":\n", ke.second); + } + mf.IssueMessage(MessageType::FATAL_ERROR, e); + return true; +} + } // namespace ArgumentParser diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index 26148d9aa2..ac788724a8 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -5,40 +5,173 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <cassert> +#include <cstddef> #include <functional> +#include <map> #include <string> #include <utility> #include <vector> #include <cm/optional> #include <cm/string_view> +#include <cm/type_traits> #include <cmext/string_view> #include "cmArgumentParserTypes.h" // IWYU pragma: keep +template <typename Result> +class cmArgumentParser; // IWYU pragma: keep + +class cmMakefile; + namespace ArgumentParser { +class ParseResult +{ + std::map<cm::string_view, std::string> KeywordErrors; + +public: + explicit operator bool() const { return this->KeywordErrors.empty(); } + + void AddKeywordError(cm::string_view key, cm::string_view text) + + { + this->KeywordErrors[key] += text; + } + + std::map<cm::string_view, std::string> const& GetKeywordErrors() const + { + return this->KeywordErrors; + } + + bool MaybeReportError(cmMakefile& mf) const; +}; + +template <typename Result> +typename std::enable_if<std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result& result) +{ + return &result; +} + +template <typename Result> +typename std::enable_if<!std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result&) +{ + return nullptr; +} + +enum class Continue +{ + No, + Yes, +}; + +struct ExpectAtLeast +{ + std::size_t Count = 0; + + ExpectAtLeast(std::size_t count) + : Count(count) + { + } +}; + class Instance; -using Action = std::function<void(Instance&, void*)>; +using KeywordAction = std::function<void(Instance&)>; +using KeywordNameAction = std::function<void(Instance&, cm::string_view)>; +using PositionAction = + std::function<void(Instance&, std::size_t, cm::string_view)>; -// using ActionMap = cm::flat_map<cm::string_view, Action>; -class ActionMap : public std::vector<std::pair<cm::string_view, Action>> +// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>; +class KeywordActionMap + : public std::vector<std::pair<cm::string_view, KeywordAction>> { public: - std::pair<iterator, bool> Emplace(cm::string_view name, Action action); + std::pair<iterator, bool> Emplace(cm::string_view name, + KeywordAction action); const_iterator Find(cm::string_view name) const; }; +// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>; +class PositionActionMap + : public std::vector<std::pair<std::size_t, PositionAction>> +{ +public: + std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action); + const_iterator Find(std::size_t pos) const; +}; + +class ActionMap +{ +public: + KeywordActionMap Keywords; + KeywordNameAction KeywordMissingValue; + KeywordNameAction ParsedKeyword; + PositionActionMap Positions; +}; + +class Base +{ +public: + using ExpectAtLeast = ArgumentParser::ExpectAtLeast; + using Continue = ArgumentParser::Continue; + using Instance = ArgumentParser::Instance; + using ParseResult = ArgumentParser::ParseResult; + + ArgumentParser::ActionMap Bindings; + + bool MaybeBind(cm::string_view name, KeywordAction action) + { + return this->Bindings.Keywords.Emplace(name, std::move(action)).second; + } + + void Bind(cm::string_view name, KeywordAction action) + { + bool const inserted = this->MaybeBind(name, std::move(action)); + assert(inserted); + static_cast<void>(inserted); + } + + void BindParsedKeyword(KeywordNameAction action) + { + assert(!this->Bindings.ParsedKeyword); + this->Bindings.ParsedKeyword = std::move(action); + } + + void BindKeywordMissingValue(KeywordNameAction action) + { + assert(!this->Bindings.KeywordMissingValue); + this->Bindings.KeywordMissingValue = std::move(action); + } + + void Bind(std::size_t pos, PositionAction action) + { + bool const inserted = + this->Bindings.Positions.Emplace(pos, std::move(action)).second; + assert(inserted); + static_cast<void>(inserted); + } +}; + class Instance { public: - Instance(ActionMap const& bindings) + Instance(ActionMap const& bindings, ParseResult* parseResult, + std::vector<std::string>* unparsedArguments, void* result = nullptr) : Bindings(bindings) + , ParseResults(parseResult) + , UnparsedArguments(unparsedArguments) + , Result(result) { } + void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect); void Bind(bool& val); void Bind(std::string& val); + void Bind(NonEmpty<std::string>& val); void Bind(Maybe<std::string>& val); void Bind(MaybeEmpty<std::vector<std::string>>& val); void Bind(NonEmpty<std::vector<std::string>>& val); @@ -54,22 +187,37 @@ public: this->Bind(*optVal); } - void Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue, - std::vector<cm::string_view>* parsedKeywords); + template <typename Range> + void Parse(Range const& args, std::size_t pos = 0) + { + for (cm::string_view arg : args) { + this->Consume(pos++, arg); + } + this->FinishKeyword(); + } private: ActionMap const& Bindings; - std::string* CurrentString = nullptr; - std::vector<std::string>* CurrentList = nullptr; - bool ExpectValue = false; + ParseResult* ParseResults = nullptr; + std::vector<std::string>* UnparsedArguments = nullptr; + void* Result = nullptr; + + cm::string_view Keyword; + std::size_t KeywordValuesSeen = 0; + std::size_t KeywordValuesExpected = 0; + std::function<Continue(cm::string_view)> KeywordValueFunc; + + void Consume(std::size_t pos, cm::string_view arg); + void FinishKeyword(); + + template <typename Result> + friend class ::cmArgumentParser; }; } // namespace ArgumentParser template <typename Result> -class cmArgumentParser +class cmArgumentParser : private ArgumentParser::Base { public: // I *think* this function could be made `constexpr` when the code is @@ -77,81 +225,194 @@ public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) { - bool const inserted = - this->Bindings - .Emplace(name, - [member](ArgumentParser::Instance& instance, void* result) { - instance.Bind(static_cast<Result*>(result)->*member); - }) - .second; - assert(inserted), (void)inserted; + this->Base::Bind(name, [member](Instance& instance) { + instance.Bind(static_cast<Result*>(instance.Result)->*member); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, member](cm::string_view arg) -> Continue { + return (result->*member)(arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view, + cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, member, keyword](cm::string_view arg) -> Continue { + return (result->*member)(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(Result&, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, &f](cm::string_view arg) -> Continue { + return f(*result, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(Result&, cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, keyword, &f](cm::string_view arg) -> Continue { + return f(*result, keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, + cm::optional<std::string> Result::*member) + { + this->Base::Bind( + position, + [member](Instance& instance, std::size_t, cm::string_view arg) { + Result* result = static_cast<Result*>(instance.Result); + result->*member = arg; + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords( + std::vector<cm::string_view> Result::*member) + { + this->Base::BindParsedKeyword( + [member](Instance& instance, cm::string_view arg) { + (static_cast<Result*>(instance.Result)->*member).emplace_back(arg); + }); return *this; } template <typename Range> - void Parse(Result& result, Range const& args, + bool Parse(Result& result, Range const& args, std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue = nullptr, - std::vector<cm::string_view>* parsedKeywords = nullptr) const + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + using ArgumentParser::AsParseResultPtr; + ParseResult* parseResultPtr = AsParseResultPtr(result); + Instance instance(this->Bindings, parseResultPtr, unparsedArguments, + &result); + instance.Parse(args, pos); + return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true; } template <typename Range> 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 + std::size_t pos = 0) const { Result result; - this->Parse(result, args, unparsedArguments, keywordsMissingValue, - parsedKeywords); + this->Parse(result, args, unparsedArguments, pos); return result; } - -private: - ArgumentParser::ActionMap Bindings; }; template <> -class cmArgumentParser<void> +class cmArgumentParser<void> : private ArgumentParser::Base { public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T& ref) { - bool const inserted = this->Bind(cm::string_view(name), ref); - assert(inserted), (void)inserted; + this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + cm::string_view keyword = instance.Keyword; + instance.Bind( + [keyword, &f](cm::string_view arg) -> Continue { + return f(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref) + { + this->Base::Bind(position, + [&ref](Instance&, std::size_t, cm::string_view arg) { + ref = std::string(arg); + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref) + { + this->Base::BindParsedKeyword( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); return *this; } template <typename Range> - 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 + ParseResult Parse(Range const& args, + std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + ParseResult parseResult; + Instance instance(this->Bindings, &parseResult, unparsedArguments); + instance.Parse(args, pos); + return parseResult; } protected: + using Base::Instance; + using Base::BindKeywordMissingValue; + template <typename T> bool Bind(cm::string_view name, T& ref) { - return this->Bindings - .Emplace(name, - [&ref](ArgumentParser::Instance& instance, void*) { - instance.Bind(ref); - }) - .second; + return this->MaybeBind(name, + [&ref](Instance& instance) { instance.Bind(ref); }); } - -private: - ArgumentParser::ActionMap Bindings; }; diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h index 9afa5c7166..7daae09744 100644 --- a/Source/cmArgumentParserTypes.h +++ b/Source/cmArgumentParserTypes.h @@ -4,21 +4,66 @@ #include "cmConfigure.h" // IWYU pragma: keep +#if defined(__SUNPRO_CC) + +# include <string> +# include <vector> + +namespace ArgumentParser { + +template <typename T> +struct Maybe; +template <> +struct Maybe<std::string> : public std::string +{ + using std::string::basic_string; +}; + +template <typename T> +struct MaybeEmpty; +template <typename T> +struct MaybeEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; + +template <typename T> +struct NonEmpty; +template <typename T> +struct NonEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; +template <> +struct NonEmpty<std::string> : public std::string +{ + using std::string::basic_string; +}; + +} // namespace ArgumentParser + +#else + namespace ArgumentParser { template <typename T> struct Maybe : public T { + using T::T; }; template <typename T> struct MaybeEmpty : public T { + using T::T; }; template <typename T> struct NonEmpty : public T { + using T::T; }; } // namespace ArgumentParser + +#endif diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 033dd6de0a..58129a06b6 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -474,7 +474,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, } std::string const& key = *args.begin(); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ValueName; bool ValueNames = false; @@ -491,19 +491,15 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, .Bind("SEPARATOR"_s, &Arguments::Separator) .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); std::vector<std::string> invalidArgs; - std::vector<cm::string_view> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs); if (!invalidArgs.empty()) { status.SetError(cmStrCat("given invalid argument(s) \"", cmJoin(invalidArgs, ", "_s), "\".")); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError(cmStrCat("missing expected value for argument(s) \"", - cmJoin(keywordsMissingValue, ", "_s), "\".")); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if ((!arguments.ValueName.empty() && (arguments.ValueNames || arguments.SubKeys)) || diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index adcffa9f68..7755082236 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCMakePathCommand.h" -#include <algorithm> #include <functional> #include <iomanip> #include <map> @@ -11,10 +10,12 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" @@ -43,15 +44,12 @@ public: } template <int Advance = 2> - Result Parse(std::vector<std::string> const& args, - std::vector<cm::string_view>* keywordsMissingValue = nullptr, - std::vector<cm::string_view>* parsedKeywords = nullptr) const + Result Parse(std::vector<std::string> const& args) const { this->Inputs.clear(); return this->cmArgumentParser<Result>::Parse( - cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue, - parsedKeywords); + cmMakeRange(args).advance(Advance), &this->Inputs); } const std::vector<std::string>& GetInputs() const { return this->Inputs; } @@ -82,52 +80,14 @@ public: template <int Advance = 2> Result Parse(std::vector<std::string> const& args) const { - this->KeywordsMissingValue.clear(); - this->ParsedKeywords.clear(); - return this->CMakePathArgumentParser<Result>::template Parse<Advance>( - args, &this->KeywordsMissingValue, &this->ParsedKeywords); - } - - const std::vector<cm::string_view>& GetKeywordsMissingValue() const - { - return this->KeywordsMissingValue; - } - const std::vector<cm::string_view>& GetParsedKeywords() const - { - return this->ParsedKeywords; - } - - bool checkOutputVariable(const Result& arguments, - cmExecutionStatus& status) const - { - if (std::find(this->GetKeywordsMissingValue().begin(), - this->GetKeywordsMissingValue().end(), - "OUTPUT_VARIABLE"_s) != - this->GetKeywordsMissingValue().end()) { - status.SetError("OUTPUT_VARIABLE requires an argument."); - return false; - } - - if (std::find(this->GetParsedKeywords().begin(), - this->GetParsedKeywords().end(), - "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() && - arguments.Output.empty()) { - status.SetError("Invalid name for output variable."); - return false; - } - - return true; + args); } - -private: - mutable std::vector<cm::string_view> KeywordsMissingValue; - mutable std::vector<cm::string_view> ParsedKeywords; }; -struct OutputVariable +struct OutputVariable : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; }; // Usable when OUTPUT_VARIABLE is the only option class OutputVariableParser @@ -297,8 +257,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1])); @@ -307,7 +267,7 @@ bool HandleAppendCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -319,8 +279,8 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } std::string inputPath; @@ -334,7 +294,7 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -346,8 +306,8 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -364,7 +324,7 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, path.RemoveFileName(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -376,8 +336,8 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -395,7 +355,7 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, parser.GetInputs().empty() ? "" : parser.GetInputs().front()); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -403,9 +363,9 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -415,8 +375,8 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -438,7 +398,7 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -446,9 +406,9 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -458,8 +418,8 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -483,7 +443,7 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -495,8 +455,8 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -512,7 +472,7 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, auto path = cmCMakePath(inputPath).Normal(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -523,10 +483,10 @@ bool HandleTransformPathCommand( const std::string& base)>& transform, bool normalizeOption = false) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string BaseDirectory; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; + cm::optional<std::string> BaseDirectory; bool Normalize = false; }; @@ -538,8 +498,8 @@ bool HandleTransformPathCommand( Arguments arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -547,17 +507,11 @@ bool HandleTransformPathCommand( return false; } - if (std::find(parser.GetKeywordsMissingValue().begin(), - parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) != - parser.GetKeywordsMissingValue().end()) { - status.SetError("BASE_DIRECTORY requires an argument."); - return false; - } - - if (std::find(parser.GetParsedKeywords().begin(), - parser.GetParsedKeywords().end(), - "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) { - arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); + std::string baseDirectory; + if (arguments.BaseDirectory) { + baseDirectory = *arguments.BaseDirectory; + } else { + baseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } std::string inputPath; @@ -565,13 +519,13 @@ bool HandleTransformPathCommand( return false; } - auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory); + auto path = transform(cmCMakePath(inputPath), baseDirectory); if (arguments.Normalize) { path = path.Normal(); } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index c6296f9f42..5e616b30d0 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -20,7 +20,6 @@ #include "cmCMakeMinimumRequired.h" #include "cmCMakePathCommand.h" #include "cmCMakePolicyCommand.h" -#include "cmCommand.h" #include "cmConfigureFileCommand.h" #include "cmContinueCommand.h" #include "cmCreateTestSourceList.h" @@ -264,9 +263,8 @@ void GetProjectCommands(cmState* state) cmTargetLinkLibrariesCommand); state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand); state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand); - state->AddBuiltinCommand("try_compile", - cm::make_unique<cmTryCompileCommand>()); - state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>()); + state->AddBuiltinCommand("try_compile", cmTryCompileCommand); + state->AddBuiltinCommand("try_run", cmTryRunCommand); state->AddBuiltinCommand("target_precompile_headers", cmTargetPrecompileHeadersCommand); diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 45afdd1650..d90b574a72 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -238,7 +238,7 @@ std::set<std::string> const ghs_platform_vars{ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, bool isTryRun) { - this->BinaryDirectory = argv[1]; + std::string const& resultVar = argv[0]; this->OutputFile.clear(); // which signature were we called with ? this->SrcFileSignature = true; @@ -264,7 +264,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - std::string sourceDirectory = argv[2]; + std::string sourceDirectory; std::string projectName; std::string targetName; std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0] @@ -283,11 +283,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::vector<std::string> linkOptions; std::string libsToLink = " "; bool useOldLinkLibs = true; - char targetNameBuf[64]; bool didOutputVariable = false; bool didCopyFile = false; bool didCopyFileError = false; - bool useSources = argv[2] == "SOURCES"; + bool useSources = false; std::vector<std::string> sources; enum Doing @@ -303,9 +302,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, DoingSources, DoingCMakeInternal }; - Doing doing = useSources ? DoingSources : DoingNone; - for (size_t i = 3; i < argv.size(); ++i) { - if (argv[i] == "CMAKE_FLAGS") { + Doing doing = DoingNone; + for (size_t i = 1; i < argv.size(); ++i) { + if (argv[i] == "SOURCES") { + useSources = true; + doing = DoingSources; + } else if (argv[i] == "CMAKE_FLAGS") { doing = DoingCMakeFlags; } else if (argv[i] == "COMPILE_DEFINITIONS") { doing = DoingCompileDefinitions; @@ -379,6 +381,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } else if (doing == DoingCMakeInternal) { cmakeInternal = argv[i]; doing = DoingNone; + } else if (i == 1) { + this->BinaryDirectory = argv[i]; + } else if (i == 2) { + sourceDirectory = argv[i]; } else if (i == 3) { this->SrcFileSignature = false; projectName = argv[i]; @@ -391,6 +397,37 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } + if (!this->BinaryDirectory.empty()) { + if (!cmSystemTools::FileIsFullPath(this->BinaryDirectory)) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("<bindir> is not an absolute path:\n '", + this->BinaryDirectory, "'")); + // Do not try to clean up the ill-specified directory. + this->BinaryDirectory.clear(); + return -1; + } + // compute the binary dir when TRY_COMPILE is called with a src file + // signature + if (this->SrcFileSignature) { + this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; + } + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "No <bindir> specified."); + return -1; + } + + if (this->SrcFileSignature) { + projectName = "CMAKE_TRY_COMPILE"; + /* Use a random file name to avoid rapid creation and deletion + of the same executable name (some filesystems fail on that). */ + char targetNameBuf[64]; + snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", + cmSystemTools::RandomSeed() & 0xFFFFF); + targetName = targetNameBuf; + } + if (didCopyFile && copyFile.empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "COPY_FILE must be followed by a file path"); @@ -425,6 +462,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, return -1; } + // only valid for srcfile signatures if (!this->SrcFileSignature) { if (!cState.Validate(this->Makefile)) { return -1; @@ -444,14 +482,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, if (!objcxxState.Validate(this->Makefile)) { return -1; } - } - // compute the binary dir when TRY_COMPILE is called with a src file - // signature - if (this->SrcFileSignature) { - this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; - } else { - // only valid for srcfile signatures if (!compileDefs.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, @@ -711,12 +742,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmJoin(compileDefs, "]==] [==[").c_str()); } - /* Use a random file name to avoid rapid creation and deletion - of the same executable name (some filesystems fail on that). */ - snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", - cmSystemTools::RandomSeed() & 0xFFFFF); - targetName = targetNameBuf; - if (!targets.empty()) { std::string fname = "/" + std::string(targetName) + "Targets.cmake"; cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile, @@ -873,7 +898,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, libsToLink.c_str()); } fclose(fout); - projectName = "CMAKE_TRY_COMPILE"; } // Forward a set of variables to the inner project cache. @@ -996,7 +1020,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } // set the result var to the return value to indicate success or failure - this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"), + this->Makefile->AddCacheDefinition(resultVar, (res == 0 ? "TRUE" : "FALSE"), "Result of TRY_COMPILE", cmStateEnums::INTERNAL); diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 594fd7f9f5..9d43899110 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -7,19 +7,24 @@ #include <string> #include <vector> -#include "cmCommand.h" #include "cmStateTypes.h" +class cmMakefile; + /** \class cmCoreTryCompile * \brief Base class for cmTryCompileCommand and cmTryRunCommand * * cmCoreTryCompile implements the functionality to build a program. * It is the base class for cmTryCompileCommand and cmTryRunCommand. */ -class cmCoreTryCompile : public cmCommand +class cmCoreTryCompile { public: -protected: + cmCoreTryCompile(cmMakefile* mf) + : Makefile(mf) + { + } + /** * This is the core code for try compile. It is here so that other * commands, such as TryRun can access the same logic without @@ -46,4 +51,5 @@ protected: std::string OutputFile; std::string FindErrorMessage; bool SrcFileSignature = false; + cmMakefile* Makefile; }; diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index af56e2dc04..7fbd826906 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -47,7 +47,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::vector<std::vector<std::string>> Commands; std::string OutputVariable; @@ -95,14 +95,10 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unparsedArguments, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unparsedArguments); - if (!keywordsMissingValue.empty()) { - status.SetError(cmStrCat(" called with no value for ", - keywordsMissingValue.front(), ".")); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { status.SetError(" given unknown argument \"" + unparsedArguments.front() + diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index d2aa63cfaa..330d9916c0 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -954,42 +954,34 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string oldRPath; - std::string newRPath; + cm::optional<std::string> oldRPath; + cm::optional<std::string> newRPath; bool removeEnvironmentRPath = false; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - 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) .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHANGE not given FILE option."); return false; } - if (oldRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") == - parsedArgs.end()) { + if (!oldRPath) { status.SetError("RPATH_CHANGE not given OLD_RPATH option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_CHANGE not given NEW_RPATH option."); return false; } @@ -1003,17 +995,17 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, + if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath, removeEnvironmentRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1026,31 +1018,25 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string newRPath; + cm::optional<std::string> newRPath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - 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); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"", unknownArgs.front(), "\".")); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_SET not given FILE option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_SET not given NEW_RPATH option."); return false; } @@ -1064,16 +1050,16 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) { + if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1088,18 +1074,16 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args, std::string file; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<cm::string_view> missingArgs; parser.Bind("FILE"_s, file); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_REMOVE not given FILE option."); @@ -1136,31 +1120,25 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string rpath; + cm::optional<std::string> rpath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - 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); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHECK not given FILE option."); return false; } - if (rpath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") == - parsedArgs.end()) { + if (!rpath) { status.SetError("RPATH_CHECK not given RPATH option."); return false; } @@ -1169,7 +1147,7 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, // delete it. This is used during installation to re-install a file // if its RPath will change. if (cmSystemTools::FileExists(file, true) && - !cmSystemTools::CheckRPath(file, rpath)) { + !cmSystemTools::CheckRPath(file, *rpath)) { cmSystemTools::RemoveFile(file); } @@ -1252,7 +1230,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { cm::optional<std::string> BaseDirectory; bool ExpandTilde = false; @@ -1263,17 +1241,15 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde); std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValue; - auto arguments = parser.Parse(cmMakeRange(args).advance(3), - &unparsedArguments, &keywordsMissingValue); + auto arguments = + parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments); if (!unparsedArguments.empty()) { status.SetError("REAL_PATH called with unexpected arguments"); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError("BASE_DIRECTORY requires a value"); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!arguments.BaseDirectory) { @@ -2495,7 +2471,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { cm::optional<std::string> Output; cm::optional<std::string> Input; @@ -2506,6 +2482,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, bool NoSourcePermissions = false; bool UseSourcePermissions = false; ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; + std::vector<cm::string_view> ParsedKeywords; }; static auto const parser = @@ -2518,20 +2495,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions) .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions) .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions) - .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle); + .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle) + .BindParsedKeywords(&Arguments::ParsedKeywords); std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValues; - std::vector<cm::string_view> parsedKeywords; Arguments const arguments = - parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments, - &keywordsMissingValues, &parsedKeywords); + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("GENERATE keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { @@ -2539,7 +2511,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - if (!arguments.Output || parsedKeywords[0] != "OUTPUT"_s) { + if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) { status.SetError("GENERATE requires OUTPUT as first option."); return false; } @@ -2549,8 +2521,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, status.SetError("GENERATE requires INPUT or CONTENT option."); return false; } - const bool inputIsContent = parsedKeywords[1] == "CONTENT"_s; - if (!inputIsContent && parsedKeywords[1] == "INPUT") { + const bool inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s; + if (!inputIsContent && arguments.ParsedKeywords[1] == "INPUT") { status.SetError("Unknown argument to GENERATE subcommand."); } std::string const& input = @@ -3045,7 +3017,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, "\n ]])"); } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ResolvedDependenciesVar; std::string UnresolvedDependenciesVar; @@ -3088,10 +3060,8 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3099,12 +3069,9 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } cmRuntimeDependencyArchive archive( @@ -3204,7 +3171,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, bool HandleConfigureCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { cm::optional<std::string> Output; cm::optional<std::string> Content; @@ -3223,10 +3190,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { @@ -3236,12 +3201,9 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("CONFIGURE keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } if (!parsedArgs.Output) { @@ -3336,7 +3298,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, bool HandleArchiveCreateCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Output; std::string Format; @@ -3362,10 +3324,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, .Bind("PATHS"_s, &Arguments::Paths); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3373,12 +3333,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } const char* knownFormats[] = { @@ -3467,7 +3424,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, bool HandleArchiveExtractCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Input; bool Verbose = false; @@ -3486,10 +3443,8 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, .Bind("TOUCH"_s, &Arguments::Touch); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3497,12 +3452,9 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } std::string inFile = parsedArgs.Input; @@ -3557,10 +3509,15 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return true; } -bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions, - mode_t& perms, cmExecutionStatus& status) +bool ValidateAndConvertPermissions( + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const& + permissions, + mode_t& perms, cmExecutionStatus& status) { - for (const auto& i : permissions) { + if (!permissions) { + return true; + } + for (const auto& i : *permissions) { if (!cmFSPermissions::stringToModeT(i, perms)) { status.SetError(i + " is an invalid permission specifier"); cmSystemTools::SetFatalErrorOccurred(); @@ -3592,11 +3549,14 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, globber.SetRecurse(recurse); globber.SetRecurseListDirs(recurse); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - ArgumentParser::NonEmpty<std::vector<std::string>> Permissions; - ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; - ArgumentParser::NonEmpty<std::vector<std::string>> DirectoryPermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + Permissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + FilePermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + DirectoryPermissions; }; static auto const parser = @@ -3606,21 +3566,20 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions); std::vector<std::string> pathEntries; - std::vector<cm::string_view> keywordsMissingValues; - Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1), - &pathEntries, &keywordsMissingValues); + Arguments parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &pathEntries); // check validity of arguments - if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() && - parsedArgs.DirectoryPermissions.empty()) // no permissions given + if (!parsedArgs.Permissions && !parsedArgs.FilePermissions && + !parsedArgs.DirectoryPermissions) // no permissions given { status.SetError("No permissions given"); cmSystemTools::SetFatalErrorOccurred(); return false; } - if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() && - !parsedArgs.DirectoryPermissions.empty()) // all keywords are used + if (parsedArgs.Permissions && parsedArgs.FilePermissions && + parsedArgs.DirectoryPermissions) // all keywords are used { status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or " "DIRECTORY_PERMISSIONS from the invocation"); @@ -3628,12 +3587,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, return false; } - if (!keywordsMissingValues.empty()) { - for (const auto& i : keywordsMissingValues) { - status.SetError(cmStrCat(i, " is not given any arguments")); - cmSystemTools::SetFatalErrorOccurred(); - } - return false; + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; } // validate permissions @@ -3677,7 +3633,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, if (cmSystemTools::FileExists(i, true)) { bool success = true; const mode_t& filePermissions = - parsedArgs.FilePermissions.empty() ? perms : fperms; + parsedArgs.FilePermissions ? fperms : perms; if (filePermissions) { success = SetPermissions(i, filePermissions, status); } @@ -3689,7 +3645,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, else if (cmSystemTools::FileIsDirectory(i)) { bool success = true; const mode_t& directoryPermissions = - parsedArgs.DirectoryPermissions.empty() ? perms : dperms; + parsedArgs.DirectoryPermissions ? dperms : perms; if (directoryPermissions) { success = SetPermissions(i, directoryPermissions, status); } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 4ad9124f74..f260ec7431 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cassert> #include <cstdio> -#include <cstring> #include <deque> #include <functional> #include <iterator> @@ -43,8 +42,403 @@ # include <StorageDefs.h> #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> +// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx +# if !defined(KEY_WOW64_32KEY) +# define KEY_WOW64_32KEY 0x0200 +# endif +# if !defined(KEY_WOW64_64KEY) +# define KEY_WOW64_64KEY 0x0100 +# endif +#endif + class cmExecutionStatus; -class cmFileList; + +namespace { + +template <template <typename> class Op> +struct StrverscmpOp +{ + bool operator()(const std::string& lhs, const std::string& rhs) const + { + return Op<int>()(cmSystemTools::strverscmp(lhs, rhs), 0); + } +}; + +std::size_t collectPathsForDebug(std::string& buffer, + cmSearchPath const& searchPath, + std::size_t const startIndex = 0) +{ + const auto& paths = searchPath.GetPaths(); + if (paths.empty()) { + buffer += " none\n"; + return 0; + } + for (auto i = startIndex; i < paths.size(); i++) { + buffer += " " + paths[i].Path + "\n"; + } + return paths.size(); +} + +#if !(defined(_WIN32) && !defined(__CYGWIN__)) +class cmFindPackageCommandHoldFile +{ + const char* File; + +public: + cmFindPackageCommandHoldFile(const char* const f) + : File(f) + { + } + ~cmFindPackageCommandHoldFile() + { + if (this->File) { + cmSystemTools::RemoveFile(this->File); + } + } + cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; + cmFindPackageCommandHoldFile& operator=( + const cmFindPackageCommandHoldFile&) = delete; + void Release() { this->File = nullptr; } +}; +#endif + +bool isDirentryToIgnore(const char* const fname) +{ + assert(fname != nullptr); + assert(fname[0] != 0); + return fname[0] == '.' && + (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0)); +} + +class cmAppendPathSegmentGenerator +{ +public: + cmAppendPathSegmentGenerator(cm::string_view dirName) + : DirName{ dirName } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->NeedReset) { + return {}; + } + this->NeedReset = true; + return cmStrCat(parent, '/', this->DirName); + } + + void Reset() { this->NeedReset = false; } + +private: + const cm::string_view DirName; + bool NeedReset = false; +}; + +class cmEnumPathSegmentsGenerator +{ +public: + cmEnumPathSegmentsGenerator(const std::vector<cm::string_view>& init) + : Names{ init } + , Current{ this->Names.get().cbegin() } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Current != this->Names.get().cend()) { + return cmStrCat(parent, '/', *this->Current++); + } + return {}; + } + + void Reset() { this->Current = this->Names.get().cbegin(); } + +private: + std::reference_wrapper<const std::vector<cm::string_view>> Names; + std::vector<cm::string_view>::const_iterator Current; +}; + +class cmCaseInsensitiveDirectoryListGenerator +{ +public: + cmCaseInsensitiveDirectoryListGenerator(cm::string_view name) + : DirectoryLister{} + , DirName{ name } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (!this->Loaded) { + this->CurrentIdx = 0ul; + this->Loaded = true; + if (!this->DirectoryLister.Load(parent)) { + return {}; + } + } + + while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) { + const char* const fname = + this->DirectoryLister.GetFile(this->CurrentIdx++); + if (isDirentryToIgnore(fname)) { + continue; + } + if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) { + auto candidate = cmStrCat(parent, '/', fname); + if (cmSystemTools::FileIsDirectory(candidate)) { + return candidate; + } + } + } + return {}; + } + + void Reset() { this->Loaded = false; } + +private: + cmsys::Directory DirectoryLister; + const cm::string_view DirName; + unsigned long CurrentIdx = 0ul; + bool Loaded = false; +}; + +class cmDirectoryListGenerator +{ +public: + cmDirectoryListGenerator(std::vector<std::string> const& names) + : Names{ names } + , Matches{} + , Current{ this->Matches.cbegin() } + { + } + virtual ~cmDirectoryListGenerator() = default; + + std::string GetNextCandidate(const std::string& parent) + { + // Construct a list of matches if not yet + if (this->Matches.empty()) { + cmsys::Directory directoryLister; + // ALERT `Directory::Load()` keeps only names + // internally and LOST entry type from `dirent`. + // So, `Directory::FileIsDirectory` gonna use + // `SystemTools::FileIsDirectory()` and waste a syscall. + // TODO Need to enhance the `Directory` class. + directoryLister.Load(parent); + + // ATTENTION Is it guaranteed that first two entries are + // `.` and `..`? + // TODO If so, just start with index 2 and drop the + // `isDirentryToIgnore(i)` condition to check. + for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) { + const char* const fname = directoryLister.GetFile(i); + if (isDirentryToIgnore(fname)) { + continue; + } + + for (const auto& n : this->Names.get()) { + // NOTE Customization point for `cmMacProjectDirectoryListGenerator` + const auto name = this->TransformNameBeforeCmp(n); + // Skip entries that don't match and non-directories. + // ATTENTION BTW, original code also didn't check if it's a symlink + // to a directory! + const auto equal = + (cmsysString_strncasecmp(fname, name.c_str(), name.length()) == 0); + if (equal && directoryLister.FileIsDirectory(i)) { + this->Matches.emplace_back(fname); + } + } + } + // NOTE Customization point for `cmProjectDirectoryListGenerator` + this->OnMatchesLoaded(); + + this->Current = this->Matches.cbegin(); + } + + if (this->Current != this->Matches.cend()) { + auto candidate = cmStrCat(parent, '/', *this->Current++); + return candidate; + } + + return {}; + } + + void Reset() + { + this->Matches.clear(); + this->Current = this->Matches.cbegin(); + } + +protected: + virtual void OnMatchesLoaded() {} + virtual std::string TransformNameBeforeCmp(std::string same) { return same; } + + std::reference_wrapper<const std::vector<std::string>> Names; + std::vector<std::string> Matches; + std::vector<std::string>::const_iterator Current; +}; + +class cmProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmProjectDirectoryListGenerator(std::vector<std::string> const& names, + cmFindPackageCommand::SortOrderType so, + cmFindPackageCommand::SortDirectionType sd) + : cmDirectoryListGenerator{ names } + , SortOrder{ so } + , SortDirection{ sd } + { + } + + void OnMatchesLoaded() override + { + // check if there is a specific sorting order to perform + if (this->SortOrder != cmFindPackageCommand::None) { + cmFindPackageCommand::Sort(this->Matches.begin(), this->Matches.end(), + this->SortOrder, this->SortDirection); + } + } + +private: + // sort parameters + const cmFindPackageCommand::SortOrderType SortOrder; + const cmFindPackageCommand::SortDirectionType SortDirection; +}; + +class cmMacProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmMacProjectDirectoryListGenerator(const std::vector<std::string>& names, + cm::string_view ext) + : cmDirectoryListGenerator{ names } + , Extension{ ext } + { + } + + std::string TransformNameBeforeCmp(std::string name) override + { + return cmStrCat(name, this->Extension); + } + +private: + const cm::string_view Extension; +}; + +class cmFileListGeneratorGlob +{ +public: + cmFileListGeneratorGlob(cm::string_view pattern) + : Pattern(pattern) + , Files{} + , Current{} + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Files.empty()) { + // Glob the set of matching files. + std::string expr = cmStrCat(parent, this->Pattern); + cmsys::Glob g; + if (!g.FindFiles(expr)) { + return {}; + } + this->Files = g.GetFiles(); + this->Current = this->Files.cbegin(); + } + + // Skip non-directories + for (; this->Current != this->Files.cend() && + !cmSystemTools::FileIsDirectory(*this->Current); + ++this->Current) { + } + + return (this->Current != this->Files.cend()) ? *this->Current++ + : std::string{}; + } + + void Reset() + { + this->Files.clear(); + this->Current = this->Files.cbegin(); + } + +private: + cm::string_view Pattern; + std::vector<std::string> Files; + std::vector<std::string>::const_iterator Current; +}; + +#if defined(__LCC__) +# define CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_suppress 1222 // invalid error number (3288, but works anyway) +# define CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_suppress 3288 // parameter was declared but never referenced +#endif + +void ResetGenerator() +{ +} + +template <typename Generator> +void ResetGenerator(Generator&& generator) +{ + std::forward<Generator&&>(generator).Reset(); +} + +template <typename Generator, typename... Generators> +void ResetGenerator(Generator&& generator, Generators&&... generators) +{ + ResetGenerator(std::forward<Generator&&>(generator)); + ResetGenerator(std::forward<Generators&&>(generators)...); +} + +template <typename CallbackFn> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& fullPath) +{ + assert(!fullPath.empty() && fullPath.back() != '/'); + return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/'); +} + +template <typename CallbackFn, typename Generator, typename... Rest> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& startPath, Generator&& gen, + Rest&&... tail) +{ + ResetGenerator(std::forward<Generator&&>(gen)); + for (auto path = gen.GetNextCandidate(startPath); !path.empty(); + path = gen.GetNextCandidate(startPath)) { + ResetGenerator(std::forward<Rest&&>(tail)...); + if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path, + std::forward<Rest&&>(tail)...)) { + return true; + } + } + return false; +} + +#ifdef CM_LCC_DIAG_SUPPRESS_3288 +# undef CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_default 3288 +#endif + +#ifdef CM_LCC_DIAG_SUPPRESS_1222 +# undef CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_default 1222 +#endif + +// Parse the version number and store the results that were +// successfully parsed. +int parseVersion(const std::string& version, unsigned int& major, + unsigned int& minor, unsigned int& patch, unsigned int& tweak) +{ + return std::sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, + &tweak); +} + +} // anonymous namespace cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT"); @@ -60,25 +454,10 @@ const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED( const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED( "EXCLUDE"); -struct StrverscmpGreater -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) > 0; - } -}; - -struct StrverscmpLesser -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) < 0; - } -}; - void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, std::vector<std::string>::iterator end, - SortOrderType order, SortDirectionType dir) + SortOrderType const order, + SortDirectionType const dir) { if (order == Name_order) { if (dir == Dec) { @@ -86,14 +465,13 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, } else { std::sort(begin, end); } - } else if (order == Natural) - // natural order uses letters and numbers (contiguous numbers digit are - // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 - { + } else if (order == Natural) { + // natural order uses letters and numbers (contiguous numbers digit are + // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 if (dir == Dec) { - std::sort(begin, end, StrverscmpGreater()); + std::sort(begin, end, StrverscmpOp<std::greater>()); } else { - std::sort(begin, end, StrverscmpLesser()); + std::sort(begin, end, StrverscmpOp<std::less>()); } } // else do not sort @@ -113,11 +491,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status) void cmFindPackageCommand::AppendSearchPathGroups() { - std::vector<cmFindCommon::PathLabel>* labels; - // Update the All group with new paths. Note that package redirection must // take precedence over everything else, so it has to be first in the array. - labels = &this->PathGroupLabelMap[PathGroup::All]; + std::vector<cmFindCommon::PathLabel>* const labels = + &this->PathGroupLabelMap[PathGroup::All]; labels->insert(labels->begin(), PathLabel::PackageRedirect); labels->insert( std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem), @@ -147,15 +524,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Lookup required version of CMake. - if (cmValue rv = + if (cmValue const rv = this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { unsigned int v[3] = { 0, 0, 0 }; - sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); + std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]); } // Lookup target architecture, if any. - if (cmValue arch = + if (cmValue const arch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { this->LibraryArchitecture = *arch; } @@ -184,7 +561,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if User Package Registry should be disabled // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY - if (cmValue def = + if (cmValue const def = this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) { this->NoUserRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) { @@ -194,7 +571,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if System Package Registry should be disabled // The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY - if (cmValue def = this->Makefile->GetDefinition( + if (cmValue const def = this->Makefile->GetDefinition( "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) { this->NoSystemRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn( @@ -208,7 +585,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Check if Sorting should be enabled - if (cmValue so = + if (cmValue const so = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) { if (*so == "NAME") { @@ -219,7 +596,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->SortOrder = None; } } - if (cmValue sd = + if (cmValue const sd = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) { this->SortDirection = (*sd == "ASC") ? Asc : Dec; } @@ -265,9 +642,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) cmsys::RegularExpression versionRegex( R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V"); bool haveVersion = false; - std::set<unsigned int> configArgs; - std::set<unsigned int> moduleArgs; - for (unsigned int i = 1; i < args.size(); ++i) { + std::vector<std::size_t> configArgs; + std::vector<std::size_t> moduleArgs; + for (std::size_t i = 1u; i < args.size(); ++i) { if (args[i] == "QUIET") { this->Quiet = true; doing = DoingNone; @@ -281,17 +658,17 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->GlobalScope = true; doing = DoingNone; } else if (args[i] == "MODULE") { - moduleArgs.insert(i); + moduleArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "CONFIG") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_MODULE") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REQUIRED") { this->Required = true; @@ -301,36 +678,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } else if (args[i] == "OPTIONAL_COMPONENTS") { doing = DoingOptionalComponents; } else if (args[i] == "NAMES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNames; } else if (args[i] == "PATHS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPaths; } else if (args[i] == "HINTS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingHints; } else if (args[i] == "PATH_SUFFIXES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPathSuffixes; } else if (args[i] == "CONFIGS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingConfigs; } else if (args[i] == "NO_POLICY_SCOPE") { this->PolicyScope = false; doing = DoingNone; } else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") { this->NoUserRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") { this->NoSystemRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_CMAKE_BUILDS_PATH") { // Ignore legacy option. - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REGISTRY_VIEW") { if (++i == args.size()) { @@ -347,7 +724,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } } else if (this->CheckCommonArgument(args[i])) { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if ((doing == DoingComponents) || (doing == DoingOptionalComponents)) { @@ -361,8 +738,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) requiredComponents.insert(args[i]); } - std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i]; - componentVarDefs.emplace_back(req_var, isRequired); + componentVarDefs.emplace_back(this->Name + "_FIND_REQUIRED_" + args[i], + isRequired); // Append to the list of required components. components += components_sep; @@ -420,11 +797,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (!this->UseFindModules && !this->UseConfigFiles) { std::ostringstream e; e << "given options exclusive to Module mode:\n"; - for (unsigned int si : moduleArgs) { + for (auto si : moduleArgs) { e << " " << args[si] << "\n"; } e << "and options exclusive to Config mode:\n"; - for (unsigned int si : configArgs) { + for (auto si : configArgs) { e << " " << args[si] << "\n"; } e << "The options are incompatible."; @@ -443,20 +820,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (this->VersionComplete.empty() || components.empty()) { // Check whether we are recursing inside "Find<name>.cmake" within // another find_package(<name>) call. - std::string mod = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const mod = cmStrCat(this->Name, "_FIND_MODULE"); if (this->Makefile->IsOn(mod)) { if (this->VersionComplete.empty()) { // Get version information from the outer call if necessary. // Requested version string. - std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); + std::string const ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); this->VersionComplete = this->Makefile->GetSafeDefinition(ver); // Whether an exact version is required. - std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); + std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); this->VersionExact = this->Makefile->IsOn(exact); } if (components.empty()) { - std::string components_var = this->Name + "_FIND_COMPONENTS"; + std::string const components_var = this->Name + "_FIND_COMPONENTS"; components = this->Makefile->GetSafeDefinition(components_var); } } @@ -497,15 +874,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } - // Parse the version number and store the results that were - // successfully parsed. - auto parseVersion = [](const std::string& version, unsigned int& major, - unsigned int& minor, unsigned int& patch, - unsigned int& tweak) -> unsigned int { - return sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, - &tweak); - }; - if (!this->Version.empty()) { this->VersionCount = parseVersion(this->Version, this->VersionMajor, this->VersionMinor, @@ -533,7 +901,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } - std::string disableFindPackageVar = + std::string const disableFindPackageVar = cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name); if (this->Makefile->IsOn(disableFindPackageVar)) { if (this->Required) { @@ -557,8 +925,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // A dependency provider (if set) gets first look before other methods. // We do this before modifying the package root path stack because a // provider might use methods that ignore that. - cmState* state = this->Makefile->GetState(); - cmState::Command providerCommand = state->GetDependencyProviderCommand( + cmState* const state = this->Makefile->GetState(); + cmState::Command const providerCommand = state->GetDependencyProviderCommand( cmDependencyProvider::Method::FindPackage); if (bypassProvider) { if (this->DebugMode && providerCommand) { @@ -725,11 +1093,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) "package configuration file provided by " << this->Name << " (" << this->Name << "Config.cmake or " << cmSystemTools::LowerCase(this->Name) - << "-config.cmake). " - "Otherwise make Find" - << this->Name - << ".cmake available in " - "CMAKE_MODULE_PATH."; + << "-config.cmake). Otherwise make Find" << this->Name + << ".cmake available in CMAKE_MODULE_PATH."; } aw << "\n" "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this " @@ -813,9 +1178,9 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode() void cmFindPackageCommand::SetVersionVariables( const std::function<void(const std::string&, cm::string_view)>& addDefinition, - const std::string& prefix, const std::string& version, unsigned int count, - unsigned int major, unsigned int minor, unsigned int patch, - unsigned int tweak) + const std::string& prefix, const std::string& version, + const unsigned int count, const unsigned int major, const unsigned int minor, + const unsigned int patch, const unsigned int tweak) { addDefinition(prefix, version); @@ -910,7 +1275,7 @@ void cmFindPackageCommand::SetModuleVariables( } void cmFindPackageCommand::AddFindDefinition(const std::string& var, - cm::string_view value) + const cm::string_view value) { if (cmValue old = this->Makefile->GetDefinition(var)) { this->OriginalDefs[var].exists = true; @@ -954,7 +1319,7 @@ bool cmFindPackageCommand::FindModule(bool& found) if (!mfile.empty()) { if (system) { - auto it = this->DeprecatedFindModules.find(this->Name); + auto const it = this->DeprecatedFindModules.find(this->Name); if (it != this->DeprecatedFindModules.end()) { cmPolicies::PolicyStatus status = this->Makefile->GetPolicyStatus(it->second); @@ -978,13 +1343,13 @@ bool cmFindPackageCommand::FindModule(bool& found) // Load the module we found, and set "<name>_FIND_MODULE" to true // while inside it. found = true; - std::string var = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const var = cmStrCat(this->Name, "_FIND_MODULE"); this->Makefile->AddDefinition(var, "1"); bool result = this->ReadListFile(mfile, DoPolicyScope); this->Makefile->RemoveDefinition(var); if (this->DebugMode) { - std::string foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); if (this->Makefile->IsDefinitionSet(foundVar) && !this->Makefile->IsOn(foundVar)) { @@ -999,7 +1364,7 @@ bool cmFindPackageCommand::FindModule(bool& found) } bool cmFindPackageCommand::HandlePackageMode( - HandlePackageModeType handlePackageModeType) + const HandlePackageModeType handlePackageModeType) { this->ConsideredConfigs.clear(); @@ -1042,8 +1407,9 @@ bool cmFindPackageCommand::HandlePackageMode( } } - std::string foundVar = cmStrCat(this->Name, "_FOUND"); - std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const notFoundMessageVar = + cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); std::string notFoundMessage; // If the directory for the config file was found, try to read the file. @@ -1123,8 +1489,9 @@ bool cmFindPackageCommand::HandlePackageMode( << (this->VersionExact ? "exactly matches" : "is compatible with") << " requested version " << (this->VersionRange.empty() ? "" : "range ") << "\"" - << this->VersionComplete << "\".\n" - << "The following configuration files were considered but not " + << this->VersionComplete + << "\".\n" + "The following configuration files were considered but not " "accepted:\n"; for (ConfigFileInfo const& info : @@ -1172,8 +1539,9 @@ bool cmFindPackageCommand::HandlePackageMode( "package or SDK, be sure it has been installed."; } else // if(!this->UseFindModules && !this->UseConfigFiles) { - e << "No \"Find" << this->Name << ".cmake\" found in " - << "CMAKE_MODULE_PATH."; + e << "No \"Find" << this->Name + << ".cmake\" found in " + "CMAKE_MODULE_PATH."; aw << "Find" << this->Name @@ -1217,16 +1585,16 @@ bool cmFindPackageCommand::HandlePackageMode( this->Makefile->AddDefinition(foundVar, found ? "1" : "0"); // Set a variable naming the configuration file that was found. - std::string fileVar = cmStrCat(this->Name, "_CONFIG"); + std::string const fileVar = cmStrCat(this->Name, "_CONFIG"); if (found) { this->Makefile->AddDefinition(fileVar, this->FileFound); } else { this->Makefile->RemoveDefinition(fileVar); } - std::string consideredConfigsVar = + std::string const consideredConfigsVar = cmStrCat(this->Name, "_CONSIDERED_CONFIGS"); - std::string consideredVersionsVar = + std::string const consideredVersionsVar = cmStrCat(this->Name, "_CONSIDERED_VERSIONS"); std::string consideredConfigFiles; @@ -1312,7 +1680,7 @@ bool cmFindPackageCommand::FindConfig() void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value) { - std::string help = + std::string const help = cmStrCat("The directory containing a CMake configuration file for ", this->Name, '.'); this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(), @@ -1351,7 +1719,7 @@ bool cmFindPackageCommand::FindAppBundleConfig() } bool cmFindPackageCommand::ReadListFile(const std::string& f, - PolicyScopeRule psr) + const PolicyScopeRule psr) { const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope; @@ -1362,12 +1730,12 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f, if (this->Makefile->ReadDependentFile(f, noPolicyScope)) { return true; } - std::string e = cmStrCat("Error reading CMake code from \"", f, "\"."); + std::string const e = cmStrCat("Error reading CMake code from \"", f, "\"."); this->SetError(e); return false; } -void cmFindPackageCommand::AppendToFoundProperty(bool found) +void cmFindPackageCommand::AppendToFoundProperty(const bool found) { std::vector<std::string> foundContents; cmValue foundProp = @@ -1410,27 +1778,28 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found) void cmFindPackageCommand::AppendSuccessInformation() { { - std::string transitivePropName = + std::string const transitivePropName = cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY"); this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False"); } - std::string found = cmStrCat(this->Name, "_FOUND"); - std::string upperFound = cmSystemTools::UpperCase(found); + std::string const found = cmStrCat(this->Name, "_FOUND"); + std::string const upperFound = cmSystemTools::UpperCase(found); - bool upperResult = this->Makefile->IsOn(upperFound); - bool result = this->Makefile->IsOn(found); - bool packageFound = (result || upperResult); + bool const upperResult = this->Makefile->IsOn(upperFound); + bool const result = this->Makefile->IsOn(found); + bool const packageFound = (result || upperResult); this->AppendToFoundProperty(packageFound); // Record whether the find was quiet or not, so this can be used // e.g. in FeatureSummary.cmake - std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET"); + std::string const quietInfoPropName = + cmStrCat("_CMAKE_", this->Name, "_QUIET"); this->Makefile->GetState()->SetGlobalProperty( quietInfoPropName, this->Quiet ? "TRUE" : "FALSE"); // set a global property to record the required version of this package - std::string versionInfoPropName = + std::string const versionInfoPropName = cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION"); std::string versionInfo; if (!this->VersionRange.empty()) { @@ -1442,28 +1811,13 @@ void cmFindPackageCommand::AppendSuccessInformation() this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName, versionInfo.c_str()); if (this->Required) { - std::string requiredInfoPropName = + std::string const requiredInfoPropName = cmStrCat("_CMAKE_", this->Name, "_TYPE"); this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName, "REQUIRED"); } } -inline std::size_t collectPathsForDebug(std::string& buffer, - cmSearchPath const& searchPath, - std::size_t startIndex = 0) -{ - const auto& paths = searchPath.GetPaths(); - if (paths.empty()) { - buffer += " none\n"; - return 0; - } - for (std::size_t i = startIndex; i < paths.size(); i++) { - buffer += " " + paths[i].Path + "\n"; - } - return paths.size(); -} - void cmFindPackageCommand::ComputePrefixes() { this->FillPrefixesPackageRedirect(); @@ -1674,14 +2028,6 @@ void cmFindPackageCommand::FillPrefixesSystemRegistry() } #if defined(_WIN32) && !defined(__CYGWIN__) -# include <windows.h> -// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx -# if !defined(KEY_WOW64_32KEY) -# define KEY_WOW64_32KEY 0x0200 -# endif -# if !defined(KEY_WOW64_64KEY) -# define KEY_WOW64_64KEY 0x0100 -# endif void cmFindPackageCommand::LoadPackageRegistryWinUser() { // HKEY_CURRENT_USER\\Software shares 32-bit and 64-bit views. @@ -1704,7 +2050,8 @@ void cmFindPackageCommand::LoadPackageRegistryWinSystem() } } -void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, +void cmFindPackageCommand::LoadPackageRegistryWin(const bool user, + const unsigned int view, cmSearchPath& outPaths) { std::wstring key = L"Software\\Kitware\\CMake\\Packages\\"; @@ -1756,28 +2103,8 @@ void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, RegCloseKey(hKey); } } -#else -class cmFindPackageCommandHoldFile -{ - const char* File; - -public: - cmFindPackageCommandHoldFile(const char* f) - : File(f) - { - } - ~cmFindPackageCommandHoldFile() - { - if (this->File) { - cmSystemTools::RemoveFile(this->File); - } - } - cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; - cmFindPackageCommandHoldFile& operator=( - const cmFindPackageCommandHoldFile&) = delete; - void Release() { this->File = nullptr; } -}; +#else void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir, cmSearchPath& outPaths) { @@ -1877,7 +2204,7 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable() std::vector<std::string> expanded = cmExpandedList(*prefix_paths); long count = 0; for (const auto& path : expanded) { - bool to_add = + bool const to_add = !(path == install_path_to_remove && ++count == install_prefix_count); if (to_add) { paths.AddPath(path); @@ -1941,7 +2268,7 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir) std::string d = dir; if (!s.empty()) { d += s; - d += "/"; + d += '/'; } if (this->CheckDirectory(d)) { return true; @@ -1955,7 +2282,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir) assert(!dir.empty() && dir.back() == '/'); // Look for the file in this directory. - std::string d = dir.substr(0, dir.size() - 1); + std::string const d = dir.substr(0, dir.size() - 1); if (this->FindConfigFile(d, this->FileFound)) { // Remove duplicate slashes. cmSystemTools::ConvertToUnixSlashes(this->FileFound); @@ -2028,8 +2355,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, std::string& result_version) { // The version file will be loaded in an isolated scope. - cmMakefile::ScopePushPop varScope(this->Makefile); - cmMakefile::PolicyPushPop polScope(this->Makefile); + cmMakefile::ScopePushPop const varScope(this->Makefile); + cmMakefile::PolicyPushPop const polScope(this->Makefile); static_cast<void>(varScope); static_cast<void>(polScope); @@ -2076,7 +2403,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, if (this->ReadListFile(version_file, NoPolicyScope)) { // Check the output variables. bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT"); - bool unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); + bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); if (!okay && !this->VersionExact) { okay = this->Makefile->IsOn("PACKAGE_VERSION_COMPATIBLE"); } @@ -2096,8 +2423,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, unsigned int parsed_patch; unsigned int parsed_tweak; this->VersionFoundCount = - sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", &parsed_major, - &parsed_minor, &parsed_patch, &parsed_tweak); + parseVersion(this->VersionFound, parsed_major, parsed_minor, + parsed_patch, parsed_tweak); switch (this->VersionFoundCount) { case 4: this->VersionFoundTweak = parsed_tweak; @@ -2129,7 +2456,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, void cmFindPackageCommand::StoreVersionFound() { // Store the whole version string. - std::string ver = cmStrCat(this->Name, "_VERSION"); + std::string const ver = cmStrCat(this->Name, "_VERSION"); auto addDefinition = [this](const std::string& variable, cm::string_view value) { this->Makefile->AddDefinition(variable, value); @@ -2145,357 +2472,6 @@ void cmFindPackageCommand::StoreVersionFound() } } -class cmFileListGeneratorBase -{ -public: - virtual ~cmFileListGeneratorBase() = default; - -protected: - bool Consider(std::string const& fullPath, cmFileList& listing); - -private: - bool Search(cmFileList&); - virtual bool Search(std::string const& parent, cmFileList&) = 0; - virtual std::unique_ptr<cmFileListGeneratorBase> Clone() const = 0; - friend class cmFileList; - cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next); - std::unique_ptr<cmFileListGeneratorBase> Next; -}; - -class cmFileList -{ -public: - virtual ~cmFileList() = default; - cmFileList& operator/(cmFileListGeneratorBase const& rhs) - { - if (this->Last) { - this->Last = this->Last->SetNext(rhs); - } else { - this->First = rhs.Clone(); - this->Last = this->First.get(); - } - return *this; - } - bool Search() - { - if (this->First) { - return this->First->Search(*this); - } - return false; - } - -private: - virtual bool Visit(std::string const& fullPath) = 0; - friend class cmFileListGeneratorBase; - std::unique_ptr<cmFileListGeneratorBase> First; - cmFileListGeneratorBase* Last = nullptr; -}; - -class cmFindPackageFileList : public cmFileList -{ -public: - cmFindPackageFileList(cmFindPackageCommand* fpc, bool use_suffixes = true) - : FPC(fpc) - , UseSuffixes(use_suffixes) - { - } - -private: - bool Visit(std::string const& fullPath) override - { - if (this->UseSuffixes) { - return this->FPC->SearchDirectory(fullPath); - } - return this->FPC->CheckDirectory(fullPath); - } - cmFindPackageCommand* FPC; - bool UseSuffixes; -}; - -bool cmFileListGeneratorBase::Search(cmFileList& listing) -{ - return this->Search("", listing); -} - -cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext( - cmFileListGeneratorBase const& next) -{ - this->Next = next.Clone(); - return this->Next.get(); -} - -bool cmFileListGeneratorBase::Consider(std::string const& fullPath, - cmFileList& listing) -{ - if (!fullPath.empty() && !cmSystemTools::FileIsDirectory(fullPath)) { - return false; - } - if (this->Next) { - return this->Next->Search(fullPath + "/", listing); - } - return listing.Visit(fullPath + "/"); -} - -class cmFileListGeneratorFixed : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorFixed(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - std::string fullPath = parent + this->String; - return this->Consider(fullPath, lister); - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorFixed(*this)); - return g; - } -}; - -class cmFileListGeneratorEnumerate : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorEnumerate(std::vector<std::string> const& v) - : Vector(v) - { - } - cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r) - : Vector(r.Vector) - { - } - -private: - std::vector<std::string> const& Vector; - bool Search(std::string const& parent, cmFileList& lister) override - { - for (std::string const& i : this->Vector) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorEnumerate(*this)); - return g; - } -}; - -class cmFileListGeneratorProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorProject(std::vector<std::string> const& names, - cmFindPackageCommand::SortOrderType so, - cmFindPackageCommand::SortDirectionType sd) - : Names(names) - { - this->SetSort(so, sd); - } - cmFileListGeneratorProject(cmFileListGeneratorProject const& r) - : Names(r.Names) - { - this->SetSort(r.SortOrder, r.SortDirection); - } - - void SetSort(cmFindPackageCommand::SortOrderType o, - cmFindPackageCommand::SortDirectionType d) - { - this->SortOrder = o; - this->SortDirection = d; - } - -protected: - // sort parameters - cmFindPackageCommand::SortOrderType SortOrder; - cmFindPackageCommand::SortDirectionType SortDirection; - -private: - std::vector<std::string> const& Names; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string const& n : this->Names) { - if (cmsysString_strncasecmp(fname, n.c_str(), n.length()) == 0) { - matches.emplace_back(fname); - } - } - } - - // before testing the matches check if there is a specific sorting order to - // perform - if (this->SortOrder != cmFindPackageCommand::None) { - cmFindPackageCommand::Sort(matches.begin(), matches.end(), - this->SortOrder, this->SortDirection); - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorProject(*this)); - return g; - } -}; - -class cmFileListGeneratorMacProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorMacProject(std::vector<std::string> const& names, - const char* ext) - : Names(names) - , Extension(ext) - { - } - cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r) - : Names(r.Names) - , Extension(r.Extension) - { - } - -private: - std::vector<std::string> const& Names; - std::string Extension; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string name : this->Names) { - name += this->Extension; - if (cmsysString_strcasecmp(fname, name.c_str()) == 0) { - matches.emplace_back(fname); - } - } - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorMacProject(*this)); - return g; - } -}; - -class cmFileListGeneratorCaseInsensitive : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorCaseInsensitive(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorCaseInsensitive( - cmFileListGeneratorCaseInsensitive const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Look for matching files. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - if (cmsysString_strcasecmp(fname, this->String.c_str()) == 0) { - if (this->Consider(parent + fname, lister)) { - return true; - } - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorCaseInsensitive(*this)); - return g; - } -}; - -class cmFileListGeneratorGlob : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorGlob(std::string str) - : Pattern(std::move(str)) - { - } - cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r) - : Pattern(r.Pattern) - { - } - -private: - std::string Pattern; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Glob the set of matching files. - std::string expr = cmStrCat(parent, this->Pattern); - cmsys::Glob g; - if (!g.FindFiles(expr)) { - return false; - } - std::vector<std::string> const& files = g.GetFiles(); - - // Look for directories among the matches. - for (std::string const& f : files) { - if (this->Consider(f, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - return cm::make_unique<cmFileListGeneratorGlob>(*this); - } -}; - bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) { assert(!prefix_in.empty() && prefix_in.back() == '/'); @@ -2515,148 +2491,95 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) return false; } - // PREFIX/ (useful on windows or in build trees) + // PREFIX/ (useful on windows or in build trees) if (this->SearchDirectory(prefix_in)) { return true; } // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); - // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto firstPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + + // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) + if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) { + return true; } // Construct list of common install locations (lib and share). - std::vector<std::string> common; + std::vector<cm::string_view> common; + std::string libArch; if (!this->LibraryArchitecture.empty()) { - common.push_back("lib/" + this->LibraryArchitecture); + libArch = "lib/" + this->LibraryArchitecture; + common.emplace_back(libArch); } if (this->UseLib32Paths) { - common.emplace_back("lib32"); + common.emplace_back("lib32"_s); } if (this->UseLib64Paths) { - common.emplace_back("lib64"); + common.emplace_back("lib64"_s); } if (this->UseLibx32Paths) { - common.emplace_back("libx32"); + common.emplace_back("libx32"_s); } - common.emplace_back("lib"); - common.emplace_back("share"); + common.emplace_back("lib"_s); + common.emplace_back("share"_s); - // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + auto cmnGen = cmEnumPathSegmentsGenerator{ common }; + auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s }; + + // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) { + return true; } + auto secondPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen, iCMakeGen); } bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) @@ -2665,56 +2588,36 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto fwGen = + cmMacProjectDirectoryListGenerator{ this->Names, ".framework"_s }; + auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s }; + auto vGen = cmAppendPathSegmentGenerator{ "Versions"_s }; + auto grGen = cmFileListGeneratorGlob{ "/*/Resources"_s }; // <prefix>/Foo.framework/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) { + return true; } + // <prefix>/Foo.framework/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen, iCMakeGen); } bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) @@ -2723,32 +2626,24 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto appGen = cmMacProjectDirectoryListGenerator{ this->Names, ".app"_s }; + auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s }; // <prefix>/Foo.app/Contents/Resources - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) { + return true; } // <prefix>/Foo.app/Contents/Resources/CMake - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths( + searchFn, prefix, appGen, crGen, + cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }); } // TODO: Debug cmsys::Glob double slash problem. diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 80fd8f8cae..28e00a109d 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -153,8 +153,6 @@ private: bool SearchFrameworkPrefix(std::string const& prefix_in); bool SearchAppBundlePrefix(std::string const& prefix_in); - friend class cmFindPackageFileList; - struct OriginalDef { bool exists; diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx index b529b8f04c..6212bbde2b 100644 --- a/Source/cmGeneratedFileStream.cxx +++ b/Source/cmGeneratedFileStream.cxx @@ -124,10 +124,10 @@ cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase() void cmGeneratedFileStreamBase::Open(std::string const& name) { // Save the original name of the file. - this->Name = name; + this->Name = cmSystemTools::CollapseFullPath(name); // Create the name of the temporary file. - this->TempName = name; + this->TempName = this->Name; #if defined(__VMS) this->TempName += "_"; #else @@ -231,7 +231,7 @@ int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname, void cmGeneratedFileStream::SetName(const std::string& fname) { - this->Name = fname; + this->Name = cmSystemTools::CollapseFullPath(fname); } void cmGeneratedFileStream::SetTempExt(std::string const& ext) diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index dace0556b6..d59ac2b3a0 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -16,6 +16,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -8660,6 +8661,11 @@ bool cmGeneratorTarget::AddHeaderSetVerification() verifyTarget->SetProperty("AUTOUIC", "OFF"); verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON"); verifyTarget->SetProperty("UNITY_BUILD", "OFF"); + cm::optional<std::map<std::string, cmValue>> + perConfigCompileDefinitions; + verifyTarget->FinalizeTargetCompileInfo( + this->Makefile->GetCompileDefinitionsEntries(), + perConfigCompileDefinitions); } if (fileCgesContextSensitive) { @@ -8706,6 +8712,12 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile( languages->insert("C"); } } + + if (languages->empty()) { + std::vector<std::string> languagesVector; + this->GlobalGenerator->GetEnabledLanguages(languagesVector); + languages->insert(languagesVector.begin(), languagesVector.end()); + } } if (languages->count("CXX")) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4cb541a752..edc4118499 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -14,6 +14,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cmext/algorithm> #include <cmext/string_view> @@ -49,6 +50,7 @@ #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateTypes.h" +#include "cmSystemTools.h" #include "cmValue.h" #include "cmVersion.h" #include "cmWorkingDirectory.h" @@ -62,10 +64,6 @@ # include "cmQtAutoGenGlobalInitializer.h" #endif -#if defined(_MSC_VER) && _MSC_VER >= 1800 -# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx -#endif - const std::string kCMAKE_PLATFORM_INFO_INITIALIZED = "CMAKE_PLATFORM_INFO_INITIALIZED"; @@ -616,34 +614,12 @@ void cmGlobalGenerator::EnableLanguage( // what platform we are running on if (!mf->GetDefinition("CMAKE_SYSTEM")) { #if defined(_WIN32) && !defined(__CYGWIN__) - /* Windows version number data. */ - OSVERSIONINFOEXW osviex; - ZeroMemory(&osviex, sizeof(osviex)); - osviex.dwOSVersionInfoSize = sizeof(osviex); - -# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx -# pragma warning(push) -# ifdef __INTEL_COMPILER -# pragma warning(disable : 1478) -# elif defined __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -# else -# pragma warning(disable : 4996) -# endif -# endif - GetVersionExW((OSVERSIONINFOW*)&osviex); -# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx -# ifdef __clang__ -# pragma clang diagnostic pop -# else -# pragma warning(pop) -# endif -# endif + cmSystemTools::WindowsVersion windowsVersion = + cmSystemTools::GetWindowsVersion(); std::ostringstream windowsVersionString; - windowsVersionString << osviex.dwMajorVersion << "." - << osviex.dwMinorVersion << "." - << osviex.dwBuildNumber; + windowsVersionString << windowsVersion.dwMajorVersion << "." + << windowsVersion.dwMinorVersion << "." + << windowsVersion.dwBuildNumber; mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str()); #endif // Read the DetermineSystem file @@ -1399,7 +1375,9 @@ void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes) this->CheckTargetProperties(); } this->CreateGeneratorTargets(targetTypes); - this->ComputeBuildFileGenerators(); + if (targetTypes == TargetTypes::AllTargets) { + this->ComputeBuildFileGenerators(); + } } void cmGlobalGenerator::CreateImportedGenerationObjects( @@ -1846,39 +1824,14 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo() // Construct per-target generator information. for (const auto& mf : this->Makefiles) { - const cmBTStringRange noconfig_compile_definitions = + const cmBTStringRange noConfigCompileDefinitions = mf->GetCompileDefinitionsEntries(); + cm::optional<std::map<std::string, cmValue>> perConfigCompileDefinitions; for (auto& target : mf->GetTargets()) { cmTarget* t = &target.second; - if (t->GetType() == cmStateEnums::GLOBAL_TARGET) { - continue; - } - - t->AppendBuildInterfaceIncludes(); - - if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - - for (auto const& def : noconfig_compile_definitions) { - t->InsertCompileDefinition(def); - } - - cmPolicies::PolicyStatus polSt = - mf->GetPolicyStatus(cmPolicies::CMP0043); - if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { - std::vector<std::string> configs = - mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); - - for (std::string const& c : configs) { - std::string defPropName = - cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c)); - if (cmValue val = mf->GetProperty(defPropName)) { - t->AppendProperty(defPropName, *val); - } - } - } + t->FinalizeTargetCompileInfo(noConfigCompileDefinitions, + perConfigCompileDefinitions); } // The standard include directories for each language diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 155efde70d..29eeb5a917 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -1286,14 +1286,6 @@ cmGlobalVisualStudio10Generator::GenerateBuildCommand( return makeCommands; } -bool cmGlobalVisualStudio10Generator::IsInSolution( - const cmGeneratorTarget* gt) const -{ - return gt->IsInBuildSystem() && - !(this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 && - gt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET); -} - bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf) { if (this->DefaultPlatformToolset == "v100") { diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 2203f7110d..4977a845d0 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -118,8 +118,6 @@ public: return this->WindowsTargetPlatformVersion; } - bool IsInSolution(const cmGeneratorTarget* gt) const override; - /** Return true if building for WindowsCE */ bool TargetsWindowsCE() const override { return this->SystemIsWindowsCE; } diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index ce943a2c97..758ce83813 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -180,7 +180,7 @@ void cmGlobalVisualStudio71Generator::WriteExternalProject( fout << "\tProjectSection(ProjectDependencies) = postProject\n"; for (BT<std::pair<std::string, bool>> const& it : depends) { std::string const& dep = it.Value.first; - if (!dep.empty()) { + if (this->IsDepInSolution(dep)) { fout << "\t\t{" << this->GetGUID(dep) << "} = {" << this->GetGUID(dep) << "}\n"; } diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index cddaaa4339..c6af20ad64 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -843,6 +843,12 @@ bool cmGlobalVisualStudioGenerator::IsInSolution( return gt->IsInBuildSystem(); } +bool cmGlobalVisualStudioGenerator::IsDepInSolution( + const std::string& targetName) const +{ + return !targetName.empty(); +} + bool cmGlobalVisualStudioGenerator::TargetCompare::operator()( cmGeneratorTarget const* l, cmGeneratorTarget const* r) const { diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 4f5f1006e7..f45b4d49a2 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -101,6 +101,9 @@ public: // return true if target should be included in solution. virtual bool IsInSolution(const cmGeneratorTarget* gt) const; + // return true if project dependency should be included in solution. + virtual bool IsDepInSolution(const std::string& targetName) const; + /** Get the top-level registry key for this VS version. */ std::string GetRegistryBase(); diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index b72fc4e80f..7e36881935 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -25,53 +25,100 @@ #include "cmVSSetupHelper.h" #include "cmake.h" -#if defined(_M_ARM64) -# define HOST_PLATFORM_NAME "ARM64" -# define HOST_TOOLS_ARCH(v) \ - (v >= cmGlobalVisualStudioGenerator::VSVersion::VS17) ? "ARM64" : "" -#elif defined(_M_ARM) -# define HOST_PLATFORM_NAME "ARM" -# define HOST_TOOLS_ARCH(v) "" -#elif defined(_M_IA64) -# define HOST_PLATFORM_NAME "Itanium" -# define HOST_TOOLS_ARCH(v) "" -#elif defined(_WIN64) -# define HOST_PLATFORM_NAME "x64" -# define HOST_TOOLS_ARCH(v) "x64" -#else +#ifndef IMAGE_FILE_MACHINE_ARM64 +# define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian +#endif + static bool VSIsWow64() { BOOL isWow64 = false; return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64; } + +static bool VSIsArm64Host() +{ + typedef BOOL(WINAPI * CM_ISWOW64PROCESS2)( + HANDLE hProcess, USHORT * pProcessMachine, USHORT * pNativeMachine); + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# define CM_VS_GCC_DIAGNOSTIC_PUSHED +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" #endif + static const CM_ISWOW64PROCESS2 s_IsWow64Process2Impl = + (CM_ISWOW64PROCESS2)GetProcAddress( + GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"), + "IsWow64Process2"); +#ifdef CM_VS_GCC_DIAGNOSTIC_PUSHED +# pragma GCC diagnostic pop +# undef CM_VS_GCC_DIAGNOSTIC_PUSHED +#endif + + USHORT processMachine, nativeMachine; + + return s_IsWow64Process2Impl != nullptr && + s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine, + &nativeMachine) && + nativeMachine == IMAGE_FILE_MACHINE_ARM64; +} + +static bool VSHasDotNETFrameworkArm64() +{ + std::string dotNetArm64; + return cmSystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRootArm64", + dotNetArm64, cmSystemTools::KeyWOW64_64); +} + +static bool VSIsWindows11OrGreater() +{ + cmSystemTools::WindowsVersion const windowsVersion = + cmSystemTools::GetWindowsVersion(); + return (windowsVersion.dwMajorVersion > 10 || + (windowsVersion.dwMajorVersion == 10 && + windowsVersion.dwMinorVersion > 0) || + (windowsVersion.dwMajorVersion == 10 && + windowsVersion.dwMinorVersion == 0 && + windowsVersion.dwBuildNumber >= 22000)); +} static std::string VSHostPlatformName() { -#ifdef HOST_PLATFORM_NAME - return HOST_PLATFORM_NAME; -#else - if (VSIsWow64()) { + if (VSIsArm64Host()) { + return "ARM64"; + } else if (VSIsWow64()) { return "x64"; } else { +#if defined(_M_ARM) + return "ARM"; +#elif defined(_M_IA64) + return "Itanium"; +#elif defined(_WIN64) + return "x64"; +#else return "Win32"; - } #endif + } } static std::string VSHostArchitecture( cmGlobalVisualStudioGenerator::VSVersion v) { - static_cast<void>(v); -#ifdef HOST_TOOLS_ARCH - return HOST_TOOLS_ARCH(v); -#else - if (VSIsWow64()) { + if (VSIsArm64Host()) { + return v >= cmGlobalVisualStudioGenerator::VSVersion::VS17 ? "ARM64" : ""; + } else if (VSIsWow64()) { return "x64"; } else { +#if defined(_M_ARM) + return ""; +#elif defined(_M_IA64) + return ""; +#elif defined(_WIN64) + return "x64"; +#else return "x86"; - } #endif + } } static unsigned int VSVersionToMajor( @@ -899,17 +946,24 @@ std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand() std::string vs; if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) { if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) { -#if defined(_M_ARM64) - std::string msbuild_arm64 = - vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe"; - if (cmSystemTools::FileExists(msbuild_arm64)) { - return msbuild_arm64; - } -#endif - - msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe"; - if (cmSystemTools::FileExists(msbuild)) { - return msbuild; + if (VSIsArm64Host()) { + if (VSHasDotNETFrameworkArm64()) { + msbuild = vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe"; + if (cmSystemTools::FileExists(msbuild)) { + return msbuild; + } + } + if (VSIsWindows11OrGreater()) { + msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe"; + if (cmSystemTools::FileExists(msbuild)) { + return msbuild; + } + } + } else { + msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe"; + if (cmSystemTools::FileExists(msbuild)) { + return msbuild; + } } } msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe"; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index b1044a8c36..82adca83ca 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2160,11 +2160,9 @@ 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<cm::string_view> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr, - &parsedArgs); + genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector); bool success = genericArgs.Finalize(); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 86ae45db95..a4080d80df 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -1343,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) { diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index 31538b6f5b..7e195664e2 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -48,6 +48,12 @@ using options_set = std::set<cm::string_view>; struct UserArgumentParser : public cmArgumentParser<void> { + void BindKeywordsMissingValue(std::vector<cm::string_view>& ref) + { + this->cmArgumentParser<void>::BindKeywordMissingValue( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); + } + template <typename T, typename H> void Bind(std::vector<std::string> const& names, std::map<std::string, T>& ref, H duplicateKey) @@ -211,8 +217,9 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, } std::vector<cm::string_view> keywordsMissingValues; + parser.BindKeywordsMissingValue(keywordsMissingValues); - parser.Parse(list, &unparsed, &keywordsMissingValues); + parser.Parse(list, &unparsed); PassParsedArguments( prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 1514a8aced..40f3ab58d1 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -909,7 +909,6 @@ bool cmQtAutoGenInitializer::InitScanFiles() // The reason is that their file names might be discovered from source files // at generation time. if (this->MocOrUicEnabled()) { - std::unordered_set<std::string> addedFiles; for (const auto& sf : this->Makefile->GetSourceFiles()) { // sf->GetExtension() is only valid after sf->ResolveFullPath() ... // Since we're iterating over source files that might be not in the @@ -951,28 +950,25 @@ bool cmQtAutoGenInitializer::InitScanFiles() cmExpandedList(uicOpts)); } - auto uiHeaderFileName = cmStrCat( - "ui_"_s, cmSystemTools::GetFilenameWithoutLastExtension(fullPath), - ".h"_s); + auto uiHeaderRelativePath = cmSystemTools::RelativePath( + this->LocalGen->GetCurrentSourceDirectory(), + cmSystemTools::GetFilenamePath(fullPath)); - // .ui files with the same base name will conflict. Yield an error. - { - auto insertResult = addedFiles.insert(uiHeaderFileName); - if (!insertResult.second) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("More than one .ui file with the name "_s, - cmSystemTools::GetFilenameName(fullPath), - " was found in the sources for target "_s, - this->GenTarget->GetName(), ".")); - } + // Avoid creating a path containing adjacent slashes + if (!uiHeaderRelativePath.empty() && + uiHeaderRelativePath.back() != '/') { + uiHeaderRelativePath += '/'; } + auto uiHeaderFilePath = cmStrCat( + '/', uiHeaderRelativePath, "ui_"_s, + cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s); + ConfigString uiHeader; std::string uiHeaderGenex; this->ConfigFileNamesAndGenex( uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s), - cmStrCat("/"_s, uiHeaderFileName)); + uiHeaderFilePath); this->Uic.UiHeaders.emplace_back( std::make_pair(uiHeader, uiHeaderGenex)); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 3de45bc7d7..672cdc79b8 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -107,6 +107,10 @@ # include <sys/utsname.h> #endif +#if defined(_MSC_VER) && _MSC_VER >= 1800 +# define CM_WINDOWS_DEPRECATED_GetVersionEx +#endif + namespace { cmSystemTools::InterruptCallback s_InterruptCallback; @@ -904,6 +908,40 @@ cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry() InitWindowsDirectoryRetry().Retry; return retry; } + +cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion() +{ + /* Windows version number data. */ + OSVERSIONINFOEXW osviex; + ZeroMemory(&osviex, sizeof(osviex)); + osviex.dwOSVersionInfoSize = sizeof(osviex); + +# ifdef CM_WINDOWS_DEPRECATED_GetVersionEx +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +# endif + GetVersionExW((OSVERSIONINFOW*)&osviex); +# ifdef CM_WINDOWS_DEPRECATED_GetVersionEx +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +# endif + + WindowsVersion result; + result.dwMajorVersion = osviex.dwMajorVersion; + result.dwMinorVersion = osviex.dwMinorVersion; + result.dwBuildNumber = osviex.dwBuildNumber; + return result; +} #endif std::string cmSystemTools::GetRealPathResolvingWindowsSubst( @@ -3319,22 +3357,12 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName, uv_fs_t req; int flags = 0; #if defined(_WIN32) - bool const isDir = cmsys::SystemTools::FileIsDirectory(origName); - if (isDir) { - flags |= UV_FS_SYMLINK_JUNCTION; + if (cmsys::SystemTools::FileIsDirectory(origName)) { + flags |= UV_FS_SYMLINK_DIR; } #endif int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(), flags, nullptr); -#if defined(_WIN32) - if (err && uv_fs_get_system_error(&req) == ERROR_NOT_SUPPORTED && isDir) { - // Try fallback to symlink for network (requires additional permissions). - flags ^= UV_FS_SYMLINK_JUNCTION | UV_FS_SYMLINK_DIR; - err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(), - flags, nullptr); - } -#endif - cmsys::Status status; if (err) { #if defined(_WIN32) diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 5f7a5ecbb0..ec650f7431 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -509,6 +509,14 @@ public: }; static WindowsFileRetry GetWindowsFileRetry(); static WindowsFileRetry GetWindowsDirectoryRetry(); + + struct WindowsVersion + { + unsigned int dwMajorVersion; + unsigned int dwMinorVersion; + unsigned int dwBuildNumber; + }; + static WindowsVersion GetWindowsVersion(); #endif /** Get the real path for a given path, removing all symlinks. diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index c76e2cb02a..eafea059e1 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1935,6 +1935,51 @@ void cmTarget::AppendBuildInterfaceIncludes() } } +void cmTarget::FinalizeTargetCompileInfo( + const cmBTStringRange& noConfigCompileDefinitions, + cm::optional<std::map<std::string, cmValue>>& perConfigCompileDefinitions) +{ + if (this->GetType() == cmStateEnums::GLOBAL_TARGET) { + return; + } + + this->AppendBuildInterfaceIncludes(); + + if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return; + } + + for (auto const& def : noConfigCompileDefinitions) { + this->InsertCompileDefinition(def); + } + + auto* mf = this->GetMakefile(); + cmPolicies::PolicyStatus polSt = mf->GetPolicyStatus(cmPolicies::CMP0043); + if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { + if (perConfigCompileDefinitions) { + for (auto const& it : *perConfigCompileDefinitions) { + if (cmValue val = it.second) { + this->AppendProperty(it.first, *val); + } + } + } else { + perConfigCompileDefinitions.emplace(); + std::vector<std::string> configs = + mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + + for (std::string const& c : configs) { + std::string defPropName = + cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c)); + cmValue val = mf->GetProperty(defPropName); + (*perConfigCompileDefinitions)[defPropName] = val; + if (val) { + this->AppendProperty(defPropName, *val); + } + } + } + } +} + void cmTarget::InsertInclude(BT<std::string> const& entry, bool before) { auto position = before ? this->impl->IncludeDirectoriesEntries.begin() diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 94d66885a7..3d0a06be0f 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -5,12 +5,15 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> +#include <map> #include <memory> #include <set> #include <string> #include <utility> #include <vector> +#include <cm/optional> + #include "cmAlgorithms.h" #include "cmFileSet.h" #include "cmPolicies.h" @@ -233,6 +236,9 @@ public: void InsertPrecompileHeader(BT<std::string> const& entry); void AppendBuildInterfaceIncludes(); + void FinalizeTargetCompileInfo( + const cmBTStringRange& noConfigCompileDefinitions, + cm::optional<std::map<std::string, cmValue>>& perConfigCompileDefinitions); std::string GetDebugGeneratorExpressions(const std::string& value, cmTargetLinkLibraryType llt) const; diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index 130c2288a8..7514a23769 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -2,34 +2,35 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTryCompileCommand.h" +#include "cmCoreTryCompile.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmake.h" -class cmExecutionStatus; - -// cmTryCompileCommand -bool cmTryCompileCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - if (argv.size() < 3) { + if (args.size() < 3) { return false; } - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( + cmMakefile& mf = status.GetMakefile(); + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The try_compile() command is not supported in --find-package mode."); return false; } - this->TryCompileCode(argv, false); + cmCoreTryCompile tc(&mf); + tc.TryCompileCode(args, false); // if They specified clean then we clean up what we can - if (this->SrcFileSignature) { - if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { - this->CleanupFiles(this->BinaryDirectory); + if (tc.SrcFileSignature) { + if (!mf.GetCMakeInstance()->GetDebugTryCompile()) { + tc.CleanupFiles(tc.BinaryDirectory); } } return true; diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h index d8cc16e59a..6a3430b6b6 100644 --- a/Source/cmTryCompileCommand.h +++ b/Source/cmTryCompileCommand.h @@ -7,33 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryCompileCommand - * \brief Specifies where to install some files - * - * cmTryCompileCommand is used to test if source code can be compiled - */ -class cmTryCompileCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryCompileCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; -}; +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index c82ac6429d..98cacdc65a 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -6,7 +6,9 @@ #include "cmsys/FStream.hxx" +#include "cmCoreTryCompile.h" #include "cmDuration.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmRange.h" @@ -17,24 +19,40 @@ #include "cmValue.h" #include "cmake.h" -class cmExecutionStatus; +namespace { -// cmTryRunCommand -bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +class TryRunCommandImpl : public cmCoreTryCompile { - if (argv.size() < 4) { - return false; - } - - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "The try_run() command is not supported in --find-package mode."); - return false; +public: + TryRunCommandImpl(cmMakefile* mf) + : cmCoreTryCompile(mf) + { } + bool TryRunCode(std::vector<std::string> const& args); + + void RunExecutable(const std::string& runArgs, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + void DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + + std::string CompileResultVariable; + std::string RunResultVariable; + std::string OutputVariable; + std::string RunOutputVariable; + std::string RunOutputStdOutVariable; + std::string RunOutputStdErrVariable; + std::string CompileOutputVariable; + std::string WorkingDirectory; +}; + +bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) +{ // build an arg list for TryCompile and extract the runArgs, std::vector<std::string> tryCompile; @@ -42,6 +60,8 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, this->RunResultVariable.clear(); this->OutputVariable.clear(); this->RunOutputVariable.clear(); + this->RunOutputStdOutVariable.clear(); + this->RunOutputStdErrVariable.clear(); this->CompileOutputVariable.clear(); std::string runArgs; @@ -76,6 +96,22 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, } i++; this->RunOutputVariable = argv[i]; + } else if (argv[i] == "RUN_OUTPUT_STDOUT_VARIABLE") { + if (argv.size() <= (i + 1)) { + cmSystemTools::Error( + "RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable"); + return false; + } + i++; + this->RunOutputStdOutVariable = argv[i]; + } else if (argv[i] == "RUN_OUTPUT_STDERR_VARIABLE") { + if (argv.size() <= (i + 1)) { + cmSystemTools::Error( + "RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable"); + return false; + } + i++; + this->RunOutputStdErrVariable = argv[i]; } else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") { if (argv.size() <= (i + 1)) { cmSystemTools::Error( @@ -102,11 +138,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, // using OUTPUT_VARIABLE makes crosscompiling harder if (!this->OutputVariable.empty() && (!this->RunOutputVariable.empty() || - !this->CompileOutputVariable.empty())) { + !this->CompileOutputVariable.empty() || + !this->RunOutputStdOutVariable.empty() || + !this->RunOutputStdErrVariable.empty())) { cmSystemTools::Error( "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE " - "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or " - "RUN_OUTPUT_VARIABLE."); + ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE. " + "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, " + "RUN_OUTPUT_STDOUT_VARIABLE " + "and/or RUN_OUTPUT_STDERR_VARIABLE."); + return false; + } + + if ((!this->RunOutputStdOutVariable.empty() || + !RunOutputStdErrVariable.empty()) && + !this->RunOutputVariable.empty()) { + cmSystemTools::Error( + "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE together " + "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or " + "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE."); return false; } @@ -119,6 +171,7 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, } bool captureRunOutput = false; + bool captureRunOutputStdOutErr = false; if (!this->OutputVariable.empty()) { captureRunOutput = true; tryCompile.emplace_back("OUTPUT_VARIABLE"); @@ -128,7 +181,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, tryCompile.emplace_back("OUTPUT_VARIABLE"); tryCompile.push_back(this->CompileOutputVariable); } - if (!this->RunOutputVariable.empty()) { + if (!this->RunOutputStdOutVariable.empty() || + !RunOutputStdErrVariable.empty()) { + captureRunOutputStdOutErr = true; + } else if (!this->RunOutputVariable.empty()) { captureRunOutput = true; } @@ -145,12 +201,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, } else { // "run" it and capture the output std::string runOutputContents; + std::string runOutputStdOutContents; + std::string runOutputStdErrContents; if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") && !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) { this->DoNotRunExecutable( - runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr); + runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty() + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty() + ? &runOutputStdErrContents + : nullptr); } else { - this->RunExecutable(runArgs, &runOutputContents); + this->RunExecutable( + runArgs, captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty() + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty() + ? &runOutputStdErrContents + : nullptr); } // now put the output into the variables @@ -158,6 +229,14 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, this->Makefile->AddDefinition(this->RunOutputVariable, runOutputContents); } + if (!this->RunOutputStdOutVariable.empty()) { + this->Makefile->AddDefinition(this->RunOutputStdOutVariable, + runOutputStdOutContents); + } + if (!this->RunOutputStdErrVariable.empty()) { + this->Makefile->AddDefinition(this->RunOutputStdErrVariable, + runOutputStdErrContents); + } if (!this->OutputVariable.empty()) { // if the TryCompileCore saved output in this outputVariable then @@ -179,8 +258,9 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, return true; } -void cmTryRunCommand::RunExecutable(const std::string& runArgs, - std::string* out) +void TryRunCommandImpl::RunExecutable(const std::string& runArgs, + std::string* out, std::string* stdOut, + std::string* stdErr) { int retVal = -1; @@ -204,7 +284,8 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, finalCommand += runArgs; } bool worked = cmSystemTools::RunSingleCommand( - finalCommand, out, out, &retVal, + finalCommand, stdOut || stdErr ? stdOut : out, + stdOut || stdErr ? stdErr : out, &retVal, this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(), cmSystemTools::OUTPUT_NONE, cmDuration::zero()); // set the run var @@ -225,9 +306,11 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, executable, two cache variables are created which will hold the results the executable would have produced. */ -void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* out) +void TryRunCommandImpl::DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string* out, + std::string* stdOut, + std::string* stdErr) { // copy the executable out of the CMakeFiles/ directory, so it is not // removed at the end of try_run() and the user can run it manually @@ -246,6 +329,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, std::string internalRunOutputName = this->RunResultVariable + "__TRYRUN_OUTPUT"; + std::string internalRunOutputStdOutName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT"; + std::string internalRunOutputStdErrName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR"; bool error = false; if (!this->Makefile->GetDefinition(this->RunResultVariable)) { @@ -269,7 +356,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, } // is the output from the executable used ? - if (out) { + if (stdOut || stdErr) { + if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stdout on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdOutName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED", + "1"); + } + + error = true; + } + + if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stderr on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdErrName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED", + "1"); + } + + error = true; + } + } else if (out) { if (!this->Makefile->GetDefinition(internalRunOutputName)) { // if the variables doesn't exist, create it with a helpful error text // and mark it as advanced @@ -317,7 +448,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " to\n" " the exit code (in many cases 0 for success), otherwise " "enter \"FAILED_TO_RUN\".\n"); - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + comment += internalRunOutputStdOutName; + comment += + "\n contains the text the executable " + "would have printed on stdout.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdOutName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + if (stdErr) { + comment += internalRunOutputStdErrName; + comment += + "\n contains the text the executable " + "would have printed on stderr.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdErrName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + } else if (out) { comment += internalRunOutputName; comment += "\n contains the text the executable " @@ -330,6 +488,7 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " check what the source file would have printed when " "called with the given arguments.\n"; } + comment += "The "; comment += this->CompileResultVariable; comment += " variable holds the build result for this try_run().\n\n" @@ -370,7 +529,35 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, return; } - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName); + } + if (stdErr) { + (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName); + } + } else if (out) { (*out) = *this->Makefile->GetDefinition(internalRunOutputName); } } +} + +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 4) { + return false; + } + + cmMakefile& mf = status.GetMakefile(); + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( + MessageType::FATAL_ERROR, + "The try_run() command is not supported in --find-package mode."); + return false; + } + + TryRunCommandImpl tr(&mf); + return tr.TryRunCode(args); +} diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h index d45acd85ec..38e36380e4 100644 --- a/Source/cmTryRunCommand.h +++ b/Source/cmTryRunCommand.h @@ -7,47 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryRunCommand - * \brief Specifies where to install some files - * - * cmTryRunCommand is used to test if source code can be compiled - */ -class cmTryRunCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryRunCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void RunExecutable(const std::string& runArgs, - std::string* runOutputContents); - void DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* runOutputContents); - - std::string CompileResultVariable; - std::string RunResultVariable; - std::string OutputVariable; - std::string RunOutputVariable; - std::string CompileOutputVariable; - std::string WorkingDirectory; -}; +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 020691d059..4c1fa0107f 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -50,24 +50,6 @@ #include "cmValue.h" #include "cmVisualStudioGeneratorOptions.h" -namespace { -std::string getProjectFileExtension(VsProjectType projectType) -{ - switch (projectType) { - case VsProjectType::csproj: - return ".csproj"; - case VsProjectType::proj: - return ".proj"; - case VsProjectType::vcxproj: - return ".vcxproj"; - // Valid inputs shouldn't reach here. This default is needed so that all - // paths return value (C4715). - default: - return ""; - } -} -} - struct cmIDEFlagTable; static void ConvertToWindowsSlash(std::string& s); @@ -253,6 +235,31 @@ static bool cmVS10IsTargetsFile(std::string const& path) return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0; } +static VsProjectType computeProjectType(cmGeneratorTarget const* t) +{ + if (t->IsCSharpOnly()) { + return VsProjectType::csproj; + } + return VsProjectType::vcxproj; +} + +static std::string computeProjectFileExtension(VsProjectType projectType) +{ + switch (projectType) { + case VsProjectType::csproj: + return ".csproj"; + case VsProjectType::proj: + return ".proj"; + default: + return ".vcxproj"; + } +} + +static std::string computeProjectFileExtension(cmGeneratorTarget const* t) +{ + return computeProjectFileExtension(computeProjectType(t)); +} + cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg) : GeneratorTarget(target) @@ -359,10 +366,10 @@ void cmVisualStudio10TargetGenerator::Generate() "by the generator")); } - this->ProjectType = this->ComputeProjectType(this->GeneratorTarget); + this->ProjectType = computeProjectType(this->GeneratorTarget); this->Managed = this->ProjectType == VsProjectType::csproj; const std::string ProjectFileExtension = - getProjectFileExtension(this->ProjectType); + computeProjectFileExtension(this->ProjectType); if (this->ProjectType == VsProjectType::csproj && this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { @@ -423,12 +430,10 @@ void cmVisualStudio10TargetGenerator::Generate() char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; BuildFileStream.write(magic, 3); - if (this->ProjectType == VsProjectType::proj) { - this->WriteZeroCheckProj(BuildFileStream); - } else if (this->ProjectType == VsProjectType::csproj && - this->GeneratorTarget->IsDotNetSdkTarget() && - this->GlobalGenerator->GetVersion() >= - cmGlobalVisualStudioGenerator::VSVersion::VS16) { + if (this->ProjectType == VsProjectType::csproj && + this->GeneratorTarget->IsDotNetSdkTarget() && + this->GlobalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VSVersion::VS16) { this->WriteSdkStyleProjectFile(BuildFileStream); } else { this->WriteClassicMsBuildProjectFile(BuildFileStream); @@ -977,45 +982,6 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( this->WriteProjectReferences(e0); } -void cmVisualStudio10TargetGenerator::WriteZeroCheckProj( - cmGeneratedFileStream& BuildFileStream) -{ - // ZERO_CHECK.proj is an XML file without any imports or targets. This is a - // ProjectReference for other targets and therefore, it needs to follow the - // ProjectReference protocol as documented here: - // https://github.com/dotnet/msbuild/blob/main/documentation/ProjectReference-Protocol.md - // - // We implement MSBuild target Build from WriteCustomCommand which calls - // WriteZeroCheckBuildTarget after setting up the command generator. MSBuild - // target Clean is a no-op as we do all the work for ZERO_CHECK on Build. - // MSBuild target GetTargetPath is needed and is no-op. - // MSBuild targets GetNativeManifest and GetCopyToOutputDirectoryItems are - // needed for MSBuild versions below 15.7 and are no-op. MSBuild target - // BeforeBuild is needed for supporting GLOBs. - BuildFileStream << "<?xml version=\"1.0\" encoding=\"" - << this->GlobalGenerator->Encoding() << "\"?>"; - { - Elem e0(BuildFileStream, "Project"); - e0.Attribute("DefaultTargets", "Build"); - e0.Attribute("ToolsVersion", this->GlobalGenerator->GetToolsVersion()); - e0.Attribute("xmlns", - "http://schemas.microsoft.com/developer/msbuild/2003"); - - this->WriteCustomCommands(e0); - - for (const char* targetName : - { "Clean", "GetTargetPath", "GetNativeManifest", - "GetCopyToOutputDirectoryItems" }) { - { - Elem e1(e0, "Target"); - e1.Attribute("Name", targetName); - } - } - - this->WriteZeroCheckBeforeBuildTarget(e0); - } -} - void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1) { e1.Attribute("Label", "Globals"); @@ -1557,6 +1523,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( this->ASanEnabledConfigurations.end()) { e1.Element("EnableAsan", "true"); } + if (this->FuzzerEnabledConfigurations.find(config) != + this->FuzzerEnabledConfigurations.end()) { + e1.Element("EnableFuzzer", "true"); + } { auto s = this->SpectreMitigation.find(config); if (s != this->SpectreMitigation.end()) { @@ -1735,16 +1705,11 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( } } } - if (this->ProjectType == VsProjectType::proj) { - this->WriteZeroCheckBuildTarget(e0, command, source); - return; - } - cmLocalVisualStudio7Generator* lg = this->LocalGenerator; std::unique_ptr<Elem> spe1; std::unique_ptr<Elem> spe2; - if (this->ProjectType == VsProjectType::vcxproj) { + if (this->ProjectType != VsProjectType::csproj) { spe1 = cm::make_unique<Elem>(e0, "ItemGroup"); spe2 = cm::make_unique<Elem>(*spe1, "CustomBuild"); this->WriteSource(*spe2, source); @@ -1942,7 +1907,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() // Write out group file std::string path = cmStrCat( this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->Name, - this->ComputeProjectFileExtension(this->GeneratorTarget), ".filters"); + computeProjectFileExtension(this->GeneratorTarget), ".filters"); cmGeneratedFileStream fout(path); fout.SetCopyIfDifferent(true); char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; @@ -2930,7 +2895,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) { @@ -3097,134 +3062,6 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental( } } -void cmVisualStudio10TargetGenerator::WriteZeroCheckBuildTarget( - cmVisualStudio10TargetGenerator::Elem& e0, const cmCustomCommand& command, - const cmSourceFile* source) -{ - cmLocalVisualStudio7Generator* lg = this->LocalGenerator; - - Elem e1(e0, "Target"); - e1.Attribute("Name", "Build"); - - std::string noConfig{}; - cmCustomCommandGenerator ccg{ command, noConfig, lg, true }; - std::string comment = lg->ConstructComment(ccg); - comment = cmVS10EscapeComment(comment); - std::string script = lg->ConstructScript(ccg); - bool symbolic = false; - // input files for custom command - std::stringstream additional_inputs; - { - const char* sep = ""; - if (this->ProjectType == VsProjectType::proj) { - // List explicitly the path to primary input. - std::string sourceFullPath = source->GetFullPath(); - ConvertToWindowsSlash(sourceFullPath); - additional_inputs << sourceFullPath; - sep = ";"; - } - - // Avoid listing an input more than once. - std::set<std::string> unique_inputs; - // The source is either implicitly an input or has been added above. - unique_inputs.insert(source->GetFullPath()); - - for (std::string const& d : ccg.GetDepends()) { - std::string dep; - if (lg->GetRealDependency(d, noConfig, dep)) { - if (!unique_inputs.insert(dep).second) { - // already listed - continue; - } - ConvertToWindowsSlash(dep); - additional_inputs << sep << dep; - sep = ";"; - if (!symbolic) { - if (cmSourceFile* sf = this->Makefile->GetSource( - dep, cmSourceFileLocationKind::Known)) { - symbolic = sf->GetPropertyAsBool("SYMBOLIC"); - } - } - } - } - } - // output files for custom command - std::stringstream outputs; - { - const char* sep = ""; - for (std::string const& o : ccg.GetOutputs()) { - std::string out = o; - ConvertToWindowsSlash(out); - outputs << sep << out; - sep = ";"; - if (!symbolic) { - if (cmSourceFile* sf = - this->Makefile->GetSource(o, cmSourceFileLocationKind::Known)) { - symbolic = sf->GetPropertyAsBool("SYMBOLIC"); - } - } - } - } - script += lg->FinishConstructScript(this->ProjectType); - - e1.Attribute("Inputs", cmVS10EscapeAttr(additional_inputs.str())); - e1.Attribute("Outputs", cmVS10EscapeAttr(outputs.str())); - - e1.SetHasElements(); - - if (!comment.empty()) { - Elem(e1, "Message").Attribute("Text", comment); - } - Elem(e1, "Exec").Attribute("Command", script); -} - -void cmVisualStudio10TargetGenerator::WriteZeroCheckBeforeBuildTarget( - cmVisualStudio10TargetGenerator::Elem& e0) -{ - const auto& commands = this->GeneratorTarget->GetPreBuildCommands(); - if (commands.empty()) { - return; - } - - { - Elem e1(e0, "Target"); - e1.Attribute("Name", "BeforeBuild"); - e1.Attribute("BeforeTargets", "Build"); - - cmLocalVisualStudio7Generator* lg = this->LocalGenerator; - std::string script; - const char* pre = ""; - std::string comment; - for (cmCustomCommand const& cc : commands) { - cmCustomCommandGenerator ccg(cc, std::string{}, lg); - if (!ccg.HasOnlyEmptyCommandLines()) { - comment += pre; - comment += lg->ConstructComment(ccg); - script += pre; - pre = "\n"; - script += lg->ConstructScript(ccg); - } - } - - if (script.empty()) { - return; - } - - script += lg->FinishConstructScript(this->ProjectType); - comment = cmVS10EscapeComment(comment); - std::string strippedComment = comment; - strippedComment.erase( - std::remove(strippedComment.begin(), strippedComment.end(), '\t'), - strippedComment.end()); - - e1.SetHasElements(); - if (!comment.empty() && !strippedComment.empty()) { - Elem(e1, "Message").Attribute("Text", comment); - } - Elem(e1, "Exec").Attribute("Command", script); - } -} - std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes( std::string const& config, std::string const& lang) const { @@ -3312,10 +3149,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } // Check if ASan is enabled. - if (flags.find("/fsanitize=address") != std::string::npos) { + if (flags.find("/fsanitize=address") != std::string::npos || + flags.find("-fsanitize=address") != std::string::npos) { this->ASanEnabledConfigurations.insert(configName); } + // Check if (lib)Fuzzer is enabled. + if (flags.find("/fsanitize=fuzzer") != std::string::npos || + flags.find("-fsanitize=fuzzer") != std::string::npos) { + this->FuzzerEnabledConfigurations.insert(configName); + } + // Precompile Headers std::string pchHeader = this->GeneratorTarget->GetPchHeader(configName, linkLanguage); @@ -3357,7 +3201,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( // anymore, because cmGeneratorTarget may not be aware that the // target uses C++/CLI. if (flags.find("/clr") != std::string::npos || - defineFlags.find("/clr") != std::string::npos) { + flags.find("-clr") != std::string::npos || + defineFlags.find("/clr") != std::string::npos || + defineFlags.find("-clr") != std::string::npos) { if (configName == this->Configurations[0]) { std::string message = "For the target \"" + this->GeneratorTarget->GetName() + @@ -4746,7 +4592,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) path = *p; } else { path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(), - this->ComputeProjectFileExtension(dt)); + computeProjectFileExtension(dt)); } ConvertToWindowsSlash(path); Elem e2(e1, "ProjectReference"); @@ -5600,26 +5446,6 @@ std::string cmVisualStudio10TargetGenerator::GetCMakeFilePath( return path; } -std::string cmVisualStudio10TargetGenerator::ComputeProjectFileExtension( - cmGeneratorTarget const* t) const -{ - return getProjectFileExtension(this->ComputeProjectType(t)); -} - -VsProjectType cmVisualStudio10TargetGenerator::ComputeProjectType( - cmGeneratorTarget const* t) const -{ - if (this->GlobalGenerator->GetVersion() >= - cmGlobalVisualStudioGenerator::VSVersion::VS16 && - t->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { - return VsProjectType::proj; - } - if (t->IsCSharpOnly()) { - return VsProjectType::csproj; - } - return VsProjectType::vcxproj; -} - void cmVisualStudio10TargetGenerator::WriteStdOutEncodingUtf8(Elem& e1) { if (this->GlobalGenerator->IsUtf8EncodingSupported()) { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 7a0b8da728..17dcecdb13 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -231,6 +231,7 @@ private: bool TargetCompileAsWinRT; std::set<std::string> IPOEnabledConfigurations; std::set<std::string> ASanEnabledConfigurations; + std::set<std::string> FuzzerEnabledConfigurations; std::map<std::string, std::string> SpectreMitigation; cmGlobalVisualStudio10Generator* const GlobalGenerator; cmLocalVisualStudio10Generator* const LocalGenerator; @@ -264,13 +265,6 @@ private: void WriteClassicMsBuildProjectFile(cmGeneratedFileStream& BuildFileStream); void WriteSdkStyleProjectFile(cmGeneratedFileStream& BuildFileStream); - void WriteZeroCheckProj(cmGeneratedFileStream& BuildFileStream); - void WriteZeroCheckBuildTarget(cmVisualStudio10TargetGenerator::Elem& e0, - const cmCustomCommand& command, - const cmSourceFile* source); - void WriteZeroCheckBeforeBuildTarget( - cmVisualStudio10TargetGenerator::Elem& e0); - void WriteCommonPropertyGroupGlobals( cmVisualStudio10TargetGenerator::Elem& e1); @@ -282,7 +276,4 @@ private: void ParseSettingsProperty(const std::string& settingsPropertyValue, ConfigToSettings& toolSettings); std::string GetCMakeFilePath(const char* name) const; - - std::string ComputeProjectFileExtension(cmGeneratorTarget const* t) const; - VsProjectType ComputeProjectType(cmGeneratorTarget const* t) const; }; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index efb2520683..12d42b230c 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -406,9 +406,6 @@ void cmake::PrintPresetEnvironment() // Parse the args bool cmake::SetCacheArgs(const std::vector<std::string>& args) { - auto findPackageMode = false; - auto seenScriptOption = false; - auto DefineLambda = [](std::string const& entry, cmake* state) -> bool { std::string var; std::string value; @@ -499,10 +496,10 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) GetProjectCommandsInScriptMode(state->GetState()); // Documented behavior of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be // set to $PWD for -P mode. + state->SetWorkingMode(SCRIPT_MODE); state->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory()); state->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory()); state->ReadListFile(args, path); - seenScriptOption = true; return true; }; @@ -566,12 +563,12 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) "No install directory specified for --install-prefix", CommandArgument::Values::One, PrefixLambda }, CommandArgument{ "--find-package", CommandArgument::Values::Zero, - CommandArgument::setToTrue(findPackageMode) }, + IgnoreAndTrueLambda }, }; for (decltype(args.size()) i = 1; i < args.size(); ++i) { std::string const& arg = args[i]; - if (arg == "--" && seenScriptOption) { + if (arg == "--" && this->GetWorkingMode() == SCRIPT_MODE) { // Stop processing CMake args and avoid possible errors // when arbitrary args are given to CMake script. break; @@ -586,7 +583,7 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) } } - if (findPackageMode) { + if (this->GetWorkingMode() == FIND_PACKAGE_MODE) { return this->FindPackage(args); } @@ -791,7 +788,6 @@ void cmake::SetArgs(const std::vector<std::string>& args) bool haveToolset = false; bool havePlatform = false; bool haveBArg = false; - bool scriptMode = false; std::string possibleUnknownArg; std::string extraProvidedPath; #if !defined(CMAKE_BOOTSTRAP) @@ -874,7 +870,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument{ "-P", "-P must be followed by a file name.", CommandArgument::Values::One, CommandArgument::RequiresSeparator::No, - CommandArgument::setToTrue(scriptMode) }, + IgnoreAndTrueLambda }, CommandArgument{ "-D", "-D must be followed with VAR=VALUE.", CommandArgument::Values::One, CommandArgument::RequiresSeparator::No, @@ -1166,7 +1162,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) // iterate each argument std::string const& arg = args[i]; - if (scriptMode && arg == "--") { + if (this->GetWorkingMode() == SCRIPT_MODE && arg == "--") { // Stop processing CMake args and avoid possible errors // when arbitrary args are given to CMake script. break; @@ -1212,12 +1208,12 @@ void cmake::SetArgs(const std::vector<std::string>& args) } } - if (!extraProvidedPath.empty() && !scriptMode) { + if (!extraProvidedPath.empty() && this->GetWorkingMode() == NORMAL_MODE) { this->IssueMessage(MessageType::WARNING, cmStrCat("Ignoring extra path from command line:\n \"", extraProvidedPath, "\"")); } - if (!possibleUnknownArg.empty() && !scriptMode) { + if (!possibleUnknownArg.empty() && this->GetWorkingMode() != SCRIPT_MODE) { cmSystemTools::Error(cmStrCat("Unknown argument ", possibleUnknownArg)); cmSystemTools::Error("Run 'cmake --help' for all supported options."); exit(1); @@ -1830,7 +1826,8 @@ void cmake::SetHomeDirectoryViaCommandLine(std::string const& path) } auto prev_path = this->GetHomeDirectory(); - if (prev_path != path && !prev_path.empty()) { + if (prev_path != path && !prev_path.empty() && + this->GetWorkingMode() == NORMAL_MODE) { this->IssueMessage(MessageType::WARNING, cmStrCat("Ignoring extra path from command line:\n \"", prev_path, "\"")); diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 97c275efc2..f931e9dbcd 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -207,7 +207,7 @@ int do_cmake(int ac, char const* const* av) #ifndef CMAKE_BOOTSTRAP cmDocumentation doc; doc.addCMakeStandardDocSections(); - if (doc.CheckOptions(ac, av)) { + if (doc.CheckOptions(ac, av, "--")) { // Construct and print requested documentation. cmake hcm(cmake::RoleInternal, cmState::Unknown); hcm.SetHomeDirectory(""); diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 8921aa0ca1..69eb19e2f0 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -273,6 +273,7 @@ int main() std::string clrest = rest; // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj clrest = replace(clrest, "/fo ", "/out:"); + clrest = replace(clrest, "-fo ", "-out:"); clrest = replace(clrest, objfile, objfile + ".dep.obj "); cl = "\"" + cl + "\" /P /DRC_INVOKED /TC "; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9ab39f1ebe..b1f1bcfe3c 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -2242,13 +2242,18 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, // Parse the link command to extract information we need. for (; arg != argEnd; ++arg) { if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0 || - cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) { + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:YES") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) { this->Incremental = true; - } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) { + } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) { this->LinkGeneratesManifest = false; - } else if (cmHasLiteralPrefix(*arg, "/Fe")) { + } else if (cmHasLiteralPrefix(*arg, "/Fe") || + cmHasLiteralPrefix(*arg, "-Fe")) { this->TargetFile = arg->substr(3); - } else if (cmHasLiteralPrefix(*arg, "/out:")) { + } else if (cmHasLiteralPrefix(*arg, "/out:") || + cmHasLiteralPrefix(*arg, "-out:")) { this->TargetFile = arg->substr(5); } } diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index d520c14404..f239576efe 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -43,12 +43,12 @@ public: { std::string Name; #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t FindData; + WIN32_FIND_DATAW FindData; #endif FileData(std::string name #if defined(_WIN32) && !defined(__CYGWIN__) , - _wfinddata_t data + WIN32_FIND_DATAW data #endif ) : Name(std::move(name)) @@ -115,8 +115,8 @@ std::string Directory::GetFilePath(std::size_t i) const bool Directory::FileIsDirectory(std::size_t i) const { #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; - return (data.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + auto const& data = this->Internal->Files[i].FindData; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; #else std::string const& path = this->GetFilePath(i); return kwsys::SystemTools::FileIsDirectory(path); @@ -127,9 +127,9 @@ bool Directory::FileIsSymlink(std::size_t i) const { std::string const& path = this->GetFilePath(i); #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; + auto const& data = this->Internal->Files[i].FindData; return kwsys::SystemTools::FileIsSymlinkWithAttr( - Encoding::ToWindowsExtendedPath(path), data.attrib); + Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes); #else return kwsys::SystemTools::FileIsSymlink(path); #endif @@ -157,7 +157,7 @@ namespace KWSYS_NAMESPACE { Status Directory::Load(std::string const& name, std::string* errorMessage) { this->Clear(); - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -176,14 +176,14 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) snprintf(buf, bufLength, "%s/*", name.c_str()); } } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array srchHandle = - _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data); + FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -193,10 +193,11 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) // Loop through names do { - this->Internal->Files.emplace_back(Encoding::ToNarrow(data.name), data); - } while (_wfindnext(srchHandle, &data) != -1); + this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName), + data); + } while (FindNextFileW(srchHandle, &data)); this->Internal->Path = name; - if (_findclose(srchHandle) == -1) { + if (!FindClose(srchHandle)) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -209,7 +210,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, std::string* errorMessage) { - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -222,13 +223,13 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, buf = new char[n + 2 + 1]; snprintf(buf, bufLength, "%s/*", name.c_str()); } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array - srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data); + srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { if (errorMessage) { if (unsigned int errorId = GetLastError()) { LPSTR message = nullptr; @@ -250,8 +251,8 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, unsigned long count = 0; do { count++; - } while (_wfindnext(srchHandle, &data) != -1); - _findclose(srchHandle); + } while (FindNextFileW(srchHandle, &data)); + FindClose(srchHandle); return count; } diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 89a3cc789f..a20901cb29 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -536,9 +536,11 @@ public: StringMap TranslationMap; #endif #ifdef _WIN32 - static std::string GetCasePathName(std::string const& pathIn); + static std::string GetCasePathName(std::string const& pathIn, + bool const cache); static std::string GetActualCaseForPathCached(std::string const& path); static const char* GetEnvBuffered(const char* key); + std::map<std::string, std::string, SystemToolsPathCaseCmp> FindFileMap; std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap; std::map<std::string, std::string> EnvMap; #endif @@ -571,7 +573,8 @@ public: static SystemToolsStatic* SystemToolsStatics; #ifdef _WIN32 -std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, + bool const cache) { std::string casePath; @@ -623,14 +626,31 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) } else { std::string test_str = casePath; test_str += path_components[idx]; - WIN32_FIND_DATAW findData; - HANDLE hFind = - ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - path_components[idx] = Encoding::ToNarrow(findData.cFileName); - ::FindClose(hFind); - } else { - converting = false; + + bool found_in_cache = false; + if (cache) { + auto const it = SystemToolsStatics->FindFileMap.find(test_str); + if (it != SystemToolsStatics->FindFileMap.end()) { + path_components[idx] = it->second; + found_in_cache = true; + } + } + + if (!found_in_cache) { + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + auto case_file_name = Encoding::ToNarrow(findData.cFileName); + if (cache) { + SystemToolsStatics->FindFileMap.emplace(test_str, + case_file_name); + } + path_components[idx] = std::move(case_file_name); + ::FindClose(hFind); + } else { + converting = false; + } } } } @@ -642,19 +662,16 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) { - // Check to see if actual case has already been called - // for this path, and the result is stored in the PathCaseMap - auto& pcm = SystemToolsStatics->PathCaseMap; - { - auto itr = pcm.find(p); - if (itr != pcm.end()) { - return itr->second; - } - } - std::string casePath = SystemToolsStatic::GetCasePathName(p); - if (casePath.size() <= MAX_PATH) { - pcm[p] = casePath; + std::string casePath; + + auto it = SystemToolsStatics->PathCaseMap.find(p); + if (it != SystemToolsStatics->PathCaseMap.end()) { + casePath = it->second; + } else { + casePath = SystemToolsStatic::GetCasePathName(p, true); + SystemToolsStatics->PathCaseMap.emplace(p, casePath); } + return casePath; } #endif @@ -3677,7 +3694,7 @@ std::string SystemTools::RelativePath(const std::string& local, std::string SystemTools::GetActualCaseForPath(const std::string& p) { #ifdef _WIN32 - return SystemToolsStatic::GetCasePathName(p); + return SystemToolsStatic::GetCasePathName(p, false); #else return p; #endif diff --git a/Templates/MSBuild/FlagTables/v10_CSharp.json b/Templates/MSBuild/FlagTables/v10_CSharp.json index 9f21d9a27b..53418416c1 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/v11_CSharp.json b/Templates/MSBuild/FlagTables/v11_CSharp.json index 9f21d9a27b..53418416c1 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 9f21d9a27b..53418416c1 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 9f21d9a27b..53418416c1 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 9f21d9a27b..53418416c1 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 ae1bd47bf6..9ea8f4baae 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 ae1bd47bf6..9ea8f4baae 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/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx index ecfc5fcfe8..58c041859e 100644 --- a/Tests/CMakeLib/testArgumentParser.cxx +++ b/Tests/CMakeLib/testArgumentParser.cxx @@ -1,9 +1,12 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ +#include <functional> #include <initializer_list> #include <iostream> +#include <map> #include <string> +#include <utility> #include <vector> #include <cm/optional> @@ -15,7 +18,7 @@ namespace { -struct Result +struct Result : public ArgumentParser::ParseResult { bool Option1 = false; bool Option2 = false; @@ -24,6 +27,8 @@ struct Result cm::optional<std::string> String2; cm::optional<std::string> String3; ArgumentParser::Maybe<std::string> String4; + ArgumentParser::NonEmpty<std::string> String5; + ArgumentParser::NonEmpty<std::string> String6; ArgumentParser::NonEmpty<std::vector<std::string>> List1; ArgumentParser::NonEmpty<std::vector<std::string>> List2; @@ -36,16 +41,71 @@ struct Result std::vector<std::vector<std::string>> Multi2; cm::optional<std::vector<std::vector<std::string>>> Multi3; cm::optional<std::vector<std::vector<std::string>>> Multi4; + + cm::optional<std::string> Pos1; + + bool Func0_ = false; + ArgumentParser::Continue Func0(cm::string_view) + { + Func0_ = true; + return ArgumentParser::Continue::No; + } + + std::string Func1_; + ArgumentParser::Continue Func1(cm::string_view arg) + { + Func1_ = std::string(arg); + return ArgumentParser::Continue::No; + } + + std::map<std::string, std::vector<std::string>> Func2_; + ArgumentParser::Continue Func2(cm::string_view key, cm::string_view arg) + { + Func2_[std::string(key)].emplace_back(arg); + return key == "FUNC_2b" ? ArgumentParser::Continue::Yes + : ArgumentParser::Continue::No; + } + + std::vector<std::string> Func3_; + ArgumentParser::Continue Func3(cm::string_view arg) + { + Func3_.emplace_back(arg); + return ArgumentParser::Continue::Yes; + } + + std::map<std::string, std::vector<std::string>> Func4_; + ArgumentParser::Continue Func4(cm::string_view key, cm::string_view arg) + { + Func4_[std::string(key)].emplace_back(arg); + return key == "FUNC_4b" ? ArgumentParser::Continue::Yes + : ArgumentParser::Continue::No; + } + + ArgumentParser::Maybe<std::string> UnboundMaybe{ 'u', 'n', 'b', 'o', + 'u', 'n', 'd' }; + ArgumentParser::MaybeEmpty<std::vector<std::string>> UnboundMaybeEmpty{ + 1, "unbound" + }; + ArgumentParser::NonEmpty<std::vector<std::string>> UnboundNonEmpty{ + 1, "unbound" + }; + ArgumentParser::NonEmpty<std::string> UnboundNonEmptyStr{ 'u', 'n', 'b', 'o', + 'u', 'n', 'd' }; + + std::vector<cm::string_view> ParsedKeywords; }; std::initializer_list<cm::string_view> const args = { /* clang-format off */ "OPTION_1", // option // "OPTION_2", // option that is not present + "pos1", // position index 1 "STRING_1", // string arg missing 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 + "STRING_5", "foo", // string arg that is not empty + "STRING_6", "", // string arg that is empty "LIST_1", // list arg missing values "LIST_2", "foo", "bar", // list arg with 2 elems "LIST_3", "bar", // list arg ... @@ -57,18 +117,63 @@ std::initializer_list<cm::string_view> const args = { "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 + "FUNC_0", // callback arg missing value + "FUNC_1", "foo", "ign1", // callback with one arg + unparsed value + "FUNC_2a", "foo", "ign2", // callback with keyword-dependent arg count + "FUNC_2b", "bar", "zot", // callback with keyword-dependent arg count + "FUNC_3", "foo", "bar", // callback with list arg ... + "FUNC_4a", "foo", "ign4", // callback with keyword-dependent arg count + "FUNC_4b", "bar", "zot", // callback with keyword-dependent arg count /* clang-format on */ }; bool verifyResult(Result const& result, - std::vector<std::string> const& unparsedArguments, - std::vector<cm::string_view> const& keywordsMissingValue) + std::vector<std::string> const& unparsedArguments) { static std::vector<std::string> const foobar = { "foo", "bar" }; static std::vector<std::string> const barfoo = { "bar", "foo" }; - static std::vector<cm::string_view> const missing = { "STRING_1"_s, - "LIST_1"_s, - "LIST_4"_s }; + static std::vector<std::string> const unbound = { "unbound" }; + static std::vector<cm::string_view> const parsedKeywords = { + /* clang-format off */ + "OPTION_1", + "STRING_1", + "STRING_2", + "STRING_4", + "STRING_5", + "STRING_6", + "LIST_1", + "LIST_2", + "LIST_3", + "LIST_3", + "LIST_4", + "LIST_6", + "MULTI_2", + "MULTI_3", + "MULTI_3", + "FUNC_0", + "FUNC_1", + "FUNC_2a", + "FUNC_2b", + "FUNC_3", + "FUNC_4a", + "FUNC_4b", + /* clang-format on */ + }; + static std::map<std::string, std::vector<std::string>> const func2map = { + { "FUNC_2a", { "foo" } }, { "FUNC_2b", { "bar", "zot" } } + }; + static std::map<std::string, std::vector<std::string>> const func4map = { + { "FUNC_4a", { "foo" } }, { "FUNC_4b", { "bar", "zot" } } + }; + static std::map<cm::string_view, std::string> const keywordErrors = { + { "STRING_1"_s, " missing required value\n" }, + { "STRING_6"_s, " empty string not allowed\n" }, + { "LIST_1"_s, " missing required value\n" }, + { "LIST_4"_s, " missing required value\n" }, + { "FUNC_0"_s, " missing required value\n" } + }; + static std::vector<std::string> const unparsed = { "bar", "ign1", "ign2", + "ign4" }; #define ASSERT_TRUE(x) \ do { \ @@ -78,6 +183,8 @@ bool verifyResult(Result const& result, } \ } while (false) + ASSERT_TRUE(!result); + ASSERT_TRUE(result.Option1); ASSERT_TRUE(!result.Option2); @@ -86,6 +193,8 @@ bool verifyResult(Result const& result, ASSERT_TRUE(*result.String2 == "foo"); ASSERT_TRUE(!result.String3); ASSERT_TRUE(result.String4.empty()); + ASSERT_TRUE(result.String5 == "foo"); + ASSERT_TRUE(result.String6.empty()); ASSERT_TRUE(result.List1.empty()); ASSERT_TRUE(result.List2 == foobar); @@ -106,9 +215,29 @@ bool verifyResult(Result const& result, ASSERT_TRUE((*result.Multi3)[1] == barfoo); ASSERT_TRUE(!result.Multi4); - ASSERT_TRUE(unparsedArguments.size() == 1); - ASSERT_TRUE(unparsedArguments[0] == "bar"); - ASSERT_TRUE(keywordsMissingValue == missing); + ASSERT_TRUE(result.Pos1 == "pos1"); + + ASSERT_TRUE(result.Func0_ == false); + ASSERT_TRUE(result.Func1_ == "foo"); + ASSERT_TRUE(result.Func2_ == func2map); + ASSERT_TRUE(result.Func3_ == foobar); + ASSERT_TRUE(result.Func4_ == func4map); + + ASSERT_TRUE(unparsedArguments == unparsed); + + ASSERT_TRUE(result.UnboundMaybe == "unbound"); + ASSERT_TRUE(result.UnboundMaybeEmpty == unbound); + ASSERT_TRUE(result.UnboundNonEmpty == unbound); + ASSERT_TRUE(result.UnboundNonEmptyStr == "unbound"); + + ASSERT_TRUE(result.ParsedKeywords == parsedKeywords); + + ASSERT_TRUE(result.GetKeywordErrors().size() == keywordErrors.size()); + for (auto const& ke : result.GetKeywordErrors()) { + auto const ki = keywordErrors.find(ke.first); + ASSERT_TRUE(ki != keywordErrors.end()); + ASSERT_TRUE(ke.second == ki->second); + } return true; } @@ -117,57 +246,112 @@ bool testArgumentParserDynamic() { Result result; std::vector<std::string> unparsedArguments; - 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); + + std::function<ArgumentParser::Continue(cm::string_view, cm::string_view)> + func4 = [&result](cm::string_view key, + cm::string_view arg) -> ArgumentParser::Continue { + return result.Func4(key, arg); + }; + + static_cast<ArgumentParser::ParseResult&>(result) = + 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("STRING_5"_s, result.String5) + .Bind("STRING_6"_s, result.String6) + .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) + .Bind(1, result.Pos1) + .Bind("FUNC_0"_s, + [&result](cm::string_view arg) -> ArgumentParser::Continue { + return result.Func0(arg); + }) + .Bind("FUNC_1"_s, + [&result](cm::string_view arg) -> ArgumentParser::Continue { + return result.Func1(arg); + }) + .Bind("FUNC_2a"_s, + [&result](cm::string_view key, cm::string_view arg) + -> ArgumentParser::Continue { return result.Func2(key, arg); }) + .Bind("FUNC_2b"_s, + [&result](cm::string_view key, cm::string_view arg) + -> ArgumentParser::Continue { return result.Func2(key, arg); }) + .Bind("FUNC_3"_s, + [&result](cm::string_view arg) -> ArgumentParser::Continue { + return result.Func3(arg); + }) + .Bind("FUNC_4a"_s, func4) + .Bind("FUNC_4b"_s, func4) + .BindParsedKeywords(result.ParsedKeywords) + .Parse(args, &unparsedArguments); + + return verifyResult(result, unparsedArguments); } +static auto const parserStaticFunc4 = + [](Result& result, cm::string_view key, + cm::string_view arg) -> ArgumentParser::Continue { + return result.Func4(key, arg); +}; +static auto const parserStatic = // + cmArgumentParser<Result>{} + .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("STRING_5"_s, &Result::String5) + .Bind("STRING_6"_s, &Result::String6) + .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) + .Bind(1, &Result::Pos1) + .Bind("FUNC_0"_s, &Result::Func0) + .Bind("FUNC_1"_s, &Result::Func1) + .Bind("FUNC_2a"_s, &Result::Func2) + .Bind("FUNC_2b"_s, &Result::Func2) + .Bind("FUNC_3"_s, + [](Result& result, cm::string_view arg) -> ArgumentParser::Continue { + return result.Func3(arg); + }) + .Bind("FUNC_4a"_s, parserStaticFunc4) + .Bind("FUNC_4b"_s, parserStaticFunc4) + .BindParsedKeywords(&Result::ParsedKeywords) + /* keep semicolon on own line */; + bool testArgumentParserStatic() { - static auto const parser = // - cmArgumentParser<Result>{} - .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); - std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValue; - Result const result = - parser.Parse(args, &unparsedArguments, &keywordsMissingValue); + Result const result = parserStatic.Parse(args, &unparsedArguments); + return verifyResult(result, unparsedArguments); +} - return verifyResult(result, unparsedArguments, keywordsMissingValue); +bool testArgumentParserStaticBool() +{ + std::vector<std::string> unparsedArguments; + Result result; + ASSERT_TRUE(parserStatic.Parse(result, args, &unparsedArguments) == false); + return verifyResult(result, unparsedArguments); } } // namespace @@ -184,5 +368,10 @@ int testArgumentParser(int /*unused*/, char* /*unused*/ []) return -1; } + if (!testArgumentParserStaticBool()) { + std::cout << "While executing testArgumentParserStaticBool().\n"; + return -1; + } + return 0; } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index f98e7e9c45..ab1fba0cb1 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1747,7 +1747,7 @@ if(BUILD_TESTING) if(NOT CMake_TEST_EXTERNAL_CMAKE) foreach(STP RANGE 2 12) - if (STP EQUAL 6) + if (STP EQUAL 7) set(pass_regex ".*using log and exp") else() set(pass_regex "The square root of 25 is 5") diff --git a/Tests/FindVulkan/Test/CMakeLists.txt b/Tests/FindVulkan/Test/CMakeLists.txt index 727e1ce838..dfcfc156a7 100644 --- a/Tests/FindVulkan/Test/CMakeLists.txt +++ b/Tests/FindVulkan/Test/CMakeLists.txt @@ -8,7 +8,9 @@ set(components shaderc_combined SPIRV-Tools volk + dxc ) + if(APPLE) list(APPEND components MoltenVK) endif() @@ -80,6 +82,10 @@ 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) +add_executable(test_tgt_dxc_lib main-dxc_lib.cxx) +target_link_libraries(test_tgt_dxc_lib Vulkan::dxc_lib) +add_test(NAME test_tgt_dxc_lib COMMAND test_tgt_dxc_lib) + if(Vulkan_GLSLC_EXECUTABLE) add_test(NAME test_glslc COMMAND ${CMAKE_COMMAND} @@ -97,3 +103,12 @@ if(Vulkan_GLSLANG_VALIDATOR_EXECUTABLE) -P "${CMAKE_CURRENT_LIST_DIR}/Run-glslangValidator.cmake" ) endif() + +if(Vulkan_dxc_EXECUTABLE) + add_test(NAME test_dxc_exe + COMMAND ${CMAKE_COMMAND} + "-DVULKAN_DXC_EXECUTABLE=${Vulkan_dxc_EXECUTABLE}" + "-DVULKAN_DXC_EXECUTABLE_TARGET=$<TARGET_FILE:Vulkan::dxc_exe>" + -P "${CMAKE_CURRENT_LIST_DIR}/Run-dxc_exe.cmake" + ) +endif() diff --git a/Tests/FindVulkan/Test/Run-dxc_exe.cmake b/Tests/FindVulkan/Test/Run-dxc_exe.cmake new file mode 100644 index 0000000000..0d38855eea --- /dev/null +++ b/Tests/FindVulkan/Test/Run-dxc_exe.cmake @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.12) + +function(run_dxc_exe exe exe_display) + execute_process(COMMAND ${exe} --help + OUTPUT_VARIABLE output + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE result + ) + + if(NOT result EQUAL 0) + message(SEND_ERROR "Result of ${exe_display} --help is ${result}, should be 0") + endif() + + if(NOT output MATCHES "^OVERVIEW: HLSL Compiler for ") + message(SEND_ERROR "Output of ${exe_display} --help is \"${output}\", should begin with \"OVERVIEW: HLSL Compiler for \"") + endif() +endfunction() + +run_dxc_exe("${VULKAN_DXC_EXECUTABLE}" "\${VULKAN_DXC_EXECUTABLE}") +run_dxc_exe("${VULKAN_DXC_EXECUTABLE_TARGET}" "Vulkan::dxc_exe") diff --git a/Tests/FindVulkan/Test/main-dxc_lib.cxx b/Tests/FindVulkan/Test/main-dxc_lib.cxx new file mode 100644 index 0000000000..6ccb0dea00 --- /dev/null +++ b/Tests/FindVulkan/Test/main-dxc_lib.cxx @@ -0,0 +1,23 @@ +#include <cstdio> + +#include "dxc/dxcapi.h" +#include "printf.h" + +int main() +{ + IDxcCompiler3* compiler; + DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&compiler)); + + assert(compiler); + + IDxcVersionInfo* version; + compiler->QueryInterface(&version); + + uint32_t major, minor; + version->GetVersion(&major, &minor); + printf("DirectX Shader Compiler: %u.%u\n", major, minor); + version->Release(); + compiler->Release(); + + return 0; +} diff --git a/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt b/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt index c94c19da33..cde76f72a1 100644 --- a/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt +++ b/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt @@ -1,4 +1,4 @@ -^-- CMAKE_ARGC='8' +^-- CMAKE_ARGC='9' -- CMAKE_ARGV1='-P' -- CMAKE_ARGV2='[^']*/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake' -- CMAKE_ARGV3='--' @@ -6,4 +6,5 @@ -- CMAKE_ARGV5='-S' -- CMAKE_ARGV6='-B' -- CMAKE_ARGV7='--fresh' --- CMAKE_ARGV8=''$ +-- CMAKE_ARGV8='--version' +-- CMAKE_ARGV9=''$ diff --git a/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake b/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake index 8dca990109..7d79af5bc9 100644 --- a/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake +++ b/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake @@ -7,3 +7,4 @@ message(STATUS "CMAKE_ARGV5='${CMAKE_ARGV5}'") message(STATUS "CMAKE_ARGV6='${CMAKE_ARGV6}'") message(STATUS "CMAKE_ARGV7='${CMAKE_ARGV7}'") message(STATUS "CMAKE_ARGV8='${CMAKE_ARGV8}'") +message(STATUS "CMAKE_ARGV9='${CMAKE_ARGV9}'") diff --git a/Tests/RunCMake/CommandLine/P_args-stdout.txt b/Tests/RunCMake/CommandLine/P_args-stdout.txt new file mode 100644 index 0000000000..f9d039fe8f --- /dev/null +++ b/Tests/RunCMake/CommandLine/P_args-stdout.txt @@ -0,0 +1,6 @@ +^-- CMAKE_ARGC='5' +-- CMAKE_ARGV1='-P' +-- CMAKE_ARGV2='[^']*/Tests/RunCMake/CommandLine/P_args.cmake' +-- CMAKE_ARGV3='relative/path' +-- CMAKE_ARGV4='[^']*/Tests/RunCMake/CommandLine' +-- CMAKE_ARGV5=''$ diff --git a/Tests/RunCMake/CommandLine/P_args.cmake b/Tests/RunCMake/CommandLine/P_args.cmake new file mode 100644 index 0000000000..6c4fa4c704 --- /dev/null +++ b/Tests/RunCMake/CommandLine/P_args.cmake @@ -0,0 +1,6 @@ +message(STATUS "CMAKE_ARGC='${CMAKE_ARGC}'") +message(STATUS "CMAKE_ARGV1='${CMAKE_ARGV1}'") +message(STATUS "CMAKE_ARGV2='${CMAKE_ARGV2}'") +message(STATUS "CMAKE_ARGV3='${CMAKE_ARGV3}'") +message(STATUS "CMAKE_ARGV4='${CMAKE_ARGV4}'") +message(STATUS "CMAKE_ARGV5='${CMAKE_ARGV5}'") diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 80a7c69c76..39d7858c23 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -52,7 +52,8 @@ run_cmake_command(G_no-arg ${CMAKE_COMMAND} -B DummyBuildDir -G) run_cmake_command(G_bad-arg ${CMAKE_COMMAND} -B DummyBuildDir -G NoSuchGenerator) run_cmake_command(P_no-arg ${CMAKE_COMMAND} -P) run_cmake_command(P_no-file ${CMAKE_COMMAND} -P nosuchscriptfile.cmake) -run_cmake_command(P_arbitrary_args ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_arbitrary_args.cmake" -- -DFOO -S -B --fresh) +run_cmake_command(P_args ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_args.cmake" relative/path "${RunCMake_SOURCE_DIR}") +run_cmake_command(P_arbitrary_args ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_arbitrary_args.cmake" -- -DFOO -S -B --fresh --version) run_cmake_command(P_fresh ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_fresh.cmake" --fresh) run_cmake_command(build-no-dir diff --git a/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt index ecfe401b8f..fd026f9297 100644 --- a/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt +++ b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt @@ -1,9 +1,19 @@ ^CMake Error at create-missing-args.cmake:[0-9]+ \(file\): - file Keywords missing values: + Error after keyword "COMPRESSION": + + missing required value + + Error after keyword "COMPRESSION_LEVEL": + + missing required value + + Error after keyword "FORMAT": + + missing required value + + Error after keyword "OUTPUT": + + missing required value - OUTPUT - FORMAT - COMPRESSION - COMPRESSION_LEVEL Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt index 96c779f581..0c93ecef2e 100644 --- a/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt +++ b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt @@ -1,7 +1,11 @@ ^CMake Error at extract-missing-args.cmake:[0-9]+ \(file\): - file Keywords missing values: + Error after keyword "DESTINATION": + + missing required value + + Error after keyword "INPUT": + + missing required value - INPUT - DESTINATION Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt b/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt index dd6a2e7a51..72292f9b50 100644 --- a/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt +++ b/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt @@ -1,6 +1,7 @@ CMake Error at BadArgContent.cmake:[0-9]+ \(file\): - file CONFIGURE keywords missing values: + Error after keyword "CONTENT": + + missing required value - 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 e1bf7b41bc..d793f48308 100644 --- a/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt +++ b/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt @@ -1,6 +1,7 @@ CMake Error at BadArgOutput.cmake:[0-9]+ \(file\): - file CONFIGURE keywords missing values: + Error after keyword "OUTPUT": + + missing required value - OUTPUT Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt b/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt index 4278bceb8e..708e6bec43 100644 --- a/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt +++ b/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt @@ -1,6 +1,7 @@ CMake Error at EmptyCondition1.cmake:2 \(file\): - file GENERATE keywords missing values: + Error after keyword "CONDITION": + + missing required value - CONDITION Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt index 4059bf8277..b1b7f80a1e 100644 --- a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt +++ b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt @@ -1,6 +1,7 @@ CMake Error at NewLineStyle-NoArg.cmake:[0-9]+ \(file\): - file GENERATE keywords missing values: + Error after keyword "NEWLINE_STYLE": + + missing required value - NEWLINE_STYLE Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake new file mode 100644 index 0000000000..a1ae6ac99d --- /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 0000000000..fc44d5ad84 --- /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 738bc6c5d2..c040e8f1ee 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 bcdc10105d..1701a36aa0 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/VerifyHeaderSets/RunCMakeTest.cmake b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake index f022a43c47..9f9a1e70bd 100644 --- a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake @@ -40,6 +40,7 @@ endif() run_cmake_build(VerifyHeaderSets lang_test_c_verify_interface_header_sets) run_cmake_build(VerifyHeaderSets lang_test_cxx_verify_interface_header_sets) +run_cmake_build(VerifyHeaderSets interface_lang_test_cxx_verify_interface_header_sets) run_cmake_build(VerifyHeaderSets list_verify_interface_header_sets) set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON) diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake index 82ed935a62..f260609b0f 100644 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake @@ -1,5 +1,7 @@ enable_language(C CXX) +add_compile_definitions(TEST_ADD_COMPILE_DEFINITIONS) + set_property(SOURCE a.h PROPERTY LANGUAGE C) set_property(SOURCE dir/c.h PROPERTY LANGUAGE C) set_property(SOURCE dir/cxx.h PROPERTY LANGUAGE CXX) @@ -59,6 +61,10 @@ add_library(lang_test_cxx STATIC lib.c lib.cxx) target_compile_definitions(lang_test_cxx INTERFACE EXPECT_CXX) target_sources(lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h) +add_library(interface_lang_test_cxx INTERFACE) +target_compile_definitions(interface_lang_test_cxx INTERFACE EXPECT_CXX) +target_sources(interface_lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h) + set_property(SOURCE error.h PROPERTY LANGUAGE C) add_library(list STATIC lib.c) diff --git a/Tests/RunCMake/VerifyHeaderSets/a.h b/Tests/RunCMake/VerifyHeaderSets/a.h index 8b1718216a..898da4953f 100644 --- a/Tests/RunCMake/VerifyHeaderSets/a.h +++ b/Tests/RunCMake/VerifyHeaderSets/a.h @@ -2,4 +2,8 @@ # error "TEST_A_H defined" #endif +#ifndef TEST_ADD_COMPILE_DEFINITIONS +# error "TEST_ADD_COMPILE_DEFINITIONS not defined" +#endif + extern void a_h(void); diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt index 6a430f1813..ea1566d9ab 100644 --- a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt @@ -1,5 +1,7 @@ CMake Error at Registry_BadQuery2.cmake:[0-9]+ \(cmake_host_system_information\): - cmake_host_system_information missing expected value for argument\(s\) - "VALUE". + Error after keyword "VALUE": + + missing required value + Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt index 5eda4ffb70..f8c96d8ffd 100644 --- a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt @@ -1,5 +1,7 @@ CMake Error at Registry_BadView1.cmake:[0-9]+ \(cmake_host_system_information\): - cmake_host_system_information missing expected value for argument\(s\) - "VIEW". + Error after keyword "VIEW": + + missing required value + Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt b/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt new file mode 100644 index 0000000000..ad7d134f02 --- /dev/null +++ b/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at .+/cmake_path/call-cmake_path.cmake:[0-9]+ \(cmake_path\): + Error after keyword "BASE_DIRECTORY": + + missing required value diff --git a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt new file mode 100644 index 0000000000..f1b52cc4a5 --- /dev/null +++ b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at .+/call-cmake_path.cmake:[0-9]+ \(cmake_path\): + Error after keyword "OUTPUT_VARIABLE": + + empty string not allowed diff --git a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt index e1d6592cf3..63289ef85f 100644 --- a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt +++ b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt @@ -1,2 +1,4 @@ CMake Error at .+/cmake_path/call-cmake_path.cmake:[0-9]+ \(cmake_path\): - cmake_path OUTPUT_VARIABLE requires an argument. + Error after keyword "OUTPUT_VARIABLE": + + missing required value diff --git a/Tests/RunCMake/cmake_path/RunCMakeTest.cmake b/Tests/RunCMake/cmake_path/RunCMakeTest.cmake index 991f46b098..1742b0662b 100644 --- a/Tests/RunCMake/cmake_path/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_path/RunCMakeTest.cmake @@ -74,6 +74,14 @@ foreach (command IN ITEMS APPEND APPEND_STRING REMOVE_FILENAME REPLACE_FILENAME endforeach() +## BASE_DIRECTORY without argument +set (RunCMake-stderr-file "BASE_DIRECTORY-no-arg-stderr.txt") + +foreach (command IN ITEMS RELATIVE_PATH ABSOLUTE_PATH) + run_cmake_command (${command}-OUTPUT_VARIABLE-no-arg "${CMAKE_COMMAND}" "-DCMAKE_PATH_ARGUMENTS=${command} path BASE_DIRECTORY" -P "${RunCMake_SOURCE_DIR}/call-cmake_path.cmake") +endforeach() + + ## Invalid output variable set (RunCMake-stderr-file "invalid-output-var-stderr.txt") @@ -106,6 +114,9 @@ foreach (command IN ITEMS NATIVE_PATH run_cmake_command (${command}-invalid-output "${CMAKE_COMMAND}" "-DCMAKE_PATH_ARGUMENTS=${command} path ${extra_args}" -DCHECK_INVALID_OUTPUT=ON -P "${RunCMake_SOURCE_DIR}/call-cmake_path.cmake") endforeach() +# OUTPUT_VARIABLE empty name +set (RunCMake-stderr-file "OUTPUT_VARIABLE-empty-stderr.txt") + foreach (command IN ITEMS APPEND APPEND_STRING REMOVE_FILENAME REPLACE_FILENAME REMOVE_EXTENSION REPLACE_EXTENSION NORMAL_PATH RELATIVE_PATH ABSOLUTE_PATH) diff --git a/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt b/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt index e27f1e6d25..62cad521fc 100644 --- a/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt +++ b/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt @@ -1,2 +1,4 @@ CMake Error at .*EchoCommand.cmake:.*\(execute_process\): - execute_process called with no value for COMMAND_ECHO. + Error after keyword "COMMAND_ECHO": + + missing required value diff --git a/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt b/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt index 1a695790fc..7f85654c6a 100644 --- a/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt +++ b/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt @@ -1,4 +1,7 @@ ^CMake Error at EncodingMissing.cmake:[0-9]+ \(execute_process\): - execute_process called with no value for ENCODING. + Error after keyword "ENCODING": + + missing required value + Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake index 0e6020f0f4..ee00b273cf 100644 --- a/Tests/RunCMake/export/RunCMakeTest.cmake +++ b/Tests/RunCMake/export/RunCMakeTest.cmake @@ -18,3 +18,4 @@ run_cmake(DependOnDoubleExport) run_cmake(UnknownExport) run_cmake(NamelinkOnlyExport) run_cmake(SeparateNamelinkExport) +run_cmake(TryCompileExport) diff --git a/Tests/RunCMake/export/TryCompileExport.cmake b/Tests/RunCMake/export/TryCompileExport.cmake new file mode 100644 index 0000000000..5ad7c6ee0b --- /dev/null +++ b/Tests/RunCMake/export/TryCompileExport.cmake @@ -0,0 +1,9 @@ +enable_language(CXX) + +add_library(interface INTERFACE) +install(TARGETS interface EXPORT export) +export(EXPORT export) + +add_library(imported IMPORTED INTERFACE) + +try_compile(tc "${CMAKE_CURRENT_BINARY_DIR}/tc" "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" LINK_LIBRARIES imported) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt deleted file mode 100644 index b22387bbd1..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt +++ /dev/null @@ -1,5 +0,0 @@ -CMake Error at CHMOD-all-perms\.cmake:[0-9]+ \(file\): - file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS - from the invocation -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake deleted file mode 100644 index b49583d5c3..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake +++ /dev/null @@ -1,6 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ - FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt deleted file mode 100644 index 8d09e35104..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at CHMOD-invalid-path\.cmake:[0-9]+ \(file\): - file does not exist: - - .*/chmod-tests/I_dont_exist -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake deleted file mode 100644 index 36915c15bd..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake +++ /dev/null @@ -1,4 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/I_dont_exist PERMISSIONS OWNER_READ) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt deleted file mode 100644 index 84ba2a299f..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt +++ /dev/null @@ -1,4 +0,0 @@ -CMake Error at CHMOD-invalid-perms\.cmake:[0-9]+ \(file\): - file INVALID_PERMISSION is an invalid permission specifier -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake deleted file mode 100644 index 22cab0b14b..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake +++ /dev/null @@ -1,5 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS INVALID_PERMISSION) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt deleted file mode 100644 index 2c248f8bf2..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt +++ /dev/null @@ -1,4 +0,0 @@ -CMake Error at CHMOD-no-keyword\.cmake:[0-9]+ \(file\): - file No permissions given -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake deleted file mode 100644 index 8b62106513..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake +++ /dev/null @@ -1,5 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt deleted file mode 100644 index a18609f2c9..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt +++ /dev/null @@ -1,4 +0,0 @@ -CMake Error at CHMOD-no-perms\.cmake:[0-9]+ \(file\): - file No permissions given -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake deleted file mode 100644 index 9fbd3599df..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake +++ /dev/null @@ -1,5 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake deleted file mode 100644 index 87e3e5766f..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake +++ /dev/null @@ -1,5 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake deleted file mode 100644 index d9226b835f..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake +++ /dev/null @@ -1,6 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a) -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ - FILE_PERMISSIONS OWNER_READ OWNER_WRITE) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt deleted file mode 100644 index 1c87a593f6..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at CHMOD-write-only\.cmake:[0-9]+ \(file\): - file failed to open for reading \(Permission denied\): - - .*/chmod-tests/a -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake deleted file mode 100644 index 1289efc103..0000000000 --- a/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake +++ /dev/null @@ -1,6 +0,0 @@ -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests) - -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a "CONTENT") -file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_WRITE) -file(READ ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a content) diff --git a/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake b/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake index 18deb89517..e6b11694de 100644 --- a/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake @@ -1,12 +1,14 @@ include(RunCMake) -run_cmake(CHMOD-no-perms) -run_cmake(CHMOD-no-keyword) -run_cmake(CHMOD-all-perms) -run_cmake(CHMOD-invalid-perms) -run_cmake(CHMOD-invalid-path) -run_cmake(CHMOD-ok) -run_cmake(CHMOD-override) +run_cmake_script(no-perms) +run_cmake_script(missing-perms) +run_cmake_script(missing-file-perms) +run_cmake_script(missing-dir-perms) +run_cmake_script(all-perms) +run_cmake_script(invalid-perms) +run_cmake_script(invalid-path) +run_cmake_script(ok) +run_cmake_script(override) if(UNIX) execute_process(COMMAND id -u $ENV{USER} @@ -15,5 +17,5 @@ if(UNIX) endif() if(NOT WIN32 AND NOT MSYS AND NOT "${uid}" STREQUAL "0") - run_cmake(CHMOD-write-only) + run_cmake_script(write-only) endif() diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt b/Tests/RunCMake/file-CHMOD/all-perms-result.txt index d00491fd7e..d00491fd7e 100644 --- a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt +++ b/Tests/RunCMake/file-CHMOD/all-perms-result.txt diff --git a/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt new file mode 100644 index 0000000000..6932a1f0b8 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at [^ +]*/all-perms\.cmake:[0-9]+ \(file\): + file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS + from the invocation$ diff --git a/Tests/RunCMake/file-CHMOD/all-perms.cmake b/Tests/RunCMake/file-CHMOD/all-perms.cmake new file mode 100644 index 0000000000..5ff81b85fd --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/all-perms.cmake @@ -0,0 +1,3 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ + FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt b/Tests/RunCMake/file-CHMOD/invalid-path-result.txt index d00491fd7e..d00491fd7e 100644 --- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt +++ b/Tests/RunCMake/file-CHMOD/invalid-path-result.txt diff --git a/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt b/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt new file mode 100644 index 0000000000..eb5fb31638 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at [^ +]*/invalid-path\.cmake:[0-9]+ \(file\): + file does not exist: + + [^ +]*/Tests/RunCMake/file-CHMOD/invalid-path-build/I_dont_exist$ diff --git a/Tests/RunCMake/file-CHMOD/invalid-path.cmake b/Tests/RunCMake/file-CHMOD/invalid-path.cmake new file mode 100644 index 0000000000..e8b0313c4a --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/invalid-path.cmake @@ -0,0 +1 @@ +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/I_dont_exist PERMISSIONS OWNER_READ) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt b/Tests/RunCMake/file-CHMOD/invalid-perms-result.txt index d00491fd7e..d00491fd7e 100644 --- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt +++ b/Tests/RunCMake/file-CHMOD/invalid-perms-result.txt diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt new file mode 100644 index 0000000000..daab22e4eb --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt @@ -0,0 +1,3 @@ +^CMake Error at [^ +]*/invalid-perms\.cmake:[0-9]+ \(file\): + file INVALID_PERMISSION is an invalid permission specifier$ diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms.cmake b/Tests/RunCMake/file-CHMOD/invalid-perms.cmake new file mode 100644 index 0000000000..42129b9dfd --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/invalid-perms.cmake @@ -0,0 +1,2 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS INVALID_PERMISSION) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt b/Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt index d00491fd7e..d00491fd7e 100644 --- a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt +++ b/Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt new file mode 100644 index 0000000000..c05bb2dd77 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at [^ +]*/missing-dir-perms.cmake:[0-9]+ \(file\): + Error after keyword "DIRECTORY_PERMISSIONS": + + missing required value$ diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake new file mode 100644 index 0000000000..9d0fdade8b --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake @@ -0,0 +1,2 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt b/Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt index d00491fd7e..d00491fd7e 100644 --- a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt +++ b/Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt new file mode 100644 index 0000000000..c3f4f0f17f --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at [^ +]*/missing-file-perms.cmake:[0-9]+ \(file\): + Error after keyword "FILE_PERMISSIONS": + + missing required value$ diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake new file mode 100644 index 0000000000..bcf35aa194 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake @@ -0,0 +1,2 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ FILE_PERMISSIONS) diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt b/Tests/RunCMake/file-CHMOD/missing-perms-result.txt index d00491fd7e..d00491fd7e 100644 --- a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt +++ b/Tests/RunCMake/file-CHMOD/missing-perms-result.txt diff --git a/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt new file mode 100644 index 0000000000..1508b130cc --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at [^ +]*/missing-perms.cmake:[0-9]+ \(file\): + Error after keyword "PERMISSIONS": + + missing required value$ diff --git a/Tests/RunCMake/file-CHMOD/missing-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-perms.cmake new file mode 100644 index 0000000000..da9f182f68 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/missing-perms.cmake @@ -0,0 +1,2 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a FILE_PERMISSIONS OWNER_READ PERMISSIONS) diff --git a/Tests/RunCMake/file-CHMOD/no-perms-result.txt b/Tests/RunCMake/file-CHMOD/no-perms-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/no-perms-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt new file mode 100644 index 0000000000..4c5a13983e --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt @@ -0,0 +1,3 @@ +^CMake Error at [^ +]*/no-perms\.cmake:[0-9]+ \(file\): + file No permissions given$ diff --git a/Tests/RunCMake/file-CHMOD/no-perms.cmake b/Tests/RunCMake/file-CHMOD/no-perms.cmake new file mode 100644 index 0000000000..602cfc20f8 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/no-perms.cmake @@ -0,0 +1,2 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a) diff --git a/Tests/RunCMake/file-CHMOD/ok.cmake b/Tests/RunCMake/file-CHMOD/ok.cmake new file mode 100644 index 0000000000..7c74d27761 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/ok.cmake @@ -0,0 +1,2 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ) diff --git a/Tests/RunCMake/file-CHMOD/override.cmake b/Tests/RunCMake/file-CHMOD/override.cmake new file mode 100644 index 0000000000..67e5a23cae --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/override.cmake @@ -0,0 +1,3 @@ +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a) +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ + FILE_PERMISSIONS OWNER_READ OWNER_WRITE) diff --git a/Tests/RunCMake/file-CHMOD/write-only-result.txt b/Tests/RunCMake/file-CHMOD/write-only-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/write-only-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file-CHMOD/write-only-stderr.txt b/Tests/RunCMake/file-CHMOD/write-only-stderr.txt new file mode 100644 index 0000000000..169a09266c --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/write-only-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at [^ +]*/write-only\.cmake:[0-9]+ \(file\): + file failed to open for reading \(Permission denied\): + + [^ +]*/Tests/RunCMake/file-CHMOD/write-only-build/a$ diff --git a/Tests/RunCMake/file-CHMOD/write-only.cmake b/Tests/RunCMake/file-CHMOD/write-only.cmake new file mode 100644 index 0000000000..aa9d8038b9 --- /dev/null +++ b/Tests/RunCMake/file-CHMOD/write-only.cmake @@ -0,0 +1,3 @@ +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/a "CONTENT") +file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_WRITE) +file(READ ${CMAKE_CURRENT_BINARY_DIR}/a content) diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt index c6ad3d090d..39f307d2e7 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt @@ -13,11 +13,21 @@ Call Stack \(most recent call first\): This warning is for project developers\. Use -Wno-dev to suppress it\. CMake Error at badargs2\.cmake:[0-9]+ \(file\): - file Keywords missing values: + Error after keyword "BUNDLE_EXECUTABLE": + + missing required value + + Error after keyword "CONFLICTING_DEPENDENCIES_PREFIX": + + missing required value + + Error after keyword "RESOLVED_DEPENDENCIES_VAR": + + missing required value + + Error after keyword "UNRESOLVED_DEPENDENCIES_VAR": + + missing required value - RESOLVED_DEPENDENCIES_VAR - UNRESOLVED_DEPENDENCIES_VAR - CONFLICTING_DEPENDENCIES_PREFIX - BUNDLE_EXECUTABLE Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt b/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt index 7c58aeb4aa..54a3e5a729 100644 --- a/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt +++ b/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt @@ -1,2 +1,7 @@ -CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\): - file BASE_DIRECTORY requires a value +^CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\): + Error after keyword "BASE_DIRECTORY": + + missing required value + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index 32e54d5027..fa41fc1807 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake @@ -55,6 +55,22 @@ run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) run_cmake(REGISTRY_VIEW-propagated) +file( + GLOB SearchPaths_TEST_CASE_LIST + LIST_DIRECTORIES TRUE + "${RunCMake_SOURCE_DIR}/SearchPaths/*" + ) +foreach(TestCasePrefix IN LISTS SearchPaths_TEST_CASE_LIST) + if(IS_DIRECTORY "${TestCasePrefix}") + cmake_path(GET TestCasePrefix FILENAME TestSuffix) + run_cmake_with_options( + SearchPaths_${TestSuffix} + "-DSearchPaths_ROOT=${TestCasePrefix}" + "--debug-find-pkg=SearchPaths" + ) + endif() +endforeach() + if(UNIX AND NOT MSYS # FIXME: This works on CYGWIN but not on MSYS ) diff --git a/Tests/RunCMake/find_package/SearchPaths.cmake b/Tests/RunCMake/find_package/SearchPaths.cmake new file mode 100644 index 0000000000..a5a10fcedb --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0074 NEW) +find_package(SearchPaths REQUIRED CONFIG) diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt new file mode 100644 index 0000000000..fae13ef3e4 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt @@ -0,0 +1,10 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt new file mode 100644 index 0000000000..eba88c94ac --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt new file mode 100644 index 0000000000..cebc169a27 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt new file mode 100644 index 0000000000..4e5d3f662b --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt new file mode 100644 index 0000000000..f29054582b --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt new file mode 100644 index 0000000000..32b9fcb7fb --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt new file mode 100644 index 0000000000..7bf5cf8b6d --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt new file mode 100644 index 0000000000..3592f72e85 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt new file mode 100644 index 0000000000..b196b7a7bc --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt new file mode 100644 index 0000000000..17e039961d --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt new file mode 100644 index 0000000000..ee01a50c54 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt new file mode 100644 index 0000000000..d6abd2ff64 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt new file mode 100644 index 0000000000..b578b2bab3 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt @@ -0,0 +1,14 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt new file mode 100644 index 0000000000..2f3f18aa2a --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt new file mode 100644 index 0000000000..3eef00243c --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt new file mode 100644 index 0000000000..c962f8bce7 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt @@ -0,0 +1,12 @@ + find_package considered the following locations for SearchPaths's Config + module: + + .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/SearchPathsConfig\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/searchpaths-config\.cmake + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake + + The file was found at + + .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake new file mode 100644 index 0000000000..d831313952 --- /dev/null +++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake") diff --git a/Tests/RunCMake/include_external_msproject/Program.cs b/Tests/RunCMake/include_external_msproject/Program.cs new file mode 100644 index 0000000000..5ed58c866f --- /dev/null +++ b/Tests/RunCMake/include_external_msproject/Program.cs @@ -0,0 +1,9 @@ +namespace ConsoleApp +{ + internal class Program + { + static void Main(string[] args) + { + } + } +} diff --git a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake index cb0eb18799..4fbf147ef2 100644 --- a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake +++ b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake @@ -10,3 +10,14 @@ if(RunCMake_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])") run_cmake(SkipGetTargetFrameworkProperties) run_cmake(VSCSharpReference) endif() + +if(RunCMake_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])") + function(run_VSCSharpOnlyProject) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VSCSharpOnlyProject-build) + run_cmake(VSCSharpOnlyProject) + set(RunCMake_TEST_NO_CLEAN 1) + set(build_flags /restore) + run_cmake_command(VSCSharpOnlyProject-build ${CMAKE_COMMAND} --build . -- ${build_flags}) + endfunction() + run_VSCSharpOnlyProject() +endif() diff --git a/Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake b/Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake new file mode 100644 index 0000000000..e7e0b99abf --- /dev/null +++ b/Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake @@ -0,0 +1,9 @@ +project(VSCSharpOnlyProject) + +file(COPY + ${CMAKE_CURRENT_SOURCE_DIR}/Program.cs + ${CMAKE_CURRENT_SOURCE_DIR}/consoleapp.csproj + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +include_external_msproject( + test "${CMAKE_CURRENT_BINARY_DIR}/consoleapp.csproj") diff --git a/Tests/RunCMake/include_external_msproject/consoleapp.csproj b/Tests/RunCMake/include_external_msproject/consoleapp.csproj new file mode 100644 index 0000000000..2894848f91 --- /dev/null +++ b/Tests/RunCMake/include_external_msproject/consoleapp.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net472</TargetFramework> + <RootNamespace>ConsoleApp</RootNamespace> + <AssemblyName>ConsoleApp</AssemblyName> + <PlatformTarget>x64</PlatformTarget> + <EnableDefaultItems>false</EnableDefaultItems> + </PropertyGroup> + + <ItemGroup> + <Compile Include="Program.cs" /> + </ItemGroup> +</Project> diff --git a/Tests/RunCMake/try_compile/BinDirEmpty-result.txt b/Tests/RunCMake/try_compile/BinDirEmpty-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_compile/BinDirEmpty-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt new file mode 100644 index 0000000000..b1f5ae3b48 --- /dev/null +++ b/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at BinDirEmpty.cmake:[0-9]+ \(try_compile\): + No <bindir> specified. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/try_compile/BinDirEmpty.cmake b/Tests/RunCMake/try_compile/BinDirEmpty.cmake new file mode 100644 index 0000000000..7bea43d853 --- /dev/null +++ b/Tests/RunCMake/try_compile/BinDirEmpty.cmake @@ -0,0 +1 @@ +try_compile(resultVar "" ${CMAKE_CURRENT_SOURCE_DIR}/src.c) diff --git a/Tests/RunCMake/try_compile/BinDirRelative-result.txt b/Tests/RunCMake/try_compile/BinDirRelative-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_compile/BinDirRelative-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt b/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt new file mode 100644 index 0000000000..a7b6302d0c --- /dev/null +++ b/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at BinDirRelative.cmake:[0-9]+ \(try_compile\): + <bindir> is not an absolute path: + + 'bin_dir_relative' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/try_compile/BinDirRelative.cmake b/Tests/RunCMake/try_compile/BinDirRelative.cmake new file mode 100644 index 0000000000..8deda11eec --- /dev/null +++ b/Tests/RunCMake/try_compile/BinDirRelative.cmake @@ -0,0 +1 @@ +try_compile(resultVar bin_dir_relative ${CMAKE_CURRENT_SOURCE_DIR}/src.c) diff --git a/Tests/RunCMake/try_compile/EmptyListArgs.cmake b/Tests/RunCMake/try_compile/EmptyListArgs.cmake new file mode 100644 index 0000000000..eed7ee4b6e --- /dev/null +++ b/Tests/RunCMake/try_compile/EmptyListArgs.cmake @@ -0,0 +1,8 @@ +enable_language(C) + +try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c + CMAKE_FLAGS # no values + COMPILE_DEFINITIONS # no values + LINK_LIBRARIES # no values + LINK_OPTIONS # no values + ) diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt b/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt b/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt new file mode 100644 index 0000000000..b1344bd43d --- /dev/null +++ b/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at EmptyValueArgs.cmake:[0-9]+ \(try_compile\): + COPY_FILE must be followed by a file path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) ++ +CMake Error at EmptyValueArgs.cmake:[0-9]+ \(try_compile\): + COPY_FILE_ERROR must be followed by a variable name +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs.cmake b/Tests/RunCMake/try_compile/EmptyValueArgs.cmake new file mode 100644 index 0000000000..f564abcc6c --- /dev/null +++ b/Tests/RunCMake/try_compile/EmptyValueArgs.cmake @@ -0,0 +1,4 @@ +try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c + COPY_FILE "") +try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c + COPY_FILE "x" COPY_FILE_ERROR "") diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake index eca7bf485d..d377cce24b 100644 --- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake @@ -15,6 +15,10 @@ run_cmake(BadSources1) run_cmake(BadSources2) run_cmake(NonSourceCopyFile) run_cmake(NonSourceCompileDefinitions) +run_cmake(BinDirEmpty) +run_cmake(BinDirRelative) +run_cmake(EmptyValueArgs) +run_cmake(EmptyListArgs) run_cmake(EnvConfig) diff --git a/Tests/RunCMake/try_run/BadStdErrVariable-result.txt b/Tests/RunCMake/try_run/BadStdErrVariable-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_run/BadStdErrVariable-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt b/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt new file mode 100644 index 0000000000..5d7e5e9127 --- /dev/null +++ b/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt @@ -0,0 +1,5 @@ +CMake Error: RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable +CMake Error at BadStdErrVariable.cmake:1 \(try_run\): + try_run unknown error. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/try_run/BadStdErrVariable.cmake b/Tests/RunCMake/try_run/BadStdErrVariable.cmake new file mode 100644 index 0000000000..88c2a72641 --- /dev/null +++ b/Tests/RunCMake/try_run/BadStdErrVariable.cmake @@ -0,0 +1,5 @@ +try_run(RUN_RESULT COMPILE_RESULT + ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir + RUN_OUTPUT_STDERR_VARIABLE + ) diff --git a/Tests/RunCMake/try_run/BadStdOutVariable-result.txt b/Tests/RunCMake/try_run/BadStdOutVariable-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_run/BadStdOutVariable-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt b/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt new file mode 100644 index 0000000000..df606582a9 --- /dev/null +++ b/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt @@ -0,0 +1,5 @@ +CMake Error: RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable +CMake Error at BadStdOutVariable.cmake:1 \(try_run\): + try_run unknown error. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/try_run/BadStdOutVariable.cmake b/Tests/RunCMake/try_run/BadStdOutVariable.cmake new file mode 100644 index 0000000000..691e8813e5 --- /dev/null +++ b/Tests/RunCMake/try_run/BadStdOutVariable.cmake @@ -0,0 +1,5 @@ +try_run(RUN_RESULT COMPILE_RESULT + ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir + RUN_OUTPUT_STDOUT_VARIABLE + ) diff --git a/Tests/RunCMake/try_run/BinDirEmpty-result.txt b/Tests/RunCMake/try_run/BinDirEmpty-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_run/BinDirEmpty-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt b/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt new file mode 100644 index 0000000000..def1c22ea9 --- /dev/null +++ b/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at BinDirEmpty.cmake:[0-9]+ \(try_run\): + No <bindir> specified. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/try_run/BinDirEmpty.cmake b/Tests/RunCMake/try_run/BinDirEmpty.cmake new file mode 100644 index 0000000000..d4b7ee3921 --- /dev/null +++ b/Tests/RunCMake/try_run/BinDirEmpty.cmake @@ -0,0 +1 @@ +try_run(runResultVar compileResultVar "" ${CMAKE_CURRENT_SOURCE_DIR}/src.c) diff --git a/Tests/RunCMake/try_run/BinDirRelative-result.txt b/Tests/RunCMake/try_run/BinDirRelative-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/try_run/BinDirRelative-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_run/BinDirRelative-stderr.txt b/Tests/RunCMake/try_run/BinDirRelative-stderr.txt new file mode 100644 index 0000000000..54d4e8666b --- /dev/null +++ b/Tests/RunCMake/try_run/BinDirRelative-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at BinDirRelative.cmake:[0-9]+ \(try_run\): + <bindir> is not an absolute path: + + 'bin_dir_relative' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/try_run/BinDirRelative.cmake b/Tests/RunCMake/try_run/BinDirRelative.cmake new file mode 100644 index 0000000000..a277403ecc --- /dev/null +++ b/Tests/RunCMake/try_run/BinDirRelative.cmake @@ -0,0 +1 @@ +try_run(runResultVar compileResultVar bin_dir_relative ${CMAKE_CURRENT_SOURCE_DIR}/src.c) diff --git a/Tests/RunCMake/try_run/RunCMakeTest.cmake b/Tests/RunCMake/try_run/RunCMakeTest.cmake index d74add05ca..76c85dd7b2 100644 --- a/Tests/RunCMake/try_run/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_run/RunCMakeTest.cmake @@ -1,6 +1,8 @@ include(RunCMake) run_cmake(BadLinkLibraries) +run_cmake(BinDirEmpty) +run_cmake(BinDirRelative) if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND CMAKE_C_COMPILER_ID MATCHES "^(MSVC|GNU|LCC|Clang|AppleClang)$") @@ -10,3 +12,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND endif() run_cmake(WorkingDirArg) + +run_cmake(BadStdOutVariable) +run_cmake(BadStdErrVariable) diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index 000fd2cbc9..7c6f970e52 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -259,11 +259,32 @@ endif() if("${COMPILE_OUTPUT}" MATCHES "hello world") message(SEND_ERROR " COMPILE_OUT contains the run output: \"${COMPILE_OUTPUT}\"") endif() -# check the run output, it should stdout +# check the run output, it should contain stdout if(NOT "${RUN_OUTPUT}" MATCHES "hello world") message(SEND_ERROR " RUN_OUTPUT didn't contain \"hello world\": \"${RUN_OUTPUT}\"") endif() +# try to run a file and parse stdout and stderr separately +try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE + ${TryCompile_BINARY_DIR} + ${TryCompile_SOURCE_DIR}/stdout_and_stderr.c + COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT + RUN_OUTPUT_STDOUT_VARIABLE RUN_OUTPUT_STDOUT + RUN_OUTPUT_STDERR_VARIABLE RUN_OUTPUT_STDERR) + +if(NOT SHOULD_COMPILE) + message(STATUS " exit_with_error failed compiling: ${COMPILE_OUTPUT}") +endif() + +# check the run stdout output +if(NOT "${RUN_OUTPUT_STDOUT}" MATCHES "hello world") + message(SEND_ERROR " RUN_OUTPUT_STDOUT didn't contain \"hello world\": \"${RUN_OUTPUT_STDOUT}\"") +endif() +# check the run stderr output +if(NOT "${RUN_OUTPUT_STDERR}" MATCHES "error") + message(SEND_ERROR " RUN_OUTPUT_STDERR didn't contain \"error\": \"${RUN_OUTPUT_STDERR}\"") +endif() + ####################################################################### # # also test that the CHECK_C_SOURCE_COMPILES, CHECK_CXX_SOURCE_COMPILES diff --git a/Tests/TryCompile/stdout_and_stderr.c b/Tests/TryCompile/stdout_and_stderr.c new file mode 100644 index 0000000000..84ded1f799 --- /dev/null +++ b/Tests/TryCompile/stdout_and_stderr.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int main() +{ + fputs("error\n", stderr); + puts("hello world\n"); + return 0; +} diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index b80fc22885..b0ed911bb0 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp @@ -91,7 +91,7 @@ { symbol: [ "std::__decay_and_strip<cmFindPackageCommand::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] }, { symbol: [ "std::__decay_and_strip<cmGlobalNinjaGenerator::TargetAlias &>::__type", private, "\"cmConfigure.h\"", public ] }, { symbol: [ "std::__decay_and_strip<__gnu_cxx::__normal_iterator<const cmCTestTestHandler::cmCTestTestProperties *, std::vector<cmCTestTestHandler::cmCTestTestProperties, std::allocator<cmCTestTestHandler::cmCTestTestProperties> > > &>::__type", private, "\"cmConfigure.h\"", public ] }, - { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] }, + { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] }, { symbol: [ "std::__success_type<std::chrono::duration<double, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] }, { symbol: [ "std::__success_type<std::chrono::duration<long, std::ratio<1, 1000000000> > >::type", private, "\"cmConfigure.h\"", public ] }, { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] }, diff --git a/Utilities/Release/WiX/CustomAction/CMakeLists.txt b/Utilities/Release/WiX/CustomAction/CMakeLists.txt index 9d89dd89aa..dd07f9f24c 100644 --- a/Utilities/Release/WiX/CustomAction/CMakeLists.txt +++ b/Utilities/Release/WiX/CustomAction/CMakeLists.txt @@ -7,6 +7,10 @@ if(MSVC) "CMAKE_CXX_FLAGS_${CONFIG}" "${CMAKE_CXX_FLAGS_${CONFIG}}" ) + string(REPLACE "-MD" "-MT" + "CMAKE_CXX_FLAGS_${CONFIG}" + "${CMAKE_CXX_FLAGS_${CONFIG}}" + ) endforeach() endif() endif() diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index f842270bc5..b8cf412ebf 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -1465,6 +1465,8 @@ if(MSVC) add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) if(CMAKE_C_FLAGS MATCHES "/W[0-4]") string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + elseif(CMAKE_C_FLAGS MATCHES "-W[0-4]") + string(REGEX REPLACE "-W[0-4]" "-W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") endif() |