Browse Source

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.
Kyle Edwards 7 years ago
parent
commit
0212d7c762

+ 51 - 8
Help/command/install.rst

@@ -103,6 +103,7 @@ Installing Targets
            [PERMISSIONS permissions...]
            [PERMISSIONS permissions...]
            [CONFIGURATIONS [Debug|Release|...]]
            [CONFIGURATIONS [Debug|Release|...]]
            [COMPONENT <component>]
            [COMPONENT <component>]
+           [NAMELINK_COMPONENT <component>]
            [OPTIONAL] [EXCLUDE_FROM_ALL]
            [OPTIONAL] [EXCLUDE_FROM_ALL]
            [NAMELINK_ONLY|NAMELINK_SKIP]
            [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
 In addition to the common options listed above, each target can accept
 the following additional arguments:
 the following additional arguments:
 
 
-``NAMELINK_ONLY``
+``NAMELINK_COMPONENT``
   On some platforms a versioned shared library has a symbolic link such
   On some platforms a versioned shared library has a symbolic link such
   as::
   as::
 
 
@@ -175,13 +176,51 @@ the following additional arguments:
 
 
   where ``lib<name>.so.1`` is the soname of the library and ``lib<name>.so``
   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
   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``
 ``NAMELINK_SKIP``
   Similar to ``NAMELINK_ONLY``, but it has the opposite effect: it causes the
   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
   installs the library. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
   ``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
 The ``install(TARGETS)`` command can also accept the following options at the
 top level:
 top level:
 
 

+ 39 - 7
Source/cmInstallCommand.cxx

@@ -33,16 +33,17 @@ class cmExecutionStatus;
 
 
 static cmInstallTargetGenerator* CreateInstallTargetGenerator(
 static cmInstallTargetGenerator* CreateInstallTargetGenerator(
   cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
   cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
-  bool forceOpt = false)
+  bool forceOpt = false, bool namelink = false)
 {
 {
   cmInstallGenerator::MessageLevel message =
   cmInstallGenerator::MessageLevel message =
     cmInstallGenerator::SelectMessageLevel(target.GetMakefile());
     cmInstallGenerator::SelectMessageLevel(target.GetMakefile());
   target.SetHaveInstallRule(true);
   target.SetHaveInstallRule(true);
+  const char* component = namelink ? args.GetNamelinkComponent().c_str()
+                                   : args.GetComponent().c_str();
   return new cmInstallTargetGenerator(
   return new cmInstallTargetGenerator(
     target.GetName(), args.GetDestination().c_str(), impLib,
     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(
 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.");
       "The NAMELINK_SKIP option may be specified only following LIBRARY.");
     return false;
     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()) {
   if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
     this->SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP.  "
     this->SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP.  "
                    "At most one of these two options may be specified.");
                    "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.
   // any files of the given type.
   bool installsArchive = false;
   bool installsArchive = false;
   bool installsLibrary = false;
   bool installsLibrary = false;
+  bool installsNamelink = false;
   bool installsRuntime = false;
   bool installsRuntime = false;
   bool installsObject = false;
   bool installsObject = false;
   bool installsFramework = false;
   bool installsFramework = false;
@@ -391,6 +407,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
     cmTarget& target = *ti;
     cmTarget& target = *ti;
     cmInstallTargetGenerator* archiveGenerator = nullptr;
     cmInstallTargetGenerator* archiveGenerator = nullptr;
     cmInstallTargetGenerator* libraryGenerator = nullptr;
     cmInstallTargetGenerator* libraryGenerator = nullptr;
+    cmInstallTargetGenerator* namelinkGenerator = nullptr;
     cmInstallTargetGenerator* runtimeGenerator = nullptr;
     cmInstallTargetGenerator* runtimeGenerator = nullptr;
     cmInstallTargetGenerator* objectGenerator = nullptr;
     cmInstallTargetGenerator* objectGenerator = nullptr;
     cmInstallTargetGenerator* frameworkGenerator = nullptr;
     cmInstallTargetGenerator* frameworkGenerator = nullptr;
@@ -453,9 +470,18 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
           } else {
           } else {
             // The shared library uses the LIBRARY properties.
             // The shared library uses the LIBRARY properties.
             if (!libraryArgs.GetDestination().empty()) {
             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 =
               namelinkOnly =
                 (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
                 (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
             } else {
             } 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
     // Keep track of whether we're installing anything in each category
     installsArchive = installsArchive || archiveGenerator != nullptr;
     installsArchive = installsArchive || archiveGenerator != nullptr;
     installsLibrary = installsLibrary || libraryGenerator != nullptr;
     installsLibrary = installsLibrary || libraryGenerator != nullptr;
+    installsNamelink = installsNamelink || namelinkGenerator != nullptr;
     installsRuntime = installsRuntime || runtimeGenerator != nullptr;
     installsRuntime = installsRuntime || runtimeGenerator != nullptr;
     installsObject = installsObject || objectGenerator != nullptr;
     installsObject = installsObject || objectGenerator != nullptr;
     installsFramework = installsFramework || frameworkGenerator != 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(archiveGenerator);
     this->Makefile->AddInstallGenerator(libraryGenerator);
     this->Makefile->AddInstallGenerator(libraryGenerator);
+    this->Makefile->AddInstallGenerator(namelinkGenerator);
     this->Makefile->AddInstallGenerator(runtimeGenerator);
     this->Makefile->AddInstallGenerator(runtimeGenerator);
     this->Makefile->AddInstallGenerator(objectGenerator);
     this->Makefile->AddInstallGenerator(objectGenerator);
     this->Makefile->AddInstallGenerator(frameworkGenerator);
     this->Makefile->AddInstallGenerator(frameworkGenerator);
@@ -735,6 +763,10 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
     this->Makefile->GetGlobalGenerator()->AddInstallComponent(
     this->Makefile->GetGlobalGenerator()->AddInstallComponent(
       libraryArgs.GetComponent().c_str());
       libraryArgs.GetComponent().c_str());
   }
   }
+  if (installsNamelink) {
+    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+      libraryArgs.GetNamelinkComponent().c_str());
+  }
   if (installsRuntime) {
   if (installsRuntime) {
     this->Makefile->GetGlobalGenerator()->AddInstallComponent(
     this->Makefile->GetGlobalGenerator()->AddInstallComponent(
       runtimeArgs.GetComponent().c_str());
       runtimeArgs.GetComponent().c_str());

+ 20 - 0
Source/cmInstallCommandArguments.cxx

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

+ 3 - 0
Source/cmInstallCommandArguments.h

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