Cargo-raze 目前是一个未维护且不受支持的产品。
rules_rust
中的crate_universe
是一个具有类似目标的受支持和维护的产品。
我们相信crate_universe
支持cargo-raze支持的所有功能。我们鼓励您尝试迁移到它。如果您发现缺少支持或错误,请针对rules_rust
提出问题或 PR。
cargo-raze
已被存档,以反映针对其提交的 PR 和问题不太可能得到解决的现实。
如果您有兴趣接管该项目的维护,请联系 Bazel Slack 中的#rust。
一个实验性的支持 Cargo 插件,用于将工作区级别的 Cargo.toml 提炼为使用 Rules_rust 的代码可以直接依赖的 BUILD 目标。
这不是谷歌的官方产品(实验性的或其他的),它只是碰巧归谷歌所有的代码。
该项目综合了 Cargo 的依赖解析逻辑和一些功能(例如特性)并将脚本构建为可执行规则,Bazel 可以运行这些规则来编译 Rust 箱。尽管标准的rules_rust规则可用于从头开始编译Rust代码,但依赖关系生态系统的细粒度使得基于该生态系统转换依赖关系树变得繁重,即使对于依赖关系很少的代码也是如此。
Cargo-raze 可以通过以下两种模式之一生成可构建的目标:Vendoring 或 Non-Vendoring。在供应模式下,开发人员使用通用的cargo vendor
子命令将其工作区Cargo.toml指示的依赖项检索到cargo-raze然后用BUILD文件填充的目录中。在非供应模式下,cargo-raze 生成 BUILD 文件的平面列表,以及可以在 WORKSPACE 文件中调用的工作区级宏,以与 Cargo 本身类似的方式自动拉取依赖项。
在这两种情况下,第一步都是决定将 Cargo 依赖项放置在工作区中的位置。这个库的设计考虑到了 monorepos,组织决定每个人都指向的一组依赖项。其目的是让依赖项中的利益相关者协作以原子方式升级依赖项,并同时修复其代码库中的损坏。如果这不可行,仍然可以在去中心化场景中使用cargo-raze,但这种解耦的存储库不太可能与当前的实现很好地交互。
无论选择哪种方法,都应将 rust_rules 引入工作空间。这是一个例子:
load ( "@bazel_tools//tools/build_defs/repo:http.bzl" , "http_archive" )
http_archive (
name = "rules_rust" ,
sha256 = "accb5a89cbe63d55dcdae85938e56ff3aa56f21eb847ed826a28a83db8500ae6" ,
strip_prefix = "rules_rust-9aa49569b2b0dacecc51c05cee52708b7255bd98" ,
urls = [
# Main branch as of 2021-02-19
"https://github.com/bazelbuild/rules_rust/archive/9aa49569b2b0dacecc51c05cee52708b7255bd98.tar.gz" ,
],
)
load ( "@rules_rust//rust:repositories.bzl" , "rust_repositories" )
rust_repositories ( edition = "2018" )
对于仅限 Bazel 的项目,用户应首先生成包含感兴趣的依赖项的标准 Cargo.toml。请注意包含[lib]
指令,以便 Cargo 不会抱怨缺少此模拟板条箱的源文件。这是一个例子:
[ package ]
name = " compile_with_bazel "
version = " 0.0.0 "
# Mandatory (or Cargo tooling is unhappy)
[ lib ]
path = " fake_lib.rs "
[ dependencies ]
log = " =0.3.6 "
标准 Cargo.toml 就位后,请在下一节中添加[package.metadata.raze]
指令。
几乎所有规范的货物设置都应该能够在cargo-raze
中正常运行。假设 Cargo 工作区现在嵌套在 Bazel 工作区下,用户只需将 RazeSettings 添加到 Cargo.toml 文件中即可用于生成 Bazel 文件
# Above this line should be the contents of your Cargo.toml file
[ package . metadata . raze ]
# The path at which to write output files.
#
# `cargo raze` will generate Bazel-compatible BUILD files into this path.
# This can either be a relative path (e.g. "foo/bar"), relative to this
# Cargo.toml file; or relative to the Bazel workspace root (e.g. "//foo/bar").
workspace_path = " //cargo "
# This causes aliases for dependencies to be rendered in the BUILD
# file located next to this `Cargo.toml` file.
package_aliases_dir = " . "
# The set of targets to generate BUILD rules for.
targets = [
" x86_64-apple-darwin " ,
" x86_64-pc-windows-msvc " ,
" x86_64-unknown-linux-gnu " ,
]
# The two acceptable options are "Remote" and "Vendored" which
# is used to indicate whether the user is using a non-vendored or
# vendored set of dependencies.
genmode = " Remote "
在使用 Cargo 工作区的项目中,用户应将所有raze
设置组织到包含[workspace]
定义的顶级Cargo.toml
文件中的 [ [workspace.metadata.raze]
字段中。这些设置应该与上一节中的[package.metadata.raze]
中看到的设置相同。但是,板条箱设置仍可以放置在工作区成员的Cargo.toml
文件中:
# Above this line should be the contents of your package's Cargo.toml file
# Note that `some-dependency` is the name of an example dependency and
# `<0.3.0` is a semver version for the dependency crate's version. This
# should always be compaitble in some way with the dependency version
# specified in the `[dependencies]` section of the package defined in
# this file
[ package . metadata . raze . crates . some-dependency . '<0 . 3 . 0' ]
additional_flags = [
" --cfg=optional_feature_a " ,
" --cfg=optional_feature_b " ,
]
# This demonstrates that multiple crate settings may be defined.
[ package . metadata . raze . crates . some-other-dependency . '*' ]
additional_flags = [
" --cfg=special_feature " ,
]
在远程模式下,选择与供应商模式类似的目录。但在这种情况下,它仅包含构建文件、工作空间的供应商指令以及显式依赖项的别名。需要稍微不同的管道。
这告诉 Raze 不要期望提供依赖项并生成不同的文件。
首先,安装cargo-raze。
$ cargo install cargo-raze
接下来,从cargo目录中执行cargo raze
$ cargo raze
最后,在您的工作空间中调用远程库获取功能:
load ( "//cargo:crates.bzl" , "raze_fetch_remote_crates" )
# Note that this method's name depends on your gen_workspace_prefix setting.
# `raze` is the default prefix.
raze_fetch_remote_crates ()
这告诉 Bazel 从哪里获取依赖项,以及如何构建它们:使用//cargo
中生成的文件。
您可以通过依赖//cargo:your_dependency_name
来依赖任何 Rust 规则中的任何显式依赖项。
在供应模式下,直接选择一个根来容纳供应的依赖项并成为这些构建规则的网关。 //cargo
是常规的,但//third_party/cargo
可能更适合满足组织需求。由于特定于实现的特性,直接供应到 root 并没有得到很好的支持,但将来可能会得到支持。从这里开始, //cargo
将是假定的目录。
首先,安装供应商和生成可构建目标所需的工具。
$ cargo install cargo-raze
接下来,从 Cargo/ 目录中提供您的依赖项。这也将更新您的Cargo.lock
文件。
$ cargo vendor --versioned-dirs
最后,再次从cargo/
目录中生成 BUILD 文件
$ cargo raze
现在,您可以通过依赖//cargo:your_dependency_name
来依赖任何 Rust 规则中的任何显式依赖项。
Cargo-raze 可以完全在 Bazel 中构建并使用,无需在主机上设置 Cargo。为此,只需将以下内容添加到项目中的 WORKSPACE 文件中:
load ( "@bazel_tools//tools/build_defs/repo:http.bzl" , "http_archive" )
http_archive (
name = "cargo_raze" ,
sha256 = "c664e258ea79e7e4ec2f2b57bca8b1c37f11c8d5748e02b8224810da969eb681" ,
strip_prefix = "cargo-raze-0.11.0" ,
url = "https://github.com/google/cargo-raze/archive/v0.11.0.tar.gz" ,
)
load ( "@cargo_raze//:repositories.bzl" , "cargo_raze_repositories" )
cargo_raze_repositories ()
load ( "@cargo_raze//:transitive_deps.bzl" , "cargo_raze_transitive_deps" )
cargo_raze_transitive_deps ()
完成此操作后,用户可以运行@cargo_raze//:raze
目标来生成新的 BUILD 文件。例如:
bazel run @cargo_raze//:raze -- --manifest-path= $( realpath /Cargo.toml )
请注意,使用vendored
genmode 的用户仍然必须以某种方式供应其依赖项,因为cargo-raze
目前无法为您执行此操作。
一些板条箱执行“构建脚本”,虽然在技术上它的功能不受限制,但通常会执行一些常见的操作之一。
下面提到的所有选项都在 src/settings.rs 文件中枚举。
在某些情况下,crate 仅使用基本信息来生成 Rust 源文件。这些构建脚本规则实际上可以在 Bazel 中执行和使用,方法是在生成之前在 Cargo.toml 中包含指令:
[ package . metadata . raze . crates . clang-sys . '0 . 21 . 1' ]
gen_buildrs = true
此设置告诉 Cargo-raze 为构建脚本生成 rust_binary 目标,并将其生成的(OUT_DIR 样式)输出定向到父包。
一些构建脚本有条件地向标准输出发出 Cargo 知道如何传播的指令。不幸的是,管理构建时生成的依赖信息并不那么简单,因此如果标志是静态已知的(也许,因为编译目标是静态已知的),则可以通过以下方式从 Cargo.toml 中提供它们
[ package . metadata . raze . crates . unicase . '2 . 1 . 0' ]
additional_flags = [
# Rustc is 1.15, enable all optional settings
" --cfg=__unicase__iter_cmp " ,
" --cfg=__unicase__defauler_hasher " ,
]
以这种方式提供的标志直接交给 rustc。参考文档的构建脚本部分来解释遇到的构建脚本和标准输出指令可能会有所帮助,可在此处找到:https://doc.rust-lang.org/cargo/reference/build-scripts.html
有两种方法可以提供 crate 编译所需的系统库。第一种是直接供应系统库,为其制定 BUILD 规则,并将依赖项添加到相应的-sys
crate。对于 openssl,这可能部分类似于:
[ package . metadata . raze . crates . openssl-sys . '0 . 9 . 24' ]
additional_flags = [
# Vendored openssl is 1.0.2m
" --cfg=ossl102 " ,
" --cfg=version=102 " ,
]
additional_deps = [
" @//third_party/openssl:crypto " ,
" @//third_party/openssl:ssl " ,
]
[ package . metadata . raze . crates . openssl . '0 . 10 . 2' ]
additional_flags = [
# Vendored openssl is 1.0.2m
" --cfg=ossl102 " ,
" --cfg=version=102 " ,
" --cfg=ossl10x " ,
]
在某些情况下,直接连接本地系统依赖项可能更好。为此,请参阅 Bazel 文档的new_local_repository
部分。对于工作空间中 llvm 的预编译版本,这可能类似于:
new_local_repository (
name = "llvm" ,
build_file = "BUILD.llvm.bazel" ,
path = "/usr/lib/llvm-3.9" ,
)
在某些情况下,可能需要完全覆盖 sys crate。这可以通过删除和补充预生成 Cargo.toml 中的依赖项来实现:
[ package . metadata . raze . crates . sdl2 . '0 . 31 . 0' ]
skipped_deps = [
" sdl2-sys-0.31.0 "
]
additional_deps = [
" @//cargo/overrides/sdl2-sys:sdl2_sys "
]
有些板条箱提供了有用的二进制文件,它们本身可以用作编译过程的一部分:Bindgen 就是一个很好的例子。 Bindgen 通过处理 C 或 C++ 文件来生成 Rust 源文件。可以将指令添加到 Cargo.toml 中,告诉 Bazel 为您公开此类二进制文件:
[ package . metadata . raze . crates . bindgen . '0 . 32 . 2' ]
gen_buildrs = true # needed to build bindgen
extra_aliased_targets = [
" cargo_bin_bindgen "
]
Cargo-raze 在二进制目标上添加了cargo_bin_
前缀,虽然 Cargo 允许二进制文件和库共享相同的目标名称,但 Bazel 不允许这样做。
目前,cargo 不会收集有关不提供任何库的 crate 的元数据。这意味着在Cargo.toml
文件的[dependencies]
部分中指定它们不会导致生成 Bazel 目标。当使用genmode = "Remote"
时,Cargo-raze 有一个特殊的字段来处理这些箱子:
[ package . metadata . raze . binary_deps ]
wasm-bindgen-cli = " 0.2.68 "
在上面的代码片段中, wasm-bindgen-cli
包被定义为二进制依赖项,Cargo-raze 将确保此包的元数据以及此处定义的任何其他包都包含在结果输出目录中。 [package.metadata.raze.binary_deps]
下指定的目标的锁定文件将生成到workspace_path
指定的路径内的lockfiles
目录中。
请注意, binary_deps
字段可以进入工作区和包元数据,但是,一次只能存在一个二进制依赖项的定义。如果您有多个依赖于单个二进制依赖项的包,则需要将该定义移至工作区元数据中。
将default_gen_buildrs设置为true将导致cargo-raze为所有需要它们的板条箱生成构建脚本:
[ package . metadata . raze ]
workspace_path = " //cargo "
genmode = " Remote "
default_gen_buildrs = true
这个设置是方便性和正确性之间的权衡。通过启用它,您应该会发现许多包可以工作,而无需显式指定任何标志,也无需手动启用单独的构建脚本。但是通过打开它,您将允许您使用的所有 crate 在构建时运行任意代码,并且它们执行的操作可能不是封闭的。
即使启用此设置,您可能仍然需要为一些 crate 提供额外的设置。例如,ring crate 需要在构建时访问源树:
[ package . metadata . raze . crates . ring . '*' ]
compile_data_attr = " glob([ " **/*.der " ]) "
如果您希望禁用单个 crate 上的构建脚本,可以按如下方式操作:
[ package . metadata . raze . crates . some_dependency . '*' ]
gen_buildrs = false
Bazel(“快速”、“正确”,选择两个)是一个经过实战考验的构建系统,Google 使用它来编译令人难以置信的大型多语言项目,无需重复工作,也不会影响正确性。它通过限制给定编译对象可以使用什么机制来发现依赖关系并强制可构建单元表达其依赖关系的完整集合来部分地实现这一点。它期望两组相同的构建目标输入产生逐字节等效的最终结果。
作为交换,用户将获得可定制和可扩展的构建系统,该系统可以编译任何类型的可编译目标,并允许表达“非常规依赖项”,例如 Protobuf 对象、预编译图形着色器或生成的代码,同时保持快速和正确。
考虑到 Bazel 的优势(高度粒度的构建单元)构建的大型应用程序也有可能(尽管尚未通过基准测试证明)编译速度会显着加快,因为它们能够更积极地缓存并避免在迭代时重新编译尽可能多的代码。
无论好坏,Rust 生态系统严重依赖 Cargo crate 来提供标准库中常见的功能。这对于语言的发展来说实际上是一件奇妙的事情,因为它描述了一个稳定的结构化过程(实验板条箱 -> 1.0 板条箱 -> RFC -> 包含在 stdlib 中),但这意味着无法访问这个生态系统的人必须重新发明许多轮子。
除此之外,还有一些很棒的包可以帮助 Rust 开发人员与行业标准系统和库进行交互,从而大大加快该语言的开发速度。
尽管模拟 Cargo 功能(如果可能的话!)的负担很高,但这似乎是维持 Bazel 赖以保持性能的保证(正确性、可重复性)的唯一方法。对于正在运行的 RFC,Cargo 可能会变得足够灵活,可以直接用于编译,但目前看来,保持功能奇偶性的外观实际上比避免由像 Rust 编译器一样对待 Cargo。
只需一点点努力,就可以构建几乎所有内容,包括依赖于 openssl-sys 的项目。许多 sys crate 需要识别它们所包装的系统库,并将其供应到项目中,或者告诉 Bazel 它位于系统上的位置。有些可能需要小的源调整,例如消除硬编码的货物环境变量要求。在某些情况下,修复可能并不简单,但示例存储库中已构建了大量最受欢迎的板条箱,可在 https://github.com/acmcarther/cargo-raze-crater 上找到
请参阅以下提供 crate 配置的示例:
使用供应商模式:
使用远程模式:
编译 OpenSSL :
[package.metadata.raze]
部分派生自 impl/src/settings.rs 中声明的结构。