Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmBinUtilsWindowsPELinker.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
4
#include "cmBinUtilsWindowsPELinker.h"
5
6
#include <algorithm>
7
#include <cstddef>
8
#include <iterator>
9
#include <utility>
10
#include <vector>
11
12
#include <cm/filesystem>
13
#include <cm/memory>
14
15
#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
16
#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
17
#include "cmRuntimeDependencyArchive.h"
18
#include "cmStringAlgorithms.h"
19
#include "cmSystemTools.h"
20
#include "cmTargetTypes.h"
21
22
#ifdef _WIN32
23
#  include <windows.h>
24
25
#  include "cmsys/Encoding.hxx"
26
#else
27
#  include "cmsys/Directory.hxx"
28
#endif
29
30
namespace {
31
32
#ifdef _WIN32
33
std::string ReplaceWithActualNameCasing(std::string path)
34
{
35
  WIN32_FIND_DATAW findData;
36
  HANDLE hFind = ::FindFirstFileW(
37
    cmsys::Encoding::ToWindowsExtendedPath(path).c_str(), &findData);
38
  if (hFind != INVALID_HANDLE_VALUE) {
39
    auto onDiskName = cmsys::Encoding::ToNarrow(findData.cFileName);
40
    ::FindClose(hFind);
41
    path.replace(path.end() - onDiskName.size(), path.end(), onDiskName);
42
  }
43
  return path;
44
}
45
#else
46
bool FindCaseInsensitive(std::string const& dir, std::string const& lowerName,
47
                         std::string& foundPath)
48
0
{
49
0
  if (!cmSystemTools::FileIsDirectory(dir)) {
50
0
    return false;
51
0
  }
52
0
  cmsys::Directory directory;
53
0
  if (!directory.Load(dir)) {
54
0
    return false;
55
0
  }
56
0
  for (std::size_t i = 0; i < directory.GetNumberOfFiles(); ++i) {
57
0
    cm::filesystem::path fileName = directory.GetFile(i);
58
59
0
    if (cmSystemTools::LowerCase(fileName) == lowerName) {
60
0
      foundPath += dir / fileName;
61
0
      return true;
62
0
    }
63
0
  }
64
65
0
  return false;
66
0
}
67
#endif
68
}
69
70
cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
71
  cmRuntimeDependencyArchive* archive)
72
0
  : cmBinUtilsLinker(archive)
73
0
{
74
0
}
75
76
bool cmBinUtilsWindowsPELinker::Prepare()
77
0
{
78
0
  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
79
0
  if (tool.empty()) {
80
0
    std::vector<std::string> command;
81
0
    if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
82
0
      tool = "dumpbin";
83
0
    } else {
84
0
      tool = "objdump";
85
0
    }
86
0
  }
87
0
  if (tool == "dumpbin") {
88
0
    this->Tool =
89
0
      cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>(
90
0
        this->Archive);
91
0
  } else if (tool == "objdump") {
92
0
    this->Tool =
93
0
      cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>(
94
0
        this->Archive);
95
0
  } else {
96
0
    this->SetError(cmStrCat(
97
0
      "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: ", tool));
98
0
    return false;
99
0
  }
100
101
0
  return true;
102
0
}
103
104
cmBinUtilsWindowsPELinker::Dependency::Dependency(std::string casedName)
105
0
  : CasedName(std::move(casedName))
106
0
  , LowerName(cmSystemTools::LowerCase(CasedName))
107
0
{
108
0
}
109
110
bool cmBinUtilsWindowsPELinker::ScanDependencies(std::string const& file,
111
                                                 cm::TargetType /* unused */)
112
0
{
113
0
  std::vector<std::string> needed;
114
0
  if (!this->Tool->GetFileInfo(file, needed)) {
115
0
    return false;
116
0
  }
117
118
0
  std::vector<Dependency> depends;
119
0
  depends.reserve(needed.size());
120
0
  std::move(needed.begin(), needed.end(), std::back_inserter(depends));
121
0
  std::string origin = cmSystemTools::GetFilenamePath(file);
122
123
0
  for (Dependency const& lib : depends) {
124
0
    if (this->Archive->IsPreExcluded(lib.LowerName)) {
125
0
      continue;
126
0
    }
127
0
    Dependency path;
128
0
    bool resolved = false;
129
0
    if (!this->ResolveDependency(lib, origin, path, resolved)) {
130
0
      return false;
131
0
    }
132
0
    if (resolved) {
133
0
      if (this->Archive->IsPostExcluded(path.LowerName, path.CasedName)) {
134
0
        continue;
135
0
      }
136
0
      bool unique;
137
0
      this->Archive->AddResolvedPath(lib.CasedName, path.CasedName, unique);
138
0
      if (unique &&
139
0
          !this->ScanDependencies(path.CasedName,
140
0
                                  cm::TargetType::SHARED_LIBRARY)) {
141
0
        return false;
142
0
      }
143
0
    } else {
144
0
      this->Archive->AddUnresolvedPath(lib.CasedName);
145
0
    }
146
0
  }
147
148
0
  return true;
149
0
}
150
151
bool cmBinUtilsWindowsPELinker::ResolveDependency(Dependency const& lib,
152
                                                  std::string const& origin,
153
                                                  Dependency& path,
154
                                                  bool& resolved)
155
0
{
156
0
  auto dirs = this->Archive->GetSearchDirectories();
157
158
#ifdef _WIN32
159
  char buf[MAX_PATH];
160
  unsigned int len;
161
  if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) {
162
    dirs.insert(dirs.begin(), std::string(buf, len));
163
  }
164
  if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) {
165
    dirs.insert(dirs.begin(), std::string(buf, len));
166
  }
167
#endif
168
169
0
  dirs.insert(dirs.begin(), origin);
170
171
0
  for (auto const& searchPath : dirs) {
172
#ifdef _WIN32
173
    path.LowerName = cmStrCat(searchPath, '/', lib.LowerName);
174
    if (!cmSystemTools::PathExists(path.LowerName)) {
175
      continue;
176
    }
177
    this->NormalizePath(path.LowerName);
178
    path.CasedName = ReplaceWithActualNameCasing(path.LowerName);
179
#else
180
0
    if (!FindCaseInsensitive(searchPath, lib.LowerName, path.CasedName)) {
181
0
      continue;
182
0
    }
183
0
    this->NormalizePath(path.CasedName);
184
0
    path.LowerName = [&lib](std::string libPath) -> std::string {
185
0
      libPath.replace(libPath.end() - lib.LowerName.size(), libPath.end(),
186
0
                      lib.LowerName);
187
0
      return libPath;
188
0
    }(path.CasedName);
189
0
#endif
190
0
    resolved = true;
191
0
    return true;
192
0
  }
193
194
0
  resolved = false;
195
0
  return true;
196
0
}