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: https://github.com/msys2/msys2-autobuild/issues/101#issuecomment-2583121845 Integrate the workaround mentioned in the CPython bug report: https://github.com/python/cpython/issues/67596#issuecomment-1918112817 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.
This commit is contained in:
parent
35ff0b71b6
commit
4f60392b3e
@ -122,6 +122,29 @@ def run_cmd(msys2_root: PathLike, args: Sequence[PathLike], **kwargs: Any) -> No
|
||||
check_call([executable, '-lc'] + [shlex_join([str(a) for a in args])], env=env, **kwargs)
|
||||
|
||||
|
||||
def make_tree_writable(topdir: PathLike) -> None:
|
||||
# Ensure all files and directories under topdir are writable
|
||||
# (and readable) by owner.
|
||||
# Taken from meson, and adjusted
|
||||
|
||||
def chmod(p: PathLike) -> None:
|
||||
print(p)
|
||||
os.chmod(p, os.stat(p).st_mode | stat.S_IWRITE | stat.S_IREAD)
|
||||
|
||||
chmod(topdir)
|
||||
for root, dirs, files in os.walk(topdir):
|
||||
for d in dirs:
|
||||
chmod(os.path.join(root, d))
|
||||
# Work around Python bug following junctions
|
||||
# https://github.com/python/cpython/issues/67596#issuecomment-1918112817
|
||||
if hasattr(os.path, 'isjunction'): # Python 3.12 only
|
||||
dirs[:] = [d for d in dirs if not os.path.isjunction(os.path.join(root, d))]
|
||||
for fname in files:
|
||||
fpath = os.path.join(root, fname)
|
||||
if os.path.isfile(fpath):
|
||||
chmod(fpath)
|
||||
|
||||
|
||||
def reset_git_repo(path: PathLike):
|
||||
|
||||
def clean():
|
||||
@ -129,17 +152,6 @@ def reset_git_repo(path: PathLike):
|
||||
check_call(["git", "clean", "-xfdf"], cwd=path)
|
||||
check_call(["git", "reset", "--hard", "HEAD"], cwd=path)
|
||||
|
||||
def make_tree_writable(topdir: PathLike) -> None:
|
||||
# Ensure all files and directories under topdir are writable
|
||||
# (and readable) by owner.
|
||||
# Taken from meson
|
||||
for d, _, files in os.walk(topdir):
|
||||
os.chmod(d, os.stat(d).st_mode | stat.S_IWRITE | stat.S_IREAD)
|
||||
for fname in files:
|
||||
fpath = os.path.join(d, fname)
|
||||
if os.path.isfile(fpath):
|
||||
os.chmod(fpath, os.stat(fpath).st_mode | stat.S_IWRITE | stat.S_IREAD)
|
||||
|
||||
made_writable = False
|
||||
for i in range(10):
|
||||
try:
|
||||
|
||||
@ -1,7 +1,40 @@
|
||||
# type: ignore
|
||||
|
||||
import os
|
||||
import stat
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from msys2_autobuild.utils import parse_optional_deps
|
||||
from msys2_autobuild.queue import parse_buildqueue, get_cycles
|
||||
from msys2_autobuild.build import make_tree_writable
|
||||
|
||||
|
||||
def test_make_tree_writable():
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
nested_dir = Path(tempdir) / "nested"
|
||||
nested_junction = nested_dir / "junction"
|
||||
nested_dir.mkdir()
|
||||
file_path = nested_dir / "test_file.txt"
|
||||
file_path.write_text("content")
|
||||
|
||||
# Create a junction loop if possible, to make sure we ignore it
|
||||
if hasattr(os.path, 'isjunction') and os.name == 'nt':
|
||||
import _winapi
|
||||
_winapi.CreateJunction(str(nested_dir), str(nested_junction))
|
||||
else:
|
||||
nested_junction.mkdir()
|
||||
|
||||
# Remove permissions
|
||||
for p in [tempdir, nested_dir, file_path, nested_junction]:
|
||||
os.chmod(p, os.stat(p).st_mode & ~stat.S_IWRITE & ~stat.S_IREAD)
|
||||
|
||||
make_tree_writable(tempdir)
|
||||
|
||||
assert os.access(tempdir, os.W_OK) and os.access(tempdir, os.R_OK)
|
||||
assert os.access(nested_dir, os.W_OK) and os.access(nested_dir, os.R_OK)
|
||||
assert os.access(file_path, os.W_OK) and os.access(file_path, os.R_OK)
|
||||
assert os.access(nested_junction, os.W_OK) and os.access(nested_junction, os.R_OK)
|
||||
|
||||
|
||||
def test_parse_optional_deps():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user