Skip to content

Commit e77fd66

Browse files
committed
Added cargo_bootstrap_repository rule.
1 parent 0346cc4 commit e77fd66

File tree

17 files changed

+519
-119
lines changed

17 files changed

+519
-119
lines changed

cargo/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ package(default_visibility = ["//visibility:public"])
55
bzl_library(
66
name = "rules",
77
srcs = glob(["**/*.bzl"]),
8+
deps = ["//cargo/private:bzl_lib"],
89
)

cargo/bootstrap/BUILD.bazel

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("//rust:defs.bzl", "rust_binary")
2+
3+
exports_files(
4+
[
5+
"bootstrap_installer.rs",
6+
],
7+
visibility = ["//visibility:public"],
8+
)
9+
10+
# This target is only used to confirm the source code is buildable
11+
# in a `cargo_bootstrap_repository` rule.
12+
rust_binary(
13+
name = "bootstrap_installer_bin",
14+
srcs = [
15+
"bootstrap_installer.rs",
16+
],
17+
rustc_env = {
18+
"RULES_RUST_CARGO_BOOTSTRAP_BINARY": "$(rootpath bootstrap_installer.rs)",
19+
},
20+
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//! A tool for installing bootstrapped Rust binaries into the requested paths.
2+
3+
use std::{
4+
env,
5+
fs::{copy, create_dir_all},
6+
path::PathBuf,
7+
};
8+
9+
fn install() -> std::io::Result<u64> {
10+
let binary = PathBuf::from(env!("RULES_RUST_CARGO_BOOTSTRAP_BINARY"));
11+
12+
// Consume only the first argument as the destination
13+
let dest = PathBuf::from(
14+
env::args()
15+
.nth(1)
16+
.expect("No destination argument provided"),
17+
);
18+
19+
// Create the parent directory structure if it doesn't exist
20+
if let Some(parent) = dest.parent() {
21+
if !parent.exists() {
22+
create_dir_all(parent)?;
23+
}
24+
}
25+
26+
// Copy the file to the requested destination
27+
copy(binary, dest)
28+
}
29+
30+
fn main() {
31+
if let Err(err) = install() {
32+
eprintln!("{:?}", err);
33+
std::process::exit(1);
34+
};
35+
}

cargo/cargo_bootstrap.bzl

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
"""The `cargo_bootstrap` rule is used for bootstrapping cargo binaries in a repository rule
2+
"""
3+
4+
load("//cargo/private:cargo_utils.bzl", "get_cargo_and_rustc", "get_host_triple")
5+
load("//rust:repositories.bzl", "DEFAULT_RUST_VERSION")
6+
load("//rust/platform:triple_mappings.bzl", "system_to_binary_ext")
7+
8+
_CARGO_BUILD_MODES = [
9+
"release",
10+
"debug",
11+
]
12+
13+
def cargo_bootstrap(
14+
repository_ctx,
15+
cargo_bin,
16+
rustc_bin,
17+
binary,
18+
cargo_manifest,
19+
quiet = False,
20+
build_mode = "release",
21+
target_dir = None):
22+
"""A function for bootstrapping a cargo binary within a repository rule
23+
24+
Args:
25+
repository_ctx (repository_ctx): The rule's context object.
26+
cargo_bin (path): The path to a Cargo binary.
27+
rustc_bin (path): The path to a Rustc binary.
28+
binary (str): The binary to build (the `--bin` parameter for Cargo).
29+
cargo_manifest (path): The path to a Cargo manifest (Cargo.toml file).
30+
quiet (bool, optional): Whether or not to print output from the Cargo command.
31+
build_mode (str, optional): The build mode to use
32+
target_dir (path, optional): The directory in which to produce build outputs
33+
(Cargo's --target-dir argument).
34+
35+
Returns:
36+
path: The path of the built binary within the target directory
37+
"""
38+
39+
if not target_dir:
40+
target_dir = repository_ctx.path(".")
41+
42+
args = [
43+
cargo_bin,
44+
"build",
45+
"--bin",
46+
binary,
47+
"--locked",
48+
"--target-dir",
49+
target_dir,
50+
"--manifest-path",
51+
cargo_manifest,
52+
]
53+
54+
if build_mode not in _CARGO_BUILD_MODES:
55+
fail("'{}' is not a supported build mode. Use one of {}".format(build_mode, _CARGO_BUILD_MODES))
56+
57+
if build_mode == "release":
58+
args.append("--release")
59+
60+
repository_ctx.report_progress("Cargo Bootstrapping {}".format(binary))
61+
result = repository_ctx.execute(
62+
args,
63+
environment = {
64+
"RUSTC": str(rustc_bin),
65+
},
66+
quiet = quiet,
67+
)
68+
69+
if result.return_code != 0:
70+
fail("exit_code: {}".format(
71+
result.return_code,
72+
))
73+
74+
extension = ""
75+
if "win" in repository_ctx.os.name:
76+
extension = ".exe"
77+
78+
binary_path = "{}/{}{}".format(
79+
build_mode,
80+
binary,
81+
extension,
82+
)
83+
84+
if not repository_ctx.path(binary_path).exists:
85+
fail("Failed to produce binary at {}".format(binary_path))
86+
87+
return binary_path
88+
89+
_BUILD_FILE_CONTENT = """\
90+
load("@rules_rust//rust:defs.bzl", "rust_binary")
91+
92+
package(default_visibility = ["//visibility:public"])
93+
94+
exports_files(["{binary}"])
95+
96+
alias(
97+
name = "{binary_name}",
98+
actual = "{binary}",
99+
)
100+
101+
rust_binary(
102+
name = "install",
103+
rustc_env = {{
104+
"RULES_RUST_CARGO_BOOTSTRAP_BINARY": "$(rootpath {binary})"
105+
}},
106+
data = [
107+
"{binary}",
108+
],
109+
srcs = [
110+
"@rules_rust//cargo/bootstrap:bootstrap_installer.rs"
111+
],
112+
)
113+
"""
114+
115+
_BUILD_FILE_EXTENSION_ALIAS = """\
116+
alias(
117+
name = "{binary}",
118+
actual = "{binary}.exe",
119+
)
120+
"""
121+
122+
def _cargo_bootstrap_repository_impl(repository_ctx):
123+
if repository_ctx.attr.version in ("beta", "nightly"):
124+
version_str = "{}-{}".format(repository_ctx.attr.version, repository_ctx.attr.iso_date)
125+
else:
126+
version_str = repository_ctx.attr.version
127+
128+
host_triple = get_host_triple(repository_ctx)
129+
tools = get_cargo_and_rustc(
130+
repository_ctx = repository_ctx,
131+
toolchain_repository_template = repository_ctx.attr.rust_toolchain_repository_template,
132+
host_triple = host_triple,
133+
version = version_str,
134+
)
135+
136+
binary = repository_ctx.attr.binary or repository_ctx.name
137+
138+
built_binary = cargo_bootstrap(
139+
repository_ctx,
140+
cargo_bin = tools.cargo,
141+
rustc_bin = tools.rustc,
142+
binary = binary,
143+
cargo_manifest = repository_ctx.path(repository_ctx.attr.cargo_toml),
144+
build_mode = repository_ctx.attr.build_mode,
145+
)
146+
147+
build_file_contents = [_BUILD_FILE_CONTENT.format(
148+
binary_name = binary,
149+
binary = built_binary,
150+
)]
151+
152+
# Windows binaries are expected to end in `.exe` extensions.
153+
# Here the extension is removed in order to generate an alias.
154+
extension = system_to_binary_ext(host_triple.system)
155+
if extension:
156+
build_file_contents.append(_BUILD_FILE_EXTENSION_ALIAS.format(
157+
binary = str(built_binary)[:-len(extension)],
158+
))
159+
160+
repository_ctx.file("BUILD.bazel", "\n".join(build_file_contents))
161+
162+
cargo_bootstrap_repository = repository_rule(
163+
doc = "A rule for bootstrapping a Rust binary using [Cargo](https://doc.rust-lang.org/cargo/)",
164+
implementation = _cargo_bootstrap_repository_impl,
165+
attrs = {
166+
"binary": attr.string(
167+
doc = "The binary to build (the `--bin` parameter for Cargo). If left empty, the repository name will be used.",
168+
),
169+
"build_mode": attr.string(
170+
doc = "The build mode the binary should be built with",
171+
values = [
172+
"debug",
173+
"release",
174+
],
175+
default = "release",
176+
),
177+
"cargo_lockfile": attr.label(
178+
doc = "The lockfile of the crate_universe resolver",
179+
allow_single_file = ["Cargo.lock"],
180+
mandatory = True,
181+
),
182+
"cargo_toml": attr.label(
183+
doc = "The path of the crate_universe resolver manifest (`Cargo.toml` file)",
184+
allow_single_file = ["Cargo.toml"],
185+
mandatory = True,
186+
),
187+
"iso_date": attr.string(
188+
doc = "The iso_date of cargo binary the resolver should use. Note: This can only be set if `version` is `beta` or `nightly`",
189+
),
190+
"rust_toolchain_repository_template": attr.string(
191+
doc = (
192+
"The template to use for finding the host `rust_toolchain` repository. `{version}` (eg. '1.53.0'), " +
193+
"`{triple}` (eg. 'x86_64-unknown-linux-gnu'), `{system}` (eg. 'darwin'), and `{arch}` (eg. 'aarch64') " +
194+
"will be replaced in the string if present."
195+
),
196+
default = "rust_{system}_{arch}",
197+
),
198+
"srcs": attr.label_list(
199+
doc = "Souces to crate to build.",
200+
allow_files = True,
201+
mandatory = True,
202+
),
203+
"version": attr.string(
204+
doc = "The version of cargo the resolver should use",
205+
default = DEFAULT_RUST_VERSION,
206+
),
207+
"_cc_toolchain": attr.label(
208+
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
209+
),
210+
},
211+
)

cargo/defs.bzl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Common definitions for the `@rules_rust//cargo` package"""
2+
3+
load(":cargo_bootstrap.bzl", _cargo_bootstrap_repository = "cargo_bootstrap_repository")
4+
load(":cargo_build_script.bzl", _cargo_build_script = "cargo_build_script")
5+
6+
cargo_bootstrap_repository = _cargo_bootstrap_repository
7+
cargo_build_script = _cargo_build_script

cargo/private/BUILD.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
2+
3+
bzl_library(
4+
name = "bzl_lib",
5+
srcs = glob(["**/*.bzl"]),
6+
visibility = ["//:__subpackages__"],
7+
)

0 commit comments

Comments
 (0)