Skip to content

Commit 3eaf55d

Browse files
committed
wip
1 parent f1db067 commit 3eaf55d

7 files changed

Lines changed: 454 additions & 184 deletions

File tree

python/private/pypi/extension.bzl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ load(":platform.bzl", _plat = "platform")
3131
load(":pypi_cache.bzl", "pypi_cache")
3232
load(":simpleapi_download.bzl", "simpleapi_download")
3333
load(":unified_hub_repo.bzl", "unified_hub_repo")
34-
load(":whl_library.bzl", "whl_library")
34+
load(":whl_library.bzl", "whl_library", "whl_library_deps")
3535

3636
def _whl_mods_impl(whl_mods_dict):
3737
"""Implementation of the pip.whl_mods tag class.
@@ -459,14 +459,21 @@ You cannot use both the additive_build_content and additive_build_content_file a
459459
exposed_packages = {}
460460
extra_aliases = {}
461461
whl_libraries = {}
462+
whl_library_deps_map = {}
462463
for hub in pip_hub_map.values():
463464
out = hub.build()
464465

465466
for whl_name, lib in out.whl_libraries.items():
466467
if whl_name in whl_libraries:
467-
fail("'{}' already in created".format(whl_name))
468+
print("'{}' already in created".format(whl_name))
469+
470+
whl_libraries[whl_name] = lib
471+
472+
for deps_name, deps_args in out.whl_library_deps.items():
473+
if deps_name in whl_library_deps_map:
474+
fail("'{}' already in created".format(deps_name))
468475
else:
469-
whl_libraries[whl_name] = lib
476+
whl_library_deps_map[deps_name] = deps_args
470477

471478
exposed_packages[hub.name] = out.exposed_packages
472479
extra_aliases[hub.name] = out.extra_aliases
@@ -483,6 +490,7 @@ You cannot use both the additive_build_content and additive_build_content_file a
483490
hub_group_map = hub_group_map,
484491
hub_whl_map = hub_whl_map,
485492
whl_libraries = whl_libraries,
493+
whl_library_deps = whl_library_deps_map,
486494
whl_mods = whl_mods,
487495
platform_config_settings = {
488496
hub_name: {
@@ -614,6 +622,9 @@ def _pip_impl(module_ctx):
614622
for name, args in mods.whl_libraries.items():
615623
whl_library(name = name, **args)
616624

625+
for name, args in mods.whl_library_deps.items():
626+
whl_library_deps(name = name, **args)
627+
617628
for hub_name, whl_map in mods.hub_whl_map.items():
618629
hub_repository(
619630
name = hub_name,

python/private/pypi/generate_whl_library_build_bazel.bzl

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
load("//python/private:text_util.bzl", "render")
1818

19-
_RENDER = {
19+
# These are functions on how to render particular args, should be reused across all rendering
20+
# invocations to make things easier.
21+
_RENDER_FNS = {
2022
"copy_executables": render.dict,
2123
"copy_files": render.dict,
2224
"data": render.list,
@@ -29,6 +31,11 @@ _RENDER = {
2931
"srcs_exclude": render.list,
3032
"tags": render.list,
3133
}
34+
def _render(**kwargs):
35+
return {
36+
arg: _RENDER_FNS.get(arg, repr)(value)
37+
for arg, value in kwargs.items
38+
}
3239

3340
# NOTE @aignas 2024-10-25: We have to keep this so that files in
3441
# this repository can be publicly visible without the need for
@@ -38,19 +45,12 @@ _TEMPLATE = """\
3845
3946
package(default_visibility = ["//visibility:public"])
4047
41-
package_metadata(
42-
name = "package_metadata",
43-
purl = {purl},
44-
visibility = ["//:__subpackages__"],
45-
)
46-
47-
{fn}(
48-
{kwargs}
49-
)
48+
{macros}
5049
"""
5150

5251
def generate_whl_library_build_bazel(
5352
*,
53+
# TODO @aignas 2026-07-04: add extra args that are used in this function
5454
annotation = None,
5555
config_load,
5656
purl = None,
@@ -72,21 +72,41 @@ def generate_whl_library_build_bazel(
7272

7373
loads = [
7474
"""load("@package_metadata//rules:package_metadata.bzl", "package_metadata")""",
75+
"""load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_srcs", "whl_library_from_requires_dist")"""
7576
]
7677

77-
fn = "whl_library_targets_from_requires"
78-
if not requires_dist:
79-
# no deps, we can leave the extra loads out
80-
pass
81-
else:
82-
loads.append("""load("{}", "{}")""".format(config_load, "packages"))
83-
kwargs["include"] = "packages"
84-
kwargs["requires_dist"] = requires_dist
85-
86-
loads.extend([
87-
"""load("@rules_python//python/private/pypi:whl_library_targets.bzl", "{}")""".format(fn),
88-
])
78+
srcs_kwargs = dict(
79+
name = name,
80+
data = [],
81+
sdist_filename = sdist_filename,
82+
data_exclude = list(data_exclude),
83+
srcs_exclude = list(srcs_exclude),
84+
tags = [
85+
"pypi_name={}".format(metadata_name),
86+
"pypi_version={}".format(metadata_version),
87+
],
88+
entry_points = entry_points,
89+
enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs,
90+
copy_files = copy_files,
91+
copy_executables = copy_executables,
92+
namespace_package_files = namespace_package_files,
93+
data = [],
94+
visibility = visibility,
95+
)
96+
from_requires_kwargs = dict(
97+
name = name,
98+
metadata_name = metadata_name,
99+
metadata_version = metadata_version,
100+
requires_dist = requires_dist,
101+
extras = extras,
102+
group_deps = group_deps,
103+
dep_template = dep_template,
104+
group_name = group_name,
105+
)
89106

107+
# NOTE, if users specify annotations, the wheel downloads are not reused this
108+
# is to ensure that we don't break users config and also to ensure that we
109+
# can have predictable results.
90110
additional_content = []
91111
if annotation:
92112
kwargs["data"] = annotation.data
@@ -97,19 +117,96 @@ def generate_whl_library_build_bazel(
97117
if annotation.additive_build_content:
98118
additional_content.append(annotation.additive_build_content)
99119

120+
macro_parts = [
121+
render.call(
122+
"package_metadata",
123+
**_render(
124+
name = "package_metadata",
125+
purl = purl,
126+
visibility = ["//:__subpackages__"],
127+
),
128+
),
129+
render.call(
130+
"whl_library_srcs",
131+
**_render(**srcs_kwargs)
132+
)
133+
]
134+
135+
if config_load:
136+
loads.append("""load("{}", "{}")""".format(config_load, "packages"))
137+
from_requires_kwargs["include"] = "packages"
138+
139+
macro_parts.append(render.call(
140+
"whl_library_from_requires_dist",
141+
**_render(**from_requires_kwargs),
142+
))
143+
100144
contents = "\n".join(
101145
[
102146
_TEMPLATE.format(
103147
loads = "\n".join(loads),
104-
fn = fn,
105-
kwargs = render.indent("\n".join([
106-
"{} = {},".format(k, _RENDER.get(k, repr)(v))
107-
for k, v in sorted(kwargs.items())
108-
])),
109-
purl = repr(purl),
148+
macros = "\n\n".join(macro_parts),
110149
),
111150
] + additional_content,
112151
)
113152

114153
# NOTE: Ensure that we terminate with a new line
115154
return contents.rstrip() + "\n"
155+
156+
def generate_whl_library_deps_build_bazel(
157+
*,
158+
# TODO @aignas 2026-07-04: add extra args that are used in this function
159+
**kwargs):
160+
"""Generate a BUILD file for an unzipped Wheel
161+
162+
163+
Returns:
164+
A complete BUILD file as a string
165+
"""
166+
167+
loads = [
168+
"""load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_from_requires_dist")"""
169+
]
170+
171+
from_requires_kwargs = dict(
172+
name = name,
173+
metadata_name = metadata_name,
174+
metadata_version = metadata_version,
175+
requires_dist = requires_dist,
176+
extras = extras,
177+
group_deps = group_deps,
178+
dep_template = dep_template,
179+
group_name = group_name,
180+
)
181+
182+
macro_parts = [
183+
render.call(
184+
"alias",
185+
name=target,
186+
actual=whl_library.same_package_label(target),
187+
)
188+
for target in [
189+
# TODO @aignas 2026-07-04: use ./labels.bzl for the following
190+
"package_metadata",
191+
"data",
192+
"dist_info",
193+
"extracted_whl_files",
194+
]
195+
]
196+
197+
if config_load:
198+
loads.append("""load("{}", "{}")""".format(config_load, "packages"))
199+
from_requires_kwargs["include"] = "packages"
200+
201+
macro_parts.append(render.call(
202+
"whl_library_from_requires_dist",
203+
**_render(**from_requires_kwargs),
204+
))
205+
206+
contents = _TEMPLATE.format(
207+
loads = "\n".join(loads),
208+
macros = "\n\n".join(macro_parts),
209+
)
210+
211+
# NOTE: Ensure that we terminate with a new line
212+
return contents.rstrip() + "\n"

python/private/pypi/hub_builder.bzl

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ def hub_builder(
7171
# Mapping of whl_library repo names and their kwargs.
7272
# dict[str repo_name, dict[str, object] kwargs]
7373
_whl_libraries = {}, # modified by _add_whl_library
74+
# Mapping of whl_library_deps repo names and their kwargs.
75+
# dict[str repo_name, dict[str, object] kwargs]
76+
_whl_library_deps = {}, # modified by _add_whl_library
7477
# Map of repos and their config settings, and repo the config
7578
# setting originated from.
7679
# dict[str whl_name, dict[str config_setting, str repo_name]]
@@ -113,6 +116,7 @@ def _build(self):
113116
extra_aliases = {},
114117
exposed_packages = [],
115118
whl_libraries = {},
119+
whl_library_deps = {},
116120
)
117121
if self._logger.failed():
118122
return ret
@@ -142,6 +146,10 @@ def _build(self):
142146
# Mapping of whl_library repo names and their kwargs.
143147
# dict[str repo_name, dict[str, object] kwargs]
144148
whl_libraries = self._whl_libraries,
149+
150+
# Mapping of whl_library_deps repo names and their kwargs.
151+
# dict[str repo_name, dict[str, object] kwargs]
152+
whl_library_deps = self._whl_library_deps,
145153
)
146154

