我怎样才能激活我所有箱子中的功能?

How can I activate features in all my crates?

我想有条件地启用 运行-time 检查和日志记录,它们彼此独立,并且与调试和发布模式无关。所以我开始在我的项目中添加两个 features,一个叫做 "invariant-checking",一个叫做 "logging"。最终,我希望通过我在项目范围内可见的板条箱中定义的宏来使用它们。

我假设如果我以相同的方式在所有箱子中以相同的方式填写 features section 然后当我在编译 bin crate 时激活该功能,那么所有的 lib crates 也会有该功能已启用,但事实并非如此!如何跨多个 crate 启用和禁用功能?希望这可以通过只改变一个东西来完成,比如 cargo 的命令行参数。

为了准确说明我想要什么,here's an example,我也将在下面重现:

共有三个 crate,main、bin、crate 和两个 lib crate,分别称为 "middle" 和 "common"。以下是相关文件的相关部分:

main.rs

extern crate common;
extern crate middle;

fn main() {
    common::check!();

    middle::run();

    println!("done");
}

主要Cargo.toml

[dependencies]

[dependencies.common]
path = "libs/common"

[dependencies.middle]
path = "libs/middle"

[features]
default = []
invariant-checking = []
logging = []

中间的lib.rs

extern crate common;

pub fn run() {
    common::check!();

    common::run();
}

中间的Cargo.toml

[dependencies]

[dependencies.common]
path = "../common"

[features]
default = []
invariant-checking = []
logging = []

普通的lib.rs

#[macro_export]
macro_rules! check {
    () => {{
        if cfg!(feature = "invariant-checking") {
            println!("invariant-checking {}:{}", file!(), line!());
        }
        if cfg!(feature = "logging") {
            println!("logging {}:{}", file!(), line!());
        }
    }};
}

pub fn run() {
    check!()
}

最后是普通的Cargo.toml

[dependencies]

[features]
default = []
invariant-checking = []
logging = []

当我 运行 cargo run --features "invariant-checking,logging" 我得到以下输出

invariant-checking src\main.rs:5
logging src\main.rs:5
done

但希望它也能登录中间和普通。我怎样才能改造这个项目,让它做到这一点,并且仍然允许我通过只改变一个地方来获得 "done" 作为输出?

How can I enable and disable features across multiple crates?

A Cargo.toml 可以添加功能,这些功能可传递地启用允许属于依赖项的其他功能。

例如,在依赖于 crate foobar 的 crate 的 Cargo.toml 中:

[dependencies]
foo = "0.1"
bar = "0.1"

[features]
default = []
invariant-checking = [ "foo/invariant-checking", "bar/invariant-checking" ]
logging = [ "foo/logging", "bar/logging" ]

此 crate 添加了 invariant-checkinglogging 功能。启用它们可传递地启用板条箱 foobar 的各自功能,因此

cargo build --features=logging,invariant-checking

将启用此 crate 及其依赖项 foobar 中的 logginginvariant-checking 功能。

在您的特定情况下,您可能希望 main 传递启用 middlecommon 的功能,而 middle 传递启用 common

当前形式的宏定义有一个问题:只要使用宏,宏内部的代码就会被内联,然后在被内联的上下文中进行编译。由于您使用

等运行时功能检查
if cfg!(feature = "invariant-checking")

这意味着您需要在使用宏的每个 crate 中定义功能。另一方面,在 common 板条箱本身中,该功能永远不会被查询,因此是多余的。

这对我来说似乎完全倒退了。功能标志应该 在公共包中查询,使用宏不需要首先在使用它的包中定义功能标志。出于这个原因,我建议使用编译时检查来 select 定义什么宏:

#[cfg(feature = "invariant-checking")]
macro_rules! check_invariant {
    () => ( println!("invariant-checking {}:{}", file!(), line!()); )
}

#[cfg(not(feature = "invariant-checking"))]
macro_rules! check_invariant {
    () => ()
}

#[cfg(feature = "logging")]
macro_rules! logging {
    () => ( println!("logging {}:{}", file!(), line!()); )
}

#[cfg(not(feature = "logging"))]
macro_rules! logging {
    () => ()
}

#[macro_export]
macro_rules! check {
    () => ( check_invariant!(); logging!(); )
}

这样,您只需要在 common 包中定义功能,这是应该的。只要你只使用那个箱子的一个版本,打开和关闭标志就会有全局效果。