Skip to content
Open
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ sdist.reproducible = true
# If set to True, CMake will be run before building the SDist.
sdist.cmake = false

# Resolve symlinks in the SDist, copying file contents instead of storing symlinks.
sdist.resolve-symlinks = true

# A list of packages to auto-copy into the wheel.
wheel.packages = ["src/<package>", "python/<package>", "<package>"]

Expand Down
13 changes: 13 additions & 0 deletions docs/reference/configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,19 @@ print(mk_skbuild_docs())
``SOURCE_DATE_EPOCH`` will be used for timestamps, or a fixed value if not set.
```

```{eval-rst}
.. confval:: sdist.resolve-symlinks
:type: ``bool``
:default: true

Resolve symlinks in the SDist, copying file contents instead of storing symlinks.

If not set, it will be ``true`` unless you set the minimum version below 0.13,
in which case it will be ``false`` to preserve backward compatibility.

.. versionadded: 0.13
```

## search

```{eval-rst}
Expand Down
7 changes: 6 additions & 1 deletion src/scikit_build_core/build/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,12 @@ def build_sdist(
)
)
tar = stack.enter_context(
tarfile.TarFile(fileobj=gzip_container, mode="w", format=tarfile.PAX_FORMAT)
tarfile.TarFile(
fileobj=gzip_container,
mode="w",
format=tarfile.PAX_FORMAT,
dereference=settings.sdist.resolve_symlinks,
)
Comment thread
henryiii marked this conversation as resolved.
)
assert settings.sdist.inclusion_mode is not None
paths = sorted(
Expand Down
4 changes: 4 additions & 0 deletions src/scikit_build_core/resources/scikit-build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@
"type": "boolean",
"default": false,
"description": "If set to True, CMake will be run before building the SDist."
},
"resolve-symlinks": {
"type": "boolean",
"description": "Resolve symlinks in the SDist, copying file contents instead of storing symlinks."
}
}
},
Expand Down
13 changes: 13 additions & 0 deletions src/scikit_build_core/settings/skbuild_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,19 @@ class SDistSettings:
If set to True, CMake will be run before building the SDist.
"""

resolve_symlinks: Optional[bool] = dataclasses.field(
default=None,
metadata=SettingsFieldMetadata(display_default="true"),
)
"""
Resolve symlinks in the SDist, copying file contents instead of storing symlinks.

If not set, it will be ``true`` unless you set the minimum version below 0.13,
in which case it will be ``false`` to preserve backward compatibility.

.. versionadded: 0.13
"""


@dataclasses.dataclass
class WheelSettings:
Expand Down
16 changes: 16 additions & 0 deletions src/scikit_build_core/settings/skbuild_read_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,22 @@ def __init__(
else:
self.settings.sdist.inclusion_mode = "default"

if self.settings.sdist.resolve_symlinks is not None:
if (
self.settings.minimum_version is not None
and self.settings.minimum_version < Version("0.13")
):
rich_error(
"minimum-version can't be less than 0.13 to use sdist.resolve-symlinks"
)
elif (
self.settings.minimum_version is not None
and self.settings.minimum_version < Version("0.13")
):
self.settings.sdist.resolve_symlinks = False
else:
self.settings.sdist.resolve_symlinks = True

def unrecognized_options(self) -> Generator[str, None, None]:
return self.sources.unrecognized_options(ScikitBuildSettings)

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

import tarfile
from pathlib import Path

import pytest

from scikit_build_core.build import build_sdist


@pytest.fixture
def can_symlink(tmp_path: Path) -> None:
"""Skip the test if symlinks are not supported on this OS."""
target = tmp_path / "target"
target.touch()
try:
tmp_path.joinpath("link").symlink_to(target)
except OSError:
pytest.skip(
"Creating symlinks is not supported/allowed on this OS without privileges"
)


@pytest.mark.usefixtures("package_simple_pyproject_ext", "can_symlink")
@pytest.mark.parametrize(
("config_settings", "expected_type"),
[
pytest.param({}, "reg", id="dereference_default"),
pytest.param({"sdist.resolve-symlinks": "false"}, "sym", id="no_dereference"),
],
)
def test_pep517_sdist_symlink(
tmp_path: Path,
config_settings: dict[str, list[str] | str],
expected_type: str,
) -> None:
Path("CMakeLists_link.txt").symlink_to("CMakeLists.txt")

out = build_sdist(str(tmp_path), config_settings=config_settings or None)

with tarfile.open(tmp_path / out, "r:gz") as tar:
link_member = tar.getmember("cmake_example-0.0.1/CMakeLists_link.txt")
if expected_type == "reg":
assert link_member.isreg(), (
"The symlink should have been stored as a regular file"
)
else:
assert link_member.issym(), (
"The symlink should have been stored as a symlink"
)
35 changes: 35 additions & 0 deletions tests/test_skbuild_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def test_skbuild_settings_default(tmp_path: Path):
assert settings.sdist.inclusion_mode == "default"
assert settings.sdist.reproducible
assert not settings.sdist.cmake
assert settings.sdist.resolve_symlinks
Comment thread
henryiii marked this conversation as resolved.
assert settings.wheel.packages is None
assert settings.wheel.py_api == ""
assert not settings.wheel.expand_macos_universal_tags
Expand Down Expand Up @@ -947,3 +948,37 @@ def test_backcompat_sdist_inclusion_mode(

settings_reader = SettingsReader.from_file(pyproject_toml, {})
assert settings_reader.settings.sdist.inclusion_mode == "classic"


def test_backcompat_sdist_resolve_symlinks(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
):
monkeypatch.setattr(
scikit_build_core.settings.skbuild_read_settings, "__version__", "0.13.0"
)
pyproject_toml = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
"""\
[tool.scikit-build]
minimum-version = "0.12"
"""
),
encoding="utf-8",
)

settings_reader = SettingsReader.from_file(pyproject_toml, {})
assert settings_reader.settings.sdist.resolve_symlinks is False

pyproject_toml.write_text(
textwrap.dedent(
"""\
[tool.scikit-build]
minimum-version = "0.13"
"""
),
encoding="utf-8",
)

settings_reader = SettingsReader.from_file(pyproject_toml, {})
assert settings_reader.settings.sdist.resolve_symlinks is True