mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-07-12 14:39:26 +09:00
Compare commits
21 commits
Author | SHA1 | Date | |
---|---|---|---|
44880c7531 | |||
1b0be30908 | |||
5ef9387d33 | |||
2baf19a190 | |||
4bbb157228 | |||
eee043cd0f | |||
83e0c6cb3d | |||
e6f09d684a | |||
3379babe12 | |||
6ee8239682 | |||
27f2f75ee2 | |||
b8d18b26fc | |||
516cfa9151 | |||
8e68c80c06 | |||
42efcc7cf6 | |||
d6ee19e000 | |||
2accbc3910 | |||
b492d097e7 | |||
12332e8565 | |||
fafd262230 | |||
9444431a61 |
15 changed files with 394 additions and 101 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
result*
|
||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Packagin with Nix flakes (with nixpkgs buildRustPackage) [!16](https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/merge_requests/16)
|
||||
- Now xml metadata can be overwritten with corresponding environmental variables. [!17](https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/merge_requests/17)
|
||||
- Support value element just under the register element by inserting single field during conversion. [!18](https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/merge_requests/18)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve error message from syn in generator. !15
|
||||
- Removed top level module (register_interface), and now it can have arbitrary name.
|
||||
- Use full path `RegisterSpec`s to avoid unused `use`
|
||||
- Use `u8` and `u16` for `T` of registers when the mask is 0xff or 0xffff, instead of defining new tuple struct.
|
||||
|
||||
## [0.3.1] - 2025-04-11
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -33,7 +33,7 @@ cargo build --features=bin --bins --release
|
|||
|
||||
`target/release`に入ってるバイナリファイルを実行するか、`cargo run -- <XML> <OUT>`で実行できます。
|
||||
|
||||
詳しくは`--help`を見てください。
|
||||
詳しくは(`-h`ではなく)`--help`を見てください。
|
||||
|
||||
### ライブラリクレートの使い方
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ to build.
|
|||
|
||||
Execute the binary generated in `target/release/`, or run with `cargo run -- <XML> <OUT>`.
|
||||
|
||||
See the `--help` for more information.
|
||||
See the `--help`(not `-h`) for more information.
|
||||
|
||||
### Library crate usage
|
||||
|
||||
|
|
82
flake.lock
generated
Normal file
82
flake.lock
generated
Normal file
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1744098102,
|
||||
"narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744166053,
|
||||
"narHash": "sha256-mpI7OzFwp+fUeDtZYQbVZ2YmtxTN2UNrrOwbYD27xKU=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "896158be1835589db6f42f45ef0a49b8b492cc66",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
37
flake.nix
Normal file
37
flake.nix
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
description = "A very basic flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
rust-overlay,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ rust-overlay.overlays.default ];
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [
|
||||
pkgs.rust-bin.stable.latest.default
|
||||
];
|
||||
};
|
||||
packages.default = pkgs.callPackage ./nix/endcap-sl-software-ri-generator.nix { };
|
||||
}
|
||||
);
|
||||
}
|
11
nix/endcap-sl-software-ri-generator.nix
Normal file
11
nix/endcap-sl-software-ri-generator.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
{ rustPlatform }:
|
||||
# https://nixos.org/manual/nixpkgs/stable/#rust
|
||||
rustPlatform.buildRustPackage {
|
||||
pname = "endcap-sl-software-ri-generator";
|
||||
version = "0.3.0";
|
||||
|
||||
src = ../.;
|
||||
cargoLock.lockFile = ../Cargo.lock;
|
||||
|
||||
buildFeatures = [ "bin" ];
|
||||
}
|
|
@ -389,11 +389,23 @@ impl Register {
|
|||
.transpose()?;
|
||||
let desc = node.attribute("desc").map(str::to_string);
|
||||
|
||||
let children: Vec<_> = node
|
||||
.children()
|
||||
.filter(|node| node.is_element() && node.tag_name().name().eq("field"))
|
||||
.map(Field::from_xml_dom)
|
||||
.collect::<Result<_, _>>()?;
|
||||
let children: Vec<_> = {
|
||||
if node
|
||||
.children()
|
||||
.filter(|node| node.is_element())
|
||||
.any(|node| node.tag_name().name().eq("value"))
|
||||
{
|
||||
// Generate virtual single field under the register, to absorb difference between
|
||||
// normal format
|
||||
vec![Field::from_xml_register_dom(node)?]
|
||||
} else {
|
||||
node.children()
|
||||
.filter(|node| node.is_element())
|
||||
.filter(|node| node.tag_name().name().eq("field"))
|
||||
.map(Field::from_xml_dom)
|
||||
.collect::<Result<_, _>>()?
|
||||
}
|
||||
};
|
||||
|
||||
// Validation
|
||||
if default.is_some() && !children.is_empty() {
|
||||
|
@ -505,6 +517,53 @@ impl Field {
|
|||
elements: children,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate virtual Field from Registers node (piracy), guessing a lot of parameters.
|
||||
pub(crate) fn from_xml_register_dom(node: Node) -> Result<Self, DomConversionError> {
|
||||
// // Prepend "val_" to avoid name confliction with struct for accessing values
|
||||
// let name = "val_".to_string() + &util::get_name(node)?;
|
||||
let name = "field".to_string();
|
||||
let mask = match node.attribute("mask") {
|
||||
Some(mask) => mask
|
||||
.parse_prefixed_u32()
|
||||
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node))?,
|
||||
None => node
|
||||
.ancestors()
|
||||
.filter_map(|node| util::get_type(node))
|
||||
.next()
|
||||
.transpose()?
|
||||
.map_or_else(
|
||||
|| Err(DomConversionError::parameter_completion_error("type", node)),
|
||||
|dtype| match dtype {
|
||||
crate::types::DataType::D32 => Ok(u32::MAX),
|
||||
},
|
||||
)?,
|
||||
};
|
||||
if node.has_attribute("default") {
|
||||
return Err(DomConversionError::other_error(
|
||||
"unsupported structure: register with @default without field",
|
||||
node,
|
||||
));
|
||||
}
|
||||
// Duplicated with parent (register) desc so should be removed, or add some comment?
|
||||
let desc = node.attribute("desc").map(str::to_string);
|
||||
let children = node
|
||||
.children()
|
||||
.filter(|node| node.is_element())
|
||||
.map(Value::from_xml_dom)
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(Field {
|
||||
name,
|
||||
mask,
|
||||
interface: None,
|
||||
multiple: None,
|
||||
default: None,
|
||||
sclr: None,
|
||||
desc,
|
||||
elements: children,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
|
|
@ -26,8 +26,12 @@ use thiserror::Error;
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CodeGenError {
|
||||
#[error("tokenization(syn) error")]
|
||||
SynError(#[from] syn::Error),
|
||||
#[error("tokenization(syn) error: {source}: {code}")]
|
||||
SynError {
|
||||
#[source]
|
||||
source: syn::Error,
|
||||
code: String,
|
||||
},
|
||||
#[error("failed to create file (name duplicated): {0}")]
|
||||
FilePathDuplicatedError(String),
|
||||
#[error("parent is required for {module}")]
|
||||
|
@ -42,11 +46,17 @@ mod util {
|
|||
use super::CodeGenError;
|
||||
|
||||
pub(super) fn parse_to_ident(s: &str) -> Result<proc_macro2::Ident, CodeGenError> {
|
||||
Ok(syn::parse_str(s)?)
|
||||
syn::parse_str(s).map_err(|e| CodeGenError::SynError {
|
||||
source: e,
|
||||
code: s.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn parse_to_literal(s: &str) -> Result<proc_macro2::Literal, CodeGenError> {
|
||||
Ok(syn::parse_str(s)?)
|
||||
syn::parse_str(s).map_err(|e| CodeGenError::SynError {
|
||||
source: e,
|
||||
code: s.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// currently only U32 is used, so `dead_code` for Debug, PartialEq
|
||||
|
@ -72,6 +82,21 @@ mod util {
|
|||
}
|
||||
}
|
||||
|
||||
/// Derive appropriate rust type for right aligned `mask`ed value.
|
||||
pub(super) fn from_exact_mask(mask: u32) -> Option<RustUxTypes> {
|
||||
if mask.trailing_zeros() != 0 {
|
||||
return None;
|
||||
}
|
||||
match 32 - mask.leading_zeros() {
|
||||
0 => panic!("mask cannot be 0"),
|
||||
1 => Some(RustUxTypes::Bool),
|
||||
8 => Some(RustUxTypes::U8),
|
||||
16 => Some(RustUxTypes::U16),
|
||||
32 => Some(RustUxTypes::U32),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_rust_type_token(&self) -> proc_macro2::Ident {
|
||||
match self {
|
||||
RustUxTypes::Bool => parse_to_ident("bool").unwrap(),
|
||||
|
@ -161,27 +186,26 @@ This code is auto generated using endcap_sl_software_ri_generator.
|
|||
GENERATOR_GIT_SHA,
|
||||
);
|
||||
let files = self.generate_register_interface(None, None, HashMap::new())?;
|
||||
Ok(files
|
||||
files
|
||||
.into_iter()
|
||||
.map(
|
||||
|(path, tokens)| -> Result<(PathBuf, syn::File), syn::Error> {
|
||||
let tokens = if path
|
||||
.file_name()
|
||||
.is_some_and(|file| file == "register_interface.rs")
|
||||
{
|
||||
quote! {
|
||||
#![doc = #build_metadata]
|
||||
.map(|(path, tokens)| -> Result<(PathBuf, syn::File), _> {
|
||||
let tokens = if path.file_name().is_some_and(|file| file == "mod.rs") {
|
||||
quote! {
|
||||
#![doc = #build_metadata]
|
||||
|
||||
#tokens
|
||||
}
|
||||
} else {
|
||||
tokens
|
||||
};
|
||||
let file: syn::File = syn::parse2(tokens)?;
|
||||
Ok((path, file))
|
||||
},
|
||||
)
|
||||
.process_results(|kv| HashMap::from_iter(kv))?)
|
||||
#tokens
|
||||
}
|
||||
} else {
|
||||
tokens
|
||||
};
|
||||
let file: syn::File =
|
||||
syn::parse2(tokens.clone()).map_err(|e| CodeGenError::SynError {
|
||||
source: e,
|
||||
code: tokens.to_string(),
|
||||
})?;
|
||||
Ok((path, file))
|
||||
})
|
||||
.process_results(|kv| HashMap::from_iter(kv))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,10 +246,10 @@ impl CodeGen for Module {
|
|||
|
||||
pub use super::RegisterInterface;
|
||||
};
|
||||
files.insert(PathBuf::from("./register_interface.rs"), out);
|
||||
files.insert(PathBuf::from("./mod.rs"), out);
|
||||
|
||||
let ident_register_interface = util::parse_to_ident("RegisterInterface").unwrap();
|
||||
let register_interface_mod = PathBuf::from("register_interface");
|
||||
let register_interface_mod = PathBuf::from("./");
|
||||
let files = self
|
||||
.elements_other
|
||||
.into_iter()
|
||||
|
@ -446,7 +470,6 @@ impl CodeGen for Register {
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::axic2c1::spec::AxiC2c1Spec;
|
||||
use crate::register_spec::{DataConversionError, Modifiable, Readable, RegisterSpec, Writable};
|
||||
|
||||
const OFFSET: usize = #addr;
|
||||
|
||||
|
|
|
@ -71,26 +71,35 @@ fn reg_type_def_masked(
|
|||
let x: RustUxTypes = basetype.into();
|
||||
x.to_rust_type_token()
|
||||
};
|
||||
let type_t = RustUxTypes::from_exact_mask(mask);
|
||||
|
||||
let out = quote! {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct #upper_camel_name(pub #type_ux);
|
||||
impl TryFrom<#type_ux> for #upper_camel_name {
|
||||
type Error = DataConversionError<#type_ux, Self>;
|
||||
// u8, u16 already implements Into and TryFrom with u32
|
||||
// but bool doen't implement TryFrom<u32>, so it cannot be used as `T` here
|
||||
match type_t {
|
||||
None | Some(RustUxTypes::Bool) => {
|
||||
let out = quote! {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct #upper_camel_name(pub #type_ux);
|
||||
impl TryFrom<#type_ux> for #upper_camel_name {
|
||||
type Error = crate::register_spec::DataConversionError<#type_ux, Self>;
|
||||
|
||||
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value & #mask))
|
||||
}
|
||||
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value & #mask))
|
||||
}
|
||||
}
|
||||
impl From<#upper_camel_name> for #type_ux {
|
||||
fn from(value: #upper_camel_name) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
};
|
||||
(out, upper_camel_name.clone(), type_ux)
|
||||
}
|
||||
impl From<#upper_camel_name> for #type_ux {
|
||||
fn from(value: #upper_camel_name) -> Self {
|
||||
value.0
|
||||
}
|
||||
Some(type_t) => {
|
||||
let out = quote! {};
|
||||
(out, type_t.to_rust_type_token(), type_ux)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
(out, upper_camel_name.clone(), type_ux)
|
||||
}
|
||||
}
|
||||
|
||||
/// Where `T` has fields.
|
||||
|
@ -137,7 +146,7 @@ fn reg_type_def_with_field(
|
|||
#(#code_setters)*
|
||||
}
|
||||
impl TryFrom<#type_ux> for #upper_camel_name {
|
||||
type Error = DataConversionError<#type_ux, Self>;
|
||||
type Error = crate::register_spec::DataConversionError<#type_ux, Self>;
|
||||
|
||||
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
||||
Ok(Self { inner: value })
|
||||
|
@ -351,6 +360,7 @@ fn generate_custom_values_const_enumdef(
|
|||
util::parse_to_ident(&field_name.to_string().to_upper_camel_case()).unwrap();
|
||||
|
||||
quote! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum #value_enum_name {
|
||||
#(#variants),*
|
||||
}
|
||||
|
|
|
@ -11,19 +11,19 @@ pub(super) fn gen_registerspec_impl(
|
|||
) -> TokenStream {
|
||||
let impl_rw = match modf {
|
||||
RwSpecifier::R => quote! {
|
||||
impl<T: AxiC2c1Spec> Readable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> crate::register_spec::Readable for #reg_name<'_, T> {}
|
||||
},
|
||||
RwSpecifier::W => quote! {
|
||||
impl<T: AxiC2c1Spec> Writable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> crate::register_spec::Writable for #reg_name<'_, T> {}
|
||||
},
|
||||
RwSpecifier::RW => quote! {
|
||||
impl<T: AxiC2c1Spec> Readable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> Writable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> Modifiable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> crate::register_spec::Readable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> crate::register_spec::Writable for #reg_name<'_, T> {}
|
||||
impl<T: AxiC2c1Spec> crate::register_spec::Modifiable for #reg_name<'_, T> {}
|
||||
},
|
||||
};
|
||||
quote! {
|
||||
impl<T: AxiC2c1Spec> RegisterSpec for #reg_name<'_, T> {
|
||||
impl<T: AxiC2c1Spec> crate::register_spec::RegisterSpec for #reg_name<'_, T> {
|
||||
type Ux = #type_ux;
|
||||
type T = #type_t;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn generate(xml: &path::Path, out: &path::Path) -> Result<(), Error> {
|
|||
log::debug!("xml parsed {doc:?}");
|
||||
|
||||
let register_map = types::Module::from_xml_dom(doc.root_element(), xml_git_info)?;
|
||||
log::debug!("converted {register_map:?}");
|
||||
log::debug!("converted {register_map:#?}");
|
||||
|
||||
let (_maps, errors) = register_map.validate();
|
||||
if !errors.is_empty() {
|
||||
|
@ -99,7 +99,11 @@ pub fn generate_flatmap(xml: &path::Path, out: Option<&path::Path>) -> Result<()
|
|||
}
|
||||
let f: Box<dyn Write> = match out {
|
||||
Some(f) => {
|
||||
let file = File::options().write(true).truncate(true).open(f)?;
|
||||
let file = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(f)?;
|
||||
Box::new(BufWriter::new(file))
|
||||
}
|
||||
None => Box::new(io::stdout()),
|
||||
|
|
126
src/io.rs
126
src/io.rs
|
@ -1,68 +1,114 @@
|
|||
//! File IO for generated codes.
|
||||
|
||||
use std::{collections::HashMap, fs, io, path, process};
|
||||
use std::{collections::HashMap, env, ffi::OsString, fs, io, path, process};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::types::XmlGitInfo;
|
||||
|
||||
const ENVVAR_XML_GIT_DESCRIBE: &str = "XML_GIT_DESCRIBE";
|
||||
const ENVVAR_XML_GIT_TIMESTAMP: &str = "XML_GIT_TIMESTAMP";
|
||||
const ENVVAR_XML_GIT_SHA: &str = "XML_GIT_SHA";
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XmlGitInfoError {
|
||||
#[error("io error while getting xml git info: {msg} {source}")]
|
||||
IOError {
|
||||
GitCommandSpawnError {
|
||||
msg: &'static str,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("git failed: {msg}: {stderr}")]
|
||||
CommandFailed { msg: &'static str, stderr: String },
|
||||
#[error("env var {var} is not unicode: {}", s.to_string_lossy())]
|
||||
NotUnicodeEnvVar { var: &'static str, s: OsString },
|
||||
}
|
||||
|
||||
impl XmlGitInfoError {
|
||||
fn io_error(e: io::Error, msg: &'static str) -> Self {
|
||||
XmlGitInfoError::IOError { msg, source: e }
|
||||
fn git_command_spawn_error(e: io::Error, msg: &'static str) -> Self {
|
||||
XmlGitInfoError::GitCommandSpawnError { 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 describe = match env::var(ENVVAR_XML_GIT_DESCRIBE) {
|
||||
Ok(describe) => describe,
|
||||
Err(env::VarError::NotUnicode(s)) => {
|
||||
return Err(XmlGitInfoError::NotUnicodeEnvVar {
|
||||
var: ENVVAR_XML_GIT_DESCRIBE,
|
||||
s,
|
||||
});
|
||||
}
|
||||
Err(env::VarError::NotPresent) => {
|
||||
let out_describe = process::Command::new("git")
|
||||
.args(["describe", "--always", "--dirty"])
|
||||
.current_dir(xml_path.parent().unwrap())
|
||||
.output()
|
||||
.map_err(|e| XmlGitInfoError::git_command_spawn_error(e, "git describe"))?;
|
||||
let describe = if out_describe.status.success() {
|
||||
String::from_utf8_lossy(&out_describe.stdout)
|
||||
.trim()
|
||||
.to_owned()
|
||||
} else {
|
||||
return Err(XmlGitInfoError::CommandFailed {
|
||||
msg: "git describe",
|
||||
stderr: String::from_utf8_lossy(&out_describe.stderr).into_owned(),
|
||||
});
|
||||
};
|
||||
describe
|
||||
}
|
||||
};
|
||||
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 timestamp = match env::var(ENVVAR_XML_GIT_TIMESTAMP) {
|
||||
Ok(timestamp) => timestamp,
|
||||
Err(env::VarError::NotUnicode(s)) => {
|
||||
return Err(XmlGitInfoError::NotUnicodeEnvVar {
|
||||
var: ENVVAR_XML_GIT_TIMESTAMP,
|
||||
s,
|
||||
});
|
||||
}
|
||||
Err(env::VarError::NotPresent) => {
|
||||
let timestamp_out = process::Command::new("git")
|
||||
.args(["log", "-1", "--pretty=format:'%cI'"])
|
||||
.current_dir(xml_path.parent().unwrap())
|
||||
.output()
|
||||
.map_err(|e| XmlGitInfoError::git_command_spawn_error(e, "git log (timestamp)"))?;
|
||||
let timestamp = if timestamp_out.status.success() {
|
||||
String::from_utf8_lossy(×tamp_out.stdout)
|
||||
.trim()
|
||||
.to_owned()
|
||||
} else {
|
||||
return Err(XmlGitInfoError::CommandFailed {
|
||||
msg: "git log (timestamp)",
|
||||
stderr: String::from_utf8_lossy(×tamp_out.stderr).into_owned(),
|
||||
});
|
||||
};
|
||||
timestamp
|
||||
}
|
||||
};
|
||||
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(),
|
||||
});
|
||||
let git_sha = match env::var(ENVVAR_XML_GIT_SHA) {
|
||||
Ok(sha) => sha,
|
||||
Err(env::VarError::NotUnicode(s)) => {
|
||||
return Err(XmlGitInfoError::NotUnicodeEnvVar {
|
||||
var: ENVVAR_XML_GIT_SHA,
|
||||
s,
|
||||
});
|
||||
}
|
||||
Err(env::VarError::NotPresent) => {
|
||||
let sha = process::Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir(xml_path.parent().unwrap())
|
||||
.output()
|
||||
.map_err(|e| XmlGitInfoError::git_command_spawn_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(),
|
||||
});
|
||||
};
|
||||
git_sha
|
||||
}
|
||||
};
|
||||
Ok(XmlGitInfo {
|
||||
describe,
|
||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -3,11 +3,16 @@ use std::path;
|
|||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
/// Generate register interface from register map xml.
|
||||
/// Validate the register map xml and generate register interface from register map xml.
|
||||
/// See `help` with each commands to see detailed explanations.
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about)]
|
||||
#[command(version, about, long_about)]
|
||||
struct Args {
|
||||
/// Input XML register map.
|
||||
///
|
||||
/// The path must be a valid git repository to get git metadata.
|
||||
/// You can also force these metadata with environmental variables `XML_GIT_DESCRIBE`,
|
||||
/// `XML_GIT_TIMESTAMP` and `XML_GIT_SHA`.
|
||||
xml: path::PathBuf,
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
|
@ -23,6 +28,8 @@ enum Commands {
|
|||
out: path::PathBuf,
|
||||
},
|
||||
/// Generate flattened register map in CSV.
|
||||
///
|
||||
/// Also run validation before generation.
|
||||
#[cfg(feature = "flatmap")]
|
||||
Flatmap {
|
||||
/// Flattened csv out path.
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct FlattenedRegisterEntry<'a> {
|
|||
}
|
||||
|
||||
/// Flattened register map.
|
||||
type FlattenedRegisterMap<'a> = Vec<Option<FlattenedRegisterEntry<'a>>>;
|
||||
pub type FlattenedRegisterMap<'a> = Vec<Option<FlattenedRegisterEntry<'a>>>;
|
||||
|
||||
impl Module {
|
||||
/// Validate the address assignment, generating a flatten register map.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue