Coverage Report

Created: 2026-09-14 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportFileGenerator.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
#include "cmExportFileGenerator.h"
4
5
#include <array>
6
#include <cstddef>
7
#include <functional>
8
#include <sstream>
9
#include <utility>
10
11
#include <cm/memory>
12
#include <cm/optional>
13
#include <cm/string_view>
14
#include <cmext/string_view>
15
16
#include "cmsys/FStream.hxx"
17
18
#include "cmComputeLinkInformation.h"
19
#include "cmFindPackageStack.h"
20
#include "cmGeneratedFileStream.h"
21
#include "cmGeneratorFileSet.h"
22
#include "cmGeneratorTarget.h"
23
#include "cmLinkItem.h"
24
#include "cmList.h"
25
#include "cmLocalGenerator.h"
26
#include "cmMakefile.h"
27
#include "cmMessageType.h"
28
#include "cmPropertyMap.h"
29
#include "cmStringAlgorithms.h"
30
#include "cmSystemTools.h"
31
#include "cmTarget.h"
32
#include "cmTargetTypes.h"
33
#include "cmValue.h"
34
35
0
cmExportFileGenerator::cmExportFileGenerator() = default;
36
37
void cmExportFileGenerator::AddConfiguration(std::string const& config)
38
0
{
39
0
  this->Configurations.push_back(config);
40
0
}
41
42
void cmExportFileGenerator::SetExportFile(char const* mainFile)
43
0
{
44
0
  this->MainImportFile = mainFile;
45
0
  this->FileDir = cmSystemTools::GetFilenamePath(this->MainImportFile);
46
0
  this->FileBase =
47
0
    cmSystemTools::GetFilenameWithoutLastExtension(this->MainImportFile);
48
0
  this->FileExt =
49
0
    cmSystemTools::GetFilenameLastExtension(this->MainImportFile);
50
0
}
51
52
std::string const& cmExportFileGenerator::GetMainExportFileName() const
53
0
{
54
0
  return this->MainImportFile;
55
0
}
56
57
bool cmExportFileGenerator::GenerateImportFile()
58
0
{
59
  // Open the output file to generate it.
60
0
  std::unique_ptr<cmsys::ofstream> foutPtr;
61
0
  if (this->AppendMode) {
62
    // Open for append.
63
0
    auto openmodeApp = std::ios::app;
64
0
    foutPtr = cm::make_unique<cmsys::ofstream>(this->MainImportFile.c_str(),
65
0
                                               openmodeApp);
66
0
  } else {
67
    // Generate atomically and with copy-if-different.
68
0
    auto ap =
69
0
      cm::make_unique<cmGeneratedFileStream>(this->MainImportFile, true);
70
0
    ap->SetCopyIfDifferent(true);
71
0
    foutPtr = std::move(ap);
72
0
  }
73
0
  if (!foutPtr || !*foutPtr) {
74
0
    std::string se = cmSystemTools::GetLastSystemError();
75
0
    std::ostringstream e;
76
0
    e << "cannot write to file \"" << this->MainImportFile << "\": " << se;
77
0
    cmSystemTools::Error(e.str());
78
0
    return false;
79
0
  }
80
81
0
  return this->GenerateImportFile(*foutPtr);
82
0
}
83
84
std::string cmExportFileGenerator::PropertyConfigSuffix(
85
  std::string const& config)
86
0
{
87
  // Construct the property configuration suffix.
88
0
  if (config.empty()) {
89
0
    return "_NOCONFIG";
90
0
  }
91
0
  return cmStrCat('_', cmSystemTools::UpperCase(config));
92
0
}
93
94
void cmExportFileGenerator::GenerateImportConfig(std::ostream& os,
95
                                                 std::string const& config)
96
0
{
97
  // Generate the per-config target information.
98
0
  this->GenerateImportTargetsConfig(os, config, PropertyConfigSuffix(config));
99
0
}
100
101
bool cmExportFileGenerator::PopulateInterfaceProperties(
102
  cmGeneratorTarget const* target, std::string const& includesDestinationDirs,
103
  cmGeneratorExpression::PreprocessContext preprocessRule,
104
  ImportPropertyMap& properties)
105
0
{
106
0
  this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", target,
107
0
                                  preprocessRule, properties);
108
0
  this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target,
109
0
                                  preprocessRule, properties);
110
0
  this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", target,
111
0
                                  preprocessRule, properties);
112
0
  this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", target,
113
0
                                  preprocessRule, properties);
114
0
  this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", target,
115
0
                                  preprocessRule, properties);
116
0
  this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", target,
117
0
                                  preprocessRule, properties);
118
0
  this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", target,
