Commit 0212d7c7 authored by Kyle Edwards's avatar Kyle Edwards Committed by Brad King

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
parent cbb60907
......@@ -103,6 +103,7 @@ Installing Targets
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
] [...]
......@@ -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:
On some platforms a versioned shared library has a symbolic link such
......@@ -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
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.
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.
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
The ``install(TARGETS)`` command can also accept the following options at the
top level:
......@@ -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 =
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()) {
"TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. "
"The NAMELINK_COMPONENT option may be specified only following "
return false;
if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
"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);
if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
libraryGenerator =
CreateInstallTargetGenerator(target, libraryArgs, false);
if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
namelinkGenerator = CreateInstallTargetGenerator(
target, libraryArgs, false, false, true);
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)
......@@ -735,6 +763,10 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
if (installsNamelink) {
if (installsRuntime) {
......@@ -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()
......@@ -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;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment