Coverage Report

Created: 2026-09-14 06:43

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