Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmRuntimeDependencyArchive.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 "cmRuntimeDependencyArchive.h"
5
6
#include <algorithm>
7
#include <sstream>
8
#include <string>
9
#include <utility>
10
#include <vector>
11
12
#include <cm/memory>
13
14
#include "cmBinUtilsLinuxELFLinker.h"
15
#include "cmBinUtilsMacOSMachOLinker.h"
16
#include "cmBinUtilsWindowsPELinker.h"
17
#include "cmExecutionStatus.h"
18
#include "cmList.h"
19
#include "cmMakefile.h"
20
#include "cmStateTypes.h"
21
#include "cmSystemTools.h"
22
23
#if defined(_WIN32)
24
#  include "cmGlobalGenerator.h"
25
#  ifndef CMAKE_BOOTSTRAP
26
#    include "cmGlobalVisualStudioVersionedGenerator.h"
27
#  endif
28
#  include "cmsys/Glob.hxx"
29
30
#  include "cmVSSetupHelper.h"
31
#endif
32
33
#if defined(_WIN32)
34
static void AddVisualStudioPath(std::vector<std::string>& paths,
35
                                std::string const& prefix,
36
                                unsigned int version, cmGlobalGenerator* gg)
37
{
38
  // If generating for the VS IDE, use the same instance.
39
  std::string vsloc;
40
  bool found = false;
41
#  ifndef CMAKE_BOOTSTRAP
42
  if (cmHasPrefix(gg->GetName(), prefix)) {
43
    cmGlobalVisualStudioVersionedGenerator* vsgen =
44
      static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
45
    if (vsgen->GetVSInstance(vsloc)) {
46
      found = true;
47
    }
48
  }
49
#  endif
50
51
  // Otherwise, find a VS instance ourselves.
52
  if (!found) {
53
    cmVSSetupAPIHelper vsSetupAPIHelper(version);
54
    if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) {
55
      cmSystemTools::ConvertToUnixSlashes(vsloc);
56
      found = true;
57
    }
58
  }
59
60
  if (found) {
61
    cmsys::Glob glob;
62
    glob.SetListDirs(true);
63
    glob.FindFiles(vsloc + "/VC/Tools/MSVC/*");
64
    for (auto const& vcdir : glob.GetFiles()) {
65
      paths.push_back(vcdir + "/bin/Hostx64/x64");
66
      paths.push_back(vcdir + "/bin/Hostx86/x64");
67
      paths.push_back(vcdir + "/bin/Hostx64/x86");
68
      paths.push_back(vcdir + "/bin/Hostx86/x86");
69
    }
70
  }
71
}
72
73
static void AddRegistryPath(std::vector<std::string>& paths,
74
                            std::string const& path, cmMakefile* mf)
75
{
76
  // We should view the registry as the target application would view
77
  // it.
78
  cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
79
  cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
80
  if (mf->PlatformIs64Bit()) {
81
    view = cmSystemTools::KeyWOW64_64;
82
    other_view = cmSystemTools::KeyWOW64_32;
83
  }
84
85
  // Expand using the view of the target application.
86
  std::string expanded = path;
87
  cmSystemTools::ExpandRegistryValues(expanded, view);
88
  cmSystemTools::GlobDirs(expanded, paths);
89
90
  // Executables can be either 32-bit or 64-bit, so expand using the
91
  // alternative view.
92
  expanded = path;
93
  cmSystemTools::ExpandRegistryValues(expanded, other_view);
94
  cmSystemTools::GlobDirs(expanded, paths);
95
}
96
97
static void AddEnvPath(std::vector<std::string>& paths, std::string const& var,
98
                       std::string const& suffix)
99
{
100
  std::string value;
101
  if (cmSystemTools::GetEnv(var, value)) {
102
    paths.push_back(value + suffix);
103
  }
104
}
105
#endif
106
107
static cmsys::RegularExpression TransformCompile(std::string const& str)
108
0
{
109
0
  return cmsys::RegularExpression(str);
110
0
}
111
112
cmRuntimeDependencyArchive::cmRuntimeDependencyArchive(
113
  cmExecutionStatus& status, std::vector<std::string> searchDirectories,
114
  std::string bundleExecutable,
115
  std::vector<std::string> const& preIncludeRegexes,
116
  std::vector<std::string> const& preExcludeRegexes,
117
  std::vector<std::string> const& postIncludeRegexes,
118
  std::vector<std::string> const& postExcludeRegexes,
119
  std::vector<std::string> postIncludeFiles,
120
  std::vector<std::string> postExcludeFiles,
121
  std::vector<std::string> postExcludeFilesStrict)
122
0
  : Status(status)
123
0
  , SearchDirectories(std::move(searchDirectories))
124
0
  , BundleExecutable(std::move(bundleExecutable))
125
0
  , PreIncludeRegexes(preIncludeRegexes.size())
