Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/scikit_build_core/_shutil.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from __future__ import annotations

import contextlib
import dataclasses
import os
import stat
import subprocess
from collections.abc import Iterable
import sys
from collections.abc import Generator, Iterable
from typing import ClassVar

from ._logging import logger
Expand Down Expand Up @@ -78,3 +81,26 @@ def _key_diff(self, k: str) -> str:
if k in self._prev_env and k not in self.env:
return "-"
return " "


def _fix_all_permissions(directory: str) -> None:
"""
Makes sure the write permission is set. Only run this on Windows.
"""
with os.scandir(directory) as it:
for entry in it:
if entry.is_dir():
_fix_all_permissions(entry.path)
continue
mode = stat.S_IMODE(entry.stat().st_mode)
if not mode & stat.S_IWRITE:
os.chmod(entry.path, mode | stat.S_IWRITE) # noqa: PTH101


@contextlib.contextmanager
def fix_win_37_all_permissions(tmpdir: str) -> Generator[None, None, None]:
try:
yield
finally:
if sys.version_info < (3, 8) and sys.platform.startswith("win"):
_fix_all_permissions(tmpdir)
3 changes: 2 additions & 1 deletion src/scikit_build_core/build/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .. import __version__
from .._compat import tomllib
from .._logging import logger, rich_print
from .._shutil import fix_win_37_all_permissions
from ..builder.builder import Builder, archs_to_tags, get_archs
from ..builder.wheel_tag import WheelTag
from ..cmake import CMake, CMaker
Expand Down Expand Up @@ -102,7 +103,7 @@ def _build_wheel_impl(
f"[red]({action})[/red]",
)

with tempfile.TemporaryDirectory() as tmpdir:
with tempfile.TemporaryDirectory() as tmpdir, fix_win_37_all_permissions(tmpdir):
build_tmp_folder = Path(tmpdir)
wheel_dir = build_tmp_folder / "wheel"

Expand Down
53 changes: 53 additions & 0 deletions tests/test_shutil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from __future__ import annotations

import shutil
import stat
import sys
import tempfile
from pathlib import Path

import pytest

from scikit_build_core._shutil import _fix_all_permissions, fix_win_37_all_permissions


def _make_dir_with_ro(tmp_path: Path) -> Path:
base = tmp_path / "fix_all_perm"
base.mkdir()
base.joinpath("normal_file.txt").touch()
ro = base / "read_only.txt"
ro.touch()
ro.chmod(stat.S_IREAD)
nested = base / "nested"
nested.mkdir()
ro2 = nested / "read_only_2.txt"
ro2.touch()
ro2.chmod(stat.S_IREAD)

# Validity check
assert not stat.S_IMODE(ro2.stat().st_mode) & stat.S_IWRITE

return base


@pytest.fixture()
def make_dir_with_ro(tmp_path: Path) -> Path:
return _make_dir_with_ro(tmp_path)


def test_broken_all_permissions(make_dir_with_ro: Path) -> None:
if sys.platform.startswith("win"):
with pytest.raises(PermissionError):
shutil.rmtree(make_dir_with_ro)
else:
shutil.rmtree(make_dir_with_ro)


def test_fix_all_permissions(make_dir_with_ro: Path) -> None:
_fix_all_permissions(str(make_dir_with_ro))
shutil.rmtree(make_dir_with_ro)


def test_tmpdir():
with tempfile.TemporaryDirectory() as tmp, fix_win_37_all_permissions(tmp):
_make_dir_with_ro(Path(tmp))