Commit 586a9427 authored by Brad King's avatar Brad King
Browse files

ENH: Created target property INSTALL_NAME_DIR initalized by...

ENH: Created target property INSTALL_NAME_DIR initalized by CMAKE_INSTALL_NAME_DIR specifying the directory portion of the OSX install_name field in shared libraries.  This is the OSX equivalent of RPATH.
parent 7db7b981
......@@ -4,8 +4,11 @@ SET(CMAKE_SHARED_MODULE_PREFIX "lib")
SET(CMAKE_SHARED_MODULE_SUFFIX ".so")
SET(CMAKE_MODULE_EXISTS 1)
SET(CMAKE_DL_LIBS "")
SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib")
SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle")
SET(CMAKE_C_LINK_FLAGS "-headerpad_max_install_names")
SET(CMAKE_CXX_LINK_FLAGS "-headerpad_max_install_names")
SET(CMAKE_PLATFORM_HAS_INSTALLNAME 1)
SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names")
SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names")
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
IF("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$")
......@@ -14,32 +17,17 @@ IF("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$")
ENDIF("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$")
IF(NOT XCODE)
# Enable shared library versioning.
# Enable shared library versioning. This flag is not actually referenced
# but the fact that the setting exists will cause the generators to support
# soname computation.
SET(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name")
SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-install_name")
ENDIF(NOT XCODE)
# OSX does not really implement an rpath, but it does allow a path to
# be specified in the soname field of a dylib.
IF(CMAKE_SKIP_RPATH)
# No rpath requested. Just use the soname directly.
SET(CMAKE_C_CREATE_SHARED_LIBRARY
"<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <LINK_FLAGS> -o <TARGET> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG> <TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
SET(CMAKE_CXX_CREATE_SHARED_LIBRARY
"<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG> <TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
ELSE(CMAKE_SKIP_RPATH)
# Support for rpath is requested. Approximate it by putting the
# full path to the library in the soname field. Then when executables
# link the library they will copy this full path as the name to use
# to find the library. We can get the directory containing the library
# by using the dirname of the <TARGET>. It may be a relative path
# so we use a "cd ...;pwd" trick to convert it to a full path at
# build time.
SET(CMAKE_C_CREATE_SHARED_LIBRARY
"<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <LINK_FLAGS> -o <TARGET> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG> \"`cd \\`dirname <TARGET>\\`\;pwd`/<TARGET_SONAME>\" <OBJECTS> <LINK_LIBRARIES>")
SET(CMAKE_CXX_CREATE_SHARED_LIBRARY
"<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG> \"`cd \\`dirname <TARGET>\\`\;pwd`/<TARGET_SONAME>\" <OBJECTS> <LINK_LIBRARIES>")
ENDIF(CMAKE_SKIP_RPATH)
SET(CMAKE_C_CREATE_SHARED_LIBRARY
"<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <LINK_FLAGS> -o <TARGET> -install_name <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
SET(CMAKE_CXX_CREATE_SHARED_LIBRARY
"<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> -install_name <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
SET(CMAKE_CXX_CREATE_SHARED_MODULE
"<CMAKE_CXX_COMPILER> <CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
......
......@@ -1145,13 +1145,30 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
outflag += "\\\"";
extraLinkOptions += " ";
extraLinkOptions += outflag;
// Add the flags to create an executable.
std::string createFlags =
this->LookupFlags("CMAKE_", lang, "_LINK_FLAGS", "");
if(!createFlags.empty())
{
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
}
else
{
fileType = "compiled.mach-o.dylib";
productType = "com.apple.product-type.library.dynamic";
extraLinkOptions += " -bundle";
// Add the flags to create a module.
std::string createFlags =
this->LookupFlags("CMAKE_SHARED_MODULE_CREATE_", lang, "_FLAGS",
"-bundle");
if(!createFlags.empty())
{
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
}
break;
}
......@@ -1166,13 +1183,31 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
this->CreateString("1"));
buildSettings->AddAttribute("DYLIB_CURRENT_VERSION",
this->CreateString("1"));
extraLinkOptions += " -dynamiclib";
// Add the flags to create a shared library.
std::string createFlags =
this->LookupFlags("CMAKE_SHARED_LIBRARY_CREATE_", lang, "_FLAGS",
"-dynamiclib");
if(!createFlags.empty())
{
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
break;
}
case cmTarget::EXECUTABLE:
{
fileType = "compiled.mach-o.executable";
// Add the flags to create an executable.
std::string createFlags =
this->LookupFlags("CMAKE_", lang, "_LINK_FLAGS", "");
if(!createFlags.empty())
{
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
// Handle bundles and normal executables separately.
if(target.GetPropertyAsBool("MACOSX_BUNDLE"))
{
......@@ -1284,8 +1319,6 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
this->CreateString(debugStr));
buildSettings->AddAttribute("GCC_OPTIMIZATION_LEVEL",
this->CreateString(optLevel));
buildSettings->AddAttribute("INSTALL_PATH",
this->CreateString(""));
buildSettings->AddAttribute("OPTIMIZATION_CFLAGS",
this->CreateString(oflagc.c_str()));
if(lang && strcmp(lang, "CXX") == 0)
......@@ -1307,9 +1340,45 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
buildSettings->AddAttribute("OTHER_CFLAGS",
this->CreateString(flags.c_str()));
}
// Create the INSTALL_PATH attribute.
std::string install_name_dir;
if(target.GetType() == cmTarget::SHARED_LIBRARY)
{
// Select whether to generate an install_name directory for the
// install tree or the build tree.
if(target.GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"))
{
install_name_dir =
target.GetInstallNameDirForInstallTree(configName);
}
else
{
install_name_dir =
target.GetInstallNameDirForBuildTree(configName);
}
if(install_name_dir.empty())
{
// Xcode will not pass the -install_name option at all if INSTALL_PATH
// is not given or is empty. We must explicitly put the flag in the
// link flags to create an install_name with just the library soname.
extraLinkOptions += " -install_name ";
extraLinkOptions += productName;
}
else
{
// Convert to a path for the native build tool.
cmSystemTools::ConvertToUnixSlashes(install_name_dir);
install_name_dir =
this->XCodeEscapePath(install_name_dir.c_str());
}
}
buildSettings->AddAttribute("INSTALL_PATH",
this->CreateString(install_name_dir.c_str()));
buildSettings->AddAttribute("OTHER_LDFLAGS",
this->CreateString(extraLinkOptions.c_str()));
buildSettings->AddAttribute("OTHER_REZFLAGS",
this->CreateString(""));
buildSettings->AddAttribute("SECTORDER_FLAGS",
......@@ -2317,3 +2386,26 @@ cmGlobalXCodeGenerator
}
}
}
//----------------------------------------------------------------------------
std::string cmGlobalXCodeGenerator::LookupFlags(const char* varNamePrefix,
const char* varNameLang,
const char* varNameSuffix,
const char* default_flags)
{
if(varNameLang)
{
std::string varName = varNamePrefix;
varName += varNameLang;
varName += varNameSuffix;
if(const char* varValue =
m_CurrentMakefile->GetDefinition(varName.c_str()))
{
if(*varValue)
{
return varValue;
}
}
}
return default_flags;
}
......@@ -142,6 +142,11 @@ private:
cmTarget& cmtarget,
const std::vector<cmCustomCommand>&);
void CreateReRunCMakeFile(cmLocalGenerator* root);
std::string LookupFlags(const char* varNamePrefix,
const char* varNameLang,
const char* varNameSuffix,
const char* default_flags);
protected:
int m_XcodeVersion;
......
......@@ -52,10 +52,11 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
}
// Write variable settings to do per-configuration references.
this->PrepareInstallReference(os);
this->PrepareScriptReference(os, this->Target, "BUILD", true, false);
// Create the per-configuration reference.
std::string fromName = this->GetInstallReference();
std::string fromName = this->GetScriptReference(this->Target, "BUILD",
false);
std::string fromFile = fromDir;
fromFile += fromName;
......@@ -150,12 +151,22 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
// Write code to install the target file.
this->AddInstallRule(os, this->Destination.c_str(), type, fromFile.c_str(),
this->ImportLibrary, properties);
// Fix the install_name settings in installed binaries.
if(type == cmTarget::SHARED_LIBRARY ||
type == cmTarget::MODULE_LIBRARY ||
type == cmTarget::EXECUTABLE)
{
this->AddInstallNamePatchRule(os);
}
}
//----------------------------------------------------------------------------
void
cmInstallTargetGenerator
::PrepareInstallReference(std::ostream& os)
::PrepareScriptReference(std::ostream& os, cmTarget* target,
const char* place, bool useConfigDir,
bool useSOName)
{
// If the target name may vary with the configuration type then
// store all possible names ahead of time in variables.
......@@ -164,42 +175,180 @@ cmInstallTargetGenerator
this->ConfigurationTypes->begin();
i != this->ConfigurationTypes->end(); ++i)
{
// Start with the configuration's subdirectory.
// Initialize the name.
fname = "";
this->Target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator()->
AppendDirectoryForConfig(i->c_str(), fname);
if(useConfigDir)
{
// Start with the configuration's subdirectory.
target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator()->
AppendDirectoryForConfig(i->c_str(), fname);
}
// Compute the name of the library.
std::string targetName;
std::string targetNameSO;
std::string targetNameReal;
std::string targetNameImport;
target->GetLibraryNames(targetName, targetNameSO, targetNameReal,
targetNameImport, i->c_str());
if(this->ImportLibrary)
{
// Use the import library name.
fname += targetNameImport;
}
else if(useSOName)
{
// Use the soname.
fname += targetNameSO;
}
else
{
// Use the canonical name.
fname += targetName;
}
// Set a variable with the target name for this configuration.
fname += this->Target->GetFullName(i->c_str(), this->ImportLibrary);
os << "SET(" << this->Target->GetName()
os << "SET(" << target->GetName() << "_" << place
<< (this->ImportLibrary? "_IMPNAME_" : "_NAME_") << *i
<< " \"" << fname << "\")\n";
}
}
//----------------------------------------------------------------------------
std::string cmInstallTargetGenerator::GetInstallReference()
std::string cmInstallTargetGenerator::GetScriptReference(cmTarget* target,
const char* place,
bool useSOName)
{
if(this->ConfigurationTypes->empty())
{
// Reference the target by its one configuration name.
return this->Target->GetFullName(this->ConfigurationName,
this->ImportLibrary);
std::string targetName;
std::string targetNameSO;
std::string targetNameReal;
std::string targetNameImport;
target->GetLibraryNames(targetName, targetNameSO, targetNameReal,
targetNameImport, this->ConfigurationName);
if(this->ImportLibrary)
{
// Use the import library name.
return targetNameImport;
}
else if(useSOName)
{
// Use the soname.
return targetNameSO;
}
else
{
// Use the canonical name.
return targetName;
}
}
else
{
// Reference the target using the per-configuration variable.
std::string ref = "${";
ref += this->Target->GetName();
ref += target->GetName();
if(this->ImportLibrary)
{
ref += "_";
ref += place;
ref += "_IMPNAME_";
}
else
{
ref += "_";
ref += place;
ref += "_NAME_";
}
ref += "${CMAKE_INSTALL_CONFIG_NAME}}";
return ref;
}
}
//----------------------------------------------------------------------------
void cmInstallTargetGenerator::AddInstallNamePatchRule(std::ostream& os)
{
// Build a map of build-tree install_name to install-tree install_name for
// shared libraries linked to this target.
std::map<cmStdString, cmStdString> install_name_remap;
cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED;
const char* config = this->ConfigurationName;
if(config && cmSystemTools::UpperCase(config) == "DEBUG")
{
linkType = cmTarget::DEBUG;
}
// TODO: Merge with ComputeLinkInformation.
const cmTarget::LinkLibraries& inLibs = this->Target->GetLinkLibraries();
for(cmTarget::LinkLibraries::const_iterator j = inLibs.begin();
j != inLibs.end(); ++j)
{
std::string lib = j->first;
if((this->Target->GetType() == cmTarget::EXECUTABLE ||
lib != this->Target->GetName()) &&
(j->second == cmTarget::GENERAL || j->second == linkType))
{
if(cmTarget* tgt = this->Target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator()->FindTarget(0, lib.c_str()))
{
if(tgt->GetType() == cmTarget::SHARED_LIBRARY)
{
// If the build tree and install tree use different path components
// of the install_name field then we need to create a mapping to be
// applied after installation.
std::string for_build = tgt->GetInstallNameDirForBuildTree(config);
std::string for_install = tgt->GetInstallNameDirForInstallTree(config);
if(for_build != for_install)
{
// Map from the build-tree install_name.
this->PrepareScriptReference(os, tgt, "REMAP_FROM",
!for_build.empty(), true);
for_build += this->GetScriptReference(tgt, "REMAP_FROM", true);
// Map to the install-tree install_name.
this->PrepareScriptReference(os, tgt, "REMAP_TO",
false, true);
for_install += this->GetScriptReference(tgt, "REMAP_TO", true);
// Store the mapping entry.
install_name_remap[for_build] = for_install;
}
}
}
}
}
// Edit the install_name of the target itself if necessary.
this->PrepareScriptReference(os, this->Target, "REMAPPED", false, true);
std::string new_id;
if(this->Target->GetType() == cmTarget::SHARED_LIBRARY)
{
std::string for_build = this->Target->GetInstallNameDirForBuildTree(config);
std::string for_install = this->Target->GetInstallNameDirForInstallTree(config);
if(for_build != for_install)
{
// Prepare to refer to the install-tree install_name.
new_id = for_install;
new_id += this->GetScriptReference(this->Target, "REMAPPED", true);
}
}
// Write a rule to run install_name_tool to set the install-tree
// install_name value and references.
if(!new_id.empty() || !install_name_remap.empty())
{
os << "EXECUTE_PROCESS(COMMAND install_name_tool";
if(!new_id.empty())
{
os << "\n -id \"" << new_id << "\"";
}
for(std::map<cmStdString, cmStdString>::const_iterator
i = install_name_remap.begin();
i != install_name_remap.end(); ++i)
{
os << "\n -change \"" << i->first << "\" \"" << i->second << "\"";
}
os << "\n \"" << this->Destination.c_str() << "/"
<< this->GetScriptReference(this->Target, "REMAPPED", true) << "\")\n";
}
}
......@@ -33,8 +33,12 @@ public:
protected:
virtual void GenerateScript(std::ostream& os);
void PrepareInstallReference(std::ostream& os);
std::string GetInstallReference();
void PrepareScriptReference(std::ostream& os, cmTarget* target,
const char* place, bool useConfigDir,
bool useSOName);
std::string GetScriptReference(cmTarget* target, const char* place,
bool useSOName);
void AddInstallNamePatchRule(std::ostream& os);
cmTarget* Target;
std::string Destination;
bool ImportLibrary;
......
......@@ -643,6 +643,13 @@ cmLocalGenerator::ExpandRuleVariable(std::string const& variable,
return "";
}
}
if(replaceValues.TargetInstallNameDir)
{
if(variable == "TARGET_INSTALLNAME_DIR")
{
return replaceValues.TargetInstallNameDir;
}
}
if(replaceValues.LinkLibraries)
{
if(variable == "LINK_LIBRARIES")
......
......@@ -182,6 +182,7 @@ public:
this->Flags= 0;
this->ObjectsQuoted= 0;
this->TargetSOName= 0;
this->TargetInstallNameDir = 0;
this->LinkFlags= 0;
}
const char* Language;
......@@ -193,6 +194,7 @@ public:
const char* Flags;
const char* ObjectsQuoted;
const char* TargetSOName;
const char* TargetInstallNameDir;
const char* LinkFlags;
};
......
......@@ -392,7 +392,46 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
vars.ObjectsQuoted = buildObjs.c_str();
vars.TargetSOName= targetNameSO.c_str();
vars.LinkFlags = linkFlags.c_str();
// Compute the directory portion of the install_name setting.
std::string install_name_dir;
if(this->Target->GetType() == cmTarget::SHARED_LIBRARY)
{
// Select whether to generate an install_name directory for the
// install tree or the build tree.
const char* config = this->LocalGenerator->m_ConfigurationName.c_str();
if(this->Target->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"))
{
install_name_dir =
this->Target->GetInstallNameDirForInstallTree(config);
}
else
{
install_name_dir =
this->Target->GetInstallNameDirForBuildTree(config);
}
// Set the rule variable replacement value.
if(install_name_dir.empty())
{
vars.TargetInstallNameDir = "";
}
else
{
// Convert to a path for the native build tool.
install_name_dir =
this->LocalGenerator->Convert(install_name_dir.c_str(),
cmLocalGenerator::FULL,
cmLocalGenerator::SHELL, false);
// The Convert method seems to strip trailing slashes, which should
// probably be fixed. Since the only platform supporting install_name
// right now uses forward slashes just add one.
install_name_dir += "/";
vars.TargetInstallNameDir = install_name_dir.c_str();
}
}
// Expand placeholders in the commands.
this->LocalGenerator->m_TargetImplib = targetOutPathImport;
for(std::vector<std::string>::iterator i = commands.begin();
......
......@@ -95,10 +95,13 @@ public:
"BUILD_WITH_INSTALL_RPATH is a boolean specifying whether to link "
"the target in the build tree with the INSTALL_RPATH. This takes "
"precedence over SKIP_BUILD_RPATH and avoids the need for relinking "
"before installation. When the target is created the values of "
"the variables CMAKE_INSTALL_RPATH, CMAKE_SKIP_BUILD_RPATH, and "
"CMAKE_BUILD_WITH_INSTALL_RPATH are used to initialize these "
"properties.\n"
"before installation. INSTALL_NAME_DIR is a string specifying the "
"directory portion of the \"install_name\" field of shared libraries "
"on Mac OSX to use in the installed targets. "
"When the target is created the values of "
"the variables CMAKE_INSTALL_RPATH, CMAKE_SKIP_BUILD_RPATH, "
"CMAKE_BUILD_WITH_INSTALL_RPATH, and CMAKE_INSTALL_NAME_DIR "
"are used to initialize these properties.\n"
"PROJECT_LABEL can be used to change the name of "
"the target in an IDE like visual studio. VS_KEYWORD can be set "
"to change the visual studio keyword, for example QT integration "
......
......@@ -52,6 +52,7 @@ void cmTarget::SetMakefile(cmMakefile* mf)
m_Makefile = mf;
// Setup default property values.
this->SetPropertyDefault("INSTALL_NAME_DIR", "");
this->SetPropertyDefault("INSTALL_RPATH", "");
this->SetPropertyDefault("SKIP_BUILD_RPATH", "OFF");
this->SetPropertyDefault("BUILD_WITH_INSTALL_RPATH", "OFF");
......@@ -775,7 +776,7 @@ void cmTarget::SetProperty(const char* prop, const char* value)
m_Properties[prop] = value;
}
const char* cmTarget::GetDirectory()
const char* cmTarget::GetDirectory(const char* config)
{
switch( this->GetType() )
{
......@@ -794,6 +795,13 @@ const char* cmTarget::GetDirectory()
{
m_Directory = m_Makefile->GetStartOutputDirectory();
}
if(config)
{
// Add the configuration's subdirectory.
m_Directory += "/";
m_Makefile->GetLocalGenerator()->GetGlobalGenerator()->
AppendDirectoryForConfig(config, m_Directory);
}
return m_Directory.c_str();
}
......@@ -1069,13 +1077,9 @@ void cmTarget::GetFullName(std::string& prefix, std::string& base,
std::string cmTarget::GetFullPath(const char* config, bool implib)
{
// Start with the output directory for the target.
std::string fpath = this->GetDirectory();
std::string fpath = this->GetDirectory(config);
fpath += "/";
// Add the configuration's subdirectory.
m_Makefile->GetLocalGenerator()->GetGlobalGenerator()->
AppendDirectoryForConfig(config, fpath);
// Add the full name of the target.
fpath += this->GetFullName(config, implib);
return fpath;
......@@ -1440,3 +1444,47 @@ bool cmTarget::NeedRelinkBeforeInstall()
// this target must be relinked.
return this->HaveBuildTreeRPATH() || this->HaveInstallTreeRPATH();
}
//----------------------------------------------------------------------------
std::string cmTarget::GetInstallNameDirForBuildTree(const char* config)
{
// If building directly for installation then the build tree install_name<