从构建脚本引用板条箱数据类型和函数

问题描述 投票:0回答:2

我有一个 rust 二进制板条箱(嗯,它还有一个用于测试的 lib.rs 文件),并且正在尝试编写一个构建脚本。此构建脚本需要使用 serde 从静态 rust 对象(自定义 struct crate::datatypes::ErrorMarkup)生成 JSON 文件,该文件由二进制 crate 导入。我知道我可以只引用二进制板条箱中的静态对象,但是二进制板条箱必须导入一个 JSON 文件,该文件最终可能来自其他来源,或者可能在构建和运行阶段之间进行修改(即,目的是发送构建的文件和 JSON 文件)。

如何从构建脚本引用 crate 数据类型和函数?

项目结构:

- cargo.lock
- cargo.toml
- markup.json (target file)
- build.rs
- src
    - main.rs
    - lib.rs (exports)
    - datatypes.rs
- tests
    - verify.rs

我已经尝试了

use pump_log_tool
(我的板条箱)和
use crate::src
以及十亿个其他变体。

我知道这可能是由于构建依赖项和运行时依赖项而导致的问题。有没有明显更好的方法来实现这个目标?

rust rust-cargo
2个回答
0
投票

构建脚本不能依赖于它构建的包

Cargo 将

build.rs
编译到自己的板条箱中,并具有自己的依赖项。通过使构建脚本依赖于它正在构建的板条箱,您创建了 Cargo 无法解析的循环依赖关系。

将所需类型移至不同的板条箱中

典型的解决方案是将(大概)在

datatypes.rs
中定义的项目移动到它们自己的单独的板条箱中,这不需要构建脚本。 (这也是过程宏的常见模式。)您的新项目结构可能如下所示

/
|- Cargo.toml
|- build.rs
|- src/
|  |- lib.rs
|  \- main.rs
|- pump_log_tool_types/
|  |- Cargo.toml
|  \- src/lib.rs

然后,在

/Cargo.toml
中,创建一个工作区部分并添加新的板条箱:

[workspace]
# This is a path relative to the workspace root.
members = ["pump_log_tool_types"]

pump_log_tool/Cargo.toml
中,添加
pump_log_tool_types
箱作为普通依赖项和构建依赖项:

[dependencies]
pump_log_tool_types = { path = "pump_log_tool_types" }

[build-dependencies]
pump_log_tool_types = { path = "pump_log_tool_types" }

现在,当您构建

pump_log_tool
时,
pump_log_tool_types
板条箱将被构建一次并在构建脚本和板条箱本身之间共享。


0
投票

对于数据定义等简单的事情,您可以在构建脚本中使用

include!()
宏。

假设我想在构建时查找引用自定义类型的表:数据定义(没有其他内容)位于

src/parser/field_type.rs

#[derive(Debug)]
#[repr(u64)]
#[allow(dead_code)]
pub(crate) enum FieldType {
    Unknown = 0,
    Encoded,
    Numeric,
    NumericDec,
    NumericHex,
    NumericOct,
    Alphanumeric,
    Alphabet,
}

在构建脚本中我包含该代码:

include!("src/parser/field_type.rs");

所以我不需要在多个地方都有定义。然后我构建一个 FST...

let fields: Vec<(String, String)> = { /* read and process a CSV */ };

let mut build =
    fst::MapBuilder::new(File::create(PathBuf::from(out_dir).join("fields.fst"))?)?;
build.extend_iter(fields.iter().map(|(name, ty)| {
    let ty = match ty.as_str() {
        "encoded" => FieldType::Encoded,
        "numeric" => FieldType::Numeric,
        "numeric decimal" => FieldType::NumericDec,
        "numeric hexadecimal" => FieldType::NumericHex,
        "numeric octal" => FieldType::NumericOct,
        "alphanumeric" => FieldType::Alphanumeric,
        "alphabet" => FieldType::Alphabet,
        _ => panic!("unrecognized {} for {}", ty, name),
    } as u64;
    (name, ty)
}))?;
build.finish()?;

…在板条箱的另一部分使用它来按名称查找

FieldType

use lazy_static::lazy_static;
lazy_static! {
    static ref FIELD_TYPES: fst::Map<Vec<u8>> = {
        fst::Map::new(Vec::from(
            include_bytes!(concat!(env!("OUT_DIR"), "/fields.fst")).as_slice(),
        ))
        .unwrap()
    };
}

fn parse_hint(key: &[u8]) -> FieldType {
    let h = FIELD_TYPES.get(key).unwrap_or_default();
    unsafe { std::mem::transmute(h) }
}

甚至应该可以将该代码移至

field_type.rs
并告诉编译器在编译
build.rs
时忽略它,但对于我的用例来说,到目前为止这已经足够了。

© www.soinside.com 2019 - 2024. All rights reserved.