Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportCMakeConfigGenerator.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 "cmExportCMakeConfigGenerator.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <sstream>
8
#include <unordered_map>
9
#include <utility>
10
#include <vector>
11
12
#include <cm/optional>
13
#include <cm/string_view>
14
#include <cmext/string_view>
15
16
#include "cmExportSet.h"
17
#include "cmFileSetMetadata.h"
18
#include "cmFindPackageStack.h"
19
#include "cmGeneratedFileStream.h"
20
#include "cmGeneratorFileSet.h"
21
#include "cmGeneratorFileSets.h"
22
#include "cmGeneratorTarget.h"
23
#include "cmLocalGenerator.h"
24
#include "cmMessageType.h"
25
#include "cmOutputConverter.h"
26
#include "cmScriptGenerator.h"
27
#include "cmStringAlgorithms.h"
28
#include "cmSystemTools.h"
29
#include "cmTarget.h"
30
#include "cmTargetTypes.h"
31
#include "cmValue.h"
32
#include "cmVersionMacros.h"
33
34
struct cmLinkInterface;
35
36
static std::string cmExportFileGeneratorEscape(std::string const& str)
37
0
{
38
  // Escape a property value for writing into a .cmake file.
39
0
  std::string result = cmOutputConverter::EscapeForCMake(str);
40
  // Un-escape variable references generated by our own export code.
41
0
  cmSystemTools::ReplaceString(result, "\\${_IMPORT_PREFIX}",
42
0
                               "${_IMPORT_PREFIX}");
43
0
  cmSystemTools::ReplaceString(result, "\\${CMAKE_IMPORT_LIBRARY_SUFFIX}",
44
0
                               "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
45
0
  return result;
46
0
}
47
48
0
cmExportCMakeConfigGenerator::cmExportCMakeConfigGenerator() = default;
49
50
cm::string_view cmExportCMakeConfigGenerator::GetImportPrefixWithSlash() const
51
0
{
52
0
  return "${_IMPORT_PREFIX}/"_s;
53
0
}
54
55
bool cmExportCMakeConfigGenerator::GenerateImportFile(std::ostream& os)
56
0
{
57
0
  std::stringstream mainFileWithHeadersAndFootersBuffer;
58
59
  // Start with the import file header.
60
0
  this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer);
61
62
  // Create all the imported targets.
63
0
  std::stringstream mainFileBuffer;
64
0
  bool result = this->GenerateMainFile(mainFileBuffer);
65
66
  // Export find_dependency() calls. Must be done after GenerateMainFile(),
67
  // because that's when target dependencies are gathered, which we need for
68
  // the find_dependency() calls.
69
0
  if (!this->AppendMode && this->GetExportSet() &&
70
0
      this->ExportPackageDependencies) {
71
0
    this->SetRequiredCMakeVersion(3, 9, 0);
72
0
    this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer);
73
0
  }
74
75
  // Write cached import code.
76
0
  mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf();
77
78
  // End with the import file footer.
79
0
  this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer);
80
0
  this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer);
81
82
  // This has to be done last, after the minimum CMake version has been
83
  // determined.
84
0
  this->GeneratePolicyHeaderCode(os);
85
0
  os << mainFileWithHeadersAndFootersBuffer.rdbuf();
86
87
0
  return result;
88
0
}
89
90
void cmExportCMakeConfigGenerator::GenerateInterfaceProperties(
91
  cmGeneratorTarget const* target, std::ostream& os,
92
  ImportPropertyMap const& properties)
93
0
{
94
0
  if (!properties.empty()) {
95
0
    std::string targetName =
96
0
      cmStrCat(this->Namespace, target->GetExportName());
97
0
    os << "set_target_properties(" << targetName << " PROPERTIES\n";
98
0
    for (auto const& property : properties) {
99
0
      os << "  " << property.first << ' '
100
0
         << cmExportFileGeneratorEscape(property.second) << '\n';
101
0
    }
102
0
    os << ")\n\n";
103
0
  }
104
0
}
105
106
void cmExportCMakeConfigGenerator::SetImportLinkInterface(
107
  std::string const& config, std::string const& suffix,
108
  cmGeneratorExpression::PreprocessContext preprocessRule,
109
  cmGeneratorTarget const* target, ImportPropertyMap& properties)
