You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
11 KiB
344 lines
11 KiB
1 month ago
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
||
|
#include "cmExportBuildCMakeConfigGenerator.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cstddef>
|
||
|
#include <map>
|
||
|
#include <memory>
|
||
|
#include <set>
|
||
|
#include <sstream>
|
||
|
#include <utility>
|
||
|
#include <vector>
|
||
|
|
||
|
#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 "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"
|
||
|
|
||
|
cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator()
|
||
|
{
|
||
|
this->LG = nullptr;
|
||
|
this->ExportSet = nullptr;
|
||
|
}
|
||
|
|
||
|
bool cmExportBuildCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
|
||
|
{
|
||
|
{
|
||
|
std::string expectedTargets;
|
||
|
std::string sep;
|
||
|
bool generatedInterfaceRequired = false;
|
||
|
auto visitor = [&](cmGeneratorTarget const* te) {
|
||
|
expectedTargets += sep + this->Namespace + te->GetExportName();
|
||
|
sep = " ";
|
||
|
|
||
|
generatedInterfaceRequired |=
|
||
|
this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
|
||
|
};
|
||
|
|
||
|
if (!this->CollectExports(visitor)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
if (!this->PopulateInterfaceProperties(gte, properties)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool const newCMP0022Behavior =
|
||
|
gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
|
||
|
gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
|
||
|
if (newCMP0022Behavior) {
|
||
|
this->PopulateInterfaceLinkLibrariesProperty(
|
||
|
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 cmExportBuildCMakeConfigGenerator::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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace {
|
||
|
bool EntryIsContextSensitive(
|
||
|
std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
|
||
|
{
|
||
|
return cge->GetHadContextSensitiveCondition();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories(
|
||
|
cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*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 cmExportBuildCMakeConfigGenerator::GetFileSetFiles(
|
||
|
cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*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, " ");
|
||
|
}
|
||
|
|
||
|
void cmExportBuildCMakeConfigGenerator::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 cmExportBuildCMakeConfigGenerator::
|
||
|
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;
|
||
|
}
|