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:
Daksh Mamodiya
2026-06-11 02:04:36 +02:00
parent 5e11ead838
commit 531f3b568c
10 changed files with 119 additions and 18 deletions
+13
View File
@@ -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.
+13 -9
View File
@@ -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)) {
+16 -3
View File
@@ -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;
+2
View File
@@ -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
View File
@@ -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));
+5 -4
View File
@@ -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)