Coverage Report

Created: 2026-07-14 07:09

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