Откат к альтернативному значению, если отсутствует цель include_bytes! (…) - PullRequest
3 голосов
/ 07 мая 2019

Мой пакет имеет двоичную цель, которая использует 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 Ответ

3 голосов
/ 07 мая 2019

Мы можем использовать скрипт сборки , чтобы убедиться, что включенный файл существует до того, как наш пакет попытается его включить. Однако сценарии сборки могут записывать только в уникальный выходной каталог текущей сборки, поэтому мы не можем просто создать отсутствующие входные файлы непосредственно в исходном каталоге.

ошибка: не удалось проверить архив пакета

Причина:
Исходный каталог был изменен на 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
    }
}
...