@@ -140,6 +140,9 @@ This is mutually exclusive with {obj}`main`.
140140
141141:::{versionadded} 1.3.0
142142:::
143+ :::{versionchanged} VERSION_NEXT_FEATURE
144+ Support added for {obj}`--bootstrap_impl=system_python`.
145+ :::
143146""" ,
144147 ),
145148 "pyc_collection" : lambda : attrb .String (
@@ -332,9 +335,10 @@ def _create_executable(
332335 # BuiltinPyRuntimeInfo providers, which is likely to come from
333336 # @bazel_tools//tools/python:autodetecting_toolchain, the toolchain used
334337 # for workspace builds when no rules_python toolchain is configured.
335- if (BootstrapImplFlag . get_value ( ctx ) == BootstrapImplFlag . SCRIPT and
338+ if (
336339 runtime_details .effective_runtime and
337- hasattr (runtime_details .effective_runtime , "stage2_bootstrap_template" )):
340+ hasattr (runtime_details .effective_runtime , "stage2_bootstrap_template" )
341+ ):
338342 venv = _create_venv (
339343 ctx ,
340344 output_prefix = base_executable_name ,
@@ -351,7 +355,11 @@ def _create_executable(
351355 runtime_details = runtime_details ,
352356 venv = venv ,
353357 )
354- extra_runfiles = ctx .runfiles ([stage2_bootstrap ] + venv .files_without_interpreter )
358+ extra_runfiles = ctx .runfiles (
359+ [stage2_bootstrap ] + (
360+ venv .files_without_interpreter if venv else []
361+ ),
362+ )
355363 zip_main = _create_zip_main (
356364 ctx ,
357365 stage2_bootstrap = stage2_bootstrap ,
@@ -460,7 +468,7 @@ def _create_executable(
460468
461469 # The interpreter is added this late in the process so that it isn't
462470 # added to the zipped files.
463- if venv :
471+ if venv and venv . interpreter :
464472 extra_runfiles = extra_runfiles .merge (ctx .runfiles ([venv .interpreter ]))
465473 return create_executable_result_struct (
466474 extra_files_to_build = depset (extra_files_to_build ),
@@ -469,7 +477,10 @@ def _create_executable(
469477 )
470478
471479def _create_zip_main (ctx , * , stage2_bootstrap , runtime_details , venv ):
472- python_binary = runfiles_root_path (ctx , venv .interpreter .short_path )
480+ if venv .interpreter :
481+ python_binary = runfiles_root_path (ctx , venv .interpreter .short_path )
482+ else :
483+ python_binary = ""
473484 python_binary_actual = venv .interpreter_actual_path
474485
475486 # The location of this file doesn't really matter. It's added to
@@ -529,62 +540,66 @@ def relative_path(from_, to):
529540# * https://github.com/python/cpython/blob/main/Modules/getpath.py
530541# * https://github.com/python/cpython/blob/main/Lib/site.py
531542def _create_venv (ctx , output_prefix , imports , runtime_details ):
543+ create_full_venv = BootstrapImplFlag .get_value (ctx ) == BootstrapImplFlag .SCRIPT
532544 venv = "_{}.venv" .format (output_prefix .lstrip ("_" ))
533545
534- # The pyvenv.cfg file must be present to trigger the venv site hooks.
535- # Because it's paths are expected to be absolute paths, we can't reliably
536- # put much in it. See https://github.com/python/cpython/issues/83650
537- pyvenv_cfg = ctx .actions .declare_file ("{}/pyvenv.cfg" .format (venv ))
538- ctx .actions .write (pyvenv_cfg , "" )
546+ if create_full_venv :
547+ # The pyvenv.cfg file must be present to trigger the venv site hooks.
548+ # Because it's paths are expected to be absolute paths, we can't reliably
549+ # put much in it. See https://github.com/python/cpython/issues/83650
550+ pyvenv_cfg = ctx .actions .declare_file ("{}/pyvenv.cfg" .format (venv ))
551+ ctx .actions .write (pyvenv_cfg , "" )
552+ else :
553+ pyvenv_cfg = None
539554
540555 runtime = runtime_details .effective_runtime
541556
542557 venvs_use_declare_symlink_enabled = (
543558 VenvsUseDeclareSymlinkFlag .get_value (ctx ) == VenvsUseDeclareSymlinkFlag .YES
544559 )
545560 recreate_venv_at_runtime = False
546- bin_dir = "{}/bin" .format (venv )
547-
548- if not venvs_use_declare_symlink_enabled or not runtime .supports_build_time_venv :
549- recreate_venv_at_runtime = True
550- if runtime .interpreter :
551- interpreter_actual_path = runfiles_root_path (ctx , runtime .interpreter .short_path )
552- else :
553- interpreter_actual_path = runtime .interpreter_path
554561
555- py_exe_basename = paths .basename (interpreter_actual_path )
562+ if runtime .interpreter :
563+ interpreter_actual_path = runfiles_root_path (ctx , runtime .interpreter .short_path )
564+ else :
565+ interpreter_actual_path = runtime .interpreter_path
556566
557- # When the venv symlinks are disabled, the $venv/bin/python3 file isn't
558- # needed or used at runtime. However, the zip code uses the interpreter
559- # File object to figure out some paths.
560- interpreter = ctx .actions .declare_file ("{}/{}" .format (bin_dir , py_exe_basename ))
561- ctx .actions .write (interpreter , "actual:{}" .format (interpreter_actual_path ))
567+ bin_dir = "{}/bin" .format (venv )
562568
563- elif runtime . interpreter :
569+ if create_full_venv :
564570 # Some wrappers around the interpreter (e.g. pyenv) use the program
565571 # name to decide what to do, so preserve the name.
566- py_exe_basename = paths .basename (runtime . interpreter . short_path )
572+ py_exe_basename = paths .basename (interpreter_actual_path )
567573
568- # Even though ctx.actions.symlink() is used, using
569- # declare_symlink() is required to ensure that the resulting file
570- # in runfiles is always a symlink. An RBE implementation, for example,
571- # may choose to write what symlink() points to instead.
572- interpreter = ctx .actions .declare_symlink ("{}/{}" .format (bin_dir , py_exe_basename ))
574+ if not venvs_use_declare_symlink_enabled or not runtime .supports_build_time_venv :
575+ recreate_venv_at_runtime = True
573576
574- interpreter_actual_path = runfiles_root_path (ctx , runtime .interpreter .short_path )
575- rel_path = relative_path (
576- # dirname is necessary because a relative symlink is relative to
577- # the directory the symlink resides within.
578- from_ = paths .dirname (runfiles_root_path (ctx , interpreter .short_path )),
579- to = interpreter_actual_path ,
580- )
577+ # When the venv symlinks are disabled, the $venv/bin/python3 file isn't
578+ # needed or used at runtime. However, the zip code uses the interpreter
579+ # File object to figure out some paths.
580+ interpreter = ctx .actions .declare_file ("{}/{}" .format (bin_dir , py_exe_basename ))
581+ ctx .actions .write (interpreter , "actual:{}" .format (interpreter_actual_path ))
581582
582- ctx .actions .symlink (output = interpreter , target_path = rel_path )
583+ elif runtime .interpreter :
584+ # Even though ctx.actions.symlink() is used, using
585+ # declare_symlink() is required to ensure that the resulting file
586+ # in runfiles is always a symlink. An RBE implementation, for example,
587+ # may choose to write what symlink() points to instead.
588+ interpreter = ctx .actions .declare_symlink ("{}/{}" .format (bin_dir , py_exe_basename ))
589+
590+ rel_path = relative_path (
591+ # dirname is necessary because a relative symlink is relative to
592+ # the directory the symlink resides within.
593+ from_ = paths .dirname (runfiles_root_path (ctx , interpreter .short_path )),
594+ to = interpreter_actual_path ,
595+ )
596+
597+ ctx .actions .symlink (output = interpreter , target_path = rel_path )
598+ else :
599+ interpreter = ctx .actions .declare_symlink ("{}/{}" .format (bin_dir , py_exe_basename ))
600+ ctx .actions .symlink (output = interpreter , target_path = runtime .interpreter_path )
583601 else :
584- py_exe_basename = paths .basename (runtime .interpreter_path )
585- interpreter = ctx .actions .declare_symlink ("{}/{}" .format (bin_dir , py_exe_basename ))
586- ctx .actions .symlink (output = interpreter , target_path = runtime .interpreter_path )
587- interpreter_actual_path = runtime .interpreter_path
602+ interpreter = None
588603
589604 if runtime .interpreter_version_info :
590605 version = "{}.{}" .format (
@@ -626,14 +641,29 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
626641 }
627642 venv_symlinks = _create_venv_symlinks (ctx , venv_dir_map )
628643
644+ files_without_interpreter = [pth , site_init ] + venv_symlinks
645+ if pyvenv_cfg :
646+ files_without_interpreter .append (pyvenv_cfg )
647+
629648 return struct (
649+ # File or None; the `bin/python3` executable in the venv.
650+ # None if a full venv isn't created.
630651 interpreter = interpreter ,
652+ # bool; True if the venv should be recreated at runtime
631653 recreate_venv_at_runtime = recreate_venv_at_runtime ,
632654 # Runfiles root relative path or absolute path
633655 interpreter_actual_path = interpreter_actual_path ,
634- files_without_interpreter = [ pyvenv_cfg , pth , site_init ] + venv_symlinks ,
656+ files_without_interpreter = files_without_interpreter ,
635657 # string; venv-relative path to the site-packages directory.
636658 venv_site_packages = venv_site_packages ,
659+ # string; runfiles-root relative path to venv root.
660+ venv_root = runfiles_root_path (
661+ ctx ,
662+ paths .join (
663+ py_internal .get_label_repo_runfiles_path (ctx .label ),
664+ venv ,
665+ ),
666+ ),
637667 )
638668
639669def _create_venv_symlinks (ctx , venv_dir_map ):
@@ -746,7 +776,7 @@ def _create_stage2_bootstrap(
746776 main_py ,
747777 imports ,
748778 runtime_details ,
749- venv = None ):
779+ venv ):
750780 output = ctx .actions .declare_file (
751781 # Prepend with underscore to prevent pytest from trying to
752782 # process the bootstrap for files starting with `test_`
@@ -758,17 +788,10 @@ def _create_stage2_bootstrap(
758788 template = runtime .stage2_bootstrap_template
759789
760790 if main_py :
761- main_py_path = "{}/{}" . format (ctx . workspace_name , main_py .short_path )
791+ main_py_path = runfiles_root_path (ctx , main_py .short_path )
762792 else :
763793 main_py_path = ""
764794
765- # The stage2 bootstrap uses the venv site-packages location to fix up issues
766- # that occur when the toolchain doesn't support the build-time venv.
767- if venv and not runtime .supports_build_time_venv :
768- venv_rel_site_packages = venv .venv_site_packages
769- else :
770- venv_rel_site_packages = ""
771-
772795 ctx .actions .expand_template (
773796 template = template ,
774797 output = output ,
@@ -779,7 +802,8 @@ def _create_stage2_bootstrap(
779802 "%main%" : main_py_path ,
780803 "%main_module%" : ctx .attr .main_module ,
781804 "%target%" : str (ctx .label ),
782- "%venv_rel_site_packages%" : venv_rel_site_packages ,
805+ "%venv_rel_site_packages%" : venv .venv_site_packages ,
806+ "%venv_root%" : venv .venv_root ,
783807 "%workspace_name%" : ctx .workspace_name ,
784808 },
785809 is_executable = True ,
@@ -800,7 +824,10 @@ def _create_stage1_bootstrap(
800824 runtime = runtime_details .effective_runtime
801825
802826 if venv :
803- python_binary_path = runfiles_root_path (ctx , venv .interpreter .short_path )
827+ if venv .interpreter :
828+ python_binary_path = runfiles_root_path (ctx , venv .interpreter .short_path )
829+ else :
830+ python_binary_path = ""
804831 else :
805832 python_binary_path = runtime_details .executable_interpreter_path
806833
0 commit comments