110
0
{
111
  // Add the transitive link dependencies for this configuration.
112
0
  cmLinkInterface const* iface = target->GetLinkInterface(config, target);
113
0
  if (!iface) {
114
0
    return;
115
0
  }
116
117
0
  cmValue propContent;
118
119
0
  if (cmValue prop_suffixed =
120
0
        target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) {
121
0
    propContent = prop_suffixed;
122
0
  } else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
123
0
    propContent = prop;
124
0
  } else {
125
0
    return;
126
0
  }
127
128
0
  if (!this->ExportOld) {
129
0
    cmLocalGenerator* lg = target->GetLocalGenerator();
130
0
    lg->IssueMessage(
131
0
      MessageType::FATAL_ERROR,
132
0
      cmStrCat(
133
0
        "Target \"", target->GetName(),
134
0
        "\" has policy CMP0022 enabled, "
135
0
        "but also has old-style LINK_INTERFACE_LIBRARIES properties "
136
0
        "populated, but it was exported without the "
137
0
        "EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties"));
138
0
    return;
139
0
  }
140
141
0
  if (propContent->empty()) {
142
0
    properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix].clear();
143
0
    return;
144
0
  }
145
146
0
  std::string prepro =
147
0
    cmGeneratorExpression::Preprocess(*propContent, preprocessRule);
148
0
  if (!prepro.empty()) {
149
0
    this->ResolveTargetsInGeneratorExpressions(prepro, target,
150
0
                                               ReplaceFreeTargets);
151
0
    properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;
152
0
  }
153
0
}
154
155
void cmExportCMakeConfigGenerator::GeneratePolicyHeaderCode(std::ostream& os)
156
0
{
157
  // Protect that file against use with older CMake versions.
158
  /* clang-format off */
159
0
  os << "# Generated by CMake\n\n"
160
0
        "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n"
161
0
        "   message(FATAL_ERROR \"CMake >= "
162
0
     << this->RequiredCMakeVersionMajor << '.'
163
0
     << this->RequiredCMakeVersionMinor << '.'
164
0
     << this->RequiredCMakeVersionPatch << " required\")\n"
165
0
        "endif()\n"
166
0
        "if(CMAKE_VERSION VERSION_LESS \""
167
0
     << this->RequiredCMakeVersionMajor << '.'
168
0
     << this->RequiredCMakeVersionMinor << '.'
169
0
     << this->RequiredCMakeVersionPatch << "\")\n"
170
0
        "   message(FATAL_ERROR \"CMake >= "
171
0
     << this->RequiredCMakeVersionMajor << '.'
172
0
     << this->RequiredCMakeVersionMinor << '.'
173
0
     << this->RequiredCMakeVersionPatch << " required\")\n"
174
0
        "endif()\n";
175
  /* clang-format on */
176
177
  // Isolate the file policy level.
178
  // Support CMake versions as far back as the
179
  // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW
180
  // policy settings for up to CMake 4.3 (this upper limit may be reviewed
181
  // and increased from time to time). This reduces the opportunity for CMake
182
  // warnings when an older export file is later used with newer CMake
183
  // versions.
184
  /* clang-format off */
185
0
  os << "cmake_policy(PUSH)\n"
186
0
        "cmake_policy(VERSION "
187
0
     << this->RequiredCMakeVersionMajor << '.'
188
0
     << this->RequiredCMakeVersionMinor << '.'
189
0
     << this->RequiredCMakeVersionPatch;
190
  /* clang-format on */
191
0
  if (CMake_VERSION_ENCODE(
192
0
        this->RequiredCMakeVersionMajor, this->RequiredCMakeVersionMinor,
193
0
        this->RequiredCMakeVersionPatch) <= CMake_VERSION_ENCODE(4, 3, 0)) {
194
0
    os << "...4.3";
195
0
  }
196
0
  os << ")\n";
197
0
}
198
199
void cmExportCMakeConfigGenerator::GeneratePolicyFooterCode(std::ostream& os)
200
0
{
201
0
  os << "cmake_policy(POP)\n";
202
0
}
203
204
void cmExportCMakeConfigGenerator::GenerateImportHeaderCode(
205
  std::ostream& os, std::string const& config)
