Skip to content

feat: add a couple of build commands #1055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 1, 2025
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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ known-local-folder = ["pathutils"]
"docs/conf.py" = ["TID251"]
"docs/examples/**" = ["ANN"]
"src/scikit_build_core/file_api/model/*.py" = ["N"]
"**/__main__.py" = ["T20"]


[tool.check-sdist]
Expand Down
18 changes: 16 additions & 2 deletions src/scikit_build_core/_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,31 @@ def rich_warning(
color: Literal[
"", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
] = "yellow",
file: object = None,
**kwargs: object,
) -> None:
rich_print("{bold.yellow}WARNING:", *args, color=color, **kwargs) # type: ignore[arg-type]
rich_print(
"{bold.yellow}WARNING:",
*args,
color=color,
file=file or sys.stderr,
**kwargs, # type: ignore[arg-type]
)


def rich_error(
*args: str,
color: Literal[
"", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
] = "red",
file: object = None,
**kwargs: object,
) -> NoReturn:
rich_print("{bold.red}ERROR:", *args, color=color, **kwargs) # type: ignore[arg-type]
rich_print(
"{bold.red}ERROR:",
*args,
color=color,
file=file or sys.stderr,
**kwargs, # type: ignore[arg-type]
)
raise SystemExit(7)
81 changes: 81 additions & 0 deletions src/scikit_build_core/build/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import argparse
import json
from pathlib import Path
from typing import Literal

from .._compat import tomllib
from .._logging import rich_warning
from ..builder._load_provider import process_dynamic_metadata
from . import (
get_requires_for_build_editable,
get_requires_for_build_sdist,
get_requires_for_build_wheel,
)


def main_project_table(_args: argparse.Namespace, /) -> None:
"""Get the full project table, including dynamic metadata."""
with Path("pyproject.toml").open("rb") as f:
pyproject = tomllib.load(f)

project = pyproject.get("project", {})
metadata = pyproject.get("tool", {}).get("scikit-build", {}).get("metadata", {})
new_project = process_dynamic_metadata(project, metadata)
print(json.dumps(new_project, indent=2))


def main_requires(args: argparse.Namespace, /) -> None:
get_requires(args.mode)


def get_requires(mode: Literal["sdist", "wheel", "editable"]) -> None:
"""Get the build requirements."""

with Path("pyproject.toml").open("rb") as f:
pyproject = tomllib.load(f)

requires = pyproject.get("build-system", {}).get("requires", [])
backend = pyproject.get("build-system", {}).get("build-backend", "")
if backend != "scikit_build_core.build":
rich_warning("Might not be a scikit-build-core project.")

Check warning on line 40 in src/scikit_build_core/build/__main__.py

View check run for this annotation

Codecov / codecov/patch

src/scikit_build_core/build/__main__.py#L40

Added line #L40 was not covered by tests
if mode == "sdist":
requires += get_requires_for_build_sdist({})
elif mode == "wheel":
requires += get_requires_for_build_wheel({})
elif mode == "editable":
requires += get_requires_for_build_editable({})
print(json.dumps(sorted(set(requires)), indent=2))


def main() -> None:
parser = argparse.ArgumentParser(
description="Build backend utilities",
)

subparsers = parser.add_subparsers(help="Commands")
requires = subparsers.add_parser(
"requires",
help="Get the build requirements",
description="Includes the static build requirements, the dynamically generated ones, and dynamic-metadata ones.",
)
requires.set_defaults(func=main_requires)
requires.add_argument(
"--mode",
choices=["sdist", "wheel", "editable"],
default="wheel",
help="The build mode to get the requirements for",
)

project_table = subparsers.add_parser(
"project-table",
help="Get the full project table, including dynamic metadata",
description="Processes static and dynamic metadata without triggering the backend, only handles scikit-build-core's dynamic metadata.",
)
project_table.set_defaults(func=main_project_table)

args = parser.parse_args()
args.func(args)


if __name__ == "__main__":
main()

Check warning on line 81 in src/scikit_build_core/build/__main__.py

