resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmExportInstallFileGenerator.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmExportInstallFileGenerator.h"
4
5 #include <algorithm>
6 #include <memory>
7 #include <sstream>
8 #include <utility>
9
10 #include <cm/string_view>
11 #include <cmext/string_view>
12
13 #include "cmExportSet.h"
14 #include "cmFileSet.h"
15 #include "cmGeneratedFileStream.h"
16 #include "cmGeneratorExpression.h"
17 #include "cmGeneratorTarget.h"
18 #include "cmGlobalGenerator.h"
19 #include "cmInstallExportGenerator.h"
20 #include "cmInstallFileSetGenerator.h"
21 #include "cmInstallTargetGenerator.h"
22 #include "cmLocalGenerator.h"
23 #include "cmMakefile.h"
24 #include "cmMessageType.h"
25 #include "cmOutputConverter.h"
26 #include "cmPolicies.h"
27 #include "cmStateTypes.h"
28 #include "cmStringAlgorithms.h"
29 #include "cmSystemTools.h"
30 #include "cmTarget.h"
31 #include "cmTargetExport.h"
32 #include "cmValue.h"
33
34 cmExportInstallFileGenerator::cmExportInstallFileGenerator(
35   cmInstallExportGenerator* iegen)
36   : IEGen(iegen)
37 {
38 }
39
40 std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
41 {
42   std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
43   return glob;
44 }
45
46 bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
47 {
48   std::vector<cmTargetExport*> allTargets;
49   {
50     std::string expectedTargets;
51     std::string sep;
52     for (std::unique_ptr<cmTargetExport> const& te :
53          this->IEGen->GetExportSet()->GetTargetExports()) {
54       if (te->NamelinkOnly) {
55         continue;
56       }
57       expectedTargets += sep + this->Namespace + te->Target->GetExportName();
58       sep = " ";
59       if (this->ExportedTargets.insert(te->Target).second) {
60         allTargets.push_back(te.get());
61       } else {
62         std::ostringstream e;
63         e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
64           << "\" ...) "
65           << "includes target \"" << te->Target->GetName()
66           << "\" more than once in the export set.";
67         cmSystemTools::Error(e.str());
68         return false;
69       }
70     }
71
72     this->GenerateExpectedTargetsCode(os, expectedTargets);
73   }
74
75   // Compute the relative import prefix for the file
76   this->GenerateImportPrefix(os);
77
78   bool require2_8_12 = false;
79   bool require3_0_0 = false;
80   bool require3_1_0 = false;
81   bool requiresConfigFiles = false;
82   // Create all the imported targets.
83   for (cmTargetExport* te : allTargets) {
84     cmGeneratorTarget* gt = te->Target;
85     cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
86
87     requiresConfigFiles =
88       requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY;
89
90     this->GenerateImportTargetCode(os, gt, targetType);
91
92     ImportPropertyMap properties;
93
94     this->PopulateIncludeDirectoriesInterface(
95       gt, cmGeneratorExpression::InstallInterface, properties, *te);
96     this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface,
97                                    properties);
98     this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
99                                     cmGeneratorExpression::InstallInterface,
100                                     properties);
101     this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt,
102                                     cmGeneratorExpression::InstallInterface,
103                                     properties);
104     this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt,
105                                     cmGeneratorExpression::InstallInterface,
106                                     properties);
107     this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt,
108                                     cmGeneratorExpression::InstallInterface,
109                                     properties);
110     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
111                                     cmGeneratorExpression::InstallInterface,
112                                     properties);
113     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
114                                     cmGeneratorExpression::InstallInterface,
115                                     properties);
116     this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
117                                     cmGeneratorExpression::InstallInterface,
118                                     properties);
119     this->PopulateLinkDirectoriesInterface(
120       gt, cmGeneratorExpression::InstallInterface, properties);
121     this->PopulateLinkDependsInterface(
122       gt, cmGeneratorExpression::InstallInterface, properties);
123
124     std::string errorMessage;
125     if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
126       cmSystemTools::Error(errorMessage);
127       return false;
128     }
129
130     const bool newCMP0022Behavior =
131       gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
132       gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
133     if (newCMP0022Behavior) {
134       if (this->PopulateInterfaceLinkLibrariesProperty(
135             gt, cmGeneratorExpression::InstallInterface, properties) &&
136           !this->ExportOld) {
137         require2_8_12 = true;
138       }
139     }
140     if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
141       require3_0_0 = true;
142     }
143     if (gt->GetProperty("INTERFACE_SOURCES")) {
144       // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
145       // can consume them.
146       require3_1_0 = true;
147     }
148
149     this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
150                                     properties);
151
152     this->PopulateCompatibleInterfaceProperties(gt, properties);
153
154     this->GenerateInterfaceProperties(gt, os, properties);
155
156     this->GenerateTargetFileSets(gt, os, te);
157   }
158
159   if (require3_1_0) {
160     this->GenerateRequiredCMakeVersion(os, "3.1.0");
161   } else if (require3_0_0) {
162     this->GenerateRequiredCMakeVersion(os, "3.0.0");
163   } else if (require2_8_12) {
164     this->GenerateRequiredCMakeVersion(os, "2.8.12");
165   }
166
167   this->LoadConfigFiles(os);
168
169   bool result = true;
170
171   this->GenerateCxxModuleInformation(os);
172   if (requiresConfigFiles) {
173     for (std::string const& c : this->Configurations) {
174       if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) {
175         result = false;
176       }
177     }
178   }
179
180   this->CleanupTemporaryVariables(os);
181   this->GenerateImportedFileCheckLoop(os);
182
183   // Generate an import file for each configuration.
184   // Don't do this if we only export INTERFACE_LIBRARY targets.
185   if (requiresConfigFiles) {
186     for (std::string const& c : this->Configurations) {
187       if (!this->GenerateImportFileConfig(c)) {
188         result = false;
189       }
190     }
191   }
192
193   this->GenerateMissingTargetsCheckCode(os);
194
195   return result;
196 }
197
198 void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os)
199 {
200   // Set an _IMPORT_PREFIX variable for import location properties
201   // to reference if they are relative to the install prefix.
202   std::string installPrefix =
203     this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
204       "CMAKE_INSTALL_PREFIX");
205   std::string const& expDest = this->IEGen->GetDestination();
206   if (cmSystemTools::FileIsFullPath(expDest)) {
207     // The export file is being installed to an absolute path so the
208     // package is not relocatable.  Use the configured install prefix.
209     /* clang-format off */
210     os <<
211       "# The installation prefix configured by this project.\n"
212       "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
213       "\n";
214     /* clang-format on */
215   } else {
216     // Add code to compute the installation prefix relative to the
217     // import file location.
218     std::string absDest = installPrefix + "/" + expDest;
219     std::string absDestS = absDest + "/";
220     os << "# Compute the installation prefix relative to this file.\n"
221        << "get_filename_component(_IMPORT_PREFIX"
222        << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
223     if (cmHasLiteralPrefix(absDestS, "/lib/") ||
224         cmHasLiteralPrefix(absDestS, "/lib64/") ||
225         cmHasLiteralPrefix(absDestS, "/libx32/") ||
226         cmHasLiteralPrefix(absDestS, "/usr/lib/") ||
227         cmHasLiteralPrefix(absDestS, "/usr/lib64/") ||
228         cmHasLiteralPrefix(absDestS, "/usr/libx32/")) {
229       // Handle "/usr move" symlinks created by some Linux distros.
230       /* clang-format off */
231       os <<
232         "# Use original install prefix when loaded through a\n"
233         "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
234         "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
235         "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
236         "if(_realCurr STREQUAL _realOrig)\n"
237         "  set(_IMPORT_PREFIX \"" << absDest << "\")\n"
238         "endif()\n"
239         "unset(_realOrig)\n"
240         "unset(_realCurr)\n";
241       /* clang-format on */
242     }
243     std::string dest = expDest;
244     while (!dest.empty()) {
245       os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
246             "PATH)\n";
247       dest = cmSystemTools::GetFilenamePath(dest);
248     }
249     os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
250        << "  set(_IMPORT_PREFIX \"\")\n"
251        << "endif()\n"
252        << "\n";
253   }
254 }
255
256 void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os)
257 {
258   /* clang-format off */
259   os << "# Cleanup temporary variables.\n"
260      << "set(_IMPORT_PREFIX)\n"
261      << "\n";
262   /* clang-format on */
263 }
264
265 void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os)
266 {
267   // Now load per-configuration properties for them.
268   /* clang-format off */
269   os << "# Load information for each installed configuration.\n"
270      << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/"
271      << this->GetConfigImportFileGlob() << "\")\n"
272      << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n"
273      << "  include(\"${_cmake_config_file}\")\n"
274      << "endforeach()\n"
275      << "unset(_cmake_config_file)\n"
276      << "unset(_cmake_config_files)\n"
277      << "\n";
278   /* clang-format on */
279 }
280
281 void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input)
282 {
283   cmGeneratorExpression::ReplaceInstallPrefix(input, "${_IMPORT_PREFIX}");
284 }
285
286 bool cmExportInstallFileGenerator::GenerateImportFileConfig(
287   const std::string& config)
288 {
289   // Skip configurations not enabled for this export.
290   if (!this->IEGen->InstallsForConfig(config)) {
291     return true;
292   }
293
294   // Construct the name of the file to generate.
295   std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-');
296   if (!config.empty()) {
297     fileName += cmSystemTools::LowerCase(config);
298   } else {
299     fileName += "noconfig";
300   }
301   fileName += this->FileExt;
302
303   // Open the output file to generate it.
304   cmGeneratedFileStream exportFileStream(fileName, true);
305   if (!exportFileStream) {
306     std::string se = cmSystemTools::GetLastSystemError();
307     std::ostringstream e;
308     e << "cannot write to file \"" << fileName << "\": " << se;
309     cmSystemTools::Error(e.str());
310     return false;
311   }
312   exportFileStream.SetCopyIfDifferent(true);
313   std::ostream& os = exportFileStream;
314
315   // Start with the import file header.
316   this->GenerateImportHeaderCode(os, config);
317
318   // Generate the per-config target information.
319   this->GenerateImportConfig(os, config);
320
321   // End with the import file footer.
322   this->GenerateImportFooterCode(os);
323
324   // Record this per-config import file.
325   this->ConfigImportFiles[config] = fileName;
326
327   return true;
328 }
329
330 void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
331   std::ostream& os, const std::string& config, std::string const& suffix)
332 {
333   // Add each target in the set to the export.
334   for (std::unique_ptr<cmTargetExport> const& te :
335        this->IEGen->GetExportSet()->GetTargetExports()) {
336     // Collect import properties for this target.
337     if (this->GetExportTargetType(te.get()) ==
338         cmStateEnums::INTERFACE_LIBRARY) {
339       continue;
340     }
341
342     ImportPropertyMap properties;
343     std::set<std::string> importedLocations;
344
345     this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
346                                     properties, importedLocations);
347     this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
348                                     properties, importedLocations);
349     this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator,
350                                     properties, importedLocations);
351     this->SetImportLocationProperty(config, suffix, te->ObjectsGenerator,
352                                     properties, importedLocations);
353     this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
354                                     properties, importedLocations);
355     this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
356                                     properties, importedLocations);
357
358     // If any file location was set for the target add it to the
359     // import file.
360     if (!properties.empty()) {
361       // Get the rest of the target details.
362       cmGeneratorTarget* gtgt = te->Target;
363       this->SetImportDetailProperties(config, suffix, gtgt, properties);
364
365       this->SetImportLinkInterface(config, suffix,
366                                    cmGeneratorExpression::InstallInterface,
367                                    gtgt, properties);
368
369       // TODO: PUBLIC_HEADER_LOCATION
370       // This should wait until the build feature propagation stuff
371       // is done.  Then this can be a propagated include directory.
372       // this->GenerateImportProperty(config, te->HeaderGenerator,
373       //                              properties);
374
375       // Generate code in the export file.
376       this->GenerateImportPropertyCode(os, config, gtgt, properties);
377       this->GenerateImportedFileChecksCode(os, gtgt, properties,
378                                            importedLocations);
379     }
380   }
381 }
382
383 void cmExportInstallFileGenerator::SetImportLocationProperty(
384   const std::string& config, std::string const& suffix,
385   cmInstallTargetGenerator* itgen, ImportPropertyMap& properties,
386   std::set<std::string>& importedLocations)
387 {
388   // Skip rules that do not match this configuration.
389   if (!(itgen && itgen->InstallsForConfig(config))) {
390     return;
391   }
392
393   // Get the target to be installed.
394   cmGeneratorTarget* target = itgen->GetTarget();
395
396   // Construct the installed location of the target.
397   std::string dest = itgen->GetDestination(config);
398   std::string value;
399   if (!cmSystemTools::FileIsFullPath(dest)) {
400     // The target is installed relative to the installation prefix.
401     value = "${_IMPORT_PREFIX}/";
402   }
403   value += dest;
404   value += "/";
405
406   if (itgen->IsImportLibrary()) {
407     // Construct the property name.
408     std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
409
410     // Append the installed file name.
411     value += cmInstallTargetGenerator::GetInstallFilename(
412       target, config, cmInstallTargetGenerator::NameImplib);
413
414     // Store the property.
415     properties[prop] = value;
416     importedLocations.insert(prop);
417   } else if (itgen->GetTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
418     // Construct the property name.
419     std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
420
421     // Compute all the object files inside this target and setup
422     // IMPORTED_OBJECTS as a list of object files
423     std::vector<std::string> objects;
424     itgen->GetInstallObjectNames(config, objects);
425     for (std::string& obj : objects) {
426       obj = cmStrCat(value, obj);
427     }
428
429     // Store the property.
430     properties[prop] = cmJoin(objects, ";");
431     importedLocations.insert(prop);
432   } else {
433     // Construct the property name.
434     std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
435
436     // Append the installed file name.
437     if (target->IsAppBundleOnApple()) {
438       value += cmInstallTargetGenerator::GetInstallFilename(target, config);
439       value += ".app/";
440       if (!target->Makefile->PlatformIsAppleEmbedded()) {
441         value += "Contents/MacOS/";
442       }
443       value += cmInstallTargetGenerator::GetInstallFilename(target, config);
444     } else {
445       value += cmInstallTargetGenerator::GetInstallFilename(
446         target, config, cmInstallTargetGenerator::NameReal);
447     }
448
449     // Store the property.
450     properties[prop] = value;
451     importedLocations.insert(prop);
452   }
453 }
454
455 cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType(
456   cmTargetExport const* targetExport) const
457 {
458   cmStateEnums::TargetType targetType = targetExport->Target->GetType();
459   // An OBJECT library installed with no OBJECTS DESTINATION
460   // is transformed to an INTERFACE library.
461   if (targetType == cmStateEnums::OBJECT_LIBRARY &&
462       targetExport->ObjectsGenerator == nullptr) {
463     targetType = cmStateEnums::INTERFACE_LIBRARY;
464   }
465   return targetType;
466 }
467
468 void cmExportInstallFileGenerator::HandleMissingTarget(
469   std::string& link_libs, cmGeneratorTarget const* depender,
470   cmGeneratorTarget* dependee)
471 {
472   const std::string name = dependee->GetName();
473   cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator();
474   auto exportInfo = this->FindNamespaces(gg, name);
475   std::vector<std::string> const& exportFiles = exportInfo.first;
476   if (exportFiles.size() == 1) {
477     std::string missingTarget = exportInfo.second;
478
479     missingTarget += dependee->GetExportName();
480     link_libs += missingTarget;
481     this->MissingTargets.emplace_back(std::move(missingTarget));
482   } else {
483     // All exported targets should be known here and should be unique.
484     // This is probably user-error.
485     this->ComplainAboutMissingTarget(depender, dependee, exportFiles);
486   }
487 }
488
489 std::pair<std::vector<std::string>, std::string>
490 cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg,
491                                              const std::string& name)
492 {
493   std::vector<std::string> exportFiles;
494   std::string ns;
495   const cmExportSetMap& exportSets = gg->GetExportSets();
496
497   for (auto const& expIt : exportSets) {
498     const cmExportSet& exportSet = expIt.second;
499
500     bool containsTarget = false;
501     for (auto const& target : exportSet.GetTargetExports()) {
502       if (name == target->TargetName) {
503         containsTarget = true;
504         break;
505       }
506     }
507
508     if (containsTarget) {
509       std::vector<cmInstallExportGenerator const*> const* installs =
510         exportSet.GetInstallations();
511       for (cmInstallExportGenerator const* install : *installs) {
512         exportFiles.push_back(install->GetDestinationFile());
513         ns = install->GetNamespace();
514       }
515     }
516   }
517
518   return { exportFiles, ns };
519 }
520
521 void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
522   cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
523   std::vector<std::string> const& exportFiles)
524 {
525   std::ostringstream e;
526   e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
527     << "\" ...) "
528     << "includes target \"" << depender->GetName()
529     << "\" which requires target \"" << dependee->GetName() << "\" ";
530   if (exportFiles.empty()) {
531     e << "that is not in any export set.";
532   } else {
533     e << "that is not in this export set, but in multiple other export sets: "
534       << cmJoin(exportFiles, ", ") << ".\n";
535     e << "An exported target cannot depend upon another target which is "
536          "exported multiple times. Consider consolidating the exports of the "
537          "\""
538       << dependee->GetName() << "\" target to a single export.";
539   }
540   cmSystemTools::Error(e.str());
541 }
542
543 std::string cmExportInstallFileGenerator::InstallNameDir(
544   cmGeneratorTarget const* target, const std::string& config)
545 {
546   std::string install_name_dir;
547
548   cmMakefile* mf = target->Target->GetMakefile();
549   if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
550     install_name_dir =
551       target->GetInstallNameDirForInstallTree(config, "${_IMPORT_PREFIX}");
552   }
553
554   return install_name_dir;
555 }
556
557 namespace {
558 bool EntryIsContextSensitive(
559   const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
560 {
561   return cge->GetHadContextSensitiveCondition();
562 }
563 }
564
565 std::string cmExportInstallFileGenerator::GetFileSetDirectories(
566   cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
567 {
568   std::vector<std::string> resultVector;
569
570   auto configs =
571     gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
572
573   cmGeneratorExpression ge;
574   auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
575
576   for (auto const& config : configs) {
577     auto dest = cmStrCat("${_IMPORT_PREFIX}/",
578                          cmOutputConverter::EscapeForCMake(
579                            cge->Evaluate(gte->LocalGenerator, config, gte),
580                            cmOutputConverter::WrapQuotes::NoWrap));
581
582     auto const& type = fileSet->GetType();
583     // C++ modules do not support interface file sets which are dependent upon
584     // the configuration.
585     if (cge->GetHadContextSensitiveCondition() &&
586         (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
587       auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
588       std::ostringstream e;
589       e << "The \"" << gte->GetName() << "\" target's interface file set \""
590         << fileSet->GetName() << "\" of type \"" << type
591         << "\" contains context-sensitive base file entries which is not "
592            "supported.";
593       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
594       return std::string{};
595     }
596
597     if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
598       resultVector.push_back(
599         cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
600     } else {
601       resultVector.push_back(cmStrCat('"', dest, '"'));
602       break;
603     }
604   }
605
606   return cmJoin(resultVector, " ");
607 }
608
609 std::string cmExportInstallFileGenerator::GetFileSetFiles(
610   cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
611 {
612   std::vector<std::string> resultVector;
613
614   auto configs =
615     gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
616
617   auto fileEntries = fileSet->CompileFileEntries();
618   auto directoryEntries = fileSet->CompileDirectoryEntries();
619
620   cmGeneratorExpression destGe;
621   auto destCge =
622     destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
623
624   for (auto const& config : configs) {
625     auto directories = fileSet->EvaluateDirectoryEntries(
626       directoryEntries, gte->LocalGenerator, config, gte);
627
628     std::map<std::string, std::vector<std::string>> files;
629     for (auto const& entry : fileEntries) {
630       fileSet->EvaluateFileEntry(directories, files, entry,
631                                  gte->LocalGenerator, config, gte);
632     }
633     auto dest = cmStrCat("${_IMPORT_PREFIX}/",
634                          cmOutputConverter::EscapeForCMake(
635                            destCge->Evaluate(gte->LocalGenerator, config, gte),
636                            cmOutputConverter::WrapQuotes::NoWrap),
637                          '/');
638
639     bool const contextSensitive = destCge->GetHadContextSensitiveCondition() ||
640       std::any_of(directoryEntries.begin(), directoryEntries.end(),
641                   EntryIsContextSensitive) ||
642       std::any_of(fileEntries.begin(), fileEntries.end(),
643                   EntryIsContextSensitive);
644
645     auto const& type = fileSet->GetType();
646     // C++ modules do not support interface file sets which are dependent upon
647     // the configuration.
648     if (contextSensitive &&
649         (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
650       auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
651       std::ostringstream e;
652       e << "The \"" << gte->GetName() << "\" target's interface file set \""
653         << fileSet->GetName() << "\" of type \"" << type
654         << "\" contains context-sensitive base file entries which is not "
655            "supported.";
656       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
657       return std::string{};
658     }
659
660     for (auto const& it : files) {
661       auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
662       for (auto const& filename : it.second) {
663         auto relFile =
664           cmStrCat(prefix, cmSystemTools::GetFilenameName(filename));
665         auto escapedFile =
666           cmStrCat(dest,
667                    cmOutputConverter::EscapeForCMake(
668                      relFile, cmOutputConverter::WrapQuotes::NoWrap));
669         if (contextSensitive && configs.size() != 1) {
670           resultVector.push_back(
671             cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
672         } else {
673           resultVector.push_back(cmStrCat('"', escapedFile, '"'));
674         }
675       }
676     }
677
678     if (!(contextSensitive && configs.size() != 1)) {
679       break;
680     }
681   }
682
683   return cmJoin(resultVector, " ");
684 }
685
686 std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const
687 {
688   return IEGen->GetCxxModuleDirectory();
689 }
690
691 void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
692   std::ostream& os) const
693 {
694   // Now load per-configuration properties for them.
695   /* clang-format off */
696   os << "# Load information for each installed configuration.\n"
697         "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-*.cmake\")\n"
698         "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
699         "  include(\"${_cmake_cxx_module_include}\")\n"
700         "endforeach()\n"
701         "unset(_cmake_cxx_module_include)\n"
702         "unset(_cmake_cxx_module_includes)\n";
703   /* clang-format on */
704 }
705
706 bool cmExportInstallFileGenerator::
707   GenerateImportCxxModuleConfigTargetInclusion(std::string const& config)
708 {
709   auto cxx_modules_dirname = this->GetCxxModulesDirectory();
710   if (cxx_modules_dirname.empty()) {
711     return true;
712   }
713
714   std::string filename_config = config;
715   if (filename_config.empty()) {
716     filename_config = "noconfig";
717   }
718
719   std::string const dest =
720     cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
721   std::string fileName =
722     cmStrCat(dest, "cxx-modules-", filename_config, ".cmake");
723
724   cmGeneratedFileStream os(fileName, true);
725   if (!os) {
726     std::string se = cmSystemTools::GetLastSystemError();
727     std::ostringstream e;
728     e << "cannot write to file \"" << fileName << "\": " << se;
729     cmSystemTools::Error(e.str());
730     return false;
731   }
732   os.SetCopyIfDifferent(true);
733
734   // Record this per-config import file.
735   this->ConfigCxxModuleFiles[config] = fileName;
736
737   auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
738   for (auto const* tgt : this->ExportedTargets) {
739     auto prop_filename = cmStrCat("target-", tgt->GetExportName(), '-',
740                                   filename_config, ".cmake");
741     prop_files.emplace_back(cmStrCat(dest, prop_filename));
742     os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
743   }
744
745   return true;
746 }