-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
embassy
#[main]
convenience for RISC-V / Xtensa (#841)
- Loading branch information
Showing
70 changed files
with
1,723 additions
and
2,224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,3 +40,4 @@ esp32s3 = ["dep:object"] | |
|
||
interrupt = [] | ||
rtc_slow = [] | ||
embassy = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
use darling::ast::NestedMeta; | ||
use syn::{ | ||
parse::{Parse, ParseBuffer}, | ||
punctuated::Punctuated, | ||
Token, | ||
}; | ||
|
||
pub(crate) struct Args { | ||
pub(crate) meta: Vec<NestedMeta>, | ||
} | ||
|
||
impl Parse for Args { | ||
fn parse(input: &ParseBuffer) -> syn::Result<Self> { | ||
let meta = Punctuated::<NestedMeta, Token![,]>::parse_terminated(input)?; | ||
Ok(Args { | ||
meta: meta.into_iter().collect(), | ||
}) | ||
} | ||
} | ||
|
||
pub(crate) mod main { | ||
use std::{cell::RefCell, fmt::Display, thread}; | ||
|
||
use darling::{export::NestedMeta, FromMeta}; | ||
use proc_macro2::{Ident, Span, TokenStream}; | ||
use proc_macro_crate::FoundCrate; | ||
use quote::{quote, ToTokens}; | ||
use syn::{ReturnType, Type}; | ||
|
||
#[derive(Debug, FromMeta)] | ||
struct Args {} | ||
|
||
pub fn run( | ||
args: &[NestedMeta], | ||
f: syn::ItemFn, | ||
main: TokenStream, | ||
) -> Result<TokenStream, TokenStream> { | ||
#[allow(unused_variables)] | ||
let args = Args::from_list(args).map_err(|e| e.write_errors())?; | ||
|
||
let fargs = f.sig.inputs.clone(); | ||
|
||
let ctxt = Ctxt::new(); | ||
|
||
if f.sig.asyncness.is_none() { | ||
ctxt.error_spanned_by(&f.sig, "main function must be async"); | ||
} | ||
if !f.sig.generics.params.is_empty() { | ||
ctxt.error_spanned_by(&f.sig, "main function must not be generic"); | ||
} | ||
if !f.sig.generics.where_clause.is_none() { | ||
ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses"); | ||
} | ||
if !f.sig.abi.is_none() { | ||
ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier"); | ||
} | ||
if !f.sig.variadic.is_none() { | ||
ctxt.error_spanned_by(&f.sig, "main function must not be variadic"); | ||
} | ||
match &f.sig.output { | ||
ReturnType::Default => {} | ||
ReturnType::Type(_, ty) => match &**ty { | ||
Type::Tuple(tuple) if tuple.elems.is_empty() => {} | ||
Type::Never(_) => {} | ||
_ => ctxt.error_spanned_by( | ||
&f.sig, | ||
"main function must either not return a value, return `()` or return `!`", | ||
), | ||
}, | ||
} | ||
|
||
if fargs.len() != 1 { | ||
ctxt.error_spanned_by(&f.sig, "main function must have 1 argument: the spawner."); | ||
} | ||
|
||
ctxt.check()?; | ||
|
||
let f_body = f.block; | ||
let out = &f.sig.output; | ||
|
||
let result = quote! { | ||
#[::embassy_executor::task()] | ||
async fn __embassy_main(#fargs) #out { | ||
#f_body | ||
} | ||
|
||
unsafe fn __make_static<T>(t: &mut T) -> &'static mut T { | ||
::core::mem::transmute(t) | ||
} | ||
|
||
#main | ||
}; | ||
|
||
Ok(result) | ||
} | ||
|
||
/// A type to collect errors together and format them. | ||
/// | ||
/// Dropping this object will cause a panic. It must be consumed using | ||
/// `check`. | ||
/// | ||
/// References can be shared since this type uses run-time exclusive mut | ||
/// checking. | ||
#[derive(Default)] | ||
pub struct Ctxt { | ||
// The contents will be set to `None` during checking. This is so that checking can be | ||
// enforced. | ||
errors: RefCell<Option<Vec<syn::Error>>>, | ||
} | ||
|
||
impl Ctxt { | ||
/// Create a new context object. | ||
/// | ||
/// This object contains no errors, but will still trigger a panic if it | ||
/// is not `check`ed. | ||
pub fn new() -> Self { | ||
Ctxt { | ||
errors: RefCell::new(Some(Vec::new())), | ||
} | ||
} | ||
|
||
/// Add an error to the context object with a tokenenizable object. | ||
/// | ||
/// The object is used for spanning in error messages. | ||
pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) { | ||
self.errors | ||
.borrow_mut() | ||
.as_mut() | ||
.unwrap() | ||
// Curb monomorphization from generating too many identical methods. | ||
.push(syn::Error::new_spanned(obj.into_token_stream(), msg)); | ||
} | ||
|
||
/// Add one of Syn's parse errors. | ||
#[allow(unused)] | ||
pub fn syn_error(&self, err: syn::Error) { | ||
self.errors.borrow_mut().as_mut().unwrap().push(err); | ||
} | ||
|
||
/// Consume this object, producing a formatted error string if there are | ||
/// errors. | ||
pub fn check(self) -> Result<(), TokenStream> { | ||
let errors = self.errors.borrow_mut().take().unwrap(); | ||
match errors.len() { | ||
0 => Ok(()), | ||
_ => Err(to_compile_errors(errors)), | ||
} | ||
} | ||
} | ||
|
||
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream { | ||
let compile_errors = errors.iter().map(syn::Error::to_compile_error); | ||
quote!(#(#compile_errors)*) | ||
} | ||
|
||
impl Drop for Ctxt { | ||
fn drop(&mut self) { | ||
if !thread::panicking() && self.errors.borrow().is_some() { | ||
panic!("forgot to check for errors"); | ||
} | ||
} | ||
} | ||
|
||
pub fn main() -> TokenStream { | ||
let (hal_crate, hal_crate_name) = crate::get_hal_crate(); | ||
|
||
let executor = match hal_crate { | ||
Ok(FoundCrate::Itself) => { | ||
quote!( #hal_crate_name::embassy::executor::Executor ) | ||
} | ||
Ok(FoundCrate::Name(ref name)) => { | ||
let ident = Ident::new(&name, Span::call_site().into()); | ||
quote!( #ident::embassy::executor::Executor ) | ||
} | ||
Err(_) => { | ||
quote!(crate::embassy::executor::Executor) | ||
} | ||
}; | ||
|
||
quote! { | ||
#[entry] | ||
fn main() -> ! { | ||
let mut executor = #executor::new(); | ||
let executor = unsafe { __make_static(&mut executor) }; | ||
executor.run(|spawner| { | ||
spawner.must_spawn(__embassy_main(spawner)); | ||
}) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.