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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ build.targets = []
# Verbose printout when building.
build.verbose = false

# Additional ``build-system.requires``. Intended to be used in combination with
# ``overrides``.
build.requires = []

# The components to install. If empty, all default components are installed.
install.components = []

Expand Down
39 changes: 39 additions & 0 deletions docs/configuration/dynamic.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,45 @@ metadata.readme.provider = "scikit_build_core.metadata.fancy_pypi_readme"
# tool.hatch.metadata.hooks.fancy-pypi-readme options here
```

## `build-system.requires`: Scikit-build-core's `build.requires`

If you need to inject and manipulate additional `build-system.requires`, you can
use the `build.requires`. This is intended to be used in combination with
[](./overrides.md).

This is not technically a dynamic metadata and thus does not have to have the
`dynamic` field defined, and it is not defined under the `metadata` table, but
similar to the other dynamic metadata it injects the additional
`build-system.requires`.

```toml
[package]
name = "mypackage"

[tool.scikit-build]
build.requires = ["foo"]

[[tool.scikit-build.overrides]]
if.from-sdist = false
build.requires = ["foo @ {root:uri}/foo"]
```

This example shows a common use-case where the package has a default
`build-system.requires` pointing to the package `foo` in the PyPI index, but
when built from the original git checkout or equivalent, the local folder is
used as dependency instead by resolving the `{root:uri}` to a file uri pointing
to the folder where the `pyproject.toml` is located.

```{note}
In order to be compliant with the package index, when building from `sdist`, the
`build.requires` **MUST NOT** have any `@` redirects. This rule may be later
enforced explicitly.
```

```{versionadded} 0.11

```

## Generate files with dynamic metadata

You can write out metadata to file(s) as well. Other info might become available
Expand Down
26 changes: 26 additions & 0 deletions src/scikit_build_core/builder/get_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import importlib.util
import os
import sysconfig
from pathlib import Path
from typing import TYPE_CHECKING, Literal

from packaging.tags import sys_tags
Expand Down Expand Up @@ -66,6 +67,28 @@
return SettingsReader.from_file("pyproject.toml", config_settings).settings


@dataclasses.dataclass()
class RootPathResolver:
"""Handle ``{root:uri}`` like formatting similar to ``hatchling``."""

path: Path = dataclasses.field(default_factory=Path)

def __post_init__(self) -> None:
self.path = self.path.resolve()

def __format__(self, fmt: str) -> str:
command, _, rest = fmt.partition(":")
if command == "parent":
parent = RootPathResolver(self.path.parent)
return parent.__format__(rest)
if command == "uri" and rest == "":
return self.path.as_uri()
if command == "" and rest == "":
return str(self)
msg = f"Could not handle format: {fmt}"
raise ValueError(msg)

Check warning on line 89 in src/scikit_build_core/builder/get_requires.py

View check run for this annotation

Codecov / codecov/patch

src/scikit_build_core/builder/get_requires.py#L86-L89

Added lines #L86 - L89 were not covered by tests


@dataclasses.dataclass(frozen=True)
class GetRequires:
settings: ScikitBuildSettings = dataclasses.field(
Expand Down Expand Up @@ -140,6 +163,9 @@
if self.settings.fail:
return

for build_require in self.settings.build.requires:
yield build_require.format(root=RootPathResolver())

for dynamic_metadata in self.settings.metadata.values():
if "provider" in dynamic_metadata:
config = dynamic_metadata.copy()
Expand Down
10 changes: 10 additions & 0 deletions src/scikit_build_core/resources/scikit-build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,13 @@
"type": "boolean",
"default": false,
"description": "Verbose printout when building."
},
"requires": {
"type": "array",
"items": {
"type": "string"
},
"description": "Additional ``build-system.requires``. Intended to be used in combination with ``overrides``."
}
}
},
Expand Down Expand Up @@ -560,6 +567,9 @@
},
"targets": {
"$ref": "#/$defs/inherit"
},
"requires": {
"$ref": "#/$defs/inherit"
}
}
},
Expand Down
6 changes: 6 additions & 0 deletions src/scikit_build_core/settings/skbuild_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@ class BuildSettings:
Verbose printout when building.
"""

requires: List[str] = dataclasses.field(default_factory=list)
"""
Additional ``build-system.requires``. Intended to be used in combination
with ``overrides``.
"""


@dataclasses.dataclass
class InstallSettings:
Expand Down
13 changes: 13 additions & 0 deletions tests/packages/dynamic_metadata/build_requires_project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[build-system]
requires = ["scikit-build-core"]
build-backend = "scikit_build_core.build"

[project]
name = "more_build_requires"

[tool.scikit-build]
build.requires = ["foo"]

[[tool.scikit-build.overrides]]
if.env.LOCAL_FOO = true
build.requires = ["foo @ {root:parent:uri}/foo"]
43 changes: 42 additions & 1 deletion tests/test_dynamic_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import types
import zipfile
from pathlib import Path
from typing import Any
from typing import TYPE_CHECKING, Any

import pytest
from packaging.version import Version
Expand All @@ -22,6 +22,9 @@

from pathutils import contained

if TYPE_CHECKING:
from typing import Literal


# these are mock plugins returning known results
# it turns out to be easier to create EntryPoint objects pointing to real
Expand Down Expand Up @@ -345,3 +348,41 @@ def test_regex_remove(
)

assert version == ("1.2.3dev1" if dev else "1.2.3")


@pytest.mark.usefixtures("package_dynamic_metadata")
@pytest.mark.parametrize("override", [None, "env", "sdist"])
def test_build_requires_field(override, monkeypatch) -> None:
shutil.copy("build_requires_project.toml", "pyproject.toml")

if override == "env":
monkeypatch.setenv("LOCAL_FOO", "True")
else:
monkeypatch.delenv("LOCAL_FOO", raising=False)

pyproject_path = Path("pyproject.toml")
with pyproject_path.open("rb") as ft:
pyproject = tomllib.load(ft)
state: Literal["sdist", "metadata_wheel"] = (
"sdist" if override == "sdist" else "metadata_wheel"
)
settings_reader = SettingsReader(pyproject, {}, state=state)

settings_reader.validate_may_exit()

if override is None:
assert set(GetRequires().dynamic_metadata()) == {
"foo",
}
elif override == "env":
# evaluate ../foo as uri
foo_path = pyproject_path.absolute().parent.parent / "foo"
foo_path = foo_path.absolute()
assert set(GetRequires().dynamic_metadata()) == {
f"foo @ {foo_path.as_uri()}",
}
elif override == "sdist":
assert set(GetRequires().dynamic_metadata()) == {
# TODO: Check if special handling should be done for sdist
"foo",
}
Loading