206
0
{
207
0
  os << "#----------------------------------------------------------------\n"
208
0
        "# Generated CMake target import file";
209
0
  if (!config.empty()) {
210
0
    os << " for configuration \"" << config << "\".\n";
211
0
  } else {
212
0
    os << ".\n";
213
0
  }
214
0
  os << "#----------------------------------------------------------------\n"
215
0
        "\n";
216
0
  this->GenerateImportVersionCode(os);
217
0
}
218
219
void cmExportCMakeConfigGenerator::GenerateImportFooterCode(std::ostream& os)
220
0
{
221
0
  os << "# Commands beyond this point should not need to know the version.\n"
222
0
        "set(CMAKE_IMPORT_FILE_VERSION)\n";
223
0
}
224
225
void cmExportCMakeConfigGenerator::GenerateImportVersionCode(std::ostream& os)
226
0
{
227
  // Store an import file format version.  This will let us change the
228
  // format later while still allowing old import files to work.
229
0
  os << "# Commands may need to know the format version.\n"
230
0
        "set(CMAKE_IMPORT_FILE_VERSION 1)\n"
231
0
        "\n";
232
0
}
233
234
void cmExportCMakeConfigGenerator::GenerateExpectedTargetsCode(
235
  std::ostream& os, std::string const& expectedTargets)
236
0
{
237
  /* clang-format off */
238
0
  os << "# Protect against multiple inclusion, which would fail when already "
239
0
        "imported targets are added once more.\n"
240
0
        "set(_cmake_targets_defined \"\")\n"
241
0
        "set(_cmake_targets_not_defined \"\")\n"
242
0
        "set(_cmake_expected_targets \"\")\n"
243
0
        "foreach(_cmake_expected_target IN ITEMS " << expectedTargets << ")\n"
244
0
        "  list(APPEND _cmake_expected_targets \"${_cmake_expected_target}\")\n"
245
0
        "  if(TARGET \"${_cmake_expected_target}\")\n"
246
0
        "    list(APPEND _cmake_targets_defined \"${_cmake_expected_target}\")\n"
247
0
        "  else()\n"
248
0
        "    list(APPEND _cmake_targets_not_defined \"${_cmake_expected_target}\")\n"
249
0
        "  endif()\n"
250
0
        "endforeach()\n"
251
0
        "unset(_cmake_expected_target)\n"
252
0
        "if(_cmake_targets_defined STREQUAL _cmake_expected_targets)\n"
253
0
        "  unset(_cmake_targets_defined)\n"
254
0
        "  unset(_cmake_targets_not_defined)\n"
255
0
        "  unset(_cmake_expected_targets)\n"
256
0
        "  unset(CMAKE_IMPORT_FILE_VERSION)\n"
257
0
        "  cmake_policy(POP)\n"
258
0
        "  return()\n"
259
0
        "endif()\n"
260
0
        "if(NOT _cmake_targets_defined STREQUAL \"\")\n"
261
0
        "  string(REPLACE \";\" \", \" _cmake_targets_defined_text \"${_cmake_targets_defined}\")\n"
262
0
        "  string(REPLACE \";\" \", \" _cmake_targets_not_defined_text \"${_cmake_targets_not_defined}\")\n"
263
0
        "  message(FATAL_ERROR \"Some (but not all) targets in this export "
264
0
        "set were already defined.\\nTargets Defined: ${_cmake_targets_defined_text}\\n"
265
0
        "Targets not yet defined: ${_cmake_targets_not_defined_text}\\n\")\n"
266
0
        "endif()\n"
267
0
        "unset(_cmake_targets_defined)\n"
268
0
        "unset(_cmake_targets_not_defined)\n"
269
0
        "unset(_cmake_expected_targets)\n"
270
0
        "\n\n";
271
  /* clang-format on */
272
0
}
273
274
void cmExportCMakeConfigGenerator::GenerateImportTargetCode(
275
  std::ostream& os, cmGeneratorTarget const* target, cm::TargetType targetType)
