Skip to content

Commit d83ef8b

Browse files
authored
feat: don't require users to build tools from source (#287)
Wire up the toolchains along two different codepaths, based on whether our version was stamped as a release, or otherwise a pre-release. When a release: - fetch tools from GitHub releases using `rctx.download` - each fetched tool goes into a separate external repo with its own `toolchain` declaration, so Bazel fetches only the needed ones - the resulting repos are returned to repositories.bzl which registers them When a pre-release: - continue to build the tools from source, using existing toolchains --- ### Type of change - New feature or functionality (change which adds functionality) ### Test plan - Manual testing; please provide instructions so we can reproduce: Use my personal fork to test against a release I performed there.
1 parent e69f5ef commit d83ef8b

File tree

8 files changed

+153
-72
lines changed

8 files changed

+153
-72
lines changed

.github/workflows/ci.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,9 @@ jobs:
2929
{"folder": ".", "bazelversion": "6.4.0"},
3030
{"folder": ".", "bzlmodEnabled": true}
3131
]
32+
33+
verify-bcr-patches:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
- run: patch --dry-run -p1 < .bcr/patches/*.patch

e2e/smoke/WORKSPACE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ crate_repositories()
5353
# Alternatively, you can skip calling this function, so long as you've
5454
# already fetched all the dependencies.
5555
load("@aspect_rules_py//py:repositories.bzl", "rules_py_dependencies")
56+
5657
rules_py_dependencies()
5758

5859
# "Installation" for rules_python

py/private/toolchain/tools.bzl

Lines changed: 101 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
1-
"""Toolchain for making toolchains"""
1+
"""Utilities for making toolchains"""
2+
load("//tools:integrity.bzl", "RELEASED_BINARY_INTEGRITY")
3+
load("//tools:version.bzl", "VERSION")
24

3-
load("//tools:version.bzl", "IS_PRERELEASE")
5+
# The expected config for each tool, whether it runs in an action or at runtime
6+
RUST_BIN_CFG = {
7+
"unpack": "exec",
8+
"venv": "target",
9+
}
410

511
TOOLCHAIN_PLATFORMS = {
612
"darwin_amd64": struct(
7-
release_platform = "macos-amd64",
13+
arch = "x86_64",
14+
vendor_os_abi = "apple-darwin",
815
compatible_with = [
916
"@platforms//os:macos",
1017
"@platforms//cpu:x86_64",
1118
],
1219
),
1320
"darwin_arm64": struct(
14-
release_platform = "macos-arm64",
21+
arch = "aarch64",
22+
vendor_os_abi = "apple-darwin",
1523
compatible_with = [
1624
"@platforms//os:macos",
1725
"@platforms//cpu:aarch64",
1826
],
1927
),
2028
"linux_amd64": struct(
21-
release_platform = "linux-amd64",
29+
arch = "x86_64",
30+
vendor_os_abi = "unknown-linux-gnu",
2231
compatible_with = [
2332
"@platforms//os:linux",
2433
"@platforms//cpu:x86_64",
2534
],
2635
),
2736
"linux_arm64": struct(
28-
release_platform = "linux-arm64",
37+
arch = "aarch64",
38+
vendor_os_abi = "unknown-linux-gnu",
2939
compatible_with = [
3040
"@platforms//os:linux",
3141
"@platforms//cpu:aarch64",
@@ -56,7 +66,7 @@ def _toolchain_impl(ctx):
5666

5767
return [toolchain_info, default_info, template_variables]
5868

59-
_toolchain = rule(
69+
py_tool_toolchain = rule(
6070
implementation = _toolchain_impl,
6171
attrs = {
6272
"bin": attr.label(
@@ -69,49 +79,97 @@ _toolchain = rule(
6979
},
7080
)
7181

72-
def _make_toolchain_name(name, platform):
73-
return "{}_{}_toolchain".format(name, platform)
74-
75-
def make_toolchain(name, toolchain_type, tools, cfg = "exec"):
82+
def source_toolchain(name, toolchain_type, bin):
7683
"""Makes vtool toolchain and repositories
7784
7885
Args:
7986
name: Override the prefix for the generated toolchain repositories.
8087
toolchain_type: Toolchain type reference.
81-
tools: Mapping of tool binary to platform.
82-
cfg: Generate a toolchain for the target or exec config.
88+
bin: the rust_binary target
8389
"""
8490

85-
if IS_PRERELEASE:
86-
toolchain_rule = "{}_toolchain_source".format(name)
87-
_toolchain(
88-
name = toolchain_rule,
89-
bin = tools["from-source"],
90-
template_var = "{}_BIN".format(name.upper()),
91-
)
92-
native.toolchain(
93-
name = _make_toolchain_name(name, "source"),
94-
toolchain = toolchain_rule,
95-
toolchain_type = toolchain_type,
91+
toolchain_rule = "{}_toolchain_source".format(name)
92+
py_tool_toolchain(
93+
name = toolchain_rule,
94+
bin = bin,
95+
template_var = "{}_BIN".format(name.upper()),
96+
)
97+
native.toolchain(
98+
name = "{}_source_toolchain".format(name),
99+
toolchain = toolchain_rule,
100+
toolchain_type = toolchain_type,
101+
)
102+
103+
def _tool_repo_impl(rctx):
104+
build_content = """\
105+
# Generated by @aspect_rules_py//py/private/toolchain:tools.bzl
106+
load("@aspect_rules_py//py/private/toolchain:tools.bzl", "py_tool_toolchain")
107+
108+
package(default_visibility = ["//visibility:public"])
109+
"""
110+
# For manual testing, override these environment variables
111+
# TODO: use rctx.getenv when available, see https://github.com/bazelbuild/bazel/pull/20944
112+
release_fork = "aspect-build"
113+
release_version = VERSION
114+
if "RULES_PY_RELEASE_FORK" in rctx.os.environ:
115+
release_fork = rctx.os.environ["RULES_PY_RELEASE_FORK"]
116+
if "RULES_PY_RELEASE_VERSION" in rctx.os.environ:
117+
release_version = rctx.os.environ["RULES_PY_RELEASE_VERSION"]
118+
119+
for tool, cfg in RUST_BIN_CFG.items():
120+
filename = "-".join([
121+
tool,
122+
TOOLCHAIN_PLATFORMS[rctx.attr.platform].arch,
123+
TOOLCHAIN_PLATFORMS[rctx.attr.platform].vendor_os_abi,
124+
])
125+
url = "https://github.com/{}/rules_py/releases/download/v{}/{}".format(
126+
release_fork,
127+
release_version,
128+
filename
96129
)
97-
return
98-
99-
for [platform, meta] in TOOLCHAIN_PLATFORMS.items():
100-
toolchain_rule = "{}_toolchain_{}".format(name, platform)
101-
_toolchain(
102-
name = toolchain_rule,
103-
bin = tools[meta.release_platform],
104-
template_var = "{}_BIN".format(name.upper()),
130+
rctx.download(
131+
url = url,
132+
sha256 = RELEASED_BINARY_INTEGRITY[filename],
133+
executable = True,
134+
output = tool,
105135
)
136+
build_content += """\
137+
py_tool_toolchain(name = "concrete_{tool}_toolchain", bin = "{tool}", template_var = "{tool_upper}_BIN")
138+
139+
toolchain(
140+
name = "{tool}_toolchain",
141+
{cfg}_compatible_with = {compatible_with},
142+
toolchain = "concrete_{tool}_toolchain",
143+
toolchain_type = "@aspect_rules_py//py/private/toolchain/{tool}:toolchain_type",
144+
)
145+
""".format(
146+
cfg = cfg,
147+
compatible_with = TOOLCHAIN_PLATFORMS[rctx.attr.platform].compatible_with,
148+
tool = tool,
149+
tool_upper = tool.upper(),
150+
)
151+
152+
rctx.file("BUILD.bazel", build_content)
106153

107-
args = dict({
108-
"name": _make_toolchain_name(name, platform),
109-
"toolchain": toolchain_rule,
110-
"toolchain_type": toolchain_type,
111-
})
112-
if cfg == "exec":
113-
args.update({"exec_compatible_with": meta.compatible_with})
114-
else:
115-
args.update({"target_compatible_with": meta.compatible_with})
116-
117-
native.toolchain(**args)
154+
_tool_repo = repository_rule(
155+
doc = "Download pre-built binary tools and create toolchains for them",
156+
implementation = _tool_repo_impl,
157+
attrs = {
158+
"platform": attr.string(mandatory = True, values = TOOLCHAIN_PLATFORMS.keys()),
159+
}
160+
)
161+
162+
def binary_tool_repos(name):
163+
"""Create a downloaded toolchain for every tool under every supported platform.
164+
165+
Args:
166+
name: prefix used in created repositories
167+
Returns:
168+
list of toolchain targets to register
169+
"""
170+
result = []
171+
for platform in TOOLCHAIN_PLATFORMS.keys():
172+
plat_repo_name = ".".join([name, platform])
173+
result.append("@{}//:all".format(plat_repo_name))
174+
_tool_repo(name = plat_repo_name, platform = platform)
175+
return result
Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
load("//py/private/toolchain:tools.bzl", "make_toolchain")
1+
load("//py/private/toolchain:tools.bzl", "source_toolchain")
22

33
toolchain_type(
44
name = "toolchain_type",
5+
visibility = ["//visibility:public"],
56
)
67

7-
make_toolchain(
8+
source_toolchain(
89
name = "unpack",
910
toolchain_type = ":toolchain_type",
10-
tools = {
11-
"from-source": "//py/tools/unpack_bin",
12-
"macos-amd64": "@aspect_rules_py//py/tools/unpack_bin/bins:unpack-x86_64-apple-darwin",
13-
"macos-arm64": "@aspect_rules_py//py/tools/unpack_bin/bins:unpack-aarch64-apple-darwin",
14-
"linux-amd64": "@aspect_rules_py//py/tools/unpack_bin/bins:unpack-x86_64-unknown-linux-musl",
15-
"linux-arm64": "@aspect_rules_py//py/tools/unpack_bin/bins:unpack-aarch64-unknown-linux-musl",
16-
},
11+
bin = "//py/tools/unpack_bin",
1712
)
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
load("//py/private/toolchain:tools.bzl", "make_toolchain")
1+
load("//py/private/toolchain:tools.bzl", "source_toolchain")
22

33
toolchain_type(
44
name = "toolchain_type",
5+
visibility = ["//visibility:public"],
56
)
67

7-
make_toolchain(
8+
source_toolchain(
89
name = "venv",
9-
cfg = "target",
10+
# When running from source, we assume the target is the exec platform.
11+
# TODO: when we have cross-compile Mac -> Linux, pre-release users should be able to
12+
# build a py_image on their Mac and have the linux venv rust_binary end up in the container.
13+
# cfg = "target",
1014
toolchain_type = ":toolchain_type",
11-
tools = {
12-
"from-source": "//py/tools/venv_bin",
13-
"macos-amd64": "@aspect_rules_py//py/tools/venv_bin/bins:venv-x86_64-apple-darwin",
14-
"macos-arm64": "@aspect_rules_py//py/tools/venv_bin/bins:venv-aarch64-apple-darwin",
15-
"linux-amd64": "@aspect_rules_py//py/tools/venv_bin/bins:venv-x86_64-unknown-linux-musl",
16-
"linux-arm64": "@aspect_rules_py//py/tools/venv_bin/bins:venv-aarch64-unknown-linux-musl",
17-
},
15+
bin = "//py/tools/venv_bin",
1816
)

py/repositories.bzl

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ See https://docs.bazel.build/versions/main/skylark/deploying.html#dependencies
77
load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive")
88
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
99
load("//py/private/toolchain:autodetecting.bzl", _register_autodetecting_python_toolchain = "register_autodetecting_python_toolchain")
10+
load("//py/private/toolchain:tools.bzl", "binary_tool_repos")
11+
load("//tools:version.bzl", "IS_PRERELEASE")
1012

1113
def http_archive(name, **kwargs):
1214
maybe(_http_archive, name = name, **kwargs)
@@ -21,11 +23,13 @@ register_autodetecting_python_toolchain = _register_autodetecting_python_toolcha
2123
# and released only in semver majors.
2224

2325
# buildifier: disable=unnamed-macro
24-
def rules_py_dependencies(register_py_toolchains = True):
25-
"""Fetch rules_py's dependencies, and optionally register toolchains.
26+
def rules_py_dependencies(name = "rules_py_tools", register = True, prerelease = IS_PRERELEASE):
27+
"""Fetch rules_py's dependencies
2628
2729
Args:
28-
register_py_toolchains: When true, rules_py's toolchains are automatically registered.
30+
name: prefix for generated repositories
31+
register: whether to register the toolchains created, should be false under bzlmod
32+
prerelease: whether to build rust tools from source, rather than download pre-built binaries
2933
"""
3034

3135
# The minimal version of bazel_skylib we require
@@ -49,9 +53,18 @@ def rules_py_dependencies(register_py_toolchains = True):
4953
strip_prefix = "rules_python-0.31.0",
5054
url = "https://github.com/bazelbuild/rules_python/releases/download/0.31.0/rules_python-0.31.0.tar.gz",
5155
)
56+
57+
# When running from a release version:
58+
# Fetch remote tools from the release and create toolchain for them
59+
if prerelease:
60+
if register:
61+
native.register_toolchains(
62+
"@aspect_rules_py//py/private/toolchain/venv/...",
63+
"@aspect_rules_py//py/private/toolchain/unpack/...",
64+
)
65+
return
5266

53-
if register_py_toolchains:
54-
native.register_toolchains(
55-
"@aspect_rules_py//py/private/toolchain/venv/...",
56-
"@aspect_rules_py//py/private/toolchain/unpack/...",
57-
)
67+
toolchains = binary_tool_repos(name)
68+
if register:
69+
native.register_toolchains(*toolchains)
70+

tools/integrity.bzl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,14 @@ This file contents are entirely replaced during release publishing.
44
The checked in content is only here to allow load() statements in the sources to resolve.
55
"""
66

7-
RELEASED_BINARY_INTEGRITY = {}
7+
# FROM https://github.com/alexeagle/rules_py/releases/tag/v0.101.0
8+
RELEASED_BINARY_INTEGRITY = {
9+
"unpack-aarch64-apple-darwin": "5199cb98d59535ba8609c4a538be99d8759e971ab9d1e680d932091529860868",
10+
"unpack-x86_64-apple-darwin": "23d0eb9ec09609c00d65779234f1210506eba1bf64090a01cef71d5175e3e6b6",
11+
"venv-aarch64-apple-darwin": "e02b478358641abf72ca5192948101e18a3dabccb48e7ee704759ad39c2f718a",
12+
"venv-x86_64-apple-darwin": "13d0c08a23b1064489df522d2327cfbaf0058b9364ca2ffee51b5b6f06a6930e",
13+
"unpack-aarch64-unknown-linux-gnu": "48552e399a1f2ab97e62ca7fce5783b6214e284330c7555383f43acf82446636",
14+
"unpack-x86_64-unknown-linux-gnu": "fd265552bfd236efef519f81ce783322a50d8d7ab5af5d08a713e519cedff87f",
15+
"venv-aarch64-unknown-linux-gnu": "b05d6cbe485e8ed1872f330012929126778744208e47485d7e2be285a027c7ca",
16+
"venv-x86_64-unknown-linux-gnu": "61f53fe45da5f2f160907c87c1a2d663bc57eee667520a46490a837825be61c9"
17+
}

tools/version.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"version information. replaced with stamped info with each release"
22

3-
# This is automagically replace by git during git archive using `git export-subst`
3+
# Automagically "stamped" by git during `git archive` thanks to `export-subst` line in .gitattributes.
44
# See https://git-scm.com/docs/git-archive#Documentation/git-archive.txt-export-subst
55
_VERSION_PRIVATE = "$Format:%(describe:tags=true)$"
66

0 commit comments

Comments
 (0)