diff options
159 files changed, 4558 insertions, 368 deletions
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst index 46b9b63..884b2ee 100644 --- a/Help/command/add_test.rst +++ b/Help/command/add_test.rst @@ -7,7 +7,8 @@ Add a test to the project to be run by :manual:`ctest(1)`. add_test(NAME <name> COMMAND <command> [<arg>...] [CONFIGURATIONS <config>...] - [WORKING_DIRECTORY <dir>]) + [WORKING_DIRECTORY <dir>] + [COMMAND_EXPAND_LISTS]) Adds a test called ``<name>``. The test name may not contain spaces, quotes, or other characters special in CMake syntax. The options are: @@ -28,6 +29,11 @@ quotes, or other characters special in CMake syntax. The options are: directory set to the build directory corresponding to the current source directory. +``COMMAND_EXPAND_LISTS`` + Lists in ``COMMAND`` arguments will be expanded, including those + created with + :manual:`generator expressions <cmake-generator-expressions(7)>`. + The given test command is expected to exit with code ``0`` to pass and non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test property is set. Any output written to stdout or stderr will be diff --git a/Help/command/file.rst b/Help/command/file.rst index f99021e..666a532 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -13,6 +13,7 @@ Synopsis file(`STRINGS`_ <filename> <out-var> [...]) file(`\<HASH\> <HASH_>`_ <filename> <out-var>) file(`TIMESTAMP`_ <filename> <out-var> [...]) + file(`GET_RUNTIME_DEPENDENCIES`_ [...]) `Writing`_ file({`WRITE`_ | `APPEND`_} <filename> <content>...) @@ -130,6 +131,273 @@ timestamp variable will be set to the empty string (""). See the :command:`string(TIMESTAMP)` command for documentation of the ``<format>`` and ``UTC`` options. +.. _GET_RUNTIME_DEPENDENCIES: + +.. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + [RESOLVED_DEPENDENCIES_VAR <deps_var>] + [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>] + [CONFLICTING_DEPENDENICES_PREFIX <conflicting_deps_prefix>] + [EXECUTABLES [<executable_files>...]] + [LIBRARIES [<library_files>...]] + [MODULES [<module_files>...]] + [DIRECTORIES [<directories>...]] + [BUNDLE_EXECUTABLE <bundle_executable_file>] + [PRE_INCLUDE_REGEXES [<regexes>...]] + [PRE_EXCLUDE_REGEXES [<regexes>...]] + [POST_INCLUDE_REGEXES [<regexes>...]] + [POST_EXCLUDE_REGEXES [<regexes>...]] + ) + +Recursively get the list of libraries depended on by the given files. + +Please note that this sub-command is not intended to be used in project mode. +Instead, use it in an :command:`install(CODE)` or :command:`install(SCRIPT)` +block. For example: + +.. code-block:: cmake + + install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + # ... + ) + ]]) + +The arguments are as follows: + +``RESOLVED_DEPENDENCIES_VAR <deps_var>`` + Name of the variable in which to store the list of resolved dependencies. + +``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>`` + Name of the variable in which to store the list of unresolved dependencies. + If this variable is not specified, and there are any unresolved dependencies, + an error is issued. + +``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>`` + Variable prefix in which to store conflicting dependency information. + Dependencies are conflicting if two files with the same name are found in + two different directories. The list of filenames that conflict are stored in + ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list of paths + that were found for that filename are stored in + ``<conflicting_deps_prefix>_<filename>``. + +``EXECUTABLES <executable_files>`` + List of executable files to read for dependencies. These are executables that + are typically created with :command:`add_executable`, but they do not have to + be created by CMake. On Apple platforms, the paths to these files determine + the value of ``@executable_path`` when recursively resolving the libraries. + Specifying ``STATIC`` libraries, ``MODULE`` s, or ``SHARED`` libraries here + will result in undefined behavior. + +``LIBRARIES <library_files>`` + List of library files to read for dependencies. These are libraries that are + typically created with :command:`add_library(SHARED)`, but they do not have + to be created by CMake. Specifying ``STATIC`` libraries, ``MODULE`` s, or + executables here will result in undefined behavior. + +``MODULES <module_files>`` + List of loadable module files to read for dependencies. These are modules + that are typically created with :command:`add_library(MODULE)`, but they do + not have to be created by CMake. They are typically used by calling + ``dlopen()`` at runtime rather than linked at link time with ``ld -l``. + Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables here + will result in undefined behavior. + +``DIRECTORIES <directories>`` + List of additional directories to search for dependencies. On Linux + platforms, these directories are searched if the dependency is not found in + any of the other usual paths. If it is found in such a directory, a warning + is issued, because it means that the file is incomplete (it does not list all + of the directories that contain its dependencies.) On Windows platforms, + these directories are searched if the dependency is not found in any of the + other search paths, but no warning is issued, because searching other paths + is a normal part of Windows dependency resolution. On Apple platforms, this + argument has no effect. + +``BUNDLE_EXECTUBLE <bundle_executable_file>`` + Executable to treat as the "bundle executable" when resolving libraries. On + Apple platforms, this argument determines the value of ``@executable_path`` + when recursively resolving libraries for ``LIBRARIES`` and ``MODULES`` files. + It has no effect on ``EXECUTABLES`` files. On other platforms, it has no + effect. This is typically (but not always) one of the executables in the + ``EXECUTABLES`` argument which designates the "main" executable of the + package. + +The following arguments specify filters for including or excluding libraries to +be resolved. See below for a full description of how they work. + +``PRE_INCLUDE_REGEXES <regexes>`` + List of pre-include regexes through which to filter the names of + not-yet-resolved dependencies. + +``PRE_EXCLUDE_REGEXES <regexes>`` + List of pre-exclude regexes through which to filter the names of + not-yet-resolved dependencies. + +``POST_INCLUDE_REGEXES <regexes>`` + List of post-include regexes through which to filter the names of resolved + dependencies. + +``POST_EXCLUDE_REGEXES <regexes>`` + List of post-exclude regexes through which to filter the names of resolved + dependencies. + +These arguments can be used to blacklist unwanted system libraries when +resolving the dependencies, or to whitelist libraries from a specific +directory. The filtering works as follows: + +1. If the not-yet-resolved dependency matches any of the + ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency + resolution proceeds to step 4. +2. If the not-yet-resolved dependency matches any of the + ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency. +3. Otherwise, dependency resolution proceeds. +4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according to + the linking rules of the platform (see below). +5. If the dependency is found, and its full path matches one of the + ``POST_INCLUDE_REGEXES``, the full path is added to the resolved + dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves + that library's own dependencies. Otherwise, resolution proceeds to step 6. +6. If the dependency is found, but its full path matches one of the + ``POST_EXCLUDE_REGEXES``, it is not added to the resolved dependencies, and + dependency resolution stops for that dependency. +7. If the dependency is found, and its full path does not match either + ``POST_INCLUDE_REGEXES`` or ``POST_EXCLUDE_REGEXES``, the full path is added + to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` + recursively resolves that library's own dependencies. + +Different platforms have different rules for how dependencies are resolved. +These specifics are described here. + +On Linux platforms, library resolution works as follows: + +1. If the depending file does not have any ``RUNPATH`` entries, and the library + exists in one of the depending file's ``RPATH`` entries, or its parents', in + that order, the dependency is resolved to that file. +2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the + library exists in one of those entries, the dependency is resolved to that + file. +3. Otherwise, if the library exists in one of the directories listed by + ``ldconfig``, the dependency is resolved to that file. +4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, the + dependency is resolved to that file. In this case, a warning is issued, + because finding a file in one of the ``DIRECTORIES`` means that the + depending file is not complete (it does not list all the directories from + which it pulls dependencies.) +5. Otherwise, the dependency is unresolved. + +On Windows platforms, library resolution works as follows: + +1. The dependent DLL name is converted to lowercase. Windows DLL names are + case-insensitive, and some linkers mangle the case of the DLL dependency + names. However, this makes it more difficult for ``PRE_INCLUDE_REGEXES``, + ``PRE_EXCLUDE_REGEXES``, ``POST_INCLUDE_REGEXES``, and + ``POST_EXCLUDE_REGEXES`` to properly filter DLL names - every regex would + have to check for both uppercase and lowercase letters. For example: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + # ... + PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$" + ) + + Converting the DLL name to lowercase allows the regexes to only match + lowercase names, thus simplifying the regex. For example: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + # ... + PRE_INCLUDE_REGEXES "^mylibrary\\.dll$" + ) + + This regex will match ``mylibrary.dll`` regardless of how it is cased, + either on disk or in the depending file. (For example, it will match + ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.) + + Please note that the directory portion of any resolved DLLs retains its + casing and is not converted to lowercase. Only the filename portion is + converted. + +2. (**Not yet implemented**) If the depending file is a Windows Store app, and + the dependency is listed as a dependency in the application's package + manifest, the dependency is resolved to that file. +3. Otherwise, if the library exists in the same directory as the depending + file, the dependency is resolved to that file. +4. Otherwise, if the library exists in either the operating system's + ``system32`` directory or the ``Windows`` directory, in that order, the + dependency is resolved to that file. +5. Otherwise, if the library exists in one of the directories specified by + ``DIRECTORIES``, in the order they are listed, the dependency is resolved to + that file. (In this case, a warning is not issued, because searching other + directories is a normal part of Windows library resolution.) +6. Otherwise, the dependency is unresolved. + +On Apple platforms, library resolution works as follows: + +1. If the dependency starts with ``@executable_path/``, and an ``EXECUTABLES`` + argument is in the process of being resolved, and replacing + ``@executable_path/`` with the directory of the executable yields an + existing file, the dependency is resolved to that file. +2. Otherwise, if the dependency starts with ``@executable_path/``, and there is + a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` with + the directory of the bundle executable yields an existing file, the + dependency is resolved to that file. +3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing + ``@loader_path/`` with the directory of the depending file yields an + existing file, the dependency is resolved to that file. +4. Otherwise, if the dependency starts with ``@rpath/``, and replacing + ``@rpath/`` with one of the ``RPATH`` entries of the depending file yields + an existing file, the dependency is resolved to that file. (Note that + ``RPATH`` entries that start with ``@executable_path/`` or ``@loader_path/`` + also have these items replaced with the appropriate path.) +5. Otherwise, if the dependency is an absolute file that exists, the dependency + is resolved to that file. +6. Otherwise, the dependency is unresolved. + +This function accepts several variables that determine which tool is used for +dependency resolution: + +.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM + + Determines which operating system and executable format the files are built + for. This could be one of several values: + + * ``linux+elf`` + * ``windows+pe`` + * ``macos+macho`` + + If this variable is not specified, it is determined automatically by system + introspection. + +.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL + + Determines the tool to use for dependency resolution. It could be one of + several values, depending on the value of + :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`: + + ================================================= ============================================= + ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`` ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL`` + ================================================= ============================================= + ``linux+elf`` ``objdump`` + ``windows+pe`` ``dumpbin`` + ``windows+pe`` ``objdump`` + ``macos+macho`` ``otool`` + ================================================= ============================================= + + If this variable is not specified, it is determined automatically by system + introspection. + +.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND + + Determines the path to the tool to use for dependency resolution. This is the + actual path to ``objdump``, ``dumpbin``, or ``otool``. + + If this variable is not specified, it is determined automatically by system + introspection. + Writing ^^^^^^^ diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 1d023cb..bac1c6c 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 + CMP0095: RPATH entries are properly escaped in the intermediary CMake install script. </policy/CMP0095> CMP0094: FindPython3, FindPython2 and FindPython use LOCATION for lookup strategy. </policy/CMP0094> CMP0093: FindBoost reports Boost_VERSION in x.y.z format. </policy/CMP0093> CMP0092: MSVC warning flags are not in CMAKE_{C,CXX}_FLAGS by default. </policy/CMP0092> diff --git a/Help/policy/CMP0095.rst b/Help/policy/CMP0095.rst new file mode 100644 index 0000000..4c56a05 --- /dev/null +++ b/Help/policy/CMP0095.rst @@ -0,0 +1,30 @@ +CMP0095 +------- + +``RPATH`` entries are properly escaped in the intermediary CMake install script. + +In CMake 3.15 and earlier, ``RPATH`` entries set via +:variable:`CMAKE_INSTALL_RPATH` or via :prop_tgt:`INSTALL_RPATH` have not been +escaped before being inserted into the ``cmake_install.cmake`` script. Dynamic +linkers on ELF-based systems (e.g. Linux and FreeBSD) allow certain keywords in +``RPATH`` entries, such as ``${ORIGIN}`` (More details are available in the +``ld.so`` man pages on those systems). The syntax of these keywords can match +CMake's variable syntax. In order to not be substituted (usually to an empty +string) already by the intermediary ``cmake_install.cmake`` script, the user had +to double-escape such ``RPATH`` keywords, e.g. +``set(CMAKE_INSTALL_RPATH "\\\${ORIGIN}/../lib")``. Since the intermediary +``cmake_install.cmake`` script is an implementation detail of CMake, CMake 3.16 +and later will make sure ``RPATH`` entries are inserted literally by escaping +any coincidental CMake syntax. + +The ``OLD`` behavior of this policy is to not escape ``RPATH`` entries in the +intermediary ``cmake_install.cmake`` script. The ``NEW`` behavior is to properly +escape coincidental CMake syntax in ``RPATH`` entries when generating the +intermediary ``cmake_install.cmake`` script. + +This policy was introduced in CMake version 3.16. CMake version |release| warns +when the policy is not set and detected usage of CMake-like syntax and uses +``OLD`` behavior. Use the :command:`cmake_policy` command to set it to ``OLD`` +or ``NEW`` explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst new file mode 100644 index 0000000..e4cc01e --- /dev/null +++ b/Help/release/dev/0-sample-topic.rst @@ -0,0 +1,7 @@ +0-sample-topic +-------------- + +* This is a sample release note for the change in a topic. + Developers should add similar notes for each topic branch + making a noteworthy change. Each document should be named + and titled to match the topic name to avoid merge conflicts. diff --git a/Help/release/dev/CMP0095.rst b/Help/release/dev/CMP0095.rst new file mode 100644 index 0000000..21d0550 --- /dev/null +++ b/Help/release/dev/CMP0095.rst @@ -0,0 +1,5 @@ +CMP0095 +------- + +* ``RPATH`` entries are properly escaped in the intermediary CMake install script. + See policy :policy:`CMP0095`. diff --git a/Help/release/dev/add_test-expand_lists.rst b/Help/release/dev/add_test-expand_lists.rst new file mode 100644 index 0000000..88d26b7 --- /dev/null +++ b/Help/release/dev/add_test-expand_lists.rst @@ -0,0 +1,6 @@ +add_test-expand_lists +--------------------- + +* The command :command:`add_test` learned the option ``COMMAND_EXPAND_LISTS`` + which causes lists in the ``COMMAND`` argument to be expanded, including + lists created by generator expressions. diff --git a/Help/release/dev/get-runtime-dependencies.rst b/Help/release/dev/get-runtime-dependencies.rst new file mode 100644 index 0000000..b9dc6e6 --- /dev/null +++ b/Help/release/dev/get-runtime-dependencies.rst @@ -0,0 +1,9 @@ +get-runtime-dependencies +------------------------ + +* The :command:`file` command learned a new sub-command, + ``GET_RUNTIME_DEPENDENCIES``, which allows you to recursively get the list of + libraries linked by an executable or library. This sub-command is intended as + a replacement for :module:`GetPrerequisites`. +* The :module:`GetPrerequisites` module has been deprecated, as it has been + superceded by :command:`file(GET_RUNTIME_DEPENDENCIES)`. diff --git a/Help/release/index.rst b/Help/release/index.rst index 35a47aa..62032e6 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst @@ -7,6 +7,8 @@ CMake Release Notes This file should include the adjacent "dev.txt" file in development versions but not in release versions. +.. include:: dev.txt + Releases ======== diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index 21f18d4..6f99c1f 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -23,6 +23,8 @@ set(CMAKE_Swift_COMPILE_OPTIONS_TARGET "-target ") set(CMAKE_Swift_COMPILER_ARG1 -frontend) set(CMAKE_Swift_DEFINE_FLAG -D) set(CMAKE_Swift_FRAMEWORK_SEARCH_FLAG "-F ") +set(CMAKE_Swift_LIBRARY_PATH_FLAG "-L ") +set(CMAKE_Swift_LIBRARY_PATH_TERMINATOR "") set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ") set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @) diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake index 5be4676..41d9dc1 100644 --- a/Modules/GetPrerequisites.cmake +++ b/Modules/GetPrerequisites.cmake @@ -5,6 +5,10 @@ GetPrerequisites ---------------- +.. deprecated:: 3.16 + + Use :command:`file(GET_RUNTIME_DEPENDENCIES)` instead. + Functions to analyze and list executable file prerequisites. This module provides functions to list the .dll, .dylib or .so files diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index 89dc6f0..7791822 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake @@ -45,7 +45,7 @@ function(extract_so_info shared_object libname version) ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(result EQUAL 0) - string(REGEX MATCH "\\(SONAME\\)[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}") + string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}") set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE) set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE) else() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 695e075..0316532 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -146,6 +146,28 @@ set(SRCS cmArgumentParser.cxx cmArgumentParser.h cmBase32.cxx + cmBinUtilsLinker.cxx + cmBinUtilsLinker.h + cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx + cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h + cmBinUtilsLinuxELFLinker.cxx + cmBinUtilsLinuxELFLinker.h + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h + cmBinUtilsMacOSMachOLinker.cxx + cmBinUtilsMacOSMachOLinker.h + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h + cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx + cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h + cmBinUtilsWindowsPELinker.cxx + cmBinUtilsWindowsPELinker.h + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h cmCacheManager.cxx cmCacheManager.h cmCLocaleEnvironmentScope.h @@ -295,6 +317,10 @@ set(SRCS cmInstallTargetGenerator.cxx cmInstallDirectoryGenerator.h cmInstallDirectoryGenerator.cxx + cmLDConfigLDConfigTool.cxx + cmLDConfigLDConfigTool.h + cmLDConfigTool.cxx + cmLDConfigTool.h cmLinkedTree.h cmLinkItem.cxx cmLinkItem.h @@ -338,7 +364,6 @@ set(SRCS cmProcessOutput.h cmProcessTools.cxx cmProcessTools.h - cmProperty.cxx cmProperty.h cmPropertyDefinition.cxx cmPropertyDefinition.h @@ -360,6 +385,8 @@ set(SRCS cmQtAutoRcc.h cmRST.cxx cmRST.h + cmRuntimeDependencyArchive.cxx + cmRuntimeDependencyArchive.h cmScriptGenerator.h cmScriptGenerator.cxx cmSourceFile.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index e22d37c..9bef298 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 15) -set(CMake_VERSION_PATCH 0) -set(CMake_VERSION_RC 1) +set(CMake_VERSION_PATCH 20190618) +#set(CMake_VERSION_RC 1) diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 0be3c85..6eb38bd 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -95,30 +95,29 @@ bool cmAddCustomCommandCommand::InitialPass( MAKE_STATIC_KEYWORD(VERBATIM); MAKE_STATIC_KEYWORD(WORKING_DIRECTORY); #undef MAKE_STATIC_KEYWORD - static std::unordered_set<std::string> keywords; - if (keywords.empty()) { - keywords.insert(keyAPPEND); - keywords.insert(keyARGS); - keywords.insert(keyBYPRODUCTS); - keywords.insert(keyCOMMAND); - keywords.insert(keyCOMMAND_EXPAND_LISTS); - keywords.insert(keyCOMMENT); - keywords.insert(keyDEPENDS); - keywords.insert(keyDEPFILE); - keywords.insert(keyIMPLICIT_DEPENDS); - keywords.insert(keyJOB_POOL); - keywords.insert(keyMAIN_DEPENDENCY); - keywords.insert(keyOUTPUT); - keywords.insert(keyOUTPUTS); - keywords.insert(keyPOST_BUILD); - keywords.insert(keyPRE_BUILD); - keywords.insert(keyPRE_LINK); - keywords.insert(keySOURCE); - keywords.insert(keyTARGET); - keywords.insert(keyUSES_TERMINAL); - keywords.insert(keyVERBATIM); - keywords.insert(keyWORKING_DIRECTORY); - } + static std::unordered_set<std::string> const keywords{ + keyAPPEND, + keyARGS, + keyBYPRODUCTS, + keyCOMMAND, + keyCOMMAND_EXPAND_LISTS, + keyCOMMENT, + keyDEPENDS, + keyDEPFILE, + keyIMPLICIT_DEPENDS, + keyJOB_POOL, + keyMAIN_DEPENDENCY, + keyOUTPUT, + keyOUTPUTS, + keyPOST_BUILD, + keyPRE_BUILD, + keyPRE_LINK, + keySOURCE, + keyTARGET, + keyUSES_TERMINAL, + keyVERBATIM, + keyWORKING_DIRECTORY + }; for (std::string const& copy : args) { if (keywords.count(copy)) { diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx index bf28702..b0c462b 100644 --- a/Source/cmAddTestCommand.cxx +++ b/Source/cmAddTestCommand.cxx @@ -58,6 +58,7 @@ bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args) std::vector<std::string> configurations; std::string working_directory; std::vector<std::string> command; + bool command_expand_lists = false; // Read the arguments. enum Doing @@ -88,6 +89,13 @@ bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args) return false; } doing = DoingWorkingDirectory; + } else if (args[i] == "COMMAND_EXPAND_LISTS") { + if (command_expand_lists) { + this->SetError(" may be given at most one COMMAND_EXPAND_LISTS."); + return false; + } + command_expand_lists = true; + doing = DoingNone; } else if (doing == DoingName) { name = args[i]; doing = DoingNone; @@ -134,6 +142,7 @@ bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args) if (!working_directory.empty()) { test->SetProperty("WORKING_DIRECTORY", working_directory.c_str()); } + test->SetCommandExpandLists(command_expand_lists); this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations)); return true; diff --git a/Source/cmBinUtilsLinker.cxx b/Source/cmBinUtilsLinker.cxx new file mode 100644 index 0000000..3dac85c --- /dev/null +++ b/Source/cmBinUtilsLinker.cxx @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinker.h" +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsLinker::cmBinUtilsLinker(cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsLinker::SetError(const std::string& e) +{ + this->Archive->SetError(e); +} diff --git a/Source/cmBinUtilsLinker.h b/Source/cmBinUtilsLinker.h new file mode 100644 index 0000000..29853a5 --- /dev/null +++ b/Source/cmBinUtilsLinker.h @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinker_h +#define cmBinUtilsLinker_h + +#include "cmStateTypes.h" + +#include <string> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinker +{ +public: + cmBinUtilsLinker(cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsLinker() = default; + + virtual bool Prepare() { return true; } + + virtual bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& e); +}; + +#endif // cmBinUtilsLinker_h diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..40de592 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx @@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsLinuxELFGetRuntimeDependenciesTool:: + cmBinUtilsLinuxELFGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsLinuxELFGetRuntimeDependenciesTool::SetError( + const std::string& error) +{ + this->Archive->SetError(error); +} diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..d514e7f --- /dev/null +++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h +#define cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinuxELFGetRuntimeDependenciesTool +{ +public: + cmBinUtilsLinuxELFGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsLinuxELFGetRuntimeDependenciesTool() = default; + + virtual bool GetFileInfo(std::string const& file, + std::vector<std::string>& needed, + std::vector<std::string>& rpaths, + std::vector<std::string>& runpaths) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& e); +}; + +#endif // cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx new file mode 100644 index 0000000..4fb15f2 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFLinker.cxx @@ -0,0 +1,177 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinuxELFLinker.h" +#include "cmAlgorithms.h" +#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h" +#include "cmLDConfigLDConfigTool.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" + +#include <cmsys/RegularExpression.hxx> + +#include <memory> +#include <sstream> + +static std::string ReplaceOrigin(const std::string& rpath, + const std::string& origin) +{ + static const cmsys::RegularExpression originRegex( + "(\\$ORIGIN)([^a-zA-Z0-9_]|$)"); + static const cmsys::RegularExpression originCurlyRegex("\\${ORIGIN}"); + + cmsys::RegularExpressionMatch match; + if (originRegex.find(rpath.c_str(), match)) { + std::string begin = rpath.substr(0, match.start(1)); + std::string end = rpath.substr(match.end(1)); + return begin + origin + end; + } + if (originCurlyRegex.find(rpath.c_str(), match)) { + std::string begin = rpath.substr(0, match.start()); + std::string end = rpath.substr(match.end()); + return begin + origin + end; + } + return rpath; +} + +cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinker(archive) +{ +} + +bool cmBinUtilsLinuxELFLinker::Prepare() +{ + std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); + if (tool.empty()) { + tool = "objdump"; + } + if (tool == "objdump") { + this->Tool = + cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>( + this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; + this->SetError(e.str()); + return false; + } + + std::string ldConfigTool = + this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL"); + if (ldConfigTool.empty()) { + ldConfigTool = "ldconfig"; + } + if (ldConfigTool == "ldconfig") { + this->LDConfigTool = + cm::make_unique<cmLDConfigLDConfigTool>(this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool; + this->SetError(e.str()); + return false; + } + + return true; +} + +bool cmBinUtilsLinuxELFLinker::ScanDependencies( + std::string const& file, cmStateEnums::TargetType /* unused */) +{ + std::vector<std::string> parentRpaths; + return this->ScanDependencies(file, parentRpaths); +} + +bool cmBinUtilsLinuxELFLinker::ScanDependencies( + std::string const& file, std::vector<std::string> const& parentRpaths) +{ + std::string origin = cmSystemTools::GetFilenamePath(file); + std::vector<std::string> needed; + std::vector<std::string> rpaths; + std::vector<std::string> runpaths; + if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) { + return false; + } + for (auto& runpath : runpaths) { + runpath = ReplaceOrigin(runpath, origin); + } + for (auto& rpath : rpaths) { + rpath = ReplaceOrigin(rpath, origin); + } + + std::vector<std::string> searchPaths; + if (!runpaths.empty()) { + searchPaths = runpaths; + } else { + searchPaths = rpaths; + searchPaths.insert(searchPaths.end(), parentRpaths.begin(), + parentRpaths.end()); + } + + std::vector<std::string> ldConfigPaths; + if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) { + return false; + } + searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(), + ldConfigPaths.end()); + + for (auto const& dep : needed) { + if (!this->Archive->IsPreExcluded(dep)) { + std::string path; + bool resolved = false; + if (dep.find('/') != std::string::npos) { + this->SetError("Paths to dependencies are not supported"); + return false; + } + if (!this->ResolveDependency(dep, searchPaths, path, resolved)) { + return false; + } + if (resolved) { + if (!this->Archive->IsPostExcluded(path)) { + bool unique; + this->Archive->AddResolvedPath(dep, path, unique); + if (unique && !this->ScanDependencies(path, rpaths)) { + return false; + } + } + } else { + this->Archive->AddUnresolvedPath(dep); + } + } + } + + return true; +} + +bool cmBinUtilsLinuxELFLinker::ResolveDependency( + std::string const& name, std::vector<std::string> const& searchPaths, + std::string& path, bool& resolved) +{ + for (auto const& searchPath : searchPaths) { + path = searchPath + '/' + name; + if (cmSystemTools::PathExists(path)) { + resolved = true; + return true; + } + } + + for (auto const& searchPath : this->Archive->GetSearchDirectories()) { + path = searchPath + '/' + name; + if (cmSystemTools::PathExists(path)) { + std::ostringstream warning; + warning << "Dependency " << name << " found in search directory:\n " + << searchPath + << "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for " + << "more information."; + this->Archive->GetMakefile()->IssueMessage(MessageType::WARNING, + warning.str()); + resolved = true; + return true; + } + } + + resolved = false; + return true; +} diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h new file mode 100644 index 0000000..348edc4 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFLinker.h @@ -0,0 +1,44 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinuxELFLinker_h +#define cmBinUtilsLinuxELFLinker_h + +#include "cmBinUtilsLinker.h" +#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h" +#include "cmLDConfigTool.h" +#include "cmStateTypes.h" + +#include <memory> // IWYU pragma: keep +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinuxELFLinker : public cmBinUtilsLinker +{ +public: + cmBinUtilsLinuxELFLinker(cmRuntimeDependencyArchive* archive); + + bool Prepare() override; + + bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) override; + +private: + std::unique_ptr<cmBinUtilsLinuxELFGetRuntimeDependenciesTool> Tool; + std::unique_ptr<cmLDConfigTool> LDConfigTool; + bool HaveLDConfigPaths = false; + std::vector<std::string> LDConfigPaths; + + bool ScanDependencies(std::string const& file, + std::vector<std::string> const& parentRpaths); + + bool ResolveDependency(std::string const& name, + std::vector<std::string> const& searchPaths, + std::string& path, bool& resolved); + + bool GetLDConfigPaths(); +}; + +#endif // cmBinUtilsLinuxELFLinker_h diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..3bf7bf8 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx @@ -0,0 +1,84 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool:: + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinuxELFGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::GetFileInfo( + std::string const& file, std::vector<std::string>& needed, + std::vector<std::string>& rpaths, std::vector<std::string>& runpaths) +{ + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) { + this->SetError("Could not find objdump"); + return false; + } + command.emplace_back("-p"); + command.push_back(file); + builder.AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$"); + static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$"); + static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (neededRegex.find(line.c_str(), match)) { + needed.push_back(match.match(1)); + } else if (rpathRegex.find(line.c_str(), match)) { + std::vector<std::string> rpathSplit = + cmSystemTools::SplitString(match.match(1), ':'); + rpaths.reserve(rpaths.size() + rpathSplit.size()); + for (auto const& rpath : rpathSplit) { + rpaths.push_back(rpath); + } + } else if (runpathRegex.find(line.c_str(), match)) { + std::vector<std::string> runpathSplit = + cmSystemTools::SplitString(match.match(1), ':'); + runpaths.reserve(runpaths.size() + runpathSplit.size()); + for (auto const& runpath : runpathSplit) { + runpaths.push_back(runpath); + } + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run objdump on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..286337f --- /dev/null +++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h @@ -0,0 +1,26 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h +#define cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h + +#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool + : public cmBinUtilsLinuxELFGetRuntimeDependenciesTool +{ +public: + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(std::string const& file, std::vector<std::string>& needed, + std::vector<std::string>& rpaths, + std::vector<std::string>& runpaths) override; +}; + +#endif // cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..a296a47 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx @@ -0,0 +1,19 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h" + +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsMacOSMachOGetRuntimeDependenciesTool:: + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::SetError( + const std::string& error) +{ + this->Archive->SetError(error); +} diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..dbb2882 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h @@ -0,0 +1,29 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h +#define cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsMacOSMachOGetRuntimeDependenciesTool +{ +public: + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsMacOSMachOGetRuntimeDependenciesTool() = default; + + virtual bool GetFileInfo(std::string const& file, + std::vector<std::string>& libs, + std::vector<std::string>& rpaths) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& error); +}; + +#endif // cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx new file mode 100644 index 0000000..e219847 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOLinker.cxx @@ -0,0 +1,228 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsMacOSMachOLinker.h" + +#include "cmAlgorithms.h" +#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" + +#include <sstream> +#include <string> +#include <vector> + +cmBinUtilsMacOSMachOLinker::cmBinUtilsMacOSMachOLinker( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinker(archive) +{ +} + +bool cmBinUtilsMacOSMachOLinker::Prepare() +{ + std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); + if (tool.empty()) { + tool = "otool"; + } + if (tool == "otool") { + this->Tool = + cm::make_unique<cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool>( + this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; + this->SetError(e.str()); + return false; + } + + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ScanDependencies( + std::string const& file, cmStateEnums::TargetType type) +{ + std::string executableFile; + if (type == cmStateEnums::EXECUTABLE) { + executableFile = file; + } else { + executableFile = this->Archive->GetBundleExecutable(); + } + std::string executablePath; + if (!executableFile.empty()) { + executablePath = cmSystemTools::GetFilenamePath(executableFile); + } + return this->ScanDependencies(file, executablePath); +} + +bool cmBinUtilsMacOSMachOLinker::ScanDependencies( + std::string const& file, std::string const& executablePath) +{ + std::vector<std::string> libs, rpaths; + if (!this->Tool->GetFileInfo(file, libs, rpaths)) { + return false; + } + + std::string loaderPath = cmSystemTools::GetFilenamePath(file); + return this->GetFileDependencies(libs, executablePath, loaderPath, rpaths); +} + +bool cmBinUtilsMacOSMachOLinker::GetFileDependencies( + std::vector<std::string> const& names, std::string const& executablePath, + std::string const& loaderPath, std::vector<std::string> const& rpaths) +{ + for (std::string const& name : names) { + if (!this->Archive->IsPreExcluded(name)) { + std::string path; + bool resolved; + if (!this->ResolveDependency(name, executablePath, loaderPath, rpaths, + path, resolved)) { + return false; + } + if (resolved) { + if (!this->Archive->IsPostExcluded(path)) { + auto filename = cmSystemTools::GetFilenameName(path); + bool unique; + this->Archive->AddResolvedPath(filename, path, unique); + if (unique && !this->ScanDependencies(path, executablePath)) { + return false; + } + } + } else { + this->Archive->AddUnresolvedPath(name); + } + } + } + + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveDependency( + std::string const& name, std::string const& executablePath, + std::string const& loaderPath, std::vector<std::string> const& rpaths, + std::string& path, bool& resolved) +{ + resolved = false; + if (cmHasLiteralPrefix(name, "@rpath/")) { + if (!this->ResolveRPathDependency(name, executablePath, loaderPath, rpaths, + path, resolved)) { + return false; + } + } else if (cmHasLiteralPrefix(name, "@loader_path/")) { + if (!this->ResolveLoaderPathDependency(name, loaderPath, path, resolved)) { + return false; + } + } else if (cmHasLiteralPrefix(name, "@executable_path/")) { + if (!this->ResolveExecutablePathDependency(name, executablePath, path, + resolved)) { + return false; + } + } else { + resolved = true; + path = name; + } + + if (resolved && !cmSystemTools::FileIsFullPath(path)) { + this->SetError("Resolved path is not absolute"); + return false; + } + + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveExecutablePathDependency( + std::string const& name, std::string const& executablePath, + std::string& path, bool& resolved) +{ + if (executablePath.empty()) { + resolved = false; + return true; + } + + // 16 is == "@executable_path".length() + path = name; + path.replace(0, 16, executablePath); + + if (!cmSystemTools::PathExists(path)) { + resolved = false; + return true; + } + + resolved = true; + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveLoaderPathDependency( + std::string const& name, std::string const& loaderPath, std::string& path, + bool& resolved) +{ + if (loaderPath.empty()) { + resolved = false; + return true; + } + + // 12 is "@loader_path".length(); + path = name; + path.replace(0, 12, loaderPath); + + if (!cmSystemTools::PathExists(path)) { + resolved = false; + return true; + } + + resolved = true; + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency( + std::string const& name, std::string const& executablePath, + std::string const& loaderPath, std::vector<std::string> const& rpaths, + std::string& path, bool& resolved) +{ + for (std::string const& rpath : rpaths) { + std::string searchFile = name; + searchFile.replace(0, 6, rpath); + if (cmHasLiteralPrefix(searchFile, "@loader_path/")) { + if (!this->ResolveLoaderPathDependency(searchFile, loaderPath, path, + resolved)) { + return false; + } + if (resolved) { + return true; + } + } else if (cmHasLiteralPrefix(searchFile, "@executable_path/")) { + if (!this->ResolveExecutablePathDependency(searchFile, executablePath, + path, resolved)) { + return false; + } + if (resolved) { + return true; + } + } else if (cmSystemTools::PathExists(searchFile)) { + /* + * paraphrasing @ben.boeckel: + * if /b/libB.dylib is supposed to be used, + * /a/libbB.dylib will be found first if it exists. CMake tries to + * sort rpath directories to avoid this, but sometimes there is no + * right answer. + * + * I believe it is possible to resolve this using otools -l + * then checking the LC_LOAD_DYLIB command whose name is + * equal to the value of search_file, UNLESS the build + * specifically sets the RPath to paths that will match + * duplicate libs; at this point can we just point to + * user error, or is there a reason why the advantages + * to this scenario outweigh its disadvantages? + * + * Also priority seems to be the order as passed in when compiled + * so as long as this method's resolution guarantees priority + * in that manner further checking should not be necessary? + */ + path = searchFile; + resolved = true; + return true; + } + } + + resolved = false; + return true; +} diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h new file mode 100644 index 0000000..0350d1e --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOLinker.h @@ -0,0 +1,59 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsMacOSMachOLinker_h +#define cmBinUtilsMacOSMachOLinker_h + +#include "cmBinUtilsLinker.h" +#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h" +#include "cmStateTypes.h" + +#include <memory> // IWYU pragma: keep +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsMacOSMachOLinker : public cmBinUtilsLinker +{ +public: + cmBinUtilsMacOSMachOLinker(cmRuntimeDependencyArchive* archive); + + bool Prepare() override; + + bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) override; + +private: + std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool; + + bool ScanDependencies(std::string const& file, + std::string const& executablePath); + + bool GetFileDependencies(std::vector<std::string> const& names, + std::string const& executablePath, + std::string const& loaderPath, + std::vector<std::string> const& rpaths); + + bool ResolveDependency(std::string const& name, + std::string const& executablePath, + std::string const& loaderPath, + std::vector<std::string> const& rpaths, + std::string& path, bool& resolved); + + bool ResolveExecutablePathDependency(std::string const& name, + std::string const& executablePath, + std::string& path, bool& resolved); + + bool ResolveLoaderPathDependency(std::string const& name, + std::string const& loaderPath, + std::string& path, bool& resolved); + + bool ResolveRPathDependency(std::string const& name, + std::string const& executablePath, + std::string const& loaderPath, + std::vector<std::string> const& rpaths, + std::string& path, bool& resolved); +}; + +#endif // cmBinUtilsMacOSMachOLinker_h diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..bab2382 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx @@ -0,0 +1,100 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h" + +#include "cmRuntimeDependencyArchive.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool:: + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo( + std::string const& file, std::vector<std::string>& libs, + std::vector<std::string>& rpaths) +{ + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("otool", command)) { + this->SetError("Could not find otool"); + return false; + } + command.emplace_back("-l"); + command.emplace_back(file); + + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start otool process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression rpathRegex("^ *cmd LC_RPATH$"); + static const cmsys::RegularExpression loadDylibRegex( + "^ *cmd LC_LOAD_DYLIB$"); + static const cmsys::RegularExpression pathRegex( + "^ *path (.*) \\(offset [0-9]+\\)$"); + static const cmsys::RegularExpression nameRegex( + "^ *name (.*) \\(offset [0-9]+\\)$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch cmdMatch; + if (rpathRegex.find(line.c_str(), cmdMatch)) { + if (!std::getline(*process.OutputStream(), line) || + !std::getline(*process.OutputStream(), line)) { + this->SetError("Invalid output from otool"); + return false; + } + + cmsys::RegularExpressionMatch pathMatch; + if (pathRegex.find(line.c_str(), pathMatch)) { + rpaths.push_back(pathMatch.match(1)); + } else { + this->SetError("Invalid output from otool"); + return false; + } + } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) { + if (!std::getline(*process.OutputStream(), line) || + !std::getline(*process.OutputStream(), line)) { + this->SetError("Invalid output from otool"); + return false; + } + + cmsys::RegularExpressionMatch nameMatch; + if (nameRegex.find(line.c_str(), nameMatch)) { + libs.push_back(nameMatch.match(1)); + } else { + this->SetError("Invalid output from otool"); + return false; + } + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on otool process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run otool on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..12bcbc1 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h +#define cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h + +#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool + : public cmBinUtilsMacOSMachOGetRuntimeDependenciesTool +{ +public: + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(std::string const& file, std::vector<std::string>& libs, + std::vector<std::string>& rpaths) override; +}; + +#endif // cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..2b35e30 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx @@ -0,0 +1,67 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool:: + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::GetFileInfo( + const std::string& file, std::vector<std::string>& needed) +{ + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) { + this->SetError("Could not find dumpbin"); + return false; + } + command.emplace_back("/dependents"); + command.push_back(file); + builder.AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start dumpbin process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression regex( + "^ ([^\n]*\\.[Dd][Ll][Ll])\r$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (regex.find(line.c_str(), match)) { + needed.push_back(match.match(1)); + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on dumpbin process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run dumpbin on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..4c17f8d --- /dev/null +++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h +#define cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h + +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool + : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool +{ +public: + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(const std::string& file, + std::vector<std::string>& needed) override; +}; + +#endif // cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..f5a4431 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx @@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsWindowsPEGetRuntimeDependenciesTool:: + cmBinUtilsWindowsPEGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsWindowsPEGetRuntimeDependenciesTool::SetError( + const std::string& error) +{ + this->Archive->SetError(error); +} diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..e9e402b --- /dev/null +++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h @@ -0,0 +1,28 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h +#define cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPEGetRuntimeDependenciesTool +{ +public: + cmBinUtilsWindowsPEGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsWindowsPEGetRuntimeDependenciesTool() = default; + + virtual bool GetFileInfo(const std::string& file, + std::vector<std::string>& needed) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& error); +}; + +#endif // cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx new file mode 100644 index 0000000..796e9ed --- /dev/null +++ b/Source/cmBinUtilsWindowsPELinker.cxx @@ -0,0 +1,121 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPELinker.h" +#include "cmAlgorithms.h" +#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h" +#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" + +#include <memory> +#include <sstream> +#include <vector> + +#ifdef _WIN32 +# include <windows.h> +#endif + +cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinker(archive) +{ +} + +bool cmBinUtilsWindowsPELinker::Prepare() +{ + std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); + if (tool.empty()) { + std::vector<std::string> command; + if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) { + tool = "dumpbin"; + } else { + tool = "objdump"; + } + } + if (tool == "dumpbin") { + this->Tool = + cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>( + this->Archive); + } else if (tool == "objdump") { + this->Tool = + cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>( + this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; + this->SetError(e.str()); + return false; + } + + return true; +} + +bool cmBinUtilsWindowsPELinker::ScanDependencies( + std::string const& file, cmStateEnums::TargetType /* unused */) +{ + std::vector<std::string> needed; + if (!this->Tool->GetFileInfo(file, needed)) { + return false; + } + for (auto& n : needed) { + n = cmSystemTools::LowerCase(n); + } + std::string origin = cmSystemTools::GetFilenamePath(file); + + for (auto const& lib : needed) { + if (!this->Archive->IsPreExcluded(lib)) { + std::string path; + bool resolved = false; + if (!this->ResolveDependency(lib, origin, path, resolved)) { + return false; + } + if (resolved) { + if (!this->Archive->IsPostExcluded(path)) { + bool unique; + this->Archive->AddResolvedPath(lib, path, unique); + if (unique && + !this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) { + return false; + } + } + } else { + this->Archive->AddUnresolvedPath(lib); + } + } + } + + return true; +} + +bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name, + std::string const& origin, + std::string& path, + bool& resolved) +{ + auto dirs = this->Archive->GetSearchDirectories(); + +#ifdef _WIN32 + char buf[MAX_PATH]; + unsigned int len; + if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) { + dirs.insert(dirs.begin(), std::string(buf, len)); + } + if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) { + dirs.insert(dirs.begin(), std::string(buf, len)); + } +#endif + + dirs.insert(dirs.begin(), origin); + + for (auto const& searchPath : dirs) { + path = searchPath + '/' + name; + if (cmSystemTools::PathExists(path)) { + resolved = true; + return true; + } + } + + resolved = false; + return true; +} diff --git a/Source/cmBinUtilsWindowsPELinker.h b/Source/cmBinUtilsWindowsPELinker.h new file mode 100644 index 0000000..d742195 --- /dev/null +++ b/Source/cmBinUtilsWindowsPELinker.h @@ -0,0 +1,33 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPELinker_h +#define cmBinUtilsWindowsPELinker_h + +#include "cmBinUtilsLinker.h" +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" +#include "cmStateTypes.h" + +#include <memory> // IWYU pragma: keep +#include <string> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPELinker : public cmBinUtilsLinker +{ +public: + cmBinUtilsWindowsPELinker(cmRuntimeDependencyArchive* archive); + + bool Prepare() override; + + bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) override; + +private: + std::unique_ptr<cmBinUtilsWindowsPEGetRuntimeDependenciesTool> Tool; + + bool ResolveDependency(std::string const& name, std::string const& origin, + std::string& path, bool& resolved); +}; + +#endif // cmBinUtilsWindowsPELinker_h diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..1f27003 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx @@ -0,0 +1,67 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool:: + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::GetFileInfo( + const std::string& file, std::vector<std::string>& needed) +{ + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) { + this->SetError("Could not find objdump"); + return false; + } + command.emplace_back("-p"); + command.push_back(file); + builder.AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression regex( + "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])\r$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (regex.find(line.c_str(), match)) { + needed.push_back(match.match(1)); + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run objdump on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..1d1a5b0 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h +#define cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h + +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool + : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool +{ +public: + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(const std::string& file, + std::vector<std::string>& needed) override; +}; + +#endif // cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 358f095..e8fc350 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -620,7 +620,7 @@ bool cmCacheManager::CacheIterator::GetValueAsBool() const std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const { - return this->Properties.GetPropertyList(); + return this->Properties.GetKeys(); } const char* cmCacheManager::CacheEntry::GetProperty( diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx index 5fafaf9..894447c 100644 --- a/Source/cmDefinitions.cxx +++ b/Source/cmDefinitions.cxx @@ -57,8 +57,7 @@ bool cmDefinitions::HasKey(const std::string& key, StackIter begin, void cmDefinitions::Set(const std::string& key, const char* value) { - Def def(value); - this->Map[key] = def; + this->Map[key] = Def(value); } std::vector<std::string> cmDefinitions::UnusedKeys() const diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index a12e0c4..c366183 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -12,7 +12,6 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmProperty.h" #include "cmPropertyMap.h" #include "cmStateTypes.h" #include "cmSystemTools.h" @@ -1205,12 +1204,9 @@ bool cmExportFileGenerator::PopulateExportProperties( std::string& errorMessage) { auto& targetProperties = gte->Target->GetProperties(); - const auto& exportProperties = targetProperties.find("EXPORT_PROPERTIES"); - if (exportProperties != targetProperties.end()) { - std::vector<std::string> propsToExport; - cmSystemTools::ExpandListArgument(exportProperties->second.GetValue(), - propsToExport); - for (auto& prop : propsToExport) { + if (const char* exportProperties = + targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) { + for (auto& prop : cmSystemTools::ExpandedListArgument(exportProperties)) { /* Black list reserved properties */ if (cmSystemTools::StringStartsWith(prop, "IMPORTED_") || cmSystemTools::StringStartsWith(prop, "INTERFACE_")) { diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 7a3954e..980ad21 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -12,7 +12,9 @@ #include <assert.h> #include <cmath> #include <ctype.h> +#include <map> #include <memory> // IWYU pragma: keep +#include <set> #include <sstream> #include <stdio.h> #include <stdlib.h> @@ -34,6 +36,8 @@ #include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmState.h" #include "cmSystemTools.h" #include "cmTimestamp.h" #include "cm_sys_stat.h" @@ -184,6 +188,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "CREATE_LINK") { return this->HandleCreateLinkCommand(args); } + if (subCommand == "GET_RUNTIME_DEPENDENCIES") { + return this->HandleGetRuntimeDependenciesCommand(args); + } std::string e = "does not recognize sub-command " + subCommand; this->SetError(e); @@ -2690,3 +2697,171 @@ bool cmFileCommand::HandleCreateLinkCommand( return true; } + +bool cmFileCommand::HandleGetRuntimeDependenciesCommand( + std::vector<std::string> const& args) +{ + static const std::set<std::string> supportedPlatforms = { "Windows", "Linux", + "Darwin" }; + std::string platform = + this->Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (!supportedPlatforms.count(platform)) { + std::ostringstream e; + e << "GET_RUNTIME_DEPENDENCIES is not supported on system \"" << platform + << "\""; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (this->Makefile->GetState()->GetMode() == cmState::Project) { + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, + "You have used file(GET_RUNTIME_DEPENDENCIES)" + " in project mode. This is probably not what " + "you intended to do. Instead, please consider" + " using it in an install(CODE) or " + "install(SCRIPT) command. For example:" + "\n install(CODE [[" + "\n file(GET_RUNTIME_DEPENDENCIES" + "\n # ..." + "\n )" + "\n ]])"); + } + + struct Arguments + { + std::string ResolvedDependenciesVar; + std::string UnresolvedDependenciesVar; + std::string ConflictingDependenciesPrefix; + std::string BundleExecutable; + std::vector<std::string> Executables; + std::vector<std::string> Libraries; + std::vector<std::string> Directories; + std::vector<std::string> Modules; + std::vector<std::string> PreIncludeRegexes; + std::vector<std::string> PreExcludeRegexes; + std::vector<std::string> PostIncludeRegexes; + std::vector<std::string> PostExcludeRegexes; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar) + .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s, + &Arguments::UnresolvedDependenciesVar) + .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s, + &Arguments::ConflictingDependenciesPrefix) + .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable) + .Bind("EXECUTABLES"_s, &Arguments::Executables) + .Bind("LIBRARIES"_s, &Arguments::Libraries) + .Bind("MODULES"_s, &Arguments::Modules) + .Bind("DIRECTORIES"_s, &Arguments::Directories) + .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes) + .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes) + .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes) + .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes); + + std::vector<std::string> unrecognizedArguments; + std::vector<std::string> keywordsMissingValues; + auto parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, + &keywordsMissingValues); + auto argIt = unrecognizedArguments.begin(); + if (argIt != unrecognizedArguments.end()) { + std::ostringstream e; + e << "Unrecognized argument: \"" << *argIt << "\""; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + argIt = keywordsMissingValues.begin(); + if (argIt != keywordsMissingValues.end()) { + std::ostringstream e; + e << "Keyword missing value: " << *argIt; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + cmRuntimeDependencyArchive archive( + this, parsedArgs.Directories, parsedArgs.BundleExecutable, + parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes, + parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes); + if (!archive.Prepare()) { + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (!archive.GetRuntimeDependencies( + parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) { + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + std::vector<std::string> deps, unresolvedDeps, conflictingDeps; + for (auto const& val : archive.GetResolvedPaths()) { + bool unique = true; + auto it = val.second.begin(); + assert(it != val.second.end()); + auto const& firstPath = *it; + while (++it != val.second.end()) { + if (!cmSystemTools::SameFile(firstPath, *it)) { + unique = false; + break; + } + } + + if (unique) { + deps.push_back(firstPath); + } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) { + conflictingDeps.push_back(val.first); + std::vector<std::string> paths; + paths.insert(paths.begin(), val.second.begin(), val.second.end()); + std::string varName = + parsedArgs.ConflictingDependenciesPrefix + "_" + val.first; + std::string pathsStr = cmJoin(paths, ";"); + this->Makefile->AddDefinition(varName, pathsStr.c_str()); + } else { + std::ostringstream e; + e << "Multiple conflicting paths found for " << val.first << ":"; + for (auto const& path : val.second) { + e << "\n " << path; + } + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + if (!archive.GetUnresolvedPaths().empty()) { + if (!parsedArgs.UnresolvedDependenciesVar.empty()) { + unresolvedDeps.insert(unresolvedDeps.begin(), + archive.GetUnresolvedPaths().begin(), + archive.GetUnresolvedPaths().end()); + } else { + auto it = archive.GetUnresolvedPaths().begin(); + assert(it != archive.GetUnresolvedPaths().end()); + std::ostringstream e; + e << "Could not resolve file " << *it; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + + if (!parsedArgs.ResolvedDependenciesVar.empty()) { + std::string val = cmJoin(deps, ";"); + this->Makefile->AddDefinition(parsedArgs.ResolvedDependenciesVar, + val.c_str()); + } + if (!parsedArgs.UnresolvedDependenciesVar.empty()) { + std::string val = cmJoin(unresolvedDeps, ";"); + this->Makefile->AddDefinition(parsedArgs.UnresolvedDependenciesVar, + val.c_str()); + } + if (!parsedArgs.ConflictingDependenciesPrefix.empty()) { + std::string val = cmJoin(conflictingDeps, ";"); + this->Makefile->AddDefinition( + parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val.c_str()); + } + return true; +} diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index 12c5115..cfff894 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -62,6 +62,8 @@ protected: bool HandleSizeCommand(std::vector<std::string> const& args); bool HandleReadSymlinkCommand(std::vector<std::string> const& args); bool HandleCreateLinkCommand(std::vector<std::string> const& args); + bool HandleGetRuntimeDependenciesCommand( + std::vector<std::string> const& args); private: void AddEvaluationFile(const std::string& inputName, diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 036a07d..de0f371 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -5032,13 +5032,7 @@ void cmGeneratorTarget::ComputeVersionedName(std::string& vName, std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const { - cmPropertyMap const& propsObject = this->Target->GetProperties(); - std::vector<std::string> props; - props.reserve(propsObject.size()); - for (auto const& it : propsObject) { - props.push_back(it.first); - } - return props; + return this->Target->GetProperties().GetKeys(); } void cmGeneratorTarget::ReportPropertyOrigin( diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 7c5a55b..0774436 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -16,6 +16,8 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -632,17 +634,34 @@ void cmInstallTargetGenerator::AddRPathCheckRule( return; } - // Get the install RPATH from the link information. - std::string newRpath = cli->GetChrpathString(); - // Write a rule to remove the installed file if its rpath is not the // new rpath. This is needed for existing build/install trees when // the installed rpath changes but the file is not rebuilt. - /* clang-format off */ os << indent << "file(RPATH_CHECK\n" - << indent << " FILE \"" << toDestDirPath << "\"\n" - << indent << " RPATH \"" << newRpath << "\")\n"; - /* clang-format on */ + << indent << " FILE \"" << toDestDirPath << "\"\n"; + + // CMP0095: ``RPATH`` entries are properly escaped in the intermediary + // CMake install script. + switch (this->Target->GetPolicyStatusCMP0095()) { + case cmPolicies::WARN: + // No author warning needed here, we warn later in + // cmInstallTargetGenerator::AddChrpathPatchRule(). + CM_FALLTHROUGH; + case cmPolicies::OLD: { + // Get the install RPATH from the link information. + std::string newRpath = cli->GetChrpathString(); + os << indent << " RPATH \"" << newRpath << "\")\n"; + break; + } + default: { + // Get the install RPATH from the link information and + // escape any CMake syntax in the install RPATH. + std::string escapedNewRpath = + cmOutputConverter::EscapeForCMake(cli->GetChrpathString()); + os << indent << " RPATH " << escapedNewRpath << ")\n"; + break; + } + } } void cmInstallTargetGenerator::AddChrpathPatchRule( @@ -731,11 +750,28 @@ void cmInstallTargetGenerator::AddChrpathPatchRule( return; } + // Escape any CMake syntax in the RPATHs. + std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath); + std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath); + // Write a rule to run chrpath to set the install-tree RPATH os << indent << "file(RPATH_CHANGE\n" << indent << " FILE \"" << toDestDirPath << "\"\n" - << indent << " OLD_RPATH \"" << oldRpath << "\"\n" - << indent << " NEW_RPATH \"" << newRpath << "\")\n"; + << indent << " OLD_RPATH " << escapedOldRpath << "\n"; + + // CMP0095: ``RPATH`` entries are properly escaped in the intermediary + // CMake install script. + switch (this->Target->GetPolicyStatusCMP0095()) { + case cmPolicies::WARN: + this->IssueCMP0095Warning(newRpath); + CM_FALLTHROUGH; + case cmPolicies::OLD: + os << indent << " NEW_RPATH \"" << newRpath << "\")\n"; + break; + default: + os << indent << " NEW_RPATH " << escapedNewRpath << ")\n"; + break; + } } } @@ -838,3 +874,26 @@ void cmInstallTargetGenerator::AddUniversalInstallRule( << "\"" << this->Target->Target->GetName() << "\" " << "\"" << toDestDirPath << "\")\n"; } + +void cmInstallTargetGenerator::IssueCMP0095Warning( + const std::string& unescapedRpath) +{ + // Reduce warning noise to cases where used RPATHs may actually be affected + // by CMP0095. This filter is meant to skip warnings in cases when + // non-curly-braces syntax (e.g. $ORIGIN) or no keyword is used which has + // worked already before CMP0095. We intend to issue a warning in all cases + // with curly-braces syntax, even if the workaround of double-escaping is in + // place, since we deprecate the need for it with CMP0095. + const bool potentially_affected(unescapedRpath.find("${") != + std::string::npos); + + if (potentially_affected) { + std::ostringstream w; + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n"; + w << "RPATH entries for target '" << this->Target->GetName() << "' " + << "will not be escaped in the intermediary " + << "cmake_install.cmake script."; + this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace()); + } +} diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index ed3ab52..9ccad63 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -104,6 +104,7 @@ protected: const std::string& toDestDirPath); void AddUniversalInstallRule(std::ostream& os, Indent indent, const std::string& toDestDirPath); + void IssueCMP0095Warning(const std::string& unescapedRpath); std::string TargetName; cmGeneratorTarget* Target; diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx index 636a8e1..8d065e1 100644 --- a/Source/cmJsonObjects.cxx +++ b/Source/cmJsonObjects.cxx @@ -14,7 +14,6 @@ #include "cmLinkLineComputer.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmProperty.h" #include "cmPropertyMap.h" #include "cmSourceFile.h" #include "cmState.h" @@ -363,12 +362,12 @@ static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, // Build up the list of properties that may have been specified Json::Value properties = Json::arrayValue; - for (auto& prop : testInfo->GetProperties()) { + for (auto& prop : testInfo->GetProperties().GetList()) { Json::Value entry = Json::objectValue; entry[kKEY_KEY] = prop.first; // Remove config variables from the value too. - auto cge_value = ge.Parse(prop.second.GetValue()); + auto cge_value = ge.Parse(prop.second); const std::string& processed_value = cge_value->Evaluate(lg, config); entry[kVALUE_KEY] = processed_value; properties.append(entry); diff --git a/Source/cmLDConfigLDConfigTool.cxx b/Source/cmLDConfigLDConfigTool.cxx new file mode 100644 index 0000000..586ea96 --- /dev/null +++ b/Source/cmLDConfigLDConfigTool.cxx @@ -0,0 +1,70 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmLDConfigLDConfigTool.h" +#include "cmMakefile.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" +#include "cmUVProcessChain.h" + +#include "cmsys/RegularExpression.hxx" + +#include <istream> +#include <string> +#include <vector> + +cmLDConfigLDConfigTool::cmLDConfigLDConfigTool( + cmRuntimeDependencyArchive* archive) + : cmLDConfigTool(archive) +{ +} + +bool cmLDConfigLDConfigTool::GetLDConfigPaths(std::vector<std::string>& paths) +{ + std::string ldConfigPath = + this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_COMMAND"); + if (ldConfigPath.empty()) { + ldConfigPath = cmSystemTools::FindProgram( + "ldconfig", { "/sbin", "/usr/sbin", "/usr/local/sbin" }); + if (ldConfigPath.empty()) { + this->Archive->SetError("Could not find ldconfig"); + return false; + } + } + + std::vector<std::string> ldConfigCommand; + cmSystemTools::ExpandListArgument(ldConfigPath, ldConfigCommand); + ldConfigCommand.emplace_back("-v"); + ldConfigCommand.emplace_back("-N"); // Don't rebuild the cache. + ldConfigCommand.emplace_back("-X"); // Don't update links. + + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .AddCommand(ldConfigCommand); + auto process = builder.Start(); + if (!process.Valid()) { + this->Archive->SetError("Failed to start ldconfig process"); + return false; + } + + std::string line; + static const cmsys::RegularExpression regex("^([^\t:]*):"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (regex.find(line.c_str(), match)) { + paths.push_back(match.match(1)); + } + } + + if (!process.Wait()) { + this->Archive->SetError("Failed to wait on ldconfig process"); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + this->Archive->SetError("Failed to run ldconfig"); + return false; + } + + return true; +} diff --git a/Source/cmLDConfigLDConfigTool.h b/Source/cmLDConfigLDConfigTool.h new file mode 100644 index 0000000..d945a9b --- /dev/null +++ b/Source/cmLDConfigLDConfigTool.h @@ -0,0 +1,22 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmLDConfigLDConfigTool_h +#define cmLDConfigLDConfigTool_h + +#include "cmLDConfigTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmLDConfigLDConfigTool : public cmLDConfigTool +{ +public: + cmLDConfigLDConfigTool(cmRuntimeDependencyArchive* archive); + + bool GetLDConfigPaths(std::vector<std::string>& paths) override; +}; + +#endif diff --git a/Source/cmLDConfigTool.cxx b/Source/cmLDConfigTool.cxx new file mode 100644 index 0000000..8d5d563 --- /dev/null +++ b/Source/cmLDConfigTool.cxx @@ -0,0 +1,9 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmLDConfigTool.h" + +cmLDConfigTool::cmLDConfigTool(cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} diff --git a/Source/cmLDConfigTool.h b/Source/cmLDConfigTool.h new file mode 100644 index 0000000..c816562 --- /dev/null +++ b/Source/cmLDConfigTool.h @@ -0,0 +1,24 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmLDConfigTool_h +#define cmLDConfigTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmLDConfigTool +{ +public: + cmLDConfigTool(cmRuntimeDependencyArchive* archive); + virtual ~cmLDConfigTool() = default; + + virtual bool GetLDConfigPaths(std::vector<std::string>& paths) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; +}; + +#endif diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index fe5c8af..3abf2dd 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1444,10 +1444,23 @@ void cmLocalGenerator::OutputLinkLibraries( std::string linkLanguage = cli.GetLinkLanguage(); - const std::string& libPathFlag = - this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); - const std::string& libPathTerminator = - this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); + std::string libPathFlag; + if (const char* value = this->Makefile->GetDefinition( + "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) { + libPathFlag = value; + } else { + libPathFlag = + this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); + } + + std::string libPathTerminator; + if (const char* value = this->Makefile->GetDefinition( + "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) { + libPathTerminator = value; + } else { + libPathTerminator = + this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); + } // Add standard libraries for this language. std::string standardLibsVar = "CMAKE_"; diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index 7d88b08..d7bcf7e 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -73,22 +73,10 @@ std::string cmOutputConverter::ConvertDirectorySeparatorsForShell( static bool cmOutputConverterIsShellOperator(const std::string& str) { - static std::set<std::string> shellOperators; - if (shellOperators.empty()) { - shellOperators.insert("<"); - shellOperators.insert(">"); - shellOperators.insert("<<"); - shellOperators.insert(">>"); - shellOperators.insert("|"); - shellOperators.insert("||"); - shellOperators.insert("&&"); - shellOperators.insert("&>"); - shellOperators.insert("1>"); - shellOperators.insert("2>"); - shellOperators.insert("2>&1"); - shellOperators.insert("1>&2"); - } - return shellOperators.count(str) > 0; + static std::set<std::string> const shellOperators{ + "<", ">", "<<", ">>", "|", "||", "&&", "&>", "1>", "2>", "2>&1", "1>&2" + }; + return (shellOperators.count(str) != 0); } std::string cmOutputConverter::EscapeForShell(const std::string& str, diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index b705119..c16a46f 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -279,7 +279,11 @@ class cmMakefile; SELECT(POLICY, CMP0094, \ "FindPython3, FindPython2 and FindPyton use " \ "LOCATION for lookup strategy.", \ - 3, 15, 0, cmPolicies::WARN) + 3, 15, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0095, \ + "RPATH entries are properly escaped in the intermediary CMake " \ + "install script.", \ + 3, 16, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -307,7 +311,8 @@ class cmMakefile; F(CMP0073) \ F(CMP0076) \ F(CMP0081) \ - F(CMP0083) + F(CMP0083) \ + F(CMP0095) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies diff --git a/Source/cmProperty.cxx b/Source/cmProperty.cxx deleted file mode 100644 index 27f0ecd..0000000 --- a/Source/cmProperty.cxx +++ /dev/null @@ -1,26 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmProperty.h" - -void cmProperty::Set(const char* value) -{ - this->Value = value; - this->ValueHasBeenSet = true; -} - -void cmProperty::Append(const char* value, bool asString) -{ - if (!this->Value.empty() && *value && !asString) { - this->Value += ";"; - } - this->Value += value; - this->ValueHasBeenSet = true; -} - -const char* cmProperty::GetValue() const -{ - if (this->ValueHasBeenSet) { - return this->Value.c_str(); - } - return nullptr; -} diff --git a/Source/cmProperty.h b/Source/cmProperty.h index d11c5ef..80f131a 100644 --- a/Source/cmProperty.h +++ b/Source/cmProperty.h @@ -5,8 +5,6 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include <string> - class cmProperty { public: @@ -22,22 +20,6 @@ public: CACHED_VARIABLE, INSTALL }; - - // set this property - void Set(const char* value); - - // append to this property - void Append(const char* value, bool asString = false); - - // get the value - const char* GetValue() const; - - // construct with the value not set - cmProperty() { this->ValueHasBeenSet = false; } - -protected: - std::string Value; - bool ValueHasBeenSet; }; #endif diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx index 3f6d7c8..3ed4c05 100644 --- a/Source/cmPropertyMap.cxx +++ b/Source/cmPropertyMap.cxx @@ -3,40 +3,21 @@ #include "cmPropertyMap.h" #include <algorithm> -#include <assert.h> #include <utility> -cmProperty* cmPropertyMap::GetOrCreateProperty(const std::string& name) +void cmPropertyMap::Clear() { - cmPropertyMap::iterator it = this->find(name); - cmProperty* prop; - if (it == this->end()) { - prop = &(*this)[name]; - } else { - prop = &(it->second); - } - return prop; -} - -std::vector<std::string> cmPropertyMap::GetPropertyList() const -{ - std::vector<std::string> keyList; - for (auto const& i : *this) { - keyList.push_back(i.first); - } - std::sort(keyList.begin(), keyList.end()); - return keyList; + Map_.clear(); } void cmPropertyMap::SetProperty(const std::string& name, const char* value) { if (!value) { - this->erase(name); + Map_.erase(name); return; } - cmProperty* prop = this->GetOrCreateProperty(name); - prop->Set(value); + Map_[name] = value; } void cmPropertyMap::AppendProperty(const std::string& name, const char* value, @@ -47,17 +28,53 @@ void cmPropertyMap::AppendProperty(const std::string& name, const char* value, return; } - cmProperty* prop = this->GetOrCreateProperty(name); - prop->Append(value, asString); + { + std::string& pVal = Map_[name]; + if (!pVal.empty() && !asString) { + pVal += ';'; + } + pVal += value; + } +} + +void cmPropertyMap::RemoveProperty(const std::string& name) +{ + Map_.erase(name); } const char* cmPropertyMap::GetPropertyValue(const std::string& name) const { - assert(!name.empty()); + { + auto it = Map_.find(name); + if (it != Map_.end()) { + return it->second.c_str(); + } + } + return nullptr; +} - cmPropertyMap::const_iterator it = this->find(name); - if (it == this->end()) { - return nullptr; +std::vector<std::string> cmPropertyMap::GetKeys() const +{ + std::vector<std::string> keyList; + keyList.reserve(Map_.size()); + for (auto const& item : Map_) { + keyList.push_back(item.first); + } + std::sort(keyList.begin(), keyList.end()); + return keyList; +} + +std::vector<std::pair<std::string, std::string>> cmPropertyMap::GetList() const +{ + typedef std::pair<std::string, std::string> StringPair; + std::vector<StringPair> kvList; + kvList.reserve(Map_.size()); + for (auto const& item : Map_) { + kvList.emplace_back(item.first, item.second); } - return it->second.GetValue(); + std::sort(kvList.begin(), kvList.end(), + [](StringPair const& a, StringPair const& b) { + return a.first < b.first; + }); + return kvList; } diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h index 5a05150..9aed349 100644 --- a/Source/cmPropertyMap.h +++ b/Source/cmPropertyMap.h @@ -5,25 +5,47 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmProperty.h" - -#include <map> #include <string> +#include <unordered_map> +#include <utility> #include <vector> -class cmPropertyMap : public std::map<std::string, cmProperty> +/** \class cmPropertyMap + * \brief String property map. + */ +class cmPropertyMap { public: - cmProperty* GetOrCreateProperty(const std::string& name); + // -- General + + //! Clear property list + void Clear(); - std::vector<std::string> GetPropertyList() const; + // -- Properties + //! Set the property value void SetProperty(const std::string& name, const char* value); + //! Append to the property value void AppendProperty(const std::string& name, const char* value, bool asString = false); + //! Get the property value const char* GetPropertyValue(const std::string& name) const; + + //! Remove the property @a name from the map + void RemoveProperty(const std::string& name); + + // -- Lists + + //! Get a sorted list of property keys + std::vector<std::string> GetKeys() const; + + //! Get a sorted by key list of property key,value pairs + std::vector<std::pair<std::string, std::string>> GetList() const; + +private: + std::unordered_map<std::string, std::string> Map_; }; #endif diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx new file mode 100644 index 0000000..b4c6c32 --- /dev/null +++ b/Source/cmRuntimeDependencyArchive.cxx @@ -0,0 +1,378 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmRuntimeDependencyArchive.h" + +#include "cmAlgorithms.h" +#include "cmBinUtilsLinuxELFLinker.h" +#include "cmBinUtilsMacOSMachOLinker.h" +#include "cmBinUtilsWindowsPELinker.h" +#include "cmCommand.h" +#include "cmMakefile.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" + +#if defined(_WIN32) +# include "cmGlobalGenerator.h" +# ifdef CMAKE_BUILD_WITH_CMAKE +# include "cmGlobalVisualStudioVersionedGenerator.h" +# endif +# include "cmVSSetupHelper.h" +# include "cmsys/Glob.hxx" +#endif + +#include <algorithm> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#if defined(_WIN32) +static void AddVisualStudioPath(std::vector<std::string>& paths, + const std::string& prefix, + unsigned int version, cmGlobalGenerator* gg) +{ + // If generating for the VS IDE, use the same instance. + std::string vsloc; + bool found = false; +# ifdef CMAKE_BUILD_WITH_CMAKE + if (gg->GetName().find(prefix) == 0) { + cmGlobalVisualStudioVersionedGenerator* vsgen = + static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg); + if (vsgen->GetVSInstance(vsloc)) { + found = true; + } + } +# endif + + // Otherwise, find a VS instance ourselves. + if (!found) { + cmVSSetupAPIHelper vsSetupAPIHelper(version); + if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) { + cmSystemTools::ConvertToUnixSlashes(vsloc); + found = true; + } + } + + if (found) { + cmsys::Glob glob; + glob.SetListDirs(true); + glob.FindFiles(vsloc + "/VC/Tools/MSVC/*"); + for (auto const& vcdir : glob.GetFiles()) { + paths.push_back(vcdir + "/bin/Hostx64/x64"); + paths.push_back(vcdir + "/bin/Hostx86/x64"); + paths.push_back(vcdir + "/bin/Hostx64/x86"); + paths.push_back(vcdir + "/bin/Hostx86/x86"); + } + } +} + +static void AddRegistryPath(std::vector<std::string>& paths, + const std::string& path, cmMakefile* mf) +{ + // We should view the registry as the target application would view + // it. + cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32; + cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64; + if (mf->PlatformIs64Bit()) { + view = cmSystemTools::KeyWOW64_64; + other_view = cmSystemTools::KeyWOW64_32; + } + + // Expand using the view of the target application. + std::string expanded = path; + cmSystemTools::ExpandRegistryValues(expanded, view); + cmSystemTools::GlobDirs(expanded, paths); + + // Executables can be either 32-bit or 64-bit, so expand using the + // alternative view. + expanded = path; + cmSystemTools::ExpandRegistryValues(expanded, other_view); + cmSystemTools::GlobDirs(expanded, paths); +} + +static void AddEnvPath(std::vector<std::string>& paths, const std::string& var, + const std::string& suffix) +{ + std::string value; + if (cmSystemTools::GetEnv(var, value)) { + paths.push_back(value + suffix); + } +} +#endif + +static cmsys::RegularExpression TransformCompile(const std::string& str) +{ + return cmsys::RegularExpression(str); +} + +cmRuntimeDependencyArchive::cmRuntimeDependencyArchive( + cmCommand* command, std::vector<std::string> searchDirectories, + std::string bundleExecutable, + const std::vector<std::string>& preIncludeRegexes, + const std::vector<std::string>& preExcludeRegexes, + const std::vector<std::string>& postIncludeRegexes, + const std::vector<std::string>& postExcludeRegexes) + : Command(command) + , SearchDirectories(std::move(searchDirectories)) + , BundleExecutable(std::move(bundleExecutable)) + , PreIncludeRegexes(preIncludeRegexes.size()) + , PreExcludeRegexes(preExcludeRegexes.size()) + , PostIncludeRegexes(postIncludeRegexes.size()) + , PostExcludeRegexes(postExcludeRegexes.size()) +{ + std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(), + this->PreIncludeRegexes.begin(), TransformCompile); + std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(), + this->PreExcludeRegexes.begin(), TransformCompile); + std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(), + this->PostIncludeRegexes.begin(), TransformCompile); + std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(), + this->PostExcludeRegexes.begin(), TransformCompile); +} + +bool cmRuntimeDependencyArchive::Prepare() +{ + std::string platform = this->GetMakefile()->GetSafeDefinition( + "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM"); + if (platform.empty()) { + std::string systemName = + this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (systemName == "Windows") { + platform = "windows+pe"; + } else if (systemName == "Darwin") { + platform = "macos+macho"; + } else if (systemName == "Linux") { + platform = "linux+elf"; + } + } + if (platform == "linux+elf") { + this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this); + } else if (platform == "windows+pe") { + this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this); + } else if (platform == "macos+macho") { + this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: " + << platform; + this->SetError(e.str()); + return false; + } + + return this->Linker->Prepare(); +} + +bool cmRuntimeDependencyArchive::GetRuntimeDependencies( + const std::vector<std::string>& executables, + const std::vector<std::string>& libraries, + const std::vector<std::string>& modules) +{ + for (auto const& exe : executables) { + if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) { + return false; + } + } + for (auto const& lib : libraries) { + if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) { + return false; + } + } + for (auto const& mod : modules) { + if (!this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY)) { + return false; + } + } + + return true; +} + +void cmRuntimeDependencyArchive::SetError(const std::string& e) +{ + this->Command->SetError(e); +} + +std::string cmRuntimeDependencyArchive::GetBundleExecutable() +{ + return this->BundleExecutable; +} + +const std::vector<std::string>& +cmRuntimeDependencyArchive::GetSearchDirectories() +{ + return this->SearchDirectories; +} + +std::string cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool() +{ + return this->GetMakefile()->GetSafeDefinition( + "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL"); +} + +bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand( + const std::string& search, std::vector<std::string>& command) +{ + // First see if it was supplied by the user + std::string toolCommand = this->GetMakefile()->GetSafeDefinition( + "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND"); + if (!toolCommand.empty()) { + cmSystemTools::ExpandListArgument(toolCommand, command); + return true; + } + + // Now go searching for it + std::vector<std::string> paths; +#ifdef _WIN32 + cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator(); + + // Add newer Visual Studio paths + AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg); + AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg); + + // Add older Visual Studio paths + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin"); + paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"); + paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin"); + paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN"); + paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/" + "../../VC7/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin"); + paths.push_back( + "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"); +#endif + + std::string program = cmSystemTools::FindProgram(search, paths); + if (!program.empty()) { + command = { program }; + return true; + } + + // Couldn't find it + return false; +} + +bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) +{ + cmsys::RegularExpressionMatch match; + + for (auto const& regex : this->PreIncludeRegexes) { + if (regex.find(name.c_str(), match)) { + return false; + } + } + + for (auto const& regex : this->PreExcludeRegexes) { + if (regex.find(name.c_str(), match)) { + return true; + } + } + + return false; +} + +bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) +{ + cmsys::RegularExpressionMatch match; + + for (auto const& regex : this->PostIncludeRegexes) { + if (regex.find(name.c_str(), match)) { + return false; + } + } + + for (auto const& regex : this->PostExcludeRegexes) { + if (regex.find(name.c_str(), match)) { + return true; + } + } + + return false; +} + +void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name, + const std::string& path, + bool& unique) +{ + auto it = + this->ResolvedPaths + .insert(std::pair<std::string, std::set<std::string>>{ name, {} }) + .first; + unique = true; + for (auto const& other : it->second) { + if (cmSystemTools::SameFile(path, other)) { + unique = false; + break; + } + } + it->second.insert(path); +} + +void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name) +{ + this->UnresolvedPaths.insert(name); +} + +cmMakefile* cmRuntimeDependencyArchive::GetMakefile() +{ + return this->Command->GetMakefile(); +} + +const std::map<std::string, std::set<std::string>>& +cmRuntimeDependencyArchive::GetResolvedPaths() +{ + return this->ResolvedPaths; +} + +const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths() +{ + return this->UnresolvedPaths; +} diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h new file mode 100644 index 0000000..ec3ecd4 --- /dev/null +++ b/Source/cmRuntimeDependencyArchive.h @@ -0,0 +1,70 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmRuntimeDependencyArchive_h +#define cmRuntimeDependencyArchive_h + +#include "cmBinUtilsLinker.h" + +#include "cmsys/RegularExpression.hxx" + +#include <map> +#include <memory> // IWYU pragma: keep +#include <set> +#include <string> +#include <vector> + +class cmCommand; +class cmMakefile; + +class cmRuntimeDependencyArchive +{ +public: + explicit cmRuntimeDependencyArchive( + cmCommand* command, std::vector<std::string> searchDirectories, + std::string bundleExecutable, + const std::vector<std::string>& preIncludeRegexes, + const std::vector<std::string>& preExcludeRegexes, + const std::vector<std::string>& postIncludeRegexes, + const std::vector<std::string>& postExcludeRegexes); + bool Prepare(); + bool GetRuntimeDependencies(const std::vector<std::string>& executables, + const std::vector<std::string>& libraries, + const std::vector<std::string>& modules); + + void SetError(const std::string& e); + + std::string GetBundleExecutable(); + const std::vector<std::string>& GetSearchDirectories(); + std::string GetGetRuntimeDependenciesTool(); + bool GetGetRuntimeDependenciesCommand(const std::string& search, + std::vector<std::string>& command); + bool IsPreExcluded(const std::string& name); + bool IsPostExcluded(const std::string& name); + + void AddResolvedPath(const std::string& name, const std::string& path, + bool& unique); + void AddUnresolvedPath(const std::string& name); + + cmMakefile* GetMakefile(); + const std::map<std::string, std::set<std::string>>& GetResolvedPaths(); + const std::set<std::string>& GetUnresolvedPaths(); + +private: + cmCommand* Command; + std::unique_ptr<cmBinUtilsLinker> Linker; + + std::string GetRuntimeDependenciesTool; + std::vector<std::string> GetRuntimeDependenciesCommand; + + std::vector<std::string> SearchDirectories; + std::string BundleExecutable; + std::vector<cmsys::RegularExpression> PreIncludeRegexes; + std::vector<cmsys::RegularExpression> PreExcludeRegexes; + std::vector<cmsys::RegularExpression> PostIncludeRegexes; + std::vector<cmsys::RegularExpression> PostExcludeRegexes; + std::map<std::string, std::set<std::string>> ResolvedPaths; + std::set<std::string> UnresolvedPaths; +}; + +#endif // cmRuntimeDependencyArchive_h diff --git a/Source/cmState.cxx b/Source/cmState.cxx index fa7df0b..091c2e0 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -267,7 +267,7 @@ void cmState::RemoveCacheEntryProperty(std::string const& key, cmStateSnapshot cmState::Reset() { - this->GlobalProperties.clear(); + this->GlobalProperties.Clear(); this->PropertyDefinitions.clear(); this->GlobVerificationManager->Reset(); @@ -289,7 +289,7 @@ cmStateSnapshot cmState::Reset() it->LinkDirectoriesBacktraces.clear(); it->DirectoryEnd = pos; it->NormalTargetNames.clear(); - it->Properties.clear(); + it->Properties.Clear(); it->Children.clear(); } diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 182d3fe..6ca1c9f 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -6,7 +6,6 @@ #include <algorithm> #include <assert.h> #include <iterator> -#include <utility> #include "cmAlgorithms.h" #include "cmProperty.h" @@ -667,12 +666,7 @@ bool cmStateDirectory::GetPropertyAsBool(const std::string& prop) const std::vector<std::string> cmStateDirectory::GetPropertyKeys() const { - std::vector<std::string> keys; - keys.reserve(this->DirectoryState->Properties.size()); - for (auto const& it : this->DirectoryState->Properties) { - keys.push_back(it.first); - } - return keys; + return this->DirectoryState->Properties.GetKeys(); } void cmStateDirectory::AddNormalTargetName(std::string const& name) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index cd67586..b10b30f 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -231,14 +231,21 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, impl->IsAndroid = (impl->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"); - std::string gKey; - gKey.reserve(128); - gKey += "CMAKE_"; - auto InitProperty = [this, mf, &gKey](const std::string& property, - const char* default_value) { + std::string defKey; + defKey.reserve(128); + defKey += "CMAKE_"; + auto initProp = [this, mf, &defKey](const std::string& property) { // Replace everything after "CMAKE_" - gKey.replace(gKey.begin() + 6, gKey.end(), property); - if (const char* value = mf->GetDefinition(gKey)) { + defKey.replace(defKey.begin() + 6, defKey.end(), property); + if (const char* value = mf->GetDefinition(defKey)) { + this->SetProperty(property, value); + } + }; + auto initPropValue = [this, mf, &defKey](const std::string& property, + const char* default_value) { + // Replace everything after "CMAKE_" + defKey.replace(defKey.begin() + 6, defKey.end(), property); + if (const char* value = mf->GetDefinition(defKey)) { this->SetProperty(property, value); } else if (default_value) { this->SetProperty(property, default_value); @@ -248,107 +255,107 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, // Setup default property values. if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("ANDROID_API", nullptr); - InitProperty("ANDROID_API_MIN", nullptr); - InitProperty("ANDROID_ARCH", nullptr); - InitProperty("ANDROID_STL_TYPE", nullptr); - InitProperty("ANDROID_SKIP_ANT_STEP", nullptr); - InitProperty("ANDROID_PROCESS_MAX", nullptr); - InitProperty("ANDROID_PROGUARD", nullptr); - InitProperty("ANDROID_PROGUARD_CONFIG_PATH", nullptr); - InitProperty("ANDROID_SECURE_PROPS_PATH", nullptr); - InitProperty("ANDROID_NATIVE_LIB_DIRECTORIES", nullptr); - InitProperty("ANDROID_NATIVE_LIB_DEPENDENCIES", nullptr); - InitProperty("ANDROID_JAVA_SOURCE_DIR", nullptr); - InitProperty("ANDROID_JAR_DIRECTORIES", nullptr); - InitProperty("ANDROID_JAR_DEPENDENCIES", nullptr); - InitProperty("ANDROID_ASSETS_DIRECTORIES", nullptr); - InitProperty("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr); - InitProperty("BUILD_RPATH", nullptr); - InitProperty("BUILD_RPATH_USE_ORIGIN", nullptr); - InitProperty("INSTALL_NAME_DIR", nullptr); - InitProperty("INSTALL_RPATH", ""); - InitProperty("INSTALL_RPATH_USE_LINK_PATH", "OFF"); - InitProperty("INTERPROCEDURAL_OPTIMIZATION", nullptr); - InitProperty("SKIP_BUILD_RPATH", "OFF"); - InitProperty("BUILD_WITH_INSTALL_RPATH", "OFF"); - InitProperty("ARCHIVE_OUTPUT_DIRECTORY", nullptr); - InitProperty("LIBRARY_OUTPUT_DIRECTORY", nullptr); - InitProperty("RUNTIME_OUTPUT_DIRECTORY", nullptr); - InitProperty("PDB_OUTPUT_DIRECTORY", nullptr); - InitProperty("COMPILE_PDB_OUTPUT_DIRECTORY", nullptr); - InitProperty("FRAMEWORK", nullptr); - InitProperty("Fortran_FORMAT", nullptr); - InitProperty("Fortran_MODULE_DIRECTORY", nullptr); - InitProperty("Fortran_COMPILER_LAUNCHER", nullptr); - InitProperty("GNUtoMS", nullptr); - InitProperty("OSX_ARCHITECTURES", nullptr); - InitProperty("IOS_INSTALL_COMBINED", nullptr); - InitProperty("AUTOMOC", nullptr); - InitProperty("AUTOUIC", nullptr); - InitProperty("AUTORCC", nullptr); - InitProperty("AUTOGEN_ORIGIN_DEPENDS", nullptr); - InitProperty("AUTOGEN_PARALLEL", nullptr); - InitProperty("AUTOMOC_COMPILER_PREDEFINES", nullptr); - InitProperty("AUTOMOC_DEPEND_FILTERS", nullptr); - InitProperty("AUTOMOC_MACRO_NAMES", nullptr); - InitProperty("AUTOMOC_MOC_OPTIONS", nullptr); - InitProperty("AUTOUIC_OPTIONS", nullptr); - InitProperty("AUTOUIC_SEARCH_PATHS", nullptr); - InitProperty("AUTORCC_OPTIONS", nullptr); - InitProperty("LINK_DEPENDS_NO_SHARED", nullptr); - InitProperty("LINK_INTERFACE_LIBRARIES", nullptr); - InitProperty("MSVC_RUNTIME_LIBRARY", nullptr); - InitProperty("WIN32_EXECUTABLE", nullptr); - InitProperty("MACOSX_BUNDLE", nullptr); - InitProperty("MACOSX_RPATH", nullptr); - InitProperty("NO_SYSTEM_FROM_IMPORTED", nullptr); - InitProperty("BUILD_WITH_INSTALL_NAME_DIR", nullptr); - InitProperty("C_CLANG_TIDY", nullptr); - InitProperty("C_COMPILER_LAUNCHER", nullptr); - InitProperty("C_CPPLINT", nullptr); - InitProperty("C_CPPCHECK", nullptr); - InitProperty("C_INCLUDE_WHAT_YOU_USE", nullptr); - InitProperty("LINK_WHAT_YOU_USE", nullptr); - InitProperty("C_STANDARD", nullptr); - InitProperty("C_STANDARD_REQUIRED", nullptr); - InitProperty("C_EXTENSIONS", nullptr); - InitProperty("CXX_CLANG_TIDY", nullptr); - InitProperty("CXX_COMPILER_LAUNCHER", nullptr); - InitProperty("CXX_CPPLINT", nullptr); - InitProperty("CXX_CPPCHECK", nullptr); - InitProperty("CXX_INCLUDE_WHAT_YOU_USE", nullptr); - InitProperty("CXX_STANDARD", nullptr); - InitProperty("CXX_STANDARD_REQUIRED", nullptr); - InitProperty("CXX_EXTENSIONS", nullptr); - InitProperty("CUDA_STANDARD", nullptr); - InitProperty("CUDA_STANDARD_REQUIRED", nullptr); - InitProperty("CUDA_EXTENSIONS", nullptr); - InitProperty("CUDA_COMPILER_LAUNCHER", nullptr); - InitProperty("CUDA_SEPARABLE_COMPILATION", nullptr); - InitProperty("LINK_SEARCH_START_STATIC", nullptr); - InitProperty("LINK_SEARCH_END_STATIC", nullptr); - InitProperty("FOLDER", nullptr); - InitProperty("Swift_MODULE_DIRECTORY", nullptr); - InitProperty("VS_JUST_MY_CODE_DEBUGGING", nullptr); + initProp("ANDROID_API"); + initProp("ANDROID_API_MIN"); + initProp("ANDROID_ARCH"); + initProp("ANDROID_STL_TYPE"); + initProp("ANDROID_SKIP_ANT_STEP"); + initProp("ANDROID_PROCESS_MAX"); + initProp("ANDROID_PROGUARD"); + initProp("ANDROID_PROGUARD_CONFIG_PATH"); + initProp("ANDROID_SECURE_PROPS_PATH"); + initProp("ANDROID_NATIVE_LIB_DIRECTORIES"); + initProp("ANDROID_NATIVE_LIB_DEPENDENCIES"); + initProp("ANDROID_JAVA_SOURCE_DIR"); + initProp("ANDROID_JAR_DIRECTORIES"); + initProp("ANDROID_JAR_DEPENDENCIES"); + initProp("ANDROID_ASSETS_DIRECTORIES"); + initProp("ANDROID_ANT_ADDITIONAL_OPTIONS"); + initProp("BUILD_RPATH"); + initProp("BUILD_RPATH_USE_ORIGIN"); + initProp("INSTALL_NAME_DIR"); + initPropValue("INSTALL_RPATH", ""); + initPropValue("INSTALL_RPATH_USE_LINK_PATH", "OFF"); + initProp("INTERPROCEDURAL_OPTIMIZATION"); + initPropValue("SKIP_BUILD_RPATH", "OFF"); + initPropValue("BUILD_WITH_INSTALL_RPATH", "OFF"); + initProp("ARCHIVE_OUTPUT_DIRECTORY"); + initProp("LIBRARY_OUTPUT_DIRECTORY"); + initProp("RUNTIME_OUTPUT_DIRECTORY"); + initProp("PDB_OUTPUT_DIRECTORY"); + initProp("COMPILE_PDB_OUTPUT_DIRECTORY"); + initProp("FRAMEWORK"); + initProp("Fortran_FORMAT"); + initProp("Fortran_MODULE_DIRECTORY"); + initProp("Fortran_COMPILER_LAUNCHER"); + initProp("GNUtoMS"); + initProp("OSX_ARCHITECTURES"); + initProp("IOS_INSTALL_COMBINED"); + initProp("AUTOMOC"); + initProp("AUTOUIC"); + initProp("AUTORCC"); + initProp("AUTOGEN_ORIGIN_DEPENDS"); + initProp("AUTOGEN_PARALLEL"); + initProp("AUTOMOC_COMPILER_PREDEFINES"); + initProp("AUTOMOC_DEPEND_FILTERS"); + initProp("AUTOMOC_MACRO_NAMES"); + initProp("AUTOMOC_MOC_OPTIONS"); + initProp("AUTOUIC_OPTIONS"); + initProp("AUTOUIC_SEARCH_PATHS"); + initProp("AUTORCC_OPTIONS"); + initProp("LINK_DEPENDS_NO_SHARED"); + initProp("LINK_INTERFACE_LIBRARIES"); + initProp("MSVC_RUNTIME_LIBRARY"); + initProp("WIN32_EXECUTABLE"); + initProp("MACOSX_BUNDLE"); + initProp("MACOSX_RPATH"); + initProp("NO_SYSTEM_FROM_IMPORTED"); + initProp("BUILD_WITH_INSTALL_NAME_DIR"); + initProp("C_CLANG_TIDY"); + initProp("C_COMPILER_LAUNCHER"); + initProp("C_CPPLINT"); + initProp("C_CPPCHECK"); + initProp("C_INCLUDE_WHAT_YOU_USE"); + initProp("LINK_WHAT_YOU_USE"); + initProp("C_STANDARD"); + initProp("C_STANDARD_REQUIRED"); + initProp("C_EXTENSIONS"); + initProp("CXX_CLANG_TIDY"); + initProp("CXX_COMPILER_LAUNCHER"); + initProp("CXX_CPPLINT"); + initProp("CXX_CPPCHECK"); + initProp("CXX_INCLUDE_WHAT_YOU_USE"); + initProp("CXX_STANDARD"); + initProp("CXX_STANDARD_REQUIRED"); + initProp("CXX_EXTENSIONS"); + initProp("CUDA_STANDARD"); + initProp("CUDA_STANDARD_REQUIRED"); + initProp("CUDA_EXTENSIONS"); + initProp("CUDA_COMPILER_LAUNCHER"); + initProp("CUDA_SEPARABLE_COMPILATION"); + initProp("LINK_SEARCH_START_STATIC"); + initProp("LINK_SEARCH_END_STATIC"); + initProp("FOLDER"); + initProp("Swift_MODULE_DIRECTORY"); + initProp("VS_JUST_MY_CODE_DEBUGGING"); #ifdef __APPLE__ if (this->GetGlobalGenerator()->IsXcode()) { - InitProperty("XCODE_GENERATE_SCHEME", nullptr); - InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER", nullptr); - InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", nullptr); - InitProperty("XCODE_SCHEME_THREAD_SANITIZER", nullptr); - InitProperty("XCODE_SCHEME_THREAD_SANITIZER_STOP", nullptr); - InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", nullptr); - InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", nullptr); - InitProperty("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", nullptr); - InitProperty("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", nullptr); - InitProperty("XCODE_SCHEME_MALLOC_SCRIBBLE", nullptr); - InitProperty("XCODE_SCHEME_MALLOC_GUARD_EDGES", nullptr); - InitProperty("XCODE_SCHEME_GUARD_MALLOC", nullptr); - InitProperty("XCODE_SCHEME_ZOMBIE_OBJECTS", nullptr); - InitProperty("XCODE_SCHEME_MALLOC_STACK", nullptr); - InitProperty("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", nullptr); - InitProperty("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", nullptr); + initProp("XCODE_GENERATE_SCHEME"); + initProp("XCODE_SCHEME_ADDRESS_SANITIZER"); + initProp("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN"); + initProp("XCODE_SCHEME_THREAD_SANITIZER"); + initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP"); + initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"); + initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"); + initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); + initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"); + initProp("XCODE_SCHEME_MALLOC_SCRIBBLE"); + initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES"); + initProp("XCODE_SCHEME_GUARD_MALLOC"); + initProp("XCODE_SCHEME_ZOMBIE_OBJECTS"); + initProp("XCODE_SCHEME_MALLOC_STACK"); + initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); + initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"); } #endif } @@ -376,7 +383,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } std::string property = prop; property += configUpper; - InitProperty(property, nullptr); + initProp(property); } // Initialize per-configuration name postfix property from the @@ -388,7 +395,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) { std::string property = cmSystemTools::UpperCase(configName); property += "_POSTFIX"; - InitProperty(property, nullptr); + initProp(property); } } } @@ -427,16 +434,16 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("C_VISIBILITY_PRESET", nullptr); - InitProperty("CXX_VISIBILITY_PRESET", nullptr); - InitProperty("CUDA_VISIBILITY_PRESET", nullptr); - InitProperty("VISIBILITY_INLINES_HIDDEN", nullptr); + initProp("C_VISIBILITY_PRESET"); + initProp("CXX_VISIBILITY_PRESET"); + initProp("CUDA_VISIBILITY_PRESET"); + initProp("VISIBILITY_INLINES_HIDDEN"); } if (impl->TargetType == cmStateEnums::EXECUTABLE) { - InitProperty("ANDROID_GUI", nullptr); - InitProperty("CROSSCOMPILING_EMULATOR", nullptr); - InitProperty("ENABLE_EXPORTS", nullptr); + initProp("ANDROID_GUI"); + initProp("CROSSCOMPILING_EMULATOR"); + initProp("ENABLE_EXPORTS"); } if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || impl->TargetType == cmStateEnums::MODULE_LIBRARY) { @@ -444,12 +451,12 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || impl->TargetType == cmStateEnums::EXECUTABLE) { - InitProperty("WINDOWS_EXPORT_ALL_SYMBOLS", nullptr); + initProp("WINDOWS_EXPORT_ALL_SYMBOLS"); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("POSITION_INDEPENDENT_CODE", nullptr); + initProp("POSITION_INDEPENDENT_CODE"); } // Record current policies for later use. @@ -465,12 +472,12 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("JOB_POOL_COMPILE", nullptr); - InitProperty("JOB_POOL_LINK", nullptr); + initProp("JOB_POOL_COMPILE"); + initProp("JOB_POOL_LINK"); } if (impl->TargetType <= cmStateEnums::UTILITY) { - InitProperty("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr); + initProp("DOTNET_TARGET_FRAMEWORK_VERSION"); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && @@ -491,7 +498,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (assignment != std::string::npos) { const std::string propName = vsGlobal + i.substr(0, assignment); const std::string propValue = i.substr(assignment + 1); - InitProperty(propName, propValue.c_str()); + initPropValue(propName, propValue.c_str()); } } } @@ -1484,7 +1491,6 @@ const char* cmTarget::GetComputedProperty( const char* cmTarget::GetProperty(const std::string& prop) const { - static std::unordered_set<std::string> specialProps; #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP MAKE_STATIC_PROP(LINK_LIBRARIES); MAKE_STATIC_PROP(TYPE); @@ -1502,23 +1508,23 @@ const char* cmTarget::GetProperty(const std::string& prop) const MAKE_STATIC_PROP(SOURCE_DIR); MAKE_STATIC_PROP(SOURCES); #undef MAKE_STATIC_PROP - if (specialProps.empty()) { - specialProps.insert(propLINK_LIBRARIES); - specialProps.insert(propTYPE); - specialProps.insert(propINCLUDE_DIRECTORIES); - specialProps.insert(propCOMPILE_FEATURES); - specialProps.insert(propCOMPILE_OPTIONS); - specialProps.insert(propCOMPILE_DEFINITIONS); - specialProps.insert(propLINK_OPTIONS); - specialProps.insert(propLINK_DIRECTORIES); - specialProps.insert(propIMPORTED); - specialProps.insert(propIMPORTED_GLOBAL); - specialProps.insert(propMANUALLY_ADDED_DEPENDENCIES); - specialProps.insert(propNAME); - specialProps.insert(propBINARY_DIR); - specialProps.insert(propSOURCE_DIR); - specialProps.insert(propSOURCES); - } + static std::unordered_set<std::string> const specialProps{ + propLINK_LIBRARIES, + propTYPE, + propINCLUDE_DIRECTORIES, + propCOMPILE_FEATURES, + propCOMPILE_OPTIONS, + propCOMPILE_DEFINITIONS, + propLINK_OPTIONS, + propLINK_DIRECTORIES, + propIMPORTED, + propIMPORTED_GLOBAL, + propMANUALLY_ADDED_DEPENDENCIES, + propNAME, + propBINARY_DIR, + propSOURCE_DIR, + propSOURCES + }; if (specialProps.count(prop)) { if (prop == propLINK_LIBRARIES) { if (impl->LinkImplementationPropertyEntries.empty()) { diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx index 3f763af..eac300f 100644 --- a/Source/cmTargetPropertyComputer.cxx +++ b/Source/cmTargetPropertyComputer.cxx @@ -56,22 +56,21 @@ bool cmTargetPropertyComputer::WhiteListedInterfaceProperty( if (std::islower(prop[0])) { return true; } - static std::unordered_set<std::string> builtIns; - if (builtIns.empty()) { - builtIns.insert("COMPATIBLE_INTERFACE_BOOL"); - builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MAX"); - builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MIN"); - builtIns.insert("COMPATIBLE_INTERFACE_STRING"); - builtIns.insert("EXPORT_NAME"); - builtIns.insert("EXPORT_PROPERTIES"); - builtIns.insert("IMPORTED"); - builtIns.insert("IMPORTED_GLOBAL"); - builtIns.insert("MANUALLY_ADDED_DEPENDENCIES"); - builtIns.insert("NAME"); - builtIns.insert("PRIVATE_HEADER"); - builtIns.insert("PUBLIC_HEADER"); - builtIns.insert("TYPE"); - } + static std::unordered_set<std::string> const builtIns{ + "COMPATIBLE_INTERFACE_BOOL", + "COMPATIBLE_INTERFACE_NUMBER_MAX", + "COMPATIBLE_INTERFACE_NUMBER_MIN", + "COMPATIBLE_INTERFACE_STRING", + "EXPORT_NAME", + "EXPORT_PROPERTIES", + "IMPORTED", + "IMPORTED_GLOBAL", + "MANUALLY_ADDED_DEPENDENCIES", + "NAME", + "PRIVATE_HEADER", + "PUBLIC_HEADER", + "TYPE" + }; if (builtIns.count(prop)) { return true; diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index 7d45cf5..01f2b96 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx @@ -8,7 +8,8 @@ #include "cmSystemTools.h" cmTest::cmTest(cmMakefile* mf) - : Backtrace(mf->GetBacktrace()) + : CommandExpandLists(false) + , Backtrace(mf->GetBacktrace()) { this->Makefile = mf; this->OldStyle = true; @@ -59,3 +60,13 @@ void cmTest::AppendProperty(const std::string& prop, const char* value, { this->Properties.AppendProperty(prop, value, asString); } + +bool cmTest::GetCommandExpandLists() const +{ + return this->CommandExpandLists; +} + +void cmTest::SetCommandExpandLists(bool b) +{ + this->CommandExpandLists = b; +} diff --git a/Source/cmTest.h b/Source/cmTest.h index 88dc730..02d8f46 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h @@ -51,10 +51,15 @@ public: bool GetOldStyle() const { return this->OldStyle; } void SetOldStyle(bool b) { this->OldStyle = b; } + /** Set/Get whether lists in command lines should be expanded. */ + bool GetCommandExpandLists() const; + void SetCommandExpandLists(bool b); + private: cmPropertyMap Properties; std::string Name; std::vector<std::string> Command; + bool CommandExpandLists; bool OldStyle; diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 571cd09..498953e 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -10,7 +10,6 @@ #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmOutputConverter.h" -#include "cmProperty.h" #include "cmPropertyMap.h" #include "cmRange.h" #include "cmStateTypes.h" @@ -76,12 +75,22 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Start the test command. os << indent << "add_test(" << this->Test->GetName() << " "; - // Get the test command line to be executed. - std::vector<std::string> const& command = this->Test->GetCommand(); + // Evaluate command line arguments + std::vector<std::string> argv = + EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config); + + // Expand arguments if COMMAND_EXPAND_LISTS is set + if (this->Test->GetCommandExpandLists()) { + argv = cmSystemTools::ExpandedLists(argv.begin(), argv.end()); + // Expanding lists on an empty command may have left it empty + if (argv.empty()) { + argv.emplace_back(); + } + } // Check whether the command executable is a target whose name is to // be translated. - std::string exe = command[0]; + std::string exe = argv[0]; cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe); if (target && target->GetType() == cmStateEnums::EXECUTABLE) { // Use the target file on disk. @@ -101,29 +110,26 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, } } else { // Use the command name given. - exe = ge.Parse(exe)->Evaluate(this->LG, config); cmSystemTools::ConvertToUnixSlashes(exe); } // Generate the command line with full escapes. os << cmOutputConverter::EscapeForCMake(exe); - for (std::string const& arg : cmMakeRange(command).advance(1)) { - os << " " - << cmOutputConverter::EscapeForCMake( - ge.Parse(arg)->Evaluate(this->LG, config)); + + for (auto const& arg : cmMakeRange(argv).advance(1)) { + os << " " << cmOutputConverter::EscapeForCMake(arg); } // Finish the test command. os << ")\n"; // Output properties for the test. - cmPropertyMap& pm = this->Test->GetProperties(); os << indent << "set_tests_properties(" << this->Test->GetName() << " PROPERTIES "; - for (auto const& i : pm) { + for (auto const& i : this->Test->GetProperties().GetList()) { os << " " << i.first << " " << cmOutputConverter::EscapeForCMake( - ge.Parse(i.second.GetValue())->Evaluate(this->LG, config)); + ge.Parse(i.second)->Evaluate(this->LG, config)); } this->GenerateInternalProperties(os); os << ")" << std::endl; @@ -173,12 +179,11 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) fout << ")" << std::endl; // Output properties for the test. - cmPropertyMap& pm = this->Test->GetProperties(); fout << indent << "set_tests_properties(" << this->Test->GetName() << " PROPERTIES "; - for (auto const& i : pm) { + for (auto const& i : this->Test->GetProperties().GetList()) { fout << " " << i.first << " " - << cmOutputConverter::EscapeForCMake(i.second.GetValue()); + << cmOutputConverter::EscapeForCMake(i.second); } this->GenerateInternalProperties(fout); fout << ")" << std::endl; @@ -208,3 +213,16 @@ void cmTestGenerator::GenerateInternalProperties(std::ostream& os) os << "\""; } + +std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments( + const std::vector<std::string>& argv, cmGeneratorExpression& ge, + const std::string& config) const +{ + // Evaluate executable name and arguments + auto evaluatedRange = + cmMakeRange(argv).transform([&](const std::string& arg) { + return ge.Parse(arg)->Evaluate(this->LG, config); + }); + + return { evaluatedRange.begin(), evaluatedRange.end() }; +} diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h index 8b9cf78..7ac68eb 100644 --- a/Source/cmTestGenerator.h +++ b/Source/cmTestGenerator.h @@ -11,6 +11,7 @@ #include <string> #include <vector> +class cmGeneratorExpression; class cmLocalGenerator; class cmTest; @@ -38,6 +39,9 @@ public: private: void GenerateInternalProperties(std::ostream& os); + std::vector<std::string> EvaluateCommandLineArguments( + const std::vector<std::string>& argv, cmGeneratorExpression& ge, + const std::string& config) const; protected: void GenerateScriptConfigs(std::ostream& os, Indent indent) override; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9368414..634c990 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -774,11 +774,11 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferences(Elem& e0) cmSystemTools::ExpandListArgument(vsDotNetReferences, references); } cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties(); - for (auto const& i : props) { + for (auto const& i : props.GetList()) { if (i.first.find("VS_DOTNET_REFERENCE_") == 0) { std::string name = i.first.substr(20); if (!name.empty()) { - std::string path = i.second.GetValue(); + std::string path = i.second; if (!cmsys::SystemTools::FileIsFullPath(path)) { path = this->Makefile->GetCurrentSourceDirectory() + "/" + path; } @@ -870,10 +870,10 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags( typedef std::map<std::string, std::string> CustomTags; CustomTags tags; cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties(); - for (const auto& i : props) { + for (const auto& i : props.GetList()) { if (i.first.find(refPropFullPrefix) == 0) { std::string refTag = i.first.substr(refPropFullPrefix.length()); - std::string refVal = i.second.GetValue(); + std::string refVal = i.second; if (!refTag.empty() && !refVal.empty()) { tags[refTag] = refVal; } @@ -967,12 +967,12 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup(Elem& e0) } } const cmPropertyMap& props = oi->GetProperties(); - for (const auto& p : props) { + for (const std::string& p : props.GetKeys()) { static const std::string propNamePrefix = "VS_CSHARP_"; - if (p.first.find(propNamePrefix) == 0) { - std::string tagName = p.first.substr(propNamePrefix.length()); + if (p.find(propNamePrefix) == 0) { + std::string tagName = p.substr(propNamePrefix.length()); if (!tagName.empty()) { - std::string value = props.GetPropertyValue(p.first); + std::string value = props.GetPropertyValue(p); if (!value.empty()) { e2.Element(tagName.c_str(), value); } @@ -4681,12 +4681,12 @@ void cmVisualStudio10TargetGenerator::GetCSharpSourceProperties( { if (this->ProjectType == csproj) { const cmPropertyMap& props = sf->GetProperties(); - for (auto const& p : props) { + for (const std::string& p : props.GetKeys()) { static const std::string propNamePrefix = "VS_CSHARP_"; - if (p.first.find(propNamePrefix) == 0) { - std::string tagName = p.first.substr(propNamePrefix.length()); + if (p.find(propNamePrefix) == 0) { + std::string tagName = p.substr(propNamePrefix.length()); if (!tagName.empty()) { - const std::string val = props.GetPropertyValue(p.first); + const std::string val = props.GetPropertyValue(p); if (!val.empty()) { tags[tagName] = val; } else { diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 69f8162..735ad5f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -408,7 +408,12 @@ else() set(NO_NAMELINK 0) endif() -add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN}) +add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN=${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN} + -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DCMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG=${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG} + -DCMAKE_EXECUTABLE_FORMAT=${CMAKE_EXECUTABLE_FORMAT}) + add_RunCMake_test(CPackCommandLine) add_RunCMake_test(CPackConfig) add_RunCMake_test(CPackInstallProperties) @@ -565,3 +570,5 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])") add_RunCMake_test(CSharpCustomCommand) add_RunCMake_test(CSharpReferenceImport) endif() + +add_RunCMake_test("CTestCommandExpandLists") diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt new file mode 100644 index 0000000..3e470a2 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.14) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in new file mode 100644 index 0000000..7d56c90 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.14) +project(@CASE_NAME@ NONE) +include("@RunCMake_SOURCE_DIR@/@CASE_NAME@.cmake") diff --git a/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake new file mode 100644 index 0000000..7c3779e --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake @@ -0,0 +1,5 @@ +include(RunCTest) + +run_ctest(expandGeneratorExpressionResult) +run_ctest(expandEmptyCommand) +run_cmake(multipleExpandOptions) diff --git a/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake new file mode 100644 index 0000000..a32e579 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake @@ -0,0 +1,14 @@ +set(range 1 2 3 4 5 6 7 8 9 10) +set(aargs "") +set(bargs "") +foreach(n IN LISTS range) + set(aval "${A${n}ARG}") + set(bval "${B${n}ARG}") + if(aval OR bval) + list(APPEND aargs "\"${aval}\"") + list(APPEND bargs "\"${bval}\"") + endif() +endforeach() +if(NOT "${aargs}" STREQUAL "${bargs}") + message(FATAL_ERROR "COMPARE_OPTIONS: \n\t${aargs} != \n\t${bargs}") +endif() diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt @@ -0,0 +1 @@ +(-1|255) diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt new file mode 100644 index 0000000..c656b4c --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt @@ -0,0 +1 @@ +Unable to find executable: diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt new file mode 100644 index 0000000..0752580 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt @@ -0,0 +1,13 @@ +Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-build +.* +Start 1: CommandExpandEmptyList +Could not find executable + +Looked in the following places: +.* +1/1 Test #1: CommandExpandEmptyList +\.+\*\*\*Not Run +[0-9.]+ sec ++ +0% tests passed, 1 tests failed out of 1 ++ +Total Test time \(real\) = +[0-9.]+ sec ++ +The following tests FAILED: +.* +1 - CommandExpandEmptyList \(Not Run\)$ diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake new file mode 100644 index 0000000..b75828e --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake @@ -0,0 +1,10 @@ +include(CTest) + +set(argv /bin/true) +list(POP_BACK argv) + +add_test( + NAME CommandExpandEmptyList + COMMAND "$<JOIN:${argv},;>" + COMMAND_EXPAND_LISTS +) diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt new file mode 100644 index 0000000..2f21592 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt @@ -0,0 +1,7 @@ +Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-build +.* +Start 1: CommandExpandList +1/1 Test #1: CommandExpandList +\.+ +Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 1 ++ +Total Test time \(real\) = +[0-9.]+ sec diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake new file mode 100644 index 0000000..20608ae --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake @@ -0,0 +1,19 @@ +include(CTest) + + +set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile" + "4ARG=content") +set(AARGS "") +foreach(arg IN LISTS cmp_args) + list(APPEND AARGS "-DA${arg}") +endforeach() + + + +add_test( + NAME CommandExpandList + COMMAND ${CMAKE_COMMAND} ${AARGS} -V + "-DB$<JOIN:${cmp_args},;-DB>" + "-P" "${CMAKE_CURRENT_LIST_DIR}/compare_options.cmake" + COMMAND_EXPAND_LISTS +) diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt new file mode 100644 index 0000000..e48513f --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at multipleExpandOptions\.cmake:3 \(add_test\): + +add_test may be given at most one COMMAND_EXPAND_LISTS\. diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt new file mode 100644 index 0000000..55bb894 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt @@ -0,0 +1,2 @@ +-- Configuring incomplete, errors occurred! +See also ".*/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-build/CMakeFiles/CMakeOutput\.log". diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake new file mode 100644 index 0000000..dcf2dc4 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake @@ -0,0 +1,8 @@ +include(CTest) + +add_test( + NAME MultipleExpandOptions + COMMAND /bin/true + COMMAND_EXPAND_LISTS + COMMAND_EXPAND_LISTS +) diff --git a/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in new file mode 100644 index 0000000..d9a8ccb --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.14) + +set(CTEST_SITE "test-site") +set(CTEST_BUILD_NAME "test-build-name") +set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@") +set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@-build") +set(CTEST_CMAKE_GENERATOR "@RunCMake_GENERATOR@") +set(CTEST_CMAKE_GENERATOR_PLATFORM "@RunCMake_GENERATOR_PLATFORM@") +set(CTEST_CMAKE_GENERATOR_TOOLSET "@RunCMake_GENERATOR_TOOLSET@") +set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") + +ctest_start(Experimental) +ctest_configure() +ctest_build() +ctest_test() diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 0bcf886..6d72fac 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -27,6 +27,7 @@ \* CMP0076 \* CMP0081 \* CMP0083 + \* CMP0095 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt index 6dd8cdf..c7e99ad 100644 --- a/Tests/RunCMake/install/CMakeLists.txt +++ b/Tests/RunCMake/install/CMakeLists.txt @@ -1,3 +1,6 @@ cmake_minimum_required(VERSION 3.4) +if(RunCMake_TEST MATCHES "^file-GET_RUNTIME_DEPENDENCIES") + cmake_policy(SET CMP0087 NEW) +endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index c637db1..21c320b 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -48,6 +48,22 @@ in directory: endif() endfunction() +# Wrapper for run_cmake() that skips platforms that are non-ELF or have no RPATH support +function(run_cmake_ELFRPATH_only case) + if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") + run_cmake(${case}) + else() + # Sanity check against a platform known to be ELF-based + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Expected platform Linux to advertize itself as ELF-based, but it did not.") + else() + message(STATUS "${case} - SKIPPED (No ELF-based platform found)") + endif() + endif() +endfunction() + +run_cmake(TARGETS-FILE_RPATH_CHANGE-old_rpath) +run_cmake_ELFRPATH_only(TARGETS-FILE_RPATH_CHANGE-new_rpath) run_cmake(DIRECTORY-MESSAGE_NEVER) run_cmake(DIRECTORY-PATTERN-MESSAGE_NEVER) run_cmake(DIRECTORY-message) @@ -139,6 +155,36 @@ run_install_test(FILES-PERMISSIONS) run_install_test(TARGETS-RPATH) run_install_test(InstallRequiredSystemLibraries) +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-unresolved) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-conflict) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-notfile) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-project) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-unresolved) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-conflict) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-notfile) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-project) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + if(NOT CMAKE_C_COMPILER_ID MATCHES "^XL") + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux) + endif() + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-unresolved) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-conflict) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-notfile) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-project) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2) +else() + run_cmake(file-GET_RUNTIME_DEPENDENCIES-unsupported) +endif() + set(run_install_test_components 1) run_install_test(FILES-EXCLUDE_FROM_ALL) run_install_test(TARGETS-EXCLUDE_FROM_ALL) diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake new file mode 100644 index 0000000..673fdde --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake @@ -0,0 +1,30 @@ +file(READ ${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake install_script) +#message(STATUS ${install_script}) + +set(wsnl " *[\n\r]+ *") # whitespace + single newline + whitespace +set(wssl " *[\n\r]+[^\n\r]*[\n\r]+ *") # ws nl skipline nl ws +string(CONCAT prefix [[file\(RPATH_CHANGE]]) +set(_msg "cmake_install.cmake does not match ") + +macro(check) + if(NOT install_script MATCHES "${regex}") + message(STATUS "${test} - check \"${target}\" - FAILED:") + string(CONCAT RunCMake_TEST_FAILED "${_msg}" ">>>${regex}<<<") + return() + else() + message(STATUS "${test} - check \"${target}\" - PASSED") + endif() +endmacro() + +macro(skip_without_rpath_change_rule) +# Not all platforms generate a file(RPATH_CHANGE) rule + if(NOT install_script MATCHES [[file\(RPATH_CHANGE]]) + # Sanity check against a platform known to generate a file(RPATH_CHANGE) rule + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Expected generated file(RPATH_CHANGE) rule on platform Linux.") + else() + message(STATUS "${test} - All checks skipped. No file(RPATH_CHANGE) rule found on this platform.") + return() + endif() + endif() +endmacro() diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake new file mode 100644 index 0000000..930ef70 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake @@ -0,0 +1,63 @@ +include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake) +skip_without_rpath_change_rule() +string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]]) + +set(target "exe1_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar]]) +check() + +set(target "exe1_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar]]) +check() + +set(target "exe1_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar]]) +check() + +set(target "exe2_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\$ORIGIN/../lib]]) +check() + +set(target "exe2_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\$ORIGIN/../lib]]) +check() + +set(target "exe2_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\\\$ORIGIN/../lib]]) +check() + +set(target "exe3_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\${ORIGIN}/../lib]]) +check() + +set(target "exe3_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\${ORIGIN}/../lib]]) +check() + +set(target "exe3_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\\\${ORIGIN}/../lib]]) +check() + +set(target "exe4_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar/\${PLATFORM}]]) +check() + +set(target "exe4_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar/\${PLATFORM}]]) +check() + +set(target "exe4_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar/\\\${PLATFORM}]]) +check() diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt new file mode 100644 index 0000000..1e123f6 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt @@ -0,0 +1,23 @@ +^CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): + Policy CMP0095 is not set: RPATH entries are properly escaped in the + intermediary CMake install script\. Run "cmake --help-policy CMP0095" for + policy details\. Use the cmake_policy command to set the policy and + suppress this warning\. + + RPATH entries for target 'exe3_cmp0095_warn' will not be escaped in the + intermediary cmake_install\.cmake script\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): + Policy CMP0095 is not set: RPATH entries are properly escaped in the + intermediary CMake install script\. Run "cmake --help-policy CMP0095" for + policy details\. Use the cmake_policy command to set the policy and + suppress this warning\. + + RPATH entries for target 'exe4_cmp0095_warn' will not be escaped in the + intermediary cmake_install\.cmake script\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake new file mode 100644 index 0000000..cba04b2 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.14) +enable_language(C) + +# test matrix +# +# A := +# | no cmake syntax | cmake syntax | +# -----------------------+-----------------+--------------+ +# absolute install RPATH | exe1 | exe4 | +# relative install RPATH | exe2 | exe3 | +# +# all := A * CMP005_OLD + A * CMP0095_WARN + A * CMP0095_NEW + +add_library(utils SHARED obj1.c) +set(targets utils) + +set(exe1_install_rpath "/foo/bar") +set(exe2_install_rpath "\$ORIGIN/../lib") +set(exe3_install_rpath "\${ORIGIN}/../lib") +set(exe4_install_rpath "/foo/bar/\${PLATFORM}") + +macro(A_CMP0095 policy_value) + cmake_policy(PUSH) + if(NOT "x${policy_value}x" STREQUAL "xWARNx") + cmake_policy(SET CMP0095 ${policy_value}) + endif() + string(TOLOWER "${policy_value}" p) + + # exe1: absolute install RPATH, no cmake syntax + set(case "exe1") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + # exe2: relative install RPATH, no cmake syntax + set(case "exe2") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + # exe3: relative install RPATH, cmake syntax + set(case "exe3") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + # exe4: absolute install RPATH, cmake syntax + set(case "exe4") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + cmake_policy(POP) +endmacro() + +A_CMP0095("OLD") +A_CMP0095("WARN") # exe3 and exe4 are expected to issue an author warning +A_CMP0095("NEW") + +install(TARGETS ${targets}) diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake new file mode 100644 index 0000000..814f405 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake @@ -0,0 +1,15 @@ +include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake) +skip_without_rpath_change_rule() +string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]]) + +set(target "exe1") +string(CONCAT regex "${prefix}${target}\"${wsnl}" + [[OLD_RPATH "]] "${RunCMake_BINARY_DIR}") +check() + +if("x${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}" STREQUAL "x\$ORIGIN") + set(target "exe2") + string(CONCAT regex "${prefix}${target}\"${wsnl}" + [[OLD_RPATH "\\\$ORIGIN]]) + check() +endif() diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake new file mode 100644 index 0000000..43ae787 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) +enable_language(C) + +add_library(utils SHARED obj1.c) + +# exe1: absolute build RPATH, no cmake syntax +set(CMAKE_BUILD_RPATH_USE_ORIGIN OFF) +set(CMAKE_INSTALL_RPATH "/foo/bar") +add_executable(exe1 main.c) +target_link_libraries(exe1 PRIVATE utils) + +# exe2: relative build RPATH, no cmake syntax +set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) +set(CMAKE_INSTALL_RPATH "/foo/bar") +add_executable(exe2 main.c) +target_link_libraries(exe2 PRIVATE utils) + +install(TARGETS utils exe1 exe2) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt new file mode 100644 index 0000000..b66d1fe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\): + You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is + probably not what you intended to do\. Instead, please consider using it in + an install\(CODE\) or install\(SCRIPT\) command\. For example: + + install\(CODE \[\[ + file\(GET_RUNTIME_DEPENDENCIES + # \.\.\. + \) + ]]\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\): + file Unrecognized argument: "invalid" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake new file mode 100644 index 0000000..f3b8ce4 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake @@ -0,0 +1,2 @@ +file(GET_RUNTIME_DEPENDENCIES invalid) +message(FATAL_ERROR "This message should not be displayed") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt new file mode 100644 index 0000000..94f0f46 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\): + You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is + probably not what you intended to do\. Instead, please consider using it in + an install\(CODE\) or install\(SCRIPT\) command\. For example: + + install\(CODE \[\[ + file\(GET_RUNTIME_DEPENDENCIES + # \.\.\. + \) + ]]\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\): + file Keyword missing value: BUNDLE_EXECUTABLE +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake new file mode 100644 index 0000000..138ab95 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake @@ -0,0 +1,2 @@ +file(GET_RUNTIME_DEPENDENCIES BUNDLE_EXECUTABLE) +message(FATAL_ERROR "This message should not be displayed") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake new file mode 100644 index 0000000..ab630f0 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake @@ -0,0 +1,44 @@ +function(check_contents filename contents_regex) + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}") + file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents) + if(NOT contents MATCHES "${contents_regex}") + string(APPEND RunCMake_TEST_FAILED "File contents: + ${contents} +do not match what we expected: + ${contents_regex} +in file: + ${CMAKE_INSTALL_PREFIX}/${filename}\n") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() + else() + string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() +endfunction() + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_rpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_runpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath/librpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_parent/librpath_parent\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search/librpath_search\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath/librunpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search/librunpath_search\.so]] + ) +check_contents(deps/deps1.txt "^${_check}$") +check_contents(deps/deps2.txt "^${_check}$") +check_contents(deps/deps3.txt "^${_check}$") +set(_check + [[librpath_unresolved\.so]] + [[librunpath_parent_unresolved\.so]] + [[librunpath_unresolved\.so]] + ) +check_contents(deps/udeps1.txt "^${_check}$") +check_contents(deps/udeps2.txt "^${_check}$") +check_contents(deps/udeps3.txt "^${_check}$") +set(_check + "^libconflict\\.so:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict/libconflict\\.so;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict2/libconflict\\.so\n$" + ) +check_contents(deps/cdeps1.txt "${_check}") +check_contents(deps/cdeps2.txt "${_check}") +check_contents(deps/cdeps3.txt "${_check}") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt new file mode 100644 index 0000000..123ae48 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt @@ -0,0 +1,119 @@ +^CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt new file mode 100644 index 0000000..1692348 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for librpath\.so: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath1/librpath\.so + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath2/librpath\.so$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake new file mode 100644 index 0000000..f719499 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake @@ -0,0 +1,54 @@ +enable_language(C) + +set(test1_names rpath) +set(test2_names rpath) + +file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n") +add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c") +install(TARGETS rpath DESTINATION lib/rpath1) +install(TARGETS rpath DESTINATION lib/rpath2) + +file(REMOVE "${CMAKE_BINARY_DIR}/test1.c") +add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n") + +target_link_libraries(test1 PRIVATE ${test1_names}) +set_property(TARGET test1 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath1" + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/test2.c") +add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n") + +target_link_libraries(test2 PRIVATE ${test2_names}) +set_property(TARGET test2 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath2" + ) + +install(TARGETS test1 test2 DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>" + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>" + PRE_INCLUDE_REGEXES "^librpath\\.so$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt new file mode 100644 index 0000000..83a87c9 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Failed to run objdump on: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-build/root-all/bin/\.\./lib/libtest\.so$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake new file mode 100644 index 0000000..6567438 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake @@ -0,0 +1,30 @@ +enable_language(C) +cmake_policy(SET CMP0095 NEW) + +file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void); + +int main(void) +{ + test(); + return 0; +} +]]) + +add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c") +add_executable(exe "${CMAKE_BINARY_DIR}/main.c") +target_link_libraries(exe PRIVATE test) +set_property(TARGET exe PROPERTY INSTALL_RPATH "\${ORIGIN}/../lib") + +install(TARGETS exe DESTINATION bin) + +install(CODE [[ + file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>") + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + PRE_INCLUDE_REGEXES "^libtest\\.so$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt new file mode 100644 index 0000000..eaca512 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve file libunresolved\.so$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake new file mode 100644 index 0000000..3efa305 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n unresolved();\n}\n") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n") +add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c") +target_link_libraries(testlib PRIVATE unresolved) +install(TARGETS testlib DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "^libunresolved\\.so$" + PRE_EXCLUDE_REGEXES ".*" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake new file mode 100644 index 0000000..bd0f9f1 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake @@ -0,0 +1,169 @@ +enable_language(C) +cmake_policy(SET CMP0095 NEW) + +set(test_rpath_names + preexcluded + rpath_postexcluded + rpath + rpath_parent_postexcluded + rpath_parent + rpath_origin_postexcluded + rpath_origin + rpath_search_postexcluded + rpath_search + rpath_unresolved + conflict + ) +set(test_runpath_names + runpath_postexcluded + runpath + runpath_origin_postexcluded + runpath_origin + runpath_parent_unresolved + runpath_search_postexcluded + runpath_search + runpath_unresolved + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/test_rpath.c") +add_library(test_rpath SHARED "${CMAKE_BINARY_DIR}/test_rpath.c") +foreach(name ${test_rpath_names}) + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n") + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + + file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "void test_rpath(void)\n{\n") +foreach(name ${test_rpath_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "}\n") + +install(TARGETS rpath_postexcluded DESTINATION lib/rpath_postexcluded) +install(TARGETS rpath DESTINATION lib/rpath) +install(TARGETS rpath_origin_postexcluded DESTINATION lib/rpath_origin_postexcluded) +install(TARGETS rpath_origin DESTINATION lib/rpath_origin) +install(TARGETS rpath_parent_postexcluded DESTINATION lib/rpath_parent_postexcluded) +install(TARGETS rpath rpath_origin rpath_parent DESTINATION lib/rpath_parent) +install(TARGETS rpath_search_postexcluded DESTINATION lib/rpath_search_postexcluded) +install(TARGETS rpath rpath_origin rpath_parent rpath_search DESTINATION lib/rpath_search) +install(TARGETS conflict DESTINATION lib/conflict) + +target_link_libraries(test_rpath PRIVATE ${test_rpath_names}) +set_property(TARGET test_rpath PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/lib/rpath" + "\$ORIGIN/rpath_origin_postexcluded" + "\${ORIGIN}/rpath_origin" + "${CMAKE_BINARY_DIR}/root-all/lib/conflict" + ) +target_link_options(test_rpath PRIVATE -Wl,--disable-new-dtags) + +file(REMOVE "${CMAKE_BINARY_DIR}/test_runpath.c") +add_library(test_runpath SHARED "${CMAKE_BINARY_DIR}/test_runpath.c") +foreach(name ${test_runpath_names} rpath conflict) + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n") + if(NOT name MATCHES "^(rpath|conflict)$") + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + endif() + + file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "void test_runpath(void)\n{\n") +foreach(name ${test_runpath_names} rpath conflict) + file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "}\n") + +install(TARGETS runpath_postexcluded DESTINATION lib/runpath_postexcluded) +install(TARGETS runpath DESTINATION lib/runpath) +install(TARGETS runpath_origin_postexcluded DESTINATION lib/runpath_origin_postexcluded) +install(TARGETS runpath_origin DESTINATION lib/runpath_origin) +install(TARGETS runpath_parent_unresolved DESTINATION lib/runpath_parent_unresolved) +install(TARGETS runpath_search_postexcluded DESTINATION lib/runpath_search_postexcluded) +install(TARGETS runpath runpath_origin runpath_search DESTINATION lib/runpath_search) +install(TARGETS conflict DESTINATION lib/conflict2) + +target_link_libraries(test_runpath PRIVATE ${test_runpath_names} rpath conflict) +set_property(TARGET test_runpath PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/runpath/../rpath" # Ensure that files that don't conflict are treated correctly + "${CMAKE_BINARY_DIR}/root-all/lib/runpath_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/lib/runpath" + "\${ORIGIN}/runpath_origin_postexcluded" + "\$ORIGIN/runpath_origin" + "${CMAKE_BINARY_DIR}/root-all/lib/conflict2" + ) +target_link_options(test_runpath PRIVATE -Wl,--enable-new-dtags) + +set_property(TARGET test_rpath ${test_rpath_names} test_runpath ${test_runpath_names} PROPERTY LIBRARY_OUTPUT_DIRECTORY lib) +install(TARGETS test_rpath test_runpath DESTINATION lib) + +add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c) +add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c) +add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c) +target_link_libraries(topexe PRIVATE test_rpath test_runpath) +target_link_libraries(toplib PRIVATE test_rpath test_runpath) +target_link_libraries(topmod PRIVATE test_rpath test_runpath) +set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib" + "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent" + "${CMAKE_BINARY_DIR}/root-all/lib/runpath_parent_unresolved" + ) +target_link_options(topexe PRIVATE -Wl,--disable-new-dtags) +target_link_options(toplib PRIVATE -Wl,--disable-new-dtags) +target_link_options(topmod PRIVATE -Wl,--disable-new-dtags) + +install(TARGETS topexe toplib RUNTIME DESTINATION bin LIBRARY DESTINATION lib) +install(TARGETS topmod LIBRARY DESTINATION lib/modules) + +install(CODE [[ + function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps + UNRESOLVED_DEPENDENCIES_VAR udeps + CONFLICTING_DEPENDENCIES_PREFIX cdeps + PRE_INCLUDE_REGEXES + "^lib(test_rpath|rpath_postexcluded|rpath|rpath_parent_postexcluded|rpath_parent|rpath_origin_postexcluded|rpath_origin|rpath_search_postexcluded|rpath_search|rpath_unresolved|test_runpath|runpath_postexcluded|runpath|runpath_origin_postexcluded|runpath_origin|runpath_parent_unresolved|runpath_search_postexcluded|runpath_search|runpath_unresolved|conflict)\\.so$" + "^libc\\.so" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES "^.*/(libtest_rpath|rpath/librpath|rpath_parent/librpath_parent|rpath_search/librpath_search|libtest_runpath|runpath/librunpath|runpath_origin_postexcluded|runpath_origin|runpath_search/librunpath_search|conflict2?/libconflict)\\.so$" + POST_EXCLUDE_REGEXES ".*" + DIRECTORIES + "${CMAKE_INSTALL_PREFIX}/lib/rpath_search_postexcluded" + "${CMAKE_INSTALL_PREFIX}/lib/rpath_search" + "${CMAKE_INSTALL_PREFIX}/lib/runpath_search_postexcluded" + "${CMAKE_INSTALL_PREFIX}/lib/runpath_search" + ${ARGN} + ) + list(SORT deps) + list(SORT udeps) + list(SORT cdeps_FILENAMES) + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "") + foreach(cdep IN LISTS cdeps_FILENAMES) + set(cdep_values ${cdeps_${cdep}}) + list(SORT cdep_values) + file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n") + endforeach() + endfunction() + + exec_get_runtime_dependencies( + deps1.txt udeps1.txt cdeps1.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>" + ) + + exec_get_runtime_dependencies( + deps2.txt udeps2.txt cdeps2.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:toplib>" + ) + + exec_get_runtime_dependencies( + deps3.txt udeps3.txt cdeps3.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/lib/modules/$<TARGET_FILE_NAME:topmod>" + ) + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c new file mode 100644 index 0000000..d196afe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c @@ -0,0 +1,9 @@ +extern void test_rpath(void); +extern void test_runpath(void); + +int main(void) +{ + test_rpath(); + test_runpath(); + return 0; +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c new file mode 100644 index 0000000..040e591 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c @@ -0,0 +1,8 @@ +extern void test_rpath(void); +extern void test_runpath(void); + +void toplib(void) +{ + test_rpath(); + test_runpath(); +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake new file mode 100644 index 0000000..4d6dde1 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake @@ -0,0 +1,157 @@ +function(check_contents filename contents_regex) + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}") + file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents) + if(NOT contents MATCHES "${contents_regex}") + string(APPEND RunCMake_TEST_FAILED "File contents: + ${contents} +do not match what we expected: + ${contents_regex} +in file: + ${CMAKE_INSTALL_PREFIX}/${filename}\n") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() + else() + string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() +endfunction() + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps1.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps1.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps2.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_executable_path_postexcluded\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps2.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps3.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_executable_path_postexcluded\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps3.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps4.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps4.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps5.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps5.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps6.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps6.txt "^${_check}$") + +set(_check + "^libconflict\\.dylib:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict/libconflict\\.dylib;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict2/libconflict\\.dylib\n$" + ) +check_contents(deps/cdeps1.txt "${_check}") +check_contents(deps/cdeps2.txt "${_check}") +check_contents(deps/cdeps3.txt "${_check}") +check_contents(deps/cdeps4.txt "${_check}") +check_contents(deps/cdeps5.txt "${_check}") +check_contents(deps/cdeps6.txt "${_check}") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt new file mode 100644 index 0000000..bc9e97a --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for librpath\.dylib: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath1/librpath\.dylib + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath2/librpath\.dylib$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake new file mode 100644 index 0000000..a8446fe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake @@ -0,0 +1,55 @@ +enable_language(C) + +set(test1_names rpath) +set(test2_names rpath) + +file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n") +add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c") +set_property(TARGET rpath PROPERTY INSTALL_NAME_DIR @rpath) +install(TARGETS rpath DESTINATION lib/rpath1) +install(TARGETS rpath DESTINATION lib/rpath2) + +file(REMOVE "${CMAKE_BINARY_DIR}/test1.c") +add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n") + +target_link_libraries(test1 PRIVATE ${test1_names}) +set_property(TARGET test1 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath1" + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/test2.c") +add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n") + +target_link_libraries(test2 PRIVATE ${test2_names}) +set_property(TARGET test2 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath2" + ) + +install(TARGETS test1 test2 DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>" + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>" + PRE_INCLUDE_REGEXES "^@rpath/librpath\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt new file mode 100644 index 0000000..73ab9f1 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Failed to run otool on: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-build/root-all/bin/\.\./lib/libtest\.dylib$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake new file mode 100644 index 0000000..3e4c434 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake @@ -0,0 +1,30 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void); + +int main(void) +{ + test(); + return 0; +} +]]) + +add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c") +set_property(TARGET test PROPERTY INSTALL_NAME_DIR @rpath) +add_executable(exe "${CMAKE_BINARY_DIR}/main.c") +target_link_libraries(exe PRIVATE test) +set_property(TARGET exe PROPERTY INSTALL_RPATH "@loader_path/../lib") + +install(TARGETS exe DESTINATION bin) + +install(CODE [[ + file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>") + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + PRE_INCLUDE_REGEXES "^@rpath/libtest\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt new file mode 100644 index 0000000..01762b4 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve file @rpath/libunresolved\.dylib$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake new file mode 100644 index 0000000..c9b6c95 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n unresolved();\n}\n") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n") +add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c") +target_link_libraries(testlib PRIVATE unresolved) +install(TARGETS testlib DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "^@rpath/libunresolved\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake new file mode 100644 index 0000000..6db05b3 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake @@ -0,0 +1,216 @@ +enable_language(C) + +set(testlib_names + preexcluded + executable_path + executable_path_bundle + executable_path_postexcluded + loader_path + loader_path_unresolved + loader_path_postexcluded + rpath + rpath_unresolved + rpath_postexcluded + rpath_executable_path + rpath_executable_path_bundle + rpath_executable_path_postexcluded + rpath_loader_path + rpath_loader_path_unresolved + rpath_loader_path_postexcluded + normal + normal_unresolved + normal_postexcluded + conflict + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +foreach(name ${testlib_names}) + if(name STREQUAL "normal") + file(WRITE "${CMAKE_BINARY_DIR}/normal.c" "extern void rpath(void);\nvoid normal(void)\n{\n rpath();\n}\n") + else() + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n") + endif() + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "void testlib(void)\n{\n") +foreach(name ${testlib_names}) + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n") +set_property(TARGET ${testlib_names} PROPERTY BUILD_WITH_INSTALL_NAME_DIR 1) +target_link_libraries(normal PRIVATE rpath) +set_property(TARGET normal PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/executable/lib/normal/../rpath" + ) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "extern void conflict(void);\nvoid testlib_conflict(void)\n{\n conflict();\n}\n") +add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c") +target_link_libraries(testlib_conflict PRIVATE conflict) + +set_property(TARGET testlib PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath" + "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_unresolved" + "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict" + @executable_path/../lib/rpath_executable_path + @executable_path/../lib/rpath_executable_path_unresolved + @executable_path/../lib/rpath_executable_path_postexcluded + @loader_path/rpath_loader_path + @loader_path/rpath_loader_path_unresolved + @loader_path/rpath_loader_path_postexcluded + ) +set_property(TARGET testlib_conflict PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict2" + ) + +foreach(t + executable_path + executable_path_postexcluded + loader_path + loader_path_postexcluded + rpath + rpath_postexcluded + rpath_executable_path + rpath_executable_path_postexcluded + rpath_loader_path + rpath_loader_path_postexcluded + conflict + ) + install(TARGETS ${t} DESTINATION executable/lib/${t}) +endforeach() +install(TARGETS conflict DESTINATION executable/lib/conflict2) + +foreach(t + executable_path_bundle + executable_path_postexcluded + loader_path_postexcluded + rpath_postexcluded + rpath_executable_path_bundle + rpath_executable_path_postexcluded + rpath_loader_path_postexcluded + ) + install(TARGETS ${t} DESTINATION bundle_executable/lib/${t}) +endforeach() + +foreach(t executable_path executable_path_bundle executable_path_postexcluded) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @executable_path/../lib/${t}) +endforeach() + +foreach(t loader_path loader_path_unresolved loader_path_postexcluded) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @loader_path/${t}) +endforeach() + +foreach(t + rpath + rpath_unresolved + rpath_postexcluded + rpath_executable_path + rpath_executable_path_bundle + rpath_executable_path_postexcluded + rpath_loader_path + rpath_loader_path_unresolved + rpath_loader_path_postexcluded + conflict + ) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @rpath) +endforeach() + +foreach(t normal normal_unresolved normal_postexcluded) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR "${CMAKE_BINARY_DIR}/root-all/executable/lib/${t}") + if(NOT t STREQUAL "normal_unresolved") + install(TARGETS ${t} DESTINATION executable/lib/${t}) + endif() +endforeach() + +target_link_libraries(testlib PRIVATE ${testlib_names}) + +add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c) +add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c) +add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c) +target_link_libraries(topexe PRIVATE testlib) +target_link_libraries(toplib PRIVATE testlib) +target_link_libraries(topmod PRIVATE testlib) + +set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH "${CMAKE_BINARY_DIR}/root-all/executable/lib") + +install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION executable/bin LIBRARY DESTINATION executable/lib) +install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION bundle_executable/bin LIBRARY DESTINATION bundle_executable/lib) + +install(CODE [[ + function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps + UNRESOLVED_DEPENDENCIES_VAR udeps + CONFLICTING_DEPENDENCIES_PREFIX cdeps + PRE_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|executable_path_postexcluded|loader_path|loader_path_unresolved|loader_path_postexcluded|rpath|rpath_unresolved|rpath_postexcluded|rpath_executable_path|rpath_executable_path_bundle|rpath_executable_path_postexcluded|rpath_loader_path|rpath_loader_path_unresolved|rpath_loader_path_postexcluded|normal|normal_unresolved|normal_postexcluded|conflict|System\\.B)\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|loader_path|rpath|rpath_executable_path|rpath_executable_path_bundle|rpath_loader_path|normal|conflict|System\\.B)\\.dylib$" + POST_EXCLUDE_REGEXES ".*" + ${ARGN} + ) + list(SORT deps) + list(SORT udeps) + list(SORT cdeps_FILENAMES) + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "") + foreach(cdep IN LISTS cdeps_FILENAMES) + set(cdep_values ${cdeps_${cdep}}) + list(SORT cdep_values) + file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n") + endforeach() + endfunction() + + exec_get_runtime_dependencies( + deps1.txt udeps1.txt cdeps1.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + ) + + exec_get_runtime_dependencies( + deps2.txt udeps2.txt cdeps2.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>" + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + ) + + exec_get_runtime_dependencies( + deps3.txt udeps3.txt cdeps3.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + ) + + exec_get_runtime_dependencies( + deps4.txt udeps4.txt cdeps4.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + BUNDLE_EXECUTABLE + "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>" + ) + + exec_get_runtime_dependencies( + deps5.txt udeps5.txt cdeps5.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>" + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>" + ) + + exec_get_runtime_dependencies( + deps6.txt udeps6.txt cdeps6.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>" + ) + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c new file mode 100644 index 0000000..20c6087 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c @@ -0,0 +1,7 @@ +extern void testlib(void); + +int main(void) +{ + testlib(); + return 0; +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c new file mode 100644 index 0000000..cff1bff --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c @@ -0,0 +1,6 @@ +extern void testlib(void); + +void toplib(void) +{ + testlib(); +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt new file mode 100644 index 0000000..d506645 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt @@ -0,0 +1,13 @@ +^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-project\.cmake:[0-9]+ \(file\): + You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is + probably not what you intended to do\. Instead, please consider using it in + an install\(CODE\) or install\(SCRIPT\) command\. For example: + + install\(CODE \[\[ + file\(GET_RUNTIME_DEPENDENCIES + # \.\.\. + \) + ]]\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake new file mode 100644 index 0000000..842d7ab --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake @@ -0,0 +1 @@ +file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt new file mode 100644 index 0000000..3db835c --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at file-GET_RUNTIME_DEPENDENCIES-unsupported\.cmake:[0-9]+ \(file\): + file GET_RUNTIME_DEPENDENCIES is not supported on system "[^ + ]+" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake new file mode 100644 index 0000000..b91eefe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake @@ -0,0 +1,2 @@ +file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps) +message(FATAL_ERROR "This message should not be displayed") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake new file mode 100644 index 0000000..c120ce4 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake @@ -0,0 +1,38 @@ +function(check_contents filename contents_regex) + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}") + file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents) + if(NOT contents MATCHES "${contents_regex}") + string(APPEND RunCMake_TEST_FAILED "File contents: + ${contents} +do not match what we expected: + ${contents_regex} +in file: + ${CMAKE_INSTALL_PREFIX}/${filename}\n") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() + else() + string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() +endfunction() + +set(_check + [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.conflict/\.\./(lib)?libdir\.dll]=] + [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.search/(lib)?search\.dll]=] + [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?testlib\.dll]=] + ) +check_contents(deps/deps1.txt "^${_check}$") +check_contents(deps/deps2.txt "^${_check}$") +check_contents(deps/deps3.txt "^${_check}$") +set(_check + [=[(lib)?unresolved\.dll]=] + ) +check_contents(deps/udeps1.txt "^${_check}$") +check_contents(deps/udeps2.txt "^${_check}$") +check_contents(deps/udeps3.txt "^${_check}$") +set(_check + "^(lib)?conflict\\.dll:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?conflict\\.dll\n$" + ) +check_contents(deps/cdeps1.txt "${_check}") +check_contents(deps/cdeps2.txt "${_check}") +check_contents(deps/cdeps3.txt "${_check}") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt new file mode 100644 index 0000000..66ecb93 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for (lib)?path\.dll: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test1/(lib)?path\.dll + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test2/(lib)?path\.dll$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake new file mode 100644 index 0000000..d413443 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake @@ -0,0 +1,47 @@ +enable_language(C) + +set(test1_names path) +set(test2_names path) + +file(WRITE "${CMAKE_BINARY_DIR}/path.c" "__declspec(dllexport) void path(void) {}\n") +add_library(path SHARED "${CMAKE_BINARY_DIR}/path.c") + +file(REMOVE "${CMAKE_BINARY_DIR}/test1.c") +add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllimport) extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllexport) void test1(void)\n{\n") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n") + +target_link_libraries(test1 PRIVATE ${test1_names}) + +file(REMOVE "${CMAKE_BINARY_DIR}/test2.c") +add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllimport) extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllexport) void test2(void)\n{\n") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n") + +target_link_libraries(test2 PRIVATE ${test2_names}) + +install(TARGETS test1 path DESTINATION lib/test1) +install(TARGETS test2 path DESTINATION lib/test2) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/test1/$<TARGET_FILE_NAME:test1>" + "${CMAKE_INSTALL_PREFIX}/lib/test2/$<TARGET_FILE_NAME:test2>" + PRE_INCLUDE_REGEXES "^(lib)?path\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt new file mode 100644 index 0000000..f921409 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Failed to run (dumpbin|objdump) on: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-build/root-all/bin/(lib)?test\.dll$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake new file mode 100644 index 0000000..6665a3b --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake @@ -0,0 +1,28 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/test.c" "__declspec(dllexport) void test(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[__declspec(dllimport) extern void test(void); + +int main(void) +{ + test(); + return 0; +} +]]) + +add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c") +add_executable(exe "${CMAKE_BINARY_DIR}/main.c") +target_link_libraries(exe PRIVATE test) + +install(TARGETS exe DESTINATION bin) + +install(CODE [[ + file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:test>") + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt new file mode 100644 index 0000000..a20654c --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve file (lib)?unresolved\.dll$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake new file mode 100644 index 0000000..4cc74c7 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void unresolved(void);\n__declspec(dllexport) void testlib(void)\n{\n unresolved();\n}\n") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "__declspec(dllexport) void unresolved(void) {}\n") +add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c") +target_link_libraries(testlib PRIVATE unresolved) +install(TARGETS testlib DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "^(lib)?unresolved\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake new file mode 100644 index 0000000..19288d8 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake @@ -0,0 +1,114 @@ +enable_language(C) + +set(testlib_names + preexcluded + libdir_postexcluded + libdir + search_postexcluded + search + unresolved + conflict + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +foreach(name ${testlib_names}) + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "__declspec(dllexport) void ${name}(void) {}\n") + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllexport) void testlib(void)\n{\n") +foreach(name ${testlib_names}) + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n") + +target_link_libraries(testlib PRIVATE ${testlib_names}) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "__declspec(dllimport) extern void conflict(void);\n__declspec(dllexport) void testlib_conflict(void)\n{\n conflict();\n}\n") +add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c") +target_link_libraries(testlib_conflict PRIVATE conflict) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib_noconflict.c" "__declspec(dllimport) extern void libdir(void);\n__declspec(dllexport) void testlib_noconflict(void)\n{\n libdir();\n}\n") +add_library(testlib_noconflict SHARED "${CMAKE_BINARY_DIR}/testlib_noconflict.c") +target_link_libraries(testlib_noconflict PRIVATE libdir) + +install(TARGETS testlib libdir_postexcluded libdir conflict testlib_noconflict DESTINATION bin) +install(TARGETS libdir search_postexcluded search DESTINATION bin/.search) # Prefixing with "." ensures it is the first item after list(SORT) +install(TARGETS testlib_conflict conflict DESTINATION bin/.conflict) + +add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c) +add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c) +add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c) +target_link_libraries(topexe PRIVATE testlib) +target_link_libraries(toplib PRIVATE testlib) +target_link_libraries(topmod PRIVATE testlib) + +install(TARGETS topexe toplib topmod DESTINATION bin) + +install(CODE [[ + function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps + UNRESOLVED_DEPENDENCIES_VAR udeps + CONFLICTING_DEPENDENCIES_PREFIX cdeps + PRE_INCLUDE_REGEXES + "^(lib)?testlib\\.dll$" + "^(lib)?libdir_postexcluded\\.dll$" + "^(lib)?libdir\\.dll$" + "^(lib)?search_postexcluded\\.dll$" + "^(lib)?search\\.dll$" + "^(lib)?unresolved\\.dll$" + "^(lib)?conflict\\.dll$" + "^kernel32\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES + "^.*/(lib)?testlib\\.dll$" + "^.*/(lib)?libdir\\.dll$" + "^.*/(lib)?search\\.dll$" + "^.*/(lib)?conflict\\.dll$" + POST_EXCLUDE_REGEXES ".*" + DIRECTORIES + "${CMAKE_INSTALL_PREFIX}/bin/.search" + ${ARGN} + ) + list(SORT deps) + list(SORT udeps) + list(SORT cdeps_FILENAMES) + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "") + foreach(cdep IN LISTS cdeps_FILENAMES) + set(cdep_values ${cdeps_${cdep}}) + list(SORT cdep_values) + file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n") + endforeach() + endfunction() + + exec_get_runtime_dependencies( + deps1.txt udeps1.txt cdeps1.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>" + ) + + exec_get_runtime_dependencies( + deps2.txt udeps2.txt cdeps2.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:toplib>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>" + ) + + exec_get_runtime_dependencies( + deps3.txt udeps3.txt cdeps3.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topmod>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>" + ) + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c new file mode 100644 index 0000000..713b8eb --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c @@ -0,0 +1,7 @@ +__declspec(dllimport) extern void testlib(void); + +int main(void) +{ + testlib(); + return 0; +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c new file mode 100644 index 0000000..6997175 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c @@ -0,0 +1,6 @@ +__declspec(dllimport) extern void testlib(void); + +__declspec(dllexport) void toplib(void) +{ + testlib(); +} diff --git a/Tests/Tutorial/Complete/tutorial.cxx b/Tests/Tutorial/Complete/tutorial.cxx index 443d195..4451cbd 100644 --- a/Tests/Tutorial/Complete/tutorial.cxx +++ b/Tests/Tutorial/Complete/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/MultiPackage/tutorial.cxx b/Tests/Tutorial/MultiPackage/tutorial.cxx index 443d195..4451cbd 100644 --- a/Tests/Tutorial/MultiPackage/tutorial.cxx +++ b/Tests/Tutorial/MultiPackage/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step10/tutorial.cxx b/Tests/Tutorial/Step10/tutorial.cxx index 443d195..4451cbd 100644 --- a/Tests/Tutorial/Step10/tutorial.cxx +++ b/Tests/Tutorial/Step10/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step11/tutorial.cxx b/Tests/Tutorial/Step11/tutorial.cxx index 3768855..38d4a79 100644 --- a/Tests/Tutorial/Step11/tutorial.cxx +++ b/Tests/Tutorial/Step11/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step2/tutorial.cxx b/Tests/Tutorial/Step2/tutorial.cxx index 75b7d67..5ba34e8 100644 --- a/Tests/Tutorial/Step2/tutorial.cxx +++ b/Tests/Tutorial/Step2/tutorial.cxx @@ -9,7 +9,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step3/tutorial.cxx b/Tests/Tutorial/Step3/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step3/tutorial.cxx +++ b/Tests/Tutorial/Step3/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step4/tutorial.cxx b/Tests/Tutorial/Step4/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step4/tutorial.cxx +++ b/Tests/Tutorial/Step4/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step5/tutorial.cxx b/Tests/Tutorial/Step5/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step5/tutorial.cxx +++ b/Tests/Tutorial/Step5/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step6/tutorial.cxx b/Tests/Tutorial/Step6/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step6/tutorial.cxx +++ b/Tests/Tutorial/Step6/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step7/tutorial.cxx b/Tests/Tutorial/Step7/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step7/tutorial.cxx +++ b/Tests/Tutorial/Step7/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step8/tutorial.cxx b/Tests/Tutorial/Step8/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step8/tutorial.cxx +++ b/Tests/Tutorial/Step8/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step9/tutorial.cxx b/Tests/Tutorial/Step9/tutorial.cxx index 73e67a9..6c41859 100644 --- a/Tests/Tutorial/Step9/tutorial.cxx +++ b/Tests/Tutorial/Step9/tutorial.cxx @@ -14,7 +14,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } @@ -261,6 +261,17 @@ CMAKE_CXX_SOURCES="\ cmAddSubDirectoryCommand \ cmAddTestCommand \ cmArgumentParser \ + cmBinUtilsLinker \ + cmBinUtilsLinuxELFGetRuntimeDependenciesTool \ + cmBinUtilsLinuxELFLinker \ + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool \ + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool \ + cmBinUtilsMacOSMachOLinker \ + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool \ + cmBinUtilsWindowsPEGetRuntimeDependenciesTool \ + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \ + cmBinUtilsWindowsPELinker \ + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \ cmBreakCommand \ cmBuildCommand \ cmCMakeMinimumRequired \ @@ -357,6 +368,8 @@ CMAKE_CXX_SOURCES="\ cmInstallTargetGenerator \ cmInstallTargetsCommand \ cmInstalledFile \ + cmLDConfigLDConfigTool \ + cmLDConfigTool \ cmLinkDirectoriesCommand \ cmLinkItem \ cmLinkLineComputer \ @@ -388,12 +401,12 @@ CMAKE_CXX_SOURCES="\ cmPolicies \ cmProcessOutput \ cmProjectCommand \ - cmProperty \ cmPropertyDefinition \ cmPropertyDefinitionMap \ cmPropertyMap \ cmReturnCommand \ cmRulePlaceholderExpander \ + cmRuntimeDependencyArchive \ cmScriptGenerator \ cmSearchPath \ cmSeparateArgumentsCommand \ @@ -443,7 +456,9 @@ CMAKE_CXX_SOURCES="\ if ${cmake_system_mingw}; then CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\ cmGlobalMSYSMakefileGenerator \ - cmGlobalMinGWMakefileGenerator" + cmGlobalMinGWMakefileGenerator \ + cmVSSetupHelper \ + " fi LexerParser_CXX_SOURCES="\ @@ -1370,7 +1385,7 @@ libs="" uv_c_flags="" if ${cmake_system_mingw}; then uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600" - libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv" + libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32" else uv_c_flags="${uv_c_flags} -DCMAKE_BOOTSTRAP" case "${cmake_system}" in |