blob: 3584abca2ba6274405adc18039da6300b8fcafd2 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#![allow(clippy::needless_doctest_main)]
//! A library for build scripts to generate `.rs` files using `regtool.py`.
//!
//! This library is intended to be used as a `build-dependencies` entry in
//! `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! regtool = { path = "path/to/regtool" }
//! ```
//!
//! By default, the library expects the environment variable `REGTOOL` to point
//! to `regtool.py`. Either set the variable before running cargo, or use
//! `Build::regtool(...)` from `build.rs` to set the path to the script
//! explicitly.
//!
//! # Examples
//!
//! Use the `Build` struct to process `hw/ip/uart/data/uart.hjson`:
//!
//! build.rs:
//!
//! ```no_run
//! fn main() {
//! regtool::Build::new()
//! .in_file_path("hw/ip/uart/data/uart.hjson")
//! .generate("uart.rs");
//! }
//! ```
//!
//! src/main.rs:
//!
//! ```no_run
//! include!(concat!(env!("OUT_DIR"), "/uart.rs"));
//! ```
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
#[derive(Clone, Debug)]
pub struct Build {
in_file_path: Option<PathBuf>, // Must be set; no default
out_dir: Option<PathBuf>, // default: $OUT_DIR
python: Option<PathBuf>,
regtool: Option<PathBuf>, // Default: $REGTOOL
}
macro_rules! option_path_setter {
($name:ident) => {
pub fn $name<P: AsRef<Path>>(&mut self, $name: P) -> &mut Build {
self.$name = Some($name.as_ref().to_owned());
self
}
};
}
impl Build {
pub fn new() -> Self {
Self {
in_file_path: None,
out_dir: None,
python: None,
regtool: None,
}
}
// Generate setter functions for Option<PathBuf> fields:
option_path_setter!(in_file_path);
option_path_setter!(out_dir);
option_path_setter!(python);
option_path_setter!(regtool);
// Run regtool. If out_file is an absolute path, write output to out_file,
// otherwise write output to out_dir/out_file.
pub fn generate(&self, out_file: &str) {
let regtool = if let Some(regtool) = &self.regtool {
regtool.to_owned()
} else {
println!("cargo:rerun-if-env-changed=REGTOOL");
PathBuf::from(env::var("REGTOOL").expect("missing environment variable 'REGTOOL'"))
};
println!("cargo:rerun-if-changed={}", regtool.display());
let mut out_file_path = if let Some(out_dir) = &self.out_dir {
out_dir.to_owned()
} else {
PathBuf::from(env::var("OUT_DIR").unwrap())
};
// NB: If out_file is absolute, it replaces the current path.
out_file_path.push(out_file);
let in_file_path: &Path = self
.in_file_path
.as_ref()
.expect("'in_file_path' is not set");
println!("cargo:rerun-if-changed={}", in_file_path.display());
let mut cmd;
if let Some(python) = &self.python {
cmd = Command::new(python);
cmd.arg(regtool);
} else {
cmd = Command::new(regtool);
}
cmd.arg("-R").arg("-o").arg(out_file_path).arg(in_file_path);
println!("Running: {:?}", cmd);
assert!(cmd.status().unwrap().success());
}
}
impl Default for Build {
fn default() -> Self {
Self::new()
}
}