Coverage Report

Created: 2026-10-28 06:38

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 <string>
8
#include <utility>
9
#include <vector>
10
11
#include <cm/memory>
12
13
#include "cmBinUtilsLinuxELFLinker.h"
14
#include "cmBinUtilsMacOSMachOLinker.h"
15
#include "cmBinUtilsWindowsPELinker.h"
16
#include "cmExecutionStatus.h"
17
#include "cmList.h"
18
#include "cmMakefile.h"
19
#include "cmStringAlgorithms.h"
20
#include "cmSystemTools.h"
21
#include "cmTargetTypes.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
    } else if (systemName == "FreeBSD") {
157
0
      platform = "freebsd+elf";
158
0
    }
159
0
  }
160
0
  if (platform == "linux+elf" || platform == "freebsd+elf") {
161
0
    this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
162
0
  } else if (platform == "windows+pe") {
163
0
    this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
164
0
  } else if (platform == "macos+macho") {
165
0
    this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
166
0
  } else {
167
0
    this->SetError(
168
0
      cmStrCat("Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: ",
169
0
               platform));
170
0
    return false;
171
0
  }
172
173
0
  return this->Linker->Prepare();
174
0
}
175
176
bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
177
  std::vector<std::string> const& executables,
178
  std::vector<std::string> const& libraries,
179
  std::vector<std::string> const& modules)
180
0
{
181
0
  for (auto const& exe : executables) {
182
0
    if (!this->Linker->ScanDependencies(exe, cm::TargetType::EXECUTABLE)) {
183
0
      return false;
184
0
    }
185
0
  }
186
0
  for (auto const& lib : libraries) {
187
0
    if (!this->Linker->ScanDependencies(lib, cm::TargetType::SHARED_LIBRARY)) {
188
0
      return false;
189
0
    }
190
0
  }
191
0
  return std::all_of(modules.begin(), modules.end(),
192
0
                     [this](std::string const& mod) -> bool {
193
0
                       return this->Linker->ScanDependencies(
194
0
                         mod, cm::TargetType::MODULE_LIBRARY);
195
0
                     });
196
0
}
197
198
void cmRuntimeDependencyArchive::SetError(std::string const& e)
199
0
{
200
0
  this->Status.SetError(e);
201
0
}
202
203
std::string const& cmRuntimeDependencyArchive::GetBundleExecutable() const
204
0
{
205
0
  return this->BundleExecutable;
206
0
}
207
208
std::vector<std::string> const&
209
cmRuntimeDependencyArchive::GetSearchDirectories() const
210
0
{
211
0
  return this->SearchDirectories;
212
0
}
213
214
std::string const& cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
215
  const
216
0
{
217
0
  return this->GetMakefile()->GetSafeDefinition(
218
0
    "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
219
0
}
220
221
bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
222
  std::string const& search, std::vector<std::string>& command) const
