如何激活所有包装箱中的功能?

时间:2018-11-03 01:55:33

标签: compilation rust rust-cargo

我想有条件地启用运行时检查和日志记录,彼此独立并且与调试和发布模式无关。因此,我已经开始在项目中添加两个features,一个称为“不变检查”,另一个称为“日志记录”。最终,我希望它们的使用通过在整个项目范围内可见的板条箱中定义的宏实现。

我曾经假设,如果我以相同的方式在所有板条箱中填充features section,那么当我在编译箱箱时激活该功能时,那么所有库箱也将具有该功能已启用,但事实并非如此!如何在多个包装箱中启用和禁用功能?希望可以通过仅将命令行参数之一更改为cargo来完成。

为明确说明我想要的内容,here's an example,我还将在下面进行复制:

一共有三个板条箱,分别是main,bin,crate和两个lib板条箱,分别称为“中间”和“公共”。以下是相关文件的相关部分:

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!()
}

,最后是Common的 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

,但也希望它登录到中间和公共位置。我如何才能变换此项目以使其能够做到这一点,并且仍然仅通过更改一个位置而仍然仅允许我将“完成”作为输出?

2 个答案:

答案 0 :(得分:2)

  

如何在多个包装箱中启用和禁用功能?

Cargo.toml可以添加可传递地启用允许属于依赖关系的其他功能的功能。

例如,在一个依赖于板条箱Cargo.tomlfoo的板条箱的bar中:

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

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

此板条箱添加了invariant-checkinglogging功能。暂时启用它们会启用包装箱foobar的相应功能,以便

cargo build --features=logging,invariant-checking

将在此板条箱及其依存关系logginginvariant-checking中启用foobar功能。

在您的特定情况下,您可能希望main暂时启用middlecommon的功能,并希望middle暂时启用{{1 }}。

答案 1 :(得分:1)

当前形式的宏定义存在一个问题:只要使用宏,宏中的代码就会被内联,然后在被内联的上下文中进行编译。由于您使用了运行时功能检查,例如

if cfg!(feature = "invariant-checking")

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

这对我来说似乎完全是倒退。在公共包装箱中应该仅查询功能标志,并且使用宏不要求先在使用它的包装箱中定义功能标志。因此,我建议使用编译时检查来选择要定义的宏:

#[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箱子中定义功能即可。只要您仅使用该板条箱的单个版本,将标志打开和关闭即可全局生效。