1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmInstallGetRuntimeDependenciesGenerator.h"
4 
5 #include <memory>
6 #include <ostream>
7 #include <set>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 #include <cm/optional>
13 #include <cm/string_view>
14 #include <cmext/string_view>
15 
16 #include "cmGeneratorExpression.h"
17 #include "cmInstallRuntimeDependencySet.h"
18 #include "cmLocalGenerator.h"
19 #include "cmMakefile.h"
20 #include "cmOutputConverter.h"
21 #include "cmStringAlgorithms.h"
22 
23 namespace {
24 template <typename T, typename F>
WriteMultiArgument(std::ostream & os,const cm::string_view & keyword,const std::vector<T> & list,cmScriptGeneratorIndent indent,F transform)25 void WriteMultiArgument(std::ostream& os, const cm::string_view& keyword,
26                         const std::vector<T>& list,
27                         cmScriptGeneratorIndent indent, F transform)
28 {
29   bool first = true;
30   for (auto const& item : list) {
31     cm::optional<std::string> result = transform(item);
32     if (result) {
33       if (first) {
34         os << indent << "  " << keyword << "\n";
35         first = false;
36       }
37       os << indent << "    " << *result << "\n";
38     }
39   }
40 }
41 
WriteFilesArgument(std::ostream & os,const cm::string_view & keyword,const std::vector<std::unique_ptr<cmInstallRuntimeDependencySet::Item>> & items,const std::string & config,cmScriptGeneratorIndent indent)42 void WriteFilesArgument(
43   std::ostream& os, const cm::string_view& keyword,
44   const std::vector<std::unique_ptr<cmInstallRuntimeDependencySet::Item>>&
45     items,
46   const std::string& config, cmScriptGeneratorIndent indent)
47 {
48   WriteMultiArgument(
49     os, keyword, items, indent,
50     [config](const std::unique_ptr<cmInstallRuntimeDependencySet::Item>& i)
51       -> std::string { return cmStrCat('"', i->GetItemPath(config), '"'); });
52 }
53 
WriteGenexEvaluatorArgument(std::ostream & os,const cm::string_view & keyword,const std::vector<std::string> & genexes,const std::string & config,cmLocalGenerator * lg,cmScriptGeneratorIndent indent)54 void WriteGenexEvaluatorArgument(std::ostream& os,
55                                  const cm::string_view& keyword,
56                                  const std::vector<std::string>& genexes,
57                                  const std::string& config,
58                                  cmLocalGenerator* lg,
59                                  cmScriptGeneratorIndent indent)
60 {
61   WriteMultiArgument(
62     os, keyword, genexes, indent,
63     [config, lg](const std::string& genex) -> cm::optional<std::string> {
64       std::string result = cmGeneratorExpression::Evaluate(genex, lg, config);
65       if (result.empty()) {
66         return cm::nullopt;
67       }
68       return cmOutputConverter::EscapeForCMake(result);
69     });
70 }
71 }
72 
73 cmInstallGetRuntimeDependenciesGenerator::
cmInstallGetRuntimeDependenciesGenerator(cmInstallRuntimeDependencySet * runtimeDependencySet,std::vector<std::string> directories,std::vector<std::string> preIncludeRegexes,std::vector<std::string> preExcludeRegexes,std::vector<std::string> postIncludeRegexes,std::vector<std::string> postExcludeRegexes,std::vector<std::string> postIncludeFiles,std::vector<std::string> postExcludeFiles,std::string libraryComponent,std::string frameworkComponent,bool noInstallRPath,const char * depsVar,const char * rpathPrefix,std::vector<std::string> const & configurations,MessageLevel message,bool exclude_from_all,cmListFileBacktrace backtrace)74   cmInstallGetRuntimeDependenciesGenerator(
75     cmInstallRuntimeDependencySet* runtimeDependencySet,
76     std::vector<std::string> directories,
77     std::vector<std::string> preIncludeRegexes,
78     std::vector<std::string> preExcludeRegexes,
79     std::vector<std::string> postIncludeRegexes,
80     std::vector<std::string> postExcludeRegexes,
81     std::vector<std::string> postIncludeFiles,
82     std::vector<std::string> postExcludeFiles, std::string libraryComponent,
83     std::string frameworkComponent, bool noInstallRPath, const char* depsVar,
84     const char* rpathPrefix, std::vector<std::string> const& configurations,
85     MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace)
86   : cmInstallGenerator("", configurations, "", message, exclude_from_all,
87                        false, std::move(backtrace))
88   , RuntimeDependencySet(runtimeDependencySet)
89   , Directories(std::move(directories))
90   , PreIncludeRegexes(std::move(preIncludeRegexes))
91   , PreExcludeRegexes(std::move(preExcludeRegexes))
92   , PostIncludeRegexes(std::move(postIncludeRegexes))
93   , PostExcludeRegexes(std::move(postExcludeRegexes))
94   , PostIncludeFiles(std::move(postIncludeFiles))
95   , PostExcludeFiles(std::move(postExcludeFiles))
96   , LibraryComponent(std::move(libraryComponent))
97   , FrameworkComponent(std::move(frameworkComponent))
98   , NoInstallRPath(noInstallRPath)
99   , DepsVar(depsVar)
100   , RPathPrefix(rpathPrefix)
101 {
102   this->ActionsPerConfig = true;
103 }
104 
Compute(cmLocalGenerator * lg)105 bool cmInstallGetRuntimeDependenciesGenerator::Compute(cmLocalGenerator* lg)
106 {
107   this->LocalGenerator = lg;
108   return true;
109 }
110 
GenerateScript(std::ostream & os)111 void cmInstallGetRuntimeDependenciesGenerator::GenerateScript(std::ostream& os)
112 {
113   // Track indentation.
114   Indent indent;
115 
116   // Begin this block of installation.
117   os << indent << "if(";
118   if (this->FrameworkComponent.empty() ||
119       this->FrameworkComponent == this->LibraryComponent) {
120     os << this->CreateComponentTest(this->LibraryComponent,
121                                     this->ExcludeFromAll);
122   } else {
123     os << this->CreateComponentTest(this->LibraryComponent, true) << " OR "
124        << this->CreateComponentTest(this->FrameworkComponent,
125                                     this->ExcludeFromAll);
126   }
127   os << ")\n";
128 
129   // Generate the script possibly with per-configuration code.
130   this->GenerateScriptConfigs(os, indent.Next());
131 
132   // End this block of installation.
133   os << indent << "endif()\n\n";
134 }
135 
GenerateScriptForConfig(std::ostream & os,const std::string & config,Indent indent)136 void cmInstallGetRuntimeDependenciesGenerator::GenerateScriptForConfig(
137   std::ostream& os, const std::string& config, Indent indent)
138 {
139   std::string installNameTool =
140     this->LocalGenerator->GetMakefile()->GetSafeDefinition(
141       "CMAKE_INSTALL_NAME_TOOL");
142 
143   os << indent << "file(GET_RUNTIME_DEPENDENCIES\n"
144      << indent << "  RESOLVED_DEPENDENCIES_VAR " << this->DepsVar << '\n';
145   WriteFilesArgument(os, "EXECUTABLES"_s,
146                      this->RuntimeDependencySet->GetExecutables(), config,
147                      indent);
148   WriteFilesArgument(os, "LIBRARIES"_s,
149                      this->RuntimeDependencySet->GetLibraries(), config,
150                      indent);
151   WriteFilesArgument(os, "MODULES"_s, this->RuntimeDependencySet->GetModules(),
152                      config, indent);
153   if (this->RuntimeDependencySet->GetBundleExecutable()) {
154     os << indent << "  BUNDLE_EXECUTABLE \""
155        << this->RuntimeDependencySet->GetBundleExecutable()->GetItemPath(
156             config)
157        << "\"\n";
158   }
159   WriteGenexEvaluatorArgument(os, "DIRECTORIES"_s, this->Directories, config,
160                               this->LocalGenerator, indent);
161   WriteGenexEvaluatorArgument(os, "PRE_INCLUDE_REGEXES"_s,
162                               this->PreIncludeRegexes, config,
163                               this->LocalGenerator, indent);
164   WriteGenexEvaluatorArgument(os, "PRE_EXCLUDE_REGEXES"_s,
165                               this->PreExcludeRegexes, config,
166                               this->LocalGenerator, indent);
167   WriteGenexEvaluatorArgument(os, "POST_INCLUDE_REGEXES"_s,
168                               this->PostIncludeRegexes, config,
169                               this->LocalGenerator, indent);
170   WriteGenexEvaluatorArgument(os, "POST_EXCLUDE_REGEXES"_s,
171                               this->PostExcludeRegexes, config,
172                               this->LocalGenerator, indent);
173   WriteGenexEvaluatorArgument(os, "POST_INCLUDE_FILES"_s,
174                               this->PostIncludeFiles, config,
175                               this->LocalGenerator, indent);
176   WriteGenexEvaluatorArgument(os, "POST_EXCLUDE_FILES"_s,
177                               this->PostExcludeFiles, config,
178                               this->LocalGenerator, indent);
179 
180   std::set<std::string> postExcludeFiles;
181   auto const addPostExclude =
182     [config, &postExcludeFiles, this](
183       const std::vector<std::unique_ptr<cmInstallRuntimeDependencySet::Item>>&
184         tgts) {
185       for (auto const& item : tgts) {
186         item->AddPostExcludeFiles(config, postExcludeFiles,
187                                   this->RuntimeDependencySet);
188       }
189     };
190   addPostExclude(this->RuntimeDependencySet->GetExecutables());
191   addPostExclude(this->RuntimeDependencySet->GetLibraries());
192   addPostExclude(this->RuntimeDependencySet->GetModules());
193   bool first = true;
194   for (auto const& file : postExcludeFiles) {
195     if (first) {
196       os << indent << "  POST_EXCLUDE_FILES_STRICT\n";
197       first = false;
198     }
199     os << indent << "    \"" << file << "\"\n";
200   }
201 
202   if (!installNameTool.empty() && !this->NoInstallRPath) {
203     os << indent << "  RPATH_PREFIX " << this->RPathPrefix << '\n';
204   }
205   os << indent << "  )\n";
206 }
207