Browse Source

Merge topic 'file-GET_RUNTIME_DEPENDENCIES-windows-casing'

fa45594407 file(GET_RUNTIME_DEPENDENCIES): Preserve casing for Windows PE binaries
14cfd6a1eb Tests: Add test for file(GET_RUNTIME_DEPENDENCIES) with MixedCase.dll

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !8291
Brad King 2 years ago
parent
commit
89b73ba980

+ 50 - 8
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);
       }
     }
   }

+ 1 - 0
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)

+ 4 - 0
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()
 

+ 12 - 2
Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake

@@ -1,19 +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)?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}")

+ 7 - 0
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$

+ 4 - 0
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$

+ 4 - 1
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"