mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-02-23 00:57:08 +09:00
Xml metadata embedding
This commit is contained in:
parent
f714b42249
commit
9555b743f9
11 changed files with 161 additions and 62 deletions
|
@ -21,7 +21,7 @@ stages: # List of stages for jobs, and their order of execution
|
||||||
- test
|
- test
|
||||||
|
|
||||||
.setup-rust:
|
.setup-rust:
|
||||||
image: rust:latest
|
image: registry.cern.ch/docker.io/library/rust:latest
|
||||||
before_script:
|
before_script:
|
||||||
- rustup component add clippy rustfmt
|
- rustup component add clippy rustfmt
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Embedding XML git metadata
|
||||||
|
- New high-level API (`generate` function)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Loosen Cargo.toml dependencies
|
- Loosen Cargo.toml dependencies
|
||||||
|
|
|
@ -25,8 +25,9 @@ rustupのインストールには公式サイトにある`curl`スクリプト
|
||||||
### バイナリクレートの使い方
|
### バイナリクレートの使い方
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --bins --release
|
cargo build --features=bin --bins --release
|
||||||
```
|
```
|
||||||
|
|
||||||
を実行します。
|
を実行します。
|
||||||
`--release`はオプションです。
|
`--release`はオプションです。
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ Note that `cargo` is available on lxplus, so you might be able to use that (it i
|
||||||
Execute
|
Execute
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --bins --release
|
cargo build --features=bin --bins --release
|
||||||
```
|
```
|
||||||
|
|
||||||
to build.
|
to build.
|
||||||
|
|
|
@ -13,7 +13,7 @@ use thiserror::Error;
|
||||||
use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error};
|
use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BitString, Block, Field, Fifo, Memory, Module, ModuleBlockElements, MultipleParams, Register,
|
BitString, Block, Field, Fifo, Memory, Module, ModuleBlockElements, MultipleParams, Register,
|
||||||
Value,
|
Value, XmlGitInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Possible errors in conversion, with positional information.
|
/// Possible errors in conversion, with positional information.
|
||||||
|
@ -164,7 +164,7 @@ mod util {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
pub fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
pub fn from_xml_dom(node: Node, git_info: XmlGitInfo) -> Result<Self, DomConversionError> {
|
||||||
assert!(node.parent().unwrap().is_root());
|
assert!(node.parent().unwrap().is_root());
|
||||||
assert_eq!(node.tag_name().name(), "module");
|
assert_eq!(node.tag_name().name(), "module");
|
||||||
|
|
||||||
|
@ -212,6 +212,7 @@ impl Module {
|
||||||
elements_bitstring: child_bitstrings,
|
elements_bitstring: child_bitstrings,
|
||||||
elements_other: child_other,
|
elements_other: child_other,
|
||||||
xmlhash: Sha256::digest(node.document().input_text()).into(),
|
xmlhash: Sha256::digest(node.document().input_text()).into(),
|
||||||
|
git_info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,8 @@ impl Module {
|
||||||
pub fn generate_code(self) -> Result<HashMap<path::PathBuf, syn::File>, CodeGenError> {
|
pub fn generate_code(self) -> Result<HashMap<path::PathBuf, syn::File>, CodeGenError> {
|
||||||
let build_metadata = format!(
|
let build_metadata = format!(
|
||||||
"
|
"
|
||||||
|
This code is auto generated using endcap_sl_software_ri_generator.
|
||||||
|
|
||||||
# Build metadata
|
# Build metadata
|
||||||
|
|
||||||
- timestamp: {}
|
- timestamp: {}
|
||||||
|
@ -137,9 +139,9 @@ impl Module {
|
||||||
## CSR XML
|
## CSR XML
|
||||||
|
|
||||||
- sha256: {}
|
- sha256: {}
|
||||||
- git describe: TODO (after building step is fixed)
|
- git describe: {}
|
||||||
- git commit timestamp: TODO
|
- git commit timestamp: {}
|
||||||
- git SHA: TODO
|
- git SHA: {}
|
||||||
|
|
||||||
## Generator
|
## Generator
|
||||||
|
|
||||||
|
@ -150,6 +152,9 @@ impl Module {
|
||||||
",
|
",
|
||||||
Local::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, false),
|
Local::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, false),
|
||||||
hex::encode(self.xmlhash),
|
hex::encode(self.xmlhash),
|
||||||
|
self.git_info.describe,
|
||||||
|
self.git_info.commit_timestamp,
|
||||||
|
self.git_info.sha,
|
||||||
GENERATOR_BUILD_TIMESTAMP,
|
GENERATOR_BUILD_TIMESTAMP,
|
||||||
GENERATOR_GIT_DESCRIBE,
|
GENERATOR_GIT_DESCRIBE,
|
||||||
GENERATOR_GIT_COMMIT_TIMESTAMP,
|
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);
|
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()) {
|
if let Some(old_out) = files.insert(out_path.clone(), out.clone()) {
|
||||||
log::error!("path {}", out_path.display());
|
log::error!("path {}", out_path.display());
|
||||||
log::error!("old {}", old_out.to_string());
|
log::error!("old {}", old_out.to_string());
|
||||||
|
|
58
src/integrated.rs
Normal file
58
src/integrated.rs
Normal file
|
@ -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(())
|
||||||
|
}
|
71
src/io.rs
71
src/io.rs
|
@ -1,6 +1,75 @@
|
||||||
//! File IO for generated codes.
|
//! 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<XmlGitInfo, XmlGitInfoError> {
|
||||||
|
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).
|
/// Write formatted codes generated with [`Module::generate_code`](crate::types::Module::generate_code).
|
||||||
pub fn write_to_files(
|
pub fn write_to_files(
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -8,26 +8,7 @@
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
//! Here's a typical usage:
|
//! See [`generate`].
|
||||||
//! ```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");
|
|
||||||
//! ```
|
|
||||||
//!
|
//!
|
||||||
//! # Overview
|
//! # Overview
|
||||||
//!
|
//!
|
||||||
|
@ -44,10 +25,12 @@
|
||||||
|
|
||||||
pub mod converter;
|
pub mod converter;
|
||||||
pub mod generator;
|
pub mod generator;
|
||||||
|
pub mod integrated;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod meta;
|
pub mod meta;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod type_traits;
|
mod type_traits;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
pub use integrated::generate;
|
||||||
pub use io::write_to_files;
|
pub use io::write_to_files;
|
||||||
|
|
34
src/main.rs
34
src/main.rs
|
@ -1,9 +1,7 @@
|
||||||
use std::{fs, path};
|
use std::path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use endcap_sl_software_ri_generator::types;
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
/// Generate register interface from register map xml.
|
/// Generate register interface from register map xml.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -21,35 +19,7 @@ fn main() -> Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
log::debug!("args: {:?}", args);
|
log::debug!("args: {:?}", args);
|
||||||
|
|
||||||
let xmlfile = fs::read_to_string(args.xml)?;
|
endcap_sl_software_ri_generator::generate(&args.xml, &args.out)?;
|
||||||
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)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub struct Module {
|
||||||
pub elements_bitstring: Vec<BitString>,
|
pub elements_bitstring: Vec<BitString>,
|
||||||
pub elements_other: Vec<ModuleBlockElements>,
|
pub elements_other: Vec<ModuleBlockElements>,
|
||||||
pub xmlhash: [u8; 32],
|
pub xmlhash: [u8; 32],
|
||||||
|
pub git_info: XmlGitInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -151,3 +152,10 @@ pub enum FieldFifoInterface {
|
||||||
Block,
|
Block,
|
||||||
Both,
|
Both,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct XmlGitInfo {
|
||||||
|
pub(crate) describe: String,
|
||||||
|
pub(crate) commit_timestamp: String,
|
||||||
|
pub(crate) sha: String,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue