Imported Upstream version 3.22.5
[platform/upstream/cmake.git] / Source / cmRuntimeDependencyArchive.cxx
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)
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
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
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
107 static cmsys::RegularExpression TransformCompile(const std::string& str)
108 {
109   return cmsys::RegularExpression(str);
110 }
111
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
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
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
196 void cmRuntimeDependencyArchive::SetError(const std::string& e)
197 {
198   this->Status.SetError(e);
199 }
200
201 const std::string& cmRuntimeDependencyArchive::GetBundleExecutable() const
202 {
203   return this->BundleExecutable;
204 }
205
206 const std::vector<std::string>&
207 cmRuntimeDependencyArchive::GetSearchDirectories() const
208 {
209   return this->SearchDirectories;
210 }
211
212 const std::string& cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
213   const
214 {
215   return this->GetMakefile()->GetSafeDefinition(
216     "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
217 }
218
219 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 17 ", 17, gg);
240   AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
241   AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
242
243   // Add older Visual Studio paths
244   AddRegistryPath(
245     paths,
246     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
247     "../../VC/bin",
248     this->GetMakefile());
249   AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
250   paths.push_back(
251     "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
252   AddRegistryPath(
253     paths,
254     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
255     "../../VC/bin",
256     this->GetMakefile());
257   AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
258   paths.push_back(
259     "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
260   AddRegistryPath(
261     paths,
262     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
263     "../../VC/bin",
264     this->GetMakefile());
265   AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
266   paths.push_back(
267     "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
268   AddRegistryPath(
269     paths,
270     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
271     "../../VC/bin",
272     this->GetMakefile());
273   AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
274   paths.push_back(
275     "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
276   AddRegistryPath(
277     paths,
278     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
279     "../../VC/bin",
280     this->GetMakefile());
281   AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
282   paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
283   paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
284   AddRegistryPath(
285     paths,
286     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
287     "../../VC/bin",
288     this->GetMakefile());
289   AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
290   paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
291   paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
292   AddRegistryPath(
293     paths,
294     "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
295     "../../VC7/bin",
296     this->GetMakefile());
297   AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
298   paths.push_back(
299     "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
300   paths.push_back(
301     "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
302 #endif
303
304   std::string program = cmSystemTools::FindProgram(search, paths);
305   if (!program.empty()) {
306     command = { program };
307     return true;
308   }
309
310   // Couldn't find it
311   return false;
312 }
313
314 bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) const
315 {
316   cmsys::RegularExpressionMatch match;
317   auto const regexMatch =
318     [&match, name](const cmsys::RegularExpression& regex) -> bool {
319     return regex.find(name.c_str(), match);
320   };
321   auto const regexSearch =
322     [&regexMatch](
323       const std::vector<cmsys::RegularExpression>& regexes) -> bool {
324     return std::any_of(regexes.begin(), regexes.end(), regexMatch);
325   };
326
327   return !regexSearch(this->PreIncludeRegexes) &&
328     regexSearch(this->PreExcludeRegexes);
329 }
330
331 bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) const
332 {
333   cmsys::RegularExpressionMatch match;
334   auto const regexMatch =
335     [&match, name](const cmsys::RegularExpression& regex) -> bool {
336     return regex.find(name.c_str(), match);
337   };
338   auto const regexSearch =
339     [&regexMatch](
340       const std::vector<cmsys::RegularExpression>& regexes) -> bool {
341     return std::any_of(regexes.begin(), regexes.end(), regexMatch);
342   };
343   auto const fileMatch = [name](const std::string& file) -> bool {
344     return cmSystemTools::SameFile(file, name);
345   };
346   auto const fileSearch =
347     [&fileMatch](const std::vector<std::string>& files) -> bool {
348     return std::any_of(files.begin(), files.end(), fileMatch);
349   };
350
351   return fileSearch(this->PostExcludeFilesStrict) ||
352     (!(regexSearch(this->PostIncludeRegexes) ||
353        fileSearch(this->PostIncludeFiles)) &&
354      (regexSearch(this->PostExcludeRegexes) ||
355       fileSearch(this->PostExcludeFiles)));
356 }
357
358 void cmRuntimeDependencyArchive::AddResolvedPath(
359   const std::string& name, const std::string& path, bool& unique,
360   std::vector<std::string> rpaths)
361 {
362   auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first;
363   unique = true;
364   for (auto const& other : it->second) {
365     if (cmSystemTools::SameFile(path, other)) {
366       unique = false;
367       break;
368     }
369   }
370   it->second.insert(path);
371   this->RPaths[path] = std::move(rpaths);
372 }
373
374 void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name)
375 {
376   this->UnresolvedPaths.insert(name);
377 }
378
379 cmMakefile* cmRuntimeDependencyArchive::GetMakefile() const
380 {
381   return &this->Status.GetMakefile();
382 }
383
384 const std::map<std::string, std::set<std::string>>&
385 cmRuntimeDependencyArchive::GetResolvedPaths() const
386 {
387   return this->ResolvedPaths;
388 }
389
390 const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths()
391   const
392 {
393   return this->UnresolvedPaths;
394 }
395
396 const std::map<std::string, std::vector<std::string>>&
397 cmRuntimeDependencyArchive::GetRPaths() const
398 {
399   return this->RPaths;
400 }
401
402 bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
403   const std::string& platform)
404 {
405   static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
406                                                             "Darwin" };
407   return supportedPlatforms.count(platform);
408 }