forked from libre-chip/cpu
96 lines
3.4 KiB
Rust
96 lines
3.4 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
|
|
use parse_powerisa_pdf::parse_powerisa_pdf_and_generate_xml;
|
|
use sha2::{Digest, Sha256};
|
|
use std::{
|
|
error::Error,
|
|
io::{ErrorKind, Read},
|
|
path::Path,
|
|
};
|
|
|
|
const EXPECTED_FILE_SIZE: u64 = 6425593;
|
|
const EXPECTED_FILE_HASH: &[u8; 32] =
|
|
&hex_literal::hex!("56372d23ece7e9e2c1b381a639443982a3e16e38109df1c141d655b779b61fdb");
|
|
|
|
fn verify_powerisa_file(file: impl std::io::Read) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
let mut buf = Vec::with_capacity(usize::try_from(EXPECTED_FILE_SIZE)? + 1);
|
|
file.take(EXPECTED_FILE_SIZE + 1).read_to_end(&mut buf)?;
|
|
if buf.len() > EXPECTED_FILE_SIZE as usize {
|
|
Err(format!("file is bigger than the expected length {EXPECTED_FILE_SIZE}").into())
|
|
} else if buf.len() < EXPECTED_FILE_SIZE as usize {
|
|
Err(format!(
|
|
"file is smaller than the expected length {EXPECTED_FILE_SIZE}: actual length {}",
|
|
buf.len()
|
|
)
|
|
.into())
|
|
} else {
|
|
let hash = Sha256::digest(&buf);
|
|
let hash = &*hash;
|
|
if hash != EXPECTED_FILE_HASH {
|
|
Err(format!(
|
|
"file's SHA256 hash doesn't match the expected hash: expected: {:x} got: {:x}",
|
|
base16ct::HexDisplay(EXPECTED_FILE_HASH),
|
|
base16ct::HexDisplay(hash)
|
|
)
|
|
.into())
|
|
} else {
|
|
Ok(buf)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_powerisa_pdf(path: impl AsRef<Path>) -> Result<bool, Box<dyn Error>> {
|
|
let path = path.as_ref();
|
|
let metadata = match std::fs::metadata(path) {
|
|
Err(e) if e.kind() == ErrorKind::NotFound => return Ok(false),
|
|
v => v?,
|
|
};
|
|
if !metadata.is_file() {
|
|
return Ok(false);
|
|
}
|
|
let file = std::fs::File::open(path)?;
|
|
verify_powerisa_file(file)?;
|
|
Ok(true)
|
|
}
|
|
|
|
fn out_dir() -> String {
|
|
std::env::var("OUT_DIR").expect("OUT_DIR env var is not set or invalid")
|
|
}
|
|
|
|
fn get_powerisa_pdf_name_and_dir<'a>(
|
|
out_dir: &'a str,
|
|
) -> Result<(&'static str, &'a Path), Box<dyn Error>> {
|
|
const FILE_NAME: &str = "OPF_PowerISA_v3.1C.pdf";
|
|
let out_dir = Path::new(out_dir);
|
|
if is_powerisa_pdf(FILE_NAME)? {
|
|
println!("cargo::rerun-if-changed={FILE_NAME}");
|
|
return Ok((FILE_NAME, Path::new(".")));
|
|
}
|
|
let full_path = out_dir.join(FILE_NAME);
|
|
let full_path = full_path
|
|
.into_os_string()
|
|
.into_string()
|
|
.expect("should be valid UTF-8");
|
|
if is_powerisa_pdf(&full_path)? {
|
|
println!("cargo::rerun-if-changed={full_path}");
|
|
return Ok((FILE_NAME, out_dir));
|
|
}
|
|
const URL: &str = "https://libre-chip.org/OPF_PowerISA_v3.1C.pdf";
|
|
println!("cargo::warning={FILE_NAME} not found locally, downloading from {URL}");
|
|
let buf = verify_powerisa_file(ureq::get(URL).call()?.into_body().into_reader())?;
|
|
std::fs::write(&full_path, buf)?;
|
|
println!("cargo::rerun-if-changed={full_path}");
|
|
Ok((FILE_NAME, out_dir))
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn Error>> {
|
|
let out_dir = out_dir();
|
|
let (pdf_name, pdf_dir) = get_powerisa_pdf_name_and_dir(&out_dir)?;
|
|
let old_dir = std::env::current_dir()?;
|
|
std::env::set_current_dir(pdf_dir)?;
|
|
let xml = parse_powerisa_pdf_and_generate_xml(pdf_name, None, false)?;
|
|
std::env::set_current_dir(old_dir)?;
|
|
std::fs::write(Path::new(&out_dir).join("powerisa-instructions.xml"), xml)?;
|
|
Ok(())
|
|
}
|