summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Auxiliary/vim/syntax/cmake.vim1
-rw-r--r--CMakeLists.txt4
-rw-r--r--Help/command/export.rst9
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst12
-rw-r--r--Help/manual/cmake-packages.7.rst17
-rw-r--r--Help/manual/cmake-policies.7.rst2
-rw-r--r--Help/manual/cmake-properties.7.rst1
-rw-r--r--Help/manual/cmake-variables.7.rst1
-rw-r--r--Help/manual/cmake.1.rst22
-rw-r--r--Help/policy/CMP0090.rst27
-rw-r--r--Help/prop_tgt/XCODE_SCHEME_DEBUG_AS_ROOT.rst7
-rw-r--r--Help/release/dev/cmake-short-target-option.rst6
-rw-r--r--Help/release/dev/export-package-default-off.rst6
-rw-r--r--Help/release/dev/pkg-config-linker-flags.rst5
-rw-r--r--Help/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY.rst9
-rw-r--r--Help/variable/CMAKE_EXPORT_PACKAGE_REGISTRY.rst15
-rw-r--r--Help/variable/CMAKE_XCODE_GENERATE_SCHEME.rst1
-rw-r--r--Modules/FindPkgConfig.cmake6
-rw-r--r--Modules/FindPython.cmake7
-rw-r--r--Modules/FindPython/Support.cmake16
-rw-r--r--Modules/FindPython2.cmake7
-rw-r--r--Modules/FindPython3.cmake7
-rw-r--r--Source/CMakeLists.txt4
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/QtDialog/CMakeLists.txt3
-rw-r--r--Source/cmDepends.cxx34
-rw-r--r--Source/cmDependsC.cxx29
-rw-r--r--Source/cmExportCommand.cxx22
-rw-r--r--Source/cmFileCommand.cxx1082
-rw-r--r--Source/cmFileCopier.cxx660
-rw-r--r--Source/cmFileCopier.h120
-rw-r--r--Source/cmFileInstaller.cxx350
-rw-r--r--Source/cmFileInstaller.h55
-rw-r--r--Source/cmFindProgramCommand.cxx2
-rw-r--r--Source/cmGeneratorExpressionNode.cxx50
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx2
-rw-r--r--Source/cmGlobalVisualStudioVersionedGenerator.cxx27
-rw-r--r--Source/cmLocalUnixMakefileGenerator3.cxx58
-rw-r--r--Source/cmLocalUnixMakefileGenerator3.h3
-rw-r--r--Source/cmNinjaTargetGenerator.cxx2
-rw-r--r--Source/cmPolicies.h3
-rw-r--r--Source/cmXCodeScheme.cxx5
-rw-r--r--Source/cmakemain.cxx16
-rw-r--r--Source/cmcmd.cxx4
-rw-r--r--Tests/CMakeLists.txt9
-rw-r--r--Tests/CudaOnly/WithDefs/CMakeLists.txt2
-rw-r--r--Tests/CudaOnly/WithDefs/main.notcu8
-rw-r--r--Tests/FindPackageTest/CMakeLists.txt20
-rw-r--r--Tests/RunCMake/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/CommandLine/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake21
-rw-r--r--Utilities/Release/WiX/WIX.template.in2
-rwxr-xr-xbootstrap2
53 files changed, 1569 insertions, 1221 deletions
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index 7e029de..9610410 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -358,6 +358,7 @@ syn keyword cmakeProperty contained
\ XCODE_SCHEME_ADDRESS_SANITIZER
\ XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN
\ XCODE_SCHEME_ARGUMENTS
+ \ XCODE_SCHEME_DEBUG_AS_ROOT
\ XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
\ XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
\ XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c4ab2ca..fc033ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -551,6 +551,10 @@ macro (CMAKE_BUILD_UTILITIES)
message(FATAL_ERROR
"CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!")
endif()
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+ set_property(TARGET JsonCpp::JsonCpp APPEND PROPERTY
+ INTERFACE_COMPILE_OPTIONS -Wno-deprecated-declarations)
+ endif()
set(CMAKE_JSONCPP_LIBRARIES JsonCpp::JsonCpp)
else()
set(CMAKE_JSONCPP_LIBRARIES cmjsoncpp)
diff --git a/Help/command/export.rst b/Help/command/export.rst
index b255ee8..ffd60e1 100644
--- a/Help/command/export.rst
+++ b/Help/command/export.rst
@@ -62,8 +62,13 @@ registry that this command creates works only in conjunction with a
package configuration file (``<PackageName>Config.cmake``) that works with the
build tree. In some cases, for example for packaging and for system
wide installations, it is not desirable to write the user package
-registry. If the :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` variable
-is enabled, the ``export(PACKAGE)`` command will do nothing.
+registry.
+
+By default the ``export(PACKAGE)`` command does nothing (see policy
+:policy:`CMP0090`) because populating the user package registry has effects
+outside the source and build trees. Set the
+:variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable to add build directories to
+the CMake user package registry.
.. code-block:: cmake
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 614358a..8cb0fe7 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -121,6 +121,9 @@ Variable Queries
``$<CXX_COMPILER_ID:compiler_id>``
``1`` if the CMake-id of the CXX compiler matches ``compiler_id``,
otherwise ``0``.
+``$<CUDA_COMPILER_ID:compiler_id>``
+ ``1`` if the CMake-id of the CUDA compiler matches ``compiler_id``,
+ otherwise ``0``.
See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
``$<Fortran_COMPILER_ID:compiler_id>``
``1`` if the CMake-id of the Fortran compiler matches ``compiler_id``,
@@ -132,6 +135,9 @@ Variable Queries
``$<CXX_COMPILER_VERSION:version>``
``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``.
See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+``$<CUDA_COMPILER_VERSION:version>``
+ ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``.
+ See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
``$<Fortran_COMPILER_VERSION:version>``
``1`` if the version of the Fortran compiler matches ``version``, otherwise ``0``.
See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
@@ -346,6 +352,9 @@ Variable Queries
``$<CXX_COMPILER_ID>``
The CMake-id of the CXX compiler used.
See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+``$<CUDA_COMPILER_ID>``
+ The CMake-id of the CUDA compiler used.
+ See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
``$<Fortran_COMPILER_ID>``
The CMake-id of the Fortran compiler used.
See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
@@ -355,6 +364,9 @@ Variable Queries
``$<CXX_COMPILER_VERSION>``
The version of the CXX compiler used.
See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+``$<CUDA_COMPILER_VERSION>``
+ The version of the CUDA compiler used.
+ See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
``$<Fortran_COMPILER_VERSION>``
The version of the Fortran compiler used.
See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
diff --git a/Help/manual/cmake-packages.7.rst b/Help/manual/cmake-packages.7.rst
index 876ca84..bbe742e 100644
--- a/Help/manual/cmake-packages.7.rst
+++ b/Help/manual/cmake-packages.7.rst
@@ -647,12 +647,17 @@ Disabling the Package Registry
In some cases using the Package Registries is not desirable. CMake
allows one to disable them using the following variables:
- * :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` disables the
- :command:`export(PACKAGE)` command.
- * :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY` disables the
- User Package Registry in all the :command:`find_package` calls.
- * :variable:`CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY` disables
- the System Package Registry in all the :command:`find_package` calls.
+* The :command:`export(PACKAGE)` command does not populate the user
+ package registry when :policy:`CMP0090` is set to ``NEW`` unless the
+ :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable explicitly enables it.
+ When :policy:`CMP0090` is *not* set to ``NEW`` then
+ :command:`export(PACKAGE)` populates the user package registry unless
+ the :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` variable explicitly
+ disables it.
+* :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY` disables the
+ User Package Registry in all the :command:`find_package` calls.
+* :variable:`CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY` disables
+ the System Package Registry in all the :command:`find_package` calls.
Package Registry Example
------------------------
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 9470d6c..e89ea3da 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.15
.. toctree::
:maxdepth: 1
+ CMP0090: export(PACKAGE) does not populate package registry by default. </policy/CMP0090>
CMP0089: Compiler id for IBM Clang-based XL compilers is now XLClang. </policy/CMP0089>
Policies Introduced by CMake 3.14
@@ -73,6 +74,7 @@ Policies Introduced by CMake 3.14
CMP0083: Add PIE options when linking executable. </policy/CMP0083>
CMP0082: Install rules from add_subdirectory() are interleaved with those in caller. </policy/CMP0082>
+
Policies Introduced by CMake 3.13
=================================
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 16c3460..fce5584 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -353,6 +353,7 @@ Properties on Targets
/prop_tgt/XCODE_SCHEME_ADDRESS_SANITIZER
/prop_tgt/XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN
/prop_tgt/XCODE_SCHEME_ARGUMENTS
+ /prop_tgt/XCODE_SCHEME_DEBUG_AS_ROOT
/prop_tgt/XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
/prop_tgt/XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
/prop_tgt/XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 6bb30cb..48d7550 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -159,6 +159,7 @@ Variables that Change Behavior
/variable/CMAKE_ERROR_DEPRECATED
/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
/variable/CMAKE_EXPORT_COMPILE_COMMANDS
+ /variable/CMAKE_EXPORT_PACKAGE_REGISTRY
/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY
/variable/CMAKE_FIND_APPBUNDLE
/variable/CMAKE_FIND_FRAMEWORK
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index e9a08b5..a3c7834 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -269,14 +269,14 @@ following options:
``--build <dir>``
Project binary directory to be built. This is required and must be first.
-``-j [<jobs>], --parallel [<jobs>]``
+``--parallel [<jobs>], -j [<jobs>]``
The maximum number of concurrent processes to use when building.
If ``<jobs>`` is omitted the native build tool's default number is used.
The :envvar:`CMAKE_BUILD_PARALLEL_LEVEL` environment variable, if set,
specifies a default parallel level when this option is not given.
-``--target <tgt>...``
+``--target <tgt>..., -t <tgt>...``
Build ``<tgt>`` instead of default targets. May be specified multiple times.
``--config <cfg>``
@@ -289,7 +289,7 @@ following options:
``--use-stderr``
Ignored. Behavior is default in CMake >= 3.0.
-``-v, --verbose``
+``--verbose, -v``
Enable verbose output - if supported - including the build commands to be
executed.
@@ -390,16 +390,20 @@ Available commands are:
Copy files to ``<destination>`` (either file or directory).
If multiple files are specified, the ``<destination>`` must be
directory and it must exist. Wildcards are not supported.
+ ``copy`` does follow symlinks. That means it does not copy symlinks,
+ but the files or directories it point to.
``copy_directory <dir>... <destination>``
Copy directories to ``<destination>`` directory.
If ``<destination>`` directory does not exist it will be created.
+ ``copy_directory`` does follow symlinks.
``copy_if_different <file>... <destination>``
Copy files to ``<destination>`` (either file or directory) if
they have changed.
If multiple files are specified, the ``<destination>`` must be
directory and it must exist.
+ ``copy_if_different`` does follow symlinks.
``echo [<string>...]``
Displays arguments as text.
@@ -459,13 +463,16 @@ Available commands are:
exist, the command returns a non-zero exit code, but no message
is logged. The ``-f`` option changes the behavior to return a
zero exit code (i.e. success) in such situations instead.
+ ``remove`` does not follow symlinks. That means it remove only symlinks
+ and not files it point to.
``remove_directory <dir>``
Remove a directory and its contents. If a directory does
not exist it will be silently ignored.
``rename <oldname> <newname>``
- Rename a file or directory (on one volume).
+ Rename a file or directory (on one volume). If file with the ``<newname>`` name
+ already exists, then it will be silently replaced.
``server``
Launch :manual:`cmake-server(7)` mode.
@@ -494,10 +501,11 @@ Available commands are:
``time <command> [<args>...]``
Run command and display elapsed time.
-``touch <file>``
- Touch a file.
+``touch <file>...``
+ Creates ``<file>`` if file do not exist.
+ If ``<file>`` exists, it is changing ``<file>`` access and modification times.
-``touch_nocreate <file>``
+``touch_nocreate <file>...``
Touch a file if it exists but do not create it. If a file does
not exist it will be silently ignored.
diff --git a/Help/policy/CMP0090.rst b/Help/policy/CMP0090.rst
new file mode 100644
index 0000000..720c17c
--- /dev/null
+++ b/Help/policy/CMP0090.rst
@@ -0,0 +1,27 @@
+CMP0090
+-------
+
+:command:`export(PACKAGE)` does not populate package registry by default.
+
+In CMake 3.14 and below the :command:`export(PACKAGE)` command populated the
+user package registry by default and users needed to set the
+:variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` to disable it, e.g. in automated
+build and packaging environments. Since the user package registry is stored
+outside the build tree, this side effect should not be enabled by default.
+Therefore CMake 3.15 and above prefer that :command:`export(PACKAGE)` does
+nothing unless an explicit :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable
+is set to enable it. This policy provides compatibility with projects that
+have not been updated.
+
+The ``OLD`` behavior for this policy is for :command:`export(PACKAGE)` command
+to populate the user package registry unless
+:variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` is enabled.
+The ``NEW`` behavior is for :command:`export(PACKAGE)` command to do nothing
+unless the :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` is enabled.
+
+This policy was introduced in CMake version 3.15. Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike most 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/XCODE_SCHEME_DEBUG_AS_ROOT.rst b/Help/prop_tgt/XCODE_SCHEME_DEBUG_AS_ROOT.rst
new file mode 100644
index 0000000..a53f836
--- /dev/null
+++ b/Help/prop_tgt/XCODE_SCHEME_DEBUG_AS_ROOT.rst
@@ -0,0 +1,7 @@
+XCODE_SCHEME_DEBUG_AS_ROOT
+--------------------------
+
+Whether to debug the target as 'root'.
+
+Please refer to the :variable:`CMAKE_XCODE_GENERATE_SCHEME` variable
+documentation to see all Xcode schema related properties.
diff --git a/Help/release/dev/cmake-short-target-option.rst b/Help/release/dev/cmake-short-target-option.rst
new file mode 100644
index 0000000..5eac042
--- /dev/null
+++ b/Help/release/dev/cmake-short-target-option.rst
@@ -0,0 +1,6 @@
+cmake-short-target-option
+----------------------------
+
+* The :manual:`cmake(1)` ``--target`` parameter gained shorter
+ version ``-t``, e.g. ``cmake --build . -t Library1 Library2`` is
+ equivalant to ``cmake --build . --target Library1 Library2``.
diff --git a/Help/release/dev/export-package-default-off.rst b/Help/release/dev/export-package-default-off.rst
new file mode 100644
index 0000000..0868c82
--- /dev/null
+++ b/Help/release/dev/export-package-default-off.rst
@@ -0,0 +1,6 @@
+export-package-default-off
+--------------------------
+
+* The :command:`export(PACKAGE)` command now does nothing unless
+ enabled via :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY`.
+ See policy :policy:`CMP0090`.
diff --git a/Help/release/dev/pkg-config-linker-flags.rst b/Help/release/dev/pkg-config-linker-flags.rst
new file mode 100644
index 0000000..85c13be
--- /dev/null
+++ b/Help/release/dev/pkg-config-linker-flags.rst
@@ -0,0 +1,5 @@
+pkg-config-linker-flags
+-----------------------
+
+* The :module:`FindPkgConfig` now populates :prop_tgt:`INTERFACE_LINK_OPTIONS`
+ property of imported targets with other (non-library) linker flags.
diff --git a/Help/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY.rst b/Help/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY.rst
index ee109ba..768ed64 100644
--- a/Help/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY.rst
+++ b/Help/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY.rst
@@ -1,11 +1,16 @@
CMAKE_EXPORT_NO_PACKAGE_REGISTRY
--------------------------------
-Disable the :command:`export(PACKAGE)` command.
+Disable the :command:`export(PACKAGE)` command when :policy:`CMP0090`
+is not set to ``NEW``.
In some cases, for example for packaging and for system wide
installations, it is not desirable to write the user package registry.
-If the :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` variable is enabled,
+If the ``CMAKE_EXPORT_NO_PACKAGE_REGISTRY`` variable is enabled,
the :command:`export(PACKAGE)` command will do nothing.
+If :policy:`CMP0090` is set to ``NEW`` this variable does nothing, and the
+:variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable controls the behavior
+instead.
+
See also :ref:`Disabling the Package Registry`.
diff --git a/Help/variable/CMAKE_EXPORT_PACKAGE_REGISTRY.rst b/Help/variable/CMAKE_EXPORT_PACKAGE_REGISTRY.rst
new file mode 100644
index 0000000..3476a19
--- /dev/null
+++ b/Help/variable/CMAKE_EXPORT_PACKAGE_REGISTRY.rst
@@ -0,0 +1,15 @@
+CMAKE_EXPORT_PACKAGE_REGISTRY
+-----------------------------
+
+Enables the :command:`export(PACKAGE)` command when :policy:`CMP0090`
+is set to ``NEW``.
+
+The :command:`export(PACKAGE)` command does nothing by default. In some cases
+it is desirable to write to the user package registry, so the
+``CMAKE_EXPORT_PACKAGE_REGISTRY`` variable may be set to enable it.
+
+If :policy:`CMP0090` is *not* set to ``NEW`` this variable does nothing, and
+the :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` variable controls the behavior
+instead.
+
+See also :ref:`Disabling the Package Registry`.
diff --git a/Help/variable/CMAKE_XCODE_GENERATE_SCHEME.rst b/Help/variable/CMAKE_XCODE_GENERATE_SCHEME.rst
index 7466784..707c6a0 100644
--- a/Help/variable/CMAKE_XCODE_GENERATE_SCHEME.rst
+++ b/Help/variable/CMAKE_XCODE_GENERATE_SCHEME.rst
@@ -30,5 +30,6 @@ The following target properties will be applied on the
"Info" and "Arguments" tab:
- :prop_tgt:`XCODE_SCHEME_ARGUMENTS`
+- :prop_tgt:`XCODE_SCHEME_DEBUG_AS_ROOT`
- :prop_tgt:`XCODE_SCHEME_ENVIRONMENT`
- :prop_tgt:`XCODE_SCHEME_EXECUTABLE`
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index e192426..cf0ae09 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -242,7 +242,7 @@ endfunction()
function(_pkg_create_imp_target _prefix _imp_target_global)
# only create the target if it is linkable, i.e. no executables
if (NOT TARGET PkgConfig::${_prefix}
- AND ( ${_prefix}_INCLUDE_DIRS OR ${_prefix}_LINK_LIBRARIES OR ${_prefix}_CFLAGS_OTHER ))
+ AND ( ${_prefix}_INCLUDE_DIRS OR ${_prefix}_LINK_LIBRARIES OR ${_prefix}_LDFLAGS_OTHER OR ${_prefix}_CFLAGS_OTHER ))
if(${_imp_target_global})
set(_global_opt "GLOBAL")
else()
@@ -258,6 +258,10 @@ function(_pkg_create_imp_target _prefix _imp_target_global)
set_property(TARGET PkgConfig::${_prefix} PROPERTY
INTERFACE_LINK_LIBRARIES "${${_prefix}_LINK_LIBRARIES}")
endif()
+ if(${_prefix}_LDFLAGS_OTHER)
+ set_property(TARGET PkgConfig::${_prefix} PROPERTY
+ INTERFACE_LINK_OPTIONS "${${_prefix}_LDFLAGS_OTHER}")
+ endif()
if(${_prefix}_CFLAGS_OTHER)
set_property(TARGET PkgConfig::${_prefix} PROPERTY
INTERFACE_COMPILE_OPTIONS "${${_prefix}_CFLAGS_OTHER}")
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake
index f014916..1c134e2 100644
--- a/Modules/FindPython.cmake
+++ b/Modules/FindPython.cmake
@@ -28,6 +28,13 @@ is searched.
To manage concurrent versions 3 and 2 of Python, use :module:`FindPython3` and
:module:`FindPython2` modules rather than this one.
+.. note::
+
+ If components ``Interpreter`` and ``Development`` are both specified, this
+ module search only for interpreter with same platform architecture as the one
+ defined by ``CMake`` configuration. This contraint does not apply if only
+ ``Interpreter`` component is specified.
+
Imported Targets
^^^^^^^^^^^^^^^^
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index ef8272c..1236bf8 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -341,14 +341,14 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
NAMES_PER_DIR
HINTS ${_${_PYTHON_PREFIX}_HINTS}
- PATHS [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
- [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
+ PATHS [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
+ [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
- [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
+ [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
@@ -402,14 +402,14 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
python
${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
NAMES_PER_DIR
- PATHS [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
- [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
+ PATHS [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
+ [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
- [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
+ [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
@@ -816,14 +816,14 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
_python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION})
set (_${_PYTHON_PREFIX}_REGISTRY_PATHS
- [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
+ [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
- [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]
+ [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath])
diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake
index 0bb7b28..b9c0b6b 100644
--- a/Modules/FindPython2.cmake
+++ b/Modules/FindPython2.cmake
@@ -29,6 +29,13 @@ concurrently with :module:`FindPython3` module to use both Python versions.
The :module:`FindPython` module can be used if Python version does not matter
for you.
+.. note::
+
+ If components ``Interpreter`` and ``Development`` are both specified, this
+ module search only for interpreter with same platform architecture as the one
+ defined by ``CMake`` configuration. This contraint does not apply if only
+ ``Interpreter`` component is specified.
+
Imported Targets
^^^^^^^^^^^^^^^^
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake
index b3dfff3..c2f3384 100644
--- a/Modules/FindPython3.cmake
+++ b/Modules/FindPython3.cmake
@@ -29,6 +29,13 @@ concurrently with :module:`FindPython2` module to use both Python versions.
The :module:`FindPython` module can be used if Python version does not matter
for you.
+.. note::
+
+ If components ``Interpreter`` and ``Development`` are both specified, this
+ module search only for interpreter with same platform architecture as the one
+ defined by ``CMake`` configuration. This contraint does not apply if only
+ ``Interpreter`` component is specified.
+
Imported Targets
^^^^^^^^^^^^^^^^
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 2db276d..696826f 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -224,6 +224,10 @@ set(SRCS
cmFileAPICodemodel.h
cmFileAPICMakeFiles.cxx
cmFileAPICMakeFiles.h
+ cmFileCopier.cxx
+ cmFileCopier.h
+ cmFileInstaller.cxx
+ cmFileInstaller.h
cmFileLock.cxx
cmFileLock.h
cmFileLockPool.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index fe45963..c935ef6 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,5 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 14)
-set(CMake_VERSION_PATCH 20190312)
+set(CMake_VERSION_PATCH 20190318)
#set(CMake_VERSION_RC 1)
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt
index 9ce0323..cb89d19 100644
--- a/Source/QtDialog/CMakeLists.txt
+++ b/Source/QtDialog/CMakeLists.txt
@@ -2,9 +2,6 @@
# file Copyright.txt or https://cmake.org/licensing for details.
project(QtDialog)
-if(POLICY CMP0020)
- cmake_policy(SET CMP0020 NEW) # Drop when CMake >= 2.8.11 required
-endif()
CMake_OPTIONAL_COMPONENT(cmake-gui)
find_package(Qt5Widgets QUIET)
if (Qt5Widgets_FOUND)
diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx
index 7d1d316..c128b02 100644
--- a/Source/cmDepends.cxx
+++ b/Source/cmDepends.cxx
@@ -29,27 +29,27 @@ cmDepends::~cmDepends()
bool cmDepends::Write(std::ostream& makeDepends, std::ostream& internalDepends)
{
- // Lookup the set of sources to scan.
- std::string srcLang = "CMAKE_DEPENDS_CHECK_";
- srcLang += this->Language;
- cmMakefile* mf = this->LocalGenerator->GetMakefile();
- std::string const& srcStr = mf->GetSafeDefinition(srcLang);
- std::vector<std::string> pairs;
- cmSystemTools::ExpandListArgument(srcStr, pairs);
-
std::map<std::string, std::set<std::string>> dependencies;
- for (std::vector<std::string>::iterator si = pairs.begin();
- si != pairs.end();) {
- // Get the source and object file.
- std::string const& src = *si++;
- if (si == pairs.end()) {
- break;
+ {
+ // Lookup the set of sources to scan.
+ std::vector<std::string> pairs;
+ {
+ std::string const srcLang = "CMAKE_DEPENDS_CHECK_" + this->Language;
+ cmMakefile* mf = this->LocalGenerator->GetMakefile();
+ cmSystemTools::ExpandListArgument(mf->GetSafeDefinition(srcLang), pairs);
+ }
+ for (std::vector<std::string>::iterator si = pairs.begin();
+ si != pairs.end();) {
+ // Get the source and object file.
+ std::string const& src = *si++;
+ if (si == pairs.end()) {
+ break;
+ }
+ std::string const& obj = *si++;
+ dependencies[obj].insert(src);
}
- std::string const& obj = *si++;
- dependencies[obj].insert(src);
}
for (auto const& d : dependencies) {
-
// Write the dependencies for this pair.
if (!this->WriteDependencies(d.second, d.first, makeDepends,
internalDepends)) {
diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx
index f5c106c..a85f5ee 100644
--- a/Source/cmDependsC.cxx
+++ b/Source/cmDependsC.cxx
@@ -6,7 +6,6 @@
#include <utility>
#include "cmAlgorithms.h"
-#include "cmFileTimeComparison.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
@@ -123,12 +122,6 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources,
}
std::set<std::string> scanned;
-
- // Use reserve to allocate enough memory for tempPathStr
- // so that during the loops no memory is allocated or freed
- std::string tempPathStr;
- tempPathStr.reserve(4 * 1024);
-
while (!this->Unscanned.empty()) {
// Get the next file to scan.
UnscannedEntry current = this->Unscanned.front();
@@ -147,22 +140,21 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources,
// the source containing the include statement.
fullName = current.QuotedLocation;
} else {
- std::map<std::string, std::string>::iterator headerLocationIt =
+ auto headerLocationIt =
this->HeaderLocationCache.find(current.FileName);
if (headerLocationIt != this->HeaderLocationCache.end()) {
fullName = headerLocationIt->second;
} else {
- for (std::string const& i : this->IncludePath) {
+ for (std::string const& iPath : this->IncludePath) {
// Construct the name of the file as if it were in the current
// include directory. Avoid using a leading "./".
-
- tempPathStr =
- cmSystemTools::CollapseCombinedPath(i, current.FileName);
+ std::string tmpPath =
+ cmSystemTools::CollapseCombinedPath(iPath, current.FileName);
// Look for the file in this location.
- if (cmSystemTools::FileExists(tempPathStr, true)) {
- fullName = tempPathStr;
- HeaderLocationCache[current.FileName] = fullName;
+ if (cmSystemTools::FileExists(tmpPath, true)) {
+ fullName = tmpPath;
+ this->HeaderLocationCache[current.FileName] = std::move(tmpPath);
break;
}
}
@@ -183,8 +175,7 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources,
scanned.insert(fullName);
// Check whether this file is already in the cache
- std::map<std::string, cmIncludeLines*>::iterator fileIt =
- this->FileCache.find(fullName);
+ auto fileIt = this->FileCache.find(fullName);
if (fileIt != this->FileCache.end()) {
fileIt->second->Used = true;
dependencies.insert(fullName);
@@ -266,8 +257,8 @@ void cmDependsC::ReadCacheFile()
if (!haveFileName) {
haveFileName = true;
int newer = 0;
- cmFileTimeComparison comp;
- bool res = comp.FileTimeCompare(this->CacheFileName, line, &newer);
+ bool res =
+ cmSystemTools::FileTimeCompare(this->CacheFileName, line, &newer);
if (res && newer == 1) // cache is newer than the parsed file
{
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 722831a..c25e1f4 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -13,6 +13,7 @@
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmPolicies.h"
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
@@ -243,10 +244,23 @@ bool cmExportCommand::HandlePackage(std::vector<std::string> const& args)
return false;
}
- // If the CMAKE_EXPORT_NO_PACKAGE_REGISTRY variable is set the command
- // export(PACKAGE) does nothing.
- if (this->Makefile->IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) {
- return true;
+ // CMP0090 decides both the default and what variable changes it.
+ switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0090)) {
+ case cmPolicies::WARN:
+ case cmPolicies::OLD:
+ // Default is to export, but can be disabled.
+ if (this->Makefile->IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) {
+ return true;
+ }
+ break;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ case cmPolicies::NEW:
+ // Default is to not export, but can be enabled.
+ if (!this->Makefile->IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) {
+ return true;
+ }
+ break;
}
// We store the current build directory in the registry as a value
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index a2018c3..d2bc851 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -3,7 +3,6 @@
#include "cmFileCommand.h"
#include "cm_kwiml.h"
-#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/RegularExpression.hxx"
@@ -16,20 +15,18 @@
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <utility>
#include <vector>
#include "cmAlgorithms.h"
#include "cmCommandArgumentsHelper.h"
#include "cmCryptoHash.h"
-#include "cmFSPermissions.h"
+#include "cmFileCopier.h"
+#include "cmFileInstaller.h"
#include "cmFileLockPool.h"
-#include "cmFileTimeComparison.h"
#include "cmGeneratorExpression.h"
#include "cmGlobalGenerator.h"
#include "cmHexFileConverter.h"
-#include "cmInstallType.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
@@ -56,8 +53,6 @@
class cmSystemToolsFileTime;
-using namespace cmFSPermissions;
-
#if defined(_WIN32)
// libcurl doesn't support file:// urls for unicode filenames on Windows.
// Convert string from UTF-8 to ACP if this is a file:// URL.
@@ -1058,1085 +1053,12 @@ bool cmFileCommand::HandleDifferentCommand(
return true;
}
-// File installation helper class.
-struct cmFileCopier
-{
- cmFileCopier(cmFileCommand* command, const char* name = "COPY")
- : FileCommand(command)
- , Makefile(command->GetMakefile())
- , Name(name)
- , Always(false)
- , MatchlessFiles(true)
- , FilePermissions(0)
- , DirPermissions(0)
- , CurrentMatchRule(nullptr)
- , UseGivenPermissionsFile(false)
- , UseGivenPermissionsDir(false)
- , UseSourcePermissions(true)
- , Doing(DoingNone)
- {
- }
- virtual ~cmFileCopier() = default;
-
- bool Run(std::vector<std::string> const& args);
-
-protected:
- cmFileCommand* FileCommand;
- cmMakefile* Makefile;
- const char* Name;
- bool Always;
- cmFileTimeComparison FileTimes;
-
- // Whether to install a file not matching any expression.
- bool MatchlessFiles;
-
- // Permissions for files and directories installed by this object.
- mode_t FilePermissions;
- mode_t DirPermissions;
-
- // Properties set by pattern and regex match rules.
- struct MatchProperties
- {
- bool Exclude = false;
- mode_t Permissions = 0;
- };
- struct MatchRule
- {
- cmsys::RegularExpression Regex;
- MatchProperties Properties;
- std::string RegexString;
- MatchRule(std::string const& regex)
- : Regex(regex)
- , RegexString(regex)
- {
- }
- };
- std::vector<MatchRule> MatchRules;
-
- // Get the properties from rules matching this input file.
- MatchProperties CollectMatchProperties(const std::string& file)
- {
-// Match rules are case-insensitive on some platforms.
-#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
- const std::string file_to_match = cmSystemTools::LowerCase(file);
-#else
- const std::string& file_to_match = file;
-#endif
-
- // Collect properties from all matching rules.
- bool matched = false;
- MatchProperties result;
- for (MatchRule& mr : this->MatchRules) {
- if (mr.Regex.find(file_to_match)) {
- matched = true;
- result.Exclude |= mr.Properties.Exclude;
- result.Permissions |= mr.Properties.Permissions;
- }
- }
- if (!matched && !this->MatchlessFiles) {
- result.Exclude = !cmSystemTools::FileIsDirectory(file);
- }
- return result;
- }
-
- bool SetPermissions(const std::string& toFile, mode_t permissions)
- {
- if (permissions) {
-#ifdef WIN32
- if (Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
- // Store the mode in an NTFS alternate stream.
- std::string mode_t_adt_filename = toFile + ":cmake_mode_t";
-
- // Writing to an NTFS alternate stream changes the modification
- // time, so we need to save and restore its original value.
- cmSystemToolsFileTime* file_time_orig = cmSystemTools::FileTimeNew();
- cmSystemTools::FileTimeGet(toFile, file_time_orig);
-
- cmsys::ofstream permissionStream(mode_t_adt_filename.c_str());
-
- if (permissionStream) {
- permissionStream << std::oct << permissions << std::endl;
- }
-
- permissionStream.close();
-
- cmSystemTools::FileTimeSet(toFile, file_time_orig);
-
- cmSystemTools::FileTimeDelete(file_time_orig);
- }
-#endif
-
- if (!cmSystemTools::SetPermissions(toFile, permissions)) {
- std::ostringstream e;
- e << this->Name << " cannot set permissions on \"" << toFile << "\"";
- this->FileCommand->SetError(e.str());
- return false;
- }
- }
- return true;
- }
-
- // Translate an argument to a permissions bit.
- bool CheckPermissions(std::string const& arg, mode_t& permissions)
- {
- if (!cmFSPermissions::stringToModeT(arg, permissions)) {
- std::ostringstream e;
- e << this->Name << " given invalid permission \"" << arg << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
- return true;
- }
-
- bool InstallSymlink(const std::string& fromFile, const std::string& toFile);
- bool InstallFile(const std::string& fromFile, const std::string& toFile,
- MatchProperties match_properties);
- bool InstallDirectory(const std::string& source,
- const std::string& destination,
- MatchProperties match_properties);
- virtual bool Install(const std::string& fromFile, const std::string& toFile);
- virtual std::string const& ToName(std::string const& fromName)
- {
- return fromName;
- }
-
- enum Type
- {
- TypeFile,
- TypeDir,
- TypeLink
- };
- virtual void ReportCopy(const std::string&, Type, bool) {}
- virtual bool ReportMissing(const std::string& fromFile)
- {
- // The input file does not exist and installation is not optional.
- std::ostringstream e;
- e << this->Name << " cannot find \"" << fromFile << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- MatchRule* CurrentMatchRule;
- bool UseGivenPermissionsFile;
- bool UseGivenPermissionsDir;
- bool UseSourcePermissions;
- std::string Destination;
- std::string FilesFromDir;
- std::vector<std::string> Files;
- int Doing;
-
- virtual bool Parse(std::vector<std::string> const& args);
- enum
- {
- DoingNone,
- DoingError,
- DoingDestination,
- DoingFilesFromDir,
- DoingFiles,
- DoingPattern,
- DoingRegex,
- DoingPermissionsFile,
- DoingPermissionsDir,
- DoingPermissionsMatch,
- DoingLast1
- };
- virtual bool CheckKeyword(std::string const& arg);
- virtual bool CheckValue(std::string const& arg);
-
- void NotBeforeMatch(std::string const& arg)
- {
- std::ostringstream e;
- e << "option " << arg << " may not appear before PATTERN or REGEX.";
- this->FileCommand->SetError(e.str());
- this->Doing = DoingError;
- }
- void NotAfterMatch(std::string const& arg)
- {
- std::ostringstream e;
- e << "option " << arg << " may not appear after PATTERN or REGEX.";
- this->FileCommand->SetError(e.str());
- this->Doing = DoingError;
- }
- virtual void DefaultFilePermissions()
- {
- // Use read/write permissions.
- this->FilePermissions = 0;
- this->FilePermissions |= mode_owner_read;
- this->FilePermissions |= mode_owner_write;
- this->FilePermissions |= mode_group_read;
- this->FilePermissions |= mode_world_read;
- }
- virtual void DefaultDirectoryPermissions()
- {
- // Use read/write/executable permissions.
- this->DirPermissions = 0;
- this->DirPermissions |= mode_owner_read;
- this->DirPermissions |= mode_owner_write;
- this->DirPermissions |= mode_owner_execute;
- this->DirPermissions |= mode_group_read;
- this->DirPermissions |= mode_group_execute;
- this->DirPermissions |= mode_world_read;
- this->DirPermissions |= mode_world_execute;
- }
-
- bool GetDefaultDirectoryPermissions(mode_t** mode)
- {
- // check if default dir creation permissions were set
- const char* default_dir_install_permissions =
- this->Makefile->GetDefinition(
- "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
- if (default_dir_install_permissions && *default_dir_install_permissions) {
- std::vector<std::string> items;
- cmSystemTools::ExpandListArgument(default_dir_install_permissions,
- items);
- for (const auto& arg : items) {
- if (!this->CheckPermissions(arg, **mode)) {
- std::ostringstream e;
- e << this->FileCommand->GetError()
- << " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
- "variable.";
- this->FileCommand->SetError(e.str());
- return false;
- }
- }
- } else {
- *mode = nullptr;
- }
-
- return true;
- }
-};
-
-bool cmFileCopier::Parse(std::vector<std::string> const& args)
-{
- this->Doing = DoingFiles;
- for (unsigned int i = 1; i < args.size(); ++i) {
- // Check this argument.
- if (!this->CheckKeyword(args[i]) && !this->CheckValue(args[i])) {
- std::ostringstream e;
- e << "called with unknown argument \"" << args[i] << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- // Quit if an argument is invalid.
- if (this->Doing == DoingError) {
- return false;
- }
- }
-
- // Require a destination.
- if (this->Destination.empty()) {
- std::ostringstream e;
- e << this->Name << " given no DESTINATION";
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- // If file permissions were not specified set default permissions.
- if (!this->UseGivenPermissionsFile && !this->UseSourcePermissions) {
- this->DefaultFilePermissions();
- }
-
- // If directory permissions were not specified set default permissions.
- if (!this->UseGivenPermissionsDir && !this->UseSourcePermissions) {
- this->DefaultDirectoryPermissions();
- }
-
- return true;
-}
-
-bool cmFileCopier::CheckKeyword(std::string const& arg)
-{
- if (arg == "DESTINATION") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingDestination;
- }
- } else if (arg == "FILES_FROM_DIR") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingFilesFromDir;
- }
- } else if (arg == "PATTERN") {
- this->Doing = DoingPattern;
- } else if (arg == "REGEX") {
- this->Doing = DoingRegex;
- } else if (arg == "EXCLUDE") {
- // Add this property to the current match rule.
- if (this->CurrentMatchRule) {
- this->CurrentMatchRule->Properties.Exclude = true;
- this->Doing = DoingNone;
- } else {
- this->NotBeforeMatch(arg);
- }
- } else if (arg == "PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->Doing = DoingPermissionsMatch;
- } else {
- this->NotBeforeMatch(arg);
- }
- } else if (arg == "FILE_PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingPermissionsFile;
- this->UseGivenPermissionsFile = true;
- }
- } else if (arg == "DIRECTORY_PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingPermissionsDir;
- this->UseGivenPermissionsDir = true;
- }
- } else if (arg == "USE_SOURCE_PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->UseSourcePermissions = true;
- }
- } else if (arg == "NO_SOURCE_PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->UseSourcePermissions = false;
- }
- } else if (arg == "FILES_MATCHING") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->MatchlessFiles = false;
- }
- } else {
- return false;
- }
- return true;
-}
-
-bool cmFileCopier::CheckValue(std::string const& arg)
-{
- switch (this->Doing) {
- case DoingFiles:
- this->Files.push_back(arg);
- break;
- case DoingDestination:
- if (arg.empty() || cmSystemTools::FileIsFullPath(arg)) {
- this->Destination = arg;
- } else {
- this->Destination = this->Makefile->GetCurrentBinaryDirectory();
- this->Destination += "/" + arg;
- }
- this->Doing = DoingNone;
- break;
- case DoingFilesFromDir:
- if (cmSystemTools::FileIsFullPath(arg)) {
- this->FilesFromDir = arg;
- } else {
- this->FilesFromDir = this->Makefile->GetCurrentSourceDirectory();
- this->FilesFromDir += "/" + arg;
- }
- cmSystemTools::ConvertToUnixSlashes(this->FilesFromDir);
- this->Doing = DoingNone;
- break;
- case DoingPattern: {
- // Convert the pattern to a regular expression. Require a
- // leading slash and trailing end-of-string in the matched
- // string to make sure the pattern matches only whole file
- // names.
- std::string regex = "/";
- regex += cmsys::Glob::PatternToRegex(arg, false);
- regex += "$";
- this->MatchRules.emplace_back(regex);
- this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
- if (this->CurrentMatchRule->Regex.is_valid()) {
- this->Doing = DoingNone;
- } else {
- std::ostringstream e;
- e << "could not compile PATTERN \"" << arg << "\".";
- this->FileCommand->SetError(e.str());
- this->Doing = DoingError;
- }
- } break;
- case DoingRegex:
- this->MatchRules.emplace_back(arg);
- this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
- if (this->CurrentMatchRule->Regex.is_valid()) {
- this->Doing = DoingNone;
- } else {
- std::ostringstream e;
- e << "could not compile REGEX \"" << arg << "\".";
- this->FileCommand->SetError(e.str());
- this->Doing = DoingError;
- }
- break;
- case DoingPermissionsFile:
- if (!this->CheckPermissions(arg, this->FilePermissions)) {
- this->Doing = DoingError;
- }
- break;
- case DoingPermissionsDir:
- if (!this->CheckPermissions(arg, this->DirPermissions)) {
- this->Doing = DoingError;
- }
- break;
- case DoingPermissionsMatch:
- if (!this->CheckPermissions(
- arg, this->CurrentMatchRule->Properties.Permissions)) {
- this->Doing = DoingError;
- }
- break;
- default:
- return false;
- }
- return true;
-}
-
-bool cmFileCopier::Run(std::vector<std::string> const& args)
-{
- if (!this->Parse(args)) {
- return false;
- }
-
- for (std::string const& f : this->Files) {
- std::string file;
- if (!f.empty() && !cmSystemTools::FileIsFullPath(f)) {
- if (!this->FilesFromDir.empty()) {
- file = this->FilesFromDir;
- } else {
- file = this->Makefile->GetCurrentSourceDirectory();
- }
- file += "/";
- file += f;
- } else if (!this->FilesFromDir.empty()) {
- this->FileCommand->SetError("option FILES_FROM_DIR requires all files "
- "to be specified as relative paths.");
- return false;
- } else {
- file = f;
- }
-
- // Split the input file into its directory and name components.
- std::vector<std::string> fromPathComponents;
- cmSystemTools::SplitPath(file, fromPathComponents);
- std::string fromName = *(fromPathComponents.end() - 1);
- std::string fromDir = cmSystemTools::JoinPath(
- fromPathComponents.begin(), fromPathComponents.end() - 1);
-
- // Compute the full path to the destination file.
- std::string toFile = this->Destination;
- if (!this->FilesFromDir.empty()) {
- std::string dir = cmSystemTools::GetFilenamePath(f);
- if (!dir.empty()) {
- toFile += "/";
- toFile += dir;
- }
- }
- std::string const& toName = this->ToName(fromName);
- if (!toName.empty()) {
- toFile += "/";
- toFile += toName;
- }
-
- // Construct the full path to the source file. The file name may
- // have been changed above.
- std::string fromFile = fromDir;
- if (!fromName.empty()) {
- fromFile += "/";
- fromFile += fromName;
- }
-
- if (!this->Install(fromFile, toFile)) {
- return false;
- }
- }
- return true;
-}
-
-bool cmFileCopier::Install(const std::string& fromFile,
- const std::string& toFile)
-{
- if (fromFile.empty()) {
- std::ostringstream e;
- e << "INSTALL encountered an empty string input file name.";
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- // Collect any properties matching this file name.
- MatchProperties match_properties = this->CollectMatchProperties(fromFile);
-
- // Skip the file if it is excluded.
- if (match_properties.Exclude) {
- return true;
- }
-
- if (cmSystemTools::SameFile(fromFile, toFile)) {
- return true;
- }
- if (cmSystemTools::FileIsSymlink(fromFile)) {
- return this->InstallSymlink(fromFile, toFile);
- }
- if (cmSystemTools::FileIsDirectory(fromFile)) {
- return this->InstallDirectory(fromFile, toFile, match_properties);
- }
- if (cmSystemTools::FileExists(fromFile)) {
- return this->InstallFile(fromFile, toFile, match_properties);
- }
- return this->ReportMissing(fromFile);
-}
-
-bool cmFileCopier::InstallSymlink(const std::string& fromFile,
- const std::string& toFile)
-{
- // Read the original symlink.
- std::string symlinkTarget;
- if (!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) {
- std::ostringstream e;
- e << this->Name << " cannot read symlink \"" << fromFile
- << "\" to duplicate at \"" << toFile << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- // Compare the symlink value to that at the destination if not
- // always installing.
- bool copy = true;
- if (!this->Always) {
- std::string oldSymlinkTarget;
- if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
- if (symlinkTarget == oldSymlinkTarget) {
- copy = false;
- }
- }
- }
-
- // Inform the user about this file installation.
- this->ReportCopy(toFile, TypeLink, copy);
-
- if (copy) {
- // Remove the destination file so we can always create the symlink.
- cmSystemTools::RemoveFile(toFile);
-
- // Create destination directory if it doesn't exist
- cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile));
-
- // Create the symlink.
- if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
- std::ostringstream e;
- e << this->Name << " cannot duplicate symlink \"" << fromFile
- << "\" at \"" << toFile << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
- }
-
- return true;
-}
-
-bool cmFileCopier::InstallFile(const std::string& fromFile,
- const std::string& toFile,
- MatchProperties match_properties)
-{
- // Determine whether we will copy the file.
- bool copy = true;
- if (!this->Always) {
- // If both files exist with the same time do not copy.
- if (!this->FileTimes.FileTimesDiffer(fromFile, toFile)) {
- copy = false;
- }
- }
-
- // Inform the user about this file installation.
- this->ReportCopy(toFile, TypeFile, copy);
-
- // Copy the file.
- if (copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) {
- std::ostringstream e;
- e << this->Name << " cannot copy file \"" << fromFile << "\" to \""
- << toFile << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- // Set the file modification time of the destination file.
- if (copy && !this->Always) {
- // Add write permission so we can set the file time.
- // Permissions are set unconditionally below anyway.
- mode_t perm = 0;
- if (cmSystemTools::GetPermissions(toFile, perm)) {
- cmSystemTools::SetPermissions(toFile, perm | mode_owner_write);
- }
- if (!cmSystemTools::CopyFileTime(fromFile, toFile)) {
- std::ostringstream e;
- e << this->Name << " cannot set modification time on \"" << toFile
- << "\"";
- this->FileCommand->SetError(e.str());
- return false;
- }
- }
-
- // Set permissions of the destination file.
- mode_t permissions =
- (match_properties.Permissions ? match_properties.Permissions
- : this->FilePermissions);
- if (!permissions) {
- // No permissions were explicitly provided but the user requested
- // that the source file permissions be used.
- cmSystemTools::GetPermissions(fromFile, permissions);
- }
- return this->SetPermissions(toFile, permissions);
-}
-
-bool cmFileCopier::InstallDirectory(const std::string& source,
- const std::string& destination,
- MatchProperties match_properties)
-{
- // Inform the user about this directory installation.
- this->ReportCopy(destination, TypeDir,
- !cmSystemTools::FileIsDirectory(destination));
-
- // check if default dir creation permissions were set
- mode_t default_dir_mode_v = 0;
- mode_t* default_dir_mode = &default_dir_mode_v;
- if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
- return false;
- }
-
- // Make sure the destination directory exists.
- if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
- std::ostringstream e;
- e << this->Name << " cannot make directory \"" << destination
- << "\": " << cmSystemTools::GetLastSystemError();
- this->FileCommand->SetError(e.str());
- return false;
- }
-
- // Compute the requested permissions for the destination directory.
- mode_t permissions =
- (match_properties.Permissions ? match_properties.Permissions
- : this->DirPermissions);
- if (!permissions) {
- // No permissions were explicitly provided but the user requested
- // that the source directory permissions be used.
- cmSystemTools::GetPermissions(source, permissions);
- }
-
- // Compute the set of permissions required on this directory to
- // recursively install files and subdirectories safely.
- mode_t required_permissions =
- mode_owner_read | mode_owner_write | mode_owner_execute;
-
- // If the required permissions are specified it is safe to set the
- // final permissions now. Otherwise we must add the required
- // permissions temporarily during file installation.
- mode_t permissions_before = 0;
- mode_t permissions_after = 0;
- if ((permissions & required_permissions) == required_permissions) {
- permissions_before = permissions;
- } else {
- permissions_before = permissions | required_permissions;
- permissions_after = permissions;
- }
-
- // Set the required permissions of the destination directory.
- if (!this->SetPermissions(destination, permissions_before)) {
- return false;
- }
-
- // Load the directory contents to traverse it recursively.
- cmsys::Directory dir;
- if (!source.empty()) {
- dir.Load(source);
- }
- unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles());
- for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) {
- if (!(strcmp(dir.GetFile(fileNum), ".") == 0 ||
- strcmp(dir.GetFile(fileNum), "..") == 0)) {
- std::string fromPath = source;
- fromPath += "/";
- fromPath += dir.GetFile(fileNum);
- std::string toPath = destination;
- toPath += "/";
- toPath += dir.GetFile(fileNum);
- if (!this->Install(fromPath, toPath)) {
- return false;
- }
- }
- }
-
- // Set the requested permissions of the destination directory.
- return this->SetPermissions(destination, permissions_after);
-}
-
bool cmFileCommand::HandleCopyCommand(std::vector<std::string> const& args)
{
cmFileCopier copier(this);
return copier.Run(args);
}
-struct cmFileInstaller : public cmFileCopier
-{
- cmFileInstaller(cmFileCommand* command)
- : cmFileCopier(command, "INSTALL")
- , InstallType(cmInstallType_FILES)
- , Optional(false)
- , MessageAlways(false)
- , MessageLazy(false)
- , MessageNever(false)
- , DestDirLength(0)
- {
- // Installation does not use source permissions by default.
- this->UseSourcePermissions = false;
- // Check whether to copy files always or only if they have changed.
- std::string install_always;
- if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) {
- this->Always = cmSystemTools::IsOn(install_always);
- }
- // Get the current manifest.
- this->Manifest =
- this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES");
- }
- ~cmFileInstaller() override
- {
- // Save the updated install manifest.
- this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
- this->Manifest.c_str());
- }
-
-protected:
- cmInstallType InstallType;
- bool Optional;
- bool MessageAlways;
- bool MessageLazy;
- bool MessageNever;
- int DestDirLength;
- std::string Rename;
-
- std::string Manifest;
- void ManifestAppend(std::string const& file)
- {
- if (!this->Manifest.empty()) {
- this->Manifest += ";";
- }
- this->Manifest += file.substr(this->DestDirLength);
- }
-
- std::string const& ToName(std::string const& fromName) override
- {
- return this->Rename.empty() ? fromName : this->Rename;
- }
-
- void ReportCopy(const std::string& toFile, Type type, bool copy) override
- {
- if (!this->MessageNever && (copy || !this->MessageLazy)) {
- std::string message = (copy ? "Installing: " : "Up-to-date: ");
- message += toFile;
- this->Makefile->DisplayStatus(message, -1);
- }
- if (type != TypeDir) {
- // Add the file to the manifest.
- this->ManifestAppend(toFile);
- }
- }
- bool ReportMissing(const std::string& fromFile) override
- {
- return (this->Optional || this->cmFileCopier::ReportMissing(fromFile));
- }
- bool Install(const std::string& fromFile, const std::string& toFile) override
- {
- // Support installing from empty source to make a directory.
- if (this->InstallType == cmInstallType_DIRECTORY && fromFile.empty()) {
- return this->InstallDirectory(fromFile, toFile, MatchProperties());
- }
- return this->cmFileCopier::Install(fromFile, toFile);
- }
-
- bool Parse(std::vector<std::string> const& args) override;
- enum
- {
- DoingType = DoingLast1,
- DoingRename,
- DoingLast2
- };
- bool CheckKeyword(std::string const& arg) override;
- bool CheckValue(std::string const& arg) override;
- void DefaultFilePermissions() override
- {
- this->cmFileCopier::DefaultFilePermissions();
- // Add execute permissions based on the target type.
- switch (this->InstallType) {
- case cmInstallType_SHARED_LIBRARY:
- case cmInstallType_MODULE_LIBRARY:
- if (this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE")) {
- break;
- }
- CM_FALLTHROUGH;
- case cmInstallType_EXECUTABLE:
- case cmInstallType_PROGRAMS:
- this->FilePermissions |= mode_owner_execute;
- this->FilePermissions |= mode_group_execute;
- this->FilePermissions |= mode_world_execute;
- break;
- default:
- break;
- }
- }
- bool GetTargetTypeFromString(const std::string& stype);
- bool HandleInstallDestination();
-};
-
-bool cmFileInstaller::Parse(std::vector<std::string> const& args)
-{
- if (!this->cmFileCopier::Parse(args)) {
- return false;
- }
-
- if (!this->Rename.empty()) {
- if (!this->FilesFromDir.empty()) {
- this->FileCommand->SetError("INSTALL option RENAME may not be "
- "combined with FILES_FROM_DIR.");
- return false;
- }
- if (this->InstallType != cmInstallType_FILES &&
- this->InstallType != cmInstallType_PROGRAMS) {
- this->FileCommand->SetError("INSTALL option RENAME may be used "
- "only with FILES or PROGRAMS.");
- return false;
- }
- if (this->Files.size() > 1) {
- this->FileCommand->SetError("INSTALL option RENAME may be used "
- "only with one file.");
- return false;
- }
- }
-
- if (!this->HandleInstallDestination()) {
- return false;
- }
-
- if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) +
- (this->MessageNever ? 1 : 0)) > 1) {
- this->FileCommand->SetError("INSTALL options MESSAGE_ALWAYS, "
- "MESSAGE_LAZY, and MESSAGE_NEVER "
- "are mutually exclusive.");
- return false;
- }
-
- return true;
-}
-
-bool cmFileInstaller::CheckKeyword(std::string const& arg)
-{
- if (arg == "TYPE") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingType;
- }
- } else if (arg == "FILES") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingFiles;
- }
- } else if (arg == "RENAME") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingRename;
- }
- } else if (arg == "OPTIONAL") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->Optional = true;
- }
- } else if (arg == "MESSAGE_ALWAYS") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->MessageAlways = true;
- }
- } else if (arg == "MESSAGE_LAZY") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->MessageLazy = true;
- }
- } else if (arg == "MESSAGE_NEVER") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- this->Doing = DoingNone;
- this->MessageNever = true;
- }
- } else if (arg == "PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->Doing = DoingPermissionsMatch;
- } else {
- // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS
- this->Doing = DoingPermissionsFile;
- this->UseGivenPermissionsFile = true;
- }
- } else if (arg == "DIR_PERMISSIONS") {
- if (this->CurrentMatchRule) {
- this->NotAfterMatch(arg);
- } else {
- // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS
- this->Doing = DoingPermissionsDir;
- this->UseGivenPermissionsDir = true;
- }
- } else if (arg == "COMPONENTS" || arg == "CONFIGURATIONS" ||
- arg == "PROPERTIES") {
- std::ostringstream e;
- e << "INSTALL called with old-style " << arg << " argument. "
- << "This script was generated with an older version of CMake. "
- << "Re-run this cmake version on your build tree.";
- this->FileCommand->SetError(e.str());
- this->Doing = DoingError;
- } else {
- return this->cmFileCopier::CheckKeyword(arg);
- }
- return true;
-}
-
-bool cmFileInstaller::CheckValue(std::string const& arg)
-{
- switch (this->Doing) {
- case DoingType:
- if (!this->GetTargetTypeFromString(arg)) {
- this->Doing = DoingError;
- }
- break;
- case DoingRename:
- this->Rename = arg;
- break;
- default:
- return this->cmFileCopier::CheckValue(arg);
- }
- return true;
-}
-
-bool cmFileInstaller::GetTargetTypeFromString(const std::string& stype)
-{
- if (stype == "EXECUTABLE") {
- this->InstallType = cmInstallType_EXECUTABLE;
- } else if (stype == "FILE") {
- this->InstallType = cmInstallType_FILES;
- } else if (stype == "PROGRAM") {
- this->InstallType = cmInstallType_PROGRAMS;
- } else if (stype == "STATIC_LIBRARY") {
- this->InstallType = cmInstallType_STATIC_LIBRARY;
- } else if (stype == "SHARED_LIBRARY") {
- this->InstallType = cmInstallType_SHARED_LIBRARY;
- } else if (stype == "MODULE") {
- this->InstallType = cmInstallType_MODULE_LIBRARY;
- } else if (stype == "DIRECTORY") {
- this->InstallType = cmInstallType_DIRECTORY;
- } else {
- std::ostringstream e;
- e << "Option TYPE given unknown value \"" << stype << "\".";
- this->FileCommand->SetError(e.str());
- return false;
- }
- return true;
-}
-
-bool cmFileInstaller::HandleInstallDestination()
-{
- std::string& destination = this->Destination;
-
- // allow for / to be a valid destination
- if (destination.size() < 2 && destination != "/") {
- this->FileCommand->SetError("called with inappropriate arguments. "
- "No DESTINATION provided or .");
- return false;
- }
-
- std::string sdestdir;
- if (cmSystemTools::GetEnv("DESTDIR", sdestdir) && !sdestdir.empty()) {
- cmSystemTools::ConvertToUnixSlashes(sdestdir);
- char ch1 = destination[0];
- char ch2 = destination[1];
- char ch3 = 0;
- if (destination.size() > 2) {
- ch3 = destination[2];
- }
- int skip = 0;
- if (ch1 != '/') {
- int relative = 0;
- if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) &&
- ch2 == ':') {
- // Assume windows
- // let's do some destdir magic:
- skip = 2;
- if (ch3 != '/') {
- relative = 1;
- }
- } else {
- relative = 1;
- }
- if (relative) {
- // This is relative path on unix or windows. Since we are doing
- // destdir, this case does not make sense.
- this->FileCommand->SetError(
- "called with relative DESTINATION. This "
- "does not make sense when using DESTDIR. Specify "
- "absolute path or remove DESTDIR environment variable.");
- return false;
- }
- } else {
- if (ch2 == '/') {
- // looks like a network path.
- std::string message =
- "called with network path DESTINATION. This "
- "does not make sense when using DESTDIR. Specify local "
- "absolute path or remove DESTDIR environment variable."
- "\nDESTINATION=\n";
- message += destination;
- this->FileCommand->SetError(message);
- return false;
- }
- }
- destination = sdestdir + (destination.c_str() + skip);
- this->DestDirLength = int(sdestdir.size());
- }
-
- // check if default dir creation permissions were set
- mode_t default_dir_mode_v = 0;
- mode_t* default_dir_mode = &default_dir_mode_v;
- if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
- return false;
- }
-
- if (this->InstallType != cmInstallType_DIRECTORY) {
- if (!cmSystemTools::FileExists(destination)) {
- if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
- std::string errstring = "cannot create directory: " + destination +
- ". Maybe need administrative privileges.";
- this->FileCommand->SetError(errstring);
- return false;
- }
- }
- if (!cmSystemTools::FileIsDirectory(destination)) {
- std::string errstring =
- "INSTALL destination: " + destination + " is not a directory.";
- this->FileCommand->SetError(errstring);
- return false;
- }
- }
- return true;
-}
-
bool cmFileCommand::HandleRPathChangeCommand(
std::vector<std::string> const& args)
{
diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx
new file mode 100644
index 0000000..560d893
--- /dev/null
+++ b/Source/cmFileCopier.cxx
@@ -0,0 +1,660 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmFileCopier.h"
+
+#include "cmFSPermissions.h"
+#include "cmFileCommand.h"
+#include "cmMakefile.h"
+#include "cmSystemTools.h"
+#include "cmsys/Directory.hxx"
+#include "cmsys/Glob.hxx"
+
+#ifdef _WIN32
+# include "cmsys/FStream.hxx"
+#endif
+
+#include <sstream>
+#include <string.h>
+
+using namespace cmFSPermissions;
+
+cmFileCopier::cmFileCopier(cmFileCommand* command, const char* name)
+ : FileCommand(command)
+ , Makefile(command->GetMakefile())
+ , Name(name)
+ , Always(false)
+ , MatchlessFiles(true)
+ , FilePermissions(0)
+ , DirPermissions(0)
+ , CurrentMatchRule(nullptr)
+ , UseGivenPermissionsFile(false)
+ , UseGivenPermissionsDir(false)
+ , UseSourcePermissions(true)
+ , Doing(DoingNone)
+{
+}
+
+cmFileCopier::~cmFileCopier() = default;
+
+cmFileCopier::MatchProperties cmFileCopier::CollectMatchProperties(
+ const std::string& file)
+{
+ // Match rules are case-insensitive on some platforms.
+#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
+ const std::string file_to_match = cmSystemTools::LowerCase(file);
+#else
+ const std::string& file_to_match = file;
+#endif
+
+ // Collect properties from all matching rules.
+ bool matched = false;
+ MatchProperties result;
+ for (MatchRule& mr : this->MatchRules) {
+ if (mr.Regex.find(file_to_match)) {
+ matched = true;
+ result.Exclude |= mr.Properties.Exclude;
+ result.Permissions |= mr.Properties.Permissions;
+ }
+ }
+ if (!matched && !this->MatchlessFiles) {
+ result.Exclude = !cmSystemTools::FileIsDirectory(file);
+ }
+ return result;
+}
+
+bool cmFileCopier::SetPermissions(const std::string& toFile,
+ mode_t permissions)
+{
+ if (permissions) {
+#ifdef WIN32
+ if (Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
+ // Store the mode in an NTFS alternate stream.
+ std::string mode_t_adt_filename = toFile + ":cmake_mode_t";
+
+ // Writing to an NTFS alternate stream changes the modification
+ // time, so we need to save and restore its original value.
+ cmSystemToolsFileTime* file_time_orig = cmSystemTools::FileTimeNew();
+ cmSystemTools::FileTimeGet(toFile, file_time_orig);
+
+ cmsys::ofstream permissionStream(mode_t_adt_filename.c_str());
+
+ if (permissionStream) {
+ permissionStream << std::oct << permissions << std::endl;
+ }
+
+ permissionStream.close();
+
+ cmSystemTools::FileTimeSet(toFile, file_time_orig);
+
+ cmSystemTools::FileTimeDelete(file_time_orig);
+ }
+#endif
+
+ if (!cmSystemTools::SetPermissions(toFile, permissions)) {
+ std::ostringstream e;
+ e << this->Name << " cannot set permissions on \"" << toFile << "\"";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+ }
+ return true;
+}
+
+// Translate an argument to a permissions bit.
+bool cmFileCopier::CheckPermissions(std::string const& arg,
+ mode_t& permissions)
+{
+ if (!cmFSPermissions::stringToModeT(arg, permissions)) {
+ std::ostringstream e;
+ e << this->Name << " given invalid permission \"" << arg << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+ return true;
+}
+
+std::string const& cmFileCopier::ToName(std::string const& fromName)
+{
+ return fromName;
+}
+
+bool cmFileCopier::ReportMissing(const std::string& fromFile)
+{
+ // The input file does not exist and installation is not optional.
+ std::ostringstream e;
+ e << this->Name << " cannot find \"" << fromFile << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+}
+
+void cmFileCopier::NotBeforeMatch(std::string const& arg)
+{
+ std::ostringstream e;
+ e << "option " << arg << " may not appear before PATTERN or REGEX.";
+ this->FileCommand->SetError(e.str());
+ this->Doing = DoingError;
+}
+
+void cmFileCopier::NotAfterMatch(std::string const& arg)
+{
+ std::ostringstream e;
+ e << "option " << arg << " may not appear after PATTERN or REGEX.";
+ this->FileCommand->SetError(e.str());
+ this->Doing = DoingError;
+}
+
+void cmFileCopier::DefaultFilePermissions()
+{
+ // Use read/write permissions.
+ this->FilePermissions = 0;
+ this->FilePermissions |= mode_owner_read;
+ this->FilePermissions |= mode_owner_write;
+ this->FilePermissions |= mode_group_read;
+ this->FilePermissions |= mode_world_read;
+}
+
+void cmFileCopier::DefaultDirectoryPermissions()
+{
+ // Use read/write/executable permissions.
+ this->DirPermissions = 0;
+ this->DirPermissions |= mode_owner_read;
+ this->DirPermissions |= mode_owner_write;
+ this->DirPermissions |= mode_owner_execute;
+ this->DirPermissions |= mode_group_read;
+ this->DirPermissions |= mode_group_execute;
+ this->DirPermissions |= mode_world_read;
+ this->DirPermissions |= mode_world_execute;
+}
+
+bool cmFileCopier::GetDefaultDirectoryPermissions(mode_t** mode)
+{
+ // check if default dir creation permissions were set
+ const char* default_dir_install_permissions = this->Makefile->GetDefinition(
+ "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
+ if (default_dir_install_permissions && *default_dir_install_permissions) {
+ std::vector<std::string> items;
+ cmSystemTools::ExpandListArgument(default_dir_install_permissions, items);
+ for (const auto& arg : items) {
+ if (!this->CheckPermissions(arg, **mode)) {
+ std::ostringstream e;
+ e << this->FileCommand->GetError()
+ << " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
+ "variable.";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+ }
+ } else {
+ *mode = nullptr;
+ }
+
+ return true;
+}
+
+bool cmFileCopier::Parse(std::vector<std::string> const& args)
+{
+ this->Doing = DoingFiles;
+ for (unsigned int i = 1; i < args.size(); ++i) {
+ // Check this argument.
+ if (!this->CheckKeyword(args[i]) && !this->CheckValue(args[i])) {
+ std::ostringstream e;
+ e << "called with unknown argument \"" << args[i] << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+
+ // Quit if an argument is invalid.
+ if (this->Doing == DoingError) {
+ return false;
+ }
+ }
+
+ // Require a destination.
+ if (this->Destination.empty()) {
+ std::ostringstream e;
+ e << this->Name << " given no DESTINATION";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+
+ // If file permissions were not specified set default permissions.
+ if (!this->UseGivenPermissionsFile && !this->UseSourcePermissions) {
+ this->DefaultFilePermissions();
+ }
+
+ // If directory permissions were not specified set default permissions.
+ if (!this->UseGivenPermissionsDir && !this->UseSourcePermissions) {
+ this->DefaultDirectoryPermissions();
+ }
+
+ return true;
+}
+
+bool cmFileCopier::CheckKeyword(std::string const& arg)
+{
+ if (arg == "DESTINATION") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingDestination;
+ }
+ } else if (arg == "FILES_FROM_DIR") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingFilesFromDir;
+ }
+ } else if (arg == "PATTERN") {
+ this->Doing = DoingPattern;
+ } else if (arg == "REGEX") {
+ this->Doing = DoingRegex;
+ } else if (arg == "EXCLUDE") {
+ // Add this property to the current match rule.
+ if (this->CurrentMatchRule) {
+ this->CurrentMatchRule->Properties.Exclude = true;
+ this->Doing = DoingNone;
+ } else {
+ this->NotBeforeMatch(arg);
+ }
+ } else if (arg == "PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->Doing = DoingPermissionsMatch;
+ } else {
+ this->NotBeforeMatch(arg);
+ }
+ } else if (arg == "FILE_PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingPermissionsFile;
+ this->UseGivenPermissionsFile = true;
+ }
+ } else if (arg == "DIRECTORY_PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingPermissionsDir;
+ this->UseGivenPermissionsDir = true;
+ }
+ } else if (arg == "USE_SOURCE_PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->UseSourcePermissions = true;
+ }
+ } else if (arg == "NO_SOURCE_PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->UseSourcePermissions = false;
+ }
+ } else if (arg == "FILES_MATCHING") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->MatchlessFiles = false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool cmFileCopier::CheckValue(std::string const& arg)
+{
+ switch (this->Doing) {
+ case DoingFiles:
+ this->Files.push_back(arg);
+ break;
+ case DoingDestination:
+ if (arg.empty() || cmSystemTools::FileIsFullPath(arg)) {
+ this->Destination = arg;
+ } else {
+ this->Destination = this->Makefile->GetCurrentBinaryDirectory();
+ this->Destination += "/" + arg;
+ }
+ this->Doing = DoingNone;
+ break;
+ case DoingFilesFromDir:
+ if (cmSystemTools::FileIsFullPath(arg)) {
+ this->FilesFromDir = arg;
+ } else {
+ this->FilesFromDir = this->Makefile->GetCurrentSourceDirectory();
+ this->FilesFromDir += "/" + arg;
+ }
+ cmSystemTools::ConvertToUnixSlashes(this->FilesFromDir);
+ this->Doing = DoingNone;
+ break;
+ case DoingPattern: {
+ // Convert the pattern to a regular expression. Require a
+ // leading slash and trailing end-of-string in the matched
+ // string to make sure the pattern matches only whole file
+ // names.
+ std::string regex = "/";
+ regex += cmsys::Glob::PatternToRegex(arg, false);
+ regex += "$";
+ this->MatchRules.emplace_back(regex);
+ this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
+ if (this->CurrentMatchRule->Regex.is_valid()) {
+ this->Doing = DoingNone;
+ } else {
+ std::ostringstream e;
+ e << "could not compile PATTERN \"" << arg << "\".";
+ this->FileCommand->SetError(e.str());
+ this->Doing = DoingError;
+ }
+ } break;
+ case DoingRegex:
+ this->MatchRules.emplace_back(arg);
+ this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
+ if (this->CurrentMatchRule->Regex.is_valid()) {
+ this->Doing = DoingNone;
+ } else {
+ std::ostringstream e;
+ e << "could not compile REGEX \"" << arg << "\".";
+ this->FileCommand->SetError(e.str());
+ this->Doing = DoingError;
+ }
+ break;
+ case DoingPermissionsFile:
+ if (!this->CheckPermissions(arg, this->FilePermissions)) {
+ this->Doing = DoingError;
+ }
+ break;
+ case DoingPermissionsDir:
+ if (!this->CheckPermissions(arg, this->DirPermissions)) {
+ this->Doing = DoingError;
+ }
+ break;
+ case DoingPermissionsMatch:
+ if (!this->CheckPermissions(
+ arg, this->CurrentMatchRule->Properties.Permissions)) {
+ this->Doing = DoingError;
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool cmFileCopier::Run(std::vector<std::string> const& args)
+{
+ if (!this->Parse(args)) {
+ return false;
+ }
+
+ for (std::string const& f : this->Files) {
+ std::string file;
+ if (!f.empty() && !cmSystemTools::FileIsFullPath(f)) {
+ if (!this->FilesFromDir.empty()) {
+ file = this->FilesFromDir;
+ } else {
+ file = this->Makefile->GetCurrentSourceDirectory();
+ }
+ file += "/";
+ file += f;
+ } else if (!this->FilesFromDir.empty()) {
+ this->FileCommand->SetError("option FILES_FROM_DIR requires all files "
+ "to be specified as relative paths.");
+ return false;
+ } else {
+ file = f;
+ }
+
+ // Split the input file into its directory and name components.
+ std::vector<std::string> fromPathComponents;
+ cmSystemTools::SplitPath(file, fromPathComponents);
+ std::string fromName = *(fromPathComponents.end() - 1);
+ std::string fromDir = cmSystemTools::JoinPath(
+ fromPathComponents.begin(), fromPathComponents.end() - 1);
+
+ // Compute the full path to the destination file.
+ std::string toFile = this->Destination;
+ if (!this->FilesFromDir.empty()) {
+ std::string dir = cmSystemTools::GetFilenamePath(f);
+ if (!dir.empty()) {
+ toFile += "/";
+ toFile += dir;
+ }
+ }
+ std::string const& toName = this->ToName(fromName);
+ if (!toName.empty()) {
+ toFile += "/";
+ toFile += toName;
+ }
+
+ // Construct the full path to the source file. The file name may
+ // have been changed above.
+ std::string fromFile = fromDir;
+ if (!fromName.empty()) {
+ fromFile += "/";
+ fromFile += fromName;
+ }
+
+ if (!this->Install(fromFile, toFile)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmFileCopier::Install(const std::string& fromFile,
+ const std::string& toFile)
+{
+ if (fromFile.empty()) {
+ std::ostringstream e;
+ e << "INSTALL encountered an empty string input file name.";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+
+ // Collect any properties matching this file name.
+ MatchProperties match_properties = this->CollectMatchProperties(fromFile);
+
+ // Skip the file if it is excluded.
+ if (match_properties.Exclude) {
+ return true;
+ }
+
+ if (cmSystemTools::SameFile(fromFile, toFile)) {
+ return true;
+ }
+ if (cmSystemTools::FileIsSymlink(fromFile)) {
+ return this->InstallSymlink(fromFile, toFile);
+ }
+ if (cmSystemTools::FileIsDirectory(fromFile)) {
+ return this->InstallDirectory(fromFile, toFile, match_properties);
+ }
+ if (cmSystemTools::FileExists(fromFile)) {
+ return this->InstallFile(fromFile, toFile, match_properties);
+ }
+ return this->ReportMissing(fromFile);
+}
+
+bool cmFileCopier::InstallSymlink(const std::string& fromFile,
+ const std::string& toFile)
+{
+ // Read the original symlink.
+ std::string symlinkTarget;
+ if (!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) {
+ std::ostringstream e;
+ e << this->Name << " cannot read symlink \"" << fromFile
+ << "\" to duplicate at \"" << toFile << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+
+ // Compare the symlink value to that at the destination if not
+ // always installing.
+ bool copy = true;
+ if (!this->Always) {
+ std::string oldSymlinkTarget;
+ if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
+ if (symlinkTarget == oldSymlinkTarget) {
+ copy = false;
+ }
+ }
+ }
+
+ // Inform the user about this file installation.
+ this->ReportCopy(toFile, TypeLink, copy);
+
+ if (copy) {
+ // Remove the destination file so we can always create the symlink.
+ cmSystemTools::RemoveFile(toFile);
+
+ // Create destination directory if it doesn't exist
+ cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile));
+
+ // Create the symlink.
+ if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
+ std::ostringstream e;
+ e << this->Name << " cannot duplicate symlink \"" << fromFile
+ << "\" at \"" << toFile << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool cmFileCopier::InstallFile(const std::string& fromFile,
+ const std::string& toFile,
+ MatchProperties match_properties)
+{
+ // Determine whether we will copy the file.
+ bool copy = true;
+ if (!this->Always) {
+ // If both files exist with the same time do not copy.
+ if (!this->FileTimes.FileTimesDiffer(fromFile, toFile)) {
+ copy = false;
+ }
+ }
+
+ // Inform the user about this file installation.
+ this->ReportCopy(toFile, TypeFile, copy);
+
+ // Copy the file.
+ if (copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) {
+ std::ostringstream e;
+ e << this->Name << " cannot copy file \"" << fromFile << "\" to \""
+ << toFile << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+
+ // Set the file modification time of the destination file.
+ if (copy && !this->Always) {
+ // Add write permission so we can set the file time.
+ // Permissions are set unconditionally below anyway.
+ mode_t perm = 0;
+ if (cmSystemTools::GetPermissions(toFile, perm)) {
+ cmSystemTools::SetPermissions(toFile, perm | mode_owner_write);
+ }
+ if (!cmSystemTools::CopyFileTime(fromFile, toFile)) {
+ std::ostringstream e;
+ e << this->Name << " cannot set modification time on \"" << toFile
+ << "\"";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+ }
+
+ // Set permissions of the destination file.
+ mode_t permissions =
+ (match_properties.Permissions ? match_properties.Permissions
+ : this->FilePermissions);
+ if (!permissions) {
+ // No permissions were explicitly provided but the user requested
+ // that the source file permissions be used.
+ cmSystemTools::GetPermissions(fromFile, permissions);
+ }
+ return this->SetPermissions(toFile, permissions);
+}
+
+bool cmFileCopier::InstallDirectory(const std::string& source,
+ const std::string& destination,
+ MatchProperties match_properties)
+{
+ // Inform the user about this directory installation.
+ this->ReportCopy(destination, TypeDir,
+ !cmSystemTools::FileIsDirectory(destination));
+
+ // check if default dir creation permissions were set
+ mode_t default_dir_mode_v = 0;
+ mode_t* default_dir_mode = &default_dir_mode_v;
+ if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
+ return false;
+ }
+
+ // Make sure the destination directory exists.
+ if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
+ std::ostringstream e;
+ e << this->Name << " cannot make directory \"" << destination
+ << "\": " << cmSystemTools::GetLastSystemError();
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+
+ // Compute the requested permissions for the destination directory.
+ mode_t permissions =
+ (match_properties.Permissions ? match_properties.Permissions
+ : this->DirPermissions);
+ if (!permissions) {
+ // No permissions were explicitly provided but the user requested
+ // that the source directory permissions be used.
+ cmSystemTools::GetPermissions(source, permissions);
+ }
+
+ // Compute the set of permissions required on this directory to
+ // recursively install files and subdirectories safely.
+ mode_t required_permissions =
+ mode_owner_read | mode_owner_write | mode_owner_execute;
+
+ // If the required permissions are specified it is safe to set the
+ // final permissions now. Otherwise we must add the required
+ // permissions temporarily during file installation.
+ mode_t permissions_before = 0;
+ mode_t permissions_after = 0;
+ if ((permissions & required_permissions) == required_permissions) {
+ permissions_before = permissions;
+ } else {
+ permissions_before = permissions | required_permissions;
+ permissions_after = permissions;
+ }
+
+ // Set the required permissions of the destination directory.
+ if (!this->SetPermissions(destination, permissions_before)) {
+ return false;
+ }
+
+ // Load the directory contents to traverse it recursively.
+ cmsys::Directory dir;
+ if (!source.empty()) {
+ dir.Load(source);
+ }
+ unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles());
+ for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) {
+ if (!(strcmp(dir.GetFile(fileNum), ".") == 0 ||
+ strcmp(dir.GetFile(fileNum), "..") == 0)) {
+ std::string fromPath = source;
+ fromPath += "/";
+ fromPath += dir.GetFile(fileNum);
+ std::string toPath = destination;
+ toPath += "/";
+ toPath += dir.GetFile(fileNum);
+ if (!this->Install(fromPath, toPath)) {
+ return false;
+ }
+ }
+ }
+
+ // Set the requested permissions of the destination directory.
+ return this->SetPermissions(destination, permissions_after);
+}
diff --git a/Source/cmFileCopier.h b/Source/cmFileCopier.h
new file mode 100644
index 0000000..a11c371
--- /dev/null
+++ b/Source/cmFileCopier.h
@@ -0,0 +1,120 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmFileCopier_h
+#define cmFileCopier_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmFileTimeComparison.h"
+#include "cm_sys_stat.h"
+#include "cmsys/RegularExpression.hxx"
+
+#include <string>
+#include <vector>
+
+class cmFileCommand;
+class cmMakefile;
+
+// File installation helper class.
+struct cmFileCopier
+{
+ cmFileCopier(cmFileCommand* command, const char* name = "COPY");
+ virtual ~cmFileCopier();
+
+ bool Run(std::vector<std::string> const& args);
+
+protected:
+ cmFileCommand* FileCommand;
+ cmMakefile* Makefile;
+ const char* Name;
+ bool Always;
+ cmFileTimeComparison FileTimes;
+
+ // Whether to install a file not matching any expression.
+ bool MatchlessFiles;
+
+ // Permissions for files and directories installed by this object.
+ mode_t FilePermissions;
+ mode_t DirPermissions;
+
+ // Properties set by pattern and regex match rules.
+ struct MatchProperties
+ {
+ bool Exclude = false;
+ mode_t Permissions = 0;
+ };
+ struct MatchRule
+ {
+ cmsys::RegularExpression Regex;
+ MatchProperties Properties;
+ std::string RegexString;
+ MatchRule(std::string const& regex)
+ : Regex(regex)
+ , RegexString(regex)
+ {
+ }
+ };
+ std::vector<MatchRule> MatchRules;
+
+ // Get the properties from rules matching this input file.
+ MatchProperties CollectMatchProperties(const std::string& file);
+
+ bool SetPermissions(const std::string& toFile, mode_t permissions);
+
+ // Translate an argument to a permissions bit.
+ bool CheckPermissions(std::string const& arg, mode_t& permissions);
+
+ bool InstallSymlink(const std::string& fromFile, const std::string& toFile);
+ bool InstallFile(const std::string& fromFile, const std::string& toFile,
+ MatchProperties match_properties);
+ bool InstallDirectory(const std::string& source,
+ const std::string& destination,
+ MatchProperties match_properties);
+ virtual bool Install(const std::string& fromFile, const std::string& toFile);
+ virtual std::string const& ToName(std::string const& fromName);
+
+ enum Type
+ {
+ TypeFile,
+ TypeDir,
+ TypeLink
+ };
+ virtual void ReportCopy(const std::string&, Type, bool) {}
+ virtual bool ReportMissing(const std::string& fromFile);
+
+ MatchRule* CurrentMatchRule;
+ bool UseGivenPermissionsFile;
+ bool UseGivenPermissionsDir;
+ bool UseSourcePermissions;
+ std::string Destination;
+ std::string FilesFromDir;
+ std::vector<std::string> Files;
+ int Doing;
+
+ virtual bool Parse(std::vector<std::string> const& args);
+ enum
+ {
+ DoingNone,
+ DoingError,
+ DoingDestination,
+ DoingFilesFromDir,
+ DoingFiles,
+ DoingPattern,
+ DoingRegex,
+ DoingPermissionsFile,
+ DoingPermissionsDir,
+ DoingPermissionsMatch,
+ DoingLast1
+ };
+ virtual bool CheckKeyword(std::string const& arg);
+ virtual bool CheckValue(std::string const& arg);
+
+ void NotBeforeMatch(std::string const& arg);
+ void NotAfterMatch(std::string const& arg);
+ virtual void DefaultFilePermissions();
+ virtual void DefaultDirectoryPermissions();
+
+ bool GetDefaultDirectoryPermissions(mode_t** mode);
+};
+
+#endif
diff --git a/Source/cmFileInstaller.cxx b/Source/cmFileInstaller.cxx
new file mode 100644
index 0000000..f3544c1
--- /dev/null
+++ b/Source/cmFileInstaller.cxx
@@ -0,0 +1,350 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmFileInstaller.h"
+
+#include "cmFSPermissions.h"
+#include "cmFileCommand.h"
+#include "cmMakefile.h"
+#include "cmSystemTools.h"
+
+#include "cm_sys_stat.h"
+
+#include <sstream>
+
+using namespace cmFSPermissions;
+
+cmFileInstaller::cmFileInstaller(cmFileCommand* command)
+ : cmFileCopier(command, "INSTALL")
+ , InstallType(cmInstallType_FILES)
+ , Optional(false)
+ , MessageAlways(false)
+ , MessageLazy(false)
+ , MessageNever(false)
+ , DestDirLength(0)
+{
+ // Installation does not use source permissions by default.
+ this->UseSourcePermissions = false;
+ // Check whether to copy files always or only if they have changed.
+ std::string install_always;
+ if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) {
+ this->Always = cmSystemTools::IsOn(install_always);
+ }
+ // Get the current manifest.
+ this->Manifest =
+ this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES");
+}
+cmFileInstaller::~cmFileInstaller()
+{
+ // Save the updated install manifest.
+ this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
+ this->Manifest.c_str());
+}
+
+void cmFileInstaller::ManifestAppend(std::string const& file)
+{
+ if (!this->Manifest.empty()) {
+ this->Manifest += ";";
+ }
+ this->Manifest += file.substr(this->DestDirLength);
+}
+
+std::string const& cmFileInstaller::ToName(std::string const& fromName)
+{
+ return this->Rename.empty() ? fromName : this->Rename;
+}
+
+void cmFileInstaller::ReportCopy(const std::string& toFile, Type type,
+ bool copy)
+{
+ if (!this->MessageNever && (copy || !this->MessageLazy)) {
+ std::string message = (copy ? "Installing: " : "Up-to-date: ");
+ message += toFile;
+ this->Makefile->DisplayStatus(message, -1);
+ }
+ if (type != TypeDir) {
+ // Add the file to the manifest.
+ this->ManifestAppend(toFile);
+ }
+}
+bool cmFileInstaller::ReportMissing(const std::string& fromFile)
+{
+ return (this->Optional || this->cmFileCopier::ReportMissing(fromFile));
+}
+bool cmFileInstaller::Install(const std::string& fromFile,
+ const std::string& toFile)
+{
+ // Support installing from empty source to make a directory.
+ if (this->InstallType == cmInstallType_DIRECTORY && fromFile.empty()) {
+ return this->InstallDirectory(fromFile, toFile, MatchProperties());
+ }
+ return this->cmFileCopier::Install(fromFile, toFile);
+}
+
+void cmFileInstaller::DefaultFilePermissions()
+{
+ this->cmFileCopier::DefaultFilePermissions();
+ // Add execute permissions based on the target type.
+ switch (this->InstallType) {
+ case cmInstallType_SHARED_LIBRARY:
+ case cmInstallType_MODULE_LIBRARY:
+ if (this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE")) {
+ break;
+ }
+ CM_FALLTHROUGH;
+ case cmInstallType_EXECUTABLE:
+ case cmInstallType_PROGRAMS:
+ this->FilePermissions |= mode_owner_execute;
+ this->FilePermissions |= mode_group_execute;
+ this->FilePermissions |= mode_world_execute;
+ break;
+ default:
+ break;
+ }
+}
+
+bool cmFileInstaller::Parse(std::vector<std::string> const& args)
+{
+ if (!this->cmFileCopier::Parse(args)) {
+ return false;
+ }
+
+ if (!this->Rename.empty()) {
+ if (!this->FilesFromDir.empty()) {
+ this->FileCommand->SetError("INSTALL option RENAME may not be "
+ "combined with FILES_FROM_DIR.");
+ return false;
+ }
+ if (this->InstallType != cmInstallType_FILES &&
+ this->InstallType != cmInstallType_PROGRAMS) {
+ this->FileCommand->SetError("INSTALL option RENAME may be used "
+ "only with FILES or PROGRAMS.");
+ return false;
+ }
+ if (this->Files.size() > 1) {
+ this->FileCommand->SetError("INSTALL option RENAME may be used "
+ "only with one file.");
+ return false;
+ }
+ }
+
+ if (!this->HandleInstallDestination()) {
+ return false;
+ }
+
+ if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) +
+ (this->MessageNever ? 1 : 0)) > 1) {
+ this->FileCommand->SetError("INSTALL options MESSAGE_ALWAYS, "
+ "MESSAGE_LAZY, and MESSAGE_NEVER "
+ "are mutually exclusive.");
+ return false;
+ }
+
+ return true;
+}
+
+bool cmFileInstaller::CheckKeyword(std::string const& arg)
+{
+ if (arg == "TYPE") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingType;
+ }
+ } else if (arg == "FILES") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingFiles;
+ }
+ } else if (arg == "RENAME") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingRename;
+ }
+ } else if (arg == "OPTIONAL") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->Optional = true;
+ }
+ } else if (arg == "MESSAGE_ALWAYS") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->MessageAlways = true;
+ }
+ } else if (arg == "MESSAGE_LAZY") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->MessageLazy = true;
+ }
+ } else if (arg == "MESSAGE_NEVER") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ this->Doing = DoingNone;
+ this->MessageNever = true;
+ }
+ } else if (arg == "PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->Doing = DoingPermissionsMatch;
+ } else {
+ // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS
+ this->Doing = DoingPermissionsFile;
+ this->UseGivenPermissionsFile = true;
+ }
+ } else if (arg == "DIR_PERMISSIONS") {
+ if (this->CurrentMatchRule) {
+ this->NotAfterMatch(arg);
+ } else {
+ // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS
+ this->Doing = DoingPermissionsDir;
+ this->UseGivenPermissionsDir = true;
+ }
+ } else if (arg == "COMPONENTS" || arg == "CONFIGURATIONS" ||
+ arg == "PROPERTIES") {
+ std::ostringstream e;
+ e << "INSTALL called with old-style " << arg << " argument. "
+ << "This script was generated with an older version of CMake. "
+ << "Re-run this cmake version on your build tree.";
+ this->FileCommand->SetError(e.str());
+ this->Doing = DoingError;
+ } else {
+ return this->cmFileCopier::CheckKeyword(arg);
+ }
+ return true;
+}
+
+bool cmFileInstaller::CheckValue(std::string const& arg)
+{
+ switch (this->Doing) {
+ case DoingType:
+ if (!this->GetTargetTypeFromString(arg)) {
+ this->Doing = DoingError;
+ }
+ break;
+ case DoingRename:
+ this->Rename = arg;
+ break;
+ default:
+ return this->cmFileCopier::CheckValue(arg);
+ }
+ return true;
+}
+
+bool cmFileInstaller::GetTargetTypeFromString(const std::string& stype)
+{
+ if (stype == "EXECUTABLE") {
+ this->InstallType = cmInstallType_EXECUTABLE;
+ } else if (stype == "FILE") {
+ this->InstallType = cmInstallType_FILES;
+ } else if (stype == "PROGRAM") {
+ this->InstallType = cmInstallType_PROGRAMS;
+ } else if (stype == "STATIC_LIBRARY") {
+ this->InstallType = cmInstallType_STATIC_LIBRARY;
+ } else if (stype == "SHARED_LIBRARY") {
+ this->InstallType = cmInstallType_SHARED_LIBRARY;
+ } else if (stype == "MODULE") {
+ this->InstallType = cmInstallType_MODULE_LIBRARY;
+ } else if (stype == "DIRECTORY") {
+ this->InstallType = cmInstallType_DIRECTORY;
+ } else {
+ std::ostringstream e;
+ e << "Option TYPE given unknown value \"" << stype << "\".";
+ this->FileCommand->SetError(e.str());
+ return false;
+ }
+ return true;
+}
+
+bool cmFileInstaller::HandleInstallDestination()
+{
+ std::string& destination = this->Destination;
+
+ // allow for / to be a valid destination
+ if (destination.size() < 2 && destination != "/") {
+ this->FileCommand->SetError("called with inappropriate arguments. "
+ "No DESTINATION provided or .");
+ return false;
+ }
+
+ std::string sdestdir;
+ if (cmSystemTools::GetEnv("DESTDIR", sdestdir) && !sdestdir.empty()) {
+ cmSystemTools::ConvertToUnixSlashes(sdestdir);
+ char ch1 = destination[0];
+ char ch2 = destination[1];
+ char ch3 = 0;
+ if (destination.size() > 2) {
+ ch3 = destination[2];
+ }
+ int skip = 0;
+ if (ch1 != '/') {
+ int relative = 0;
+ if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) &&
+ ch2 == ':') {
+ // Assume windows
+ // let's do some destdir magic:
+ skip = 2;
+ if (ch3 != '/') {
+ relative = 1;
+ }
+ } else {
+ relative = 1;
+ }
+ if (relative) {
+ // This is relative path on unix or windows. Since we are doing
+ // destdir, this case does not make sense.
+ this->FileCommand->SetError(
+ "called with relative DESTINATION. This "
+ "does not make sense when using DESTDIR. Specify "
+ "absolute path or remove DESTDIR environment variable.");
+ return false;
+ }
+ } else {
+ if (ch2 == '/') {
+ // looks like a network path.
+ std::string message =
+ "called with network path DESTINATION. This "
+ "does not make sense when using DESTDIR. Specify local "
+ "absolute path or remove DESTDIR environment variable."
+ "\nDESTINATION=\n";
+ message += destination;
+ this->FileCommand->SetError(message);
+ return false;
+ }
+ }
+ destination = sdestdir + (destination.c_str() + skip);
+ this->DestDirLength = int(sdestdir.size());
+ }
+
+ // check if default dir creation permissions were set
+ mode_t default_dir_mode_v = 0;
+ mode_t* default_dir_mode = &default_dir_mode_v;
+ if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
+ return false;
+ }
+
+ if (this->InstallType != cmInstallType_DIRECTORY) {
+ if (!cmSystemTools::FileExists(destination)) {
+ if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
+ std::string errstring = "cannot create directory: " + destination +
+ ". Maybe need administrative privileges.";
+ this->FileCommand->SetError(errstring);
+ return false;
+ }
+ }
+ if (!cmSystemTools::FileIsDirectory(destination)) {
+ std::string errstring =
+ "INSTALL destination: " + destination + " is not a directory.";
+ this->FileCommand->SetError(errstring);
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/Source/cmFileInstaller.h b/Source/cmFileInstaller.h
new file mode 100644
index 0000000..312529a
--- /dev/null
+++ b/Source/cmFileInstaller.h
@@ -0,0 +1,55 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmFileInstaller_h
+#define cmFileInstaller_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmFileCopier.h"
+
+#include "cmInstallType.h"
+
+#include <string>
+#include <vector>
+
+class cmFileCommand;
+
+struct cmFileInstaller : public cmFileCopier
+{
+ cmFileInstaller(cmFileCommand* command);
+ ~cmFileInstaller() override;
+
+protected:
+ cmInstallType InstallType;
+ bool Optional;
+ bool MessageAlways;
+ bool MessageLazy;
+ bool MessageNever;
+ int DestDirLength;
+ std::string Rename;
+
+ std::string Manifest;
+ void ManifestAppend(std::string const& file);
+
+ std::string const& ToName(std::string const& fromName) override;
+
+ void ReportCopy(const std::string& toFile, Type type, bool copy) override;
+ bool ReportMissing(const std::string& fromFile) override;
+ bool Install(const std::string& fromFile,
+ const std::string& toFile) override;
+
+ bool Parse(std::vector<std::string> const& args) override;
+ enum
+ {
+ DoingType = DoingLast1,
+ DoingRename,
+ DoingLast2
+ };
+ bool CheckKeyword(std::string const& arg) override;
+ bool CheckValue(std::string const& arg) override;
+ void DefaultFilePermissions() override;
+ bool GetTargetTypeFromString(const std::string& stype);
+ bool HandleInstallDestination();
+};
+
+#endif
diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx
index db34077..782f746 100644
--- a/Source/cmFindProgramCommand.cxx
+++ b/Source/cmFindProgramCommand.cxx
@@ -77,7 +77,7 @@ struct cmFindProgramHelper
this->TestNameExt = name;
this->TestNameExt += ext;
this->TestPath =
- cmSystemTools::CollapseCombinedPath(path, this->TestNameExt);
+ cmSystemTools::CollapseFullPath(this->TestNameExt, path);
if (cmSystemTools::FileExists(this->TestPath, true)) {
this->BestPath = this->TestPath;
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 50fd16e..50413c8 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -692,6 +692,28 @@ static const struct CXXCompilerIdNode : public CompilerIdNode
}
} cxxCompilerIdNode;
+static const struct CUDACompilerIdNode : public CompilerIdNode
+{
+ CUDACompilerIdNode() {} // NOLINT(modernize-use-equals-default)
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagChecker) const override
+ {
+ if (!context->HeadTarget) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<CUDA_COMPILER_ID> may only be used with binary targets. It may "
+ "not be used with add_custom_command or add_custom_target.");
+ return std::string();
+ }
+ return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
+ "CUDA");
+ }
+} cudaCompilerIdNode;
+
static const struct FortranCompilerIdNode : public CompilerIdNode
{
FortranCompilerIdNode() {} // NOLINT(modernize-use-equals-default)
@@ -773,9 +795,9 @@ static const struct CCompilerVersionNode : public CompilerVersionNode
}
} cCompilerVersionNode;
-static const struct CxxCompilerVersionNode : public CompilerVersionNode
+static const struct CXXCompilerVersionNode : public CompilerVersionNode
{
- CxxCompilerVersionNode() {} // NOLINT(modernize-use-equals-default)
+ CXXCompilerVersionNode() {} // NOLINT(modernize-use-equals-default)
std::string Evaluate(
const std::vector<std::string>& parameters,
@@ -795,6 +817,28 @@ static const struct CxxCompilerVersionNode : public CompilerVersionNode
}
} cxxCompilerVersionNode;
+static const struct CUDACompilerVersionNode : public CompilerVersionNode
+{
+ CUDACompilerVersionNode() {} // NOLINT(modernize-use-equals-default)
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagChecker) const override
+ {
+ if (!context->HeadTarget) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<CUDA_COMPILER_VERSION> may only be used with binary targets. It "
+ "may not be used with add_custom_command or add_custom_target.");
+ return std::string();
+ }
+ return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
+ "CUDA");
+ }
+} cudaCompilerVersionNode;
+
static const struct FortranCompilerVersionNode : public CompilerVersionNode
{
FortranCompilerVersionNode() {} // NOLINT(modernize-use-equals-default)
@@ -2082,6 +2126,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "NOT", &notNode },
{ "C_COMPILER_ID", &cCompilerIdNode },
{ "CXX_COMPILER_ID", &cxxCompilerIdNode },
+ { "CUDA_COMPILER_ID", &cudaCompilerIdNode },
{ "Fortran_COMPILER_ID", &fortranCompilerIdNode },
{ "VERSION_GREATER", &versionGreaterNode },
{ "VERSION_GREATER_EQUAL", &versionGreaterEqNode },
@@ -2090,6 +2135,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "VERSION_EQUAL", &versionEqualNode },
{ "C_COMPILER_VERSION", &cCompilerVersionNode },
{ "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
+ { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
{ "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
{ "PLATFORM_ID", &platformIdNode },
{ "COMPILE_FEATURES", &compileFeaturesNode },
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 841587c..e443678 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -1997,7 +1997,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
std::string const dir_top_bld = tdi["dir-top-bld"].asString();
std::string const dir_top_src = tdi["dir-top-src"].asString();
std::string module_dir = tdi["module-dir"].asString();
- if (!module_dir.empty()) {
+ if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) {
module_dir += "/";
}
std::vector<std::string> linked_target_dirs;
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index 94f6b68..2ba1aff 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -18,8 +18,15 @@
#elif defined(_M_IA64)
# define HOST_PLATFORM_NAME "Itanium"
# define HOST_TOOLS_ARCH ""
+#elif defined(_WIN64)
+# define HOST_PLATFORM_NAME "x64"
+# define HOST_TOOLS_ARCH "x64"
#else
-# include "cmsys/SystemInformation.hxx"
+static bool VSIsWow64()
+{
+ BOOL isWow64 = false;
+ return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
+}
#endif
static std::string VSHostPlatformName()
@@ -27,8 +34,7 @@ static std::string VSHostPlatformName()
#ifdef HOST_PLATFORM_NAME
return HOST_PLATFORM_NAME;
#else
- cmsys::SystemInformation info;
- if (info.Is64Bits()) {
+ if (VSIsWow64()) {
return "x64";
} else {
return "Win32";
@@ -41,8 +47,7 @@ static std::string VSHostArchitecture()
#ifdef HOST_TOOLS_ARCH
return HOST_TOOLS_ARCH;
#else
- cmsys::SystemInformation info;
- if (info.Is64Bits()) {
+ if (VSIsWow64()) {
return "x64";
} else {
return "x86";
@@ -478,18 +483,6 @@ std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
// Ask Visual Studio Installer tool.
std::string vs;
if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
- std::string const& hostArch =
- this->GetPlatformToolsetHostArchitectureString();
- if (hostArch == "x64") {
- msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
- if (cmSystemTools::FileExists(msbuild)) {
- return msbuild;
- }
- msbuild = vs + "/MSBuild/15.0/Bin/amd64/MSBuild.exe";
- if (cmSystemTools::FileExists(msbuild)) {
- return msbuild;
- }
- }
msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
if (cmSystemTools::FileExists(msbuild)) {
return msbuild;
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 33a31dd..d1093be 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -1264,9 +1264,9 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
// Check if any multiple output pairs have a missing file.
this->CheckMultipleOutputs(verbose);
- std::string dir = cmSystemTools::GetFilenamePath(tgtInfo);
- std::string internalDependFile = dir + "/depend.internal";
- std::string dependFile = dir + "/depend.make";
+ std::string const targetDir = cmSystemTools::GetFilenamePath(tgtInfo);
+ std::string const internalDependFile = targetDir + "/depend.internal";
+ std::string const dependFile = targetDir + "/depend.make";
// If the target DependInfo.cmake file has changed since the last
// time dependencies were scanned then force rescanning. This may
@@ -1292,10 +1292,10 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
// If the directory information is newer than depend.internal, include dirs
// may have changed. In this case discard all old dependencies.
bool needRescanDirInfo = false;
- std::string dirInfoFile = this->GetCurrentBinaryDirectory();
- dirInfoFile += "/CMakeFiles";
- dirInfoFile += "/CMakeDirectoryInformation.cmake";
{
+ std::string dirInfoFile = this->GetCurrentBinaryDirectory();
+ dirInfoFile += "/CMakeFiles";
+ dirInfoFile += "/CMakeDirectoryInformation.cmake";
int result;
if (!ftc->FileTimeCompare(internalDependFile, dirInfoFile, &result) ||
result < 0) {
@@ -1335,7 +1335,7 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) {
// The dependencies must be regenerated.
- std::string targetName = cmSystemTools::GetFilenameName(dir);
+ std::string targetName = cmSystemTools::GetFilenameName(targetDir);
targetName = targetName.substr(0, targetName.length() - 4);
std::string message = "Scanning dependencies of target ";
message += targetName;
@@ -1343,7 +1343,8 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
cmsysTerminal_Color_ForegroundBold,
message.c_str(), true, color);
- return this->ScanDependencies(dir, validDependencies);
+ return this->ScanDependencies(targetDir, dependFile, internalDependFile,
+ validDependencies);
}
// The dependencies are already up-to-date.
@@ -1351,17 +1352,21 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
}
bool cmLocalUnixMakefileGenerator3::ScanDependencies(
- const std::string& targetDir,
+ std::string const& targetDir, std::string const& dependFile,
+ std::string const& internalDependFile,
std::map<std::string, cmDepends::DependencyVector>& validDeps)
{
// Read the directory information file.
cmMakefile* mf = this->Makefile;
bool haveDirectoryInfo = false;
- std::string dirInfoFile = this->GetCurrentBinaryDirectory();
- dirInfoFile += "/CMakeFiles";
- dirInfoFile += "/CMakeDirectoryInformation.cmake";
- if (mf->ReadListFile(dirInfoFile) && !cmSystemTools::GetErrorOccuredFlag()) {
- haveDirectoryInfo = true;
+ {
+ std::string dirInfoFile = this->GetCurrentBinaryDirectory();
+ dirInfoFile += "/CMakeFiles";
+ dirInfoFile += "/CMakeDirectoryInformation.cmake";
+ if (mf->ReadListFile(dirInfoFile) &&
+ !cmSystemTools::GetErrorOccuredFlag()) {
+ haveDirectoryInfo = true;
+ }
}
// Lookup useful directory information.
@@ -1390,10 +1395,8 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(
// Open the make depends file. This should be copy-if-different
// because the make tool may try to reload it needlessly otherwise.
- std::string ruleFileNameFull = targetDir;
- ruleFileNameFull += "/depend.make";
cmGeneratedFileStream ruleFileStream(
- ruleFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
+ dependFile, false, this->GlobalGenerator->GetMakefileEncoding());
ruleFileStream.SetCopyIfDifferent(true);
if (!ruleFileStream) {
return false;
@@ -1402,11 +1405,8 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(
// Open the cmake dependency tracking file. This should not be
// copy-if-different because dependencies are re-scanned when it is
// older than the DependInfo.cmake.
- std::string internalRuleFileNameFull = targetDir;
- internalRuleFileNameFull += "/depend.internal";
cmGeneratedFileStream internalRuleFileStream(
- internalRuleFileNameFull, false,
- this->GlobalGenerator->GetMakefileEncoding());
+ internalDependFile, false, this->GlobalGenerator->GetMakefileEncoding());
if (!internalRuleFileStream) {
return false;
}
@@ -1415,26 +1415,25 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(
this->WriteDisclaimer(internalRuleFileStream);
// for each language we need to scan, scan it
- std::string const& langStr =
- mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES");
std::vector<std::string> langs;
- cmSystemTools::ExpandListArgument(langStr, langs);
+ cmSystemTools::ExpandListArgument(
+ mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES"), langs);
for (std::string const& lang : langs) {
// construct the checker
// Create the scanner for this language
- cmDepends* scanner = nullptr;
+ std::unique_ptr<cmDepends> scanner;
if (lang == "C" || lang == "CXX" || lang == "RC" || lang == "ASM" ||
lang == "CUDA") {
// TODO: Handle RC (resource files) dependencies correctly.
- scanner = new cmDependsC(this, targetDir, lang, &validDeps);
+ scanner = cm::make_unique<cmDependsC>(this, targetDir, lang, &validDeps);
}
#ifdef CMAKE_BUILD_WITH_CMAKE
else if (lang == "Fortran") {
ruleFileStream << "# Note that incremental build could trigger "
<< "a call to cmake_copy_f90_mod on each re-build\n";
- scanner = new cmDependsFortran(this);
+ scanner = cm::make_unique<cmDependsFortran>(this);
} else if (lang == "Java") {
- scanner = new cmDependsJava();
+ scanner = cm::make_unique<cmDependsJava>();
}
#endif
@@ -1445,9 +1444,6 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(
scanner->SetLanguage(lang);
scanner->SetTargetDirectory(targetDir);
scanner->Write(ruleFileStream, internalRuleFileStream);
-
- // free the scanner for this language
- delete scanner;
}
}
diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h
index ced2dbd..ba882da 100644
--- a/Source/cmLocalUnixMakefileGenerator3.h
+++ b/Source/cmLocalUnixMakefileGenerator3.h
@@ -231,7 +231,8 @@ protected:
// Helper methods for dependency updates.
bool ScanDependencies(
- const std::string& targetDir,
+ std::string const& targetDir, std::string const& dependFile,
+ std::string const& internalDependFile,
std::map<std::string, cmDepends::DependencyVector>& validDeps);
void CheckMultipleOutputs(bool verbose);
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 8d11408..1ad26dd 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -1113,7 +1113,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
// Explicit preprocessing always uses a depfile.
ppVars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
- objectFileName + ".d", cmOutputConverter::SHELL);
+ objectFileName + ".pp.d", cmOutputConverter::SHELL);
if (compilePP) {
// The actual compilation does not need a depfile because it
// depends on the already-preprocessed source.
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 8b21166..7677186 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -264,6 +264,9 @@ class cmMakefile;
3, 14, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0089, \
"Compiler id for IBM Clang-based XL compilers is now XLClang.", 3, \
+ 15, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0090, \
+ "export(PACKAGE) does not populate package registry by default.", 3, \
15, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx
index 1b16198..c33bb7e 100644
--- a/Source/cmXCodeScheme.cxx
+++ b/Source/cmXCodeScheme.cxx
@@ -177,6 +177,11 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
WriteLaunchActionAttribute(xout, "stopOnEveryMainThreadCheckerIssue",
"XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
+ if (this->Target->GetTarget()->GetPropertyAsBool(
+ "XCODE_SCHEME_DEBUG_AS_ROOT")) {
+ xout.Attribute("debugAsWhichUser", "root");
+ }
+
// Diagnostics tab end
if (IsExecutable(this->Target)) {
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 1c56bcd..b83c744 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -54,20 +54,21 @@ static const char* cmDocumentationUsageNote[][2] = {
# define CMAKE_BUILD_OPTIONS \
" <dir> = Project binary directory to be built.\n" \
- " -j [<jobs>] --parallel [<jobs>] = Build in parallel using\n" \
- " the given number of jobs. If <jobs> is omitted\n" \
- " the native build tool's default number is used.\n" \
+ " --parallel [<jobs>], -j [<jobs>]\n" \
+ " = Build in parallel using the given number of jobs. \n" \
+ " If <jobs> is omitted the native build tool's \n" \
+ " default number is used.\n" \
" The CMAKE_BUILD_PARALLEL_LEVEL environment " \
"variable\n" \
" specifies a default parallel level when this " \
"option\n" \
" is not given.\n" \
- " --target <tgt> = Build <tgt> instead of default targets.\n" \
- " May be specified multiple times.\n" \
+ " --target <tgt>..., -t <tgt>... \n" \
+ " = Build <tgt> instead of default targets.\n" \
" --config <cfg> = For multi-configuration tools, choose <cfg>.\n" \
" --clean-first = Build target 'clean' first, then build.\n" \
" (To clean only, use --target 'clean'.)\n" \
- " -v --verbose = Enable verbose output - if supported - including\n" \
+ " --verbose, -v = Enable verbose output - if supported - including\n" \
" the build commands to be executed. \n" \
" -- = Pass remaining options to the native tool.\n"
@@ -429,7 +430,8 @@ static int do_build(int ac, char const* const* av)
dir.clear();
}
doing = DoingNone;
- } else if (strcmp(av[i], "--target") == 0) {
+ } else if ((strcmp(av[i], "--target") == 0) ||
+ (strcmp(av[i], "-t") == 0)) {
doing = DoingTarget;
} else if (strcmp(av[i], "--config") == 0) {
doing = DoingConfig;
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index cefac7a..f996a3e 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -111,8 +111,8 @@ void CMakeCommandUsage(const char* program)
<< " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n"
<< " - create or extract a tar or zip archive\n"
<< " time command [args...] - run command and display elapsed time\n"
- << " touch file - touch a file.\n"
- << " touch_nocreate file - touch a file but do not create it.\n"
+ << " touch <file>... - touch a <file>.\n"
+ << " touch_nocreate <file>... - touch a <file> but do not create it.\n"
<< " create_symlink old new - create a symbolic link new -> old\n"
#if defined(_WIN32) && !defined(__CYGWIN__)
<< "Available on Windows only:\n"
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 1b5ed03..01f8226 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1454,6 +1454,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
add_subdirectory(GoogleTest)
endif()
+ if(CMake_TEST_FindGTK2)
+ add_subdirectory(FindGTK2)
+ endif()
+
if(CMake_TEST_FindIconv)
add_subdirectory(FindIconv)
endif()
@@ -1597,11 +1601,6 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
ADD_TEST_MACRO(FindMatlab.r2018a_check ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>)
endif()
- find_package(GTK2 QUIET)
- if(GTK2_FOUND)
- add_subdirectory(FindGTK2)
- endif()
-
add_test(ExternalProject ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/ExternalProject"
diff --git a/Tests/CudaOnly/WithDefs/CMakeLists.txt b/Tests/CudaOnly/WithDefs/CMakeLists.txt
index e58204d..00fd7d2 100644
--- a/Tests/CudaOnly/WithDefs/CMakeLists.txt
+++ b/Tests/CudaOnly/WithDefs/CMakeLists.txt
@@ -37,6 +37,8 @@ target_compile_definitions(CudaOnlyWithDefs
$<$<CONFIG:RELEASE>:$<BUILD_INTERFACE:${release_compile_defs}>>
-DDEF_COMPILE_LANG_$<COMPILE_LANGUAGE>
-DDEF_LANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA>
+ -DDEF_CUDA_COMPILER=$<CUDA_COMPILER_ID>
+ -DDEF_CUDA_COMPILER_VERSION=$<CUDA_COMPILER_VERSION>
)
target_include_directories(CudaOnlyWithDefs
diff --git a/Tests/CudaOnly/WithDefs/main.notcu b/Tests/CudaOnly/WithDefs/main.notcu
index 98f73ce..68a296b 100644
--- a/Tests/CudaOnly/WithDefs/main.notcu
+++ b/Tests/CudaOnly/WithDefs/main.notcu
@@ -39,6 +39,14 @@
# error "Expected DEF_LANG_IS_CUDA"
#endif
+#ifndef DEF_CUDA_COMPILER
+# error "DEF_CUDA_COMPILER not defined!"
+#endif
+
+#ifndef DEF_CUDA_COMPILER_VERSION
+# error "DEF_CUDA_COMPILER_VERSION not defined!"
+#endif
+
static __global__ void DetermineIfValidCudaDevice()
{
}
diff --git a/Tests/FindPackageTest/CMakeLists.txt b/Tests/FindPackageTest/CMakeLists.txt
index f8b36c5..972580c 100644
--- a/Tests/FindPackageTest/CMakeLists.txt
+++ b/Tests/FindPackageTest/CMakeLists.txt
@@ -380,6 +380,7 @@ try_compile(EXPORTER_COMPILED
${FindPackageTest_SOURCE_DIR}/Exporter
CMakeTestExportPackage dummy
CMAKE_FLAGS "-UCMAKE_EXPORT_NO_PACKAGE_REGISTRY"
+ "-DCMAKE_POLICY_DEFAULT_CMP0090:STRING=OLD"
-Dversion=${version}
OUTPUT_VARIABLE output)
message(STATUS "Searching for export(PACKAGE) test project")
@@ -417,6 +418,25 @@ if(CMakeTestExportPackage_FOUND)
message(SEND_ERROR "CMakeTestExportPackage should not be FOUND!")
endif()
+message(STATUS "Remove export(PACKAGE) test project")
+file(REMOVE_RECURSE ${FindPackageTest_BINARY_DIR}/Exporter-build)
+
+message(STATUS "Preparing export(PACKAGE) test project with POLICY CMP0090=NEW")
+try_compile(EXPORTER_COMPILED
+ ${FindPackageTest_BINARY_DIR}/Exporter-build
+ ${FindPackageTest_SOURCE_DIR}/Exporter
+ CMakeTestExportPackage dummy
+ CMAKE_FLAGS
+ "-DCMAKE_POLICY_DEFAULT_CMP0090:STRING=NEW"
+ -Dversion=${version}
+ OUTPUT_VARIABLE output)
+message(STATUS "Searching for export(PACKAGE) test project")
+find_package(CMakeTestExportPackage 1.${version} EXACT QUIET)
+if(CMakeTestExportPackage_FOUND)
+ message(SEND_ERROR "CMakeTestExportPackage should not be FOUND!")
+endif()
+
+
#-----------------------------------------------------------------------------
# Test configure_package_config_file().
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index f2b7ff1..afa8df7 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -339,8 +339,7 @@ if(PKG_CONFIG_FOUND)
add_RunCMake_test(FindPkgConfig)
endif()
-find_package(GTK2 QUIET)
-if (GTK2_FOUND)
+if(CMake_TEST_FindGTK2)
add_RunCMake_test(FindGTK2)
endif()
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index ff2a8a5..5b5c5a5 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -106,7 +106,7 @@ function(run_BuildDir)
run_cmake_command(BuildDir--build ${CMAKE_COMMAND} -E chdir ..
${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget)
run_cmake_command(BuildDir--build-multiple-targets ${CMAKE_COMMAND} -E chdir ..
- ${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget2 --target CustomTarget3)
+ ${CMAKE_COMMAND} --build BuildDir-build -t CustomTarget2 --target CustomTarget3)
run_cmake_command(BuildDir--build-multiple-targets-jobs ${CMAKE_COMMAND} -E chdir ..
${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget CustomTarget2 -j2 --target CustomTarget3)
run_cmake_command(BuildDir--build-multiple-targets-with-clean-first ${CMAKE_COMMAND} -E chdir ..
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
index 24e7202..e82b05f 100644
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
@@ -109,3 +109,24 @@ pkg_check_modules(FakePackage2 REQUIRED QUIET IMPORTED_TARGET cmakeinternalfakep
if (NOT FakePackage2_LINK_LIBRARIES STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a")
message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on second run: ${FakePackage2_LINK_LIBRARIES}")
endif()
+
+set(pname fakelinkoptionspackage)
+file(WRITE ${fakePkgDir}/lib/pkgconfig/${pname}.pc
+"Name: FakeLinkOptionsPackage
+Description: Dummy package for FindPkgConfig IMPORTED_TARGET INTERFACE_LINK_OPTIONS test
+Version: 1.2.3
+Libs: -e dummy_main
+")
+
+set(expected_link_options -e dummy_main)
+pkg_check_modules(FakeLinkOptionsPackage REQUIRED QUIET IMPORTED_TARGET fakelinkoptionspackage)
+if (NOT TARGET PkgConfig::FakeLinkOptionsPackage)
+ message(FATAL_ERROR "No import target for fake link options package")
+endif()
+get_target_property(link_options PkgConfig::FakeLinkOptionsPackage INTERFACE_LINK_OPTIONS)
+if (NOT link_options STREQUAL expected_link_options)
+ message(FATAL_ERROR
+ "Additional link options not present in INTERFACE_LINK_OPTIONS property"
+ "expected: \"${expected_link_options}\", but got \"${link_options}\""
+ )
+endif()
diff --git a/Utilities/Release/WiX/WIX.template.in b/Utilities/Release/WiX/WIX.template.in
index fe176ca..8abf9d8 100644
--- a/Utilities/Release/WiX/WIX.template.in
+++ b/Utilities/Release/WiX/WIX.template.in
@@ -20,6 +20,8 @@
Schedule="afterInstallInitialize"
AllowDowngrades="yes"/>
+ <Property Id="REINSTALLMODE" Value="amus"/>
+
<WixVariable Id="WixUILicenseRtf" Value="$(var.CPACK_WIX_LICENSE_RTF)"/>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/>
diff --git a/bootstrap b/bootstrap
index 950201e..af05024 100755
--- a/bootstrap
+++ b/bootstrap
@@ -302,6 +302,8 @@ CMAKE_CXX_SOURCES="\
cmExprParserHelper \
cmExternalMakefileProjectGenerator \
cmFileCommand \
+ cmFileCopier \
+ cmFileInstaller \
cmFileTimeComparison \
cmFindBase \
cmFindCommon \