1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3
4 #include "cmRuntimeDependencyArchive.h"
5
6 #include "cmBinUtilsLinuxELFLinker.h"
7 #include "cmBinUtilsMacOSMachOLinker.h"
8 #include "cmBinUtilsWindowsPELinker.h"
9 #include "cmExecutionStatus.h"
10 #include "cmMakefile.h"
11 #include "cmStateTypes.h"
12 #include "cmStringAlgorithms.h"
13 #include "cmSystemTools.h"
14
15 #if defined(_WIN32)
16 # include "cmGlobalGenerator.h"
17 # ifndef CMAKE_BOOTSTRAP
18 # include "cmGlobalVisualStudioVersionedGenerator.h"
19 # endif
20 # include "cmsys/Glob.hxx"
21
22 # include "cmVSSetupHelper.h"
23 #endif
24
25 #include <algorithm>
26 #include <sstream>
27 #include <string>
28 #include <utility>
29 #include <vector>
30
31 #include <cm/memory>
32
33 #if defined(_WIN32)
AddVisualStudioPath(std::vector<std::string> & paths,const std::string & prefix,unsigned int version,cmGlobalGenerator * gg)34 static void AddVisualStudioPath(std::vector<std::string>& paths,
35 const std::string& 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
AddRegistryPath(std::vector<std::string> & paths,const std::string & path,cmMakefile * mf)73 static void AddRegistryPath(std::vector<std::string>& paths,
74 const std::string& 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
AddEnvPath(std::vector<std::string> & paths,const std::string & var,const std::string & suffix)97 static void AddEnvPath(std::vector<std::string>& paths, const std::string& var,
98 const std::string& suffix)
99 {
100 std::string value;
101 if (cmSystemTools::GetEnv(var, value)) {
102 paths.push_back(value + suffix);
103 }
104 }
105 #endif
106
TransformCompile(const std::string & str)107 static cmsys::RegularExpression TransformCompile(const std::string& str)
108 {
109 return cmsys::RegularExpression(str);
110 }
111
cmRuntimeDependencyArchive(cmExecutionStatus & status,std::vector<std::string> searchDirectories,std::string bundleExecutable,const std::vector<std::string> & preIncludeRegexes,const std::vector<std::string> & preExcludeRegexes,const std::vector<std::string> & postIncludeRegexes,const std::vector<std::string> & postExcludeRegexes,std::vector<std::string> postIncludeFiles,std::vector<std::string> postExcludeFiles,std::vector<std::string> postExcludeFilesStrict)112 cmRuntimeDependencyArchive::cmRuntimeDependencyArchive(
113 cmExecutionStatus& status, std::vector<std::string> searchDirectories,
114 std::string bundleExecutable,
115 const std::vector<std::string>& preIncludeRegexes,
116 const std::vector<std::string>& preExcludeRegexes,
117 const std::vector<std::string>& postIncludeRegexes,
118 const std::vector<std::string>& postExcludeRegexes,
119 std::vector<std::string> postIncludeFiles,
120 std::vector<std::string> postExcludeFiles,
121 std::vector<std::string> postExcludeFilesStrict)
122 : Status(status)
123 , SearchDirectories(std::move(searchDirectories))
124 , BundleExecutable(std::move(bundleExecutable))
125 , PreIncludeRegexes(preIncludeRegexes.size())
126 , PreExcludeRegexes(preExcludeRegexes.size())
127 , PostIncludeRegexes(postIncludeRegexes.size())
128 , PostExcludeRegexes(postExcludeRegexes.size())
129 , PostIncludeFiles(std::move(postIncludeFiles))
130 , PostExcludeFiles(std::move(postExcludeFiles))
131 , PostExcludeFilesStrict(std::move(postExcludeFilesStrict))
132 {
133 std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(),
134 this->PreIncludeRegexes.begin(), TransformCompile);
135 std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(),
136 this->PreExcludeRegexes.begin(), TransformCompile);
137 std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(),
138 this->PostIncludeRegexes.begin(), TransformCompile);
139 std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(),
140 this->PostExcludeRegexes.begin(), TransformCompile);
141 }
142
Prepare()143 bool cmRuntimeDependencyArchive::Prepare()
144 {
145 std::string platform = this->GetMakefile()->GetSafeDefinition(
146 "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM");
147 if (platform.empty()) {
148 std::string systemName =
149 this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
150 if (systemName == "Windows") {
151 platform = "windows+pe";
152 } else if (systemName == "Darwin") {
153 platform = "macos+macho";
154 } else if (systemName == "Linux") {
155 platform = "linux+elf";
156 }
157 }
158 if (platform == "linux+elf") {
159 this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
160 } else if (platform == "windows+pe") {
161 this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
162 } else if (platform == "macos+macho") {
163 this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
164 } else {
165 std::ostringstream e;
166 e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: "
167 << platform;
168 this->SetError(e.str());
169 return false;
170 }
171
172 return this->Linker->Prepare();
173 }
174
GetRuntimeDependencies(const std::vector<std::string> & executables,const std::vector<std::string> & libraries,const std::vector<std::string> & modules)175 bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
176 const std::vector<std::string>& executables,
177 const std::vector<std::string>& libraries,
178 const std::vector<std::string>& modules)
179 {
180 for (auto const& exe : executables) {
181 if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) {
182 return false;
183 }
184 }
185 for (auto const& lib : libraries) {
186 if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) {
187 return false;
188 }
189 }
190 return std::all_of(
191 modules.begin(), modules.end(), [this](std::string const& mod) -> bool {
192 return this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY);
193 });
194 }
195
SetError(const std::string & e)196 void cmRuntimeDependencyArchive::SetError(const std::string& e)
197 {
198 this->Status.SetError(e);
199 }
200
GetBundleExecutable() const201 const std::string& cmRuntimeDependencyArchive::GetBundleExecutable() const
202 {
203 return this->BundleExecutable;
204 }
205
206 const std::vector<std::string>&
GetSearchDirectories() const207 cmRuntimeDependencyArchive::GetSearchDirectories() const
208 {
209 return this->SearchDirectories;
210 }
211
GetGetRuntimeDependenciesTool() const212 const std::string& cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
213 const
214 {
215 return this->GetMakefile()->GetSafeDefinition(
216 "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
217 }
218
GetGetRuntimeDependenciesCommand(const std::string & search,std::vector<std::string> & command) const219 bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
220 const std::string& search, std::vector<std::string>& command) const
221 {
222 // First see if it was supplied by the user
223 std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
224 "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
225 if (toolCommand.empty() && search == "objdump") {
226 toolCommand = this->GetMakefile()->GetSafeDefinition("CMAKE_OBJDUMP");
227 }
228 if (!toolCommand.empty()) {
229 cmExpandList(toolCommand, command);
230 return true;
231 }
232
233 // Now go searching for it
234 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 16 ", 16, gg);
240 AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
241
242 // Add older Visual Studio paths
243 AddRegistryPath(
244 paths,
245 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
246 "../../VC/bin",
247 this->GetMakefile());
248 AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
249 paths.push_back(
250 "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
251 AddRegistryPath(
252 paths,
253 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
254 "../../VC/bin",
255 this->GetMakefile());
256 AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
257 paths.push_back(
258 "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
259 AddRegistryPath(
260 paths,
261 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
262 "../../VC/bin",
263 this->GetMakefile());
264 AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
265 paths.push_back(
266 "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
267 AddRegistryPath(
268 paths,
269 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
270 "../../VC/bin",
271 this->GetMakefile());
272 AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
273 paths.push_back(
274 "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
275 AddRegistryPath(
276 paths,
277 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
278 "../../VC/bin",
279 this->GetMakefile());
280 AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
281 paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
282 paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
283 AddRegistryPath(
284 paths,
285 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
286 "../../VC/bin",
287 this->GetMakefile());
288 AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
289 paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
290 paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
291 AddRegistryPath(
292 paths,
293 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
294 "../../VC7/bin",
295 this->GetMakefile());
296 AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
297 paths.push_back(
298 "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
299 paths.push_back(
300 "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
301 #endif
302
303 std::string program = cmSystemTools::FindProgram(search, paths);
304 if (!program.empty()) {
305 command = { program };
306 return true;
307 }
308
309 // Couldn't find it
310 return false;
311 }
312
IsPreExcluded(const std::string & name) const313 bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) const
314 {
315 cmsys::RegularExpressionMatch match;
316 auto const regexMatch =
317 [&match, name](const cmsys::RegularExpression& regex) -> bool {
318 return regex.find(name.c_str(), match);
319 };
320 auto const regexSearch =
321 [®exMatch](
322 const std::vector<cmsys::RegularExpression>& regexes) -> bool {
323 return std::any_of(regexes.begin(), regexes.end(), regexMatch);
324 };
325
326 return !regexSearch(this->PreIncludeRegexes) &&
327 regexSearch(this->PreExcludeRegexes);
328 }
329
IsPostExcluded(const std::string & name) const330 bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) const
331 {
332 cmsys::RegularExpressionMatch match;
333 auto const regexMatch =
334 [&match, name](const cmsys::RegularExpression& regex) -> bool {
335 return regex.find(name.c_str(), match);
336 };
337 auto const regexSearch =
338 [®exMatch](
339 const std::vector<cmsys::RegularExpression>& regexes) -> bool {
340 return std::any_of(regexes.begin(), regexes.end(), regexMatch);
341 };
342 auto const fileMatch = [name](const std::string& file) -> bool {
343 return cmSystemTools::SameFile(file, name);
344 };
345 auto const fileSearch =
346 [&fileMatch](const std::vector<std::string>& files) -> bool {
347 return std::any_of(files.begin(), files.end(), fileMatch);
348 };
349
350 return fileSearch(this->PostExcludeFilesStrict) ||
351 (!(regexSearch(this->PostIncludeRegexes) ||
352 fileSearch(this->PostIncludeFiles)) &&
353 (regexSearch(this->PostExcludeRegexes) ||
354 fileSearch(this->PostExcludeFiles)));
355 }
356
AddResolvedPath(const std::string & name,const std::string & path,bool & unique,std::vector<std::string> rpaths)357 void cmRuntimeDependencyArchive::AddResolvedPath(
358 const std::string& name, const std::string& path, bool& unique,
359 std::vector<std::string> rpaths)
360 {
361 auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first;
362 unique = true;
363 for (auto const& other : it->second) {
364 if (cmSystemTools::SameFile(path, other)) {
365 unique = false;
366 break;
367 }
368 }
369 it->second.insert(path);
370 this->RPaths[path] = std::move(rpaths);
371 }
372
AddUnresolvedPath(const std::string & name)373 void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name)
374 {
375 this->UnresolvedPaths.insert(name);
376 }
377
GetMakefile() const378 cmMakefile* cmRuntimeDependencyArchive::GetMakefile() const
379 {
380 return &this->Status.GetMakefile();
381 }
382
383 const std::map<std::string, std::set<std::string>>&
GetResolvedPaths() const384 cmRuntimeDependencyArchive::GetResolvedPaths() const
385 {
386 return this->ResolvedPaths;
387 }
388
GetUnresolvedPaths() const389 const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths()
390 const
391 {
392 return this->UnresolvedPaths;
393 }
394
395 const std::map<std::string, std::vector<std::string>>&
GetRPaths() const396 cmRuntimeDependencyArchive::GetRPaths() const
397 {
398 return this->RPaths;
399 }
400
PlatformSupportsRuntimeDependencies(const std::string & platform)401 bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
402 const std::string& platform)
403 {
404 static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
405 "Darwin" };
406 return supportedPlatforms.count(platform);
407 }
408