diff options
Diffstat (limited to 'Source/cmFileCommand.cxx')
-rw-r--r-- | Source/cmFileCommand.cxx | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 79110ab..8b450d0 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -8,6 +8,7 @@ #include <cmath> #include <cstdio> #include <cstdlib> +#include <iterator> #include <map> #include <set> #include <sstream> @@ -33,12 +34,14 @@ #include "cmFileInstaller.h" #include "cmFileLockPool.h" #include "cmFileTimes.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmHexFileConverter.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmNewLineStyle.h" #include "cmPolicies.h" #include "cmRange.h" #include "cmRuntimeDependencyArchive.h" @@ -47,6 +50,7 @@ #include "cmSubcommandTable.h" #include "cmSystemTools.h" #include "cmTimestamp.h" +#include "cmWorkingDirectory.h" #include "cmake.h" #if !defined(CMAKE_BOOTSTRAP) @@ -2776,6 +2780,325 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, return true; } +bool HandleConfigureCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 5) { + status.SetError("Incorrect arguments to CONFIGURE subcommand."); + return false; + } + if (args[1] != "OUTPUT") { + status.SetError("Incorrect arguments to CONFIGURE subcommand."); + return false; + } + if (args[3] != "CONTENT") { + status.SetError("Incorrect arguments to CONFIGURE subcommand."); + return false; + } + + std::string errorMessage; + cmNewLineStyle newLineStyle; + if (!newLineStyle.ReadFromArguments(args, errorMessage)) { + status.SetError(cmStrCat("CONFIGURE ", errorMessage)); + return false; + } + + bool escapeQuotes = false; + bool atOnly = false; + for (unsigned int i = 5; i < args.size(); ++i) { + if (args[i] == "@ONLY") { + atOnly = true; + } else if (args[i] == "ESCAPE_QUOTES") { + escapeQuotes = true; + } else if (args[i] == "NEWLINE_STYLE" || args[i] == "LF" || + args[i] == "UNIX" || args[i] == "CRLF" || args[i] == "WIN32" || + args[i] == "DOS") { + /* Options handled by NewLineStyle member above. */ + } else { + status.SetError( + cmStrCat("CONFIGURE Unrecognized argument \"", args[i], "\"")); + return false; + } + } + + // Check for generator expressions + const std::string input = args[4]; + std::string outputFile = args[2]; + + std::string::size_type pos = input.find_first_of("<>"); + if (pos != std::string::npos) { + status.SetError(cmStrCat("CONFIGURE called with CONTENT containing a \"", + input[pos], + "\". This character is not allowed.")); + return false; + } + + pos = outputFile.find_first_of("<>"); + if (pos != std::string::npos) { + status.SetError(cmStrCat("CONFIGURE called with OUTPUT containing a \"", + outputFile[pos], + "\". This character is not allowed.")); + return false; + } + + cmMakefile& makeFile = status.GetMakefile(); + if (!makeFile.CanIWriteThisFile(outputFile)) { + cmSystemTools::Error("Attempt to write file: " + outputFile + + " into a source directory."); + return false; + } + + cmSystemTools::ConvertToUnixSlashes(outputFile); + + // Re-generate if non-temporary outputs are missing. + // when we finalize the configuration we will remove all + // output files that now don't exist. + makeFile.AddCMakeOutputFile(outputFile); + + // Create output directory + const std::string::size_type slashPos = outputFile.rfind('/'); + if (slashPos != std::string::npos) { + const std::string path = outputFile.substr(0, slashPos); + cmSystemTools::MakeDirectory(path); + } + + std::string newLineCharacters; + bool open_with_binary_flag = false; + if (newLineStyle.IsValid()) { + open_with_binary_flag = true; + newLineCharacters = newLineStyle.GetCharacters(); + } + + cmGeneratedFileStream fout; + fout.Open(outputFile, false, open_with_binary_flag); + if (!fout) { + cmSystemTools::Error("Could not open file for write in copy operation " + + outputFile); + cmSystemTools::ReportLastSystemError(""); + return false; + } + fout.SetCopyIfDifferent(true); + + // copy intput to output and expand variables from input at the same time + std::stringstream sin(input, std::ios::in); + std::string inLine; + std::string outLine; + while (cmSystemTools::GetLineFromStream(sin, inLine)) { + outLine.clear(); + makeFile.ConfigureString(inLine, outLine, atOnly, escapeQuotes); + fout << outLine << newLineCharacters; + } + + // close file before attempting to copy + fout.close(); + + return true; +} + +bool HandleArchiveCreateCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + struct Arguments + { + std::string Output; + std::string Format; + std::string Type; + std::string MTime; + bool Verbose = false; + std::vector<std::string> Files; + std::vector<std::string> Directories; + }; + + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("OUTPUT"_s, &Arguments::Output) + .Bind("FORMAT"_s, &Arguments::Format) + .Bind("TYPE"_s, &Arguments::Type) + .Bind("MTIME"_s, &Arguments::MTime) + .Bind("VERBOSE"_s, &Arguments::Verbose) + .Bind("FILES"_s, &Arguments::Files) + .Bind("DIRECTORY"_s, &Arguments::Directories); + + std::vector<std::string> unrecognizedArguments; + std::vector<std::string> keywordsMissingValues; + auto parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, + &keywordsMissingValues); + auto argIt = unrecognizedArguments.begin(); + if (argIt != unrecognizedArguments.end()) { + status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + const std::vector<std::string> LIST_ARGS = { + "OUTPUT", "FORMAT", "TYPE", "MTIME", "FILES", "DIRECTORY", + }; + auto kwbegin = keywordsMissingValues.cbegin(); + auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); + if (kwend != kwbegin) { + status.SetError(cmStrCat("Keywords missing values:\n ", + cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + const char* knownFormats[] = { + "7zip", "gnutar", "pax", "paxr", "raw", "zip" + }; + + if (!parsedArgs.Format.empty() && + !cmContains(knownFormats, parsedArgs.Format)) { + status.SetError( + cmStrCat("archive format ", parsedArgs.Format, " not supported")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + const char* zipFileFormats[] = { "7zip", "zip" }; + if (!parsedArgs.Type.empty() && + cmContains(zipFileFormats, parsedArgs.Format)) { + status.SetError(cmStrCat("archive format ", parsedArgs.Format, + " does not support TYPE arguments")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + static std::map<std::string, cmSystemTools::cmTarCompression> + compressionTypeMap = { { "None", cmSystemTools::TarCompressNone }, + { "BZip2", cmSystemTools::TarCompressBZip2 }, + { "GZip", cmSystemTools::TarCompressGZip }, + { "XZ", cmSystemTools::TarCompressXZ }, + { "Zstd", cmSystemTools::TarCompressZstd } }; + + std::string const& outFile = parsedArgs.Output; + std::vector<std::string> files = parsedArgs.Files; + std::copy(parsedArgs.Directories.begin(), parsedArgs.Directories.end(), + std::back_inserter(files)); + + cmSystemTools::cmTarCompression compress = cmSystemTools::TarCompressNone; + auto typeIt = compressionTypeMap.find(parsedArgs.Type); + if (typeIt != compressionTypeMap.end()) { + compress = typeIt->second; + } else if (!parsedArgs.Type.empty()) { + status.SetError( + cmStrCat("compression type ", parsedArgs.Type, " is not supported")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (files.empty()) { + status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, + "No files or directories specified"); + } + + if (!cmSystemTools::CreateTar(outFile, files, compress, parsedArgs.Verbose, + parsedArgs.MTime, parsedArgs.Format)) { + status.SetError(cmStrCat("failed to compress: ", outFile)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + return true; +} + +bool HandleArchiveExtractCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + struct Arguments + { + std::string Input; + bool Verbose = false; + bool ListOnly = false; + std::string Destination; + std::vector<std::string> Files; + std::vector<std::string> Directories; + }; + + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("INPUT"_s, &Arguments::Input) + .Bind("VERBOSE"_s, &Arguments::Verbose) + .Bind("LIST_ONLY"_s, &Arguments::ListOnly) + .Bind("DESTINATION"_s, &Arguments::Destination) + .Bind("FILES"_s, &Arguments::Files) + .Bind("DIRECTORY"_s, &Arguments::Directories); + + std::vector<std::string> unrecognizedArguments; + std::vector<std::string> keywordsMissingValues; + auto parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, + &keywordsMissingValues); + auto argIt = unrecognizedArguments.begin(); + if (argIt != unrecognizedArguments.end()) { + status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + const std::vector<std::string> LIST_ARGS = { + "INPUT", + "DESTINATION", + "FILES", + "DIRECTORY", + }; + auto kwbegin = keywordsMissingValues.cbegin(); + auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); + if (kwend != kwbegin) { + status.SetError(cmStrCat("Keywords missing values:\n ", + cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + std::string inFile = parsedArgs.Input; + std::vector<std::string> files = parsedArgs.Files; + std::copy(parsedArgs.Directories.begin(), parsedArgs.Directories.end(), + std::back_inserter(files)); + + if (parsedArgs.ListOnly) { + if (!cmSystemTools::ListTar(inFile, files, parsedArgs.Verbose)) { + status.SetError(cmStrCat("failed to list: ", inFile)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } else { + std::string destDir = cmSystemTools::GetCurrentWorkingDirectory(); + if (!parsedArgs.Destination.empty()) { + if (cmSystemTools::FileIsFullPath(parsedArgs.Destination)) { + destDir = parsedArgs.Destination; + } else { + destDir = cmStrCat(destDir, "/", parsedArgs.Destination); + } + + if (!cmSystemTools::MakeDirectory(destDir)) { + status.SetError(cmStrCat("failed to create directory: ", destDir)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (!cmSystemTools::FileIsFullPath(inFile)) { + inFile = + cmStrCat(cmSystemTools::GetCurrentWorkingDirectory(), "/", inFile); + } + } + + cmWorkingDirectory workdir(destDir); + if (workdir.Failed()) { + status.SetError( + cmStrCat("failed to change working directory to: ", destDir)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (!cmSystemTools::ExtractTar(inFile, files, parsedArgs.Verbose)) { + status.SetError(cmStrCat("failed to extract: ", inFile)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + + return true; +} + } // namespace bool cmFileCommand(std::vector<std::string> const& args, @@ -2829,6 +3152,9 @@ bool cmFileCommand(std::vector<std::string> const& args, { "READ_SYMLINK"_s, HandleReadSymlinkCommand }, { "CREATE_LINK"_s, HandleCreateLinkCommand }, { "GET_RUNTIME_DEPENDENCIES"_s, HandleGetRuntimeDependenciesCommand }, + { "CONFIGURE"_s, HandleConfigureCommand }, + { "ARCHIVE_CREATE"_s, HandleArchiveCreateCommand }, + { "ARCHIVE_EXTRACT"_s, HandleArchiveExtractCommand }, }; return subcommand(args[0], args, status); |