126
0
  , PreExcludeRegexes(preExcludeRegexes.size())
127
0
  , PostIncludeRegexes(postIncludeRegexes.size())
128
0
  , PostExcludeRegexes(postExcludeRegexes.size())
129
0
  , PostIncludeFiles(std::move(postIncludeFiles))
130
0
  , PostExcludeFiles(std::move(postExcludeFiles))
131
0
  , PostExcludeFilesStrict(std::move(postExcludeFilesStrict))
132
0
{
133
0
  std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(),
134
0
                 this->PreIncludeRegexes.begin(), TransformCompile);
135
0
  std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(),
136
0
                 this->PreExcludeRegexes.begin(), TransformCompile);
137
0
  std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(),
138
0
                 this->PostIncludeRegexes.begin(), TransformCompile);
139
0
  std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(),
140
0
                 this->PostExcludeRegexes.begin(), TransformCompile);
141
0
}
142
143
bool cmRuntimeDependencyArchive::Prepare()
144
0
{
145
0
  std::string platform = this->GetMakefile()->GetSafeDefinition(
146
0
    "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM");
147
0
  if (platform.empty()) {
148
0
    std::string systemName =
149
0
      this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
150
0
    if (systemName == "Windows") {
151
0
      platform = "windows+pe";
152
0
    } else if (systemName == "Darwin") {
153
0
      platform = "macos+macho";
154
0
    } else if (systemName == "Linux") {
155
0
      platform = "linux+elf";
156
0
    }
157
0
  }
158
0
  if (platform == "linux+elf") {
159
0
    this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
160
0
  } else if (platform == "windows+pe") {
161
0
    this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
162
0
  } else if (platform == "macos+macho") {
163
0
    this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
164
0
  } else {
165
0
    std::ostringstream e;
166
0
    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: "
167
0
      << platform;
168
0
    this->SetError(e.str());
169
0
    return false;
170
0
  }
171
172
0
  return this->Linker->Prepare();
173
0
}
174
175
bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
176
  std::vector<std::string> const& executables,
177
  std::vector<std::string> const& libraries,
178
  std::vector<std::string> const& modules)
179
0
{
180
0
  for (auto const& exe : executables) {
181
0
    if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) {
182
0
      return false;
183
0
    }
184
0
  }
185
0
  for (auto const& lib : libraries) {
186
0
    if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) {
187
0
      return false;
188
0
    }
189
0
  }
190
0
  return std::all_of(
191
0
    modules.begin(), modules.end(), [this](std::string const& mod) -> bool {
192
0
      return this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY);
193
0
    });
194
0
}
195
196
void cmRuntimeDependencyArchive::SetError(std::string const& e)
197
0
{
198
0
  this->Status.SetError(e);
199
0
}
200
201
std::string const& cmRuntimeDependencyArchive::GetBundleExecutable() const
202
0
{
203
0
  return this->BundleExecutable;
204
0
}
205
206
std::vector<std::string> const&
207
cmRuntimeDependencyArchive::GetSearchDirectories() const
208
0
{
209
0
  return this->SearchDirectories;
210
0
}
211
212
std::string const& cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
213
  const
214
0
{
215
0
  return this->GetMakefile()->GetSafeDefinition(
216
0
    "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
217
0
}
218
219
bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
220
  std::string const& search, std::vector<std::string>& command) const
