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.
This commit is contained in:
Brad King 2009-07-10 13:53:28 -04:00
parent ea00bb990b
commit 19792bf30e
2 changed files with 135 additions and 19 deletions

View File

@ -85,6 +85,9 @@ public:
// Cache link implementation computation from each configuration. // Cache link implementation computation from each configuration.
typedef std::map<cmStdString, cmTarget::LinkImplementation> LinkImplMapType; typedef std::map<cmStdString, cmTarget::LinkImplementation> LinkImplMapType;
LinkImplMapType LinkImplMap; LinkImplMapType LinkImplMap;
typedef std::map<cmStdString, cmTarget::LinkClosure> LinkClosureMapType;
LinkClosureMapType LinkClosureMap;
}; };
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -2321,22 +2324,106 @@ bool cmTarget::GetPropertyAsBool(const char* prop)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
const char* cmTarget::GetLinkerLanguage(const char*) class cmTargetCollectLinkLanguages
{ {
cmGlobalGenerator* gg = public:
this->Makefile->GetLocalGenerator()->GetGlobalGenerator(); cmTargetCollectLinkLanguages(cmTarget* target, const char* config,
std::set<cmStdString>& languages):
Config(config), Languages(languages) { this->Visited.insert(target); }
void Visit(cmTarget* target)
{
if(!target || !this->Visited.insert(target).second)
{
return;
}
cmTarget::LinkInterface const* iface =
target->GetLinkInterface(this->Config);
if(!iface) { return; }
for(std::vector<std::string>::const_iterator
li = iface->Languages.begin(); li != iface->Languages.end(); ++li)
{
this->Languages.insert(*li);
}
cmMakefile* mf = target->GetMakefile();
for(std::vector<std::string>::const_iterator
li = iface->Libraries.begin(); li != iface->Libraries.end(); ++li)
{
this->Visit(mf->FindTargetToUse(li->c_str()));
}
}
private:
const char* Config;
std::set<cmStdString>& Languages;
std::set<cmTarget*> 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())
{
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<cmStdString> languages;
LinkImplementation const* impl = this->GetLinkImplementation(config);
for(std::vector<std::string>::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<std::string>::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<cmStdString>::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")) if(this->GetProperty("HAS_CXX"))
{ {
const_cast<cmTarget*>(this)->SetProperty("LINKER_LANGUAGE", "CXX"); lc.LinkerLanguage = "CXX";
} }
const char* linkerLang = this->GetProperty("LINKER_LANGUAGE"); else if(const char* linkerLang = this->GetProperty("LINKER_LANGUAGE"))
if (linkerLang==0)
{ {
// if the property has not yet been set, collect all languages in the lc.LinkerLanguage = linkerLang;
// target and then find the language with the highest preference value }
std::set<cmStdString> languages; else
this->GetLanguages(languages); {
// Find the language with the highest preference value.
cmGlobalGenerator* gg =
this->Makefile->GetLocalGenerator()->GetGlobalGenerator();
std::string linkerLangList; // only used for the error message std::string linkerLangList; // only used for the error message
int maxLinkerPref = 0; int maxLinkerPref = 0;
bool multiplePreferedLanguages = false; bool multiplePreferedLanguages = false;
@ -2344,10 +2431,10 @@ const char* cmTarget::GetLinkerLanguage(const char*)
sit != languages.end(); ++sit) sit != languages.end(); ++sit)
{ {
int linkerPref = gg->GetLinkerPreference(sit->c_str()); int linkerPref = gg->GetLinkerPreference(sit->c_str());
if ((linkerPref > maxLinkerPref) || (linkerLang==0)) if (lc.LinkerLanguage.empty() || (linkerPref > maxLinkerPref))
{ {
maxLinkerPref = linkerPref; maxLinkerPref = linkerPref;
linkerLang = sit->c_str(); lc.LinkerLanguage = *sit;
linkerLangList = *sit; linkerLangList = *sit;
multiplePreferedLanguages = false; multiplePreferedLanguages = false;
} }
@ -2359,21 +2446,16 @@ const char* cmTarget::GetLinkerLanguage(const char*)
} }
} }
if (linkerLang!=0)
{
const_cast<cmTarget*>(this)->SetProperty("LINKER_LANGUAGE", linkerLang);
}
if (multiplePreferedLanguages) if (multiplePreferedLanguages)
{ {
cmOStringStream err; cmOStringStream err;
err << "Error: Target " << this->Name << " contains multiple languages " err << "Error: Target " << this->Name << " contains multiple languages "
<< "with the highest linker preference (" << maxLinkerPref << "): " << "with the highest linker preference (" << maxLinkerPref << "): "
<< linkerLangList << "\n" << linkerLangList << "\n"
<< "You must set the LINKER_LANGUAGE property for this target."; << "You must set the LINKER_LANGUAGE property for this target.";
cmSystemTools::Error(err.str().c_str()); 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); LinkImplementation const* impl = this->GetLinkImplementation(config);
iface.Libraries = impl->Libraries; iface.Libraries = impl->Libraries;
iface.WrongConfigLibraries = impl->WrongConfigLibraries; 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; return true;
@ -3869,6 +3956,7 @@ void cmTarget::ComputeLinkImplementation(const char* config,
// Compute which library configuration to link. // Compute which library configuration to link.
cmTarget::LinkLibraryType linkType = this->ComputeLinkType(config); cmTarget::LinkLibraryType linkType = this->ComputeLinkType(config);
// Collect libraries directly linked in this configuration.
LinkLibraryVectorType const& llibs = this->GetOriginalLinkLibraries(); LinkLibraryVectorType const& llibs = this->GetOriginalLinkLibraries();
for(cmTarget::LinkLibraryVectorType::const_iterator li = llibs.begin(); for(cmTarget::LinkLibraryVectorType::const_iterator li = llibs.begin();
li != llibs.end(); ++li) li != llibs.end(); ++li)
@ -3891,6 +3979,15 @@ void cmTarget::ComputeLinkImplementation(const char* config,
impl.WrongConfigLibraries.push_back(item); impl.WrongConfigLibraries.push_back(item);
} }
} }
// This target needs runtime libraries for its source languages.
std::set<cmStdString> languages;
this->GetLanguages(languages);
for(std::set<cmStdString>::iterator li = languages.begin();
li != languages.end(); ++li)
{
impl.Languages.push_back(*li);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -240,6 +240,9 @@ public:
other information needed by targets that link to this target. */ other information needed by targets that link to this target. */
struct LinkInterface struct LinkInterface
{ {
// Languages whose runtime libraries must be linked.
std::vector<std::string> Languages;
// Libraries listed in the interface. // Libraries listed in the interface.
std::vector<std::string> Libraries; std::vector<std::string> Libraries;
@ -259,6 +262,9 @@ public:
dependencies needed by the object files of the target. */ dependencies needed by the object files of the target. */
struct LinkImplementation struct LinkImplementation
{ {
// Languages whose runtime libraries must be linked.
std::vector<std::string> Languages;
// Libraries linked directly in this configuration. // Libraries linked directly in this configuration.
std::vector<std::string> Libraries; std::vector<std::string> Libraries;
@ -268,6 +274,18 @@ public:
}; };
LinkImplementation const* GetLinkImplementation(const char* config); 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<std::string> Languages;
};
LinkClosure const* GetLinkClosure(const char* config);
/** Strip off leading and trailing whitespace from an item named in /** Strip off leading and trailing whitespace from an item named in
the link dependencies of this target. */ the link dependencies of this target. */
std::string CheckCMP0004(std::string const& item); std::string CheckCMP0004(std::string const& item);
@ -535,6 +553,7 @@ private:
void ComputeLinkImplementation(const char* config, void ComputeLinkImplementation(const char* config,
LinkImplementation& impl); LinkImplementation& impl);
void ComputeLinkClosure(const char* config, LinkClosure& lc);
// The cmMakefile instance that owns this target. This should // The cmMakefile instance that owns this target. This should
// always be set. // always be set.