|
|
@@ -0,0 +1,652 @@
|
|
|
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
+ file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
+
|
|
|
+#include "cmDyndepCollation.h"
|
|
|
+
|
|
|
+#include <algorithm>
|
|
|
+#include <map>
|
|
|
+#include <ostream>
|
|
|
+#include <set>
|
|
|
+#include <utility>
|
|
|
+#include <vector>
|
|
|
+
|
|
|
+#include <cm/memory>
|
|
|
+#include <cm/string_view>
|
|
|
+#include <cmext/string_view>
|
|
|
+
|
|
|
+#include <cm3p/json/value.h>
|
|
|
+
|
|
|
+#include "cmExportBuildFileGenerator.h"
|
|
|
+#include "cmExportSet.h"
|
|
|
+#include "cmFileSet.h"
|
|
|
+#include "cmGeneratedFileStream.h"
|
|
|
+#include "cmGeneratorExpression.h" // IWYU pragma: keep
|
|
|
+#include "cmGeneratorTarget.h"
|
|
|
+#include "cmGlobalGenerator.h"
|
|
|
+#include "cmInstallCxxModuleBmiGenerator.h"
|
|
|
+#include "cmInstallExportGenerator.h"
|
|
|
+#include "cmInstallFileSetGenerator.h"
|
|
|
+#include "cmInstallGenerator.h"
|
|
|
+#include "cmMakefile.h"
|
|
|
+#include "cmMessageType.h"
|
|
|
+#include "cmOutputConverter.h"
|
|
|
+#include "cmScanDepFormat.h"
|
|
|
+#include "cmSourceFile.h"
|
|
|
+#include "cmStringAlgorithms.h"
|
|
|
+#include "cmSystemTools.h"
|
|
|
+#include "cmTarget.h"
|
|
|
+#include "cmTargetExport.h"
|
|
|
+
|
|
|
+namespace {
|
|
|
+
|
|
|
+Json::Value CollationInformationCxxModules(
|
|
|
+ cmGeneratorTarget const* gt, std::string const& config,
|
|
|
+ cmDyndepGeneratorCallbacks const& cb)
|
|
|
+{
|
|
|
+ cmTarget const* tgt = gt->Target;
|
|
|
+ auto all_file_sets = tgt->GetAllFileSetNames();
|
|
|
+ Json::Value tdi_cxx_module_info = Json::objectValue;
|
|
|
+ for (auto const& file_set_name : all_file_sets) {
|
|
|
+ auto const* file_set = tgt->GetFileSet(file_set_name);
|
|
|
+ if (!file_set) {
|
|
|
+ gt->Makefile->IssueMessage(MessageType::INTERNAL_ERROR,
|
|
|
+ cmStrCat("Target \"", tgt->GetName(),
|
|
|
+ "\" is tracked to have file set \"",
|
|
|
+ file_set_name,
|
|
|
+ "\", but it was not found."));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ auto fs_type = file_set->GetType();
|
|
|
+ // We only care about C++ module sources here.
|
|
|
+ if (fs_type != "CXX_MODULES"_s) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto fileEntries = file_set->CompileFileEntries();
|
|
|
+ auto directoryEntries = file_set->CompileDirectoryEntries();
|
|
|
+
|
|
|
+ auto directories = file_set->EvaluateDirectoryEntries(
|
|
|
+ directoryEntries, gt->LocalGenerator, config, gt);
|
|
|
+ std::map<std::string, std::vector<std::string>> files_per_dirs;
|
|
|
+ for (auto const& entry : fileEntries) {
|
|
|
+ file_set->EvaluateFileEntry(directories, files_per_dirs, entry,
|
|
|
+ gt->LocalGenerator, config, gt);
|
|
|
+ }
|
|
|
+
|
|
|
+ std::map<std::string, cmSourceFile const*> sf_map;
|
|
|
+ {
|
|
|
+ std::vector<cmSourceFile const*> objectSources;
|
|
|
+ gt->GetObjectSources(objectSources, config);
|
|
|
+ for (auto const* sf : objectSources) {
|
|
|
+ auto full_path = sf->GetFullPath();
|
|
|
+ if (full_path.empty()) {
|
|
|
+ gt->Makefile->IssueMessage(
|
|
|
+ MessageType::INTERNAL_ERROR,
|
|
|
+ cmStrCat("Target \"", tgt->GetName(),
|
|
|
+ "\" has a full path-less source file."));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sf_map[full_path] = sf;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Json::Value fs_dest = Json::nullValue;
|
|
|
+ for (auto const& ig : gt->Makefile->GetInstallGenerators()) {
|
|
|
+ if (auto const* fsg =
|
|
|
+ dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) {
|
|
|
+ if (fsg->GetTarget() == gt && fsg->GetFileSet() == file_set) {
|
|
|
+ fs_dest = fsg->GetDestination(config);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (auto const& files_per_dir : files_per_dirs) {
|
|
|
+ for (auto const& file : files_per_dir.second) {
|
|
|
+ auto lookup = sf_map.find(file);
|
|
|
+ if (lookup == sf_map.end()) {
|
|
|
+ gt->Makefile->IssueMessage(
|
|
|
+ MessageType::INTERNAL_ERROR,
|
|
|
+ cmStrCat("Target \"", tgt->GetName(), "\" has source file \"",
|
|
|
+ file,
|
|
|
+ R"(" which is not in any of its "FILE_SET BASE_DIRS".)"));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto const* sf = lookup->second;
|
|
|
+
|
|
|
+ if (!sf) {
|
|
|
+ gt->Makefile->IssueMessage(
|
|
|
+ MessageType::INTERNAL_ERROR,
|
|
|
+ cmStrCat("Target \"", tgt->GetName(), "\" has source file \"",
|
|
|
+ file, "\" which has not been tracked properly."));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto obj_path = cb.ObjectFilePath(sf, config);
|
|
|
+ Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] =
|
|
|
+ Json::objectValue;
|
|
|
+
|
|
|
+ tdi_module_info["source"] = file;
|
|
|
+ tdi_module_info["relative-directory"] = files_per_dir.first;
|
|
|
+ tdi_module_info["name"] = file_set->GetName();
|
|
|
+ tdi_module_info["type"] = file_set->GetType();
|
|
|
+ tdi_module_info["visibility"] =
|
|
|
+ std::string(cmFileSetVisibilityToName(file_set->GetVisibility()));
|
|
|
+ tdi_module_info["destination"] = fs_dest;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return tdi_cxx_module_info;
|
|
|
+}
|
|
|
+
|
|
|
+Json::Value CollationInformationBmiInstallation(cmGeneratorTarget const* gt,
|
|
|
+ std::string const& config)
|
|
|
+{
|
|
|
+ cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr;
|
|
|
+ for (auto const& ig : gt->Makefile->GetInstallGenerators()) {
|
|
|
+ if (auto const* bmig =
|
|
|
+ dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) {
|
|
|
+ if (bmig->GetTarget() == gt) {
|
|
|
+ bmi_gen = bmig;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (bmi_gen) {
|
|
|
+ Json::Value tdi_bmi_info = Json::objectValue;
|
|
|
+
|
|
|
+ tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions();
|
|
|
+ tdi_bmi_info["destination"] = bmi_gen->GetDestination(config);
|
|
|
+ const char* msg_level = "";
|
|
|
+ switch (bmi_gen->GetMessageLevel()) {
|
|
|
+ case cmInstallGenerator::MessageDefault:
|
|
|
+ break;
|
|
|
+ case cmInstallGenerator::MessageAlways:
|
|
|
+ msg_level = "MESSAGE_ALWAYS";
|
|
|
+ break;
|
|
|
+ case cmInstallGenerator::MessageLazy:
|
|
|
+ msg_level = "MESSAGE_LAZY";
|
|
|
+ break;
|
|
|
+ case cmInstallGenerator::MessageNever:
|
|
|
+ msg_level = "MESSAGE_NEVER";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ tdi_bmi_info["message-level"] = msg_level;
|
|
|
+ tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config);
|
|
|
+
|
|
|
+ return tdi_bmi_info;
|
|
|
+ }
|
|
|
+ return Json::nullValue;
|
|
|
+}
|
|
|
+
|
|
|
+Json::Value CollationInformationExports(cmGeneratorTarget const* gt)
|
|
|
+{
|
|
|
+ Json::Value tdi_exports = Json::arrayValue;
|
|
|
+ std::string export_name = gt->GetExportName();
|
|
|
+
|
|
|
+ auto const& all_install_exports = gt->GetGlobalGenerator()->GetExportSets();
|
|
|
+ for (auto const& exp : all_install_exports) {
|
|
|
+ // Ignore exports sets which are not for this target.
|
|
|
+ auto const& targets = exp.second.GetTargetExports();
|
|
|
+ auto tgt_export =
|
|
|
+ std::find_if(targets.begin(), targets.end(),
|
|
|
+ [gt](std::unique_ptr<cmTargetExport> const& te) {
|
|
|
+ return te->Target == gt;
|
|
|
+ });
|
|
|
+ if (tgt_export == targets.end()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto const* installs = exp.second.GetInstallations();
|
|
|
+ for (auto const* install : *installs) {
|
|
|
+ Json::Value tdi_export_info = Json::objectValue;
|
|
|
+
|
|
|
+ auto const& ns = install->GetNamespace();
|
|
|
+ auto const& dest = install->GetDestination();
|
|
|
+ auto const& cxxm_dir = install->GetCxxModuleDirectory();
|
|
|
+ auto const& export_prefix = install->GetTempDir();
|
|
|
+
|
|
|
+ tdi_export_info["namespace"] = ns;
|
|
|
+ tdi_export_info["export-name"] = export_name;
|
|
|
+ tdi_export_info["destination"] = dest;
|
|
|
+ tdi_export_info["cxx-module-info-dir"] = cxxm_dir;
|
|
|
+ tdi_export_info["export-prefix"] = export_prefix;
|
|
|
+ tdi_export_info["install"] = true;
|
|
|
+
|
|
|
+ tdi_exports.append(tdi_export_info);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ auto const& all_build_exports = gt->Makefile->GetExportBuildFileGenerators();
|
|
|
+ for (auto const& exp : all_build_exports) {
|
|
|
+ std::vector<std::string> targets;
|
|
|
+ exp->GetTargets(targets);
|
|
|
+
|
|
|
+ // Ignore exports sets which are not for this target.
|
|
|
+ auto const& name = gt->GetName();
|
|
|
+ bool has_current_target =
|
|
|
+ std::any_of(targets.begin(), targets.end(),
|
|
|
+ [name](std::string const& tname) { return tname == name; });
|
|
|
+ if (!has_current_target) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Json::Value tdi_export_info = Json::objectValue;
|
|
|
+
|
|
|
+ auto const& ns = exp->GetNamespace();
|
|
|
+ auto const& main_fn = exp->GetMainExportFileName();
|
|
|
+ auto const& cxxm_dir = exp->GetCxxModuleDirectory();
|
|
|
+ auto dest = cmsys::SystemTools::GetParentDirectory(main_fn);
|
|
|
+ auto const& export_prefix =
|
|
|
+ cmSystemTools::GetFilenamePath(exp->GetMainExportFileName());
|
|
|
+
|
|
|
+ tdi_export_info["namespace"] = ns;
|
|
|
+ tdi_export_info["export-name"] = export_name;
|
|
|
+ tdi_export_info["destination"] = dest;
|
|
|
+ tdi_export_info["cxx-module-info-dir"] = cxxm_dir;
|
|
|
+ tdi_export_info["export-prefix"] = export_prefix;
|
|
|
+ tdi_export_info["install"] = false;
|
|
|
+
|
|
|
+ tdi_exports.append(tdi_export_info);
|
|
|
+ }
|
|
|
+
|
|
|
+ return tdi_exports;
|
|
|
+}
|
|
|
+}
|
|
|
+
|
|
|
+void cmDyndepCollation::AddCollationInformation(
|
|
|
+ Json::Value& tdi, cmGeneratorTarget const* gt, std::string const& config,
|
|
|
+ cmDyndepGeneratorCallbacks const& cb)
|
|
|
+{
|
|
|
+ tdi["cxx-modules"] = CollationInformationCxxModules(gt, config, cb);
|
|
|
+ tdi["bmi-installation"] = CollationInformationBmiInstallation(gt, config);
|
|
|
+ tdi["exports"] = CollationInformationExports(gt);
|
|
|
+ tdi["config"] = config;
|
|
|
+}
|
|
|
+
|
|
|
+struct CxxModuleFileSet
|
|
|
+{
|
|
|
+ std::string Name;
|
|
|
+ std::string RelativeDirectory;
|
|
|
+ std::string SourcePath;
|
|
|
+ std::string Type;
|
|
|
+ cmFileSetVisibility Visibility;
|
|
|
+ cm::optional<std::string> Destination;
|
|
|
+};
|
|
|
+
|
|
|
+struct CxxModuleBmiInstall
|
|
|
+{
|
|
|
+ std::string Component;
|
|
|
+ std::string Destination;
|
|
|
+ bool ExcludeFromAll;
|
|
|
+ bool Optional;
|
|
|
+ std::string Permissions;
|
|
|
+ std::string MessageLevel;
|
|
|
+ std::string ScriptLocation;
|
|
|
+};
|
|
|
+
|
|
|
+struct CxxModuleExport
|
|
|
+{
|
|
|
+ std::string Name;
|
|
|
+ std::string Destination;
|
|
|
+ std::string Prefix;
|
|
|
+ std::string CxxModuleInfoDir;
|
|
|
+ std::string Namespace;
|
|
|
+ bool Install;
|
|
|
+};
|
|
|
+
|
|
|
+struct cmCxxModuleExportInfo
|
|
|
+{
|
|
|
+ std::map<std::string, CxxModuleFileSet> ObjectToFileSet;
|
|
|
+ cm::optional<CxxModuleBmiInstall> BmiInstallation;
|
|
|
+ std::vector<CxxModuleExport> Exports;
|
|
|
+ std::string Config;
|
|
|
+};
|
|
|
+
|
|
|
+void cmCxxModuleExportInfoDeleter::operator()(cmCxxModuleExportInfo* ei) const
|
|
|
+{
|
|
|
+ delete ei;
|
|
|
+}
|
|
|
+
|
|
|
+std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>
|
|
|
+cmDyndepCollation::ParseExportInfo(Json::Value const& tdi)
|
|
|
+{
|
|
|
+ auto export_info =
|
|
|
+ std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>(
|
|
|
+ new cmCxxModuleExportInfo);
|
|
|
+
|
|
|
+ export_info->Config = tdi["config"].asString();
|
|
|
+ if (export_info->Config.empty()) {
|
|
|
+ export_info->Config = "noconfig";
|
|
|
+ }
|
|
|
+ Json::Value const& tdi_exports = tdi["exports"];
|
|
|
+ if (tdi_exports.isArray()) {
|
|
|
+ for (auto const& tdi_export : tdi_exports) {
|
|
|
+ CxxModuleExport exp;
|
|
|
+ exp.Install = tdi_export["install"].asBool();
|
|
|
+ exp.Name = tdi_export["export-name"].asString();
|
|
|
+ exp.Destination = tdi_export["destination"].asString();
|
|
|
+ exp.Prefix = tdi_export["export-prefix"].asString();
|
|
|
+ exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString();
|
|
|
+ exp.Namespace = tdi_export["namespace"].asString();
|
|
|
+
|
|
|
+ export_info->Exports.push_back(exp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ auto const& bmi_installation = tdi["bmi-installation"];
|
|
|
+ if (bmi_installation.isObject()) {
|
|
|
+ CxxModuleBmiInstall bmi_install;
|
|
|
+
|
|
|
+ bmi_install.Component = bmi_installation["component"].asString();
|
|
|
+ bmi_install.Destination = bmi_installation["destination"].asString();
|
|
|
+ bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool();
|
|
|
+ bmi_install.Optional = bmi_installation["optional"].asBool();
|
|
|
+ bmi_install.Permissions = bmi_installation["permissions"].asString();
|
|
|
+ bmi_install.MessageLevel = bmi_installation["message-level"].asString();
|
|
|
+ bmi_install.ScriptLocation =
|
|
|
+ bmi_installation["script-location"].asString();
|
|
|
+
|
|
|
+ export_info->BmiInstallation = bmi_install;
|
|
|
+ }
|
|
|
+ Json::Value const& tdi_cxx_modules = tdi["cxx-modules"];
|
|
|
+ if (tdi_cxx_modules.isObject()) {
|
|
|
+ for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) {
|
|
|
+ CxxModuleFileSet& fsi = export_info->ObjectToFileSet[i.key().asString()];
|
|
|
+ auto const& tdi_cxx_module_info = *i;
|
|
|
+ fsi.Name = tdi_cxx_module_info["name"].asString();
|
|
|
+ fsi.RelativeDirectory =
|
|
|
+ tdi_cxx_module_info["relative-directory"].asString();
|
|
|
+ fsi.SourcePath = tdi_cxx_module_info["source"].asString();
|
|
|
+ fsi.Type = tdi_cxx_module_info["type"].asString();
|
|
|
+ fsi.Visibility = cmFileSetVisibilityFromName(
|
|
|
+ tdi_cxx_module_info["visibility"].asString(), nullptr);
|
|
|
+ auto const& tdi_fs_dest = tdi_cxx_module_info["destination"];
|
|
|
+ if (tdi_fs_dest.isString()) {
|
|
|
+ fsi.Destination = tdi_fs_dest.asString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return export_info;
|
|
|
+}
|
|
|
+
|
|
|
+bool cmDyndepCollation::WriteDyndepMetadata(
|
|
|
+ std::string const& lang, std::vector<cmScanDepInfo> const& objects,
|
|
|
+ cmCxxModuleExportInfo const& export_info,
|
|
|
+ cmDyndepMetadataCallbacks const& cb)
|
|
|
+{
|
|
|
+ // Only C++ supports any of the file-set or BMI installation considered
|
|
|
+ // below.
|
|
|
+ if (lang != "CXX"_s) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool result = true;
|
|
|
+
|
|
|
+ // Prepare the export information blocks.
|
|
|
+ std::string const config_upper =
|
|
|
+ cmSystemTools::UpperCase(export_info.Config);
|
|
|
+ std::vector<
|
|
|
+ std::pair<std::unique_ptr<cmGeneratedFileStream>, CxxModuleExport const*>>
|
|
|
+ exports;
|
|
|
+ for (auto const& exp : export_info.Exports) {
|
|
|
+ std::unique_ptr<cmGeneratedFileStream> properties;
|
|
|
+
|
|
|
+ std::string const export_dir =
|
|
|
+ cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/');
|
|
|
+ std::string const property_file_path = cmStrCat(
|
|
|
+ export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake");
|
|
|
+ properties = cm::make_unique<cmGeneratedFileStream>(property_file_path);
|
|
|
+
|
|
|
+ // Set up the preamble.
|
|
|
+ *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name
|
|
|
+ << "\"\n"
|
|
|
+ << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper << '\n';
|
|
|
+
|
|
|
+ exports.emplace_back(std::move(properties), &exp);
|
|
|
+ }
|
|
|
+
|
|
|
+ std::unique_ptr<cmGeneratedFileStream> bmi_install_script;
|
|
|
+ if (export_info.BmiInstallation) {
|
|
|
+ bmi_install_script = cm::make_unique<cmGeneratedFileStream>(
|
|
|
+ export_info.BmiInstallation->ScriptLocation);
|
|
|
+ }
|
|
|
+
|
|
|
+ auto cmEscape = [](cm::string_view str) {
|
|
|
+ return cmOutputConverter::EscapeForCMake(
|
|
|
+ str, cmOutputConverter::WrapQuotes::NoWrap);
|
|
|
+ };
|
|
|
+ auto install_destination =
|
|
|
+ [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> {
|
|
|
+ if (cmSystemTools::FileIsFullPath(dest)) {
|
|
|
+ return std::make_pair(true, cmEscape(dest));
|
|
|
+ }
|
|
|
+ return std::make_pair(false,
|
|
|
+ cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest)));
|
|
|
+ };
|
|
|
+
|
|
|
+ // public/private requirement tracking.
|
|
|
+ std::set<std::string> private_modules;
|
|
|
+ std::map<std::string, std::set<std::string>> public_source_requires;
|
|
|
+
|
|
|
+ for (cmScanDepInfo const& object : objects) {
|
|
|
+ // Convert to forward slashes.
|
|
|
+ auto output_path = object.PrimaryOutput;
|
|
|
+#ifdef _WIN32
|
|
|
+ cmSystemTools::ConvertToUnixSlashes(output_path);
|
|
|
+#endif
|
|
|
+ // Find the fileset for this object.
|
|
|
+ auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path);
|
|
|
+ bool const has_provides = !object.Provides.empty();
|
|
|
+ if (fileset_info_itr == export_info.ObjectToFileSet.end()) {
|
|
|
+ // If it provides anything, it should have a `CXX_MODULES` or
|
|
|
+ // `CXX_MODULE_INTERNAL_PARTITIONS` type and be present.
|
|
|
+ if (has_provides) {
|
|
|
+ // Take the first module provided to provide context.
|
|
|
+ auto const& provides = object.Provides[0];
|
|
|
+ char const* ok_types = "`CXX_MODULES`";
|
|
|
+ if (provides.LogicalName.find(':') != std::string::npos) {
|
|
|
+ ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
|
|
|
+ "it is not `export`ed)";
|
|
|
+ }
|
|
|
+ cmSystemTools::Error(cmStrCat(
|
|
|
+ "Output ", object.PrimaryOutput, " provides the `",
|
|
|
+ provides.LogicalName,
|
|
|
+ "` module but it is not found in a `FILE_SET` of type ", ok_types));
|
|
|
+ result = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This object file does not provide anything, so nothing more needs to
|
|
|
+ // be done.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto const& file_set = fileset_info_itr->second;
|
|
|
+
|
|
|
+ // Verify the fileset type for the object.
|
|
|
+ if (file_set.Type == "CXX_MODULES"_s) {
|
|
|
+ if (!has_provides) {
|
|
|
+ cmSystemTools::Error(
|
|
|
+ cmStrCat("Output ", object.PrimaryOutput,
|
|
|
+ " is of type `CXX_MODULES` but does not provide a module"));
|
|
|
+ result = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ } else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) {
|
|
|
+ if (!has_provides) {
|
|
|
+ cmSystemTools::Error(
|
|
|
+ cmStrCat("Source ", file_set.SourcePath,
|
|
|
+ " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
|
|
|
+ "provide a module"));
|
|
|
+ result = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ auto const& provides = object.Provides[0];
|
|
|
+ if (provides.LogicalName.find(':') == std::string::npos) {
|
|
|
+ cmSystemTools::Error(
|
|
|
+ cmStrCat("Source ", file_set.SourcePath,
|
|
|
+ " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
|
|
|
+ "provide a module partition"));
|
|
|
+ result = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) {
|
|
|
+ // TODO.
|
|
|
+ } else {
|
|
|
+ if (has_provides) {
|
|
|
+ auto const& provides = object.Provides[0];
|
|
|
+ char const* ok_types = "`CXX_MODULES`";
|
|
|
+ if (provides.LogicalName.find(':') != std::string::npos) {
|
|
|
+ ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
|
|
|
+ "it is not `export`ed)";
|
|
|
+ }
|
|
|
+ cmSystemTools::Error(
|
|
|
+ cmStrCat("Source ", file_set.SourcePath, " provides the `",
|
|
|
+ provides.LogicalName, "` C++ module but is of type `",
|
|
|
+ file_set.Type, "` module but must be of type ", ok_types));
|
|
|
+ result = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Not a C++ module; ignore.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) {
|
|
|
+ // Nothing needs to be conveyed about non-`PUBLIC` modules.
|
|
|
+ for (auto const& p : object.Provides) {
|
|
|
+ private_modules.insert(p.LogicalName);
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The module is public. Record what it directly requires.
|
|
|
+ {
|
|
|
+ auto& reqs = public_source_requires[file_set.SourcePath];
|
|
|
+ for (auto const& r : object.Requires) {
|
|
|
+ reqs.insert(r.LogicalName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Write out properties and install rules for any exports.
|
|
|
+ for (auto const& p : object.Provides) {
|
|
|
+ bool bmi_dest_is_abs = false;
|
|
|
+ std::string bmi_destination;
|
|
|
+ if (export_info.BmiInstallation) {
|
|
|
+ auto dest =
|
|
|
+ install_destination(export_info.BmiInstallation->Destination);
|
|
|
+ bmi_dest_is_abs = dest.first;
|
|
|
+ bmi_destination = cmStrCat(dest.second, '/');
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string install_bmi_path;
|
|
|
+ std::string build_bmi_path;
|
|
|
+ auto m = cb.ModuleFile(p.LogicalName);
|
|
|
+ if (m) {
|
|
|
+ install_bmi_path = cmStrCat(
|
|
|
+ bmi_destination, cmEscape(cmSystemTools::GetFilenameName(*m)));
|
|
|
+ build_bmi_path = cmEscape(*m);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (auto const& exp : exports) {
|
|
|
+ std::string iface_source;
|
|
|
+ if (exp.second->Install && file_set.Destination) {
|
|
|
+ auto dest = install_destination(*file_set.Destination);
|
|
|
+ iface_source = cmStrCat(
|
|
|
+ dest.second, '/', cmEscape(file_set.RelativeDirectory),
|
|
|
+ cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath)));
|
|
|
+ } else {
|
|
|
+ iface_source = cmEscape(file_set.SourcePath);
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string bmi_path;
|
|
|
+ if (exp.second->Install && export_info.BmiInstallation) {
|
|
|
+ bmi_path = install_bmi_path;
|
|
|
+ } else if (!exp.second->Install) {
|
|
|
+ bmi_path = build_bmi_path;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (iface_source.empty()) {
|
|
|
+ // No destination for the C++ module source; ignore this property
|
|
|
+ // value.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ *exp.first << " \"" << cmEscape(p.LogicalName) << '='
|
|
|
+ << iface_source;
|
|
|
+ if (!bmi_path.empty()) {
|
|
|
+ *exp.first << ',' << bmi_path;
|
|
|
+ }
|
|
|
+ *exp.first << "\"\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bmi_install_script) {
|
|
|
+ auto const& bmi_install = *export_info.BmiInstallation;
|
|
|
+
|
|
|
+ *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \""
|
|
|
+ << cmEscape(bmi_install.Component) << '\"';
|
|
|
+ if (!bmi_install.ExcludeFromAll) {
|
|
|
+ *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT";
|
|
|
+ }
|
|
|
+ *bmi_install_script << ")\n";
|
|
|
+ *bmi_install_script << " file(INSTALL\n"
|
|
|
+ " DESTINATION \"";
|
|
|
+ if (!bmi_dest_is_abs) {
|
|
|
+ *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/";
|
|
|
+ }
|
|
|
+ *bmi_install_script << cmEscape(bmi_install.Destination)
|
|
|
+ << "\"\n"
|
|
|
+ " TYPE FILE\n";
|
|
|
+ if (bmi_install.Optional) {
|
|
|
+ *bmi_install_script << " OPTIONAL\n";
|
|
|
+ }
|
|
|
+ if (!bmi_install.MessageLevel.empty()) {
|
|
|
+ *bmi_install_script << " " << bmi_install.MessageLevel << "\n";
|
|
|
+ }
|
|
|
+ if (!bmi_install.Permissions.empty()) {
|
|
|
+ *bmi_install_script << " PERMISSIONS" << bmi_install.Permissions
|
|
|
+ << "\n";
|
|
|
+ }
|
|
|
+ *bmi_install_script << " FILES \"" << *m << "\")\n";
|
|
|
+ if (bmi_dest_is_abs) {
|
|
|
+ *bmi_install_script
|
|
|
+ << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"
|
|
|
+ " \""
|
|
|
+ << cmEscape(cmSystemTools::GetFilenameName(*m))
|
|
|
+ << "\")\n"
|
|
|
+ " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
|
|
|
+ " message(WARNING\n"
|
|
|
+ " \"ABSOLUTE path INSTALL DESTINATION : "
|
|
|
+ "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
|
|
|
+ " endif ()\n"
|
|
|
+ " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
|
|
|
+ " message(FATAL_ERROR\n"
|
|
|
+ " \"ABSOLUTE path INSTALL DESTINATION forbidden (by "
|
|
|
+ "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
|
|
|
+ " endif ()\n";
|
|
|
+ }
|
|
|
+ *bmi_install_script << "endif ()\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add trailing parenthesis for the `set_property` call.
|
|
|
+ for (auto const& exp : exports) {
|
|
|
+ *exp.first << ")\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check that public sources only require public modules.
|
|
|
+ for (auto const& pub_reqs : public_source_requires) {
|
|
|
+ for (auto const& req : pub_reqs.second) {
|
|
|
+ if (private_modules.count(req)) {
|
|
|
+ cmSystemTools::Error(cmStrCat(
|
|
|
+ "Public C++ module source `", pub_reqs.first, "` requires the `",
|
|
|
+ req, "` C++ module which is provided by a private source"));
|
|
|
+ result = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|