/src/CMake/Source/cmGeneratorTarget_Options.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | /* clang-format off */ |
4 | | #include "cmGeneratorTarget.h" |
5 | | /* clang-format on */ |
6 | | |
7 | | #include <algorithm> |
8 | | #include <iterator> |
9 | | #include <map> |
10 | | #include <memory> |
11 | | #include <string> |
12 | | #include <unordered_set> |
13 | | #include <utility> |
14 | | #include <vector> |
15 | | |
16 | | #include <cm/string_view> |
17 | | #include <cmext/algorithm> |
18 | | #include <cmext/string_view> |
19 | | |
20 | | #include "cmEvaluatedTargetProperty.h" |
21 | | #include "cmGenExContext.h" |
22 | | #include "cmGeneratorExpressionDAGChecker.h" |
23 | | #include "cmList.h" |
24 | | #include "cmListFileCache.h" |
25 | | #include "cmLocalGenerator.h" |
26 | | #include "cmMakefile.h" |
27 | | #include "cmMessageType.h" |
28 | | #include "cmPolicies.h" |
29 | | #include "cmRange.h" |
30 | | #include "cmStringAlgorithms.h" |
31 | | #include "cmSystemTools.h" |
32 | | #include "cmValue.h" |
33 | | #include "cmake.h" |
34 | | |
35 | | enum class OptionsParse |
36 | | { |
37 | | None, |
38 | | Shell |
39 | | }; |
40 | | |
41 | | struct MsvcCharSetInfo |
42 | | { |
43 | | cmGeneratorTarget::MsvcCharSet CharSet; |
44 | | bool IsNeedToAddDefine; |
45 | | }; |
46 | | |
47 | | namespace { |
48 | | auto const DL_BEGIN = "<DEVICE_LINK>"_s; |
49 | | auto const DL_END = "</DEVICE_LINK>"_s; |
50 | | |
51 | | void processOptions(cmGeneratorTarget const* tgt, |
52 | | EvaluatedTargetPropertyEntries const& entries, |
53 | | std::vector<BT<std::string>>& options, |
54 | | std::unordered_set<std::string>& uniqueOptions, |
55 | | bool debugOptions, char const* logName, OptionsParse parse, |
56 | | bool processDeviceOptions = false) |
57 | 0 | { |
58 | 0 | bool splitOption = !processDeviceOptions; |
59 | 0 | for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { |
60 | 0 | std::string usedOptions; |
61 | 0 | for (std::string const& opt : entry.Values) { |
62 | 0 | if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) { |
63 | 0 | options.emplace_back(opt, entry.Backtrace); |
64 | 0 | splitOption = opt == DL_BEGIN; |
65 | 0 | continue; |
66 | 0 | } |
67 | | |
68 | 0 | if (uniqueOptions.insert(opt).second) { |
69 | 0 | if (parse == OptionsParse::Shell && |
70 | 0 | cmHasLiteralPrefix(opt, "SHELL:")) { |
71 | 0 | if (splitOption) { |
72 | 0 | std::vector<std::string> tmp; |
73 | 0 | cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp); |
74 | 0 | for (std::string& o : tmp) { |
75 | 0 | options.emplace_back(std::move(o), entry.Backtrace); |
76 | 0 | } |
77 | 0 | } else { |
78 | 0 | options.emplace_back(std::string(opt.c_str() + 6), |
79 | 0 | entry.Backtrace); |
80 | 0 | } |
81 | 0 | } else { |
82 | 0 | options.emplace_back(opt, entry.Backtrace); |
83 | 0 | } |
84 | 0 | if (debugOptions) { |
85 | 0 | usedOptions += cmStrCat(" * ", opt, '\n'); |
86 | 0 | } |
87 | 0 | } |
88 | 0 | } |
89 | 0 | if (!usedOptions.empty()) { |
90 | 0 | tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
91 | 0 | MessageType::LOG, |
92 | 0 | cmStrCat("Used ", logName, " for target ", tgt->GetName(), ":\n", |
93 | 0 | usedOptions), |
94 | 0 | entry.Backtrace); |
95 | 0 | } |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | enum class NestedLinkerFlags |
100 | | { |
101 | | PreserveAsSpelled, |
102 | | Normalize |
103 | | }; |
104 | | |
105 | | std::vector<BT<std::string>> wrapOptions( |
106 | | std::vector<std::string>& options, cmListFileBacktrace const& bt, |
107 | | std::vector<std::string> const& wrapperFlag, std::string const& wrapperSep, |
108 | | bool concatFlagAndArgs, NestedLinkerFlags nestedLinkerFlags) |
109 | 0 | { |
110 | 0 | std::vector<BT<std::string>> result; |
111 | |
|
112 | 0 | if (options.empty()) { |
113 | 0 | return result; |
114 | 0 | } |
115 | | |
116 | 0 | if (wrapperFlag.empty()) { |
117 | | // nothing specified, insert elements as is |
118 | 0 | result.reserve(options.size()); |
119 | 0 | for (std::string& o : options) { |
120 | 0 | result.emplace_back(std::move(o), bt); |
121 | 0 | } |
122 | 0 | return result; |
123 | 0 | } |
124 | | |
125 | 0 | auto insertWrapped = [&](std::vector<std::string>& opts) { |
126 | 0 | if (!wrapperSep.empty()) { |
127 | 0 | if (concatFlagAndArgs) { |
128 | | // insert flag elements except last one |
129 | 0 | for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { |
130 | 0 | result.emplace_back(*i, bt); |
131 | 0 | } |
132 | | // concatenate last flag element and all list values |
133 | | // in one option |
134 | 0 | result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); |
135 | 0 | } else { |
136 | 0 | for (std::string const& i : wrapperFlag) { |
137 | 0 | result.emplace_back(i, bt); |
138 | 0 | } |
139 | | // concatenate all list values in one option |
140 | 0 | result.emplace_back(cmJoin(opts, wrapperSep), bt); |
141 | 0 | } |
142 | 0 | } else { |
143 | | // prefix each element of list with wrapper |
144 | 0 | if (concatFlagAndArgs) { |
145 | 0 | std::transform(opts.begin(), opts.end(), opts.begin(), |
146 | 0 | [&wrapperFlag](std::string const& o) -> std::string { |
147 | 0 | return wrapperFlag.back() + o; |
148 | 0 | }); |
149 | 0 | } |
150 | 0 | for (std::string& o : opts) { |
151 | 0 | for (auto i = wrapperFlag.begin(), |
152 | 0 | e = concatFlagAndArgs ? wrapperFlag.end() - 1 |
153 | 0 | : wrapperFlag.end(); |
154 | 0 | i != e; ++i) { |
155 | 0 | result.emplace_back(*i, bt); |
156 | 0 | } |
157 | 0 | result.emplace_back(std::move(o), bt); |
158 | 0 | } |
159 | 0 | } |
160 | 0 | }; |
161 | |
|
162 | 0 | if (nestedLinkerFlags == NestedLinkerFlags::PreserveAsSpelled) { |
163 | 0 | insertWrapped(options); |
164 | 0 | return result; |
165 | 0 | } |
166 | | |
167 | 0 | for (std::vector<std::string>::size_type index = 0; index < options.size(); |
168 | 0 | index++) { |
169 | 0 | if (cmHasLiteralPrefix(options[index], "LINKER:")) { |
170 | | // LINKER wrapper specified, insert elements as is |
171 | 0 | result.emplace_back(std::move(options[index]), bt); |
172 | 0 | continue; |
173 | 0 | } |
174 | 0 | if (cmHasLiteralPrefix(options[index], "-Wl,")) { |
175 | | // replace option by LINKER wrapper |
176 | 0 | result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt); |
177 | 0 | continue; |
178 | 0 | } |
179 | 0 | if (cmHasLiteralPrefix(options[index], "-Xlinker=")) { |
180 | | // replace option by LINKER wrapper |
181 | 0 | result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt); |
182 | 0 | continue; |
183 | 0 | } |
184 | 0 | if (options[index] == "-Xlinker") { |
185 | | // replace option by LINKER wrapper |
186 | 0 | if (index + 1 < options.size()) { |
187 | 0 | result.emplace_back("LINKER:" + options[++index], bt); |
188 | 0 | } else { |
189 | 0 | result.emplace_back(std::move(options[index]), bt); |
190 | 0 | } |
191 | 0 | continue; |
192 | 0 | } |
193 | | |
194 | | // collect all options which must be transformed |
195 | 0 | std::vector<std::string> opts; |
196 | 0 | while (index < options.size()) { |
197 | 0 | if (!cmHasLiteralPrefix(options[index], "LINKER:") && |
198 | 0 | !cmHasLiteralPrefix(options[index], "-Wl,") && |
199 | 0 | !cmHasLiteralPrefix(options[index], "-Xlinker")) { |
200 | 0 | opts.emplace_back(std::move(options[index++])); |
201 | 0 | } else { |
202 | 0 | --index; |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | } |
206 | 0 | if (opts.empty()) { |
207 | 0 | continue; |
208 | 0 | } |
209 | | |
210 | 0 | insertWrapped(opts); |
211 | 0 | } |
212 | 0 | return result; |
213 | 0 | } |
214 | | |
215 | | cm::string_view const UNICODE_DEFINITION = "_UNICODE"_s; |
216 | | cm::string_view const MBCS_DEFINITION = "_MBCS"_s; |
217 | | cm::string_view const SBCS_DEFINITION = "_SBCS"_s; |
218 | | |
219 | | constexpr char UNICODE_DEFINITION_PREFIX[] = "_UNICODE="; |
220 | | constexpr char MBCS_DEFINITION_PREFIX[] = "_MBCS="; |
221 | | constexpr char SBCS_DEFINITION_PREFIX[] = "_SBCS="; |
222 | | |
223 | | MsvcCharSetInfo GetMsvcCharSetInfo( |
224 | | cmGeneratorTarget const& tgt, std::string const& lang, |
225 | | EvaluatedTargetPropertyEntries const& entries) |
226 | 0 | { |
227 | 0 | using MsvcCharSet = cmGeneratorTarget::MsvcCharSet; |
228 | |
|
229 | 0 | if (tgt.Makefile->GetSafeDefinition( |
230 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_ID")) != "MSVC"_s && |
231 | 0 | tgt.Makefile->GetSafeDefinition( |
232 | 0 | cmStrCat("CMAKE_", lang, "_SIMULATE_ID")) != "MSVC"_s) { |
233 | | |
234 | | // Only MSVC ABI uses this feature |
235 | 0 | return { MsvcCharSet::None, false }; |
236 | 0 | } |
237 | | |
238 | 0 | for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { |
239 | 0 | for (std::string const& value : entry.Values) { |
240 | 0 | MsvcCharSet charSet = cmGeneratorTarget::GetMsvcCharSet(value); |
241 | 0 | if (charSet != MsvcCharSet::None) { |
242 | 0 | return { charSet, false }; |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | // Default to multi-byte, similar to the Visual Studio generator |
248 | | // Define the default charset for Visual Studio too: |
249 | | // it should filter it out if need |
250 | 0 | return { MsvcCharSet::MultiByte, true }; |
251 | 0 | } |
252 | | |
253 | | } |
254 | | |
255 | | void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, |
256 | | std::string const& config, |
257 | | std::string const& language) const |
258 | 0 | { |
259 | 0 | std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language); |
260 | 0 | result.reserve(tmp.size()); |
261 | 0 | for (BT<std::string>& v : tmp) { |
262 | 0 | result.emplace_back(std::move(v.Value)); |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | | std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions( |
267 | | std::string const& config, std::string const& language) const |
268 | 0 | { |
269 | 0 | ConfigAndLanguage cacheKey(config, language); |
270 | 0 | { |
271 | 0 | auto it = this->CompileOptionsCache.find(cacheKey); |
272 | 0 | if (it != this->CompileOptionsCache.end()) { |
273 | 0 | return it->second; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | std::vector<BT<std::string>> result; |
277 | 0 | std::unordered_set<std::string> uniqueOptions; |
278 | |
|
279 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, language); |
280 | |
|
281 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
282 | 0 | this, "COMPILE_OPTIONS", nullptr, nullptr, context, |
283 | 0 | }; |
284 | |
|
285 | 0 | cmList debugProperties{ this->Makefile->GetDefinition( |
286 | 0 | "CMAKE_DEBUG_TARGET_PROPERTIES") }; |
287 | 0 | bool debugOptions = !this->DebugCompileOptionsDone && |
288 | 0 | cm::contains(debugProperties, "COMPILE_OPTIONS"); |
289 | |
|
290 | 0 | this->DebugCompileOptionsDone = true; |
291 | |
|
292 | 0 | EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( |
293 | 0 | this, context, &dagChecker, this->CompileOptionsEntries); |
294 | |
|
295 | 0 | AddInterfaceEntries(this, "INTERFACE_COMPILE_OPTIONS", context, &dagChecker, |
296 | 0 | entries, IncludeRuntimeInterface::Yes); |
297 | |
|
298 | 0 | processOptions(this, entries, result, uniqueOptions, debugOptions, |
299 | 0 | "compile options", OptionsParse::Shell); |
300 | |
|
301 | 0 | CompileOptionsCache.emplace(cacheKey, result); |
302 | 0 | return result; |
303 | 0 | } |
304 | | |
305 | | void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, |
306 | | std::string const& config) const |
307 | 0 | { |
308 | 0 | std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config); |
309 | 0 | result.reserve(tmp.size()); |
310 | 0 | for (BT<std::string>& v : tmp) { |
311 | 0 | result.emplace_back(std::move(v.Value)); |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures( |
316 | | std::string const& config) const |
317 | 0 | { |
318 | 0 | std::vector<BT<std::string>> result; |
319 | 0 | std::unordered_set<std::string> uniqueFeatures; |
320 | |
|
321 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, |
322 | 0 | /*language=*/std::string()); |
323 | |
|
324 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
325 | 0 | this, "COMPILE_FEATURES", nullptr, nullptr, context, |
326 | 0 | }; |
327 | |
|
328 | 0 | cmList debugProperties{ this->Makefile->GetDefinition( |
329 | 0 | "CMAKE_DEBUG_TARGET_PROPERTIES") }; |
330 | 0 | bool debugFeatures = !this->DebugCompileFeaturesDone && |
331 | 0 | cm::contains(debugProperties, "COMPILE_FEATURES"); |
332 | |
|
333 | 0 | this->DebugCompileFeaturesDone = true; |
334 | |
|
335 | 0 | EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( |
336 | 0 | this, context, &dagChecker, this->CompileFeaturesEntries); |
337 | |
|
338 | 0 | AddInterfaceEntries(this, "INTERFACE_COMPILE_FEATURES", context, &dagChecker, |
339 | 0 | entries, IncludeRuntimeInterface::Yes); |
340 | |
|
341 | 0 | processOptions(this, entries, result, uniqueFeatures, debugFeatures, |
342 | 0 | "compile features", OptionsParse::None); |
343 | |
|
344 | 0 | return result; |
345 | 0 | } |
346 | | |
347 | | void cmGeneratorTarget::GetCompileDefinitions( |
348 | | std::vector<std::string>& result, std::string const& config, |
349 | | std::string const& language) const |
350 | 0 | { |
351 | 0 | std::vector<BT<std::string>> tmp = |
352 | 0 | this->GetCompileDefinitions(config, language); |
353 | 0 | result.reserve(result.size() + tmp.size()); |
354 | 0 | for (BT<std::string>& v : tmp) { |
355 | 0 | result.emplace_back(std::move(v.Value)); |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions( |
360 | | std::string const& config, std::string const& language) const |
361 | 0 | { |
362 | 0 | ConfigAndLanguage cacheKey(config, language); |
363 | 0 | { |
364 | 0 | auto it = this->CompileDefinitionsCache.find(cacheKey); |
365 | 0 | if (it != this->CompileDefinitionsCache.end()) { |
366 | 0 | return it->second; |
367 | 0 | } |
368 | 0 | } |
369 | 0 | std::vector<BT<std::string>> list; |
370 | 0 | std::unordered_set<std::string> uniqueOptions; |
371 | |
|
372 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, language); |
373 | |
|
374 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
375 | 0 | this, "COMPILE_DEFINITIONS", nullptr, nullptr, context, |
376 | 0 | }; |
377 | |
|
378 | 0 | cmList debugProperties{ this->Makefile->GetDefinition( |
379 | 0 | "CMAKE_DEBUG_TARGET_PROPERTIES") }; |
380 | 0 | bool debugDefines = !this->DebugCompileDefinitionsDone && |
381 | 0 | cm::contains(debugProperties, "COMPILE_DEFINITIONS"); |
382 | |
|
383 | 0 | this->DebugCompileDefinitionsDone = true; |
384 | |
|
385 | 0 | EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( |
386 | 0 | this, context, &dagChecker, this->CompileDefinitionsEntries); |
387 | |
|
388 | 0 | AddInterfaceEntries(this, "INTERFACE_COMPILE_DEFINITIONS", context, |
389 | 0 | &dagChecker, entries, IncludeRuntimeInterface::Yes); |
390 | | |
391 | | // Add the character set definition |
392 | 0 | MsvcCharSetInfo charSetInfo = GetMsvcCharSetInfo(*this, language, entries); |
393 | 0 | if (charSetInfo.IsNeedToAddDefine && |
394 | 0 | this->GetPolicyStatusCMP0204() == cmPolicies::NEW) { |
395 | 0 | cm::string_view define; |
396 | 0 | switch (charSetInfo.CharSet) { |
397 | 0 | case MsvcCharSet::None: |
398 | | // Nothing to set |
399 | 0 | break; |
400 | 0 | case MsvcCharSet::Unicode: |
401 | 0 | define = UNICODE_DEFINITION; |
402 | 0 | break; |
403 | 0 | case MsvcCharSet::MultiByte: |
404 | 0 | define = MBCS_DEFINITION; |
405 | 0 | break; |
406 | 0 | case MsvcCharSet::SingleByte: |
407 | 0 | define = SBCS_DEFINITION; |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | if (!define.empty()) { |
411 | 0 | std::unique_ptr<TargetPropertyEntry> property = |
412 | 0 | TargetPropertyEntry::Create(*this->LocalGenerator->GetCMakeInstance(), |
413 | 0 | std::string{ define }); |
414 | 0 | entries.Entries.emplace_back( |
415 | 0 | EvaluateTargetPropertyEntry(this, context, &dagChecker, *property)); |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | 0 | processOptions(this, entries, list, uniqueOptions, debugDefines, |
420 | 0 | "compile definitions", OptionsParse::None); |
421 | |
|
422 | 0 | this->CompileDefinitionsCache.emplace(cacheKey, list); |
423 | 0 | return list; |
424 | 0 | } |
425 | | |
426 | | std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders( |
427 | | std::string const& config, std::string const& language) const |
428 | 0 | { |
429 | 0 | ConfigAndLanguage cacheKey(config, language); |
430 | 0 | { |
431 | 0 | auto it = this->PrecompileHeadersCache.find(cacheKey); |
432 | 0 | if (it != this->PrecompileHeadersCache.end()) { |
433 | 0 | return it->second; |
434 | 0 | } |
435 | 0 | } |
436 | 0 | std::unordered_set<std::string> uniqueOptions; |
437 | |
|
438 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, language); |
439 | |
|
440 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
441 | 0 | this, "PRECOMPILE_HEADERS", nullptr, nullptr, context, |
442 | 0 | }; |
443 | |
|
444 | 0 | cmList debugProperties{ this->Makefile->GetDefinition( |
445 | 0 | "CMAKE_DEBUG_TARGET_PROPERTIES") }; |
446 | 0 | bool debugDefines = !this->DebugPrecompileHeadersDone && |
447 | 0 | std::find(debugProperties.begin(), debugProperties.end(), |
448 | 0 | "PRECOMPILE_HEADERS") != debugProperties.end(); |
449 | |
|
450 | 0 | this->DebugPrecompileHeadersDone = true; |
451 | |
|
452 | 0 | EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( |
453 | 0 | this, context, &dagChecker, this->PrecompileHeadersEntries); |
454 | |
|
455 | 0 | AddInterfaceEntries(this, "INTERFACE_PRECOMPILE_HEADERS", context, |
456 | 0 | &dagChecker, entries, IncludeRuntimeInterface::Yes); |
457 | |
|
458 | 0 | std::vector<BT<std::string>> list; |
459 | 0 | processOptions(this, entries, list, uniqueOptions, debugDefines, |
460 | 0 | "precompile headers", OptionsParse::None); |
461 | |
|
462 | 0 | this->PrecompileHeadersCache.emplace(cacheKey, list); |
463 | 0 | return list; |
464 | 0 | } |
465 | | |
466 | | void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result, |
467 | | std::string const& config, |
468 | | std::string const& language) const |
469 | 0 | { |
470 | 0 | if (this->IsDeviceLink() && |
471 | 0 | this->GetPolicyStatusCMP0105() != cmPolicies::NEW) { |
472 | | // link options are not propagated to the device link step |
473 | 0 | return; |
474 | 0 | } |
475 | | |
476 | 0 | std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language); |
477 | 0 | result.reserve(tmp.size()); |
478 | 0 | for (BT<std::string>& v : tmp) { |
479 | 0 | result.emplace_back(std::move(v.Value)); |
480 | 0 | } |
481 | 0 | } |
482 | | |
483 | | std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( |
484 | | std::string const& config, std::string const& language) const |
485 | 0 | { |
486 | 0 | ConfigAndLanguage cacheKey( |
487 | 0 | config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); |
488 | 0 | { |
489 | 0 | auto it = this->LinkOptionsCache.find(cacheKey); |
490 | 0 | if (it != this->LinkOptionsCache.end()) { |
491 | 0 | return it->second; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | std::vector<BT<std::string>> result; |
495 | 0 | std::unordered_set<std::string> uniqueOptions; |
496 | |
|
497 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, language); |
498 | |
|
499 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
500 | 0 | this, "LINK_OPTIONS", nullptr, nullptr, context, |
501 | 0 | }; |
502 | |
|
503 | 0 | cmList debugProperties{ this->Makefile->GetDefinition( |
504 | 0 | "CMAKE_DEBUG_TARGET_PROPERTIES") }; |
505 | 0 | bool debugOptions = !this->DebugLinkOptionsDone && |
506 | 0 | cm::contains(debugProperties, "LINK_OPTIONS"); |
507 | |
|
508 | 0 | this->DebugLinkOptionsDone = true; |
509 | |
|
510 | 0 | EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( |
511 | 0 | this, context, &dagChecker, this->LinkOptionsEntries); |
512 | |
|
513 | 0 | AddInterfaceEntries(this, "INTERFACE_LINK_OPTIONS", context, &dagChecker, |
514 | 0 | entries, IncludeRuntimeInterface::Yes, |
515 | 0 | this->GetPolicyStatusCMP0099() == cmPolicies::NEW |
516 | 0 | ? UseTo::Link |
517 | 0 | : UseTo::Compile); |
518 | |
|
519 | 0 | processOptions(this, entries, result, uniqueOptions, debugOptions, |
520 | 0 | "link options", OptionsParse::Shell, this->IsDeviceLink()); |
521 | |
|
522 | 0 | if (this->IsDeviceLink()) { |
523 | | // wrap host link options |
524 | 0 | std::string const wrapper(this->Makefile->GetSafeDefinition( |
525 | 0 | cmStrCat("CMAKE_", language, "_DEVICE_COMPILER_WRAPPER_FLAG"))); |
526 | 0 | cmList wrapperFlag{ wrapper }; |
527 | 0 | std::string const wrapperSep(this->Makefile->GetSafeDefinition( |
528 | 0 | cmStrCat("CMAKE_", language, "_DEVICE_COMPILER_WRAPPER_FLAG_SEP"))); |
529 | 0 | bool concatFlagAndArgs = true; |
530 | 0 | if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { |
531 | 0 | concatFlagAndArgs = false; |
532 | 0 | wrapperFlag.pop_back(); |
533 | 0 | } |
534 | |
|
535 | 0 | auto it = result.begin(); |
536 | 0 | while (it != result.end()) { |
537 | 0 | if (it->Value == DL_BEGIN) { |
538 | | // device link options, no treatment |
539 | 0 | it = result.erase(it); |
540 | 0 | it = std::find_if(it, result.end(), [](const BT<std::string>& item) { |
541 | 0 | return item.Value == DL_END; |
542 | 0 | }); |
543 | 0 | if (it != result.end()) { |
544 | 0 | it = result.erase(it); |
545 | 0 | } |
546 | 0 | } else { |
547 | | // host link options must be wrapped |
548 | 0 | std::vector<std::string> options; |
549 | 0 | cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options); |
550 | 0 | auto hostOptions = |
551 | 0 | wrapOptions(options, it->Backtrace, wrapperFlag, wrapperSep, |
552 | 0 | concatFlagAndArgs, NestedLinkerFlags::Normalize); |
553 | 0 | it = result.erase(it); |
554 | | // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect |
555 | | // C++11 standard: 'std::vector::insert()' do not returns an iterator, |
556 | | // so need to recompute the iterator after insertion. |
557 | 0 | if (it == result.end()) { |
558 | 0 | cm::append(result, hostOptions); |
559 | 0 | it = result.end(); |
560 | 0 | } else { |
561 | 0 | auto index = it - result.begin(); |
562 | 0 | result.insert(it, hostOptions.begin(), hostOptions.end()); |
563 | 0 | it = result.begin() + index + hostOptions.size(); |
564 | 0 | } |
565 | 0 | } |
566 | 0 | } |
567 | 0 | } |
568 | | |
569 | | // Last step: replace "LINKER:" prefixed elements by |
570 | | // actual linker wrapper |
571 | 0 | result = this->ResolveLinkerWrapper(result, language); |
572 | |
|
573 | 0 | this->LinkOptionsCache.emplace(cacheKey, result); |
574 | 0 | return result; |
575 | 0 | } |
576 | | |
577 | | std::vector<BT<std::string>>& cmGeneratorTarget::ResolvePrefixWrapper( |
578 | | std::vector<BT<std::string>>& result, cm::string_view prefix, |
579 | | std::string const& language, bool joinItems) const |
580 | 0 | { |
581 | | // replace "LINKER:" or "ARCHIVER:" prefixed elements by actual linker or |
582 | | // archiver wrapper |
583 | 0 | std::string const wrapper(this->Makefile->GetSafeDefinition( |
584 | 0 | cmStrCat("CMAKE_", language, (this->IsDeviceLink() ? "_DEVICE_" : "_"), |
585 | 0 | prefix, "_WRAPPER_FLAG"))); |
586 | 0 | cmList wrapperFlag{ wrapper }; |
587 | 0 | std::string const wrapperSep(this->Makefile->GetSafeDefinition( |
588 | 0 | cmStrCat("CMAKE_", language, (this->IsDeviceLink() ? "_DEVICE_" : "_"), |
589 | 0 | prefix, "_WRAPPER_FLAG_SEP"))); |
590 | 0 | bool concatFlagAndArgs = true; |
591 | 0 | if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { |
592 | 0 | concatFlagAndArgs = false; |
593 | 0 | wrapperFlag.pop_back(); |
594 | 0 | } |
595 | |
|
596 | 0 | std::string const PREFIX{ cmStrCat(prefix, ':') }; |
597 | 0 | std::string const SHELL{ "SHELL:" }; |
598 | 0 | std::string const PREFIX_SHELL = cmStrCat(PREFIX, SHELL); |
599 | |
|
600 | 0 | for (auto entry = result.begin(); entry != result.end();) { |
601 | 0 | if (entry->Value.compare(0, PREFIX.length(), PREFIX) != 0) { |
602 | 0 | ++entry; |
603 | 0 | continue; |
604 | 0 | } |
605 | | |
606 | 0 | std::string value = std::move(entry->Value); |
607 | 0 | cmListFileBacktrace bt = std::move(entry->Backtrace); |
608 | 0 | entry = result.erase(entry); |
609 | |
|
610 | 0 | std::vector<std::string> options; |
611 | 0 | if (value.compare(0, PREFIX_SHELL.length(), PREFIX_SHELL) == 0) { |
612 | 0 | cmSystemTools::ParseUnixCommandLine( |
613 | 0 | value.c_str() + PREFIX_SHELL.length(), options); |
614 | 0 | } else { |
615 | 0 | options = |
616 | 0 | cmTokenize(value.substr(PREFIX.length()), ',', cmTokenizerMode::New); |
617 | 0 | } |
618 | |
|
619 | 0 | if (options.empty()) { |
620 | 0 | continue; |
621 | 0 | } |
622 | | |
623 | | // for now, raise an error if prefix SHELL: is part of arguments |
624 | 0 | if (std::find_if(options.begin(), options.end(), |
625 | 0 | [&SHELL](std::string const& item) -> bool { |
626 | 0 | return item.find(SHELL) != std::string::npos; |
627 | 0 | }) != options.end()) { |
628 | 0 | this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
629 | 0 | MessageType::FATAL_ERROR, |
630 | 0 | cmStrCat("'SHELL:' prefix is not supported as part of '", prefix, |
631 | 0 | ":' arguments."), |
632 | 0 | this->GetBacktrace()); |
633 | 0 | return result; |
634 | 0 | } |
635 | | |
636 | | // Very old versions of the C++ standard library return void for insert, so |
637 | | // can't use it to get the new iterator |
638 | 0 | auto const index = entry - result.begin(); |
639 | 0 | std::vector<BT<std::string>> processedOptions = |
640 | 0 | wrapOptions(options, bt, wrapperFlag, wrapperSep, concatFlagAndArgs, |
641 | 0 | NestedLinkerFlags::PreserveAsSpelled); |
642 | 0 | if (joinItems) { |
643 | 0 | result.insert( |
644 | 0 | entry, |
645 | 0 | cmJoin(cmMakeRange(processedOptions.begin(), processedOptions.end()), |
646 | 0 | " "_s)); |
647 | 0 | entry = std::next(result.begin(), index + 1); |
648 | 0 | } else { |
649 | 0 | result.insert(entry, processedOptions.begin(), processedOptions.end()); |
650 | 0 | entry = std::next(result.begin(), index + processedOptions.size()); |
651 | 0 | } |
652 | 0 | } |
653 | 0 | return result; |
654 | 0 | } |
655 | | |
656 | | std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( |
657 | | std::vector<BT<std::string>>& result, std::string const& language, |
658 | | bool joinItems) const |
659 | 0 | { |
660 | 0 | return this->ResolvePrefixWrapper(result, "LINKER"_s, language, joinItems); |
661 | 0 | } |
662 | | |
663 | | void cmGeneratorTarget::GetStaticLibraryLinkOptions( |
664 | | std::vector<std::string>& result, std::string const& config, |
665 | | std::string const& language) const |
666 | 0 | { |
667 | 0 | std::vector<BT<std::string>> tmp = |
668 | 0 | this->GetStaticLibraryLinkOptions(config, language); |
669 | 0 | result.reserve(tmp.size()); |
670 | 0 | for (BT<std::string>& v : tmp) { |
671 | 0 | result.emplace_back(std::move(v.Value)); |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | | std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( |
676 | | std::string const& config, std::string const& language) const |
677 | 0 | { |
678 | 0 | std::vector<BT<std::string>> result; |
679 | 0 | std::unordered_set<std::string> uniqueOptions; |
680 | |
|
681 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, language); |
682 | |
|
683 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
684 | 0 | this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, context, |
685 | 0 | }; |
686 | |
|
687 | 0 | EvaluatedTargetPropertyEntries entries; |
688 | 0 | if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { |
689 | 0 | std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create( |
690 | 0 | *this->LocalGenerator->GetCMakeInstance(), *linkOptions); |
691 | 0 | entries.Entries.emplace_back( |
692 | 0 | EvaluateTargetPropertyEntry(this, context, &dagChecker, *entry)); |
693 | 0 | } |
694 | 0 | processOptions(this, entries, result, uniqueOptions, false, |
695 | 0 | "static library link options", OptionsParse::Shell); |
696 | | |
697 | | // Last step: replace "ARCHIVER:" prefixed elements by |
698 | | // actual archiver wrapper |
699 | 0 | this->ResolveArchiverWrapper(result, language); |
700 | |
|
701 | 0 | return result; |
702 | 0 | } |
703 | | |
704 | | std::vector<BT<std::string>>& cmGeneratorTarget::ResolveArchiverWrapper( |
705 | | std::vector<BT<std::string>>& result, std::string const& language, |
706 | | bool joinItems) const |
707 | 0 | { |
708 | 0 | return this->ResolvePrefixWrapper(result, "ARCHIVER"_s, language, joinItems); |
709 | 0 | } |
710 | | |
711 | | void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result, |
712 | | std::string const& config, |
713 | | std::string const& language) const |
714 | 0 | { |
715 | 0 | std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language); |
716 | 0 | result.reserve(tmp.size()); |
717 | 0 | for (BT<std::string>& v : tmp) { |
718 | 0 | result.emplace_back(std::move(v.Value)); |
719 | 0 | } |
720 | 0 | } |
721 | | |
722 | | std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( |
723 | | std::string const& config, std::string const& language) const |
724 | 0 | { |
725 | 0 | std::vector<BT<std::string>> result; |
726 | 0 | std::unordered_set<std::string> uniqueOptions; |
727 | 0 | cm::GenEx::Context context(this->LocalGenerator, config, language); |
728 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
729 | 0 | this, "LINK_DEPENDS", nullptr, nullptr, context, |
730 | 0 | }; |
731 | |
|
732 | 0 | EvaluatedTargetPropertyEntries entries; |
733 | 0 | if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) { |
734 | 0 | cmList depends{ *linkDepends }; |
735 | 0 | for (auto const& depend : depends) { |
736 | 0 | std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create( |
737 | 0 | *this->LocalGenerator->GetCMakeInstance(), depend); |
738 | 0 | entries.Entries.emplace_back( |
739 | 0 | EvaluateTargetPropertyEntry(this, context, &dagChecker, *entry)); |
740 | 0 | } |
741 | 0 | } |
742 | 0 | AddInterfaceEntries(this, "INTERFACE_LINK_DEPENDS", context, &dagChecker, |
743 | 0 | entries, IncludeRuntimeInterface::Yes, |
744 | 0 | this->GetPolicyStatusCMP0099() == cmPolicies::NEW |
745 | 0 | ? UseTo::Link |
746 | 0 | : UseTo::Compile); |
747 | |
|
748 | 0 | processOptions(this, entries, result, uniqueOptions, false, "link depends", |
749 | 0 | OptionsParse::None); |
750 | |
|
751 | 0 | return result; |
752 | 0 | } |
753 | | |
754 | | cmGeneratorTarget::MsvcCharSet cmGeneratorTarget::GetMsvcCharSet( |
755 | | std::string const& singleDefine) |
756 | 0 | { |
757 | 0 | if (singleDefine == UNICODE_DEFINITION || |
758 | 0 | cmHasLiteralPrefix(singleDefine, UNICODE_DEFINITION_PREFIX)) { |
759 | 0 | return MsvcCharSet::Unicode; |
760 | 0 | } |
761 | | |
762 | 0 | if (singleDefine == MBCS_DEFINITION || |
763 | 0 | cmHasLiteralPrefix(singleDefine, MBCS_DEFINITION_PREFIX)) { |
764 | 0 | return MsvcCharSet::MultiByte; |
765 | 0 | } |
766 | | |
767 | 0 | if (singleDefine == SBCS_DEFINITION || |
768 | 0 | cmHasLiteralPrefix(singleDefine, SBCS_DEFINITION_PREFIX)) { |
769 | 0 | return MsvcCharSet::SingleByte; |
770 | 0 | } |
771 | | |
772 | 0 | return MsvcCharSet::None; |
773 | 0 | } |