221
0
{
222
  // First see if it was supplied by the user
223
0
  std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
224
0
    "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
225
0
  if (toolCommand.empty() && search == "objdump") {
226
0
    toolCommand = this->GetMakefile()->GetSafeDefinition("CMAKE_OBJDUMP");
227
0
  }
228
0
  if (!toolCommand.empty()) {
229
0
    cmExpandList(toolCommand, command);
230
0
    return true;
231
0
  }
232
233
  // Now go searching for it
234
0
  std::vector<std::string> paths;
235
#ifdef _WIN32
236
  cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator();
237
238
  // Add newer Visual Studio paths
239
  AddVisualStudioPath(paths, "Visual Studio 18 ", 18, gg);
240
  AddVisualStudioPath(paths, "Visual Studio 17 ", 17, gg);
241
  AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
242
  AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
243
244
  // Add older Visual Studio paths
245
  AddRegistryPath(
246
    paths,
247
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
248
    "../../VC/bin",
249
    this->GetMakefile());
250
  AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
251
  paths.push_back(
252
    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
253
  AddRegistryPath(
254
    paths,
255
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
256
    "../../VC/bin",
257
    this->GetMakefile());
258
  AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
259
  paths.push_back(
260
    "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
261
  AddRegistryPath(
262
    paths,
263
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
264
    "../../VC/bin",
265
    this->GetMakefile());
266
  AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
267
  paths.push_back(
268
    "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
269
  AddRegistryPath(
270
    paths,
271
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
272
    "../../VC/bin",
273
    this->GetMakefile());
274
  AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
275
  paths.push_back(
276
    "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
277
  AddRegistryPath(
278
    paths,
279
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
280
    "../../VC/bin",
281
    this->GetMakefile());
282
  AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
283
  paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
284
  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
285
  AddRegistryPath(
286
    paths,
287
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
288
    "../../VC/bin",
289
    this->GetMakefile());
290
  AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
291
  paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
292
  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
293
  AddRegistryPath(
294
    paths,
295
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
296
    "../../VC7/bin",
297
    this->GetMakefile());
298
  AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
299
  paths.push_back(
300
    "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
301
  paths.push_back(
302
    "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
303
#endif
304
305
0
  std::string program = cmSystemTools::FindProgram(search, paths);
306
0
  if (!program.empty()) {
307
0
    command = { program };
308
0
    return true;
309
0
  }
310
311
  // Couldn't find it
312
0
  return false;
313
0
}
314
315
bool cmRuntimeDependencyArchive::IsPreExcluded(std::string const& name) const
316
0
{
317
0
  cmsys::RegularExpressionMatch match;
318
0
  auto const regexMatch =
319
0
    [&match, name](cmsys::RegularExpression const& regex) -> bool {
320
0
    return regex.find(name.c_str(), match);
321
0
  };
322
0
  auto const regexSearch =
323
0
    [&regexMatch](
324
0
      std::vector<cmsys::RegularExpression> const& regexes) -> bool {
325
0
    return std::any_of(regexes.begin(), regexes.end(), regexMatch);
326
0
  };
327
328
0
  return !regexSearch(this->PreIncludeRegexes) &&
329
0
    regexSearch(this->PreExcludeRegexes);
330
0
}
331
332
bool cmRuntimeDependencyArchive::IsPostExcluded(std::string const& name) const
333
0
{
334
0
  cmsys::RegularExpressionMatch match;
335
0
  auto const regexMatch =
336
0
    [&match, name](cmsys::RegularExpression const& regex) -> bool {
337
0
    return regex.find(name.c_str(), match);
338
0
  };
339
0
  auto const regexSearch =
340
0
    [&regexMatch](
341
0
      std::vector<cmsys::RegularExpression> const& regexes) -> bool {
342
0
    return std::any_of(regexes.begin(), regexes.end(), regexMatch);
343
0
  };
344
0
  auto const fileMatch = [name](std::string const& file) -> bool {
345
0
    return cmSystemTools::SameFile(file, name);
346
0
  };
347
0
  auto const fileSearch =
348
0
    [&fileMatch](std::vector<std::string> const& files) -> bool {
349
0
    return std::any_of(files.begin(), files.end(), fileMatch);
350
0
  };
351
352
0
  return fileSearch(this->PostExcludeFilesStrict) ||
353
0
    (!(regexSearch(this->PostIncludeRegexes) ||
354
0
       fileSearch(this->PostIncludeFiles)) &&
355
0
     (regexSearch(this->PostExcludeRegexes) ||
356
0
      fileSearch(this->PostExcludeFiles)));
357
0
}
358
359
void cmRuntimeDependencyArchive::AddResolvedPath(
360
  std::string const& name, std::string const& path, bool& unique,
361
  std::vector<std::string> rpaths)
362
0
{
363
0
  auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first;
364
0
  unique = true;
365
0
  for (auto const& other : it->second) {
366
0
    if (cmSystemTools::SameFile(path, other)) {
367
0
      unique = false;
368
0
      break;
369
0
    }
370
0
  }
371
0
  it->second.emplace(path);
372
0
  this->RPaths[path] = std::move(rpaths);
373
0
}
374
375
void cmRuntimeDependencyArchive::AddUnresolvedPath(std::string const& name)
376
0
{
377
0
  this->UnresolvedPaths.insert(name);
378
0
}
379
380
cmMakefile* cmRuntimeDependencyArchive::GetMakefile() const
381
0
{
382
0
  return &this->Status.GetMakefile();
383
0
}
384
385
std::map<std::string, std::set<std::string>> const&
386
cmRuntimeDependencyArchive::GetResolvedPaths() const
387
0
{
388
0
  return this->ResolvedPaths;
389
0
}
390
391
std::set<std::string> const& cmRuntimeDependencyArchive::GetUnresolvedPaths()
392
  const
393
0
{
394
0
  return this->UnresolvedPaths;
395
0
}
396
397
std::map<std::string, std::vector<std::string>> const&
398
cmRuntimeDependencyArchive::GetRPaths() const
399
0
{
400
0
  return this->RPaths;
401
0
}
402
403
bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
404
  std::string const& platform)
405
0
{
406
0
  static std::set<std::string> const supportedPlatforms = { "Windows", "Linux",
407
0
                                                            "Darwin" };
408
0
  return supportedPlatforms.count(platform);
409
0
}