mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-05 21:31:08 -06:00
export: Factor out CMake-specific export generation (2/2)
In order to support generation of Common Package Specifications, the mechanisms CMake uses to export package information need to be made more abstract. The prior commits began this refactoring; this continues by (actually) restructuring the classes used to generate the actual export files. To minimize churn, this introduces virtual base classes and diamond inheritance in order to separate logic which is format-agnostic but depends on the export mode (build-tree versus install-tree) from logic which is format-specific but mode-agnostic. This could probably be refactored further to use helper classes instead, and a future commit may do that, however an initial attempt to do that was proving even more invasive, such that this approach was deemed more manageable. While we're at it, add 'const' in more places where possible.
This commit is contained in:
@@ -3,20 +3,13 @@
|
||||
#include "cmExportBuildFileGenerator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/string_view>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmCryptoHash.h"
|
||||
#include "cmExportSet.h"
|
||||
#include "cmFileSet.h"
|
||||
#include "cmGeneratedFileStream.h"
|
||||
#include "cmGeneratorExpression.h"
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
@@ -24,11 +17,8 @@
|
||||
#include "cmLocalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmMessageType.h"
|
||||
#include "cmOutputConverter.h"
|
||||
#include "cmPolicies.h"
|
||||
#include "cmStateTypes.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmTarget.h"
|
||||
#include "cmTargetExport.h"
|
||||
#include "cmValue.h"
|
||||
@@ -50,195 +40,6 @@ void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
|
||||
}
|
||||
}
|
||||
|
||||
bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
|
||||
{
|
||||
{
|
||||
std::string expectedTargets;
|
||||
std::string sep;
|
||||
std::vector<TargetExport> targets;
|
||||
bool generatedInterfaceRequired = false;
|
||||
this->GetTargets(targets);
|
||||
for (auto const& tei : targets) {
|
||||
cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
|
||||
expectedTargets += sep + this->Namespace + te->GetExportName();
|
||||
sep = " ";
|
||||
if (this->ExportedTargets.insert(te).second) {
|
||||
this->Exports.emplace_back(te, tei.XcFrameworkLocation);
|
||||
} else {
|
||||
std::ostringstream e;
|
||||
e << "given target \"" << te->GetName() << "\" more than once.";
|
||||
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
||||
MessageType::FATAL_ERROR, e.str(),
|
||||
this->LG->GetMakefile()->GetBacktrace());
|
||||
return false;
|
||||
}
|
||||
generatedInterfaceRequired |=
|
||||
this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
|
||||
}
|
||||
|
||||
if (generatedInterfaceRequired) {
|
||||
this->SetRequiredCMakeVersion(3, 0, 0);
|
||||
}
|
||||
this->GenerateExpectedTargetsCode(os, expectedTargets);
|
||||
}
|
||||
|
||||
// Create all the imported targets.
|
||||
for (auto const& exp : this->Exports) {
|
||||
cmGeneratorTarget* gte = exp.Target;
|
||||
this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
|
||||
|
||||
gte->Target->AppendBuildInterfaceIncludes();
|
||||
|
||||
ImportPropertyMap properties;
|
||||
|
||||
this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
|
||||
properties);
|
||||
|
||||
std::string errorMessage;
|
||||
if (!this->PopulateCxxModuleExportProperties(
|
||||
gte, properties, cmGeneratorExpression::BuildInterface, {},
|
||||
errorMessage)) {
|
||||
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
||||
MessageType::FATAL_ERROR, errorMessage,
|
||||
this->LG->GetMakefile()->GetBacktrace());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->PopulateExportProperties(gte, properties, errorMessage)) {
|
||||
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
||||
MessageType::FATAL_ERROR, errorMessage,
|
||||
this->LG->GetMakefile()->GetBacktrace());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool const newCMP0022Behavior =
|
||||
gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
|
||||
gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
|
||||
if (newCMP0022Behavior) {
|
||||
this->PopulateInterfaceLinkLibrariesProperty(
|
||||
gte, cmGeneratorExpression::BuildInterface, properties);
|
||||
}
|
||||
this->PopulateCompatibleInterfaceProperties(gte, properties);
|
||||
this->PopulateCustomTransitiveInterfaceProperties(
|
||||
gte, cmGeneratorExpression::BuildInterface, properties);
|
||||
|
||||
this->GenerateInterfaceProperties(gte, os, properties);
|
||||
|
||||
this->GenerateTargetFileSets(gte, os);
|
||||
}
|
||||
|
||||
std::string cxx_modules_name;
|
||||
if (this->ExportSet) {
|
||||
cxx_modules_name = this->ExportSet->GetName();
|
||||
} else {
|
||||
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
|
||||
constexpr std::size_t HASH_TRUNCATION = 12;
|
||||
for (auto const& target : this->Targets) {
|
||||
hasher.Append(target.Name);
|
||||
}
|
||||
cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
|
||||
}
|
||||
|
||||
this->GenerateCxxModuleInformation(cxx_modules_name, os);
|
||||
|
||||
// Generate import file content for each configuration.
|
||||
for (std::string const& c : this->Configurations) {
|
||||
this->GenerateImportConfig(os, c);
|
||||
}
|
||||
|
||||
// Generate import file content for each configuration.
|
||||
for (std::string const& c : this->Configurations) {
|
||||
this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c);
|
||||
}
|
||||
|
||||
this->GenerateMissingTargetsCheckCode(os);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
|
||||
std::ostream& os, std::string const& config, std::string const& suffix)
|
||||
{
|
||||
for (auto const& exp : this->Exports) {
|
||||
cmGeneratorTarget* target = exp.Target;
|
||||
|
||||
// Collect import properties for this target.
|
||||
ImportPropertyMap properties;
|
||||
|
||||
if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
|
||||
this->SetImportLocationProperty(config, suffix, target, properties);
|
||||
}
|
||||
if (!properties.empty()) {
|
||||
// Get the rest of the target details.
|
||||
if (this->GetExportTargetType(target) !=
|
||||
cmStateEnums::INTERFACE_LIBRARY) {
|
||||
this->SetImportDetailProperties(config, suffix, target, properties);
|
||||
this->SetImportLinkInterface(config, suffix,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
target, properties);
|
||||
}
|
||||
|
||||
// TODO: PUBLIC_HEADER_LOCATION
|
||||
// This should wait until the build feature propagation stuff
|
||||
// is done. Then this can be a propagated include directory.
|
||||
// this->GenerateImportProperty(config, te->HeaderGenerator,
|
||||
// properties);
|
||||
|
||||
// Generate code in the export file.
|
||||
std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
|
||||
if (!importedXcFrameworkLocation.empty()) {
|
||||
importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
|
||||
importedXcFrameworkLocation,
|
||||
cmGeneratorExpression::PreprocessContext::BuildInterface);
|
||||
importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
|
||||
importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
|
||||
exp.Target, nullptr, exp.Target);
|
||||
if (!importedXcFrameworkLocation.empty() &&
|
||||
!cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
|
||||
importedXcFrameworkLocation =
|
||||
cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
|
||||
importedXcFrameworkLocation);
|
||||
}
|
||||
}
|
||||
this->GenerateImportPropertyCode(os, config, suffix, target, properties,
|
||||
importedXcFrameworkLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
|
||||
cmGeneratorTarget const* target) const
|
||||
{
|
||||
@@ -311,13 +112,32 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
|
||||
}
|
||||
}
|
||||
|
||||
bool cmExportBuildFileGenerator::CollectExports(
|
||||
std::function<void(cmGeneratorTarget const*)> visitor)
|
||||
{
|
||||
std::vector<TargetExport> targets;
|
||||
this->GetTargets(targets);
|
||||
for (auto const& tei : targets) {
|
||||
cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
|
||||
if (this->ExportedTargets.insert(te).second) {
|
||||
this->Exports.emplace_back(te, tei.XcFrameworkLocation);
|
||||
visitor(te);
|
||||
} else {
|
||||
this->ComplainAboutDuplicateTarget(te->GetName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cmExportBuildFileGenerator::HandleMissingTarget(
|
||||
std::string& link_libs, cmGeneratorTarget const* depender,
|
||||
cmGeneratorTarget* dependee)
|
||||
{
|
||||
// The target is not in the export.
|
||||
if (!this->AppendMode) {
|
||||
std::string const name = dependee->GetName();
|
||||
std::string const& name = dependee->GetName();
|
||||
cmGlobalGenerator* gg =
|
||||
dependee->GetLocalGenerator()->GetGlobalGenerator();
|
||||
auto exportInfo = this->FindBuildExportInfo(gg, name);
|
||||
@@ -383,7 +203,7 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
|
||||
|
||||
void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
|
||||
cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
|
||||
std::vector<std::string> const& exportFiles)
|
||||
std::vector<std::string> const& exportFiles) const
|
||||
{
|
||||
std::ostringstream e;
|
||||
e << "export called with target \"" << depender->GetName()
|
||||
@@ -399,8 +219,22 @@ void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
|
||||
<< dependee->GetName() << "\" target to a single export.";
|
||||
}
|
||||
|
||||
this->ReportError(e.str());
|
||||
}
|
||||
|
||||
void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget(
|
||||
std::string const& targetName) const
|
||||
{
|
||||
std::ostringstream e;
|
||||
e << "given target \"" << targetName << "\" more than once.";
|
||||
this->ReportError(e.str());
|
||||
}
|
||||
|
||||
void cmExportBuildFileGenerator::ReportError(
|
||||
std::string const& errorMessage) const
|
||||
{
|
||||
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
||||
MessageType::FATAL_ERROR, e.str(),
|
||||
MessageType::FATAL_ERROR, errorMessage,
|
||||
this->LG->GetMakefile()->GetBacktrace());
|
||||
}
|
||||
|
||||
@@ -417,185 +251,22 @@ std::string cmExportBuildFileGenerator::InstallNameDir(
|
||||
return install_name_dir;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool EntryIsContextSensitive(
|
||||
std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
|
||||
bool cmExportBuildFileGenerator::PopulateInterfaceProperties(
|
||||
cmGeneratorTarget const* target, ImportPropertyMap& properties)
|
||||
{
|
||||
return cge->GetHadContextSensitiveCondition();
|
||||
}
|
||||
}
|
||||
|
||||
std::string cmExportBuildFileGenerator::GetFileSetDirectories(
|
||||
cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/)
|
||||
{
|
||||
std::vector<std::string> resultVector;
|
||||
|
||||
auto configs =
|
||||
gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
|
||||
auto directoryEntries = fileSet->CompileDirectoryEntries();
|
||||
|
||||
for (auto const& config : configs) {
|
||||
auto directories = fileSet->EvaluateDirectoryEntries(
|
||||
directoryEntries, gte->LocalGenerator, config, gte);
|
||||
|
||||
bool const contextSensitive =
|
||||
std::any_of(directoryEntries.begin(), directoryEntries.end(),
|
||||
EntryIsContextSensitive);
|
||||
|
||||
auto const& type = fileSet->GetType();
|
||||
// C++ modules do not support interface file sets which are dependent upon
|
||||
// the configuration.
|
||||
if (contextSensitive && type == "CXX_MODULES"_s) {
|
||||
auto* mf = this->LG->GetMakefile();
|
||||
std::ostringstream e;
|
||||
e << "The \"" << gte->GetName() << "\" target's interface file set \""
|
||||
<< fileSet->GetName() << "\" of type \"" << type
|
||||
<< "\" contains context-sensitive base directory entries which is not "
|
||||
"supported.";
|
||||
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
for (auto const& directory : directories) {
|
||||
auto dest = cmOutputConverter::EscapeForCMake(
|
||||
directory, cmOutputConverter::WrapQuotes::NoWrap);
|
||||
|
||||
if (contextSensitive && configs.size() != 1) {
|
||||
resultVector.push_back(
|
||||
cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
|
||||
} else {
|
||||
resultVector.emplace_back(cmStrCat('"', dest, '"'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmJoin(resultVector, " ");
|
||||
}
|
||||
|
||||
std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
|
||||
cmFileSet* fileSet,
|
||||
cmTargetExport* /*te*/)
|
||||
{
|
||||
std::vector<std::string> resultVector;
|
||||
|
||||
auto configs =
|
||||
gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
|
||||
|
||||
auto fileEntries = fileSet->CompileFileEntries();
|
||||
auto directoryEntries = fileSet->CompileDirectoryEntries();
|
||||
|
||||
for (auto const& config : configs) {
|
||||
auto directories = fileSet->EvaluateDirectoryEntries(
|
||||
directoryEntries, gte->LocalGenerator, config, gte);
|
||||
|
||||
std::map<std::string, std::vector<std::string>> files;
|
||||
for (auto const& entry : fileEntries) {
|
||||
fileSet->EvaluateFileEntry(directories, files, entry,
|
||||
gte->LocalGenerator, config, gte);
|
||||
}
|
||||
|
||||
bool const contextSensitive =
|
||||
std::any_of(directoryEntries.begin(), directoryEntries.end(),
|
||||
EntryIsContextSensitive) ||
|
||||
std::any_of(fileEntries.begin(), fileEntries.end(),
|
||||
EntryIsContextSensitive);
|
||||
|
||||
auto const& type = fileSet->GetType();
|
||||
// C++ modules do not support interface file sets which are dependent upon
|
||||
// the configuration.
|
||||
if (contextSensitive && type == "CXX_MODULES"_s) {
|
||||
auto* mf = this->LG->GetMakefile();
|
||||
std::ostringstream e;
|
||||
e << "The \"" << gte->GetName() << "\" target's interface file set \""
|
||||
<< fileSet->GetName() << "\" of type \"" << type
|
||||
<< "\" contains context-sensitive file entries which is not "
|
||||
"supported.";
|
||||
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
for (auto const& it : files) {
|
||||
for (auto const& filename : it.second) {
|
||||
auto escapedFile = cmOutputConverter::EscapeForCMake(
|
||||
filename, cmOutputConverter::WrapQuotes::NoWrap);
|
||||
if (contextSensitive && configs.size() != 1) {
|
||||
resultVector.push_back(
|
||||
cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
|
||||
} else {
|
||||
resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(contextSensitive && configs.size() != 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cmJoin(resultVector, " ");
|
||||
}
|
||||
|
||||
std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const
|
||||
{
|
||||
return this->CxxModulesDirectory;
|
||||
}
|
||||
|
||||
void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation(
|
||||
std::string const& name, std::ostream& os) const
|
||||
{
|
||||
char const* opt = "";
|
||||
if (this->Configurations.size() > 1) {
|
||||
// With more than one configuration, each individual file is optional.
|
||||
opt = " OPTIONAL";
|
||||
}
|
||||
|
||||
// Generate import file content for each configuration.
|
||||
for (std::string c : this->Configurations) {
|
||||
if (c.empty()) {
|
||||
c = "noconfig";
|
||||
}
|
||||
os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-'
|
||||
<< c << ".cmake\"" << opt << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion(
|
||||
std::string const& name, std::string config) const
|
||||
{
|
||||
auto cxx_modules_dirname = this->GetCxxModulesDirectory();
|
||||
if (cxx_modules_dirname.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (config.empty()) {
|
||||
config = "noconfig";
|
||||
}
|
||||
|
||||
std::string fileName =
|
||||
cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name,
|
||||
'-', config, ".cmake");
|
||||
|
||||
cmGeneratedFileStream os(fileName, true);
|
||||
if (!os) {
|
||||
std::string se = cmSystemTools::GetLastSystemError();
|
||||
std::ostringstream e;
|
||||
e << "cannot write to file \"" << fileName << "\": " << se;
|
||||
cmSystemTools::Error(e.str());
|
||||
return false;
|
||||
}
|
||||
os.SetCopyIfDifferent(true);
|
||||
|
||||
for (auto const* tgt : this->ExportedTargets) {
|
||||
// Only targets with C++ module sources will have a
|
||||
// collator-generated install script.
|
||||
if (!tgt->HaveCxx20ModuleSources()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-"
|
||||
<< tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", target,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", target,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", target,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
this->PopulateInterfaceProperty("INTERFACE_SOURCES", target,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties);
|
||||
|
||||
return this->PopulateInterfaceProperties(
|
||||
target, {}, cmGeneratorExpression::BuildInterface, properties);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user