From 19792bf30e0499cc49ee518e42274ef02e91db9a Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 10 Jul 2009 13:53:28 -0400 Subject: ENH: Consider link dependencies for link language This teaches cmTarget to account for the languages compiled into link dependencies when determining the linker language for its target. We list the languages compiled into a static archive in its link interface. Any target linking to it knows that the runtime libraries for the static archive's languages must be available at link time. For now this affects only the linker language selection, but later it will allow CMake to automatically list the language runtime libraries. --- Source/cmTarget.cxx | 135 ++++++++++++++++++++++++++++++++++++++++++++-------- Source/cmTarget.h | 19 ++++++++ 2 files changed, 135 insertions(+), 19 deletions(-) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 5f2217d..2a20030 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -85,6 +85,9 @@ public: // Cache link implementation computation from each configuration. typedef std::map LinkImplMapType; LinkImplMapType LinkImplMap; + + typedef std::map LinkClosureMapType; + LinkClosureMapType LinkClosureMap; }; //---------------------------------------------------------------------------- @@ -2321,22 +2324,106 @@ bool cmTarget::GetPropertyAsBool(const char* prop) } //---------------------------------------------------------------------------- -const char* cmTarget::GetLinkerLanguage(const char*) +class cmTargetCollectLinkLanguages { - cmGlobalGenerator* gg = - this->Makefile->GetLocalGenerator()->GetGlobalGenerator(); - if(this->GetProperty("HAS_CXX")) +public: + cmTargetCollectLinkLanguages(cmTarget* target, const char* config, + std::set& languages): + Config(config), Languages(languages) { this->Visited.insert(target); } + void Visit(cmTarget* target) { - const_cast(this)->SetProperty("LINKER_LANGUAGE", "CXX"); + if(!target || !this->Visited.insert(target).second) + { + return; + } + + cmTarget::LinkInterface const* iface = + target->GetLinkInterface(this->Config); + if(!iface) { return; } + + for(std::vector::const_iterator + li = iface->Languages.begin(); li != iface->Languages.end(); ++li) + { + this->Languages.insert(*li); + } + + cmMakefile* mf = target->GetMakefile(); + for(std::vector::const_iterator + li = iface->Libraries.begin(); li != iface->Libraries.end(); ++li) + { + this->Visit(mf->FindTargetToUse(li->c_str())); + } } - const char* linkerLang = this->GetProperty("LINKER_LANGUAGE"); - if (linkerLang==0) +private: + const char* Config; + std::set& Languages; + std::set Visited; +}; + +//---------------------------------------------------------------------------- +const char* cmTarget::GetLinkerLanguage(const char* config) +{ + const char* lang = this->GetLinkClosure(config)->LinkerLanguage.c_str(); + return *lang? lang : 0; +} + +//---------------------------------------------------------------------------- +cmTarget::LinkClosure const* cmTarget::GetLinkClosure(const char* config) +{ + std::string key = cmSystemTools::UpperCase(config? config : ""); + cmTargetInternals::LinkClosureMapType::iterator + i = this->Internal->LinkClosureMap.find(key); + if(i == this->Internal->LinkClosureMap.end()) { - // if the property has not yet been set, collect all languages in the - // target and then find the language with the highest preference value - std::set languages; - this->GetLanguages(languages); + LinkClosure lc; + this->ComputeLinkClosure(config, lc); + cmTargetInternals::LinkClosureMapType::value_type entry(key, lc); + i = this->Internal->LinkClosureMap.insert(entry).first; + } + return &i->second; +} +//---------------------------------------------------------------------------- +void cmTarget::ComputeLinkClosure(const char* config, LinkClosure& lc) +{ + // Get languages built in this target. + std::set languages; + LinkImplementation const* impl = this->GetLinkImplementation(config); + for(std::vector::const_iterator li = impl->Languages.begin(); + li != impl->Languages.end(); ++li) + { + languages.insert(*li); + } + + // Add interface languages from linked targets. + cmTargetCollectLinkLanguages cll(this, config, languages); + for(std::vector::const_iterator li = impl->Libraries.begin(); + li != impl->Libraries.end(); ++li) + { + cll.Visit(this->Makefile->FindTargetToUse(li->c_str())); + } + + // Store the transitive closure of languages. + for(std::set::const_iterator li = languages.begin(); + li != languages.end(); ++li) + { + lc.Languages.push_back(*li); + } + + // Choose the language whose linker should be used. + if(this->GetProperty("HAS_CXX")) + { + lc.LinkerLanguage = "CXX"; + } + else if(const char* linkerLang = this->GetProperty("LINKER_LANGUAGE")) + { + lc.LinkerLanguage = linkerLang; + } + else + { + // Find the language with the highest preference value. + cmGlobalGenerator* gg = + this->Makefile->GetLocalGenerator()->GetGlobalGenerator(); std::string linkerLangList; // only used for the error message int maxLinkerPref = 0; bool multiplePreferedLanguages = false; @@ -2344,10 +2431,10 @@ const char* cmTarget::GetLinkerLanguage(const char*) sit != languages.end(); ++sit) { int linkerPref = gg->GetLinkerPreference(sit->c_str()); - if ((linkerPref > maxLinkerPref) || (linkerLang==0)) + if (lc.LinkerLanguage.empty() || (linkerPref > maxLinkerPref)) { maxLinkerPref = linkerPref; - linkerLang = sit->c_str(); + lc.LinkerLanguage = *sit; linkerLangList = *sit; multiplePreferedLanguages = false; } @@ -2359,21 +2446,16 @@ const char* cmTarget::GetLinkerLanguage(const char*) } } - if (linkerLang!=0) - { - const_cast(this)->SetProperty("LINKER_LANGUAGE", linkerLang); - } if (multiplePreferedLanguages) { cmOStringStream err; err << "Error: Target " << this->Name << " contains multiple languages " - << "with the highest linker preference (" << maxLinkerPref << "): " + << "with the highest linker preference (" << maxLinkerPref << "): " << linkerLangList << "\n" << "You must set the LINKER_LANGUAGE property for this target."; cmSystemTools::Error(err.str().c_str()); } } - return this->GetProperty("LINKER_LANGUAGE"); } //---------------------------------------------------------------------------- @@ -3829,6 +3911,11 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface) LinkImplementation const* impl = this->GetLinkImplementation(config); iface.Libraries = impl->Libraries; iface.WrongConfigLibraries = impl->WrongConfigLibraries; + if(this->GetType() == cmTarget::STATIC_LIBRARY) + { + // Targets using this archive need its language runtime libraries. + iface.Languages = impl->Languages; + } } return true; @@ -3869,6 +3956,7 @@ void cmTarget::ComputeLinkImplementation(const char* config, // Compute which library configuration to link. cmTarget::LinkLibraryType linkType = this->ComputeLinkType(config); + // Collect libraries directly linked in this configuration. LinkLibraryVectorType const& llibs = this->GetOriginalLinkLibraries(); for(cmTarget::LinkLibraryVectorType::const_iterator li = llibs.begin(); li != llibs.end(); ++li) @@ -3891,6 +3979,15 @@ void cmTarget::ComputeLinkImplementation(const char* config, impl.WrongConfigLibraries.push_back(item); } } + + // This target needs runtime libraries for its source languages. + std::set languages; + this->GetLanguages(languages); + for(std::set::iterator li = languages.begin(); + li != languages.end(); ++li) + { + impl.Languages.push_back(*li); + } } //---------------------------------------------------------------------------- diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 63e8516..015a3a6 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -240,6 +240,9 @@ public: other information needed by targets that link to this target. */ struct LinkInterface { + // Languages whose runtime libraries must be linked. + std::vector Languages; + // Libraries listed in the interface. std::vector Libraries; @@ -259,6 +262,9 @@ public: dependencies needed by the object files of the target. */ struct LinkImplementation { + // Languages whose runtime libraries must be linked. + std::vector Languages; + // Libraries linked directly in this configuration. std::vector Libraries; @@ -268,6 +274,18 @@ public: }; LinkImplementation const* GetLinkImplementation(const char* config); + /** Link information from the transitive closure of the link + implementation and the interfaces of its dependencies. */ + struct LinkClosure + { + // The preferred linker language. + std::string LinkerLanguage; + + // Languages whose runtime libraries must be linked. + std::vector Languages; + }; + LinkClosure const* GetLinkClosure(const char* config); + /** Strip off leading and trailing whitespace from an item named in the link dependencies of this target. */ std::string CheckCMP0004(std::string const& item); @@ -535,6 +553,7 @@ private: void ComputeLinkImplementation(const char* config, LinkImplementation& impl); + void ComputeLinkClosure(const char* config, LinkClosure& lc); // The cmMakefile instance that owns this target. This should // always be set. -- cgit v0.12