276
0
{
277
  // Construct the imported target name.
278
0
  std::string targetName = this->Namespace;
279
280
0
  targetName += target->GetExportName();
281
282
  // Create the imported target.
283
0
  os << "# Create imported target " << targetName << "\n";
284
0
  switch (targetType) {
285
0
    case cm::TargetType::EXECUTABLE:
286
0
      os << "add_executable(" << targetName << " IMPORTED)\n";
287
0
      break;
288
0
    case cm::TargetType::STATIC_LIBRARY:
289
0
      os << "add_library(" << targetName << " STATIC IMPORTED)\n";
290
0
      break;
291
0
    case cm::TargetType::SHARED_LIBRARY:
292
0
      os << "add_library(" << targetName << " SHARED IMPORTED)\n";
293
0
      break;
294
0
    case cm::TargetType::MODULE_LIBRARY:
295
0
      os << "add_library(" << targetName << " MODULE IMPORTED)\n";
296
0
      break;
297
0
    case cm::TargetType::UNKNOWN_LIBRARY:
298
0
      os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n";
299
0
      break;
300
0
    case cm::TargetType::OBJECT_LIBRARY:
301
0
      os << "add_library(" << targetName << " OBJECT IMPORTED)\n";
302
0
      break;
303
0
    case cm::TargetType::INTERFACE_LIBRARY:
304
0
      if (target->IsSymbolic()) {
305
0
        os << "if(CMAKE_VERSION VERSION_GREATER_EQUAL \"4.2.0\")\n"
306
0
              "  add_library("
307
0
           << targetName << " INTERFACE SYMBOLIC IMPORTED)\n"
308
0
           << "elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.2.0)\n"
309
0
           << "  add_library(" << targetName << " INTERFACE IMPORTED)\n"
310
0
           << "  set_target_properties(" << targetName
311
0
           << " PROPERTIES SYMBOLIC TRUE)\n"
312
0
           << "endif()\n";
313
0
      } else {
314
0
        os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
315
0
      }
316
0
      break;
317
0
    default: // should never happen
318
0
      break;
319
0
  }
320
321
  // Mark the imported executable if it has exports.
322
0
  if (target->IsExecutableWithExports() ||
323
0
      (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) {
324
0
    os << "set_property(TARGET " << targetName
325
0
       << " PROPERTY ENABLE_EXPORTS 1)\n";
326
0
  }
327
328
  // Mark the imported library if it is a framework.
329
0
  if (target->IsFrameworkOnApple()) {
330
0
    os << "set_property(TARGET " << targetName << " PROPERTY FRAMEWORK 1)\n";
331
0
  }
332
333
  // Mark the imported executable if it is an application bundle.
334
0
  if (target->IsAppBundleOnApple()) {
335
0
    os << "set_property(TARGET " << targetName
336
0
       << " PROPERTY MACOSX_BUNDLE 1)\n";
337
0
  }
338
339
0
  if (target->IsCFBundleOnApple()) {
340
0
    os << "set_property(TARGET " << targetName << " PROPERTY BUNDLE 1)\n";
341
0
  }
342
343
  // Mark the imported library if it is an AIX shared library archive.
344
0
  if (target->IsArchivedAIXSharedLibrary()) {
345
0
    os << "set_property(TARGET " << targetName
346
0
       << " PROPERTY AIX_SHARED_LIBRARY_ARCHIVE 1)\n";
347
0
  }
348
349
  // generate DEPRECATION
350
0
  if (target->IsDeprecated()) {
351
0
    os << "set_property(TARGET " << targetName << " PROPERTY DEPRECATION "
352
0
       << cmExportFileGeneratorEscape(target->GetDeprecation()) << ")\n";
353
0
  }
354
355
0
  if (target->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
356
0
    os << "set_property(TARGET " << targetName
357
0
       << " PROPERTY IMPORTED_NO_SYSTEM 1)\n";
358
0
  }
359
360
0
  if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) {
361
0
    os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n";
362
0
  }
363
364
0
  os << '\n';
365
0
}
366
367
void cmExportCMakeConfigGenerator::GenerateImportPropertyCode(
368
  std::ostream& os, std::string const& config, std::string const& suffix,
369
  cmGeneratorTarget const* target, ImportPropertyMap const& properties,
370
  std::string const& importedXcFrameworkLocation)
371
0
{
372
  // Construct the imported target name.
373
0
  std::string targetName = this->Namespace;
374
375
0
  targetName += target->GetExportName();
376
377
  // Set the import properties.
378
0
  os << "# Import target \"" << targetName << "\" for configuration \""
379
0
     << config
380
0
     << "\"\n"
381
0
        "set_property(TARGET "
382
0
     << targetName << " APPEND PROPERTY IMPORTED_CONFIGURATIONS ";
383
0
  if (!config.empty()) {
384
0
    os << cmSystemTools::UpperCase(config);
385
0
  } else {
386
0
    os << "NOCONFIG";
387
0
  }
388
0
  os << ")\n"
389
0
        "set_target_properties("
390
0
     << targetName << " PROPERTIES\n";
391
0
  std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix);
