avr-toolchain-wasi
The AVR toolchain as WebAssembly. avr-as, avr-ld, avr-objcopy, cc1 and
cc1plus built for wasm32-wasip1 and driven from Python through
wasmtime — one architecture-independent wheel instead
of a native build per platform.
pip install pymcu-avr-toolchain-wasi
Built for PyMCU, which compiles Python to native microcontroller code.
Why
Shipping a native toolchain means building and maintaining it for every platform
a user might have. In practice that is where the time goes, and none of it is
the compiler's fault: Rosetta 2 missing on a new Mac, MSYS2's config.guess
making a native build look like a cross, uname -m saying x86_64 while
file says x86-64, GCC 14 sources refusing to compile against a 2026
libstdc++. Six consecutive failed attempts on Windows alone.
The same modules run everywhere wasmtime does. That class of problem disappears.
| native, per platform | this | |
|---|---|---|
| Artifacts to build | 5 | 0 |
| Wheel | 70.7 MB | 22.9 MB |
| Installed | 226 MB | 105 MB |
| Platforms | 5, each verified separately | anywhere wasmtime runs |
It is also faster in real use — 0.27 s against 0.51 s for 53 builds — because
the module is compiled once and each build is a fresh instance, with no
fork/exec per tool.
Verified, not assumed
Every firmware is compared to the native toolchain's output by sha256, not by whether the build succeeded:
Linux x86_64 plain 53/53 FFI 6/6 TOTAL 59/59
Linux aarch64 plain 53/53 FFI 6/6 TOTAL 59/59
macOS arm64 plain 53/53 FFI 6/6 TOTAL 59/59
Windows AMD64 plain 53/53 FFI 6/6 TOTAL 59/59
Windows ARM64 plain 53/53 FFI 6/6 TOTAL 59/59
The same five modules on all five runners. The FFI cases cover @extern with C
sources and one C++ case (class, method, template, extern "C"). Also verified
across 20 chips and the five library subdirectories, and against binutils 2.45.1
and a libgcc from GCC 9.5.0 — neither changes a byte of firmware.
macOS Intel is not verified. GitHub retired the macos-13 runner, so there
is nowhere to measure it. wasmtime publishes a macosx_10_13_x86_64 wheel, so
there is reason to expect it works, but that is an expectation and not a
measurement.
How it works
WASI has no fork/exec, so the GCC driver cannot be ported — it is replaced.
The exact command line each tool needs is taken from avr-gcc -### rather than
reconstructed by hand, and tabulated per chip:
avr-as -mmcu=<chip> -mno-skip-bug firmware.asm -o firmware.o
avr-ld -m <emulation> -Tdata 0x<800000+RAMSTART> --relax firmware.o
-L<libgcc/libdir> -L<avr/lib/libdir> --start-group -lgcc -lm --end-group
objcopy -O ihex -R .eeprom firmware.elf firmware.hex
Deriving those tables from the compiler instead of from the chip's name is not pedantry. It caught three defects that would have produced broken firmware silently:
atmega1280isavr51, notavr6as the family name suggests.attiny13,13a,24,25and2313areavr25for the linker but must link againstavr25/tiny-stack, the 8-bit stack pointer variant. The wrong one emits code that writesSPHon parts that have noSPH.- A hand-maintained chip list had drifted from the one the product supports. The table is now generated from PyMCU's own chip directory, so it cannot.
Building
The modules are built in a container and the recipe is reproducible:
docker build --output type=local,dest=./dist-docker -f Dockerfile . # as/ld/objcopy
docker build --output type=local,dest=./dist-ffi -f Dockerfile.cc1 . # cc1/cc1plus
What is reproducible is the firmware: modules built on macOS and in the
container differ in size yet produce identical .hex. Bit-identical modules
across build hosts is a separate problem and is not claimed here.
Build products, SDKs and caches are deliberately absent from this repository — CI assembles the wheel from the verified payload attached to each release, so what reaches PyPI is what was measured, not a rebuild nobody checked.
Harnesses
python difftest.py # 53 plain examples, native vs wasi
python ffi_difftest.py # the FFI path
python chip_difftest.py # 20 chips across 5 library subdirectories
python driver_difftest.py # through PyMCU's own driver
Limits
- Programmers stay native.
avrdudetalks to a serial port; WASI does not. - C++ with global constructors does not link, and does not link natively
either: it emits
__do_global_ctorsand__do_clear_bss, which PyMCU's-nostartfileslink and its own linker script do not provide. That is a linker-script matter, not a toolchain one. - Roughly 2× slower than native on inputs of megabytes — far above anything that fits in an AVR.
Licence
GPL-3.0-or-later, as the GNU toolchain it is built from.
Metadata
Release files for pymcu-avr-toolchain-wasi 15.2.0.post1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pymcu_avr_toolchain_wasi-15.2.0.post1-py3-none-any.whl | Python 3 | none | any | Details |
Release files / pymcu_avr_toolchain_wasi-15.2.0.post1-py3-none-any.whl
| Download URL | pymcu_avr_toolchain_wasi-15.2.0.post1-py3-none-any.whl |
|---|---|
| Size | 24.0 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
59d9bd27e0eaa23770a6392499fd560cf32faf4becc4a0b9dbc24b4b07badb69
|
|
BLAKE2b-256 checksum How to use checksums |
b7b2885f287c6d9a694ec40681e08d0140f603cf0b0831097a00141134ef032a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.
Transparency log