119
0
                                  preprocessRule, properties);
120
0
  this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
121
0
                                  target, properties);
122
123
0
  this->PopulateInterfaceProperty("SPDX_LICENSE", target, preprocessRule,
124
0
                                  properties);
125
126
0
  std::string errorMessage;
127
0
  if (!this->PopulateCxxModuleExportProperties(
128
0
        target, properties, preprocessRule, includesDestinationDirs,
129
0
        errorMessage)) {
130
0
    this->ReportError(errorMessage);
131
0
    return false;
132
0
  }
133
134
0
  if (!this->PopulateExportProperties(target, properties, errorMessage)) {
135
0
    this->ReportError(errorMessage);
136
0
    return false;
137
0
  }
138
0
  this->PopulateCompatibleInterfaceProperties(target, properties);
139
0
  this->PopulateCustomTransitiveInterfaceProperties(target, preprocessRule,
140
0
                                                    properties);
141
142
0
  return true;
143
0
}
144
145
bool cmExportFileGenerator::PopulateFileSetInterfaceProperties(
146
  cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet,
147
  cmGeneratorExpression::PreprocessContext preprocessRule,
148
  ImportPropertyMap& properties)
149
0
{
150
0
  this->PopulateFileSetInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS",
151
0
                                         target, fileSet, preprocessRule,
152
0
                                         properties);
153
0
  this->PopulateFileSetInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target,
154
0
                                         fileSet, preprocessRule, properties);
155
156
0
  return true;
157
0
}
158
159
void cmExportFileGenerator::PopulateInterfaceProperty(
160
  std::string const& propName, cmGeneratorTarget const* target,
161
  ImportPropertyMap& properties) const
162
0
{
163
0
  cmValue input = target->GetProperty(propName);
164
0
  if (input) {
165
0
    properties[propName] = *input;
166
0
  }
167
0
}
168
169
void cmExportFileGenerator::PopulateInterfaceProperty(
170
  std::string const& propName, std::string const& outputName,
171
  cmGeneratorTarget const* target,
172
  cmGeneratorExpression::PreprocessContext preprocessRule,
173
  ImportPropertyMap& properties)
174
0
{
175
0
  cmValue input = target->GetProperty(propName);
176
0
  if (input) {
177
0
    if (input->empty()) {
178
      // Set to empty
179
0
      properties[outputName].clear();
180
0
      return;
181
0
    }
182
183
0
    std::string prepro =
184
0
      cmGeneratorExpression::Preprocess(*input, preprocessRule);
185
0
    if (!prepro.empty()) {
186
0
      this->ResolveTargetsInGeneratorExpressions(prepro, target);
187
0
      properties[outputName] = prepro;
188
0
    }
189
0
  }
190
0
}
191
192
void cmExportFileGenerator::PopulateInterfaceProperty(
193
  std::string const& propName, cmGeneratorTarget const* target,
194
  cmGeneratorExpression::PreprocessContext preprocessRule,
195
  ImportPropertyMap& properties)
196
0
{
197
0
  this->PopulateInterfaceProperty(propName, propName, target, preprocessRule,
198
0
                                  properties);
199
0
}
200
201
void cmExportFileGenerator::PopulateFileSetInterfaceProperty(
202
  std::string const& propName, cmGeneratorTarget const* target,
203
  cmGeneratorFileSet const* fileSet,
204
  cmGeneratorExpression::PreprocessContext preprocessRule,
205
  ImportPropertyMap& properties)
206
0
{
207
0
  cmValue input = fileSet->GetProperty(propName);
208
0
  if (input) {
209
0
    if (input->empty()) {
210
      // Set to empty
211
0
      properties[propName].clear();
212
0
      return;
213
0
    }
214
215
0
    std::string prepro =
216
0
      cmGeneratorExpression::Preprocess(*input, preprocessRule);
217
0
    if (!prepro.empty()) {
218
0
      this->ResolveTargetsInGeneratorExpressions(prepro, target);
219
0
      properties[propName] = prepro;
220
0
    }
221
0
  }
222
0
}
223
224
bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
225
  cmGeneratorTarget const* target,
226
  cmGeneratorExpression::PreprocessContext preprocessRule,
227
  ImportPropertyMap& properties)
