3737 find_compatible_wheel ,
3838 get_pip_version ,
3939)
40- from ..venv import constraint_flags , virtualenv
40+ from ..venv import constraint_flags , find_uv , virtualenv
4141from .macos import install_cpython as install_build_cpython
4242
4343
@@ -151,6 +151,7 @@ def cross_virtualenv(
151151 venv_path : Path ,
152152 dependency_constraint : Path | None ,
153153 xbuild_tools : Sequence [str ] | None ,
154+ build_frontend : str ,
154155) -> dict [str , str ]:
155156 """Create a cross-compilation virtual environment.
156157
@@ -181,13 +182,15 @@ def cross_virtualenv(
181182 :param xbuild_tools: A list of executable names (without paths) that are
182183 on the path, but must be preserved in the cross environment.
183184 """
185+ use_uv = build_frontend == "build[uv]"
186+
184187 # Create an initial macOS virtual environment
185188 env = virtualenv (
186189 py_version ,
187190 build_python ,
188191 venv_path ,
189192 dependency_constraint ,
190- use_uv = False ,
193+ use_uv = use_uv ,
191194 )
192195
193196 # Convert the macOS virtual environment into an iOS virtual environment
@@ -282,9 +285,12 @@ def setup_python(
282285 build_frontend : BuildFrontendName ,
283286 xbuild_tools : Sequence [str ] | None ,
284287) -> 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" ]
288294
289295 # An iOS environment requires 2 python installs - one for the build machine
290296 # (macOS), and one for the target (iOS). We'll only ever interact with the
@@ -334,6 +340,7 @@ def setup_python(
334340 venv_path = venv_path ,
335341 dependency_constraint = dependency_constraint ,
336342 xbuild_tools = xbuild_tools ,
343+ build_frontend = build_frontend ,
337344 )
338345 venv_bin_path = venv_path / "bin"
339346 assert venv_bin_path .exists ()
@@ -343,16 +350,17 @@ def setup_python(
343350
344351 # upgrade pip to the version matching our constraints
345352 # 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+ )
356364
357365 # Apply our environment after pip is ready
358366 env = environment .as_dictionary (prev_environment = env )
@@ -370,17 +378,18 @@ def setup_python(
370378 call ("python" , "--version" , env = env )
371379
372380 # 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 )
384393
385394 # Ensure that IPHONEOS_DEPLOYMENT_TARGET is set in the environment
386395 env .setdefault ("IPHONEOS_DEPLOYMENT_TARGET" , "13.0" )
@@ -392,13 +401,22 @@ def setup_python(
392401 pass
393402 case "build" :
394403 call (
395- " pip" ,
404+ * pip ,
396405 "install" ,
397406 "--upgrade" ,
398407 "build[virtualenv]" ,
399408 * constraint_flags (dependency_constraint ),
400409 env = env ,
401410 )
411+ case "build[uv]" :
412+ call (
413+ * pip ,
414+ "install" ,
415+ "--upgrade" ,
416+ "build" ,
417+ * constraint_flags (dependency_constraint ),
418+ env = env ,
419+ )
402420 case _:
403421 assert_never (build_frontend )
404422
@@ -438,10 +456,12 @@ def build(options: Options, tmp_path: Path) -> None:
438456 for config in python_configurations :
439457 build_options = options .build_options (config .identifier )
440458 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" ]
445465
446466 log .build_start (config .identifier )
447467
@@ -461,7 +481,6 @@ def build(options: Options, tmp_path: Path) -> None:
461481 build_frontend = build_frontend .name ,
462482 xbuild_tools = build_options .xbuild_tools ,
463483 )
464- pip_version = get_pip_version (env )
465484
466485 compatible_wheel = find_compatible_wheel (built_wheels , config .identifier )
467486 if compatible_wheel :
@@ -490,7 +509,9 @@ def build(options: Options, tmp_path: Path) -> None:
490509 )
491510
492511 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
494515 if constraints_path :
495516 combine_constraints (build_env , constraints_path , None )
496517
@@ -521,6 +542,18 @@ def build(options: Options, tmp_path: Path) -> None:
521542 * extra_flags ,
522543 env = build_env ,
523544 )
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+ )
524557 case _:
525558 assert_never (build_frontend )
526559
@@ -582,20 +615,35 @@ def build(options: Options, tmp_path: Path) -> None:
582615 ios_version = test_env ["IPHONEOS_DEPLOYMENT_TARGET" ]
583616 platform_tag = f"ios_{ ios_version .replace ('.' , '_' )} _{ config .arch } _{ config .sdk } "
584617
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+ )
599647
600648 log .step ("Running test suite..." )
601649
0 commit comments