392
0
  for (auto const& property : properties) {
393
0
    if (importedXcFrameworkLocation.empty() ||
394
0
        property.first != importedLocationProp) {
395
0
      os << "  " << property.first << ' '
396
0
         << cmExportFileGeneratorEscape(property.second) << '\n';
397
0
    }
398
0
  }
399
0
  os << "  )\n";
400
0
  if (!importedXcFrameworkLocation.empty()) {
401
0
    auto importedLocationIt = properties.find(importedLocationProp);
402
0
    if (importedLocationIt != properties.end()) {
403
0
      os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY "
404
0
         << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
405
0
         << ")\n"
406
0
            "  set_property(TARGET "
407
0
         << targetName << " PROPERTY " << importedLocationProp << ' '
408
0
         << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
409
0
         << ")\nelse()\n  set_property(TARGET " << targetName << " PROPERTY "
410
0
         << importedLocationProp << ' '
411
0
         << cmExportFileGeneratorEscape(importedLocationIt->second)
412
0
         << ")\nendif()\n";
413
0
    }
414
0
  }
415
0
  os << '\n';
416
0
}
417
418
void cmExportCMakeConfigGenerator::GenerateFindDependencyCalls(
419
  std::ostream& os)
420
0
{
421
0
  os << "include(CMakeFindDependencyMacro)\n";
422
0
  std::map<std::string, cmExportSet::PackageDependency> packageDependencies;
423
0
  auto* exportSet = this->GetExportSet();
424
0
  if (exportSet) {
425
0
    packageDependencies = exportSet->GetPackageDependencies();
426
0
  }
427
428
0
  for (cmGeneratorTarget const* gt : this->ExternalTargets) {
429
0
    std::string findPackageName;
430
0
    auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME");
431
0
    cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack();
432
0
    if (!exportFindPackageName.IsEmpty()) {
433
0
      findPackageName = *exportFindPackageName;
434
0
    } else {
435
0
      if (!pkgStack.Empty()) {
436
0
        cmFindPackageCall const& fpc = pkgStack.Top();
437
0
        findPackageName = fpc.Name;
438
0
      }
439
0
    }
440
0
    if (!findPackageName.empty()) {
441
0
      auto& dep = packageDependencies[findPackageName];
442
0
      if (!pkgStack.Empty()) {
443
0
        dep.FindPackageIndex = pkgStack.Top().Index;
444
0
      }
445
0
      if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) {
446
0
        dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On;
447
0
      }
448
0
    }
449
0
  }
450
451
0
  std::vector<std::pair<std::string, cmExportSet::PackageDependency>>
452
0
    packageDependenciesSorted(packageDependencies.begin(),
453
0
                              packageDependencies.end());
454
0
  std::sort(
455
0
    packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
456
0
    [](std::pair<std::string, cmExportSet::PackageDependency> const& lhs,
457
0
       std::pair<std::string, cmExportSet::PackageDependency> const& rhs)
458
0
      -> bool {
459
0
      if (lhs.second.SpecifiedIndex) {
460
0
        if (rhs.second.SpecifiedIndex) {
461
0
          return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex;
462
0
        }
463
0
        assert(rhs.second.FindPackageIndex);
464
0
        return true;
465
0
      }
466
0
      assert(lhs.second.FindPackageIndex);
467
0
      if (rhs.second.SpecifiedIndex) {
468
0
        return false;
469
0
      }
470
0
      assert(rhs.second.FindPackageIndex);
471
0
      return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex;
472
0
    });
473
474
  // Unwinding is only valid in a find_package() context
475
0
  os << "if(DEFINED CMAKE_FIND_PACKAGE_NAME)\n"
476
0
     << "  set(_cmake_unwind_arg UNWIND_INCLUDE)\n"
477
0
     << "endif()\n\n";
478
479
0
  for (auto const& it : packageDependenciesSorted) {
480
0
    if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) {
481
0
      os << "__find_dependency_no_return(" << it.first;
482
0
      for (auto const& arg : it.second.ExtraArguments) {
483
0
        os << ' ' << cmScriptGenerator::Quote(arg);
484
0
      }
485
0
      os << " ${_cmake_unwind_arg})\n";
486
0
      os << "if(NOT " << it.first << "_FOUND)\n"
487
0
         << "  unset(_cmake_unwind_arg)\n"
488
0
         << "  cmake_policy(POP)\n"
489
0
         << "  return()\n"
490
0
         << "endif()\n\n";
491
0
    }
