4848 "filter_to_py_srcs" ,
4949 "get_imports" ,
5050 "is_bool" ,
51+ "relative_path" ,
5152 "runfiles_root_path" ,
5253 "target_platform_has_any_constraint" ,
5354)
6768 TOOLCHAIN_TYPE = "TARGET_TOOLCHAIN_TYPE" ,
6869)
6970load (":transition_labels.bzl" , "TRANSITION_LABELS" )
71+ load (":venv_runfiles.bzl" , "create_venv_app_files" )
7072
7173_py_builtins = py_internal
7274_EXTERNAL_PATH_PREFIX = "external"
@@ -504,37 +506,6 @@ def _create_zip_main(ctx, *, stage2_bootstrap, runtime_details, venv):
504506 )
505507 return output
506508
507- def relative_path (from_ , to ):
508- """Compute a relative path from one path to another.
509-
510- Args:
511- from_: {type}`str` the starting directory. Note that it should be
512- a directory because relative-symlinks are relative to the
513- directory the symlink resides in.
514- to: {type}`str` the path that `from_` wants to point to
515-
516- Returns:
517- {type}`str` a relative path
518- """
519- from_parts = from_ .split ("/" )
520- to_parts = to .split ("/" )
521-
522- # Strip common leading parts from both paths
523- n = min (len (from_parts ), len (to_parts ))
524- for _ in range (n ):
525- if from_parts [0 ] == to_parts [0 ]:
526- from_parts .pop (0 )
527- to_parts .pop (0 )
528- else :
529- break
530-
531- # Impossible to compute a relative path without knowing what ".." is
532- if from_parts and from_parts [0 ] == ".." :
533- fail ("cannot compute relative path from '%s' to '%s'" , from_ , to )
534-
535- parts = ([".." ] * len (from_parts )) + to_parts
536- return paths .join (* parts )
537-
538509# Create a venv the executable can use.
539510# For venv details and the venv startup process, see:
540511# * https://docs.python.org/3/library/venv.html
@@ -641,9 +612,9 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
641612 VenvSymlinkKind .BIN : bin_dir ,
642613 VenvSymlinkKind .LIB : site_packages ,
643614 }
644- venv_symlinks = _create_venv_symlinks (ctx , venv_dir_map )
615+ venv_app_files = create_venv_app_files (ctx , venv_dir_map )
645616
646- files_without_interpreter = [pth , site_init ] + venv_symlinks
617+ files_without_interpreter = [pth , site_init ] + venv_app_files
647618 if pyvenv_cfg :
648619 files_without_interpreter .append (pyvenv_cfg )
649620
@@ -668,94 +639,6 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
668639 ),
669640 )
670641
671- def _create_venv_symlinks (ctx , venv_dir_map ):
672- """Creates symlinks within the venv.
673-
674- Args:
675- ctx: current rule ctx
676- venv_dir_map: mapping of VenvSymlinkKind constants to the
677- venv path.
678-
679- Returns:
680- {type}`list[File]` list of the File symlink objects created.
681- """
682-
683- # maps venv-relative path to the runfiles path it should point to
684- entries = depset (
685- transitive = [
686- dep [PyInfo ].venv_symlinks
687- for dep in ctx .attr .deps
688- if PyInfo in dep
689- ],
690- ).to_list ()
691-
692- link_map = _build_link_map (entries )
693- venv_files = []
694- for kind , kind_map in link_map .items ():
695- base = venv_dir_map [kind ]
696- for venv_path , link_to in kind_map .items ():
697- venv_link = ctx .actions .declare_symlink (paths .join (base , venv_path ))
698- venv_link_rf_path = runfiles_root_path (ctx , venv_link .short_path )
699- rel_path = relative_path (
700- # dirname is necessary because a relative symlink is relative to
701- # the directory the symlink resides within.
702- from_ = paths .dirname (venv_link_rf_path ),
703- to = link_to ,
704- )
705- ctx .actions .symlink (output = venv_link , target_path = rel_path )
706- venv_files .append (venv_link )
707-
708- return venv_files
709-
710- def _build_link_map (entries ):
711- # dict[str package, dict[str kind, dict[str rel_path, str link_to_path]]]
712- pkg_link_map = {}
713-
714- # dict[str package, str version]
715- version_by_pkg = {}
716-
717- for entry in entries :
718- link_map = pkg_link_map .setdefault (entry .package , {})
719- kind_map = link_map .setdefault (entry .kind , {})
720-
721- if version_by_pkg .setdefault (entry .package , entry .version ) != entry .version :
722- # We ignore duplicates by design.
723- continue
724- elif entry .venv_path in kind_map :
725- # We ignore duplicates by design.
726- continue
727- else :
728- kind_map [entry .venv_path ] = entry .link_to_path
729-
730- # An empty link_to value means to not create the site package symlink. Because of the
731- # ordering, this allows binaries to remove entries by having an earlier dependency produce
732- # empty link_to values.
733- for link_map in pkg_link_map .values ():
734- for kind , kind_map in link_map .items ():
735- for dir_path , link_to in kind_map .items ():
736- if not link_to :
737- kind_map .pop (dir_path )
738-
739- # dict[str kind, dict[str rel_path, str link_to_path]]
740- keep_link_map = {}
741-
742- # Remove entries that would be a child path of a created symlink.
743- # Earlier entries have precedence to match how exact matches are handled.
744- for link_map in pkg_link_map .values ():
745- for kind , kind_map in link_map .items ():
746- keep_kind_map = keep_link_map .setdefault (kind , {})
747- for _ in range (len (kind_map )):
748- if not kind_map :
749- break
750- dirname , value = kind_map .popitem ()
751- keep_kind_map [dirname ] = value
752- prefix = dirname + "/" # Add slash to prevent /X matching /XY
753- for maybe_suffix in kind_map .keys ():
754- maybe_suffix += "/" # Add slash to prevent /X matching /XY
755- if maybe_suffix .startswith (prefix ) or prefix .startswith (maybe_suffix ):
756- kind_map .pop (maybe_suffix )
757- return keep_link_map
758-
759642def _map_each_identity (v ):
760643 return v
761644
0 commit comments