From 60bf0531b01b641a00f6844dfd38ef803d2deda1 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 2 Jan 2008 15:17:56 -0500 Subject: ENH: Added FILES_MATCHING option to INSTALL(DIRECTORY). This will help install a tree of header files while ignoring non-headers. --- Source/cmFileCommand.cxx | 36 ++++++++++++++++++++++++++++++++---- Source/cmInstallCommand.cxx | 23 +++++++++++++++++++++++ Source/cmInstallCommand.h | 27 ++++++++++++++++++++------- Tests/SimpleInstall/CMakeLists.txt | 13 +++++++++++++ Tests/SimpleInstallS2/CMakeLists.txt | 13 +++++++++++++ 5 files changed, 101 insertions(+), 11 deletions(-) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 3a31a3f..092dfb1 100644 --- a/Source/cmFileCommand.cxx +++ b/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 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::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 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; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index a2e3764..c04a682 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -871,6 +871,29 @@ cmInstallCommand::HandleDirectoryMode(std::vector 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) diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h index d7b3f64..15332ae 100644 --- a/Source/cmInstallCommand.h +++ b/Source/cmInstallCommand.h @@ -178,7 +178,7 @@ public: " [DIRECTORY_PERMISSIONS permissions...]\n" " [USE_SOURCE_PERMISSIONS]\n" " [CONFIGURATIONS [Debug|Release|...]]\n" - " [COMPONENT ]\n" + " [COMPONENT ] [FILES_MATCHING]\n" " [[PATTERN | 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. " diff --git a/Tests/SimpleInstall/CMakeLists.txt b/Tests/SimpleInstall/CMakeLists.txt index 2f181ef..25b54ab 100644 --- a/Tests/SimpleInstall/CMakeLists.txt +++ b/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) diff --git a/Tests/SimpleInstallS2/CMakeLists.txt b/Tests/SimpleInstallS2/CMakeLists.txt index 2f181ef..25b54ab 100644 --- a/Tests/SimpleInstallS2/CMakeLists.txt +++ b/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) -- cgit v0.12