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