CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmExportInstallFileGenerator.cxx
blobf5f22efa899d3e1c928a0b0d341dfa499dfe9e81
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"
5 #include <algorithm>
6 #include <memory>
7 #include <sstream>
8 #include <utility>
10 #include <cm/string_view>
11 #include <cmext/string_view>
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 "cmList.h"
23 #include "cmLocalGenerator.h"
24 #include "cmMakefile.h"
25 #include "cmMessageType.h"
26 #include "cmOutputConverter.h"
27 #include "cmPolicies.h"
28 #include "cmStateTypes.h"
29 #include "cmStringAlgorithms.h"
30 #include "cmSystemTools.h"
31 #include "cmTarget.h"
32 #include "cmTargetExport.h"
33 #include "cmValue.h"
35 cmExportInstallFileGenerator::cmExportInstallFileGenerator(
36 cmInstallExportGenerator* iegen)
37 : IEGen(iegen)
41 std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
43 std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
44 return glob;
47 bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
49 std::vector<cmTargetExport*> allTargets;
51 std::string expectedTargets;
52 std::string sep;
53 for (std::unique_ptr<cmTargetExport> const& te :
54 this->IEGen->GetExportSet()->GetTargetExports()) {
55 if (te->NamelinkOnly) {
56 continue;
58 expectedTargets += sep + this->Namespace + te->Target->GetExportName();
59 sep = " ";
60 if (this->ExportedTargets.insert(te->Target).second) {
61 allTargets.push_back(te.get());
62 } else {
63 std::ostringstream e;
64 e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
65 << "\" ...) "
66 << "includes target \"" << te->Target->GetName()
67 << "\" more than once in the export set.";
68 cmSystemTools::Error(e.str());
69 return false;
73 this->GenerateExpectedTargetsCode(os, expectedTargets);
76 // Compute the relative import prefix for the file
77 this->GenerateImportPrefix(os);
79 bool requiresConfigFiles = false;
80 // Create all the imported targets.
81 for (cmTargetExport* te : allTargets) {
82 cmGeneratorTarget* gt = te->Target;
83 cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
85 requiresConfigFiles =
86 requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY;
88 this->GenerateImportTargetCode(os, gt, targetType);
90 ImportPropertyMap properties;
92 std::string includesDestinationDirs;
93 this->PopulateIncludeDirectoriesInterface(
94 gt, cmGeneratorExpression::InstallInterface, properties, *te,
95 includesDestinationDirs);
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_AUTOMOC_MACRO_NAMES", gt,
114 cmGeneratorExpression::InstallInterface,
115 properties);
116 this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
117 cmGeneratorExpression::InstallInterface,
118 properties);
119 this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
120 cmGeneratorExpression::InstallInterface,
121 properties);
122 this->PopulateLinkDirectoriesInterface(
123 gt, cmGeneratorExpression::InstallInterface, properties);
124 this->PopulateLinkDependsInterface(
125 gt, cmGeneratorExpression::InstallInterface, properties);
127 std::string errorMessage;
128 if (!this->PopulateCxxModuleExportProperties(
129 gt, properties, cmGeneratorExpression::InstallInterface,
130 includesDestinationDirs, errorMessage)) {
131 cmSystemTools::Error(errorMessage);
132 return false;
135 if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
136 cmSystemTools::Error(errorMessage);
137 return false;
140 const bool newCMP0022Behavior =
141 gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
142 gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
143 if (newCMP0022Behavior) {
144 if (this->PopulateInterfaceLinkLibrariesProperty(
145 gt, cmGeneratorExpression::InstallInterface, properties) &&
146 !this->ExportOld) {
147 this->SetRequiredCMakeVersion(2, 8, 12);
150 if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
151 this->SetRequiredCMakeVersion(3, 0, 0);
153 if (gt->GetProperty("INTERFACE_SOURCES")) {
154 // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
155 // can consume them.
156 this->SetRequiredCMakeVersion(3, 1, 0);
159 this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
160 properties);
162 this->PopulateCompatibleInterfaceProperties(gt, properties);
163 this->PopulateCustomTransitiveInterfaceProperties(
164 gt, cmGeneratorExpression::InstallInterface, properties);
166 this->GenerateInterfaceProperties(gt, os, properties);
168 this->GenerateTargetFileSets(gt, os, te);
171 this->LoadConfigFiles(os);
173 bool result = true;
175 std::string cxx_modules_name = this->IEGen->GetExportSet()->GetName();
176 this->GenerateCxxModuleInformation(cxx_modules_name, os);
177 if (requiresConfigFiles) {
178 for (std::string const& c : this->Configurations) {
179 if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name,
180 c)) {
181 result = false;
186 this->CleanupTemporaryVariables(os);
187 this->GenerateImportedFileCheckLoop(os);
189 // Generate an import file for each configuration.
190 // Don't do this if we only export INTERFACE_LIBRARY targets.
191 if (requiresConfigFiles) {
192 for (std::string const& c : this->Configurations) {
193 if (!this->GenerateImportFileConfig(c)) {
194 result = false;
199 this->GenerateMissingTargetsCheckCode(os);
201 return result;
204 void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os)
206 // Set an _IMPORT_PREFIX variable for import location properties
207 // to reference if they are relative to the install prefix.
208 std::string installPrefix =
209 this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
210 "CMAKE_INSTALL_PREFIX");
211 std::string const& expDest = this->IEGen->GetDestination();
212 if (cmSystemTools::FileIsFullPath(expDest)) {
213 // The export file is being installed to an absolute path so the
214 // package is not relocatable. Use the configured install prefix.
215 /* clang-format off */
216 os <<
217 "# The installation prefix configured by this project.\n"
218 "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
219 "\n";
220 /* clang-format on */
221 } else {
222 // Add code to compute the installation prefix relative to the
223 // import file location.
224 std::string absDest = installPrefix + "/" + expDest;
225 std::string absDestS = absDest + "/";
226 os << "# Compute the installation prefix relative to this file.\n"
227 << "get_filename_component(_IMPORT_PREFIX"
228 << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
229 if (cmHasLiteralPrefix(absDestS, "/lib/") ||
230 cmHasLiteralPrefix(absDestS, "/lib64/") ||
231 cmHasLiteralPrefix(absDestS, "/libx32/") ||
232 cmHasLiteralPrefix(absDestS, "/usr/lib/") ||
233 cmHasLiteralPrefix(absDestS, "/usr/lib64/") ||
234 cmHasLiteralPrefix(absDestS, "/usr/libx32/")) {
235 // Handle "/usr move" symlinks created by some Linux distros.
236 /* clang-format off */
237 os <<
238 "# Use original install prefix when loaded through a\n"
239 "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
240 "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
241 "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
242 "if(_realCurr STREQUAL _realOrig)\n"
243 " set(_IMPORT_PREFIX \"" << absDest << "\")\n"
244 "endif()\n"
245 "unset(_realOrig)\n"
246 "unset(_realCurr)\n";
247 /* clang-format on */
249 std::string dest = expDest;
250 while (!dest.empty()) {
251 os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
252 "PATH)\n";
253 dest = cmSystemTools::GetFilenamePath(dest);
255 os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
256 << " set(_IMPORT_PREFIX \"\")\n"
257 << "endif()\n"
258 << "\n";
262 void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os)
264 /* clang-format off */
265 os << "# Cleanup temporary variables.\n"
266 << "set(_IMPORT_PREFIX)\n"
267 << "\n";
268 /* clang-format on */
271 void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os)
273 // Now load per-configuration properties for them.
274 /* clang-format off */
275 os << "# Load information for each installed configuration.\n"
276 << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/"
277 << this->GetConfigImportFileGlob() << "\")\n"
278 << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n"
279 << " include(\"${_cmake_config_file}\")\n"
280 << "endforeach()\n"
281 << "unset(_cmake_config_file)\n"
282 << "unset(_cmake_config_files)\n"
283 << "\n";
284 /* clang-format on */
287 void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input)
289 cmGeneratorExpression::ReplaceInstallPrefix(input, "${_IMPORT_PREFIX}");
292 bool cmExportInstallFileGenerator::GenerateImportFileConfig(
293 const std::string& config)
295 // Skip configurations not enabled for this export.
296 if (!this->IEGen->InstallsForConfig(config)) {
297 return true;
300 // Construct the name of the file to generate.
301 std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-');
302 if (!config.empty()) {
303 fileName += cmSystemTools::LowerCase(config);
304 } else {
305 fileName += "noconfig";
307 fileName += this->FileExt;
309 // Open the output file to generate it.
310 cmGeneratedFileStream exportFileStream(fileName, true);
311 if (!exportFileStream) {
312 std::string se = cmSystemTools::GetLastSystemError();
313 std::ostringstream e;
314 e << "cannot write to file \"" << fileName << "\": " << se;
315 cmSystemTools::Error(e.str());
316 return false;
318 exportFileStream.SetCopyIfDifferent(true);
319 std::ostream& os = exportFileStream;
321 // Start with the import file header.
322 this->GenerateImportHeaderCode(os, config);
324 // Generate the per-config target information.
325 this->GenerateImportConfig(os, config);
327 // End with the import file footer.
328 this->GenerateImportFooterCode(os);
330 // Record this per-config import file.
331 this->ConfigImportFiles[config] = fileName;
333 return true;
336 void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
337 std::ostream& os, const std::string& config, std::string const& suffix)
339 // Add each target in the set to the export.
340 for (std::unique_ptr<cmTargetExport> const& te :
341 this->IEGen->GetExportSet()->GetTargetExports()) {
342 // Collect import properties for this target.
343 if (this->GetExportTargetType(te.get()) ==
344 cmStateEnums::INTERFACE_LIBRARY) {
345 continue;
348 ImportPropertyMap properties;
349 std::set<std::string> importedLocations;
351 this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
352 properties, importedLocations);
353 this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
354 properties, importedLocations);
355 this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator,
356 properties, importedLocations);
357 this->SetImportLocationProperty(config, suffix, te->ObjectsGenerator,
358 properties, importedLocations);
359 this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
360 properties, importedLocations);
361 this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
362 properties, importedLocations);
364 // If any file location was set for the target add it to the
365 // import file.
366 if (!properties.empty()) {
367 // Get the rest of the target details.
368 cmGeneratorTarget* gtgt = te->Target;
369 this->SetImportDetailProperties(config, suffix, gtgt, properties);
371 this->SetImportLinkInterface(config, suffix,
372 cmGeneratorExpression::InstallInterface,
373 gtgt, properties);
375 // TODO: PUBLIC_HEADER_LOCATION
376 // This should wait until the build feature propagation stuff
377 // is done. Then this can be a propagated include directory.
378 // this->GenerateImportProperty(config, te->HeaderGenerator,
379 // properties);
381 // Generate code in the export file.
382 std::string importedXcFrameworkLocation = te->XcFrameworkLocation;
383 if (!importedXcFrameworkLocation.empty()) {
384 importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
385 importedXcFrameworkLocation,
386 cmGeneratorExpression::PreprocessContext::InstallInterface, true);
387 importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
388 importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config,
389 te->Target, nullptr, te->Target);
390 if (!importedXcFrameworkLocation.empty() &&
391 !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) &&
392 !cmHasLiteralPrefix(importedXcFrameworkLocation,
393 "${_IMPORT_PREFIX}/")) {
394 importedXcFrameworkLocation =
395 cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation);
398 this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
399 importedXcFrameworkLocation);
400 this->GenerateImportedFileChecksCode(
401 os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
406 void cmExportInstallFileGenerator::SetImportLocationProperty(
407 const std::string& config, std::string const& suffix,
408 cmInstallTargetGenerator* itgen, ImportPropertyMap& properties,
409 std::set<std::string>& importedLocations)
411 // Skip rules that do not match this configuration.
412 if (!(itgen && itgen->InstallsForConfig(config))) {
413 return;
416 // Get the target to be installed.
417 cmGeneratorTarget* target = itgen->GetTarget();
419 // Construct the installed location of the target.
420 std::string dest = itgen->GetDestination(config);
421 std::string value;
422 if (!cmSystemTools::FileIsFullPath(dest)) {
423 // The target is installed relative to the installation prefix.
424 value = "${_IMPORT_PREFIX}/";
426 value += dest;
427 value += "/";
429 if (itgen->IsImportLibrary()) {
430 // Construct the property name.
431 std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
433 // Append the installed file name.
434 value += cmInstallTargetGenerator::GetInstallFilename(
435 target, config, cmInstallTargetGenerator::NameImplibReal);
437 // Store the property.
438 properties[prop] = value;
439 importedLocations.insert(prop);
440 } else if (itgen->GetTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
441 // Construct the property name.
442 std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
444 // Compute all the object files inside this target and setup
445 // IMPORTED_OBJECTS as a list of object files
446 std::vector<std::string> objects;
447 itgen->GetInstallObjectNames(config, objects);
448 for (std::string& obj : objects) {
449 obj = cmStrCat(value, obj);
452 // Store the property.
453 properties[prop] = cmList::to_string(objects);
454 importedLocations.insert(prop);
455 } else {
456 if (target->IsFrameworkOnApple() && target->HasImportLibrary(config)) {
457 // store as well IMPLIB value
458 auto importProp = cmStrCat("IMPORTED_IMPLIB", suffix);
459 auto importValue =
460 cmStrCat(value,
461 cmInstallTargetGenerator::GetInstallFilename(
462 target, config, cmInstallTargetGenerator::NameImplibReal));
464 // Store the property.
465 properties[importProp] = importValue;
466 importedLocations.insert(importProp);
469 // Construct the property name.
470 std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
472 // Append the installed file name.
473 if (target->IsAppBundleOnApple()) {
474 value += cmInstallTargetGenerator::GetInstallFilename(target, config);
475 value += ".app/";
476 if (!target->Makefile->PlatformIsAppleEmbedded()) {
477 value += "Contents/MacOS/";
479 value += cmInstallTargetGenerator::GetInstallFilename(target, config);
480 } else {
481 value += cmInstallTargetGenerator::GetInstallFilename(
482 target, config, cmInstallTargetGenerator::NameReal);
485 // Store the property.
486 properties[prop] = value;
487 importedLocations.insert(prop);
491 cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType(
492 cmTargetExport const* targetExport) const
494 cmStateEnums::TargetType targetType = targetExport->Target->GetType();
495 // An OBJECT library installed with no OBJECTS DESTINATION
496 // is transformed to an INTERFACE library.
497 if (targetType == cmStateEnums::OBJECT_LIBRARY &&
498 targetExport->ObjectsGenerator == nullptr) {
499 targetType = cmStateEnums::INTERFACE_LIBRARY;
501 return targetType;
504 void cmExportInstallFileGenerator::HandleMissingTarget(
505 std::string& link_libs, cmGeneratorTarget const* depender,
506 cmGeneratorTarget* dependee)
508 const std::string name = dependee->GetName();
509 cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator();
510 auto exportInfo = this->FindNamespaces(gg, name);
511 std::vector<std::string> const& exportFiles = exportInfo.first;
512 if (exportFiles.size() == 1) {
513 std::string missingTarget = exportInfo.second;
515 missingTarget += dependee->GetExportName();
516 link_libs += missingTarget;
517 this->MissingTargets.emplace_back(std::move(missingTarget));
518 } else {
519 // All exported targets should be known here and should be unique.
520 // This is probably user-error.
521 this->ComplainAboutMissingTarget(depender, dependee, exportFiles);
525 std::pair<std::vector<std::string>, std::string>
526 cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg,
527 const std::string& name)
529 std::vector<std::string> exportFiles;
530 std::string ns;
531 const cmExportSetMap& exportSets = gg->GetExportSets();
533 for (auto const& expIt : exportSets) {
534 const cmExportSet& exportSet = expIt.second;
536 bool containsTarget = false;
537 for (auto const& target : exportSet.GetTargetExports()) {
538 if (name == target->TargetName) {
539 containsTarget = true;
540 break;
544 if (containsTarget) {
545 std::vector<cmInstallExportGenerator const*> const* installs =
546 exportSet.GetInstallations();
547 for (cmInstallExportGenerator const* install : *installs) {
548 exportFiles.push_back(install->GetDestinationFile());
549 ns = install->GetNamespace();
554 return { exportFiles, ns };
557 void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
558 cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
559 std::vector<std::string> const& exportFiles)
561 std::ostringstream e;
562 e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
563 << "\" ...) "
564 << "includes target \"" << depender->GetName()
565 << "\" which requires target \"" << dependee->GetName() << "\" ";
566 if (exportFiles.empty()) {
567 e << "that is not in any export set.";
568 } else {
569 e << "that is not in this export set, but in multiple other export sets: "
570 << cmJoin(exportFiles, ", ") << ".\n";
571 e << "An exported target cannot depend upon another target which is "
572 "exported multiple times. Consider consolidating the exports of the "
573 "\""
574 << dependee->GetName() << "\" target to a single export.";
576 cmSystemTools::Error(e.str());
579 std::string cmExportInstallFileGenerator::InstallNameDir(
580 cmGeneratorTarget const* target, const std::string& config)
582 std::string install_name_dir;
584 cmMakefile* mf = target->Target->GetMakefile();
585 if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
586 install_name_dir =
587 target->GetInstallNameDirForInstallTree(config, "${_IMPORT_PREFIX}");
590 return install_name_dir;
593 namespace {
594 bool EntryIsContextSensitive(
595 const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
597 return cge->GetHadContextSensitiveCondition();
601 std::string cmExportInstallFileGenerator::GetFileSetDirectories(
602 cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
604 std::vector<std::string> resultVector;
606 auto configs =
607 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
609 cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
610 auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
612 for (auto const& config : configs) {
613 auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
614 auto dest = cmOutputConverter::EscapeForCMake(
615 unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
616 if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
617 dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
620 auto const& type = fileSet->GetType();
621 // C++ modules do not support interface file sets which are dependent upon
622 // the configuration.
623 if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) {
624 auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
625 std::ostringstream e;
626 e << "The \"" << gte->GetName() << "\" target's interface file set \""
627 << fileSet->GetName() << "\" of type \"" << type
628 << "\" contains context-sensitive base file entries which is not "
629 "supported.";
630 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
631 return std::string{};
634 if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
635 resultVector.push_back(
636 cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
637 } else {
638 resultVector.emplace_back(cmStrCat('"', dest, '"'));
639 break;
643 return cmJoin(resultVector, " ");
646 std::string cmExportInstallFileGenerator::GetFileSetFiles(
647 cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
649 std::vector<std::string> resultVector;
651 auto configs =
652 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
654 auto fileEntries = fileSet->CompileFileEntries();
655 auto directoryEntries = fileSet->CompileDirectoryEntries();
657 cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance());
658 auto destCge =
659 destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
661 for (auto const& config : configs) {
662 auto directories = fileSet->EvaluateDirectoryEntries(
663 directoryEntries, gte->LocalGenerator, config, gte);
665 std::map<std::string, std::vector<std::string>> files;
666 for (auto const& entry : fileEntries) {
667 fileSet->EvaluateFileEntry(directories, files, entry,
668 gte->LocalGenerator, config, gte);
670 auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte);
671 auto dest =
672 cmStrCat(cmOutputConverter::EscapeForCMake(
673 unescapedDest, cmOutputConverter::WrapQuotes::NoWrap),
674 '/');
675 if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
676 dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
679 bool const contextSensitive = destCge->GetHadContextSensitiveCondition() ||
680 std::any_of(directoryEntries.begin(), directoryEntries.end(),
681 EntryIsContextSensitive) ||
682 std::any_of(fileEntries.begin(), fileEntries.end(),
683 EntryIsContextSensitive);
685 auto const& type = fileSet->GetType();
686 // C++ modules do not support interface file sets which are dependent upon
687 // the configuration.
688 if (contextSensitive && type == "CXX_MODULES"_s) {
689 auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
690 std::ostringstream e;
691 e << "The \"" << gte->GetName() << "\" target's interface file set \""
692 << fileSet->GetName() << "\" of type \"" << type
693 << "\" contains context-sensitive base file entries which is not "
694 "supported.";
695 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
696 return std::string{};
699 for (auto const& it : files) {
700 auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
701 for (auto const& filename : it.second) {
702 auto relFile =
703 cmStrCat(prefix, cmSystemTools::GetFilenameName(filename));
704 auto escapedFile =
705 cmStrCat(dest,
706 cmOutputConverter::EscapeForCMake(
707 relFile, cmOutputConverter::WrapQuotes::NoWrap));
708 if (contextSensitive && configs.size() != 1) {
709 resultVector.push_back(
710 cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
711 } else {
712 resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
717 if (!(contextSensitive && configs.size() != 1)) {
718 break;
722 return cmJoin(resultVector, " ");
725 std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const
727 return IEGen->GetCxxModuleDirectory();
730 void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
731 std::string const& name, std::ostream& os) const
733 // Now load per-configuration properties for them.
734 /* clang-format off */
735 os << "# Load information for each installed configuration.\n"
736 "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n"
737 "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
738 " include(\"${_cmake_cxx_module_include}\")\n"
739 "endforeach()\n"
740 "unset(_cmake_cxx_module_include)\n"
741 "unset(_cmake_cxx_module_includes)\n";
742 /* clang-format on */
745 bool cmExportInstallFileGenerator::
746 GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
747 std::string const& config)
749 auto cxx_modules_dirname = this->GetCxxModulesDirectory();
750 if (cxx_modules_dirname.empty()) {
751 return true;
754 std::string filename_config = config;
755 if (filename_config.empty()) {
756 filename_config = "noconfig";
759 std::string const dest =
760 cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
761 std::string fileName =
762 cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake");
764 cmGeneratedFileStream os(fileName, true);
765 if (!os) {
766 std::string se = cmSystemTools::GetLastSystemError();
767 std::ostringstream e;
768 e << "cannot write to file \"" << fileName << "\": " << se;
769 cmSystemTools::Error(e.str());
770 return false;
772 os.SetCopyIfDifferent(true);
774 // Record this per-config import file.
775 this->ConfigCxxModuleFiles[config] = fileName;
777 auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
778 for (auto const* tgt : this->ExportedTargets) {
779 // Only targets with C++ module sources will have a
780 // collator-generated install script.
781 if (!tgt->HaveCxx20ModuleSources()) {
782 continue;
785 auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(),
786 '-', filename_config, ".cmake");
787 prop_files.emplace_back(cmStrCat(dest, prop_filename));
788 os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
791 return true;