mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
file(ARCHIVE_EXTRACT): Add PATTERNS_EXCLUDE option
Omit archive entries matching the given patterns when extracting or listing. May be combined with PATTERNS; on overlap the exclusion takes precedence. Fixes: #27837
This commit is contained in:
@@ -1080,6 +1080,7 @@ Archiving
|
||||
[DESTINATION <dir>]
|
||||
[ENCODING <encoding>]
|
||||
[PATTERNS <pattern>...]
|
||||
[PATTERNS_EXCLUDE <pattern>...]
|
||||
[LIST_ONLY]
|
||||
[VERBOSE]
|
||||
[TOUCH])
|
||||
@@ -1137,6 +1138,18 @@ Archiving
|
||||
patterns. Wildcards are supported. If the ``PATTERNS`` option is
|
||||
not given, the entire archive will be listed or extracted.
|
||||
|
||||
``PATTERNS_EXCLUDE <pattern>...``
|
||||
.. versionadded:: 4.5
|
||||
|
||||
Do not extract or list files and directories that match one of the given
|
||||
patterns. Wildcards are supported. This option may be combined with
|
||||
``PATTERNS``; an entry that matches both an inclusion pattern and an
|
||||
exclusion pattern is excluded.
|
||||
|
||||
Unlike ``PATTERNS``, exclusion patterns are not anchored to the start of
|
||||
an entry's path: a pattern matches if it matches any portion of the path.
|
||||
This is consistent with the ``--exclude`` option of command-line ``tar``.
|
||||
|
||||
``LIST_ONLY``
|
||||
List the files in the archive rather than extract them.
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
file-ARCHIVE_EXTRACT-patterns-exclude
|
||||
-------------------------------------
|
||||
|
||||
* The :command:`file(ARCHIVE_EXTRACT)` command gained a ``PATTERNS_EXCLUDE``
|
||||
option to omit archive entries matching the given patterns from being
|
||||
extracted or listed.
|
||||
@@ -3888,17 +3888,20 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
|
||||
bool ListOnly = false;
|
||||
std::string Destination;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> PatternsExclude;
|
||||
bool Touch = false;
|
||||
};
|
||||
|
||||
static auto const parser = cmArgumentParser<Arguments>{}
|
||||
.Bind("INPUT"_s, &Arguments::Input)
|
||||
.Bind("ENCODING"_s, &Arguments::Encoding)
|
||||
.Bind("VERBOSE"_s, &Arguments::Verbose)
|
||||
.Bind("LIST_ONLY"_s, &Arguments::ListOnly)
|
||||
.Bind("DESTINATION"_s, &Arguments::Destination)
|
||||
.Bind("PATTERNS"_s, &Arguments::Patterns)
|
||||
.Bind("TOUCH"_s, &Arguments::Touch);
|
||||
static auto const parser =
|
||||
cmArgumentParser<Arguments>{}
|
||||
.Bind("INPUT"_s, &Arguments::Input)
|
||||
.Bind("ENCODING"_s, &Arguments::Encoding)
|
||||
.Bind("VERBOSE"_s, &Arguments::Verbose)
|
||||
.Bind("LIST_ONLY"_s, &Arguments::ListOnly)
|
||||
.Bind("DESTINATION"_s, &Arguments::Destination)
|
||||
.Bind("PATTERNS"_s, &Arguments::Patterns)
|
||||
.Bind("PATTERNS_EXCLUDE"_s, &Arguments::PatternsExclude)
|
||||
.Bind("TOUCH"_s, &Arguments::Touch);
|
||||
|
||||
std::vector<std::string> unrecognizedArguments;
|
||||
auto parsedArgs =
|
||||
@@ -3928,6 +3931,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
|
||||
|
||||
if (parsedArgs.ListOnly) {
|
||||
if (!cmSystemTools::ListTar(inFile, parsedArgs.Patterns,
|
||||
parsedArgs.PatternsExclude,
|
||||
parsedArgs.Encoding, parsedArgs.Verbose)) {
|
||||
status.SetError(cmStrCat("failed to list: ", inFile));
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
@@ -3962,7 +3966,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
|
||||
if (!cmSystemTools::ExtractTar(
|
||||
inFile, parsedArgs.Patterns,
|
||||
inFile, parsedArgs.Patterns, parsedArgs.PatternsExclude,
|
||||
parsedArgs.Touch ? cmSystemTools::cmTarExtractTimestamps::No
|
||||
: cmSystemTools::cmTarExtractTimestamps::Yes,
|
||||
parsedArgs.Encoding, parsedArgs.Verbose)) {
|
||||
|
||||
@@ -2505,6 +2505,7 @@ bool copy_data(struct archive* ar, struct archive* aw)
|
||||
|
||||
bool extract_tar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
std::vector<std::string> const& excludeFiles,
|
||||
std::string const& encoding, bool verbose,
|
||||
cmSystemTools::cmTarExtractTimestamps extractTimestamps,
|
||||
bool extract)
|
||||
@@ -2551,6 +2552,14 @@ bool extract_tar(std::string const& arFileName,
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& filename : excludeFiles) {
|
||||
if (archive_match_exclude_pattern(matching, filename.c_str()) !=
|
||||
ARCHIVE_OK) {
|
||||
cmSystemTools::Error("Failed to add to exclusion list: " + filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int r = cm_archive_read_open_filename(a, arFileName.c_str(), 10240);
|
||||
if (r) {
|
||||
ArchiveError("Problem with archive_read_open_filename(): ", a);
|
||||
@@ -2644,15 +2653,17 @@ bool extract_tar(std::string const& arFileName,
|
||||
|
||||
bool cmSystemTools::ExtractTar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
std::vector<std::string> const& excludeFiles,
|
||||
cmTarExtractTimestamps extractTimestamps,
|
||||
std::string const& encoding, bool verbose)
|
||||
{
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
return extract_tar(arFileName, files, encoding, verbose, extractTimestamps,
|
||||
true);
|
||||
return extract_tar(arFileName, files, excludeFiles, encoding, verbose,
|
||||
extractTimestamps, true);
|
||||
#else
|
||||
(void)arFileName;
|
||||
(void)files;
|
||||
(void)excludeFiles;
|
||||
(void)extractTimestamps;
|
||||
(void)encoding;
|
||||
(void)verbose;
|
||||
@@ -2662,14 +2673,16 @@ bool cmSystemTools::ExtractTar(std::string const& arFileName,
|
||||
|
||||
bool cmSystemTools::ListTar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
std::vector<std::string> const& excludeFiles,
|
||||
std::string const& encoding, bool verbose)
|
||||
{
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
return extract_tar(arFileName, files, encoding, verbose,
|
||||
return extract_tar(arFileName, files, excludeFiles, encoding, verbose,
|
||||
cmTarExtractTimestamps::Yes, false);
|
||||
#else
|
||||
(void)arFileName;
|
||||
(void)files;
|
||||
(void)excludeFiles;
|
||||
(void)encoding;
|
||||
(void)verbose;
|
||||
return false;
|
||||
|
||||
@@ -523,6 +523,7 @@ public:
|
||||
|
||||
static bool ListTar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
std::vector<std::string> const& excludeFiles,
|
||||
std::string const& encoding, bool verbose);
|
||||
static bool CreateTar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
@@ -534,6 +535,7 @@ public:
|
||||
int compressionLevel = 0, int numThreads = 1);
|
||||
static bool ExtractTar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
std::vector<std::string> const& excludeFiles,
|
||||
cmTarExtractTimestamps extractTimestamps,
|
||||
std::string const& encoding, bool verbose);
|
||||
|
||||
|
||||
+2
-2
@@ -2121,7 +2121,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
|
||||
if (action == cmSystemTools::TarActionList) {
|
||||
if (!cmSystemTools::ListTar(outFile, files, encoding, verbose)) {
|
||||
if (!cmSystemTools::ListTar(outFile, files, {}, encoding, verbose)) {
|
||||
cmSystemTools::Error("Problem listing tar: " + outFile);
|
||||
return 1;
|
||||
}
|
||||
@@ -2136,7 +2136,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
|
||||
return 1;
|
||||
}
|
||||
} else if (action == cmSystemTools::TarActionExtract) {
|
||||
if (!cmSystemTools::ExtractTar(outFile, files, extractTimestamps,
|
||||
if (!cmSystemTools::ExtractTar(outFile, files, {}, extractTimestamps,
|
||||
encoding, verbose)) {
|
||||
cmSystemTools::Error(
|
||||
cmStrCat("Problem extracting tar:\n ", outFile));
|
||||
|
||||
@@ -86,9 +86,10 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
std::vector<std::string> files;
|
||||
|
||||
// Extract without verbose, with timestamps
|
||||
std::vector<std::string> excludeFiles;
|
||||
bool result1 = cmSystemTools::ExtractTar(
|
||||
archiveFile, files, cmSystemTools::cmTarExtractTimestamps::Yes, "UTF-8",
|
||||
false);
|
||||
archiveFile, files, excludeFiles,
|
||||
cmSystemTools::cmTarExtractTimestamps::Yes, "UTF-8", false);
|
||||
(void)result1;
|
||||
|
||||
// Restore directory BEFORE removing (can't remove cwd)
|
||||
@@ -103,8 +104,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
// Extract with verbose, without timestamps
|
||||
files.clear();
|
||||
bool result2 = cmSystemTools::ExtractTar(
|
||||
archiveFile, files, cmSystemTools::cmTarExtractTimestamps::No, "UTF-8",
|
||||
true);
|
||||
archiveFile, files, excludeFiles,
|
||||
cmSystemTools::cmTarExtractTimestamps::No, "UTF-8", true);
|
||||
(void)result2;
|
||||
|
||||
// Restore directory
|
||||
|
||||
@@ -68,6 +68,10 @@ endif()
|
||||
# Extracting only selected files or directories
|
||||
run_cmake(zip-filtered)
|
||||
|
||||
# Excluding selected files or directories from extraction
|
||||
run_cmake(zip-filtered-exclude)
|
||||
run_cmake(zip-filtered-exclude-precedence)
|
||||
|
||||
run_cmake(create-missing-args)
|
||||
run_cmake(extract-missing-args)
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
set(OUTPUT_NAME "test.zip")
|
||||
|
||||
set(ARCHIVE_FORMAT zip)
|
||||
|
||||
# Combine inclusion and exclusion patterns. When an entry matches both,
|
||||
# the exclusion takes precedence.
|
||||
set(DECOMPRESSION_OPTIONS
|
||||
PATTERNS
|
||||
"compress_dir/d1/*" # include contents of d1
|
||||
"compress_dir/d_4/*" # include contents of d_4 ...
|
||||
PATTERNS_EXCLUDE
|
||||
"d_4" # ... but exclude d_4: exclusion wins
|
||||
)
|
||||
|
||||
# Only the included-and-not-excluded entry survives.
|
||||
set(CUSTOM_CHECK_FILES
|
||||
"d1/f1.txt"
|
||||
)
|
||||
|
||||
set(NOT_EXISTING_FILES_CHECK
|
||||
"f1.txt" # never included by PATTERNS
|
||||
"d 2/f1.txt" # never included by PATTERNS
|
||||
"d_4/f1.txt" # included by PATTERNS but excluded: exclusion wins
|
||||
)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
|
||||
|
||||
check_magic("504b0304" LIMIT 4 HEX)
|
||||
@@ -0,0 +1,30 @@
|
||||
set(OUTPUT_NAME "test.zip")
|
||||
|
||||
set(ARCHIVE_FORMAT zip)
|
||||
|
||||
# Extract the whole archive except entries matching PATTERNS_EXCLUDE.
|
||||
set(DECOMPRESSION_OPTIONS
|
||||
PATTERNS_EXCLUDE
|
||||
"compress_dir/d 2/*" # exclude everything under a directory
|
||||
"d-4" # exclude by unanchored name (dir + contents)
|
||||
"no_such_entry" # matches nothing: must not be an error
|
||||
)
|
||||
|
||||
# Everything that was not excluded must still be extracted.
|
||||
set(CUSTOM_CHECK_FILES
|
||||
"f1.txt"
|
||||
"d1/f1.txt"
|
||||
"d + 3/f1.txt"
|
||||
"d_4/f1.txt" # underscore, not the excluded "d-4"
|
||||
"My Special Directory/f1.txt"
|
||||
)
|
||||
|
||||
# The excluded entries must not be extracted.
|
||||
set(NOT_EXISTING_FILES_CHECK
|
||||
"d 2/f1.txt"
|
||||
"d-4/f1.txt"
|
||||
)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
|
||||
|
||||
check_magic("504b0304" LIMIT 4 HEX)
|
||||
Reference in New Issue
Block a user