如果缺少 include_bytes!(...) 目标,则回退到替代值

6 rust rust-cargo

我的包有一个二进制目标,用于include_bytes!(…)将一些预计算值的副本捆绑到编译后的二进制文件中。这是一种优化,但不是绝对必要的:如果捆绑的数据切片,程序能够在运行时计算这些值.is_empty()

该程序需要能够在没有这些数据的情况下进行构建。但是,include_bytes!("data/computed.bin")如果目标文件不存在,则会导致构建错误。

error: couldn't read src/data/computed.bin: No such file or directory (os error 2)
Run Code Online (Sandbox Code Playgroud)

目前,我有一个 Bash 构建脚本,用于touch data/computed.bin在构建之前确保文件存在。但是,我不想依赖像 Bash 这样的特定于平台的解决方案;我希望能够在任何支持的平台上使用cargo build.

如果文件不存在,我的 Rust 程序include_bytes!(…)include_str!(…)文件如何退出,但仅使用标准 Cargo 构建工具,但如果文件不存在,如何优雅地回退到替代值或行为?

小智 6

我们可以使用构建脚本来确保包含的文件在 out 包尝试包含它之前存在。但是,构建脚本只能写入当前构建的唯一输出目录,因此我们不能直接在源目录中创建缺少的输入文件。

错误:无法验证包 tarball

引起:
  源目录build.rscargo publish. 构建脚本不应修改OUT_DIR.

相反,我们的构建脚本可以在构建目录中创建要包含的文件,复制源数据(如果存在),并且我们可以更新我们的包代码以包含来自构建目录而不是源目录的这些数据。构建路径将OUT_DIR在构建期间在环境变量中可用,因此我们可以从std::env::var("OUT_DIR")构建脚本和env!("OUT_DIR")包的其余部分访问它。

//! build.rs

use std::{fs, io};

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();

    fs::create_dir_all(&format!("{}/src/data", out_dir))
        .expect("unable to create data directory");

    let path = format!("src/data/computed.bin", name);
    let out_path = format!("{}/{}", out_dir, path);

    let mut out_file = fs::OpenOptions::new()
        .append(true)
        .create(true)
        .open(&out_path)
        .expect("unable to open/create data file");

    if let Ok(mut source_file) = fs::File::open(&path) {
        io::copy(&mut source_file, &mut out_file).expect("failed to copy data after opening");
    }
}
Run Code Online (Sandbox Code Playgroud)
//! src/foo.rs

fn precomputed_data() -> Option<&'static [u8]> {
    let data = include_bytes!(concat!(env!("OUT_DIR"), "/src/data/computed.bin")).as_ref();
    if !data.is_empty() {
        Some(data)
    } else {
        None
    }
}
Run Code Online (Sandbox Code Playgroud)