228
0
{
229
0
  if (!target->IsLinkable()) {
230
0
    return false;
231
0
  }
232
0
  static std::array<std::string, 3> const linkIfaceProps = {
233
0
    { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT",
234
0
      "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" }
235
0
  };
236
0
  bool hadINTERFACE_LINK_LIBRARIES = false;
237
0
  for (std::string const& linkIfaceProp : linkIfaceProps) {
238
0
    if (cmValue input = target->GetProperty(linkIfaceProp)) {
239
0
      std::string prepro =
240
0
        cmGeneratorExpression::Preprocess(*input, preprocessRule);
241
0
      if (!prepro.empty()) {
242
0
        this->ResolveTargetsInGeneratorExpressions(prepro, target,
243
0
                                                   ReplaceFreeTargets);
244
0
        properties[linkIfaceProp] = prepro;
245
0
        hadINTERFACE_LINK_LIBRARIES = true;
246
0
      }
247
0
    }
248
0
  }
249
0
  return hadINTERFACE_LINK_LIBRARIES;
250
0
}
251
252
void cmExportFileGenerator::AddImportPrefix(std::string& exportDirs) const
253
0
{
254
0
  std::vector<std::string> entries;
255
0
  cmGeneratorExpression::Split(exportDirs, entries);
256
0
  exportDirs.clear();
257
0
  char const* sep = "";
258
0
  cm::string_view const prefixWithSlash = this->GetImportPrefixWithSlash();
259
0
  for (std::string const& e : entries) {
260
0
    exportDirs += sep;
261
0
    sep = ";";
262
0
    if (!cmSystemTools::FileIsFullPath(e) &&
263
0
        !cmHasPrefix(e, prefixWithSlash)) {
264
0
      exportDirs += prefixWithSlash;
265
0
    }
266
0
    exportDirs += e;
267
0
  }
268
0
}
269
270
namespace {
271
void getPropertyContents(cmGeneratorTarget const* tgt, std::string const& prop,
272
                         std::set<std::string>& ifaceProperties)
273
0
{
274
0
  cmValue p = tgt->GetProperty(prop);
275
0
  if (!p) {
276
0
    return;
277
0
  }
278
0
  cmList content{ *p };
279
0
  ifaceProperties.insert(content.begin(), content.end());
280
0
}
281
282
void getCompatibleInterfaceProperties(cmGeneratorTarget const* target,
283
                                      std::set<std::string>& ifaceProperties,
284
                                      std::string const& config)
285
0
{
286
0
  if (target->GetType() == cm::TargetType::OBJECT_LIBRARY) {
287
    // object libraries have no link information, so nothing to compute
288
0
    return;
289
0
  }
290
291
0
  cmComputeLinkInformation* info = target->GetLinkInformation(config);
292
293
0
  if (!info) {
294
0
    cmLocalGenerator* lg = target->GetLocalGenerator();
295
0
    std::ostringstream e;
296
0
    e << "Exporting the target \"" << target->GetName()
297
0
      << "\" is not "
298
0
         "allowed since its linker language cannot be determined";
299
0
    lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
300
0
    return;
301
0
  }
302
303
0
  cmComputeLinkInformation::ItemVector const& deps = info->GetItems();
304
305
0
  for (auto const& dep : deps) {
306
0
    if (!dep.Target ||
307
0
        dep.Target->GetType() == cm::TargetType::OBJECT_LIBRARY) {
308
0
      continue;
309
0
    }
310
0
    getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_BOOL",
311
0
                        ifaceProperties);
312
0
    getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_STRING",
313
0
                        ifaceProperties);
314
0
    getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_NUMBER_MIN",
315
0
                        ifaceProperties);
316
0
    getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_NUMBER_MAX",
317
0
                        ifaceProperties);
318
0
  }
319
0
}
320
}
321
322
void cmExportFileGenerator::PopulateCompatibleInterfaceProperties(
323
  cmGeneratorTarget const* gtarget, ImportPropertyMap& properties) const
324
0
{
325
0
  this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_BOOL", gtarget,
326
0
                                  properties);
327
0
  this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_STRING", gtarget,
328
0
                                  properties);
329
0
  this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_NUMBER_MIN", gtarget,
330
0
                                  properties);
331
0
  this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_NUMBER_MAX", gtarget,
332
0
                                  properties);
333
334
0
  std::set<std::string> ifaceProperties;
335
336
0
  getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_BOOL", ifaceProperties);
337
0
  getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_STRING", ifaceProperties);
338
0
  getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_NUMBER_MIN",
339
0
                      ifaceProperties);
340
0
  getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_NUMBER_MAX",
341
0
                      ifaceProperties);
342
343
0
  if (gtarget->GetType() != cm::TargetType::INTERFACE_LIBRARY) {
344
0
    std::vector<std::string> configNames =
345
0
      gtarget->Target->GetMakefile()->GetGeneratorConfigs(
346
0
        cmMakefile::IncludeEmptyConfig);
347
348
0
    for (std::string const& cn : configNames) {
349
0
      getCompatibleInterfaceProperties(gtarget, ifaceProperties, cn);
350
0
    }
351
0
  }
