Coverage Report

Created: 2026-03-12 06:35

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