Skip to content

Commit f849860

Browse files
committed
implement #552
1 parent 1ce34f2 commit f849860

File tree

6 files changed

+382
-1
lines changed

6 files changed

+382
-1
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[workspace]
2+
members = ["esp-idf-macros"]
3+
14
[package]
25
name = "esp-idf-hal"
36
version = "0.45.2"
@@ -25,7 +28,7 @@ harness = false
2528
default = ["std", "binstart"]
2629
std = ["alloc", "esp-idf-sys/std"]
2730
alloc = []
28-
nightly = []
31+
nightly = ["esp-idf-macros/doc-cfg"]
2932
experimental = []
3033
wake-from-isr = [] # Only enable if you plan to use the `edge-executor` crate
3134
embassy-sync = [] # For now, the dependecy on the `embassy-sync` crate is non-optional, but this might change in future
@@ -62,6 +65,7 @@ enumset = { version = "1.1.4", default-features = false }
6265
log = { version = "0.4", default-features = false }
6366
atomic-waker = { version = "1.1.1", default-features = false }
6467
embassy-sync = "0.7"
68+
esp-idf-macros = { path = "esp-idf-macros" }
6569

6670
[build-dependencies]
6771
embuild = "0.33"

esp-idf-macros/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "esp-idf-macros"
3+
version = "0.1.0"
4+
edition = "2021"
5+
rust-version = "1.79"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[features]
11+
default = []
12+
doc-cfg = []
13+
14+
[dependencies]
15+
proc-macro2 = "1"
16+
quote = "1"
17+
syn = { version = "2", features = ["extra-traits", "full", "fold"] }

esp-idf-macros/src/dcfg.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use proc_macro2::TokenStream;
2+
use quote::{quote, quote_spanned};
3+
use syn::spanned::Spanned;
4+
5+
pub fn dcfg(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
6+
// This will either emit #[cfg(...)] or #[cfg(...)] #[doc(cfg(...))] depending on whether
7+
// the "doc-cfg" feature is enabled.
8+
9+
let attrs = {
10+
#[cfg(feature = "doc-cfg")]
11+
{
12+
quote_spanned!(args.span() => #[cfg(#args)] #[doc(cfg(#args))])
13+
}
14+
#[cfg(not(feature = "doc-cfg"))]
15+
{
16+
quote_spanned!(args.span() => #[cfg(#args)])
17+
}
18+
};
19+
20+
Ok(quote! {
21+
#attrs
22+
#input
23+
})
24+
}

esp-idf-macros/src/lib.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use proc_macro::TokenStream;
2+
3+
mod dcfg;
4+
mod ram;
5+
6+
/// This attribute places the annotated function or static variable into the internal RAM
7+
/// of the ESP32 chip.
8+
///
9+
/// # Possible issues with functions
10+
///
11+
/// If a function is placed into RAM, the literals are not automatically placed into RAM as well:
12+
///
13+
/// ```rust,ignore
14+
/// #[ram]
15+
/// fn gpio_isr_handler() -> usize {
16+
/// let s = "I am string still stored in flash";
17+
/// }
18+
/// ```
19+
///
20+
/// To store the literal in flash, one could do
21+
/// ```rust,ignore
22+
/// #[ram]
23+
/// fn gpio_isr_handler() -> usize {
24+
/// #[ram]
25+
/// static _S: &str = "I am string still stored in flash";
26+
/// let s = _S;
27+
/// }
28+
/// ```
29+
///
30+
/// # Possible issues with statics that reference data
31+
///
32+
/// If the attribute is applied to a static variable that references some data, for example
33+
/// `#[ram] static MESSAGE: &str = "Error, invalid argument";` or `#[ram] static BUFFER: &[u8] = &[0]`
34+
/// the referenced data might not be placed into RAM.
35+
///
36+
/// For byte string and string literals, the attribute will ensure that the referenced data is placed in
37+
/// RAM, but for arbitrary slices or references it is unable to do so.
38+
///
39+
/// The first option is to declare an owned static array that is then referenced by the static variable,
40+
/// applying the ram attribute to both:
41+
///
42+
/// ```rust,ignore
43+
/// #[ram]
44+
/// static BUFFER_DATA: [u8; 3] = [0, 1, 2];
45+
/// #[ram]
46+
/// static BUFFER: &[u8] = &BUFFER_DATA;
47+
/// ```
48+
///
49+
/// or like this:
50+
///
51+
/// ```rust,ignore
52+
/// #[ram]
53+
/// static BUFFER: &[u8] = {
54+
/// #[ram]
55+
/// static DATA: [u8; 3] = [0, 1, 2];
56+
/// DATA.as_slice()
57+
/// };
58+
/// ```
59+
///
60+
/// If the slice value is [`Copy`], the attribute can do this automatically:
61+
/// ```rust,ignore
62+
/// #[ram(copy)]
63+
/// static BUFFER: [u8; 3] = &[0, 1, 2];
64+
/// ```
65+
#[proc_macro_attribute]
66+
pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
67+
ram::ram(args.into(), input.into())
68+
.unwrap_or_else(|err| err.to_compile_error().into())
69+
.into()
70+
}
71+
72+
/// This attribute forwards its arguments to `cfg`, and if the `doc-cfg` feature of this
73+
/// crate is enabled, it will emit a `doc(cfg(...))` attribute as well.
74+
///
75+
/// # Example
76+
///
77+
/// For rustdoc to document that a piece of code is only available when a specific condition is met,
78+
/// one would have to write (because it is nightly-only):
79+
///
80+
/// ```rust,ignore
81+
/// pub enum ZeroCrossMode {
82+
// PositionZero,
83+
// NegativeZero,
84+
// NegativePosition,
85+
// PositiveNegative,
86+
// #[cfg(esp_idf_version_at_least_5_4_0)]
87+
// #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_4_0)))]
88+
// Invalid,
89+
// }
90+
/// ```
91+
///
92+
/// with this attribute, one can shorten it to:
93+
///
94+
/// ```rust,ignore
95+
/// pub enum ZeroCrossMode {
96+
// PositionZero,
97+
// NegativeZero,
98+
// NegativePosition,
99+
// PositiveNegative,
100+
// #[dcfg(esp_idf_version_at_least_5_4_0)]
101+
// Invalid,
102+
// }
103+
/// ```
104+
///
105+
/// The macro will then expand to the following if the `doc-cfg` feature is enabled:
106+
///
107+
/// ```rust,ignore
108+
/// pub enum ZeroCrossMode {
109+
/// PositionZero,
110+
/// NegativeZero,
111+
/// NegativePosition,
112+
/// PositiveNegative,
113+
/// #[cfg(esp_idf_version_at_least_5_4_0)]
114+
/// #[doc(cfg(esp_idf_version_at_least_5_4_0))]
115+
/// Invalid,
116+
/// }
117+
/// ```
118+
///
119+
/// and to the following if the `doc-cfg` feature is not enabled:
120+
///
121+
/// ```rust,ignore
122+
/// pub enum ZeroCrossMode {
123+
/// PositionZero,
124+
/// NegativeZero,
125+
/// NegativePosition,
126+
/// PositiveNegative,
127+
/// #[cfg(esp_idf_version_at_least_5_4_0)]
128+
/// Invalid,
129+
/// }
130+
/// ```
131+
#[proc_macro_attribute]
132+
pub fn dcfg(args: TokenStream, input: TokenStream) -> TokenStream {
133+
dcfg::dcfg(args.into(), input.into())
134+
.unwrap_or_else(|err| err.to_compile_error().into())
135+
.into()
136+
}

0 commit comments

Comments
 (0)