file(GET_RUNTIME_DEPENDENCIES): Normalize paths before matching

Regex-based filtering should not have to account for slash differences.
Add policy CMP0207 for compatibility.

Fixes: #26202
This commit is contained in:
hanna.rusakovich
2025-10-24 21:29:24 +03:00
committed by HannaWAR
parent 22e4e7448f
commit bf3f69834d
27 changed files with 297 additions and 8 deletions

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
file Could not resolve runtime dependencies:
[^
]*test\.dll$

View File

@@ -1122,6 +1122,8 @@ Handling Runtime Binaries
The following arguments specify filters for including or excluding libraries
to be resolved. See below for a full description of how they work.
Directory separators in file paths may be matched using forward
slashes unless policy :policy:`CMP0207` is not set to ``NEW``.
``PRE_INCLUDE_REGEXES <regexes>...``
List of pre-include regexes through which to filter the names of

View File

@@ -98,6 +98,7 @@ Policies Introduced by CMake 4.3
.. toctree::
:maxdepth: 1
CMP0207: file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching. </policy/CMP0207>
CMP0206: The CPack Archive Generator defaults to UID 0 and GID 0. </policy/CMP0206>
CMP0205: file(CREATE_LINK) with COPY_ON_ERROR copies directory content. </policy/CMP0205>

26
Help/policy/CMP0207.rst Normal file
View File

@@ -0,0 +1,26 @@
CMP0207
-------
.. versionadded:: 4.3
:command:`file(GET_RUNTIME_DEPENDENCIES)` normalizes paths before matching.
The :command:`file(GET_RUNTIME_DEPENDENCIES)` and
:command:`install(RUNTIME_DEPENDENCY_SET)` commands support filtering
resolved dependencies using regular expressions matching their paths.
In CMake 4.2 and below, callers were responsible for matching both forward
and backward slashes as path separators on Windows, e.g., via ``[\/]``.
CMake 4.3 and above prefer to normalize paths to use forward slashes before
matching. This policy provides compaitiblity for projects that may have
been relying on matching backslashes only.
The ``OLD`` behavior for this policy matches filters against paths that
may contain any combination of forward and backward slashes on Windows.
The ``NEW`` behavior for this policy to convert all paths to forward
slashes before matching filters.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.3
.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
.. include:: include/STANDARD_ADVICE.rst
.. include:: include/DEPRECATED.rst

View File

@@ -0,0 +1,6 @@
file-GET_RUNTIME_DEPENDENCIES-matching
--------------------------------------
* The :command:`file(GET_RUNTIME_DEPENDENCIES)`
and :command:`install(RUNTIME_DEPENDENCY_SET)` commands now normalize
paths before matching filters. See policy :policy:`CMP0207`.

View File

@@ -3,7 +3,14 @@
#include "cmBinUtilsLinker.h"
#include <utility>
#include "cmCMakePath.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRuntimeDependencyArchive.h"
#include "cmStringAlgorithms.h"
cmBinUtilsLinker::cmBinUtilsLinker(cmRuntimeDependencyArchive* archive)
: Archive(archive)
@@ -14,3 +21,27 @@ void cmBinUtilsLinker::SetError(std::string const& e)
{
this->Archive->SetError(e);
}
void cmBinUtilsLinker::NormalizePath(std::string& path) const
{
std::string normalizedPath =
cmCMakePath(path, cmCMakePath::auto_format).GenericString();
if (path == normalizedPath) {
return;
}
cmPolicies::PolicyStatus policy =
this->Archive->GetMakefile()->GetPolicyStatus(cmPolicies::CMP0207);
if (policy == cmPolicies::WARN) {
this->Archive->GetMakefile()->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0207), '\n',
"Path\n \"", path,
"\"\n"
"would be converted to\n \"",
normalizedPath, "\"\n"));
} else if (policy == cmPolicies::NEW) {
path = std::move(normalizedPath);
}
}

View File

@@ -24,4 +24,6 @@ protected:
cmRuntimeDependencyArchive* Archive;
void SetError(std::string const& e);
void NormalizePath(std::string& path) const;
};

View File