492
0
  }
493
494
0
  os << "unset(_cmake_unwind_arg)\n\n\n";
495
0
}
496
497
void cmExportCMakeConfigGenerator::GenerateMissingTargetsCheckCode(
498
  std::ostream& os)
499
0
{
500
0
  if (this->MissingTargets.empty()) {
501
0
    os << "# This file does not depend on other imported targets which have\n"
502
0
          "# been exported from the same project but in a separate "
503
0
          "export set.\n\n";
504
0
    return;
505
0
  }
506
0
  os << "# Make sure the targets which have been exported in some other\n"
507
0
        "# export set exist.\n"
508
0
        "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
509
0
        "foreach(_target ";
510
0
  std::set<std::string> emitted;
511
0
  for (std::string const& missingTarget : this->MissingTargets) {
512
0
    if (emitted.insert(missingTarget).second) {
513
0
      os << '"' << missingTarget << "\" ";
514
0
    }
515
0
  }
516
0
  os << ")\n"
517
0
        "  if(NOT TARGET \"${_target}\" )\n"
518
0
        "    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets \""
519
0
        "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets} ${_target}\")"
520
0
        "\n"
521
0
        "  endif()\n"
522
0
        "endforeach()\n"
523
0
        "\n"
524
0
        "if(DEFINED ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
525
0
        "  if(CMAKE_FIND_PACKAGE_NAME)\n"
526
0
        "    set( ${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)\n"
527
0
        "    set( ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "
528
0
        "\"The following imported targets are "
529
0
        "referenced, but are missing: "
530
0
        "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
531
0
        "  else()\n"
532
0
        "    message(FATAL_ERROR \"The following imported targets are "
533
0
        "referenced, but are missing: "
534
0
        "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
535
0
        "  endif()\n"
536
0
        "endif()\n"
537
0
        "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
538
0
        "\n";
539
0
}
540
541
void cmExportCMakeConfigGenerator::GenerateImportedFileCheckLoop(
542
  std::ostream& os)
543
0
{
544
  // Add code which verifies at cmake time that the file which is being
545
  // imported actually exists on disk. This should in theory always be theory
546
  // case, but still when packages are split into normal and development
547
  // packages this might get broken (e.g. the Config.cmake could be part of
548
  // the non-development package, something similar happened to me without
549
  // on SUSE with a mysql pkg-config file, which claimed everything is fine,
550
  // but the development package was not installed.).
551
0
  os << "# Loop over all imported files and verify that they actually exist\n"
552
0
        "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n"
553
0
        "  if(CMAKE_VERSION VERSION_LESS \"3.28\"\n"
554
0
        "      OR NOT DEFINED "
555
0
        "_cmake_import_check_xcframework_for_${_cmake_target}\n"
556
0
        "      OR NOT IS_DIRECTORY "
557
0
        "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n"
558
0
        "    foreach(_cmake_file IN LISTS "
559
0
        "\"_cmake_import_check_files_for_${_cmake_target}\")\n"
560
0
        "      if(NOT EXISTS \"${_cmake_file}\")\n"
561
0
        "        message(FATAL_ERROR \"The imported target "
562
0
        "\\\"${_cmake_target}\\\" references the file\n"
563
0
        "   \\\"${_cmake_file}\\\"\n"
564
0
        "but this file does not exist.  Possible reasons include:\n"
565
0
        "* The file was deleted, renamed, or moved to another location.\n"
566
0
        "* An install or uninstall procedure did not complete successfully.\n"
567
0
        "* The installation package was faulty and contained\n"
568
0
        "   \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n"
569
0
        "but not all the files it references.\n"
570
0
        "\")\n"
571
0
        "      endif()\n"
572
0
        "    endforeach()\n"
573
0
        "  endif()\n"
574
0
        "  unset(_cmake_file)\n"
575
0
        "  unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n"
576
0
        "endforeach()\n"
577
0
        "unset(_cmake_target)\n"
578
0
        "unset(_cmake_import_check_targets)\n"
579
0
        "\n";
580
0
}
581
582
void cmExportCMakeConfigGenerator::GenerateImportedFileChecksCode(
583
  std::ostream& os, cmGeneratorTarget const* target,
584
  ImportPropertyMap const& properties,
585
  std::set<std::string> const& importedLocations,
586
  std::string const& importedXcFrameworkLocation)
