From 14cfd6a1ebf12c043aa4b5031bf9621bde068eb3 Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Mon, 6 Mar 2023 13:48:24 -0500 Subject: Tests: Add test for file(GET_RUNTIME_DEPENDENCIES) with MixedCase.dll Issue: #23091 --- Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake | 1 + Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) 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 cb0e534..9f05246 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake @@ -1,6 +1,7 @@ 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)?testlib\.dll]=] ) check_contents(deps/deps1.txt "^${_check}$") diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake index 9160ce5..aad9077 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake @@ -8,6 +8,7 @@ set(testlib_names search unresolved conflict + MixedCase ) file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c") @@ -34,7 +35,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/testlib_noconflict.c" "__declspec(dllimport) ext 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 testlib libdir_postexcluded libdir conflict MixedCase 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) @@ -61,6 +62,7 @@ install(CODE [[ "^(lib)?search\\.dll$" "^(lib)?unresolved\\.dll$" "^(lib)?conflict\\.dll$" + "^(lib)?mixedcase\\.dll$" "^kernel32\\.dll$" PRE_EXCLUDE_REGEXES ".*" POST_INCLUDE_REGEXES @@ -68,6 +70,7 @@ install(CODE [[ "^.*/(lib)?libdir\\.dll$" "^.*/(lib)?search\\.dll$" "^.*/(lib)?conflict\\.dll$" + "^.*/(lib)?mixedcase\\.dll$" POST_EXCLUDE_REGEXES ".*" DIRECTORIES "${CMAKE_INSTALL_PREFIX}/bin/.search" -- cgit v0.12 From fa45594407c2650e9d350e04a534dd17314bf40a Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Mon, 6 Mar 2023 13:48:24 -0500 Subject: 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 --- Source/cmBinUtilsWindowsPELinker.cxx | 58 +++++++++++++++++++--- Tests/RunCMake/CMakeLists.txt | 1 + .../RunCMakeTest.cmake | 4 ++ .../windows-all-check.cmake | 15 ++++-- .../windows-conflict-all-stderr-Borland.txt | 7 +++ .../windows-unresolved-all-stderr-Borland.txt | 4 ++ 6 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt create mode 100644 Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt 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 +#include #include +#include #include #include @@ -16,6 +19,27 @@ #ifdef _WIN32 # include + +# 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 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$ -- cgit v0.12