@@ -204,6 +204,7 @@ bool cmBinUtilsLinuxELFLinker::ResolveDependency(
path = cmStrCat(searchPath, '/', name);
if (cmSystemTools::PathExists(path) &&
FileHasArchitecture(path.c_str(), this->Machine)) {
this->NormalizePath(path);
resolved = true;
return true;
}

View File

@@ -170,6 +170,7 @@ bool cmBinUtilsMacOSMachOLinker::ResolveDependency(
} else {
resolved = true;
path = name;
this->NormalizePath(path);
}
if (resolved && !cmSystemTools::FileIsFullPath(path)) {
@@ -198,6 +199,7 @@ bool cmBinUtilsMacOSMachOLinker::ResolveExecutablePathDependency(
return true;
}
this->NormalizePath(path);
resolved = true;
return true;
}
@@ -220,6 +222,7 @@ bool cmBinUtilsMacOSMachOLinker::ResolveLoaderPathDependency(
return true;
}
this->NormalizePath(path);
resolved = true;
return true;
}
@@ -252,7 +255,7 @@ bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency(
/*
* 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
* /a/libB.dylib will be found first if it exists. CMake tries to
* sort rpath directories to avoid this, but sometimes there is no
* right answer.
*
@@ -268,7 +271,9 @@ bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency(
* so as long as this method's resolution guarantees priority
* in that manner further checking should not be necessary?
*/
path = searchFile;
path = std::move(searchFile);
this->NormalizePath(path);
resolved = true;
return true;
}

View File

@@ -155,6 +155,7 @@ bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name,
for (auto const& searchPath : dirs) {
path = cmStrCat(searchPath, '/', name);
if (cmSystemTools::PathExists(path)) {
this->NormalizePath(path);
resolved = true;
return true;
}

View File

@@ -247,7 +247,8 @@ void AddInstallRuntimeDependenciesGenerator(
cmInstallGenerator::SelectMessageLevel(helper.Makefile),
runtimeDependenciesArgsRef.GetExcludeFromAll() &&
(apple ? frameworkArgs.GetExcludeFromAll() : true),
helper.Makefile->GetBacktrace());
helper.Makefile->GetBacktrace(),
helper.Makefile->GetPolicyStatus(cmPolicies::CMP0207));
helper.Makefile->AddInstallGenerator(
std::move(getRuntimeDependenciesGenerator));

View File

@@ -19,6 +19,7 @@
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmOutputConverter.h"
#include "cmPolicies.h"
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
@@ -83,7 +84,8 @@ cmInstallGetRuntimeDependenciesGenerator::
std::vector<std::string> postExcludeFiles, std::string libraryComponent,
std::string frameworkComponent, bool noInstallRPath, char const* depsVar,
char const* rpathPrefix, std::vector<std::string> const& configurations,
MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace)
MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace,
cmPolicies::PolicyStatus policyStatusCMP0207)
: cmInstallGenerator("", configurations, "", message, exclude_from_all,
false, std::move(backtrace))
, RuntimeDependencySet(runtimeDependencySet)
@@ -96,6 +98,7 @@ cmInstallGetRuntimeDependenciesGenerator::
, PostExcludeFiles(std::move(postExcludeFiles))
, LibraryComponent(std::move(libraryComponent))
, FrameworkComponent(std::move(frameworkComponent))
, PolicyStatusCMP0207(policyStatusCMP0207)
, NoInstallRPath(noInstallRPath)
, DepsVar(depsVar)
, RPathPrefix(rpathPrefix)
@@ -141,6 +144,14 @@ void cmInstallGetRuntimeDependenciesGenerator::GenerateScriptForConfig(
this->LocalGenerator->GetMakefile()->GetSafeDefinition(
"CMAKE_INSTALL_NAME_TOOL");
Indent inputIndent = indent;
if (this->PolicyStatusCMP0207 != cmPolicies::WARN) {
indent = indent.Next();
os << inputIndent << "block(SCOPE_FOR POLICIES)\n"
<< indent << "cmake_policy(SET CMP0207 "
<< (this->PolicyStatusCMP0207 == cmPolicies::NEW ? "NEW" : "OLD")
<< ")\n";
}
os << indent << "file(GET_RUNTIME_DEPENDENCIES\n"
<< indent << " RESOLVED_DEPENDENCIES_VAR " << this->DepsVar << '\n';
WriteFilesArgument(os, "EXECUTABLES"_s,
@@ -204,4 +215,8 @@ void cmInstallGetRuntimeDependenciesGenerator::GenerateScriptForConfig(
os << indent << " RPATH_PREFIX " << this->RPathPrefix << '\n';
}
os << indent << " )\n";
if (this->PolicyStatusCMP0207 != cmPolicies::WARN) {
os << inputIndent << "endblock()\n";
}
}

View File

@@ -7,6 +7,7 @@
#include <vector>
#include "cmInstallGenerator.h"
#include "cmPolicies.h"
class cmListFileBacktrace;
class cmLocalGenerator;
@@ -26,8 +27,8 @@ public:
std::vector<std::string> postExcludeFiles, std::string libraryComponent,
std::string frameworkComponent, bool noInstallRPath, char const* depsVar,
char const* rpathPrefix, std::vector<std::string> const& configurations,
MessageLevel message, bool exclude_from_all,
cmListFileBacktrace backtrace);
MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace,
cmPolicies::PolicyStatus policyStatusCMP0207);
bool Compute(cmLocalGenerator* lg) override;
@@ -48,6 +49,7 @@ private:
std::vector<std::string> PostExcludeFiles;
std::string LibraryComponent;
std::string FrameworkComponent;
cmPolicies::PolicyStatus PolicyStatusCMP0207;
bool NoInstallRPath;
char const* DepsVar;
char const* RPathPrefix;