352
353
0
  for (std::string const& ip : ifaceProperties) {
354
0
    this->PopulateInterfaceProperty("INTERFACE_" + ip, gtarget, properties);
355
0
  }
356
0
}
357
358
void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties(
359
  cmGeneratorTarget const* target,
360
  cmGeneratorExpression::PreprocessContext preprocessRule,
361
  ImportPropertyMap& properties)
362
0
{
363
0
  this->PopulateInterfaceProperty("TRANSITIVE_COMPILE_PROPERTIES", target,
364
0
                                  properties);
365
0
  this->PopulateInterfaceProperty("TRANSITIVE_LINK_PROPERTIES", target,
366
0
                                  properties);
367
0
  cmGeneratorTarget::CheckLinkLibrariesSuppressionRAII suppress;
368
0
  std::set<std::string> ifaceProperties;
369
0
  for (std::string const& config : this->Configurations) {
370
0
    for (auto const& i : target->GetCustomTransitiveProperties(
371
0
           config, cmGeneratorTarget::PropertyFor::Interface)) {
372
0
      ifaceProperties.emplace(i.second.InterfaceName);
373
0
    }
374
0
  }
375
0
  for (std::string const& ip : ifaceProperties) {
376
0
    this->PopulateInterfaceProperty(ip, target, preprocessRule, properties);
377
0
  }
378
0
}
379
380
bool cmExportFileGenerator::NoteLinkedTarget(
381
  cmGeneratorTarget const* /*target*/, std::string const& /*linkedName*/,
382
  cmGeneratorTarget const* /*linkedTarget*/)
383
0
{
384
  // Default implementation does nothing; only needed by some generators.
385
0
  return true;
386
0
}
387
388
bool cmExportFileGenerator::AddTargetNamespace(std::string& input,
389
                                               cmGeneratorTarget const* target,
390
                                               cmLocalGenerator const* lg)
391
0
{
392
0
  cmGeneratorTarget::TargetOrString resolved =
393
0
    target->ResolveTargetReference(input, lg);
394
395
0
  cmGeneratorTarget* tgt = resolved.Target;
396
0
  if (!tgt) {
397
0
    input = resolved.String;
398
0
    return false;
399
0
  }
400
401
0
  std::vector<std::string> replace = tgt->Target->GetExportTargets();
402
0
  if (!replace.empty()) {
403
0
    std::vector<std::string> out;
404
0
    out.reserve(replace.size());
405
406
0
    bool result = true;
407
0
    for (std::string& t : replace) {
408
0
      result = this->AddTargetNamespace(t, target, lg) && result;
409
0
      out.emplace_back(std::move(t));
410
0
    }
411
412
0
    input = cmJoin(out, ";"_s);
413
0
    return result;
414
0
  }
415
416
0
  cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack();
417
0
  if (!pkgStack.Empty() ||
418
0
      tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) {
419
0
    this->ExternalTargets.emplace(tgt);
420
0
  }
421
422
0
  if (tgt->IsImported()) {
423
0
    input = tgt->GetName();
424
0
    return this->NoteLinkedTarget(target, input, tgt);
425
0
  }
426
427
0
  if (this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) {
428
0
    input = this->Namespace + tgt->GetExportName();
429
0
  } else {
430
0
    std::string namespacedTarget;
431
0
    this->HandleMissingTarget(namespacedTarget, target, tgt);
432
0
    if (!namespacedTarget.empty()) {
433
0
      input = namespacedTarget;
434
0
    } else {
435
0
      input = tgt->GetName();
436
0
    }
437
0
  }
438
439
0
  return this->NoteLinkedTarget(target, input, tgt);
440
0
}
441
442
void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
443
  std::string& input, cmGeneratorTarget const* target,
444
  FreeTargetsReplace replace)
445
0
{
446
0
  cmLocalGenerator const* lg = target->GetLocalGenerator();
447
0
  if (replace == NoReplaceFreeTargets) {
448
0
    this->ResolveTargetsInGeneratorExpression(input, target, lg);
449
0
    return;
450
0
  }
451
0
  std::vector<std::string> parts;
452
0
  cmGeneratorExpression::Split(input, parts);
453
454
0
  std::string sep;
455
0
  input.clear();
456
0
  for (std::string& li : parts) {
457
0
    if (target->IsLinkLookupScope(li, lg)) {
458
0
      continue;
459
0
    }
460
0
    if (cmGeneratorExpression::Find(li) == std::string::npos) {
461
0
      this->AddTargetNamespace(li, target, lg);
462
0
    } else {
463
0
      this->ResolveTargetsInGeneratorExpression(li, target, lg);
464
0
    }
465
0
    input += sep + li;
466
0
    sep = ";";
467
0
  }
468
0
}
469
470
cm::optional<std::string> cmResolveTargetsInGeneratorExpression(
471
  std::string& input,
472
  std::function<bool(std::string& name)> const& addTargetNamespace)
