37
37
find_compatible_wheel ,
38
38
get_pip_version ,
39
39
)
40
- from ..venv import constraint_flags , virtualenv
40
+ from ..venv import constraint_flags , find_uv , virtualenv
41
41
from .macos import install_cpython as install_build_cpython
42
42
43
43
@@ -151,6 +151,7 @@ def cross_virtualenv(
151
151
venv_path : Path ,
152
152
dependency_constraint : Path | None ,
153
153
xbuild_tools : Sequence [str ] | None ,
154
+ build_frontend : str ,
154
155
) -> dict [str , str ]:
155
156
"""Create a cross-compilation virtual environment.
156
157
@@ -181,13 +182,15 @@ def cross_virtualenv(
181
182
:param xbuild_tools: A list of executable names (without paths) that are
182
183
on the path, but must be preserved in the cross environment.
183
184
"""
185
+ use_uv = build_frontend == "build[uv]"
186
+
184
187
# Create an initial macOS virtual environment
185
188
env = virtualenv (
186
189
py_version ,
187
190
build_python ,
188
191
venv_path ,
189
192
dependency_constraint ,
190
- use_uv = False ,
193
+ use_uv = use_uv ,
191
194
)
192
195
193
196
# Convert the macOS virtual environment into an iOS virtual environment
@@ -282,9 +285,12 @@ def setup_python(
282
285
build_frontend : BuildFrontendName ,
283
286
xbuild_tools : Sequence [str ] | None ,
284
287
) -> tuple [Path , dict [str , str ]]:
285
- if build_frontend == "build[uv]" :
286
- msg = "uv doesn't support iOS"
287
- raise errors .FatalError (msg )
288
+ use_uv = build_frontend == "build[uv]"
289
+ uv_path = find_uv ()
290
+ if use_uv and uv_path is None :
291
+ msg = "uv not found"
292
+ raise AssertionError (msg )
293
+ pip = ["pip" ] if not use_uv else [str (uv_path ), "pip" ]
288
294
289
295
# An iOS environment requires 2 python installs - one for the build machine
290
296
# (macOS), and one for the target (iOS). We'll only ever interact with the
@@ -334,6 +340,7 @@ def setup_python(
334
340
venv_path = venv_path ,
335
341
dependency_constraint = dependency_constraint ,
336
342
xbuild_tools = xbuild_tools ,
343
+ build_frontend = build_frontend ,
337
344
)
338
345
venv_bin_path = venv_path / "bin"
339
346
assert venv_bin_path .exists ()
@@ -343,16 +350,17 @@ def setup_python(
343
350
344
351
# upgrade pip to the version matching our constraints
345
352
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
346
- pip = ["python" , "-m" , "pip" ]
347
- call (
348
- * pip ,
349
- "install" ,
350
- "--upgrade" ,
351
- "pip" ,
352
- * constraint_flags (dependency_constraint ),
353
- env = env ,
354
- cwd = venv_path ,
355
- )
353
+ if not use_uv :
354
+ pip = ["python" , "-m" , "pip" ]
355
+ call (
356
+ * pip ,
357
+ "install" ,
358
+ "--upgrade" ,
359
+ "pip" ,
360
+ * constraint_flags (dependency_constraint ),
361
+ env = env ,
362
+ cwd = venv_path ,
363
+ )
356
364
357
365
# Apply our environment after pip is ready
358
366
env = environment .as_dictionary (prev_environment = env )
@@ -370,17 +378,18 @@ def setup_python(
370
378
call ("python" , "--version" , env = env )
371
379
372
380
# Check what pip version we're on
373
- assert (venv_bin_path / "pip" ).exists ()
374
- which_pip = call ("which" , "pip" , env = env , capture_stdout = True ).strip ()
375
- print (which_pip )
376
- if which_pip != str (venv_bin_path / "pip" ):
377
- msg = (
378
- "cibuildwheel: pip available on PATH doesn't match our installed instance. "
379
- "If you have modified PATH, ensure that you don't overwrite cibuildwheel's "
380
- "entry or insert pip above it."
381
- )
382
- raise errors .FatalError (msg )
383
- call ("pip" , "--version" , env = env )
381
+ if not use_uv :
382
+ assert (venv_bin_path / "pip" ).exists ()
383
+ which_pip = call ("which" , "pip" , env = env , capture_stdout = True ).strip ()
384
+ print (which_pip )
385
+ if which_pip != str (venv_bin_path / "pip" ):
386
+ msg = (
387
+ "cibuildwheel: pip available on PATH doesn't match our installed instance. "
388
+ "If you have modified PATH, ensure that you don't overwrite cibuildwheel's "
389
+ "entry or insert pip above it."
390
+ )
391
+ raise errors .FatalError (msg )
392
+ call ("pip" , "--version" , env = env )
384
393
385
394
# Ensure that IPHONEOS_DEPLOYMENT_TARGET is set in the environment
386
395
env .setdefault ("IPHONEOS_DEPLOYMENT_TARGET" , "13.0" )
@@ -392,13 +401,22 @@ def setup_python(
392
401
pass
393
402
case "build" :
394
403
call (
395
- " pip" ,
404
+ * pip ,
396
405
"install" ,
397
406
"--upgrade" ,
398
407
"build[virtualenv]" ,
399
408
* constraint_flags (dependency_constraint ),
400
409
env = env ,
401
410
)
411
+ case "build[uv]" :
412
+ call (
413
+ * pip ,
414
+ "install" ,
415
+ "--upgrade" ,
416
+ "build" ,
417
+ * constraint_flags (dependency_constraint ),
418
+ env = env ,
419
+ )
402
420
case _:
403
421
assert_never (build_frontend )
404
422
@@ -438,10 +456,12 @@ def build(options: Options, tmp_path: Path) -> None:
438
456
for config in python_configurations :
439
457
build_options = options .build_options (config .identifier )
440
458
build_frontend = build_options .build_frontend
441
- # uv doesn't support iOS
442
- if build_frontend .name == "build[uv]" :
443
- msg = "uv doesn't support iOS"
444
- raise errors .FatalError (msg )
459
+ use_uv = build_frontend .name == "build[uv]"
460
+ uv_path = find_uv ()
461
+ if use_uv and uv_path is None :
462
+ msg = "uv not found"
463
+ raise AssertionError (msg )
464
+ pip = ["pip" ] if not use_uv else [str (uv_path ), "pip" ]
445
465
446
466
log .build_start (config .identifier )
447
467
@@ -461,7 +481,6 @@ def build(options: Options, tmp_path: Path) -> None:
461
481
build_frontend = build_frontend .name ,
462
482
xbuild_tools = build_options .xbuild_tools ,
463
483
)
464
- pip_version = get_pip_version (env )
465
484
466
485
compatible_wheel = find_compatible_wheel (built_wheels , config .identifier )
467
486
if compatible_wheel :
@@ -490,7 +509,9 @@ def build(options: Options, tmp_path: Path) -> None:
490
509
)
491
510
492
511
build_env = env .copy ()
493
- build_env ["VIRTUALENV_PIP" ] = pip_version
512
+ if not use_uv :
513
+ pip_version = get_pip_version (env )
514
+ build_env ["VIRTUALENV_PIP" ] = pip_version
494
515
if constraints_path :
495
516
combine_constraints (build_env , constraints_path , None )
496
517
@@ -521,6 +542,18 @@ def build(options: Options, tmp_path: Path) -> None:
521
542
* extra_flags ,
522
543
env = build_env ,
523
544
)
545
+ case "build[uv]" :
546
+ call (
547
+ "python" ,
548
+ "-m" ,
549
+ "build" ,
550
+ build_options .package_dir ,
551
+ "--wheel" ,
552
+ "--installer=uv" ,
553
+ f"--outdir={ built_wheel_dir } " ,
554
+ * extra_flags ,
555
+ env = build_env ,
556
+ )
524
557
case _:
525
558
assert_never (build_frontend )
526
559
@@ -582,20 +615,35 @@ def build(options: Options, tmp_path: Path) -> None:
582
615
ios_version = test_env ["IPHONEOS_DEPLOYMENT_TARGET" ]
583
616
platform_tag = f"ios_{ ios_version .replace ('.' , '_' )} _{ config .arch } _{ config .sdk } "
584
617
585
- call (
586
- "python" ,
587
- "-m" ,
588
- "pip" ,
589
- "install" ,
590
- "--only-binary=:all:" ,
591
- "--platform" ,
592
- platform_tag ,
593
- "--target" ,
594
- testbed_path / "iOSTestbed" / "app_packages" ,
595
- f"{ test_wheel } { build_options .test_extras } " ,
596
- * build_options .test_requires ,
597
- env = test_env ,
598
- )
618
+ if use_uv :
619
+ call (
620
+ * pip ,
621
+ "install" ,
622
+ "--only-binary=:all:" ,
623
+ "--platform" ,
624
+ platform_tag ,
625
+ "--target" ,
626
+ testbed_path / "iOSTestbed" / "app_packages" ,
627
+ f"{ test_wheel } { build_options .test_extras } " ,
628
+ * build_options .test_requires ,
629
+ env = test_env ,
630
+ )
631
+
632
+ else :
633
+ call (
634
+ "python" ,
635
+ "-m" ,
636
+ "pip" ,
637
+ "install" ,
638
+ "--only-binary=:all:" ,
639
+ "--platform" ,
640
+ platform_tag ,
641
+ "--target" ,
642
+ testbed_path / "iOSTestbed" / "app_packages" ,
643
+ f"{ test_wheel } { build_options .test_extras } " ,
644
+ * build_options .test_requires ,
645
+ env = test_env ,
646
+ )
599
647
600
648
log .step ("Running test suite..." )
601
649
0 commit comments