Changes in Rust macro re-exporting
May 6, 2018 · 6 minute readRustembedded
The changes in recent Rust
This commit to the Rust ecosystem removed the previous macro re-export functionality which is now subsumed by a cleaner mechanism which is also going to be stabilised in the end, this is tracked by this issue.
Unfortunately the previous re-export mechanism was used quite a lot in the
embedded field, causing quite a few crates to break when compiled with rustc
versions from the beginning of May 2018, which is especially frustrating when
pulling in broken dependencies since those can not be easily (and permanently)
addresses locally. While some HAL implementations, Board support crates and
drivers may also be affected, the most significate breakage can be found in
every peripheral register crate created by the popular svd2rust
tool.
This post is trying to explain which changes are required to fix broken crates and how to temporarily work around problems with foreign crates.
The signs of the problem
Spotting the issue is rather simple. Compile some code and if the compiler barfs, then either the code itself or one of its pulled in dependencies are affected:
cargo build --release --examples
Updating git repository `https://github.com/japaric/stm32f103xx-hal`
Updating registry `https://github.com/rust-lang/crates.io-index`
Compiling num-traits v0.2.0
Compiling semver-parser v0.7.0
Compiling libc v0.2.36
Compiling cortex-m v0.3.1
Compiling vcell v0.1.0
Compiling cortex-m-rt v0.4.0
Compiling bare-metal v0.1.1
Compiling aligned v0.1.1
Compiling cortex-m v0.4.3
Compiling r0 v0.2.2
Compiling untagged-option v0.1.1
Compiling stm32f103xx-hal v0.1.0 (https://github.com/japaric/stm32f103xx-hal#330c9044)
Compiling nb v0.1.1
Compiling cast v0.2.2
Compiling numtoa v0.0.7
Compiling static-ref v0.2.1
Compiling volatile-register v0.2.0
Compiling embedded-hal v0.1.2
Compiling time v0.1.39
Compiling semver v0.9.0
Compiling stm32f103xx v0.9.1
Compiling num-integer v0.1.36
Compiling num-iter v0.1.35
Compiling num v0.1.42
Compiling chrono v0.4.0
Compiling rustc_version v0.2.2
Compiling cortex-m-rt v0.3.13
Compiling stm32f103xx v0.8.0
error[E0557]: feature has been removed
--> /Users/egger/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f103xx-0.8.0/src/lib.rs:1:106
|
1 | # ! [ cfg_attr ( feature = "rt" , feature ( global_asm ) ) ] # ! [ cfg_attr ( feature = "rt" , feature ( macro_reexport ) ) ] # ! [ cfg_attr ( feature = "rt" , feature ( used ) ) ] # ! [ doc = "Peripheral access API for STM32F103XX microcontrollers (generated using svd2rust v0.12.0)\n\nYou can find an overview of the API [here].\n\n[here]: https://docs.rs/svd2rust/0.12.0/svd2rust/#peripheral-api" ] # ! [ allow ( private_no_mangle_statics ) ] # ! [ deny ( missing_docs ) ] # ! [ deny ( warnings ) ] # ! [ allow ( non_camel_case_types ) ] # ! [ feature ( const_fn ) ] # ! [ no_std ]
| ^^^^^^^^^^^^^^
|
note: subsumed by `#![feature(use_extern_macros)]` and `pub use`
--> /Users/egger/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f103xx-0.8.0/src/lib.rs:1:106
|
1 | # ! [ cfg_attr ( feature = "rt" , feature ( global_asm ) ) ] # ! [ cfg_attr ( feature = "rt" , feature ( macro_reexport ) ) ] # ! [ cfg_attr ( feature = "rt" , feature ( used ) ) ] # ! [ doc = "Peripheral access API for STM32F103XX microcontrollers (generated using svd2rust v0.12.0)\n\nYou can find an overview of the API [here].\n\n[here]: https://docs.rs/svd2rust/0.12.0/svd2rust/#peripheral-api" ] # ! [ allow ( private_no_mangle_statics ) ] # ! [ deny ( missing_docs ) ] # ! [ deny ( warnings ) ] # ! [ allow ( non_camel_case_types ) ] # ! [ feature ( const_fn ) ] # ! [ no_std ]
| ^^^^^^^^^^^^^^
error[E0658]: The attribute `macro_reexport` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
--> /Users/egger/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f103xx-0.8.0/src/lib.rs:4:1
|
4 | #[macro_reexport(default_handler, exception)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(custom_attribute)] to the crate attributes to enable
error: aborting due to 2 previous errors
Some errors occurred: E0557, E0658.
For more information about an error, try `rustc --explain E0557`.
error: Could not compile `stm32f103xx`.
warning: build failed, waiting for other jobs to finish...
error: build failed
As can be easily seen the compiler will make a good effort showing what didn’t work and (at least partially) also shows the fix. But as can also be seen, the breakage occurs in a pulled in dependency which requires an upstream fix and can at best be worked around temporarily by messing with the cached data until the maintainer addresses the problem and uploads a fixed version.
How to address the issue?
For peripheral register crates using svd2rust
Peripheral register crates can be easily addressed by simply using a svd2rust
version 0.12.1 or later. This will change from the use of the
macro_reexport
feature to the use_extern_macros
feature which is eventually
going to be stabilised. However, the maintainer japaric already announced
in this comment
that re-exports of macros are going to be purged from svd2rust
with version
0.13.0 altogether.
For all other crates (e.g. HAL implementation, BSP or driver)
If you’re using re-exported macros in your crate, you will need to change the
code to use the new use_extern_macros
feature as indicated by the error output.
If your code read something like:
#![cfg_attr(feature = "rt", feature(macro_reexport))]
#[macro_reexport(block)]
pub extern crate nb;
then the new version would typically read:
#![cfg_attr(feature = "rt", feature(use_extern_macros))]
#[macro_use(block)]
pub extern crate nb;
pub use nb::block;
This will make the block
macro available both for local use in your crate and
for crates depending on your crate.
Temporary workaround for cached upstream crates
As you may have noticed from my initial example, cargo will fetch the crate only once and then rely on the local cache on disk to provide the source code to the dependant crate and happily print the local path on the filesystem:
...
error[E0557]: feature has been removed
--> /Users/egger/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f103xx-0.8.0/src/lib.rs:1:106
...
Hence you can easily modify the local copy to contain the change mentioned above and your code will happily compile again (unless there’s another depedency with the same issue) as long as the dependency doesn’t change to a different version.
Please make sure to notify the maintainer of such a crate about the issue so it can fixed upstream.
Get rid of re-exports altogether
As japaric suggested one could also simply get rid of all macro re-exports and always “use” directly from the original crate which results in a loss of comfort and may break dependent crates if you offered re-exported macros in the past but is guaranteed to be and stay stable for all times. Otherwise if all hell breaks lose and the this feature fails to stabilize until 1.28, such a crate might be excluded from being used with stable Rust.
In conclusion
I hope this post was a bit helpful if you were as surprised as I was about this sudden and thorough breakage of major parts of the embedded Rust ecosystem. Let’s make sure we get all the fallout addressed as quickly as possible to continue with the regular development program.