Jelajahi Sumber

ENH: Added FILES_MATCHING option to INSTALL(DIRECTORY). This will help install a tree of header files while ignoring non-headers.

Brad King 18 tahun lalu
induk
melakukan
60bf0531b0

+ 32 - 4
Source/cmFileCommand.cxx

@@ -702,7 +702,7 @@ struct cmFileInstaller
 
   // All instances need the file command and makefile using them.
   cmFileInstaller(cmFileCommand* fc, cmMakefile* mf):
-    FileCommand(fc), Makefile(mf), DestDirLength(0)
+    FileCommand(fc), Makefile(mf), DestDirLength(0), MatchlessFiles(true)
     {
     // Get the current manifest.
     this->Manifest =
@@ -724,6 +724,9 @@ public:
   // The length of the destdir setting.
   int DestDirLength;
 
+  // Whether to install a file not matching any expression.
+  bool MatchlessFiles;
+
   // The current file manifest (semicolon separated list).
   std::string Manifest;
 
@@ -749,7 +752,8 @@ public:
   std::vector<MatchRule> MatchRules;
 
   // Get the properties from rules matching this input file.
-  MatchProperties CollectMatchProperties(const char* file)
+  MatchProperties CollectMatchProperties(const char* file,
+                                         bool isDirectory)
     {
     // Match rules are case-insensitive on some platforms.
 #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
@@ -758,16 +762,22 @@ public:
 #endif
 
     // Collect properties from all matching rules.
+    bool matched = false;
     MatchProperties result;
     for(std::vector<MatchRule>::iterator mr = this->MatchRules.begin();
         mr != this->MatchRules.end(); ++mr)
       {
       if(mr->Regex.find(file))
         {
+        matched = true;
         result.Exclude |= mr->Properties.Exclude;
         result.Permissions |= mr->Properties.Permissions;
         }
       }
+    if(!matched && !this->MatchlessFiles && !isDirectory)
+      {
+      result.Exclude = true;
+      }
     return result;
     }
 
@@ -868,7 +878,8 @@ bool cmFileInstaller::InstallFile(const char* fromFile, const char* toFile,
                                   bool always)
 {
   // Collect any properties matching this file name.
-  MatchProperties match_properties = this->CollectMatchProperties(fromFile);
+  MatchProperties match_properties =
+    this->CollectMatchProperties(fromFile, false);
 
   // Skip the file if it is excluded.
   if(match_properties.Exclude)
@@ -946,7 +957,8 @@ bool cmFileInstaller::InstallDirectory(const char* source,
                                        bool always)
 {
   // Collect any properties matching this directory name.
-  MatchProperties match_properties = this->CollectMatchProperties(source);
+  MatchProperties match_properties =
+    this->CollectMatchProperties(source, true);
 
   // Skip the directory if it is excluded.
   if(match_properties.Exclude)
@@ -1463,6 +1475,22 @@ bool cmFileCommand::ParseInstallArgs(std::vector<std::string> const& args,
         doing_permissions_dir = false;
         use_source_permissions = true;
         }
+      else if ( *cstr == "FILES_MATCHING" )
+        {
+        if(current_match_rule)
+          {
+          cmOStringStream e;
+          e << "INSTALL does not allow \"" << *cstr << "\" after REGEX.";
+          this->SetError(e.str().c_str());
+          return false;
+          }
+
+        doing_properties = false;
+        doing_files = false;
+        doing_permissions_file = false;
+        doing_permissions_dir = false;
+        installer.MatchlessFiles = false;
+        }
       else if ( *cstr == "COMPONENTS"  )
         {
         cmOStringStream e;

+ 23 - 0
Source/cmInstallCommand.cxx

@@ -871,6 +871,29 @@ cmInstallCommand::HandleDirectoryMode(std::vector<std::string> const& args)
       doing_component = false;
       literal_args += " USE_SOURCE_PERMISSIONS";
       }
+    else if(args[i] == "FILES_MATCHING")
+      {
+      if(in_match_mode)
+        {
+        cmOStringStream e;
+        e << args[0] << " does not allow \""
+          << args[i] << "\" after PATTERN or REGEX.";
+        this->SetError(e.str().c_str());
+        return false;
+        }
+
+      // Add this option literally.
+      doing_dirs = false;
+      doing_destination = false;
+      doing_pattern = false;
+      doing_regex = false;
+      doing_permissions_file = false;
+      doing_permissions_dir = false;
+      doing_permissions_match = false;
+      doing_configurations = false;
+      doing_component = false;
+      literal_args += " FILES_MATCHING";
+      }
     else if(args[i] == "CONFIGURATIONS")
       {
       if(in_match_mode)

+ 20 - 7
Source/cmInstallCommand.h

@@ -178,7 +178,7 @@ public:
       "          [DIRECTORY_PERMISSIONS permissions...]\n"
       "          [USE_SOURCE_PERMISSIONS]\n"
       "          [CONFIGURATIONS [Debug|Release|...]]\n"
-      "          [COMPONENT <component>]\n"
+      "          [COMPONENT <component>] [FILES_MATCHING]\n"
       "          [[PATTERN <pattern> | REGEX <regex>]\n"
       "           [EXCLUDE] [PERMISSIONS permissions...]] [...])\n"
       "The DIRECTORY form installs contents of one or more directories "
@@ -198,18 +198,31 @@ public:
       "If no permissions are specified files will be given the default "
       "permissions specified in the FILES form of the command, and the "
       "directories will be given the default permissions specified in the "
-      "PROGRAMS form of the command.  "
-      "The PATTERN and REGEX options specify a globbing pattern or regular "
-      "expression to match directories or files encountered during traversal "
-      "of an input directory.  The full path to an input file or directory "
+      "PROGRAMS form of the command.\n"
+
+      "Installation of directories may be controlled with fine granularity "
+      "using the PATTERN or REGEX options.  These \"match\" options specify a "
+      "globbing pattern or regular expression to match directories or files "
+      "encountered within input directories.  They may be used to apply "
+      "certain options (see below) to a subset of the files and directories "
+      "encountered.  "
+      "The full path to each input file or directory "
       "(with forward slashes) is matched against the expression.  "
       "A PATTERN will match only complete file names: the portion of the "
       "full path matching the pattern must occur at the end of the file name "
       "and be preceded by a slash.  "
       "A REGEX will match any portion of the full path but it may use "
       "'/' and '$' to simulate the PATTERN behavior.  "
-      "Options following one of these matching expressions "
-      "are applied only to files or directories matching them.  "
+      "By default all files and directories are installed whether "
+      "or not they are matched.  "
+      "The FILES_MATCHING option may be given before the first match option "
+      "to disable installation of files (but not directories) not matched by "
+      "any expression.  For example, the code\n"
+      "  install(DIRECTORY src/ DESTINATION include/myproj\n"
+      "          FILES_MATCHING PATTERN \"*.h\")\n"
+      "will extract and install header files from a source tree.\n"
+      "Some options may follow a PATTERN or REGEX expression and are "
+      "applied only to files or directories matching them.  "
       "The EXCLUDE option will skip the matched file or directory.  "
       "The PERMISSIONS option overrides the permissions setting for the "
       "matched file or directory.  "

+ 13 - 0
Tests/SimpleInstall/CMakeLists.txt

@@ -97,6 +97,12 @@ IF(STAGE2)
   IF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/CMakeLists.txt")
     MESSAGE(FATAL_ERROR "Directory installation installed CMakeLists.txt.")
   ENDIF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/CMakeLists.txt")
+  IF(NOT EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.h")
+    MESSAGE(FATAL_ERROR "Directory installation did not install alternate TSD.h")
+  ENDIF(NOT EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.h")
+  IF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.cxx")
+    MESSAGE(FATAL_ERROR "Directory installation installed alternate TSD.cxx")
+  ENDIF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.cxx")
 
   # Check that scripts properly installed.
   IF(WIN32 AND NOT CYGWIN)
@@ -248,6 +254,13 @@ ELSE(STAGE2)
     PATTERN "CVS" EXCLUDE
     REGEX "\\.txt$" EXCLUDE
     )
+  INSTALL(
+    DIRECTORY TestSubDir DESTINATION MyTest/share/alt
+    FILE_PERMISSIONS OWNER_READ OWNER_WRITE
+    DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+                          GROUP_READ GROUP_EXECUTE
+    FILES_MATCHING PATTERN "*.h"
+    )
 
   # Test empty directory installation.
   INSTALL(DIRECTORY DESTINATION MyTest/share/empty)

+ 13 - 0
Tests/SimpleInstallS2/CMakeLists.txt

@@ -97,6 +97,12 @@ IF(STAGE2)
   IF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/CMakeLists.txt")
     MESSAGE(FATAL_ERROR "Directory installation installed CMakeLists.txt.")
   ENDIF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/CMakeLists.txt")
+  IF(NOT EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.h")
+    MESSAGE(FATAL_ERROR "Directory installation did not install alternate TSD.h")
+  ENDIF(NOT EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.h")
+  IF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.cxx")
+    MESSAGE(FATAL_ERROR "Directory installation installed alternate TSD.cxx")
+  ENDIF(EXISTS "${CMAKE_INSTALL_PREFIX}/MyTest/share/alt/TestSubDir/TSD.cxx")
 
   # Check that scripts properly installed.
   IF(WIN32 AND NOT CYGWIN)
@@ -248,6 +254,13 @@ ELSE(STAGE2)
     PATTERN "CVS" EXCLUDE
     REGEX "\\.txt$" EXCLUDE
     )
+  INSTALL(
+    DIRECTORY TestSubDir DESTINATION MyTest/share/alt
+    FILE_PERMISSIONS OWNER_READ OWNER_WRITE
+    DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+                          GROUP_READ GROUP_EXECUTE
+    FILES_MATCHING PATTERN "*.h"
+    )
 
   # Test empty directory installation.
   INSTALL(DIRECTORY DESTINATION MyTest/share/empty)