MXE: a GNU Makefile that cross compiles MinGW-w64 with its own dependency graph
MXE (M cross environment)
At a glance
- What is it?
- MXE is a Makefile rather than a compiler, which builds MinGW-w64 from source and then cross compiles free libraries such as SDL and Qt against it, verifying every download by checksum and pulling dependencies automatically.
- Who is it for?
- MXE suits a Unix or macOS developer who ships Windows binaries and wants one reproducible command that builds a whole dependency stack, and a team that would rather compile from source than ship vendored binaries. It is a poor fit if you need targets other than MinGW-w64, and a poor fit if you have no patience for a source build, because the cross compiler itself is compiled from source before anything else can be cross compiled.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Makefile, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A Makefile that builds the compiler and then uses it
MXE, the M cross environment, is a GNU Makefile that compiles a cross compiler and cross compiles many free libraries such as SDL and Qt. So the deliverable is not a toolchain you download, it is a build system that produces one.
That single decision explains most of the project's character. The toolchain is MinGW-w64, and the host triplets are i686-w64-mingw32 and x86_64-w64-mingw32, with packages built as static or shared. The GCC threading libraries have winpthreads always available, and then posix as the default, with win32 supported by a limited amount of packages. GCC exception handling defaults to sjlj on i686 and seh on x86_64, with alternative schemes offered as experimental.
The listed design goals are a good summary of the trade-off space. It is designed to run on any Unix system, easy to adapt and to extend, builds many free libraries in addition to the cross compiler, and can also build just a subset of the packages while automatically building their dependencies. It downloads all needed packages and verifies them by their checksums, and is able to update the version numbers of all packages automatically.
The two goals that stand out are transparency and reproducibility. It directly uses source packages, thus ensuring the whole build mechanism is transparent, and it allows inter-package and intra-package parallel builds whenever possible. Bundling ccache to speed up repeated builds is also in the list.
The project has been in continuous development since 2007 and is used by several projects, which is worth noting for a tool of this kind, since a cross build environment is infrastructure that nobody wants to be the maintainer of. The homepage is mxe.cc, the licence file is LICENSE.md, and the last push was on 2026-10-12.
The build command, and the target syntax
You start the build with the make command, and the example in the documentation is a full cross compile of the GTK3 project to a statically linked Windows 64-bit library:
make gtk3 -j 8 MXE_TARGETS='x86_64-w64-mingw32.static'Three things in that line carry the design.
The package name comes first, gtk3, and the makefile resolves the dependency graph from there, so you do not list the dependencies yourself.
The -j 8 is a job count, and it matters more here than in a normal build because MXE supports inter-package and intra-package parallel builds whenever possible. A dependency like Qt pulls in a long chain, and building that chain serially is the difference between a coffee break and an afternoon.
The MXE_TARGETS variable selects what you are building, and the syntax is triplet followed by a dot and a library type. The triplet is one of the two MinGW-w64 hosts, and the type is static or shared, giving four combinations, and the top level Makefile builds that list out as MXE_TARGET_LIST. The default is i686-w64-mingw32.static, so a plain make with no arguments builds the 32-bit static variant.
The top level Makefile also sets a default goal of all-filtered, so a bare make does not try to build everything in the tree but the subset that matches the current target selection, and DEFAULT_MAX_JOBS is 6, which is the fallback parallelism when you do not pass -j.
The mirror chain, and why the checksum step is in the middle of it
The top level Makefile is where the download machinery lives, and it is worth reading because it shows how the project handles the network.
There are three mirrors. MXE_MIRROR is https://mirror.mxe.cc/pkg, PKG_MIRROR is https://mxe-pkg.s3.amazonaws.com, and PKG_CDN is http://d1yihgixbnrglp.cloudfront.net, and the MIRROR_SITES list orders them with a comment saying to reorder as required and ensure the final one is an http fallback.
So a build tries the primary mirror, falls back to the object storage bucket, and finally to the CDN over plain http. The http fallback is the last resort on purpose, since a mirror failure should not stop a build, but a plain http mirror also means a download on that path is not protected by TLS, which is precisely why the checksum verification is not optional.
The checksum is where the trust actually comes from. Within the MXE makefiles in the src directory, each package defines either $(PKG)_GH_CONF or $(PKG)_URL, which is what the build uses to download the package, and then the checksum of the downloaded archive file is validated, a sha256 checksum. Updating a package or updating its checksum is done through the make commands.
So the model is: MXE tells you where to get a source tarball, and tells you what its hash should be. The mirror only has to be fast and available, and it does not have to be trusted. A mirror that serves a modified archive fails the checksum check, and the build stops.
The packages directory is the src directory of the repository, one makefile per package, and the top level includes mxe.github.mk, mxe.patch.mk and mxe.updates.mk, which are the shared fragments for the GitHub release fetching, the patch application and the automated version updating.
The makefile fragments, and the DLL dependency problem
The repository is mostly makefiles, which is unusual for a project of this size and is the direct consequence of the design decision to be a Makefile.
At the top level there are the shared fragments. mxe.github.mk handles packages fetched from GitHub releases, mxe.patch.mk handles applying patches, and mxe.updates.mk is the machinery behind the automatic version updating. The ext directory holds the GNU Make Standard Library, which the top level Makefile includes, and the docs directory holds the gmsl documentation. The plugins directory and the tools directory hold the plugin system and the helper scripts, and there is a src directory with one makefile per package.
The tools directory contains the answer to a problem that anyone building Windows binaries hits quickly. There are several approaches to recursively finding DLL dependencies, listed alphabetically in the README: a go script from the gml project, pe-util which is packaged with mxe itself under src/pe-util.mk, a python script at tools/copydlldeps.py, and a shell script at tools/copydlldeps.md.
That list of four approaches tells you the problem is not solved once. The difficulty is that a shared build produces a set of DLLs with interdependencies, and to ship a working directory you need to copy the transitive closure of those DLLs, and computing it means parsing PE import tables and following the chain. A go implementation, a Python implementation, a shell implementation and a dedicated tool all exist in the ecosystem, and MXE ships three of them because the audience has different toolchains available.
The Makefile also shows the care taken with host tool detection. BUILD_CC picks gcc if it responds to --help and falls back to clang, and the same pattern applies to C++, and DATE, INSTALL, LIBTOOL and LIBTOOLIZE all prefer the GNU variant with the g prefix on macOS. The SHELL is set to bash, and ORIG_PATH strips the MXE bin directories out of the PATH so that the cross toolchain does not shadow the host compiler during a build, which is a subtle and important detail.
What the design costs
The first limitation is the target scope. The runtime is MinGW-w64 and the host triplets are i686 and x86_64, so there is no path to cross compile to macOS, Linux or Windows on ARM from this toolchain, and no path to a different C library such as musl. If your target is not a Windows x86 binary, MXE is the wrong tool.
The second limitation is the time and space cost of building a toolchain from source. The cross compiler is compiled before anything can be cross compiled, and the libraries are compiled from source as well. The project bundles ccache to mitigate repeated builds, which tells you the author expects people to rebuild often, but the first full build is a long one and it needs disk.
The third limitation is the platform list in the settings themselves. The win32 threading model is supported by a limited amount of packages, so a project that needs it will find gaps. The alternative exception handling schemes are marked experimental, so sjlj on i686 and seh on x86_64 are the ones to plan around.
The fourth is that a Makefile is the whole product. There is no command line tool, no configuration file, no manifest of what you built. You drive it with make variables and target names, and the documentation for the full usage lives on the site rather than in the repository, so an offline install of MXE means the README's short example is what you have. That is a deliberate choice for a project whose users are comfortable with make, and a poor fit for someone who is not.
The licence file is LICENSE.md, and the repository-level metadata lists the licence as a custom one that GitHub cannot classify, so read that file rather than assuming a standard identifier.
Editorial conclusion
MXE suits a Unix or macOS developer who ships Windows binaries and wants one reproducible command that builds a whole dependency stack, and a team that would rather compile from source than ship vendored binaries. It is a poor fit if you need targets other than MinGW-w64, and a poor fit if you have no patience for a source build, because the cross compiler itself is compiled from source before anything else can be cross compiled. Before adopting, read the support matrix at mxe.cc for the packages you actually need, check the threading and exception handling defaults for your triplet, and plan for the space and time the first full build takes.
Frequently asked questions
What is MXE and what does it actually build?
MXE, the M cross environment, is a GNU Makefile that compiles a cross compiler and cross compiles many free libraries such as SDL and Qt. The runtime is MinGW-w64, with host triplets i686-w64-mingw32 and x86_64-w64-mingw32, and packages built as static or shared.
How do I cross compile a project with MXE?
Use the make command with the package name, a job count and the target selection, for example make gtk3 -j 8 MXE_TARGETS='x86_64-w64-mingw32.static'. MXE resolves the dependency graph itself, builds the cross compiler first, and can also build just a subset of the packages.
How does MXE verify the packages it downloads?
Within the MXE makefiles in the src directory each package defines either $(PKG)_GH_CONF or $(PKG)_URL, and the checksum of the downloaded archive is validated as a sha256 checksum. Three mirrors are tried in order, with the last one an http fallback, so the mirror only has to be fast and available, and the checksum is what provides the trust.
How do I find the DLL dependencies of a shared MXE build?
The documentation lists several approaches: a go script, the pe-util tool packaged with MXE under src/pe-util.mk, a python script at tools/copydlldeps.py, and a shell script described at tools/copydlldeps.md.
Which threading and exception handling models does MXE use by default?
The GCC threading library winpthreads is always available, with posix as the default and win32 supported by a limited amount of packages. GCC exception handling defaults to sjlj on i686 and seh on x86_64, with alternative schemes offered as experimental.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mxe-mxe)Was this useful?