147155
def _pip_parse(self, module_ctx, pip_attr, python_version = None):
@@ -308,17 +316,43 @@ def _add_whl_library(self, *, python_version, whl, repo):
308316
# are more platforms defined than there are wheels for and users
309317
# disallow building from sdist.
310318
return
319+
320+
forbidden_args = {
321+
"annotation": True,
322+
"extra_pip_args": True,
323+
"python_interpreter": True,
324+
"python_interpreter_target": True,
325+
}
311326

312-
# TODO @aignas 2025-06-29: we should not need the version in the repo_name if
313-
# we are using pipstar and we are downloading the wheel using the downloader
314-
#
315-
# However, for that we should first have a different way to reference closures with
316-
# extras. For example, if some package depends on `foo[extra]` and another depends on
317-
# `foo`, we should have 2 py_library targets.
318-
repo_name = "{}_{}_{}".format(self.name, version_label(python_version), repo.repo_name)
327+
if [arg for arg in repo.args if arg in forbidden_args]:
328+
# no reuse of the whl_library because we have args that force the extraction of the whl
329+
# in the hub context. If we have whl-only pipstar extraction, then we can reuse the
330+
# extracted sources.
331+
repos_dict = self._whl_libraries
332+
deps_args = repo.args
333+
else:
334+
whl_repo_name = "whl_{}".format(repo.whl_repo_name)
319335

320-
if repo_name in self._whl_libraries:
321-
diff = _diff_dict(self._whl_libraries[repo_name], repo.args)
336+
self._whl_libraries[whl_repo_name] = {
337+
k: v for k, v in repo.args.items()
338+
if k not in forbidden_args | {
339+
"config_load": True
340+
}
341+
}
342+
343+
args = repo.args
344+
345+
deps_args = {}
346+
for key in ("config_load", "dep_template", "group_deps", "group_name", "annotation", "pip_data_exclude"):
347+
if key in args and args[key] != None:
348+
deps_args[key] = args[key]
349+
350+
deps_args["whl_library"] = "@{}//:BUILD.bazel".format(whl_repo_name)
351+
repos_dict = self._whl_library_deps
352+
353+
repo_name = "{}_{}_{}".format(self.name, version_label(python_version), repo.repo_name)
354+
if repo_name in repos_dict:
355+
diff = _diff_dict(repos_dict[repo_name], deps_args)
322356
if diff:
323357
self._logger.fail(lambda: (
324358
"Attempting to create a duplicate library {repo_name} for {whl_name} with different arguments. Already existing declaration has:\n".format(
@@ -331,7 +365,7 @@ def _add_whl_library(self, *, python_version, whl, repo):
331365
])
332366
))
333367
return
334-
self._whl_libraries[repo_name] = repo.args
368+
repos_dict[repo_name] = deps_args
335369

336370
mapping = self._whl_map.setdefault(whl.name, {})
337371
if repo.config_setting in mapping and mapping[repo.config_setting] != repo_name:
@@ -685,6 +719,7 @@ def _whl_repo(
685719

686720
return struct(
687721
repo_name = whl_repo_name(src.filename, src.sha256, *target_platforms),
722+
whl_repo_name = whl_repo_name(src.filename, src.sha256),
688723
args = args,
689724
config_setting = whl_config_setting(
690725
version = python_version,

python/private/pypi/labels.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ PY_LIBRARY_IMPL_LABEL = "_pkg"
2222
DATA_LABEL = "data"
2323
DIST_INFO_LABEL = "dist_info"
2424
NODEPS_LABEL = "no_deps"
25+
NODEPS_WHL_FILE_LABEL = "_whl_file"
26+
NODEPS_PY_LIBRARY_LABEL = "_srcs"

python/private/pypi/whl_extract.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ def whl_extract(rctx, *, whl_path, logger):
2626
logger = logger,
2727
)
2828

29+
# Symlink the METADATA file to be able to refer to it from another repository_rule. This allows
30+
# us to split the extracted sources and the closure itself to 2 different repository rules
31+
# allowing us to not extract the same wheels multiple times.
32+
rctx.symlink(metadata_file, "METADATA")
33+
2934
# Get the <prefix>.dist_info dir name
3035
dist_info_dir = metadata_file.dirname
3136
rctx.file(

0 commit comments

Comments
 (0)