587
0
{
588
  // Construct the imported target name.
589
0
  std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
590
591
0
  os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n";
592
0
  if (!importedXcFrameworkLocation.empty()) {
593
0
    os << "set(_cmake_import_check_xcframework_for_" << targetName << ' '
594
0
       << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n";
595
0
  }
596
0
  os << "list(APPEND _cmake_import_check_files_for_" << targetName << ' ';
597
598
0
  for (std::string const& li : importedLocations) {
599
0
    auto pi = properties.find(li);
600
0
    if (pi != properties.end()) {
601
0
      os << cmExportFileGeneratorEscape(pi->second) << ' ';
602
0
    }
603
0
  }
604
605
0
  os << ")\n\n";
606
0
}
607
608
namespace {
609
struct FileSetInformation
610
{
611
  cm::string_view Type;
612
  cm::string_view CMakeVersion;
613
};
614
615
cm::optional<FileSetInformation> GetFileSetInformation(cm::string_view type)
616
0
{
617
0
  static std::unordered_map<cm::string_view, FileSetInformation>
618
0
    fileSetsInformation{
619
0
      { cm::FileSetMetadata::HEADERS,
620
0
        { cm::FileSetMetadata::HEADERS, "3.23.0"_s } },
621
0
      { cm::FileSetMetadata::SOURCES,
622
0
        { cm::FileSetMetadata::SOURCES, "4.4.0"_s } },
623
0
      { cm::FileSetMetadata::CXX_MODULES,
624
0
        { cm::FileSetMetadata::CXX_MODULES, "3.28.0"_s } },
625
0
    };
626
627
0
  auto fsInfo = fileSetsInformation.find(type);
628
0
  if (fsInfo != fileSetsInformation.end()) {
629
0
    return fsInfo->second;
630
0
  }
631
0
  return {};
632
0
}
633
}
634
635
void cmExportCMakeConfigGenerator::GenerateTargetFileSets(
636
  cmGeneratorTarget* gte, std::ostream& os,
637
  ImportFileSetPropertyMap const& properties, cmTargetExport const* te)
