1
+ """
2
+ Build backend for gpt-oss that supports two modes:
3
+
4
+ 1) Default (pure wheel for PyPI)
5
+ - Delegates to setuptools.build_meta.
6
+ - Produces a py3-none-any wheel so PyPI accepts it (no linux_x86_64 tag).
7
+
8
+ 2) Optional Metal/C extension build (local only)
9
+ - If the environment variable GPTOSS_BUILD_METAL is set to a truthy value
10
+ (1/true/on/yes), delegates to scikit_build_core.build.
11
+ - Dynamically injects build requirements (scikit-build-core, cmake, ninja,
12
+ pybind11) only for this mode.
13
+
14
+ Why this is needed
15
+ - PyPI rejects Linux wheels tagged linux_x86_64; manylinux/musllinux is required
16
+ for binary wheels. We ship a pure wheel by default, but still allow developers
17
+ to build/install the native Metal backend locally when needed.
18
+
19
+ Typical usage
20
+ - Publish pure wheel: `python -m build` (do not set GPTOSS_BUILD_METAL).
21
+ - Local Metal dev: `GPTOSS_BUILD_METAL=1 pip install -e ".[metal]"`.
22
+ - CI: keep GPTOSS_BUILD_METAL unset for releases; set it in internal jobs that
23
+ exercise the extension.
24
+
25
+ Notes
26
+ - The base package remains importable without the extension. The Metal backend
27
+ is only used when `gpt_oss.metal` is explicitly imported.
28
+ - This file is discovered via `backend-path = ["_build"]` and
29
+ `build-backend = "gpt_oss_build_backend.backend"` in pyproject.toml.
30
+ """
31
+ import os
32
+ from importlib import import_module
33
+ from typing import Any , Mapping , Sequence
34
+
35
+
36
+ TRUE_VALUES = {"1" , "true" , "TRUE" , "on" , "ON" , "yes" , "YES" }
37
+
38
+
39
+ def _use_metal_backend () -> bool :
40
+ return str (os .environ .get ("GPTOSS_BUILD_METAL" , "" )).strip () in TRUE_VALUES
41
+
42
+
43
+ def _setuptools_backend ():
44
+ from setuptools import build_meta as _bm # type: ignore
45
+
46
+ return _bm
47
+
48
+
49
+ def _scikit_build_backend ():
50
+ return import_module ("scikit_build_core.build" )
51
+
52
+
53
+ def _backend ():
54
+ return _scikit_build_backend () if _use_metal_backend () else _setuptools_backend ()
55
+
56
+
57
+ # Required PEP 517 hooks
58
+
59
+ def build_wheel (
60
+ wheel_directory : str ,
61
+ config_settings : Mapping [str , Any ] | None = None ,
62
+ metadata_directory : str | None = None ,
63
+ ) -> str :
64
+ return _backend ().build_wheel (wheel_directory , config_settings , metadata_directory )
65
+
66
+
67
+ def build_sdist (
68
+ sdist_directory : str , config_settings : Mapping [str , Any ] | None = None
69
+ ) -> str :
70
+ return _backend ().build_sdist (sdist_directory , config_settings )
71
+
72
+
73
+ def prepare_metadata_for_build_wheel (
74
+ metadata_directory : str , config_settings : Mapping [str , Any ] | None = None
75
+ ) -> str :
76
+ # Fallback if backend doesn't implement it
77
+ be = _backend ()
78
+ fn = getattr (be , "prepare_metadata_for_build_wheel" , None )
79
+ if fn is None :
80
+ # setuptools exposes it; scikit-build-core may not. Defer to building a wheel for metadata.
81
+ return _setuptools_backend ().prepare_metadata_for_build_wheel (
82
+ metadata_directory , config_settings
83
+ )
84
+ return fn (metadata_directory , config_settings )
85
+
86
+
87
+ # Optional hooks
88
+
89
+ def build_editable (
90
+ editable_directory : str , config_settings : Mapping [str , Any ] | None = None
91
+ ) -> str :
92
+ be = _backend ()
93
+ fn = getattr (be , "build_editable" , None )
94
+ if fn is None :
95
+ # setuptools implements build_editable; if not available, raise the standard error
96
+ raise RuntimeError ("Editable installs not supported by the selected backend" )
97
+ return fn (editable_directory , config_settings )
98
+
99
+
100
+ def get_requires_for_build_wheel (
101
+ config_settings : Mapping [str , Any ] | None = None ,
102
+ ) -> Sequence [str ]:
103
+ if _use_metal_backend ():
104
+ # Add dynamic build requirements only when building the Metal backend
105
+ return [
106
+ "scikit-build-core>=0.10" ,
107
+ "pybind11>=2.12" ,
108
+ "cmake>=3.26" ,
109
+ "ninja" ,
110
+ ]
111
+ # setuptools usually returns []
112
+ return list (_setuptools_backend ().get_requires_for_build_wheel (config_settings ))
113
+
114
+
115
+ def get_requires_for_build_sdist (
116
+ config_settings : Mapping [str , Any ] | None = None ,
117
+ ) -> Sequence [str ]:
118
+ # No special requirements for SDist
119
+ be = _backend ()
120
+ fn = getattr (be , "get_requires_for_build_sdist" , None )
121
+ if fn is None :
122
+ return []
123
+ return list (fn (config_settings ))
124
+
125
+
126
+ def get_requires_for_build_editable (
127
+ config_settings : Mapping [str , Any ] | None = None ,
128
+ ) -> Sequence [str ]:
129
+ if _use_metal_backend ():
130
+ return [
131
+ "scikit-build-core>=0.10" ,
132
+ "pybind11>=2.12" ,
133
+ "cmake>=3.26" ,
134
+ "ninja" ,
135
+ ]
136
+ be = _setuptools_backend ()
137
+ fn = getattr (be , "get_requires_for_build_editable" , None )
138
+ if fn is None :
139
+ return []
140
+ return list (fn (config_settings ))
0 commit comments