diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b5030f4..ca206958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Option `-f`(`--form`) let you split `lib.rs` on modules, as the `form` tool does +- Option `-o`(`--output`) let you specify output directory path + ### Fixed - Keyword sanitizing (`async`) diff --git a/src/generate/device.rs b/src/generate/device.rs index 3794ea99..ec14a078 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -5,6 +5,7 @@ use std::fs::File; use std::io::Write; use crate::errors::*; +use crate::modules::Module; use crate::util::{self, ToSanitizedUpperCase}; use crate::Target; @@ -17,8 +18,8 @@ pub fn render( nightly: bool, generic_mod: bool, device_x: &mut String, -) -> Result { - let mut out = TokenStream::new(); +) -> Result { + let mut out = Module::new("lib", ""); let doc = format!( "Peripheral access API for {0} microcontrollers \ @@ -159,16 +160,16 @@ pub fn render( if generic_mod { writeln!(File::create("generic.rs").unwrap(), "{}", generic_file).unwrap(); } else { - let tokens = syn::parse_file(generic_file).unwrap().into_token_stream(); - - out.extend(quote! { + let mut generic_module = Module::new( + "generic", + "Common register and bit access and modify traits", + ); + generic_module.out.extend(quote! { #[allow(unused_imports)] use generic::*; - ///Common register and bit access and modify traits - pub mod generic { - #tokens - } }); + generic_module.extend(syn::parse_file(generic_file).unwrap().into_token_stream()); + out.push_module(generic_module); } for p in &d.peripherals { @@ -177,7 +178,7 @@ pub fn render( continue; } - out.extend(peripheral::render( + out.push_module(peripheral::render( p, &d.peripherals, &d.default_register_properties, diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index bd41cccf..69edc391 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -1,3 +1,4 @@ +use crate::modules::Module; use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashMap; @@ -20,9 +21,7 @@ pub fn render( all_peripherals: &[Peripheral], defaults: &RegisterProperties, nightly: bool, -) -> Result { - let mut out = TokenStream::new(); - +) -> Result { let p_derivedfrom = p_original .derived_from .as_ref() @@ -31,13 +30,19 @@ pub fn render( let p_merged = p_derivedfrom.map(|ancestor| p_original.derive_from(ancestor)); let p = p_merged.as_ref().unwrap_or(p_original); + let name_sc = p.name.to_sanitized_snake_case(); + let description = + util::escape_brackets(util::respace(p.description.as_ref().unwrap_or(&p.name)).as_ref()); + + let mut module = Module::new(&name_sc, &description); + if p_original.derived_from.is_some() && p_derivedfrom.is_none() { eprintln!( "Couldn't find derivedFrom original: {} for {}, skipping", p_original.derived_from.as_ref().unwrap(), p_original.name ); - return Ok(out); + return Ok(module); } let span = Span::call_site(); @@ -46,15 +51,14 @@ pub fn render( let description = util::respace(p.description.as_ref().unwrap_or(&p.name)); let derive_regs = p_derivedfrom.is_some() && p_original.registers.is_none(); - let name_sc = Ident::new(&p.name.to_sanitized_snake_case(), span); let base = if derive_regs { Ident::new(&p_derivedfrom.unwrap().name.to_sanitized_snake_case(), span) } else { - name_sc.clone() + Ident::new(&name_sc, span) }; // Insert the peripheral structure - out.extend(quote! { + module.out.extend(quote! { #[doc = #description] pub struct #name_pc { _marker: PhantomData<*const ()> } @@ -81,7 +85,7 @@ pub fn render( // Derived peripherals may not require re-implementation, and will instead // use a single definition of the non-derived version. if derive_regs { - return Ok(out); + return Ok(module); } // erc: *E*ither *R*egister or *C*luster @@ -143,23 +147,22 @@ pub fn render( // No `struct RegisterBlock` can be generated if registers.is_empty() && clusters.is_empty() { // Drop the definition of the peripheral - return Ok(TokenStream::new()); + return Ok(Module::new(&name_sc, &description)); } let defaults = p.default_register_properties.derive_from(defaults); // Push any register or cluster blocks into the output - let mut mod_items = TokenStream::new(); - mod_items.extend(register_or_cluster_block(ercs, &defaults, None, nightly)?); + module.extend(register_or_cluster_block(ercs, &defaults, None, nightly)?); // Push all cluster related information into the peripheral module for c in &clusters { - mod_items.extend(cluster_block(c, &defaults, p, all_peripherals, nightly)?); + module.push_module(cluster_block(c, &defaults, p, all_peripherals, nightly)?); } // Push all regsiter realted information into the peripheral module for reg in registers { - mod_items.extend(register::render( + module.push_module(register::render( reg, registers, p, @@ -168,22 +171,7 @@ pub fn render( )?); } - let description = - util::escape_brackets(util::respace(p.description.as_ref().unwrap_or(&p.name)).as_ref()); - - let open = Punct::new('{', Spacing::Alone); - let close = Punct::new('}', Spacing::Alone); - - out.extend(quote! { - #[doc = #description] - pub mod #name_sc #open - }); - - out.extend(mod_items); - - close.to_tokens(&mut out); - - Ok(out) + Ok(module) } #[derive(Clone, Debug)] @@ -716,9 +704,7 @@ fn cluster_block( p: &Peripheral, all_peripherals: &[Peripheral], nightly: bool, -) -> Result { - let mut mod_items = TokenStream::new(); - +) -> Result { // name_sc needs to take into account array type. let description = util::escape_brackets(util::respace(c.description.as_ref().unwrap_or(&c.name)).as_ref()); @@ -731,16 +717,25 @@ fn cluster_block( }, "", ); - let name_sc = Ident::new(&mod_name.to_sanitized_snake_case(), Span::call_site()); + + let mut module = Module::new( + &mod_name.to_sanitized_snake_case(), + &("Register block\n".to_string() + &description), + ); let defaults = c.default_register_properties.derive_from(defaults); - let reg_block = register_or_cluster_block(&c.children, &defaults, Some(&mod_name), nightly)?; + module.out.extend(register_or_cluster_block( + &c.children, + &defaults, + Some(&mod_name), + nightly, + )?); // Generate definition for each of the registers. let registers = util::only_registers(&c.children); for reg in ®isters { - mod_items.extend(register::render( + module.push_module(register::render( reg, ®isters, p, @@ -752,18 +747,9 @@ fn cluster_block( // Generate the sub-cluster blocks. let clusters = util::only_clusters(&c.children); for c in &clusters { - mod_items.extend(cluster_block(c, &defaults, p, all_peripherals, nightly)?); + module.push_module(cluster_block(c, &defaults, p, all_peripherals, nightly)?); } - - Ok(quote! { - #reg_block - - ///Register block - #[doc = #description] - pub mod #name_sc { - #mod_items - } - }) + Ok(module) } /// Takes a svd::Register which may be a register array, and turn in into diff --git a/src/generate/register.rs b/src/generate/register.rs index 53749f0f..2b40e330 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -1,3 +1,4 @@ +use crate::modules::Module; use crate::quote::ToTokens; use crate::svd::{ Access, BitRange, EnumeratedValues, Field, Peripheral, Register, RegisterCluster, @@ -16,13 +17,13 @@ pub fn render( peripheral: &Peripheral, all_peripherals: &[Peripheral], defs: &RegisterProperties, -) -> Result { +) -> Result { let access = util::access_of(register); let name = util::name_of(register); let span = Span::call_site(); let name_pc = Ident::new(&name.to_sanitized_upper_case(), span); let _name_pc = Ident::new(&format!("_{}", &name.to_sanitized_upper_case()), span); - let name_sc = Ident::new(&name.to_sanitized_snake_case(), span); + let name_sc = name.to_sanitized_snake_case(); let rsize = register .size .or(defs.size) @@ -43,7 +44,7 @@ pub fn render( .as_ref(), ); - let mut mod_items = TokenStream::new(); + let mut module = Module::new(&name_sc, &description); let mut r_impl_items = TokenStream::new(); let mut w_impl_items = TokenStream::new(); let mut methods = vec![]; @@ -53,7 +54,7 @@ pub fn render( if can_read { let desc = format!("Reader of register {}", register.name); - mod_items.extend(quote! { + module.extend(quote! { #[doc = #desc] pub type R = crate::R<#rty, super::#name_pc>; }); @@ -63,13 +64,13 @@ pub fn render( let res_val = register.reset_value.or(defs.reset_value).map(|v| v as u64); if can_write { let desc = format!("Writer for register {}", register.name); - mod_items.extend(quote! { + module.extend(quote! { #[doc = #desc] pub type W = crate::W<#rty, super::#name_pc>; }); if let Some(rv) = res_val.map(util::hex) { let doc = format!("Register {} `reset()`'s with value {}", register.name, &rv); - mod_items.extend(quote! { + module.extend(quote! { #[doc = #doc] impl crate::ResetValue for super::#name_pc { type Type = #rty; @@ -106,7 +107,7 @@ pub fn render( &rty, res_val, access, - &mut mod_items, + &mut module, &mut r_impl_items, &mut w_impl_items, )?; @@ -114,29 +115,28 @@ pub fn render( } let open = Punct::new('{', Spacing::Alone); - let close = Punct::new('}', Spacing::Alone); + let close = Punct::new('}', Spacing::Alone).into_token_stream(); if can_read { - mod_items.extend(quote! { + module.extend(quote! { impl R #open }); - mod_items.extend(r_impl_items); + module.extend(r_impl_items); - close.to_tokens(&mut mod_items); + module.extend(close.clone()); } if can_write { - mod_items.extend(quote! { + module.extend(quote! { impl W #open }); - mod_items.extend(w_impl_items); + module.extend(w_impl_items); - close.to_tokens(&mut mod_items); + module.extend(close); } - let mut out = TokenStream::new(); let methods = methods .iter() .map(|s| format!("[`{0}`](crate::generic::Reg::{0})", s)) @@ -151,7 +151,7 @@ pub fn render( ) .as_str(); } - out.extend(quote! { + module.out.extend(quote! { #[doc = #doc] pub type #name_pc = crate::Reg<#rty, #_name_pc>; @@ -165,7 +165,7 @@ pub fn render( "`read()` method returns [{0}::R]({0}::R) reader structure", &name_sc ); - out.extend(quote! { + module.out.extend(quote! { #[doc = #doc] impl crate::Readable for #name_pc {} }); @@ -175,24 +175,12 @@ pub fn render( "`write(|w| ..)` method takes [{0}::W]({0}::W) writer structure", &name_sc ); - out.extend(quote! { + module.out.extend(quote! { #[doc = #doc] impl crate::Writable for #name_pc {} }); } - - out.extend(quote! { - #[doc = #description] - pub mod #name_sc #open - }); - - out.extend(mod_items); - - out.extend(quote! { - #close - }); - - Ok(out) + Ok(module) } pub fn fields( @@ -204,7 +192,7 @@ pub fn fields( rty: &Ident, reset_value: Option, access: Access, - mod_items: &mut TokenStream, + module: &mut Module, r_impl_items: &mut TokenStream, w_impl_items: &mut TokenStream, ) -> Result<()> { @@ -374,9 +362,9 @@ pub fn fields( let pc = util::replace_suffix(base.field, ""); let pc = pc.to_sanitized_upper_case(); let base_pc_r = Ident::new(&(pc + "_A"), span); - derive_from_base(mod_items, &base, &pc_r, &base_pc_r, &description); + derive_from_base(module, &base, &pc_r, &base_pc_r, &description); - mod_items.extend(quote! { + module.extend(quote! { #[doc = #readerdoc] pub type #_pc_r = crate::R<#fty, #pc_r>; }); @@ -384,7 +372,7 @@ pub fn fields( let has_reserved_variant = evs.values.len() != (1 << width); let variants = Variant::from_enumerated_values(evs)?; - add_from_variants(mod_items, &variants, &pc_r, &fty, &description, rv); + add_from_variants(module, &variants, &pc_r, &fty, &description, rv); let mut enum_items = TokenStream::new(); @@ -458,7 +446,7 @@ pub fn fields( }); } - mod_items.extend(quote! { + module.extend(quote! { #[doc = #readerdoc] pub type #_pc_r = crate::R<#fty, #pc_r>; impl #_pc_r { @@ -467,7 +455,7 @@ pub fn fields( }); } } else { - mod_items.extend(quote! { + module.extend(quote! { #[doc = #readerdoc] pub type #_pc_r = crate::R<#fty, #fty>; }) @@ -494,9 +482,9 @@ pub fn fields( let pc = util::replace_suffix(base.field, ""); let pc = pc.to_sanitized_upper_case(); let base_pc_w = Ident::new(&(pc + "_AW"), span); - derive_from_base(mod_items, &base, &pc_w, &base_pc_w, &description) + derive_from_base(module, &base, &pc_w, &base_pc_w, &description) } else { - add_from_variants(mod_items, &variants, pc_w, &fty, &description, rv); + add_from_variants(module, &variants, pc_w, &fty, &description, rv); } } @@ -584,7 +572,7 @@ pub fn fields( offset_entry = quote! {}; } - mod_items.extend(quote! { + module.extend(quote! { #[doc = #doc] pub struct #_pc_w<'a> { w: &'a mut W, @@ -696,7 +684,7 @@ impl Variant { } fn add_from_variants( - mod_items: &mut TokenStream, + module: &mut Module, variants: &[Variant], pc: &Ident, fty: &Ident, @@ -728,7 +716,7 @@ fn add_from_variants( desc.to_owned() }; - mod_items.extend(quote! { + module.extend(quote! { #[doc = #desc] #[derive(Clone, Copy, Debug, PartialEq)] #repr @@ -779,13 +767,7 @@ fn description_with_bits(description: &str, offset: u64, width: u32) -> String { res } -fn derive_from_base( - mod_items: &mut TokenStream, - base: &Base, - pc: &Ident, - base_pc: &Ident, - desc: &str, -) { +fn derive_from_base(module: &mut Module, base: &Base, pc: &Ident, base_pc: &Ident, desc: &str) { let span = Span::call_site(); if let (Some(peripheral), Some(register)) = (&base.peripheral, &base.register) { let pmod_ = peripheral.to_sanitized_snake_case(); @@ -793,7 +775,7 @@ fn derive_from_base( let pmod_ = Ident::new(&pmod_, span); let rmod_ = Ident::new(&rmod_, span); - mod_items.extend(quote! { + module.extend(quote! { #[doc = #desc] pub type #pc = crate::#pmod_::#rmod_::#base_pc; @@ -802,13 +784,13 @@ fn derive_from_base( let mod_ = register.to_sanitized_snake_case(); let mod_ = Ident::new(&mod_, span); - mod_items.extend(quote! { + module.extend(quote! { #[doc = #desc] pub type #pc = super::#mod_::#base_pc; }); } else { - mod_items.extend(quote! { + module.extend(quote! { #[doc = #desc] pub type #pc = #base_pc; }); diff --git a/src/lib.rs b/src/lib.rs index 4c013f98..68a2a183 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -489,6 +489,7 @@ use svd_parser as svd; mod errors; mod generate; +mod modules; mod util; pub use crate::util::Target; @@ -523,7 +524,8 @@ pub fn generate(xml: &str, target: Target, nightly: bool) -> Result let device = svd::parse(xml).unwrap(); //TODO(AJM) let mut device_x = String::new(); let items = generate::device::render(&device, target, nightly, false, &mut device_x) - .or(Err(SvdError::Render))?; + .or(Err(SvdError::Render))? + .items_into_token_stream(); let mut lib_rs = String::new(); writeln!( diff --git a/src/main.rs b/src/main.rs index 20237961..9d9e4223 100755 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,12 @@ extern crate error_chain; extern crate log; #[macro_use] extern crate quote; +use std::path::PathBuf; use svd_parser as svd; mod errors; mod generate; +mod modules; mod util; use std::fs::File; @@ -51,6 +53,18 @@ fn run() -> Result<()> { .short("g") .help("Push generic mod in separate file"), ) + .arg( + Arg::with_name("form") + .long("form") + .short("f") + .help("Split on modules"), + ) + .arg( + Arg::with_name("output") + .long("output") + .short("o") + .help("Directory to place generated files"), + ) .arg( Arg::with_name("log_level") .long("log") @@ -98,17 +112,41 @@ fn run() -> Result<()> { let generic_mod = matches.is_present("generic_mod"); + let form = matches.is_present("form"); + let mut device_x = String::new(); let items = generate::device::render(&device, target, nightly, generic_mod, &mut device_x)?; - let mut file = File::create("lib.rs").expect("Couldn't create lib.rs file"); - let data = items.to_string().replace("] ", "]\n"); - file.write_all(data.as_ref()) - .expect("Could not write code to lib.rs"); + let path = PathBuf::from(match matches.value_of("output") { + Some(path) => path, + None => ".", + }); + if form { + items.lib_to_files(&path.join("src")); + } else { + let mut file = File::create(&path.join("lib.rs")).expect("Couldn't create lib.rs file"); + + let data = items + .items_into_token_stream() + .to_string() + .replace("] ", "]\n"); + file.write_all(data.as_ref()) + .expect("Could not write code to lib.rs"); + } if target == Target::CortexM || target == Target::Msp430 { - writeln!(File::create("device.x").unwrap(), "{}", device_x).unwrap(); - writeln!(File::create("build.rs").unwrap(), "{}", build_rs()).unwrap(); + writeln!( + File::create(&path.join("device.x")).unwrap(), + "{}", + device_x + ) + .unwrap(); + writeln!( + File::create(&path.join("build.rs")).unwrap(), + "{}", + build_rs() + ) + .unwrap(); } Ok(()) diff --git a/src/modules.rs b/src/modules.rs new file mode 100644 index 00000000..42fbed7f --- /dev/null +++ b/src/modules.rs @@ -0,0 +1,119 @@ +use crate::quote::ToTokens; +use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream}; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +pub enum Item { + Tokens(TokenStream), + Module(Module), +} + +/// Structure of module: +/// +/// | #out +/// | +/// | /// #description +/// | mod #name { +/// | #items +/// | } +pub struct Module { + name: String, + description: String, + pub out: TokenStream, + items: Vec, +} + +impl Module { + pub fn new(name: &str, description: &str) -> Self { + Self { + name: name.to_string(), + description: description.to_string(), + out: TokenStream::new(), + items: Vec::new(), + } + } + pub fn push_module(&mut self, module: Module) { + self.items.push(Item::Module(module)) + } + pub fn extend(&mut self, tokens: TokenStream) { + self.items.push(Item::Tokens(tokens)) + } + pub fn into_token_stream(self) -> TokenStream { + let mut tokens = self.out; + let open = Punct::new('{', Spacing::Alone); + let close = Punct::new('}', Spacing::Alone); + let name = Ident::new(&self.name, Span::call_site()); + let description = self.description; + if !self.items.is_empty() { + tokens.extend(quote! { + #[doc = #description] + pub mod #name #open + }); + for item in self.items.into_iter() { + tokens.extend(match item { + Item::Tokens(t) => t, + Item::Module(m) => m.into_token_stream(), + }); + } + close.to_tokens(&mut tokens); + } + tokens + } + pub fn items_into_token_stream(self) -> TokenStream { + let mut tokens = TokenStream::new(); + for item in self.items.into_iter() { + tokens.extend(match item { + Item::Tokens(t) => t, + Item::Module(m) => m.into_token_stream(), + }); + } + tokens + } + #[allow(dead_code)] + fn items_to_files(items: Vec, path: &Path, dir_path: &Path) { + let mut tokens = TokenStream::new(); + std::fs::create_dir_all(&dir_path) + .expect(&format!("Could not create directory {:?}", dir_path)); + for item in items.into_iter() { + tokens.extend(match item { + Item::Tokens(t) => t, + Item::Module(m) => m.to_files(&dir_path), + }); + let mut file = File::create(path).unwrap(); + file.write_all(tokens.to_string().as_ref()) + .expect(&format!("Could not write code to {:?}", path)); + } + } + #[allow(dead_code)] + pub fn to_files(self, path: &Path) -> TokenStream { + let Module { + name, + description, + mut out, + items, + } = self; + if !items.is_empty() { + Self::items_to_files( + items, + &path.join(&format!("{}.rs", name)), + &path.join(&name), + ); + let name = Ident::new(&name, Span::call_site()); + out.extend(quote! { + #[doc = #description] + pub mod #name; + }); + } + out + } + #[allow(dead_code)] + pub fn lib_to_files(self, path: &Path) { + if path.exists() { + std::fs::remove_dir_all(path).unwrap(); + } + if !self.items.is_empty() { + Self::items_to_files(self.items, &path.join("lib.rs"), path); + } + } +}