mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
Merge #767
767: allow #[init] and #[idle] to be externed r=korken89 a=wiktorwieclaw
I updated `rtic-macros` to a allow init and idle to be externally defined.
## Design notes
* Updated `extern_binds` example to include external #[init] and #[idle] functions.
* Added docs to Local and Shared structs. The `extern_binds` example has a `#![deny(missing_docs)]` which caused some issues.
## Testing
Apart from building the example, I also used this feature in one of my projects and ran it on a MCU [here](98ca7bd42e/crates/cansat-stm32f4/src/main.rs (L59-L74)
)
## Related issues
* https://github.com/rtic-rs/rtic/issues/505
## Related PRs
* https://github.com/rtic-rs/rtic-syntax/pull/71
Co-authored-by: Vixu <lonevixu@gmail.com>
This commit is contained in:
commit
08c0065c02
8 changed files with 173 additions and 47 deletions
|
@ -34,16 +34,20 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
|||
let attrs = &idle.attrs;
|
||||
let context = &idle.context;
|
||||
let stmts = &idle.stmts;
|
||||
let user_idle = Some(quote!(
|
||||
#(#attrs)*
|
||||
#[allow(non_snake_case)]
|
||||
fn #name(#context: #name::Context) -> ! {
|
||||
use rtic::Mutex as _;
|
||||
use rtic::mutex::prelude::*;
|
||||
let user_idle = if !idle.is_extern {
|
||||
Some(quote!(
|
||||
#(#attrs)*
|
||||
#[allow(non_snake_case)]
|
||||
fn #name(#context: #name::Context) -> ! {
|
||||
use rtic::Mutex as _;
|
||||
use rtic::mutex::prelude::*;
|
||||
|
||||
#(#stmts)*
|
||||
}
|
||||
));
|
||||
#(#stmts)*
|
||||
}
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
quote!(
|
||||
#(#mod_app)*
|
||||
|
|
|
@ -54,10 +54,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
|||
.collect();
|
||||
|
||||
root_init.push(quote! {
|
||||
#[doc = r"Shared resources"]
|
||||
#shared_vis struct #shared {
|
||||
#(#shared_resources)*
|
||||
}
|
||||
|
||||
#[doc = r"Local resources"]
|
||||
#local_vis struct #local {
|
||||
#(#local_resources)*
|
||||
}
|
||||
|
@ -67,14 +69,18 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
|||
|
||||
let user_init_return = quote! {#shared, #local};
|
||||
|
||||
let user_init = quote!(
|
||||
#(#attrs)*
|
||||
#[inline(always)]
|
||||
#[allow(non_snake_case)]
|
||||
fn #name(#context: #name::Context) -> (#user_init_return) {
|
||||
#(#stmts)*
|
||||
}
|
||||
);
|
||||
let user_init = if !init.is_extern {
|
||||
Some(quote!(
|
||||
#(#attrs)*
|
||||
#[inline(always)]
|
||||
#[allow(non_snake_case)]
|
||||
fn #name(#context: #name::Context) -> (#user_init_return) {
|
||||
#(#stmts)*
|
||||
}
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut mod_app = None;
|
||||
|
||||
|
|
|
@ -91,6 +91,9 @@ pub struct Init {
|
|||
|
||||
/// The name of the user provided local resources struct
|
||||
pub user_local_struct: Ident,
|
||||
|
||||
/// The init function is declared externally
|
||||
pub is_extern: bool,
|
||||
}
|
||||
|
||||
/// `init` context metadata
|
||||
|
@ -127,6 +130,9 @@ pub struct Idle {
|
|||
|
||||
/// The statements that make up this `idle` function
|
||||
pub stmts: Vec<Stmt>,
|
||||
|
||||
/// The idle function is declared externally
|
||||
pub is_extern: bool,
|
||||
}
|
||||
|
||||
/// `idle` context metadata
|
||||
|
|
|
@ -365,6 +365,42 @@ impl App {
|
|||
if let ForeignItem::Fn(mut item) = item {
|
||||
let span = item.sig.ident.span();
|
||||
if let Some(pos) = item
|
||||
.attrs
|
||||
.iter()
|
||||
.position(|attr| util::attr_eq(attr, "init"))
|
||||
{
|
||||
let args = InitArgs::parse(item.attrs.remove(pos).tokens)?;
|
||||
|
||||
// If an init function already exists, error
|
||||
if init.is_some() {
|
||||
return Err(parse::Error::new(
|
||||
span,
|
||||
"`#[init]` function must appear at most once",
|
||||
));
|
||||
}
|
||||
|
||||
check_ident(&item.sig.ident)?;
|
||||
|
||||
init = Some(Init::parse_foreign(args, item)?);
|
||||
} else if let Some(pos) = item
|
||||
.attrs
|
||||
.iter()
|
||||
.position(|attr| util::attr_eq(attr, "idle"))
|
||||
{
|
||||
let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?;
|
||||
|
||||
// If an idle function already exists, error
|
||||
if idle.is_some() {
|
||||
return Err(parse::Error::new(
|
||||
span,
|
||||
"`#[idle]` function must appear at most once",
|
||||
));
|
||||
}
|
||||
|
||||
check_ident(&item.sig.ident)?;
|
||||
|
||||
idle = Some(Idle::parse_foreign(args, item)?);
|
||||
} else if let Some(pos) = item
|
||||
.attrs
|
||||
.iter()
|
||||
.position(|attr| util::attr_eq(attr, "task"))
|
||||
|
@ -408,7 +444,8 @@ impl App {
|
|||
} else {
|
||||
return Err(parse::Error::new(
|
||||
span,
|
||||
"`extern` task required `#[task(..)]` attribute",
|
||||
"`extern` task, init or idle must have either `#[task(..)]`,
|
||||
`#[init(..)]` or `#[idle(..)]` attribute",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use syn::{parse, ItemFn};
|
||||
use syn::{parse, ForeignItemFn, ItemFn, Stmt};
|
||||
|
||||
use crate::syntax::{
|
||||
ast::{Idle, IdleArgs},
|
||||
|
@ -29,6 +29,35 @@ impl Idle {
|
|||
context,
|
||||
name: item.sig.ident,
|
||||
stmts: item.block.stmts,
|
||||
is_extern: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(parse::Error::new(
|
||||
item.sig.ident.span(),
|
||||
format!("this `#[idle]` function must have signature `fn({name}::Context) -> !`"),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_foreign(args: IdleArgs, item: ForeignItemFn) -> parse::Result<Self> {
|
||||
let valid_signature = util::check_foreign_fn_signature(&item, false)
|
||||
&& item.sig.inputs.len() == 1
|
||||
&& util::type_is_bottom(&item.sig.output);
|
||||
|
||||
let name = item.sig.ident.to_string();
|
||||
|
||||
if valid_signature {
|
||||
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
|
||||
if rest.is_empty() {
|
||||
return Ok(Idle {
|
||||
args,
|
||||
attrs: item.attrs,
|
||||
context,
|
||||
name: item.sig.ident,
|
||||
stmts: Vec::<Stmt>::new(),
|
||||
is_extern: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
use syn::{parse, ItemFn};
|
||||
use syn::{parse, ForeignItemFn, ItemFn, Stmt};
|
||||
|
||||
use crate::syntax::{
|
||||
ast::{Init, InitArgs},
|
||||
|
@ -35,6 +35,44 @@ impl Init {
|
|||
stmts: item.block.stmts,
|
||||
user_shared_struct,
|
||||
user_local_struct,
|
||||
is_extern: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(parse::Error::new(
|
||||
span,
|
||||
format!(
|
||||
"the `#[init]` function must have signature `fn({name}::Context) -> (Shared resources struct, Local resources struct)`"
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_foreign(args: InitArgs, item: ForeignItemFn) -> parse::Result<Self> {
|
||||
let valid_signature =
|
||||
util::check_foreign_fn_signature(&item, false) && item.sig.inputs.len() == 1;
|
||||
|
||||
let span = item.sig.ident.span();
|
||||
|
||||
let name = item.sig.ident.to_string();
|
||||
|
||||
if valid_signature {
|
||||
if let Ok((user_shared_struct, user_local_struct)) =
|
||||
util::type_is_init_return(&item.sig.output)
|
||||
{
|
||||
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
|
||||
if rest.is_empty() {
|
||||
return Ok(Init {
|
||||
args,
|
||||
attrs: item.attrs,
|
||||
context,
|
||||
name: item.sig.ident,
|
||||
stmts: Vec::<Stmt>::new(),
|
||||
user_shared_struct,
|
||||
user_local_struct,
|
||||
is_extern: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
|
|||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Allow #[init] and #[idle] to be defined externally
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -6,9 +6,31 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use cortex_m_semihosting::hprintln;
|
||||
use cortex_m_semihosting::{debug, hprintln};
|
||||
use lm3s6965::Interrupt;
|
||||
use panic_semihosting as _;
|
||||
|
||||
// Free function implementing `init`.
|
||||
fn init(_: app::init::Context) -> (app::Shared, app::Local) {
|
||||
rtic::pend(Interrupt::UART0);
|
||||
|
||||
hprintln!("init");
|
||||
|
||||
(app::Shared {}, app::Local {})
|
||||
}
|
||||
|
||||
// Free function implementing `idle`.
|
||||
fn idle(_: app::idle::Context) -> ! {
|
||||
hprintln!("idle");
|
||||
|
||||
rtic::pend(Interrupt::UART0);
|
||||
|
||||
loop {
|
||||
cortex_m::asm::nop();
|
||||
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
||||
}
|
||||
}
|
||||
|
||||
// Free function implementing the interrupt bound task `foo`.
|
||||
fn foo(_: app::foo::Context) {
|
||||
hprintln!("foo called");
|
||||
|
@ -16,38 +38,21 @@ fn foo(_: app::foo::Context) {
|
|||
|
||||
#[rtic::app(device = lm3s6965)]
|
||||
mod app {
|
||||
use crate::foo;
|
||||
use cortex_m_semihosting::{debug, hprintln};
|
||||
use lm3s6965::Interrupt;
|
||||
use crate::{foo, idle, init};
|
||||
|
||||
#[shared]
|
||||
struct Shared {}
|
||||
pub struct Shared {}
|
||||
|
||||
#[local]
|
||||
struct Local {}
|
||||
|
||||
#[init]
|
||||
fn init(_: init::Context) -> (Shared, Local) {
|
||||
rtic::pend(Interrupt::UART0);
|
||||
|
||||
hprintln!("init");
|
||||
|
||||
(Shared {}, Local {})
|
||||
}
|
||||
|
||||
#[idle]
|
||||
fn idle(_: idle::Context) -> ! {
|
||||
hprintln!("idle");
|
||||
|
||||
rtic::pend(Interrupt::UART0);
|
||||
|
||||
loop {
|
||||
cortex_m::asm::nop();
|
||||
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
||||
}
|
||||
}
|
||||
pub struct Local {}
|
||||
|
||||
extern "Rust" {
|
||||
#[init]
|
||||
fn init(_: init::Context) -> (Shared, Local);
|
||||
|
||||
#[idle]
|
||||
fn idle(_: idle::Context) -> !;
|
||||
|
||||
#[task(binds = UART0)]
|
||||
fn foo(_: foo::Context);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue