The "unix" port runs in standard Unix-like environments including Linux, BSD, macOS, and Windows Subsystem for Linux.
The x86 and x64 architectures are supported (i.e. x86 32- and 64-bit), as well as ARM, MIPS, RISC-V, and LoongArch. Extending the unix port to another architecture requires writing some assembly code for the exception handling and garbage collection. Alternatively, a fallback implementation based on setjmp/longjmp can be used.
To build the unix port locally then you will need:
- git command line executable, unless you downloaded a source .tar.xz file from https://micropython.org/download/
- an appropriate GCC or Clang toolchain for your target (macOS only supports Clang)
- GNU Make
- Python 3.x
To build the default "standard" variant and configuration, then you will also need:
pkg-configtoollibffilibrary and headers
On Debian/Ubuntu/Mint and related Linux distros, you can install all these dependencies with a command like:
# apt install build-essential git python3 pkg-config libffi-dev
(See below for steps to build either a standalone or minimal MicroPython
executable that doesn't require system libffi or pkg-config.)
To set up the environment for building (not needed every time), starting from the top-level MicroPython directory:
$ cd ports/unix
$ make -C ../../mpy-cross
$ make submodules
The mpy-cross step builds the MicroPython
cross-compiler.
The make submodules step can be skipped if you didn't clone the MicroPython
source from git.
Next, to build the actual executable (still in the ports/unix directory):
$ make
Then to give it a try:
$ ./build-standard/micropython
>>> list(5 * x + y for x in range(10) for y in [4, 2, 1])
Use CTRL-D (i.e. EOF) to exit the shell.
Learn about command-line options (in particular, how to increase heap size which may be needed for larger applications):
$ ./build-standard/micropython -h
To run the complete testsuite, use:
$ make test
There are other make targets to interact with the testsuite:
$ make test//int # Run all tests matching the pattern "int"
$ make test/ports/unix # Run all tests in ports/unix
$ make test-failures # Re-run only the failed tests
$ make print-failures # print the differences for failed tests
$ make clean-failures # delete the .exp and .out files from failed tests
The Unix port comes with a built-in package manager called mip, e.g.:
$ ./build-standard/micropython -m mip install hmac
or
$ ./build-standard/micropython
>>> import mip
>>> mip.install("hmac")
Browse available modules at
micropython-lib. See
Package management
for more information about mip.
The "standard" variant of MicroPython is the default. It enables most features,
including external modules interfaced using libffi. To instead build the
"minimal" variant, which disables almost all optional features and modules:
$ cd ports/unix
$ make submodules
$ make VARIANT=minimal
The executable will be built at build-minimal/micropython.
Additional variants can be found in the variants sub-directory of the port,
although these are mostly of interest to MicroPython maintainers.
By default, the "standard" variant uses pkg-config to link to the system's
shared libffi library.
It is possible to instead build a standalone MicroPython where libffi is built
from source and linked statically into the micropython executable. This is
mostly useful for embedded or cross-compiled applications.
Building standalone requires autoconf and libtool to also be installed.
To build standalone:
$ export MICROPY_STANDALONE=1
$ make submodules # fetches libffi submodule
$ make deplibs # build just the external libraries
$ make # build MicroPython itself
make deplibs causes all supported external libraries (currently only libffi)
to be built inside the build directory, so it needs to run again only after
make clean.
If you intend to build MicroPython with additional options (like
cross-compiling), the same set of options should be passed to both make deplibs and make.
To actually enable/disable use of dependencies, edit the
ports/unix/mpconfigport.mk file, which has inline descriptions of the
options. For example, to build the SSL module, MICROPY_PY_SSL should be
set to 1.
By default, builds are stripped of symbols and debug information to save size.
To build a debuggable version of the Unix port, there are two options:
- Run
make [other arguments] DEBUG=1. Note settingDEBUGalso reduces the optimisation level and enables assertions, so it's not a good option for builds that also want the best performance. - Run
make [other arguments] STRIP=. Note that the value ofSTRIPis empty. This will skip the build step that strips symbols and debug information, but changes nothing else in the build configuration.
The default compiler optimisation level is -Os, or -Og if DEBUG=1 is set.
Setting the variable COPT will explicitly set the optimisation level. For
example make [other arguments] COPT=-O0 DEBUG=1 will build a binary with no
optimisations, assertions enabled, and debug symbols.
Sanitizers are extra runtime checks supported by GCC and Clang. The CI process
supports building with the "undefined behavior" (UBSan) or "address" (ASan)
sanitizers. The script tools/ci.sh is the source of truth about how to build
and run in these modes.
Several classes of checks are disabled via compiler flags:
- In the undefined behavior sanitizer, checks based on the presence of the
non_nullattribute are disabled because the code makes technically incorrect calls likememset(NULL, 0, 0). A future C standard is likely to permit such calls. - In the address sanitizer,
detect_stack_use_after_returnis disabled. This check is intended to make sure locals in a "returned from" stack frame are not used. However, this mode interferes with various assumptions that MicroPython's stack checking, NLR, and GC rely on.
It used to be possible to create an x86 (32-bits) build on a x64 (64-bits) host
by passing MICROPY_FORCE_32BIT=1 to make. That option was retired: x86
usage has declined quite a bit in the past few years, and MicroPython now
supports at least one more mixed 32/64-bits target architecture for which
enabling MICROPY_FORCE_32BIT would make builds fail.
x86 will be treated as a cross-compilation target from now on. This means you
will need to install a suitable compiler (on Ubuntu you can install either the
gcc-i686-linux-gnu and g++-i686-linux-gnu packages for GCC, or the clang
package for Clang, for example) and pass the appropriate command arguments to
make depending on which compiler you chose.
For GCC, you will need to pass the toolchain's command prefix to the
CROSS_COMPILE command line variable.
This change is also extended to native modules, for which you may need to pass
the toolchain's command prefix to the CROSS command line variable if your
x86 compiler cannot be invoked with i686-linux-gnu-gcc.
Or, as an example:
$ printf "%s %s %s %s\n" $(lsb_release -d | cut -f 2) $(uname -m)
Ubuntu 24.04.4 LTS x86_64
$ i686-linux-gnu-gcc --version | head -n 1
i686-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
$ CROSS_COMPILE=i686-linux-gnu- make -C ports/unix
make: Entering directory '/ports/unix'
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
...
LINK build-standard/micropython
text data bss dec hex filename
723115 36104 2124 761343 b9dff build-standard/micropython
make: Leaving directory '/ports/unix'
$ file -b ports/unix/build-standard/micropython | cut -d, -f-2
ELF 32-bit LSB pie executable, Intel 80386
# `i686-linux-gnu-` is the default prefix for x86 native modules now, it has
# been explicitly mentioned here in case you want to see how to set it.
$ make -C examples/natmod/features0 ARCH=x86 CROSS=i686-linux-gnu-
make: Entering directory '/examples/natmod/features0'
GEN build/features0.config.h
CC features0.c
LINK build/features0.o
arch: EM_386
text size: 192
bss size: 0
GOT entries: 4
GEN features0.mpy
make: Leaving directory '/examples/natmod/features0'
$ cd examples/natmod/features0 && ../../../ports/unix/build-standard/micropython
MicroPython v1.29.0-preview.490.gb4c58f7ba5.dirty on 2026-07-04; linux [GCC 13.3.0] version
Type "help()" for more information.
>>> import features0
>>> features0.factorial(10)
3628800For Clang, you will need need to pass both the name of the compiler to invoke
as the CC command line variable and the extra command line arguments needed
to let Clang know you are building an i686 binary as the CFLAGS_EXTRA and
LDFLAGS_EXTRA command line variables (usually
--target=i686-unknown-linux-gnu).
For native modules, since linking is not done by the compiler, only the CC
and CFLAGS_EXTRA arguments are needed.
Or, as an example:
$ printf "%s %s %s %s\n" $(lsb_release -d | cut -f 2) $(uname -m)
Ubuntu 24.04.4 LTS x86_64
$ clang --version | head -1
Ubuntu clang version 18.1.3 (1ubuntu1)
$ clang -print-targets | grep -i 32-bit.x86
x86 - 32-bit X86: Pentium-Pro and above
$ make -C ports/unix CC=clang CFLAGS_EXTRA="--target=i686-unknown-linux-gnu" LDFLAGS_EXTRA='--target=i686-unknown-linux-gnu'
make: Entering directory '/ports/unix'
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
...
LINK build-standard/micropython
text data bss dec hex filename
836241 34748 2052 873041 d5251 build-standard/micropython
make: Leaving directory '/ports/unix'
$ file -b ports/unix/build-standard/micropython | cut -d, -f-2
ELF 32-bit LSB pie executable, Intel 80386
$ make -C examples/natmod/features0 ARCH=x86 CC=clang CFLAGS_EXTRA="--target=i686-unknown-linux-gnu"
make: Entering directory '/examples/natmod/features0'
GEN build-x86/features0.config.h
CC features0.c
LINK build-x86/features0.o
arch: EM_386
text size: 180
bss size: 0
GOT entries: 2
GEN features0.mpy
make: Leaving directory '/examples/natmod/features0'
$ cd examples/natmod/features0 && ../../../ports/unix/build-standard/micropython
MicroPython v1.29.0-preview.490.gb4c58f7ba5.dirty on 2026-07-04; linux [Clang 18.1.3] version
Type "help()" for more information.
>>> import features0
>>> features0.factorial(10)
3628800