473
0
{
474
0
  std::string::size_type pos = 0;
475
0
  std::string::size_type lastPos = pos;
476
477
0
  while ((pos = input.find("$<TARGET_PROPERTY:", lastPos)) !=
478
0
         std::string::npos) {
479
0
    std::string::size_type nameStartPos = pos + cmStrLen("$<TARGET_PROPERTY:");
480
0
    std::string::size_type closePos = input.find('>', nameStartPos);
481
0
    std::string::size_type commaPos = input.find(',', nameStartPos);
482
0
    std::string::size_type nextOpenPos = input.find("$<", nameStartPos);
483
0
    if (commaPos == std::string::npos    // Implied 'this' target
484
0
        || closePos == std::string::npos // Incomplete expression.
485
0
        || closePos < commaPos           // Implied 'this' target
486
0
        || nextOpenPos < commaPos)       // Non-literal
487
0
    {
488
0
      lastPos = nameStartPos;
489
0
      continue;
490
0
    }
491
492
0
    std::string targetName =
493
0
      input.substr(nameStartPos, commaPos - nameStartPos);
494
495
0
    if (addTargetNamespace(targetName)) {
496
0
      input.replace(nameStartPos, commaPos - nameStartPos, targetName);
497
0
    }
498
0
    lastPos = nameStartPos + targetName.size() + 1;
499
0
  }
500
501
0
  cm::optional<std::string> errorString;
502
0
  pos = 0;
503
0
  lastPos = pos;
504
0
  while ((pos = input.find("$<TARGET_NAME:", lastPos)) != std::string::npos) {
505
0
    std::string::size_type nameStartPos = pos + cmStrLen("$<TARGET_NAME:");
506
0
    std::string::size_type endPos = input.find('>', nameStartPos);
507
0
    if (endPos == std::string::npos) {
508
0
      errorString = "$<TARGET_NAME:...> expression incomplete";
509
0
      break;
510
0
    }
511
0
    std::string targetName = input.substr(nameStartPos, endPos - nameStartPos);
512
0
    if (targetName.find("$<") != std::string::npos) {
513
0
      errorString = "$<TARGET_NAME:...> requires its parameter to be a "
514
0
                    "literal.";
515
0
      break;
516
0
    }
517
0
    if (!addTargetNamespace(targetName)) {
518
0
      errorString = "$<TARGET_NAME:...> requires its parameter to be a "
519
0
                    "reachable target.";
520
0
      break;
521
0
    }
522
0
    input.replace(pos, endPos - pos + 1, targetName);
523
0
    lastPos = pos + targetName.size();
524
0
  }
525
526
0
  pos = 0;
527
0
  lastPos = pos;
528
0
  while (!errorString &&
529
0
         (pos = input.find("$<LINK_ONLY:", lastPos)) != std::string::npos) {
530
0
    std::string::size_type nameStartPos = pos + cmStrLen("$<LINK_ONLY:");
531
0
    std::string::size_type endPos = input.find('>', nameStartPos);
532
0
    if (endPos == std::string::npos) {
533
0
      errorString = "$<LINK_ONLY:...> expression incomplete";
534
0
      break;
535
0
    }
536
0
    std::string libName = input.substr(nameStartPos, endPos - nameStartPos);
537
0
    if (cmGeneratorExpression::IsValidTargetName(libName) &&
538
0
        addTargetNamespace(libName)) {
539
0
      input.replace(nameStartPos, endPos - nameStartPos, libName);
540
0
    }
541
0
    lastPos = nameStartPos + libName.size() + 1;
542
0
  }
543
544
0
  while (!errorString &&
545
0
         (pos = input.find("$<COMPILE_ONLY:", lastPos)) != std::string::npos) {
546
0
    std::string::size_type nameStartPos = pos + cmStrLen("$<COMPILE_ONLY:");
547
0
    std::string::size_type endPos = input.find('>', nameStartPos);
548
0
    if (endPos == std::string::npos) {
549
0
      errorString = "$<COMPILE_ONLY:...> expression incomplete";
550
0
      break;
551
0
    }
552
0
    std::string libName = input.substr(nameStartPos, endPos - nameStartPos);
553
0
    if (cmGeneratorExpression::IsValidTargetName(libName) &&
554
0
        addTargetNamespace(libName)) {
555
0
      input.replace(nameStartPos, endPos - nameStartPos, libName);
556
0
    }
557
0
    lastPos = nameStartPos + libName.size() + 1;
558
0
  }
559
560
0
  return errorString;
561
0
}
562
563
void cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
564
  std::string& input, cmGeneratorTarget const* target,
