3131from ..util .packaging import (
3232 find_compatible_wheel ,
3333)
34- from ..venv import constraint_flags , virtualenv
34+ from ..venv import constraint_flags , find_uv , virtualenv
3535from .macos import install_cpython as install_build_cpython
3636
3737if TYPE_CHECKING :
@@ -153,6 +153,7 @@ def cross_virtualenv(
153153 venv_path : Path ,
154154 dependency_constraint : Path | None ,
155155 xbuild_tools : Sequence [str ] | None ,
156+ build_frontend : str ,
156157) -> dict [str , str ]:
157158 """Create a cross-compilation virtual environment.
158159
@@ -183,13 +184,15 @@ def cross_virtualenv(
183184 :param xbuild_tools: A list of executable names (without paths) that are
184185 on the path, but must be preserved in the cross environment.
185186 """
187+ use_uv = build_frontend == "build[uv]"
188+
186189 # Create an initial macOS virtual environment
187190 env = virtualenv (
188191 py_version ,
189192 build_python ,
190193 venv_path ,
191194 dependency_constraint ,
192- use_uv = False ,
195+ use_uv = use_uv ,
193196 )
194197
195198 # Convert the macOS virtual environment into an iOS virtual environment
@@ -284,9 +287,12 @@ def setup_python(
284287 build_frontend : BuildFrontendName ,
285288 xbuild_tools : Sequence [str ] | None ,
286289) -> tuple [Path , dict [str , str ]]:
287- if build_frontend == "build[uv]" :
288- msg = "uv doesn't support iOS"
289- raise errors .FatalError (msg )
290+ use_uv = build_frontend == "build[uv]"
291+ uv_path = find_uv ()
292+ if use_uv and uv_path is None :
293+ msg = "uv not found"
294+ raise AssertionError (msg )
295+ pip = ["pip" ] if not use_uv else [str (uv_path ), "pip" ]
290296
291297 # An iOS environment requires 2 python installs - one for the build machine
292298 # (macOS), and one for the target (iOS). We'll only ever interact with the
@@ -336,6 +342,7 @@ def setup_python(
336342 venv_path = venv_path ,
337343 dependency_constraint = dependency_constraint ,
338344 xbuild_tools = xbuild_tools ,
345+ build_frontend = build_frontend ,
339346 )
340347 venv_bin_path = venv_path / "bin"
341348 assert venv_bin_path .exists ()
@@ -345,16 +352,17 @@ def setup_python(
345352
346353 # upgrade pip to the version matching our constraints
347354 # if necessary, reinstall it to ensure that it's available on PATH as 'pip'
348- pip = ["python" , "-m" , "pip" ]
349- call (
350- * pip ,
351- "install" ,
352- "--upgrade" ,
353- "pip" ,
354- * constraint_flags (dependency_constraint ),
355- env = env ,
356- cwd = venv_path ,
357- )
355+ if not use_uv :
356+ pip = ["python" , "-m" , "pip" ]
357+ call (
358+ * pip ,
359+ "install" ,
360+ "--upgrade" ,
361+ "pip" ,
362+ * constraint_flags (dependency_constraint ),
363+ env = env ,
364+ cwd = venv_path ,
365+ )
358366
359367 # Apply our environment after pip is ready
360368 env = environment .as_dictionary (prev_environment = env )
@@ -372,17 +380,18 @@ def setup_python(
372380 call ("python" , "--version" , env = env )
373381
374382 # Check what pip version we're on
375- assert (venv_bin_path / "pip" ).exists ()
376- which_pip = call ("which" , "pip" , env = env , capture_stdout = True ).strip ()
377- print (which_pip )
378- if which_pip != str (venv_bin_path / "pip" ):
379- msg = (
380- "cibuildwheel: pip available on PATH doesn't match our installed instance. "
381- "If you have modified PATH, ensure that you don't overwrite cibuildwheel's "
382- "entry or insert pip above it."
383- )
384- raise errors .FatalError (msg )
385- call ("pip" , "--version" , env = env )
383+ if not use_uv :
384+ assert (venv_bin_path / "pip" ).exists ()
385+ which_pip = call ("which" , "pip" , env = env , capture_stdout = True ).strip ()
386+ print (which_pip )
387+ if which_pip != str (venv_bin_path / "pip" ):
388+ msg = (
389+ "cibuildwheel: pip available on PATH doesn't match our installed instance. "
390+ "If you have modified PATH, ensure that you don't overwrite cibuildwheel's "
391+ "entry or insert pip above it."
392+ )
393+ raise errors .FatalError (msg )
394+ call ("pip" , "--version" , env = env )
386395
387396 # Ensure that IPHONEOS_DEPLOYMENT_TARGET is set in the environment
388397 env .setdefault ("IPHONEOS_DEPLOYMENT_TARGET" , "13.0" )
@@ -394,13 +403,22 @@ def setup_python(
394403 pass
395404 case "build" :
396405 call (
397- " pip" ,
406+ * pip ,
398407 "install" ,
399408 "--upgrade" ,
400409 "build[virtualenv]" ,
401410 * constraint_flags (dependency_constraint ),
402411 env = env ,
403412 )
413+ case "build[uv]" :
414+ call (
415+ * pip ,
416+ "install" ,
417+ "--upgrade" ,
418+ "build" ,
419+ * constraint_flags (dependency_constraint ),
420+ env = env ,
421+ )
404422 case _:
405423 assert_never (build_frontend )
406424
@@ -440,10 +458,12 @@ def build(options: Options, tmp_path: Path) -> None:
440458 for config in python_configurations :
441459 build_options = options .build_options (config .identifier )
442460 build_frontend = build_options .build_frontend
443- # uv doesn't support iOS
444- if build_frontend .name == "build[uv]" :
445- msg = "uv doesn't support iOS"
446- raise errors .FatalError (msg )
461+ use_uv = build_frontend .name == "build[uv]"
462+ uv_path = find_uv ()
463+ if use_uv and uv_path is None :
464+ msg = "uv not found"
465+ raise AssertionError (msg )
466+ pip = ["pip" ] if not use_uv else [str (uv_path ), "pip" ]
447467
448468 log .build_start (config .identifier )
449469
@@ -517,6 +537,18 @@ def build(options: Options, tmp_path: Path) -> None:
517537 * extra_flags ,
518538 env = env ,
519539 )
540+ case "build[uv]" :
541+ call (
542+ "python" ,
543+ "-m" ,
544+ "build" ,
545+ build_options .package_dir ,
546+ "--wheel" ,
547+ "--installer=uv" ,
548+ f"--outdir={ built_wheel_dir } " ,
549+ * extra_flags ,
550+ env = env ,
551+ )
520552 case _:
521553 assert_never (build_frontend )
522554
@@ -576,20 +608,35 @@ def build(options: Options, tmp_path: Path) -> None:
576608 ios_version = test_env ["IPHONEOS_DEPLOYMENT_TARGET" ]
577609 platform_tag = f"ios_{ ios_version .replace ('.' , '_' )} _{ config .arch } _{ config .sdk } "
578610
579- call (
580- "python" ,
581- "-m" ,
582- "pip" ,
583- "install" ,
584- "--only-binary=:all:" ,
585- "--platform" ,
586- platform_tag ,
587- "--target" ,
588- testbed_path / "iOSTestbed" / "app_packages" ,
589- f"{ test_wheel } { build_options .test_extras } " ,
590- * build_options .test_requires ,
591- env = test_env ,
592- )
611+ if use_uv :
612+ call (
613+ * pip ,
614+ "install" ,
615+ "--only-binary=:all:" ,
616+ "--platform" ,
617+ platform_tag ,
618+ "--target" ,
619+ testbed_path / "iOSTestbed" / "app_packages" ,
620+ f"{ test_wheel } { build_options .test_extras } " ,
621+ * build_options .test_requires ,
622+ env = test_env ,
623+ )
624+
625+ else :
626+ call (
627+ "python" ,
628+ "-m" ,
629+ "pip" ,
630+ "install" ,
631+ "--only-binary=:all:" ,
632+ "--platform" ,
633+ platform_tag ,
634+ "--target" ,
635+ testbed_path / "iOSTestbed" / "app_packages" ,
636+ f"{ test_wheel } { build_options .test_extras } " ,
637+ * build_options .test_requires ,
638+ env = test_env ,
639+ )
593640
594641 log .step ("Running test suite..." )
595642
0 commit comments