diff --git a/msys2_autobuild/build.py b/msys2_autobuild/build.py index 2195a11..c648456 100644 --- a/msys2_autobuild/build.py +++ b/msys2_autobuild/build.py @@ -122,6 +122,29 @@ def shlex_join(split_command: Sequence[str]) -> str: 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 clean(): 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: diff --git a/tests/main_test.py b/tests/main_test.py index 7b24583..7bab414 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -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():