Files
CMake/Source/cmExportFileGenerator.cxx
Marc Chevrier dee8799d16 FileSet management: Introduce dedicated classes for generation
To manage the generation step, introduce cmGeneratorFileSet and
cmGeneratorFileSets classes.
These reorganizations of the code are done in preparation of the
implementation of SOURCES file set type as well as user's file set types.

Issues: #27550, #27383
2026-02-19 17:35:56 +01:00

816 lines
27 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmExportFileGenerator.h"
#include <array>
#include <cstddef>
#include <sstream>
#include <utility>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
#include "cmComputeLinkInformation.h"
#include "cmFindPackageStack.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPropertyMap.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
cmExportFileGenerator::cmExportFileGenerator() = default;
void cmExportFileGenerator::AddConfiguration(std::string const& config)
{
this->Configurations.push_back(config);
}
void cmExportFileGenerator::SetExportFile(char const* mainFile)
{
this->MainImportFile = mainFile;
this->FileDir = cmSystemTools::GetFilenamePath(this->MainImportFile);
this->FileBase =
cmSystemTools::GetFilenameWithoutLastExtension(this->MainImportFile);
this->FileExt =
cmSystemTools::GetFilenameLastExtension(this->MainImportFile);
}
std::string const& cmExportFileGenerator::GetMainExportFileName() const
{
return this->MainImportFile;
}
bool cmExportFileGenerator::GenerateImportFile()
{
// Open the output file to generate it.
std::unique_ptr<cmsys::ofstream> foutPtr;
if (this->AppendMode) {
// Open for append.
auto openmodeApp = std::ios::app;
foutPtr = cm::make_unique<cmsys::ofstream>(this->MainImportFile.c_str(),
openmodeApp);
} else {
// Generate atomically and with copy-if-different.
std::unique_ptr<cmGeneratedFileStream> ap(
new cmGeneratedFileStream(this->MainImportFile, true));
ap->SetCopyIfDifferent(true);
foutPtr = std::move(ap);
}
if (!foutPtr || !*foutPtr) {
std::string se = cmSystemTools::GetLastSystemError();
std::ostringstream e;
e << "cannot write to file \"" << this->MainImportFile << "\": " << se;
cmSystemTools::Error(e.str());
return false;
}
return this->GenerateImportFile(*foutPtr);
}
std::string cmExportFileGenerator::PropertyConfigSuffix(
std::string const& config)
{
// Construct the property configuration suffix.
if (config.empty()) {
return "_NOCONFIG";
}
return cmStrCat('_', cmSystemTools::UpperCase(config));
}
void cmExportFileGenerator::GenerateImportConfig(std::ostream& os,
std::string const& config)
{
// Generate the per-config target information.
this->GenerateImportTargetsConfig(os, config, PropertyConfigSuffix(config));
}
bool cmExportFileGenerator::PopulateInterfaceProperties(
cmGeneratorTarget const* target, std::string const& includesDestinationDirs,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", target,
preprocessRule, properties);
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
target, properties);
this->PopulateInterfaceProperty("SPDX_LICENSE", target, preprocessRule,
properties);
std::string errorMessage;
if (!this->PopulateCxxModuleExportProperties(
target, properties, preprocessRule, includesDestinationDirs,
errorMessage)) {
this->ReportError(errorMessage);
return false;
}
if (!this->PopulateExportProperties(target, properties, errorMessage)) {
this->ReportError(errorMessage);
return false;
}
this->PopulateCompatibleInterfaceProperties(target, properties);
this->PopulateCustomTransitiveInterfaceProperties(target, preprocessRule,
properties);
return true;
}
void cmExportFileGenerator::PopulateInterfaceProperty(
std::string const& propName, cmGeneratorTarget const* target,
ImportPropertyMap& properties) const
{
cmValue input = target->GetProperty(propName);
if (input) {
properties[propName] = *input;
}
}
void cmExportFileGenerator::PopulateInterfaceProperty(
std::string const& propName, std::string const& outputName,
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
cmValue input = target->GetProperty(propName);
if (input) {
if (input->empty()) {
// Set to empty
properties[outputName].clear();
return;
}
std::string prepro =
cmGeneratorExpression::Preprocess(*input, preprocessRule);
if (!prepro.empty()) {
this->ResolveTargetsInGeneratorExpressions(prepro, target);
properties[outputName] = prepro;
}
}
}
void cmExportFileGenerator::PopulateInterfaceProperty(
std::string const& propName, cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
this->PopulateInterfaceProperty(propName, propName, target, preprocessRule,
properties);
}
bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
if (!target->IsLinkable()) {
return false;
}
static std::array<std::string, 3> const linkIfaceProps = {
{ "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT",
"INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" }
};
bool hadINTERFACE_LINK_LIBRARIES = false;
for (std::string const& linkIfaceProp : linkIfaceProps) {
if (cmValue input = target->GetProperty(linkIfaceProp)) {
std::string prepro =
cmGeneratorExpression::Preprocess(*input, preprocessRule);
if (!prepro.empty()) {
this->ResolveTargetsInGeneratorExpressions(prepro, target,
ReplaceFreeTargets);
properties[linkIfaceProp] = prepro;
hadINTERFACE_LINK_LIBRARIES = true;
}
}
}
return hadINTERFACE_LINK_LIBRARIES;
}
void cmExportFileGenerator::AddImportPrefix(std::string& exportDirs) const
{
std::vector<std::string> entries;
cmGeneratorExpression::Split(exportDirs, entries);
exportDirs.clear();
char const* sep = "";
cm::string_view const prefixWithSlash = this->GetImportPrefixWithSlash();
for (std::string const& e : entries) {
exportDirs += sep;
sep = ";";
if (!cmSystemTools::FileIsFullPath(e) &&
!cmHasPrefix(e, prefixWithSlash)) {
exportDirs += prefixWithSlash;
}
exportDirs += e;
}
}
namespace {
void getPropertyContents(cmGeneratorTarget const* tgt, std::string const& prop,
std::set<std::string>& ifaceProperties)
{
cmValue p = tgt->GetProperty(prop);
if (!p) {
return;
}
cmList content{ *p };
ifaceProperties.insert(content.begin(), content.end());
}
void getCompatibleInterfaceProperties(cmGeneratorTarget const* target,
std::set<std::string>& ifaceProperties,
std::string const& config)
{
if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
// object libraries have no link information, so nothing to compute
return;
}
cmComputeLinkInformation* info = target->GetLinkInformation(config);
if (!info) {
cmLocalGenerator* lg = target->GetLocalGenerator();
std::ostringstream e;
e << "Exporting the target \"" << target->GetName()
<< "\" is not "
"allowed since its linker language cannot be determined";
lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
return;
}
cmComputeLinkInformation::ItemVector const& deps = info->GetItems();
for (auto const& dep : deps) {
if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
continue;
}
getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_BOOL",
ifaceProperties);
getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_STRING",
ifaceProperties);
getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_NUMBER_MIN",
ifaceProperties);
getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_NUMBER_MAX",
ifaceProperties);
}
}
}
void cmExportFileGenerator::PopulateCompatibleInterfaceProperties(
cmGeneratorTarget const* gtarget, ImportPropertyMap& properties) const
{
this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_BOOL", gtarget,
properties);
this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_STRING", gtarget,
properties);
this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_NUMBER_MIN", gtarget,
properties);
this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_NUMBER_MAX", gtarget,
properties);
std::set<std::string> ifaceProperties;
getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_BOOL", ifaceProperties);
getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_STRING", ifaceProperties);
getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_NUMBER_MIN",
ifaceProperties);
getPropertyContents(gtarget, "COMPATIBLE_INTERFACE_NUMBER_MAX",
ifaceProperties);
if (gtarget->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
std::vector<std::string> configNames =
gtarget->Target->GetMakefile()->GetGeneratorConfigs(
cmMakefile::IncludeEmptyConfig);
for (std::string const& cn : configNames) {
getCompatibleInterfaceProperties(gtarget, ifaceProperties, cn);
}
}
for (std::string const& ip : ifaceProperties) {
this->PopulateInterfaceProperty("INTERFACE_" + ip, gtarget, properties);
}
}
void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
this->PopulateInterfaceProperty("TRANSITIVE_COMPILE_PROPERTIES", target,
properties);
this->PopulateInterfaceProperty("TRANSITIVE_LINK_PROPERTIES", target,
properties);
cmGeneratorTarget::CheckLinkLibrariesSuppressionRAII suppress;
std::set<std::string> ifaceProperties;
for (std::string const& config : this->Configurations) {
for (auto const& i : target->GetCustomTransitiveProperties(
config, cmGeneratorTarget::PropertyFor::Interface)) {
ifaceProperties.emplace(i.second.InterfaceName);
}
}
for (std::string const& ip : ifaceProperties) {
this->PopulateInterfaceProperty(ip, target, preprocessRule, properties);
}
}
bool cmExportFileGenerator::NoteLinkedTarget(
cmGeneratorTarget const* /*target*/, std::string const& /*linkedName*/,
cmGeneratorTarget const* /*linkedTarget*/)
{
// Default implementation does nothing; only needed by some generators.
return true;
}
bool cmExportFileGenerator::AddTargetNamespace(std::string& input,
cmGeneratorTarget const* target,
cmLocalGenerator const* lg)
{
cmGeneratorTarget::TargetOrString resolved =
target->ResolveTargetReference(input, lg);
cmGeneratorTarget* tgt = resolved.Target;
if (!tgt) {
input = resolved.String;
return false;
}
cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack();
if (!pkgStack.Empty() ||
tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) {
this->ExternalTargets.emplace(tgt);
}
if (tgt->IsImported()) {
input = tgt->GetName();
return this->NoteLinkedTarget(target, input, tgt);
}
if (this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) {
input = this->Namespace + tgt->GetExportName();
} else {
std::string namespacedTarget;
this->HandleMissingTarget(namespacedTarget, target, tgt);
if (!namespacedTarget.empty()) {
input = namespacedTarget;
} else {
input = tgt->GetName();
}
}
return this->NoteLinkedTarget(target, input, tgt);
}
void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
std::string& input, cmGeneratorTarget const* target,
FreeTargetsReplace replace)
{
cmLocalGenerator const* lg = target->GetLocalGenerator();
if (replace == NoReplaceFreeTargets) {
this->ResolveTargetsInGeneratorExpression(input, target, lg);
return;
}
std::vector<std::string> parts;
cmGeneratorExpression::Split(input, parts);
std::string sep;
input.clear();
for (std::string& li : parts) {
if (target->IsLinkLookupScope(li, lg)) {
continue;
}
if (cmGeneratorExpression::Find(li) == std::string::npos) {
this->AddTargetNamespace(li, target, lg);
} else {
this->ResolveTargetsInGeneratorExpression(li, target, lg);
}
input += sep + li;
sep = ";";
}
}
void cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
std::string& input, cmGeneratorTarget const* target,
cmLocalGenerator const* lg)
{
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
while ((pos = input.find("$<TARGET_PROPERTY:", lastPos)) !=
std::string::npos) {
std::string::size_type nameStartPos = pos + cmStrLen("$<TARGET_PROPERTY:");
std::string::size_type closePos = input.find('>', nameStartPos);
std::string::size_type commaPos = input.find(',', nameStartPos);
std::string::size_type nextOpenPos = input.find("$<", nameStartPos);
if (commaPos == std::string::npos // Implied 'this' target
|| closePos == std::string::npos // Incomplete expression.
|| closePos < commaPos // Implied 'this' target
|| nextOpenPos < commaPos) // Non-literal
{
lastPos = nameStartPos;
continue;
}
std::string targetName =
input.substr(nameStartPos, commaPos - nameStartPos);
if (this->AddTargetNamespace(targetName, target, lg)) {
input.replace(nameStartPos, commaPos - nameStartPos, targetName);
}
lastPos = nameStartPos + targetName.size() + 1;
}
std::string errorString;
pos = 0;
lastPos = pos;
while ((pos = input.find("$<TARGET_NAME:", lastPos)) != std::string::npos) {
std::string::size_type nameStartPos = pos + cmStrLen("$<TARGET_NAME:");
std::string::size_type endPos = input.find('>', nameStartPos);
if (endPos == std::string::npos) {
errorString = "$<TARGET_NAME:...> expression incomplete";
break;
}
std::string targetName = input.substr(nameStartPos, endPos - nameStartPos);
if (targetName.find("$<") != std::string::npos) {
errorString = "$<TARGET_NAME:...> requires its parameter to be a "
"literal.";
break;
}
if (!this->AddTargetNamespace(targetName, target, lg)) {
errorString = "$<TARGET_NAME:...> requires its parameter to be a "
"reachable target.";
break;
}
input.replace(pos, endPos - pos + 1, targetName);
lastPos = pos + targetName.size();
}
pos = 0;
lastPos = pos;
while (errorString.empty() &&
(pos = input.find("$<LINK_ONLY:", lastPos)) != std::string::npos) {
std::string::size_type nameStartPos = pos + cmStrLen("$<LINK_ONLY:");
std::string::size_type endPos = input.find('>', nameStartPos);
if (endPos == std::string::npos) {
errorString = "$<LINK_ONLY:...> expression incomplete";
break;
}
std::string libName = input.substr(nameStartPos, endPos - nameStartPos);
if (cmGeneratorExpression::IsValidTargetName(libName) &&
this->AddTargetNamespace(libName, target, lg)) {
input.replace(nameStartPos, endPos - nameStartPos, libName);
}
lastPos = nameStartPos + libName.size() + 1;
}
while (errorString.empty() &&
(pos = input.find("$<COMPILE_ONLY:", lastPos)) != std::string::npos) {
std::string::size_type nameStartPos = pos + cmStrLen("$<COMPILE_ONLY:");
std::string::size_type endPos = input.find('>', nameStartPos);
if (endPos == std::string::npos) {
errorString = "$<COMPILE_ONLY:...> expression incomplete";
break;
}
std::string libName = input.substr(nameStartPos, endPos - nameStartPos);
if (cmGeneratorExpression::IsValidTargetName(libName) &&
this->AddTargetNamespace(libName, target, lg)) {
input.replace(nameStartPos, endPos - nameStartPos, libName);
}
lastPos = nameStartPos + libName.size() + 1;
}
this->ReplaceInstallPrefix(input);
if (!errorString.empty()) {
target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
errorString);
}
}
void cmExportFileGenerator::ReplaceInstallPrefix(std::string& /*unused*/) const
{
// Do nothing
}
void cmExportFileGenerator::SetImportDetailProperties(
std::string const& config, std::string const& suffix,
cmGeneratorTarget const* target, ImportPropertyMap& properties)
{
// Get the makefile in which to lookup target information.
cmMakefile* mf = target->Makefile;
// Add the soname for unix shared libraries.
if (target->GetType() == cmStateEnums::SHARED_LIBRARY ||
target->GetType() == cmStateEnums::MODULE_LIBRARY) {
if (!target->IsDLLPlatform()) {
std::string prop;
std::string value;
if (target->HasSOName(config)) {
if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
value = this->InstallNameDir(target, config);
}
prop = "IMPORTED_SONAME";
value += target->GetSOName(config);
} else {
prop = "IMPORTED_NO_SONAME";
value = "TRUE";
}
prop += suffix;
properties[prop] = value;
}
}
// Add the transitive link dependencies for this configuration.
if (cmLinkInterface const* iface =
target->GetLinkInterface(config, target)) {
this->SetImportLinkProperty(
suffix, target, "IMPORTED_LINK_INTERFACE_LANGUAGES", iface->Languages,
properties, ImportLinkPropertyTargetNames::No);
// Export IMPORTED_LINK_DEPENDENT_LIBRARIES to help consuming linkers
// find private dependencies of shared libraries.
std::size_t oldMissingTargetsSize = this->MissingTargets.size();
auto oldExternalTargets = this->ExternalTargets;
this->SetImportLinkProperty(
suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps,
properties, ImportLinkPropertyTargetNames::Yes);
// Avoid enforcing shared library private dependencies as public package
// dependencies by ignoring missing targets added for them.
this->MissingTargets.resize(oldMissingTargetsSize);
this->ExternalTargets = std::move(oldExternalTargets);
if (iface->Multiplicity > 0) {
std::string prop =
cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
properties[prop] = std::to_string(iface->Multiplicity);
}
}
// Add information if this target is a managed target
if (target->GetManagedType(config) !=
cmGeneratorTarget::ManagedType::Native) {
std::string prop = cmStrCat("IMPORTED_COMMON_LANGUAGE_RUNTIME", suffix);
std::string propval;
if (cmValue p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
propval = *p;
} else if (target->IsCSharpOnly()) {
// C# projects do not have the /clr flag, so we set the property
// here to mark the target as (only) managed (i.e. no .lib file
// to link to). Otherwise the COMMON_LANGUAGE_RUNTIME target
// property would have to be set manually for C# targets to make
// exporting/importing work.
propval = "CSharp";
}
properties[prop] = propval;
}
}
namespace {
std::string const& asString(std::string const& l)
{
return l;
}
std::string const& asString(cmLinkItem const& l)
{
return l.AsStr();
}
}
template <typename T>
void cmExportFileGenerator::SetImportLinkProperty(
std::string const& suffix, cmGeneratorTarget const* target,
std::string const& propName, std::vector<T> const& entries,
ImportPropertyMap& properties, ImportLinkPropertyTargetNames targetNames)
{
// Skip the property if there are no entries.
if (entries.empty()) {
return;
}
cmLocalGenerator const* lg = target->GetLocalGenerator();
// Construct the property value.
std::string link_entries;
char const* sep = "";
for (T const& l : entries) {
// Separate this from the previous entry.
link_entries += sep;
sep = ";";
if (targetNames == ImportLinkPropertyTargetNames::Yes) {
std::string temp = asString(l);
this->AddTargetNamespace(temp, target, lg);
link_entries += temp;
} else {
link_entries += asString(l);
}
}
// Store the property.
std::string prop = cmStrCat(propName, suffix);
properties[prop] = link_entries;
}
template void cmExportFileGenerator::SetImportLinkProperty<std::string>(
std::string const&, cmGeneratorTarget const*, std::string const&,
std::vector<std::string> const&, ImportPropertyMap& properties,
ImportLinkPropertyTargetNames);
template void cmExportFileGenerator::SetImportLinkProperty<cmLinkItem>(
std::string const&, cmGeneratorTarget const*, std::string const&,
std::vector<cmLinkItem> const&, ImportPropertyMap& properties,
ImportLinkPropertyTargetNames);
namespace {
enum class ExportWhen
{
Defined,
Always,
};
enum class PropertyType
{
Strings,
Paths,
IncludePaths,
};
bool PropertyTypeIsForPaths(PropertyType pt)
{
switch (pt) {
case PropertyType::Strings:
return false;
case PropertyType::Paths:
case PropertyType::IncludePaths:
return true;
}
return false;
}
}
bool cmExportFileGenerator::PopulateCxxModuleExportProperties(
cmGeneratorTarget const* gte, ImportPropertyMap& properties,
cmGeneratorExpression::PreprocessContext ctx,
std::string const& includesDestinationDirs, std::string&)
{
if (!gte->HaveCxx20ModuleSources()) {
return true;
}
struct ModuleTargetPropertyTable
{
cm::static_string_view Name;
ExportWhen Cond;
};
ModuleTargetPropertyTable const exportedDirectModuleProperties[] = {
{ "CXX_EXTENSIONS"_s, ExportWhen::Defined },
// Always define this property as it is an intrinsic property of the target
// and should not be inherited from the in-scope `CMAKE_CXX_MODULE_STD`
// variable.
//
// TODO(cxxmodules): A future policy may make this "ON" based on the target
// policies if unset. Add a new `ExportWhen` condition to handle it when
// this happens.
{ "CXX_MODULE_STD"_s, ExportWhen::Always },
};
for (auto const& prop : exportedDirectModuleProperties) {
auto const propNameStr = std::string(prop.Name);
cmValue propValue = gte->Target->GetComputedProperty(
propNameStr, *gte->Target->GetMakefile());
if (!propValue) {
propValue = gte->Target->GetProperty(propNameStr);
}
if (propValue) {
properties[propNameStr] =
cmGeneratorExpression::Preprocess(*propValue, ctx);
} else if (prop.Cond == ExportWhen::Always) {
properties[propNameStr] = "";
}
}
struct ModulePropertyTable
{
cm::static_string_view Name;
PropertyType Type;
};
ModulePropertyTable const exportedModuleProperties[] = {
{ "INCLUDE_DIRECTORIES"_s, PropertyType::IncludePaths },
{ "COMPILE_DEFINITIONS"_s, PropertyType::Strings },
{ "COMPILE_OPTIONS"_s, PropertyType::Strings },
{ "COMPILE_FEATURES"_s, PropertyType::Strings },
};
for (auto const& propEntry : exportedModuleProperties) {
auto const propNameStr = std::string(propEntry.Name);
cmValue prop = gte->Target->GetComputedProperty(
propNameStr, *gte->Target->GetMakefile());
if (!prop) {
prop = gte->Target->GetProperty(propNameStr);
}
if (prop) {
auto const exportedPropName =
cmStrCat("IMPORTED_CXX_MODULES_", propEntry.Name);
properties[exportedPropName] =
cmGeneratorExpression::Preprocess(*prop, ctx);
if (ctx == cmGeneratorExpression::InstallInterface &&
PropertyTypeIsForPaths(propEntry.Type)) {
this->ReplaceInstallPrefix(properties[exportedPropName]);
this->AddImportPrefix(properties[exportedPropName]);
if (propEntry.Type == PropertyType::IncludePaths &&
!includesDestinationDirs.empty()) {
if (!properties[exportedPropName].empty()) {
properties[exportedPropName] += ';';
}
properties[exportedPropName] += includesDestinationDirs;
}
}
}
}
cm::static_string_view const exportedLinkModuleProperties[] = {
"LINK_LIBRARIES"_s,
};
for (auto const& propName : exportedLinkModuleProperties) {
auto const propNameStr = std::string(propName);
cmValue prop = gte->Target->GetComputedProperty(
propNameStr, *gte->Target->GetMakefile());
if (!prop) {
prop = gte->Target->GetProperty(propNameStr);
}
if (prop) {
auto const exportedPropName =
cmStrCat("IMPORTED_CXX_MODULES_", propName);
auto value = cmGeneratorExpression::Preprocess(*prop, ctx);
this->ResolveTargetsInGeneratorExpressions(value, gte,
ReplaceFreeTargets);
properties[exportedPropName] = value;
}
}
return true;
}
bool cmExportFileGenerator::PopulateExportProperties(
cmGeneratorTarget const* gte, ImportPropertyMap& properties,
std::string& errorMessage) const
{
auto const& targetProperties = gte->Target->GetProperties();
if (cmValue exportProperties =
targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
for (auto& prop : cmList{ *exportProperties }) {
/* Black list reserved properties */
if (cmHasLiteralPrefix(prop, "IMPORTED_") ||
cmHasLiteralPrefix(prop, "INTERFACE_")) {
std::ostringstream e;
e << "Target \"" << gte->Target->GetName() << "\" contains property \""
<< prop << "\" in EXPORT_PROPERTIES but IMPORTED_* and INTERFACE_* "
<< "properties are reserved.";
errorMessage = e.str();
return false;
}
cmValue propertyValue = targetProperties.GetPropertyValue(prop);
if (!propertyValue) {
// Asked to export a property that isn't defined on the target. Do not
// consider this an error, there's just nothing to export.
continue;
}
std::string evaluatedValue = cmGeneratorExpression::Preprocess(
*propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions);
if (evaluatedValue != *propertyValue) {
std::ostringstream e;
e << "Target \"" << gte->Target->GetName() << "\" contains property \""
<< prop << "\" in EXPORT_PROPERTIES but this property contains a "
<< "generator expression. This is not allowed.";
errorMessage = e.str();
return false;
}
properties[prop] = *propertyValue;
}
}
return true;
}