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,17 @@ 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
8282
8383 Args:
8484 ctx: {type}`ctx` current ctx.
8585 entries: {type}`list[VenvSymlinkEntry]` the entries that describe the
8686 venv-relative
87+ return_conflicts: {type}`bool`. Only present for testing. If True,
88+ also return a list of the groups that had overlapping paths and had
89+ to be resolved and merged.
8790
8891 Returns:
8992 {type}`dict[str, dict[str, str|File]]` Mappings of venv paths to their
@@ -113,6 +116,7 @@ def build_link_map(ctx, entries):
113116
114117 # final paths to keep, grouped by kind
115118 keep_link_map = {} # dict[str kind, dict[path, str|File]]
119+ conflicts = [] if return_conflicts else None
116120 for kind , entries in entries_by_kind .items ():
117121 # dict[str kind-relative path, str|File link_to]
118122 keep_kind_link_map = {}
@@ -128,12 +132,17 @@ def build_link_map(ctx, entries):
128132 else :
129133 keep_kind_link_map [entry .venv_path ] = entry .link_to_path
130134 else :
135+ if return_conflicts :
136+ conflicts .append (group )
137+
131138 # Merge a group of overlapping prefixes
132139 _merge_venv_path_group (ctx , group , keep_kind_link_map )
133140
134141 keep_link_map [kind ] = keep_kind_link_map
135-
136- return keep_link_map
142+ if return_conflicts :
143+ return keep_link_map , conflicts
144+ else :
145+ return keep_link_map
137146
138147def _group_venv_path_entries (entries ):
139148 """Group entries by VenvSymlinkEntry.venv_path overlap.
@@ -236,17 +245,6 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
236245
237246 all_files = sorted (files , key = lambda f : f .short_path )
238247
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-
250248 # venv paths that cannot be directly linked. Dict acting as set.
251249 cannot_be_linked_directly = {}
252250
@@ -257,7 +255,14 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
257255 # List of (File, str venv_path) tuples
258256 files_left_to_link = []
259257
260- repo_runfiles_dirname = None
258+ # We want to minimize the number of files symlinked. Ideally, only the
259+ # top-level directories are symlinked. Unfortunately, shared libraries
260+ # complicate matters: if a shared library's directory is linked, then the
261+ # dynamic linker computes the wrong search path.
262+ #
263+ # To fix, we have to directly link shared libraries. This then means that
264+ # all the parent directories of the shared library can't be linked
265+ # directly.
261266 for src in all_files :
262267 if src .owner .repo_name != ctx .label .repo_name :
263268 # Files in other repos complicate symlink optimization, so
@@ -280,11 +285,13 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
280285 )
281286 parent = paths .dirname (venv_path )
282287 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 :
288+ if not parent :
287289 break
290+ if cannot_be_linked_directly .get (parent , False ):
291+ # Already seen
292+ break
293+ cannot_be_linked_directly [parent ] = True
294+ parent = paths .dirname (parent )
288295 else :
289296 files_left_to_link .append ((src , venv_path ))
290297
@@ -298,7 +305,7 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
298305
299306 for src , venv_path in files_left_to_link :
300307 parent = paths .dirname (venv_path )
301- if not parent or parent == "." :
308+ if not parent :
302309 # File in root, must be linked directly
303310 optimized_groups .setdefault (venv_path , [])
304311 optimized_groups [venv_path ].append (src )
@@ -314,8 +321,8 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
314321 venv_path = parent
315322 next_parent = paths .dirname (parent )
316323 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 ) :
324+ if next_parent :
325+ if next_parent not in cannot_be_linked_directly :
319326 venv_path = next_parent
320327 next_parent = paths .dirname (next_parent )
321328 else :
@@ -328,7 +335,7 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
328335
329336 repo_name = ctx .label .repo_name
330337 if repo_name == "" :
331- repo_name = "_main"
338+ repo_name = "_main" if BZLMOD_ENABLED else "__main__"
332339 runfiles_root_prefix = paths .join (repo_name , site_packages_root )
333340
334341 # Finally, for each group, we create the VenvSymlinkEntry objects
@@ -339,14 +346,10 @@ def get_venv_symlinks(ctx, files, package, version_str, site_packages_root):
339346 ).format (
340347 venv_path = venv_path ,
341348 ))
342- if len (files ) == 1 :
343- link_to_file = files [0 ]
344- else :
345- link_to_file = None
346349 venv_symlinks [venv_path ] = VenvSymlinkEntry (
347350 kind = VenvSymlinkKind .LIB ,
348351 link_to_path = paths .join (runfiles_root_prefix , venv_path ),
349- link_to_file = link_to_file ,
352+ link_to_file = None ,
350353 package = package ,
351354 version = version_str ,
352355 venv_path = venv_path ,
0 commit comments