| /* 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 <functional> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/string_view> | |
| #include "cmsys/FStream.hxx" | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmFindPackageStack.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmLinkItem.h" | |
| #include "cmList.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmPropertyMap.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetTypes.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. | |
| auto ap = | |
| cm::make_unique<cmGeneratedFileStream>(this->MainImportFile, true); | |
| ap->SetCopyIfDifferent(true); | |
| foutPtr = std::move(ap); | |
| } | |
| if (!foutPtr || !*foutPtr) { | |
| std::string se = cmSystemTools::GetLastSystemError(); | |
| cmSystemTools::Error( | |
| cmStrCat("cannot write to file \"", this->MainImportFile, "\": ", se)); | |
| 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; | |
| } | |
| bool cmExportFileGenerator::PopulateFileSetInterfaceProperties( | |
| cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet, | |
| cmGeneratorExpression::PreprocessContext preprocessRule, | |
| ImportPropertyMap& properties) | |
| { | |
| this->PopulateFileSetInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", | |
| target, fileSet, preprocessRule, | |
| properties); | |
| this->PopulateFileSetInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target, | |
| fileSet, preprocessRule, properties); | |
| this->PopulateFileSetInterfaceProperty("LANGUAGE", target, fileSet, | |
| 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); | |
| } | |
| void cmExportFileGenerator::PopulateFileSetInterfaceProperty( | |
| std::string const& propName, cmGeneratorTarget const* target, | |
| cmGeneratorFileSet const* fileSet, | |
| cmGeneratorExpression::PreprocessContext preprocessRule, | |
| ImportPropertyMap& properties) | |
| { | |
| cmValue input = fileSet->GetProperty(propName); | |
| if (input) { | |
| if (input->empty()) { | |
| // Set to empty | |
| properties[propName].clear(); | |
| return; | |
| } | |
| std::string prepro = | |
| cmGeneratorExpression::Preprocess(*input, preprocessRule); | |
| if (!prepro.empty()) { | |
| this->ResolveTargetsInGeneratorExpressions(prepro, target); | |
| properties[propName] = prepro; | |
| } | |
| } | |
| } | |
| 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() == cm::TargetType::OBJECT_LIBRARY) { | |
| // object libraries have no link information, so nothing to compute | |
| return; | |
| } | |
| cmComputeLinkInformation* info = target->GetLinkInformation(config); | |
| if (!info) { | |
| cmLocalGenerator* lg = target->GetLocalGenerator(); | |
| lg->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Exporting the target \"", target->GetName(), | |
| "\" is not allowed since its linker language cannot be " | |
| "determined")); | |
| return; | |
| } | |
| cmComputeLinkInformation::ItemVector const& deps = info->GetItems(); | |
| for (auto const& dep : deps) { | |
| if (!dep.Target || | |
| dep.Target->GetType() == cm::TargetType::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() != cm::TargetType::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; | |
| } | |
| std::vector<std::string> replace = tgt->Target->GetExportTargets(); | |
| if (!replace.empty()) { | |
| std::vector<std::string> out; | |
| out.reserve(replace.size()); | |
| bool result = true; | |
| for (std::string& t : replace) { | |
| result = this->AddTargetNamespace(t, target, lg) && result; | |
| out.emplace_back(std::move(t)); | |
| } | |
| input = cmJoin(out, ";"_s); | |
| return result; | |
| } | |
| 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 = cmStrCat(std::move(input), sep, li); | |
| sep = ";"; | |
| } | |
| } | |
| cm::optional<std::string> cmResolveTargetsInGeneratorExpression( | |
| std::string& input, | |
| std::function<bool(std::string& name)> const& addTargetNamespace) | |
| { | |
| 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 (addTargetNamespace(targetName)) { | |
| input.replace(nameStartPos, commaPos - nameStartPos, targetName); | |
| } | |
| lastPos = nameStartPos + targetName.size() + 1; | |
| } | |
| cm::optional<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 (!addTargetNamespace(targetName)) { | |
| 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 && | |
| (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) && | |
| addTargetNamespace(libName)) { | |
| input.replace(nameStartPos, endPos - nameStartPos, libName); | |
| } | |
| lastPos = nameStartPos + libName.size() + 1; | |
| } | |
| while (!errorString && | |
| (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) && | |
| addTargetNamespace(libName)) { | |
| input.replace(nameStartPos, endPos - nameStartPos, libName); | |
| } | |
| lastPos = nameStartPos + libName.size() + 1; | |
| } | |
| return errorString; | |
| } | |
| void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( | |
| std::string& input, cmGeneratorTarget const* target, | |
| cmLocalGenerator const* lg) | |
| { | |
| auto err = cmResolveTargetsInGeneratorExpression( | |
| input, [this, target, lg](std::string& name) { | |
| return this->AddTargetNamespace(name, target, lg); | |
| }); | |
| this->ReplaceInstallPrefix(input); | |
| if (err) { | |
| target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, *err); | |
| } | |
| } | |
| 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() == cm::TargetType::SHARED_LIBRARY || | |
| target->GetType() == cm::TargetType::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 }, | |
| }; | |
| 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->GetDirectProperties(); | |
| if (cmValue exportProperties = | |
| targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) { | |
| for (auto& prop : cmList{ *exportProperties }) { | |
| /* Black list reserved properties */ | |
| if (cmHasLiteralPrefix(prop, "IMPORTED_") || | |
| cmHasLiteralPrefix(prop, "INTERFACE_")) { | |
| errorMessage = cmStrCat( | |
| "Target \"", gte->Target->GetName(), "\" contains property \"", prop, | |
| "\" in EXPORT_PROPERTIES but IMPORTED_* and INTERFACE_* " | |
| "properties are reserved."); | |
| 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) { | |
| errorMessage = cmStrCat( | |
| "Target \"", gte->Target->GetName(), "\" contains property \"", prop, | |
| "\" in EXPORT_PROPERTIES but this property contains a " | |
| "generator expression. This is not allowed."); | |
| return false; | |
| } | |
| properties[prop] = *propertyValue; | |
| } | |
| } | |
| return true; | |
| } |