View check run for this annotation

Codecov / codecov/patch

src/scikit_build_core/build/__main__.py#L81

Added line #L81 was not covered by tests
4 changes: 2 additions & 2 deletions tests/test_broken_fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ def test_fail_setting(
build_wheel("dist")

assert exc.value.code == 7
out, _ = capsys.readouterr()
assert "fail setting was enabled" in out
_, err = capsys.readouterr()
assert "fail setting was enabled" in err


@pytest.mark.usefixtures("broken_fallback")
Expand Down
101 changes: 101 additions & 0 deletions tests/test_build_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from __future__ import annotations

import json
import shutil
import sys
import sysconfig
from typing import TYPE_CHECKING

import pytest

from scikit_build_core._logging import rich_warning
from scikit_build_core.build.__main__ import main

if TYPE_CHECKING:
from pathlib import Path

PYPROJECT_1 = """
[build-system]
requires = ["scikit-build-core"]
build-backend = "scikit_build_core.build"
[project]
name = "test"
dynamic = ["version"]

[tool.scikit-build.metadata.version]
provider = "scikit_build_core.metadata.setuptools_scm"
"""


@pytest.mark.parametrize("mode", ["sdist", "wheel", "editable"])
def test_requires_command(
capsys: pytest.CaptureFixture[str],
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
mode: str,
) -> None:
monkeypatch.setattr(
sys, "argv", ["scikit_build_core.build", "requires", f"--mode={mode}"]
)
monkeypatch.setattr(shutil, "which", lambda _: None)
(tmp_path / "pyproject.toml").write_text(PYPROJECT_1)
monkeypatch.chdir(tmp_path)

main()
rich_warning.cache_clear()
out, err = capsys.readouterr()
assert "CMakeLists.txt not found" in err
jout = json.loads(out)
if mode == "sdist":
assert frozenset(jout) == {"scikit-build-core", "setuptools-scm"}
elif sysconfig.get_platform().startswith("win-"):
assert frozenset(jout) == {
"cmake>=3.15",
"scikit-build-core",
"setuptools-scm",
}
else:
assert frozenset(jout) == {
"cmake>=3.15",
"ninja>=1.5",
"scikit-build-core",
"setuptools-scm",
}


PYPROJECT_2 = """
[build-system]
requires = ["scikit-build-core"]
build-backend = "scikit_build_core.build"
[project]
name = "test"
dynamic = ["version", "dependencies"]

[tool.scikit-build.metadata.version]
provider = "scikit_build_core.metadata.regex"
input = "version.py"

[tool.scikit-build.metadata.dependencies]
provider = "scikit_build_core.metadata.template"
result = ["self=={project[version]}"]
"""


def test_metadata_command(
capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(sys, "argv", ["scikit_build_core.build", "project-table"])
monkeypatch.setattr(shutil, "which", lambda _: None)
(tmp_path / "pyproject.toml").write_text(PYPROJECT_2)
(tmp_path / "version.py").write_text("version = '0.1.3'")
monkeypatch.chdir(tmp_path)

main()
out, _ = capsys.readouterr()
jout = json.loads(out)
assert jout == {
"name": "test",
"version": "0.1.3",
"dynamic": [],
"dependencies": ["self==0.1.3"],
}
9 changes: 8 additions & 1 deletion tests/test_printouts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from scikit_build_core.builder.__main__ import main

if TYPE_CHECKING:
import pytest


def test_builder_printout(capsys):
def test_builder_printout(capsys: pytest.CaptureFixture[str]) -> None:
main()
out, err = capsys.readouterr()
assert "Detected Python Library" in out
2 changes: 1 addition & 1 deletion tests/test_skbuild_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ def test_skbuild_settings_auto_cmake_warning(

assert settings_reader.settings.cmake.version == SpecifierSet(">=3.15")

ex = capsys.readouterr().out
ex = capsys.readouterr().err
ex = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", ex)
print(ex)
assert ex.split() == [
Expand Down
Loading