View File

@@ -618,7 +618,10 @@ class cmMakefile;
3, 0, WARN) \
SELECT(POLICY, CMP0206, \
"The CPack Archive Generator defaults to UID 0 and GID 0.", 4, 3, 0, \
WARN)
WARN) \
SELECT(POLICY, CMP0207, \
"file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching.", \
4, 3, 0, WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@@ -367,7 +367,7 @@ void cmRuntimeDependencyArchive::AddResolvedPath(
break;
}
}
it->second.insert(path);
it->second.emplace(path);
this->RPaths[path] = std::move(rpaths);
}

View File

@@ -1,4 +1,5 @@
set(CMAKE_SKIP_RPATH OFF)
cmake_policy(SET CMP0207 NEW)
# Import targets from the install tree.
include(${Import_BINARY_DIR}/../Root/install-RUNTIME_DEPENDENCY_SET/targets.cmake)

View File

@@ -0,0 +1,13 @@
cmake_policy(SET CMP0207 NEW)
include("${CMAKE_CURRENT_LIST_DIR}/CMP0207-common.cmake")
install(CODE [[
if(results_old)
message(SEND_ERROR "Old dependencies are not empty: `${results_old}`")
endif()
if(NOT results_new)
message(SEND_ERROR "New dependencies are empty: `${results_new}`")
endif()
]])

View File

@@ -0,0 +1,13 @@
cmake_policy(SET CMP0207 OLD)
include("${CMAKE_CURRENT_LIST_DIR}/CMP0207-common.cmake")
install(CODE [[
if(NOT results_old)
message(SEND_ERROR "Old dependencies are empty: `${results_old}`")
endif()
if(results_new)
message(SEND_ERROR "New dependencies are not empty: `${results_new}`")
endif()
]])

View File

@@ -0,0 +1,44 @@
^CMake Warning \(dev\) at cmake_install\.cmake:[0-9]+ \(file\):
Policy CMP0207 is not set: file\(GET_RUNTIME_DEPENDENCIES\) normalizes paths
before matching\. Run "cmake --help-policy CMP0207" for policy details\.
Use the cmake_policy command to set the policy and suppress this warning\.
Path
"[^"]*test\.dll"
would be converted to
"[^"]*test\.dll"
This warning is for project developers\. Use -Wno-dev to suppress it\.
+
CMake Warning \(dev\) at cmake_install\.cmake:[0-9]+ \(file\):
Policy CMP0207 is not set: file\(GET_RUNTIME_DEPENDENCIES\) normalizes paths
before matching\. Run "cmake --help-policy CMP0207" for policy details\.
Use the cmake_policy command to set the policy and suppress this warning\.
Path
"[^"]*test\.dll"
would be converted to
"[^"]*test\.dll"
This warning is for project developers\. Use -Wno-dev to suppress it\.
+
CMake Warning \(dev\) at cmake_install\.cmake:[0-9]+ \(file\):
Policy CMP0207 is not set: file\(GET_RUNTIME_DEPENDENCIES\) normalizes paths
before matching\. Run "cmake --help-policy CMP0207" for policy details\.
Use the cmake_policy command to set the policy and suppress this warning\.
Path
"[^"]*test\.dll"
would be converted to
"[^"]*test\.dll"
This warning is for project developers\. Use -Wno-dev to suppress it\.$

