aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor-macros/src
diff options
context:
space:
mode:
authoroutfoxxed <[email protected]>2025-04-04 01:33:51 -0700
committeroutfoxxed <[email protected]>2025-04-06 18:52:32 -0700
commitef8d168df6fa1d8020d7490e4469d196a35077ce (patch)
treec4edd4bebd6bae564006bfa45dbeacb2071fa533 /embassy-executor-macros/src
parenta44abaf7e4562fa5393087fd845bf0d02141325b (diff)
executor: add executor selection to #[embassy_executor::main]
Diffstat (limited to 'embassy-executor-macros/src')
-rw-r--r--embassy-executor-macros/src/lib.rs24
-rw-r--r--embassy-executor-macros/src/macros/main.rs48
2 files changed, 67 insertions, 5 deletions
diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs
index 5f2182f10..8e737db6a 100644
--- a/embassy-executor-macros/src/lib.rs
+++ b/embassy-executor-macros/src/lib.rs
@@ -173,3 +173,27 @@ pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
173pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { 173pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream {
174 main::run(args.into(), item.into(), &main::ARCH_WASM).into() 174 main::run(args.into(), item.into(), &main::ARCH_WASM).into()
175} 175}
176
177/// Creates a new `executor` instance and declares an application entry point for an unspecified architecture, spawning the corresponding function body as an async task.
178///
179/// The following restrictions apply:
180///
181/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
182/// * The function must be declared `async`.
183/// * The function must not use generics.
184/// * Only a single `main` task may be declared.
185///
186/// A user-defined entry macro and executor type must be provided via the `entry` and `executor` arguments of the `main` macro.
187///
188/// ## Examples
189/// Spawning a task:
190/// ``` rust
191/// #[embassy_executor::main(entry = "your_hal::entry", executor = "your_hal::Executor")]
192/// async fn main(_s: embassy_executor::Spawner) {
193/// // Function body
194/// }
195/// ```
196#[proc_macro_attribute]
197pub fn main_unspecified(args: TokenStream, item: TokenStream) -> TokenStream {
198 main::run(args.into(), item.into(), &main::ARCH_UNSPECIFIED).into()
199}
diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs
index 24f61f30b..95722b19b 100644
--- a/embassy-executor-macros/src/macros/main.rs
+++ b/embassy-executor-macros/src/macros/main.rs
@@ -16,42 +16,57 @@ enum Flavor {
16pub(crate) struct Arch { 16pub(crate) struct Arch {
17 default_entry: Option<&'static str>, 17 default_entry: Option<&'static str>,
18 flavor: Flavor, 18 flavor: Flavor,
19 executor_required: bool,
19} 20}
20 21
21pub static ARCH_AVR: Arch = Arch { 22pub static ARCH_AVR: Arch = Arch {
22 default_entry: Some("avr_device::entry"), 23 default_entry: Some("avr_device::entry"),
23 flavor: Flavor::Standard, 24 flavor: Flavor::Standard,
25 executor_required: false,
24}; 26};
25 27
26pub static ARCH_RISCV: Arch = Arch { 28pub static ARCH_RISCV: Arch = Arch {
27 default_entry: Some("riscv_rt::entry"), 29 default_entry: Some("riscv_rt::entry"),
28 flavor: Flavor::Standard, 30 flavor: Flavor::Standard,
31 executor_required: false,
29}; 32};
30 33
31pub static ARCH_CORTEX_M: Arch = Arch { 34pub static ARCH_CORTEX_M: Arch = Arch {
32 default_entry: Some("cortex_m_rt::entry"), 35 default_entry: Some("cortex_m_rt::entry"),
33 flavor: Flavor::Standard, 36 flavor: Flavor::Standard,
37 executor_required: false,
34}; 38};
35 39
36pub static ARCH_SPIN: Arch = Arch { 40pub static ARCH_SPIN: Arch = Arch {
37 default_entry: None, 41 default_entry: None,
38 flavor: Flavor::Standard, 42 flavor: Flavor::Standard,
43 executor_required: false,
39}; 44};
40 45
41pub static ARCH_STD: Arch = Arch { 46pub static ARCH_STD: Arch = Arch {
42 default_entry: None, 47 default_entry: None,
43 flavor: Flavor::Standard, 48 flavor: Flavor::Standard,
49 executor_required: false,
44}; 50};
45 51
46pub static ARCH_WASM: Arch = Arch { 52pub static ARCH_WASM: Arch = Arch {
47 default_entry: Some("wasm_bindgen::prelude::wasm_bindgen(start)"), 53 default_entry: Some("wasm_bindgen::prelude::wasm_bindgen(start)"),
48 flavor: Flavor::Wasm, 54 flavor: Flavor::Wasm,
55 executor_required: false,
56};
57
58pub static ARCH_UNSPECIFIED: Arch = Arch {
59 default_entry: None,
60 flavor: Flavor::Standard,
61 executor_required: true,
49}; 62};
50 63
51#[derive(Debug, FromMeta, Default)] 64#[derive(Debug, FromMeta, Default)]
52struct Args { 65struct Args {
53 #[darling(default)] 66 #[darling(default)]
54 entry: Option<String>, 67 entry: Option<String>,
68 #[darling(default)]
69 executor: Option<String>,
55} 70}
56 71
57pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream { 72pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {
@@ -112,9 +127,10 @@ pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {
112 error(&mut errors, &f.sig, "main function must have 1 argument: the spawner."); 127 error(&mut errors, &f.sig, "main function must have 1 argument: the spawner.");
113 } 128 }
114 129
115 let entry = match args.entry.as_deref().or(arch.default_entry) { 130 let entry = match (args.entry.as_deref(), arch.default_entry.as_deref()) {
116 None => TokenStream::new(), 131 (None, None) => TokenStream::new(),
117 Some(x) => match TokenStream::from_str(x) { 132 (Some(x), _) | (None, Some(x)) if x == "" => TokenStream::new(),
133 (Some(x), _) | (None, Some(x)) => match TokenStream::from_str(x) {
118 Ok(x) => quote!(#[#x]), 134 Ok(x) => quote!(#[#x]),
119 Err(e) => { 135 Err(e) => {
120 error(&mut errors, &f.sig, e); 136 error(&mut errors, &f.sig, e);
@@ -123,6 +139,28 @@ pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {
123 }, 139 },
124 }; 140 };
125 141
142 let executor = match (args.executor.as_deref(), arch.executor_required) {
143 (None, true) => {
144 error(
145 &mut errors,
146 &f.sig,
147 "\
148No architecture selected for embassy-executor. Make sure you've enabled one of the `arch-*` features in your Cargo.toml.
149
150Alternatively, if you would like to use a custom executor implementation, specify it with the `executor` argument.
151For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Executor\")]",
152 );
153 ""
154 }
155 (Some(x), _) => x,
156 (None, _) => "::embassy_executor::Executor",
157 };
158
159 let executor = TokenStream::from_str(executor).unwrap_or_else(|e| {
160 error(&mut errors, &f.sig, e);
161 TokenStream::new()
162 });
163
126 let f_body = f.body; 164 let f_body = f.body;
127 let out = &f.sig.output; 165 let out = &f.sig.output;
128 166
@@ -134,7 +172,7 @@ pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {
134 ::core::mem::transmute(t) 172 ::core::mem::transmute(t)
135 } 173 }
136 174
137 let mut executor = ::embassy_executor::Executor::new(); 175 let mut executor = #executor::new();
138 let executor = unsafe { __make_static(&mut executor) }; 176 let executor = unsafe { __make_static(&mut executor) };
139 executor.run(|spawner| { 177 executor.run(|spawner| {
140 spawner.must_spawn(__embassy_main(spawner)); 178 spawner.must_spawn(__embassy_main(spawner));
@@ -144,7 +182,7 @@ pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {
144 Flavor::Wasm => ( 182 Flavor::Wasm => (
145 quote!(Result<(), wasm_bindgen::JsValue>), 183 quote!(Result<(), wasm_bindgen::JsValue>),
146 quote! { 184 quote! {
147 let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new())); 185 let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(#executor::new()));
148 186
149 executor.start(|spawner| { 187 executor.start(|spawner| {
150 spawner.must_spawn(__embassy_main(spawner)); 188 spawner.must_spawn(__embassy_main(spawner));