summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimlich <chris@pcserenity.com>2023-03-06 18:48:24 (GMT)
committerChristian Heimlich <chris@pcserenity.com>2023-03-23 19:19:26 (GMT)
commitfa45594407c2650e9d350e04a534dd17314bf40a (patch)
treeece7462b15067412385c43b771f58c1ffc00a549
parent14cfd6a1ebf12c043aa4b5031bf9621bde068eb3 (diff)
downloadCMake-fa45594407c2650e9d350e04a534dd17314bf40a.zip
CMake-fa45594407c2650e9d350e04a534dd17314bf40a.tar.gz
CMake-fa45594407c2650e9d350e04a534dd17314bf40a.tar.bz2
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
-rw-r--r--Source/cmBinUtilsWindowsPELinker.cxx58
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake15
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt7
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt4
6 files changed, 78 insertions, 11 deletions
diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx
index 79e39e9..918f563 100644
--- a/Source/cmBinUtilsWindowsPELinker.cxx
+++ b/Source/cmBinUtilsWindowsPELinker.cxx
@@ -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);
}
}
}
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 0ebb720..080740c 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -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)
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
index 43b406b..a68607e 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
@@ -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()
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
index 9f05246..10b7b82 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
@@ -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}")
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt
new file mode 100644
index 0000000..607e4b8
--- /dev/null
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt
@@ -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$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt
new file mode 100644
index 0000000..fea1083
--- /dev/null
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt
@@ -0,0 +1,4 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+ file Could not resolve runtime dependencies:
+
+ UNRESOLVED\.DLL$