使用 Buck2 和 Reindeer 生成 Rust 项目 BUCK
不管是 Buck2 还是 Bazel 构建 Rust 工程时都没有使用 Cargo.toml
文件, 这就意味着需要手动维护依赖关系, 这对于 Rust 项目来说是一个非常大的挑战。 Meta 提供的 Reindeer 可以帮助自动生成 BUCK 文件, 可以解决这部分的问题。
在示例工程中加入 rand 依赖
首先在项目中的 Cargo.toml
文件中加入 rand
依赖, 在 src/main.rs
文件中使用 rand
生成一个随机数。
use rand::{thread_rng, Rng};
fn main() {
let mut rng = thread_rng();
let x: u32 = rng.gen();
println!("A random value: {}", x);
}
虽然是使用 cargo build
或者 cargo run
命令都可以正常执行, 但是如果直接使用 buck2 build
命令构建项目, 会报错:
$ buck2 build //:rust-buck2
File changed: root//Cargo.toml
File changed: root//Cargo.lock
File changed: root//target/debug/.fingerprint/rust-buck2-fe6911eada5a5db6
387 additional file change events
Action failed: root//:rust-buck2 (rustc bin-pic-static_pic-link/rust_buck2-link bin,pic,link [diag])
Local command returned non-zero exit code 1
Reproduce locally: `env -- 'BUCK_SCRATCH_PATH=buck-out/v2/tmp/root/524f8da68ea2a374/__rust-buck2__/rustc/_buck_5339cff94 ...<omitted>... ck-out/v2/gen/root/524f8da68ea2a374/__rust-buck2__/bin-pic-static_pic-link/rust_buck2-link-diag.args (run `buck2 log what-failed` to get the full command)`
stdout:
stderr:
error[E0432]: unresolved import `rand`
--> ./src/main.rs:1:5
|
1 | use rand::{thread_rng, Rng};
| ^^^^ use of undeclared crate or module `rand`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0432`.
Build ID: 41b05c41-4e1d-45f2-a35b-ca2bb7ac0565
Jobs completed: 6. Time elapsed: 0.0s.
Cache hits: 0%. Commands: 1 (cached: 0, remote: 0, local: 1)
BUILD FAILED
Failed to build 'root//:rust-buck2 (prelude//platforms:default#524f8da68ea2a374)'
说明此时 Buck2 并不知道如何处理 rand
依赖。
使用 Reindeer 生成 BUCK 文件
首先在系统中安装 Reindeer,
$ cargo install --locked --git https://github.com/facebookincubator/reindeer reindeer
根据 Reindeer 的文档, 可以使用 reindeer buckify
命令生成 BUCK 文件。
$ reindeer buckify
这时候发现 BUCK
文件中多了很多内容, 但是之前的 rust_binary
的 rust-buck2
任务被移除了,
# @generated by `reindeer buckify`
load("@prelude//rust:cargo_buildscript.bzl", "buildscript_run")
load("@prelude//rust:cargo_package.bzl", "cargo")
http_archive(
name = "cfg-if-1.0.0.crate",
sha256 = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
strip_prefix = "cfg-if-1.0.0",
urls = ["https://crates.io/api/v1/crates/cfg-if/1.0.0/download"],
visibility = [],
)
cargo.rust_library(
name = "cfg-if-1.0.0",
srcs = [":cfg-if-1.0.0.crate"],
crate = "cfg_if",
crate_root = "cfg-if-1.0.0.crate/src/lib.rs",
edition = "2018",
visibility = [],
)
http_archive(
name = "getrandom-0.2.12.crate",
sha256 = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5",
strip_prefix = "getrandom-0.2.12",
urls = ["https://crates.io/api/v1/crates/getrandom/0.2.12/download"],
visibility = [],
)
cargo.rust_library(
name = "getrandom-0.2.12",
srcs = [":getrandom-0.2.12.crate"],
crate = "getrandom",
crate_root = "getrandom-0.2.12.crate/src/lib.rs",
edition = "2018",
features = ["std"],
platform = {
"linux-arm64": dict(
deps = [":libc-0.2.153"],
),
"linux-x86_64": dict(
deps = [":libc-0.2.153"],
),
"macos-arm64": dict(
deps = [":libc-0.2.153"],
),
"macos-x86_64": dict(
deps = [":libc-0.2.153"],
),
},
visibility = [],
deps = [":cfg-if-1.0.0"],
)
http_archive(
name = "libc-0.2.153.crate",
sha256 = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd",
strip_prefix = "libc-0.2.153",
urls = ["https://crates.io/api/v1/crates/libc/0.2.153/download"],
visibility = [],
)
cargo.rust_library(
name = "libc-0.2.153",
srcs = [":libc-0.2.153.crate"],
crate = "libc",
crate_root = "libc-0.2.153.crate/src/lib.rs",
edition = "2015",
visibility = [],
)
http_archive(
name = "ppv-lite86-0.2.17.crate",
sha256 = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de",
strip_prefix = "ppv-lite86-0.2.17",
urls = ["https://crates.io/api/v1/crates/ppv-lite86/0.2.17/download"],
visibility = [],
)
cargo.rust_library(
name = "ppv-lite86-0.2.17",
srcs = [":ppv-lite86-0.2.17.crate"],
crate = "ppv_lite86",
crate_root = "ppv-lite86-0.2.17.crate/src/lib.rs",
edition = "2018",
features = [
"simd",
"std",
],
visibility = [],
)
alias(
name = "rand",
actual = ":rand-0.8.5",
visibility = ["PUBLIC"],
)
http_archive(
name = "rand-0.8.5.crate",
sha256 = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
strip_prefix = "rand-0.8.5",
urls = ["https://crates.io/api/v1/crates/rand/0.8.5/download"],
visibility = [],
)
cargo.rust_library(
name = "rand-0.8.5",
srcs = [":rand-0.8.5.crate"],
crate = "rand",
crate_root = "rand-0.8.5.crate/src/lib.rs",
edition = "2018",
features = [
"alloc",
"default",
"getrandom",
"libc",
"rand_chacha",
"std",
"std_rng",
],
platform = {
"linux-arm64": dict(
deps = [":libc-0.2.153"],
),
"linux-x86_64": dict(
deps = [":libc-0.2.153"],
),
"macos-arm64": dict(
deps = [":libc-0.2.153"],
),
"macos-x86_64": dict(
deps = [":libc-0.2.153"],
),
},
visibility = [],
deps = [
":rand_chacha-0.3.1",
":rand_core-0.6.4",
],
)
http_archive(
name = "rand_chacha-0.3.1.crate",
sha256 = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
strip_prefix = "rand_chacha-0.3.1",
urls = ["https://crates.io/api/v1/crates/rand_chacha/0.3.1/download"],
visibility = [],
)
cargo.rust_library(
name = "rand_chacha-0.3.1",
srcs = [":rand_chacha-0.3.1.crate"],
crate = "rand_chacha",
crate_root = "rand_chacha-0.3.1.crate/src/lib.rs",
edition = "2018",
features = ["std"],
visibility = [],
deps = [
":ppv-lite86-0.2.17",
":rand_core-0.6.4",
],
)
http_archive(
name = "rand_core-0.6.4.crate",
sha256 = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
strip_prefix = "rand_core-0.6.4",
urls = ["https://crates.io/api/v1/crates/rand_core/0.6.4/download"],
visibility = [],
)
cargo.rust_library(
name = "rand_core-0.6.4",
srcs = [":rand_core-0.6.4.crate"],
crate = "rand_core",
crate_root = "rand_core-0.6.4.crate/src/lib.rs",
edition = "2018",
features = [
"alloc",
"getrandom",
"std",
],
visibility = [],
deps = [":getrandom-0.2.12"],
)
只能在 BUCK
文件中手动加入 rust_binary
任务, 并且加入 rand
依赖。
rust_binary(
name = "rust-buck2",
srcs = glob(["src/**/*.rs"]),
crate_root = "src/main.rs",
deps = [
":rand",
],
)
再次使用 buck2 build
命令构建项目或 buck2 run
命令运行项目, 都可以正常执行,
$ buck2 build //:rust-buck2
Build ID: 6b33c530-8f47-4c65-84d9-08586c0b56fa
Network: Up: 0B Down: 910KiB
Jobs completed: 154. Time elapsed: 21.6s.
Cache hits: 0%. Commands: 21 (cached: 0, remote: 0, local: 21)
BUILD SUCCEEDED
$ buck2 run //:rust-buck2
Build ID: 5ac5973f-423a-4248-be29-2935bdfdb606
Network: Up: 0B Down: 0B
Jobs completed: 3. Time elapsed: 0.0s.
BUILD SUCCEEDED
A random value: 3295093910
deps, alias, http_archive 和 cargo.rust_library
alias(
name = "rand",
actual = ":rand-0.8.5",
visibility = ["PUBLIC"],
)
在 BUCK
文件中可以看到一个 alias
指向了 rand-0.8.5
这个 cargo.rust_library
, 如果把 rust_binary
任务中的 :rand
替换成 :rand-0.8.5
也是可以正常执行的。 这里的 visibility
是指定了 alias
的可见性, 如果不指定的话默认是 PUBLIC
。
cargo.rust_library(
name = "rand-0.8.5",
srcs = [":rand-0.8.5.crate"],
crate = "rand",
crate_root = "rand-0.8.5.crate/src/lib.rs",
edition = "2018",
features = [
"alloc",
"default",
"getrandom",
"libc",
"rand_chacha",
"std",
"std_rng",
],
platform = {
"linux-arm64": dict(
deps = [":libc-0.2.153"],
),
"linux-x86_64": dict(
deps = [":libc-0.2.153"],
),
"macos-arm64": dict(
deps = [":libc-0.2.153"],
),
"macos-x86_64": dict(
deps = [":libc-0.2.153"],
),
},
visibility = [],
deps = [
":rand_chacha-0.3.1",
":rand_core-0.6.4",
],
)
cargo.rust_library
是文件头load("@prelude//rust:cargo_package.bzl", "cargo")
中定义的一个函数, 用于指定一个 Rust 依赖库,可以在工程目录下prelude/rust/cargo_package.bzl
文件中找到这个函数的定义。alias
的引入为 crate 升级提供了便利, 同时代码中的deps
也可以更加清晰, 和Cargo.toml
文件中的dependencies
更加匹配, 让 Rust 开发者更加容易理解。
http_archive(
name = "rand-0.8.5.crate",
sha256 = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
strip_prefix = "rand-0.8.5",
urls = ["https://crates.io/api/v1/crates/rand/0.8.5/download"],
visibility = [],
)
http_archive 是一个 Buck2 的内置 Rule, 用于从网络上下载文件, 这里用于下载 rand-0.8.5
这个 crate 的源码, 并且指定了 sha256
值, 用于校验下载的文件是否正确。
测试的项目在 GitHub - Rust Buck2 Example , 后续会持续更新。
Rust Monorepo Template with Buck2 中, 正在持续尝试构建一个完整的
Rust Monorepo
工程模板, 通过Buck2
和Reindeer
管理整个工程的构建和测试, 可以从这个项目中开始你的Rust Monorepo
之旅。