install: add NAMELINK_COMPONENT argument

For shared libraries, this allows you to specify separate components
for the shared library and for the namelink.

Suggested in https://cmake.org/pipermail/cmake-developers/2014-December/024032.html.
This commit is contained in:
Kyle Edwards 2018-04-18 14:53:10 -04:00 committed by Brad King
parent cbb609072f
commit 0212d7c762
4 changed files with 113 additions and 15 deletions

View File

@ -103,6 +103,7 @@ Installing Targets
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
@ -167,7 +168,7 @@ just a DLL or just an import library.)
In addition to the common options listed above, each target can accept
the following additional arguments:
``NAMELINK_ONLY``
``NAMELINK_COMPONENT``
On some platforms a versioned shared library has a symbolic link such
as::
@ -175,13 +176,51 @@ the following additional arguments:
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 the installation of only
the namelink when a library target is installed. On platforms where
versioned shared libraries do not have namelinks or when a library is not
versioned, the ``NAMELINK_ONLY`` option installs nothing. It is an error to
use this parameter outside of a ``LIBRARY`` block. See the
:prop_tgt:`VERSION` and :prop_tgt:`SOVERSION` target properties for details
on creating versioned shared libraries.
``-l<name>``. The ``NAMELINK_COMPONENT`` option is similar to the
``COMPONENT`` option, but it changes the installation component of a shared
library namelink if one is generated. If not specified, this defaults to the
value of ``COMPONENT``. It is an error to use this parameter outside of a
``LIBRARY`` block.
Consider the following example:
.. code-block:: cmake
install(TARGETS mylib
LIBRARY
DESTINATION lib
COMPONENT Libraries
NAMELINK_COMPONENT Development
PUBLIC_HEADER
DESTINATION include
COMPONENT Development
)
In this scenario, if you choose to install only the ``Development``
component, both the headers and namelink will be installed without the
library. (If you don't also install the ``Libraries`` component, the
namelink will be a dangling symlink, and projects that link to the library
will have build errors.) If you install only the ``Libraries`` component,
only the library will be installed, without the headers and namelink.
This option is typically used for package managers that have separate
runtime and development packages. For example, on Debian systems, the
library is expected to be in the runtime package, and the headers and
namelink are expected to be in the development package.
See the :prop_tgt:`VERSION` and :prop_tgt:`SOVERSION` target properties for
details on creating versioned shared libraries.
``NAMELINK_ONLY``
This option causes the installation of only the namelink when a library
target is installed. On platforms where versioned shared libraries do not
have namelinks or when a library is not versioned, the ``NAMELINK_ONLY``
option installs nothing. It is an error to use this parameter outside of a
``LIBRARY`` block.
When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or
``COMPONENT`` may be used to specify the installation component of the
namelink, but ``COMPONENT`` should generally be preferred.
``NAMELINK_SKIP``
Similar to ``NAMELINK_ONLY``, but it has the opposite effect: it causes the
@ -192,6 +231,10 @@ the following additional arguments:
installs the library. It is an error to use this parameter outside of a
``LIBRARY`` block.
If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It
is not recommended to use ``NAMELINK_SKIP`` in conjunction with
``NAMELINK_COMPONENT``.
The ``install(TARGETS)`` command can also accept the following options at the
top level:

View File

