Coverage Report

Created: 2026-04-29 07:01

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