Merge "regtool: add a crate that can be used in build.rs to run regtool.py"
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()
+ }
+}