diff options
83 files changed, 4285 insertions, 1085 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2c81b37b46..b26172d360 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1051,7 +1051,7 @@ t:windows-vs2019-x64: t:windows-vs2022-x64-nmake: extends: - .windows_vs2022_x64_nmake - - .cmake_test_windows_nmake + - .cmake_test_windows_external - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent @@ -1065,7 +1065,7 @@ t:windows-vs2022-x64-nmake: t:windows-vs2022-x64-jom: extends: - .windows_vs2022_x64_jom - - .cmake_test_windows_jom + - .cmake_test_windows_external - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent @@ -1079,7 +1079,7 @@ t:windows-vs2022-x64-jom: t:windows-borland5.5: extends: - .windows_borland5.5 - - .cmake_test_windows_borland + - .cmake_test_windows_external - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent @@ -1093,7 +1093,7 @@ t:windows-borland5.5: t:windows-borland5.8: extends: - .windows_borland5.8 - - .cmake_test_windows_borland + - .cmake_test_windows_external - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent @@ -1195,7 +1195,7 @@ t:mingw_osdn_io-msys_makefiles: t:windows-msvc-v71-nmake: extends: - .windows_msvc_v71_nmake - - .cmake_test_windows_msvc + - .cmake_test_windows_external - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent @@ -1209,7 +1209,7 @@ t:windows-msvc-v71-nmake: t:windows-openwatcom1.9: extends: - .windows_openwatcom1.9 - - .cmake_test_windows_openwatcom + - .cmake_test_windows_external - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent diff --git a/.gitlab/ci/borland-env.ps1 b/.gitlab/ci/borland-env.ps1 new file mode 100755 index 0000000000..b3b532e37c --- /dev/null +++ b/.gitlab/ci/borland-env.ps1 @@ -0,0 +1,3 @@ +Invoke-Expression -Command .gitlab/ci/borland.ps1 +$pwdpath = $pwd.Path +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\bcc\bin;$env:PATH" diff --git a/.gitlab/ci/download_python3.cmake b/.gitlab/ci/download_python3.cmake deleted file mode 100644 index 0f5b18b4ff..0000000000 --- a/.gitlab/ci/download_python3.cmake +++ /dev/null @@ -1,41 +0,0 @@ -cmake_minimum_required(VERSION 3.17) - -set(version "3.8.6") -set(sha256sum "376e18eef7e3ea467f0e3af041b01fc7e2f12855506c2ab2653ceb5e0951212e") -set(dirname "python-${version}-embed-win-x86_64") -set(tarball "${dirname}.tar.xz") - -# Download the file. -file(DOWNLOAD - "https://cmake.org/files/dependencies/${tarball}" - ".gitlab/${tarball}" - STATUS download_status - EXPECTED_HASH "SHA256=${sha256sum}") - -# Check the download status. -list(GET download_status 0 res) -if (res) - list(GET download_status 1 err) - message(FATAL_ERROR - "Failed to download ${tarball}: ${err}") -endif () - -# Extract the file. -execute_process( - COMMAND - "${CMAKE_COMMAND}" - -E tar - xzf "${tarball}" - WORKING_DIRECTORY ".gitlab" - RESULT_VARIABLE res - ERROR_VARIABLE err - ERROR_STRIP_TRAILING_WHITESPACE) -if (res) - message(FATAL_ERROR - "Failed to extract ${tarball}: ${err}") -endif () - -# Move to a predictable directory. -file(RENAME - ".gitlab/${dirname}" - ".gitlab/python3") diff --git a/.gitlab/ci/env_windows_borland5.5.ps1 b/.gitlab/ci/env_windows_borland5.5.ps1 new file mode 100755 index 0000000000..0d2e46b67f --- /dev/null +++ b/.gitlab/ci/env_windows_borland5.5.ps1 @@ -0,0 +1 @@ +. .gitlab/ci/borland-env.ps1 diff --git a/.gitlab/ci/env_windows_borland5.8.ps1 b/.gitlab/ci/env_windows_borland5.8.ps1 new file mode 100755 index 0000000000..0d2e46b67f --- /dev/null +++ b/.gitlab/ci/env_windows_borland5.8.ps1 @@ -0,0 +1 @@ +. .gitlab/ci/borland-env.ps1 diff --git a/.gitlab/ci/env_windows_msvc_v71_nmake.ps1 b/.gitlab/ci/env_windows_msvc_v71_nmake.ps1 new file mode 100755 index 0000000000..cb3806d1ca --- /dev/null +++ b/.gitlab/ci/env_windows_msvc_v71_nmake.ps1 @@ -0,0 +1,2 @@ +Invoke-Expression -Command .gitlab/ci/msvc.ps1 +Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 diff --git a/.gitlab/ci/env_windows_openwatcom1.9.ps1 b/.gitlab/ci/env_windows_openwatcom1.9.ps1 new file mode 100755 index 0000000000..49c28f7a20 --- /dev/null +++ b/.gitlab/ci/env_windows_openwatcom1.9.ps1 @@ -0,0 +1 @@ +. .gitlab/ci/openwatcom-env.ps1 diff --git a/.gitlab/ci/env_windows_vs2022_x64_jom.ps1 b/.gitlab/ci/env_windows_vs2022_x64_jom.ps1 new file mode 100755 index 0000000000..c933421d3a --- /dev/null +++ b/.gitlab/ci/env_windows_vs2022_x64_jom.ps1 @@ -0,0 +1,4 @@ +Invoke-Expression -Command .gitlab/ci/jom.ps1 +$pwdpath = $pwd.Path +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\jom;$env:PATH" +Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 diff --git a/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1 b/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1 new file mode 100755 index 0000000000..62463cd98c --- /dev/null +++ b/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1 @@ -0,0 +1 @@ +Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 diff --git a/.gitlab/ci/openwatcom-env.ps1 b/.gitlab/ci/openwatcom-env.ps1 new file mode 100755 index 0000000000..14ea523028 --- /dev/null +++ b/.gitlab/ci/openwatcom-env.ps1 @@ -0,0 +1,7 @@ +Invoke-Expression -Command .gitlab/ci/openwatcom.ps1 +$pwdpath = $pwd.Path +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\watcom\binnt;$pwdpath\.gitlab\watcom\binw;$env:PATH" +Set-Item -Force -Path "env:INCLUDE" -Value "$pwdpath\.gitlab\watcom\h;$pwdpath\.gitlab\watcom\h\nt" +Set-Item -Force -Path "env:EDPATH" -Value "$pwdpath\.gitlab\watcom\eddat" +Set-Item -Force -Path "env:WATCOM" -Value "$pwdpath\.gitlab\watcom" +Set-Item -Force -Path "env:WLINKTMP" -Value "." diff --git a/.gitlab/ci/python-env.ps1 b/.gitlab/ci/python-env.ps1 index 4e897d8dc6..ce16493e61 100755 --- a/.gitlab/ci/python-env.ps1 +++ b/.gitlab/ci/python-env.ps1 @@ -1,4 +1,4 @@ $pwdpath = $pwd.Path -cmake -P .gitlab/ci/download_python3.cmake +& "$pwsh" -File ".gitlab/ci/python.ps1" Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\python3;$env:PATH" python --version diff --git a/.gitlab/ci/python.ps1 b/.gitlab/ci/python.ps1 new file mode 100755 index 0000000000..27f18071a6 --- /dev/null +++ b/.gitlab/ci/python.ps1 @@ -0,0 +1,30 @@ +$erroractionpreference = "stop" + +$version = "3.11.3" + +if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { + $sha256sum = "7419B2E98516FBD0B66A1237B80187FFB21D32E47B4A4235C2D9D6379597070F" + $arch = "amd64" +} elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { + $sha256sum = "03BAD6A7C898FC8F693982437AAB6DB698107B82EA93F76424195AE2C161246C" + $arch = "arm64" +} else { + throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE") +} + +$filename = "python-$version-embed-$arch" +$tarball = "$filename.zip" + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +$ProgressPreference = 'SilentlyContinue' +Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/internal/$tarball" -OutFile "$outdir\$tarball" +$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir\python3") +Remove-Item "$outdir\python3\*._pth" # Avoid sys.path specific to embedded python. +Remove-Item "$outdir\$tarball" diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index ded3e659ff..f1e86ad3e5 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -349,6 +349,10 @@ - . .gitlab/ci/qt-env.ps1 - . .gitlab/ci/python-env.ps1 +.before_script_windows_external: &before_script_windows_external + - . .gitlab/ci/env.ps1 + - . .gitlab/ci/python-env.ps1 + .cmake_build_windows: stage: build @@ -381,69 +385,7 @@ stage: test-ext script: - - . .gitlab/ci/env.ps1 - - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake - - interruptible: true - -.cmake_test_windows_nmake: - stage: test-ext - - script: - - . .gitlab/ci/env.ps1 - - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 - - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake - - interruptible: true - -.cmake_test_windows_jom: - stage: test-ext - - script: - - . .gitlab/ci/env.ps1 - - Invoke-Expression -Command .gitlab/ci/jom.ps1 - - $pwdpath = $pwd.Path - - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\jom;$env:PATH" - - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 - - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake - - interruptible: true - -.cmake_test_windows_borland: - stage: test-ext - - script: - - . .gitlab/ci/env.ps1 - - Invoke-Expression -Command .gitlab/ci/borland.ps1 - - $pwdpath = $pwd.Path - - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\bcc\bin;$env:PATH" - - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake - - interruptible: true - -.cmake_test_windows_msvc: - stage: test-ext - - script: - - . .gitlab/ci/env.ps1 - - Invoke-Expression -Command .gitlab/ci/msvc.ps1 - - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 - - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake - - interruptible: true - -.cmake_test_windows_openwatcom: - stage: test-ext - - script: - - . .gitlab/ci/env.ps1 - - Invoke-Expression -Command .gitlab/ci/openwatcom.ps1 - - $pwdpath = $pwd.Path - - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\watcom\binnt;$pwdpath\.gitlab\watcom\binw;$env:PATH" - - Set-Item -Force -Path "env:INCLUDE" -Value "$pwdpath\.gitlab\watcom\h;$pwdpath\.gitlab\watcom\h\nt" - - Set-Item -Force -Path "env:EDPATH" -Value "$pwdpath\.gitlab\watcom\eddat" - - Set-Item -Force -Path "env:WATCOM" -Value "$pwdpath\.gitlab\watcom" - - Set-Item -Force -Path "env:WLINKTMP" -Value "." + - *before_script_windows_external - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake interruptible: true diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el index a11becbe20..7590ee3c1b 100644 --- a/Auxiliary/cmake-mode.el +++ b/Auxiliary/cmake-mode.el @@ -372,7 +372,7 @@ optional argument topic will be appended to the argument list." (interactive "s") (let* ((bufname (if buffer buffer (concat "*CMake" type (if topic "-") topic "*"))) (buffer (if (get-buffer bufname) (get-buffer bufname) (generate-new-buffer bufname))) - (command (concat cmake-mode-cmake-executable " " type " " topic)) + (command (concat cmake-mode-cmake-executable " " type " " (shell-quote-argument topic))) ;; Turn of resizing of mini-windows for shell-command. (resize-mini-windows nil) ) @@ -391,7 +391,7 @@ optional argument topic will be appended to the argument list." (interactive "s") (let* ((bufname (if buffer buffer (concat "*CMake" type (if topic "-") topic "*"))) (buffer (if (get-buffer bufname) (get-buffer bufname) (generate-new-buffer bufname))) - (command (concat cmake-mode-cmake-executable " " type " " topic)) + (command (concat cmake-mode-cmake-executable " " type " " (shell-quote-argument topic))) ;; Turn of resizing of mini-windows for shell-command. (resize-mini-windows nil) ) diff --git a/Help/command/add_compile_options.rst b/Help/command/add_compile_options.rst index 0ccebc679b..869d0c2ced 100644 --- a/Help/command/add_compile_options.rst +++ b/Help/command/add_compile_options.rst @@ -11,6 +11,11 @@ Adds options to the :prop_dir:`COMPILE_OPTIONS` directory property. These options are used when compiling targets from the current directory and below. +.. note:: + + These options are not used when linking. + See the :command:`add_link_options` command for that. + Arguments ^^^^^^^^^ @@ -48,5 +53,15 @@ See Also * The command :command:`target_compile_options` adds target-specific options. +* This command adds compile options for all languages. + Use the :genex:`COMPILE_LANGUAGE` generator expression to specify + per-language compile options. + * The source file property :prop_sf:`COMPILE_OPTIONS` adds options to one source file. + +* :command:`add_link_options` adds options for linking. + +* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` + add language-wide flags passed to all invocations of the compiler. + This includes invocations that drive compiling and those that drive linking. diff --git a/Help/command/add_link_options.rst b/Help/command/add_link_options.rst index c09e106908..df72715431 100644 --- a/Help/command/add_link_options.rst +++ b/Help/command/add_link_options.rst @@ -38,3 +38,7 @@ See Also * :command:`link_libraries` * :command:`target_link_libraries` * :command:`target_link_options` + +* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` + add language-wide flags passed to all invocations of the compiler. + This includes invocations that drive compiling and those that drive linking. diff --git a/Help/command/target_compile_options.rst b/Help/command/target_compile_options.rst index f3ac97bd09..7cfb24b18f 100644 --- a/Help/command/target_compile_options.rst +++ b/Help/command/target_compile_options.rst @@ -15,6 +15,11 @@ are used when compiling the given ``<target>``, which must have been created by a command such as :command:`add_executable` or :command:`add_library` and must not be an :ref:`ALIAS target <Alias Targets>`. +.. note:: + + These options are not used when linking the target. + See the :command:`target_link_options` command for that. + Arguments ^^^^^^^^^ @@ -60,3 +65,7 @@ See Also * :command:`target_link_options` * :command:`target_precompile_headers` * :command:`target_sources` + +* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` + add language-wide flags passed to all invocations of the compiler. + This includes invocations that drive compiling and those that drive linking. diff --git a/Help/command/target_link_options.rst b/Help/command/target_link_options.rst index 0d026f2456..dca9598611 100644 --- a/Help/command/target_link_options.rst +++ b/Help/command/target_link_options.rst @@ -62,3 +62,7 @@ See Also * :command:`target_link_directories` * :command:`target_precompile_headers` * :command:`target_sources` + +* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` + add language-wide flags passed to all invocations of the compiler. + This includes invocations that drive compiling and those that drive linking. diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 158bf62155..8c7189a72b 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.27 .. toctree:: :maxdepth: 1 + CMP0149: Visual Studio generators select latest Windows SDK by default. </policy/CMP0149> CMP0148: The FindPythonInterp and FindPythonLibs modules are removed. </policy/CMP0148> CMP0147: Visual Studio generators build custom commands in parallel. </policy/CMP0147> CMP0146: The FindCUDA module is removed. </policy/CMP0146> diff --git a/Help/manual/cmake-toolchains.7.rst b/Help/manual/cmake-toolchains.7.rst index 9feb4d203a..a831fa6ef1 100644 --- a/Help/manual/cmake-toolchains.7.rst +++ b/Help/manual/cmake-toolchains.7.rst @@ -273,7 +273,7 @@ supported out of the box. Other versions may require one to set Cross Compiling for Windows 10 Universal Applications ----------------------------------------------------- -A toolchain file to configure a Visual Studio generator for a +A toolchain file to configure :ref:`Visual Studio Generators` for a Windows 10 Universal Application may look like this: .. code-block:: cmake @@ -283,9 +283,10 @@ Windows 10 Universal Application may look like this: A Windows 10 Universal Application targets both Windows Store and Windows Phone. Specify the :variable:`CMAKE_SYSTEM_VERSION` variable -to be ``10.0`` to build with the latest available Windows 10 SDK. -Specify a more specific version (e.g. ``10.0.10240.0`` for RTM) -to build with the corresponding SDK. +to be ``10.0`` or higher. + +CMake selects a Windows SDK as described by documentation of the +:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable. Cross Compiling for Windows Phone --------------------------------- diff --git a/Help/policy/CMP0149.rst b/Help/policy/CMP0149.rst new file mode 100644 index 0000000000..714eeaff2b --- /dev/null +++ b/Help/policy/CMP0149.rst @@ -0,0 +1,47 @@ +CMP0149 +------- + +.. versionadded:: 3.27 + +:ref:`Visual Studio Generators` select latest Windows SDK by default. + +Visual Studio Generators select a Windows SDK version to put in the +``WindowsTargetPlatformVersion`` setting in ``.vcxproj`` files. +CMake sets the :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` +variable to the selected SDK version. + +Prior to CMake 3.27, the SDK version was always selected by the value of +the :variable:`CMAKE_SYSTEM_VERSION` variable. Users or toolchain files +could set that variable to one of the exact Windows SDK versions available +on the host system. Since :variable:`CMAKE_SYSTEM_VERSION` defaults to +:variable:`CMAKE_HOST_SYSTEM_VERSION`, and it is not guaranteed that a +matching Windows SDK version is available, CMake had to fall back to +using the latest Windows SDK version if no exact match was available. +This approach was problematic: + +* The latest Windows SDK might or might not be selected based on whether + the host version of Windows happens to match an available SDK version. + +* An old Windows SDK version might be selected that has not been updated + for newer language standards such as C11. + +CMake 3.27 and higher prefer to ignore the exact value of +:variable:`CMAKE_SYSTEM_VERSION` and by default select the latest SDK +version available. An exact SDK version may be specified explicitly +using a ``version=`` field in the :variable:`CMAKE_GENERATOR_PLATFORM` +variable. See :ref:`Visual Studio Platform Selection`. + +This policy provides compatibility for projects, toolchain files, and +build scripts that have not been ported away from using +:variable:`CMAKE_SYSTEM_VERSION` to specify an exact SDK version. + +The ``OLD`` behavior for this policy is to use the exact value of +:variable:`CMAKE_SYSTEM_VERSION` if possible. The ``NEW`` behavior +for this policy is to ignore it. + +This policy was introduced in CMake version 3.27. Use the +:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. +Unlike many policies, CMake version |release| does *not* warn +when this policy is not set and simply uses ``OLD`` behavior. + +.. include:: DEPRECATED.txt diff --git a/Help/prop_tgt/COMPILE_OPTIONS.rst b/Help/prop_tgt/COMPILE_OPTIONS.rst index 0dec25015d..8b032adeee 100644 --- a/Help/prop_tgt/COMPILE_OPTIONS.rst +++ b/Help/prop_tgt/COMPILE_OPTIONS.rst @@ -6,7 +6,7 @@ List of options to pass to the compiler. This property holds a :ref:`semicolon-separated list <CMake Language Lists>` of options specified so far for its target. Use the :command:`target_compile_options` command to append more options. -The options will be added after after flags in the +The options will be added after flags in the :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables, but before those propagated from dependencies by the :prop_tgt:`INTERFACE_COMPILE_OPTIONS` property. diff --git a/Help/release/3.26.rst b/Help/release/3.26.rst index 67ace4a459..9e9327ea57 100644 --- a/Help/release/3.26.rst +++ b/Help/release/3.26.rst @@ -175,8 +175,8 @@ Updates Changes made since CMake 3.26.0 include the following. -3.26.1, 3.26.2 --------------- +3.26.1, 3.26.2, 3.26.3 +---------------------- * These versions made no changes to documented features or interfaces. Some implementation updates were made to support ecosystem changes diff --git a/Help/release/dev/vs-sdk-selection.rst b/Help/release/dev/vs-sdk-selection.rst new file mode 100644 index 0000000000..856a2033c5 --- /dev/null +++ b/Help/release/dev/vs-sdk-selection.rst @@ -0,0 +1,7 @@ +vs-sdk-selection +---------------- + +* The :ref:`Visual Studio Generators` for VS 2015 and above learned to + select the Windows SDK version explicitly using a ``version=`` field + in the :variable:`CMAKE_GENERATOR_PLATFORM` variable. + See :ref:`Visual Studio Platform Selection`. diff --git a/Help/variable/CMAKE_GENERATOR_PLATFORM.rst b/Help/variable/CMAKE_GENERATOR_PLATFORM.rst index acb7b2e1a2..416ff60f34 100644 --- a/Help/variable/CMAKE_GENERATOR_PLATFORM.rst +++ b/Help/variable/CMAKE_GENERATOR_PLATFORM.rst @@ -26,8 +26,46 @@ Platform specification is supported only on specific generators: See native build system documentation for allowed platform names. +.. _`Visual Studio Platform Selection`: + Visual Studio Platform Selection ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On :ref:`Visual Studio Generators` the selected platform name -is provided in the :variable:`CMAKE_VS_PLATFORM_NAME` variable. +The :ref:`Visual Studio Generators` support platform specification +using one of these forms: + +* ``platform`` +* ``platform[,key=value]*`` +* ``key=value[,key=value]*`` + +The ``platform`` specifies the target platform (VS target architecture), +such as ``x64``, ``ARM64``, or ``Win32``. The selected platform +name is provided in the :variable:`CMAKE_VS_PLATFORM_NAME` variable. + +The ``key=value`` pairs form a comma-separated list of options to +specify generator-specific details of the platform selection. +Supported pairs are: + +``version=<version>`` + .. versionadded:: 3.27 + + Specify the Windows SDK version to use. This is supported by VS 2015 and + above when targeting Windows 10.0+ or Windows Store. CMake will set the + :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable to the + selected SDK version. + + The ``<version>`` may be one of: + + ``10.0`` + Specify that any 10.0 SDK version may be used, and let Visual Studio + pick one. This is supported by VS 2019 and above. + + ``10.0.<build>.<increment>`` + Specify the exact 4-component SDK version, e.g., ``10.0.19041.0``. + The specified version of the SDK must be installed. It may not exceed + the value of :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM`, + if that variable is set. + + If the ``version`` field is not specified, CMake selects a version as + described in the :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` + variable documentation. diff --git a/Help/variable/CMAKE_LANG_FLAGS.rst b/Help/variable/CMAKE_LANG_FLAGS.rst index 4b39b1deff..909a001a9c 100644 --- a/Help/variable/CMAKE_LANG_FLAGS.rst +++ b/Help/variable/CMAKE_LANG_FLAGS.rst @@ -1,9 +1,10 @@ CMAKE_<LANG>_FLAGS ------------------ -Flags for all build types. - -``<LANG>`` flags used regardless of the value of :variable:`CMAKE_BUILD_TYPE`. +Language-wide flags for language ``<LANG>`` used when building for +all configurations. These flags will be passed to all invocations +of the compiler. This includes invocations that drive compiling +and those that drive linking. For each language, if this variable is not defined, it is initialized and stored in the cache using values from environment variables in @@ -27,7 +28,10 @@ combination with CMake's builtin defaults for the toolchain: This value is a command-line string fragment. Therefore, multiple options should be separated by spaces, and options with spaces should be quoted. -The flags in this variable will be passed to the compiler before those -in the per-configuration :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variant, -and before flags added by the :command:`add_compile_options` or -:command:`target_compile_options` commands. +The flags in this variable will be passed before those in the +per-configuration :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variable. +On invocations driving compiling, flags from both variables will be passed +before flags added by commands such as :command:`add_compile_options` and +:command:`target_compile_options`. On invocations driving linking, +they will be passed before flags added by commands such as +:command:`add_link_options` and :command:`target_link_options`. diff --git a/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst b/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst index f0900fdc8d..5daa4c06a1 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst @@ -1,9 +1,16 @@ CMAKE_<LANG>_FLAGS_<CONFIG> --------------------------- -Flags for language ``<LANG>`` when building for the ``<CONFIG>`` configuration. +Language-wide flags for language ``<LANG>`` used when building for +the ``<CONFIG>`` configuration. These flags will be passed to all +invocations of the compiler in the corresponding configuration. +This includes invocations that drive compiling and those that drive +linking. -The flags in this variable will be passed to the compiler after those -in the :variable:`CMAKE_<LANG>_FLAGS` variable, but before flags added -by the :command:`add_compile_options` or :command:`target_compile_options` -commands. +The flags in this variable will be passed after those in the +:variable:`CMAKE_<LANG>_FLAGS` variable. On invocations driving compiling, +flags from both variables will be passed before flags added by commands +such as :command:`add_compile_options` and :command:`target_compile_options`. +On invocations driving linking, they will be passed before flags added by +commands such as :command:`add_link_options` and +:command:`target_link_options`. diff --git a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst index eb7104998a..2c14d3900d 100644 --- a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst +++ b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst @@ -5,11 +5,30 @@ CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION Visual Studio Windows Target Platform Version. -When targeting Windows 10 and above Visual Studio 2015 and above support -specification of a target Windows version to select a corresponding SDK. -The :variable:`CMAKE_SYSTEM_VERSION` variable may be set to specify a -version. Otherwise CMake computes a default version based on the Windows -SDK versions available. The chosen Windows target version number is provided +When targeting Windows 10 and above, :ref:`Visual Studio Generators` for +VS 2015 and above support specification of a Windows SDK version: + +* If :variable:`CMAKE_GENERATOR_PLATFORM` specifies a ``version=`` field, + as documented by :ref:`Visual Studio Platform Selection`, that SDK + version is selected. + +* Otherwise, if the ``WindowsSDKVersion`` environment variable + is set to an available SDK version, that version is selected. + This is intended for use in environments established by ``vcvarsall.bat`` + or similar scripts. + + .. versionadded:: 3.27 + This is enabled by policy :policy:`CMP0149`. + +* Otherwise, if :variable:`CMAKE_SYSTEM_VERSION` is set to an available + SDK version, that version is selected. + + .. versionchanged:: 3.27 + This is disabled by policy :policy:`CMP0149`. + +* Otherwise, CMake uses the latest Windows SDK version available. + +The chosen Windows target version number is provided in ``CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION``. If no Windows 10 SDK is available this value will be empty. diff --git a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst index f1a1977448..727ccc95be 100644 --- a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst +++ b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst @@ -10,5 +10,5 @@ be set to a false value (e.g. ``OFF``, ``FALSE``, or ``0``) or the SDK version to use as the maximum (e.g. ``10.0.14393.0``). If unset, the default depends on which version of Visual Studio is targeted by the current generator. -This can be used in conjunction with :variable:`CMAKE_SYSTEM_VERSION`, which -CMake uses to select :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`. +This can be used to exclude Windows SDK versions from consideration for +:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`. diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index 4169734af0..3fd54cc7ef 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake @@ -163,7 +163,6 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) PROJECT IntelFortranImplicit SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath - IntelFortranImplicit CMAKE_FLAGS "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}" OUTPUT_VARIABLE _output) diff --git a/Modules/CMakeDetermineSwiftCompiler.cmake b/Modules/CMakeDetermineSwiftCompiler.cmake index f0a63a8cff..7239424652 100644 --- a/Modules/CMakeDetermineSwiftCompiler.cmake +++ b/Modules/CMakeDetermineSwiftCompiler.cmake @@ -15,7 +15,14 @@ if("${CMAKE_GENERATOR}" STREQUAL "Xcode") message(FATAL_ERROR "Swift language not supported by Xcode ${XCODE_VERSION}") endif() set(CMAKE_Swift_COMPILER_XCODE_TYPE sourcecode.swift) - _cmake_find_compiler_path(Swift) + execute_process(COMMAND xcrun --find swiftc + OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE _xcrun_err RESULT_VARIABLE _xcrun_result) + if(_xcrun_result EQUAL 0 AND EXISTS "${_xcrun_out}") + set(CMAKE_Swift_COMPILER "${_xcrun_out}") + else() + _cmake_find_compiler_path(Swift) + endif() elseif("${CMAKE_GENERATOR}" MATCHES "^Ninja") if(CMAKE_Swift_COMPILER) _cmake_find_compiler_path(Swift) @@ -52,9 +59,6 @@ if(NOT CMAKE_Swift_COMPILER_ID_RUN) if("${CMAKE_GENERATOR}" STREQUAL "Xcode") list(APPEND CMAKE_Swift_COMPILER_ID_MATCH_VENDORS Apple) set(CMAKE_Swift_COMPILER_ID_MATCH_VENDOR_REGEX_Apple "com.apple.xcode.tools.swift.compiler") - - set(CMAKE_Swift_COMPILER_ID_TOOL_MATCH_REGEX "\nCompileSwift[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]* -c[^\r\n]*CompilerIdSwift/CompilerId/main.swift") - set(CMAKE_Swift_COMPILER_ID_TOOL_MATCH_INDEX 2) endif() # Try to identify the compiler. diff --git a/Modules/Compiler/NVHPC-Fortran.cmake b/Modules/Compiler/NVHPC-Fortran.cmake index 59755b3a71..5c0645737d 100644 --- a/Modules/Compiler/NVHPC-Fortran.cmake +++ b/Modules/Compiler/NVHPC-Fortran.cmake @@ -1,3 +1,4 @@ include(Compiler/PGI-Fortran) include(Compiler/NVHPC) __compiler_nvhpc(Fortran) +set(CMAKE_Fortran_PREPROCESS_SOURCE_EXCLUDE_FLAGS_REGEX "(^| )-Werror +[a-z][a-z-]+( |$)") diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index 7af1017006..19bef9471c 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake @@ -386,10 +386,10 @@ set(BLAS_LINKER_FLAGS) set(BLAS_LIBRARIES) set(BLAS95_LIBRARIES) set(_blas_fphsa_req_var BLAS_LIBRARIES) -if(NOT $ENV{BLA_VENDOR} STREQUAL "") - set(BLA_VENDOR $ENV{BLA_VENDOR}) -else() - if(NOT BLA_VENDOR) +if(NOT BLA_VENDOR) + if(NOT "$ENV{BLA_VENDOR}" STREQUAL "") + set(BLA_VENDOR "$ENV{BLA_VENDOR}") + else() set(BLA_VENDOR "All") endif() endif() diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index 67bf4246d2..4423ebb628 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake @@ -1036,12 +1036,20 @@ if(CUDAToolkit_FOUND) endif() endif() + if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.0.0) + _CUDAToolkit_find_and_add_import_lib(nvJitLink) + _CUDAToolkit_find_and_add_import_lib(nvJitLink_static DEPS cudart_static_deps) + endif() + _CUDAToolkit_find_and_add_import_lib(culibos) # it's a static library - foreach (cuda_lib cublasLt cufft curand cusparse nppc nvjpeg) + foreach (cuda_lib cublasLt cufft curand nppc nvjpeg) _CUDAToolkit_find_and_add_import_lib(${cuda_lib}) _CUDAToolkit_find_and_add_import_lib(${cuda_lib}_static DEPS culibos) endforeach() + _CUDAToolkit_find_and_add_import_lib(cusparse DEPS nvJitLink) + _CUDAToolkit_find_and_add_import_lib(cusparse_static DEPS nvJitLink_static culibos) + if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.0.0) # cublas depends on cublasLt # https://docs.nvidia.com/cuda/archive/11.0/cublas/index.html#static-library @@ -1129,16 +1137,11 @@ if(CUDAToolkit_FOUND) if(NOT TARGET CUDA::nvptxcompiler_static) _CUDAToolkit_find_and_add_import_lib(nvptxcompiler_static DEPS cuda_driver) if(TARGET CUDA::nvptxcompiler_static) - target_link_libraries(CUDA::nvptxcompiler_static INTERFACE Threads::Threads) + target_link_libraries(CUDA::nvptxcompiler_static INTERFACE CUDA::cudart_static_deps) endif() endif() endif() - if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.0.0) - _CUDAToolkit_find_and_add_import_lib(nvJitLink DEPS cuda_driver) - _CUDAToolkit_find_and_add_import_lib(nvJitLink_static DEPS cuda_driver) - endif() - _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins DEPS cuda_driver) _CUDAToolkit_find_and_add_import_lib(nvrtc DEPS nvrtc_builtins nvJitLink) if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.5.0) diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index 1eecb1cbd2..17117bd871 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -304,10 +304,12 @@ endif() # Search for different LAPACK distributions if BLAS is found if(NOT LAPACK_NOT_FOUND_MESSAGE) set(LAPACK_LINKER_FLAGS ${BLAS_LINKER_FLAGS}) - if(NOT $ENV{BLA_VENDOR} STREQUAL "") - set(BLA_VENDOR $ENV{BLA_VENDOR}) - elseif(NOT BLA_VENDOR) - set(BLA_VENDOR "All") + if(NOT BLA_VENDOR) + if(NOT "$ENV{BLA_VENDOR}" STREQUAL "") + set(BLA_VENDOR "$ENV{BLA_VENDOR}") + else() + set(BLA_VENDOR "All") + endif() endif() # LAPACK in the Intel MKL 10+ library? diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index 4e8374c68e..45dc9ac809 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake @@ -210,7 +210,7 @@ endif () # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES if(OPENSSL_USE_STATIC_LIBS) set(_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) + if(MSVC) set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) @@ -230,7 +230,7 @@ else() set(_OPENSSL_FIND_PATH_SUFFIX "include") endif() -if (WIN32) +if (MSVC) # http://www.slproweb.com/products/Win32OpenSSL.html set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index dcc717426c..4a7d9bc591 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -335,6 +335,8 @@ add_library( cmLinkLineComputer.h cmLinkLineDeviceComputer.cxx cmLinkLineDeviceComputer.h + cmList.h + cmList.cxx cmListFileCache.cxx cmListFileCache.h cmLocalCommonGenerator.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 73f213e32a..605dc3cb50 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 26) -set(CMake_VERSION_PATCH 20230403) +set(CMake_VERSION_PATCH 20230410) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index a1830f9f8e..2c3ee9b5ef 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -95,7 +95,7 @@ typename Range::const_iterator cmRemoveIndices(Range& r, InputRange const& rem) } template <typename Range, typename MatchRange> -typename Range::const_iterator cmRemoveMatching(Range& r, MatchRange const& m) +auto cmRemoveMatching(Range& r, MatchRange const& m) -> decltype(r.begin()) { return std::remove_if(r.begin(), r.end(), ContainerAlgorithms::BinarySearcher<MatchRange>(m)); diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 6e78f55776..a47366b4a1 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -12,6 +12,7 @@ #include <memory> #include <set> #include <sstream> +#include <stdexcept> #include <unordered_map> #include <utility> @@ -25,7 +26,6 @@ #include "cmsys/RegularExpression.hxx" #include "cmsys/String.h" -#include "cmAlgorithms.h" #include "cmCMakePath.h" #include "cmComputeLinkInformation.h" #include "cmGeneratorExpression.h" @@ -35,6 +35,7 @@ #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" +#include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -291,18 +292,18 @@ static const struct InListNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - std::vector<std::string> values; - std::vector<std::string> checkValues; + cmList values; + cmList checkValues; bool check = false; switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) { case cmPolicies::WARN: if (parameters.front().empty()) { check = true; - cmExpandList(parameters[1], checkValues, true); + checkValues.assign(parameters[1], cmList::EmptyElements::Yes); } CM_FALLTHROUGH; case cmPolicies::OLD: - cmExpandList(parameters[1], values); + values.assign(parameters[1]); if (check && values != checkValues) { std::ostringstream e; e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085) @@ -319,11 +320,11 @@ static const struct InListNode : public cmGeneratorExpressionNode case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: case cmPolicies::NEW: - cmExpandList(parameters[1], values, true); + values.assign(parameters[1], cmList::EmptyElements::Yes); break; } - return cm::contains(values, parameters.front()) ? "1" : "0"; + return values.find(parameters.front()) != cmList::npos ? "1" : "0"; } } inListNode; @@ -352,24 +353,17 @@ static const struct FilterNode : public cmGeneratorExpressionNode return {}; } - const bool exclude = parameters[1] == "EXCLUDE"; - - cmsys::RegularExpression re; - if (!re.compile(parameters[2])) { + try { + return cmList{ parameters.front(), cmList::EmptyElements::Yes } + .filter(parameters[2], + parameters[1] == "EXCLUDE" ? cmList::FilterMode::EXCLUDE + : cmList::FilterMode::INCLUDE) + .to_string(); + } catch (std::invalid_argument&) { reportError(context, content->GetOriginalExpression(), "$<FILTER:...> failed to compile regex"); return {}; } - - std::vector<std::string> values; - std::vector<std::string> result; - cmExpandList(parameters.front(), values, true); - - std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result), - [&re, exclude](std::string const& input) { - return exclude ^ re.find(input); - }); - return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";"); } } filterNode; @@ -391,11 +385,7 @@ static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode "$<REMOVE_DUPLICATES:...> expression requires one parameter"); } - std::vector<std::string> values = cmExpandedList(parameters.front(), true); - - auto valuesEnd = cmRemoveDuplicates(values); - auto valuesBegin = values.cbegin(); - return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";"); + return cmList{ parameters.front() }.remove_duplicates().to_string(); } } removeDuplicatesNode; @@ -1500,8 +1490,7 @@ static const struct JoinNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - std::vector<std::string> list = cmExpandedList(parameters.front()); - return cmJoin(list, parameters[1]); + return cmList{ parameters.front() }.join(parameters[1]); } } joinNode; diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 1e01dd6a2b..321f3779d0 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -525,6 +525,30 @@ bool cmGlobalVisualStudio10Generator::InitializeAndroid(cmMakefile* mf) return false; } +bool cmGlobalVisualStudio10Generator::InitializePlatform(cmMakefile* mf) +{ + if (this->SystemName == "Windows" || this->SystemName == "WindowsStore") { + if (!this->InitializePlatformWindows(mf)) { + return false; + } + } else if (!this->SystemName.empty() && + !this->VerifyNoGeneratorPlatformVersion(mf)) { + return false; + } + return this->cmGlobalVisualStudio8Generator::InitializePlatform(mf); +} + +bool cmGlobalVisualStudio10Generator::InitializePlatformWindows(cmMakefile*) +{ + return true; +} + +bool cmGlobalVisualStudio10Generator::VerifyNoGeneratorPlatformVersion( + cmMakefile*, cm::optional<std::string>) const +{ + return true; +} + bool cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset( std::string& toolset) const { diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index deed206c13..6917ffcc06 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -183,6 +183,11 @@ protected: virtual bool InitializeTegraAndroid(cmMakefile* mf); virtual bool InitializeAndroid(cmMakefile* mf); + bool InitializePlatform(cmMakefile* mf) override; + virtual bool InitializePlatformWindows(cmMakefile* mf); + virtual bool VerifyNoGeneratorPlatformVersion( + cmMakefile* mf, cm::optional<std::string> reason = cm::nullopt) const; + virtual bool ProcessGeneratorToolsetField(std::string const& key, std::string const& value); diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index 7424ca3980..4300d5c9f3 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx @@ -12,6 +12,7 @@ #include "cmGlobalVisualStudioGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" @@ -137,12 +138,36 @@ bool cmGlobalVisualStudio14Generator::MatchesGeneratorName( return false; } -bool cmGlobalVisualStudio14Generator::InitializeWindows(cmMakefile* mf) +bool cmGlobalVisualStudio14Generator::InitializePlatformWindows(cmMakefile* mf) { if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) { - return this->SelectWindows10SDK(mf, false); + return this->SelectWindows10SDK(mf); } - return true; + return this->VerifyNoGeneratorPlatformVersion(mf); +} + +bool cmGlobalVisualStudio14Generator::VerifyNoGeneratorPlatformVersion( + cmMakefile* mf, cm::optional<std::string> reason) const +{ + if (!this->GeneratorPlatformVersion) { + return true; + } + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given platform specification containing a\n" + " version=" << *this->GeneratorPlatformVersion << "\n" + "field. The version field is not supported when targeting\n" + " " << this->SystemName << " " << this->SystemVersion << "\n" + ; + /* clang-format on */ + if (reason) { + e << *reason << "."; + } + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; } bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf) @@ -162,9 +187,6 @@ bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf) mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); return false; } - if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) { - return this->SelectWindows10SDK(mf, true); - } return true; } @@ -173,19 +195,51 @@ bool cmGlobalVisualStudio14Generator::InitializeAndroid(cmMakefile*) return true; } -bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf, - bool required) +bool cmGlobalVisualStudio14Generator::ProcessGeneratorPlatformField( + std::string const& key, std::string const& value) +{ + if (key == "version") { + this->GeneratorPlatformVersion = value; + return true; + } + return false; +} + +bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf) { + if (this->GeneratorPlatformVersion && + this->GeneratorPlatformVersion->empty()) { + mf->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Generator\n ", this->GetName(), + "\ngiven platform specification with empty\n version=\n" + "field.")); + return false; + } + // Find the default version of the Windows 10 SDK. std::string const version = this->GetWindows10SDKVersion(mf); - if (required && version.empty()) { - std::ostringstream e; - e << "Could not find an appropriate version of the Windows 10 SDK" - << " installed on this machine"; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return false; + if (version.empty()) { + if (this->GeneratorPlatformVersion) { + mf->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Generator\n ", this->GetName(), + "\ngiven platform specification with\n version=", + *this->GeneratorPlatformVersion, + "\nfield, but no Windows SDK with that version was found.")); + return false; + } + + if (this->SystemName == "WindowsStore") { + mf->IssueMessage( + MessageType::FATAL_ERROR, + "Could not find an appropriate version of the Windows 10 SDK" + " installed on this machine"); + return false; + } } + this->SetWindowsTargetPlatformVersion(version, mf); return true; } @@ -302,6 +356,16 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion( cmMakefile* mf) { #if defined(_WIN32) && !defined(__CYGWIN__) + // Accept specific version requests as-is. + if (this->GeneratorPlatformVersion) { + std::string const& ver = *this->GeneratorPlatformVersion; + + // VS 2019 and above support specifying plain "10.0". + if (this->Version >= VSVersion::VS16 && ver == "10.0") { + return ver; + } + } + std::vector<std::string> win10Roots; { @@ -360,10 +424,35 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion( // Sort the results to make sure we select the most recent one. std::sort(sdks.begin(), sdks.end(), cmSystemTools::VersionCompareGreater); - // Look for a SDK exactly matching the requested target version. - for (std::string const& i : sdks) { - if (cmSystemTools::VersionCompareEqual(i, this->SystemVersion)) { - return i; + // Look for a SDK exactly matching the requested version, if any. + if (this->GeneratorPlatformVersion) { + for (std::string const& i : sdks) { + if (cmSystemTools::VersionCompareEqual( + i, *this->GeneratorPlatformVersion)) { + return i; + } + } + // An exact version was requested but not found. + // Our caller will issue the error message. + return std::string(); + } + + if (mf->GetPolicyStatus(cmPolicies::CMP0149) == cmPolicies::NEW) { + if (cm::optional<std::string> const envVer = + cmSystemTools::GetEnvVar("WindowsSDKVersion")) { + // Look for a SDK exactly matching the environment variable. + for (std::string const& i : sdks) { + if (cmSystemTools::VersionCompareEqual(i, *envVer)) { + return i; + } + } + } + } else { + // Look for a SDK exactly matching the target Windows version. + for (std::string const& i : sdks) { + if (cmSystemTools::VersionCompareEqual(i, this->SystemVersion)) { + return i; + } } } diff --git a/Source/cmGlobalVisualStudio14Generator.h b/Source/cmGlobalVisualStudio14Generator.h index 7fb9b4bbb8..f59a323308 100644 --- a/Source/cmGlobalVisualStudio14Generator.h +++ b/Source/cmGlobalVisualStudio14Generator.h @@ -7,6 +7,8 @@ #include <memory> #include <string> +#include <cm/optional> + #include "cmGlobalVisualStudio12Generator.h" class cmGlobalGeneratorFactory; @@ -30,7 +32,6 @@ protected: cmGlobalVisualStudio14Generator(cmake* cm, const std::string& name, std::string const& platformInGeneratorName); - bool InitializeWindows(cmMakefile* mf) override; bool InitializeWindowsStore(cmMakefile* mf) override; bool InitializeAndroid(cmMakefile* mf) override; bool SelectWindowsStoreToolset(std::string& toolset) const override; @@ -39,6 +40,14 @@ protected: // of the toolset is installed bool IsWindowsStoreToolsetInstalled() const; + bool InitializePlatformWindows(cmMakefile* mf) override; + bool VerifyNoGeneratorPlatformVersion( + cmMakefile* mf, + cm::optional<std::string> reason = cm::nullopt) const override; + + bool ProcessGeneratorPlatformField(std::string const& key, + std::string const& value) override; + // Used to adjust the max-SDK-version calculation to accommodate user // configuration. std::string GetWindows10SDKMaxVersion(cmMakefile* mf) const; @@ -47,7 +56,7 @@ protected: // version of the toolset. virtual std::string GetWindows10SDKMaxVersionDefault(cmMakefile* mf) const; - virtual bool SelectWindows10SDK(cmMakefile* mf, bool required); + virtual bool SelectWindows10SDK(cmMakefile* mf); void SetWindowsTargetPlatformVersion(std::string const& version, cmMakefile* mf); @@ -61,4 +70,6 @@ protected: private: class Factory; friend class Factory; + + cm::optional<std::string> GeneratorPlatformVersion; }; diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 2e2c8b686d..2aba46fe2c 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -94,7 +94,9 @@ bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p, return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform(p, mf); } - this->GeneratorPlatform = p; + if (!this->ParseGeneratorPlatform(p, mf)) { + return false; + } // FIXME: Add CMAKE_GENERATOR_PLATFORM field to set the framework. // For now, just report the generator's default, if any. @@ -114,12 +116,100 @@ bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p, *targetFrameworkTargetsVersion); } + if (!this->InitializePlatform(mf)) { + return false; + } + // The generator name does not contain the platform name, and so supports // explicit platform specification. We handled that above, so pass an // empty platform name to our base class implementation so it does not error. return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform("", mf); } +bool cmGlobalVisualStudio8Generator::ParseGeneratorPlatform( + std::string const& p, cmMakefile* mf) +{ + this->GeneratorPlatform.clear(); + + std::vector<std::string> const fields = cmTokenize(p, ","); + auto fi = fields.begin(); + if (fi == fields.end()) { + return true; + } + + // The first field may be the VS platform. + if (fi->find('=') == fi->npos) { + this->GeneratorPlatform = *fi; + ++fi; + } + + std::set<std::string> handled; + + // The rest of the fields must be key=value pairs. + for (; fi != fields.end(); ++fi) { + std::string::size_type pos = fi->find('='); + if (pos == fi->npos) { + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given platform specification\n" + " " << p << "\n" + "that contains a field after the first ',' with no '='." + ; + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; + } + std::string const key = fi->substr(0, pos); + std::string const value = fi->substr(pos + 1); + if (!handled.insert(key).second) { + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given platform specification\n" + " " << p << "\n" + "that contains duplicate field key '" << key << "'." + ; + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; + } + if (!this->ProcessGeneratorPlatformField(key, value)) { + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given platform specification\n" + " " << p << "\n" + "that contains invalid field '" << *fi << "'." + ; + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; + } + } + + return true; +} + +bool cmGlobalVisualStudio8Generator::ProcessGeneratorPlatformField( + std::string const& key, std::string const& value) +{ + static_cast<void>(key); + static_cast<void>(value); + return false; +} + +bool cmGlobalVisualStudio8Generator::InitializePlatform(cmMakefile*) +{ + return true; +} + cm::optional<std::string> const& cmGlobalVisualStudio8Generator::GetTargetFrameworkVersion() const { diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index fe57c545fd..5555e9b2de 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -60,6 +60,11 @@ protected: cmGlobalVisualStudio8Generator(cmake* cm, const std::string& name, std::string const& platformInGeneratorName); + virtual bool InitializePlatform(cmMakefile* mf); + + virtual bool ProcessGeneratorPlatformField(std::string const& key, + std::string const& value); + void AddExtraIDETargets() override; std::string FindDevEnvCommand() override; @@ -96,4 +101,7 @@ protected: cm::optional<std::string> DefaultTargetFrameworkVersion; cm::optional<std::string> DefaultTargetFrameworkIdentifier; cm::optional<std::string> DefaultTargetFrameworkTargetsVersion; + +private: + bool ParseGeneratorPlatform(std::string const& is, cmMakefile* mf); }; diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index 415eb7cf55..f28419a80c 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -885,7 +885,8 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset( return AuxToolset::PropsMissing; } -bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf) +bool cmGlobalVisualStudioVersionedGenerator::InitializePlatformWindows( + cmMakefile* mf) { // If the Win 8.1 SDK is installed then we can select a SDK matching // the target Windows version. @@ -894,13 +895,14 @@ bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf) if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 && !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) { this->SetWindowsTargetPlatformVersion("8.1", mf); - return true; + return this->VerifyNoGeneratorPlatformVersion( + mf, "with the Windows 8.1 SDK installed"); } - return cmGlobalVisualStudio14Generator::InitializeWindows(mf); + return cmGlobalVisualStudio14Generator::InitializePlatformWindows(mf); } // Otherwise we must choose a Win 10 SDK even if we are not targeting // Windows 10. - return this->SelectWindows10SDK(mf, false); + return this->SelectWindows10SDK(mf); } bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset( diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.h b/Source/cmGlobalVisualStudioVersionedGenerator.h index 45aca74a80..fb4b1d7934 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.h +++ b/Source/cmGlobalVisualStudioVersionedGenerator.h @@ -61,7 +61,6 @@ protected: VSVersion version, cmake* cm, const std::string& name, std::string const& platformInGeneratorName); - bool InitializeWindows(cmMakefile* mf) override; bool SelectWindowsStoreToolset(std::string& toolset) const override; // Used to verify that the Desktop toolset for the current generator is @@ -72,6 +71,8 @@ protected: // of the toolset is installed bool IsWindowsStoreToolsetInstalled() const; + bool InitializePlatformWindows(cmMakefile* mf) override; + // Check for a Win 8 SDK known to the registry or VS installer tool. bool IsWin81SDKInstalled() const; diff --git a/Source/cmList.cxx b/Source/cmList.cxx new file mode 100644 index 0000000000..bf5a65459a --- /dev/null +++ b/Source/cmList.cxx @@ -0,0 +1,998 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmList.h" + +#include <algorithm> +#include <cstddef> +#include <functional> +#include <iterator> +#include <set> +#include <stdexcept> +#include <utility> + +#include <cm/memory> + +#include "cmsys/RegularExpression.hxx" + +#include "cmAlgorithms.h" +#include "cmGeneratorExpression.h" +#include "cmRange.h" +#include "cmStringAlgorithms.h" +#include "cmStringReplaceHelper.h" +#include "cmSystemTools.h" + +cm::string_view cmList::element_separator{ ";" }; + +cmList cmList::sublist(size_type pos, size_type length) const +{ + if (pos >= this->Values.size()) { + throw std::out_of_range(cmStrCat( + "begin index: ", pos, " is out of range 0 - ", this->Values.size() - 1)); + } + + size_type count = (length == npos || pos + length > this->size()) + ? this->size() + : pos + length; + return this->sublist(this->begin() + pos, this->begin() + count); +} + +cmList::size_type cmList::find(cm::string_view value) const +{ + auto res = std::find(this->Values.begin(), this->Values.end(), value); + if (res == this->Values.end()) { + return npos; + } + + return std::distance(this->Values.begin(), res); +} + +cmList& cmList::remove_duplicates() +{ + auto newEnd = cmRemoveDuplicates(this->Values); + this->Values.erase(newEnd, this->Values.end()); + + return *this; +} + +namespace { +class MatchesRegex +{ +public: + MatchesRegex(cmsys::RegularExpression& regex, cmList::FilterMode mode) + : Regex(regex) + , IncludeMatches(mode == cmList::FilterMode::INCLUDE) + { + } + + bool operator()(const std::string& target) + { + return this->Regex.find(target) ^ this->IncludeMatches; + } + +private: + cmsys::RegularExpression& Regex; + const bool IncludeMatches; +}; +} + +cmList& cmList::filter(cm::string_view pattern, FilterMode mode) +{ + cmsys::RegularExpression regex(std::string{ pattern }); + if (!regex.is_valid()) { + throw std::invalid_argument( + cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"", + pattern, "\".")); + } + + auto it = std::remove_if(this->Values.begin(), this->Values.end(), + MatchesRegex{ regex, mode }); + this->Values.erase(it, this->Values.end()); + + return *this; +} + +namespace { +class StringSorter +{ +protected: + using StringFilter = std::function<std::string(const std::string&)>; + + using OrderMode = cmList::SortConfiguration::OrderMode; + using CompareMethod = cmList::SortConfiguration::CompareMethod; + using CaseSensitivity = cmList::SortConfiguration::CaseSensitivity; + + StringFilter GetCompareFilter(CompareMethod compare) + { + return (compare == CompareMethod::FILE_BASENAME) + ? cmSystemTools::GetFilenameName + : nullptr; + } + + StringFilter GetCaseFilter(CaseSensitivity sensitivity) + { + return (sensitivity == CaseSensitivity::INSENSITIVE) + ? cmSystemTools::LowerCase + : nullptr; + } + + using ComparisonFunction = + std::function<bool(const std::string&, const std::string&)>; + ComparisonFunction GetComparisonFunction(CompareMethod compare) + { + if (compare == CompareMethod::NATURAL) { + return std::function<bool(const std::string&, const std::string&)>( + [](const std::string& x, const std::string& y) { + return cmSystemTools::strverscmp(x, y) < 0; + }); + } + return std::function<bool(const std::string&, const std::string&)>( + [](const std::string& x, const std::string& y) { return x < y; }); + } + +public: + StringSorter(cmList::SortConfiguration const& config) + : Filters{ this->GetCompareFilter(config.Compare), + this->GetCaseFilter(config.Case) } + , SortMethod(this->GetComparisonFunction(config.Compare)) + , Descending(config.Order == OrderMode::DESCENDING) + { + } + + std::string ApplyFilter(const std::string& argument) + { + std::string result = argument; + for (auto const& filter : this->Filters) { + if (filter != nullptr) { + result = filter(result); + } + } + return result; + } + + bool operator()(const std::string& a, const std::string& b) + { + std::string af = this->ApplyFilter(a); + std::string bf = this->ApplyFilter(b); + bool result; + if (this->Descending) { + result = this->SortMethod(bf, af); + } else { + result = this->SortMethod(af, bf); + } + return result; + } + +private: + StringFilter Filters[2] = { nullptr, nullptr }; + ComparisonFunction SortMethod; + bool Descending; +}; +} + +cmList::SortConfiguration::SortConfiguration() = default; + +cmList& cmList::sort(const SortConfiguration& cfg) +{ + SortConfiguration config{ cfg }; + + if (config.Order == SortConfiguration::OrderMode::DEFAULT) { + config.Order = SortConfiguration::OrderMode::ASCENDING; + } + if (config.Compare == SortConfiguration::CompareMethod::DEFAULT) { + config.Compare = SortConfiguration::CompareMethod::STRING; + } + if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) { + config.Case = SortConfiguration::CaseSensitivity::SENSITIVE; + } + + if ((config.Compare == SortConfiguration::CompareMethod::STRING) && + (config.Case == SortConfiguration::CaseSensitivity::SENSITIVE) && + (config.Order == SortConfiguration::OrderMode::ASCENDING)) { + std::sort(this->Values.begin(), this->Values.end()); + } else { + StringSorter sorter(config); + std::sort(this->Values.begin(), this->Values.end(), sorter); + } + + return *this; +} + +namespace { +using transform_type = std::function<std::string(const std::string&)>; +using transform_error = cmList::transform_error; + +class TransformSelector : public cmList::TransformSelector +{ +public: + ~TransformSelector() override = default; + + std::string Tag; + + const std::string& GetTag() override { return this->Tag; } + + virtual bool Validate(std::size_t count = 0) = 0; + + virtual bool InSelection(const std::string&) = 0; + + virtual void Transform(cmList::container_type& list, + const transform_type& transform) + { + std::transform(list.begin(), list.end(), list.begin(), transform); + } + +protected: + TransformSelector(std::string&& tag) + : Tag(std::move(tag)) + { + } +}; + +class TransformNoSelector : public TransformSelector +{ +public: + TransformNoSelector() + : TransformSelector("NO SELECTOR") + { + } + + bool Validate(std::size_t) override { return true; } + + bool InSelection(const std::string&) override { return true; } +}; +class TransformSelectorRegex : public TransformSelector +{ +public: + TransformSelectorRegex(const std::string& regex) + : TransformSelector("REGEX") + , Regex(regex) + { + } + TransformSelectorRegex(std::string&& regex) + : TransformSelector("REGEX") + , Regex(regex) + { + } + + bool Validate(std::size_t) override { return this->Regex.is_valid(); } + + bool InSelection(const std::string& value) override + { + return this->Regex.find(value); + } + + cmsys::RegularExpression Regex; +}; +class TransformSelectorIndexes : public TransformSelector +{ +public: + std::vector<index_type> Indexes; + + bool InSelection(const std::string&) override { return true; } + + void Transform(std::vector<std::string>& list, + const transform_type& transform) override + { + this->Validate(list.size()); + + for (auto index : this->Indexes) { + list[index] = transform(list[index]); + } + } + +protected: + TransformSelectorIndexes(std::string&& tag) + : TransformSelector(std::move(tag)) + { + } + TransformSelectorIndexes(std::string&& tag, std::vector<int> const& indexes) + : TransformSelector(std::move(tag)) + , Indexes(indexes) + { + } + TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes) + : TransformSelector(std::move(tag)) + , Indexes(indexes) + { + } + + int NormalizeIndex(index_type index, std::size_t count) + { + if (index < 0) { + index = static_cast<index_type>(count) + index; + } + if (index < 0 || count <= static_cast<std::size_t>(index)) { + throw transform_error(cmStrCat( + "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index, + " out of range (-", count, ", ", count - 1, ").")); + } + return index; + } +}; +class TransformSelectorAt : public TransformSelectorIndexes +{ +public: + TransformSelectorAt(std::vector<index_type> const& indexes) + : TransformSelectorIndexes("AT", indexes) + { + } + TransformSelectorAt(std::vector<index_type>&& indexes) + : TransformSelectorIndexes("AT", std::move(indexes)) + { + } + + bool Validate(std::size_t count) override + { + decltype(this->Indexes) indexes; + + for (auto index : this->Indexes) { + indexes.push_back(this->NormalizeIndex(index, count)); + } + this->Indexes = std::move(indexes); + + return true; + } +}; +class TransformSelectorFor : public TransformSelectorIndexes +{ +public: + TransformSelectorFor(int start, int stop, int step) + : TransformSelectorIndexes("FOR") + , Start(start) + , Stop(stop) + , Step(step) + { + } + + bool Validate(std::size_t count) override + { + this->Start = this->NormalizeIndex(this->Start, count); + this->Stop = this->NormalizeIndex(this->Stop, count); + + // Does stepping move us further from the end? + if (this->Start > this->Stop) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, selector FOR " + "expects <start> to be no greater than <stop> (", + this->Start, " > ", this->Stop, ")")); + } + + // compute indexes + auto size = (this->Stop - this->Start + 1) / this->Step; + if ((this->Stop - this->Start + 1) % this->Step != 0) { + size += 1; + } + + this->Indexes.resize(size); + auto start = this->Start; + auto step = this->Step; + std::generate(this->Indexes.begin(), this->Indexes.end(), + [&start, step]() -> int { + auto r = start; + start += step; + return r; + }); + + return true; + } + +private: + index_type Start, Stop, Step; +}; + +class TransformAction +{ +public: + virtual ~TransformAction() = default; + + void Initialize(TransformSelector* selector) { this->Selector = selector; } + virtual void Initialize(TransformSelector*, const std::string&) {} + virtual void Initialize(TransformSelector*, const std::string&, + const std::string&) + { + } + virtual void Initialize(TransformSelector* selector, + const std::vector<std::string>&) + { + this->Initialize(selector); + } + + virtual std::string operator()(const std::string& s) = 0; + +protected: + TransformSelector* Selector; +}; +class TransformActionAppend : public TransformAction +{ +public: + using TransformAction::Initialize; + + void Initialize(TransformSelector* selector, + const std::string& append) override + { + TransformAction::Initialize(selector); + this->Append = append; + } + void Initialize(TransformSelector* selector, + const std::vector<std::string>& append) override + { + this->Initialize(selector, append.front()); + } + + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + return cmStrCat(s, this->Append); + } + + return s; + } + +private: + std::string Append; +}; +class TransformActionPrepend : public TransformAction +{ +public: + using TransformAction::Initialize; + + void Initialize(TransformSelector* selector, + const std::string& prepend) override + { + TransformAction::Initialize(selector); + this->Prepend = prepend; + } + void Initialize(TransformSelector* selector, + const std::vector<std::string>& prepend) override + { + this->Initialize(selector, prepend.front()); + } + + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + return cmStrCat(this->Prepend, s); + } + + return s; + } + +private: + std::string Prepend; +}; +class TransformActionToUpper : public TransformAction +{ +public: + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + return cmSystemTools::UpperCase(s); + } + + return s; + } +}; +class TransformActionToLower : public TransformAction +{ +public: + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + return cmSystemTools::LowerCase(s); + } + + return s; + } +}; +class TransformActionStrip : public TransformAction +{ +public: + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + return cmTrimWhitespace(s); + } + + return s; + } +}; +class TransformActionGenexStrip : public TransformAction +{ +public: + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + return cmGeneratorExpression::Preprocess( + s, cmGeneratorExpression::StripAllGeneratorExpressions); + } + + return s; + } +}; +class TransformActionReplace : public TransformAction +{ +public: + using TransformAction::Initialize; + + void Initialize(TransformSelector* selector, const std::string& regex, + const std::string& replace) override + { + TransformAction::Initialize(selector); + this->ReplaceHelper = + cm::make_unique<cmStringReplaceHelper>(regex, replace); + + if (!this->ReplaceHelper->IsRegularExpressionValid()) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile " + "regex \"", + regex, "\".")); + } + if (!this->ReplaceHelper->IsReplaceExpressionValid()) { + throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", + this->ReplaceHelper->GetError(), ".")); + } + } + void Initialize(TransformSelector* selector, + const std::vector<std::string>& args) override + { + this->Initialize(selector, args[0], args[1]); + } + + std::string operator()(const std::string& s) override + { + if (this->Selector->InSelection(s)) { + // Scan through the input for all matches. + std::string output; + + if (!this->ReplaceHelper->Replace(s, output)) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, action REPLACE: ", + this->ReplaceHelper->GetError(), ".")); + } + + return output; + } + + return s; + } + +private: + std::unique_ptr<cmStringReplaceHelper> ReplaceHelper; +}; + +// Descriptor of action +// Arity: number of arguments required for the action +// Transform: Object implementing the action +struct ActionDescriptor +{ + ActionDescriptor(cmList::TransformAction action) + : Action(action) + { + } + ActionDescriptor(cmList::TransformAction action, std::string name, + std::size_t arity, + std::unique_ptr<TransformAction> transform) + : Action(action) + , Name(std::move(name)) + , Arity(arity) + , Transform(std::move(transform)) + { + } + + operator cmList::TransformAction() const { return this->Action; } + + cmList::TransformAction Action; + std::string Name; + std::size_t Arity = 0; + std::unique_ptr<TransformAction> Transform; +}; + +// Build a set of supported actions. +using ActionDescriptorSet = std::set< + ActionDescriptor, + std::function<bool(cmList::TransformAction, cmList::TransformAction)>>; + +ActionDescriptorSet Descriptors([](cmList::TransformAction x, + cmList::TransformAction y) { + return x < y; +}); + +ActionDescriptorSet::iterator TransformConfigure( + cmList::TransformAction action, + std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity) +{ + if (Descriptors.empty()) { + Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1, + cm::make_unique<TransformActionAppend>()); + Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1, + cm::make_unique<TransformActionPrepend>()); + Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0, + cm::make_unique<TransformActionToUpper>()); + Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0, + cm::make_unique<TransformActionToLower>()); + Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0, + cm::make_unique<TransformActionStrip>()); + Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0, + cm::make_unique<TransformActionGenexStrip>()); + Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2, + cm::make_unique<TransformActionReplace>()); + } + + auto descriptor = Descriptors.find(action); + if (descriptor == Descriptors.end()) { + throw transform_error(cmStrCat(" sub-command TRANSFORM, ", + std::to_string(static_cast<int>(action)), + " invalid action.")); + } + + if (descriptor->Arity != arity) { + throw transform_error(cmStrCat("sub-command TRANSFORM, action ", + descriptor->Name, " expects ", + descriptor->Arity, " argument(s).")); + } + if (!selector) { + selector = cm::make_unique<TransformNoSelector>(); + } + + return descriptor; +} +} + +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT( + std::initializer_list<index_type> indexes) +{ + return cm::make_unique<TransformSelectorAt>( + std::vector<index_type>{ indexes.begin(), indexes.end() }); + ; +} +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT( + std::vector<index_type> const& indexes) +{ + return cm::make_unique<TransformSelectorAt>(indexes); +} +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT( + std::vector<index_type>&& indexes) +{ + return cm::make_unique<TransformSelectorAt>(std::move(indexes)); +} + +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR( + std::initializer_list<index_type> indexes) +{ + if (indexes.size() < 2 || indexes.size() > 3) { + throw transform_error("sub-command TRANSFORM, selector FOR " + "expects 2 or 3 arguments"); + } + if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) { + throw transform_error("sub-command TRANSFORM, selector FOR expects " + "positive numeric value for <step>."); + } + + return cm::make_unique<TransformSelectorFor>( + *indexes.begin(), *(indexes.begin() + 1), + indexes.size() == 3 ? *(indexes.begin() + 2) : 1); +} +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR( + std::vector<index_type> const& indexes) +{ + if (indexes.size() < 2 || indexes.size() > 3) { + throw transform_error("sub-command TRANSFORM, selector FOR " + "expects 2 or 3 arguments"); + } + if (indexes.size() == 3 && indexes[2] < 0) { + throw transform_error("sub-command TRANSFORM, selector FOR expects " + "positive numeric value for <step>."); + } + + return cm::make_unique<TransformSelectorFor>( + indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1); +} +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR( + std::vector<index_type>&& indexes) +{ + if (indexes.size() < 2 || indexes.size() > 3) { + throw transform_error("sub-command TRANSFORM, selector FOR " + "expects 2 or 3 arguments"); + } + if (indexes.size() == 3 && indexes[2] < 0) { + throw transform_error("sub-command TRANSFORM, selector FOR expects " + "positive numeric value for <step>."); + } + + return cm::make_unique<TransformSelectorFor>( + indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1); +} + +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX( + std::string const& regex) +{ + std::unique_ptr<::TransformSelector> selector = + cm::make_unique<TransformSelectorRegex>(regex); + if (!selector->Validate()) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile " + "regex \"", + regex, "\".")); + } + // weird construct to please all compilers + return std::unique_ptr<cmList::TransformSelector>(selector.release()); +} +std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX( + std::string&& regex) +{ + std::unique_ptr<::TransformSelector> selector = + cm::make_unique<TransformSelectorRegex>(std::move(regex)); + if (!selector->Validate()) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile " + "regex \"", + regex, "\".")); + } + // weird construct to please all compilers + return std::unique_ptr<cmList::TransformSelector>(selector.release()); +} + +cmList& cmList::transform(TransformAction action, + std::unique_ptr<TransformSelector> selector) +{ + auto descriptor = TransformConfigure(action, selector, 0); + + descriptor->Transform->Initialize( + static_cast<::TransformSelector*>(selector.get())); + + static_cast<::TransformSelector&>(*selector).Transform( + this->Values, [&descriptor](const std::string& s) -> std::string { + return (*descriptor->Transform)(s); + }); + + return *this; +} + +cmList& cmList::transform(TransformAction action, std::string const& arg, + std::unique_ptr<TransformSelector> selector) +{ + auto descriptor = TransformConfigure(action, selector, 1); + + descriptor->Transform->Initialize( + static_cast<::TransformSelector*>(selector.get()), arg); + + static_cast<::TransformSelector&>(*selector).Transform( + this->Values, [&descriptor](const std::string& s) -> std::string { + return (*descriptor->Transform)(s); + }); + + return *this; +} + +cmList& cmList::transform(TransformAction action, std::string const& arg1, + std::string const& arg2, + std::unique_ptr<TransformSelector> selector) +{ + auto descriptor = TransformConfigure(action, selector, 2); + + descriptor->Transform->Initialize( + static_cast<::TransformSelector*>(selector.get()), arg1, arg2); + + static_cast<::TransformSelector&>(*selector).Transform( + this->Values, [&descriptor](const std::string& s) -> std::string { + return (*descriptor->Transform)(s); + }); + + return *this; +} + +cmList& cmList::transform(TransformAction action, + std::vector<std::string> const& args, + std::unique_ptr<TransformSelector> selector) +{ + auto descriptor = TransformConfigure(action, selector, args.size()); + + descriptor->Transform->Initialize( + static_cast<::TransformSelector*>(selector.get()), args); + + static_cast<::TransformSelector&>(*selector).Transform( + this->Values, [&descriptor](const std::string& s) -> std::string { + return (*descriptor->Transform)(s); + }); + + return *this; +} + +std::string cmList::join(cm::string_view glue) const +{ + return cmJoin(this->Values, glue); +} + +std::string& cmList::append(cm::string_view value, std::string& list) +{ + if (list.empty()) { + list = std::string(value); + } else { + list += cmStrCat(cmList::element_separator, value); + } + + return list; +} + +std::string& cmList::prepend(cm::string_view value, std::string& list) +{ + if (list.empty()) { + list = std::string(value); + } else { + list.insert(0, cmStrCat(value, cmList::element_separator)); + } + + return list; +} + +cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const +{ + if (boundCheck) { + if (this->Values.empty()) { + throw std::out_of_range( + cmStrCat("index: ", pos, " out of range (0, 0)")); + } + + if (!this->Values.empty()) { + auto length = this->Values.size(); + if (pos < 0) { + pos = static_cast<index_type>(length) + pos; + } + if (pos < 0 || length <= static_cast<size_type>(pos)) { + throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-", + this->Values.size(), ", ", + this->Values.size() - 1, ")")); + } + } + return pos; + } + + return pos < 0 ? this->Values.size() + pos : pos; +} +cmList::size_type cmList::ComputeInsertIndex(index_type pos, + bool boundCheck) const +{ + if (boundCheck) { + if (this->Values.empty() && pos != 0) { + throw std::out_of_range( + cmStrCat("index: ", pos, " out of range (0, 0)")); + } + + if (!this->Values.empty()) { + auto length = this->Values.size(); + if (pos < 0) { + pos = static_cast<index_type>(length) + pos; + } + if (pos < 0 || length < static_cast<size_type>(pos)) { + throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-", + this->Values.size(), ", ", + this->Values.size(), ")")); + } + } + return pos; + } + + return pos < 0 ? this->Values.size() + pos : pos; +} + +cmList cmList::GetItems(std::vector<index_type>&& indexes) const +{ + cmList listItems; + + for (auto index : indexes) { + listItems.emplace_back(this->at(index)); + } + + return listItems; +} + +cmList& cmList::RemoveItems(std::vector<index_type>&& indexes) +{ + if (indexes.empty()) { + return *this; + } + + // compute all indexes + std::vector<size_type> idx(indexes.size()); + std::transform( + indexes.cbegin(), indexes.cend(), idx.begin(), + [this](const index_type& index) { return this->ComputeIndex(index); }); + + std::sort(idx.begin(), idx.end(), + [](size_type l, size_type r) { return l > r; }); + auto newEnd = std::unique(idx.begin(), idx.end()); + idx.erase(newEnd, idx.end()); + + for (auto index : idx) { + this->erase(this->begin() + index); + } + + return *this; +} + +cmList& cmList::RemoveItems(std::vector<std::string>&& items) +{ + std::sort(items.begin(), items.end()); + auto last = std::unique(items.begin(), items.end()); + auto first = items.begin(); + + auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last)); + this->Values.erase(newEnd, this->Values.end()); + + return *this; +} + +cmList::container_type::iterator cmList::Insert( + container_type::const_iterator pos, std::string&& value, + container_type& container, ExpandElements expandElements, + EmptyElements emptyElements) +{ + auto delta = std::distance(container.cbegin(), pos); + auto insertPos = container.begin() + delta; + + if (expandElements == ExpandElements::Yes) { + // If argument is empty, it is an empty list. + if (emptyElements == EmptyElements::No && value.empty()) { + return insertPos; + } + + // if there are no ; in the name then just copy the current string + if (value.find(';') == std::string::npos) { + return container.insert(insertPos, std::move(value)); + } + + std::string newValue; + // Break the string at non-escaped semicolons not nested in []. + int squareNesting = 0; + auto last = value.begin(); + auto const cend = value.end(); + for (auto c = last; c != cend; ++c) { + switch (*c) { + case '\\': { + // We only want to allow escaping of semicolons. Other + // escapes should not be processed here. + auto cnext = c + 1; + if ((cnext != cend) && *cnext == ';') { + newValue.append(last, c); + // Skip over the escape character + last = cnext; + c = cnext; + } + } break; + case '[': { + ++squareNesting; + } break; + case ']': { + --squareNesting; + } break; + case ';': { + // brackets. + if (squareNesting == 0) { + newValue.append(last, c); + // Skip over the semicolon + last = c + 1; + if (!newValue.empty() || emptyElements == EmptyElements::Yes) { + // Add the last argument. + insertPos = container.insert(insertPos, newValue); + insertPos++; + newValue.clear(); + } + } + } break; + default: { + // Just append this character. + } break; + } + } + newValue.append(last, cend); + if (!newValue.empty() || emptyElements == EmptyElements::Yes) { + // Add the last argument. + container.insert(insertPos, std::move(newValue)); + } + } else if (!value.empty() || emptyElements == EmptyElements::Yes) { + return container.insert(insertPos, std::move(value)); + } + return container.begin() + delta; +} diff --git a/Source/cmList.h b/Source/cmList.h new file mode 100644 index 0000000000..d994ad3890 --- /dev/null +++ b/Source/cmList.h @@ -0,0 +1,1198 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <algorithm> +#include <initializer_list> +#include <iterator> +#include <memory> +#include <numeric> +#include <stdexcept> +#include <string> +#include <utility> +#include <vector> + +#include <cm/string_view> + +#include "cmValue.h" + +/** + * CMake lists management + * + * For all operations, input arguments (single value like cm::string_view or + * multiple values specified through pair of iterators) are, by default, + * expanded. The expansion can be controlled by the cmList::ExpandElements + * option. + * + * There is an exception to this rule. The following methods do not expand + * their argument: cmList::push_back, cmList::emplace and cmList::emplace_back. + */ + +class cmList +{ +public: + using container_type = std::vector<std::string>; + + using value_type = container_type::value_type; + using allocator_type = container_type::allocator_type; + using index_type = int; + using size_type = container_type::size_type; + using difference_type = container_type::difference_type; + using reference = container_type::reference; + using const_reference = container_type::const_reference; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using reverse_iterator = container_type::reverse_iterator; + using const_reverse_iterator = container_type::const_reverse_iterator; + + static const size_type npos = static_cast<size_type>(-1); + + static cm::string_view element_separator; + + enum class EmptyElements + { + No, + Yes, + }; + enum class ExpandElements + { + No, + Yes, + }; + + cmList() = default; + cmList(const cmList&) = default; + cmList(cmList&&) = default; + + cmList(cm::string_view value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->assign(value, expandElements, emptyElements); + } + cmList(cm::string_view value, EmptyElements emptyElements) + { + this->assign(value, ExpandElements::Yes, emptyElements); + } + cmList(cmValue list, ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + if (list) { + this->assign(*list, expandElements, emptyElements); + } + } + cmList(cmValue list, EmptyElements emptyElements) + : cmList(list, ExpandElements::Yes, emptyElements) + { + } + template <typename InputIterator> + cmList(InputIterator first, InputIterator last, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->assign(first, last, expandElements, emptyElements); + } + template <typename InputIterator> + cmList(InputIterator first, InputIterator last, EmptyElements emptyElements) + : cmList(first, last, ExpandElements::Yes, emptyElements) + { + } + cmList(const container_type& init, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + : cmList(init.begin(), init.end(), expandElements, emptyElements) + { + } + cmList(const container_type& init, EmptyElements emptyElements) + : cmList(init, ExpandElements::Yes, emptyElements) + { + } + cmList(container_type&& init, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + : cmList(std::make_move_iterator(init.begin()), + std::make_move_iterator(init.end()), expandElements, + emptyElements) + { + init.clear(); + } + cmList(container_type&& init, EmptyElements emptyElements) + : cmList(std::move(init), ExpandElements::Yes, emptyElements) + { + } + cmList(std::initializer_list<std::string> init) { this->assign(init); } + + ~cmList() = default; + + cmList& operator=(const cmList&) = default; + cmList& operator=(cmList&&) = default; + + cmList& operator=(cm::string_view value) + { + this->assign(value); + return *this; + } + cmList& operator=(cmValue value) + { + if (value) { + this->operator=(*value); + } else { + this->clear(); + } + + return *this; + } + + cmList& operator=(const container_type& init) + { + this->assign(init); + return *this; + } + cmList& operator=(container_type&& init) + { + this->assign(std::move(init)); + + return *this; + } + + cmList& operator=(std::initializer_list<std::string> init) + { + this->assign(init); + return *this; + } + + void assign(cm::string_view value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->clear(); + this->append(value, expandElements, emptyElements); + } + void assign(cm::string_view value, EmptyElements emptyElements) + { + this->assign(value, ExpandElements::Yes, emptyElements); + } + void assign(cmValue value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + this->assign(*value, expandElements, emptyElements); + } else { + this->clear(); + } + } + void assign(cmValue value, EmptyElements emptyElements) + { + this->assign(value, ExpandElements::Yes, emptyElements); + } + template <typename InputIterator> + void assign(InputIterator first, InputIterator last, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->clear(); + this->append(first, last, expandElements, emptyElements); + } + template <typename InputIterator> + void assign(InputIterator first, InputIterator last, + EmptyElements emptyElements) + { + this->assign(first, last, ExpandElements::Yes, emptyElements); + } + void assign(const cmList& init, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->assign(init.begin(), init.end(), expandElements, emptyElements); + } + void assign(const cmList& init, EmptyElements emptyElements) + { + this->assign(init, ExpandElements::Yes, emptyElements); + } + void assign(cmList&& init, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->assign(std::make_move_iterator(init.begin()), + std::make_move_iterator(init.end()), expandElements, + emptyElements); + init.clear(); + } + void assign(cmList&& init, EmptyElements emptyElements) + { + this->assign(std::move(init), ExpandElements::Yes, emptyElements); + } + void assign(const container_type& init, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->assign(init.begin(), init.end(), expandElements, emptyElements); + } + void assign(const container_type& init, EmptyElements emptyElements) + { + this->assign(init, ExpandElements::Yes, emptyElements); + } + void assign(container_type&& init, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + this->assign(std::make_move_iterator(init.begin()), + std::make_move_iterator(init.end()), expandElements, + emptyElements); + init.clear(); + } + void assign(container_type&& init, EmptyElements emptyElements) + { + this->assign(std::move(init), ExpandElements::Yes, emptyElements); + } + void assign(std::initializer_list<std::string> init) + { + this->assign(init.begin(), init.end()); + } + + // Conversions + std::string to_string() const + { + return this->join(cmList::element_separator); + } + + operator container_type&() & noexcept { return this->Values; } + operator const container_type&() const& noexcept { return this->Values; } + operator container_type&&() && noexcept { return std::move(this->Values); } + + // Element access + reference at(index_type pos) + { + return this->Values.at(this->ComputeIndex(pos)); + } + const_reference at(index_type pos) const + { + return this->Values.at(this->ComputeIndex(pos)); + } + + reference operator[](index_type pos) + { + return this->Values[this->ComputeIndex(pos, false)]; + } + const_reference operator[](index_type pos) const + { + return this->Values[this->ComputeIndex(pos, false)]; + } + + reference front() { return this->Values.front(); } + const_reference front() const { return this->Values.front(); } + + reference back() { return this->Values.back(); } + const_reference back() const { return this->Values.back(); } + + // extract sublist in range [first, last) + cmList sublist(const_iterator first, const_iterator last) const + { + return cmList{ first, last }; + } + // Extract sublist in range [first, last) + // Throw std::out_of_range if pos is invalid + cmList sublist(size_type pos = 0, size_type length = npos) const; + + // Returns the list of elements + // Throw std::out_of_range if any index is invalid + cmList get_items(std::initializer_list<index_type> indexes) const + { + return this->GetItems( + std::vector<index_type>{ indexes.begin(), indexes.end() }); + } + template <typename InputIterator> + cmList get_items(InputIterator first, InputIterator last) const + { + return this->GetItems(std::vector<index_type>{ first, last }); + } + + size_type find(cm::string_view value) const; + size_type find(cmValue value) const + { + if (value) { + return this->find(*value); + } + + return npos; + } + + container_type& data() noexcept { return this->Values; } + const container_type& data() const noexcept { return this->Values; } + + // Iterators + iterator begin() noexcept { return this->Values.begin(); } + const_iterator begin() const noexcept { return this->Values.begin(); } + const_iterator cbegin() const noexcept { return this->Values.cbegin(); } + + iterator end() noexcept { return this->Values.end(); } + const_iterator end() const noexcept { return this->Values.end(); } + const_iterator cend() const noexcept { return this->Values.cend(); } + + reverse_iterator rbegin() noexcept { return this->Values.rbegin(); } + const_reverse_iterator rbegin() const noexcept + { + return this->Values.rbegin(); + } + const_reverse_iterator crbegin() const noexcept + { + return this->Values.crbegin(); + } + + reverse_iterator rend() noexcept { return this->Values.rend(); } + const_reverse_iterator rend() const noexcept { return this->Values.rend(); } + const_reverse_iterator crend() const noexcept + { + return this->Values.crend(); + } + + // Capacity + bool empty() const noexcept { return this->Values.empty(); } + size_type size() const noexcept { return this->Values.size(); } + + // Modifiers + void clear() noexcept { this->Values.clear(); } + + iterator insert(const_iterator pos, cm::string_view value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::Insert(pos, std::string(value), this->Values, + expandElements, emptyElements); + } + iterator insert(const_iterator pos, cm::string_view value, + EmptyElements emptyElements) + { + return this->insert(pos, value, ExpandElements::Yes, emptyElements); + } + iterator insert(const_iterator pos, cmValue value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + return this->insert(pos, *value, expandElements, emptyElements); + } + + auto delta = std::distance(this->cbegin(), pos); + return this->begin() + delta; + } + iterator insert(const_iterator pos, cmValue value, + EmptyElements emptyElements) + { + return this->insert(pos, value, ExpandElements::Yes, emptyElements); + } + template <typename InputIterator> + iterator insert(const_iterator pos, InputIterator first, InputIterator last, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::Insert(pos, first, last, this->Values, expandElements, + emptyElements); + } + template <typename InputIterator> + iterator insert(const_iterator pos, InputIterator first, InputIterator last, + EmptyElements emptyElements) + { + return this->insert(pos, first, last, ExpandElements::Yes, emptyElements); + } + iterator insert(const_iterator pos, const cmList& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->insert(pos, values.begin(), values.end(), expandElements, + emptyElements); + } + iterator insert(const_iterator pos, const cmList& values, + EmptyElements emptyElements) + { + return this->insert(pos, values, ExpandElements::Yes, emptyElements); + } + iterator insert(const_iterator pos, cmList&& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + auto result = this->insert(pos, std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()), + expandElements, emptyElements); + values.clear(); + + return result; + } + iterator insert(const_iterator pos, cmList&& values, + EmptyElements emptyElements) + { + return this->insert(pos, std::move(values), ExpandElements::Yes, + emptyElements); + } + iterator insert(const_iterator pos, const container_type& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->insert(pos, values.begin(), values.end(), expandElements, + emptyElements); + } + iterator insert(const_iterator pos, const container_type& values, + EmptyElements emptyElements) + { + return this->insert(pos, values, ExpandElements::Yes, emptyElements); + } + iterator insert(const_iterator pos, container_type&& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + auto result = this->insert(pos, std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()), + expandElements, emptyElements); + values.clear(); + + return result; + } + iterator insert(const_iterator pos, container_type&& values, + EmptyElements emptyElements) + { + return this->insert(pos, std::move(values), ExpandElements::Yes, + emptyElements); + } + iterator insert(const_iterator pos, std::initializer_list<std::string> ilist) + { + return this->insert(pos, ilist.begin(), ilist.end()); + } + + iterator append(cm::string_view value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->insert(this->cend(), value, expandElements, emptyElements); + } + iterator append(cm::string_view value, EmptyElements emptyElements) + { + return this->append(value, ExpandElements::Yes, emptyElements); + } + iterator append(cmValue value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + return this->append(*value, expandElements, emptyElements); + } + + return this->end(); + } + iterator append(cmValue value, EmptyElements emptyElements) + { + return this->append(value, ExpandElements::Yes, emptyElements); + } + template <typename InputIterator> + iterator append(InputIterator first, InputIterator last, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->insert(this->cend(), first, last, expandElements, + emptyElements); + } + template <typename InputIterator> + iterator append(InputIterator first, InputIterator last, + EmptyElements emptyElements) + { + return this->append(first, last, ExpandElements::Yes, emptyElements); + } + iterator append(const cmList& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->append(values.begin(), values.end(), expandElements, + emptyElements); + } + iterator append(const cmList& values, EmptyElements emptyElements) + { + return this->append(values, ExpandElements::Yes, emptyElements); + } + iterator append(cmList&& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + auto result = this->append(std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()), + expandElements, emptyElements); + values.clear(); + + return result; + } + iterator append(cmList&& values, EmptyElements emptyElements) + { + return this->append(std::move(values), ExpandElements::Yes, emptyElements); + } + iterator append(const container_type& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->append(values.begin(), values.end(), expandElements, + emptyElements); + } + iterator append(const container_type& values, EmptyElements emptyElements) + { + return this->append(values, ExpandElements::Yes, emptyElements); + } + iterator append(container_type&& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + auto result = this->append(std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()), + expandElements, emptyElements); + values.clear(); + + return result; + } + iterator append(container_type&& values, EmptyElements emptyElements) + { + return this->append(std::move(values), ExpandElements::Yes, emptyElements); + } + iterator append(std::initializer_list<std::string> ilist) + { + return this->insert(this->cend(), ilist); + } + + iterator prepend(cm::string_view value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->insert(this->cbegin(), value, expandElements, emptyElements); + } + iterator prepend(cm::string_view value, EmptyElements emptyElements) + { + return this->prepend(value, ExpandElements::Yes, emptyElements); + } + iterator prepend(cmValue value, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + return this->prepend(*value, expandElements, emptyElements); + } + + return this->begin(); + } + iterator prepend(cmValue value, EmptyElements emptyElements) + { + return this->prepend(value, ExpandElements::Yes, emptyElements); + } + template <typename InputIterator> + iterator prepend(InputIterator first, InputIterator last, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->insert(this->cbegin(), first, last, expandElements, + emptyElements); + } + template <typename InputIterator> + iterator prepend(InputIterator first, InputIterator last, + EmptyElements emptyElements) + { + return this->prepend(first, last, ExpandElements::Yes, emptyElements); + } + iterator prepend(const cmList& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->prepend(values.begin(), values.end(), expandElements, + emptyElements); + } + iterator prepend(const cmList& values, EmptyElements emptyElements) + { + return this->prepend(values, ExpandElements::Yes, emptyElements); + } + iterator prepend(cmList&& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + auto result = this->prepend(std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()), + expandElements, emptyElements); + values.clear(); + + return result; + } + iterator prepend(cmList&& values, EmptyElements emptyElements) + { + return this->prepend(std::move(values), ExpandElements::Yes, + emptyElements); + } + iterator prepend(const container_type& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + return this->prepend(values.begin(), values.end(), expandElements, + emptyElements); + } + iterator prepend(const container_type& values, EmptyElements emptyElements) + { + return this->prepend(values, ExpandElements::Yes, emptyElements); + } + iterator prepend(container_type&& values, + ExpandElements expandElements = ExpandElements::Yes, + EmptyElements emptyElements = EmptyElements::No) + { + auto result = this->prepend(std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()), + expandElements, emptyElements); + values.clear(); + + return result; + } + iterator prepend(container_type&& values, EmptyElements emptyElements) + { + return this->prepend(std::move(values), ExpandElements::Yes, + emptyElements); + } + iterator prepend(std::initializer_list<std::string> ilist) + { + return this->insert(this->cbegin(), ilist); + } + + void push_back(cm::string_view value) + { + this->Values.push_back(std::string{ value }); + } + void push_back(std::string&& value) + { + this->Values.push_back(std::move(value)); + } + + template <typename... Args> + iterator emplace(const_iterator pos, Args&&... args) + { + return this->Values.emplace(pos, std::forward<Args>(args)...); + } + + template <typename... Args> + void emplace_back(Args&&... args) + { + this->Values.emplace_back(std::forward<Args>(args)...); + } + + // Inserts elements in the list + // Throw std::out_of_range if index is invalid + template <typename InputIterator> + cmList& insert_items(index_type index, InputIterator first, + InputIterator last) + { + this->insert(this->begin() + this->ComputeInsertIndex(index), first, last); + return *this; + } + cmList& insert_items(index_type index, + std::initializer_list<std::string> values) + { + return this->insert_items(index, values.begin(), values.end()); + } + + iterator erase(const_iterator pos) + { + // convert const_iterator in iterator to please non standard c++11 + // compilers (gcc 4.8 for example) + auto pos2 = + this->Values.begin() + std::distance(this->Values.cbegin(), pos); + return this->Values.erase(pos2); + } + iterator erase(const_iterator first, const_iterator last) + { + // convert const_iterator in iterator to please non standard c++11 + // compilers (gcc 4.8 for example) + auto first2 = + this->Values.begin() + std::distance(this->Values.cbegin(), first); + auto last2 = + this->Values.begin() + std::distance(this->Values.cbegin(), last); + return this->Values.erase(first2, last2); + } + + void pop_back() { this->Values.pop_back(); } + void pop_front() { this->Values.erase(this->begin()); } + + // Removes elements from the list + // Throw std::out_of_range if any index is invalid + cmList& remove_items(std::initializer_list<index_type> indexes) + { + return this->RemoveItems( + std::vector<index_type>{ indexes.begin(), indexes.end() }); + } + cmList& remove_items(std::initializer_list<std::string> values) + { + return this->RemoveItems( + std::vector<std::string>{ values.begin(), values.end() }); + } + template <typename InputIterator> + cmList& remove_items(InputIterator first, InputIterator last) + { + return this->RemoveItems( + std::vector<typename InputIterator::value_type>{ first, last }); + } + + cmList& remove_duplicates(); + + enum class FilterMode + { + INCLUDE, + EXCLUDE + }; + // Includes or removes items from the list + // Throw std::invalid_argument if regular expression is invalid + cmList& filter(cm::string_view regex, FilterMode mode); + + cmList& reverse() + { + std::reverse(this->Values.begin(), this->Values.end()); + return *this; + } + + struct SortConfiguration + { + enum class OrderMode + { + DEFAULT, + ASCENDING, + DESCENDING, + } Order = OrderMode::DEFAULT; + + enum class CompareMethod + { + DEFAULT, + STRING, + FILE_BASENAME, + NATURAL, + } Compare = CompareMethod::DEFAULT; + + enum class CaseSensitivity + { + DEFAULT, + SENSITIVE, + INSENSITIVE, + } Case = CaseSensitivity::DEFAULT; + + // declare the default constructor to work-around clang bug + SortConfiguration(); + + SortConfiguration(OrderMode order, CompareMethod compare, + CaseSensitivity caseSensitivity) + : Order(order) + , Compare(compare) + , Case(caseSensitivity) + { + } + }; + cmList& sort(const SortConfiguration& config = SortConfiguration{}); + + // exception raised on error during transform operations + class transform_error : public std::runtime_error + { + public: + transform_error(const std::string& error) + : std::runtime_error(error) + { + } + }; + + class TransformSelector + { + public: + using index_type = cmList::index_type; + + // define some structs used as template selector + struct AT; + struct FOR; + struct REGEX; + + virtual ~TransformSelector() = default; + + virtual const std::string& GetTag() = 0; + + // method NEW is used to allocate a selector of the needed type. + // For example: + // cmList::TransformSelector::New<AT>({1, 2, 5, 6}); + // or + // cmList::TransformSelector::New<REGEX>("^XX.*"); + template <typename Type> + static std::unique_ptr<TransformSelector> New( + std::initializer_list<index_type>); + template <typename Type> + static std::unique_ptr<TransformSelector> New( + std::vector<index_type> const&); + template <typename Type> + static std::unique_ptr<TransformSelector> New(std::vector<index_type>&&); + + template <typename Type> + static std::unique_ptr<TransformSelector> New(std::string const&); + template <typename Type> + static std::unique_ptr<TransformSelector> New(std::string&&); + + private: + static std::unique_ptr<TransformSelector> NewAT( + std::initializer_list<index_type> init); + static std::unique_ptr<TransformSelector> NewAT( + std::vector<index_type> const& init); + static std::unique_ptr<TransformSelector> NewAT( + std::vector<index_type>&& init); + + static std::unique_ptr<TransformSelector> NewFOR( + std::initializer_list<index_type> init); + static std::unique_ptr<TransformSelector> NewFOR( + std::vector<index_type> const& init); + static std::unique_ptr<TransformSelector> NewFOR( + std::vector<index_type>&& init); + + static std::unique_ptr<TransformSelector> NewREGEX( + std::string const& init); + static std::unique_ptr<TransformSelector> NewREGEX(std::string&& init); + }; + + enum class TransformAction + { + APPEND, + PREPEND, + TOLOWER, + TOUPPER, + STRIP, + GENEX_STRIP, + REPLACE + }; + + // Transforms the list by applying an action + // Throw std::transform_error is any of arguments specified are invalid + cmList& transform(TransformAction action, + std::unique_ptr<TransformSelector> = {}); + cmList& transform(TransformAction action, std::string const& arg, + std::unique_ptr<TransformSelector> = {}); + cmList& transform(TransformAction action, std::string const& arg1, + std::string const& arg2, + std::unique_ptr<TransformSelector> = {}); + cmList& transform(TransformAction action, + std::vector<std::string> const& args, + std::unique_ptr<TransformSelector> = {}); + + std::string join(cm::string_view glue) const; + + void swap(cmList& other) noexcept { this->Values.swap(other.Values); } + + // static members + // ============== + // these methods can be used to store CMake list expansion directly in a + // std::vector. + static void assign(cm::string_view value, + std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + container.clear(); + cmList::append(value, container, emptyElements); + } + static void assign(cmValue value, std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + cmList::assign(*value, container, emptyElements); + } else { + container.clear(); + } + } + template <typename InputIterator> + static void assign(InputIterator first, InputIterator last, + std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + container.clear(); + cmList::append(first, last, container, emptyElements); + } + + static std::vector<std::string>::iterator insert( + std::vector<std::string>::const_iterator pos, cm::string_view value, + std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::Insert(pos, std::string(value), container, + ExpandElements::Yes, emptyElements); + } + static std::vector<std::string>::iterator insert( + std::vector<std::string>::const_iterator pos, cmValue value, + std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + return cmList::insert(pos, *value, container, emptyElements); + } + + auto delta = std::distance(container.cbegin(), pos); + return container.begin() + delta; + } + template <typename InputIterator> + static std::vector<std::string>::iterator insert( + std::vector<std::string>::const_iterator pos, InputIterator first, + InputIterator last, std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::Insert(pos, first, last, container, ExpandElements::Yes, + emptyElements); + } + + static std::vector<std::string>::iterator append( + cm::string_view value, std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::insert(container.cend(), value, container, emptyElements); + } + static std::vector<std::string>::iterator append( + cmValue value, std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + return cmList::append(*value, container, emptyElements); + } + + return container.end(); + } + template <typename InputIterator> + static std::vector<std::string>::iterator append( + InputIterator first, InputIterator last, + std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::insert(container.cend(), first, last, container, + emptyElements); + } + + static std::vector<std::string>::iterator prepend( + cm::string_view value, std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::insert(container.cbegin(), value, container, emptyElements); + } + static std::vector<std::string>::iterator prepend( + cmValue value, std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + if (value) { + return cmList::prepend(*value, container, emptyElements); + } + + return container.begin(); + } + template <typename InputIterator> + static std::vector<std::string>::iterator prepend( + InputIterator first, InputIterator last, + std::vector<std::string>& container, + EmptyElements emptyElements = EmptyElements::No) + { + return cmList::insert(container.cbegin(), first, last, container, + emptyElements); + } + + // The following methods offer the possibility to extend a CMake list + // but without any intermediate expansion. So the operation is simply a + // string concatenation with special handling for the CMake list item + // separator + static std::string& append(cm::string_view value, std::string& list); + template <typename InputIterator> + static std::string& append(InputIterator first, InputIterator last, + std::string& list) + { + if (first == last) { + return list; + } + + return cmList::append(cm::string_view{ std::accumulate( + std::next(first), last, *first, + [](std::string a, const std::string& b) { + return std::move(a) + + std::string(cmList::element_separator) + b; + }) }, + list); + } + + static std::string& prepend(cm::string_view value, std::string& list); + template <typename InputIterator> + static std::string& prepend(InputIterator first, InputIterator last, + std::string& list) + { + if (first == last) { + return list; + } + + return cmList::prepend(cm::string_view{ std::accumulate( + std::next(first), last, *first, + [](std::string a, const std::string& b) { + return std::move(a) + + std::string(cmList::element_separator) + b; + }) }, + list); + } + + // Non-members + // =========== + friend inline bool operator==(const cmList& lhs, const cmList& rhs) noexcept + { + return lhs.Values == rhs.Values; + } + friend inline bool operator!=(const cmList& lhs, const cmList& rhs) noexcept + { + return lhs.Values != rhs.Values; + } + +private: + size_type ComputeIndex(index_type pos, bool boundCheck = true) const; + size_type ComputeInsertIndex(index_type pos, bool boundCheck = true) const; + + cmList GetItems(std::vector<index_type>&& indexes) const; + + cmList& RemoveItems(std::vector<index_type>&& indexes); + cmList& RemoveItems(std::vector<std::string>&& items); + + static container_type::iterator Insert(container_type::const_iterator pos, + std::string&& value, + container_type& container, + ExpandElements expandElements, + EmptyElements emptyElements); + static container_type::iterator Insert(container_type::const_iterator pos, + const std::string& value, + container_type& container, + ExpandElements expandElements, + EmptyElements emptyElements) + { + auto tmp = value; + return cmList::Insert(pos, std::move(tmp), container, expandElements, + emptyElements); + } + template <typename InputIterator> + static container_type::iterator Insert(container_type::const_iterator pos, + InputIterator first, + InputIterator last, + container_type& container, + ExpandElements expandElements, + EmptyElements emptyElements) + { + auto delta = std::distance(container.cbegin(), pos); + + if (first == last) { + return container.begin() + delta; + } + + auto insertPos = container.begin() + delta; + if (expandElements == ExpandElements::Yes) { + for (; first != last; ++first) { + auto size = container.size(); + insertPos = cmList::Insert(insertPos, *first, container, + expandElements, emptyElements); + insertPos += container.size() - size; + } + } else { + for (; first != last; ++first) { + if (!first->empty() || emptyElements == EmptyElements::Yes) { + insertPos = container.insert(insertPos, *first); + insertPos++; + } + } + } + + return container.begin() + delta; + } + + container_type Values; +}; + +// specializations for cmList::TransformSelector allocators +// ======================================================== +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::AT>( + std::initializer_list<index_type> init) +{ + return cmList::TransformSelector::NewAT(init); +} +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::AT>( + std::vector<index_type> const& init) +{ + return cmList::TransformSelector::NewAT(init); +} +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::AT>( + std::vector<index_type>&& init) +{ + return cmList::TransformSelector::NewAT(std::move(init)); +} + +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::FOR>( + std::initializer_list<index_type> init) +{ + return cmList::TransformSelector::NewFOR(init); +} +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::FOR>( + std::vector<index_type> const& init) +{ + return cmList::TransformSelector::NewFOR(init); +} +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::FOR>( + std::vector<index_type>&& init) +{ + return cmList::TransformSelector::NewFOR(std::move(init)); +} + +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::REGEX>( + std::string const& init) +{ + return cmList::TransformSelector::NewREGEX(init); +} +template <> +inline std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::New<cmList::TransformSelector::REGEX>( + std::string&& init) +{ + return cmList::TransformSelector::NewREGEX(std::move(init)); +} + +// Non-member functions +// ==================== +inline std::vector<std::string>& operator+=(std::vector<std::string>& l, + const cmList& r) +{ + l.insert(l.end(), r.begin(), r.end()); + return l; +} +inline std::vector<std::string>& operator+=(std::vector<std::string>& l, + cmList&& r) +{ + std::move(r.begin(), r.end(), std::back_inserter(l)); + r.clear(); + + return l; +} + +namespace cm { +inline void erase(cmList& list, const std::string& value) +{ + list.erase(std::remove(list.begin(), list.end(), value), list.end()); +} + +template <typename Predicate> +inline void erase_if(cmList& list, Predicate pred) +{ + list.erase(std::remove_if(list.begin(), list.end(), pred), list.end()); +} +} + +namespace srd { +inline void swap(cmList& lhs, cmList& rhs) noexcept +{ + lhs.swap(rhs); +} +} diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index d412534c0e..40be0ceeb5 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -2,11 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmListCommand.h" -#include <algorithm> #include <cassert> #include <cstdio> #include <functional> -#include <iterator> #include <set> #include <sstream> #include <stdexcept> @@ -14,22 +12,18 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/RegularExpression.hxx" - -#include "cmAlgorithms.h" #include "cmExecutionStatus.h" -#include "cmGeneratorExpression.h" +#include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" #include "cmStringAlgorithms.h" -#include "cmStringReplaceHelper.h" #include "cmSubcommandTable.h" -#include "cmSystemTools.h" #include "cmValue.h" namespace { @@ -70,11 +64,6 @@ bool GetIndexArg(const std::string& arg, int* idx, cmMakefile& mf) return true; } -bool FilterRegex(std::vector<std::string> const& args, bool includeMatches, - std::string const& listName, - std::vector<std::string>& varArgsExpanded, - cmExecutionStatus& status); - bool GetListString(std::string& listString, const std::string& var, const cmMakefile& makefile) { @@ -87,22 +76,25 @@ bool GetListString(std::string& listString, const std::string& var, return true; } -bool GetList(std::vector<std::string>& list, const std::string& var, - const cmMakefile& makefile) +cm::optional<cmList> GetList(const std::string& var, + const cmMakefile& makefile) { + cm::optional<cmList> list; + std::string listString; if (!GetListString(listString, var, makefile)) { - return false; + return list; } // if the size of the list if (listString.empty()) { - return true; + list.emplace(); + return list; } // expand the variable into a list - cmExpandList(listString, list, true); + list.emplace(listString, cmList::EmptyElements::Yes); // if no empty elements then just return - if (!cm::contains(list, std::string())) { - return true; + if (!cm::contains(*list, std::string())) { + return list; } // if we have empty elements we need to check policy CMP0007 switch (makefile.GetPolicyStatus(cmPolicies::CMP0007)) { @@ -111,31 +103,29 @@ bool GetList(std::vector<std::string>& list, const std::string& var, // OLD behavior is to allow compatibility, so recall // ExpandListArgument without the true which will remove // empty values - list.clear(); - cmExpandList(listString, list); + list->assign(listString); std::string warn = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007), " List has value = [", listString, "]."); makefile.IssueMessage(MessageType::AUTHOR_WARNING, warn); - return true; + return list; } case cmPolicies::OLD: // OLD behavior is to allow compatibility, so recall // ExpandListArgument without the true which will remove // empty values - list.clear(); - cmExpandList(listString, list); - return true; + list->assign(listString); + return list; case cmPolicies::NEW: - return true; + return list; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: makefile.IssueMessage( MessageType::FATAL_ERROR, cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007)); - return false; + return {}; } - return true; + return list; } bool HandleLengthCommand(std::vector<std::string> const& args, @@ -148,16 +138,11 @@ bool HandleLengthCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; const std::string& variableName = args.back(); - std::vector<std::string> varArgsExpanded; - // do not check the return value here - // if the list var is not found varArgsExpanded will have size 0 - // and we will return 0 - GetList(varArgsExpanded, listName, status.GetMakefile()); - size_t length = varArgsExpanded.size(); - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(length)); - - status.GetMakefile().AddDefinition(variableName, buffer); + + auto list = GetList(listName, status.GetMakefile()); + status.GetMakefile().AddDefinition(variableName, + std::to_string(list ? list->size() : 0)); + return true; } @@ -172,42 +157,35 @@ bool HandleGetCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; const std::string& variableName = args.back(); // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + if (!list) { status.GetMakefile().AddDefinition(variableName, "NOTFOUND"); return true; } // FIXME: Add policy to make non-existing lists an error like empty lists. - if (varArgsExpanded.empty()) { + if (list->empty()) { status.SetError("GET given empty list"); return false; } - std::string value; - size_t cc; - const char* sep = ""; - size_t nitem = varArgsExpanded.size(); - for (cc = 2; cc < args.size() - 1; cc++) { - int item; - if (!GetIndexArg(args[cc], &item, status.GetMakefile())) { + std::vector<int> indexes; + for (std::size_t cc = 2; cc < args.size() - 1; cc++) { + int index; + if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); return false; } - value += sep; - sep = ";"; - if (item < 0) { - item = static_cast<int>(nitem) + item; - } - if (item < 0 || nitem <= static_cast<size_t>(item)) { - status.SetError(cmStrCat("index: ", item, " out of range (-", nitem, - ", ", nitem - 1, ")")); - return false; - } - value += varArgsExpanded[item]; + indexes.push_back(index); } - status.GetMakefile().AddDefinition(variableName, value); - return true; + try { + auto values = list->get_items(indexes.begin(), indexes.end()); + status.GetMakefile().AddDefinition(variableName, values.to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; + } } bool HandleAppendCommand(std::vector<std::string> const& args, @@ -226,13 +204,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, std::string listString; GetListString(listString, listName, makefile); - // If `listString` or `args` is empty, no need to append `;`, - // then index is going to be `1` and points to the end-of-string ";" - auto const offset = - static_cast<std::string::size_type>(listString.empty() || args.empty()); - listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";"); - - makefile.AddDefinition(listName, listString); + makefile.AddDefinition( + listName, cmList::append(args.begin() + 2, args.end(), listString)); return true; } @@ -252,14 +225,8 @@ bool HandlePrependCommand(std::vector<std::string> const& args, std::string listString; GetListString(listString, listName, makefile); - // If `listString` or `args` is empty, no need to append `;`, - // then `offset` is going to be `1` and points to the end-of-string ";" - auto const offset = - static_cast<std::string::size_type>(listString.empty() || args.empty()); - listString.insert(0, - cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]); - - makefile.AddDefinition(listName, listString); + makefile.AddDefinition( + listName, cmList::prepend(args.begin() + 2, args.end(), listString)); return true; } @@ -272,8 +239,9 @@ bool HandlePopBackCommand(std::vector<std::string> const& args, auto ai = args.cbegin(); ++ai; // Skip subcommand name std::string const& listName = *ai++; - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, makefile)) { + auto list = GetList(listName, makefile); + + if (!list) { // Can't get the list definition... undefine any vars given after. for (; ai != args.cend(); ++ai) { makefile.RemoveDefinition(*ai); @@ -281,16 +249,16 @@ bool HandlePopBackCommand(std::vector<std::string> const& args, return true; } - if (!varArgsExpanded.empty()) { + if (!list->empty()) { if (ai == args.cend()) { // No variables are given... Just remove one element. - varArgsExpanded.pop_back(); + list->pop_back(); } else { // Ok, assign elements to be removed to the given variables - for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) { + for (; !list->empty() && ai != args.cend(); ++ai) { assert(!ai->empty()); - makefile.AddDefinition(*ai, varArgsExpanded.back()); - varArgsExpanded.pop_back(); + makefile.AddDefinition(*ai, list->back()); + list->pop_back(); } // Undefine the rest variables if the list gets empty earlier... for (; ai != args.cend(); ++ai) { @@ -298,7 +266,7 @@ bool HandlePopBackCommand(std::vector<std::string> const& args, } } - makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";")); + makefile.AddDefinition(listName, list->to_string()); } else if (ai != args.cend()) { // The list is empty, but some args were given @@ -320,8 +288,9 @@ bool HandlePopFrontCommand(std::vector<std::string> const& args, auto ai = args.cbegin(); ++ai; // Skip subcommand name std::string const& listName = *ai++; - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, makefile)) { + auto list = GetList(listName, makefile); + + if (!list) { // Can't get the list definition... undefine any vars given after. for (; ai != args.cend(); ++ai) { makefile.RemoveDefinition(*ai); @@ -329,25 +298,25 @@ bool HandlePopFrontCommand(std::vector<std::string> const& args, return true; } - if (!varArgsExpanded.empty()) { + if (!list->empty()) { if (ai == args.cend()) { // No variables are given... Just remove one element. - varArgsExpanded.erase(varArgsExpanded.begin()); + list->pop_front(); } else { // Ok, assign elements to be removed to the given variables - auto vi = varArgsExpanded.begin(); - for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) { + auto vi = list->begin(); + for (; vi != list->end() && ai != args.cend(); ++ai, ++vi) { assert(!ai->empty()); makefile.AddDefinition(*ai, *vi); } - varArgsExpanded.erase(varArgsExpanded.begin(), vi); + list->erase(list->begin(), vi); // Undefine the rest variables if the list gets empty earlier... for (; ai != args.cend(); ++ai) { makefile.RemoveDefinition(*ai); } } - makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";")); + makefile.AddDefinition(listName, list->to_string()); } else if (ai != args.cend()) { // The list is empty, but some args were given @@ -371,21 +340,16 @@ bool HandleFindCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; const std::string& variableName = args.back(); // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { - status.GetMakefile().AddDefinition(variableName, "-1"); - return true; - } + auto list = GetList(listName, status.GetMakefile()); - auto it = std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]); - if (it != varArgsExpanded.end()) { - status.GetMakefile().AddDefinition( - variableName, - std::to_string(std::distance(varArgsExpanded.begin(), it))); + if (!list) { + status.GetMakefile().AddDefinition(variableName, "-1"); return true; } - status.GetMakefile().AddDefinition(variableName, "-1"); + auto index = list->find(args[2]); + status.GetMakefile().AddDefinition( + variableName, index == cmList::npos ? "-1" : std::to_string(index)); return true; } @@ -400,38 +364,24 @@ bool HandleInsertCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - int item; - if (!GetIndexArg(args[2], &item, status.GetMakefile())) { + int index; + if (!GetIndexArg(args[2], &index, status.GetMakefile())) { status.SetError(cmStrCat("index: ", args[2], " is not a valid index")); return false; } - std::vector<std::string> varArgsExpanded; - if ((!GetList(varArgsExpanded, listName, status.GetMakefile()) || - varArgsExpanded.empty()) && - item != 0) { - status.SetError(cmStrCat("index: ", item, " out of range (0, 0)")); - return false; + auto list = GetList(listName, status.GetMakefile()); + if (!list) { + list = cmList{}; } - if (!varArgsExpanded.empty()) { - size_t nitem = varArgsExpanded.size(); - if (item < 0) { - item = static_cast<int>(nitem) + item; - } - if (item < 0 || nitem < static_cast<size_t>(item)) { - status.SetError(cmStrCat("index: ", item, " out of range (-", - varArgsExpanded.size(), ", ", - varArgsExpanded.size(), ")")); - return false; - } + try { + list->insert_items(index, args.begin() + 3, args.end()); + status.GetMakefile().AddDefinition(listName, list->to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; } - - varArgsExpanded.insert(varArgsExpanded.begin() + item, args.begin() + 3, - args.end()); - - std::string value = cmJoin(varArgsExpanded, ";"); - status.GetMakefile().AddDefinition(listName, value); - return true; } bool HandleJoinCommand(std::vector<std::string> const& args, @@ -448,16 +398,14 @@ bool HandleJoinCommand(std::vector<std::string> const& args, const std::string& variableName = args[3]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { status.GetMakefile().AddDefinition(variableName, ""); return true; } - std::string value = - cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue); - - status.GetMakefile().AddDefinition(variableName, value); + status.GetMakefile().AddDefinition(variableName, list->join(glue)); return true; } @@ -472,21 +420,14 @@ bool HandleRemoveItemCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } - std::vector<std::string> remove(args.begin() + 2, args.end()); - std::sort(remove.begin(), remove.end()); - auto remEnd = std::unique(remove.begin(), remove.end()); - auto remBegin = remove.begin(); - - auto argsEnd = - cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd)); - auto argsBegin = varArgsExpanded.cbegin(); - std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";"); - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition( + listName, list->remove_items(args.begin() + 2, args.end()).to_string()); return true; } @@ -501,14 +442,13 @@ bool HandleReverseCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } - std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";"); - - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition(listName, list->reverse().to_string()); return true; } @@ -523,237 +463,17 @@ bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } - auto argsEnd = cmRemoveDuplicates(varArgsExpanded); - auto argsBegin = varArgsExpanded.cbegin(); - std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";"); - - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition(listName, + list->remove_duplicates().to_string()); return true; } -// Helpers for list(TRANSFORM <list> ...) -using transform_type = std::function<std::string(const std::string&)>; - -class transform_error : public std::runtime_error -{ -public: - transform_error(const std::string& error) - : std::runtime_error(error) - { - } -}; - -class TransformSelector -{ -public: - virtual ~TransformSelector() = default; - - std::string Tag; - - virtual bool Validate(std::size_t count = 0) = 0; - - virtual bool InSelection(const std::string&) = 0; - - virtual void Transform(std::vector<std::string>& list, - const transform_type& transform) - { - std::transform(list.begin(), list.end(), list.begin(), transform); - } - -protected: - TransformSelector(std::string&& tag) - : Tag(std::move(tag)) - { - } -}; -class TransformNoSelector : public TransformSelector -{ -public: - TransformNoSelector() - : TransformSelector("NO SELECTOR") - { - } - - bool Validate(std::size_t) override { return true; } - - bool InSelection(const std::string&) override { return true; } -}; -class TransformSelectorRegex : public TransformSelector -{ -public: - TransformSelectorRegex(const std::string& regex) - : TransformSelector("REGEX") - , Regex(regex) - { - } - - bool Validate(std::size_t) override { return this->Regex.is_valid(); } - - bool InSelection(const std::string& value) override - { - return this->Regex.find(value); - } - - cmsys::RegularExpression Regex; -}; -class TransformSelectorIndexes : public TransformSelector -{ -public: - std::vector<int> Indexes; - - bool InSelection(const std::string&) override { return true; } - - void Transform(std::vector<std::string>& list, - const transform_type& transform) override - { - this->Validate(list.size()); - - for (auto index : this->Indexes) { - list[index] = transform(list[index]); - } - } - -protected: - TransformSelectorIndexes(std::string&& tag) - : TransformSelector(std::move(tag)) - { - } - TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes) - : TransformSelector(std::move(tag)) - , Indexes(indexes) - { - } - - int NormalizeIndex(int index, std::size_t count) - { - if (index < 0) { - index = static_cast<int>(count) + index; - } - if (index < 0 || count <= static_cast<std::size_t>(index)) { - throw transform_error(cmStrCat( - "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index, - " out of range (-", count, ", ", count - 1, ").")); - } - return index; - } -}; -class TransformSelectorAt : public TransformSelectorIndexes -{ -public: - TransformSelectorAt(std::vector<int>&& indexes) - : TransformSelectorIndexes("AT", std::move(indexes)) - { - } - - bool Validate(std::size_t count) override - { - decltype(this->Indexes) indexes; - - for (auto index : this->Indexes) { - indexes.push_back(this->NormalizeIndex(index, count)); - } - this->Indexes = std::move(indexes); - - return true; - } -}; -class TransformSelectorFor : public TransformSelectorIndexes -{ -public: - TransformSelectorFor(int start, int stop, int step) - : TransformSelectorIndexes("FOR") - , Start(start) - , Stop(stop) - , Step(step) - { - } - - bool Validate(std::size_t count) override - { - this->Start = this->NormalizeIndex(this->Start, count); - this->Stop = this->NormalizeIndex(this->Stop, count); - - // Does stepping move us further from the end? - if (this->Start > this->Stop) { - throw transform_error( - cmStrCat("sub-command TRANSFORM, selector FOR " - "expects <start> to be no greater than <stop> (", - this->Start, " > ", this->Stop, ")")); - } - - // compute indexes - auto size = (this->Stop - this->Start + 1) / this->Step; - if ((this->Stop - this->Start + 1) % this->Step != 0) { - size += 1; - } - - this->Indexes.resize(size); - auto start = this->Start; - auto step = this->Step; - std::generate(this->Indexes.begin(), this->Indexes.end(), - [&start, step]() -> int { - auto r = start; - start += step; - return r; - }); - - return true; - } - -private: - int Start, Stop, Step; -}; - -class TransformAction -{ -public: - virtual ~TransformAction() = default; - - virtual std::string Transform(const std::string& input) = 0; -}; -class TransformReplace : public TransformAction -{ -public: - TransformReplace(const std::vector<std::string>& arguments, - cmMakefile* makefile) - : ReplaceHelper(arguments[0], arguments[1], makefile) - { - makefile->ClearMatches(); - - if (!this->ReplaceHelper.IsRegularExpressionValid()) { - throw transform_error( - cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile " - "regex \"", - arguments[0], "\".")); - } - if (!this->ReplaceHelper.IsReplaceExpressionValid()) { - throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", - this->ReplaceHelper.GetError(), ".")); - } - } - - std::string Transform(const std::string& input) override - { - // Scan through the input for all matches. - std::string output; - - if (!this->ReplaceHelper.Replace(input, output)) { - throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", - this->ReplaceHelper.GetError(), ".")); - } - - return output; - } - -private: - cmStringReplaceHelper ReplaceHelper; -}; - bool HandleTransformCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -763,119 +483,50 @@ bool HandleTransformCommand(std::vector<std::string> const& args, return false; } - // Structure collecting all elements of the command - struct Command - { - Command(const std::string& listName) - : ListName(listName) - , OutputName(listName) - { - } - - std::string Name; - std::string ListName; - std::vector<std::string> Arguments; - std::unique_ptr<TransformAction> Action; - std::unique_ptr<TransformSelector> Selector; - std::string OutputName; - } command(args[1]); - // Descriptor of action + // Action: enum value identifying action // Arity: number of arguments required for the action - // Transform: lambda function implementing the action struct ActionDescriptor { ActionDescriptor(std::string name) : Name(std::move(name)) { } - ActionDescriptor(std::string name, int arity, transform_type transform) + ActionDescriptor(std::string name, cmList::TransformAction action, + int arity) : Name(std::move(name)) + , Action(action) , Arity(arity) -#if defined(__GNUC__) && __GNUC__ == 6 && defined(__aarch64__) - // std::function move constructor miscompiles on this architecture - , Transform(transform) -#else - , Transform(std::move(transform)) -#endif { } operator const std::string&() const { return this->Name; } std::string Name; + cmList::TransformAction Action; int Arity = 0; - transform_type Transform; }; // Build a set of supported actions. std::set<ActionDescriptor, std::function<bool(const std::string&, const std::string&)>> - descriptors( - [](const std::string& x, const std::string& y) { return x < y; }); - descriptors = { { "APPEND", 1, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return s + command.Arguments[0]; - } - - return s; - } }, - { "PREPEND", 1, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return command.Arguments[0] + s; - } - - return s; - } }, - { "TOUPPER", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmSystemTools::UpperCase(s); - } - - return s; - } }, - { "TOLOWER", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmSystemTools::LowerCase(s); - } - - return s; - } }, - { "STRIP", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmTrimWhitespace(s); - } - - return s; - } }, - { "GENEX_STRIP", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmGeneratorExpression::Preprocess( - s, - cmGeneratorExpression::StripAllGeneratorExpressions); - } - - return s; - } }, - { "REPLACE", 2, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return command.Action->Transform(s); - } - - return s; - } } }; + descriptors{ { { "APPEND", cmList::TransformAction::APPEND, 1 }, + { "PREPEND", cmList::TransformAction::PREPEND, 1 }, + { "TOUPPER", cmList::TransformAction::TOUPPER, 0 }, + { "TOLOWER", cmList::TransformAction::TOLOWER, 0 }, + { "STRIP", cmList::TransformAction::STRIP, 0 }, + { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP, 0 }, + { "REPLACE", cmList::TransformAction::REPLACE, 2 } }, + [](const std::string& x, const std::string& y) { + return x < y; + } }; + const std::string& listName = args[1]; + + // Parse all possible function parameters using size_type = std::vector<std::string>::size_type; size_type index = 2; - // Parse all possible function parameters auto descriptor = descriptors.find(args[index]); if (descriptor == descriptors.end()) { @@ -893,295 +544,184 @@ bool HandleTransformCommand(std::vector<std::string> const& args, return false; } - command.Name = descriptor->Name; + std::vector<std::string> arguments; index += descriptor->Arity; if (descriptor->Arity > 0) { - command.Arguments = + arguments = std::vector<std::string>(args.begin() + 3, args.begin() + index); } - if (command.Name == "REPLACE") { - try { - command.Action = cm::make_unique<TransformReplace>( - command.Arguments, &status.GetMakefile()); - } catch (const transform_error& e) { - status.SetError(e.what()); - return false; - } - } - const std::string REGEX{ "REGEX" }; const std::string AT{ "AT" }; const std::string FOR{ "FOR" }; const std::string OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" }; + std::unique_ptr<cmList::TransformSelector> selector; + std::string outputName = listName; - // handle optional arguments - while (args.size() > index) { - if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && - command.Selector) { - status.SetError( - cmStrCat("sub-command TRANSFORM, selector already specified (", - command.Selector->Tag, ").")); - - return false; - } + try { + // handle optional arguments + while (args.size() > index) { + if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && + selector) { + status.SetError( + cmStrCat("sub-command TRANSFORM, selector already specified (", + selector->GetTag(), ").")); - // REGEX selector - if (args[index] == REGEX) { - if (args.size() == ++index) { - status.SetError("sub-command TRANSFORM, selector REGEX expects " - "'regular expression' argument."); return false; } - command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]); - if (!command.Selector->Validate()) { - status.SetError( - cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile " - "regex \"", - args[index], "\".")); - return false; - } + // REGEX selector + if (args[index] == REGEX) { + if (args.size() == ++index) { + status.SetError("sub-command TRANSFORM, selector REGEX expects " + "'regular expression' argument."); + return false; + } - index += 1; - continue; - } + selector = + cmList::TransformSelector::New<cmList::TransformSelector::REGEX>( + args[index]); - // AT selector - if (args[index] == AT) { - // get all specified indexes - std::vector<int> indexes; - while (args.size() > ++index) { - std::size_t pos; - int value; + index += 1; + continue; + } - try { - value = std::stoi(args[index], &pos); - if (pos != args[index].length()) { + // AT selector + if (args[index] == AT) { + // get all specified indexes + std::vector<cmList::index_type> indexes; + while (args.size() > ++index) { + std::size_t pos; + int value; + + try { + value = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number, stop processing + break; + } + indexes.push_back(value); + } catch (const std::invalid_argument&) { // this is not a number, stop processing break; } - indexes.push_back(value); - } catch (const std::invalid_argument&) { - // this is not a number, stop processing - break; } - } - if (indexes.empty()) { - status.SetError( - "sub-command TRANSFORM, selector AT expects at least one " - "numeric value."); - return false; - } + if (indexes.empty()) { + status.SetError( + "sub-command TRANSFORM, selector AT expects at least one " + "numeric value."); + return false; + } - command.Selector = - cm::make_unique<TransformSelectorAt>(std::move(indexes)); + selector = + cmList::TransformSelector::New<cmList::TransformSelector::AT>( + std::move(indexes)); - continue; - } - - // FOR selector - if (args[index] == FOR) { - if (args.size() <= ++index + 1) { - status.SetError( - "sub-command TRANSFORM, selector FOR expects, at least," - " two arguments."); - return false; + continue; } - int start = 0; - int stop = 0; - int step = 1; - bool valid = true; - try { - std::size_t pos; - - start = std::stoi(args[index], &pos); - if (pos != args[index].length()) { - // this is not a number - valid = false; - } else { - stop = std::stoi(args[++index], &pos); - if (pos != args[index].length()) { - // this is not a number - valid = false; - } + // FOR selector + if (args[index] == FOR) { + if (args.size() <= ++index + 1) { + status.SetError( + "sub-command TRANSFORM, selector FOR expects, at least," + " two arguments."); + return false; } - } catch (const std::invalid_argument&) { - // this is not numbers - valid = false; - } - if (!valid) { - status.SetError("sub-command TRANSFORM, selector FOR expects, " - "at least, two numeric values."); - return false; - } - // try to read a third numeric value for step - if (args.size() > ++index) { + + cmList::index_type start = 0; + cmList::index_type stop = 0; + cmList::index_type step = 1; + bool valid = true; try { std::size_t pos; - step = std::stoi(args[index], &pos); + start = std::stoi(args[index], &pos); if (pos != args[index].length()) { // this is not a number - step = 1; + valid = false; } else { - index += 1; + stop = std::stoi(args[++index], &pos); + if (pos != args[index].length()) { + // this is not a number + valid = false; + } } } catch (const std::invalid_argument&) { - // this is not number, ignore exception + // this is not numbers + valid = false; + } + if (!valid) { + status.SetError("sub-command TRANSFORM, selector FOR expects, " + "at least, two numeric values."); + return false; + } + // try to read a third numeric value for step + if (args.size() > ++index) { + try { + std::size_t pos; + + step = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number + step = 1; + } else { + index += 1; + } + } catch (const std::invalid_argument&) { + // this is not number, ignore exception + } } - } - if (step <= 0) { - status.SetError("sub-command TRANSFORM, selector FOR expects " - "positive numeric value for <step>."); - return false; - } + if (step <= 0) { + status.SetError("sub-command TRANSFORM, selector FOR expects " + "positive numeric value for <step>."); + return false; + } - command.Selector = - cm::make_unique<TransformSelectorFor>(start, stop, step); + selector = + cmList::TransformSelector::New<cmList::TransformSelector::FOR>( + { start, stop, step }); - continue; - } + continue; + } - // output variable - if (args[index] == OUTPUT_VARIABLE) { - if (args.size() == ++index) { - status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " - "expects variable name argument."); - return false; + // output variable + if (args[index] == OUTPUT_VARIABLE) { + if (args.size() == ++index) { + status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " + "expects variable name argument."); + return false; + } + + outputName = args[index++]; + continue; } - command.OutputName = args[index++]; - continue; + status.SetError(cmStrCat("sub-command TRANSFORM, '", + cmJoin(cmMakeRange(args).advance(index), " "), + "': unexpected argument(s).")); + return false; } - status.SetError(cmStrCat("sub-command TRANSFORM, '", - cmJoin(cmMakeRange(args).advance(index), " "), - "': unexpected argument(s).")); - return false; - } - - // expand the list variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, command.ListName, status.GetMakefile())) { - status.GetMakefile().AddDefinition(command.OutputName, ""); - return true; - } + // expand the list variable + auto list = GetList(listName, status.GetMakefile()); - if (!command.Selector) { - // no selector specified, apply transformation to all elements - command.Selector = cm::make_unique<TransformNoSelector>(); - } + if (!list) { + status.GetMakefile().AddDefinition(outputName, ""); + return true; + } - try { - command.Selector->Transform(varArgsExpanded, descriptor->Transform); - } catch (const transform_error& e) { + list->transform(descriptor->Action, arguments, std::move(selector)); + status.GetMakefile().AddDefinition(outputName, list->to_string()); + return true; + } catch (cmList::transform_error& e) { status.SetError(e.what()); return false; } - - status.GetMakefile().AddDefinition(command.OutputName, - cmJoin(varArgsExpanded, ";")); - - return true; } -class cmStringSorter -{ -public: - enum class Order - { - UNINITIALIZED, - ASCENDING, - DESCENDING, - }; - - enum class Compare - { - UNINITIALIZED, - STRING, - FILE_BASENAME, - NATURAL, - }; - enum class CaseSensitivity - { - UNINITIALIZED, - SENSITIVE, - INSENSITIVE, - }; - -protected: - using StringFilter = std::string (*)(const std::string&); - StringFilter GetCompareFilter(Compare compare) - { - return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName - : nullptr; - } - - StringFilter GetCaseFilter(CaseSensitivity sensitivity) - { - return (sensitivity == CaseSensitivity::INSENSITIVE) - ? cmSystemTools::LowerCase - : nullptr; - } - - using ComparisonFunction = - std::function<bool(const std::string&, const std::string&)>; - ComparisonFunction GetComparisonFunction(Compare compare) - { - if (compare == Compare::NATURAL) { - return std::function<bool(const std::string&, const std::string&)>( - [](const std::string& x, const std::string& y) { - return cmSystemTools::strverscmp(x, y) < 0; - }); - } - return std::function<bool(const std::string&, const std::string&)>( - [](const std::string& x, const std::string& y) { return x < y; }); - } - -public: - cmStringSorter(Compare compare, CaseSensitivity caseSensitivity, - Order desc = Order::ASCENDING) - : filters{ this->GetCompareFilter(compare), - this->GetCaseFilter(caseSensitivity) } - , sortMethod(this->GetComparisonFunction(compare)) - , descending(desc == Order::DESCENDING) - { - } - - std::string ApplyFilter(const std::string& argument) - { - std::string result = argument; - for (auto filter : this->filters) { - if (filter != nullptr) { - result = filter(result); - } - } - return result; - } - - bool operator()(const std::string& a, const std::string& b) - { - std::string af = this->ApplyFilter(a); - std::string bf = this->ApplyFilter(b); - bool result; - if (this->descending) { - result = this->sortMethod(bf, af); - } else { - result = this->sortMethod(af, bf); - } - return result; - } - -protected: - StringFilter filters[2] = { nullptr, nullptr }; - ComparisonFunction sortMethod; - bool descending; -}; - bool HandleSortCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -1191,9 +731,8 @@ bool HandleSortCommand(std::vector<std::string> const& args, return false; } - auto sortCompare = cmStringSorter::Compare::UNINITIALIZED; - auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED; - auto sortOrder = cmStringSorter::Order::UNINITIALIZED; + using SortConfig = cmList::SortConfiguration; + SortConfig sortConfig; size_t argumentIndex = 2; const std::string messageHint = "sub-command SORT "; @@ -1201,7 +740,7 @@ bool HandleSortCommand(std::vector<std::string> const& args, while (argumentIndex < args.size()) { std::string const& option = args[argumentIndex++]; if (option == "COMPARE") { - if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) { + if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { std::string error = cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times."); status.SetError(error); @@ -1210,11 +749,11 @@ bool HandleSortCommand(std::vector<std::string> const& args, if (argumentIndex < args.size()) { std::string const& argument = args[argumentIndex++]; if (argument == "STRING") { - sortCompare = cmStringSorter::Compare::STRING; + sortConfig.Compare = SortConfig::CompareMethod::STRING; } else if (argument == "FILE_BASENAME") { - sortCompare = cmStringSorter::Compare::FILE_BASENAME; + sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME; } else if (argument == "NATURAL") { - sortCompare = cmStringSorter::Compare::NATURAL; + sortConfig.Compare = SortConfig::CompareMethod::NATURAL; } else { std::string error = cmStrCat(messageHint, "value \"", argument, "\" for option \"", @@ -1228,8 +767,7 @@ bool HandleSortCommand(std::vector<std::string> const& args, return false; } } else if (option == "CASE") { - if (sortCaseSensitivity != - cmStringSorter::CaseSensitivity::UNINITIALIZED) { + if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) { status.SetError(cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times.")); return false; @@ -1237,9 +775,9 @@ bool HandleSortCommand(std::vector<std::string> const& args, if (argumentIndex < args.size()) { std::string const& argument = args[argumentIndex++]; if (argument == "SENSITIVE") { - sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE; + sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE; } else if (argument == "INSENSITIVE") { - sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE; + sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE; } else { status.SetError(cmStrCat(messageHint, "value \"", argument, "\" for option \"", option, @@ -1253,7 +791,7 @@ bool HandleSortCommand(std::vector<std::string> const& args, } } else if (option == "ORDER") { - if (sortOrder != cmStringSorter::Order::UNINITIALIZED) { + if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) { status.SetError(cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times.")); return false; @@ -1261,9 +799,9 @@ bool HandleSortCommand(std::vector<std::string> const& args, if (argumentIndex < args.size()) { std::string const& argument = args[argumentIndex++]; if (argument == "ASCENDING") { - sortOrder = cmStringSorter::Order::ASCENDING; + sortConfig.Order = SortConfig::OrderMode::ASCENDING; } else if (argument == "DESCENDING") { - sortOrder = cmStringSorter::Order::DESCENDING; + sortConfig.Order = SortConfig::OrderMode::DESCENDING; } else { status.SetError(cmStrCat(messageHint, "value \"", argument, "\" for option \"", option, @@ -1281,35 +819,17 @@ bool HandleSortCommand(std::vector<std::string> const& args, return false; } } - // set Default Values if Option is not given - if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) { - sortCompare = cmStringSorter::Compare::STRING; - } - if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) { - sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE; - } - if (sortOrder == cmStringSorter::Order::UNINITIALIZED) { - sortOrder = cmStringSorter::Order::ASCENDING; - } const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { - return true; - } + auto list = GetList(listName, status.GetMakefile()); - if ((sortCompare == cmStringSorter::Compare::STRING) && - (sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) && - (sortOrder == cmStringSorter::Order::ASCENDING)) { - std::sort(varArgsExpanded.begin(), varArgsExpanded.end()); - } else { - cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder); - std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter); + if (!list) { + return true; } - std::string value = cmJoin(varArgsExpanded, ";"); - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition(listName, + list->sort(sortConfig).to_string()); return true; } @@ -1326,9 +846,9 @@ bool HandleSublistCommand(std::vector<std::string> const& args, const std::string& variableName = args.back(); // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile()) || - varArgsExpanded.empty()) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list || list->empty()) { status.GetMakefile().AddDefinition(variableName, ""); return true; } @@ -1344,11 +864,9 @@ bool HandleSublistCommand(std::vector<std::string> const& args, return false; } - using size_type = decltype(varArgsExpanded)::size_type; - - if (start < 0 || static_cast<size_type>(start) >= varArgsExpanded.size()) { + if (start < 0) { status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ", - varArgsExpanded.size() - 1)); + list->size() - 1)); return false; } if (length < -1) { @@ -1356,15 +874,17 @@ bool HandleSublistCommand(std::vector<std::string> const& args, return false; } - const size_type end = - (length == -1 || - static_cast<size_type>(start + length) > varArgsExpanded.size()) - ? varArgsExpanded.size() - : static_cast<size_type>(start + length); - std::vector<std::string> sublist(varArgsExpanded.begin() + start, - varArgsExpanded.begin() + end); - status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";")); - return true; + using size_type = cmList::size_type; + + try { + auto sublist = list->sublist(static_cast<size_type>(start), + static_cast<size_type>(length)); + status.GetMakefile().AddDefinition(variableName, sublist.to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; + } } bool HandleRemoveAtCommand(std::vector<std::string> const& args, @@ -1378,9 +898,9 @@ bool HandleRemoveAtCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile()) || - varArgsExpanded.empty()) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list || list->empty()) { std::ostringstream str; str << "index: "; for (size_t i = 1; i < args.size(); ++i) { @@ -1395,36 +915,25 @@ bool HandleRemoveAtCommand(std::vector<std::string> const& args, } size_t cc; - std::vector<size_t> removed; - size_t nitem = varArgsExpanded.size(); + std::vector<cmList::index_type> removed; for (cc = 2; cc < args.size(); ++cc) { - int item; - if (!GetIndexArg(args[cc], &item, status.GetMakefile())) { + int index; + if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); return false; } - if (item < 0) { - item = static_cast<int>(nitem) + item; - } - if (item < 0 || nitem <= static_cast<size_t>(item)) { - status.SetError(cmStrCat("index: ", item, " out of range (-", nitem, - ", ", nitem - 1, ")")); - return false; - } - removed.push_back(static_cast<size_t>(item)); + removed.push_back(index); } - std::sort(removed.begin(), removed.end()); - auto remEnd = std::unique(removed.begin(), removed.end()); - auto remBegin = removed.begin(); - - auto argsEnd = - cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd)); - auto argsBegin = varArgsExpanded.cbegin(); - std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";"); - - status.GetMakefile().AddDefinition(listName, value); - return true; + try { + status.GetMakefile().AddDefinition( + listName, + list->remove_items(removed.begin(), removed.end()).to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; + } } bool HandleFilterCommand(std::vector<std::string> const& args, @@ -1447,11 +956,11 @@ bool HandleFilterCommand(std::vector<std::string> const& args, } const std::string& op = args[2]; - bool includeMatches; + cmList::FilterMode filterMode; if (op == "INCLUDE") { - includeMatches = true; + filterMode = cmList::FilterMode::INCLUDE; } else if (op == "EXCLUDE") { - includeMatches = false; + filterMode = cmList::FilterMode::EXCLUDE; } else { status.SetError("sub-command FILTER does not recognize operator " + op); return false; @@ -1459,70 +968,33 @@ bool HandleFilterCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } const std::string& mode = args[3]; - if (mode == "REGEX") { - if (args.size() != 5) { - status.SetError("sub-command FILTER, mode REGEX " - "requires five arguments."); - return false; - } - return FilterRegex(args, includeMatches, listName, varArgsExpanded, - status); - } - - status.SetError("sub-command FILTER does not recognize mode " + mode); - return false; -} - -class MatchesRegex -{ -public: - MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches) - : regex(in_regex) - , includeMatches(in_includeMatches) - { + if (mode != "REGEX") { + status.SetError("sub-command FILTER does not recognize mode " + mode); + return false; } - - bool operator()(const std::string& target) - { - return this->regex.find(target) ^ this->includeMatches; + if (args.size() != 5) { + status.SetError("sub-command FILTER, mode REGEX " + "requires five arguments."); + return false; } - -private: - cmsys::RegularExpression& regex; - const bool includeMatches; -}; - -bool FilterRegex(std::vector<std::string> const& args, bool includeMatches, - std::string const& listName, - std::vector<std::string>& varArgsExpanded, - cmExecutionStatus& status) -{ const std::string& pattern = args[4]; - cmsys::RegularExpression regex(pattern); - if (!regex.is_valid()) { - std::string error = - cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"", - pattern, "\"."); - status.SetError(error); + + try { + status.GetMakefile().AddDefinition( + listName, list->filter(pattern, filterMode).to_string()); + return true; + } catch (std::invalid_argument& e) { + status.SetError(e.what()); return false; } - - auto argsBegin = varArgsExpanded.begin(); - auto argsEnd = varArgsExpanded.end(); - auto newArgsEnd = - std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches)); - - std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";"); - status.GetMakefile().AddDefinition(listName, value); - return true; } - } // namespace bool cmListCommand(std::vector<std::string> const& args, diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index e163edbda7..5dbc283fac 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -21,6 +21,8 @@ #include <cm3p/json/value.h> #include <cm3p/json/writer.h> +#include "cmsys/RegularExpression.hxx" + #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" #include "cmDyndepCollation.h" @@ -1259,6 +1261,7 @@ namespace { cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, const std::string& ppFileName, bool compilePP, bool compilePPWithDefines, + cmValue ppExcludeFlagsRegex, cmNinjaBuild& objBuild, cmNinjaVars& vars, const std::string& objectFileName, cmLocalGenerator* lg) @@ -1287,6 +1290,20 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, // Scanning and compilation generally use the same flags. scanBuild.Variables["FLAGS"] = vars["FLAGS"]; + // Exclude flags not valid during preprocessing. + if (compilePP && !ppExcludeFlagsRegex.IsEmpty()) { + std::string in = std::move(scanBuild.Variables["FLAGS"]); + std::string out; + cmsys::RegularExpression regex(*ppExcludeFlagsRegex); + std::string::size_type pos = 0; + while (regex.find(in.c_str() + pos)) { + out = cmStrCat(out, in.substr(pos, regex.start()), ' '); + pos += regex.end(); + } + out = cmStrCat(out, in.substr(pos)); + scanBuild.Variables["FLAGS"] = std::move(out); + } + if (compilePP && !compilePPWithDefines) { // Move preprocessor definitions to the scan/preprocessor build statement. std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]); @@ -1511,18 +1528,22 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( std::string scanRuleName; std::string ppFileName; + cmValue ppExcludeFlagsRegex; if (compilePP) { scanRuleName = this->LanguagePreprocessAndScanRule(language, config); ppFileName = this->ConvertToNinjaPath( this->GetPreprocessedFilePath(source, config)); + ppExcludeFlagsRegex = this->Makefile->GetDefinition(cmStrCat( + "CMAKE_", language, "_PREPROCESS_SOURCE_EXCLUDE_FLAGS_REGEX")); } else { scanRuleName = this->LanguageScanRule(language, config); ppFileName = cmStrCat(objectFileName, ".ddi.i"); } cmNinjaBuild ppBuild = GetScanBuildStatement( - scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild, - vars, objectFileName, this->LocalGenerator); + scanRuleName, ppFileName, compilePP, compilePPWithDefines, + ppExcludeFlagsRegex, objBuild, vars, objectFileName, + this->LocalGenerator); if (compilePP) { // In case compilation requires flags that are incompatible with diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 37d697fc87..fe883826aa 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -447,6 +447,9 @@ class cmMakefile; 27, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0148, \ "The FindPythonInterp and FindPythonLibs modules are removed.", 3, \ + 27, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0149, \ + "Visual Studio generators select latest Windows SDK by default.", 3, \ 27, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 612d4b4d82..0fc3debc9f 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -30,6 +30,7 @@ set(CMakeLib_TESTS testCMExtMemory.cxx testCMExtAlgorithm.cxx testCMExtEnumSet.cxx + testList.cxx ) if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM) list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx) diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx new file mode 100644 index 0000000000..7294be0b31 --- /dev/null +++ b/Tests/CMakeLib/testList.cxx @@ -0,0 +1,995 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <iostream> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include <cm/string_view> + +#include "cmList.h" + +namespace { + +void checkResult(bool success) +{ + if (!success) { + std::cout << " => failed"; + } + std::cout << std::endl; +} + +bool testConstructors() +{ + std::cout << "testConstructors()"; + + bool result = true; + + { + cmList list; + if (!list.empty() || list != cmList{}) { + result = false; + } + } + { + cmList list{ "aa;bb" }; + if (list.size() != 2 || list.to_string() != "aa;bb") { + result = false; + } + } + { + cmList list1{ "aa", "bb" }; + cmList list2("aa;bb"); + + if (list1.size() != 2 || list2.size() != 2 || list1 != list2) { + result = false; + } + if (list1.to_string() != "aa;bb") { + result = false; + } + if (list1.to_string() != list2.to_string()) { + result = false; + } + } + { + std::vector<std::string> v{ "aa", "bb", "cc" }; + cmList list(v.begin(), v.end()); + if (list.size() != 3 || list.to_string() != "aa;bb;cc") { + result = false; + } + } + { + std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" }; + cmList list1(values.begin(), values.end()); + cmList list2(values.begin(), values.end(), cmList::ExpandElements::No); + + if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + } + { + std::vector<std::string> values{ "aa;bb;;cc", "", "dd;ee" }; + cmList list1(values.begin(), values.end(), cmList::ExpandElements::No, + cmList::EmptyElements::No); + cmList list2(values.begin(), values.end(), cmList::ExpandElements::No, + cmList::EmptyElements::Yes); + cmList list3(values.begin(), values.end(), cmList::ExpandElements::Yes, + cmList::EmptyElements::No); + cmList list4(values.begin(), values.end(), cmList::ExpandElements::Yes, + cmList::EmptyElements::Yes); + + if (list1.size() != 2 || list1.to_string() != "aa;bb;;cc;dd;ee") { + result = false; + } + if (list2.size() != 3 || list2.to_string() != "aa;bb;;cc;;dd;ee") { + result = false; + } + if (list3.size() != 5 || list3.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (list4.size() != 7 || list4.to_string() != "aa;bb;;cc;;dd;ee") { + result = false; + } + } + { + std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" }; + cmList list1(values); + cmList list2(values, cmList::ExpandElements::No); + + if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + } + { + std::vector<std::string> values{ "aa", "bb", "cc", "dd", "ee" }; + cmList list(std::move(values)); + + if (list.size() != 5 || list.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (!values.empty()) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testAssign() +{ + std::cout << "testAssign()"; + + bool result = true; + + { + cmList list1{ "aa", "bb" }; + cmList list2{ "cc", "dd" }; + + list2 = list1; + if (list1.size() != 2 || list2.size() != 2 || list1 != list2) { + result = false; + } + if (list1.to_string() != "aa;bb") { + result = false; + } + if (list1.to_string() != list2.to_string()) { + result = false; + } + } + { + cmList list1{ "aa", "bb" }; + cmList list2{ "cc", "dd" }; + + list2 = std::move(list1); + if (!list1.empty() || list2.size() != 2) { + result = false; + } + if (list2.to_string() != "aa;bb") { + result = false; + } + } + { + std::vector<std::string> v{ "aa", "bb" }; + cmList list{ "cc", "dd" }; + + list = std::move(v); + if (!v.empty() || list.size() != 2) { + result = false; + } + if (list.to_string() != "aa;bb") { + result = false; + } + } + { + cmList list{ "cc", "dd" }; + + list = "aa;bb"; + if (list.size() != 2) { + result = false; + } + if (list.to_string() != "aa;bb") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testConversions() +{ + std::cout << "testConversions()"; + + bool result = true; + + { + cmList list("a;b;c"); + std::string s = list.to_string(); + + if (s != "a;b;c") { + result = false; + } + } + { + cmList list("a;b;c"); + std::vector<std::string> v = list; + + if (list.size() != 3 || v.size() != 3) { + result = false; + } + } + { + cmList list("a;b;c"); + std::vector<std::string> v = std::move(list); + + // Microsoft compiler is not able to handle correctly the move semantics + // so the initial list is not moved, so do not check its size... + if (v.size() != 3) { + result = false; + } + } + { + cmList list("a;b;c"); + std::vector<std::string> v; + + // compiler is not able to select the cmList conversion operator + // and the std::vector assignment operator using the move semantics + // v = std::move(list); + v = std::move(list.data()); + + if (!list.empty() || v.size() != 3) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testAccess() +{ + std::cout << "testAccess()"; + + bool result = true; + + { + cmList list{ "a", "b", "c" }; + if (list.at(1) != "b") { + result = false; + } + } + { + cmList list{ "a", "b", "c" }; + if (list.at(-3) != "a") { + result = false; + } + } + { + try { + cmList list{ "a", "b", "c" }; + if (list.at(4) != "a") { + result = false; + } + } catch (std::out_of_range&) { + } + } + { + try { + cmList list{ "a", "b", "c" }; + if (list.at(-4) != "a") { + result = false; + } + } catch (std::out_of_range&) { + } + } + { + cmList list{ "a", "b", "c", "d", "e" }; + auto sublist = list.sublist(list.begin() + 1, list.begin() + 3); + if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e" }; + auto sublist = list.sublist(1, 2); + if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) { + result = false; + } + + sublist = list.sublist(1, cmList::npos); + if (sublist.size() != 4 || sublist != cmList{ "b", "c", "d", "e" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + auto sublist = list.get_items({ 1, 3, 5 }); + if (sublist.size() != 3 || sublist != cmList{ "b", "d", "f" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + auto sublist = list.get_items({ 1, -3, 5, -3 }); + if (sublist.size() != 4 || sublist != cmList{ "b", "d", "f", "d" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + try { + if (list.get_items({ 1, -3, 5, -3, 10 }).size() != 5) { + result = false; + } + } catch (std::out_of_range&) { + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + + if (list.find("b") != 1) { + result = false; + } + if (list.find("x") != cmList::npos) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testModifiers() +{ + std::cout << "testModifiers()"; + + bool result = true; + + { + cmList list{ "1;2;3;4;5" }; + + auto it = list.insert(list.begin() + 2, "6;7;8"); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + + auto it = + list.insert(list.begin() + 2, "6;7;8", cmList::ExpandElements::No); + if (list.size() != 6 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6;7;8") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + cmList v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + cmList v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, std::move(v)); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + + if (!v.empty()) { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;7", "8" }; + + auto it = list.insert(list.begin() + 2, v, cmList::ExpandElements::No); + if (list.size() != 7 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6;7") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;;7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;;7", "8" }; + + auto it = list.insert(list.begin() + 2, v, cmList::EmptyElements::Yes); + if (list.size() != 9 || list.to_string() != "1;2;6;;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, std::move(v)); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + + if (!v.empty()) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testRemoveItems() +{ + std::cout << "testRemoveItems()"; + + bool result = true; + + { + cmList list("a;b;c;d;e;f;g;h"); + + list.remove_items({ 1, 3, 5 }); + + if (list.size() != 5 || list.to_string() != "a;c;e;g;h") { + result = false; + } + } + { + cmList list("a;b;c;b;a;d;e;f"); + + list.remove_items({ "a", "b", "h" }); + + if (list.size() != 4 || list.to_string() != "c;d;e;f") { + result = false; + } + } + { + cmList list("a;b;c;d;e;f;g;h"); + std::vector<cmList::index_type> remove{ 1, 3, 5 }; + + list.remove_items(remove.begin(), remove.end()); + + if (list.size() != 5 || list.to_string() != "a;c;e;g;h") { + result = false; + } + } + { + cmList list("a;b;c;b;a;d;e;f"); + std::vector<std::string> remove{ "b", "a", "h" }; + + list.remove_items(remove.begin(), remove.end()); + + if (list.size() != 4 || list.to_string() != "c;d;e;f") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testRemoveDuplicates() +{ + std::cout << "testRemoveDuplicates()"; + + bool result = true; + + { + cmList list("b;c;b;a;a;c;b;a;c;b"); + + list.remove_duplicates(); + + if (list.size() != 3 || list.to_string() != "b;c;a") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testFilter() +{ + std::cout << "testFilter()"; + + bool result = true; + + { + cmList list{ "AA", "Aa", "aA" }; + + list.filter("^A", cmList::FilterMode::INCLUDE); + if (list.size() != 2 || list.to_string() != "AA;Aa") { + result = false; + } + } + { + cmList list{ "AA", "Aa", "aA" }; + + list.filter("^A", cmList::FilterMode::EXCLUDE); + if (list.size() != 1 || list.to_string() != "aA") { + result = false; + } + } + { + cmList list{ "AA", "Aa", "aA" }; + + try { + list.filter("^(A", cmList::FilterMode::EXCLUDE); + if (list.size() != 1) { + result = false; + } + } catch (const std::invalid_argument&) { + } + } + + checkResult(result); + + return result; +} + +bool testReverse() +{ + std::cout << "testReverse()"; + + bool result = true; + + { + cmList list{ "a", "b", "c" }; + if (list.reverse().to_string() != "c;b;a") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testSort() +{ + std::cout << "testSort()"; + + bool result = true; + + using SortConfiguration = cmList::SortConfiguration; + + { + cmList list{ "A", "D", "C", "B", "A" }; + + list.sort(); + if (list.to_string() != "A;A;B;C;D") { + result = false; + } + + list.sort({ SortConfiguration::OrderMode::DESCENDING, + SortConfiguration::CompareMethod::DEFAULT, + SortConfiguration::CaseSensitivity::DEFAULT }); + if (list.to_string() != "D;C;B;A;A") { + result = false; + } + } + { + SortConfiguration sortCfg; + cmList list{ "1.0", "1.1", "2.5", "10.2" }; + + list.sort(sortCfg); + if (list.to_string() != "1.0;1.1;10.2;2.5") { + result = false; + } + + sortCfg.Compare = SortConfiguration::CompareMethod::NATURAL; + list.sort(sortCfg); + if (list.to_string() != "1.0;1.1;2.5;10.2") { + result = false; + } + + sortCfg.Order = SortConfiguration::OrderMode::DESCENDING; + list.sort(sortCfg); + if (list.to_string() != "10.2;2.5;1.1;1.0") { + result = false; + } + } + { + SortConfiguration sortCfg; + cmList list{ "/zz/bb.cc", "/xx/yy/dd.cc", "/aa/cc.aa" }; + + list.sort(sortCfg); + if (list.to_string() != "/aa/cc.aa;/xx/yy/dd.cc;/zz/bb.cc") { + result = false; + } + + sortCfg.Compare = SortConfiguration::CompareMethod::FILE_BASENAME; + if (list.sort(sortCfg).to_string() != "/zz/bb.cc;/aa/cc.aa;/xx/yy/dd.cc") { + result = false; + } + } + { + SortConfiguration sortCfg; + cmList list{ "c/B", "a/c", "B/a" }; + + if (list.sort().to_string() != "B/a;a/c;c/B") { + result = false; + } + + sortCfg.Case = SortConfiguration::CaseSensitivity::INSENSITIVE; + if (list.sort(sortCfg).to_string() != "a/c;B/a;c/B") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testTransform() +{ + std::cout << "testTransform()"; + + bool result = true; + + using AT = cmList::TransformSelector::AT; + using FOR = cmList::TransformSelector::FOR; + using REGEX = cmList::TransformSelector::REGEX; + + { + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::APPEND, "-X"); + if (list.to_string() != "AA-X;BB-X;CC-X;DD-X;EE-X") { + result = false; + } + } + { + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::PREPEND, "X-"); + if (list.to_string() != "X-AA;X-BB;X-CC;X-DD;X-EE") { + result = false; + } + } + { + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER); + if (list.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + } + { + cmList list({ "aa", "bb", "cc", "dd", "ee" }); + + list.transform(cmList::TransformAction::TOUPPER); + if (list.to_string() != "AA;BB;CC;DD;EE") { + result = false; + } + } + { + cmList list({ " AA", "BB ", " CC ", "DD", "EE" }); + + list.transform(cmList::TransformAction::STRIP); + if (list.to_string() != "AA;BB;CC;DD;EE") { + result = false; + } + } + { + cmList list({ "$<CONFIG>AA", "BB$<OR>", "C$<AND>C", "$<OR>DD$<AND>", + "$<>E$<>E$<>" }); + + list.transform(cmList::TransformAction::GENEX_STRIP); + if (list.to_string() != "AA;BB;CC;DD;EE") { + result = false; + } + } + { + cmList list({ "ABC", "BBCB", "BCCCBC", "BCBCDD", "EBCBCEBC" }); + + list.transform(cmList::TransformAction::REPLACE, "^BC|BC$", "X"); + if (list.to_string() != "AX;BBCB;XCCX;XXDD;EBCBCEX") { + result = false; + } + } + { + auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, 4 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector)); + if (list.to_string() != "AA;bb;cc;DD;ee") { + result = false; + } + } + { + auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, -1 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector)); + if (list.to_string() != "AA;bb;cc;DD;ee") { + result = false; + } + } + { + auto forSelector = cmList::TransformSelector::New<FOR>({ 1, 3 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector)); + if (list.to_string() != "AA;bb;cc;dd;EE") { + result = false; + } + } + { + auto forSelector = cmList::TransformSelector::New<FOR>({ 0, 4, 2 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector)); + if (list.to_string() != "aa;BB;cc;DD;ee") { + result = false; + } + } + { + auto regexSelector = cmList::TransformSelector::New<REGEX>("^(A|D|E)"); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(regexSelector)); + if (list.to_string() != "aa;BB;CC;dd;ee") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testStaticModifiers() +{ + std::cout << "testStaticModifiers()"; + + bool result = true; + + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::assign("d;e", v); + + if (v.size() != 2 || v[0] != "d" || v[1] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::append("d;;e", v); + + if (v.size() != 5 || v[3] != "d" || v[4] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::append("d;;e", v, cmList::EmptyElements::Yes); + + if (v.size() != 6 || v[3] != "d" || !v[4].empty() || v[5] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::prepend("d;e", v); + + if (v.size() != 5 || v[0] != "d" || v[1] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::prepend("d;;e", v, cmList::EmptyElements::Yes); + + if (v.size() != 6 || v[0] != "d" || !v[1].empty() || v[2] != "e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::append("d;e", list); + + if (list != "a;b;c;d;e") { + result = false; + } + } + { + std::string list; + cmList::append("d;e", list); + + if (list != "d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::append("", list); + + if (list != "a;b;c;") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v{ "d", "e" }; + cmList::append(v.begin(), v.end(), list); + + if (list != "a;b;c;d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v; + cmList::append(v.begin(), v.end(), list); + + if (list != "a;b;c") { + result = false; + } + } + { + std::string list; + std::vector<std::string> v{ "d", "e" }; + cmList::append(v.begin(), v.end(), list); + + if (list != "d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::prepend("d;e", list); + + if (list != "d;e;a;b;c") { + result = false; + } + } + { + std::string list; + cmList::prepend("d;e", list); + + if (list != "d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::prepend("", list); + + if (list != ";a;b;c") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v{ "d", "e" }; + cmList::prepend(v.begin(), v.end(), list); + + if (list != "d;e;a;b;c") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v; + cmList::prepend(v.begin(), v.end(), list); + + if (list != "a;b;c") { + result = false; + } + } + { + std::string list; + std::vector<std::string> v{ "d", "e" }; + cmList::prepend(v.begin(), v.end(), list); + + if (list != "d;e") { + result = false; + } + } + + checkResult(result); + + return result; +} +} + +int testList(int /*unused*/, char* /*unused*/[]) +{ + int result = 0; + + if (!testConstructors()) { + result = 1; + } + if (!testAssign()) { + result = 1; + } + if (!testConversions()) { + result = 1; + } + if (!testAccess()) { + result = 1; + } + if (!testModifiers()) { + result = 1; + } + if (!testRemoveItems()) { + result = 1; + } + if (!testRemoveDuplicates()) { + result = 1; + } + if (!testFilter()) { + result = 1; + } + if (!testReverse()) { + result = 1; + } + if (!testSort()) { + result = 1; + } + if (!testTransform()) { + result = 1; + } + if (!testStaticModifiers()) { + result = 1; + } + + return result; +} diff --git a/Tests/RunCMake/Autogen/MocPredefs.cmake b/Tests/RunCMake/Autogen/MocPredefs.cmake index 7a89bb5471..8307e04cce 100644 --- a/Tests/RunCMake/Autogen/MocPredefs.cmake +++ b/Tests/RunCMake/Autogen/MocPredefs.cmake @@ -1,4 +1,3 @@ -cmake_policy(SET CMP0129 NEW) enable_language(CXX) find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core) diff --git a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt index 1e7b127571..cb4ea46439 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt @@ -7,7 +7,7 @@ Call Stack \(most recent call first\): CMP0121-ERANGE-WARN.cmake:2 \(include\) CMakeLists.txt:3 \(include\) This warning is for project developers. Use -Wno-dev to suppress it. - +.* CMake Error at CMP0121-ERANGE-Common.cmake:3 \(list\): list index: (-2147483643|2147483647) out of range \(-5, 4\) Call Stack \(most recent call first\): diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 080740c7b2..f05f784d7d 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -15,6 +15,9 @@ macro(add_RunCMake_test test) else() set(Test_Dir ${test}) endif() + if(CMAKE_C_COMPILER_ID STREQUAL "LCC") + list(APPEND TEST_ARGS -DRunCMake_TEST_LCC=1) + endif() add_test(NAME RunCMake.${test} COMMAND ${CMAKE_CMAKE_COMMAND} -DCMAKE_MODULE_PATH=${CMAKE_CURRENT_SOURCE_DIR} -DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig} @@ -41,6 +44,11 @@ function(add_RunCMake_test_group test types) file(REMOVE_RECURSE "${TEST_CONFIG_DIR}") file(MAKE_DIRECTORY "${TEST_CONFIG_DIR}") + set(TEST_ARGS "") + if(CMAKE_C_COMPILER_ID STREQUAL "LCC") + list(APPEND TEST_ARGS -DRunCMake_TEST_LCC=1) + endif() + foreach(type IN LISTS types) # generate prerequirements config file in cmake as ctest doesn't have as # much system information so it is easier to set programs and environment @@ -68,6 +76,7 @@ function(add_RunCMake_test_group test types) -DRunCMake_MAKE_PROGRAM=${CMake_TEST_EXPLICIT_MAKE_PROGRAM} -DRunCMake_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${test} -DRunCMake_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/${type}/${test} + ${TEST_ARGS} -Dconfig_file=${TEST_CONFIG_DIR}/${type}_config.cmake -P "${CMAKE_CURRENT_SOURCE_DIR}/${test}/RunCMakeTest.cmake" ) diff --git a/Tests/RunCMake/CMakePresets/validate_schema.py b/Tests/RunCMake/CMakePresets/validate_schema.py index b2a67fc4b9..836147aab2 100644 --- a/Tests/RunCMake/CMakePresets/validate_schema.py +++ b/Tests/RunCMake/CMakePresets/validate_schema.py @@ -4,13 +4,13 @@ import os.path import sys -with open(sys.argv[1], "rb") as f: - contents = json.loads(f.read().decode("utf-8-sig")) +with open(sys.argv[1], "r", encoding="utf-8-sig") as f: + contents = json.load(f) schema_file = os.path.join( os.path.dirname(__file__), "..", "..", "..", "Help", "manual", "presets", "schema.json") -with open(schema_file) as f: +with open(schema_file, "r", encoding="utf-8") as f: schema = json.load(f) jsonschema.validate(contents, schema) diff --git a/Tests/RunCMake/CPack/CMakeLists.txt b/Tests/RunCMake/CPack/CMakeLists.txt index f210474d82..18a673e7d7 100644 --- a/Tests/RunCMake/CPack/CMakeLists.txt +++ b/Tests/RunCMake/CPack/CMakeLists.txt @@ -1,9 +1,5 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -if(POLICY CMP0129) - cmake_policy(SET CMP0129 NEW) -endif() - set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "") project(${RunCMake_TEST} CXX) diff --git a/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt b/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt index 30cb9aebf0..67eae6576c 100644 --- a/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt +++ b/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt @@ -1,9 +1,3 @@ cmake_minimum_required(VERSION 3.13) - -if(POLICY CMP0129) - cmake_policy(SET CMP0129 NEW) -endif() - project(${RunCMake_TEST} LANGUAGES NONE) - include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt new file mode 100644 index 0000000000..b2098bd425 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt @@ -0,0 +1,11 @@ +^CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Visual Studio [^ +]+ + + given platform specification + + Test Platform,nocomma + + that contains a field after the first ',' with no '='\.$ diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake new file mode 100644 index 0000000000..2fc38e5c59 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt new file mode 100644 index 0000000000..654f9201c8 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt @@ -0,0 +1,11 @@ +^CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Visual Studio [^ +]+ + + given platform specification + + Test Platform,unknown= + + that contains invalid field 'unknown='\.$ diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake new file mode 100644 index 0000000000..2fc38e5c59 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt new file mode 100644 index 0000000000..1b7804da36 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt @@ -0,0 +1,11 @@ +^CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Visual Studio [^ +]+ + + given platform specification with empty + + version= + + field\.$ diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake new file mode 100644 index 0000000000..2fc38e5c59 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt new file mode 100644 index 0000000000..d82eb0bbac --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt @@ -0,0 +1,11 @@ +^CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Visual Studio [^ +]+ + + given platform specification with + + version=1\.2\.3\.4 + + field, but no Windows SDK with that version was found\.$ diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake new file mode 100644 index 0000000000..2fc38e5c59 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt new file mode 100644 index 0000000000..d3c62e3629 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt @@ -0,0 +1,19 @@ +^CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Visual Studio [^ +]+ + + given platform specification (containing a + + version=8\.1 + + field\. The version field is not supported when targeting + + Windows 8\.1( + + with the Windows 8\.1 SDK installed\.)?|with + + version=8\.1 + + field, but no Windows SDK with that version was found\.)$ diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake new file mode 100644 index 0000000000..2fc38e5c59 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake index a7519c373c..233eb0a2f0 100644 --- a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake @@ -26,3 +26,86 @@ else() run_cmake(BadPlatformToolchain) unset(RunCMake_TEST_OPTIONS) endif() + +if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio (1[4567])( 20[0-9][0-9])?$") + unset(ENV{WindowsSDKVersion}) + + set(RunCMake_GENERATOR_PLATFORM "Test Platform,nocomma") + run_cmake(BadFieldNoComma) + set(RunCMake_GENERATOR_PLATFORM "Test Platform,unknown=") + run_cmake(BadFieldUnknown) + set(RunCMake_GENERATOR_PLATFORM "version=") + run_cmake(BadVersionEmpty) + set(RunCMake_GENERATOR_PLATFORM "version=1.2.3.4") + run_cmake(BadVersionMissing) + set(RunCMake_GENERATOR_PLATFORM "version=8.1") + run_cmake_with_options(BadVersionPlatform -DCMAKE_SYSTEM_VERSION=8.1) + + if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio (1[45]) ") + set(expect_version "10.0") + set(RunCMake_GENERATOR_PLATFORM "version=${expect_version}") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${expect_version}") + run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0) + unset(RunCMake_GENERATOR_PLATFORM) + endif() + + set(kits "") + cmake_host_system_information(RESULT kitsRoot10 + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Microsoft/Windows Kits/Installed Roots" + VALUE "KitsRoot10" + VIEW 64_32 + ERROR_VARIABLE kitsRoot10Error + ) + if(NOT kitsRoot10Error AND IS_DIRECTORY "${kitsRoot10}/include") + cmake_path(SET kitsInclude "${kitsRoot10}/include") + file(GLOB kits RELATIVE "${kitsInclude}" "${kitsInclude}/*/um/windows.h") + list(TRANSFORM kits REPLACE "/.*" "") + endif() + if(kits) + message(STATUS "Available Kits: ${kits}") + if(RunCMake_GENERATOR MATCHES "^Visual Studio 14 ") + set(kitMax 10.0.14393.0) + else() + set(kitMax "") + endif() + if(kitMax) + set(kitsIn "${kits}") + set(kits "") + foreach(kit IN LISTS kitsIn) + if(kit VERSION_LESS_EQUAL "${kitMax}") + list(APPEND kits "${kit}") + else() + message(STATUS "Excluding Kit ${kit} > ${kitMax}") + endif() + endforeach() + endif() + elseif(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 14 ") + message(FATAL_ERROR "Could not find any Windows SDKs to drive test cases.") + endif() + + if(kits) + foreach(expect_version IN LISTS kits) + set(RunCMake_GENERATOR_PLATFORM "version=${expect_version}") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${expect_version}") + run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0) + unset(RunCMake_GENERATOR_PLATFORM) + endforeach() + foreach(expect_version IN LISTS kits) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMP0149-OLD-${expect_version}") + run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=${expect_version} -DCMAKE_POLICY_DEFAULT_CMP0149=OLD) + endforeach() + if(kits MATCHES "(^|;)([0-9.]+)$") + set(expect_version "${CMAKE_MATCH_2}") + foreach(test_version IN LISTS kits) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMP0149-NEW-${test_version}") + run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=${test_version} -DCMAKE_POLICY_DEFAULT_CMP0149=NEW) + endforeach() + endif() + foreach(expect_version IN LISTS kits) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-WindowsSDKVersion-${expect_version}") + set(ENV{WindowsSDKVersion} "${expect_version}\\") + run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_POLICY_DEFAULT_CMP0149=NEW) + unset(ENV{WindowsSDKVersion}) + endforeach() + endif() +endif() diff --git a/Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake b/Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake new file mode 100644 index 0000000000..6c3c8e542b --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake @@ -0,0 +1,9 @@ +if(actual_stdout MATCHES "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION='([^']+)'") + set(actual_version "${CMAKE_MATCH_1}") + if(NOT "${actual_version}" STREQUAL "${expect_version}") + set(RunCMake_TEST_FAILED "Actual SDK version '${actual_version}' did not match expected '${expect_version}'") + return() + endif() +else() + set(RunCMake_TEST_FAILED "No CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION found in output.") +endif() diff --git a/Tests/RunCMake/GeneratorPlatform/VersionExists.cmake b/Tests/RunCMake/GeneratorPlatform/VersionExists.cmake new file mode 100644 index 0000000000..5c30e2b616 --- /dev/null +++ b/Tests/RunCMake/GeneratorPlatform/VersionExists.cmake @@ -0,0 +1,5 @@ +cmake_policy(GET CMP0149 cmp0149) +message(STATUS "CMP0149='${cmp0149}'") +message(STATUS "CMAKE_SYSTEM_VERSION='${CMAKE_SYSTEM_VERSION}'") +message(STATUS "ENV{WindowsSDKVersion}='$ENV{WindowsSDKVersion}'") +message(STATUS "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION='${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}'") diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index e1c923d621..8e85f6cf46 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -11,8 +11,12 @@ foreach( endforeach() function(run_cmake test) - if(DEFINED ENV{RunCMake_TEST_FILTER} AND NOT test MATCHES "$ENV{RunCMake_TEST_FILTER}") - return() + if(DEFINED ENV{RunCMake_TEST_FILTER}) + set(test_and_variant "${test}${RunCMake_TEST_VARIANT_DESCRIPTION}") + if(NOT test_and_variant MATCHES "$ENV{RunCMake_TEST_FILTER}") + return() + endif() + unset(test_and_variant) endif() set(top_src "${RunCMake_SOURCE_DIR}") @@ -94,7 +98,7 @@ function(run_cmake test) if(APPLE) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0025=NEW) endif() - if(NOT RunCMake_TEST_NO_CMP0129 AND CMAKE_C_COMPILER_ID STREQUAL "LCC") + if(RunCMake_TEST_LCC AND NOT RunCMake_TEST_NO_CMP0129) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0129=NEW) endif() if(RunCMake_MAKE_PROGRAM) @@ -174,6 +178,7 @@ function(run_cmake test) "|icp?c: remark #10441: The Intel\\(R\\) C\\+\\+ Compiler Classic \\(ICC\\) is deprecated" "|[^\n]*install_name_tool: warning: changes being made to the file will invalidate the code signature in:" + "|[^\n]*xcodebuild[^\n]*DVTCoreDeviceEnabledState: DVTCoreDeviceEnabledState_Disabled set via user default" "|[^\n]*xcodebuild[^\n]*DVTPlugInManager" "|[^\n]*xcodebuild[^\n]*DVTSDK: Warning: SDK path collision for path" "|[^\n]*xcodebuild[^\n]*Requested but did not find extension point with identifier" diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py index 2ccaf9a7fe..ffef5b36a2 100644 --- a/Utilities/Sphinx/cmake.py +++ b/Utilities/Sphinx/cmake.py @@ -242,12 +242,13 @@ class CMakeTransform(Transform): The cmake --help-*-list commands also depend on this convention. Return the title or False if the document file does not exist. """ - env = self.document.settings.env + settings = self.document.settings + env = settings.env title = self.titles.get(docname) if title is None: fname = os.path.join(env.srcdir, docname+'.rst') try: - f = open(fname, 'r') + f = open(fname, 'r', encoding=settings.input_encoding) except IOError: title = False else: diff --git a/Utilities/Sphinx/create_identifiers.py b/Utilities/Sphinx/create_identifiers.py index 0ff39a0c2a..61dd819b42 100755 --- a/Utilities/Sphinx/create_identifiers.py +++ b/Utilities/Sphinx/create_identifiers.py @@ -6,12 +6,12 @@ if len(sys.argv) != 2: sys.exit(-1) name = sys.argv[1] + "/CMake.qhp" -f = open(name, "rb") +f = open(name, "r", encoding="utf-8") if not f: sys.exit(-1) -lines = f.read().decode("utf-8").splitlines() +lines = f.read().splitlines() if not lines: sys.exit(-1) @@ -47,5 +47,5 @@ for line in lines: line = part1 + prefix + "id=\"" + domain_object_type + "/" + domain_object + "\" " + part2 newlines.append(line + "\n") -f = open(name, "wb") -f.writelines(map(lambda line: line.encode("utf-8"), newlines)) +f = open(name, "w", encoding="utf-8") +f.writelines(newlines) @@ -307,6 +307,7 @@ CMAKE_CXX_SOURCES="\ cmBuildCommand \ cmCMakeLanguageCommand \ cmCMakeMinimumRequired \ + cmList \ cmCMakePath \ cmCMakePathCommand \ cmCMakePolicyCommand \ |