View File

@@ -0,0 +1,12 @@
# CMP0207 is unset
include("${CMAKE_CURRENT_LIST_DIR}/CMP0207-common.cmake")
install(CODE [[
if(NOT results_old)
message(SEND_ERROR "Old dependencies are empty: `${results_old}`")
endif()
if(results_new)
message(SEND_ERROR "New dependencies are not empty: `${results_new}`")
endif()
]])

View File

@@ -0,0 +1,93 @@
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_subdirectory(CMP0207-subdir)
add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
target_link_libraries(exe PRIVATE test)
install(TARGETS test DESTINATION bin/lib1)
install(
TARGETS exe
DESTINATION results_old
RUNTIME_DEPENDENCIES
DIRECTORIES "${CMAKE_BINARY_DIR}/root-all\\bin\\lib1"
PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$"
PRE_EXCLUDE_REGEXES ".*"
POST_INCLUDE_REGEXES "\\\\lib1/(lib)?test\\.dll$"
POST_EXCLUDE_REGEXES ".*"
)
install(
TARGETS exe
DESTINATION results_new
RUNTIME_DEPENDENCIES
DIRECTORIES "${CMAKE_BINARY_DIR}/root-all\\bin\\lib1"
PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$"
PRE_EXCLUDE_REGEXES ".*"
POST_INCLUDE_REGEXES "/lib1/(lib)?test\\.dll$"
POST_EXCLUDE_REGEXES ".*"
)
install(
TARGETS exe
DESTINATION results_any
RUNTIME_DEPENDENCIES
DIRECTORIES "${CMAKE_BINARY_DIR}/root-all\\bin\\lib1"
PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$"
PRE_EXCLUDE_REGEXES ".*"
POST_INCLUDE_REGEXES "(\\\\|/)lib1/(lib)?test\\.dll$"
POST_EXCLUDE_REGEXES ".*"
)
install(
TARGETS exe
DESTINATION results_any_forward
RUNTIME_DEPENDENCIES
DIRECTORIES "${CMAKE_BINARY_DIR}/root-all/bin/lib1"
PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$"
PRE_EXCLUDE_REGEXES ".*"
POST_INCLUDE_REGEXES "(\\\\|/)lib1/(lib)?test\\.dll$"
POST_EXCLUDE_REGEXES ".*"
)
install(
CODE [[
function(check_installed_lib directory out_var)
file(GLOB_RECURSE actual
LIST_DIRECTORIES FALSE
RELATIVE ${CMAKE_INSTALL_PREFIX}/${directory}
${CMAKE_INSTALL_PREFIX}/${directory}/*.dll
)
if(actual)
list(SORT actual)
endif()
set(${out_var} "${actual}" PARENT_SCOPE)
endfunction()
check_installed_lib("results_old" results_old)
check_installed_lib("results_new" results_new)
check_installed_lib("results_any" results_any)
check_installed_lib("results_any_forward" results_any_forward)
if(NOT results_any)
message(SEND_ERROR "Any dependencies are empty: `${results_any}`")
endif()
if(NOT results_any_forward)
message(SEND_ERROR "Any forward dependencies are empty: `${results_any_forward}`")
endif()
]]
)

View File

@@ -0,0 +1 @@
add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")

View File

@@ -49,6 +49,9 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
run_cmake(badargs1)
run_cmake(badargs2)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
run_install_test(CMP0207-OLD)
run_install_test(CMP0207-WARN)
run_install_test(CMP0207-NEW)
run_install_test(windows)
run_install_test(windows-unresolved)
run_install_test(windows-conflict)

View File

@@ -50,6 +50,7 @@ install(TARGETS topexe toplib topmod DESTINATION bin)
install(CODE [[
function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
cmake_policy(SET CMP0207 NEW)
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR deps
UNRESOLVED_DEPENDENCIES_VAR udeps

View File

@@ -0,0 +1,5 @@
^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
file Could not resolve runtime dependencies:
[^
]*test\.dll$