@ -33,16 +33,17 @@ class cmExecutionStatus;
static cmInstallTargetGenerator* CreateInstallTargetGenerator(
cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
bool forceOpt = false)
bool forceOpt = false, bool namelink = false)
{
cmInstallGenerator::MessageLevel message =
cmInstallGenerator::SelectMessageLevel(target.GetMakefile());
target.SetHaveInstallRule(true);
const char* component = namelink ? args.GetNamelinkComponent().c_str()
: args.GetComponent().c_str();
return new cmInstallTargetGenerator(
target.GetName(), args.GetDestination().c_str(), impLib,
args.GetPermissions().c_str(), args.GetConfigurations(),
args.GetComponent().c_str(), message, args.GetExcludeFromAll(),
args.GetOptional() || forceOpt);
args.GetPermissions().c_str(), args.GetConfigurations(), component,
message, args.GetExcludeFromAll(), args.GetOptional() || forceOpt);
}
static cmInstallFilesGenerator* CreateInstallFilesGenerator(
@ -313,6 +314,20 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
"The NAMELINK_SKIP option may be specified only following LIBRARY.");
return false;
}
if (archiveArgs.HasNamelinkComponent() ||
runtimeArgs.HasNamelinkComponent() ||
objectArgs.HasNamelinkComponent() ||
frameworkArgs.HasNamelinkComponent() ||
bundleArgs.HasNamelinkComponent() ||
privateHeaderArgs.HasNamelinkComponent() ||
publicHeaderArgs.HasNamelinkComponent() ||
resourceArgs.HasNamelinkComponent()) {
this->SetError(
"TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. "
"The NAMELINK_COMPONENT 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.");
@ -377,6 +392,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
// any files of the given type.
bool installsArchive = false;
bool installsLibrary = false;
bool installsNamelink = false;
bool installsRuntime = false;
bool installsObject = false;
bool installsFramework = false;
@ -391,6 +407,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
cmTarget& target = *ti;
cmInstallTargetGenerator* archiveGenerator = nullptr;
cmInstallTargetGenerator* libraryGenerator = nullptr;
cmInstallTargetGenerator* namelinkGenerator = nullptr;
cmInstallTargetGenerator* runtimeGenerator = nullptr;
cmInstallTargetGenerator* objectGenerator = nullptr;
cmInstallTargetGenerator* frameworkGenerator = nullptr;
@ -453,9 +470,18 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
} else {
// The shared library uses the LIBRARY properties.
if (!libraryArgs.GetDestination().empty()) {
libraryGenerator =
CreateInstallTargetGenerator(target, libraryArgs, false);
libraryGenerator->SetNamelinkMode(namelinkMode);
if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
libraryGenerator =
CreateInstallTargetGenerator(target, libraryArgs, false);
libraryGenerator->SetNamelinkMode(
cmInstallTargetGenerator::NamelinkModeSkip);
}
if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
namelinkGenerator = CreateInstallTargetGenerator(
target, libraryArgs, false, false, true);
namelinkGenerator->SetNamelinkMode(
cmInstallTargetGenerator::NamelinkModeOnly);
}
namelinkOnly =
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
} else {
@ -684,6 +710,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
// Keep track of whether we're installing anything in each category
installsArchive = installsArchive || archiveGenerator != nullptr;
installsLibrary = installsLibrary || libraryGenerator != nullptr;
installsNamelink = installsNamelink || namelinkGenerator != nullptr;
installsRuntime = installsRuntime || runtimeGenerator != nullptr;
installsObject = installsObject || objectGenerator != nullptr;
installsFramework = installsFramework || frameworkGenerator != nullptr;
@ -696,6 +723,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
this->Makefile->AddInstallGenerator(archiveGenerator);
this->Makefile->AddInstallGenerator(libraryGenerator);
this->Makefile->AddInstallGenerator(namelinkGenerator);
this->Makefile->AddInstallGenerator(runtimeGenerator);
this->Makefile->AddInstallGenerator(objectGenerator);
this->Makefile->AddInstallGenerator(frameworkGenerator);
@ -735,6 +763,10 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
this->Makefile->GetGlobalGenerator()->AddInstallComponent(
libraryArgs.GetComponent().c_str());
}
if (installsNamelink) {
this->Makefile->GetGlobalGenerator()->AddInstallComponent(
libraryArgs.GetNamelinkComponent().c_str());
}
if (installsRuntime) {
this->Makefile->GetGlobalGenerator()->AddInstallComponent(
runtimeArgs.GetComponent().c_str());

View File

@ -21,6 +21,7 @@ cmInstallCommandArguments::cmInstallCommandArguments(
, ArgumentGroup()
, Destination(&Parser, "DESTINATION", &ArgumentGroup)
, Component(&Parser, "COMPONENT", &ArgumentGroup)
, NamelinkComponent(&Parser, "NAMELINK_COMPONENT", &ArgumentGroup)
, ExcludeFromAll(&Parser, "EXCLUDE_FROM_ALL", &ArgumentGroup)
, Rename(&Parser, "RENAME", &ArgumentGroup)
, Permissions(&Parser, "PERMISSIONS", &ArgumentGroup)
@ -59,6 +60,14 @@ const std::string& cmInstallCommandArguments::GetComponent() const
return unspecifiedComponent;
}
const std::string& cmInstallCommandArguments::GetNamelinkComponent() const
{
if (!this->NamelinkComponent.GetString().empty()) {
return this->NamelinkComponent.GetString();
}
return this->GetComponent();
}
const std::string& cmInstallCommandArguments::GetRename() const
{
if (!this->Rename.GetString().empty()) {
@ -125,6 +134,17 @@ bool cmInstallCommandArguments::GetNamelinkSkip() const
return false;
}
bool cmInstallCommandArguments::HasNamelinkComponent() const
{
if (!this->NamelinkComponent.GetString().empty()) {
return true;
}
if (this->GenericArguments != nullptr) {
return this->GenericArguments->HasNamelinkComponent();
}
return false;
}
const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations()
const
{

View File

@ -26,6 +26,7 @@ public:
const std::string& GetDestination() const;
const std::string& GetComponent() const;
const std::string& GetNamelinkComponent() const;
bool GetExcludeFromAll() const;
const std::string& GetRename() const;
const std::string& GetPermissions() const;
@ -33,6 +34,7 @@ public:
bool GetOptional() const;
bool GetNamelinkOnly() const;
bool GetNamelinkSkip() const;
bool HasNamelinkComponent() const;
// once HandleDirectoryMode() is also switched to using
// cmInstallCommandArguments then these two functions can become non-static
@ -45,6 +47,7 @@ private:
cmInstallCommandArguments(); // disabled
cmCAString Destination;
cmCAString Component;
cmCAString NamelinkComponent;
cmCAEnabler ExcludeFromAll;
cmCAString Rename;
cmCAStringVector Permissions;