Skip to content

Commit 813cdeb

Browse files
committed
make_tree_writable: handle junctions and add tests
As found out here, os.walk() by default follows junctions, which we don't want and can even lead to loops: msys2#101 (comment) Integrate the workaround mentioned in the CPython bug report: python/cpython#67596 (comment) Since this is Python 3.12+ only and we still support 3.10 make it optional though. This also adds tests, which uncovered some other minor issues: It was not chmoding top-down, which meant that os.walk would skip things if there were no read permissions. So chmod before os.walk() lists the dir.
1 parent 35ff0b7 commit 813cdeb

File tree

2 files changed

+56
-11
lines changed

2 files changed

+56
-11
lines changed

msys2_autobuild/build.py

+23-11
Original file line numberDiff line numberDiff line change
@@ -122,24 +122,36 @@ def shlex_join(split_command: Sequence[str]) -> str:
122122
check_call([executable, '-lc'] + [shlex_join([str(a) for a in args])], env=env, **kwargs)
123123

124124

125+
def make_tree_writable(topdir: PathLike) -> None:
126+
# Ensure all files and directories under topdir are writable
127+
# (and readable) by owner.
128+
# Taken from meson, and adjusted
129+
130+
def chmod(p: PathLike) -> None:
131+
print(p)
132+
os.chmod(p, os.stat(p).st_mode | stat.S_IWRITE | stat.S_IREAD)
133+
134+
chmod(topdir)
135+
for root, dirs, files in os.walk(topdir):
136+
for d in dirs:
137+
chmod(os.path.join(root, d))
138+
# Work around Python bug following junctions
139+
# https://github.com/python/cpython/issues/67596#issuecomment-1918112817
140+
if hasattr(os.path, 'isjunction'): # Python 3.12 only
141+
dirs[:] = [d for d in dirs if not os.path.isjunction(os.path.join(root, d))]
142+
for fname in files:
143+
fpath = os.path.join(root, fname)
144+
if os.path.isfile(fpath):
145+
chmod(fpath)
146+
147+
125148
def reset_git_repo(path: PathLike):
126149

127150
def clean():
128151
assert os.path.exists(path)
129152
check_call(["git", "clean", "-xfdf"], cwd=path)
130153
check_call(["git", "reset", "--hard", "HEAD"], cwd=path)
131154

132-
def make_tree_writable(topdir: PathLike) -> None:
133-
# Ensure all files and directories under topdir are writable
134-
# (and readable) by owner.
135-
# Taken from meson
136-
for d, _, files in os.walk(topdir):
137-
os.chmod(d, os.stat(d).st_mode | stat.S_IWRITE | stat.S_IREAD)
138-
for fname in files:
139-
fpath = os.path.join(d, fname)
140-
if os.path.isfile(fpath):
141-
os.chmod(fpath, os.stat(fpath).st_mode | stat.S_IWRITE | stat.S_IREAD)
142-
143155
made_writable = False
144156
for i in range(10):
145157
try:

tests/main_test.py

+33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
# type: ignore
22

3+
import os
4+
import stat
5+
import tempfile
6+
from pathlib import Path
7+
38
from msys2_autobuild.utils import parse_optional_deps
49
from msys2_autobuild.queue import parse_buildqueue, get_cycles
10+
from msys2_autobuild.build import make_tree_writable
11+
12+
13+
def test_make_tree_writable():
14+
with tempfile.TemporaryDirectory() as tempdir:
15+
nested_dir = Path(tempdir) / "nested"
16+
nested_junction = nested_dir / "junction"
17+
nested_dir.mkdir()
18+
file_path = nested_dir / "test_file.txt"
19+
file_path.write_text("content")
20+
21+
# Create a junction loop if possible, to make sure we ignore it
22+
if hasattr(os.path, 'isjunction') and os.name == 'nt':
23+
import _winapi
24+
_winapi.CreateJunction(str(nested_dir), str(nested_junction))
25+
else:
26+
nested_junction.mkdir()
27+
28+
# Remove permissions
29+
for p in [tempdir, nested_dir, file_path, nested_junction]:
30+
os.chmod(p, os.stat(p).st_mode & ~stat.S_IWRITE & ~stat.S_IREAD)
31+
32+
make_tree_writable(tempdir)
33+
34+
assert os.access(tempdir, os.W_OK) and os.access(tempdir, os.R_OK)
35+
assert os.access(nested_dir, os.W_OK) and os.access(nested_dir, os.R_OK)
36+
assert os.access(file_path, os.W_OK) and os.access(file_path, os.R_OK)
37+
assert os.access(nested_junction, os.W_OK) and os.access(nested_junction, os.R_OK)
538

639

740
def test_parse_optional_deps():

0 commit comments

Comments
 (0)