565
  cmLocalGenerator const* lg)
566
0
{
567
0
  auto err = cmResolveTargetsInGeneratorExpression(
568
0
    input, [this, target, lg](std::string& name) {
569
0
      return this->AddTargetNamespace(name, target, lg);
570
0
    });
571
0
  this->ReplaceInstallPrefix(input);
572
0
  if (err) {
573
0
    target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, *err);
574
0
  }
575
0
}
576
577
void cmExportFileGenerator::ReplaceInstallPrefix(std::string& /*unused*/) const
578
0
{
579
  // Do nothing
580
0
}
581
582
void cmExportFileGenerator::SetImportDetailProperties(
583
  std::string const& config, std::string const& suffix,
584
  cmGeneratorTarget const* target, ImportPropertyMap& properties)
585
0
{
586
  // Get the makefile in which to lookup target information.
587
0
  cmMakefile* mf = target->Makefile;
588
589
  // Add the soname for unix shared libraries.
590
0
  if (target->GetType() == cm::TargetType::SHARED_LIBRARY ||
591
0
      target->GetType() == cm::TargetType::MODULE_LIBRARY) {
592
0
    if (!target->IsDLLPlatform()) {
593
0
      std::string prop;
594
0
      std::string value;
595
0
      if (target->HasSOName(config)) {
596
0
        if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
597
0
          value = this->InstallNameDir(target, config);
598
0
        }
599
0
        prop = "IMPORTED_SONAME";
600
0
        value += target->GetSOName(config);
601
0
      } else {
602
0
        prop = "IMPORTED_NO_SONAME";
603
0
        value = "TRUE";
604
0
      }
605
0
      prop += suffix;
606
0
      properties[prop] = value;
607
0
    }
608
0
  }
609
610
  // Add the transitive link dependencies for this configuration.
611
0
  if (cmLinkInterface const* iface =
612
0
        target->GetLinkInterface(config, target)) {
613
0
    this->SetImportLinkProperty(
614
0
      suffix, target, "IMPORTED_LINK_INTERFACE_LANGUAGES", iface->Languages,
615
0
      properties, ImportLinkPropertyTargetNames::No);
616
617
    // Export IMPORTED_LINK_DEPENDENT_LIBRARIES to help consuming linkers
618
    // find private dependencies of shared libraries.
619
0
    std::size_t oldMissingTargetsSize = this->MissingTargets.size();
620
0
    auto oldExternalTargets = this->ExternalTargets;
621
0
    this->SetImportLinkProperty(
622
0
      suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps,
623
0
      properties, ImportLinkPropertyTargetNames::Yes);
624
    // Avoid enforcing shared library private dependencies as public package
625
    // dependencies by ignoring missing targets added for them.
626
0
    this->MissingTargets.resize(oldMissingTargetsSize);
627
0
    this->ExternalTargets = std::move(oldExternalTargets);
628
629
0
    if (iface->Multiplicity > 0) {
630
0
      std::string prop =
631
0
        cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
632
0
      properties[prop] = std::to_string(iface->Multiplicity);
633
0
    }
634
0
  }
635
636
  // Add information if this target is a managed target
637
0
  if (target->GetManagedType(config) !=
638
0
      cmGeneratorTarget::ManagedType::Native) {
639
0
    std::string prop = cmStrCat("IMPORTED_COMMON_LANGUAGE_RUNTIME", suffix);
640
0
    std::string propval;
641
0
    if (cmValue p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
642
0
      propval = *p;
643
0
    } else if (target->IsCSharpOnly()) {
644
      // C# projects do not have the /clr flag, so we set the property
645
      // here to mark the target as (only) managed (i.e. no .lib file
646
      // to link to). Otherwise the  COMMON_LANGUAGE_RUNTIME target
647
      // property would have to be set manually for C# targets to make
648
      // exporting/importing work.
649
0
      propval = "CSharp";
650
0
    }
651
0
    properties[prop] = propval;
652
0
  }
653
0
}
654
655
namespace {
656
std::string const& asString(std::string const& l)
657
0
{
658
0
  return l;
659
0
}
660
661
std::string const& asString(cmLinkItem const& l)
662
0
{
663
0
  return l.AsStr();
664
0
}
665
}
666
667
template <typename T>
668
void cmExportFileGenerator::SetImportLinkProperty(
669
  std::string const& suffix, cmGeneratorTarget const* target,
670
  std::string const& propName, std::vector<T> const& entries,
671
  ImportPropertyMap& properties, ImportLinkPropertyTargetNames targetNames)
