Coverage Report

Created: 2026-04-29 07:01

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