diff options
-rw-r--r-- | Source/cmFileCommand.cxx | 122 | ||||
-rw-r--r-- | Source/cmFileCommand.h | 38 | ||||
-rw-r--r-- | Tests/StringFileTest/CMakeLists.txt | 11 |
3 files changed, 155 insertions, 16 deletions
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index c7348b7..cceea97 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -112,6 +112,10 @@ bool cmFileCommand { return this->HandleRemove(args, true); } + else if ( subCommand == "COPY" ) + { + return this->HandleCopyCommand(args); + } else if ( subCommand == "INSTALL" ) { return this->HandleInstallCommand(args); @@ -903,9 +907,10 @@ cmFileCommand::HandleDifferentCommand(std::vector<std::string> const& args) // File installation helper class. struct cmFileCopier { - cmFileCopier(cmFileCommand* command): + cmFileCopier(cmFileCommand* command, const char* name = "COPY"): FileCommand(command), Makefile(command->GetMakefile()), + Name(name), Always(false), MatchlessFiles(true), FilePermissions(0), @@ -913,7 +918,7 @@ struct cmFileCopier CurrentMatchRule(0), UseGivenPermissionsFile(false), UseGivenPermissionsDir(false), - UseSourcePermissions(false), + UseSourcePermissions(true), Doing(DoingNone) { } @@ -923,6 +928,7 @@ protected: cmFileCommand* FileCommand; cmMakefile* Makefile; + const char* Name; bool Always; cmFileTimeComparison FileTimes; @@ -984,7 +990,7 @@ protected: if(permissions && !cmSystemTools::SetPermissions(toFile, permissions)) { cmOStringStream e; - e << "INSTALL cannot set permissions on \"" << toFile << "\""; + e << this->Name << " cannot set permissions on \"" << toFile << "\""; this->FileCommand->SetError(e.str().c_str()); return false; } @@ -1008,7 +1014,7 @@ protected: else { cmOStringStream e; - e << "INSTALL given invalid permission \"" << arg << "\"."; + e << this->Name << " given invalid permission \"" << arg << "\"."; this->FileCommand->SetError(e.str().c_str()); return false; } @@ -1035,7 +1041,7 @@ protected: { // The input file does not exist and installation is not optional. cmOStringStream e; - e << "INSTALL cannot find \"" << fromFile << "\"."; + e << this->Name << " cannot find \"" << fromFile << "\"."; this->FileCommand->SetError(e.str().c_str()); return false; } @@ -1055,6 +1061,7 @@ protected: DoingError, DoingDestination, DoingFiles, + DoingPattern, DoingRegex, DoingPermissionsFile, DoingPermissionsDir, @@ -1067,14 +1074,14 @@ protected: void NotBeforeMatch(std::string const& arg) { cmOStringStream e; - e << "option " << arg << " may not appear before REGEX."; + e << "option " << arg << " may not appear before PATTERN or REGEX."; this->FileCommand->SetError(e.str().c_str()); this->Doing = DoingError; } void NotAfterMatch(std::string const& arg) { cmOStringStream e; - e << "option " << arg << " may not appear after REGEX."; + e << "option " << arg << " may not appear after PATTERN or REGEX."; this->FileCommand->SetError(e.str().c_str()); this->Doing = DoingError; } @@ -1128,7 +1135,7 @@ bool cmFileCopier::Parse(std::vector<std::string> const& args) if(this->Destination.empty()) { cmOStringStream e; - e << "INSTALL given no DESTINATION"; + e << this->Name << " given no DESTINATION"; this->FileCommand->SetError(e.str().c_str()); return false; } @@ -1162,6 +1169,10 @@ bool cmFileCopier::CheckKeyword(std::string const& arg) this->Doing = DoingDestination; } } + else if(arg == "PATTERN") + { + this->Doing = DoingPattern; + } else if(arg == "REGEX") { this->Doing = DoingRegex; @@ -1179,6 +1190,41 @@ bool cmFileCopier::CheckKeyword(std::string const& arg) this->NotBeforeMatch(arg); } } + else if(arg == "PERMISSIONS") + { + if(this->CurrentMatchRule) + { + this->Doing = DoingPermissionsMatch; + } + else + { + this->NotBeforeMatch(arg); + } + } + else if(arg == "FILE_PERMISSIONS") + { + if(this->CurrentMatchRule) + { + this->NotAfterMatch(arg); + } + else + { + this->Doing = DoingPermissionsFile; + this->UseGivenPermissionsFile = true; + } + } + else if(arg == "DIRECTORY_PERMISSIONS") + { + if(this->CurrentMatchRule) + { + this->NotAfterMatch(arg); + } + else + { + this->Doing = DoingPermissionsDir; + this->UseGivenPermissionsDir = true; + } + } else if(arg == "USE_SOURCE_PERMISSIONS") { if(this->CurrentMatchRule) @@ -1191,6 +1237,18 @@ bool cmFileCopier::CheckKeyword(std::string const& arg) this->UseSourcePermissions = true; } } + else if(arg == "NO_SOURCE_PERMISSIONS") + { + if(this->CurrentMatchRule) + { + this->NotAfterMatch(arg); + } + else + { + this->Doing = DoingNone; + this->UseSourcePermissions = false; + } + } else if(arg == "FILES_MATCHING") { if(this->CurrentMatchRule) @@ -1239,6 +1297,30 @@ bool cmFileCopier::CheckValue(std::string const& arg) } this->Doing = DoingNone; break; + case DoingPattern: + { + // Convert the pattern to a regular expression. Require a + // leading slash and trailing end-of-string in the matched + // string to make sure the pattern matches only whole file + // names. + std::string regex = "/"; + regex += cmsys::Glob::PatternToRegex(arg, false); + regex += "$"; + this->MatchRules.push_back(MatchRule(regex)); + this->CurrentMatchRule = &*(this->MatchRules.end()-1); + if(this->CurrentMatchRule->Regex.is_valid()) + { + this->Doing = DoingNone; + } + else + { + cmOStringStream e; + e << "could not compile PATTERN \"" << arg << "\"."; + this->FileCommand->SetError(e.str().c_str()); + this->Doing = DoingError; + } + } + break; case DoingRegex: this->MatchRules.push_back(MatchRule(arg)); this->CurrentMatchRule = &*(this->MatchRules.end()-1); @@ -1370,7 +1452,7 @@ bool cmFileCopier::InstallSymlink(const char* fromFile, const char* toFile) if(!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) { cmOStringStream e; - e << "INSTALL cannot read symlink \"" << fromFile + e << this->Name << " cannot read symlink \"" << fromFile << "\" to duplicate at \"" << toFile << "\"."; this->FileCommand->SetError(e.str().c_str()); return false; @@ -1403,7 +1485,7 @@ bool cmFileCopier::InstallSymlink(const char* fromFile, const char* toFile) if(!cmSystemTools::CreateSymlink(symlinkTarget.c_str(), toFile)) { cmOStringStream e; - e << "INSTALL cannot duplicate symlink \"" << fromFile + e << this->Name << " cannot duplicate symlink \"" << fromFile << "\" at \"" << toFile << "\"."; this->FileCommand->SetError(e.str().c_str()); return false; @@ -1435,7 +1517,7 @@ bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile, if(copy && !cmSystemTools::CopyAFile(fromFile, toFile, true, false)) { cmOStringStream e; - e << "INSTALL cannot copy file \"" << fromFile + e << this->Name << " cannot copy file \"" << fromFile << "\" to \"" << toFile << "\"."; this->FileCommand->SetError(e.str().c_str()); return false; @@ -1447,7 +1529,8 @@ bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile, if (!cmSystemTools::CopyFileTime(fromFile, toFile)) { cmOStringStream e; - e << "INSTALL cannot set modification time on \"" << toFile << "\""; + e << this->Name << " cannot set modification time on \"" + << toFile << "\""; this->FileCommand->SetError(e.str().c_str()); return false; } @@ -1477,7 +1560,7 @@ bool cmFileCopier::InstallDirectory(const char* source, if(!cmSystemTools::MakeDirectory(destination)) { cmOStringStream e; - e << "INSTALL cannot make directory \"" << destination << "\": " + e << this->Name << " cannot make directory \"" << destination << "\": " << cmSystemTools::GetLastSystemError(); this->FileCommand->SetError(e.str().c_str()); return false; @@ -1549,14 +1632,23 @@ bool cmFileCopier::InstallDirectory(const char* source, } //---------------------------------------------------------------------------- +bool cmFileCommand::HandleCopyCommand(std::vector<std::string> const& args) +{ + cmFileCopier copier(this); + return copier.Run(args); +} + +//---------------------------------------------------------------------------- struct cmFileInstaller: public cmFileCopier { cmFileInstaller(cmFileCommand* command): - cmFileCopier(command), + cmFileCopier(command, "INSTALL"), InstallType(cmTarget::INSTALL_FILES), Optional(false), DestDirLength(0) { + // Installation does not use source permissions by default. + this->UseSourcePermissions = false; // Check whether to copy files always or only if they have changed. this->Always = cmSystemTools::IsOn(cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS")); @@ -1736,6 +1828,7 @@ bool cmFileInstaller::CheckKeyword(std::string const& arg) } else { + // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS this->Doing = DoingPermissionsFile; this->UseGivenPermissionsFile = true; } @@ -1748,6 +1841,7 @@ bool cmFileInstaller::CheckKeyword(std::string const& arg) } else { + // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS this->Doing = DoingPermissionsDir; this->UseGivenPermissionsDir = true; } diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index 04bb661..cadb1a9 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -157,7 +157,42 @@ public: "and the second element is a string value for the error. A 0 " "numeric error means no error in the operation. " "If TIMEOUT time is specified, the operation will " - "timeout after time seconds, time can be specified as a float.\n"; + "timeout after time seconds, time can be specified as a float." + "\n" + "The file() command also provides COPY and INSTALL signatures:\n" + " file(<COPY|INSTALL> files... DESTINATION <dir>\n" + " [FILE_PERMISSIONS permissions...]\n" + " [DIRECTORY_PERMISSIONS permissions...]\n" + " [NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]\n" + " [FILES_MATCHING]\n" + " [[PATTERN <pattern> | REGEX <regex>]\n" + " [EXCLUDE] [PERMISSIONS permissions...]] [...])\n" + "The COPY signature copies files, directories, and symlinks to a " + "destination folder. " + "Relative input paths are evaluated with respect to the current " + "source directory, and a relative destination is evaluated with " + "respect to the current build directory. " + "Copying preserves input file timestamps, and optimizes out a file " + "if it exists at the destination with the same timestamp. " + "Copying preserves input permissions unless explicit permissions or " + "NO_SOURCE_PERMISSIONS are given (default is USE_SOURCE_PERMISSIONS). " + "See the install(DIRECTORY) command for documentation of permissions, " + "PATTERN, REGEX, and EXCLUDE options. " + "\n" + "The INSTALL signature differs slightly from COPY: " + "it prints status messages, and NO_SOURCE_PERMISSIONS is default. " + "Installation scripts generated by the install() command use this " + "signature (with some undocumented options for internal use)." + // Undocumented INSTALL options: + // - RENAME <name> + // - OPTIONAL + // - FILES keyword to re-enter files... list + // - PERMISSIONS before REGEX is alias for FILE_PERMISSIONS + // - DIR_PERMISSIONS is alias for DIRECTORY_PERMISSIONS + // - TYPE <FILE|DIRECTORY|EXECUTABLE|PROGRAM| + // STATIC_LIBRARY|SHARED_LIBRARY|MODULE> + // - COMPONENTS, CONFIGURATIONS, PROPERTIES (ignored for compat) + ; } cmTypeMacro(cmFileCommand, cmCommand); @@ -179,6 +214,7 @@ protected: bool HandleRPathRemoveCommand(std::vector<std::string> const& args); bool HandleDifferentCommand(std::vector<std::string> const& args); + bool HandleCopyCommand(std::vector<std::string> const& args); bool HandleInstallCommand(std::vector<std::string> const& args); bool HandleDownloadCommand(std::vector<std::string> const& args); }; diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt index b50d203..398243c 100644 --- a/Tests/StringFileTest/CMakeLists.txt +++ b/Tests/StringFileTest/CMakeLists.txt @@ -188,10 +188,19 @@ FILE(WRITE "${CMAKE_CURRENT_BINARY_DIR}/OutputFile.h-tmp" "${outfile}") FILE(RENAME "${CMAKE_CURRENT_BINARY_DIR}/OutputFile.h-tmp" "${CMAKE_CURRENT_BINARY_DIR}/OutputFile.h") +# Test file copy with relative paths +FILE(COPY . + DESTINATION src + FILE_PERMISSIONS OWNER_READ OWNER_WRITE + DIRECTORY_PERMISSIONS OWNER_READ OWNER_EXECUTE # test no OWNER_WRITE + FILES_MATCHING PATTERN *.cxx # Only copy the main source file + REGEX /src$ EXCLUDE # Block recursion for in-source build + ) + # Test file glob FILE(GLOB_RECURSE src_files "${CMAKE_CURRENT_SOURCE_DIR}/*") MESSAGE("Files in ${CMAKE_CURRENT_SOURCE_DIR} are ${src_files}") -SET(expr "${CMAKE_CURRENT_SOURCE_DIR}/[sS][!a-su-zA-Z0-9][^a-qs-zA-Z0-9]ing?ile*.cxx") +SET(expr "${CMAKE_CURRENT_BINARY_DIR}/src/[sS][!a-su-zA-Z0-9][^a-qs-zA-Z0-9]ing?ile*.cxx") MESSAGE("Glob expression is [${expr}].") FILE(GLOB src_files "${expr}") MESSAGE("Globbed files [${src_files}].") |