672
0
{
673
  // Skip the property if there are no entries.
674
0
  if (entries.empty()) {
675
0
    return;
676
0
  }
677
678
0
  cmLocalGenerator const* lg = target->GetLocalGenerator();
679
680
  // Construct the property value.
681
0
  std::string link_entries;
682
0
  char const* sep = "";
683
0
  for (T const& l : entries) {
684
    // Separate this from the previous entry.
685
0
    link_entries += sep;
686
0
    sep = ";";
687
688
0
    if (targetNames == ImportLinkPropertyTargetNames::Yes) {
689
0
      std::string temp = asString(l);
690
0
      this->AddTargetNamespace(temp, target, lg);
691
0
      link_entries += temp;
692
0
    } else {
693
0
      link_entries += asString(l);
694
0
    }
695
0
  }
696
697
  // Store the property.
698
0
  std::string prop = cmStrCat(propName, suffix);
699
0
  properties[prop] = link_entries;
700
0
}
Unexecuted instantiation: void cmExportFileGenerator::SetImportLinkProperty<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmGeneratorTarget const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&, cmExportFileGenerator::ImportLinkPropertyTargetNames)
Unexecuted instantiation: void cmExportFileGenerator::SetImportLinkProperty<cmLinkItem>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmGeneratorTarget const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<cmLinkItem, std::__1::allocator<cmLinkItem> > const&, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&, cmExportFileGenerator::ImportLinkPropertyTargetNames)
701
702
template void cmExportFileGenerator::SetImportLinkProperty<std::string>(
703
  std::string const&, cmGeneratorTarget const*, std::string const&,
704
  std::vector<std::string> const&, ImportPropertyMap& properties,
705
  ImportLinkPropertyTargetNames);
706
707
template void cmExportFileGenerator::SetImportLinkProperty<cmLinkItem>(
708
  std::string const&, cmGeneratorTarget const*, std::string const&,
709
  std::vector<cmLinkItem> const&, ImportPropertyMap& properties,
710
  ImportLinkPropertyTargetNames);
711
712
namespace {
713
enum class ExportWhen
714
{
715
  Defined,
716
  Always,
717
};
718
719
enum class PropertyType
720
{
721
  Strings,
722
  Paths,
723
  IncludePaths,
724
};
725
726
bool PropertyTypeIsForPaths(PropertyType pt)
727
0
{
728
0
  switch (pt) {
729
0
    case PropertyType::Strings:
730
0
      return false;
731
0
    case PropertyType::Paths:
732
0
    case PropertyType::IncludePaths:
733
0
      return true;
734
0
  }
735
0
  return false;
736
0
}
737
}
738
739
bool cmExportFileGenerator::PopulateCxxModuleExportProperties(
740
  cmGeneratorTarget const* gte, ImportPropertyMap& properties,
741
  cmGeneratorExpression::PreprocessContext ctx,
742
  std::string const& includesDestinationDirs, std::string&)
