1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #include "cmDyndepCollation.h"
14 #include <cm/string_view>
15 #include <cmext/string_view>
17 #include <cm3p/json/value.h>
19 #include "cmBuildDatabase.h"
20 #include "cmExportBuildFileGenerator.h"
21 #include "cmExportSet.h"
22 #include "cmFileSet.h"
23 #include "cmGeneratedFileStream.h"
24 #include "cmGeneratorExpression.h" // IWYU pragma: keep
25 #include "cmGeneratorTarget.h"
26 #include "cmGlobalGenerator.h"
27 #include "cmInstallCxxModuleBmiGenerator.h"
28 #include "cmInstallExportGenerator.h"
29 #include "cmInstallFileSetGenerator.h"
30 #include "cmInstallGenerator.h"
31 #include "cmMakefile.h"
32 #include "cmMessageType.h"
33 #include "cmOutputConverter.h"
34 #include "cmScanDepFormat.h"
35 #include "cmSourceFile.h"
36 #include "cmStringAlgorithms.h"
37 #include "cmSystemTools.h"
39 #include "cmTargetExport.h"
46 Json::Value CxxModules
;
49 TdiSourceInfo
CollationInformationSources(cmGeneratorTarget
const* gt
,
50 std::string
const& config
,
51 cmDyndepGeneratorCallbacks
const& cb
)
54 cmTarget
const* tgt
= gt
->Target
;
55 auto all_file_sets
= tgt
->GetAllFileSetNames();
56 Json::Value
& tdi_sources
= info
.Sources
= Json::objectValue
;
57 Json::Value
& tdi_cxx_module_info
= info
.CxxModules
= Json::objectValue
;
59 enum class CompileType
64 std::map
<std::string
, std::pair
<cmSourceFile
const*, CompileType
>> sf_map
;
66 auto fill_sf_map
= [gt
, tgt
, &sf_map
](cmSourceFile
const* sf
,
68 auto full_path
= sf
->GetFullPath();
69 if (full_path
.empty()) {
70 gt
->Makefile
->IssueMessage(
71 MessageType::INTERNAL_ERROR
,
72 cmStrCat("Target \"", tgt
->GetName(),
73 "\" has a full path-less source file."));
76 sf_map
[full_path
] = std::make_pair(sf
, type
);
79 std::vector
<cmSourceFile
const*> objectSources
;
80 gt
->GetObjectSources(objectSources
, config
);
81 for (auto const* sf
: objectSources
) {
82 fill_sf_map(sf
, CompileType::ObjectAndBmi
);
85 std::vector
<cmSourceFile
const*> cxxModuleSources
;
86 gt
->GetCxxModuleSources(cxxModuleSources
, config
);
87 for (auto const* sf
: cxxModuleSources
) {
88 fill_sf_map(sf
, CompileType::BmiOnly
);
92 for (auto const& file_set_name
: all_file_sets
) {
93 auto const* file_set
= tgt
->GetFileSet(file_set_name
);
95 gt
->Makefile
->IssueMessage(MessageType::INTERNAL_ERROR
,
96 cmStrCat("Target \"", tgt
->GetName(),
97 "\" is tracked to have file set \"",
99 "\", but it was not found."));
102 auto fs_type
= file_set
->GetType();
103 // We only care about C++ module sources here.
104 if (fs_type
!= "CXX_MODULES"_s
) {
108 auto fileEntries
= file_set
->CompileFileEntries();
109 auto directoryEntries
= file_set
->CompileDirectoryEntries();
111 auto directories
= file_set
->EvaluateDirectoryEntries(
112 directoryEntries
, gt
->LocalGenerator
, config
, gt
);
113 std::map
<std::string
, std::vector
<std::string
>> files_per_dirs
;
114 for (auto const& entry
: fileEntries
) {
115 file_set
->EvaluateFileEntry(directories
, files_per_dirs
, entry
,
116 gt
->LocalGenerator
, config
, gt
);
119 Json::Value fs_dest
= Json::nullValue
;
120 for (auto const& ig
: gt
->Makefile
->GetInstallGenerators()) {
121 if (auto const* fsg
=
122 dynamic_cast<cmInstallFileSetGenerator
const*>(ig
.get())) {
123 if (fsg
->GetTarget() == gt
&& fsg
->GetFileSet() == file_set
) {
124 fs_dest
= fsg
->GetDestination(config
);
130 for (auto const& files_per_dir
: files_per_dirs
) {
131 for (auto const& file
: files_per_dir
.second
) {
132 auto const full_file
= cmSystemTools::CollapseFullPath(file
);
133 auto lookup
= sf_map
.find(full_file
);
134 if (lookup
== sf_map
.end()) {
135 gt
->Makefile
->IssueMessage(
136 MessageType::FATAL_ERROR
,
137 cmStrCat("Target \"", tgt
->GetName(), "\" has source file\n ",
139 "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not "
140 "scheduled for compilation."));
144 auto const* sf
= lookup
->second
.first
;
145 CompileType
const ct
= lookup
->second
.second
;
147 sf_map
.erase(lookup
);
150 gt
->Makefile
->IssueMessage(
151 MessageType::INTERNAL_ERROR
,
152 cmStrCat("Target \"", tgt
->GetName(), "\" has source file \"",
153 file
, "\" which has not been tracked properly."));
157 auto obj_path
= ct
== CompileType::ObjectAndBmi
158 ? cb
.ObjectFilePath(sf
, config
)
159 : cb
.BmiFilePath(sf
, config
);
160 Json::Value
& tdi_module_info
= tdi_cxx_module_info
[obj_path
] =
163 tdi_module_info
["source"] = full_file
;
164 tdi_module_info
["bmi-only"] = ct
== CompileType::BmiOnly
;
165 tdi_module_info
["relative-directory"] = files_per_dir
.first
;
166 tdi_module_info
["name"] = file_set
->GetName();
167 tdi_module_info
["type"] = file_set
->GetType();
168 tdi_module_info
["visibility"] =
169 std::string(cmFileSetVisibilityToName(file_set
->GetVisibility()));
170 tdi_module_info
["destination"] = fs_dest
;
175 for (auto const& sf_entry
: sf_map
) {
176 CompileType
const ct
= sf_entry
.second
.second
;
177 if (ct
== CompileType::BmiOnly
) {
181 auto const* sf
= sf_entry
.second
.first
;
182 if (!gt
->NeedDyndepForSource(sf
->GetLanguage(), config
, sf
)) {
186 auto full_file
= cmSystemTools::CollapseFullPath(sf
->GetFullPath());
187 auto obj_path
= cb
.ObjectFilePath(sf
, config
);
188 Json::Value
& tdi_source_info
= tdi_sources
[obj_path
] = Json::objectValue
;
190 tdi_source_info
["source"] = full_file
;
191 tdi_source_info
["language"] = sf
->GetLanguage();
197 Json::Value
CollationInformationDatabaseInfo(cmGeneratorTarget
const* gt
,
198 std::string
const& config
)
202 auto db_path
= gt
->BuildDatabasePath("CXX", config
);
203 if (!db_path
.empty()) {
204 db_info
["template-path"] = cmStrCat(db_path
, ".in");
205 db_info
["output"] = db_path
;
211 Json::Value
CollationInformationBmiInstallation(cmGeneratorTarget
const* gt
,
212 std::string
const& config
)
214 cmInstallCxxModuleBmiGenerator
const* bmi_gen
= nullptr;
215 for (auto const& ig
: gt
->Makefile
->GetInstallGenerators()) {
216 if (auto const* bmig
=
217 dynamic_cast<cmInstallCxxModuleBmiGenerator
const*>(ig
.get())) {
218 if (bmig
->GetTarget() == gt
) {
225 Json::Value tdi_bmi_info
= Json::objectValue
;
227 tdi_bmi_info
["permissions"] = bmi_gen
->GetFilePermissions();
228 tdi_bmi_info
["destination"] = bmi_gen
->GetDestination(config
);
229 const char* msg_level
= "";
230 switch (bmi_gen
->GetMessageLevel()) {
231 case cmInstallGenerator::MessageDefault
:
233 case cmInstallGenerator::MessageAlways
:
234 msg_level
= "MESSAGE_ALWAYS";
236 case cmInstallGenerator::MessageLazy
:
237 msg_level
= "MESSAGE_LAZY";
239 case cmInstallGenerator::MessageNever
:
240 msg_level
= "MESSAGE_NEVER";
243 tdi_bmi_info
["message-level"] = msg_level
;
244 tdi_bmi_info
["script-location"] = bmi_gen
->GetScriptLocation(config
);
248 return Json::nullValue
;
251 Json::Value
CollationInformationExports(cmGeneratorTarget
const* gt
)
253 Json::Value tdi_exports
= Json::arrayValue
;
254 std::string export_name
= gt
->GetExportName();
255 std::string fs_export_name
= gt
->GetFilesystemExportName();
257 auto const& all_install_exports
= gt
->GetGlobalGenerator()->GetExportSets();
258 for (auto const& exp
: all_install_exports
) {
259 // Ignore exports sets which are not for this target.
260 auto const& targets
= exp
.second
.GetTargetExports();
262 std::find_if(targets
.begin(), targets
.end(),
263 [gt
](std::unique_ptr
<cmTargetExport
> const& te
) {
264 return te
->Target
== gt
;
266 if (tgt_export
== targets
.end()) {
270 auto const* installs
= exp
.second
.GetInstallations();
271 for (auto const* install
: *installs
) {
272 Json::Value tdi_export_info
= Json::objectValue
;
274 auto const& ns
= install
->GetNamespace();
275 auto const& dest
= install
->GetDestination();
276 auto const& cxxm_dir
= install
->GetCxxModuleDirectory();
277 auto const& export_prefix
= install
->GetTempDir();
279 tdi_export_info
["namespace"] = ns
;
280 tdi_export_info
["export-name"] = export_name
;
281 tdi_export_info
["filesystem-export-name"] = fs_export_name
;
282 tdi_export_info
["destination"] = dest
;
283 tdi_export_info
["cxx-module-info-dir"] = cxxm_dir
;
284 tdi_export_info
["export-prefix"] = export_prefix
;
285 tdi_export_info
["install"] = true;
287 tdi_exports
.append(tdi_export_info
);
291 auto const& all_build_exports
=
292 gt
->GetGlobalGenerator()->GetBuildExportSets();
293 for (auto const& exp_entry
: all_build_exports
) {
294 auto const* exp
= exp_entry
.second
;
295 std::vector
<cmExportBuildFileGenerator::TargetExport
> targets
;
296 exp
->GetTargets(targets
);
298 // Ignore exports sets which are not for this target.
299 auto const& name
= gt
->GetName();
300 bool has_current_target
=
301 std::any_of(targets
.begin(), targets
.end(),
302 [name
](cmExportBuildFileGenerator::TargetExport
const& te
) {
303 return te
.Name
== name
;
305 if (!has_current_target
) {
309 Json::Value tdi_export_info
= Json::objectValue
;
311 auto const& ns
= exp
->GetNamespace();
312 auto const& main_fn
= exp
->GetMainExportFileName();
313 auto const& cxxm_dir
= exp
->GetCxxModuleDirectory();
314 auto dest
= cmsys::SystemTools::GetParentDirectory(main_fn
);
315 auto const& export_prefix
=
316 cmSystemTools::GetFilenamePath(exp
->GetMainExportFileName());
318 tdi_export_info
["namespace"] = ns
;
319 tdi_export_info
["export-name"] = export_name
;
320 tdi_export_info
["filesystem-export-name"] = fs_export_name
;
321 tdi_export_info
["destination"] = dest
;
322 tdi_export_info
["cxx-module-info-dir"] = cxxm_dir
;
323 tdi_export_info
["export-prefix"] = export_prefix
;
324 tdi_export_info
["install"] = false;
326 tdi_exports
.append(tdi_export_info
);
333 void cmDyndepCollation::AddCollationInformation(
334 Json::Value
& tdi
, cmGeneratorTarget
const* gt
, std::string
const& config
,
335 cmDyndepGeneratorCallbacks
const& cb
)
337 auto sourcesInfo
= CollationInformationSources(gt
, config
, cb
);
338 tdi
["sources"] = sourcesInfo
.Sources
;
339 tdi
["cxx-modules"] = sourcesInfo
.CxxModules
;
340 tdi
["database-info"] = CollationInformationDatabaseInfo(gt
, config
);
341 tdi
["bmi-installation"] = CollationInformationBmiInstallation(gt
, config
);
342 tdi
["exports"] = CollationInformationExports(gt
);
343 tdi
["config"] = config
;
348 std::string SourcePath
;
349 std::string Language
;
352 struct CxxModuleFileSet
355 bool BmiOnly
= false;
356 std::string RelativeDirectory
;
357 std::string SourcePath
;
359 cmFileSetVisibility Visibility
= cmFileSetVisibility::Private
;
360 cm::optional
<std::string
> Destination
;
363 struct CxxModuleDatabaseInfo
365 std::string TemplatePath
;
369 struct CxxModuleBmiInstall
371 std::string Component
;
372 std::string Destination
;
375 std::string Permissions
;
376 std::string MessageLevel
;
377 std::string ScriptLocation
;
380 struct CxxModuleExport
383 std::string FilesystemName
;
384 std::string Destination
;
386 std::string CxxModuleInfoDir
;
387 std::string Namespace
;
391 struct cmCxxModuleExportInfo
393 std::map
<std::string
, SourceInfo
> ObjectToSource
;
394 std::map
<std::string
, CxxModuleFileSet
> ObjectToFileSet
;
395 cm::optional
<CxxModuleDatabaseInfo
> DatabaseInfo
;
396 cm::optional
<CxxModuleBmiInstall
> BmiInstallation
;
397 std::vector
<CxxModuleExport
> Exports
;
401 void cmCxxModuleExportInfoDeleter::operator()(cmCxxModuleExportInfo
* ei
) const
406 std::unique_ptr
<cmCxxModuleExportInfo
, cmCxxModuleExportInfoDeleter
>
407 cmDyndepCollation::ParseExportInfo(Json::Value
const& tdi
)
410 std::unique_ptr
<cmCxxModuleExportInfo
, cmCxxModuleExportInfoDeleter
>(
411 new cmCxxModuleExportInfo
);
413 export_info
->Config
= tdi
["config"].asString();
414 if (export_info
->Config
.empty()) {
415 export_info
->Config
= "noconfig";
417 Json::Value
const& tdi_exports
= tdi
["exports"];
418 if (tdi_exports
.isArray()) {
419 for (auto const& tdi_export
: tdi_exports
) {
421 exp
.Install
= tdi_export
["install"].asBool();
422 exp
.Name
= tdi_export
["export-name"].asString();
423 exp
.FilesystemName
= tdi_export
["filesystem-export-name"].asString();
424 exp
.Destination
= tdi_export
["destination"].asString();
425 exp
.Prefix
= tdi_export
["export-prefix"].asString();
426 exp
.CxxModuleInfoDir
= tdi_export
["cxx-module-info-dir"].asString();
427 exp
.Namespace
= tdi_export
["namespace"].asString();
429 export_info
->Exports
.push_back(exp
);
432 auto const& database_info
= tdi
["database-info"];
433 if (database_info
.isObject()) {
434 CxxModuleDatabaseInfo db_info
;
436 db_info
.TemplatePath
= database_info
["template-path"].asString();
437 db_info
.Output
= database_info
["output"].asString();
439 export_info
->DatabaseInfo
= db_info
;
441 auto const& bmi_installation
= tdi
["bmi-installation"];
442 if (bmi_installation
.isObject()) {
443 CxxModuleBmiInstall bmi_install
;
445 bmi_install
.Component
= bmi_installation
["component"].asString();
446 bmi_install
.Destination
= bmi_installation
["destination"].asString();
447 bmi_install
.ExcludeFromAll
= bmi_installation
["exclude-from-all"].asBool();
448 bmi_install
.Optional
= bmi_installation
["optional"].asBool();
449 bmi_install
.Permissions
= bmi_installation
["permissions"].asString();
450 bmi_install
.MessageLevel
= bmi_installation
["message-level"].asString();
451 bmi_install
.ScriptLocation
=
452 bmi_installation
["script-location"].asString();
454 export_info
->BmiInstallation
= bmi_install
;
456 Json::Value
const& tdi_cxx_modules
= tdi
["cxx-modules"];
457 if (tdi_cxx_modules
.isObject()) {
458 for (auto i
= tdi_cxx_modules
.begin(); i
!= tdi_cxx_modules
.end(); ++i
) {
459 CxxModuleFileSet
& fsi
= export_info
->ObjectToFileSet
[i
.key().asString()];
460 auto const& tdi_cxx_module_info
= *i
;
461 fsi
.Name
= tdi_cxx_module_info
["name"].asString();
462 fsi
.BmiOnly
= tdi_cxx_module_info
["bmi-only"].asBool();
463 fsi
.RelativeDirectory
=
464 tdi_cxx_module_info
["relative-directory"].asString();
465 if (!fsi
.RelativeDirectory
.empty() &&
466 fsi
.RelativeDirectory
.back() != '/') {
467 fsi
.RelativeDirectory
= cmStrCat(fsi
.RelativeDirectory
, '/');
469 fsi
.SourcePath
= tdi_cxx_module_info
["source"].asString();
470 fsi
.Type
= tdi_cxx_module_info
["type"].asString();
471 fsi
.Visibility
= cmFileSetVisibilityFromName(
472 tdi_cxx_module_info
["visibility"].asString(), nullptr);
473 auto const& tdi_fs_dest
= tdi_cxx_module_info
["destination"];
474 if (tdi_fs_dest
.isString()) {
475 fsi
.Destination
= tdi_fs_dest
.asString();
479 Json::Value
const& tdi_sources
= tdi
["sources"];
480 if (tdi_sources
.isObject()) {
481 for (auto i
= tdi_sources
.begin(); i
!= tdi_sources
.end(); ++i
) {
482 SourceInfo
& si
= export_info
->ObjectToSource
[i
.key().asString()];
483 auto const& tdi_source
= *i
;
484 si
.SourcePath
= tdi_source
["source"].asString();
485 si
.Language
= tdi_source
["language"].asString();
492 bool cmDyndepCollation::WriteDyndepMetadata(
493 std::string
const& lang
, std::vector
<cmScanDepInfo
> const& objects
,
494 cmCxxModuleExportInfo
const& export_info
,
495 cmDyndepMetadataCallbacks
const& cb
)
497 // Only C++ supports any of the file-set or BMI installation considered
499 if (lang
!= "CXX"_s
) {
505 // Prepare the export information blocks.
506 std::string
const config_upper
=
507 cmSystemTools::UpperCase(export_info
.Config
);
509 std::pair
<std::unique_ptr
<cmGeneratedFileStream
>, CxxModuleExport
const*>>
511 for (auto const& exp
: export_info
.Exports
) {
512 std::unique_ptr
<cmGeneratedFileStream
> properties
;
514 std::string
const export_dir
=
515 cmStrCat(exp
.Prefix
, '/', exp
.CxxModuleInfoDir
, '/');
516 std::string
const property_file_path
=
517 cmStrCat(export_dir
, "target-", exp
.FilesystemName
, '-',
518 export_info
.Config
, ".cmake");
519 properties
= cm::make_unique
<cmGeneratedFileStream
>(property_file_path
);
521 // Set up the preamble.
522 *properties
<< "set_property(TARGET \"" << exp
.Namespace
<< exp
.Name
524 << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper
<< '\n';
526 exports
.emplace_back(std::move(properties
), &exp
);
529 std::unique_ptr
<cmBuildDatabase
> module_database
;
530 cmBuildDatabase::LookupTable build_database_lookup
;
531 if (export_info
.DatabaseInfo
) {
533 cmBuildDatabase::Load(export_info
.DatabaseInfo
->TemplatePath
);
534 if (module_database
) {
535 build_database_lookup
= module_database
->GenerateLookupTable();
537 cmSystemTools::Error(
538 cmStrCat("Failed to read the template build database ",
539 export_info
.DatabaseInfo
->TemplatePath
));
544 std::unique_ptr
<cmGeneratedFileStream
> bmi_install_script
;
545 if (export_info
.BmiInstallation
) {
546 bmi_install_script
= cm::make_unique
<cmGeneratedFileStream
>(
547 export_info
.BmiInstallation
->ScriptLocation
);
550 auto cmEscape
= [](cm::string_view str
) {
551 return cmOutputConverter::EscapeForCMake(
552 str
, cmOutputConverter::WrapQuotes::NoWrap
);
554 auto install_destination
=
555 [&cmEscape
](std::string
const& dest
) -> std::pair
<bool, std::string
> {
556 if (cmSystemTools::FileIsFullPath(dest
)) {
557 return std::make_pair(true, cmEscape(dest
));
559 return std::make_pair(false,
560 cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest
)));
563 // public/private requirement tracking.
564 std::set
<std::string
> private_modules
;
565 std::map
<std::string
, std::set
<std::string
>> public_source_requires
;
567 for (cmScanDepInfo
const& object
: objects
) {
568 // Convert to forward slashes.
569 auto output_path
= object
.PrimaryOutput
;
571 cmSystemTools::ConvertToUnixSlashes(output_path
);
574 auto source_info_itr
= export_info
.ObjectToSource
.find(output_path
);
576 // Update the module compilation database `requires` field if needed.
577 if (source_info_itr
!= export_info
.ObjectToSource
.end()) {
578 auto const& sourcePath
= source_info_itr
->second
.SourcePath
;
579 auto bdb_entry
= build_database_lookup
.find(sourcePath
);
580 if (bdb_entry
!= build_database_lookup
.end()) {
581 bdb_entry
->second
->Requires
.clear();
582 for (auto const& req
: object
.Requires
) {
583 bdb_entry
->second
->Requires
.push_back(req
.LogicalName
);
585 } else if (export_info
.DatabaseInfo
) {
586 cmSystemTools::Error(
587 cmStrCat("Failed to find module database entry for ", sourcePath
));
592 // Find the fileset for this object.
593 auto fileset_info_itr
= export_info
.ObjectToFileSet
.find(output_path
);
594 bool const has_provides
= !object
.Provides
.empty();
595 if (fileset_info_itr
== export_info
.ObjectToFileSet
.end()) {
596 // If it provides anything, it should have type `CXX_MODULES`
599 // Take the first module provided to provide context.
600 auto const& provides
= object
.Provides
[0];
601 cmSystemTools::Error(
602 cmStrCat("Output ", object
.PrimaryOutput
, " provides the `",
603 provides
.LogicalName
,
604 "` module but it is not found in a `FILE_SET` of type "
609 // This object file does not provide anything, so nothing more needs to
614 auto const& file_set
= fileset_info_itr
->second
;
616 // Update the module compilation database `provides` field if needed.
618 auto bdb_entry
= build_database_lookup
.find(file_set
.SourcePath
);
619 if (bdb_entry
!= build_database_lookup
.end()) {
620 // Clear the provides mapping; we will re-initialize it here.
621 if (!object
.Provides
.empty()) {
622 bdb_entry
->second
->Provides
.clear();
624 for (auto const& prov
: object
.Provides
) {
625 auto bmiName
= cb
.ModuleFile(prov
.LogicalName
);
627 bdb_entry
->second
->Provides
[prov
.LogicalName
] = *bmiName
;
629 cmSystemTools::Error(
630 cmStrCat("Failed to find BMI location for ", prov
.LogicalName
));
634 } else if (export_info
.DatabaseInfo
) {
635 cmSystemTools::Error(cmStrCat(
636 "Failed to find module database entry for ", file_set
.SourcePath
));
641 // Verify the fileset type for the object.
642 if (file_set
.Type
== "CXX_MODULES"_s
) {
644 cmSystemTools::Error(
645 cmStrCat("Output ", object
.PrimaryOutput
,
646 " is of type `CXX_MODULES` but does not provide a module "
647 "interface unit or partition"));
651 } else if (file_set
.Type
== "CXX_MODULE_HEADERS"_s
) {
655 auto const& provides
= object
.Provides
[0];
656 cmSystemTools::Error(cmStrCat(
657 "Source ", file_set
.SourcePath
, " provides the `",
658 provides
.LogicalName
, "` C++ module but is of type `", file_set
.Type
,
659 "` module but must be of type `CXX_MODULES`"));
663 // Not a C++ module; ignore.
667 if (!cmFileSetVisibilityIsForInterface(file_set
.Visibility
)) {
668 // Nothing needs to be conveyed about non-`PUBLIC` modules.
669 for (auto const& p
: object
.Provides
) {
670 private_modules
.insert(p
.LogicalName
);
675 // The module is public. Record what it directly requires.
677 auto& reqs
= public_source_requires
[file_set
.SourcePath
];
678 for (auto const& r
: object
.Requires
) {
679 reqs
.insert(r
.LogicalName
);
683 // Write out properties and install rules for any exports.
684 for (auto const& p
: object
.Provides
) {
685 bool bmi_dest_is_abs
= false;
686 std::string bmi_destination
;
687 if (export_info
.BmiInstallation
) {
689 install_destination(export_info
.BmiInstallation
->Destination
);
690 bmi_dest_is_abs
= dest
.first
;
691 bmi_destination
= cmStrCat(dest
.second
, '/');
694 std::string install_bmi_path
;
695 std::string build_bmi_path
;
696 auto m
= cb
.ModuleFile(p
.LogicalName
);
698 install_bmi_path
= cmStrCat(
699 bmi_destination
, cmEscape(cmSystemTools::GetFilenameName(*m
)));
700 build_bmi_path
= cmEscape(*m
);
703 for (auto const& exp
: exports
) {
704 std::string iface_source
;
705 if (exp
.second
->Install
&& file_set
.Destination
) {
706 auto dest
= install_destination(*file_set
.Destination
);
707 iface_source
= cmStrCat(
708 dest
.second
, '/', cmEscape(file_set
.RelativeDirectory
),
709 cmEscape(cmSystemTools::GetFilenameName(file_set
.SourcePath
)));
711 iface_source
= cmEscape(file_set
.SourcePath
);
714 std::string bmi_path
;
715 if (exp
.second
->Install
&& export_info
.BmiInstallation
) {
716 bmi_path
= install_bmi_path
;
717 } else if (!exp
.second
->Install
) {
718 bmi_path
= build_bmi_path
;
721 if (iface_source
.empty()) {
722 // No destination for the C++ module source; ignore this property
727 *exp
.first
<< " \"" << cmEscape(p
.LogicalName
) << '='
729 if (!bmi_path
.empty()) {
730 *exp
.first
<< ',' << bmi_path
;
732 *exp
.first
<< "\"\n";
735 if (bmi_install_script
) {
736 auto const& bmi_install
= *export_info
.BmiInstallation
;
738 *bmi_install_script
<< "if (CMAKE_INSTALL_COMPONENT STREQUAL \""
739 << cmEscape(bmi_install
.Component
) << '\"';
740 if (!bmi_install
.ExcludeFromAll
) {
741 *bmi_install_script
<< " OR NOT CMAKE_INSTALL_COMPONENT";
743 *bmi_install_script
<< ")\n";
744 *bmi_install_script
<< " file(INSTALL\n"
746 if (!bmi_dest_is_abs
) {
747 *bmi_install_script
<< "${CMAKE_INSTALL_PREFIX}/";
749 *bmi_install_script
<< cmEscape(bmi_install
.Destination
)
752 if (bmi_install
.Optional
) {
753 *bmi_install_script
<< " OPTIONAL\n";
755 if (!bmi_install
.MessageLevel
.empty()) {
756 *bmi_install_script
<< " " << bmi_install
.MessageLevel
<< "\n";
758 if (!bmi_install
.Permissions
.empty()) {
759 *bmi_install_script
<< " PERMISSIONS" << bmi_install
.Permissions
762 *bmi_install_script
<< " FILES \"" << *m
<< "\")\n";
763 if (bmi_dest_is_abs
) {
765 << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"
767 << cmEscape(cmSystemTools::GetFilenameName(*m
))
769 " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
771 " \"ABSOLUTE path INSTALL DESTINATION : "
772 "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
774 " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
775 " message(FATAL_ERROR\n"
776 " \"ABSOLUTE path INSTALL DESTINATION forbidden (by "
777 "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
780 *bmi_install_script
<< "endif ()\n";
785 // Add trailing parenthesis for the `set_property` call.
786 for (auto const& exp
: exports
) {
790 // Check that public sources only require public modules.
791 for (auto const& pub_reqs
: public_source_requires
) {
792 for (auto const& req
: pub_reqs
.second
) {
793 if (private_modules
.count(req
)) {
794 cmSystemTools::Error(cmStrCat(
795 "Public C++ module source `", pub_reqs
.first
, "` requires the `",
796 req
, "` C++ module which is provided by a private source"));
802 if (module_database
) {
803 if (module_database
->HasPlaceholderNames()) {
804 cmSystemTools::Error(
805 "Module compilation database still contains placeholders");
808 module_database
->Write(export_info
.DatabaseInfo
->Output
);
815 bool cmDyndepCollation::IsObjectPrivate(
816 std::string
const& object
, cmCxxModuleExportInfo
const& export_info
)
819 std::string output_path
= object
;
820 cmSystemTools::ConvertToUnixSlashes(output_path
);
822 std::string
const& output_path
= object
;
824 auto fileset_info_itr
= export_info
.ObjectToFileSet
.find(output_path
);
825 if (fileset_info_itr
== export_info
.ObjectToFileSet
.end()) {
828 auto const& file_set
= fileset_info_itr
->second
;
829 return !cmFileSetVisibilityIsForInterface(file_set
.Visibility
);
832 bool cmDyndepCollation::IsBmiOnly(cmCxxModuleExportInfo
const& exportInfo
,
833 std::string
const& object
)
836 auto object_path
= object
;
837 cmSystemTools::ConvertToUnixSlashes(object_path
);
839 auto const& object_path
= object
;
841 auto fs
= exportInfo
.ObjectToFileSet
.find(object_path
);
842 return (fs
!= exportInfo
.ObjectToFileSet
.end()) && fs
->second
.BmiOnly
;