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"
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 "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"
31 #include "cmTargetExport.h"
34 cmExportInstallFileGenerator::cmExportInstallFileGenerator(
35 cmInstallExportGenerator* iegen)
40 std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
42 std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
46 bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
48 std::vector<cmTargetExport*> allTargets;
50 std::string expectedTargets;
52 for (std::unique_ptr<cmTargetExport> const& te :
53 this->IEGen->GetExportSet()->GetTargetExports()) {
54 if (te->NamelinkOnly) {
57 expectedTargets += sep + this->Namespace + te->Target->GetExportName();
59 if (this->ExportedTargets.insert(te->Target).second) {
60 allTargets.push_back(te.get());
63 e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
65 << "includes target \"" << te->Target->GetName()
66 << "\" more than once in the export set.";
67 cmSystemTools::Error(e.str());
72 this->GenerateExpectedTargetsCode(os, expectedTargets);
75 // Compute the relative import prefix for the file
76 this->GenerateImportPrefix(os);
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);
88 requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY;
90 this->GenerateImportTargetCode(os, gt, targetType);
92 ImportPropertyMap properties;
94 this->PopulateIncludeDirectoriesInterface(
95 gt, cmGeneratorExpression::InstallInterface, properties, *te);
96 this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface,
98 this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
99 cmGeneratorExpression::InstallInterface,
101 this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt,
102 cmGeneratorExpression::InstallInterface,
104 this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt,
105 cmGeneratorExpression::InstallInterface,
107 this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt,
108 cmGeneratorExpression::InstallInterface,
110 this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
111 cmGeneratorExpression::InstallInterface,
113 this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
114 cmGeneratorExpression::InstallInterface,
116 this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
117 cmGeneratorExpression::InstallInterface,
119 this->PopulateLinkDirectoriesInterface(
120 gt, cmGeneratorExpression::InstallInterface, properties);
121 this->PopulateLinkDependsInterface(
122 gt, cmGeneratorExpression::InstallInterface, properties);
124 std::string errorMessage;
125 if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
126 cmSystemTools::Error(errorMessage);
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) &&
137 require2_8_12 = true;
140 if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
143 if (gt->GetProperty("INTERFACE_SOURCES")) {
144 // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
149 this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
152 this->PopulateCompatibleInterfaceProperties(gt, properties);
154 this->GenerateInterfaceProperties(gt, os, properties);
156 this->GenerateTargetFileSets(gt, os, te);
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");
167 this->LoadConfigFiles(os);
171 this->GenerateCxxModuleInformation(os);
172 if (requiresConfigFiles) {
173 for (std::string const& c : this->Configurations) {
174 if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) {
180 this->CleanupTemporaryVariables(os);
181 this->GenerateImportedFileCheckLoop(os);
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)) {
193 this->GenerateMissingTargetsCheckCode(os);
198 void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os)
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 */
211 "# The installation prefix configured by this project.\n"
212 "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
214 /* clang-format on */
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 */
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"
240 "unset(_realCurr)\n";
241 /* clang-format on */
243 std::string dest = expDest;
244 while (!dest.empty()) {
245 os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
247 dest = cmSystemTools::GetFilenamePath(dest);
249 os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
250 << " set(_IMPORT_PREFIX \"\")\n"
256 void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os)
258 /* clang-format off */
259 os << "# Cleanup temporary variables.\n"
260 << "set(_IMPORT_PREFIX)\n"
262 /* clang-format on */
265 void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os)
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"
275 << "unset(_cmake_config_file)\n"
276 << "unset(_cmake_config_files)\n"
278 /* clang-format on */
281 void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input)
283 cmGeneratorExpression::ReplaceInstallPrefix(input, "${_IMPORT_PREFIX}");
286 bool cmExportInstallFileGenerator::GenerateImportFileConfig(
287 const std::string& config)
289 // Skip configurations not enabled for this export.
290 if (!this->IEGen->InstallsForConfig(config)) {
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);
299 fileName += "noconfig";
301 fileName += this->FileExt;
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());
312 exportFileStream.SetCopyIfDifferent(true);
313 std::ostream& os = exportFileStream;
315 // Start with the import file header.
316 this->GenerateImportHeaderCode(os, config);
318 // Generate the per-config target information.
319 this->GenerateImportConfig(os, config);
321 // End with the import file footer.
322 this->GenerateImportFooterCode(os);
324 // Record this per-config import file.
325 this->ConfigImportFiles[config] = fileName;
330 void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
331 std::ostream& os, const std::string& config, std::string const& suffix)
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) {
342 ImportPropertyMap properties;
343 std::set<std::string> importedLocations;
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);
358 // If any file location was set for the target add it to the
360 if (!properties.empty()) {
361 // Get the rest of the target details.
362 cmGeneratorTarget* gtgt = te->Target;
363 this->SetImportDetailProperties(config, suffix, gtgt, properties);
365 this->SetImportLinkInterface(config, suffix,
366 cmGeneratorExpression::InstallInterface,
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,
375 // Generate code in the export file.
376 this->GenerateImportPropertyCode(os, config, gtgt, properties);
377 this->GenerateImportedFileChecksCode(os, gtgt, properties,
383 void cmExportInstallFileGenerator::SetImportLocationProperty(
384 const std::string& config, std::string const& suffix,
385 cmInstallTargetGenerator* itgen, ImportPropertyMap& properties,
386 std::set<std::string>& importedLocations)
388 // Skip rules that do not match this configuration.
389 if (!(itgen && itgen->InstallsForConfig(config))) {
393 // Get the target to be installed.
394 cmGeneratorTarget* target = itgen->GetTarget();
396 // Construct the installed location of the target.
397 std::string dest = itgen->GetDestination(config);
399 if (!cmSystemTools::FileIsFullPath(dest)) {
400 // The target is installed relative to the installation prefix.
401 value = "${_IMPORT_PREFIX}/";
406 if (itgen->IsImportLibrary()) {
407 // Construct the property name.
408 std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
410 // Append the installed file name.
411 value += cmInstallTargetGenerator::GetInstallFilename(
412 target, config, cmInstallTargetGenerator::NameImplib);
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);
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);
429 // Store the property.
430 properties[prop] = cmJoin(objects, ";");
431 importedLocations.insert(prop);
433 // Construct the property name.
434 std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
436 // Append the installed file name.
437 if (target->IsAppBundleOnApple()) {
438 value += cmInstallTargetGenerator::GetInstallFilename(target, config);
440 if (!target->Makefile->PlatformIsAppleEmbedded()) {
441 value += "Contents/MacOS/";
443 value += cmInstallTargetGenerator::GetInstallFilename(target, config);
445 value += cmInstallTargetGenerator::GetInstallFilename(
446 target, config, cmInstallTargetGenerator::NameReal);
449 // Store the property.
450 properties[prop] = value;
451 importedLocations.insert(prop);
455 cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType(
456 cmTargetExport const* targetExport) const
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;
468 void cmExportInstallFileGenerator::HandleMissingTarget(
469 std::string& link_libs, cmGeneratorTarget const* depender,
470 cmGeneratorTarget* dependee)
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;
479 missingTarget += dependee->GetExportName();
480 link_libs += missingTarget;
481 this->MissingTargets.emplace_back(std::move(missingTarget));
483 // All exported targets should be known here and should be unique.
484 // This is probably user-error.
485 this->ComplainAboutMissingTarget(depender, dependee, exportFiles);
489 std::pair<std::vector<std::string>, std::string>
490 cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg,
491 const std::string& name)
493 std::vector<std::string> exportFiles;
495 const cmExportSetMap& exportSets = gg->GetExportSets();
497 for (auto const& expIt : exportSets) {
498 const cmExportSet& exportSet = expIt.second;
500 bool containsTarget = false;
501 for (auto const& target : exportSet.GetTargetExports()) {
502 if (name == target->TargetName) {
503 containsTarget = true;
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();
518 return { exportFiles, ns };
521 void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
522 cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
523 std::vector<std::string> const& exportFiles)
525 std::ostringstream e;
526 e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
528 << "includes target \"" << depender->GetName()
529 << "\" which requires target \"" << dependee->GetName() << "\" ";
530 if (exportFiles.empty()) {
531 e << "that is not in any export set.";
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 "
538 << dependee->GetName() << "\" target to a single export.";
540 cmSystemTools::Error(e.str());
543 std::string cmExportInstallFileGenerator::InstallNameDir(
544 cmGeneratorTarget const* target, const std::string& config)
546 std::string install_name_dir;
548 cmMakefile* mf = target->Target->GetMakefile();
549 if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
551 target->GetInstallNameDirForInstallTree(config, "${_IMPORT_PREFIX}");
554 return install_name_dir;
558 bool EntryIsContextSensitive(
559 const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
561 return cge->GetHadContextSensitiveCondition();
565 std::string cmExportInstallFileGenerator::GetFileSetDirectories(
566 cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
568 std::vector<std::string> resultVector;
571 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
573 cmGeneratorExpression ge;
574 auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
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));
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 "
593 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
594 return std::string{};
597 if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
598 resultVector.push_back(
599 cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
601 resultVector.push_back(cmStrCat('"', dest, '"'));
606 return cmJoin(resultVector, " ");
609 std::string cmExportInstallFileGenerator::GetFileSetFiles(
610 cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
612 std::vector<std::string> resultVector;
615 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
617 auto fileEntries = fileSet->CompileFileEntries();
618 auto directoryEntries = fileSet->CompileDirectoryEntries();
620 cmGeneratorExpression destGe;
622 destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
624 for (auto const& config : configs) {
625 auto directories = fileSet->EvaluateDirectoryEntries(
626 directoryEntries, gte->LocalGenerator, config, gte);
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);
633 auto dest = cmStrCat("${_IMPORT_PREFIX}/",
634 cmOutputConverter::EscapeForCMake(
635 destCge->Evaluate(gte->LocalGenerator, config, gte),
636 cmOutputConverter::WrapQuotes::NoWrap),
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);
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 "
656 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
657 return std::string{};
660 for (auto const& it : files) {
661 auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
662 for (auto const& filename : it.second) {
664 cmStrCat(prefix, cmSystemTools::GetFilenameName(filename));
667 cmOutputConverter::EscapeForCMake(
668 relFile, cmOutputConverter::WrapQuotes::NoWrap));
669 if (contextSensitive && configs.size() != 1) {
670 resultVector.push_back(
671 cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
673 resultVector.push_back(cmStrCat('"', escapedFile, '"'));
678 if (!(contextSensitive && configs.size() != 1)) {
683 return cmJoin(resultVector, " ");
686 std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const
688 return IEGen->GetCxxModuleDirectory();
691 void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
692 std::ostream& os) const
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"
701 "unset(_cmake_cxx_module_include)\n"
702 "unset(_cmake_cxx_module_includes)\n";
703 /* clang-format on */
706 bool cmExportInstallFileGenerator::
707 GenerateImportCxxModuleConfigTargetInclusion(std::string const& config)
709 auto cxx_modules_dirname = this->GetCxxModulesDirectory();
710 if (cxx_modules_dirname.empty()) {
714 std::string filename_config = config;
715 if (filename_config.empty()) {
716 filename_config = "noconfig";
719 std::string const dest =
720 cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
721 std::string fileName =
722 cmStrCat(dest, "cxx-modules-", filename_config, ".cmake");
724 cmGeneratedFileStream os(fileName, true);
726 std::string se = cmSystemTools::GetLastSystemError();
727 std::ostringstream e;
728 e << "cannot write to file \"" << fileName << "\": " << se;
729 cmSystemTools::Error(e.str());
732 os.SetCopyIfDifferent(true);
734 // Record this per-config import file.
735 this->ConfigCxxModuleFiles[config] = fileName;
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";