743
0
{
744
0
  if (!gte->HaveCxx20ModuleSources()) {
745
0
    return true;
746
0
  }
747
748
0
  struct ModuleTargetPropertyTable
749
0
  {
750
0
    cm::static_string_view Name;
751
0
    ExportWhen Cond;
752
0
  };
753
754
0
  ModuleTargetPropertyTable const exportedDirectModuleProperties[] = {
755
0
    { "CXX_EXTENSIONS"_s, ExportWhen::Defined },
756
0
  };
757
0
  for (auto const& prop : exportedDirectModuleProperties) {
758
0
    auto const propNameStr = std::string(prop.Name);
759
0
    cmValue propValue = gte->Target->GetComputedProperty(
760
0
      propNameStr, *gte->Target->GetMakefile());
761
0
    if (!propValue) {
762
0
      propValue = gte->Target->GetProperty(propNameStr);
763
0
    }
764
0
    if (propValue) {
765
0
      properties[propNameStr] =
766
0
        cmGeneratorExpression::Preprocess(*propValue, ctx);
767
0
    } else if (prop.Cond == ExportWhen::Always) {
768
0
      properties[propNameStr] = "";
769
0
    }
770
0
  }
771
772
0
  struct ModulePropertyTable
773
0
  {
774
0
    cm::static_string_view Name;
775
0
    PropertyType Type;
776
0
  };
777
778
0
  ModulePropertyTable const exportedModuleProperties[] = {
779
0
    { "INCLUDE_DIRECTORIES"_s, PropertyType::IncludePaths },
780
0
    { "COMPILE_DEFINITIONS"_s, PropertyType::Strings },
781
0
    { "COMPILE_OPTIONS"_s, PropertyType::Strings },
782
0
    { "COMPILE_FEATURES"_s, PropertyType::Strings },
783
0
  };
784
0
  for (auto const& propEntry : exportedModuleProperties) {
785
0
    auto const propNameStr = std::string(propEntry.Name);
786
0
    cmValue prop = gte->Target->GetComputedProperty(
787
0
      propNameStr, *gte->Target->GetMakefile());
788
0
    if (!prop) {
789
0
      prop = gte->Target->GetProperty(propNameStr);
790
0
    }
791
0
    if (prop) {
792
0
      auto const exportedPropName =
793
0
        cmStrCat("IMPORTED_CXX_MODULES_", propEntry.Name);
794
0
      properties[exportedPropName] =
795
0
        cmGeneratorExpression::Preprocess(*prop, ctx);
796
0
      if (ctx == cmGeneratorExpression::InstallInterface &&
797
0
          PropertyTypeIsForPaths(propEntry.Type)) {
798
0
        this->ReplaceInstallPrefix(properties[exportedPropName]);
799
0
        this->AddImportPrefix(properties[exportedPropName]);
800
0
        if (propEntry.Type == PropertyType::IncludePaths &&
801
0
            !includesDestinationDirs.empty()) {
802
0
          if (!properties[exportedPropName].empty()) {
803
0
            properties[exportedPropName] += ';';
804
0
          }
805
0
          properties[exportedPropName] += includesDestinationDirs;
806
0
        }
807
0
      }
808
0
    }
809
0
  }
810
811
0
  cm::static_string_view const exportedLinkModuleProperties[] = {
812
0
    "LINK_LIBRARIES"_s,
813
0
  };
814
0
  for (auto const& propName : exportedLinkModuleProperties) {
815
0
    auto const propNameStr = std::string(propName);
816
0
    cmValue prop = gte->Target->GetComputedProperty(
817
0
      propNameStr, *gte->Target->GetMakefile());
818
0
    if (!prop) {
819
0
      prop = gte->Target->GetProperty(propNameStr);
820
0
    }
821
0
    if (prop) {
822
0
      auto const exportedPropName =
823
0
        cmStrCat("IMPORTED_CXX_MODULES_", propName);
824
0
      auto value = cmGeneratorExpression::Preprocess(*prop, ctx);
825
0
      this->ResolveTargetsInGeneratorExpressions(value, gte,
826
0
                                                 ReplaceFreeTargets);
827
0
      properties[exportedPropName] = value;
828
0
    }
829
0
  }
830
831
0
  return true;
832
0
}
833
834
bool cmExportFileGenerator::PopulateExportProperties(
835
  cmGeneratorTarget const* gte, ImportPropertyMap& properties,
836
  std::string& errorMessage) const
837
0
{
838
0
  auto const& targetProperties = gte->Target->GetDirectProperties();
839
0
  if (cmValue exportProperties =
840
0
        targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
841
0
    for (auto& prop : cmList{ *exportProperties }) {
842
      /* Black list reserved properties */
843
0
      if (cmHasLiteralPrefix(prop, "IMPORTED_") ||
844
0
          cmHasLiteralPrefix(prop, "INTERFACE_")) {
845
0
        std::ostringstream e;
846
0
        e << "Target \"" << gte->Target->GetName() << "\" contains property \""
847
0
          << prop << "\" in EXPORT_PROPERTIES but IMPORTED_* and INTERFACE_* "
848
0
          << "properties are reserved.";
849
0
        errorMessage = e.str();
850
0
        return false;
851
0
      }
852
0
      cmValue propertyValue = targetProperties.GetPropertyValue(prop);
853
0
      if (!propertyValue) {
854
        // Asked to export a property that isn't defined on the target. Do not
855
        // consider this an error, there's just nothing to export.
856
0
        continue;
857
0
      }
858
0
      std::string evaluatedValue = cmGeneratorExpression::Preprocess(
859
0
        *propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions);
860
0
      if (evaluatedValue != *propertyValue) {
861
0
        std::ostringstream e;
862
0
        e << "Target \"" << gte->Target->GetName() << "\" contains property \""
863
0
          << prop << "\" in EXPORT_PROPERTIES but this property contains a "
864
0
          << "generator expression. This is not allowed.";
865
0
        errorMessage = e.str();
866
0
        return false;
867
0
      }
868
0
      properties[prop] = *propertyValue;
869
0
    }
870
0
  }
871
0
  return true;
872
0
}