mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 19:00:54 -06:00
file(GET_RUNTIME_DEPENDENCIES): Preserve casing for Windows PE binaries
For Windows PE files the `file(GET_RUNTIME_DEPENDENCIES)` command converts the name of all DLLs found during binary scanning to lowercase in order to simplify the syntax requirements of its regex filters; however, this has the side-effect of causing all DLL paths returned via RESOLVED_DEPENDENCIES_VAR to be in lowercase, regardless of their actual casing. Instead, respect the original casing as closely as possible when returning resolved dependencies after all filters have been passed: When evaluating a Windows PE format binary on a non-Windows host the casing of dependencies recorded within the binary are used. When the host is running Windows, the actual casing of the dependencies on-disk are used instead. Fixes: #23091
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
|
||||
#include "cmBinUtilsWindowsPELinker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/memory>
|
||||
@@ -16,6 +19,27 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
||||
# include "cmsys/Encoding.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace {
|
||||
|
||||
void ReplaceWithActualNameCasing(std::string& path)
|
||||
{
|
||||
WIN32_FIND_DATAW findData;
|
||||
HANDLE hFind = ::FindFirstFileW(
|
||||
cmsys::Encoding::ToWindowsExtendedPath(path).c_str(), &findData);
|
||||
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
auto onDiskName = cmsys::Encoding::ToNarrow(findData.cFileName);
|
||||
::FindClose(hFind);
|
||||
path.replace(path.end() - onDiskName.size(), path.end(), onDiskName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
|
||||
@@ -60,29 +84,47 @@ bool cmBinUtilsWindowsPELinker::ScanDependencies(
|
||||
if (!this->Tool->GetFileInfo(file, needed)) {
|
||||
return false;
|
||||
}
|
||||
for (auto& n : needed) {
|
||||
n = cmSystemTools::LowerCase(n);
|
||||
}
|
||||
|
||||
struct WinPEDependency
|
||||
{
|
||||
WinPEDependency(std::string o)
|
||||
: Original(std::move(o))
|
||||
, LowerCase(cmSystemTools::LowerCase(Original))
|
||||
{
|
||||
}
|
||||
std::string const Original;
|
||||
std::string const LowerCase;
|
||||
};
|
||||
|
||||
std::vector<WinPEDependency> depends;
|
||||
depends.reserve(needed.size());
|
||||
std::move(needed.begin(), needed.end(), std::back_inserter(depends));
|
||||
std::string origin = cmSystemTools::GetFilenamePath(file);
|
||||
|
||||
for (auto const& lib : needed) {
|
||||
if (!this->Archive->IsPreExcluded(lib)) {
|
||||
for (auto const& lib : depends) {
|
||||
if (!this->Archive->IsPreExcluded(lib.LowerCase)) {
|
||||
std::string path;
|
||||
bool resolved = false;
|
||||
if (!this->ResolveDependency(lib, origin, path, resolved)) {
|
||||
if (!this->ResolveDependency(lib.LowerCase, origin, path, resolved)) {
|
||||
return false;
|
||||
}
|
||||
if (resolved) {
|
||||
if (!this->Archive->IsPostExcluded(path)) {
|
||||
#ifdef _WIN32
|
||||
ReplaceWithActualNameCasing(path);
|
||||
#else
|
||||
path.replace(path.end() - lib.Original.size(), path.end(),
|
||||
lib.Original);
|
||||
#endif
|
||||
bool unique;
|
||||
this->Archive->AddResolvedPath(lib, path, unique);
|
||||
this->Archive->AddResolvedPath(lib.Original, path, unique);
|
||||
if (unique &&
|
||||
!this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->Archive->AddUnresolvedPath(lib);
|
||||
this->Archive->AddUnresolvedPath(lib.Original);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,6 +784,7 @@ if(DEFINED CMake_COMPILER_FORCES_NEW_DTAGS)
|
||||
endif()
|
||||
add_RunCMake_test(file-GET_RUNTIME_DEPENDENCIES
|
||||
-DCMake_INSTALL_NAME_TOOL_BUG=${CMake_INSTALL_NAME_TOOL_BUG}
|
||||
-DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
|
||||
)
|
||||
|
||||
add_RunCMake_test(CPackCommandLine)
|
||||
|
||||
@@ -9,6 +9,10 @@ function(run_install_test case)
|
||||
run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
|
||||
# Check "all" components.
|
||||
set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-all)
|
||||
set(maybe_stderr "${case}-all-stderr-${CMAKE_C_COMPILER_ID}.txt")
|
||||
if(EXISTS "${RunCMake_SOURCE_DIR}/${maybe_stderr}")
|
||||
set(RunCMake-stderr-file "${maybe_stderr}")
|
||||
endif()
|
||||
run_cmake_command(${case}-all ${CMAKE_COMMAND} --install . --prefix ${CMAKE_INSTALL_PREFIX} --config Debug)
|
||||
endfunction()
|
||||
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "Borland")
|
||||
# Borland upper-cases dll names referenced in import libraries.
|
||||
set(conflict_dll [[CONFLICT\.DLL]])
|
||||
set(unresolved_dll [[UNRESOLVED\.DLL]])
|
||||
else()
|
||||
set(conflict_dll [[conflict\.dll]])
|
||||
set(unresolved_dll [[unresolved\.dll]])
|
||||
endif()
|
||||
|
||||
set(_check
|
||||
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\.conflict/\.\./(lib)?libdir\.dll]=]
|
||||
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\.search/(lib)?search\.dll]=]
|
||||
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?mixedcase\.dll]=]
|
||||
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?MixedCase\.dll]=]
|
||||
[=[[^;]*/Tests/RunCMake/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]=]
|
||||
"(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/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?conflict\\.dll\n$"
|
||||
"^(lib)?${conflict_dll}:[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/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}")
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
|
||||
file Multiple conflicting paths found for PATH\.DLL:
|
||||
|
||||
[^
|
||||
]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test1/path\.dll
|
||||
[^
|
||||
]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test2/path\.dll$
|
||||
@@ -0,0 +1,4 @@
|
||||
^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
|
||||
file Could not resolve runtime dependencies:
|
||||
|
||||
UNRESOLVED\.DLL$
|
||||
Reference in New Issue
Block a user