From 9555b743f98ccb9becf8f92168b1a959807b24bc Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Wed, 12 Feb 2025 13:10:48 +0000 Subject: [PATCH] Xml metadata embedding --- .gitlab-ci.yml | 2 +- CHANGELOG.md | 5 ++++ README-ja.md | 3 +- README.md | 2 +- src/converter.rs | 5 ++-- src/generator.rs | 12 +++++--- src/integrated.rs | 58 ++++++++++++++++++++++++++++++++++++++ src/io.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 23 ++------------- src/main.rs | 34 ++--------------------- src/types.rs | 8 ++++++ 11 files changed, 161 insertions(+), 62 deletions(-) create mode 100644 src/integrated.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 84408f8..f55da9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ stages: # List of stages for jobs, and their order of execution - test .setup-rust: - image: rust:latest + image: registry.cern.ch/docker.io/library/rust:latest before_script: - rustup component add clippy rustfmt diff --git a/CHANGELOG.md b/CHANGELOG.md index d7baa7f..db78a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Embedding XML git metadata +- New high-level API (`generate` function) + ### Fixed - Loosen Cargo.toml dependencies diff --git a/README-ja.md b/README-ja.md index 1c22a35..7e770e3 100644 --- a/README-ja.md +++ b/README-ja.md @@ -25,8 +25,9 @@ rustupのインストールには公式サイトにある`curl`スクリプト ### バイナリクレートの使い方 ```bash -cargo build --bins --release +cargo build --features=bin --bins --release ``` + を実行します。 `--release`はオプションです。 diff --git a/README.md b/README.md index d892165..9685c6f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Note that `cargo` is available on lxplus, so you might be able to use that (it i Execute ```bash -cargo build --bins --release +cargo build --features=bin --bins --release ``` to build. diff --git a/src/converter.rs b/src/converter.rs index 3ccc699..81056e2 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -13,7 +13,7 @@ use thiserror::Error; use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error}; use crate::types::{ BitString, Block, Field, Fifo, Memory, Module, ModuleBlockElements, MultipleParams, Register, - Value, + Value, XmlGitInfo, }; /// Possible errors in conversion, with positional information. @@ -164,7 +164,7 @@ mod util { } impl Module { - pub fn from_xml_dom(node: Node) -> Result { + pub fn from_xml_dom(node: Node, git_info: XmlGitInfo) -> Result { assert!(node.parent().unwrap().is_root()); assert_eq!(node.tag_name().name(), "module"); @@ -212,6 +212,7 @@ impl Module { elements_bitstring: child_bitstrings, elements_other: child_other, xmlhash: Sha256::digest(node.document().input_text()).into(), + git_info, }) } } diff --git a/src/generator.rs b/src/generator.rs index 7e9c2bc..67c12c7 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -130,6 +130,8 @@ impl Module { pub fn generate_code(self) -> Result, CodeGenError> { let build_metadata = format!( " +This code is auto generated using endcap_sl_software_ri_generator. + # Build metadata - timestamp: {} @@ -137,9 +139,9 @@ impl Module { ## CSR XML - sha256: {} -- git describe: TODO (after building step is fixed) -- git commit timestamp: TODO -- git SHA: TODO +- git describe: {} +- git commit timestamp: {} +- git SHA: {} ## Generator @@ -150,6 +152,9 @@ impl Module { ", Local::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, false), hex::encode(self.xmlhash), + self.git_info.describe, + self.git_info.commit_timestamp, + self.git_info.sha, GENERATOR_BUILD_TIMESTAMP, GENERATOR_GIT_DESCRIBE, GENERATOR_GIT_COMMIT_TIMESTAMP, @@ -364,7 +369,6 @@ impl CodeGen for Block { } }; let (out_path, next_parent_path) = mod_file_path(parent_path, &snake_case_name); - log::info!("{:?}", out_path); if let Some(old_out) = files.insert(out_path.clone(), out.clone()) { log::error!("path {}", out_path.display()); log::error!("old {}", old_out.to_string()); diff --git a/src/integrated.rs b/src/integrated.rs new file mode 100644 index 0000000..a342730 --- /dev/null +++ b/src/integrated.rs @@ -0,0 +1,58 @@ +use std::path; + +use thiserror::Error; + +use crate::{ + converter, generator, + io::{get_xml_gitinfo, XmlGitInfoError}, + types, +}; + +#[derive(Debug, Error)] +pub enum Error { + #[error("error in getting xml git info: {0}")] + XmlGitInfoError(#[from] XmlGitInfoError), + #[error("io error: {0}")] + IOError(#[from] std::io::Error), + #[error("xml parse error: {0}")] + XmlParseError(#[from] roxmltree::Error), + #[error("dom conversion error: {0}")] + DomConversionError(#[from] converter::DomConversionError), + #[error("code generation error: {0}")] + CodeGenError(#[from] generator::CodeGenError), +} + +/// Generate register interface code from xml in `xml` and write them under directory `out`. +pub fn generate(xml: &path::Path, out: &path::Path) -> Result<(), Error> { + log::debug!("generate called: xml:{:?}, out: {:?}", xml, out); + + let xml_git_info = get_xml_gitinfo(xml)?; + log::debug!("xml git info {xml_git_info:?}"); + + let xmlfile = std::fs::read_to_string(xml)?; + let doc = roxmltree::Document::parse_with_options( + &xmlfile, + roxmltree::ParsingOptions { + allow_dtd: true, + nodes_limit: u32::MAX, + }, + )?; + log::debug!("xml parsed {doc:?}"); + + let register_map = types::Module::from_xml_dom(doc.root_element(), xml_git_info)?; + log::debug!("converted {register_map:?}"); + + let files = register_map.generate_code()?; + if log::log_enabled!(log::Level::Debug) { + for (path, code) in &files { + log::debug!("path: {:?}", path); + log::debug!("{}", prettyplease::unparse(code)); + log::trace!("{}", prettyplease::unparse(code)); + } + } + + crate::write_to_files(files, out)?; + log::debug!("wrote to files"); + + Ok(()) +} diff --git a/src/io.rs b/src/io.rs index fc6ac57..8b2b1eb 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,6 +1,75 @@ //! File IO for generated codes. -use std::{collections::HashMap, fs, io, path}; +use std::{collections::HashMap, fs, io, path, process}; + +use thiserror::Error; + +use crate::types::XmlGitInfo; + +#[derive(Debug, Error)] +pub enum XmlGitInfoError { + #[error("io error while getting xml git info: {msg} {source}")] + IOError { + msg: &'static str, + #[source] + source: io::Error, + }, + #[error("git failed: {msg}: {stderr}")] + CommandFailed { msg: &'static str, stderr: String }, +} + +impl XmlGitInfoError { + fn io_error(e: io::Error, msg: &'static str) -> Self { + XmlGitInfoError::IOError { msg, source: e } + } +} + +pub(crate) fn get_xml_gitinfo(xml_path: &path::Path) -> Result { + let describe = process::Command::new("git") + .args(["describe", "--always", "--dirty"]) + .current_dir(xml_path.parent().unwrap()) + .output() + .map_err(|e| XmlGitInfoError::io_error(e, "git describe"))?; + let describe = if describe.status.success() { + String::from_utf8_lossy(&describe.stdout).trim().to_owned() + } else { + return Err(XmlGitInfoError::CommandFailed { + msg: "git describe", + stderr: String::from_utf8_lossy(&describe.stderr).into_owned(), + }); + }; + let timestamp = process::Command::new("git") + .args(["log", "-1", "--pretty=format:'%cI'"]) + .current_dir(xml_path.parent().unwrap()) + .output() + .map_err(|e| XmlGitInfoError::io_error(e, "git log (timestamp)"))?; + let timestamp = if timestamp.status.success() { + String::from_utf8_lossy(×tamp.stdout).trim().to_owned() + } else { + return Err(XmlGitInfoError::CommandFailed { + msg: "git log (timestamp)", + stderr: String::from_utf8_lossy(×tamp.stderr).into_owned(), + }); + }; + let sha = process::Command::new("git") + .args(["rev-parse", "HEAD"]) + .current_dir(xml_path.parent().unwrap()) + .output() + .map_err(|e| XmlGitInfoError::io_error(e, "git rev-parse (sha)"))?; + let git_sha = if sha.status.success() { + String::from_utf8_lossy(&sha.stdout).trim().to_owned() + } else { + return Err(XmlGitInfoError::CommandFailed { + msg: "git rev-parse (sha)", + stderr: String::from_utf8_lossy(&sha.stderr).into_owned(), + }); + }; + Ok(XmlGitInfo { + describe, + commit_timestamp: timestamp, + sha: git_sha, + }) +} /// Write formatted codes generated with [`Module::generate_code`](crate::types::Module::generate_code). pub fn write_to_files( diff --git a/src/lib.rs b/src/lib.rs index 58ac50a..e76a395 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,26 +8,7 @@ //! //! # Example //! -//! Here's a typical usage: -//! ```no_run -//! use endcap_sl_software_ri_generator::types; -//! -//! let xmlfile = std::fs::read_to_string("./csr.xml").expect("failed to open file"); -//! let doc = roxmltree::Document::parse_with_options( -//! &xmlfile, -//! roxmltree::ParsingOptions { -//! allow_dtd: true, -//! nodes_limit: u32::MAX, -//! }, -//! ) -//! .expect("failed to parse"); -//! -//! let register_map = types::Module::from_xml_dom(doc.root_element()).expect("failed to convert"); -//! -//! let files = register_map.generate_code().expect("failed to generate code"); -//! endcap_sl_software_ri_generator::write_to_files(files, -//! &std::path::PathBuf::from("out")).expect("failed to write"); -//! ``` +//! See [`generate`]. //! //! # Overview //! @@ -44,10 +25,12 @@ pub mod converter; pub mod generator; +pub mod integrated; pub mod io; pub mod meta; mod parser; mod type_traits; pub mod types; +pub use integrated::generate; pub use io::write_to_files; diff --git a/src/main.rs b/src/main.rs index c320cef..3dc85ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ -use std::{fs, path}; +use std::path; use anyhow::Result; use clap::Parser; -use endcap_sl_software_ri_generator::types; -use itertools::Itertools; /// Generate register interface from register map xml. #[derive(Parser, Debug)] @@ -21,35 +19,7 @@ fn main() -> Result<()> { let args = Args::parse(); log::debug!("args: {:?}", args); - let xmlfile = fs::read_to_string(args.xml)?; - let doc = roxmltree::Document::parse_with_options( - &xmlfile, - roxmltree::ParsingOptions { - allow_dtd: true, - nodes_limit: u32::MAX, - }, - )?; - log::debug!("Parsed: {:#?}", doc); - - let register_map = types::Module::from_xml_dom(doc.root_element())?; - log::info!("read: {:?}", register_map); - log::debug!("read: {:#?}", register_map); - - let files = register_map.generate_code()?; - if log::log_enabled!(log::Level::Debug) { - for (path, code) in &files { - log::debug!("path: {:?}", path); - log::debug!("{}", prettyplease::unparse(code)); - log::trace!("{}", prettyplease::unparse(code)); - } - } - if log::log_enabled!(log::Level::Info) { - for filepath in files.keys().sorted() { - log::info!("{}", filepath.display()); - } - } - - endcap_sl_software_ri_generator::write_to_files(files, &args.out)?; + endcap_sl_software_ri_generator::generate(&args.xml, &args.out)?; Ok(()) } diff --git a/src/types.rs b/src/types.rs index e074786..11d4800 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,6 +11,7 @@ pub struct Module { pub elements_bitstring: Vec, pub elements_other: Vec, pub xmlhash: [u8; 32], + pub git_info: XmlGitInfo, } #[derive(Debug)] @@ -151,3 +152,10 @@ pub enum FieldFifoInterface { Block, Both, } + +#[derive(Debug)] +pub struct XmlGitInfo { + pub(crate) describe: String, + pub(crate) commit_timestamp: String, + pub(crate) sha: String, +}