16
16
The {obj}`config_settings` macro is used to create the config setting targets
17
17
that can be used in the {obj}`pkg_aliases` macro for selecting the compatible
18
18
repositories.
19
-
20
- Bazel's selects work by selecting the most-specialized configuration setting
21
- that matches the target platform, which is further described in [bazel documentation][docs].
22
- We can leverage this fact to ensure that the most specialized matches are used
23
- by default with the users being able to configure string_flag values to select
24
- the less specialized ones.
25
-
26
- [docs]: https://bazel.build/docs/configurable-attributes
27
-
28
- The config settings in the order from the least specialized to the most
29
- specialized is as follows:
30
- * `:is_cp3<minor_version><suffix>`
31
- * `:is_cp3<minor_version>_sdist<suffix>`
32
- * `:is_cp3<minor_version>_py_none_any<suffix>`
33
- * `:is_cp3<minor_version>_py3_none_any<suffix>`
34
- * `:is_cp3<minor_version>_py3_abi3_any<suffix>`
35
- * `:is_cp3<minor_version>_none_any<suffix>`
36
- * `:is_cp3<minor_version>_any_any<suffix>`
37
- * `:is_cp3<minor_version>_cp3<minor_version>_any<suffix>` and `:is_cp3<minor_version>_cp3<minor_version>t_any<suffix>`
38
- * `:is_cp3<minor_version>_py_none_<platform_suffix>`
39
- * `:is_cp3<minor_version>_py3_none_<platform_suffix>`
40
- * `:is_cp3<minor_version>_py3_abi3_<platform_suffix>`
41
- * `:is_cp3<minor_version>_none_<platform_suffix>`
42
- * `:is_cp3<minor_version>_abi3_<platform_suffix>`
43
- * `:is_cp3<minor_version>_cp3<minor_version>_<platform_suffix>` and `:is_cp3<minor_version>_cp3<minor_version>t_<platform_suffix>`
44
-
45
- Optionally instead of `<minor_version>` there sometimes may be `<minor_version>.<micro_version>` used in order to fully specify the versions
46
-
47
- The specialization of free-threaded vs non-free-threaded wheels is the same as
48
- they are just variants of each other. The same goes for the specialization of
49
- `musllinux` vs `manylinux`.
50
-
51
- The goal of this macro is to provide config settings that provide unambigous
52
- matches if any pair of them is used together for any target configuration
53
- setting. We achieve this by using dummy internal `flag_values` keys to force the
54
- items further down the list to appear to be more specialized than the ones above.
55
-
56
- What is more, the names of the config settings are as similar to the platform wheel
57
- specification as possible. How the wheel names map to the config setting names defined
58
- in here is described in {obj}`pkg_aliases` documentation.
59
-
60
- :::{note}
61
- Right now the specialization of adjacent config settings where one is with
62
- `constraint_values` and one is without is ambiguous. I.e. `py_none_any` and
63
- `sdist_linux_x86_64` have the same specialization from bazel point of view
64
- because one has one `flag_value` entry and `constraint_values` and the
65
- other has 2 flag_value entries. And unfortunately there is no way to disambiguate
66
- it, because we are essentially in two dimensions here (`flag_values` and
67
- `constraint_values`). Hence, when using the `config_settings` from here,
68
- either have all of them with empty `suffix` or all of them with a non-empty
69
- suffix.
70
- :::
71
19
"""
72
20
73
21
load ("@bazel_skylib//lib:selects.bzl" , "selects" )
74
- load ("//python/private:flags.bzl" , "LibcFlag" )
75
- load (":flags.bzl" , "INTERNAL_FLAGS" , "UniversalWhlFlag" )
76
-
77
- FLAGS = struct (
78
- ** {
79
- f : str (Label ("//python/config_settings:" + f ))
80
- for f in [
81
- "is_pip_whl_auto" ,
82
- "is_pip_whl_no" ,
83
- "is_pip_whl_only" ,
84
- "_is_py_freethreaded_yes" ,
85
- "_is_py_freethreaded_no" ,
86
- "pip_whl_glibc_version" ,
87
- "pip_whl_muslc_version" ,
88
- "pip_whl_osx_arch" ,
89
- "pip_whl_osx_version" ,
90
- "py_linux_libc" ,
91
- "python_version" ,
92
- ]
93
- }
94
- )
95
-
96
- _DEFAULT = "//conditions:default"
97
- _INCOMPATIBLE = "@platforms//:incompatible"
98
-
99
- # Here we create extra string flags that are just to work with the select
100
- # selecting the most specialized match. We don't allow the user to change
101
- # them.
102
- _flags = struct (
103
- ** {
104
- f : str (Label ("//python/config_settings:_internal_pip_" + f ))
105
- for f in INTERNAL_FLAGS
106
- }
107
- )
108
22
109
23
def config_settings (
110
24
* ,
111
25
python_versions = [],
112
- glibc_versions = [],
113
- muslc_versions = [],
114
- osx_versions = [],
115
26
name = None ,
116
27
platform_config_settings = {},
117
28
** kwargs ):
@@ -121,12 +32,6 @@ def config_settings(
121
32
name (str): Currently unused.
122
33
python_versions (list[str]): The list of python versions to configure
123
34
config settings for.
124
- glibc_versions (list[str]): The list of glibc version of the wheels to
125
- configure config settings for.
126
- muslc_versions (list[str]): The list of musl version of the wheels to
127
- configure config settings for.
128
- osx_versions (list[str]): The list of OSX OS versions to configure
129
- config settings for.
130
35
platform_config_settings: {type}`dict[str, list[str]]` the constraint
131
36
values to use instead of the default ones. Key are platform names
132
37
(a human-friendly platform string). Values are lists of
@@ -135,48 +40,22 @@ def config_settings(
135
40
{obj}`native`.
136
41
"""
137
42
138
- glibc_versions = ["" ] + glibc_versions
139
- muslc_versions = ["" ] + muslc_versions
140
- osx_versions = ["" ] + osx_versions
141
43
target_platforms = {
142
44
"" : [],
143
- # TODO @aignas 2025-06-15: allowing universal2 and platform specific wheels in one
144
- # closure is making things maybe a little bit too complicated.
145
- "osx_universal2" : ["@platforms//os:osx" ],
146
45
} | platform_config_settings
147
46
148
47
for python_version in python_versions :
149
48
for platform_name , config_settings in target_platforms .items ():
150
49
suffix = "_{}" .format (platform_name ) if platform_name else ""
151
- os , _ , cpu = platform_name .partition ("_" )
152
-
153
- # We parse the target settings and if there is a "platforms//os" or
154
- # "platforms//cpu" value in here, we also add it into the constraint_values
155
- #
156
- # this is to ensure that we can still pass all of the unit tests for config
157
- # setting specialization.
158
- constraint_values = []
159
- for setting in config_settings :
160
- setting_label = Label (setting )
161
- if setting_label .repo_name == "platforms" and setting_label .package in ["os" , "cpu" ]:
162
- constraint_values .append (setting )
163
50
164
51
_dist_config_settings (
165
52
suffix = suffix ,
166
- plat_flag_values = _plat_flag_values (
167
- os = os ,
168
- cpu = cpu ,
169
- osx_versions = osx_versions ,
170
- glibc_versions = glibc_versions ,
171
- muslc_versions = muslc_versions ,
172
- ),
173
53
config_settings = config_settings ,
174
- constraint_values = constraint_values ,
175
54
python_version = python_version ,
176
55
** kwargs
177
56
)
178
57
179
- def _dist_config_settings (* , suffix , plat_flag_values , python_version , ** kwargs ):
58
+ def _dist_config_settings (* , suffix , python_version , ** kwargs ):
180
59
flag_values = {
181
60
Label ("//python/config_settings:python_version_major_minor" ): python_version ,
182
61
}
@@ -190,156 +69,11 @@ def _dist_config_settings(*, suffix, plat_flag_values, python_version, **kwargs)
190
69
** kwargs
191
70
)
192
71
193
- flag_values [_flags .dist ] = ""
194
-
195
- # First create an sdist, we will be building upon the flag values, which
196
- # will ensure that each sdist config setting is the least specialized of
197
- # all. However, we need at least one flag value to cover the case where we
198
- # have `sdist` for any platform, hence we have a non-empty `flag_values`
199
- # here.
200
- _dist_config_setting (
201
- name = "{}_sdist{}" .format (prefix , suffix ),
202
- flag_values = flag_values ,
203
- compatible_with = (FLAGS .is_pip_whl_no , FLAGS .is_pip_whl_auto ),
204
- ** kwargs
205
- )
206
-
207
- used_flags = {}
208
-
209
- # NOTE @aignas 2024-12-01: the abi3 is not compatible with freethreaded
210
- # builds as per PEP703 (https://peps.python.org/pep-0703/#backwards-compatibility)
211
- #
212
- # The discussion here also reinforces this notion:
213
- # https://discuss.python.org/t/pep-703-making-the-global-interpreter-lock-optional-3-12-updates/26503/99
214
-
215
- for name , f , compatible_with in [
216
- ("py_none" , _flags .whl , None ),
217
- ("py3_none" , _flags .whl_py3 , None ),
218
- ("py3_abi3" , _flags .whl_py3_abi3 , (FLAGS ._is_py_freethreaded_no ,)),
219
- ("none" , _flags .whl_pycp3x , None ),
220
- ("abi3" , _flags .whl_pycp3x_abi3 , (FLAGS ._is_py_freethreaded_no ,)),
221
- # The below are not specializations of one another, they are variants
222
- (cpv , _flags .whl_pycp3x_abicp , (FLAGS ._is_py_freethreaded_no ,)),
223
- (cpv + "t" , _flags .whl_pycp3x_abicp , (FLAGS ._is_py_freethreaded_yes ,)),
224
- ]:
225
- if (f , compatible_with ) in used_flags :
226
- # This should never happen as all of the different whls should have
227
- # unique flag values
228
- fail ("BUG: the flag {} is attempted to be added twice to the list" .format (f ))
229
- else :
230
- flag_values [f ] = "yes" if f == _flags .whl else ""
231
- used_flags [(f , compatible_with )] = True
232
-
233
- _dist_config_setting (
234
- name = "{}_{}_any{}" .format (prefix , name , suffix ),
235
- flag_values = flag_values ,
236
- compatible_with = compatible_with ,
237
- ** kwargs
238
- )
239
-
240
- generic_flag_values = flag_values
241
- generic_used_flags = used_flags
242
-
243
- for (suffix , flag_values ) in plat_flag_values :
244
- used_flags = {(f , None ): True for f in flag_values } | generic_used_flags
245
- flag_values = flag_values | generic_flag_values
246
-
247
- for name , f , compatible_with in [
248
- ("py_none" , _flags .whl_plat , None ),
249
- ("py3_none" , _flags .whl_plat_py3 , None ),
250
- ("py3_abi3" , _flags .whl_plat_py3_abi3 , (FLAGS ._is_py_freethreaded_no ,)),
251
- ("none" , _flags .whl_plat_pycp3x , None ),
252
- ("abi3" , _flags .whl_plat_pycp3x_abi3 , (FLAGS ._is_py_freethreaded_no ,)),
253
- # The below are not specializations of one another, they are variants
254
- (cpv , _flags .whl_plat_pycp3x_abicp , (FLAGS ._is_py_freethreaded_no ,)),
255
- (cpv + "t" , _flags .whl_plat_pycp3x_abicp , (FLAGS ._is_py_freethreaded_yes ,)),
256
- ]:
257
- if (f , compatible_with ) in used_flags :
258
- # This should never happen as all of the different whls should have
259
- # unique flag values.
260
- fail ("BUG: the flag {} is attempted to be added twice to the list" .format (f ))
261
- else :
262
- flag_values [f ] = ""
263
- used_flags [(f , compatible_with )] = True
264
-
265
- _dist_config_setting (
266
- name = "{}_{}_{}" .format (prefix , name , suffix ),
267
- flag_values = flag_values ,
268
- compatible_with = compatible_with ,
269
- ** kwargs
270
- )
271
-
272
- def _to_version_string (version , sep = "." ):
273
- if not version :
274
- return ""
275
-
276
- return "{}{}{}" .format (version [0 ], sep , version [1 ])
277
-
278
- def _plat_flag_values (os , cpu , osx_versions , glibc_versions , muslc_versions ):
279
- ret = []
280
- if os == "" :
281
- return []
282
- elif os == "windows" :
283
- ret .append (("{}_{}" .format (os , cpu ), {}))
284
- elif os == "osx" :
285
- for osx_version in osx_versions :
286
- flags = {
287
- FLAGS .pip_whl_osx_version : _to_version_string (osx_version ),
288
- }
289
- if cpu != "universal2" :
290
- flags [FLAGS .pip_whl_osx_arch ] = UniversalWhlFlag .ARCH
291
-
292
- if not osx_version :
293
- suffix = "{}_{}" .format (os , cpu )
294
- else :
295
- suffix = "{}_{}_{}" .format (os , _to_version_string (osx_version , "_" ), cpu )
296
-
297
- ret .append ((suffix , flags ))
298
-
299
- elif os == "linux" :
300
- for os_prefix , linux_libc in {
301
- os : LibcFlag .GLIBC ,
302
- "many" + os : LibcFlag .GLIBC ,
303
- "musl" + os : LibcFlag .MUSL ,
304
- }.items ():
305
- if linux_libc == LibcFlag .GLIBC :
306
- libc_versions = glibc_versions
307
- libc_flag = FLAGS .pip_whl_glibc_version
308
- elif linux_libc == LibcFlag .MUSL :
309
- libc_versions = muslc_versions
310
- libc_flag = FLAGS .pip_whl_muslc_version
311
- else :
312
- fail ("Unsupported libc type: {}" .format (linux_libc ))
313
-
314
- for libc_version in libc_versions :
315
- if libc_version and os_prefix == os :
316
- continue
317
- elif libc_version :
318
- suffix = "{}_{}_{}" .format (os_prefix , _to_version_string (libc_version , "_" ), cpu )
319
- else :
320
- suffix = "{}_{}" .format (os_prefix , cpu )
321
-
322
- ret .append ((
323
- suffix ,
324
- {
325
- FLAGS .py_linux_libc : linux_libc ,
326
- libc_flag : _to_version_string (libc_version ),
327
- },
328
- ))
329
- else :
330
- fail ("Unsupported os: {}" .format (os ))
331
-
332
- return ret
333
-
334
- def _dist_config_setting (* , name , compatible_with = None , selects = selects , native = native , config_settings = None , ** kwargs ):
72
+ def _dist_config_setting (* , name , selects = selects , native = native , config_settings = None , ** kwargs ):
335
73
"""A macro to create a target for matching Python binary and source distributions.
336
74
337
75
Args:
338
76
name: The name of the public target.
339
- compatible_with: {type}`tuple[Label]` A collection of config settings that are
340
- compatible with the given dist config setting. For example, if only
341
- non-freethreaded python builds are allowed, add
342
- FLAGS._is_py_freethreaded_no here.
343
77
config_settings: {type}`list[str | Label]` the list of target settings that must
344
78
be matched before we try to evaluate the config_setting that we may create in
345
79
this function.
@@ -352,18 +86,6 @@ def _dist_config_setting(*, name, compatible_with = None, selects = selects, nat
352
86
**kwargs: The kwargs passed to the config_setting rule. Visibility of
353
87
the main alias target is also taken from the kwargs.
354
88
"""
355
- if compatible_with :
356
- dist_config_setting_name = "_" + name
357
- native .alias (
358
- name = name ,
359
- actual = select (
360
- {setting : dist_config_setting_name for setting in compatible_with } | {
361
- _DEFAULT : _INCOMPATIBLE ,
362
- },
363
- ),
364
- visibility = kwargs .get ("visibility" ),
365
- )
366
- name = dist_config_setting_name
367
89
368
90
# first define the config setting that has all of the constraint values
369
91
_name = "_" + name
0 commit comments