diff options
author | Brad King <brad.king@kitware.com> | 2024-05-21 13:21:30 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2024-05-21 13:21:38 (GMT) |
commit | 886b07a41cb6a47970b60bcc9e4ac680be50c9b8 (patch) | |
tree | 5d8a962968bd8e2c4e88fb9a4fb9273ca0fc5c5e | |
parent | 85e9025f80c7b3cc71984e9b20b0d443b71e71ef (diff) | |
parent | 09c74c6d0c891bfb7372548b5231406d9a6ef7f9 (diff) | |
download | CMake-886b07a41cb6a47970b60bcc9e4ac680be50c9b8.zip CMake-886b07a41cb6a47970b60bcc9e4ac680be50c9b8.tar.gz CMake-886b07a41cb6a47970b60bcc9e4ac680be50c9b8.tar.bz2 |
Merge topic 'split-cmGeneratorTarget'
09c74c6d0c cmGeneratorTarget: Factor transitive property methods into own source
feaca40931 cmGeneratorTarget: Factor link interface/impl methods into own source
b4924c562a cmGeneratorTarget: Factor compatible interface properties into own source
cdd7112a5d cmGeneratorTarget: Factor options-like usage requirements into own source
881a46819e cmGeneratorTarget: Factor LINK_DIRECTORIES impl into own source
0a3c97308d cmGeneratorTarget: Factor INCLUDE_DIRECTORIES impl into own source
7ffb92022c cmGeneratorTarget: Factor SOURCES impl into own source
d0fc932528 cmGeneratorTarget: Factor TargetPropertyEntry into own source
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !9536
-rw-r--r-- | Source/CMakeLists.txt | 10 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 4315 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.h | 12 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_CompatibleInterface.cxx | 737 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_IncludeDirectories.cxx | 334 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_Link.cxx | 1489 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_LinkDirectories.cxx | 154 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_Options.cxx | 657 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_Sources.cxx | 560 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_TargetPropertyEntry.cxx | 189 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_TransitiveProperty.cxx | 207 | ||||
-rw-r--r-- | Source/cmTargetTraceDependencies.cxx | 239 | ||||
-rw-r--r-- | Source/cmTargetTraceDependencies.h | 46 | ||||
-rwxr-xr-x | bootstrap | 9 |
14 files changed, 4649 insertions, 4309 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 9953caf..2bd3bdd 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -285,6 +285,14 @@ add_library( cmGeneratorExpression.h cmGeneratorTarget.cxx cmGeneratorTarget.h + cmGeneratorTarget_CompatibleInterface.cxx + cmGeneratorTarget_IncludeDirectories.cxx + cmGeneratorTarget_Link.cxx + cmGeneratorTarget_LinkDirectories.cxx + cmGeneratorTarget_Options.cxx + cmGeneratorTarget_Sources.cxx + cmGeneratorTarget_TargetPropertyEntry.cxx + cmGeneratorTarget_TransitiveProperty.cxx cmLinkItemGraphVisitor.cxx cmLinkItemGraphVisitor.h cmGetPipes.cxx @@ -437,6 +445,8 @@ add_library( cmTargetPropertyComputer.cxx cmTargetPropertyComputer.h cmTargetExport.h + cmTargetTraceDependencies.cxx + cmTargetTraceDependencies.h cmTest.cxx cmTest.h cmTestGenerator.cxx diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 8d1cd84..ce53316 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -6,13 +6,9 @@ #include <array> #include <cassert> #include <cctype> -#include <cerrno> #include <cstddef> #include <cstdio> -#include <cstdlib> #include <cstring> -#include <iterator> -#include <queue> #include <sstream> #include <type_traits> #include <unordered_set> @@ -24,22 +20,16 @@ #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/RegularExpression.hxx" - #include "cmAlgorithms.h" -#include "cmComputeLinkInformation.h" +#include "cmComputeLinkInformation.h" // IWYU pragma: keep #include "cmCryptoHash.h" -#include "cmCustomCommandGenerator.h" #include "cmCxxModuleUsageEffects.h" -#include "cmEvaluatedTargetProperty.h" #include "cmExperimental.h" #include "cmFileSet.h" #include "cmFileTimes.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" -#include "cmGeneratorExpressionContext.h" #include "cmGeneratorExpressionDAGChecker.h" -#include "cmGeneratorExpressionNode.h" #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmLocalGenerator.h" @@ -47,11 +37,9 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPropertyMap.h" -#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" -#include "cmSourceGroup.h" #include "cmStandardLevel.h" #include "cmStandardLevelResolver.h" #include "cmState.h" @@ -61,40 +49,12 @@ #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" #include "cmTargetPropertyComputer.h" +#include "cmTargetTraceDependencies.h" #include "cmake.h" namespace { using UseTo = cmGeneratorTarget::UseTo; -using TransitiveProperty = cmGeneratorTarget::TransitiveProperty; - -const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES"; -const std::string kINTERFACE_LINK_LIBRARIES_DIRECT = - "INTERFACE_LINK_LIBRARIES_DIRECT"; -const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE = - "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"; -} - -const std::map<cm::string_view, TransitiveProperty> - cmGeneratorTarget::BuiltinTransitiveProperties = { - { "AUTOMOC_MACRO_NAMES"_s, - { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } }, - { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } }, - { "COMPILE_DEFINITIONS"_s, - { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } }, - { "COMPILE_FEATURES"_s, - { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } }, - { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } }, - { "INCLUDE_DIRECTORIES"_s, - { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, - { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } }, - { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } }, - { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } }, - { "PRECOMPILE_HEADERS"_s, - { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } }, - { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } }, - { "SYSTEM_INCLUDE_DIRECTORIES"_s, - { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, - }; +} template <> cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>( @@ -119,176 +79,14 @@ cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>( return tgt->GetLocation(config); } -cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem; - -class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryString(BT<std::string> propertyValue, - cmLinkImplItem const& item = NoLinkImplItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , PropertyValue(std::move(propertyValue)) - { - } - - const std::string& Evaluate(cmLocalGenerator*, const std::string&, - cmGeneratorTarget const*, - cmGeneratorExpressionDAGChecker*, - std::string const&) const override - { - return this->PropertyValue.Value; - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->PropertyValue.Backtrace; - } - std::string const& GetInput() const override - { - return this->PropertyValue.Value; - } - -private: - BT<std::string> PropertyValue; -}; - -class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, - cmLinkImplItem const& item = NoLinkImplItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , ge(std::move(cge)) - { - } - - const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, - cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& language) const override - { - return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr, - language); - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->ge->GetBacktrace(); - } - - std::string const& GetInput() const override { return this->ge->GetInput(); } - - bool GetHadContextSensitiveCondition() const override - { - return this->ge->GetHadContextSensitiveCondition(); - } - -private: - const std::unique_ptr<cmCompiledGeneratorExpression> ge; -}; - -class TargetPropertyEntryFileSet - : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryFileSet( - std::vector<std::string> dirs, bool contextSensitiveDirs, - std::unique_ptr<cmCompiledGeneratorExpression> entryCge, - const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , BaseDirs(std::move(dirs)) - , ContextSensitiveDirs(contextSensitiveDirs) - , EntryCge(std::move(entryCge)) - , FileSet(fileSet) - { - } - - const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, - cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& /*lang*/) const override - { - std::map<std::string, std::vector<std::string>> filesPerDir; - this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, - this->EntryCge, lg, config, headTarget, - dagChecker); - - std::vector<std::string> files; - for (auto const& it : filesPerDir) { - files.insert(files.end(), it.second.begin(), it.second.end()); - } - - static std::string filesStr; - filesStr = cmList::to_string(files); - return filesStr; - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->EntryCge->GetBacktrace(); - } - - std::string const& GetInput() const override - { - return this->EntryCge->GetInput(); - } - - bool GetHadContextSensitiveCondition() const override - { - return this->ContextSensitiveDirs || - this->EntryCge->GetHadContextSensitiveCondition(); - } - -private: - const std::vector<std::string> BaseDirs; - const bool ContextSensitiveDirs; - const std::unique_ptr<cmCompiledGeneratorExpression> EntryCge; - const cmFileSet* FileSet; -}; - -std::unique_ptr< - cmGeneratorTarget:: - TargetPropertyEntry> static CreateTargetPropertyEntry(cmake& cmakeInstance, - const BT< - std::string>& - propertyValue, - bool - evaluateForBuildsystem = - false) -{ - if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) { - cmGeneratorExpression ge(cmakeInstance, propertyValue.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(propertyValue.Value); - cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); - return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>( - cm::make_unique<TargetPropertyEntryGenex>(std::move(cge))); - } - - return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>( - cm::make_unique<TargetPropertyEntryString>(propertyValue)); -} - -cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry( - cmLinkImplItem const& item) - : LinkImplItem(item) -{ -} - -bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition() - const -{ - return false; -} - static void CreatePropertyGeneratorExpressions( cmake& cmakeInstance, cmBTStringRange entries, std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items, bool evaluateForBuildsystem = false) { for (auto const& entry : entries) { - items.push_back( - CreateTargetPropertyEntry(cmakeInstance, entry, evaluateForBuildsystem)); + items.push_back(cmGeneratorTarget::TargetPropertyEntry::Create( + cmakeInstance, entry, evaluateForBuildsystem)); } } @@ -735,7 +533,7 @@ void cmGeneratorTarget::AddSourceCommon(const std::string& src, bool before) { this->SourceEntries.insert( before ? this->SourceEntries.begin() : this->SourceEntries.end(), - CreateTargetPropertyEntry( + TargetPropertyEntry::Create( *this->LocalGenerator->GetCMakeInstance(), BT<std::string>(src, this->Makefile->GetBacktrace()), true)); this->ClearSourcesCache(); @@ -763,7 +561,7 @@ void cmGeneratorTarget::AddIncludeDirectory(const std::string& src, this->IncludeDirectoriesEntries.insert( before ? this->IncludeDirectoriesEntries.begin() : this->IncludeDirectoriesEntries.end(), - CreateTargetPropertyEntry( + TargetPropertyEntry::Create( *this->Makefile->GetCMakeInstance(), BT<std::string>(src, this->Makefile->GetBacktrace()), true)); } @@ -1425,777 +1223,6 @@ bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const return this->Target->GetPropertyAsBool(prop); } -bool cmGeneratorTarget::MaybeHaveInterfaceProperty( - std::string const& prop, cmGeneratorExpressionContext* context, - UseTo usage) const -{ - std::string const key = prop + '@' + context->Config; - auto i = this->MaybeInterfacePropertyExists.find(key); - if (i == this->MaybeInterfacePropertyExists.end()) { - // Insert an entry now in case there is a cycle. - i = this->MaybeInterfacePropertyExists.emplace(key, false).first; - bool& maybeInterfaceProp = i->second; - - // If this target itself has a non-empty property value, we are done. - maybeInterfaceProp = cmNonempty(this->GetProperty(prop)); - - // Otherwise, recurse to interface dependencies. - if (!maybeInterfaceProp) { - cmGeneratorTarget const* headTarget = - context->HeadTarget ? context->HeadTarget : this; - if (cmLinkInterfaceLibraries const* iface = - this->GetLinkInterfaceLibraries(context->Config, headTarget, - usage)) { - if (iface->HadHeadSensitiveCondition) { - // With a different head target we may get to a library with - // this interface property. - maybeInterfaceProp = true; - } else { - // The transitive interface libraries do not depend on the - // head target, so we can follow them. - for (cmLinkItem const& lib : iface->Libraries) { - if (lib.Target && - lib.Target->MaybeHaveInterfaceProperty(prop, context, usage)) { - maybeInterfaceProp = true; - break; - } - } - } - } - } - } - return i->second; -} - -std::string cmGeneratorTarget::EvaluateInterfaceProperty( - std::string const& prop, cmGeneratorExpressionContext* context, - cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const -{ - std::string result; - - // If the property does not appear transitively at all, we are done. - if (!this->MaybeHaveInterfaceProperty(prop, context, usage)) { - return result; - } - - // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is - // a subset of TargetPropertyNode::Evaluate without stringify/parse steps - // but sufficient for transitive interface properties. - cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop, - nullptr, dagCheckerParent, - this->LocalGenerator); - switch (dagChecker.Check()) { - case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: - dagChecker.ReportError( - context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">"); - return result; - case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: - // No error. We just skip cyclic references. - case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: - // No error. We have already seen this transitive property. - return result; - case cmGeneratorExpressionDAGChecker::DAG: - break; - } - - cmGeneratorTarget const* headTarget = - context->HeadTarget ? context->HeadTarget : this; - - if (cmValue p = this->GetProperty(prop)) { - result = cmGeneratorExpressionNode::EvaluateDependentExpression( - *p, context->LG, context, headTarget, &dagChecker, this); - } - - if (cmLinkInterfaceLibraries const* iface = - this->GetLinkInterfaceLibraries(context->Config, headTarget, usage)) { - context->HadContextSensitiveCondition = - context->HadContextSensitiveCondition || - iface->HadContextSensitiveCondition; - for (cmLinkItem const& lib : iface->Libraries) { - // Broken code can have a target in its own link interface. - // Don't follow such link interface entries so as not to create a - // self-referencing loop. - if (lib.Target && lib.Target != this) { - // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the - // above property and hand-evaluate it as if it were compiled. - // Create a context as cmCompiledGeneratorExpression::Evaluate does. - cmGeneratorExpressionContext libContext( - context->LG, context->Config, context->Quiet, headTarget, this, - context->EvaluateForBuildsystem, context->Backtrace, - context->Language); - std::string libResult = cmGeneratorExpression::StripEmptyListElements( - lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker, - usage)); - if (!libResult.empty()) { - if (result.empty()) { - result = std::move(libResult); - } else { - result.reserve(result.size() + 1 + libResult.size()); - result += ";"; - result += libResult; - } - } - context->HadContextSensitiveCondition = - context->HadContextSensitiveCondition || - libContext.HadContextSensitiveCondition; - context->HadHeadSensitiveCondition = - context->HadHeadSensitiveCondition || - libContext.HadHeadSensitiveCondition; - } - } - } - - return result; -} - -cm::optional<cmGeneratorTarget::TransitiveProperty> -cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop, - cmLocalGenerator const* lg) const -{ - cm::optional<TransitiveProperty> result; - static const cm::string_view kINTERFACE_ = "INTERFACE_"_s; - if (cmHasPrefix(prop, kINTERFACE_)) { - prop = prop.substr(kINTERFACE_.length()); - } - auto i = BuiltinTransitiveProperties.find(prop); - if (i != BuiltinTransitiveProperties.end()) { - result = i->second; - if (result->Usage != cmGeneratorTarget::UseTo::Compile) { - cmPolicies::PolicyStatus cmp0166 = - lg->GetPolicyStatus(cmPolicies::CMP0166); - if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) && - (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s || - prop == "LINK_OPTIONS"_s)) { - result->Usage = cmGeneratorTarget::UseTo::Compile; - } - } - } else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) { - cmPolicies::PolicyStatus cmp0043 = - lg->GetPolicyStatus(cmPolicies::CMP0043); - if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) { - result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s, - UseTo::Compile }; - } - } - return result; -} - -namespace { - -enum class IncludeDirectoryFallBack -{ - BINARY, - OBJECT -}; - -std::string AddLangSpecificInterfaceIncludeDirectories( - const cmGeneratorTarget* root, const cmGeneratorTarget* target, - const std::string& lang, const std::string& config, - const std::string& propertyName, IncludeDirectoryFallBack mode, - cmGeneratorExpressionDAGChecker* context) -{ - cmGeneratorExpressionDAGChecker dag{ - target->GetBacktrace(), target, propertyName, nullptr, context, - target->GetLocalGenerator() - }; - switch (dag.Check()) { - case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: - dag.ReportError( - nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName"); - CM_FALLTHROUGH; - case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: - // No error. We just skip cyclic references. - case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: - // No error. We have already seen this transitive property. - return ""; - case cmGeneratorExpressionDAGChecker::DAG: - break; - } - - std::string directories; - if (const auto* link_interface = - target->GetLinkInterfaceLibraries(config, root, UseTo::Compile)) { - for (const cmLinkItem& library : link_interface->Libraries) { - if (const cmGeneratorTarget* dependency = library.Target) { - if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { - auto* lg = dependency->GetLocalGenerator(); - std::string value = dependency->GetSafeProperty(propertyName); - if (value.empty()) { - if (mode == IncludeDirectoryFallBack::BINARY) { - value = lg->GetCurrentBinaryDirectory(); - } else if (mode == IncludeDirectoryFallBack::OBJECT) { - value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', - lg->GetTargetDirectory(dependency)); - } - } - - if (!directories.empty()) { - directories += ";"; - } - directories += value; - } - } - } - } - return directories; -} - -void AddLangSpecificImplicitIncludeDirectories( - const cmGeneratorTarget* target, const std::string& lang, - const std::string& config, const std::string& propertyName, - IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries) -{ - if (const auto* libraries = - target->GetLinkImplementationLibraries(config, UseTo::Compile)) { - cmGeneratorExpressionDAGChecker dag{ - target->GetBacktrace(), target, propertyName, nullptr, nullptr, - target->GetLocalGenerator() - }; - - for (const cmLinkImplItem& library : libraries->Libraries) { - if (const cmGeneratorTarget* dependency = library.Target) { - if (!dependency->IsInBuildSystem()) { - continue; - } - if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { - auto* lg = dependency->GetLocalGenerator(); - EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; - - if (cmValue val = dependency->GetProperty(propertyName)) { - entry.Values.emplace_back(*val); - } else { - if (mode == IncludeDirectoryFallBack::BINARY) { - entry.Values.emplace_back(lg->GetCurrentBinaryDirectory()); - } else if (mode == IncludeDirectoryFallBack::OBJECT) { - entry.Values.emplace_back( - dependency->GetObjectDirectory(config)); - } - } - - cmExpandList( - AddLangSpecificInterfaceIncludeDirectories( - target, dependency, lang, config, propertyName, mode, &dag), - entry.Values); - entries.Entries.emplace_back(std::move(entry)); - } - } - } - } -} - -void AddObjectEntries(cmGeneratorTarget const* headTarget, - std::string const& config, - cmGeneratorExpressionDAGChecker* dagChecker, - EvaluatedTargetPropertyEntries& entries) -{ - if (cmLinkImplementationLibraries const* impl = - headTarget->GetLinkImplementationLibraries(config, UseTo::Compile)) { - entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; - for (cmLinkImplItem const& lib : impl->Libraries) { - if (lib.Target && - lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::string uniqueName = - headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely( - lib.Target); - std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">"; - cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(), - lib.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); - cge->SetEvaluateForBuildsystem(true); - - EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); - cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config, - headTarget, dagChecker), - ee.Values); - if (cge->GetHadContextSensitiveCondition()) { - ee.ContextDependent = true; - } - entries.Entries.emplace_back(std::move(ee)); - } - } - } -} - -void addFileSetEntry(cmGeneratorTarget const* headTarget, - std::string const& config, - cmGeneratorExpressionDAGChecker* dagChecker, - cmFileSet const* fileSet, - EvaluatedTargetPropertyEntries& entries) -{ - auto dirCges = fileSet->CompileDirectoryEntries(); - auto dirs = fileSet->EvaluateDirectoryEntries( - dirCges, headTarget->GetLocalGenerator(), config, headTarget, dagChecker); - bool contextSensitiveDirs = false; - for (auto const& dirCge : dirCges) { - if (dirCge->GetHadContextSensitiveCondition()) { - contextSensitiveDirs = true; - break; - } - } - cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance(); - for (auto& entryCge : fileSet->CompileFileEntries()) { - TargetPropertyEntryFileSet tpe(dirs, contextSensitiveDirs, - std::move(entryCge), fileSet); - entries.Entries.emplace_back( - EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, tpe)); - EvaluatedTargetPropertyEntry const& entry = entries.Entries.back(); - for (auto const& file : entry.Values) { - auto* sf = headTarget->Makefile->GetOrCreateSource(file); - if (fileSet->GetType() == "HEADERS"_s) { - sf->SetProperty("HEADER_FILE_ONLY", "TRUE"); - } - -#ifndef CMAKE_BOOTSTRAP - std::string e; - std::string w; - auto path = sf->ResolveFullPath(&e, &w); - if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); - } - if (path.empty()) { - if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); - } - return; - } - bool found = false; - for (auto const& sg : headTarget->Makefile->GetSourceGroups()) { - if (sg.MatchChildrenFiles(path)) { - found = true; - break; - } - } - if (!found) { - if (fileSet->GetType() == "HEADERS"_s) { - headTarget->Makefile->GetOrCreateSourceGroup("Header Files") - ->AddGroupFile(path); - } - } -#endif - } - } -} - -void AddFileSetEntries(cmGeneratorTarget const* headTarget, - std::string const& config, - cmGeneratorExpressionDAGChecker* dagChecker, - EvaluatedTargetPropertyEntries& entries) -{ - for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) { - for (auto const& name : cmList{ entry.Value }) { - auto const* headerSet = headTarget->Target->GetFileSet(name); - addFileSetEntry(headTarget, config, dagChecker, headerSet, entries); - } - } - for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) { - for (auto const& name : cmList{ entry.Value }) { - auto const* cxxModuleSet = headTarget->Target->GetFileSet(name); - addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries); - } - } -} - -bool processSources(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, - std::vector<BT<std::string>>& srcs, - std::unordered_set<std::string>& uniqueSrcs, - bool debugSources) -{ - cmMakefile* mf = tgt->Target->GetMakefile(); - - bool contextDependent = entries.HadContextSensitiveCondition; - - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { - if (entry.ContextDependent) { - contextDependent = true; - } - - cmLinkImplItem const& item = entry.LinkImplItem; - std::string const& targetName = item.AsStr(); - - for (std::string& src : entry.Values) { - cmSourceFile* sf = mf->GetOrCreateSource(src); - std::string e; - std::string w; - std::string fullPath = sf->ResolveFullPath(&e, &w); - cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); - if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); - } - if (fullPath.empty()) { - if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); - } - return contextDependent; - } - - if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) { - std::ostringstream err; - if (!targetName.empty()) { - err << "Target \"" << targetName - << "\" contains relative path in its INTERFACE_SOURCES:\n \"" - << src << "\""; - } else { - err << "Found relative path while evaluating sources of \"" - << tgt->GetName() << "\":\n \"" << src << "\"\n"; - } - tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, - err.str()); - return contextDependent; - } - src = fullPath; - } - std::string usedSources; - for (std::string const& src : entry.Values) { - if (uniqueSrcs.insert(src).second) { - srcs.emplace_back(src, entry.Backtrace); - if (debugSources) { - usedSources += " * " + src + "\n"; - } - } - } - if (!usedSources.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used sources for target ") + tgt->GetName() + ":\n" + - usedSources, - entry.Backtrace); - } - } - return contextDependent; -} -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( - std::string const& config) const -{ - std::vector<BT<std::string>> files; - - if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { - // At configure-time, this method can be called as part of getting the - // LOCATION property or to export() a file to be include()d. However - // there is no cmGeneratorTarget at configure-time, so search the SOURCES - // for TARGET_OBJECTS instead for backwards compatibility with OLD - // behavior of CMP0024 and CMP0026 only. - - cmBTStringRange sourceEntries = this->Target->GetSourceEntries(); - for (auto const& entry : sourceEntries) { - cmList items{ entry.Value }; - for (auto const& item : items) { - if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") && - item.back() == '>') { - continue; - } - files.emplace_back(item); - } - } - return files; - } - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugSources = - !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES"); - - if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { - this->DebugSourcesDone = true; - } - - cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, nullptr, - this->LocalGenerator); - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, std::string(), &dagChecker, this->SourceEntries); - - std::unordered_set<std::string> uniqueSrcs; - bool contextDependentDirectSources = - processSources(this, entries, files, uniqueSrcs, debugSources); - - // Collect INTERFACE_SOURCES of all direct link-dependencies. - EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries; - AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(), - &dagChecker, linkInterfaceSourcesEntries, - IncludeRuntimeInterface::No, UseTo::Compile); - bool contextDependentInterfaceSources = processSources( - this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); - - // Collect TARGET_OBJECTS of direct object link-dependencies. - bool contextDependentObjects = false; - if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { - EvaluatedTargetPropertyEntries linkObjectsEntries; - AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); - contextDependentObjects = processSources(this, linkObjectsEntries, files, - uniqueSrcs, debugSources); - // Note that for imported targets or multi-config generators supporting - // cross-config builds the paths to the object files must be per-config, - // so contextDependentObjects will be true here even if object libraries - // are specified without per-config generator expressions. - } - - // Collect this target's file sets. - EvaluatedTargetPropertyEntries fileSetEntries; - AddFileSetEntries(this, config, &dagChecker, fileSetEntries); - bool contextDependentFileSets = - processSources(this, fileSetEntries, files, uniqueSrcs, debugSources); - - // Determine if sources are context-dependent or not. - if (!contextDependentDirectSources && !contextDependentInterfaceSources && - !contextDependentObjects && !contextDependentFileSets) { - this->SourcesAreContextDependent = Tribool::False; - } else { - this->SourcesAreContextDependent = Tribool::True; - } - - return files; -} - -void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, - const std::string& config) const -{ - std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config); - files.reserve(tmp.size()); - for (BT<cmSourceFile*>& v : tmp) { - files.push_back(v.Value); - } -} - -std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles( - std::string const& config) const -{ - std::vector<BT<cmSourceFile*>> files; - if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) { - // Since we are still configuring not all sources may exist yet, - // so we need to avoid full source classification because that - // requires the absolute paths to all sources to be determined. - // Since this is only for compatibility with old policies that - // projects should not depend on anymore, just compute the files - // without memoizing them. - std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); - std::set<cmSourceFile*> emitted; - for (BT<std::string> const& s : srcs) { - cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); - if (emitted.insert(sf).second) { - files.emplace_back(sf, s.Backtrace); - } - } - return files; - } - - KindedSources const& kinded = this->GetKindedSources(config); - files.reserve(kinded.Sources.size()); - for (SourceAndKind const& si : kinded.Sources) { - files.push_back(si.Source); - } - return files; -} - -void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( - std::vector<cmSourceFile*>& files, const std::string& config) const -{ - std::vector<BT<cmSourceFile*>> tmp = - this->GetSourceFilesWithoutObjectLibraries(config); - files.reserve(tmp.size()); - for (BT<cmSourceFile*>& v : tmp) { - files.push_back(v.Value); - } -} - -std::vector<BT<cmSourceFile*>> -cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( - std::string const& config) const -{ - std::vector<BT<cmSourceFile*>> files; - KindedSources const& kinded = this->GetKindedSources(config); - files.reserve(kinded.Sources.size()); - for (SourceAndKind const& si : kinded.Sources) { - if (si.Source.Value->GetObjectLibrary().empty()) { - files.push_back(si.Source); - } - } - return files; -} - -cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( - std::string const& config) const -{ - // If we already processed one configuration and found no dependency - // on configuration then always use the one result. - if (this->SourcesAreContextDependent == Tribool::False) { - return this->KindedSourcesMap.begin()->second; - } - - // Lookup any existing link implementation for this configuration. - std::string const key = cmSystemTools::UpperCase(config); - auto it = this->KindedSourcesMap.find(key); - if (it != this->KindedSourcesMap.end()) { - if (!it->second.Initialized) { - std::ostringstream e; - e << "The SOURCES of \"" << this->GetName() - << "\" use a generator expression that depends on the " - "SOURCES themselves."; - this->GlobalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); - static KindedSources empty; - return empty; - } - return it->second; - } - - // Add an entry to the map for this configuration. - KindedSources& files = this->KindedSourcesMap[key]; - this->ComputeKindedSources(files, config); - files.Initialized = true; - return files; -} - -void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, - std::string const& config) const -{ - // Get the source file paths by string. - std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); - - cmsys::RegularExpression header_regex(CM_HEADER_REGEX); - std::vector<cmSourceFile*> badObjLib; - - std::set<cmSourceFile*> emitted; - for (BT<std::string> const& s : srcs) { - // Create each source at most once. - cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); - if (!emitted.insert(sf).second) { - continue; - } - - // Compute the kind (classification) of this source file. - SourceKind kind; - std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); - cmFileSet const* fs = this->GetFileSetForSource(config, sf); - if (sf->GetCustomCommand()) { - kind = SourceKindCustomCommand; - } else if (!this->Target->IsNormal() && !this->Target->IsImported() && - fs && (fs->GetType() == "CXX_MODULES"_s)) { - kind = SourceKindCxxModuleSource; - } else if (this->Target->GetType() == cmStateEnums::UTILITY || - this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY - // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 - // NOLINTNEXTLINE(bugprone-branch-clone) - ) { - kind = SourceKindExtra; - } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) { - kind = SourceKindUnityBatched; - // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) { - kind = SourceKindHeader; - } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - kind = SourceKindExternalObject; - } else if (!sf->GetOrDetermineLanguage().empty()) { - kind = SourceKindObjectSource; - } else if (ext == "def") { - kind = SourceKindModuleDefinition; - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { - badObjLib.push_back(sf); - } - } else if (ext == "idl") { - kind = SourceKindIDL; - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { - badObjLib.push_back(sf); - } - } else if (ext == "resx") { - kind = SourceKindResx; - } else if (ext == "appxmanifest") { - kind = SourceKindAppManifest; - } else if (ext == "manifest") { - if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) { - kind = SourceKindExtra; - } else { - kind = SourceKindManifest; - } - } else if (ext == "pfx") { - kind = SourceKindCertificate; - } else if (ext == "xaml") { - kind = SourceKindXaml; - } else if (header_regex.find(sf->ResolveFullPath())) { - kind = SourceKindHeader; - } else { - kind = SourceKindExtra; - } - - // Save this classified source file in the result vector. - files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind }); - } - - if (!badObjLib.empty()) { - std::ostringstream e; - e << "OBJECT library \"" << this->GetName() << "\" contains:\n"; - for (cmSourceFile* i : badObjLib) { - e << " " << i->GetLocation().GetName() << "\n"; - } - e << "but may contain only sources that compile, header files, and " - "other files that would not affect linking of a normal library."; - this->GlobalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); - } -} - -std::vector<cmGeneratorTarget::AllConfigSource> const& -cmGeneratorTarget::GetAllConfigSources() const -{ - if (this->AllConfigSources.empty()) { - this->ComputeAllConfigSources(); - } - return this->AllConfigSources; -} - -void cmGeneratorTarget::ComputeAllConfigSources() const -{ - std::vector<std::string> configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - - std::map<cmSourceFile const*, size_t> index; - - for (size_t ci = 0; ci < configs.size(); ++ci) { - KindedSources const& sources = this->GetKindedSources(configs[ci]); - for (SourceAndKind const& src : sources.Sources) { - auto mi = index.find(src.Source.Value); - if (mi == index.end()) { - AllConfigSource acs; - acs.Source = src.Source.Value; - acs.Kind = src.Kind; - this->AllConfigSources.push_back(std::move(acs)); - std::map<cmSourceFile const*, size_t>::value_type entry( - src.Source.Value, this->AllConfigSources.size() - 1); - mi = index.insert(entry).first; - } - this->AllConfigSources[mi->second].Configs.push_back(ci); - } - } -} - -std::vector<cmGeneratorTarget::AllConfigSource> -cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const -{ - std::vector<AllConfigSource> result; - for (AllConfigSource const& source : this->GetAllConfigSources()) { - if (source.Kind == kind) { - result.push_back(source); - } - } - return result; -} - -std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const -{ - std::set<std::string> languages; - std::vector<AllConfigSource> const& sources = this->GetAllConfigSources(); - for (AllConfigSource const& si : sources) { - std::string const& lang = si.Source->GetOrDetermineLanguage(); - if (!lang.empty()) { - languages.emplace(lang); - } - } - return languages; -} - std::string cmGeneratorTarget::GetCompilePDBName( const std::string& config) const { @@ -2750,214 +1777,6 @@ const std::string* cmGeneratorTarget::GetExportMacro() const return nullptr; } -class cmTargetCollectLinkLanguages -{ -public: - cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, - std::string config, - std::unordered_set<std::string>& languages, - cmGeneratorTarget const* head, bool secondPass) - : Config(std::move(config)) - , Languages(languages) - , HeadTarget(head) - , SecondPass(secondPass) - { - this->Visited.insert(target); - } - - void Visit(cmLinkItem const& item) - { - if (!item.Target) { - return; - } - if (!this->Visited.insert(item.Target).second) { - return; - } - cmLinkInterface const* iface = item.Target->GetLinkInterface( - this->Config, this->HeadTarget, this->SecondPass); - if (!iface) { - return; - } - if (iface->HadLinkLanguageSensitiveCondition) { - this->HadLinkLanguageSensitiveCondition = true; - } - - for (std::string const& language : iface->Languages) { - this->Languages.insert(language); - } - - for (cmLinkItem const& lib : iface->Libraries) { - this->Visit(lib); - } - } - - bool GetHadLinkLanguageSensitiveCondition() const - { - return this->HadLinkLanguageSensitiveCondition; - } - -private: - std::string Config; - std::unordered_set<std::string>& Languages; - cmGeneratorTarget const* HeadTarget; - std::set<cmGeneratorTarget const*> Visited; - bool SecondPass; - bool HadLinkLanguageSensitiveCondition = false; -}; - -cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( - const std::string& config) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - static LinkClosure const empty = { {}, {} }; - return ∅ - } - - std::string key(cmSystemTools::UpperCase(config)); - auto i = this->LinkClosureMap.find(key); - if (i == this->LinkClosureMap.end()) { - LinkClosure lc; - this->ComputeLinkClosure(config, lc); - LinkClosureMapType::value_type entry(key, lc); - i = this->LinkClosureMap.insert(entry).first; - } - return &i->second; -} - -class cmTargetSelectLinker -{ - int Preference = 0; - cmGeneratorTarget const* Target; - cmGlobalGenerator* GG; - std::set<std::string> Preferred; - -public: - cmTargetSelectLinker(cmGeneratorTarget const* target) - : Target(target) - { - this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); - } - void Consider(const std::string& lang) - { - int preference = this->GG->GetLinkerPreference(lang); - if (preference > this->Preference) { - this->Preference = preference; - this->Preferred.clear(); - } - if (preference == this->Preference) { - this->Preferred.insert(lang); - } - } - std::string Choose() - { - if (this->Preferred.empty()) { - return ""; - } - if (this->Preferred.size() > 1) { - std::ostringstream e; - e << "Target " << this->Target->GetName() - << " contains multiple languages with the highest linker preference" - << " (" << this->Preference << "):\n"; - for (std::string const& li : this->Preferred) { - e << " " << li << "\n"; - } - e << "Set the LINKER_LANGUAGE property for this target."; - cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); - cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Target->GetBacktrace()); - } - return *this->Preferred.begin(); - } -}; - -bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config, - LinkClosure& lc, - bool secondPass) const -{ - // Get languages built in this target. - std::unordered_set<std::string> languages; - cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Link, secondPass); - assert(impl); - languages.insert(impl->Languages.cbegin(), impl->Languages.cend()); - - // Add interface languages from linked targets. - // cmTargetCollectLinkLanguages cll(this, config, languages, this, - // secondPass); - cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass); - for (cmLinkImplItem const& lib : impl->Libraries) { - cll.Visit(lib); - } - - // Store the transitive closure of languages. - cm::append(lc.Languages, languages); - - // Choose the language whose linker should be used. - if (secondPass || lc.LinkerLanguage.empty()) { - // Find the language with the highest preference value. - cmTargetSelectLinker tsl(this); - - // First select from the languages compiled directly in this target. - for (std::string const& l : impl->Languages) { - tsl.Consider(l); - } - - // Now consider languages that propagate from linked targets. - for (std::string const& lang : languages) { - std::string propagates = - "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; - if (this->Makefile->IsOn(propagates)) { - tsl.Consider(lang); - } - } - - lc.LinkerLanguage = tsl.Choose(); - } - - return impl->HadLinkLanguageSensitiveCondition || - cll.GetHadLinkLanguageSensitiveCondition(); -} - -void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, - LinkClosure& lc) const -{ - bool secondPass = false; - - { - LinkClosure linkClosure; - linkClosure.LinkerLanguage = this->LinkerLanguage; - - bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") || - !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty(); - - // Get languages built in this target. - secondPass = this->ComputeLinkClosure(config, linkClosure, false) && - !hasHardCodedLinkerLanguage; - this->LinkerLanguage = linkClosure.LinkerLanguage; - if (!secondPass) { - lc = std::move(linkClosure); - } - } - - if (secondPass) { - LinkClosure linkClosure; - - this->ComputeLinkClosure(config, linkClosure, secondPass); - lc = std::move(linkClosure); - - // linker language must not be changed between the two passes - if (this->LinkerLanguage != lc.LinkerLanguage) { - std::ostringstream e; - e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> " - "changes\nthe linker language for target \"" - << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '" - << lc.LinkerLanguage << "') which is invalid."; - cmSystemTools::Error(e.str()); - } - } -} - cmGeneratorTarget::NameComponents const& cmGeneratorTarget::GetFullNameComponents( std::string const& config, cmStateEnums::ArtifactType artifact) const @@ -3122,296 +1941,6 @@ void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result, result); } -static void processILibs(const std::string& config, - cmGeneratorTarget const* headTarget, - cmLinkItem const& item, cmGlobalGenerator* gg, - std::vector<cmGeneratorTarget const*>& tgts, - std::set<cmGeneratorTarget const*>& emitted) -{ - if (item.Target && emitted.insert(item.Target).second) { - tgts.push_back(item.Target); - if (cmLinkInterfaceLibraries const* iface = - item.Target->GetLinkInterfaceLibraries(config, headTarget, - UseTo::Compile)) { - for (cmLinkItem const& lib : iface->Libraries) { - processILibs(config, headTarget, lib, gg, tgts, emitted); - } - } - } -} - -const std::vector<const cmGeneratorTarget*>& -cmGeneratorTarget::GetLinkImplementationClosure( - const std::string& config) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - static std::vector<const cmGeneratorTarget*> const empty; - return empty; - } - - LinkImplClosure& tgts = this->LinkImplClosureMap[config]; - if (!tgts.Done) { - tgts.Done = true; - std::set<cmGeneratorTarget const*> emitted; - - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config, UseTo::Compile); - assert(impl); - - for (cmLinkImplItem const& lib : impl->Libraries) { - processILibs(config, this, lib, - this->LocalGenerator->GetGlobalGenerator(), tgts, emitted); - } - } - return tgts; -} - -class cmTargetTraceDependencies -{ -public: - cmTargetTraceDependencies(cmGeneratorTarget* target); - void Trace(); - -private: - cmGeneratorTarget* GeneratorTarget; - cmMakefile* Makefile; - cmLocalGenerator* LocalGenerator; - cmGlobalGenerator const* GlobalGenerator; - using SourceEntry = cmGeneratorTarget::SourceEntry; - SourceEntry* CurrentEntry; - std::queue<cmSourceFile*> SourceQueue; - std::set<cmSourceFile*> SourcesQueued; - using NameMapType = std::map<std::string, cmSourcesWithOutput>; - NameMapType NameMap; - std::vector<std::string> NewSources; - - void QueueSource(cmSourceFile* sf); - void FollowName(std::string const& name); - void FollowNames(std::vector<std::string> const& names); - bool IsUtility(std::string const& dep); - void CheckCustomCommand(cmCustomCommand const& cc); - void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); -}; - -cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) - : GeneratorTarget(target) -{ - // Convenience. - this->Makefile = target->Target->GetMakefile(); - this->LocalGenerator = target->GetLocalGenerator(); - this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); - this->CurrentEntry = nullptr; - - // Queue all the source files already specified for the target. - std::set<cmSourceFile*> emitted; - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& c : configs) { - std::vector<cmSourceFile*> sources; - this->GeneratorTarget->GetSourceFiles(sources, c); - for (cmSourceFile* sf : sources) { - const std::set<cmGeneratorTarget const*> tgts = - this->GlobalGenerator->GetFilenameTargetDepends(sf); - if (cm::contains(tgts, this->GeneratorTarget)) { - std::ostringstream e; - e << "Evaluation output file\n \"" << sf->ResolveFullPath() - << "\"\ndepends on the sources of a target it is used in. This " - "is a dependency loop and is not allowed."; - this->GeneratorTarget->LocalGenerator->IssueMessage( - MessageType::FATAL_ERROR, e.str()); - return; - } - if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - } - } - } - - // Queue pre-build, pre-link, and post-build rule dependencies. - this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); - this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); - this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); -} - -void cmTargetTraceDependencies::Trace() -{ - // Process one dependency at a time until the queue is empty. - while (!this->SourceQueue.empty()) { - // Get the next source from the queue. - cmSourceFile* sf = this->SourceQueue.front(); - this->SourceQueue.pop(); - this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; - - // Queue dependencies added explicitly by the user. - if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { - cmList objDeps{ *additionalDeps }; - for (auto& objDep : objDeps) { - if (cmSystemTools::FileIsFullPath(objDep)) { - objDep = cmSystemTools::CollapseFullPath(objDep); - } - } - this->FollowNames(objDeps); - } - - // Queue the source needed to generate this file, if any. - this->FollowName(sf->ResolveFullPath()); - - // Queue dependencies added programmatically by commands. - this->FollowNames(sf->GetDepends()); - - // Queue custom command dependencies. - if (cmCustomCommand const* cc = sf->GetCustomCommand()) { - this->CheckCustomCommand(*cc); - } - } - this->CurrentEntry = nullptr; - - this->GeneratorTarget->AddTracedSources(this->NewSources); -} - -void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) -{ - if (this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - - // Make sure this file is in the target at the end. - this->NewSources.push_back(sf->ResolveFullPath()); - } -} - -void cmTargetTraceDependencies::FollowName(std::string const& name) -{ - // Use lower bound with key comparison to not repeat the search for the - // insert position if the name could not be found (which is the common case). - auto i = this->NameMap.lower_bound(name); - if (i == this->NameMap.end() || i->first != name) { - // Check if we know how to generate this file. - cmSourcesWithOutput sources = - this->LocalGenerator->GetSourcesWithOutput(name); - // If we failed to find a target or source and we have a relative path, it - // might be a valid source if made relative to the current binary - // directory. - if (!sources.Target && !sources.Source && - !cmSystemTools::FileIsFullPath(name)) { - auto fullname = - cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); - fullname = cmSystemTools::CollapseFullPath( - fullname, this->Makefile->GetHomeOutputDirectory()); - sources = this->LocalGenerator->GetSourcesWithOutput(fullname); - } - i = this->NameMap.emplace_hint(i, name, sources); - } - if (cmTarget* t = i->second.Target) { - // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or - // POST_BUILD command. - this->GeneratorTarget->Target->AddUtility(t->GetName(), false); - } - if (cmSourceFile* sf = i->second.Source) { - // For now only follow the dependency if the source file is not a - // byproduct. Semantics of byproducts in a non-Ninja context will have to - // be defined first. - if (!i->second.SourceIsByproduct) { - // Record the dependency we just followed. - if (this->CurrentEntry) { - this->CurrentEntry->Depends.push_back(sf); - } - this->QueueSource(sf); - } - } -} - -void cmTargetTraceDependencies::FollowNames( - std::vector<std::string> const& names) -{ - for (std::string const& name : names) { - this->FollowName(name); - } -} - -bool cmTargetTraceDependencies::IsUtility(std::string const& dep) -{ - // Dependencies on targets (utilities) are supposed to be named by - // just the target name. However for compatibility we support - // naming the output file generated by the target (assuming there is - // no output-name property which old code would not have set). In - // that case the target name will be the file basename of the - // dependency. - std::string util = cmSystemTools::GetFilenameName(dep); - if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { - util = cmSystemTools::GetFilenameWithoutLastExtension(util); - } - - // Check for a target with this name. - if (cmGeneratorTarget* t = - this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( - util)) { - // If we find the target and the dep was given as a full path, - // then make sure it was not a full path to something else, and - // the fact that the name matched a target was just a coincidence. - if (cmSystemTools::FileIsFullPath(dep)) { - if (t->GetType() >= cmStateEnums::EXECUTABLE && - t->GetType() <= cmStateEnums::MODULE_LIBRARY) { - // This is really only for compatibility so we do not need to - // worry about configuration names and output names. - std::string tLocation = t->GetLocationForBuild(); - tLocation = cmSystemTools::GetFilenamePath(tLocation); - std::string depLocation = cmSystemTools::GetFilenamePath(dep); - depLocation = cmSystemTools::CollapseFullPath(depLocation); - tLocation = cmSystemTools::CollapseFullPath(tLocation); - if (depLocation == tLocation) { - this->GeneratorTarget->Target->AddUtility(util, false); - return true; - } - } - } else { - // The original name of the dependency was not a full path. It - // must name a target, so add the target-level dependency. - this->GeneratorTarget->Target->AddUtility(util, true); - return true; - } - } - - // The dependency does not name a target built in this project. - return false; -} - -void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) -{ - // Collect dependencies referenced by all configurations. - std::set<std::string> depends; - for (std::string const& config : - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { - for (cmCustomCommandGenerator const& ccg : - this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { - // Collect target-level dependencies referenced in command lines. - for (auto const& util : ccg.GetUtilities()) { - this->GeneratorTarget->Target->AddUtility(util); - } - - // Collect file-level dependencies referenced in DEPENDS. - depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); - } - } - - // Queue file-level dependencies. - for (std::string const& dep : depends) { - if (!this->IsUtility(dep)) { - // The dependency does not name a target and may be a file we - // know how to generate. Queue it. - this->FollowName(dep); - } - } -} - -void cmTargetTraceDependencies::CheckCustomCommands( - const std::vector<cmCustomCommand>& commands) -{ - for (cmCustomCommand const& command : commands) { - this->CheckCustomCommand(command); - } -} - void cmGeneratorTarget::TraceDependencies() { // CMake-generated targets have no dependencies to trace. Normally tracing @@ -3797,570 +2326,6 @@ std::string cmGeneratorTarget::GetClangTidyExportFixesDirectory( return cmSystemTools::CollapseFullPath(path); } -namespace { -void processIncludeDirectories(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, - std::vector<BT<std::string>>& includes, - std::unordered_set<std::string>& uniqueIncludes, - bool debugIncludes) -{ - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { - cmLinkImplItem const& item = entry.LinkImplItem; - std::string const& targetName = item.AsStr(); - bool const fromImported = item.Target && item.Target->IsImported(); - bool const checkCMP0027 = item.CheckCMP0027; - - std::string usedIncludes; - for (std::string& entryInclude : entry.Values) { - if (fromImported && !cmSystemTools::FileExists(entryInclude)) { - std::ostringstream e; - MessageType messageType = MessageType::FATAL_ERROR; - if (checkCMP0027) { - switch (tgt->GetPolicyStatusCMP0027()) { - case cmPolicies::WARN: - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n"; - CM_FALLTHROUGH; - case cmPolicies::OLD: - messageType = MessageType::AUTHOR_WARNING; - break; - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::NEW: - break; - } - } - /* clang-format off */ - e << "Imported target \"" << targetName << "\" includes " - "non-existent path\n \"" << entryInclude << "\"\nin its " - "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n" - "* The path was deleted, renamed, or moved to another " - "location.\n" - "* An install or uninstall procedure did not complete " - "successfully.\n" - "* The installation package was faulty and references files it " - "does not provide.\n"; - /* clang-format on */ - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - return; - } - - if (!cmSystemTools::FileIsFullPath(entryInclude)) { - std::ostringstream e; - bool noMessage = false; - MessageType messageType = MessageType::FATAL_ERROR; - if (!targetName.empty()) { - /* clang-format off */ - e << "Target \"" << targetName << "\" contains relative " - "path in its INTERFACE_INCLUDE_DIRECTORIES:\n" - " \"" << entryInclude << "\""; - /* clang-format on */ - } else { - switch (tgt->GetPolicyStatusCMP0021()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - e << "Found relative path while evaluating include directories of " - "\"" - << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n"; - } - if (!noMessage) { - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - - if (!cmIsOff(entryInclude)) { - cmSystemTools::ConvertToUnixSlashes(entryInclude); - } - - if (uniqueIncludes.insert(entryInclude).second) { - includes.emplace_back(entryInclude, entry.Backtrace); - if (debugIncludes) { - usedIncludes += " * " + entryInclude + "\n"; - } - } - } - if (!usedIncludes.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used includes for target ") + tgt->GetName() + ":\n" + - usedIncludes, - entry.Backtrace); - } - } -} -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( - const std::string& config, const std::string& lang) const -{ - ConfigAndLanguage cacheKey(config, lang); - { - auto it = this->IncludeDirectoriesCache.find(cacheKey); - if (it != this->IncludeDirectoriesCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> includes; - std::unordered_set<std::string> uniqueIncludes; - - cmGeneratorExpressionDAGChecker dagChecker( - this, "INCLUDE_DIRECTORIES", nullptr, nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugIncludes = !this->DebugIncludesDone && - cm::contains(debugProperties, "INCLUDE_DIRECTORIES"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugIncludesDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, lang, &dagChecker, this->IncludeDirectoriesEntries); - - if (lang == "Swift") { - AddLangSpecificImplicitIncludeDirectories( - this, lang, config, "Swift_MODULE_DIRECTORY", - IncludeDirectoryFallBack::BINARY, entries); - } - - if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) { - - const std::string propertyName = "ISPC_HEADER_DIRECTORY"; - - // If this target has ISPC sources make sure to add the header - // directory to other compilation units - if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) { - if (cmValue val = this->GetProperty(propertyName)) { - includes.emplace_back(*val); - } else { - includes.emplace_back(this->GetObjectDirectory(config)); - } - } - - AddLangSpecificImplicitIncludeDirectories( - this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT, - entries); - } - - AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - processIncludeDirectories(this, entries, includes, uniqueIncludes, - debugIncludes); - - if (this->IsApple()) { - if (cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config, UseTo::Compile)) { - for (cmLinkImplItem const& lib : impl->Libraries) { - std::string libDir; - if (lib.Target == nullptr) { - libDir = cmSystemTools::CollapseFullPath( - lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); - } else if (lib.Target->Target->IsFrameworkOnApple() || - this->IsImportedFrameworkFolderOnApple(config)) { - libDir = lib.Target->GetLocation(config); - } else { - continue; - } - - auto fwDescriptor = - this->GetGlobalGenerator()->SplitFrameworkPath(libDir); - if (!fwDescriptor) { - continue; - } - - auto fwInclude = fwDescriptor->GetFrameworkPath(); - if (uniqueIncludes.insert(fwInclude).second) { - includes.emplace_back(fwInclude, cmListFileBacktrace()); - } - } - } - } - - this->IncludeDirectoriesCache.emplace(cacheKey, includes); - return includes; -} - -enum class OptionsParse -{ - None, - Shell -}; - -namespace { -const auto DL_BEGIN = "<DEVICE_LINK>"_s; -const auto DL_END = "</DEVICE_LINK>"_s; - -void processOptions(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries const& entries, - std::vector<BT<std::string>>& options, - std::unordered_set<std::string>& uniqueOptions, - bool debugOptions, const char* logName, OptionsParse parse, - bool processDeviceOptions = false) -{ - bool splitOption = !processDeviceOptions; - for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { - std::string usedOptions; - for (std::string const& opt : entry.Values) { - if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) { - options.emplace_back(opt, entry.Backtrace); - splitOption = opt == DL_BEGIN; - continue; - } - - if (uniqueOptions.insert(opt).second) { - if (parse == OptionsParse::Shell && - cmHasLiteralPrefix(opt, "SHELL:")) { - if (splitOption) { - std::vector<std::string> tmp; - cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp); - for (std::string& o : tmp) { - options.emplace_back(std::move(o), entry.Backtrace); - } - } else { - options.emplace_back(std::string(opt.c_str() + 6), - entry.Backtrace); - } - } else { - options.emplace_back(opt, entry.Backtrace); - } - if (debugOptions) { - usedOptions += " * " + opt + "\n"; - } - } - } - if (!usedOptions.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used ") + logName + std::string(" for target ") + - tgt->GetName() + ":\n" + usedOptions, - entry.Backtrace); - } - } -} - -std::vector<BT<std::string>> wrapOptions( - std::vector<std::string>& options, const cmListFileBacktrace& bt, - const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep, - bool concatFlagAndArgs) -{ - std::vector<BT<std::string>> result; - - if (options.empty()) { - return result; - } - - if (wrapperFlag.empty()) { - // nothing specified, insert elements as is - result.reserve(options.size()); - for (std::string& o : options) { - result.emplace_back(std::move(o), bt); - } - return result; - } - - for (std::vector<std::string>::size_type index = 0; index < options.size(); - index++) { - if (cmHasLiteralPrefix(options[index], "LINKER:")) { - // LINKER wrapper specified, insert elements as is - result.emplace_back(std::move(options[index]), bt); - continue; - } - if (cmHasLiteralPrefix(options[index], "-Wl,")) { - // replace option by LINKER wrapper - result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt); - continue; - } - if (cmHasLiteralPrefix(options[index], "-Xlinker=")) { - // replace option by LINKER wrapper - result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt); - continue; - } - if (options[index] == "-Xlinker") { - // replace option by LINKER wrapper - if (index + 1 < options.size()) { - result.emplace_back("LINKER:" + options[++index], bt); - } else { - result.emplace_back(std::move(options[index]), bt); - } - continue; - } - - // collect all options which must be transformed - std::vector<std::string> opts; - while (index < options.size()) { - if (!cmHasLiteralPrefix(options[index], "LINKER:") && - !cmHasLiteralPrefix(options[index], "-Wl,") && - !cmHasLiteralPrefix(options[index], "-Xlinker")) { - opts.emplace_back(std::move(options[index++])); - } else { - --index; - break; - } - } - if (opts.empty()) { - continue; - } - - if (!wrapperSep.empty()) { - if (concatFlagAndArgs) { - // insert flag elements except last one - for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { - result.emplace_back(*i, bt); - } - // concatenate last flag element and all list values - // in one option - result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); - } else { - for (std::string const& i : wrapperFlag) { - result.emplace_back(i, bt); - } - // concatenate all list values in one option - result.emplace_back(cmJoin(opts, wrapperSep), bt); - } - } else { - // prefix each element of list with wrapper - if (concatFlagAndArgs) { - std::transform(opts.begin(), opts.end(), opts.begin(), - [&wrapperFlag](std::string const& o) -> std::string { - return wrapperFlag.back() + o; - }); - } - for (std::string& o : opts) { - for (auto i = wrapperFlag.begin(), - e = concatFlagAndArgs ? wrapperFlag.end() - 1 - : wrapperFlag.end(); - i != e; ++i) { - result.emplace_back(*i, bt); - } - result.emplace_back(std::move(o), bt); - } - } - } - return result; -} -} - -void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions( - std::string const& config, std::string const& language) const -{ - ConfigAndLanguage cacheKey(config, language); - { - auto it = this->CompileOptionsCache.find(cacheKey); - if (it != this->CompileOptionsCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr, - nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugOptions = !this->DebugCompileOptionsDone && - cm::contains(debugProperties, "COMPILE_OPTIONS"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugCompileOptionsDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->CompileOptionsEntries); - - AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - processOptions(this, entries, result, uniqueOptions, debugOptions, - "compile options", OptionsParse::Shell); - - CompileOptionsCache.emplace(cacheKey, result); - return result; -} - -void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, - const std::string& config) const -{ - std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures( - std::string const& config) const -{ - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueFeatures; - - cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr, - nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugFeatures = !this->DebugCompileFeaturesDone && - cm::contains(debugProperties, "COMPILE_FEATURES"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugCompileFeaturesDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, std::string(), &dagChecker, this->CompileFeaturesEntries); - - AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES", - std::string(), &dagChecker, entries, - IncludeRuntimeInterface::Yes); - - processOptions(this, entries, result, uniqueFeatures, debugFeatures, - "compile features", OptionsParse::None); - - return result; -} - -void cmGeneratorTarget::GetCompileDefinitions( - std::vector<std::string>& result, const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = - this->GetCompileDefinitions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions( - std::string const& config, std::string const& language) const -{ - ConfigAndLanguage cacheKey(config, language); - { - auto it = this->CompileDefinitionsCache.find(cacheKey); - if (it != this->CompileDefinitionsCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> list; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker( - this, "COMPILE_DEFINITIONS", nullptr, nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugDefines = !this->DebugCompileDefinitionsDone && - cm::contains(debugProperties, "COMPILE_DEFINITIONS"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugCompileDefinitionsDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->CompileDefinitionsEntries); - - AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - if (!config.empty()) { - std::string configPropName = - "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); - cmValue configProp = this->GetProperty(configPropName); - if (configProp) { - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) { - case cmPolicies::WARN: { - this->LocalGenerator->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0043)); - CM_FALLTHROUGH; - } - case cmPolicies::OLD: { - std::unique_ptr<TargetPropertyEntry> entry = - CreateTargetPropertyEntry( - *this->LocalGenerator->GetCMakeInstance(), *configProp); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - this, config, language, &dagChecker, *entry)); - } break; - case cmPolicies::NEW: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - break; - } - } - } - - processOptions(this, entries, list, uniqueOptions, debugDefines, - "compile definitions", OptionsParse::None); - - this->CompileDefinitionsCache.emplace(cacheKey, list); - return list; -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders( - const std::string& config, const std::string& language) const -{ - ConfigAndLanguage cacheKey(config, language); - { - auto it = this->PrecompileHeadersCache.find(cacheKey); - if (it != this->PrecompileHeadersCache.end()) { - return it->second; - } - } - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker( - this, "PRECOMPILE_HEADERS", nullptr, nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugDefines = !this->DebugPrecompileHeadersDone && - std::find(debugProperties.begin(), debugProperties.end(), - "PRECOMPILE_HEADERS") != debugProperties.end(); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugPrecompileHeadersDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->PrecompileHeadersEntries); - - AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - std::vector<BT<std::string>> list; - processOptions(this, entries, list, uniqueOptions, debugDefines, - "precompile headers", OptionsParse::None); - - this->PrecompileHeadersCache.emplace(cacheKey, list); - return list; -} - std::vector<std::string> cmGeneratorTarget::GetPchArchs( std::string const& config, std::string const& lang) const { @@ -4706,388 +2671,6 @@ bool cmGeneratorTarget::IsSourceFilePartOfUnityBatch( this->UnityBatchedSourceFiles.end(); } -void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - if (this->IsDeviceLink() && - this->GetPolicyStatusCMP0105() != cmPolicies::NEW) { - // link options are not propagated to the device link step - return; - } - - std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( - std::string const& config, std::string const& language) const -{ - ConfigAndLanguage cacheKey( - config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); - { - auto it = this->LinkOptionsCache.find(cacheKey); - if (it != this->LinkOptionsCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr, - nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugOptions = !this->DebugLinkOptionsDone && - cm::contains(debugProperties, "LINK_OPTIONS"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugLinkOptionsDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->LinkOptionsEntries); - - AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes, - this->GetPolicyStatusCMP0099() == cmPolicies::NEW - ? UseTo::Link - : UseTo::Compile); - - processOptions(this, entries, result, uniqueOptions, debugOptions, - "link options", OptionsParse::Shell, this->IsDeviceLink()); - - if (this->IsDeviceLink()) { - // wrap host link options - const std::string wrapper(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG")); - cmList wrapperFlag{ wrapper }; - const std::string wrapperSep(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP")); - bool concatFlagAndArgs = true; - if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { - concatFlagAndArgs = false; - wrapperFlag.pop_back(); - } - - auto it = result.begin(); - while (it != result.end()) { - if (it->Value == DL_BEGIN) { - // device link options, no treatment - it = result.erase(it); - it = std::find_if(it, result.end(), [](const BT<std::string>& item) { - return item.Value == DL_END; - }); - if (it != result.end()) { - it = result.erase(it); - } - } else { - // host link options must be wrapped - std::vector<std::string> options; - cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options); - auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag, - wrapperSep, concatFlagAndArgs); - it = result.erase(it); - // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect - // C++11 standard: 'std::vector::insert()' do not returns an iterator, - // so need to recompute the iterator after insertion. - if (it == result.end()) { - cm::append(result, hostOptions); - it = result.end(); - } else { - auto index = it - result.begin(); - result.insert(it, hostOptions.begin(), hostOptions.end()); - it = result.begin() + index + hostOptions.size(); - } - } - } - } - - // Last step: replace "LINKER:" prefixed elements by - // actual linker wrapper - result = this->ResolveLinkerWrapper(result, language); - - this->LinkOptionsCache.emplace(cacheKey, result); - return result; -} - -std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( - std::vector<BT<std::string>>& result, const std::string& language, - bool joinItems) const -{ - // replace "LINKER:" prefixed elements by actual linker wrapper - const std::string wrapper(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + - (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG" - : "_LINKER_WRAPPER_FLAG"))); - cmList wrapperFlag{ wrapper }; - const std::string wrapperSep(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + - (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP" - : "_LINKER_WRAPPER_FLAG_SEP"))); - bool concatFlagAndArgs = true; - if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { - concatFlagAndArgs = false; - wrapperFlag.pop_back(); - } - - const std::string LINKER{ "LINKER:" }; - const std::string SHELL{ "SHELL:" }; - const std::string LINKER_SHELL = LINKER + SHELL; - - std::vector<BT<std::string>>::iterator entry; - while ((entry = std::find_if(result.begin(), result.end(), - [&LINKER](BT<std::string> const& item) -> bool { - return item.Value.compare(0, LINKER.length(), - LINKER) == 0; - })) != result.end()) { - std::string value = std::move(entry->Value); - cmListFileBacktrace bt = std::move(entry->Backtrace); - entry = result.erase(entry); - - std::vector<std::string> linkerOptions; - if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) { - cmSystemTools::ParseUnixCommandLine( - value.c_str() + LINKER_SHELL.length(), linkerOptions); - } else { - linkerOptions = cmTokenize(value.substr(LINKER.length()), ","); - } - - if (linkerOptions.empty() || - (linkerOptions.size() == 1 && linkerOptions.front().empty())) { - continue; - } - - // for now, raise an error if prefix SHELL: is part of arguments - if (std::find_if(linkerOptions.begin(), linkerOptions.end(), - [&SHELL](const std::string& item) -> bool { - return item.find(SHELL) != std::string::npos; - }) != linkerOptions.end()) { - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, - "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.", - this->GetBacktrace()); - return result; - } - - std::vector<BT<std::string>> options = wrapOptions( - linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs); - if (joinItems) { - result.insert(entry, - cmJoin(cmRange<decltype(options.cbegin())>( - options.cbegin(), options.cend()), - " "_s)); - } else { - result.insert(entry, options.begin(), options.end()); - } - } - return result; -} - -void cmGeneratorTarget::GetStaticLibraryLinkOptions( - std::vector<std::string>& result, const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = - this->GetStaticLibraryLinkOptions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( - std::string const& config, std::string const& language) const -{ - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker( - this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, this->LocalGenerator); - - EvaluatedTargetPropertyEntries entries; - if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { - std::unique_ptr<TargetPropertyEntry> entry = CreateTargetPropertyEntry( - *this->LocalGenerator->GetCMakeInstance(), *linkOptions); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - this, config, language, &dagChecker, *entry)); - } - processOptions(this, entries, result, uniqueOptions, false, - "static library link options", OptionsParse::Shell); - - return result; -} - -namespace { -void processLinkDirectories(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, - std::vector<BT<std::string>>& directories, - std::unordered_set<std::string>& uniqueDirectories, - bool debugDirectories) -{ - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { - cmLinkImplItem const& item = entry.LinkImplItem; - std::string const& targetName = item.AsStr(); - - std::string usedDirectories; - for (std::string& entryDirectory : entry.Values) { - if (!cmSystemTools::FileIsFullPath(entryDirectory)) { - std::ostringstream e; - bool noMessage = false; - MessageType messageType = MessageType::FATAL_ERROR; - if (!targetName.empty()) { - /* clang-format off */ - e << "Target \"" << targetName << "\" contains relative " - "path in its INTERFACE_LINK_DIRECTORIES:\n" - " \"" << entryDirectory << "\""; - /* clang-format on */ - } else { - switch (tgt->GetPolicyStatusCMP0081()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - e << "Found relative path while evaluating link directories of " - "\"" - << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; - } - if (!noMessage) { - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - - // Sanitize the path the same way the link_directories command does - // in case projects set the LINK_DIRECTORIES property directly. - cmSystemTools::ConvertToUnixSlashes(entryDirectory); - if (uniqueDirectories.insert(entryDirectory).second) { - directories.emplace_back(entryDirectory, entry.Backtrace); - if (debugDirectories) { - usedDirectories += " * " + entryDirectory + "\n"; - } - } - } - if (!usedDirectories.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used link directories for target ") + tgt->GetName() + - ":\n" + usedDirectories, - entry.Backtrace); - } - } -} -} - -void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = - this->GetLinkDirectories(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories( - std::string const& config, std::string const& language) const -{ - ConfigAndLanguage cacheKey( - config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); - { - auto it = this->LinkDirectoriesCache.find(cacheKey); - if (it != this->LinkDirectoriesCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueDirectories; - - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr, - nullptr, this->LocalGenerator); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugDirectories = !this->DebugLinkDirectoriesDone && - cm::contains(debugProperties, "LINK_DIRECTORIES"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugLinkDirectoriesDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->LinkDirectoriesEntries); - - AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes, - this->GetPolicyStatusCMP0099() == cmPolicies::NEW - ? UseTo::Link - : UseTo::Compile); - - processLinkDirectories(this, entries, result, uniqueDirectories, - debugDirectories); - - this->LinkDirectoriesCache.emplace(cacheKey, result); - return result; -} - -void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( - std::string const& config, std::string const& language) const -{ - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr, - nullptr, this->LocalGenerator); - - EvaluatedTargetPropertyEntries entries; - if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) { - cmList depends{ *linkDepends }; - for (const auto& depend : depends) { - std::unique_ptr<TargetPropertyEntry> entry = CreateTargetPropertyEntry( - *this->LocalGenerator->GetCMakeInstance(), depend); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - this, config, language, &dagChecker, *entry)); - } - } - AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes, - this->GetPolicyStatusCMP0099() == cmPolicies::NEW - ? UseTo::Link - : UseTo::Compile); - - processOptions(this, entries, result, uniqueOptions, false, "link depends", - OptionsParse::None); - - return result; -} - void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const { if (this->IsImported()) { @@ -5894,676 +3477,6 @@ void cmGeneratorTarget::ConstructSourceFileFlags() const } } -const cmGeneratorTarget::CompatibleInterfacesBase& -cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const -{ - cmGeneratorTarget::CompatibleInterfaces& compat = - this->CompatibleInterfacesMap[config]; - if (!compat.Done) { - compat.Done = true; - compat.PropsBool.insert("POSITION_INDEPENDENT_CODE"); - compat.PropsString.insert("AUTOUIC_OPTIONS"); - std::vector<cmGeneratorTarget const*> const& deps = - this->GetLinkImplementationClosure(config); - for (cmGeneratorTarget const* li : deps) { -#define CM_READ_COMPATIBLE_INTERFACE(X, x) \ - if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \ - cmList props(*prop); \ - compat.Props##x.insert(props.begin(), props.end()); \ - } - CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool) - CM_READ_COMPATIBLE_INTERFACE(STRING, String) - CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin) - CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax) -#undef CM_READ_COMPATIBLE_INTERFACE - } - } - return compat; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0; -} - -enum CompatibleType -{ - BoolType, - StringType, - NumberMinType, - NumberMaxType -}; - -template <typename PropertyType> -PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, - const std::string& prop, - const std::string& config, - CompatibleType, PropertyType*); - -template <> -bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, - const std::string& prop, - const std::string& config, - CompatibleType /*unused*/, - bool* /*unused*/) -{ - return tgt->GetLinkInterfaceDependentBoolProperty(prop, config); -} - -template <> -const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, - const std::string& prop, - const std::string& config, - CompatibleType t, - const char** /*unused*/) -{ - switch (t) { - case BoolType: - assert(false && - "String compatibility check function called for boolean"); - return nullptr; - case StringType: - return tgt->GetLinkInterfaceDependentStringProperty(prop, config); - case NumberMinType: - return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config); - case NumberMaxType: - return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config); - } - assert(false && "Unreachable!"); - return nullptr; -} - -template <typename PropertyType> -void checkPropertyConsistency(cmGeneratorTarget const* depender, - cmGeneratorTarget const* dependee, - const std::string& propName, - std::set<std::string>& emitted, - const std::string& config, CompatibleType t, - PropertyType* /*unused*/) -{ - cmValue prop = dependee->GetProperty(propName); - if (!prop) { - return; - } - - cmList props{ *prop }; - std::string pdir = - cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/"); - - for (std::string const& p : props) { - std::string pname = cmSystemTools::HelpFileName(p); - std::string pfile = pdir + pname + ".rst"; - if (cmSystemTools::FileExists(pfile, true)) { - std::ostringstream e; - e << "Target \"" << dependee->GetName() << "\" has property \"" << p - << "\" listed in its " << propName - << " property. " - "This is not allowed. Only user-defined properties may appear " - "listed in the " - << propName << " property."; - depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, - e.str()); - return; - } - if (emitted.insert(p).second) { - getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t, - nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - } - } -} - -namespace { -std::string intersect(const std::set<std::string>& s1, - const std::set<std::string>& s2) -{ - std::set<std::string> intersect; - std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), - std::inserter(intersect, intersect.begin())); - if (!intersect.empty()) { - return *intersect.begin(); - } - return ""; -} - -std::string intersect(const std::set<std::string>& s1, - const std::set<std::string>& s2, - const std::set<std::string>& s3) -{ - std::string result; - result = intersect(s1, s2); - if (!result.empty()) { - return result; - } - result = intersect(s1, s3); - if (!result.empty()) { - return result; - } - return intersect(s2, s3); -} - -std::string intersect(const std::set<std::string>& s1, - const std::set<std::string>& s2, - const std::set<std::string>& s3, - const std::set<std::string>& s4) -{ - std::string result; - result = intersect(s1, s2); - if (!result.empty()) { - return result; - } - result = intersect(s1, s3); - if (!result.empty()) { - return result; - } - result = intersect(s1, s4); - if (!result.empty()) { - return result; - } - return intersect(s2, s3, s4); -} -} - -void cmGeneratorTarget::CheckPropertyCompatibility( - cmComputeLinkInformation& info, const std::string& config) const -{ - const cmComputeLinkInformation::ItemVector& deps = info.GetItems(); - - std::set<std::string> emittedBools; - static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL"; - std::set<std::string> emittedStrings; - static const std::string strString = "COMPATIBLE_INTERFACE_STRING"; - std::set<std::string> emittedMinNumbers; - static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN"; - std::set<std::string> emittedMaxNumbers; - static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX"; - - for (auto const& dep : deps) { - if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - continue; - } - - checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools, - config, BoolType, nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - checkPropertyConsistency<const char*>(this, dep.Target, strString, - emittedStrings, config, StringType, - nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - checkPropertyConsistency<const char*>(this, dep.Target, strNumMin, - emittedMinNumbers, config, - NumberMinType, nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - checkPropertyConsistency<const char*>(this, dep.Target, strNumMax, - emittedMaxNumbers, config, - NumberMaxType, nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - } - - std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers, - emittedMaxNumbers); - - if (!prop.empty()) { - // Use a sorted std::vector to keep the error message sorted. - std::vector<std::string> props; - auto i = emittedBools.find(prop); - if (i != emittedBools.end()) { - props.push_back(strBool); - } - i = emittedStrings.find(prop); - if (i != emittedStrings.end()) { - props.push_back(strString); - } - i = emittedMinNumbers.find(prop); - if (i != emittedMinNumbers.end()) { - props.push_back(strNumMin); - } - i = emittedMaxNumbers.find(prop); - if (i != emittedMaxNumbers.end()) { - props.push_back(strNumMax); - } - std::sort(props.begin(), props.end()); - - std::string propsString = cmStrCat( - cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back()); - - std::ostringstream e; - e << "Property \"" << prop << "\" appears in both the " << propsString - << " property in the dependencies of target \"" << this->GetName() - << "\". This is not allowed. A property may only require " - "compatibility " - "in a boolean interpretation, a numeric minimum, a numeric maximum " - "or a " - "string interpretation, but not a mixture."; - this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str()); - } -} - -template <typename PropertyType> -std::string valueAsString(PropertyType); -template <> -std::string valueAsString<bool>(bool value) -{ - return value ? "TRUE" : "FALSE"; -} -template <> -std::string valueAsString<const char*>(const char* value) -{ - return value ? value : "(unset)"; -} -template <> -std::string valueAsString<std::string>(std::string value) -{ - return value; -} -template <> -std::string valueAsString<cmValue>(cmValue value) -{ - return value ? *value : std::string("(unset)"); -} -template <> -std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/) -{ - return "(unset)"; -} - -static std::string compatibilityType(CompatibleType t) -{ - switch (t) { - case BoolType: - return "Boolean compatibility"; - case StringType: - return "String compatibility"; - case NumberMaxType: - return "Numeric maximum compatibility"; - case NumberMinType: - return "Numeric minimum compatibility"; - } - assert(false && "Unreachable!"); - return ""; -} - -static std::string compatibilityAgree(CompatibleType t, bool dominant) -{ - switch (t) { - case BoolType: - case StringType: - return dominant ? "(Disagree)\n" : "(Agree)\n"; - case NumberMaxType: - case NumberMinType: - return dominant ? "(Dominant)\n" : "(Ignored)\n"; - } - assert(false && "Unreachable!"); - return ""; -} - -template <typename PropertyType> -PropertyType getTypedProperty( - cmGeneratorTarget const* tgt, const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter = nullptr); - -template <> -bool getTypedProperty<bool>(cmGeneratorTarget const* tgt, - const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter) -{ - if (genexInterpreter == nullptr) { - return tgt->GetPropertyAsBool(prop); - } - - cmValue value = tgt->GetProperty(prop); - return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop)); -} - -template <> -const char* getTypedProperty<const char*>( - cmGeneratorTarget const* tgt, const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter) -{ - cmValue value = tgt->GetProperty(prop); - - if (genexInterpreter == nullptr) { - return value.GetCStr(); - } - - return genexInterpreter->Evaluate(value ? *value : "", prop).c_str(); -} - -template <> -std::string getTypedProperty<std::string>( - cmGeneratorTarget const* tgt, const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter) -{ - cmValue value = tgt->GetProperty(prop); - - if (genexInterpreter == nullptr) { - return valueAsString(value); - } - - return genexInterpreter->Evaluate(value ? *value : "", prop); -} - -template <typename PropertyType> -PropertyType impliedValue(PropertyType); -template <> -bool impliedValue<bool>(bool /*unused*/) -{ - return false; -} -template <> -const char* impliedValue<const char*>(const char* /*unused*/) -{ - return ""; -} -template <> -std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*) -{ - return std::string(); -} - -template <typename PropertyType> -std::pair<bool, PropertyType> consistentProperty(PropertyType lhs, - PropertyType rhs, - CompatibleType t); - -template <> -std::pair<bool, bool> consistentProperty(bool lhs, bool rhs, - CompatibleType /*unused*/) -{ - return { lhs == rhs, lhs }; -} - -static std::pair<bool, const char*> consistentStringProperty(const char* lhs, - const char* rhs) -{ - const bool b = strcmp(lhs, rhs) == 0; - return { b, b ? lhs : nullptr }; -} - -static std::pair<bool, std::string> consistentStringProperty( - const std::string& lhs, const std::string& rhs) -{ - const bool b = lhs == rhs; - return { b, b ? lhs : valueAsString(nullptr) }; -} - -static std::pair<bool, const char*> consistentNumberProperty(const char* lhs, - const char* rhs, - CompatibleType t) -{ - char* pEnd; - - long lnum = strtol(lhs, &pEnd, 0); - if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) { - return { false, nullptr }; - } - - long rnum = strtol(rhs, &pEnd, 0); - if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) { - return { false, nullptr }; - } - - if (t == NumberMaxType) { - return { true, std::max(lnum, rnum) == lnum ? lhs : rhs }; - } - - return { true, std::min(lnum, rnum) == lnum ? lhs : rhs }; -} - -template <> -std::pair<bool, const char*> consistentProperty(const char* lhs, - const char* rhs, - CompatibleType t) -{ - if (!lhs && !rhs) { - return { true, lhs }; - } - if (!lhs) { - return { true, rhs }; - } - if (!rhs) { - return { true, lhs }; - } - - switch (t) { - case BoolType: { - bool same = cmIsOn(lhs) == cmIsOn(rhs); - return { same, same ? lhs : nullptr }; - } - case StringType: - return consistentStringProperty(lhs, rhs); - case NumberMinType: - case NumberMaxType: - return consistentNumberProperty(lhs, rhs, t); - } - assert(false && "Unreachable!"); - return { false, nullptr }; -} - -static std::pair<bool, std::string> consistentProperty(const std::string& lhs, - const std::string& rhs, - CompatibleType t) -{ - const std::string null_ptr = valueAsString(nullptr); - - if (lhs == null_ptr && rhs == null_ptr) { - return { true, lhs }; - } - if (lhs == null_ptr) { - return { true, rhs }; - } - if (rhs == null_ptr) { - return { true, lhs }; - } - - switch (t) { - case BoolType: { - bool same = cmIsOn(lhs) == cmIsOn(rhs); - return { same, same ? lhs : null_ptr }; - } - case StringType: - return consistentStringProperty(lhs, rhs); - case NumberMinType: - case NumberMaxType: { - auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t); - return { value.first, - value.first ? std::string(value.second) : null_ptr }; - } - } - assert(false && "Unreachable!"); - return { false, null_ptr }; -} - -template <typename PropertyType> -PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, - const std::string& p, - const std::string& config, - const char* defaultValue, - CompatibleType t, - PropertyType* /*unused*/) -{ - PropertyType propContent = getTypedProperty<PropertyType>(tgt, p); - - std::vector<std::string> headPropKeys = tgt->GetPropertyKeys(); - const bool explicitlySet = cm::contains(headPropKeys, p); - - const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p); - assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet)); - - std::vector<cmGeneratorTarget const*> const& deps = - tgt->GetLinkImplementationClosure(config); - - if (deps.empty()) { - return propContent; - } - bool propInitialized = explicitlySet; - - std::string report = cmStrCat(" * Target \"", tgt->GetName()); - if (explicitlySet) { - report += "\" has property content \""; - report += valueAsString<PropertyType>(propContent); - report += "\"\n"; - } else if (impliedByUse) { - report += "\" property is implied by use.\n"; - } else { - report += "\" property not set.\n"; - } - - std::string interfaceProperty = "INTERFACE_" + p; - std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter; - if (p == "POSITION_INDEPENDENT_CODE") { - // Corresponds to EvaluatingPICExpression. - genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>( - tgt->GetLocalGenerator(), config, tgt); - } - - for (cmGeneratorTarget const* theTarget : deps) { - // An error should be reported if one dependency - // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other - // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the - // target itself has a POSITION_INDEPENDENT_CODE which disagrees - // with a dependency. - - std::vector<std::string> propKeys = theTarget->GetPropertyKeys(); - - const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty); - PropertyType ifacePropContent = getTypedProperty<PropertyType>( - theTarget, interfaceProperty, genexInterpreter.get()); - - std::string reportEntry; - if (ifaceIsSet) { - reportEntry += " * Target \""; - reportEntry += theTarget->GetName(); - reportEntry += "\" property value \""; - reportEntry += valueAsString<PropertyType>(ifacePropContent); - reportEntry += "\" "; - } - - if (explicitlySet) { - if (ifaceIsSet) { - std::pair<bool, PropertyType> consistent = - consistentProperty(propContent, ifacePropContent, t); - report += reportEntry; - report += compatibilityAgree(t, propContent != consistent.second); - if (!consistent.first) { - std::ostringstream e; - e << "Property " << p << " on target \"" << tgt->GetName() - << "\" does\nnot match the " - "INTERFACE_" - << p - << " property requirement\nof " - "dependency \"" - << theTarget->GetName() << "\".\n"; - cmSystemTools::Error(e.str()); - break; - } - propContent = consistent.second; - continue; - } - // Explicitly set on target and not set in iface. Can't disagree. - continue; - } - if (impliedByUse) { - propContent = impliedValue<PropertyType>(propContent); - - if (ifaceIsSet) { - std::pair<bool, PropertyType> consistent = - consistentProperty(propContent, ifacePropContent, t); - report += reportEntry; - report += compatibilityAgree(t, propContent != consistent.second); - if (!consistent.first) { - std::ostringstream e; - e << "Property " << p << " on target \"" << tgt->GetName() - << "\" is\nimplied to be " << defaultValue - << " because it was used to determine the link libraries\n" - "already. The INTERFACE_" - << p << " property on\ndependency \"" << theTarget->GetName() - << "\" is in conflict.\n"; - cmSystemTools::Error(e.str()); - break; - } - propContent = consistent.second; - continue; - } - // Implicitly set on target and not set in iface. Can't disagree. - continue; - } - if (ifaceIsSet) { - if (propInitialized) { - std::pair<bool, PropertyType> consistent = - consistentProperty(propContent, ifacePropContent, t); - report += reportEntry; - report += compatibilityAgree(t, propContent != consistent.second); - if (!consistent.first) { - std::ostringstream e; - e << "The INTERFACE_" << p << " property of \"" - << theTarget->GetName() << "\" does\nnot agree with the value of " - << p << " already determined\nfor \"" << tgt->GetName() << "\".\n"; - cmSystemTools::Error(e.str()); - break; - } - propContent = consistent.second; - continue; - } - report += reportEntry + "(Interface set)\n"; - propContent = ifacePropContent; - propInitialized = true; - } else { - // Not set. Nothing to agree on. - continue; - } - } - - tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent), - report, compatibilityType(t)); - return propContent; -} - bool cmGeneratorTarget::SetDeviceLink(bool deviceLink) { bool previous = this->DeviceLink; @@ -6571,200 +3484,6 @@ bool cmGeneratorTarget::SetDeviceLink(bool deviceLink) return previous; } -bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE", - BoolType, nullptr); -} - -std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<std::string>( - this, p, config, "FALSE", BoolType, nullptr); -} - -const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<const char*>( - this, p, config, "empty", StringType, nullptr); -} - -const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<const char*>( - this, p, config, "empty", NumberMinType, nullptr); -} - -const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<const char*>( - this, p, config, "empty", NumberMaxType, nullptr); -} - -cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( - const std::string& config) const -{ - // Lookup any existing information for this configuration. - std::string key(cmSystemTools::UpperCase(config)); - auto i = this->LinkInformation.find(key); - if (i == this->LinkInformation.end()) { - // Compute information for this configuration. - auto info = cm::make_unique<cmComputeLinkInformation>(this, config); - if (info && !info->Compute()) { - info.reset(); - } - - // Store the information for this configuration. - i = this->LinkInformation.emplace(key, std::move(info)).first; - - if (i->second) { - this->CheckPropertyCompatibility(*i->second, config); - } - } - return i->second.get(); -} - -void cmGeneratorTarget::CheckLinkLibraries() const -{ - bool linkLibrariesOnlyTargets = - this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS"); - - // Evaluate the link interface of this target if needed for extra checks. - if (linkLibrariesOnlyTargets) { - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& config : configs) { - this->GetLinkInterfaceLibraries(config, this, UseTo::Link); - } - } - - // Check link the implementation for each generated configuration. - for (auto const& hmp : this->LinkImplMap) { - HeadToLinkImplementationMap const& hm = hmp.second; - // There could be several entries used when computing the pre-CMP0022 - // default link interface. Check only the entry for our own link impl. - auto const hmi = hm.find(this); - if (hmi == hm.end() || !hmi->second.LibrariesDone) { - continue; - } - for (cmLinkImplItem const& item : hmi->second.Libraries) { - if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) { - return; - } - if (linkLibrariesOnlyTargets && - !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) { - return; - } - } - } - - // Check link the interface for each generated combination of - // configuration and consuming head target. We should not need to - // consider LinkInterfaceUsageRequirementsOnlyMap because its entries - // should be a subset of LinkInterfaceMap (with LINK_ONLY left out). - for (auto const& hmp : this->LinkInterfaceMap) { - for (auto const& hmi : hmp.second) { - if (!hmi.second.LibrariesDone) { - continue; - } - for (cmLinkItem const& item : hmi.second.Libraries) { - if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) { - return; - } - if (linkLibrariesOnlyTargets && - !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) { - return; - } - } - } - } -} - -namespace { -cm::string_view missingTargetPossibleReasons = - "Possible reasons include:\n" - " * There is a typo in the target name.\n" - " * A find_package call is missing for an IMPORTED target.\n" - " * An ALIAS target is missing.\n"_s; -} - -bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, - cmLinkItem const& item) const -{ - if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || - item.AsStr().find("::") == std::string::npos) { - return true; - } - MessageType messageType = MessageType::FATAL_ERROR; - std::string e; - switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) { - case cmPolicies::WARN: { - e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n"); - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - return true; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - - if (role == LinkItemRole::Implementation) { - e = cmStrCat(e, "Target \"", this->GetName(), "\" links to"); - } else { - e = cmStrCat(e, "The link interface of target \"", this->GetName(), - "\" contains"); - } - e = - cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ", - missingTargetPossibleReasons); - cmListFileBacktrace backtrace = item.Backtrace; - if (backtrace.Empty()) { - backtrace = this->GetBacktrace(); - } - this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e, - backtrace); - return false; -} - -bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, - cmLinkItem const& item) const -{ - if (item.Target) { - return true; - } - std::string const& str = item.AsStr(); - if (!str.empty() && - (str[0] == '-' || str[0] == '$' || str[0] == '`' || - str.find_first_of("/\\") != std::string::npos || - cmHasPrefix(str, "<LINK_LIBRARY:"_s) || - cmHasPrefix(str, "<LINK_GROUP:"_s))) { - return true; - } - - std::string e = cmStrCat("Target \"", this->GetName(), - "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ", - role == LinkItemRole::Implementation - ? "it links to" - : "its link interface contains", - ":\n ", item.AsStr(), "\nwhich is not a target. ", - missingTargetPossibleReasons); - cmListFileBacktrace backtrace = item.Backtrace; - if (backtrace.Empty()) { - backtrace = this->GetBacktrace(); - } - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e, backtrace); - return false; -} - void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const { int patch; @@ -7003,291 +3722,6 @@ void cmGeneratorTarget::ReportPropertyOrigin( areport); } -bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, - cmLocalGenerator const*& lg) const -{ - if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) { - cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP)); - if (dirId.String.empty()) { - lg = this->LocalGenerator; - return true; - } - if (cmLocalGenerator const* otherLG = - this->GlobalGenerator->FindLocalGenerator(dirId)) { - lg = otherLG; - return true; - } - } - return false; -} - -cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( - std::string const& n, cmListFileBacktrace const& bt, - std::string const& linkFeature, LookupLinkItemScope* scope, - LookupSelf lookupSelf) const -{ - cm::optional<cmLinkItem> maybeItem; - if (this->IsLinkLookupScope(n, scope->LG)) { - return maybeItem; - } - - std::string name = this->CheckCMP0004(n); - if (name.empty() || - (lookupSelf == LookupSelf::No && name == this->GetName())) { - return maybeItem; - } - maybeItem = - this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); - return maybeItem; -} - -void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, - cmBTStringRange entries, - std::string const& config, - cmGeneratorTarget const* headTarget, - UseTo usage, LinkInterfaceField field, - cmLinkInterface& iface) const -{ - if (entries.empty()) { - return; - } - // Keep this logic in sync with ComputeLinkImplementationLibraries. - cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr, - this->LocalGenerator); - // The $<LINK_ONLY> expression may be in a link interface to specify - // private link dependencies that are otherwise excluded from usage - // requirements. - if (usage == UseTo::Compile) { - dagChecker.SetTransitivePropertiesOnly(); - dagChecker.SetTransitivePropertiesOnlyCMP0131(); - } - cmMakefile const* mf = this->LocalGenerator->GetMakefile(); - LookupLinkItemScope scope{ this->LocalGenerator }; - for (BT<std::string> const& entry : entries) { - cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), - entry.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value); - cge->SetEvaluateForBuildsystem(true); - cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, - &dagChecker, this, - headTarget->LinkerLanguage) }; - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& lib : libs) { - if (auto maybeLinkFeature = ParseLinkFeature(lib)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - lib, cge->GetBacktrace(), linkFeature, &scope, - field == LinkInterfaceField::Libraries ? LookupSelf::No - : LookupSelf::Yes)) { - cmLinkItem item = std::move(*maybeItem); - - if (field == LinkInterfaceField::HeadInclude) { - iface.HeadInclude.emplace_back(std::move(item)); - continue; - } - if (field == LinkInterfaceField::HeadExclude) { - iface.HeadExclude.emplace_back(std::move(item)); - continue; - } - if (!item.Target) { - // Report explicitly linked object files separately. - std::string const& maybeObj = item.AsStr(); - if (cmSystemTools::FileIsFullPath(maybeObj)) { - cmSourceFile const* sf = - mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); - if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - item.ObjectSource = sf; - iface.Objects.emplace_back(std::move(item)); - continue; - } - } - } - - iface.Libraries.emplace_back(std::move(item)); - } - } - if (cge->GetHadHeadSensitiveCondition()) { - iface.HadHeadSensitiveCondition = true; - } - if (cge->GetHadContextSensitiveCondition()) { - iface.HadContextSensitiveCondition = true; - } - if (cge->GetHadLinkLanguageSensitiveCondition()) { - iface.HadLinkLanguageSensitiveCondition = true; - } - } -} - -cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( - const std::string& config, cmGeneratorTarget const* head) const -{ - return this->GetLinkInterface(config, head, false); -} - -cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( - const std::string& config, cmGeneratorTarget const* head, - bool secondPass) const -{ - // Imported targets have their own link interface. - if (this->IsImported()) { - return this->GetImportLinkInterface(config, head, UseTo::Link, secondPass); - } - - // Link interfaces are not supported for executables that do not - // export symbols. - if (this->GetType() == cmStateEnums::EXECUTABLE && - !this->IsExecutableWithExports()) { - return nullptr; - } - - // Lookup any existing link interface for this configuration. - cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[head]; - if (secondPass) { - iface = cmOptionalLinkInterface(); - } - if (!iface.LibrariesDone) { - iface.LibrariesDone = true; - this->ComputeLinkInterfaceLibraries(config, iface, head, UseTo::Link); - } - if (!iface.AllDone) { - iface.AllDone = true; - if (iface.Exists) { - this->ComputeLinkInterface(config, iface, head, secondPass); - this->ComputeLinkInterfaceRuntimeLibraries(config, iface); - } - } - - return iface.Exists ? &iface : nullptr; -} - -void cmGeneratorTarget::ComputeLinkInterface( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget) const -{ - this->ComputeLinkInterface(config, iface, headTarget, false); -} - -void cmGeneratorTarget::ComputeLinkInterface( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget, bool secondPass) const -{ - if (iface.Explicit) { - if (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->GetType() == cmStateEnums::STATIC_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - // Shared libraries may have runtime implementation dependencies - // on other shared libraries that are not in the interface. - std::set<cmLinkItem> emitted; - for (cmLinkItem const& lib : iface.Libraries) { - emitted.insert(lib); - } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Link, secondPass); - for (cmLinkImplItem const& lib : impl->Libraries) { - if (emitted.insert(lib).second) { - if (lib.Target) { - // This is a runtime dependency on another shared library. - if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { - iface.SharedDeps.push_back(lib); - } - } else { - // TODO: Recognize shared library file names. Perhaps this - // should be moved to cmComputeLinkInformation, but that - // creates a chicken-and-egg problem since this list is needed - // for its construction. - } - } - } - } - } - } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || - this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { - // The link implementation is the default link interface. - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget, - UseTo::Link); - iface.ImplementationIsInterface = true; - iface.WrongConfigLibraries = impl->WrongConfigLibraries; - } - - if (this->LinkLanguagePropagatesToDependents()) { - // Targets using this archive need its language runtime libraries. - if (cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Link, secondPass)) { - iface.Languages = impl->Languages; - } - } - - if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { - // Construct the property name suffix for this configuration. - std::string suffix = "_"; - if (!config.empty()) { - suffix += cmSystemTools::UpperCase(config); - } else { - suffix += "NOCONFIG"; - } - - // How many repetitions are needed if this library has cyclic - // dependencies? - std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix); - if (cmValue config_reps = this->GetProperty(propName)) { - sscanf(config_reps->c_str(), "%u", &iface.Multiplicity); - } else if (cmValue reps = - this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { - sscanf(reps->c_str(), "%u", &iface.Multiplicity); - } - } -} - -const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( - const std::string& config, cmGeneratorTarget const* head, UseTo usage) const -{ - // Imported targets have their own link interface. - if (this->IsImported()) { - return this->GetImportLinkInterface(config, head, usage); - } - - // Link interfaces are not supported for executables that do not - // export symbols. - if (this->GetType() == cmStateEnums::EXECUTABLE && - !this->IsExecutableWithExports()) { - return nullptr; - } - - // Lookup any existing link interface for this configuration. - cmHeadToLinkInterfaceMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) - : this->GetHeadToLinkInterfaceMap(config)); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[head]; - if (!iface.LibrariesDone) { - iface.LibrariesDone = true; - this->ComputeLinkInterfaceLibraries(config, iface, head, usage); - } - - return iface.Exists ? &iface : nullptr; -} - std::string cmGeneratorTarget::GetDirectory( const std::string& config, cmStateEnums::ArtifactType artifact) const { @@ -7543,303 +3977,6 @@ bool cmGeneratorTarget::GetRPATH(const std::string& config, return true; } -void cmGeneratorTarget::ComputeLinkInterfaceLibraries( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget, UseTo usage) const -{ - // Construct the property name suffix for this configuration. - std::string suffix = "_"; - if (!config.empty()) { - suffix += cmSystemTools::UpperCase(config); - } else { - suffix += "NOCONFIG"; - } - - // An explicit list of interface libraries may be set for shared - // libraries and executables that export symbols. - bool haveExplicitLibraries = false; - cmValue explicitLibrariesCMP0022OLD; - std::string linkIfacePropCMP0022OLD; - bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD && - this->GetPolicyStatusCMP0022() != cmPolicies::WARN); - if (cmp0022NEW) { - // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES. - haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() || - !this->Target->GetLinkInterfaceDirectEntries().empty() || - !this->Target->GetLinkInterfaceDirectExcludeEntries().empty(); - } else { - // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a - // shared lib or executable. - if (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->IsExecutableWithExports()) { - // Lookup the per-configuration property. - linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix); - explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD); - - // If not set, try the generic property. - if (!explicitLibrariesCMP0022OLD) { - linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES"; - explicitLibrariesCMP0022OLD = - this->GetProperty(linkIfacePropCMP0022OLD); - } - } - - if (explicitLibrariesCMP0022OLD && - this->GetPolicyStatusCMP0022() == cmPolicies::WARN && - !this->PolicyWarnedCMP0022) { - // Compare the explicitly set old link interface properties to the - // preferred new link interface property one and warn if different. - cmValue newExplicitLibraries = - this->GetProperty("INTERFACE_LINK_LIBRARIES"); - if (newExplicitLibraries && - (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) { - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" - "Target \"" << this->GetName() << "\" has an " - "INTERFACE_LINK_LIBRARIES property which differs from its " << - linkIfacePropCMP0022OLD << " properties." - "\n" - "INTERFACE_LINK_LIBRARIES:\n" - " " << *newExplicitLibraries << "\n" << - linkIfacePropCMP0022OLD << ":\n" - " " << *explicitLibrariesCMP0022OLD << "\n"; - /* clang-format on */ - this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, - w.str()); - this->PolicyWarnedCMP0022 = true; - } - } - - haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD); - } - - // There is no implicit link interface for executables or modules - // so if none was explicitly set then there is no link interface. - if (!haveExplicitLibraries && - (this->GetType() == cmStateEnums::EXECUTABLE || - (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { - return; - } - iface.Exists = true; - - // If CMP0022 is NEW then the plain tll signature sets the - // INTERFACE_LINK_LIBRARIES property. Even if the project - // clears it, the link interface is still explicit. - iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD; - - if (cmp0022NEW) { - // The interface libraries are specified by INTERFACE_LINK_LIBRARIES. - // Use its special representation directly to get backtraces. - this->ExpandLinkItems( - kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), - config, headTarget, usage, LinkInterfaceField::Libraries, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, - this->Target->GetLinkInterfaceDirectEntries(), - config, headTarget, usage, - LinkInterfaceField::HeadInclude, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, - this->Target->GetLinkInterfaceDirectExcludeEntries(), - config, headTarget, usage, - LinkInterfaceField::HeadExclude, iface); - } else if (explicitLibrariesCMP0022OLD) { - // The interface libraries have been explicitly set in pre-CMP0022 style. - std::vector<BT<std::string>> entries; - entries.emplace_back(*explicitLibrariesCMP0022OLD); - this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries), - config, headTarget, usage, - LinkInterfaceField::Libraries, iface); - } - - // If the link interface is explicit, do not fall back to the link impl. - if (iface.Explicit) { - return; - } - - // The link implementation is the default link interface. - if (cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget, - usage)) { - iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), - impl->Libraries.end()); - if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && - !this->PolicyWarnedCMP0022 && usage == UseTo::Link) { - // Compare the link implementation fallback link interface to the - // preferred new link interface property and warn if different. - cmLinkInterface ifaceNew; - this->ExpandLinkItems( - kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), - config, headTarget, usage, LinkInterfaceField::Libraries, ifaceNew); - if (ifaceNew.Libraries != iface.Libraries) { - std::string oldLibraries = cmJoin(impl->Libraries, ";"); - std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); - if (oldLibraries.empty()) { - oldLibraries = "(empty)"; - } - if (newLibraries.empty()) { - newLibraries = "(empty)"; - } - - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" - "Target \"" << this->GetName() << "\" has an " - "INTERFACE_LINK_LIBRARIES property. " - "This should be preferred as the source of the link interface " - "for this library but because CMP0022 is not set CMake is " - "ignoring the property and using the link implementation " - "as the link interface instead." - "\n" - "INTERFACE_LINK_LIBRARIES:\n" - " " << newLibraries << "\n" - "Link implementation:\n" - " " << oldLibraries << "\n"; - /* clang-format on */ - this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, - w.str()); - this->PolicyWarnedCMP0022 = true; - } - } - } -} - -namespace { - -template <typename ReturnType> -ReturnType constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt); - -template <> -inline cmLinkImplItem constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt) -{ - return cmLinkImplItem(cmLinkItem(target, false, bt), false); -} - -template <> -inline cmLinkItem constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt) -{ - return cmLinkItem(target, false, bt); -} - -template <typename ValueType> -std::vector<ValueType> computeImplicitLanguageTargets( - std::string const& lang, std::string const& config, - cmGeneratorTarget const* currentTarget) -{ - cmListFileBacktrace bt; - std::vector<ValueType> result; - cmLocalGenerator* lg = currentTarget->GetLocalGenerator(); - - std::string const& runtimeLibrary = - currentTarget->GetRuntimeLinkLibrary(lang, config); - if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition( - "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) { - cmList libsList{ *runtimeLinkOptions }; - result.reserve(libsList.size()); - - for (auto const& i : libsList) { - cmGeneratorTarget::TargetOrString resolved = - currentTarget->ResolveTargetReference(i, lg); - if (resolved.Target) { - result.emplace_back(constructItem<ValueType>(resolved.Target, bt)); - } - } - } - - return result; -} -} - -void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries( - const std::string& config, cmOptionalLinkInterface& iface) const -{ - for (std::string const& lang : iface.Languages) { - if ((lang == "CUDA" || lang == "HIP") && - iface.LanguageRuntimeLibraries.find(lang) == - iface.LanguageRuntimeLibraries.end()) { - auto implicitTargets = - computeImplicitLanguageTargets<cmLinkItem>(lang, config, this); - iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); - } - } -} - -void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries( - const std::string& config, cmOptionalLinkImplementation& impl) const -{ - for (std::string const& lang : impl.Languages) { - if ((lang == "CUDA" || lang == "HIP") && - impl.LanguageRuntimeLibraries.find(lang) == - impl.LanguageRuntimeLibraries.end()) { - auto implicitTargets = - computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this); - impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); - } - } -} - -const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( - const std::string& config, cmGeneratorTarget const* headTarget, UseTo usage, - bool secondPass) const -{ - cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); - if (!info) { - return nullptr; - } - - cmHeadToLinkInterfaceMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) - : this->GetHeadToLinkInterfaceMap(config)); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - headTarget = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[headTarget]; - if (secondPass) { - iface = cmOptionalLinkInterface(); - } - if (!iface.AllDone) { - iface.AllDone = true; - iface.LibrariesDone = true; - iface.Multiplicity = info->Multiplicity; - cmExpandList(info->Languages, iface.Languages); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, - cmMakeRange(info->LibrariesHeadInclude), config, - headTarget, usage, LinkInterfaceField::HeadInclude, - iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, - cmMakeRange(info->LibrariesHeadExclude), config, - headTarget, usage, LinkInterfaceField::HeadExclude, - iface); - this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), - config, headTarget, usage, - LinkInterfaceField::Libraries, iface); - cmList deps{ info->SharedDeps }; - LookupLinkItemScope scope{ this->LocalGenerator }; - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& dep : deps) { - if (auto maybeLinkFeature = ParseLinkFeature(dep)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { - iface.SharedDeps.emplace_back(std::move(*maybeItem)); - } - } - } - - return &iface; -} - cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo( const std::string& config) const { @@ -8029,69 +4166,6 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config, } } -cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( - const std::string& config) const -{ - return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)]; -} - -cmHeadToLinkInterfaceMap& -cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( - const std::string& config) const -{ - return this - ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; -} - -const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( - const std::string& config, UseTo usage) const -{ - return this->GetLinkImplementation(config, usage, false); -} - -const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( - const std::string& config, UseTo usage, bool secondPass) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - return nullptr; - } - - HeadToLinkImplementationMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) - : this->GetHeadToLinkImplementationMap(config)); - cmOptionalLinkImplementation& impl = hm[this]; - if (secondPass) { - impl = cmOptionalLinkImplementation(); - } - if (!impl.LibrariesDone) { - impl.LibrariesDone = true; - this->ComputeLinkImplementationLibraries(config, impl, this, usage); - } - if (!impl.LanguagesDone) { - impl.LanguagesDone = true; - this->ComputeLinkImplementationLanguages(config, impl); - this->ComputeLinkImplementationRuntimeLibraries(config, impl); - } - return &impl; -} - -cmGeneratorTarget::HeadToLinkImplementationMap& -cmGeneratorTarget::GetHeadToLinkImplementationMap( - std::string const& config) const -{ - return this->LinkImplMap[cmSystemTools::UpperCase(config)]; -} - -cmGeneratorTarget::HeadToLinkImplementationMap& -cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap( - std::string const& config) const -{ - return this - ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; -} - bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode( std::vector<cmSourceFile*>& files) const { @@ -8341,156 +4415,12 @@ bool cmGeneratorTarget::HaveBuildTreeRPATH(const std::string& config) const return false; } -cmLinkImplementationLibraries const* -cmGeneratorTarget::GetLinkImplementationLibraries(const std::string& config, - UseTo usage) const -{ - return this->GetLinkImplementationLibrariesInternal(config, this, usage); -} - -cmLinkImplementationLibraries const* -cmGeneratorTarget::GetLinkImplementationLibrariesInternal( - const std::string& config, cmGeneratorTarget const* head, UseTo usage) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - return nullptr; - } - - // Populate the link implementation libraries for this configuration. - HeadToLinkImplementationMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) - : this->GetHeadToLinkImplementationMap(config)); - - // If the link implementation does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkImplementation& impl = hm[head]; - if (!impl.LibrariesDone) { - impl.LibrariesDone = true; - this->ComputeLinkImplementationLibraries(config, impl, head, usage); - } - return &impl; -} - bool cmGeneratorTarget::IsNullImpliedByLinkLibraries( const std::string& p) const { return cm::contains(this->LinkImplicitNullProperties, p); } -namespace { -class TransitiveLinkImpl -{ - cmGeneratorTarget const* Self; - std::string const& Config; - UseTo ImplFor; - cmLinkImplementation& Impl; - - std::set<cmLinkItem> Emitted; - std::set<cmLinkItem> Excluded; - std::unordered_set<cmGeneratorTarget const*> Followed; - - void Follow(cmGeneratorTarget const* target); - -public: - TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config, - UseTo usage, cmLinkImplementation& impl) - : Self(self) - , Config(config) - , ImplFor(usage) - , Impl(impl) - { - } - - void Compute(); -}; - -void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target) -{ - if (!target || !this->Followed.insert(target).second || - target->GetPolicyStatusCMP0022() == cmPolicies::OLD || - target->GetPolicyStatusCMP0022() == cmPolicies::WARN) { - return; - } - - // Get this target's usage requirements. - cmLinkInterfaceLibraries const* iface = - target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor); - if (!iface) { - return; - } - if (iface->HadContextSensitiveCondition) { - this->Impl.HadContextSensitiveCondition = true; - } - - // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements. - for (cmLinkItem const& item : iface->HeadInclude) { - // Inject direct dependencies from the item's usage requirements - // before the item itself. - this->Follow(item.Target); - - // Add the item itself, but at most once. - if (this->Emitted.insert(item).second) { - this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false); - } - } - - // Follow transitive dependencies. - for (cmLinkItem const& item : iface->Libraries) { - this->Follow(item.Target); - } - - // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' - // usage requirements. - for (cmLinkItem const& item : iface->HeadExclude) { - this->Excluded.insert(item); - } -} - -void TransitiveLinkImpl::Compute() -{ - // Save the original items and start with an empty list. - std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries); - - // Avoid injecting any original items as usage requirements. - // This gives LINK_LIBRARIES final control over the order - // if it explicitly lists everything. - this->Emitted.insert(original.cbegin(), original.cend()); - - // Process each original item. - for (cmLinkImplItem& item : original) { - // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT' - // usage requirements before the item itself. - this->Follow(item.Target); - - // Add the item itself. - this->Impl.Libraries.emplace_back(std::move(item)); - } - - // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' - // usage requirements found through any dependency above. - this->Impl.Libraries.erase( - std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(), - [this](cmLinkImplItem const& item) { - return this->Excluded.find(item) != this->Excluded.end(); - }), - this->Impl.Libraries.end()); -} - -void ComputeLinkImplTransitive(cmGeneratorTarget const* self, - std::string const& config, UseTo usage, - cmLinkImplementation& impl) -{ - TransitiveLinkImpl transitiveLinkImpl(self, config, usage, impl); - transitiveLinkImpl.Compute(); -} -} - bool cmGeneratorTarget::ApplyCXXStdTargets() { cmStandardLevelResolver standardResolver(this->Makefile); @@ -8716,237 +4646,6 @@ bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, return true; } -void cmGeneratorTarget::ComputeLinkImplementationLibraries( - const std::string& config, cmOptionalLinkImplementation& impl, - cmGeneratorTarget const* head, UseTo usage) const -{ - cmLocalGenerator const* lg = this->LocalGenerator; - cmMakefile const* mf = lg->GetMakefile(); - cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); - auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; - // Collect libraries directly linked in this configuration. - for (auto const& entry : entryRange) { - // Keep this logic in sync with ExpandLinkItems. - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr, - nullptr, this->LocalGenerator); - // The $<LINK_ONLY> expression may be used to specify link dependencies - // that are otherwise excluded from usage requirements. - if (usage == UseTo::Compile) { - dagChecker.SetTransitivePropertiesOnly(); - switch (this->GetPolicyStatusCMP0131()) { - case cmPolicies::WARN: - case cmPolicies::OLD: - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - dagChecker.SetTransitivePropertiesOnlyCMP0131(); - break; - } - } - cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), - entry.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> const cge = - ge.Parse(entry.Value); - cge->SetEvaluateForBuildsystem(true); - std::string const& evaluated = - cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, - this->LinkerLanguage); - bool const checkCMP0027 = evaluated != entry.Value; - cmList llibs(evaluated); - if (cge->GetHadHeadSensitiveCondition()) { - impl.HadHeadSensitiveCondition = true; - } - if (cge->GetHadContextSensitiveCondition()) { - impl.HadContextSensitiveCondition = true; - } - if (cge->GetHadLinkLanguageSensitiveCondition()) { - impl.HadLinkLanguageSensitiveCondition = true; - } - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& lib : llibs) { - if (auto maybeLinkFeature = ParseLinkFeature(lib)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (this->IsLinkLookupScope(lib, lg)) { - continue; - } - - // Skip entries that resolve to the target itself or are empty. - std::string name = this->CheckCMP0004(lib); - if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) { - // resolve alias name - auto* target = this->Makefile->FindTargetToUse(name); - if (target) { - name = target->GetName(); - } - } - if (name == this->GetName() || name.empty()) { - if (name == this->GetName()) { - bool noMessage = false; - MessageType messageType = MessageType::FATAL_ERROR; - std::ostringstream e; - switch (this->GetPolicyStatusCMP0038()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - - if (!noMessage) { - e << "Target \"" << this->GetName() << "\" links to itself."; - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - messageType, e.str(), this->GetBacktrace()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - continue; - } - - // The entry is meant for this configuration. - cmLinkItem item = this->ResolveLinkItem( - BT<std::string>(name, entry.Backtrace), lg, linkFeature); - if (item.Target) { - auto depsForTarget = synthTargetsForConfig.find(item.Target); - if (depsForTarget != synthTargetsForConfig.end()) { - for (auto const* depForTarget : depsForTarget->second) { - cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); - impl.Libraries.emplace_back(std::move(synthItem), false); - } - } - } else { - // Report explicitly linked object files separately. - std::string const& maybeObj = item.AsStr(); - if (cmSystemTools::FileIsFullPath(maybeObj)) { - cmSourceFile const* sf = - mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); - if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - item.ObjectSource = sf; - impl.Objects.emplace_back(std::move(item)); - continue; - } - } - } - - impl.Libraries.emplace_back(std::move(item), checkCMP0027); - } - - std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); - for (std::string const& sp : seenProps) { - if (!this->GetProperty(sp)) { - this->LinkImplicitNullProperties.insert(sp); - } - } - cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); - } - - // Update the list of direct link dependencies from usage requirements. - if (head == this) { - ComputeLinkImplTransitive(this, config, usage, impl); - } - - // Get the list of configurations considered to be DEBUG. - std::vector<std::string> debugConfigs = - this->Makefile->GetCMakeInstance()->GetDebugConfigs(); - - cmTargetLinkLibraryType linkType = - CMP0003_ComputeLinkType(config, debugConfigs); - cmTarget::LinkLibraryVectorType const& oldllibs = - this->Target->GetOriginalLinkLibraries(); - - auto linkFeature = cmLinkItem::DEFAULT; - for (cmTarget::LibraryID const& oldllib : oldllibs) { - if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { - std::string name = this->CheckCMP0004(oldllib.first); - if (name == this->GetName() || name.empty()) { - continue; - } - // Support OLD behavior for CMP0003. - impl.WrongConfigLibraries.push_back( - this->ResolveLinkItem(BT<std::string>(name), linkFeature)); - } - } -} - -cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( - std::string const& name) const -{ - return this->ResolveTargetReference(name, this->LocalGenerator); -} - -cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( - std::string const& name, cmLocalGenerator const* lg) const -{ - TargetOrString resolved; - - if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) { - resolved.Target = tgt; - } else { - resolved.String = name; - } - - return resolved; -} - -cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name, std::string const& linkFeature) const -{ - return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); -} - -cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name, cmLocalGenerator const* lg, - std::string const& linkFeature) const -{ - auto bt = name.Backtrace; - TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); - - if (!resolved.Target) { - return cmLinkItem(resolved.String, false, bt, linkFeature); - } - - // Check deprecation, issue message with `bt` backtrace. - if (resolved.Target->IsDeprecated()) { - std::ostringstream w; - /* clang-format off */ - w << - "The library that is being linked to, " << resolved.Target->GetName() << - ", is marked as being deprecated by the owner. The message provided by " - "the developer is: \n" << resolved.Target->GetDeprecation() << "\n"; - /* clang-format on */ - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, w.str(), bt); - } - - // Skip targets that will not really be linked. This is probably a - // name conflict between an external library and an executable - // within the project. - if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && - !resolved.Target->IsExecutableWithExports()) { - return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); - } - - return cmLinkItem(resolved.Target, false, bt, linkFeature); -} - bool cmGeneratorTarget::HasPackageReferences() const { return this->IsInBuildSystem() && diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index b0ac524..51f112e 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -18,7 +18,6 @@ #include <cm/string_view> #include "cmAlgorithms.h" -#include "cmComputeLinkInformation.h" #include "cmLinkItem.h" #include "cmListFileCache.h" #include "cmPolicies.h" @@ -26,7 +25,10 @@ #include "cmStateTypes.h" #include "cmValue.h" +class cmake; enum class cmBuildStep; +class cmCompiledGeneratorExpression; +class cmComputeLinkInformation; class cmCustomCommand; class cmFileSet; class cmGlobalGenerator; @@ -1384,6 +1386,14 @@ public: TargetPropertyEntry(cmLinkImplItem const& item); virtual ~TargetPropertyEntry() = default; + static std::unique_ptr<TargetPropertyEntry> Create( + cmake& cmakeInstance, const BT<std::string>& propertyValue, + bool evaluateForBuildsystem = false); + static std::unique_ptr<TargetPropertyEntry> CreateFileSet( + std::vector<std::string> dirs, bool contextSensitiveDirs, + std::unique_ptr<cmCompiledGeneratorExpression> entryCge, + const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem); + virtual const std::string& Evaluate( cmLocalGenerator* lg, const std::string& config, cmGeneratorTarget const* headTarget, diff --git a/Source/cmGeneratorTarget_CompatibleInterface.cxx b/Source/cmGeneratorTarget_CompatibleInterface.cxx new file mode 100644 index 0000000..b6eb373 --- /dev/null +++ b/Source/cmGeneratorTarget_CompatibleInterface.cxx @@ -0,0 +1,737 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <algorithm> +#include <cassert> +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include <iterator> +#include <map> +#include <set> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cmext/algorithm> + +#include "cmComputeLinkInformation.h" +#include "cmGeneratorExpression.h" +#include "cmList.h" +#include "cmLocalGenerator.h" +#include "cmMessageType.h" +#include "cmRange.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmValue.h" + +const cmGeneratorTarget::CompatibleInterfacesBase& +cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const +{ + cmGeneratorTarget::CompatibleInterfaces& compat = + this->CompatibleInterfacesMap[config]; + if (!compat.Done) { + compat.Done = true; + compat.PropsBool.insert("POSITION_INDEPENDENT_CODE"); + compat.PropsString.insert("AUTOUIC_OPTIONS"); + std::vector<cmGeneratorTarget const*> const& deps = + this->GetLinkImplementationClosure(config); + for (cmGeneratorTarget const* li : deps) { +#define CM_READ_COMPATIBLE_INTERFACE(X, x) \ + if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \ + cmList props(*prop); \ + compat.Props##x.insert(props.begin(), props.end()); \ + } + CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool) + CM_READ_COMPATIBLE_INTERFACE(STRING, String) + CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin) + CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax) +#undef CM_READ_COMPATIBLE_INTERFACE + } + } + return compat; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0; +} + +enum CompatibleType +{ + BoolType, + StringType, + NumberMinType, + NumberMaxType +}; + +template <typename PropertyType> +PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, + const std::string& prop, + const std::string& config, + CompatibleType, PropertyType*); + +template <> +bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, + const std::string& prop, + const std::string& config, + CompatibleType /*unused*/, + bool* /*unused*/) +{ + return tgt->GetLinkInterfaceDependentBoolProperty(prop, config); +} + +template <> +const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, + const std::string& prop, + const std::string& config, + CompatibleType t, + const char** /*unused*/) +{ + switch (t) { + case BoolType: + assert(false && + "String compatibility check function called for boolean"); + return nullptr; + case StringType: + return tgt->GetLinkInterfaceDependentStringProperty(prop, config); + case NumberMinType: + return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config); + case NumberMaxType: + return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config); + } + assert(false && "Unreachable!"); + return nullptr; +} + +template <typename PropertyType> +void checkPropertyConsistency(cmGeneratorTarget const* depender, + cmGeneratorTarget const* dependee, + const std::string& propName, + std::set<std::string>& emitted, + const std::string& config, CompatibleType t, + PropertyType* /*unused*/) +{ + cmValue prop = dependee->GetProperty(propName); + if (!prop) { + return; + } + + cmList props{ *prop }; + std::string pdir = + cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/"); + + for (std::string const& p : props) { + std::string pname = cmSystemTools::HelpFileName(p); + std::string pfile = pdir + pname + ".rst"; + if (cmSystemTools::FileExists(pfile, true)) { + std::ostringstream e; + e << "Target \"" << dependee->GetName() << "\" has property \"" << p + << "\" listed in its " << propName + << " property. " + "This is not allowed. Only user-defined properties may appear " + "listed in the " + << propName << " property."; + depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, + e.str()); + return; + } + if (emitted.insert(p).second) { + getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t, + nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + } + } +} + +namespace { +std::string intersect(const std::set<std::string>& s1, + const std::set<std::string>& s2) +{ + std::set<std::string> intersect; + std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), + std::inserter(intersect, intersect.begin())); + if (!intersect.empty()) { + return *intersect.begin(); + } + return ""; +} + +std::string intersect(const std::set<std::string>& s1, + const std::set<std::string>& s2, + const std::set<std::string>& s3) +{ + std::string result; + result = intersect(s1, s2); + if (!result.empty()) { + return result; + } + result = intersect(s1, s3); + if (!result.empty()) { + return result; + } + return intersect(s2, s3); +} + +std::string intersect(const std::set<std::string>& s1, + const std::set<std::string>& s2, + const std::set<std::string>& s3, + const std::set<std::string>& s4) +{ + std::string result; + result = intersect(s1, s2); + if (!result.empty()) { + return result; + } + result = intersect(s1, s3); + if (!result.empty()) { + return result; + } + result = intersect(s1, s4); + if (!result.empty()) { + return result; + } + return intersect(s2, s3, s4); +} +} + +void cmGeneratorTarget::CheckPropertyCompatibility( + cmComputeLinkInformation& info, const std::string& config) const +{ + const cmComputeLinkInformation::ItemVector& deps = info.GetItems(); + + std::set<std::string> emittedBools; + static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL"; + std::set<std::string> emittedStrings; + static const std::string strString = "COMPATIBLE_INTERFACE_STRING"; + std::set<std::string> emittedMinNumbers; + static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN"; + std::set<std::string> emittedMaxNumbers; + static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX"; + + for (auto const& dep : deps) { + if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + continue; + } + + checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools, + config, BoolType, nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + checkPropertyConsistency<const char*>(this, dep.Target, strString, + emittedStrings, config, StringType, + nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + checkPropertyConsistency<const char*>(this, dep.Target, strNumMin, + emittedMinNumbers, config, + NumberMinType, nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + checkPropertyConsistency<const char*>(this, dep.Target, strNumMax, + emittedMaxNumbers, config, + NumberMaxType, nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + } + + std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers, + emittedMaxNumbers); + + if (!prop.empty()) { + // Use a sorted std::vector to keep the error message sorted. + std::vector<std::string> props; + auto i = emittedBools.find(prop); + if (i != emittedBools.end()) { + props.push_back(strBool); + } + i = emittedStrings.find(prop); + if (i != emittedStrings.end()) { + props.push_back(strString); + } + i = emittedMinNumbers.find(prop); + if (i != emittedMinNumbers.end()) { + props.push_back(strNumMin); + } + i = emittedMaxNumbers.find(prop); + if (i != emittedMaxNumbers.end()) { + props.push_back(strNumMax); + } + std::sort(props.begin(), props.end()); + + std::string propsString = cmStrCat( + cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back()); + + std::ostringstream e; + e << "Property \"" << prop << "\" appears in both the " << propsString + << " property in the dependencies of target \"" << this->GetName() + << "\". This is not allowed. A property may only require " + "compatibility " + "in a boolean interpretation, a numeric minimum, a numeric maximum " + "or a " + "string interpretation, but not a mixture."; + this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } +} + +template <typename PropertyType> +std::string valueAsString(PropertyType); +template <> +std::string valueAsString<bool>(bool value) +{ + return value ? "TRUE" : "FALSE"; +} +template <> +std::string valueAsString<const char*>(const char* value) +{ + return value ? value : "(unset)"; +} +template <> +std::string valueAsString<std::string>(std::string value) +{ + return value; +} +template <> +std::string valueAsString<cmValue>(cmValue value) +{ + return value ? *value : std::string("(unset)"); +} +template <> +std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/) +{ + return "(unset)"; +} + +static std::string compatibilityType(CompatibleType t) +{ + switch (t) { + case BoolType: + return "Boolean compatibility"; + case StringType: + return "String compatibility"; + case NumberMaxType: + return "Numeric maximum compatibility"; + case NumberMinType: + return "Numeric minimum compatibility"; + } + assert(false && "Unreachable!"); + return ""; +} + +static std::string compatibilityAgree(CompatibleType t, bool dominant) +{ + switch (t) { + case BoolType: + case StringType: + return dominant ? "(Disagree)\n" : "(Agree)\n"; + case NumberMaxType: + case NumberMinType: + return dominant ? "(Dominant)\n" : "(Ignored)\n"; + } + assert(false && "Unreachable!"); + return ""; +} + +template <typename PropertyType> +PropertyType getTypedProperty( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter = nullptr); + +template <> +bool getTypedProperty<bool>(cmGeneratorTarget const* tgt, + const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) +{ + if (genexInterpreter == nullptr) { + return tgt->GetPropertyAsBool(prop); + } + + cmValue value = tgt->GetProperty(prop); + return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop)); +} + +template <> +const char* getTypedProperty<const char*>( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) +{ + cmValue value = tgt->GetProperty(prop); + + if (genexInterpreter == nullptr) { + return value.GetCStr(); + } + + return genexInterpreter->Evaluate(value ? *value : "", prop).c_str(); +} + +template <> +std::string getTypedProperty<std::string>( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) +{ + cmValue value = tgt->GetProperty(prop); + + if (genexInterpreter == nullptr) { + return valueAsString(value); + } + + return genexInterpreter->Evaluate(value ? *value : "", prop); +} + +template <typename PropertyType> +PropertyType impliedValue(PropertyType); +template <> +bool impliedValue<bool>(bool /*unused*/) +{ + return false; +} +template <> +const char* impliedValue<const char*>(const char* /*unused*/) +{ + return ""; +} +template <> +std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*) +{ + return std::string(); +} + +template <typename PropertyType> +std::pair<bool, PropertyType> consistentProperty(PropertyType lhs, + PropertyType rhs, + CompatibleType t); + +template <> +std::pair<bool, bool> consistentProperty(bool lhs, bool rhs, + CompatibleType /*unused*/) +{ + return { lhs == rhs, lhs }; +} + +static std::pair<bool, const char*> consistentStringProperty(const char* lhs, + const char* rhs) +{ + const bool b = strcmp(lhs, rhs) == 0; + return { b, b ? lhs : nullptr }; +} + +static std::pair<bool, std::string> consistentStringProperty( + const std::string& lhs, const std::string& rhs) +{ + const bool b = lhs == rhs; + return { b, b ? lhs : valueAsString(nullptr) }; +} + +static std::pair<bool, const char*> consistentNumberProperty(const char* lhs, + const char* rhs, + CompatibleType t) +{ + char* pEnd; + + long lnum = strtol(lhs, &pEnd, 0); + if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) { + return { false, nullptr }; + } + + long rnum = strtol(rhs, &pEnd, 0); + if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) { + return { false, nullptr }; + } + + if (t == NumberMaxType) { + return { true, std::max(lnum, rnum) == lnum ? lhs : rhs }; + } + + return { true, std::min(lnum, rnum) == lnum ? lhs : rhs }; +} + +template <> +std::pair<bool, const char*> consistentProperty(const char* lhs, + const char* rhs, + CompatibleType t) +{ + if (!lhs && !rhs) { + return { true, lhs }; + } + if (!lhs) { + return { true, rhs }; + } + if (!rhs) { + return { true, lhs }; + } + + switch (t) { + case BoolType: { + bool same = cmIsOn(lhs) == cmIsOn(rhs); + return { same, same ? lhs : nullptr }; + } + case StringType: + return consistentStringProperty(lhs, rhs); + case NumberMinType: + case NumberMaxType: + return consistentNumberProperty(lhs, rhs, t); + } + assert(false && "Unreachable!"); + return { false, nullptr }; +} + +static std::pair<bool, std::string> consistentProperty(const std::string& lhs, + const std::string& rhs, + CompatibleType t) +{ + const std::string null_ptr = valueAsString(nullptr); + + if (lhs == null_ptr && rhs == null_ptr) { + return { true, lhs }; + } + if (lhs == null_ptr) { + return { true, rhs }; + } + if (rhs == null_ptr) { + return { true, lhs }; + } + + switch (t) { + case BoolType: { + bool same = cmIsOn(lhs) == cmIsOn(rhs); + return { same, same ? lhs : null_ptr }; + } + case StringType: + return consistentStringProperty(lhs, rhs); + case NumberMinType: + case NumberMaxType: { + auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t); + return { value.first, + value.first ? std::string(value.second) : null_ptr }; + } + } + assert(false && "Unreachable!"); + return { false, null_ptr }; +} + +template <typename PropertyType> +PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, + const std::string& p, + const std::string& config, + const char* defaultValue, + CompatibleType t, + PropertyType* /*unused*/) +{ + PropertyType propContent = getTypedProperty<PropertyType>(tgt, p); + + std::vector<std::string> headPropKeys = tgt->GetPropertyKeys(); + const bool explicitlySet = cm::contains(headPropKeys, p); + + const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p); + assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet)); + + std::vector<cmGeneratorTarget const*> const& deps = + tgt->GetLinkImplementationClosure(config); + + if (deps.empty()) { + return propContent; + } + bool propInitialized = explicitlySet; + + std::string report = cmStrCat(" * Target \"", tgt->GetName()); + if (explicitlySet) { + report += "\" has property content \""; + report += valueAsString<PropertyType>(propContent); + report += "\"\n"; + } else if (impliedByUse) { + report += "\" property is implied by use.\n"; + } else { + report += "\" property not set.\n"; + } + + std::string interfaceProperty = "INTERFACE_" + p; + std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter; + if (p == "POSITION_INDEPENDENT_CODE") { + // Corresponds to EvaluatingPICExpression. + genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>( + tgt->GetLocalGenerator(), config, tgt); + } + + for (cmGeneratorTarget const* theTarget : deps) { + // An error should be reported if one dependency + // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other + // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the + // target itself has a POSITION_INDEPENDENT_CODE which disagrees + // with a dependency. + + std::vector<std::string> propKeys = theTarget->GetPropertyKeys(); + + const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty); + PropertyType ifacePropContent = getTypedProperty<PropertyType>( + theTarget, interfaceProperty, genexInterpreter.get()); + + std::string reportEntry; + if (ifaceIsSet) { + reportEntry += " * Target \""; + reportEntry += theTarget->GetName(); + reportEntry += "\" property value \""; + reportEntry += valueAsString<PropertyType>(ifacePropContent); + reportEntry += "\" "; + } + + if (explicitlySet) { + if (ifaceIsSet) { + std::pair<bool, PropertyType> consistent = + consistentProperty(propContent, ifacePropContent, t); + report += reportEntry; + report += compatibilityAgree(t, propContent != consistent.second); + if (!consistent.first) { + std::ostringstream e; + e << "Property " << p << " on target \"" << tgt->GetName() + << "\" does\nnot match the " + "INTERFACE_" + << p + << " property requirement\nof " + "dependency \"" + << theTarget->GetName() << "\".\n"; + cmSystemTools::Error(e.str()); + break; + } + propContent = consistent.second; + continue; + } + // Explicitly set on target and not set in iface. Can't disagree. + continue; + } + if (impliedByUse) { + propContent = impliedValue<PropertyType>(propContent); + + if (ifaceIsSet) { + std::pair<bool, PropertyType> consistent = + consistentProperty(propContent, ifacePropContent, t); + report += reportEntry; + report += compatibilityAgree(t, propContent != consistent.second); + if (!consistent.first) { + std::ostringstream e; + e << "Property " << p << " on target \"" << tgt->GetName() + << "\" is\nimplied to be " << defaultValue + << " because it was used to determine the link libraries\n" + "already. The INTERFACE_" + << p << " property on\ndependency \"" << theTarget->GetName() + << "\" is in conflict.\n"; + cmSystemTools::Error(e.str()); + break; + } + propContent = consistent.second; + continue; + } + // Implicitly set on target and not set in iface. Can't disagree. + continue; + } + if (ifaceIsSet) { + if (propInitialized) { + std::pair<bool, PropertyType> consistent = + consistentProperty(propContent, ifacePropContent, t); + report += reportEntry; + report += compatibilityAgree(t, propContent != consistent.second); + if (!consistent.first) { + std::ostringstream e; + e << "The INTERFACE_" << p << " property of \"" + << theTarget->GetName() << "\" does\nnot agree with the value of " + << p << " already determined\nfor \"" << tgt->GetName() << "\".\n"; + cmSystemTools::Error(e.str()); + break; + } + propContent = consistent.second; + continue; + } + report += reportEntry + "(Interface set)\n"; + propContent = ifacePropContent; + propInitialized = true; + } else { + // Not set. Nothing to agree on. + continue; + } + } + + tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent), + report, compatibilityType(t)); + return propContent; +} + +bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE", + BoolType, nullptr); +} + +std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<std::string>( + this, p, config, "FALSE", BoolType, nullptr); +} + +const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<const char*>( + this, p, config, "empty", StringType, nullptr); +} + +const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<const char*>( + this, p, config, "empty", NumberMinType, nullptr); +} + +const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<const char*>( + this, p, config, "empty", NumberMaxType, nullptr); +} diff --git a/Source/cmGeneratorTarget_IncludeDirectories.cxx b/Source/cmGeneratorTarget_IncludeDirectories.cxx new file mode 100644 index 0000000..eaa3959 --- /dev/null +++ b/Source/cmGeneratorTarget_IncludeDirectories.cxx @@ -0,0 +1,334 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include "cmConfigure.h" + +#include <set> +#include <sstream> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/optional> +#include <cmext/algorithm> + +#include "cmEvaluatedTargetProperty.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +enum class IncludeDirectoryFallBack +{ + BINARY, + OBJECT +}; + +std::string AddLangSpecificInterfaceIncludeDirectories( + const cmGeneratorTarget* root, const cmGeneratorTarget* target, + const std::string& lang, const std::string& config, + const std::string& propertyName, IncludeDirectoryFallBack mode, + cmGeneratorExpressionDAGChecker* context) +{ + cmGeneratorExpressionDAGChecker dag{ + target->GetBacktrace(), target, propertyName, nullptr, context, + target->GetLocalGenerator() + }; + switch (dag.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dag.ReportError( + nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName"); + CM_FALLTHROUGH; + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + // No error. We have already seen this transitive property. + return ""; + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + std::string directories; + if (const auto* link_interface = + target->GetLinkInterfaceLibraries(config, root, UseTo::Compile)) { + for (const cmLinkItem& library : link_interface->Libraries) { + if (const cmGeneratorTarget* dependency = library.Target) { + if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { + auto* lg = dependency->GetLocalGenerator(); + std::string value = dependency->GetSafeProperty(propertyName); + if (value.empty()) { + if (mode == IncludeDirectoryFallBack::BINARY) { + value = lg->GetCurrentBinaryDirectory(); + } else if (mode == IncludeDirectoryFallBack::OBJECT) { + value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', + lg->GetTargetDirectory(dependency)); + } + } + + if (!directories.empty()) { + directories += ";"; + } + directories += value; + } + } + } + } + return directories; +} + +void AddLangSpecificImplicitIncludeDirectories( + const cmGeneratorTarget* target, const std::string& lang, + const std::string& config, const std::string& propertyName, + IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries) +{ + if (const auto* libraries = + target->GetLinkImplementationLibraries(config, UseTo::Compile)) { + cmGeneratorExpressionDAGChecker dag{ + target->GetBacktrace(), target, propertyName, nullptr, nullptr, + target->GetLocalGenerator() + }; + + for (const cmLinkImplItem& library : libraries->Libraries) { + if (const cmGeneratorTarget* dependency = library.Target) { + if (!dependency->IsInBuildSystem()) { + continue; + } + if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { + auto* lg = dependency->GetLocalGenerator(); + EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; + + if (cmValue val = dependency->GetProperty(propertyName)) { + entry.Values.emplace_back(*val); + } else { + if (mode == IncludeDirectoryFallBack::BINARY) { + entry.Values.emplace_back(lg->GetCurrentBinaryDirectory()); + } else if (mode == IncludeDirectoryFallBack::OBJECT) { + entry.Values.emplace_back( + dependency->GetObjectDirectory(config)); + } + } + + cmExpandList( + AddLangSpecificInterfaceIncludeDirectories( + target, dependency, lang, config, propertyName, mode, &dag), + entry.Values); + entries.Entries.emplace_back(std::move(entry)); + } + } + } + } +} + +void processIncludeDirectories(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries& entries, + std::vector<BT<std::string>>& includes, + std::unordered_set<std::string>& uniqueIncludes, + bool debugIncludes) +{ + for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + cmLinkImplItem const& item = entry.LinkImplItem; + std::string const& targetName = item.AsStr(); + bool const fromImported = item.Target && item.Target->IsImported(); + bool const checkCMP0027 = item.CheckCMP0027; + + std::string usedIncludes; + for (std::string& entryInclude : entry.Values) { + if (fromImported && !cmSystemTools::FileExists(entryInclude)) { + std::ostringstream e; + MessageType messageType = MessageType::FATAL_ERROR; + if (checkCMP0027) { + switch (tgt->GetPolicyStatusCMP0027()) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n"; + CM_FALLTHROUGH; + case cmPolicies::OLD: + messageType = MessageType::AUTHOR_WARNING; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::NEW: + break; + } + } + /* clang-format off */ + e << "Imported target \"" << targetName << "\" includes " + "non-existent path\n \"" << entryInclude << "\"\nin its " + "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n" + "* The path was deleted, renamed, or moved to another " + "location.\n" + "* An install or uninstall procedure did not complete " + "successfully.\n" + "* The installation package was faulty and references files it " + "does not provide.\n"; + /* clang-format on */ + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + return; + } + + if (!cmSystemTools::FileIsFullPath(entryInclude)) { + std::ostringstream e; + bool noMessage = false; + MessageType messageType = MessageType::FATAL_ERROR; + if (!targetName.empty()) { + /* clang-format off */ + e << "Target \"" << targetName << "\" contains relative " + "path in its INTERFACE_INCLUDE_DIRECTORIES:\n" + " \"" << entryInclude << "\""; + /* clang-format on */ + } else { + switch (tgt->GetPolicyStatusCMP0021()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << "\n"; + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + e << "Found relative path while evaluating include directories of " + "\"" + << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n"; + } + if (!noMessage) { + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + + if (!cmIsOff(entryInclude)) { + cmSystemTools::ConvertToUnixSlashes(entryInclude); + } + + if (uniqueIncludes.insert(entryInclude).second) { + includes.emplace_back(entryInclude, entry.Backtrace); + if (debugIncludes) { + usedIncludes += " * " + entryInclude + "\n"; + } + } + } + if (!usedIncludes.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used includes for target ") + tgt->GetName() + ":\n" + + usedIncludes, + entry.Backtrace); + } + } +} +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( + const std::string& config, const std::string& lang) const +{ + ConfigAndLanguage cacheKey(config, lang); + { + auto it = this->IncludeDirectoriesCache.find(cacheKey); + if (it != this->IncludeDirectoriesCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> includes; + std::unordered_set<std::string> uniqueIncludes; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "INCLUDE_DIRECTORIES", nullptr, nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugIncludes = !this->DebugIncludesDone && + cm::contains(debugProperties, "INCLUDE_DIRECTORIES"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugIncludesDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, lang, &dagChecker, this->IncludeDirectoriesEntries); + + if (lang == "Swift") { + AddLangSpecificImplicitIncludeDirectories( + this, lang, config, "Swift_MODULE_DIRECTORY", + IncludeDirectoryFallBack::BINARY, entries); + } + + if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) { + + const std::string propertyName = "ISPC_HEADER_DIRECTORY"; + + // If this target has ISPC sources make sure to add the header + // directory to other compilation units + if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) { + if (cmValue val = this->GetProperty(propertyName)) { + includes.emplace_back(*val); + } else { + includes.emplace_back(this->GetObjectDirectory(config)); + } + } + + AddLangSpecificImplicitIncludeDirectories( + this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT, + entries); + } + + AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + processIncludeDirectories(this, entries, includes, uniqueIncludes, + debugIncludes); + + if (this->IsApple()) { + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibraries(config, UseTo::Compile)) { + for (cmLinkImplItem const& lib : impl->Libraries) { + std::string libDir; + if (lib.Target == nullptr) { + libDir = cmSystemTools::CollapseFullPath( + lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); + } else if (lib.Target->Target->IsFrameworkOnApple() || + this->IsImportedFrameworkFolderOnApple(config)) { + libDir = lib.Target->GetLocation(config); + } else { + continue; + } + + auto fwDescriptor = + this->GetGlobalGenerator()->SplitFrameworkPath(libDir); + if (!fwDescriptor) { + continue; + } + + auto fwInclude = fwDescriptor->GetFrameworkPath(); + if (uniqueIncludes.insert(fwInclude).second) { + includes.emplace_back(fwInclude, cmListFileBacktrace()); + } + } + } + } + + this->IncludeDirectoriesCache.emplace(cacheKey, includes); + return includes; +} diff --git a/Source/cmGeneratorTarget_Link.cxx b/Source/cmGeneratorTarget_Link.cxx new file mode 100644 index 0000000..3ab36b6 --- /dev/null +++ b/Source/cmGeneratorTarget_Link.cxx @@ -0,0 +1,1489 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <algorithm> +#include <cassert> +#include <cstdio> +#include <map> +#include <set> +#include <sstream> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmAlgorithms.h" +#include "cmComputeLinkInformation.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmRange.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocationKind.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetLinkLibraryType.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES"; +const std::string kINTERFACE_LINK_LIBRARIES_DIRECT = + "INTERFACE_LINK_LIBRARIES_DIRECT"; +const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE = + "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"; +} + +class cmTargetCollectLinkLanguages +{ +public: + cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, + std::string config, + std::unordered_set<std::string>& languages, + cmGeneratorTarget const* head, bool secondPass) + : Config(std::move(config)) + , Languages(languages) + , HeadTarget(head) + , SecondPass(secondPass) + { + this->Visited.insert(target); + } + + void Visit(cmLinkItem const& item) + { + if (!item.Target) { + return; + } + if (!this->Visited.insert(item.Target).second) { + return; + } + cmLinkInterface const* iface = item.Target->GetLinkInterface( + this->Config, this->HeadTarget, this->SecondPass); + if (!iface) { + return; + } + if (iface->HadLinkLanguageSensitiveCondition) { + this->HadLinkLanguageSensitiveCondition = true; + } + + for (std::string const& language : iface->Languages) { + this->Languages.insert(language); + } + + for (cmLinkItem const& lib : iface->Libraries) { + this->Visit(lib); + } + } + + bool GetHadLinkLanguageSensitiveCondition() const + { + return this->HadLinkLanguageSensitiveCondition; + } + +private: + std::string Config; + std::unordered_set<std::string>& Languages; + cmGeneratorTarget const* HeadTarget; + std::set<cmGeneratorTarget const*> Visited; + bool SecondPass; + bool HadLinkLanguageSensitiveCondition = false; +}; + +cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( + const std::string& config) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static LinkClosure const empty = { {}, {} }; + return ∅ + } + + std::string key(cmSystemTools::UpperCase(config)); + auto i = this->LinkClosureMap.find(key); + if (i == this->LinkClosureMap.end()) { + LinkClosure lc; + this->ComputeLinkClosure(config, lc); + LinkClosureMapType::value_type entry(key, lc); + i = this->LinkClosureMap.insert(entry).first; + } + return &i->second; +} + +class cmTargetSelectLinker +{ + int Preference = 0; + cmGeneratorTarget const* Target; + cmGlobalGenerator* GG; + std::set<std::string> Preferred; + +public: + cmTargetSelectLinker(cmGeneratorTarget const* target) + : Target(target) + { + this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); + } + void Consider(const std::string& lang) + { + int preference = this->GG->GetLinkerPreference(lang); + if (preference > this->Preference) { + this->Preference = preference; + this->Preferred.clear(); + } + if (preference == this->Preference) { + this->Preferred.insert(lang); + } + } + std::string Choose() + { + if (this->Preferred.empty()) { + return ""; + } + if (this->Preferred.size() > 1) { + std::ostringstream e; + e << "Target " << this->Target->GetName() + << " contains multiple languages with the highest linker preference" + << " (" << this->Preference << "):\n"; + for (std::string const& li : this->Preferred) { + e << " " << li << "\n"; + } + e << "Set the LINKER_LANGUAGE property for this target."; + cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); + cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), + this->Target->GetBacktrace()); + } + return *this->Preferred.begin(); + } +}; + +bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config, + LinkClosure& lc, + bool secondPass) const +{ + // Get languages built in this target. + std::unordered_set<std::string> languages; + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass); + assert(impl); + languages.insert(impl->Languages.cbegin(), impl->Languages.cend()); + + // Add interface languages from linked targets. + // cmTargetCollectLinkLanguages cll(this, config, languages, this, + // secondPass); + cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass); + for (cmLinkImplItem const& lib : impl->Libraries) { + cll.Visit(lib); + } + + // Store the transitive closure of languages. + cm::append(lc.Languages, languages); + + // Choose the language whose linker should be used. + if (secondPass || lc.LinkerLanguage.empty()) { + // Find the language with the highest preference value. + cmTargetSelectLinker tsl(this); + + // First select from the languages compiled directly in this target. + for (std::string const& l : impl->Languages) { + tsl.Consider(l); + } + + // Now consider languages that propagate from linked targets. + for (std::string const& lang : languages) { + std::string propagates = + "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; + if (this->Makefile->IsOn(propagates)) { + tsl.Consider(lang); + } + } + + lc.LinkerLanguage = tsl.Choose(); + } + + return impl->HadLinkLanguageSensitiveCondition || + cll.GetHadLinkLanguageSensitiveCondition(); +} + +void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, + LinkClosure& lc) const +{ + bool secondPass = false; + + { + LinkClosure linkClosure; + linkClosure.LinkerLanguage = this->LinkerLanguage; + + bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") || + !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty(); + + // Get languages built in this target. + secondPass = this->ComputeLinkClosure(config, linkClosure, false) && + !hasHardCodedLinkerLanguage; + this->LinkerLanguage = linkClosure.LinkerLanguage; + if (!secondPass) { + lc = std::move(linkClosure); + } + } + + if (secondPass) { + LinkClosure linkClosure; + + this->ComputeLinkClosure(config, linkClosure, secondPass); + lc = std::move(linkClosure); + + // linker language must not be changed between the two passes + if (this->LinkerLanguage != lc.LinkerLanguage) { + std::ostringstream e; + e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> " + "changes\nthe linker language for target \"" + << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '" + << lc.LinkerLanguage << "') which is invalid."; + cmSystemTools::Error(e.str()); + } + } +} + +static void processILibs(const std::string& config, + cmGeneratorTarget const* headTarget, + cmLinkItem const& item, cmGlobalGenerator* gg, + std::vector<cmGeneratorTarget const*>& tgts, + std::set<cmGeneratorTarget const*>& emitted) +{ + if (item.Target && emitted.insert(item.Target).second) { + tgts.push_back(item.Target); + if (cmLinkInterfaceLibraries const* iface = + item.Target->GetLinkInterfaceLibraries(config, headTarget, + UseTo::Compile)) { + for (cmLinkItem const& lib : iface->Libraries) { + processILibs(config, headTarget, lib, gg, tgts, emitted); + } + } + } +} + +const std::vector<const cmGeneratorTarget*>& +cmGeneratorTarget::GetLinkImplementationClosure( + const std::string& config) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static std::vector<const cmGeneratorTarget*> const empty; + return empty; + } + + LinkImplClosure& tgts = this->LinkImplClosureMap[config]; + if (!tgts.Done) { + tgts.Done = true; + std::set<cmGeneratorTarget const*> emitted; + + cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibraries(config, UseTo::Compile); + assert(impl); + + for (cmLinkImplItem const& lib : impl->Libraries) { + processILibs(config, this, lib, + this->LocalGenerator->GetGlobalGenerator(), tgts, emitted); + } + } + return tgts; +} + +cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( + const std::string& config) const +{ + // Lookup any existing information for this configuration. + std::string key(cmSystemTools::UpperCase(config)); + auto i = this->LinkInformation.find(key); + if (i == this->LinkInformation.end()) { + // Compute information for this configuration. + auto info = cm::make_unique<cmComputeLinkInformation>(this, config); + if (info && !info->Compute()) { + info.reset(); + } + + // Store the information for this configuration. + i = this->LinkInformation.emplace(key, std::move(info)).first; + + if (i->second) { + this->CheckPropertyCompatibility(*i->second, config); + } + } + return i->second.get(); +} + +void cmGeneratorTarget::CheckLinkLibraries() const +{ + bool linkLibrariesOnlyTargets = + this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS"); + + // Evaluate the link interface of this target if needed for extra checks. + if (linkLibrariesOnlyTargets) { + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& config : configs) { + this->GetLinkInterfaceLibraries(config, this, UseTo::Link); + } + } + + // Check link the implementation for each generated configuration. + for (auto const& hmp : this->LinkImplMap) { + HeadToLinkImplementationMap const& hm = hmp.second; + // There could be several entries used when computing the pre-CMP0022 + // default link interface. Check only the entry for our own link impl. + auto const hmi = hm.find(this); + if (hmi == hm.end() || !hmi->second.LibrariesDone) { + continue; + } + for (cmLinkImplItem const& item : hmi->second.Libraries) { + if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) { + return; + } + if (linkLibrariesOnlyTargets && + !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) { + return; + } + } + } + + // Check link the interface for each generated combination of + // configuration and consuming head target. We should not need to + // consider LinkInterfaceUsageRequirementsOnlyMap because its entries + // should be a subset of LinkInterfaceMap (with LINK_ONLY left out). + for (auto const& hmp : this->LinkInterfaceMap) { + for (auto const& hmi : hmp.second) { + if (!hmi.second.LibrariesDone) { + continue; + } + for (cmLinkItem const& item : hmi.second.Libraries) { + if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) { + return; + } + if (linkLibrariesOnlyTargets && + !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) { + return; + } + } + } + } +} + +namespace { +cm::string_view missingTargetPossibleReasons = + "Possible reasons include:\n" + " * There is a typo in the target name.\n" + " * A find_package call is missing for an IMPORTED target.\n" + " * An ALIAS target is missing.\n"_s; +} + +bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, + cmLinkItem const& item) const +{ + if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || + item.AsStr().find("::") == std::string::npos) { + return true; + } + MessageType messageType = MessageType::FATAL_ERROR; + std::string e; + switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) { + case cmPolicies::WARN: { + e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n"); + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + + if (role == LinkItemRole::Implementation) { + e = cmStrCat(e, "Target \"", this->GetName(), "\" links to"); + } else { + e = cmStrCat(e, "The link interface of target \"", this->GetName(), + "\" contains"); + } + e = + cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ", + missingTargetPossibleReasons); + cmListFileBacktrace backtrace = item.Backtrace; + if (backtrace.Empty()) { + backtrace = this->GetBacktrace(); + } + this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e, + backtrace); + return false; +} + +bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, + cmLinkItem const& item) const +{ + if (item.Target) { + return true; + } + std::string const& str = item.AsStr(); + if (!str.empty() && + (str[0] == '-' || str[0] == '$' || str[0] == '`' || + str.find_first_of("/\\") != std::string::npos || + cmHasPrefix(str, "<LINK_LIBRARY:"_s) || + cmHasPrefix(str, "<LINK_GROUP:"_s))) { + return true; + } + + std::string e = cmStrCat("Target \"", this->GetName(), + "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ", + role == LinkItemRole::Implementation + ? "it links to" + : "its link interface contains", + ":\n ", item.AsStr(), "\nwhich is not a target. ", + missingTargetPossibleReasons); + cmListFileBacktrace backtrace = item.Backtrace; + if (backtrace.Empty()) { + backtrace = this->GetBacktrace(); + } + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e, backtrace); + return false; +} + +bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, + cmLocalGenerator const*& lg) const +{ + if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) { + cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP)); + if (dirId.String.empty()) { + lg = this->LocalGenerator; + return true; + } + if (cmLocalGenerator const* otherLG = + this->GlobalGenerator->FindLocalGenerator(dirId)) { + lg = otherLG; + return true; + } + } + return false; +} + +cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( + std::string const& n, cmListFileBacktrace const& bt, + std::string const& linkFeature, LookupLinkItemScope* scope, + LookupSelf lookupSelf) const +{ + cm::optional<cmLinkItem> maybeItem; + if (this->IsLinkLookupScope(n, scope->LG)) { + return maybeItem; + } + + std::string name = this->CheckCMP0004(n); + if (name.empty() || + (lookupSelf == LookupSelf::No && name == this->GetName())) { + return maybeItem; + } + maybeItem = + this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); + return maybeItem; +} + +void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, + cmBTStringRange entries, + std::string const& config, + cmGeneratorTarget const* headTarget, + UseTo usage, LinkInterfaceField field, + cmLinkInterface& iface) const +{ + if (entries.empty()) { + return; + } + // Keep this logic in sync with ComputeLinkImplementationLibraries. + cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr, + this->LocalGenerator); + // The $<LINK_ONLY> expression may be in a link interface to specify + // private link dependencies that are otherwise excluded from usage + // requirements. + if (usage == UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + dagChecker.SetTransitivePropertiesOnlyCMP0131(); + } + cmMakefile const* mf = this->LocalGenerator->GetMakefile(); + LookupLinkItemScope scope{ this->LocalGenerator }; + for (BT<std::string> const& entry : entries) { + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), + entry.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value); + cge->SetEvaluateForBuildsystem(true); + cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, + &dagChecker, this, + headTarget->LinkerLanguage) }; + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& lib : libs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + lib, cge->GetBacktrace(), linkFeature, &scope, + field == LinkInterfaceField::Libraries ? LookupSelf::No + : LookupSelf::Yes)) { + cmLinkItem item = std::move(*maybeItem); + + if (field == LinkInterfaceField::HeadInclude) { + iface.HeadInclude.emplace_back(std::move(item)); + continue; + } + if (field == LinkInterfaceField::HeadExclude) { + iface.HeadExclude.emplace_back(std::move(item)); + continue; + } + if (!item.Target) { + // Report explicitly linked object files separately. + std::string const& maybeObj = item.AsStr(); + if (cmSystemTools::FileIsFullPath(maybeObj)) { + cmSourceFile const* sf = + mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); + if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + item.ObjectSource = sf; + iface.Objects.emplace_back(std::move(item)); + continue; + } + } + } + + iface.Libraries.emplace_back(std::move(item)); + } + } + if (cge->GetHadHeadSensitiveCondition()) { + iface.HadHeadSensitiveCondition = true; + } + if (cge->GetHadContextSensitiveCondition()) { + iface.HadContextSensitiveCondition = true; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + iface.HadLinkLanguageSensitiveCondition = true; + } + } +} + +cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( + const std::string& config, cmGeneratorTarget const* head) const +{ + return this->GetLinkInterface(config, head, false); +} + +cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( + const std::string& config, cmGeneratorTarget const* head, + bool secondPass) const +{ + // Imported targets have their own link interface. + if (this->IsImported()) { + return this->GetImportLinkInterface(config, head, UseTo::Link, secondPass); + } + + // Link interfaces are not supported for executables that do not + // export symbols. + if (this->GetType() == cmStateEnums::EXECUTABLE && + !this->IsExecutableWithExports()) { + return nullptr; + } + + // Lookup any existing link interface for this configuration. + cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[head]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } + if (!iface.LibrariesDone) { + iface.LibrariesDone = true; + this->ComputeLinkInterfaceLibraries(config, iface, head, UseTo::Link); + } + if (!iface.AllDone) { + iface.AllDone = true; + if (iface.Exists) { + this->ComputeLinkInterface(config, iface, head, secondPass); + this->ComputeLinkInterfaceRuntimeLibraries(config, iface); + } + } + + return iface.Exists ? &iface : nullptr; +} + +void cmGeneratorTarget::ComputeLinkInterface( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget) const +{ + this->ComputeLinkInterface(config, iface, headTarget, false); +} + +void cmGeneratorTarget::ComputeLinkInterface( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget, bool secondPass) const +{ + if (iface.Explicit) { + if (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->GetType() == cmStateEnums::STATIC_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + // Shared libraries may have runtime implementation dependencies + // on other shared libraries that are not in the interface. + std::set<cmLinkItem> emitted; + for (cmLinkItem const& lib : iface.Libraries) { + emitted.insert(lib); + } + if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass); + for (cmLinkImplItem const& lib : impl->Libraries) { + if (emitted.insert(lib).second) { + if (lib.Target) { + // This is a runtime dependency on another shared library. + if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { + iface.SharedDeps.push_back(lib); + } + } else { + // TODO: Recognize shared library file names. Perhaps this + // should be moved to cmComputeLinkInformation, but that + // creates a chicken-and-egg problem since this list is needed + // for its construction. + } + } + } + } + } + } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || + this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { + // The link implementation is the default link interface. + cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget, + UseTo::Link); + iface.ImplementationIsInterface = true; + iface.WrongConfigLibraries = impl->WrongConfigLibraries; + } + + if (this->LinkLanguagePropagatesToDependents()) { + // Targets using this archive need its language runtime libraries. + if (cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass)) { + iface.Languages = impl->Languages; + } + } + + if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { + // Construct the property name suffix for this configuration. + std::string suffix = "_"; + if (!config.empty()) { + suffix += cmSystemTools::UpperCase(config); + } else { + suffix += "NOCONFIG"; + } + + // How many repetitions are needed if this library has cyclic + // dependencies? + std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix); + if (cmValue config_reps = this->GetProperty(propName)) { + sscanf(config_reps->c_str(), "%u", &iface.Multiplicity); + } else if (cmValue reps = + this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { + sscanf(reps->c_str(), "%u", &iface.Multiplicity); + } + } +} + +const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( + const std::string& config, cmGeneratorTarget const* head, UseTo usage) const +{ + // Imported targets have their own link interface. + if (this->IsImported()) { + return this->GetImportLinkInterface(config, head, usage); + } + + // Link interfaces are not supported for executables that do not + // export symbols. + if (this->GetType() == cmStateEnums::EXECUTABLE && + !this->IsExecutableWithExports()) { + return nullptr; + } + + // Lookup any existing link interface for this configuration. + cmHeadToLinkInterfaceMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) + : this->GetHeadToLinkInterfaceMap(config)); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[head]; + if (!iface.LibrariesDone) { + iface.LibrariesDone = true; + this->ComputeLinkInterfaceLibraries(config, iface, head, usage); + } + + return iface.Exists ? &iface : nullptr; +} + +void cmGeneratorTarget::ComputeLinkInterfaceLibraries( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget, UseTo usage) const +{ + // Construct the property name suffix for this configuration. + std::string suffix = "_"; + if (!config.empty()) { + suffix += cmSystemTools::UpperCase(config); + } else { + suffix += "NOCONFIG"; + } + + // An explicit list of interface libraries may be set for shared + // libraries and executables that export symbols. + bool haveExplicitLibraries = false; + cmValue explicitLibrariesCMP0022OLD; + std::string linkIfacePropCMP0022OLD; + bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD && + this->GetPolicyStatusCMP0022() != cmPolicies::WARN); + if (cmp0022NEW) { + // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES. + haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() || + !this->Target->GetLinkInterfaceDirectEntries().empty() || + !this->Target->GetLinkInterfaceDirectExcludeEntries().empty(); + } else { + // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a + // shared lib or executable. + if (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->IsExecutableWithExports()) { + // Lookup the per-configuration property. + linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix); + explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD); + + // If not set, try the generic property. + if (!explicitLibrariesCMP0022OLD) { + linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES"; + explicitLibrariesCMP0022OLD = + this->GetProperty(linkIfacePropCMP0022OLD); + } + } + + if (explicitLibrariesCMP0022OLD && + this->GetPolicyStatusCMP0022() == cmPolicies::WARN && + !this->PolicyWarnedCMP0022) { + // Compare the explicitly set old link interface properties to the + // preferred new link interface property one and warn if different. + cmValue newExplicitLibraries = + this->GetProperty("INTERFACE_LINK_LIBRARIES"); + if (newExplicitLibraries && + (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) { + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" + "Target \"" << this->GetName() << "\" has an " + "INTERFACE_LINK_LIBRARIES property which differs from its " << + linkIfacePropCMP0022OLD << " properties." + "\n" + "INTERFACE_LINK_LIBRARIES:\n" + " " << *newExplicitLibraries << "\n" << + linkIfacePropCMP0022OLD << ":\n" + " " << *explicitLibrariesCMP0022OLD << "\n"; + /* clang-format on */ + this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); + this->PolicyWarnedCMP0022 = true; + } + } + + haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD); + } + + // There is no implicit link interface for executables or modules + // so if none was explicitly set then there is no link interface. + if (!haveExplicitLibraries && + (this->GetType() == cmStateEnums::EXECUTABLE || + (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { + return; + } + iface.Exists = true; + + // If CMP0022 is NEW then the plain tll signature sets the + // INTERFACE_LINK_LIBRARIES property. Even if the project + // clears it, the link interface is still explicit. + iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD; + + if (cmp0022NEW) { + // The interface libraries are specified by INTERFACE_LINK_LIBRARIES. + // Use its special representation directly to get backtraces. + this->ExpandLinkItems( + kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), + config, headTarget, usage, LinkInterfaceField::Libraries, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + this->Target->GetLinkInterfaceDirectEntries(), + config, headTarget, usage, + LinkInterfaceField::HeadInclude, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + this->Target->GetLinkInterfaceDirectExcludeEntries(), + config, headTarget, usage, + LinkInterfaceField::HeadExclude, iface); + } else if (explicitLibrariesCMP0022OLD) { + // The interface libraries have been explicitly set in pre-CMP0022 style. + std::vector<BT<std::string>> entries; + entries.emplace_back(*explicitLibrariesCMP0022OLD); + this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries), + config, headTarget, usage, + LinkInterfaceField::Libraries, iface); + } + + // If the link interface is explicit, do not fall back to the link impl. + if (iface.Explicit) { + return; + } + + // The link implementation is the default link interface. + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget, + usage)) { + iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), + impl->Libraries.end()); + if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && + !this->PolicyWarnedCMP0022 && usage == UseTo::Link) { + // Compare the link implementation fallback link interface to the + // preferred new link interface property and warn if different. + cmLinkInterface ifaceNew; + this->ExpandLinkItems( + kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), + config, headTarget, usage, LinkInterfaceField::Libraries, ifaceNew); + if (ifaceNew.Libraries != iface.Libraries) { + std::string oldLibraries = cmJoin(impl->Libraries, ";"); + std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); + if (oldLibraries.empty()) { + oldLibraries = "(empty)"; + } + if (newLibraries.empty()) { + newLibraries = "(empty)"; + } + + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" + "Target \"" << this->GetName() << "\" has an " + "INTERFACE_LINK_LIBRARIES property. " + "This should be preferred as the source of the link interface " + "for this library but because CMP0022 is not set CMake is " + "ignoring the property and using the link implementation " + "as the link interface instead." + "\n" + "INTERFACE_LINK_LIBRARIES:\n" + " " << newLibraries << "\n" + "Link implementation:\n" + " " << oldLibraries << "\n"; + /* clang-format on */ + this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); + this->PolicyWarnedCMP0022 = true; + } + } + } +} + +namespace { + +template <typename ReturnType> +ReturnType constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt); + +template <> +inline cmLinkImplItem constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt) +{ + return cmLinkImplItem(cmLinkItem(target, false, bt), false); +} + +template <> +inline cmLinkItem constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt) +{ + return cmLinkItem(target, false, bt); +} + +template <typename ValueType> +std::vector<ValueType> computeImplicitLanguageTargets( + std::string const& lang, std::string const& config, + cmGeneratorTarget const* currentTarget) +{ + cmListFileBacktrace bt; + std::vector<ValueType> result; + cmLocalGenerator* lg = currentTarget->GetLocalGenerator(); + + std::string const& runtimeLibrary = + currentTarget->GetRuntimeLinkLibrary(lang, config); + if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition( + "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) { + cmList libsList{ *runtimeLinkOptions }; + result.reserve(libsList.size()); + + for (auto const& i : libsList) { + cmGeneratorTarget::TargetOrString resolved = + currentTarget->ResolveTargetReference(i, lg); + if (resolved.Target) { + result.emplace_back(constructItem<ValueType>(resolved.Target, bt)); + } + } + } + + return result; +} +} + +void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries( + const std::string& config, cmOptionalLinkInterface& iface) const +{ + for (std::string const& lang : iface.Languages) { + if ((lang == "CUDA" || lang == "HIP") && + iface.LanguageRuntimeLibraries.find(lang) == + iface.LanguageRuntimeLibraries.end()) { + auto implicitTargets = + computeImplicitLanguageTargets<cmLinkItem>(lang, config, this); + iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); + } + } +} + +void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries( + const std::string& config, cmOptionalLinkImplementation& impl) const +{ + for (std::string const& lang : impl.Languages) { + if ((lang == "CUDA" || lang == "HIP") && + impl.LanguageRuntimeLibraries.find(lang) == + impl.LanguageRuntimeLibraries.end()) { + auto implicitTargets = + computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this); + impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); + } + } +} + +const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( + const std::string& config, cmGeneratorTarget const* headTarget, UseTo usage, + bool secondPass) const +{ + cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); + if (!info) { + return nullptr; + } + + cmHeadToLinkInterfaceMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) + : this->GetHeadToLinkInterfaceMap(config)); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + headTarget = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[headTarget]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } + if (!iface.AllDone) { + iface.AllDone = true; + iface.LibrariesDone = true; + iface.Multiplicity = info->Multiplicity; + cmExpandList(info->Languages, iface.Languages); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + cmMakeRange(info->LibrariesHeadInclude), config, + headTarget, usage, LinkInterfaceField::HeadInclude, + iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + cmMakeRange(info->LibrariesHeadExclude), config, + headTarget, usage, LinkInterfaceField::HeadExclude, + iface); + this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), + config, headTarget, usage, + LinkInterfaceField::Libraries, iface); + cmList deps{ info->SharedDeps }; + LookupLinkItemScope scope{ this->LocalGenerator }; + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& dep : deps) { + if (auto maybeLinkFeature = ParseLinkFeature(dep)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { + iface.SharedDeps.emplace_back(std::move(*maybeItem)); + } + } + } + + return &iface; +} + +cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( + const std::string& config) const +{ + return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)]; +} + +cmHeadToLinkInterfaceMap& +cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( + const std::string& config) const +{ + return this + ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; +} + +const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( + const std::string& config, UseTo usage) const +{ + return this->GetLinkImplementation(config, usage, false); +} + +const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( + const std::string& config, UseTo usage, bool secondPass) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + return nullptr; + } + + HeadToLinkImplementationMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) + : this->GetHeadToLinkImplementationMap(config)); + cmOptionalLinkImplementation& impl = hm[this]; + if (secondPass) { + impl = cmOptionalLinkImplementation(); + } + if (!impl.LibrariesDone) { + impl.LibrariesDone = true; + this->ComputeLinkImplementationLibraries(config, impl, this, usage); + } + if (!impl.LanguagesDone) { + impl.LanguagesDone = true; + this->ComputeLinkImplementationLanguages(config, impl); + this->ComputeLinkImplementationRuntimeLibraries(config, impl); + } + return &impl; +} + +cmGeneratorTarget::HeadToLinkImplementationMap& +cmGeneratorTarget::GetHeadToLinkImplementationMap( + std::string const& config) const +{ + return this->LinkImplMap[cmSystemTools::UpperCase(config)]; +} + +cmGeneratorTarget::HeadToLinkImplementationMap& +cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap( + std::string const& config) const +{ + return this + ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; +} + +cmLinkImplementationLibraries const* +cmGeneratorTarget::GetLinkImplementationLibraries(const std::string& config, + UseTo usage) const +{ + return this->GetLinkImplementationLibrariesInternal(config, this, usage); +} + +cmLinkImplementationLibraries const* +cmGeneratorTarget::GetLinkImplementationLibrariesInternal( + const std::string& config, cmGeneratorTarget const* head, UseTo usage) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + return nullptr; + } + + // Populate the link implementation libraries for this configuration. + HeadToLinkImplementationMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) + : this->GetHeadToLinkImplementationMap(config)); + + // If the link implementation does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkImplementation& impl = hm[head]; + if (!impl.LibrariesDone) { + impl.LibrariesDone = true; + this->ComputeLinkImplementationLibraries(config, impl, head, usage); + } + return &impl; +} + +namespace { +class TransitiveLinkImpl +{ + cmGeneratorTarget const* Self; + std::string const& Config; + UseTo ImplFor; + cmLinkImplementation& Impl; + + std::set<cmLinkItem> Emitted; + std::set<cmLinkItem> Excluded; + std::unordered_set<cmGeneratorTarget const*> Followed; + + void Follow(cmGeneratorTarget const* target); + +public: + TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config, + UseTo usage, cmLinkImplementation& impl) + : Self(self) + , Config(config) + , ImplFor(usage) + , Impl(impl) + { + } + + void Compute(); +}; + +void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target) +{ + if (!target || !this->Followed.insert(target).second || + target->GetPolicyStatusCMP0022() == cmPolicies::OLD || + target->GetPolicyStatusCMP0022() == cmPolicies::WARN) { + return; + } + + // Get this target's usage requirements. + cmLinkInterfaceLibraries const* iface = + target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor); + if (!iface) { + return; + } + if (iface->HadContextSensitiveCondition) { + this->Impl.HadContextSensitiveCondition = true; + } + + // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements. + for (cmLinkItem const& item : iface->HeadInclude) { + // Inject direct dependencies from the item's usage requirements + // before the item itself. + this->Follow(item.Target); + + // Add the item itself, but at most once. + if (this->Emitted.insert(item).second) { + this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false); + } + } + + // Follow transitive dependencies. + for (cmLinkItem const& item : iface->Libraries) { + this->Follow(item.Target); + } + + // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' + // usage requirements. + for (cmLinkItem const& item : iface->HeadExclude) { + this->Excluded.insert(item); + } +} + +void TransitiveLinkImpl::Compute() +{ + // Save the original items and start with an empty list. + std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries); + + // Avoid injecting any original items as usage requirements. + // This gives LINK_LIBRARIES final control over the order + // if it explicitly lists everything. + this->Emitted.insert(original.cbegin(), original.cend()); + + // Process each original item. + for (cmLinkImplItem& item : original) { + // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT' + // usage requirements before the item itself. + this->Follow(item.Target); + + // Add the item itself. + this->Impl.Libraries.emplace_back(std::move(item)); + } + + // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' + // usage requirements found through any dependency above. + this->Impl.Libraries.erase( + std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(), + [this](cmLinkImplItem const& item) { + return this->Excluded.find(item) != this->Excluded.end(); + }), + this->Impl.Libraries.end()); +} + +void ComputeLinkImplTransitive(cmGeneratorTarget const* self, + std::string const& config, UseTo usage, + cmLinkImplementation& impl) +{ + TransitiveLinkImpl transitiveLinkImpl(self, config, usage, impl); + transitiveLinkImpl.Compute(); +} +} + +void cmGeneratorTarget::ComputeLinkImplementationLibraries( + const std::string& config, cmOptionalLinkImplementation& impl, + cmGeneratorTarget const* head, UseTo usage) const +{ + cmLocalGenerator const* lg = this->LocalGenerator; + cmMakefile const* mf = lg->GetMakefile(); + cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); + auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; + // Collect libraries directly linked in this configuration. + for (auto const& entry : entryRange) { + // Keep this logic in sync with ExpandLinkItems. + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr, + nullptr, this->LocalGenerator); + // The $<LINK_ONLY> expression may be used to specify link dependencies + // that are otherwise excluded from usage requirements. + if (usage == UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + switch (this->GetPolicyStatusCMP0131()) { + case cmPolicies::WARN: + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + dagChecker.SetTransitivePropertiesOnlyCMP0131(); + break; + } + } + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), + entry.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> const cge = + ge.Parse(entry.Value); + cge->SetEvaluateForBuildsystem(true); + std::string const& evaluated = + cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, + this->LinkerLanguage); + bool const checkCMP0027 = evaluated != entry.Value; + cmList llibs(evaluated); + if (cge->GetHadHeadSensitiveCondition()) { + impl.HadHeadSensitiveCondition = true; + } + if (cge->GetHadContextSensitiveCondition()) { + impl.HadContextSensitiveCondition = true; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + impl.HadLinkLanguageSensitiveCondition = true; + } + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& lib : llibs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (this->IsLinkLookupScope(lib, lg)) { + continue; + } + + // Skip entries that resolve to the target itself or are empty. + std::string name = this->CheckCMP0004(lib); + if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) { + // resolve alias name + auto* target = this->Makefile->FindTargetToUse(name); + if (target) { + name = target->GetName(); + } + } + if (name == this->GetName() || name.empty()) { + if (name == this->GetName()) { + bool noMessage = false; + MessageType messageType = MessageType::FATAL_ERROR; + std::ostringstream e; + switch (this->GetPolicyStatusCMP0038()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + + if (!noMessage) { + e << "Target \"" << this->GetName() << "\" links to itself."; + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + messageType, e.str(), this->GetBacktrace()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + continue; + } + + // The entry is meant for this configuration. + cmLinkItem item = this->ResolveLinkItem( + BT<std::string>(name, entry.Backtrace), lg, linkFeature); + if (item.Target) { + auto depsForTarget = synthTargetsForConfig.find(item.Target); + if (depsForTarget != synthTargetsForConfig.end()) { + for (auto const* depForTarget : depsForTarget->second) { + cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); + impl.Libraries.emplace_back(std::move(synthItem), false); + } + } + } else { + // Report explicitly linked object files separately. + std::string const& maybeObj = item.AsStr(); + if (cmSystemTools::FileIsFullPath(maybeObj)) { + cmSourceFile const* sf = + mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); + if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + item.ObjectSource = sf; + impl.Objects.emplace_back(std::move(item)); + continue; + } + } + } + + impl.Libraries.emplace_back(std::move(item), checkCMP0027); + } + + std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); + for (std::string const& sp : seenProps) { + if (!this->GetProperty(sp)) { + this->LinkImplicitNullProperties.insert(sp); + } + } + cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); + } + + // Update the list of direct link dependencies from usage requirements. + if (head == this) { + ComputeLinkImplTransitive(this, config, usage, impl); + } + + // Get the list of configurations considered to be DEBUG. + std::vector<std::string> debugConfigs = + this->Makefile->GetCMakeInstance()->GetDebugConfigs(); + + cmTargetLinkLibraryType linkType = + CMP0003_ComputeLinkType(config, debugConfigs); + cmTarget::LinkLibraryVectorType const& oldllibs = + this->Target->GetOriginalLinkLibraries(); + + auto linkFeature = cmLinkItem::DEFAULT; + for (cmTarget::LibraryID const& oldllib : oldllibs) { + if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { + std::string name = this->CheckCMP0004(oldllib.first); + if (name == this->GetName() || name.empty()) { + continue; + } + // Support OLD behavior for CMP0003. + impl.WrongConfigLibraries.push_back( + this->ResolveLinkItem(BT<std::string>(name), linkFeature)); + } + } +} + +cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( + std::string const& name) const +{ + return this->ResolveTargetReference(name, this->LocalGenerator); +} + +cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( + std::string const& name, cmLocalGenerator const* lg) const +{ + TargetOrString resolved; + + if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) { + resolved.Target = tgt; + } else { + resolved.String = name; + } + + return resolved; +} + +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, std::string const& linkFeature) const +{ + return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); +} + +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, cmLocalGenerator const* lg, + std::string const& linkFeature) const +{ + auto bt = name.Backtrace; + TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); + + if (!resolved.Target) { + return cmLinkItem(resolved.String, false, bt, linkFeature); + } + + // Check deprecation, issue message with `bt` backtrace. + if (resolved.Target->IsDeprecated()) { + std::ostringstream w; + /* clang-format off */ + w << + "The library that is being linked to, " << resolved.Target->GetName() << + ", is marked as being deprecated by the owner. The message provided by " + "the developer is: \n" << resolved.Target->GetDeprecation() << "\n"; + /* clang-format on */ + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, w.str(), bt); + } + + // Skip targets that will not really be linked. This is probably a + // name conflict between an external library and an executable + // within the project. + if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && + !resolved.Target->IsExecutableWithExports()) { + return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); + } + + return cmLinkItem(resolved.Target, false, bt, linkFeature); +} diff --git a/Source/cmGeneratorTarget_LinkDirectories.cxx b/Source/cmGeneratorTarget_LinkDirectories.cxx new file mode 100644 index 0000000..0955dfb --- /dev/null +++ b/Source/cmGeneratorTarget_LinkDirectories.cxx @@ -0,0 +1,154 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <map> +#include <sstream> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cmext/algorithm> + +#include "cmEvaluatedTargetProperty.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +void processLinkDirectories(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries& entries, + std::vector<BT<std::string>>& directories, + std::unordered_set<std::string>& uniqueDirectories, + bool debugDirectories) +{ + for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + cmLinkImplItem const& item = entry.LinkImplItem; + std::string const& targetName = item.AsStr(); + + std::string usedDirectories; + for (std::string& entryDirectory : entry.Values) { + if (!cmSystemTools::FileIsFullPath(entryDirectory)) { + std::ostringstream e; + bool noMessage = false; + MessageType messageType = MessageType::FATAL_ERROR; + if (!targetName.empty()) { + /* clang-format off */ + e << "Target \"" << targetName << "\" contains relative " + "path in its INTERFACE_LINK_DIRECTORIES:\n" + " \"" << entryDirectory << "\""; + /* clang-format on */ + } else { + switch (tgt->GetPolicyStatusCMP0081()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n"; + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + e << "Found relative path while evaluating link directories of " + "\"" + << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; + } + if (!noMessage) { + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + + // Sanitize the path the same way the link_directories command does + // in case projects set the LINK_DIRECTORIES property directly. + cmSystemTools::ConvertToUnixSlashes(entryDirectory); + if (uniqueDirectories.insert(entryDirectory).second) { + directories.emplace_back(entryDirectory, entry.Backtrace); + if (debugDirectories) { + usedDirectories += " * " + entryDirectory + "\n"; + } + } + } + if (!usedDirectories.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used link directories for target ") + tgt->GetName() + + ":\n" + usedDirectories, + entry.Backtrace); + } + } +} +} + +void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = + this->GetLinkDirectories(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey( + config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); + { + auto it = this->LinkDirectoriesCache.find(cacheKey); + if (it != this->LinkDirectoriesCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueDirectories; + + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr, + nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugDirectories = !this->DebugLinkDirectoriesDone && + cm::contains(debugProperties, "LINK_DIRECTORIES"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugLinkDirectoriesDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->LinkDirectoriesEntries); + + AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes, + this->GetPolicyStatusCMP0099() == cmPolicies::NEW + ? UseTo::Link + : UseTo::Compile); + + processLinkDirectories(this, entries, result, uniqueDirectories, + debugDirectories); + + this->LinkDirectoriesCache.emplace(cacheKey, result); + return result; +} diff --git a/Source/cmGeneratorTarget_Options.cxx b/Source/cmGeneratorTarget_Options.cxx new file mode 100644 index 0000000..05f325d --- /dev/null +++ b/Source/cmGeneratorTarget_Options.cxx @@ -0,0 +1,657 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include "cmConfigure.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmEvaluatedTargetProperty.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmRange.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmValue.h" +#include "cmake.h" + +enum class OptionsParse +{ + None, + Shell +}; + +namespace { +const auto DL_BEGIN = "<DEVICE_LINK>"_s; +const auto DL_END = "</DEVICE_LINK>"_s; + +void processOptions(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries const& entries, + std::vector<BT<std::string>>& options, + std::unordered_set<std::string>& uniqueOptions, + bool debugOptions, const char* logName, OptionsParse parse, + bool processDeviceOptions = false) +{ + bool splitOption = !processDeviceOptions; + for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { + std::string usedOptions; + for (std::string const& opt : entry.Values) { + if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) { + options.emplace_back(opt, entry.Backtrace); + splitOption = opt == DL_BEGIN; + continue; + } + + if (uniqueOptions.insert(opt).second) { + if (parse == OptionsParse::Shell && + cmHasLiteralPrefix(opt, "SHELL:")) { + if (splitOption) { + std::vector<std::string> tmp; + cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp); + for (std::string& o : tmp) { + options.emplace_back(std::move(o), entry.Backtrace); + } + } else { + options.emplace_back(std::string(opt.c_str() + 6), + entry.Backtrace); + } + } else { + options.emplace_back(opt, entry.Backtrace); + } + if (debugOptions) { + usedOptions += " * " + opt + "\n"; + } + } + } + if (!usedOptions.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used ") + logName + std::string(" for target ") + + tgt->GetName() + ":\n" + usedOptions, + entry.Backtrace); + } + } +} + +std::vector<BT<std::string>> wrapOptions( + std::vector<std::string>& options, const cmListFileBacktrace& bt, + const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep, + bool concatFlagAndArgs) +{ + std::vector<BT<std::string>> result; + + if (options.empty()) { + return result; + } + + if (wrapperFlag.empty()) { + // nothing specified, insert elements as is + result.reserve(options.size()); + for (std::string& o : options) { + result.emplace_back(std::move(o), bt); + } + return result; + } + + for (std::vector<std::string>::size_type index = 0; index < options.size(); + index++) { + if (cmHasLiteralPrefix(options[index], "LINKER:")) { + // LINKER wrapper specified, insert elements as is + result.emplace_back(std::move(options[index]), bt); + continue; + } + if (cmHasLiteralPrefix(options[index], "-Wl,")) { + // replace option by LINKER wrapper + result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt); + continue; + } + if (cmHasLiteralPrefix(options[index], "-Xlinker=")) { + // replace option by LINKER wrapper + result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt); + continue; + } + if (options[index] == "-Xlinker") { + // replace option by LINKER wrapper + if (index + 1 < options.size()) { + result.emplace_back("LINKER:" + options[++index], bt); + } else { + result.emplace_back(std::move(options[index]), bt); + } + continue; + } + + // collect all options which must be transformed + std::vector<std::string> opts; + while (index < options.size()) { + if (!cmHasLiteralPrefix(options[index], "LINKER:") && + !cmHasLiteralPrefix(options[index], "-Wl,") && + !cmHasLiteralPrefix(options[index], "-Xlinker")) { + opts.emplace_back(std::move(options[index++])); + } else { + --index; + break; + } + } + if (opts.empty()) { + continue; + } + + if (!wrapperSep.empty()) { + if (concatFlagAndArgs) { + // insert flag elements except last one + for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { + result.emplace_back(*i, bt); + } + // concatenate last flag element and all list values + // in one option + result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); + } else { + for (std::string const& i : wrapperFlag) { + result.emplace_back(i, bt); + } + // concatenate all list values in one option + result.emplace_back(cmJoin(opts, wrapperSep), bt); + } + } else { + // prefix each element of list with wrapper + if (concatFlagAndArgs) { + std::transform(opts.begin(), opts.end(), opts.begin(), + [&wrapperFlag](std::string const& o) -> std::string { + return wrapperFlag.back() + o; + }); + } + for (std::string& o : opts) { + for (auto i = wrapperFlag.begin(), + e = concatFlagAndArgs ? wrapperFlag.end() - 1 + : wrapperFlag.end(); + i != e; ++i) { + result.emplace_back(*i, bt); + } + result.emplace_back(std::move(o), bt); + } + } + } + return result; +} +} + +void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->CompileOptionsCache.find(cacheKey); + if (it != this->CompileOptionsCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr, + nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugOptions = !this->DebugCompileOptionsDone && + cm::contains(debugProperties, "COMPILE_OPTIONS"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugCompileOptionsDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->CompileOptionsEntries); + + AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + processOptions(this, entries, result, uniqueOptions, debugOptions, + "compile options", OptionsParse::Shell); + + CompileOptionsCache.emplace(cacheKey, result); + return result; +} + +void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, + const std::string& config) const +{ + std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures( + std::string const& config) const +{ + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueFeatures; + + cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr, + nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugFeatures = !this->DebugCompileFeaturesDone && + cm::contains(debugProperties, "COMPILE_FEATURES"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugCompileFeaturesDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, std::string(), &dagChecker, this->CompileFeaturesEntries); + + AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES", + std::string(), &dagChecker, entries, + IncludeRuntimeInterface::Yes); + + processOptions(this, entries, result, uniqueFeatures, debugFeatures, + "compile features", OptionsParse::None); + + return result; +} + +void cmGeneratorTarget::GetCompileDefinitions( + std::vector<std::string>& result, const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = + this->GetCompileDefinitions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->CompileDefinitionsCache.find(cacheKey); + if (it != this->CompileDefinitionsCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> list; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "COMPILE_DEFINITIONS", nullptr, nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugDefines = !this->DebugCompileDefinitionsDone && + cm::contains(debugProperties, "COMPILE_DEFINITIONS"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugCompileDefinitionsDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->CompileDefinitionsEntries); + + AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + if (!config.empty()) { + std::string configPropName = + "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); + cmValue configProp = this->GetProperty(configPropName); + if (configProp) { + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) { + case cmPolicies::WARN: { + this->LocalGenerator->IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0043)); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: { + std::unique_ptr<TargetPropertyEntry> entry = + TargetPropertyEntry::Create( + *this->LocalGenerator->GetCMakeInstance(), *configProp); + entries.Entries.emplace_back(EvaluateTargetPropertyEntry( + this, config, language, &dagChecker, *entry)); + } break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + break; + } + } + } + + processOptions(this, entries, list, uniqueOptions, debugDefines, + "compile definitions", OptionsParse::None); + + this->CompileDefinitionsCache.emplace(cacheKey, list); + return list; +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders( + const std::string& config, const std::string& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->PrecompileHeadersCache.find(cacheKey); + if (it != this->PrecompileHeadersCache.end()) { + return it->second; + } + } + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "PRECOMPILE_HEADERS", nullptr, nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugDefines = !this->DebugPrecompileHeadersDone && + std::find(debugProperties.begin(), debugProperties.end(), + "PRECOMPILE_HEADERS") != debugProperties.end(); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugPrecompileHeadersDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->PrecompileHeadersEntries); + + AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + std::vector<BT<std::string>> list; + processOptions(this, entries, list, uniqueOptions, debugDefines, + "precompile headers", OptionsParse::None); + + this->PrecompileHeadersCache.emplace(cacheKey, list); + return list; +} + +void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + if (this->IsDeviceLink() && + this->GetPolicyStatusCMP0105() != cmPolicies::NEW) { + // link options are not propagated to the device link step + return; + } + + std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey( + config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); + { + auto it = this->LinkOptionsCache.find(cacheKey); + if (it != this->LinkOptionsCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr, + nullptr, this->LocalGenerator); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugOptions = !this->DebugLinkOptionsDone && + cm::contains(debugProperties, "LINK_OPTIONS"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugLinkOptionsDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->LinkOptionsEntries); + + AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes, + this->GetPolicyStatusCMP0099() == cmPolicies::NEW + ? UseTo::Link + : UseTo::Compile); + + processOptions(this, entries, result, uniqueOptions, debugOptions, + "link options", OptionsParse::Shell, this->IsDeviceLink()); + + if (this->IsDeviceLink()) { + // wrap host link options + const std::string wrapper(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG")); + cmList wrapperFlag{ wrapper }; + const std::string wrapperSep(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP")); + bool concatFlagAndArgs = true; + if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { + concatFlagAndArgs = false; + wrapperFlag.pop_back(); + } + + auto it = result.begin(); + while (it != result.end()) { + if (it->Value == DL_BEGIN) { + // device link options, no treatment + it = result.erase(it); + it = std::find_if(it, result.end(), [](const BT<std::string>& item) { + return item.Value == DL_END; + }); + if (it != result.end()) { + it = result.erase(it); + } + } else { + // host link options must be wrapped + std::vector<std::string> options; + cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options); + auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag, + wrapperSep, concatFlagAndArgs); + it = result.erase(it); + // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect + // C++11 standard: 'std::vector::insert()' do not returns an iterator, + // so need to recompute the iterator after insertion. + if (it == result.end()) { + cm::append(result, hostOptions); + it = result.end(); + } else { + auto index = it - result.begin(); + result.insert(it, hostOptions.begin(), hostOptions.end()); + it = result.begin() + index + hostOptions.size(); + } + } + } + } + + // Last step: replace "LINKER:" prefixed elements by + // actual linker wrapper + result = this->ResolveLinkerWrapper(result, language); + + this->LinkOptionsCache.emplace(cacheKey, result); + return result; +} + +std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( + std::vector<BT<std::string>>& result, const std::string& language, + bool joinItems) const +{ + // replace "LINKER:" prefixed elements by actual linker wrapper + const std::string wrapper(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + + (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG" + : "_LINKER_WRAPPER_FLAG"))); + cmList wrapperFlag{ wrapper }; + const std::string wrapperSep(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + + (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP" + : "_LINKER_WRAPPER_FLAG_SEP"))); + bool concatFlagAndArgs = true; + if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { + concatFlagAndArgs = false; + wrapperFlag.pop_back(); + } + + const std::string LINKER{ "LINKER:" }; + const std::string SHELL{ "SHELL:" }; + const std::string LINKER_SHELL = LINKER + SHELL; + + std::vector<BT<std::string>>::iterator entry; + while ((entry = std::find_if(result.begin(), result.end(), + [&LINKER](BT<std::string> const& item) -> bool { + return item.Value.compare(0, LINKER.length(), + LINKER) == 0; + })) != result.end()) { + std::string value = std::move(entry->Value); + cmListFileBacktrace bt = std::move(entry->Backtrace); + entry = result.erase(entry); + + std::vector<std::string> linkerOptions; + if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) { + cmSystemTools::ParseUnixCommandLine( + value.c_str() + LINKER_SHELL.length(), linkerOptions); + } else { + linkerOptions = cmTokenize(value.substr(LINKER.length()), ","); + } + + if (linkerOptions.empty() || + (linkerOptions.size() == 1 && linkerOptions.front().empty())) { + continue; + } + + // for now, raise an error if prefix SHELL: is part of arguments + if (std::find_if(linkerOptions.begin(), linkerOptions.end(), + [&SHELL](const std::string& item) -> bool { + return item.find(SHELL) != std::string::npos; + }) != linkerOptions.end()) { + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.", + this->GetBacktrace()); + return result; + } + + std::vector<BT<std::string>> options = wrapOptions( + linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs); + if (joinItems) { + result.insert(entry, + cmJoin(cmRange<decltype(options.cbegin())>( + options.cbegin(), options.cend()), + " "_s)); + } else { + result.insert(entry, options.begin(), options.end()); + } + } + return result; +} + +void cmGeneratorTarget::GetStaticLibraryLinkOptions( + std::vector<std::string>& result, const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = + this->GetStaticLibraryLinkOptions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( + std::string const& config, std::string const& language) const +{ + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, this->LocalGenerator); + + EvaluatedTargetPropertyEntries entries; + if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { + std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create( + *this->LocalGenerator->GetCMakeInstance(), *linkOptions); + entries.Entries.emplace_back(EvaluateTargetPropertyEntry( + this, config, language, &dagChecker, *entry)); + } + processOptions(this, entries, result, uniqueOptions, false, + "static library link options", OptionsParse::Shell); + + return result; +} + +void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( + std::string const& config, std::string const& language) const +{ + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr, + nullptr, this->LocalGenerator); + + EvaluatedTargetPropertyEntries entries; + if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) { + cmList depends{ *linkDepends }; + for (const auto& depend : depends) { + std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create( + *this->LocalGenerator->GetCMakeInstance(), depend); + entries.Entries.emplace_back(EvaluateTargetPropertyEntry( + this, config, language, &dagChecker, *entry)); + } + } + AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes, + this->GetPolicyStatusCMP0099() == cmPolicies::NEW + ? UseTo::Link + : UseTo::Compile); + + processOptions(this, entries, result, uniqueOptions, false, "link depends", + OptionsParse::None); + + return result; +} diff --git a/Source/cmGeneratorTarget_Sources.cxx b/Source/cmGeneratorTarget_Sources.cxx new file mode 100644 index 0000000..de18df5 --- /dev/null +++ b/Source/cmGeneratorTarget_Sources.cxx @@ -0,0 +1,560 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <cstddef> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <string> +#include <type_traits> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmsys/RegularExpression.hxx" + +#include "cmAlgorithms.h" +#include "cmEvaluatedTargetProperty.h" +#include "cmFileSet.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocation.h" +#include "cmSourceGroup.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +void AddObjectEntries(cmGeneratorTarget const* headTarget, + std::string const& config, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries) +{ + if (cmLinkImplementationLibraries const* impl = + headTarget->GetLinkImplementationLibraries(config, UseTo::Compile)) { + entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; + for (cmLinkImplItem const& lib : impl->Libraries) { + if (lib.Target && + lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + std::string uniqueName = + headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely( + lib.Target); + std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">"; + cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(), + lib.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); + cge->SetEvaluateForBuildsystem(true); + + EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); + cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config, + headTarget, dagChecker), + ee.Values); + if (cge->GetHadContextSensitiveCondition()) { + ee.ContextDependent = true; + } + entries.Entries.emplace_back(std::move(ee)); + } + } + } +} + +void addFileSetEntry(cmGeneratorTarget const* headTarget, + std::string const& config, + cmGeneratorExpressionDAGChecker* dagChecker, + cmFileSet const* fileSet, + EvaluatedTargetPropertyEntries& entries) +{ + auto dirCges = fileSet->CompileDirectoryEntries(); + auto dirs = fileSet->EvaluateDirectoryEntries( + dirCges, headTarget->GetLocalGenerator(), config, headTarget, dagChecker); + bool contextSensitiveDirs = false; + for (auto const& dirCge : dirCges) { + if (dirCge->GetHadContextSensitiveCondition()) { + contextSensitiveDirs = true; + break; + } + } + cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance(); + for (auto& entryCge : fileSet->CompileFileEntries()) { + auto tpe = cmGeneratorTarget::TargetPropertyEntry::CreateFileSet( + dirs, contextSensitiveDirs, std::move(entryCge), fileSet); + entries.Entries.emplace_back( + EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, *tpe)); + EvaluatedTargetPropertyEntry const& entry = entries.Entries.back(); + for (auto const& file : entry.Values) { + auto* sf = headTarget->Makefile->GetOrCreateSource(file); + if (fileSet->GetType() == "HEADERS"_s) { + sf->SetProperty("HEADER_FILE_ONLY", "TRUE"); + } + +#ifndef CMAKE_BOOTSTRAP + std::string e; + std::string w; + auto path = sf->ResolveFullPath(&e, &w); + if (!w.empty()) { + cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); + } + if (path.empty()) { + if (!e.empty()) { + cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); + } + return; + } + bool found = false; + for (auto const& sg : headTarget->Makefile->GetSourceGroups()) { + if (sg.MatchChildrenFiles(path)) { + found = true; + break; + } + } + if (!found) { + if (fileSet->GetType() == "HEADERS"_s) { + headTarget->Makefile->GetOrCreateSourceGroup("Header Files") + ->AddGroupFile(path); + } + } +#endif + } + } +} + +void AddFileSetEntries(cmGeneratorTarget const* headTarget, + std::string const& config, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries) +{ + for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) { + for (auto const& name : cmList{ entry.Value }) { + auto const* headerSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, headerSet, entries); + } + } + for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) { + for (auto const& name : cmList{ entry.Value }) { + auto const* cxxModuleSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries); + } + } +} + +bool processSources(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries& entries, + std::vector<BT<std::string>>& srcs, + std::unordered_set<std::string>& uniqueSrcs, + bool debugSources) +{ + cmMakefile* mf = tgt->Target->GetMakefile(); + + bool contextDependent = entries.HadContextSensitiveCondition; + + for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + if (entry.ContextDependent) { + contextDependent = true; + } + + cmLinkImplItem const& item = entry.LinkImplItem; + std::string const& targetName = item.AsStr(); + + for (std::string& src : entry.Values) { + cmSourceFile* sf = mf->GetOrCreateSource(src); + std::string e; + std::string w; + std::string fullPath = sf->ResolveFullPath(&e, &w); + cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); + if (!w.empty()) { + cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); + } + if (fullPath.empty()) { + if (!e.empty()) { + cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); + } + return contextDependent; + } + + if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) { + std::ostringstream err; + if (!targetName.empty()) { + err << "Target \"" << targetName + << "\" contains relative path in its INTERFACE_SOURCES:\n \"" + << src << "\""; + } else { + err << "Found relative path while evaluating sources of \"" + << tgt->GetName() << "\":\n \"" << src << "\"\n"; + } + tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, + err.str()); + return contextDependent; + } + src = fullPath; + } + std::string usedSources; + for (std::string const& src : entry.Values) { + if (uniqueSrcs.insert(src).second) { + srcs.emplace_back(src, entry.Backtrace); + if (debugSources) { + usedSources += " * " + src + "\n"; + } + } + } + if (!usedSources.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used sources for target ") + tgt->GetName() + ":\n" + + usedSources, + entry.Backtrace); + } + } + return contextDependent; +} +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( + std::string const& config) const +{ + std::vector<BT<std::string>> files; + + if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { + // At configure-time, this method can be called as part of getting the + // LOCATION property or to export() a file to be include()d. However + // there is no cmGeneratorTarget at configure-time, so search the SOURCES + // for TARGET_OBJECTS instead for backwards compatibility with OLD + // behavior of CMP0024 and CMP0026 only. + + cmBTStringRange sourceEntries = this->Target->GetSourceEntries(); + for (auto const& entry : sourceEntries) { + cmList items{ entry.Value }; + for (auto const& item : items) { + if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") && + item.back() == '>') { + continue; + } + files.emplace_back(item); + } + } + return files; + } + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugSources = + !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES"); + + if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { + this->DebugSourcesDone = true; + } + + cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, nullptr, + this->LocalGenerator); + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, std::string(), &dagChecker, this->SourceEntries); + + std::unordered_set<std::string> uniqueSrcs; + bool contextDependentDirectSources = + processSources(this, entries, files, uniqueSrcs, debugSources); + + // Collect INTERFACE_SOURCES of all direct link-dependencies. + EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries; + AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(), + &dagChecker, linkInterfaceSourcesEntries, + IncludeRuntimeInterface::No, UseTo::Compile); + bool contextDependentInterfaceSources = processSources( + this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); + + // Collect TARGET_OBJECTS of direct object link-dependencies. + bool contextDependentObjects = false; + if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { + EvaluatedTargetPropertyEntries linkObjectsEntries; + AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); + contextDependentObjects = processSources(this, linkObjectsEntries, files, + uniqueSrcs, debugSources); + // Note that for imported targets or multi-config generators supporting + // cross-config builds the paths to the object files must be per-config, + // so contextDependentObjects will be true here even if object libraries + // are specified without per-config generator expressions. + } + + // Collect this target's file sets. + EvaluatedTargetPropertyEntries fileSetEntries; + AddFileSetEntries(this, config, &dagChecker, fileSetEntries); + bool contextDependentFileSets = + processSources(this, fileSetEntries, files, uniqueSrcs, debugSources); + + // Determine if sources are context-dependent or not. + if (!contextDependentDirectSources && !contextDependentInterfaceSources && + !contextDependentObjects && !contextDependentFileSets) { + this->SourcesAreContextDependent = Tribool::False; + } else { + this->SourcesAreContextDependent = Tribool::True; + } + + return files; +} + +void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, + const std::string& config) const +{ + std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config); + files.reserve(tmp.size()); + for (BT<cmSourceFile*>& v : tmp) { + files.push_back(v.Value); + } +} + +std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles( + std::string const& config) const +{ + std::vector<BT<cmSourceFile*>> files; + if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) { + // Since we are still configuring not all sources may exist yet, + // so we need to avoid full source classification because that + // requires the absolute paths to all sources to be determined. + // Since this is only for compatibility with old policies that + // projects should not depend on anymore, just compute the files + // without memoizing them. + std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); + std::set<cmSourceFile*> emitted; + for (BT<std::string> const& s : srcs) { + cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); + if (emitted.insert(sf).second) { + files.emplace_back(sf, s.Backtrace); + } + } + return files; + } + + KindedSources const& kinded = this->GetKindedSources(config); + files.reserve(kinded.Sources.size()); + for (SourceAndKind const& si : kinded.Sources) { + files.push_back(si.Source); + } + return files; +} + +void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( + std::vector<cmSourceFile*>& files, const std::string& config) const +{ + std::vector<BT<cmSourceFile*>> tmp = + this->GetSourceFilesWithoutObjectLibraries(config); + files.reserve(tmp.size()); + for (BT<cmSourceFile*>& v : tmp) { + files.push_back(v.Value); + } +} + +std::vector<BT<cmSourceFile*>> +cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( + std::string const& config) const +{ + std::vector<BT<cmSourceFile*>> files; + KindedSources const& kinded = this->GetKindedSources(config); + files.reserve(kinded.Sources.size()); + for (SourceAndKind const& si : kinded.Sources) { + if (si.Source.Value->GetObjectLibrary().empty()) { + files.push_back(si.Source); + } + } + return files; +} + +cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( + std::string const& config) const +{ + // If we already processed one configuration and found no dependency + // on configuration then always use the one result. + if (this->SourcesAreContextDependent == Tribool::False) { + return this->KindedSourcesMap.begin()->second; + } + + // Lookup any existing link implementation for this configuration. + std::string const key = cmSystemTools::UpperCase(config); + auto it = this->KindedSourcesMap.find(key); + if (it != this->KindedSourcesMap.end()) { + if (!it->second.Initialized) { + std::ostringstream e; + e << "The SOURCES of \"" << this->GetName() + << "\" use a generator expression that depends on the " + "SOURCES themselves."; + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); + static KindedSources empty; + return empty; + } + return it->second; + } + + // Add an entry to the map for this configuration. + KindedSources& files = this->KindedSourcesMap[key]; + this->ComputeKindedSources(files, config); + files.Initialized = true; + return files; +} + +void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, + std::string const& config) const +{ + // Get the source file paths by string. + std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); + + cmsys::RegularExpression header_regex(CM_HEADER_REGEX); + std::vector<cmSourceFile*> badObjLib; + + std::set<cmSourceFile*> emitted; + for (BT<std::string> const& s : srcs) { + // Create each source at most once. + cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); + if (!emitted.insert(sf).second) { + continue; + } + + // Compute the kind (classification) of this source file. + SourceKind kind; + std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); + cmFileSet const* fs = this->GetFileSetForSource(config, sf); + if (sf->GetCustomCommand()) { + kind = SourceKindCustomCommand; + } else if (!this->Target->IsNormal() && !this->Target->IsImported() && + fs && (fs->GetType() == "CXX_MODULES"_s)) { + kind = SourceKindCxxModuleSource; + } else if (this->Target->GetType() == cmStateEnums::UTILITY || + this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY + // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 + // NOLINTNEXTLINE(bugprone-branch-clone) + ) { + kind = SourceKindExtra; + } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) { + kind = SourceKindUnityBatched; + // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 + // NOLINTNEXTLINE(bugprone-branch-clone) + } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) { + kind = SourceKindHeader; + } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + kind = SourceKindExternalObject; + } else if (!sf->GetOrDetermineLanguage().empty()) { + kind = SourceKindObjectSource; + } else if (ext == "def") { + kind = SourceKindModuleDefinition; + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { + badObjLib.push_back(sf); + } + } else if (ext == "idl") { + kind = SourceKindIDL; + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { + badObjLib.push_back(sf); + } + } else if (ext == "resx") { + kind = SourceKindResx; + } else if (ext == "appxmanifest") { + kind = SourceKindAppManifest; + } else if (ext == "manifest") { + if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) { + kind = SourceKindExtra; + } else { + kind = SourceKindManifest; + } + } else if (ext == "pfx") { + kind = SourceKindCertificate; + } else if (ext == "xaml") { + kind = SourceKindXaml; + } else if (header_regex.find(sf->ResolveFullPath())) { + kind = SourceKindHeader; + } else { + kind = SourceKindExtra; + } + + // Save this classified source file in the result vector. + files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind }); + } + + if (!badObjLib.empty()) { + std::ostringstream e; + e << "OBJECT library \"" << this->GetName() << "\" contains:\n"; + for (cmSourceFile* i : badObjLib) { + e << " " << i->GetLocation().GetName() << "\n"; + } + e << "but may contain only sources that compile, header files, and " + "other files that would not affect linking of a normal library."; + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); + } +} + +std::vector<cmGeneratorTarget::AllConfigSource> const& +cmGeneratorTarget::GetAllConfigSources() const +{ + if (this->AllConfigSources.empty()) { + this->ComputeAllConfigSources(); + } + return this->AllConfigSources; +} + +void cmGeneratorTarget::ComputeAllConfigSources() const +{ + std::vector<std::string> configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + std::map<cmSourceFile const*, size_t> index; + + for (size_t ci = 0; ci < configs.size(); ++ci) { + KindedSources const& sources = this->GetKindedSources(configs[ci]); + for (SourceAndKind const& src : sources.Sources) { + auto mi = index.find(src.Source.Value); + if (mi == index.end()) { + AllConfigSource acs; + acs.Source = src.Source.Value; + acs.Kind = src.Kind; + this->AllConfigSources.push_back(std::move(acs)); + std::map<cmSourceFile const*, size_t>::value_type entry( + src.Source.Value, this->AllConfigSources.size() - 1); + mi = index.insert(entry).first; + } + this->AllConfigSources[mi->second].Configs.push_back(ci); + } + } +} + +std::vector<cmGeneratorTarget::AllConfigSource> +cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const +{ + std::vector<AllConfigSource> result; + for (AllConfigSource const& source : this->GetAllConfigSources()) { + if (source.Kind == kind) { + result.push_back(source); + } + } + return result; +} + +std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const +{ + std::set<std::string> languages; + std::vector<AllConfigSource> const& sources = this->GetAllConfigSources(); + for (AllConfigSource const& si : sources) { + std::string const& lang = si.Source->GetOrDetermineLanguage(); + if (!lang.empty()) { + languages.emplace(lang); + } + } + return languages; +} diff --git a/Source/cmGeneratorTarget_TargetPropertyEntry.cxx b/Source/cmGeneratorTarget_TargetPropertyEntry.cxx new file mode 100644 index 0000000..9b70f63 --- /dev/null +++ b/Source/cmGeneratorTarget_TargetPropertyEntry.cxx @@ -0,0 +1,189 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include <cm/memory> + +#include "cmFileSet.h" +#include "cmGeneratorExpression.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" + +class cmLocalGenerator; +class cmake; +struct cmGeneratorExpressionDAGChecker; + +cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem; + +class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryString(BT<std::string> propertyValue, + cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , PropertyValue(std::move(propertyValue)) + { + } + + const std::string& Evaluate(cmLocalGenerator*, const std::string&, + cmGeneratorTarget const*, + cmGeneratorExpressionDAGChecker*, + std::string const&) const override + { + return this->PropertyValue.Value; + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->PropertyValue.Backtrace; + } + std::string const& GetInput() const override + { + return this->PropertyValue.Value; + } + +private: + BT<std::string> PropertyValue; +}; + +class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, + cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , ge(std::move(cge)) + { + } + + const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& language) const override + { + return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr, + language); + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->ge->GetBacktrace(); + } + + std::string const& GetInput() const override { return this->ge->GetInput(); } + + bool GetHadContextSensitiveCondition() const override + { + return this->ge->GetHadContextSensitiveCondition(); + } + +private: + const std::unique_ptr<cmCompiledGeneratorExpression> ge; +}; + +class TargetPropertyEntryFileSet + : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryFileSet( + std::vector<std::string> dirs, bool contextSensitiveDirs, + std::unique_ptr<cmCompiledGeneratorExpression> entryCge, + const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , BaseDirs(std::move(dirs)) + , ContextSensitiveDirs(contextSensitiveDirs) + , EntryCge(std::move(entryCge)) + , FileSet(fileSet) + { + } + + const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& /*lang*/) const override + { + std::map<std::string, std::vector<std::string>> filesPerDir; + this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, + this->EntryCge, lg, config, headTarget, + dagChecker); + + std::vector<std::string> files; + for (auto const& it : filesPerDir) { + files.insert(files.end(), it.second.begin(), it.second.end()); + } + + static std::string filesStr; + filesStr = cmList::to_string(files); + return filesStr; + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->EntryCge->GetBacktrace(); + } + + std::string const& GetInput() const override + { + return this->EntryCge->GetInput(); + } + + bool GetHadContextSensitiveCondition() const override + { + return this->ContextSensitiveDirs || + this->EntryCge->GetHadContextSensitiveCondition(); + } + +private: + const std::vector<std::string> BaseDirs; + const bool ContextSensitiveDirs; + const std::unique_ptr<cmCompiledGeneratorExpression> EntryCge; + const cmFileSet* FileSet; +}; + +std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry> +cmGeneratorTarget::TargetPropertyEntry::Create( + cmake& cmakeInstance, const BT<std::string>& propertyValue, + bool evaluateForBuildsystem) +{ + if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) { + cmGeneratorExpression ge(cmakeInstance, propertyValue.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(propertyValue.Value); + cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); + return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>( + cm::make_unique<TargetPropertyEntryGenex>(std::move(cge))); + } + + return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>( + cm::make_unique<TargetPropertyEntryString>(propertyValue)); +} + +std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry> +cmGeneratorTarget::TargetPropertyEntry::CreateFileSet( + std::vector<std::string> dirs, bool contextSensitiveDirs, + std::unique_ptr<cmCompiledGeneratorExpression> entryCge, + const cmFileSet* fileSet, cmLinkImplItem const& item) +{ + return cm::make_unique<TargetPropertyEntryFileSet>( + std::move(dirs), contextSensitiveDirs, std::move(entryCge), fileSet, item); +} + +cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry( + cmLinkImplItem const& item) + : LinkImplItem(item) +{ +} + +bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition() + const +{ + return false; +} diff --git a/Source/cmGeneratorTarget_TransitiveProperty.cxx b/Source/cmGeneratorTarget_TransitiveProperty.cxx new file mode 100644 index 0000000..f5d9a1a --- /dev/null +++ b/Source/cmGeneratorTarget_TransitiveProperty.cxx @@ -0,0 +1,207 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <map> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionContext.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorExpressionNode.h" +#include "cmLinkItem.h" +#include "cmLocalGenerator.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmValue.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; +using TransitiveProperty = cmGeneratorTarget::TransitiveProperty; +} + +const std::map<cm::string_view, TransitiveProperty> + cmGeneratorTarget::BuiltinTransitiveProperties = { + { "AUTOMOC_MACRO_NAMES"_s, + { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } }, + { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } }, + { "COMPILE_DEFINITIONS"_s, + { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } }, + { "COMPILE_FEATURES"_s, + { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } }, + { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } }, + { "INCLUDE_DIRECTORIES"_s, + { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, + { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } }, + { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } }, + { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } }, + { "PRECOMPILE_HEADERS"_s, + { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } }, + { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } }, + { "SYSTEM_INCLUDE_DIRECTORIES"_s, + { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, + }; + +bool cmGeneratorTarget::MaybeHaveInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + UseTo usage) const +{ + std::string const key = prop + '@' + context->Config; + auto i = this->MaybeInterfacePropertyExists.find(key); + if (i == this->MaybeInterfacePropertyExists.end()) { + // Insert an entry now in case there is a cycle. + i = this->MaybeInterfacePropertyExists.emplace(key, false).first; + bool& maybeInterfaceProp = i->second; + + // If this target itself has a non-empty property value, we are done. + maybeInterfaceProp = cmNonempty(this->GetProperty(prop)); + + // Otherwise, recurse to interface dependencies. + if (!maybeInterfaceProp) { + cmGeneratorTarget const* headTarget = + context->HeadTarget ? context->HeadTarget : this; + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(context->Config, headTarget, + usage)) { + if (iface->HadHeadSensitiveCondition) { + // With a different head target we may get to a library with + // this interface property. + maybeInterfaceProp = true; + } else { + // The transitive interface libraries do not depend on the + // head target, so we can follow them. + for (cmLinkItem const& lib : iface->Libraries) { + if (lib.Target && + lib.Target->MaybeHaveInterfaceProperty(prop, context, usage)) { + maybeInterfaceProp = true; + break; + } + } + } + } + } + } + return i->second; +} + +std::string cmGeneratorTarget::EvaluateInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const +{ + std::string result; + + // If the property does not appear transitively at all, we are done. + if (!this->MaybeHaveInterfaceProperty(prop, context, usage)) { + return result; + } + + // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is + // a subset of TargetPropertyNode::Evaluate without stringify/parse steps + // but sufficient for transitive interface properties. + cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop, + nullptr, dagCheckerParent, + this->LocalGenerator); + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.ReportError( + context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">"); + return result; + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + // No error. We have already seen this transitive property. + return result; + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + cmGeneratorTarget const* headTarget = + context->HeadTarget ? context->HeadTarget : this; + + if (cmValue p = this->GetProperty(prop)) { + result = cmGeneratorExpressionNode::EvaluateDependentExpression( + *p, context->LG, context, headTarget, &dagChecker, this); + } + + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(context->Config, headTarget, usage)) { + context->HadContextSensitiveCondition = + context->HadContextSensitiveCondition || + iface->HadContextSensitiveCondition; + for (cmLinkItem const& lib : iface->Libraries) { + // Broken code can have a target in its own link interface. + // Don't follow such link interface entries so as not to create a + // self-referencing loop. + if (lib.Target && lib.Target != this) { + // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the + // above property and hand-evaluate it as if it were compiled. + // Create a context as cmCompiledGeneratorExpression::Evaluate does. + cmGeneratorExpressionContext libContext( + context->LG, context->Config, context->Quiet, headTarget, this, + context->EvaluateForBuildsystem, context->Backtrace, + context->Language); + std::string libResult = cmGeneratorExpression::StripEmptyListElements( + lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker, + usage)); + if (!libResult.empty()) { + if (result.empty()) { + result = std::move(libResult); + } else { + result.reserve(result.size() + 1 + libResult.size()); + result += ";"; + result += libResult; + } + } + context->HadContextSensitiveCondition = + context->HadContextSensitiveCondition || + libContext.HadContextSensitiveCondition; + context->HadHeadSensitiveCondition = + context->HadHeadSensitiveCondition || + libContext.HadHeadSensitiveCondition; + } + } + } + + return result; +} + +cm::optional<cmGeneratorTarget::TransitiveProperty> +cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop, + cmLocalGenerator const* lg) const +{ + cm::optional<TransitiveProperty> result; + static const cm::string_view kINTERFACE_ = "INTERFACE_"_s; + if (cmHasPrefix(prop, kINTERFACE_)) { + prop = prop.substr(kINTERFACE_.length()); + } + auto i = BuiltinTransitiveProperties.find(prop); + if (i != BuiltinTransitiveProperties.end()) { + result = i->second; + if (result->Usage != cmGeneratorTarget::UseTo::Compile) { + cmPolicies::PolicyStatus cmp0166 = + lg->GetPolicyStatus(cmPolicies::CMP0166); + if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) && + (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s || + prop == "LINK_OPTIONS"_s)) { + result->Usage = cmGeneratorTarget::UseTo::Compile; + } + } + } else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) { + cmPolicies::PolicyStatus cmp0043 = + lg->GetPolicyStatus(cmPolicies::CMP0043); + if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) { + result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s, + UseTo::Compile }; + } + } + return result; +} diff --git a/Source/cmTargetTraceDependencies.cxx b/Source/cmTargetTraceDependencies.cxx new file mode 100644 index 0000000..cc91a42 --- /dev/null +++ b/Source/cmTargetTraceDependencies.cxx @@ -0,0 +1,239 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTargetTraceDependencies.h" + +#include <sstream> +#include <utility> + +#include <cmext/algorithm> + +#include "cmCustomCommandGenerator.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" + +cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) + : GeneratorTarget(target) +{ + // Convenience. + this->Makefile = target->Target->GetMakefile(); + this->LocalGenerator = target->GetLocalGenerator(); + this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); + this->CurrentEntry = nullptr; + + // Queue all the source files already specified for the target. + std::set<cmSourceFile*> emitted; + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& c : configs) { + std::vector<cmSourceFile*> sources; + this->GeneratorTarget->GetSourceFiles(sources, c); + for (cmSourceFile* sf : sources) { + const std::set<cmGeneratorTarget const*> tgts = + this->GlobalGenerator->GetFilenameTargetDepends(sf); + if (cm::contains(tgts, this->GeneratorTarget)) { + std::ostringstream e; + e << "Evaluation output file\n \"" << sf->ResolveFullPath() + << "\"\ndepends on the sources of a target it is used in. This " + "is a dependency loop and is not allowed."; + this->GeneratorTarget->LocalGenerator->IssueMessage( + MessageType::FATAL_ERROR, e.str()); + return; + } + if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); + } + } + } + + // Queue pre-build, pre-link, and post-build rule dependencies. + this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); + this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); + this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); +} + +void cmTargetTraceDependencies::Trace() +{ + // Process one dependency at a time until the queue is empty. + while (!this->SourceQueue.empty()) { + // Get the next source from the queue. + cmSourceFile* sf = this->SourceQueue.front(); + this->SourceQueue.pop(); + this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; + + // Queue dependencies added explicitly by the user. + if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { + cmList objDeps{ *additionalDeps }; + for (auto& objDep : objDeps) { + if (cmSystemTools::FileIsFullPath(objDep)) { + objDep = cmSystemTools::CollapseFullPath(objDep); + } + } + this->FollowNames(objDeps); + } + + // Queue the source needed to generate this file, if any. + this->FollowName(sf->ResolveFullPath()); + + // Queue dependencies added programmatically by commands. + this->FollowNames(sf->GetDepends()); + + // Queue custom command dependencies. + if (cmCustomCommand const* cc = sf->GetCustomCommand()) { + this->CheckCustomCommand(*cc); + } + } + this->CurrentEntry = nullptr; + + this->GeneratorTarget->AddTracedSources(this->NewSources); +} + +void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) +{ + if (this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); + + // Make sure this file is in the target at the end. + this->NewSources.push_back(sf->ResolveFullPath()); + } +} + +void cmTargetTraceDependencies::FollowName(std::string const& name) +{ + // Use lower bound with key comparison to not repeat the search for the + // insert position if the name could not be found (which is the common case). + auto i = this->NameMap.lower_bound(name); + if (i == this->NameMap.end() || i->first != name) { + // Check if we know how to generate this file. + cmSourcesWithOutput sources = + this->LocalGenerator->GetSourcesWithOutput(name); + // If we failed to find a target or source and we have a relative path, it + // might be a valid source if made relative to the current binary + // directory. + if (!sources.Target && !sources.Source && + !cmSystemTools::FileIsFullPath(name)) { + auto fullname = + cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); + fullname = cmSystemTools::CollapseFullPath( + fullname, this->Makefile->GetHomeOutputDirectory()); + sources = this->LocalGenerator->GetSourcesWithOutput(fullname); + } + i = this->NameMap.emplace_hint(i, name, sources); + } + if (cmTarget* t = i->second.Target) { + // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or + // POST_BUILD command. + this->GeneratorTarget->Target->AddUtility(t->GetName(), false); + } + if (cmSourceFile* sf = i->second.Source) { + // For now only follow the dependency if the source file is not a + // byproduct. Semantics of byproducts in a non-Ninja context will have to + // be defined first. + if (!i->second.SourceIsByproduct) { + // Record the dependency we just followed. + if (this->CurrentEntry) { + this->CurrentEntry->Depends.push_back(sf); + } + this->QueueSource(sf); + } + } +} + +void cmTargetTraceDependencies::FollowNames( + std::vector<std::string> const& names) +{ + for (std::string const& name : names) { + this->FollowName(name); + } +} + +bool cmTargetTraceDependencies::IsUtility(std::string const& dep) +{ + // Dependencies on targets (utilities) are supposed to be named by + // just the target name. However for compatibility we support + // naming the output file generated by the target (assuming there is + // no output-name property which old code would not have set). In + // that case the target name will be the file basename of the + // dependency. + std::string util = cmSystemTools::GetFilenameName(dep); + if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { + util = cmSystemTools::GetFilenameWithoutLastExtension(util); + } + + // Check for a target with this name. + if (cmGeneratorTarget* t = + this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( + util)) { + // If we find the target and the dep was given as a full path, + // then make sure it was not a full path to something else, and + // the fact that the name matched a target was just a coincidence. + if (cmSystemTools::FileIsFullPath(dep)) { + if (t->GetType() >= cmStateEnums::EXECUTABLE && + t->GetType() <= cmStateEnums::MODULE_LIBRARY) { + // This is really only for compatibility so we do not need to + // worry about configuration names and output names. + std::string tLocation = t->GetLocationForBuild(); + tLocation = cmSystemTools::GetFilenamePath(tLocation); + std::string depLocation = cmSystemTools::GetFilenamePath(dep); + depLocation = cmSystemTools::CollapseFullPath(depLocation); + tLocation = cmSystemTools::CollapseFullPath(tLocation); + if (depLocation == tLocation) { + this->GeneratorTarget->Target->AddUtility(util, false); + return true; + } + } + } else { + // The original name of the dependency was not a full path. It + // must name a target, so add the target-level dependency. + this->GeneratorTarget->Target->AddUtility(util, true); + return true; + } + } + + // The dependency does not name a target built in this project. + return false; +} + +void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) +{ + // Collect dependencies referenced by all configurations. + std::set<std::string> depends; + for (std::string const& config : + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { + for (cmCustomCommandGenerator const& ccg : + this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { + // Collect target-level dependencies referenced in command lines. + for (auto const& util : ccg.GetUtilities()) { + this->GeneratorTarget->Target->AddUtility(util); + } + + // Collect file-level dependencies referenced in DEPENDS. + depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); + } + } + + // Queue file-level dependencies. + for (std::string const& dep : depends) { + if (!this->IsUtility(dep)) { + // The dependency does not name a target and may be a file we + // know how to generate. Queue it. + this->FollowName(dep); + } + } +} + +void cmTargetTraceDependencies::CheckCustomCommands( + const std::vector<cmCustomCommand>& commands) +{ + for (cmCustomCommand const& command : commands) { + this->CheckCustomCommand(command); + } +} diff --git a/Source/cmTargetTraceDependencies.h b/Source/cmTargetTraceDependencies.h new file mode 100644 index 0000000..5bbe8df --- /dev/null +++ b/Source/cmTargetTraceDependencies.h @@ -0,0 +1,46 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <queue> +#include <set> +#include <string> +#include <vector> + +#include "cmGeneratorTarget.h" +#include "cmLocalGenerator.h" + +class cmGlobalGenerator; +class cmSourceFile; +class cmCustomCommand; +class cmMakefile; + +class cmTargetTraceDependencies +{ +public: + cmTargetTraceDependencies(cmGeneratorTarget* target); + void Trace(); + +private: + cmGeneratorTarget* GeneratorTarget; + cmMakefile* Makefile; + cmLocalGenerator* LocalGenerator; + cmGlobalGenerator const* GlobalGenerator; + using SourceEntry = cmGeneratorTarget::SourceEntry; + SourceEntry* CurrentEntry; + std::queue<cmSourceFile*> SourceQueue; + std::set<cmSourceFile*> SourcesQueued; + using NameMapType = std::map<std::string, cmSourcesWithOutput>; + NameMapType NameMap; + std::vector<std::string> NewSources; + + void QueueSource(cmSourceFile* sf); + void FollowName(std::string const& name); + void FollowNames(std::vector<std::string> const& names); + bool IsUtility(std::string const& dep); + void CheckCustomCommand(cmCustomCommand const& cc); + void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); +}; @@ -381,6 +381,14 @@ CMAKE_CXX_SOURCES="\ cmGeneratorExpressionNode \ cmGeneratorExpressionParser \ cmGeneratorTarget \ + cmGeneratorTarget_CompatibleInterface \ + cmGeneratorTarget_IncludeDirectories \ + cmGeneratorTarget_Link \ + cmGeneratorTarget_LinkDirectories \ + cmGeneratorTarget_Options \ + cmGeneratorTarget_Sources \ + cmGeneratorTarget_TargetPropertyEntry \ + cmGeneratorTarget_TransitiveProperty \ cmGetCMakePropertyCommand \ cmGetDirectoryPropertyCommand \ cmGetFilenameComponentCommand \ @@ -492,6 +500,7 @@ CMAKE_CXX_SOURCES="\ cmTargetPropCommandBase \ cmTargetPropertyComputer \ cmTargetSourcesCommand \ + cmTargetTraceDependencies \ cmTest \ cmTestGenerator \ cmTimestamp \ |