diff options
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", ¬Node }, { "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"/> @@ -302,6 +302,8 @@ CMAKE_CXX_SOURCES="\ cmExprParserHelper \ cmExternalMakefileProjectGenerator \ cmFileCommand \ + cmFileCopier \ + cmFileInstaller \ cmFileTimeComparison \ cmFindBase \ cmFindCommon \ |