【问题标题】:How can I activate features in all my crates?如何激活所有 crate 中的功能?
【发布时间】:2018-11-04 02:39:53
【问题描述】:

我想有条件地启用运行时检查和日志记录,它们相互独立,也不受调试和发布模式的影响。所以我开始在我的项目中添加两个features,一个叫做“invariant-checking”,一个叫做“logging”。最终,我希望通过我在项目范围内可见的 crate 中定义的宏来使用它们。

我曾假设,如果我在所有 crate 中以相同方式填写 features section,那么当我在编译 bin crate 时激活该功能时,所有 lib crate 也会启用该功能,但这种情况并非如此!如何跨多个 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 = []

common 的 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

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

【问题讨论】:

  • 您应该根据common 中选择的功能定义不同的宏,因此功能选择发生在编译时并且仅在common 板条箱中。那么你只需要在那个 crate 中声明 features,并且切换 features 将具有全局效果,只要所有 crate 使用相同版本的common
  • 我相信How do I 'pass down' feature flags to subdependencies in Cargo?的答案已经回答了您的问题。如果您不同意,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。
  • @SvenMarnach 这听起来确实比将标志传递给每个板条箱要少。如果我理解正确,您建议定义多个具有相同名称的宏,并选择使用#[cfg(feature = "logging")]#[cfg(not(feature = "logging"))] 等属性启用的宏。这样可行。至少在我的玩具示例中,我只需要提及主文件和commonCargo.toml 文件中的功能!
  • @Shepmaster 我想以前的答案在技术上可能让我找出答案,但我发现 gnzlbg 的解释比仅仅指出我错过的一小段文档更有帮助。不过,请随意标记这个问题。
  • @Ryan1729 是的,我就是这个意思。既然我在办公桌前,我可以扩展答案(在移动设备上写评论)。

标签: compilation rust rust-cargo


【解决方案1】:

如何跨多个 crate 启用和禁用功能?

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" ]

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

cargo build --features=logging,invariant-checking

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

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

【讨论】:

    【解决方案2】:

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

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

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

    这对我来说似乎完全倒退了。特性标志应该在公共 crate 中查询,并且使用宏不需要首先在使用它的 crate 中定义特性标志。出于这个原因,我建议使用编译时检查来选择要定义的宏:

    #[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 crate 中定义该功能即可。只要您只使用该 crate 的单个版本,打开和关闭标志就会具有全局效果。

    【讨论】:

    • 我曾假设if cfg!(...) { /*...*/ } 很容易被编译器删除。我在godbolt 上尝试了一些简单的示例,似乎这些示例已成功删除。不过,有很多 cfg! 调用的更复杂的情况可能不是这样,所以这种技术似乎很有用,因为它应该适用于每种情况。
    • @Ryan1729 如果配置设置在编译时静态已知,优化器应该总是能够静态解析分支。然而,这是一个运行时检查的事实只是一个侧面说明。更重要的一点是解决该功能的上下文。此版本解决了 common crate 中的所有功能查询,而您的版本在使用宏的 crate 中解决了它们。
    猜你喜欢
    • 2011-02-03
    • 2018-12-26
    • 2017-06-14
    • 2017-01-12
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 2018-09-11
    • 2018-04-06
    相关资源
    最近更新 更多