From 9444431a61d90a8692b4d9be8850c29a70dc04aa Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 10 Apr 2025 02:54:01 +0900 Subject: [PATCH 01/21] new(nix): build with nixpkgs buildRustPackage --- .gitignore | 1 + flake.lock | 82 +++++++++++++++++++++++++ flake.nix | 37 +++++++++++ nix/endcap-sl-software-ri-generator.nix | 11 ++++ 4 files changed, 131 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/endcap-sl-software-ri-generator.nix diff --git a/.gitignore b/.gitignore index ea8c4bf..8ea0ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +result* diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d28d4ad --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6a76064 --- /dev/null +++ b/flake.nix @@ -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 { }; + } + ); +} diff --git a/nix/endcap-sl-software-ri-generator.nix b/nix/endcap-sl-software-ri-generator.nix new file mode 100644 index 0000000..0ffc9b7 --- /dev/null +++ b/nix/endcap-sl-software-ri-generator.nix @@ -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" ]; +} From fafd262230d87b9d14c911ec24bfaf34e7bd97fe Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Mon, 14 Apr 2025 07:10:54 +0000 Subject: [PATCH 02/21] Add functionality to overwrite xml metadata with env var --- README-ja.md | 2 +- README.md | 2 +- src/io.rs | 120 +++++++++++++++++++++++++++++++++++---------------- src/main.rs | 11 ++++- 4 files changed, 94 insertions(+), 41 deletions(-) diff --git a/README-ja.md b/README-ja.md index 7e770e3..ddda1ba 100644 --- a/README-ja.md +++ b/README-ja.md @@ -33,7 +33,7 @@ cargo build --features=bin --bins --release `target/release`に入ってるバイナリファイルを実行するか、`cargo run -- `で実行できます。 -詳しくは`--help`を見てください。 +詳しくは(`-h`ではなく)`--help`を見てください。 ### ライブラリクレートの使い方 diff --git a/README.md b/README.md index 9685c6f..e9fd84e 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ to build. Execute the binary generated in `target/release/`, or run with `cargo run -- `. -See the `--help` for more information. +See the `--help`(not `-h`) for more information. ### Library crate usage diff --git a/src/io.rs b/src/io.rs index 8b2b1eb..bdf712f 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,11 +1,15 @@ //! 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}")] @@ -16,6 +20,8 @@ pub enum XmlGitInfoError { }, #[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 { @@ -25,44 +31,84 @@ impl XmlGitInfoError { } 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 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::io_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::io_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::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(), + }); + }; + git_sha + } }; Ok(XmlGitInfo { describe, diff --git a/src/main.rs b/src/main.rs index 01ca8e7..6208678 100644 --- a/src/main.rs +++ b/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. From 12332e8565820294a1468e34ceb623b661e7f8cc Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Mon, 14 Apr 2025 16:21:45 +0900 Subject: [PATCH 03/21] update(CHANGELOG): on !17 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 840fc67..317a18b 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 + +- 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) + ## [0.3.1] - 2025-04-11 ### Changed From b492d097e7dc1332c6de8c2edcc7888ef562d92d Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Mon, 14 Apr 2025 07:27:15 +0000 Subject: [PATCH 04/21] Improve errormsg --- CHANGELOG.md | 4 ++++ src/generator.rs | 58 +++++++++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 317a18b..a155b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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) +### Changed + +- Improve error message from syn in generator. !15 + ## [0.3.1] - 2025-04-11 ### Changed diff --git a/src/generator.rs b/src/generator.rs index f4487b4..110e4f1 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -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 { - 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 { - 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 @@ -161,27 +171,29 @@ 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 == "register_interface.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)) } } From 2accbc3910c2022f208797d28061d30806a86820 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Mon, 21 Apr 2025 21:39:06 +0900 Subject: [PATCH 05/21] fix(flatmap): create output file if it doesn't exist --- src/integrated.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/integrated.rs b/src/integrated.rs index c2dd810..341c448 100644 --- a/src/integrated.rs +++ b/src/integrated.rs @@ -99,7 +99,11 @@ pub fn generate_flatmap(xml: &path::Path, out: Option<&path::Path>) -> Result<() } let f: Box = 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()), From d6ee19e000fd83b5e2b6c5eccb5797f155003157 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Mon, 21 Apr 2025 21:39:39 +0900 Subject: [PATCH 06/21] fix(validator): make FlattenedRegisterMap public warned by rustdoc --- src/validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validator.rs b/src/validator.rs index 17a379e..74aa4c3 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -16,7 +16,7 @@ pub struct FlattenedRegisterEntry<'a> { } /// Flattened register map. -type FlattenedRegisterMap<'a> = Vec>>; +pub type FlattenedRegisterMap<'a> = Vec>>; impl Module { /// Validate the address assignment, generating a flatten register map. From 42efcc7cf67716e304a8747eb5df939c5d7fe606 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Tue, 22 Apr 2025 16:39:15 +0900 Subject: [PATCH 07/21] refactor(XmlGitInfo): improve error msg --- src/io.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/io.rs b/src/io.rs index bdf712f..f87edc5 100644 --- a/src/io.rs +++ b/src/io.rs @@ -13,7 +13,7 @@ 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, @@ -25,8 +25,8 @@ pub enum XmlGitInfoError { } 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 } } } @@ -44,7 +44,7 @@ pub(crate) fn get_xml_gitinfo(xml_path: &path::Path) -> Result Result Result Date: Thu, 24 Apr 2025 02:02:47 +0900 Subject: [PATCH 08/21] update(generator): change top mod path (remove top level mod) --- CHANGELOG.md | 1 + src/generator.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a155b60..5212799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improve error message from syn in generator. !15 +- Removed top level module (register_interface), and now it can have arbitrary name. ## [0.3.1] - 2025-04-11 diff --git a/src/generator.rs b/src/generator.rs index 110e4f1..b6cd10d 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -176,7 +176,7 @@ This code is auto generated using endcap_sl_software_ri_generator. .map(|(path, tokens)| -> Result<(PathBuf, syn::File), _> { let tokens = if path .file_name() - .is_some_and(|file| file == "register_interface.rs") + .is_some_and(|file| file == "lib.rs") { quote! { #![doc = #build_metadata] @@ -234,10 +234,10 @@ impl CodeGen for Module { pub use super::RegisterInterface; }; - files.insert(PathBuf::from("./register_interface.rs"), out); + files.insert(PathBuf::from("./lib.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() From 516cfa9151502554d0fc0b13fa77fb55127e7c78 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 24 Apr 2025 02:07:38 +0900 Subject: [PATCH 09/21] fix(generator): top level mod should be mod.rs instead of lib.rs --- src/generator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index b6cd10d..2c9ac1e 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -176,7 +176,7 @@ This code is auto generated using endcap_sl_software_ri_generator. .map(|(path, tokens)| -> Result<(PathBuf, syn::File), _> { let tokens = if path .file_name() - .is_some_and(|file| file == "lib.rs") + .is_some_and(|file| file == "mod.rs") { quote! { #![doc = #build_metadata] @@ -234,7 +234,7 @@ impl CodeGen for Module { pub use super::RegisterInterface; }; - files.insert(PathBuf::from("./lib.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("./"); From b8d18b26fccfef6c5554d67f151788d8f65ccbb9 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 24 Apr 2025 02:11:31 +0900 Subject: [PATCH 10/21] format --- src/generator.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 2c9ac1e..8fa5ff7 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -174,10 +174,7 @@ This code is auto generated using endcap_sl_software_ri_generator. files .into_iter() .map(|(path, tokens)| -> Result<(PathBuf, syn::File), _> { - let tokens = if path - .file_name() - .is_some_and(|file| file == "mod.rs") - { + let tokens = if path.file_name().is_some_and(|file| file == "mod.rs") { quote! { #![doc = #build_metadata] From 27f2f75ee2603310898665d059e05d2029dbe5ce Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 24 Apr 2025 03:04:04 +0900 Subject: [PATCH 11/21] add(converter): support value under register, by inserting field during conversion --- src/converter.rs | 57 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index 27d78b0..472ef0f 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -389,11 +389,19 @@ 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::>()?; + let children: Vec<_> = { + let mut child_elements = node.children().filter(|node| node.is_element()); + if child_elements.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 { + child_elements + .filter(|node| node.tag_name().name().eq("field")) + .map(Field::from_xml_dom) + .collect::>()? + } + }; // Validation if default.is_some() && !children.is_empty() { @@ -505,6 +513,45 @@ impl Field { elements: children, }) } + + /// Generate Field from Registers (piracy), guessing a lot of parameters. + pub(crate) fn from_xml_register_dom(node: Node) -> Result { + let name = util::get_name(node)?; + let mask = 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, + )); + } + let desc = node.attribute("desc").map(str::to_string); + let children = node + .children() + .filter(|node| node.is_element()) + .map(Value::from_xml_dom) + .collect::>()?; + + Ok(Field { + name, + mask, + interface: None, + multiple: None, + default: None, + sclr: None, + desc, + elements: children, + }) + } } impl Value { From 6ee8239682ff140c793952a83a9ba15639ebc04a Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 24 Apr 2025 03:06:26 +0900 Subject: [PATCH 12/21] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5212799..9adca3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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. [!3](https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/merge_requests/18) ### Changed From 3379babe12f7d651d804b6c6c2d99558caae216d Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 24 Apr 2025 03:11:08 +0900 Subject: [PATCH 13/21] fix(converter): use mask of parent register for virtual field if exists --- src/converter.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index 472ef0f..fbdf7f1 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -517,17 +517,22 @@ impl Field { /// Generate Field from Registers (piracy), guessing a lot of parameters. pub(crate) fn from_xml_register_dom(node: Node) -> Result { let name = util::get_name(node)?; - let mask = 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), - }, - )?; + 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", From e6f09d684a2ed369ed0c4166eefbaee87723e1d8 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Tue, 20 May 2025 18:09:10 +0900 Subject: [PATCH 14/21] fix(converter): prepend "val_" to avoid name confliction with register obj --- src/converter.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index fbdf7f1..7ab276a 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -514,9 +514,10 @@ impl Field { }) } - /// Generate Field from Registers (piracy), guessing a lot of parameters. + /// Generate virtual Field from Registers node (piracy), guessing a lot of parameters. pub(crate) fn from_xml_register_dom(node: Node) -> Result { - let name = util::get_name(node)?; + // Prepend "val_" to avoid name confliction with struct for accessing values + let name = "val_".to_string() + &util::get_name(node)?; let mask = match node.attribute("mask") { Some(mask) => mask .parse_prefixed_u32() @@ -539,6 +540,7 @@ impl 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() From 83e0c6cb3d60b5c112e070bdbb525c48c404868d Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Tue, 20 May 2025 18:49:01 +0900 Subject: [PATCH 15/21] fix(converter): wrong iterator usage all iterator was consumed for searching value before collecting field --- src/converter.rs | 10 +++++++--- src/integrated.rs | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index 7ab276a..2b9ecad 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -390,13 +390,17 @@ impl Register { let desc = node.attribute("desc").map(str::to_string); let children: Vec<_> = { - let mut child_elements = node.children().filter(|node| node.is_element()); - if child_elements.any(|node| node.tag_name().name().eq("value")) { + 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 { - child_elements + node.children() + .filter(|node| node.is_element()) .filter(|node| node.tag_name().name().eq("field")) .map(Field::from_xml_dom) .collect::>()? diff --git a/src/integrated.rs b/src/integrated.rs index 341c448..0d64ee8 100644 --- a/src/integrated.rs +++ b/src/integrated.rs @@ -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() { From eee043cd0f785780d4f7b32edac90244dbf900b8 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Tue, 20 May 2025 18:55:07 +0900 Subject: [PATCH 16/21] change(converter): name "field" for autogened virtual field instead of val_ prefixed register name --- src/converter.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index 2b9ecad..2fcc4a4 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -520,8 +520,9 @@ impl Field { /// Generate virtual Field from Registers node (piracy), guessing a lot of parameters. pub(crate) fn from_xml_register_dom(node: Node) -> Result { - // Prepend "val_" to avoid name confliction with struct for accessing values - let name = "val_".to_string() + &util::get_name(node)?; + // // 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() From 4bbb157228860ed305ac690982446e07b4a99278 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Tue, 20 May 2025 20:17:54 +0900 Subject: [PATCH 17/21] fix CHANGELOG (wrong mr number) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9adca3b..e8fb5f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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. [!3](https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/merge_requests/18) +- 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 From 2baf19a19053f10db2f98e9e884b983d3bb694c6 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 30 May 2025 05:19:06 +0900 Subject: [PATCH 18/21] update(generator): use full path RegisterSpec traits to avoid unused `use` warning --- CHANGELOG.md | 1 + src/generator.rs | 1 - src/generator/codegen_register.rs | 4 ++-- src/generator/codegen_registerspec_impl.rs | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8fb5f4..0f7d9a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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` ## [0.3.1] - 2025-04-11 diff --git a/src/generator.rs b/src/generator.rs index 8fa5ff7..94ea55f 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -455,7 +455,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; diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index b54e4a9..07533f3 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -76,7 +76,7 @@ fn reg_type_def_masked( #[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>; + type Error = register_spec::DataConversionError<#type_ux, Self>; fn try_from(value: #type_ux) -> Result { Ok(Self(value & #mask)) @@ -137,7 +137,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 = register_spec::DataConversionError<#type_ux, Self>; fn try_from(value: #type_ux) -> Result { Ok(Self { inner: value }) diff --git a/src/generator/codegen_registerspec_impl.rs b/src/generator/codegen_registerspec_impl.rs index 32604a0..5eca06c 100644 --- a/src/generator/codegen_registerspec_impl.rs +++ b/src/generator/codegen_registerspec_impl.rs @@ -11,19 +11,19 @@ pub(super) fn gen_registerspec_impl( ) -> TokenStream { let impl_rw = match modf { RwSpecifier::R => quote! { - impl Readable for #reg_name<'_, T> {} + impl register_spec::Readable for #reg_name<'_, T> {} }, RwSpecifier::W => quote! { - impl Writable for #reg_name<'_, T> {} + impl register_spec::Writable for #reg_name<'_, T> {} }, RwSpecifier::RW => quote! { - impl Readable for #reg_name<'_, T> {} - impl Writable for #reg_name<'_, T> {} - impl Modifiable for #reg_name<'_, T> {} + impl register_spec::Readable for #reg_name<'_, T> {} + impl register_spec::Writable for #reg_name<'_, T> {} + impl register_spec::Modifiable for #reg_name<'_, T> {} }, }; quote! { - impl RegisterSpec for #reg_name<'_, T> { + impl register_spec::RegisterSpec for #reg_name<'_, T> { type Ux = #type_ux; type T = #type_t; From 5ef9387d333eb4de0c4109e26821313db0334fc9 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 30 May 2025 05:24:34 +0900 Subject: [PATCH 19/21] fix: path of RegisterSpec --- src/generator/codegen_register.rs | 4 ++-- src/generator/codegen_registerspec_impl.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 07533f3..931ed3a 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -76,7 +76,7 @@ fn reg_type_def_masked( #[derive(Debug, Clone, Copy, Default)] pub struct #upper_camel_name(pub #type_ux); impl TryFrom<#type_ux> for #upper_camel_name { - type Error = register_spec::DataConversionError<#type_ux, Self>; + type Error = crate::register_spec::DataConversionError<#type_ux, Self>; fn try_from(value: #type_ux) -> Result { Ok(Self(value & #mask)) @@ -137,7 +137,7 @@ fn reg_type_def_with_field( #(#code_setters)* } impl TryFrom<#type_ux> for #upper_camel_name { - type Error = register_spec::DataConversionError<#type_ux, Self>; + type Error = crate::register_spec::DataConversionError<#type_ux, Self>; fn try_from(value: #type_ux) -> Result { Ok(Self { inner: value }) diff --git a/src/generator/codegen_registerspec_impl.rs b/src/generator/codegen_registerspec_impl.rs index 5eca06c..fc3dfb6 100644 --- a/src/generator/codegen_registerspec_impl.rs +++ b/src/generator/codegen_registerspec_impl.rs @@ -11,19 +11,19 @@ pub(super) fn gen_registerspec_impl( ) -> TokenStream { let impl_rw = match modf { RwSpecifier::R => quote! { - impl register_spec::Readable for #reg_name<'_, T> {} + impl crate::register_spec::Readable for #reg_name<'_, T> {} }, RwSpecifier::W => quote! { - impl register_spec::Writable for #reg_name<'_, T> {} + impl crate::register_spec::Writable for #reg_name<'_, T> {} }, RwSpecifier::RW => quote! { - impl register_spec::Readable for #reg_name<'_, T> {} - impl register_spec::Writable for #reg_name<'_, T> {} - impl register_spec::Modifiable for #reg_name<'_, T> {} + impl crate::register_spec::Readable for #reg_name<'_, T> {} + impl crate::register_spec::Writable for #reg_name<'_, T> {} + impl crate::register_spec::Modifiable for #reg_name<'_, T> {} }, }; quote! { - impl register_spec::RegisterSpec for #reg_name<'_, T> { + impl crate::register_spec::RegisterSpec for #reg_name<'_, T> { type Ux = #type_ux; type T = #type_t; From 1b0be30908983946b2492150ba99e8e34f128eb1 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 7 Jun 2025 05:04:39 +0900 Subject: [PATCH 20/21] update(generator): use u8 and u16 for register value when possible --- CHANGELOG.md | 1 + src/generator.rs | 15 +++++++++++ src/generator/codegen_register.rs | 41 +++++++++++++++++++------------ 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f7d9a0..b256eb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 diff --git a/src/generator.rs b/src/generator.rs index 94ea55f..83d3dfd 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -82,6 +82,21 @@ mod util { } } + /// Derive appropriate rust type for right aligned `mask`ed value. + pub(super) fn from_exact_mask(mask: u32) -> Option { + 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(), diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 931ed3a..4936ad9 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -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 = crate::register_spec::DataConversionError<#type_ux, Self>; + // u8, u16 already implements Into and TryFrom with u32 + // but bool doen't implement TryFrom, 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 { - Ok(Self(value & #mask)) - } + fn try_from(value: #type_ux) -> Result { + 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. From 44880c7531fcc3785dcd05a384704fc0c388fcfc Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Mon, 23 Jun 2025 17:04:36 +0900 Subject: [PATCH 21/21] update(codegen_register): add derive(Copy) to register value enum --- src/generator/codegen_register.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 4936ad9..fea2d10 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -360,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),* }