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