Coverage Report

Created: 2026-02-09 06:05

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