638
0
{
639
0
  cmGeneratorFileSets const* gfs = gte->GetGeneratorFileSets();
640
0
  auto const& types = gfs->GetInterfaceFileSetTypes();
641
642
0
  if (!types.empty()) {
643
0
    std::string targetName = cmStrCat(this->Namespace, gte->GetExportName());
644
0
    for (auto const& type : types) {
645
0
      auto fsInfo = GetFileSetInformation(type);
646
0
      bool const requiresFileSetLanguage = fsInfo &&
647
0
        std::any_of(gfs->GetInterfaceFileSets(type).begin(),
648
0
                    gfs->GetInterfaceFileSets(type).end(),
649
0
                    [](cmGeneratorFileSet const* fileSet) {
650
0
                      return fileSet->GetName() == cm::FileSetMetadata::SYCL ||
651
0
                        fileSet->GetName() ==
652
0
                        cm::FileSetMetadata::SYCL_HEADERS ||
653
0
                        !fileSet->GetProperty("LANGUAGE").IsEmpty();
654
0
                    });
655
0
      if (requiresFileSetLanguage) {
656
0
        fsInfo->CMakeVersion = "4.5.0"_s;
657
0
      }
658
0
      if (fsInfo) {
659
0
        os << "if(NOT CMAKE_VERSION VERSION_LESS \"" << fsInfo->CMakeVersion
660
0
           << "\")\n  ";
661
0
      }
662
0
      os << "target_sources(" << targetName << '\n';
663
664
0
      for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) {
665
0
        os << "    INTERFACE"
666
0
           << "\n      FILE_SET "
667
0
           << cmScriptGenerator::Quote(fileSet->GetName()) << "\n      TYPE "
668
0
           << cmScriptGenerator::Quote(type) << "\n      BASE_DIRS "
669
0
           << this->GetFileSetDirectories(gte, fileSet, te) << "\n      FILES "
670
0
           << this->GetFileSetFiles(gte, fileSet, te) << '\n';
671
0
      }
672
0
      os << "  )\n";
673
0
      for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) {
674
0
        auto fsProperties = properties.find(fileSet->GetName());
675
0
        if (fsProperties != properties.end()) {
676
0
          for (auto const& property : fsProperties->second) {
677
0
            os << "\n  set_property(FILE_SET "
678
0
               << cmScriptGenerator::Quote(fileSet->GetName()) << " TARGET "
679
0
               << targetName << "\n    PROPERTY " << property.first << ' '
680
0
               << cmExportFileGeneratorEscape(property.second) << "\n  )";
681
0
          }
682
0
        }
683
0
      }
684
685
0
      if (fsInfo) {
686
0
        if (type == cm::FileSetMetadata::HEADERS) {
687
0
          if (requiresFileSetLanguage) {
688
0
            os << "\nelse()\n  message(FATAL_ERROR \"The target '"
689
0
               << targetName
690
0
               << "' cannot be imported because it relies on the 'LANGUAGE' "
691
0
                  "property of a 'HEADERS' file set which is not supported "
692
0
                  "by this CMake version.\")";
693
0
          } else {
694
0
            os << "\nelse()\n  set_property(TARGET " << targetName
695
0
               << "\n    APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES";
696
0
            for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) {
697
0
              os << "\n      "
698
0
                 << this->GetFileSetDirectories(gte, fileSet, te);
699
0
            }
700
0
            os << "\n  )";
701
0
          }
702
0
        } else if (type == cm::FileSetMetadata::SOURCES) {
703
0
          os << "\nelse()\n  message(FATAL_ERROR \"The target '" << targetName
704
0
             << "' cannot be imported because it relies on 'FILE_SET' of type "
705
0
                "'SOURCES' which is not supported by this CMake version.\")";
706
0
        } else if (type == cm::FileSetMetadata::CXX_MODULES) {
707
0
          os << "\nelse()\n  message(AUTHOR_WARNING \"The target '"
708
0
             << targetName
709
0
             << "' cannot be imported properly because it relies on C++ "
710
0
                "modules which are not supported by this CMake version.\")";
711
0
        }
712
0
        os << "\nendif()\n\n";
713
0
      } else {
714
0
        os << ")\n\n";
715
0
      }
716
0
    }
717
0
  }
718
0
}
719
720
std::string cmExportCMakeConfigGenerator::GetCxxModuleFile(
721
  std::string const& name) const
722
0
{
723
0
  auto const& cxxModuleDirname = this->GetCxxModulesDirectory();
724
0
  if (cxxModuleDirname.empty()) {
725
0
    return {};
726
0
  }
727
728
0
  return cmStrCat(cmSystemTools::GetFilenamePath(this->MainImportFile), '/',
729
0
                  cxxModuleDirname, "/cxx-modules-", name, ".cmake");
730
0
}
731
732
void cmExportCMakeConfigGenerator::GenerateCxxModuleInformation(
733
  std::string const& name, std::ostream& os)
734
0
{
735
0
  auto const cxx_module_dirname = this->GetCxxModulesDirectory();
736
0
  if (cxx_module_dirname.empty()) {
737
0
    return;
738
0
  }
739
740
  // Write the include.
741
0
  os << "# Include C++ module properties\n"
742
0
        "include(\"${CMAKE_CURRENT_LIST_DIR}/"
743
0
     << cxx_module_dirname << "/cxx-modules-" << name << ".cmake\")\n\n";
744
745
  // Include all configuration-specific include files.
746
0
  cmGeneratedFileStream ap(this->GetCxxModuleFile(name), true);
747
0
  ap.SetCopyIfDifferent(true);
748
749
0
  this->GenerateCxxModuleConfigInformation(name, ap);
750
0
}
751
752
void cmExportCMakeConfigGenerator::SetRequiredCMakeVersion(unsigned int major,
753
                                                           unsigned int minor,
754
                                                           unsigned int patch)
755
0
{
756
0
  if (CMake_VERSION_ENCODE(major, minor, patch) >
757
0
      CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor,
758
0
                           this->RequiredCMakeVersionMinor,
759
0
                           this->RequiredCMakeVersionPatch)) {
760
0
    this->RequiredCMakeVersionMajor = major;
761
0
    this->RequiredCMakeVersionMinor = minor;
762
0
    this->RequiredCMakeVersionPatch = patch;
763
0
  }
764
0
}