Skip to content

Commit 196c8ce

Browse files
authored
feat: SDist CMake option (#454)
Working on #392. - [x] Add to setuptools build_meta, too For later PR: ```diff + libpl = sysconfig.get_config_var("LIBPL") + library = sysconfig.get_config_var("LIBRARY") + + if libpl and library and Path(libpl).joinpath(library).is_file(): + assert isinstance(libpl, str) + assert isinstance(library, str) + return Path(libpl) / library ``` Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
1 parent d8e622f commit 196c8ce

File tree

18 files changed

+267
-15
lines changed

18 files changed

+267
-15
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ sdist.exclude = []
178178
# value if not set.
179179
sdist.reproducible = true
180180

181+
# If set to True, CMake will be run before building the SDist.
182+
sdist.cmake = false
183+
181184
# A list of packages to auto-copy into the wheel. If this is not set, it will
182185
# default to the first of ``src/<package>`` or ``<package>`` if they exist. The
183186
# prefix(s) will be stripped from the package name inside the wheel.
@@ -195,9 +198,11 @@ wheel.py-api = ""
195198
# (before 21.0.1) find the correct wheel.
196199
wheel.expand-macos-universal-tags = false
197200

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

203208
# A list of license files to include in the wheel. Supports glob patterns.

docs/configuration.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,52 @@ reproducible builds if you prefer, however:
152152
sdist.reproducible = false
153153
```
154154

155+
You can also request CMake to run during this step:
156+
157+
```toml
158+
[tool.scikit-build]
159+
sdist.cmake = true
160+
```
161+
162+
:::{note}
163+
164+
If you do this, you'll want to have some artifact from the configure in your
165+
source directory; for example:
166+
167+
```cmake
168+
include(FetchContent)
169+
170+
if(NOT SKBUILD_STATE STREQUAL "sdist"
171+
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/pybind11/CMakeLists.txt")
172+
message(STATUS "Using integrated pybind11")
173+
set(FETCHCONTENT_FULLY_DISCONNECTED ON)
174+
endif()
175+
176+
FetchContent_Declare(
177+
pybind11
178+
GIT_REPOSITORY https://github.com/pybind/pybind11.git
179+
GIT_TAG v2.11.1
180+
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/pybind11)
181+
182+
set(PYBIND11_FINDPYTHON ON)
183+
FetchContent_MakeAvailable(pybind11)
184+
```
185+
186+
The `/pybind11` directory is in the `.gitignore` and important parts are in
187+
`sdist.include`:
188+
189+
```toml
190+
[tool.scikit-build]
191+
sdist.cmake = true
192+
sdist.include = [
193+
"pybind11/tools",
194+
"pybind11/include",
195+
"pybind11/CMakeLists.txt",
196+
]
197+
```
198+
199+
:::
200+
155201
## Customizing the built wheel
156202

157203
The wheel will automatically look for Python packages at `<package_name>` and

noxfile.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010

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

13-
nox.options.sessions = ["lint", "pylint", "tests"]
13+
nox.options.sessions = [
14+
"lint",
15+
"pylint",
16+
"generate_schema",
17+
"readme",
18+
"build_api_docs",
19+
"tests",
20+
"test_doc_examples",
21+
]
1422

1523

1624
@nox.session(reuse_venv=True)

src/scikit_build_core/build/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,15 @@ def get_requires_for_build_sdist(
104104

105105
requires = GetRequires(config_settings)
106106

107+
# These are only injected if cmake is required for the SDist step
108+
cmake_requires = (
109+
[*requires.cmake(), *requires.ninja()] if requires.settings.sdist.cmake else []
110+
)
111+
107112
return [
108113
"pathspec",
109114
"pyproject_metadata",
115+
*cmake_requires,
110116
*requires.dynamic_metadata(),
111117
]
112118

src/scikit_build_core/build/sdist.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ..settings.skbuild_read_settings import SettingsReader
1919
from ._file_processor import each_unignored_file
2020
from ._init import setup_logging
21+
from .wheel import _build_wheel_impl
2122

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

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

114+
if settings.sdist.cmake:
115+
_build_wheel_impl(
116+
None, config_settings, None, exit_after_config=True, editable=False
117+
)
118+
113119
sdist_dir.mkdir(parents=True, exist_ok=True)
114120
with contextlib.ExitStack() as stack:
115121
gzip_container = stack.enter_context(

src/scikit_build_core/build/wheel.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def _build_wheel_impl(
7171
config_settings: dict[str, list[str] | str] | None,
7272
metadata_directory: str | None,
7373
*,
74+
exit_after_config: bool = False,
7475
editable: bool,
7576
) -> WheelImplReturn:
7677
"""
@@ -97,6 +98,8 @@ def _build_wheel_impl(
9798
action = "editable" if editable else "wheel"
9899
if wheel_directory is None:
99100
action = f"metadata_{action}"
101+
if exit_after_config:
102+
action = "sdist"
100103

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

@@ -176,7 +179,7 @@ def _build_wheel_impl(
176179
config=config,
177180
)
178181

179-
if wheel_directory is None:
182+
if wheel_directory is None and not exit_after_config:
180183
if metadata_directory is None:
181184
msg = "metadata_directory must be specified if wheel_directory is None"
182185
raise AssertionError(msg)
@@ -209,6 +212,11 @@ def _build_wheel_impl(
209212
version=metadata.version,
210213
)
211214

215+
if exit_after_config:
216+
return WheelImplReturn("")
217+
218+
assert wheel_directory is not None
219+
212220
generator = builder.config.env.get(
213221
"CMAKE_GENERATOR",
214222
"MSVC"

src/scikit_build_core/builder/get_requires.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
)
2020
from ..resources import resources
2121
from ..settings._load_provider import load_provider
22+
from ..settings.skbuild_model import ScikitBuildSettings
2223
from ..settings.skbuild_read_settings import SettingsReader
2324

2425
__all__ = ["GetRequires"]
@@ -48,8 +49,12 @@ def __post_init__(self) -> None:
4849
"pyproject.toml", self.config_settings
4950
).settings
5051

52+
@property
53+
def settings(self) -> ScikitBuildSettings:
54+
return self._settings
55+
5156
def cmake(self) -> Generator[str, None, None]:
52-
cmake_min = self._settings.cmake.minimum_version
57+
cmake_min = self.settings.cmake.minimum_version
5358
cmake = best_program(
5459
get_cmake_programs(module=False), minimum_version=cmake_min
5560
)
@@ -73,7 +78,7 @@ def ninja(self) -> Generator[str, None, None]:
7378
if os.environ.get("CMAKE_MAKE_PROGRAM", ""):
7479
return
7580

76-
ninja_min = self._settings.ninja.minimum_version
81+
ninja_min = self.settings.ninja.minimum_version
7782
ninja = best_program(
7883
get_ninja_programs(module=False), minimum_version=ninja_min
7984
)
@@ -82,7 +87,7 @@ def ninja(self) -> Generator[str, None, None]:
8287
return
8388

8489
if (
85-
self._settings.ninja.make_fallback
90+
self.settings.ninja.make_fallback
8691
and not is_known_platform(known_wheels("ninja"))
8792
and list(get_make_programs())
8893
):
@@ -93,7 +98,7 @@ def ninja(self) -> Generator[str, None, None]:
9398
yield f"ninja>={ninja_min}"
9499

95100
def dynamic_metadata(self) -> Generator[str, None, None]:
96-
for dynamic_metadata in self._settings.metadata.values():
101+
for dynamic_metadata in self.settings.metadata.values():
97102
if "provider" in dynamic_metadata:
98103
config = dynamic_metadata.copy()
99104
provider = config.pop("provider")

src/scikit_build_core/resources/scikit-build.schema.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@
110110
"type": "boolean",
111111
"default": true,
112112
"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."
113+
},
114+
"cmake": {
115+
"type": "boolean",
116+
"default": false,
117+
"description": "If set to True, CMake will be run before building the SDist."
113118
}
114119
}
115120
},
@@ -137,7 +142,7 @@
137142
"install-dir": {
138143
"type": "string",
139144
"default": "",
140-
"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\"."
145+
"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\"."
141146
},
142147
"license-files": {
143148
"type": "array",

src/scikit_build_core/settings/skbuild_model.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ class SDistSettings:
113113
fixed value if not set.
114114
"""
115115

116+
cmake: bool = False
117+
"""
118+
If set to True, CMake will be run before building the SDist.
119+
"""
120+
116121

117122
@dataclasses.dataclass
118123
class WheelSettings:
@@ -142,6 +147,8 @@ class WheelSettings:
142147
install_dir: str = ""
143148
"""
144149
The install directory for the wheel. This is relative to the platlib root.
150+
You might set this to the package name. The original dir is still at
151+
SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available).
145152
EXPERIMENTAL: An absolute path will be one level higher than the platlib
146153
root, giving access to "/platlib", "/data", "/headers", and "/scripts".
147154
"""

src/scikit_build_core/setuptools/build_meta.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
prepare_metadata_for_build_wheel,
88
)
99

10+
from ..builder.get_requires import GetRequires
11+
1012
if hasattr(setuptools.build_meta, "build_editable"):
1113
from setuptools.build_meta import build_editable
1214

@@ -38,14 +40,18 @@ def get_requires_for_build_sdist(
3840
setuptools_reqs = setuptools.build_meta.get_requires_for_build_sdist(
3941
config_settings
4042
)
41-
return [*setuptools_reqs]
43+
requires = GetRequires(config_settings)
44+
45+
# These are only injected if cmake is required for the SDist step
46+
cmake_requires = (
47+
[*requires.cmake(), *requires.ninja()] if requires.settings.sdist.cmake else []
48+
)
49+
return [*setuptools_reqs, *cmake_requires]
4250

4351

4452
def get_requires_for_build_wheel(
4553
config_settings: dict[str, str | list[str]] | None = None
4654
) -> list[str]:
47-
from ..builder.get_requires import GetRequires
48-
4955
requires = GetRequires(config_settings)
5056

5157
setuptools_reqs = setuptools.build_meta.get_requires_for_build_wheel(
@@ -60,8 +66,6 @@ def get_requires_for_build_wheel(
6066
def get_requires_for_build_editable(
6167
config_settings: dict[str, str | list[str]] | None = None
6268
) -> list[str]:
63-
from ..builder.get_requires import GetRequires
64-
6569
requires = GetRequires(config_settings)
6670
setuptools_reqs = setuptools.build_meta.get_requires_for_build_editable(
6771
config_settings

0 commit comments

Comments
 (0)