diff --git a/src/scikit_build_core/build/__init__.py b/src/scikit_build_core/build/__init__.py index 40c940ba6..d4a403a84 100644 --- a/src/scikit_build_core/build/__init__.py +++ b/src/scikit_build_core/build/__init__.py @@ -54,18 +54,30 @@ def build_sdist( def get_requires_for_build_sdist( - config_settings: dict[str, str | list[str]] | None = None # noqa: ARG001 + config_settings: dict[str, str | list[str]] | None = None ) -> list[str]: - return ["pathspec", "pyproject_metadata"] + from ..builder.get_requires import GetRequires + + requires = GetRequires(config_settings) + + return [ + "pathspec", + "pyproject_metadata", + *requires.dynamic_metadata(), + ] def get_requires_for_build_wheel( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: - from ..builder.get_requires import cmake_ninja_for_build_wheel + from ..builder.get_requires import GetRequires + + requires = GetRequires(config_settings) return [ "pathspec", "pyproject_metadata", - *cmake_ninja_for_build_wheel(config_settings), + *requires.cmake(), + *requires.ninja(), + *requires.dynamic_metadata(), ] diff --git a/src/scikit_build_core/builder/get_requires.py b/src/scikit_build_core/builder/get_requires.py index 454566542..7d563691c 100644 --- a/src/scikit_build_core/builder/get_requires.py +++ b/src/scikit_build_core/builder/get_requires.py @@ -1,6 +1,8 @@ from __future__ import annotations +import dataclasses import functools +import importlib import os import sys from collections.abc import Mapping @@ -20,7 +22,7 @@ from ..resources import resources from ..settings.skbuild_read_settings import SettingsReader -__all__ = ["cmake_ninja_for_build_wheel"] +__all__ = ["GetRequires"] def __dir__() -> list[str]: @@ -38,40 +40,59 @@ def is_known_platform(platforms: frozenset[str]) -> bool: return any(tag.platform in platforms for tag in sys_tags()) -def cmake_ninja_for_build_wheel( - config_settings: Mapping[str, str | list[str]] | None = None +def _load_get_requires_hook( + mod_name: str, + config_settings: Mapping[str, list[str] | str] | None = None, ) -> list[str]: - settings = SettingsReader.from_file("pyproject.toml", config_settings).settings - - packages = [] - cmake_min = Version(settings.cmake.minimum_version) - cmake = best_program(get_cmake_programs(module=False), minimum_version=cmake_min) - if cmake is None: - packages.append(f"cmake>={cmake_min}") - else: - logger.debug("Found system CMake: {} - not requiring PyPI package", cmake) + module = importlib.import_module(mod_name) + hook = getattr(module, "get_requires_for_dynamic_metadata", None) + return [] if hook is None else hook(config_settings) # type: ignore[no-any-return] + + +@dataclasses.dataclass +class GetRequires: + config_settings: Mapping[str, list[str] | str] | None = None - if ( - not sys.platform.startswith("win") - and os.environ.get("CMAKE_GENERATOR", "Ninja") == "Ninja" - and not os.environ.get("CMAKE_MAKE_PROGRAM", "") - ): - ninja_min = Version(settings.ninja.minimum_version) - ninja = best_program( - get_ninja_programs(module=False), minimum_version=ninja_min + def __post_init__(self) -> None: + self._settings = SettingsReader.from_file( + "pyproject.toml", self.config_settings + ).settings + + def cmake(self) -> list[str]: + cmake_min = Version(self._settings.cmake.minimum_version) + cmake = best_program( + get_cmake_programs(module=False), minimum_version=cmake_min ) - if ninja is None: - if ( - not settings.ninja.make_fallback - or is_known_platform(known_wheels("ninja")) - or not list(get_make_programs()) - ): - packages.append(f"ninja>={ninja_min}") - else: + if cmake is None: + return [f"cmake>={cmake_min}"] + logger.debug("Found system CMake: {} - not requiring PyPI package", cmake) + return [] + + def ninja(self) -> list[str]: + if ( + not sys.platform.startswith("win") + and os.environ.get("CMAKE_GENERATOR", "Ninja") == "Ninja" + and not os.environ.get("CMAKE_MAKE_PROGRAM", "") + ): + ninja_min = Version(self._settings.ninja.minimum_version) + ninja = best_program( + get_ninja_programs(module=False), minimum_version=ninja_min + ) + if ninja is None: + if ( + not self._settings.ninja.make_fallback + or is_known_platform(known_wheels("ninja")) + or not list(get_make_programs()) + ): + return [f"ninja>={ninja_min}"] logger.debug( "Found system Make & not on known platform - not requiring PyPI package for Ninja" ) - else: logger.debug("Found system Ninja: {} - not requiring PyPI package", ninja) + return [] - return packages + def dynamic_metadata(self) -> list[str]: + retval = [] + for plugins in self._settings.metadata.values(): + retval += _load_get_requires_hook(plugins, self.config_settings) + return retval diff --git a/src/scikit_build_core/metadata/fancy_pypi_readme.py b/src/scikit_build_core/metadata/fancy_pypi_readme.py index f67f67a61..b0ba5d022 100644 --- a/src/scikit_build_core/metadata/fancy_pypi_readme.py +++ b/src/scikit_build_core/metadata/fancy_pypi_readme.py @@ -26,3 +26,9 @@ def dynamic_metadata( "text": build_text(config.fragments, config.substitutions), } } + + +def get_requires_for_dynamic_metadata( + _config_settings: dict[str, list[str] | str] | None = None, +) -> list[str]: + return ["hatch-fancy-pypi-readme"] diff --git a/src/scikit_build_core/metadata/setuptools_scm.py b/src/scikit_build_core/metadata/setuptools_scm.py index 18ff7dabd..1988c83e8 100644 --- a/src/scikit_build_core/metadata/setuptools_scm.py +++ b/src/scikit_build_core/metadata/setuptools_scm.py @@ -19,3 +19,9 @@ def dynamic_metadata( version: str = _get_version(config) return {"version": version} + + +def get_requires_for_dynamic_metadata( + _config_settings: dict[str, list[str] | str] | None = None, +) -> list[str]: + return ["setuptools-scm"] diff --git a/src/scikit_build_core/setuptools/build_meta.py b/src/scikit_build_core/setuptools/build_meta.py index a7284a9d5..4f1d9b2a2 100644 --- a/src/scikit_build_core/setuptools/build_meta.py +++ b/src/scikit_build_core/setuptools/build_meta.py @@ -7,8 +7,6 @@ prepare_metadata_for_build_wheel, ) -from ..builder.get_requires import cmake_ninja_for_build_wheel - if hasattr(setuptools.build_meta, "build_editable"): from setuptools.build_meta import build_editable # type: ignore[attr-defined] @@ -40,17 +38,21 @@ def get_requires_for_build_sdist( setuptools_reqs = setuptools.build_meta.get_requires_for_build_sdist( config_settings ) - return [*setuptools_reqs, *cmake_ninja_for_build_wheel(config_settings)] + return [*setuptools_reqs] def get_requires_for_build_wheel( config_settings: dict[str, str | list[str]] | None = None ) -> list[str]: + from ..builder.get_requires import GetRequires + + requires = GetRequires(config_settings) + setuptools_reqs = setuptools.build_meta.get_requires_for_build_wheel( config_settings ) - return [*setuptools_reqs, *cmake_ninja_for_build_wheel(config_settings)] + return [*setuptools_reqs, *requires.cmake(), *requires.ninja()] if hasattr(setuptools.build_meta, "get_requires_for_build_editable"): @@ -58,7 +60,10 @@ def get_requires_for_build_wheel( def get_requires_for_build_editable( config_settings: dict[str, str | list[str]] | None = None ) -> list[str]: + from ..builder.get_requires import GetRequires + + requires = GetRequires(config_settings) setuptools_reqs = setuptools.build_meta.get_requires_for_build_editable( # type: ignore[attr-defined] config_settings ) - return [*setuptools_reqs, *cmake_ninja_for_build_wheel(config_settings)] + return [*setuptools_reqs, *requires.cmake(), *requires.ninja()] diff --git a/tests/test_dynamic_metadata.py b/tests/test_dynamic_metadata.py index dbe097794..8e172e098 100644 --- a/tests/test_dynamic_metadata.py +++ b/tests/test_dynamic_metadata.py @@ -14,6 +14,7 @@ from scikit_build_core._compat import tomllib from scikit_build_core.build import build_wheel +from scikit_build_core.builder.get_requires import GetRequires from scikit_build_core.settings.metadata import get_standard_metadata from scikit_build_core.settings.skbuild_read_settings import SettingsReader @@ -141,6 +142,11 @@ def test_plugin_metadata(tmp_path, monkeypatch): "Fragment #1Fragment #2", None, "text/x-rst" ) + assert set(GetRequires().dynamic_metadata()) == { + "hatch-fancy-pypi-readme", + "setuptools-scm", + } + def test_faulty_metadata(monkeypatch): monkeypatch.chdir(DYNAMIC) diff --git a/tests/test_get_requires.py b/tests/test_get_requires.py index e6cd6a0ab..2de656ddd 100644 --- a/tests/test_get_requires.py +++ b/tests/test_get_requires.py @@ -4,7 +4,7 @@ import sys from pathlib import Path -from scikit_build_core.builder.get_requires import cmake_ninja_for_build_wheel +from scikit_build_core.builder.get_requires import GetRequires ninja = [] if sys.platform.startswith("win") else ["ninja>=1.5"] @@ -24,7 +24,8 @@ def test_get_requires_for_build_wheel(fp, monkeypatch): monkeypatch.setattr(shutil, "which", which_mock) monkeypatch.delenv("CMAKE_GENERATOR", raising=False) fp.register([cmake, "--version"], stdout="3.14.0") - assert cmake_ninja_for_build_wheel() == ["cmake>=3.15", *ninja] + assert GetRequires().cmake() == ["cmake>=3.15"] + assert GetRequires().ninja() == [*ninja] def test_get_requires_for_build_wheel_uneeded(fp, monkeypatch): @@ -33,7 +34,8 @@ def test_get_requires_for_build_wheel_uneeded(fp, monkeypatch): monkeypatch.setattr(shutil, "which", which_mock) monkeypatch.delenv("CMAKE_GENERATOR", raising=False) fp.register([cmake, "--version"], stdout="3.18.0") - assert cmake_ninja_for_build_wheel() == [*ninja] + assert GetRequires().cmake() == [] + assert GetRequires().ninja() == [*ninja] def test_get_requires_for_build_wheel_settings(fp, monkeypatch): @@ -43,10 +45,8 @@ def test_get_requires_for_build_wheel_settings(fp, monkeypatch): monkeypatch.delenv("CMAKE_GENERATOR", raising=False) fp.register([cmake, "--version"], stdout="3.18.0") config = {"cmake.minimum-version": "3.20"} - assert cmake_ninja_for_build_wheel(config) == [ - "cmake>=3.20", - *ninja, - ] + assert GetRequires(config).cmake() == ["cmake>=3.20"] + assert GetRequires(config).ninja() == [*ninja] def test_get_requires_for_build_wheel_pyproject(fp, monkeypatch, tmp_path): @@ -62,4 +62,6 @@ def test_get_requires_for_build_wheel_pyproject(fp, monkeypatch, tmp_path): monkeypatch.setattr(shutil, "which", which_mock) monkeypatch.delenv("CMAKE_GENERATOR", raising=False) fp.register([cmake, "--version"], stdout="3.18.0") - assert cmake_ninja_for_build_wheel() == ["cmake>=3.21", *ninja] + + assert GetRequires().cmake() == ["cmake>=3.21"] + assert GetRequires().ninja() == [*ninja]