【问题标题】:Can I create my own conditional compilation attributes?我可以创建自己的条件编译属性吗?
【发布时间】:2020-06-02 21:11:40
【问题描述】:

在我的 crate 中做某事的方法有好几种,有些会导致执行速度很快,有些二进制大小较小,有些还有其他优点,所以我为所有这些都提供了用户界面。未使用的函数将被编译器优化掉。我的 crate 中的内部函数也必须使用这些接口,我希望它们在编译时尊重用户的选择。

有像target_os这样的条件编译属性,它存储像linuxwindows这样的值。如何创建这样的属性,例如prefer_method,以便我和用户可以使用它有点像下面的代码sn-ps?

我的箱子:

#[cfg(not(any(
    not(prefer_method),
    prefer_method = "fast",
    prefer_method = "small"
)))]
compile_error("invalid `prefer_method` value");

pub fn bla() {
    #[cfg(prefer_method = "fast")]
    foo_fast();

    #[cfg(prefer_method = "small")]
    foo_small();

    #[cfg(not(prefer_method))]
    foo_default();
}

pub fn foo_fast() {
    // Fast execution.
}

pub fn foo_small() {
    // Small binary file.
}

pub fn foo_default() {
    // Medium size, medium fast.
}

用户箱:

#[prefer_method = "small"]
extern crate my_crate;

fn f() {
    // Uses the `foo_small` function, the other `foo_*` functions will not end up in the binary.
    my_crate::bla();

    // But the user can also call any function, which of course will also end up in the binary.
    my_crate::foo_default();
}

我知道有 --cfg 属性,但 AFAIK 这些只表示布尔标志,而不是枚举值,当只有一个枚举值有效时,它允许设置多个标志。

【问题讨论】:

    标签: rust conditional-compilation


    【解决方案1】:

    首先,--cfg 标志支持使用语法 --cfg 'prefer_method="fast"' 的键值对。这将允许您编写如下代码:

    #[cfg(prefer_method = "fast")]
    fn foo_fast() { }
    

    您还可以设置这些 cfg 选项from a build script。例如:

    // build.rs
    fn main() {
        println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
    }
    
    // src/main.rs
    #[cfg(prefer_method = "method_a")]
    fn main() {
        println!("It's A");
    }
    
    #[cfg(prefer_method = "method_b")]
    fn main() {
        println!("It's B");
    }
    
    #[cfg(not(any(prefer_method = "method_a", prefer_method = "method_b")))]
    fn main() {
        println!("No preferred method");
    }
    

    上面的代码将生成一个打印“It's A”的可执行文件。

    没有像您建议的那样指定 cfg 设置的语法。向您的 crate 用户公开这些选项的最佳方式是通过Cargo features

    例如:

    # Library Cargo.toml
    # ...
    [features]
    method_a = []
    method_b = []
    
    // build.rs
    fn main() {
        // prefer method A if both method A and B are selected
        if cfg!(feature = "method_a") {
            println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
        } else if cfg!(feature = "method_b") {
            println!("cargo:rustc-cfg=prefer_method=\"method_b\"");
        }
    }
    
    # User Cargo.toml
    # ...
    [dependencies.my_crate]
    version = "..."
    features = ["method_a"]
    

    但是,在这种情况下,我建议直接在代码中使用 Cargo 功能(即#[cfg(feature = "fast")]),而不是添加构建脚本,因为 cargo 功能和 rustc 之间存在一对一的对应关系-cfg 正在添加中。

    【讨论】:

    • 谢谢!除了构建脚本之外,您还可以在答案中添加以下内容到 .cargo/config 也可以:toml [target.YOUR_TARGET] rustflags = ['--cfg', 'prefer_method="method_a"']
    猜你喜欢
    • 2022-07-09
    • 2010-11-03
    • 2013-04-03
    • 2013-06-19
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 2013-12-24
    • 2021-10-06
    相关资源
    最近更新 更多