Skip to main content

xtask/
util.rs

1use anyhow::{Context, Result, ensure};
2use serde_json::Value;
3use sha2::{Digest, Sha256};
4use std::{
5    fs,
6    io::Read,
7    path::{Path, PathBuf},
8    process::{Command, Output},
9};
10
11pub fn root() -> PathBuf {
12    Path::new(env!("CARGO_MANIFEST_DIR"))
13        .parent()
14        .unwrap()
15        .to_owned()
16}
17
18pub fn absolute(path: &Path) -> Result<PathBuf> {
19    Ok(if path.is_absolute() {
20        path.to_owned()
21    } else {
22        std::env::current_dir()?.join(path)
23    })
24}
25
26pub fn command(args: &[&str]) -> Command {
27    let mut command = Command::new(args[0]);
28
29    command.args(&args[1..]).current_dir(root());
30
31    command
32}
33
34pub fn checked(output: Output) -> Result<String> {
35    ensure!(
36        output.status.success(),
37        "command failed: {}",
38        String::from_utf8_lossy(&output.stderr)
39    );
40
41    Ok(String::from_utf8(output.stdout)?.trim().to_owned())
42}
43
44pub fn output(args: &[&str]) -> Result<String> {
45    checked(
46        command(args)
47            .output()
48            .with_context(|| format!("running {}", args[0]))?,
49    )
50}
51
52pub fn digest(path: &Path) -> Result<String> {
53    let mut file = fs::File::open(path)?;
54    let mut hash = Sha256::new();
55    let mut buffer = [0u8; 64 * 1024];
56
57    loop {
58        let n = file.read(&mut buffer)?;
59
60        if n == 0 {
61            break;
62        }
63
64        hash.update(&buffer[..n]);
65    }
66
67    Ok(hash.finalize().iter().map(|b| format!("{b:02x}")).collect())
68}
69
70pub fn files(path: &Path) -> Result<Vec<PathBuf>> {
71    let mut result = Vec::new();
72
73    for entry in fs::read_dir(path)? {
74        let entry = entry?;
75
76        if entry.file_type()?.is_dir() {
77            result.extend(files(&entry.path())?);
78        } else {
79            result.push(entry.path());
80        }
81    }
82
83    result.sort();
84
85    Ok(result)
86}
87
88pub fn source_digest() -> Result<String> {
89    let root = root();
90    let mut sources = files(&root.join("src"))?;
91
92    sources.extend(files(&root.join("kernels"))?);
93    sources.sort();
94    sources.extend([root.join("Cargo.toml"), root.join("Cargo.lock")]);
95
96    let mut hash = Sha256::new();
97
98    for path in sources {
99        hash.update(path.strip_prefix(&root)?.to_string_lossy().as_bytes());
100        hash.update(fs::read(path)?);
101    }
102
103    Ok(hash.finalize().iter().map(|b| format!("{b:02x}")).collect())
104}
105
106pub fn json(path: &Path) -> Result<Value> {
107    serde_json::from_slice(&fs::read(path)?).with_context(|| path.display().to_string())
108}
109
110pub fn write_json(path: &Path, value: &Value) -> Result<()> {
111    let mut file = tempfile::NamedTempFile::new_in(path.parent().context("output parent")?)?;
112
113    use std::io::Write;
114
115    writeln!(file, "{}", serde_json::to_string_pretty(value)?)?;
116    file.persist(path)?;
117
118    Ok(())
119}
120
121pub fn utc() -> Result<String> {
122    output(&["date", "-u", "+%Y%m%dT%H%M%SZ"])
123}
124
125pub fn build() -> Result<()> {
126    ensure!(
127        command(&[
128            "cargo",
129            "build",
130            "--release",
131            "--offline",
132            "-p",
133            "cherenkov"
134        ])
135        .status()?
136        .success(),
137        "release build failed"
138    );
139
140    Ok(())
141}