Skip to content

Commit 7771c1b

Browse files
authored
Tools files rm: Exclude pattern to avoid removing everything (#16350)
* Add tests to validate exclude rm Signed-off-by: Uilian Ries <uilianries@gmail.com> * Add excludes parameter to tools.files.rm Signed-off-by: Uilian Ries <uilianries@gmail.com> * Add support for tuple in excludes Signed-off-by: Uilian Ries <uilianries@gmail.com> * Update excludes description Signed-off-by: Uilian Ries <uilianries@gmail.com> --------- Signed-off-by: Uilian Ries <uilianries@gmail.com>
1 parent 76fca6d commit 7771c1b

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

conan/tools/files/files.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,25 @@ def rmdir(conanfile, path):
6666
_internal_rmdir(path)
6767

6868

69-
def rm(conanfile, pattern, folder, recursive=False):
69+
def rm(conanfile, pattern, folder, recursive=False, excludes=None):
7070
"""
7171
Utility functions to remove files matching a ``pattern`` in a ``folder``.
7272
7373
:param conanfile: The current recipe object. Always use ``self``.
7474
:param pattern: Pattern that the files to be removed have to match (fnmatch).
7575
:param folder: Folder to search/remove the files.
7676
:param recursive: If ``recursive`` is specified it will search in the subfolders.
77+
:param excludes: (Optional, defaulted to None) A tuple/list of fnmatch patterns or even a
78+
single one to be excluded from the remove pattern.
7779
"""
80+
if excludes and not isinstance(excludes, (tuple, list)):
81+
excludes = (excludes,)
82+
elif not excludes:
83+
excludes = []
84+
7885
for root, _, filenames in os.walk(folder):
7986
for filename in filenames:
80-
if fnmatch(filename, pattern):
87+
if fnmatch(filename, pattern) and not any(fnmatch(filename, it) for it in excludes):
8188
fullname = os.path.join(root, filename)
8289
os.unlink(fullname)
8390
if not recursive:

test/unittests/tools/files/test_rm.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import pytest
23

34
# Check it is importable from tools
45
from conan.tools.files import rm, chdir
@@ -63,3 +64,46 @@ def test_remove_files_by_mask_non_recursively():
6364

6465
assert os.path.exists(os.path.join(tmpdir, "1.txt"))
6566
assert os.path.exists(os.path.join(tmpdir, "subdir", "2.txt"))
67+
68+
69+
@pytest.mark.parametrize("recursive", [False, True])
70+
@pytest.mark.parametrize("results", [
71+
["*.dll", ("foo.dll",)],
72+
[("*.dll",), ("foo.dll",)],
73+
[["*.dll"], ("foo.dll",)],
74+
[("*.dll", "*.lib"), ("foo.dll", "foo.dll.lib")],
75+
])
76+
def test_exclude_pattern_from_remove_list(recursive, results):
77+
""" conan.tools.files.rm should not remove files that match the pattern but are excluded
78+
by the excludes parameter.
79+
It should obey the recursive parameter, only excluding the files in the root folder in case
80+
it is False.
81+
"""
82+
excludes, expected_files = results
83+
temporary_folder = temp_folder()
84+
with chdir(None, temporary_folder):
85+
os.makedirs("subdir")
86+
87+
save_files(temporary_folder, {
88+
"1.txt": "",
89+
"1.pdb": "",
90+
"1.pdb1": "",
91+
"foo.dll": "",
92+
"foo.dll.lib": "",
93+
os.path.join("subdir", "2.txt"): "",
94+
os.path.join("subdir", "2.pdb"): "",
95+
os.path.join("subdir", "foo.dll"): "",
96+
os.path.join("subdir", "foo.dll.lib"): "",
97+
os.path.join("subdir", "2.pdb1"): ""})
98+
99+
rm(None, "*", temporary_folder, excludes=excludes, recursive=recursive)
100+
101+
for it in expected_files:
102+
assert os.path.exists(os.path.join(temporary_folder, it))
103+
assert not os.path.exists(os.path.join(temporary_folder, "1.pdb"))
104+
105+
# Check the recursive parameter and subfolder
106+
condition = (lambda x: not x) if recursive else (lambda x: x)
107+
assert condition(os.path.exists(os.path.join(temporary_folder, "subdir", "2.pdb")))
108+
for it in expected_files:
109+
assert os.path.exists(os.path.join(temporary_folder, "subdir", it))

0 commit comments

Comments
 (0)