Commit ffac622a authored by Brad King's avatar Brad King
Browse files

ENH: Add cmTarget::GetLinkInformation method to allow several places in the...

ENH: Add cmTarget::GetLinkInformation method to allow several places in the generators to share link information while only computing it once per configuration for a target.  Use it to simplify the chrpath feature.
parent bb52f45e
......@@ -168,6 +168,12 @@ cmComputeLinkInformation
// Get the language used for linking this target.
this->LinkLanguage =
this->Target->GetLinkerLanguage(this->GlobalGenerator);
if(!this->LinkLanguage)
{
// The Compute method will do nothing, so skip the rest of the
// initialization.
return;
}
// Check whether we should use an import library for linking a target.
this->UseImportLibrary =
......@@ -194,6 +200,31 @@ cmComputeLinkInformation
this->LibLinkSuffix =
this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
// Get options needed to specify RPATHs.
this->RuntimeUseChrpath = false;
if(this->Target->GetType() != cmTarget::STATIC_LIBRARY)
{
std::string rtVar = "CMAKE_";
if(this->Target->GetType() == cmTarget::EXECUTABLE)
{
rtVar += "EXECUTABLE";
}
else
{
rtVar += "SHARED_LIBRARY";
}
rtVar += "_RUNTIME_";
rtVar += this->LinkLanguage;
rtVar += "_FLAG";
std::string rtSepVar = rtVar + "_SEP";
this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar.c_str());
this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar.c_str());
this->RuntimeAlways =
(this->Makefile->
GetSafeDefinition("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH"));
this->RuntimeUseChrpath = this->Target->IsChrpathUsed();
}
// Get link type information.
this->ComputeLinkTypeInfo();
......@@ -1227,3 +1258,105 @@ void cmComputeLinkInformation::DiagnoseCycle()
}
cmSystemTools::Message(e.str().c_str());
}
//----------------------------------------------------------------------------
void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
bool for_install)
{
// Select whether to generate runtime search directories.
bool outputRuntime =
!this->Makefile->IsOn("CMAKE_SKIP_RPATH") && !this->RuntimeFlag.empty();
// Select whether to generate an rpath for the install tree or the
// build tree.
bool linking_for_install =
(for_install ||
this->Target->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"));
bool use_install_rpath =
(outputRuntime && this->Target->HaveInstallTreeRPATH() &&
linking_for_install);
bool use_build_rpath =
(outputRuntime && this->Target->HaveBuildTreeRPATH() &&
!linking_for_install);
bool use_link_rpath =
outputRuntime && linking_for_install &&
this->Target->GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH");
// Construct the RPATH.
if(use_install_rpath)
{
const char* install_rpath = this->Target->GetProperty("INSTALL_RPATH");
cmSystemTools::ExpandListArgument(install_rpath, runtimeDirs);
}
if(use_build_rpath || use_link_rpath)
{
std::vector<std::string> const& rdirs = this->GetRuntimeSearchPath();
for(std::vector<std::string>::const_iterator ri = rdirs.begin();
ri != rdirs.end(); ++ri)
{
// Put this directory in the rpath if using build-tree rpath
// support or if using the link path as an rpath.
if(use_build_rpath)
{
runtimeDirs.push_back(*ri);
}
else if(use_link_rpath)
{
// Do not add any path inside the source or build tree.
const char* topSourceDir = this->Makefile->GetHomeDirectory();
const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory();
if(!cmSystemTools::ComparePath(ri->c_str(), topSourceDir) &&
!cmSystemTools::ComparePath(ri->c_str(), topBinaryDir) &&
!cmSystemTools::IsSubDirectory(ri->c_str(), topSourceDir) &&
!cmSystemTools::IsSubDirectory(ri->c_str(), topBinaryDir))
{
runtimeDirs.push_back(*ri);
}
}
}
}
// Add runtime paths required by the platform to always be
// present. This is done even when skipping rpath support.
cmSystemTools::ExpandListArgument(this->RuntimeAlways.c_str(), runtimeDirs);
}
//----------------------------------------------------------------------------
std::string cmComputeLinkInformation::GetRPathString(bool for_install)
{
// Get the directories to use.
std::vector<std::string> runtimeDirs;
this->GetRPath(runtimeDirs, for_install);
// Concatenate the paths.
std::string rpath;
const char* sep = "";
for(std::vector<std::string>::const_iterator ri = runtimeDirs.begin();
ri != runtimeDirs.end(); ++ri)
{
// Separate from previous path.
rpath += sep;
sep = this->GetRuntimeSep().c_str();
// Add this path.
rpath += *ri;
}
return rpath;
}
//----------------------------------------------------------------------------
std::string cmComputeLinkInformation::GetChrpathString()
{
if(!this->RuntimeUseChrpath)
{
return "";
}
return this->GetRPathString(true);
}
//----------------------------------------------------------------------------
std::string cmComputeLinkInformation::GetChrpathTool()
{
return this->Makefile->GetSafeDefinition("CMAKE_CHRPATH");
}
......@@ -50,6 +50,12 @@ public:
std::vector<std::string> const& GetFrameworkPaths();
const char* GetLinkLanguage() const { return this->LinkLanguage; }
std::vector<std::string> const& GetRuntimeSearchPath();
std::string const& GetRuntimeFlag() const { return this->RuntimeFlag; }
std::string const& GetRuntimeSep() const { return this->RuntimeSep; }
void GetRPath(std::vector<std::string>& runtimeDirs, bool for_install);
std::string GetRPathString(bool for_install);
std::string GetChrpathString();
std::string GetChrpathTool();
private:
void AddItem(std::string const& item, cmTarget* tgt);
......@@ -76,6 +82,10 @@ private:
std::string LibLinkFlag;
std::string LibLinkFileFlag;
std::string LibLinkSuffix;
std::string RuntimeFlag;
std::string RuntimeSep;
std::string RuntimeAlways;
bool RuntimeUseChrpath;
// Link type adjustment.
void ComputeLinkTypeInfo();
......
......@@ -2105,11 +2105,12 @@ void cmGlobalXCodeGenerator
}
// Compute the link library and directory information.
cmComputeLinkInformation cli(cmtarget, configName);
if(!cli.Compute())
cmComputeLinkInformation* pcli = cmtarget->GetLinkInformation(configName);
if(!pcli)
{
continue;
}
cmComputeLinkInformation& cli = *pcli;
// Add dependencies directly on library files.
{
......
......@@ -16,6 +16,7 @@
=========================================================================*/
#include "cmInstallTargetGenerator.h"
#include "cmComputeLinkInformation.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
......@@ -283,7 +284,7 @@ cmInstallTargetGenerator
os << indent << "IF(EXISTS \"" << toDestDirPath << "\")\n";
this->AddInstallNamePatchRule(os, indent.Next(), config, toDestDirPath);
this->AddChrpathPatchRule(os, indent.Next(), toDestDirPath);
this->AddChrpathPatchRule(os, indent.Next(), config, toDestDirPath);
this->AddRanlibRule(os, indent.Next(), type, toDestDirPath);
this->AddStripRule(os, indent.Next(), type, toDestDirPath);
os << indent << "ENDIF(EXISTS \"" << toDestDirPath << "\")\n";
......@@ -487,7 +488,7 @@ cmInstallTargetGenerator
void
cmInstallTargetGenerator
::AddChrpathPatchRule(std::ostream& os, Indent const& indent,
std::string const& toDestDirPath)
const char* config, std::string const& toDestDirPath)
{
if(this->ImportLibrary ||
!(this->Target->GetType() == cmTarget::SHARED_LIBRARY ||
......@@ -497,40 +498,24 @@ cmInstallTargetGenerator
return;
}
if((this->Target->GetMakefile()->IsOn("CMAKE_USE_CHRPATH")==false)
|| (this->Target->IsChrpathAvailable()==false))
if(!this->Target->IsChrpathUsed())
{
return;
}
// Fix the RPATH in installed ELF binaries using chrpath.
std::string chrpathTool =
this->Target->GetMakefile()->GetSafeDefinition("CMAKE_CHRPATH");
std::string installRpath;
std::string dummy;
this->Target->GetMakefile()->GetLocalGenerator()->GetLinkerArgs(
installRpath, dummy, *this->Target, true, 0);
const char* linkLanguage = this->Target->GetLinkerLanguage(this->Target->
GetMakefile()->GetLocalGenerator()->GetGlobalGenerator());
if (linkLanguage==0)
// Get the link information for this target.
// It can provide the RPATH.
cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
if(!cli)
{
return;
}
std::string runTimeFlagVar = "CMAKE_SHARED_LIBRARY_RUNTIME_";
runTimeFlagVar += linkLanguage;
runTimeFlagVar += "_FLAG";
std::string runtimeFlag =
this->Target->GetMakefile()->GetSafeDefinition(runTimeFlagVar.c_str());
// Get the install RPATH from the link information.
std::string newRpath = cli->GetChrpathString();
const char* newRpath=installRpath.c_str();
if (strstr(installRpath.c_str(), runtimeFlag.c_str())==installRpath.c_str())
{
newRpath = installRpath.c_str()+strlen(runtimeFlag.c_str());
}
// Fix the RPATH in installed ELF binaries using chrpath.
std::string chrpathTool = cli->GetChrpathTool();
// Write a rule to run chrpath to set the install-tree RPATH
os << indent << "EXECUTE_PROCESS(COMMAND \"" << chrpathTool;
......
......@@ -58,6 +58,7 @@ protected:
const char* config,
const std::string& toDestDirPath);
void AddChrpathPatchRule(std::ostream& os, Indent const& indent,
const char* config,
std::string const& toDestDirPath);
void AddStripRule(std::ostream& os, Indent const& indent,
......
......@@ -1483,70 +1483,33 @@ std::string cmLocalGenerator::ConvertToLinkReference(std::string const& lib)
return this->Convert(lib.c_str(), START_OUTPUT, SHELL);
}
bool cmLocalGenerator::GetLinkerArgs(std::string& rpath,
std::string& linkLibs,
cmTarget& tgt,
bool relink,
unsigned int minRpathSize)
/**
* Output the linking rules on a command line. For executables,
* targetLibrary should be a NULL pointer. For libraries, it should point
* to the name of the library. This will not link a library against itself.
*/
void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
cmTarget& tgt,
bool relink)
{
rpath = "";
// collect all the flags needed for linking libraries
linkLibs = "";
const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE");
cmComputeLinkInformation cli(&tgt, config);
if(!cli.Compute())
cmComputeLinkInformation* pcli = tgt.GetLinkInformation(config);
if(!pcli)
{
return false;
}
const char* linkLanguage = cli.GetLinkLanguage();
// Embed runtime search paths if possible and if required.
bool outputRuntime = !this->Makefile->IsOn("CMAKE_SKIP_RPATH");
// Lookup rpath specification flags.
std::string runtimeFlag;
std::string runtimeSep;
if(tgt.GetType() != cmTarget::STATIC_LIBRARY)
{
std::string runTimeFlagVar = "CMAKE_";
if(tgt.GetType() == cmTarget::EXECUTABLE)
{
runTimeFlagVar += "EXECUTABLE";
}
else
{
runTimeFlagVar += "SHARED_LIBRARY";
}
runTimeFlagVar += "_RUNTIME_";
runTimeFlagVar += linkLanguage;
runTimeFlagVar += "_FLAG";
std::string runTimeFlagSepVar = runTimeFlagVar + "_SEP";
runtimeFlag = this->Makefile->GetSafeDefinition(runTimeFlagVar.c_str());
runtimeSep = this->Makefile->GetSafeDefinition(runTimeFlagSepVar.c_str());
return;
}
// concatenate all paths or no?
bool runtimeConcatenate = !runtimeSep.empty();
cmComputeLinkInformation& cli = *pcli;
const char* runtimeAlways =
this->Makefile->GetDefinition("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH");
// Collect library linking flags command line options.
std::string linkLibs;
// Turn off rpath support if no flag is available to specify it.
if(runtimeFlag.empty())
{
outputRuntime = false;
runtimeAlways = 0;
}
const char* linkLanguage = cli.GetLinkLanguage();
std::string libPathFlag =
this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
std::string libPathTerminator =
this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
std::string libLinkFlag =
this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG");
// Flags to link an executable to shared libraries.
std::string linkFlagsVar = "CMAKE_SHARED_LIBRARY_LINK_";
linkFlagsVar += linkLanguage;
......@@ -1595,146 +1558,55 @@ bool cmLocalGenerator::GetLinkerArgs(std::string& rpath,
linkLibs += " ";
}
// Select whether to generate an rpath for the install tree or the
// build tree.
bool linking_for_install =
relink || tgt.GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH");
bool use_install_rpath =
outputRuntime && tgt.HaveInstallTreeRPATH() && linking_for_install;
bool use_build_rpath =
outputRuntime && tgt.HaveBuildTreeRPATH() && !linking_for_install;
bool use_link_rpath =
outputRuntime && linking_for_install &&
tgt.GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH");
// Write the library flags to the build rule.
fout << linkLibs;
// Construct the RPATH.
// Get the RPATH entries.
std::vector<std::string> runtimeDirs;
if(use_install_rpath)
{
const char* install_rpath = tgt.GetProperty("INSTALL_RPATH");
cmSystemTools::ExpandListArgument(install_rpath, runtimeDirs);
}
if(use_build_rpath || use_link_rpath)
cli.GetRPath(runtimeDirs, relink);
// Check what kind of rpath flags to use.
if(cli.GetRuntimeSep().empty())
{
std::vector<std::string> const& rdirs = cli.GetRuntimeSearchPath();
for(std::vector<std::string>::const_iterator ri = rdirs.begin();
ri != rdirs.end(); ++ri)
// Each rpath entry gets its own option ("-R a -R b -R c")
std::string rpath;
for(std::vector<std::string>::iterator ri = runtimeDirs.begin();
ri != runtimeDirs.end(); ++ri)
{
// Put this directory in the rpath if using build-tree rpath
// support or if using the link path as an rpath.
if(use_build_rpath)
{
runtimeDirs.push_back(*ri);
}
else if(use_link_rpath)
{
// Do not add any path inside the source or build tree.
const char* topSourceDir = this->Makefile->GetHomeDirectory();
const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory();
if(!cmSystemTools::ComparePath(ri->c_str(), topSourceDir) &&
!cmSystemTools::ComparePath(ri->c_str(), topBinaryDir) &&
!cmSystemTools::IsSubDirectory(ri->c_str(), topSourceDir) &&
!cmSystemTools::IsSubDirectory(ri->c_str(), topBinaryDir))
{
runtimeDirs.push_back(*ri);
}
}
rpath += cli.GetRuntimeFlag();
rpath += this->Convert(ri->c_str(), FULL, SHELL, false);
rpath += " ";
}
fout << rpath;
}
if(runtimeAlways)
{
// Add runtime paths required by the platform to always be
// present. This is done even when skipping rpath support.
cmSystemTools::ExpandListArgument(runtimeAlways, runtimeDirs);
}
// Convert the runtime directory names for use in the build file.
for(std::vector<std::string>::iterator ri = runtimeDirs.begin();
ri != runtimeDirs.end(); ++ri)
else
{
*ri = this->Convert(ri->c_str(), FULL, SHELL, false);
}
// All rpath entries are combined ("-Wl,-rpath,a:b:c").
std::string rpath = cli.GetRPathString(relink);
if(!runtimeDirs.empty())
{
// For the runtime search directories, do a "-Wl,-rpath,a:b:c" or
// a "-R a -R b -R c" type link line
rpath += runtimeFlag;
std::vector<std::string>::iterator itr = runtimeDirs.begin();
rpath += *itr;
++itr;
for( ; itr != runtimeDirs.end(); ++itr )
// If not relinking, make sure the rpath string is long enough to
// support a subsequent chrpath on installation.
if(!relink)
{
if(runtimeConcatenate)
std::string::size_type minLength = cli.GetChrpathString().size();
while(rpath.size() < minLength)
{
rpath += runtimeSep;
rpath += *itr;
}
else
{
rpath += " ";
rpath += runtimeFlag;
rpath += *itr;
rpath += cli.GetRuntimeSep();
}
}
}
while (rpath.size() < minRpathSize)
{
if (rpath.size()==0)
// Store the rpath option in the stream.
if(!rpath.empty())
{
rpath += runtimeFlag;
fout << cli.GetRuntimeFlag();
fout << this->EscapeForShell(rpath.c_str(), true);
fout << " ";
}
rpath += runtimeSep;
}
return true;
}
/**
* Output the linking rules on a command line. For executables,
* targetLibrary should be a NULL pointer. For libraries, it should point
* to the name of the library. This will not link a library against itself.
*/
void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout,
cmTarget& tgt,
bool relink)
{
std::string rpath;
std::string linkLibs;
unsigned int minBuildRpathSize = 0;
if ((relink==false)
&& this->Makefile->IsOn("CMAKE_USE_CHRPATH")
&& (tgt.IsChrpathAvailable()))
{
std::string installRpath;
std::string dummy;
this->GetLinkerArgs(installRpath, dummy, tgt, true, 0);
minBuildRpathSize = static_cast<unsigned int>(installRpath.size());
}
if (!this->GetLinkerArgs(rpath, linkLibs, tgt, relink, minBuildRpathSize))
{
return;
}
const char* linkLanguage =
tgt.GetLinkerLanguage(this->GetGlobalGenerator());
if(!linkLanguage)
{
cmSystemTools::
Error("CMake can not determine linker language for target:",
tgt.GetName());
return;
}
fout << linkLibs;
fout << rpath << " ";
// Add standard libraries for this language.
std::string standardLibsVar = "CMAKE_";
standardLibsVar += linkLanguage;
standardLibsVar += cli.GetLinkLanguage();
standardLibsVar += "_STANDARD_LIBRARIES";
if(const char* stdLibs =
this->Makefile->GetDefinition(standardLibsVar.c_str()))
......
......@@ -248,13 +248,6 @@ public:
*/
virtual std::string GetTargetDirectory(cmTarget const& target) const;
///! Determine the arguments for the linker call, used also by
/// cmInstallTargetGenerator
bool GetLinkerArgs(std::string& rpath, std::string& linkLibs,
cmTarget& tgt, bool relink, unsigned int minRpathSize);
bool IsChrpathAvailable(const cmTarget& target);
/**
* Get the level of backwards compatibility requested by the project
* in this directory. This is the value of the CMake variable
......
......@@ -1570,11 +1570,12 @@ void cmLocalVisualStudio6Generator
std::string& options)
{
// Compute the link information for this configuration.
cmComputeLinkInformation cli(&target, configName);
if(!cli.Compute())
cmComputeLinkInformation* pcli = target.GetLinkInformation(configName);
if(!pcli)
{
return;
}
cmComputeLinkInformation& cli = *pcli;
typedef cmComputeLinkInformation::ItemVector ItemVector;
ItemVector const& linkLibs = cli.GetItems();
std::vector<std::string> const& linkDirs = cli.GetDirectories();
......
......@@ -762,11 +762,12 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout,
targetNameImport, targetNamePDB, configName);
// Compute the link library and directory information.
cmComputeLinkInformation cli(&target, configName);
if(!cli.Compute())
cmComputeLinkInformation* pcli = target.GetLinkInformation(configName);
if(!pcli)
{
return;
}
cmComputeLinkInformation& cli = *pcli;
const char* linkLanguage = cli.GetLinkLanguage();
// Compute the variable name to lookup standard libraries for this
......@@ -831,11 +832,12 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout,
targetNameImport, targetNamePDB, configName);
// Compute the link library and directory information.
cmComputeLinkInformation cli(&target, configName);
if(!cli.Compute())
cmComputeLinkInformation* pcli = target.GetLinkInformation(configName);
if(!pcli)
{
return;
}
cmComputeLinkInformation& cli = *pcli;
const char* linkLanguage = cli.GetLinkLanguage();
// Compute the variable name to lookup standard libraries for this
......