regtool: add a crate that can be used in build.rs to run regtool.py Change-Id: I142301b11a06fd98d42cef49144a31d5c6fc21a1
diff --git a/util/regtool/Cargo.toml b/util/regtool/Cargo.toml new file mode 100644 index 0000000..ed409cc --- /dev/null +++ b/util/regtool/Cargo.toml
@@ -0,0 +1,6 @@ +[package] +name = "regtool" +version = "0.1.0" +edition = "2018" + +[dependencies]
diff --git a/util/regtool/src/lib.rs b/util/regtool/src/lib.rs new file mode 100644 index 0000000..3584abc --- /dev/null +++ b/util/regtool/src/lib.rs
@@ -0,0 +1,124 @@ +// 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() + } +}