Coverage Report

Created: 2026-02-09 06:05

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