223
0
{
224
  // First see if it was supplied by the user
225
0
  std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
226
0
    "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
227
0
  if (toolCommand.empty() && search == "objdump") {
228
0
    toolCommand = this->GetMakefile()->GetSafeDefinition("CMAKE_OBJDUMP");
229
0
  }
230
0
  if (!toolCommand.empty()) {
231
0
    cmExpandList(toolCommand, command);
232
0
    return true;
233
0
  }
234
235
  // Now go searching for it
236
0
  std::vector<std::string> paths;
237
#ifdef _WIN32
238
  cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator();
239
240
  // Add newer Visual Studio paths
241
  AddVisualStudioPath(paths, "Visual Studio 18 ", 18, gg);
242
  AddVisualStudioPath(paths, "Visual Studio 17 ", 17, gg);
243
  AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
244
  AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
245
246
  // Add older Visual Studio paths
247
  AddRegistryPath(
248
    paths,
249
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
250
    "../../VC/bin",
251
    this->GetMakefile());
252
  AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
253
  paths.push_back(
254
    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
255
  AddRegistryPath(
256
    paths,
257
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
258
    "../../VC/bin",
259
    this->GetMakefile());
260
  AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
261
  paths.push_back(
262
    "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
263
  AddRegistryPath(
264
    paths,
265
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
266
    "../../VC/bin",
267
    this->GetMakefile());
268
  AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
269
  paths.push_back(
270
    "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
271
  AddRegistryPath(
272
    paths,
273
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
274
    "../../VC/bin",
275
    this->GetMakefile());
276
  AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
277
  paths.push_back(
278
    "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
279
  AddRegistryPath(
280
    paths,
281
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
282
    "../../VC/bin",
283
    this->GetMakefile());
284
  AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
285
  paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
286
  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
287
  AddRegistryPath(
288
    paths,
289
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
290
    "../../VC/bin",
291
    this->GetMakefile());
292
  AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
293
  paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
294
  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
295
  AddRegistryPath(
296
    paths,
297
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
298
    "../../VC7/bin",
299
    this->GetMakefile());
300
  AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
301
  paths.push_back(
302
    "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
303
  paths.push_back(
304
    "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
305
#endif
306
307
0
  std::string program = cmSystemTools::FindProgram(search, paths);
308
0
  if (!program.empty()) {
309
0
    command = { program };
310
0
    return true;
311
0
  }
312
313
  // Couldn't find it
314
0
  return false;
315
0
}
316
317
bool cmRuntimeDependencyArchive::IsPreExcluded(std::string const& name) const
318
0
{
319
0
  cmsys::RegularExpressionMatch match;
320
0
  auto const regexMatch =
321
0
    [&match, name](cmsys::RegularExpression const& regex) -> bool {
322
0
    return regex.find(name.c_str(), match);
323
0
  };
324
0
  auto const regexSearch =
325
0
    [&regexMatch](
326
0
      std::vector<cmsys::RegularExpression> const& regexes) -> bool {
327
0
    return std::any_of(regexes.begin(), regexes.end(), regexMatch);
328
0
  };
329
330
0
  return !regexSearch(this->PreIncludeRegexes) &&
331
0
    regexSearch(this->PreExcludeRegexes);
332
0
}
333
334
bool cmRuntimeDependencyArchive::IsPostExcluded(std::string const& name) const
335
0
{
336
0
  return this->IsPostExcluded(name, name);
337
0
}
338
339
bool cmRuntimeDependencyArchive::IsPostExcluded(
340
  std::string const& name, std::string const& fileName) const
341
0
{
342
0
  cmsys::RegularExpressionMatch match;
343
0
  auto const regexMatch =
344
0
    [&match, name](cmsys::RegularExpression const& regex) -> bool {
345
0
    return regex.find(name.c_str(), match);
346
0
  };
347
0
  auto const regexSearch =
348
0
    [&regexMatch](
349
0
      std::vector<cmsys::RegularExpression> const& regexes) -> bool {
350
0
    return std::any_of(regexes.begin(), regexes.end(), regexMatch);
351
0
  };
352
0
  auto const fileMatch = [fileName](std::string const& file) -> bool {
353
0
    return cmSystemTools::SameFile(file, fileName);
354
0
  };
355
0
  auto const fileSearch =
356
0
    [&fileMatch](std::vector<std::string> const& files) -> bool {
357
0
    return std::any_of(files.begin(), files.end(), fileMatch);
358
0
  };
359
360
0
  return fileSearch(this->PostExcludeFilesStrict) ||
361
0
    (!(regexSearch(this->PostIncludeRegexes) ||
362
0
       fileSearch(this->PostIncludeFiles)) &&
363
0
     (regexSearch(this->PostExcludeRegexes) ||
364
0
      fileSearch(this->PostExcludeFiles)));
365
0
}
366
367
void cmRuntimeDependencyArchive::AddResolvedPath(
368
  std::string const& name, std::string const& path, bool& unique,
369
  std::vector<std::string> rpaths)
370
0
{
371
0
  auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first;
372
0
  unique = true;
373
0
  for (auto const& other : it->second) {
374
0
    if (cmSystemTools::SameFile(path, other)) {
375
0
      unique = false;
376
0
      break;
377
0
    }
378
0
  }
379
0
  it->second.emplace(path);
380
0
  this->RPaths[path] = std::move(rpaths);
381
0
}
382
383
void cmRuntimeDependencyArchive::AddUnresolvedPath(std::string const& name)
384
0
{
385
0
  this->UnresolvedPaths.insert(name);
386
0
}
387
388
cmMakefile* cmRuntimeDependencyArchive::GetMakefile() const
389
0
{
390
0
  return &this->Status.GetMakefile();
391
0
}
392
393
std::map<std::string, std::set<std::string>> const&
394
cmRuntimeDependencyArchive::GetResolvedPaths() const
395
0
{
396
0
  return this->ResolvedPaths;
397
0
}
398
399
std::set<std::string> const& cmRuntimeDependencyArchive::GetUnresolvedPaths()
400
  const
401
0
{
402
0
  return this->UnresolvedPaths;
403
0
}
404
405
std::map<std::string, std::vector<std::string>> const&
406
cmRuntimeDependencyArchive::GetRPaths() const
407
0
{
408
0
  return this->RPaths;
409
0
}
410
411
bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
412
  std::string const& platform)
413
0
{
414
0
  static std::set<std::string> const supportedPlatforms = {
415
0
    "Windows",
416
0
    "Linux",
417
0
    "Darwin",
418
0
    "FreeBSD",
419
0
  };
420
0
  return supportedPlatforms.count(platform);
421
0
}