Skip to content

Commit ed0c6e9

Browse files
authored
Added cargo_bootstrap_repository rule (#891)
* Added `cargo_bootstrap_repository` rule. * Regenerate documentation
1 parent 80e0090 commit ed0c6e9

22 files changed

+652
-210
lines changed

cargo/BUILD.bazel

+1
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

+20
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+
)
+35
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

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

cargo/defs.bzl

+7
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

+7
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)