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)
@@ -63,6 +64,7 @@ load(":reexports.bzl", "BuiltinPyInfo", "BuiltinPyRuntimeInfo")
6364load (":rule_builders.bzl" , "ruleb" )
6465load (":toolchain_types.bzl" , "EXEC_TOOLS_TOOLCHAIN_TYPE" , "TARGET_TOOLCHAIN_TYPE" , TOOLCHAIN_TYPE = "TARGET_TOOLCHAIN_TYPE" )
6566load (":transition_labels.bzl" , "TRANSITION_LABELS" )
67+ load (":venv_runfiles.bzl" , "create_venv_app_files" )
6668
6769_py_builtins = py_internal
6870_EXTERNAL_PATH_PREFIX = "external"
@@ -499,37 +501,6 @@ def _create_zip_main(ctx, *, stage2_bootstrap, runtime_details, venv):
499501 )
500502 return output
501503
502- def relative_path (from_ , to ):
503- """Compute a relative path from one path to another.
504-
505- Args:
506- from_: {type}`str` the starting directory. Note that it should be
507- a directory because relative-symlinks are relative to the
508- directory the symlink resides in.
509- to: {type}`str` the path that `from_` wants to point to
510-
511- Returns:
512- {type}`str` a relative path
513- """
514- from_parts = from_ .split ("/" )
515- to_parts = to .split ("/" )
516-
517- # Strip common leading parts from both paths
518- n = min (len (from_parts ), len (to_parts ))
519- for _ in range (n ):
520- if from_parts [0 ] == to_parts [0 ]:
521- from_parts .pop (0 )
522- to_parts .pop (0 )
523- else :
524- break
525-
526- # Impossible to compute a relative path without knowing what ".." is
527- if from_parts and from_parts [0 ] == ".." :
528- fail ("cannot compute relative path from '%s' to '%s'" , from_ , to )
529-
530- parts = ([".." ] * len (from_parts )) + to_parts
531- return paths .join (* parts )
532-
533504# Create a venv the executable can use.
534505# For venv details and the venv startup process, see:
535506# * https://docs.python.org/3/library/venv.html
@@ -636,9 +607,9 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
636607 VenvSymlinkKind .BIN : bin_dir ,
637608 VenvSymlinkKind .LIB : site_packages ,
638609 }
639- venv_symlinks = _create_venv_symlinks (ctx , venv_dir_map )
610+ venv_app_files = create_venv_app_files (ctx , ctx . attr . deps , venv_dir_map )
640611
641- files_without_interpreter = [pth , site_init ] + venv_symlinks
612+ files_without_interpreter = [pth , site_init ] + venv_app_files
642613 if pyvenv_cfg :
643614 files_without_interpreter .append (pyvenv_cfg )
644615
@@ -663,94 +634,6 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
663634 ),
664635 )
665636
666- def _create_venv_symlinks (ctx , venv_dir_map ):
667- """Creates symlinks within the venv.
668-
669- Args:
670- ctx: current rule ctx
671- venv_dir_map: mapping of VenvSymlinkKind constants to the
672- venv path.
673-
674- Returns:
675- {type}`list[File]` list of the File symlink objects created.
676- """
677-
678- # maps venv-relative path to the runfiles path it should point to
679- entries = depset (
680- transitive = [
681- dep [PyInfo ].venv_symlinks
682- for dep in ctx .attr .deps
683- if PyInfo in dep
684- ],
685- ).to_list ()
686-
687- link_map = _build_link_map (entries )
688- venv_files = []
689- for kind , kind_map in link_map .items ():
690- base = venv_dir_map [kind ]
691- for venv_path , link_to in kind_map .items ():
692- venv_link = ctx .actions .declare_symlink (paths .join (base , venv_path ))
693- venv_link_rf_path = runfiles_root_path (ctx , venv_link .short_path )
694- rel_path = relative_path (
695- # dirname is necessary because a relative symlink is relative to
696- # the directory the symlink resides within.
697- from_ = paths .dirname (venv_link_rf_path ),
698- to = link_to ,
699- )
700- ctx .actions .symlink (output = venv_link , target_path = rel_path )
701- venv_files .append (venv_link )
702-
703- return venv_files
704-
705- def _build_link_map (entries ):
706- # dict[str package, dict[str kind, dict[str rel_path, str link_to_path]]]
707- pkg_link_map = {}
708-
709- # dict[str package, str version]
710- version_by_pkg = {}
711-
712- for entry in entries :
713- link_map = pkg_link_map .setdefault (entry .package , {})
714- kind_map = link_map .setdefault (entry .kind , {})
715-
716- if version_by_pkg .setdefault (entry .package , entry .version ) != entry .version :
717- # We ignore duplicates by design.
718- continue
719- elif entry .venv_path in kind_map :
720- # We ignore duplicates by design.
721- continue
722- else :
723- kind_map [entry .venv_path ] = entry .link_to_path
724-
725- # An empty link_to value means to not create the site package symlink. Because of the
726- # ordering, this allows binaries to remove entries by having an earlier dependency produce
727- # empty link_to values.
728- for link_map in pkg_link_map .values ():
729- for kind , kind_map in link_map .items ():
730- for dir_path , link_to in kind_map .items ():
731- if not link_to :
732- kind_map .pop (dir_path )
733-
734- # dict[str kind, dict[str rel_path, str link_to_path]]
735- keep_link_map = {}
736-
737- # Remove entries that would be a child path of a created symlink.
738- # Earlier entries have precedence to match how exact matches are handled.
739- for link_map in pkg_link_map .values ():
740- for kind , kind_map in link_map .items ():
741- keep_kind_map = keep_link_map .setdefault (kind , {})
742- for _ in range (len (kind_map )):
743- if not kind_map :
744- break
745- dirname , value = kind_map .popitem ()
746- keep_kind_map [dirname ] = value
747- prefix = dirname + "/" # Add slash to prevent /X matching /XY
748- for maybe_suffix in kind_map .keys ():
749- maybe_suffix += "/" # Add slash to prevent /X matching /XY
750- if maybe_suffix .startswith (prefix ) or prefix .startswith (maybe_suffix ):
751- kind_map .pop (maybe_suffix )
752- return keep_link_map
753-
754637def _map_each_identity (v ):
755638 return v
756639
0 commit comments