如果缺少include_bytes!(…)目标,则退回到备用值

时间:2019-05-07 02:35:19

标签: 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)

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

如果文件退出,我的Rust程序include_bytes!(…)include_str!(…)如何从文件中退出,但是如果该文件不存在,而仅使用标准Cargo,则可以优雅地退回到其他值或行为构建工具?

1 个答案:

答案 0 :(得分:3)

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

  

错误:无法验证包tarball

     

原因:
  build.rs期间cargo 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");
    }
}
//! 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
    }
}