mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-23 20:22:51 +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 attrs = &idle.attrs;
|
||||||
let context = &idle.context;
|
let context = &idle.context;
|
||||||
let stmts = &idle.stmts;
|
let stmts = &idle.stmts;
|
||||||
let user_idle = Some(quote!(
|
let user_idle = if !idle.is_extern {
|
||||||
#(#attrs)*
|
Some(quote!(
|
||||||
#[allow(non_snake_case)]
|
#(#attrs)*
|
||||||
fn #name(#context: #name::Context) -> ! {
|
#[allow(non_snake_case)]
|
||||||
use rtic::Mutex as _;
|
fn #name(#context: #name::Context) -> ! {
|
||||||
use rtic::mutex::prelude::*;
|
use rtic::Mutex as _;
|
||||||
|
use rtic::mutex::prelude::*;
|
||||||
|
|
||||||
#(#stmts)*
|
#(#stmts)*
|
||||||
}
|
}
|
||||||
));
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#(#mod_app)*
|
#(#mod_app)*
|
||||||
|
|
|
@ -54,10 +54,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
root_init.push(quote! {
|
root_init.push(quote! {
|
||||||
|
#[doc = r"Shared resources"]
|
||||||
#shared_vis struct #shared {
|
#shared_vis struct #shared {
|
||||||
#(#shared_resources)*
|
#(#shared_resources)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = r"Local resources"]
|
||||||
#local_vis struct #local {
|
#local_vis struct #local {
|
||||||
#(#local_resources)*
|
#(#local_resources)*
|
||||||
}
|
}
|
||||||
|
@ -67,14 +69,18 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
|
|
||||||
let user_init_return = quote! {#shared, #local};
|
let user_init_return = quote! {#shared, #local};
|
||||||
|
|
||||||
let user_init = quote!(
|
let user_init = if !init.is_extern {
|
||||||
#(#attrs)*
|
Some(quote!(
|
||||||
#[inline(always)]
|
#(#attrs)*
|
||||||
#[allow(non_snake_case)]
|
#[inline(always)]
|
||||||
fn #name(#context: #name::Context) -> (#user_init_return) {
|
#[allow(non_snake_case)]
|
||||||
#(#stmts)*
|
fn #name(#context: #name::Context) -> (#user_init_return) {
|
||||||
}
|
#(#stmts)*
|
||||||
);
|
}
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut mod_app = None;
|
let mut mod_app = None;
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,9 @@ pub struct Init {
|
||||||
|
|
||||||
/// The name of the user provided local resources struct
|
/// The name of the user provided local resources struct
|
||||||
pub user_local_struct: Ident,
|
pub user_local_struct: Ident,
|
||||||
|
|
||||||
|
/// The init function is declared externally
|
||||||
|
pub is_extern: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `init` context metadata
|
/// `init` context metadata
|
||||||
|
@ -127,6 +130,9 @@ pub struct Idle {
|
||||||
|
|
||||||
/// The statements that make up this `idle` function
|
/// The statements that make up this `idle` function
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
|
|
||||||
|
/// The idle function is declared externally
|
||||||
|
pub is_extern: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `idle` context metadata
|
/// `idle` context metadata
|
||||||
|
|
|
@ -365,6 +365,42 @@ impl App {
|
||||||
if let ForeignItem::Fn(mut item) = item {
|
if let ForeignItem::Fn(mut item) = item {
|
||||||
let span = item.sig.ident.span();
|
let span = item.sig.ident.span();
|
||||||
if let Some(pos) = item
|
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
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.position(|attr| util::attr_eq(attr, "task"))
|
.position(|attr| util::attr_eq(attr, "task"))
|
||||||
|
@ -408,7 +444,8 @@ impl App {
|
||||||
} else {
|
} else {
|
||||||
return Err(parse::Error::new(
|
return Err(parse::Error::new(
|
||||||
span,
|
span,
|
||||||
"`extern` task required `#[task(..)]` attribute",
|
"`extern` task, init or idle must have either `#[task(..)]`,
|
||||||
|
`#[init(..)]` or `#[idle(..)]` attribute",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use syn::{parse, ItemFn};
|
use syn::{parse, ForeignItemFn, ItemFn, Stmt};
|
||||||
|
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
ast::{Idle, IdleArgs},
|
ast::{Idle, IdleArgs},
|
||||||
|
@ -29,6 +29,35 @@ impl Idle {
|
||||||
context,
|
context,
|
||||||
name: item.sig.ident,
|
name: item.sig.ident,
|
||||||
stmts: item.block.stmts,
|
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 proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
use syn::{parse, ItemFn};
|
use syn::{parse, ForeignItemFn, ItemFn, Stmt};
|
||||||
|
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
ast::{Init, InitArgs},
|
ast::{Init, InitArgs},
|
||||||
|
@ -35,6 +35,44 @@ impl Init {
|
||||||
stmts: item.block.stmts,
|
stmts: item.block.stmts,
|
||||||
user_shared_struct,
|
user_shared_struct,
|
||||||
user_local_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]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Allow #[init] and #[idle] to be defined externally
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,31 @@
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use cortex_m_semihosting::hprintln;
|
use cortex_m_semihosting::{debug, hprintln};
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
use panic_semihosting as _;
|
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`.
|
// Free function implementing the interrupt bound task `foo`.
|
||||||
fn foo(_: app::foo::Context) {
|
fn foo(_: app::foo::Context) {
|
||||||
hprintln!("foo called");
|
hprintln!("foo called");
|
||||||
|
@ -16,38 +38,21 @@ fn foo(_: app::foo::Context) {
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
use crate::foo;
|
use crate::{foo, idle, init};
|
||||||
use cortex_m_semihosting::{debug, hprintln};
|
|
||||||
use lm3s6965::Interrupt;
|
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {}
|
pub struct Shared {}
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {}
|
pub 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
|
#[init]
|
||||||
|
fn init(_: init::Context) -> (Shared, Local);
|
||||||
|
|
||||||
|
#[idle]
|
||||||
|
fn idle(_: idle::Context) -> !;
|
||||||
|
|
||||||
#[task(binds = UART0)]
|
#[task(binds = UART0)]
|
||||||
fn foo(_: foo::Context);
|
fn foo(_: foo::Context);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue