Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}