فهرست منبع

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
Brad King 17 سال پیش
والد
کامیت
8401c5ba06

+ 65 - 0
Source/cmInstallCommand.cxx

@@ -283,6 +283,57 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
     return false;
     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.
   // Check if there is something to do.
   if(targetList.GetVector().empty())
   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.
         // cygwin.  Currently no other platform is a DLL platform.
         if(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.
           // This is a DLL platform.
           if(!archiveArgs.GetDestination().empty())
           if(!archiveArgs.GetDestination().empty())
             {
             {
@@ -378,6 +435,12 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
           // INSTALL properties. Otherwise, use the LIBRARY properties.
           // INSTALL properties. Otherwise, use the LIBRARY properties.
           if(target.IsFrameworkOnApple())
           if(target.IsFrameworkOnApple())
             {
             {
+            // When in namelink only mode skip frameworks.
+            if(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly)
+              {
+              continue;
+              }
+
             // Use the FRAMEWORK properties.
             // Use the FRAMEWORK properties.
             if (!frameworkArgs.GetDestination().empty())
             if (!frameworkArgs.GetDestination().empty())
               {
               {
@@ -400,6 +463,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
               {
               {
               libraryGenerator = CreateInstallTargetGenerator(target, 
               libraryGenerator = CreateInstallTargetGenerator(target, 
                                                            libraryArgs, false);
                                                            libraryArgs, false);
+              libraryGenerator->SetNamelinkMode(namelinkMode);
               }
               }
             else
             else
               {
               {
@@ -438,6 +502,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
           {
           {
           libraryGenerator = CreateInstallTargetGenerator(target, libraryArgs, 
           libraryGenerator = CreateInstallTargetGenerator(target, libraryArgs, 
                                                           false);
                                                           false);
+          libraryGenerator->SetNamelinkMode(namelinkMode);
           }
           }
         else
         else
           {
           {

+ 20 - 1
Source/cmInstallCommand.h

@@ -105,7 +105,7 @@ public:
       "           [PERMISSIONS permissions...]\n"
       "           [PERMISSIONS permissions...]\n"
       "           [CONFIGURATIONS [Debug|Release|...]]\n"
       "           [CONFIGURATIONS [Debug|Release|...]]\n"
       "           [COMPONENT <component>]\n"
       "           [COMPONENT <component>]\n"
-      "           [OPTIONAL]\n"
+      "           [OPTIONAL] [NAMELINK_ONLY|NAMELINK_SKIP]\n"
       "          ] [...])\n"
       "          ] [...])\n"
       "The TARGETS form specifies rules for installing targets from a "
       "The TARGETS form specifies rules for installing targets from a "
       "project.  There are five kinds of target files that may be "
       "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 "
       "See documentation of the PRIVATE_HEADER, PUBLIC_HEADER, and RESOURCE "
       "target properties for details."
       "target properties for details."
       "\n"
       "\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 "
       "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 "
       "to the TARGETS form of this command.  A target may be installed more "
       "than once to different locations.  Consider hypothetical "
       "than once to different locations.  Consider hypothetical "

+ 28 - 0
Source/cmInstallCommandArguments.cxx

@@ -37,6 +37,8 @@ cmInstallCommandArguments::cmInstallCommandArguments()
 ,Permissions   (&Parser, "PERMISSIONS"   , &ArgumentGroup)
 ,Permissions   (&Parser, "PERMISSIONS"   , &ArgumentGroup)
 ,Configurations(&Parser, "CONFIGURATIONS", &ArgumentGroup)
 ,Configurations(&Parser, "CONFIGURATIONS", &ArgumentGroup)
 ,Optional      (&Parser, "OPTIONAL"      , &ArgumentGroup)
 ,Optional      (&Parser, "OPTIONAL"      , &ArgumentGroup)
+,NamelinkOnly  (&Parser, "NAMELINK_ONLY" , &ArgumentGroup)
+,NamelinkSkip  (&Parser, "NAMELINK_SKIP" , &ArgumentGroup)
 ,GenericArguments(0)
 ,GenericArguments(0)
 {
 {
   this->Component.SetDefaultString("Unspecified");
   this->Component.SetDefaultString("Unspecified");
@@ -107,6 +109,32 @@ bool cmInstallCommandArguments::GetOptional() const
   return false;
   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>& 
 const std::vector<std::string>& 
     cmInstallCommandArguments::GetConfigurations() const
     cmInstallCommandArguments::GetConfigurations() const
 {
 {

+ 4 - 0
Source/cmInstallCommandArguments.h

@@ -39,6 +39,8 @@ class cmInstallCommandArguments
     const std::string& GetPermissions() const;
     const std::string& GetPermissions() const;
     const std::vector<std::string>& GetConfigurations() const;
     const std::vector<std::string>& GetConfigurations() const;
     bool GetOptional() const;
     bool GetOptional() const;
+    bool GetNamelinkOnly() const;
+    bool GetNamelinkSkip() 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
@@ -54,6 +56,8 @@ class cmInstallCommandArguments
     cmCAStringVector Permissions;
     cmCAStringVector Permissions;
     cmCAStringVector Configurations;
     cmCAStringVector Configurations;
     cmCAEnabler Optional;
     cmCAEnabler Optional;
+    cmCAEnabler NamelinkOnly;
+    cmCAEnabler NamelinkSkip;
 
 
     std::string DestinationString;
     std::string DestinationString;
     std::string PermissionsString;
     std::string PermissionsString;

+ 94 - 19
Source/cmInstallTargetGenerator.cxx

@@ -22,9 +22,6 @@
 #include "cmMakefile.h"
 #include "cmMakefile.h"
 #include "cmake.h"
 #include "cmake.h"
 
 
-// TODO:
-//   - Skip IF(EXISTS) checks if nothing is done with the installed file
-
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 cmInstallTargetGenerator
 cmInstallTargetGenerator
 ::cmInstallTargetGenerator(cmTarget& t, const char* dest, bool implib,
 ::cmInstallTargetGenerator(cmTarget& t, const char* dest, bool implib,
@@ -34,6 +31,7 @@ cmInstallTargetGenerator
   cmInstallGenerator(dest, configurations, component), Target(&t),
   cmInstallGenerator(dest, configurations, component), Target(&t),
   ImportLibrary(implib), FilePermissions(file_permissions), Optional(optional)
   ImportLibrary(implib), FilePermissions(file_permissions), Optional(optional)
 {
 {
+  this->NamelinkMode = NamelinkModeNone;
   this->Target->SetHaveInstallRule(true);
   this->Target->SetHaveInstallRule(true);
 }
 }
 
 
@@ -149,12 +147,19 @@ cmInstallTargetGenerator
   toInstallPath += this->GetInstallFilename(this->Target, config,
   toInstallPath += this->GetInstallFilename(this->Target, config,
                                               this->ImportLibrary, false);
                                               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.
   // Compute the list of files to install for this target.
   std::vector<std::string> files;
   std::vector<std::string> files;
   std::string literal_args;
   std::string literal_args;
   cmTarget::TargetType type = this->Target->GetType();
   cmTarget::TargetType type = this->Target->GetType();
   if(type == cmTarget::EXECUTABLE)
   if(type == cmTarget::EXECUTABLE)
     {
     {
+    // There is a bug in cmInstallCommand if this fails.
+    assert(this->NamelinkMode == NamelinkModeNone);
+
     std::string targetName;
     std::string targetName;
     std::string targetNameReal;
     std::string targetNameReal;
     std::string targetNameImport;
     std::string targetNameImport;
@@ -215,6 +220,9 @@ cmInstallTargetGenerator
                                   config);
                                   config);
     if(this->ImportLibrary)
     if(this->ImportLibrary)
       {
       {
+      // There is a bug in cmInstallCommand if this fails.
+      assert(this->NamelinkMode == NamelinkModeNone);
+
       std::string from1 = fromDirConfig;
       std::string from1 = fromDirConfig;
       from1 += targetNameImport;
       from1 += targetNameImport;
       files.push_back(from1);
       files.push_back(from1);
@@ -224,6 +232,9 @@ cmInstallTargetGenerator
       }
       }
     else if(this->Target->IsFrameworkOnApple())
     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
       // Compute the build tree location of the framework directory
       std::string from1 = fromDirConfig;
       std::string from1 = fromDirConfig;
       // Remove trailing slashes... so that from1 ends with ".framework":
       // Remove trailing slashes... so that from1 ends with ".framework":
@@ -243,25 +254,82 @@ cmInstallTargetGenerator
       }
       }
     else
     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)
       if(targetNameSO != targetName)
         {
         {
-        std::string from2 = fromDirConfig;
-        from2 += targetNameSO;
-        files.push_back(from2);
+        haveNamelink = true;
+        fromSOName = fromDirConfig;
+        fromSOName += targetNameSO;
         }
         }
       if(targetNameReal != targetName &&
       if(targetNameReal != targetName &&
          targetNameReal != targetNameSO)
          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.
   // Write code to install the target file.
   const char* no_dir_permissions = 0;
   const char* no_dir_permissions = 0;
   const char* no_rename = 0;
   const char* no_rename = 0;
@@ -273,19 +341,26 @@ cmInstallTargetGenerator
                        no_rename, literal_args.c_str(),
                        no_rename, literal_args.c_str(),
                        indent);
                        indent);
 
 
+  // Construct the path of the file on disk after installation on
+  // which tweaks may be performed.
   std::string toDestDirPath = "$ENV{DESTDIR}";
   std::string toDestDirPath = "$ENV{DESTDIR}";
-  if(toInstallPath[0] != '/')
+  if(toInstallPath[0] != '/' && toInstallPath[0] != '$')
     {
     {
     toDestDirPath += "/";
     toDestDirPath += "/";
     }
     }
   toDestDirPath += toInstallPath;
   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";
+    }
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------

+ 11 - 0
Source/cmInstallTargetGenerator.h

@@ -36,6 +36,16 @@ public:
     );
     );
   virtual ~cmInstallTargetGenerator();
   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;
   std::string GetInstallFilename(const char* config) const;
   static std::string GetInstallFilename(cmTarget*target, const char* config, 
   static std::string GetInstallFilename(cmTarget*target, const char* config, 
                                         bool implib, bool useSOName);
                                         bool implib, bool useSOName);
@@ -72,6 +82,7 @@ protected:
   bool ImportLibrary;
   bool ImportLibrary;
   std::string FilePermissions;
   std::string FilePermissions;
   bool Optional;
   bool Optional;
+  NamelinkModeType NamelinkMode;
 };
 };
 
 
 #endif
 #endif