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 "cmLinkLineComputer.h"
5 
6 #include <sstream>
7 #include <utility>
8 #include <vector>
9 
10 #include "cmComputeLinkInformation.h"
11 #include "cmGeneratorTarget.h"
12 #include "cmListFileCache.h"
13 #include "cmOutputConverter.h"
14 #include "cmStateTypes.h"
15 #include "cmStringAlgorithms.h"
16 
cmLinkLineComputer(cmOutputConverter * outputConverter,cmStateDirectory const & stateDir)17 cmLinkLineComputer::cmLinkLineComputer(cmOutputConverter* outputConverter,
18                                        cmStateDirectory const& stateDir)
19   : StateDir(stateDir)
20   , OutputConverter(outputConverter)
21   , ForResponse(false)
22   , UseWatcomQuote(false)
23   , UseNinjaMulti(false)
24   , Relink(false)
25 {
26 }
27 
28 cmLinkLineComputer::~cmLinkLineComputer() = default;
29 
SetUseWatcomQuote(bool useWatcomQuote)30 void cmLinkLineComputer::SetUseWatcomQuote(bool useWatcomQuote)
31 {
32   this->UseWatcomQuote = useWatcomQuote;
33 }
34 
SetUseNinjaMulti(bool useNinjaMulti)35 void cmLinkLineComputer::SetUseNinjaMulti(bool useNinjaMulti)
36 {
37   this->UseNinjaMulti = useNinjaMulti;
38 }
39 
SetForResponse(bool forResponse)40 void cmLinkLineComputer::SetForResponse(bool forResponse)
41 {
42   this->ForResponse = forResponse;
43 }
44 
SetRelink(bool relink)45 void cmLinkLineComputer::SetRelink(bool relink)
46 {
47   this->Relink = relink;
48 }
49 
ConvertToLinkReference(std::string const & lib) const50 std::string cmLinkLineComputer::ConvertToLinkReference(
51   std::string const& lib) const
52 {
53   return this->OutputConverter->MaybeRelativeToCurBinDir(lib);
54 }
55 
ComputeLinkLibs(cmComputeLinkInformation & cli)56 std::string cmLinkLineComputer::ComputeLinkLibs(cmComputeLinkInformation& cli)
57 {
58   std::string linkLibs;
59   std::vector<BT<std::string>> linkLibsList;
60   this->ComputeLinkLibs(cli, linkLibsList);
61   cli.AppendValues(linkLibs, linkLibsList);
62   return linkLibs;
63 }
64 
ComputeLinkLibs(cmComputeLinkInformation & cli,std::vector<BT<std::string>> & linkLibraries)65 void cmLinkLineComputer::ComputeLinkLibs(
66   cmComputeLinkInformation& cli, std::vector<BT<std::string>>& linkLibraries)
67 {
68   using ItemVector = cmComputeLinkInformation::ItemVector;
69   ItemVector const& items = cli.GetItems();
70   for (auto const& item : items) {
71     if (item.Target &&
72         item.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
73       continue;
74     }
75 
76     BT<std::string> linkLib;
77     if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
78       if (item.IsObject == cmComputeLinkInformation::ItemIsObject::Yes) {
79         linkLib.Value += cli.GetObjLinkFileFlag();
80       } else {
81         linkLib.Value += cli.GetLibLinkFileFlag();
82       }
83       linkLib.Value += this->ConvertToOutputFormat(
84         this->ConvertToLinkReference(item.Value.Value));
85       linkLib.Backtrace = item.Value.Backtrace;
86     } else {
87       linkLib = item.Value;
88     }
89     linkLib.Value += " ";
90 
91     linkLibraries.emplace_back(linkLib);
92   }
93 }
94 
ConvertToOutputFormat(std::string const & input)95 std::string cmLinkLineComputer::ConvertToOutputFormat(std::string const& input)
96 {
97   cmOutputConverter::OutputFormat shellFormat = cmOutputConverter::SHELL;
98   if (this->ForResponse) {
99     shellFormat = cmOutputConverter::RESPONSE;
100   } else if (this->UseWatcomQuote) {
101     shellFormat = cmOutputConverter::WATCOMQUOTE;
102   } else if (this->UseNinjaMulti) {
103     shellFormat = cmOutputConverter::NINJAMULTI;
104   }
105 
106   return this->OutputConverter->ConvertToOutputFormat(input, shellFormat);
107 }
108 
ConvertToOutputForExisting(std::string const & input)109 std::string cmLinkLineComputer::ConvertToOutputForExisting(
110   std::string const& input)
111 {
112   cmOutputConverter::OutputFormat shellFormat = cmOutputConverter::SHELL;
113   if (this->ForResponse) {
114     shellFormat = cmOutputConverter::RESPONSE;
115   } else if (this->UseWatcomQuote) {
116     shellFormat = cmOutputConverter::WATCOMQUOTE;
117   } else if (this->UseNinjaMulti) {
118     shellFormat = cmOutputConverter::NINJAMULTI;
119   }
120 
121   return this->OutputConverter->ConvertToOutputForExisting(input, shellFormat);
122 }
123 
ComputeLinkPath(cmComputeLinkInformation & cli,std::string const & libPathFlag,std::string const & libPathTerminator)124 std::string cmLinkLineComputer::ComputeLinkPath(
125   cmComputeLinkInformation& cli, std::string const& libPathFlag,
126   std::string const& libPathTerminator)
127 {
128   std::string linkPath;
129   std::vector<BT<std::string>> linkPathList;
130   this->ComputeLinkPath(cli, libPathFlag, libPathTerminator, linkPathList);
131   cli.AppendValues(linkPath, linkPathList);
132   return linkPath;
133 }
134 
ComputeLinkPath(cmComputeLinkInformation & cli,std::string const & libPathFlag,std::string const & libPathTerminator,std::vector<BT<std::string>> & linkPath)135 void cmLinkLineComputer::ComputeLinkPath(
136   cmComputeLinkInformation& cli, std::string const& libPathFlag,
137   std::string const& libPathTerminator, std::vector<BT<std::string>>& linkPath)
138 {
139   if (cli.GetLinkLanguage() == "Swift") {
140     std::string linkPathNoBT;
141 
142     for (const cmComputeLinkInformation::Item& item : cli.GetItems()) {
143       const cmGeneratorTarget* target = item.Target;
144       if (!target) {
145         continue;
146       }
147 
148       if (target->GetType() == cmStateEnums::STATIC_LIBRARY ||
149           target->GetType() == cmStateEnums::SHARED_LIBRARY) {
150         cmStateEnums::ArtifactType type = cmStateEnums::RuntimeBinaryArtifact;
151         if (target->HasImportLibrary(cli.GetConfig())) {
152           type = cmStateEnums::ImportLibraryArtifact;
153         }
154 
155         linkPathNoBT +=
156           cmStrCat(" ", libPathFlag,
157                    this->ConvertToOutputForExisting(
158                      item.Target->GetDirectory(cli.GetConfig(), type)),
159                    libPathTerminator, " ");
160       }
161     }
162 
163     if (!linkPathNoBT.empty()) {
164       linkPath.emplace_back(std::move(linkPathNoBT));
165     }
166   }
167 
168   for (BT<std::string> libDir : cli.GetDirectoriesWithBacktraces()) {
169     libDir.Value = cmStrCat(" ", libPathFlag,
170                             this->ConvertToOutputForExisting(libDir.Value),
171                             libPathTerminator, " ");
172     linkPath.emplace_back(libDir);
173   }
174 }
175 
ComputeRPath(cmComputeLinkInformation & cli)176 std::string cmLinkLineComputer::ComputeRPath(cmComputeLinkInformation& cli)
177 {
178   std::string rpath;
179   // Check what kind of rpath flags to use.
180   if (cli.GetRuntimeSep().empty()) {
181     // Each rpath entry gets its own option ("-R a -R b -R c")
182     std::vector<std::string> runtimeDirs;
183     cli.GetRPath(runtimeDirs, this->Relink);
184 
185     for (std::string const& rd : runtimeDirs) {
186       rpath += cli.GetRuntimeFlag();
187       rpath += this->ConvertToOutputFormat(rd);
188       rpath += " ";
189     }
190   } else {
191     // All rpath entries are combined ("-Wl,-rpath,a:b:c").
192     std::string rpathString = cli.GetRPathString(this->Relink);
193 
194     // Store the rpath option in the stream.
195     if (!rpathString.empty()) {
196       rpath += cli.GetRuntimeFlag();
197       rpath +=
198         this->OutputConverter->EscapeForShell(rpathString, !this->ForResponse);
199       rpath += " ";
200     }
201   }
202   return rpath;
203 }
204 
ComputeFrameworkPath(cmComputeLinkInformation & cli,std::string const & fwSearchFlag)205 std::string cmLinkLineComputer::ComputeFrameworkPath(
206   cmComputeLinkInformation& cli, std::string const& fwSearchFlag)
207 {
208   std::string frameworkPath;
209   if (!fwSearchFlag.empty()) {
210     std::vector<std::string> const& fwDirs = cli.GetFrameworkPaths();
211     for (std::string const& fd : fwDirs) {
212       frameworkPath += fwSearchFlag;
213       frameworkPath += this->ConvertToOutputFormat(fd);
214       frameworkPath += " ";
215     }
216   }
217   return frameworkPath;
218 }
219 
ComputeLinkLibraries(cmComputeLinkInformation & cli,std::string const & stdLibString)220 std::string cmLinkLineComputer::ComputeLinkLibraries(
221   cmComputeLinkInformation& cli, std::string const& stdLibString)
222 {
223   std::string linkLibraries;
224   std::vector<BT<std::string>> linkLibrariesList;
225   this->ComputeLinkLibraries(cli, stdLibString, linkLibrariesList);
226   cli.AppendValues(linkLibraries, linkLibrariesList);
227   return linkLibraries;
228 }
229 
ComputeLinkLibraries(cmComputeLinkInformation & cli,std::string const & stdLibString,std::vector<BT<std::string>> & linkLibraries)230 void cmLinkLineComputer::ComputeLinkLibraries(
231   cmComputeLinkInformation& cli, std::string const& stdLibString,
232   std::vector<BT<std::string>>& linkLibraries)
233 {
234   std::ostringstream rpathOut;
235   rpathOut << this->ComputeRPath(cli);
236 
237   std::string rpath = rpathOut.str();
238   if (!rpath.empty()) {
239     linkLibraries.emplace_back(std::move(rpath));
240   }
241 
242   // Write the library flags to the build rule.
243   this->ComputeLinkLibs(cli, linkLibraries);
244 
245   // Add the linker runtime search path if any.
246   std::ostringstream fout;
247   std::string rpath_link = cli.GetRPathLinkString();
248   if (!cli.GetRPathLinkFlag().empty() && !rpath_link.empty()) {
249     fout << cli.GetRPathLinkFlag();
250     fout << this->OutputConverter->EscapeForShell(rpath_link,
251                                                   !this->ForResponse);
252     fout << " ";
253   }
254 
255   if (!stdLibString.empty()) {
256     fout << stdLibString << " ";
257   }
258 
259   std::string remainingLibs = fout.str();
260   if (!remainingLibs.empty()) {
261     linkLibraries.emplace_back(remainingLibs);
262   }
263 }
264 
GetLinkerLanguage(cmGeneratorTarget * target,std::string const & config)265 std::string cmLinkLineComputer::GetLinkerLanguage(cmGeneratorTarget* target,
266                                                   std::string const& config)
267 {
268   return target->GetLinkerLanguage(config);
269 }
270