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
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ sdist.exclude = []
# value if not set.
sdist.reproducible = true

# If set to True, CMake will be run before building the SDist.
sdist.cmake = false

# A list of packages to auto-copy into the wheel. If this is not set, it will
# default to the first of ``src/<package>`` or ``<package>`` if they exist. The
# prefix(s) will be stripped from the package name inside the wheel.
Expand All @@ -195,9 +198,11 @@ wheel.py-api = ""
# (before 21.0.1) find the correct wheel.
wheel.expand-macos-universal-tags = false

# The install directory for the wheel. This is relative to the platlib root.
# EXPERIMENTAL: An absolute path will be one level higher than the platlib root,
# giving access to "/platlib", "/data", "/headers", and "/scripts".
# The install directory for the wheel. This is relative to the platlib root. You
# might set this to the package name. The original dir is still at
# SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available). EXPERIMENTAL:
# An absolute path will be one level higher than the platlib root, giving access
# to "/platlib", "/data", "/headers", and "/scripts".
wheel.install-dir = ""

# A list of license files to include in the wheel. Supports glob patterns.
Expand Down
46 changes: 46 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,52 @@ reproducible builds if you prefer, however:
sdist.reproducible = false
```

You can also request CMake to run during this step:

```toml
[tool.scikit-build]
sdist.cmake = true
```

:::{note}

If you do this, you'll want to have some artifact from the configure in your
source directory; for example:

```cmake
include(FetchContent)

