Commit 8401c5ba authored by Brad King's avatar Brad King
Browse files

ENH: Allow separate installation of shared libs and their links.

  - Add NAMELINK_ONLY and NAMELINK_SKIP to INSTALL command
  - Options select a \"namelink\" mode
  - cmInstallTargetGenerator selects files/link based on mode
  - See bug #4419
parent 852f6018
......@@ -283,6 +283,57 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
return false;
}
// Enforce argument rules too complex to specify for the
// general-purpose parser.
if(archiveArgs.GetNamelinkOnly() ||
runtimeArgs.GetNamelinkOnly() ||
frameworkArgs.GetNamelinkOnly() ||
bundleArgs.GetNamelinkOnly() ||
privateHeaderArgs.GetNamelinkOnly() ||
publicHeaderArgs.GetNamelinkOnly() ||
resourceArgs.GetNamelinkOnly())
{
this->SetError(
"TARGETS given NAMELINK_ONLY option not in LIBRARY group. "
"The NAMELINK_ONLY option may be specified only following LIBRARY."
);
return false;
}
if(archiveArgs.GetNamelinkSkip() ||
runtimeArgs.GetNamelinkSkip() ||
frameworkArgs.GetNamelinkSkip() ||
bundleArgs.GetNamelinkSkip() ||
privateHeaderArgs.GetNamelinkSkip() ||
publicHeaderArgs.GetNamelinkSkip() ||
resourceArgs.GetNamelinkSkip())
{
this->SetError(
"TARGETS given NAMELINK_SKIP option not in LIBRARY group. "
"The NAMELINK_SKIP option may be specified only following LIBRARY."
);
return false;
}
if(libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip())
{
this->SetError(
"TARGETS given NAMELINK_ONLY and NAMELINK_SKIP. "
"At most one of these two options may be specified."
);
return false;
}
// Select the mode for installing symlinks to versioned shared libraries.
cmInstallTargetGenerator::NamelinkModeType
namelinkMode = cmInstallTargetGenerator::NamelinkModeNone;
if(libraryArgs.GetNamelinkOnly())
{
namelinkMode = cmInstallTargetGenerator::NamelinkModeOnly;
}
else if(libraryArgs.GetNamelinkSkip())
{
namelinkMode = cmInstallTargetGenerator::NamelinkModeSkip;
}
// Check if there is something to do.
if(targetList.GetVector().empty())
{
......@@ -352,6 +403,12 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
// cygwin. Currently no other platform is a DLL platform.
if(dll_platform)
{
// When in namelink only mode skip all libraries on Windows.
if(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly)
{
continue;
}
// This is a DLL platform.
if(!archiveArgs.GetDestination().empty())
{
......@@ -378,6 +435,12 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
// INSTALL properties. Otherwise, use the LIBRARY properties.
if(target.IsFrameworkOnApple())
{
// When in namelink only mode skip frameworks.
if(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly)
{
continue;
}
// Use the FRAMEWORK properties.
if (!frameworkArgs.GetDestination().empty())
{
......@@ -400,6 +463,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
{
libraryGenerator = CreateInstallTargetGenerator(target,
libraryArgs, false);
libraryGenerator->SetNamelinkMode(namelinkMode);
}
else
{
......@@ -438,6 +502,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
{
libraryGenerator = CreateInstallTargetGenerator(target, libraryArgs,
false);
libraryGenerator->SetNamelinkMode(namelinkMode);
}
else
{
......
......@@ -105,7 +105,7 @@ public:
" [PERMISSIONS permissions...]\n"
" [CONFIGURATIONS [Debug|Release|...]]\n"
" [COMPONENT <component>]\n"
" [OPTIONAL]\n"
" [OPTIONAL] [NAMELINK_ONLY|NAMELINK_SKIP]\n"
" ] [...])\n"
"The TARGETS form specifies rules for installing targets from a "
"project. There are five kinds of target files that may be "
......@@ -140,6 +140,25 @@ public:
"See documentation of the PRIVATE_HEADER, PUBLIC_HEADER, and RESOURCE "
"target properties for details."
"\n"
"Either NAMELINK_ONLY or NAMELINK_SKIP may be specified as a LIBRARY "
"option. "
"On some platforms a versioned shared library has a symbolic link "
"such as\n"
" lib<name>.so -> lib<name>.so.1\n"
"where \"lib<name>.so.1\" is the soname of the library and "
"\"lib<name>.so\" is a \"namelink\" allowing linkers to find the "
"library when given \"-l<name>\". "
"The NAMELINK_ONLY option causes installation of only the namelink "
"when a library target is installed. "
"The NAMELINK_SKIP option causes installation of library files other "
"than the namelink when a library target is installed. "
"When neither option is given both portions are installed. "
"On platforms where versioned shared libraries do not have namelinks "
"or when a library is not versioned the NAMELINK_SKIP option installs "
"the library and the NAMELINK_ONLY option installs nothing. "
"See the VERSION and SOVERSION target properties for details on "
"creating versioned shared libraries."
"\n"
"One or more groups of properties may be specified in a single call "
"to the TARGETS form of this command. A target may be installed more "
"than once to different locations. Consider hypothetical "
......
......@@ -37,6 +37,8 @@ cmInstallCommandArguments::cmInstallCommandArguments()
,Permissions (&Parser, "PERMISSIONS" , &ArgumentGroup)
,Configurations(&Parser, "CONFIGURATIONS", &ArgumentGroup)
,Optional (&Parser, "OPTIONAL" , &ArgumentGroup)
,NamelinkOnly (&Parser, "NAMELINK_ONLY" , &ArgumentGroup)
,NamelinkSkip (&Parser, "NAMELINK_SKIP" , &ArgumentGroup)
,GenericArguments(0)
{
this->Component.SetDefaultString("Unspecified");
......@@ -107,6 +109,32 @@ bool cmInstallCommandArguments::GetOptional() const
return false;
}
bool cmInstallCommandArguments::GetNamelinkOnly() const
{
if (this->NamelinkOnly.IsEnabled())
{
return true;
}
if (this->GenericArguments!=0)
{
return this->GenericArguments->GetNamelinkOnly();
}
return false;
}
bool cmInstallCommandArguments::GetNamelinkSkip() const
{
if (this->NamelinkSkip.IsEnabled())
{
return true;
}
if (this->GenericArguments!=0)
{
return this->GenericArguments->GetNamelinkSkip();
}
return false;
}
const std::vector<std::string>&
cmInstallCommandArguments::GetConfigurations() const
{
......
......@@ -39,6 +39,8 @@ class cmInstallCommandArguments
const std::string& GetPermissions() const;
const std::vector<std::string>& GetConfigurations() const;
bool GetOptional() const;
bool GetNamelinkOnly() const;
bool GetNamelinkSkip() const;
// once HandleDirectoryMode() is also switched to using
// cmInstallCommandArguments then these two functions can become non-static
......@@ -54,6 +56,8 @@ class cmInstallCommandArguments
cmCAStringVector Permissions;
cmCAStringVector Configurations;
cmCAEnabler Optional;
cmCAEnabler NamelinkOnly;
cmCAEnabler NamelinkSkip;
std::string DestinationString;
std::string PermissionsString;
......
......@@ -22,9 +22,6 @@
#include "cmMakefile.h"
#include "cmake.h"
// TODO:
// - Skip IF(EXISTS) checks if nothing is done with the installed file
//----------------------------------------------------------------------------
cmInstallTargetGenerator
::cmInstallTargetGenerator(cmTarget& t, const char* dest, bool implib,
......@@ -34,6 +31,7 @@ cmInstallTargetGenerator
cmInstallGenerator(dest, configurations, component), Target(&t),
ImportLibrary(implib), FilePermissions(file_permissions), Optional(optional)
{
this->NamelinkMode = NamelinkModeNone;
this->Target->SetHaveInstallRule(true);
}
......@@ -149,12 +147,19 @@ cmInstallTargetGenerator
toInstallPath += this->GetInstallFilename(this->Target, config,
this->ImportLibrary, false);
// Track whether post-install operations should be added to the
// script.
bool tweakInstalledFile = true;
// Compute the list of files to install for this target.
std::vector<std::string> files;
std::string literal_args;
cmTarget::TargetType type = this->Target->GetType();
if(type == cmTarget::EXECUTABLE)
{
// There is a bug in cmInstallCommand if this fails.
assert(this->NamelinkMode == NamelinkModeNone);
std::string targetName;
std::string targetNameReal;
std::string targetNameImport;
......@@ -215,6 +220,9 @@ cmInstallTargetGenerator
config);
if(this->ImportLibrary)
{
// There is a bug in cmInstallCommand if this fails.
assert(this->NamelinkMode == NamelinkModeNone);
std::string from1 = fromDirConfig;
from1 += targetNameImport;
files.push_back(from1);
......@@ -224,6 +232,9 @@ cmInstallTargetGenerator
}
else if(this->Target->IsFrameworkOnApple())
{
// There is a bug in cmInstallCommand if this fails.
assert(this->NamelinkMode == NamelinkModeNone);
// Compute the build tree location of the framework directory
std::string from1 = fromDirConfig;
// Remove trailing slashes... so that from1 ends with ".framework":
......@@ -243,25 +254,82 @@ cmInstallTargetGenerator
}
else
{
std::string from1 = fromDirConfig;
from1 += targetName;
files.push_back(from1);
// Operations done at install time on the installed file should
// be done on the real file and not any of the symlinks.
toInstallPath = this->GetInstallDestination();
toInstallPath += "/";
toInstallPath += targetNameReal;
// Construct the list of file names to install for this library.
bool haveNamelink = false;
std::string fromName;
std::string fromSOName;
std::string fromRealName;
fromName = fromDirConfig;
fromName += targetName;
if(targetNameSO != targetName)
{
std::string from2 = fromDirConfig;
from2 += targetNameSO;
files.push_back(from2);
haveNamelink = true;
fromSOName = fromDirConfig;
fromSOName += targetNameSO;
}
if(targetNameReal != targetName &&
targetNameReal != targetNameSO)
{
std::string from3 = fromDirConfig;
from3 += targetNameReal;
files.push_back(from3);
haveNamelink = true;
fromRealName = fromDirConfig;
fromRealName += targetNameReal;
}
// Add the names based on the current namelink mode.
if(haveNamelink)
{
// With a namelink we need to check the mode.
if(this->NamelinkMode == NamelinkModeOnly)
{
// Install the namelink only.
files.push_back(fromName);
tweakInstalledFile = false;
}
else
{
// Install the real file if it has its own name.
if(!fromRealName.empty())
{
files.push_back(fromRealName);
}
// Install the soname link if it has its own name.
if(!fromSOName.empty())
{
files.push_back(fromSOName);
}
// Install the namelink if it is not to be skipped.
if(this->NamelinkMode != NamelinkModeSkip)
{
files.push_back(fromName);
}
}
}
else
{
// Without a namelink there will be only one file. Install it
// if this is not a namelink-only rule.
if(this->NamelinkMode != NamelinkModeOnly)
{
files.push_back(fromName);
}
}
}
}
// Skip this rule if no files are to be installed for the target.
if(files.empty())
{
return;
}
// Write code to install the target file.
const char* no_dir_permissions = 0;
const char* no_rename = 0;
......@@ -273,19 +341,26 @@ cmInstallTargetGenerator
no_rename, literal_args.c_str(),
indent);
// Construct the path of the file on disk after installation on
// which tweaks may be performed.
std::string toDestDirPath = "$ENV{DESTDIR}";
if(toInstallPath[0] != '/')
if(toInstallPath[0] != '/' && toInstallPath[0] != '$')
{
toDestDirPath += "/";
}
toDestDirPath += toInstallPath;
os << indent << "IF(EXISTS \"" << toDestDirPath << "\")\n";
this->AddInstallNamePatchRule(os, indent.Next(), config, 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";
// TODO:
// - Skip IF(EXISTS) checks if nothing is done with the installed file
if(tweakInstalledFile)
{
os << indent << "IF(EXISTS \"" << toDestDirPath << "\")\n";
this->AddInstallNamePatchRule(os, indent.Next(), config, 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";
}
}
//----------------------------------------------------------------------------
......
......@@ -36,6 +36,16 @@ public:
);
virtual ~cmInstallTargetGenerator();
/** Select the policy for installing shared library linkable name
symlinks. */
enum NamelinkModeType
{
NamelinkModeNone,
NamelinkModeOnly,
NamelinkModeSkip
};
void SetNamelinkMode(NamelinkModeType mode) { this->NamelinkMode = mode; }
std::string GetInstallFilename(const char* config) const;
static std::string GetInstallFilename(cmTarget*target, const char* config,
bool implib, bool useSOName);
......@@ -72,6 +82,7 @@ protected:
bool ImportLibrary;
std::string FilePermissions;
bool Optional;
NamelinkModeType NamelinkMode;
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment