11"""Code for constructing venvs."""
22
33load ("@bazel_skylib//lib:paths.bzl" , "paths" )
4+ load ("//python/private:bzlmod_enabled.bzl" , "BZLMOD_ENABLED" ) # buildifier: disable=bzl-visibility
45load (
56 ":common.bzl" ,
67 "is_file" ,
@@ -57,7 +58,6 @@ def create_venv_app_files(ctx, deps, venv_dir_map):
5758 if is_file (link_to ):
5859 symlink_from = "{}/{}" .format (ctx .label .package , bin_venv_path )
5960 runfiles_symlinks [symlink_from ] = link_to
60-
6161 else :
6262 venv_link = ctx .actions .declare_symlink (bin_venv_path )
6363 venv_link_rf_path = runfiles_root_path (ctx , venv_link .short_path )
@@ -76,14 +76,16 @@ def create_venv_app_files(ctx, deps, venv_dir_map):
7676 )
7777
7878# Visible for testing
79- def build_link_map (ctx , entries ):
79+ def build_link_map (ctx , entries , return_conflicts = False ):
8080 """Compute the mapping of venv paths to their backing objects.
8181
82-
8382 Args:
8483 ctx: {type}`ctx` current ctx.
8584 entries: {type}`list[VenvSymlinkEntry]` the entries that describe the
8685 venv-relative
86+ return_conflicts: {type}`bool`. Only present for testing. If True,
87+ also return a list of the groups that had overlapping paths and had
88+ to be resolved and merged.
8789
8890 Returns:
8991 {type}`dict[str, dict[str, str|File]]` Mappings of venv paths to their
@@ -113,6 +115,7 @@ def build_link_map(ctx, entries):
113115
114116 # final paths to keep, grouped by kind
115117 keep_link_map = {} # dict[str kind, dict[path, str|File]]
118+ conflicts = [] if return_conflicts else None
116119 for kind , entries in entries_by_kind .items ():
117120 # dict[str kind-relative path, str|File link_to]
118121 keep_kind_link_map = {}
@@ -128,12 +131,17 @@ def build_link_map(ctx, entries):
128131 else :
129132 keep_kind_link_map [entry .venv_path ] = entry .link_to_path
130133 else :
134+ if return_conflicts :
135+ conflicts .append (group )
136+
131137 # Merge a group of overlapping prefixes
132138 _merge_venv_path_group (ctx , group , keep_kind_link_map )
133139
134140 keep_link_map [kind ] = keep_kind_link_map
135-
136- return keep_link_map
141+ if return_conflicts :
142+ return keep_link_map , conflicts
143+ else :
144+ return keep_link_map
137145
138146def _group_venv_path_entries (entries ):
139147 """Group entries by VenvSymlinkEntry.venv_path overlap.
@@ -236,17 +244,6 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
236244
237245 all_files = sorted (files , key = lambda f : f .short_path )
238246
239- # We want to minimize the number of files symlinked. Ideally only
240- # the top-level directories under site-packages are symlinked.
241- # Unfortunately, shared libraries complicate matters: if a
242- # shared library's directory is linked, then the dynamic linker
243- # computes the wrong search path. To fix, we have to directly link
244- # shared libraries. This then means that all the parent directories of
245- # the shared library can't be linked directly.
246- # So what we do is identify the shared libraries, mark all their
247- # parent directories as "cannot be directly linked", then compute
248- # the files that can be linked with that constraint.
249-
250247 # venv paths that cannot be directly linked. Dict acting as set.
251248 cannot_be_linked_directly = {}
252249
@@ -257,7 +254,14 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
257254 # List of (File, str venv_path) tuples
258255 files_left_to_link = []
259256
260- repo_runfiles_dirname = None
257+ # We want to minimize the number of files symlinked. Ideally, only the
258+ # top-level directories are symlinked. Unfortunately, shared libraries
259+ # complicate matters: if a shared library's directory is linked, then the
260+ # dynamic linker computes the wrong search path.
261+ #
262+ # To fix, we have to directly link shared libraries. This then means that
263+ # all the parent directories of the shared library can't be linked
264+ # directly.
261265 for src in all_files :
262266 if src .owner .repo_name != ctx .label .repo_name :
263267 # Files in other repos complicate symlink optimization, so
@@ -280,11 +284,13 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
280284 )
281285 parent = paths .dirname (venv_path )
282286 for _ in range (len (venv_path ) + 1 ): # Iterate enough times to traverse up
283- if parent and parent != "." :
284- cannot_be_linked_directly [parent ] = True
285- parent = paths .dirname (parent )
286- else :
287+ if not parent :
287288 break
289+ if cannot_be_linked_directly .get (parent , False ):
290+ # Already seen
291+ break
292+ cannot_be_linked_directly [parent ] = True
293+ parent = paths .dirname (parent )
288294 else :
289295 files_left_to_link .append ((src , venv_path ))
290296
@@ -298,7 +304,7 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
298304
299305 for src , venv_path in files_left_to_link :
300306 parent = paths .dirname (venv_path )
301- if not parent or parent == "." :
307+ if not parent :
302308 # File in root, must be linked directly
303309 optimized_groups .setdefault (venv_path , [])
304310 optimized_groups [venv_path ].append (src )
@@ -314,8 +320,8 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
314320 venv_path = parent
315321 next_parent = paths .dirname (parent )
316322 for _ in range (len (venv_path ) + 1 ): # Iterate enough times
317- if next_parent and next_parent != "." :
318- if not ( next_parent in cannot_be_linked_directly ) :
323+ if next_parent :
324+ if next_parent not in cannot_be_linked_directly :
319325 venv_path = next_parent
320326 next_parent = paths .dirname (next_parent )
321327 else :
@@ -328,7 +334,7 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
328334
329335 repo_name = ctx .label .repo_name
330336 if repo_name == "" :
331- repo_name = "_main"
337+ repo_name = "_main" if BZLMOD_ENABLED else "__main__"
332338 runfiles_root_prefix = paths .join (repo_name , site_packages_root )
333339
334340 # Finally, for each group, we create the VenvSymlinkEntry objects
@@ -339,14 +345,10 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
339345 ).format (
340346 venv_path = venv_path ,
341347 ))
342- if len (files ) == 1 :
343- link_to_file = files [0 ]
344- else :
345- link_to_file = None
346348 venv_symlinks [venv_path ] = VenvSymlinkEntry (
347349 kind = VenvSymlinkKind .LIB ,
348350 link_to_path = paths .join (runfiles_root_prefix , venv_path ),
349- link_to_file = link_to_file ,
351+ link_to_file = None ,
350352 package = package ,
351353 version = version_str ,
352354 venv_path = venv_path ,
0 commit comments