Skip to content

Commit 3fad9da

Browse files
committed
rename venv_symlinks
1 parent c0415c6 commit 3fad9da

File tree

8 files changed

+178
-101
lines changed

8 files changed

+178
-101
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ END_UNRELEASED_TEMPLATE
7070
`_test` target is deprecated and will be removed in the next major release.
7171
([#2794](https://github.com/bazel-contrib/rules_python/issues/2794)
7272
* (py_wheel) py_wheel always creates zip64-capable wheel zips
73+
* (providers) (experimental) {obj}`PyInfo.venv_symlinks` replaces
74+
`PyInfo.site_packages_symlinks`
7375

7476
{#v0-0-0-fixed}
7577
### Fixed
@@ -203,7 +205,7 @@ END_UNRELEASED_TEMPLATE
203205
please check the {obj}`uv.configure` tag class.
204206
* Add support for riscv64 linux platform.
205207
* (toolchains) Add python 3.13.2 and 3.12.9 toolchains
206-
* (providers) (experimental) {obj}`PyInfo.site_packages_symlinks` field added to
208+
* (providers) (experimental) `PyInfo.site_packages_symlinks` field added to
207209
allow specifying links to create within the venv site packages (only
208210
applicable with {obj}`--bootstrap_impl=script`)
209211
([#2156](https://github.com/bazelbuild/rules_python/issues/2156)).

python/features.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ def _features_typedef():
3131
:::
3232
::::
3333
34-
::::{field} py_info_site_packages_symlinks
34+
::::{field} py_info_venv_symlinks
3535
36-
True if the `PyInfo.site_packages_symlinks` field is available.
36+
True if the `PyInfo.venv_symlinks` field is available.
3737
3838
:::{versionadded} 1.4.0
3939
:::
@@ -61,7 +61,7 @@ features = struct(
6161
TYPEDEF = _features_typedef,
6262
# keep sorted
6363
precompile = True,
64-
py_info_site_packages_symlinks = True,
64+
py_info_venv_symlinks = True,
6565
uses_builtin_rules = not config.enable_pystar,
6666
version = _VERSION_PRIVATE if "$Format" not in _VERSION_PRIVATE else "",
6767
)

python/private/attributes.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ The order of this list can matter because it affects the order that information
260260
from dependencies is merged in, which can be relevant depending on the ordering
261261
mode of depsets that are merged.
262262
263-
* {obj}`PyInfo.site_packages_symlinks` uses topological ordering.
263+
* {obj}`PyInfo.venv_symlinks` uses topological ordering.
264264
265265
See {obj}`PyInfo` for more information about the ordering of its depsets and
266266
how its fields are merged.

python/private/common.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ def create_py_info(
378378
implicit_pyc_files,
379379
implicit_pyc_source_files,
380380
imports,
381-
site_packages_symlinks = []):
381+
venv_symlinks = []):
382382
"""Create PyInfo provider.
383383
384384
Args:
@@ -396,7 +396,7 @@ def create_py_info(
396396
implicit_pyc_files: {type}`depset[File]` Implicitly generated pyc files
397397
that a binary can choose to include.
398398
imports: depset of strings; the import path values to propagate.
399-
site_packages_symlinks: {type}`list[tuple[str, str]]` tuples of
399+
venv_symlinks: {type}`list[tuple[str, str]]` tuples of
400400
`(runfiles_path, site_packages_path)` for symlinks to create
401401
in the consuming binary's venv site packages.
402402
@@ -406,7 +406,7 @@ def create_py_info(
406406
necessary for deprecated extra actions support).
407407
"""
408408
py_info = PyInfoBuilder.new()
409-
py_info.site_packages_symlinks.add(site_packages_symlinks)
409+
py_info.venv_symlinks.add(venv_symlinks)
410410
py_info.direct_original_sources.add(original_sources)
411411
py_info.direct_pyc_files.add(required_pyc_files)
412412
py_info.direct_pyi_files.add(ctx.files.pyi_srcs)

python/private/flags.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,12 @@ def _venvs_site_packages_is_enabled(ctx):
154154
flag_value = ctx.attr.experimental_venvs_site_packages[BuildSettingInfo].value
155155
return flag_value == VenvsSitePackages.YES
156156

157-
# Decides if libraries try to use a site-packages layout using site_packages_symlinks
157+
# Decides if libraries try to use a site-packages layout using venv_symlinks
158158
# buildifier: disable=name-conventions
159159
VenvsSitePackages = FlagEnum(
160-
# Use site_packages_symlinks
160+
# Use venv_symlinks
161161
YES = "yes",
162-
# Don't use site_packages_symlinks
162+
# Don't use venv_symlinks
163163
NO = "no",
164164
is_enabled = _venvs_site_packages_is_enabled,
165165
)

python/private/py_executable.bzl

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ load(":flags.bzl", "BootstrapImplFlag", "VenvsUseDeclareSymlinkFlag")
5454
load(":precompile.bzl", "maybe_precompile")
5555
load(":py_cc_link_params_info.bzl", "PyCcLinkParamsInfo")
5656
load(":py_executable_info.bzl", "PyExecutableInfo")
57-
load(":py_info.bzl", "PyInfo")
57+
load(":py_info.bzl", "PyInfo", "VenvSymlinkKind")
5858
load(":py_internal.bzl", "py_internal")
5959
load(":py_runtime_info.bzl", "DEFAULT_STUB_SHEBANG", "PyRuntimeInfo")
6060
load(":reexports.bzl", "BuiltinPyInfo", "BuiltinPyRuntimeInfo")
@@ -618,89 +618,104 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
618618
},
619619
computed_substitutions = computed_subs,
620620
)
621-
site_packages_symlinks = _create_site_packages_symlinks(ctx, site_packages)
621+
622+
bin_dir = "bin"
623+
venv_dir_map = {
624+
VenvSymlinkKind.BIN: bin_dir,
625+
VenvSymlinkKind.LIB: site_packages,
626+
}
627+
venv_symlinks = _create_venv_symlinks(ctx, venv_dir_map)
622628

623629
return struct(
624630
interpreter = interpreter,
625631
recreate_venv_at_runtime = recreate_venv_at_runtime,
626632
# Runfiles root relative path or absolute path
627633
interpreter_actual_path = interpreter_actual_path,
628-
files_without_interpreter = [pyvenv_cfg, pth, site_init] + site_packages_symlinks,
634+
files_without_interpreter = [pyvenv_cfg, pth, site_init] + venv_symlinks,
629635
# string; venv-relative path to the site-packages directory.
630636
venv_site_packages = venv_site_packages,
631637
)
632638

633-
def _create_site_packages_symlinks(ctx, site_packages):
634-
"""Creates symlinks within site-packages.
639+
def _create_venv_symlinks(ctx, venv_dir_map):
640+
"""Creates symlinks within the venv.
635641
636642
Args:
637643
ctx: current rule ctx
638-
site_packages: runfiles-root-relative path to the site-packages directory
644+
venv_dir_map: todo
639645
640646
Returns:
641647
{type}`list[File]` list of the File symlink objects created.
642648
"""
643649

644-
# maps site-package symlink to the runfiles path it should point to
650+
# maps venv-relative path to the runfiles path it should point to
645651
entries = depset(
646652
# NOTE: Topological ordering is used so that dependencies closer to the
647653
# binary have precedence in creating their symlinks. This allows the
648654
# binary a modicum of control over the result.
649655
order = "topological",
650656
transitive = [
651-
dep[PyInfo].site_packages_symlinks
657+
dep[PyInfo].venv_symlinks
652658
for dep in ctx.attr.deps
653659
if PyInfo in dep
654660
],
655661
).to_list()
662+
656663
link_map = _build_link_map(entries)
664+
venv_files = []
665+
for kind, kind_map in link_map.items():
666+
base = venv_dir_map[kind]
667+
for venv_path, link_to in kind_map.items():
668+
venv_link = ctx.actions.declare_symlink(paths.join(base, venv_path))
669+
venv_link_rf_path = runfiles_root_path(ctx, venv_link.short_path)
670+
rel_path = relative_path(
671+
# dirname is necessary because a relative symlink is relative to
672+
# the directory the symlink resides within.
673+
from_ = paths.dirname(venv_link_rf_path),
674+
to = link_to,
675+
)
676+
ctx.actions.symlink(output = venv_link, target_path = rel_path)
677+
venv_files.append(venv_link)
657678

658-
sp_files = []
659-
for sp_dir_path, link_to in link_map.items():
660-
sp_link = ctx.actions.declare_symlink(paths.join(site_packages, sp_dir_path))
661-
sp_link_rf_path = runfiles_root_path(ctx, sp_link.short_path)
662-
rel_path = relative_path(
663-
# dirname is necessary because a relative symlink is relative to
664-
# the directory the symlink resides within.
665-
from_ = paths.dirname(sp_link_rf_path),
666-
to = link_to,
667-
)
668-
ctx.actions.symlink(output = sp_link, target_path = rel_path)
669-
sp_files.append(sp_link)
670-
return sp_files
679+
return venv_files
671680

672681
def _build_link_map(entries):
682+
# dict[str kind, dict[str rel_path, str link_to_path]]
673683
link_map = {}
674-
for link_to_runfiles_path, site_packages_path in entries:
675-
if site_packages_path in link_map:
684+
for entry in entries:
685+
kind = entry.kind
686+
kind_map = link_map.setdefault(kind, {})
687+
if entry.venv_path in kind_map:
676688
# We ignore duplicates by design. The dependency closer to the
677689
# binary gets precedence due to the topological ordering.
678690
continue
679691
else:
680-
link_map[site_packages_path] = link_to_runfiles_path
692+
kind_map[entry.venv_path] = entry.link_to_path
681693

682694
# An empty link_to value means to not create the site package symlink.
683695
# Because of the topological ordering, this allows binaries to remove
684696
# entries by having an earlier dependency produce empty link_to values.
685-
for sp_dir_path, link_to in link_map.items():
686-
if not link_to:
687-
link_map.pop(sp_dir_path)
697+
for kind, kind_map in link_map.items():
698+
for dir_path, link_to in kind_map.items():
699+
if not link_to:
700+
kind_map.pop(dir_path)
688701

689-
# Remove entries that would be a child path of a created symlink.
690-
# Earlier entries have precedence to match how exact matches are handled.
702+
# dict[str kind, dict[str rel_path, str link_to_path]]
691703
keep_link_map = {}
692-
for _ in range(len(link_map)):
693-
if not link_map:
694-
break
695-
dirname, value = link_map.popitem()
696-
keep_link_map[dirname] = value
697-
698-
prefix = dirname + "/" # Add slash to prevent /X matching /XY
699-
for maybe_suffix in link_map.keys():
700-
maybe_suffix += "/" # Add slash to prevent /X matching /XY
701-
if maybe_suffix.startswith(prefix) or prefix.startswith(maybe_suffix):
702-
link_map.pop(maybe_suffix)
703704

705+
# Remove entries that would be a child path of a created symlink.
706+
# Earlier entries have precedence to match how exact matches are handled.
707+
for kind, kind_map in link_map.items():
708+
keep_kind_map = keep_link_map.setdefault(kind, {})
709+
for _ in range(len(kind_map)):
710+
if not kind_map:
711+
break
712+
dirname, value = kind_map.popitem()
713+
keep_kind_map[dirname] = value
714+
prefix = dirname + "/" # Add slash to prevent /X matching /XY
715+
for maybe_suffix in kind_map.keys():
716+
maybe_suffix += "/" # Add slash to prevent /X matching /XY
717+
if maybe_suffix.startswith(prefix) or prefix.startswith(maybe_suffix):
718+
kind_map.pop(maybe_suffix)
704719
return keep_link_map
705720

706721
def _map_each_identity(v):

0 commit comments

Comments
 (0)