if(NOT SKBUILD_STATE STREQUAL "sdist"
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/pybind11/CMakeLists.txt")
message(STATUS "Using integrated pybind11")
set(FETCHCONTENT_FULLY_DISCONNECTED ON)
endif()

FetchContent_Declare(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
GIT_TAG v2.11.1
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/pybind11)

set(PYBIND11_FINDPYTHON ON)
FetchContent_MakeAvailable(pybind11)
```

The `/pybind11` directory is in the `.gitignore` and important parts are in
`sdist.include`:

```toml
[tool.scikit-build]
sdist.cmake = true
sdist.include = [
"pybind11/tools",
"pybind11/include",
"pybind11/CMakeLists.txt",
]
```

:::

## Customizing the built wheel

The wheel will automatically look for Python packages at `<package_name>` and
Expand Down
10 changes: 9 additions & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@

DIR = Path(__file__).parent.resolve()

nox.options.sessions = ["lint", "pylint", "tests"]
nox.options.sessions = [
"lint",
"pylint",
"generate_schema",
"readme",
"build_api_docs",
"tests",
"test_doc_examples",
]


@nox.session(reuse_venv=True)
Expand Down
6 changes: 6 additions & 0 deletions src/scikit_build_core/build/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,15 @@ def get_requires_for_build_sdist(

requires = GetRequires(config_settings)

# These are only injected if cmake is required for the SDist step
cmake_requires = (
[*requires.cmake(), *requires.ninja()] if requires.settings.sdist.cmake else []
)

return [
"pathspec",
"pyproject_metadata",
*cmake_requires,
*requires.dynamic_metadata(),
]

Expand Down
6 changes: 6 additions & 0 deletions src/scikit_build_core/build/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from ..settings.skbuild_read_settings import SettingsReader
from ._file_processor import each_unignored_file
from ._init import setup_logging
from .wheel import _build_wheel_impl

__all__: list[str] = ["build_sdist"]

Expand Down Expand Up @@ -110,6 +111,11 @@ def build_sdist(
srcdirname = f"{sdist_name}-{metadata.version}"
filename = f"{srcdirname}.tar.gz"

if settings.sdist.cmake:
_build_wheel_impl(
None, config_settings, None, exit_after_config=True, editable=False
)

sdist_dir.mkdir(parents=True, exist_ok=True)
with contextlib.ExitStack() as stack:
gzip_container = stack.enter_context(
Expand Down
10 changes: 9 additions & 1 deletion src/scikit_build_core/build/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def _build_wheel_impl(
config_settings: dict[str, list[str] | str] | None,
metadata_directory: str | None,
*,
exit_after_config: bool = False,
editable: bool,
) -> WheelImplReturn:
"""
Expand All @@ -97,6 +98,8 @@ def _build_wheel_impl(
action = "editable" if editable else "wheel"
if wheel_directory is None:
action = f"metadata_{action}"
if exit_after_config:
action = "sdist"

cmake = CMake.default_search(minimum_version=settings.cmake.minimum_version)

Expand Down Expand Up @@ -176,7 +179,7 @@ def _build_wheel_impl(
config=config,
)

if wheel_directory is None:
if wheel_directory is None and not exit_after_config:
if metadata_directory is None:
msg = "metadata_directory must be specified if wheel_directory is None"
raise AssertionError(msg)
Expand Down Expand Up @@ -209,6 +212,11 @@ def _build_wheel_impl(
version=metadata.version,
)

if exit_after_config:
return WheelImplReturn("")

assert wheel_directory is not None

generator = builder.config.env.get(
"CMAKE_GENERATOR",
"MSVC"
Expand Down
13 changes: 9 additions & 4 deletions src/scikit_build_core/builder/get_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from ..resources import resources
from ..settings._load_provider import load_provider
from ..settings.skbuild_model import ScikitBuildSettings
from ..settings.skbuild_read_settings import SettingsReader

__all__ = ["GetRequires"]
Expand Down Expand Up @@ -48,8 +49,12 @@ def __post_init__(self) -> None:
"pyproject.toml", self.config_settings
).settings

@property
def settings(self) -> ScikitBuildSettings:
return self._settings

def cmake(self) -> Generator[str, None, None]:
cmake_min = self._settings.cmake.minimum_version
cmake_min = self.settings.cmake.minimum_version
cmake = best_program(
get_cmake_programs(module=False), minimum_version=cmake_min
)
Expand All @@ -73,7 +78,7 @@ def ninja(self) -> Generator[str, None, None]:
if os.environ.get("CMAKE_MAKE_PROGRAM", ""):
return

ninja_min = self._settings.ninja.minimum_version
ninja_min = self.settings.ninja.minimum_version
ninja = best_program(
get_ninja_programs(module=False), minimum_version=ninja_min
)
Expand All @@ -82,7 +87,7 @@ def ninja(self) -> Generator[str, None, None]:
return

if (
self._settings.ninja.make_fallback
self.settings.ninja.make_fallback
and not is_known_platform(known_wheels("ninja"))
and list(get_make_programs())
):
Expand All @@ -93,7 +98,7 @@ def ninja(self) -> Generator[str, None, None]:
yield f"ninja>={ninja_min}"

def dynamic_metadata(self) -> Generator[str, None, None]:
for dynamic_metadata in self._settings.metadata.values():
for dynamic_metadata in self.settings.metadata.values():
if "provider" in dynamic_metadata:
config = dynamic_metadata.copy()
provider = config.pop("provider")
Expand Down
7 changes: 6 additions & 1 deletion src/scikit_build_core/resources/scikit-build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
"type": "boolean",
"default": true,
"description": "If set to True, try to build a reproducible distribution (Unix and Python 3.9+ recommended). ``SOURCE_DATE_EPOCH`` will be used for timestamps, or a fixed value if not set."
},
"cmake": {
"type": "boolean",
"default": false,
"description": "If set to True, CMake will be run before building the SDist."
}
}
},
Expand Down Expand Up @@ -137,7 +142,7 @@
"install-dir": {
"type": "string",
"default": "",
"description": "The install directory for the wheel. This is relative to the platlib root. EXPERIMENTAL: An absolute path will be one level higher than the platlib root, giving access to \"/platlib\", \"/data\", \"/headers\", and \"/scripts\"."
"description": "The install directory for the wheel. This is relative to the platlib root. You might set this to the package name. The original dir is still at SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available). EXPERIMENTAL: An absolute path will be one level higher than the platlib root, giving access to \"/platlib\", \"/data\", \"/headers\", and \"/scripts\"."
},
"license-files": {
"type": "array",
Expand Down
7 changes: 7 additions & 0 deletions src/scikit_build_core/settings/skbuild_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ class SDistSettings:
fixed value if not set.
"""

cmake: bool = False
"""
If set to True, CMake will be run before building the SDist.
"""


@dataclasses.dataclass
class WheelSettings:
Expand Down Expand Up @@ -142,6 +147,8 @@ class WheelSettings:
install_dir: str = ""
"""
The install directory for the wheel. This is relative to the platlib root.
You might set this to the package name. The original dir is still at
SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available).
EXPERIMENTAL: An absolute path will be one level higher than the platlib
root, giving access to "/platlib", "/data", "/headers", and "/scripts".
"""
Expand Down
14 changes: 9 additions & 5 deletions src/scikit_build_core/setuptools/build_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
prepare_metadata_for_build_wheel,
)

from ..builder.get_requires import GetRequires

if hasattr(setuptools.build_meta, "build_editable"):
from setuptools.build_meta import build_editable

Expand Down Expand Up @@ -38,14 +40,18 @@ def get_requires_for_build_sdist(
setuptools_reqs = setuptools.build_meta.get_requires_for_build_sdist(
config_settings
)
return [*setuptools_reqs]
requires = GetRequires(config_settings)

# These are only injected if cmake is required for the SDist step
cmake_requires = (
[*requires.cmake(), *requires.ninja()] if requires.settings.sdist.cmake else []
)
return [*setuptools_reqs, *cmake_requires]


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(
Expand All @@ -60,8 +66,6 @@ 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(
config_settings
Expand Down
11 changes: 11 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,17 @@ def package_simplest_c(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Packa
return package


@pytest.fixture()
def package_sdist_config(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> PackageInfo:
package = PackageInfo(
"sdist_config",
)
process_package(package, tmp_path, monkeypatch)
return package


def pytest_collection_modifyitems(items: list[pytest.Item]) -> None:
for item in items:
# Ensure all tests using virtualenv are marked as such
Expand Down
1 change: 1 addition & 0 deletions tests/packages/sdist_config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/pybind11
22 changes: 22 additions & 0 deletions tests/packages/sdist_config/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.15...3.27)
project(sdist_config LANGUAGES CXX)

include(FetchContent)

if(NOT SKBUILD_STATE STREQUAL "sdist"
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/pybind11/CMakeLists.txt")
message(STATUS "Using integrated pybind11")
set(FETCHCONTENT_FULLY_DISCONNECTED ON)
endif()

FetchContent_Declare(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
GIT_TAG v2.11.1
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/pybind11)

set(PYBIND11_FINDPYTHON ON)
FetchContent_MakeAvailable(pybind11)

pybind11_add_module(sdist_config main.cpp)
install(TARGETS sdist_config DESTINATION .)
7 changes: 7 additions & 0 deletions tests/packages/sdist_config/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <pybind11/pybind11.h>

namespace py = pybind11;

PYBIND11_MODULE(sdist_config, m) {
m.def("life", []() { return 42; });
}
18 changes: 18 additions & 0 deletions tests/packages/sdist_config/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[build-system]
requires = ["scikit-build-core[pyproject]"]
build-backend = "scikit_build_core.build"

[project]
name = "sdist_config"
version = "0.1.0"

[tool.scikit-build]
sdist.cmake = true
sdist.include = [
"pybind11/tools",
"pybind11/include",
"pybind11/CMakeLists.txt",
]
wheel.license-files = []
wheel.packages = []
cmake.define.FETCHCONTENT_QUIET = false
6 changes: 6 additions & 0 deletions tests/test_get_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ def test_get_requires_for_build_sdist(fp):
assert set(get_requires_for_build_sdist({})) == {"pathspec", "pyproject_metadata"}


def test_get_requires_for_build_sdist_cmake(fp):
expected = {"pathspec", "pyproject_metadata", "cmake>=3.15", *ninja}
fp.register([Path("cmake/path"), "--version"], stdout="3.14.0")
assert set(get_requires_for_build_sdist({"sdist.cmake": "True"})) == expected


def test_get_requires_for_build_wheel(fp):
expected = {"pathspec", "pyproject_metadata", "cmake>=3.15", *ninja}
fp.register([Path("cmake/path"), "--version"], stdout="3.14.0")
Expand Down
Loading