cargo-xwin cross compiles Rust to Windows MSVC from Linux
Cross compile Cargo project to Windows MSVC target with ease
At a glance
- What is it?
- cargo-xwin is a Cargo subcommand that cross compiles a Rust project to the Windows MSVC target without a Windows machine or a Visual Studio install, by downloading the Microsoft CRT and Windows SDK with xwin and driving clang-cl or clang. It suits a Linux CI runner that has to ship a Windows binary, and the SDK download and caching are the part that needs thought.
- Who is it for?
- Adopt cargo-xwin when your Linux or macOS build machine has to produce Windows MSVC binaries and you do not want to run a Windows host or install Visual Studio on it. Do not adopt it if your project depends on a build step that needs a real Windows environment, since the tool supplies headers and libraries rather than the OS.
- 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 45 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A Cargo subcommand that installs the Windows SDK for you
cargo-xwin cross compiles a Cargo project to the Windows MSVC target with ease, using either xwin or windows-msvc-sysroot to obtain the necessary pieces. The tool was formerly called cargo-xwinbuild, which is a clue about its history, since the original name suggests the goal was always to package a build environment rather than just a linker. The problem it solves is specific. Rust's MSVC target needs the Microsoft C runtime and the Windows SDK headers and import libraries, and on a non Windows machine you do not have them. The conventional answers are to run the build inside a Windows CI runner, to install Visual Studio build tools under Wine, or to maintain a sysroot by hand. cargo-xwin takes the third path and automates it, downloading the CRT and SDK from Microsoft's own distribution endpoints and laying them out where the LLVM toolchain can find them. The README is explicit that using the software means you are consented to accept the Microsoft licence linked from the header.
Two backends, and the environment variables that tune them
The customisation table is the practical reference for the tool, and it maps environment variables one to one onto CLI options. The cross compiler defaults to clang-cl and can also be clang, selected with XWIN_CROSS_COMPILER or --cross-compiler. The architectures default to x86_64,aarch64 with x86, x86_64, aarch and aarch64 available, through XWIN_ARCH or --xwin-arch. The variant defaults to desktop with onecore and spectre available. The Windows version defaults to 17 and accepts a major version of 15, 16 or 17, or a full major.minor. The SDK version and the CRT version each default to the latest. There are also switches for including the Active Template Library, for including debug libraries and debug symbols, both false by default, for the cache directory, and for the number of HTTP retries when downloading, which defaults to 3. The version and architecture choices are the ones that matter in practice, because a build targeting an older Windows release needs a matching SDK, and a binary built against the newest SDK will not run on an older Windows.
Caching the SDK is what makes it viable in CI
The pre-caching section is short and it is the feature that turns cargo-xwin from a curiosity into something usable in a pipeline. You can pre-download and cache the MSVC CRT and Windows SDK for offline builds, with one command per backend:
# Cache Microsoft CRT and Windows SDK (for clang-cl backend)
cargo xwin cache xwin
# Cache windows-msvc-sysroot (for clang backend)
cargo xwin cache windows-msvc-sysrootThe README says this is useful for Docker images or CI/CD pipelines where you want to cache dependencies ahead of time. That is the whole reason to use a subcommand rather than a script: the download is a known, cacheable artefact rather than an implicit side effect of a build, and once it is in the image layer the build becomes hermetic. The cache command also documents an asymmetry between the backends, since the clang-cl backend supports cache paths containing spaces while the cache command for the sysroot is a separate target. The two backends are not interchangeable, which is worth knowing before you script around them.
The Cargo configuration edge cases
Three notes in the README describe limitations that would otherwise cost an afternoon. First, if you configure target.<triple>.rustflags in Cargo configuration, you have to use an array, for example rustflags = ["-C", "target-feature=+crt-static"], because Cargo cannot merge its space-preserving array override with string-valued rustflags. Second, native compiler flags use shell quoting through CC_SHELL_ESCAPED_FLAGS=1. Third, and this is the one with a real consequence, cargo xwin env cannot export flags that require Cargo --config arguments, including library paths containing spaces, so in that case you use cargo xwin build, check, or another build command directly. The pattern behind all three is the same. Cargo's configuration layer and the shell's quoting layer both have ideas about how flags are passed, and the tool has to pick one convention per case rather than trying to be clever in all of them. If you are cross compiling a crate that sets rustflags in its own config, or that has a dependency with a path containing spaces, read these three notes before debugging.
Running the Windows binary without a Windows machine
There are two ways to get from a built binary to a running program, and the README covers both. With wine installed you can run the tests with the cargo xwin test command, for example cargo xwin test --target x86_64-pc-windows-msvc. If you would rather run Windows executables directly in WSL without using wine, you set the runner environment variable, and the example given is:
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUNNER="/usr/bin/env" cargo xwin run --target x86_64-pc-windows-msvcThe WSL route is the more interesting one because it uses the Windows kernel underneath, so the binary is executed natively rather than emulated, and the runner variable pointing at /usr/bin/env is what tells Cargo to invoke the program without an extra layer in between. The Docker image the project publishes takes the other route, since it has wine pre-installed in addition to cargo-xwin and Rust, and the example build command for an x86_64 Windows target is docker run --rm -it -v $(pwd):/io -w /io messense/cargo-xwin cargo xwin build --release --target x86_64-pc-windows-msvc. The wine route is slower than WSL but works on any Linux, which is why both exist.
CMake dependencies get a generated toolchain
A category of Rust projects breaks naive cross compilation, and the README addresses it directly. Some Rust crates use the cmake crate to build C or C++ dependencies, and cargo-xwin will generate a CMake toolchain file automatically to make cross compilation work out of the box. The one condition is that ninja is required to enable CMake support, which is why the Python package definition in the repository declares a dependency on ninja and the Dockerfile installs cmake and ninja-build alongside clang, llvm and winehq-staging. This is a quiet but significant feature. Without a generated toolchain file, cmake would look for a Windows compiler on a Linux machine and fail in a confusing way, and the fix is normally a long manual configuration. Here it is one command. It also means the constraint propagates, since a crate whose CMake dependency cannot find a Windows compiler will fail during the build step rather than during the Rust compile, and the failure will point at cmake rather than at the cross toolchain.
How the tool is built and distributed
The packaging is unusual enough to be worth noting, because cargo-xwin ships in three different forms. It is a Rust program, with the package at version 0.23.1 on edition 2024 and a rust-version of 1.89, and the manifest declares MIT as the licence even though the repository records a custom licence classification, so the LICENSE file is the authority. It is also a Python package, built with maturin, with bindings set to bin and a dependency on ninja, which is what makes pip install cargo-xwin work and lets the tool be distributed to environments where a Rust toolchain is awkward to install. The third form is the Docker image messense/cargo-xwin, whose Dockerfile is a three stage build on rust:1.89.0 that compiles dependencies separately for layer caching, then builds the binary, then assembles a runtime image with the Wine apt repository added, i386 architecture enabled, and clang, llvm, winehq-staging, cmake and ninja-build installed before the caches are cleared. The Rust dependency list tells you what the tool actually does, with xwin for the SDK download, ureq with gzip, json and socks-proxy support for the HTTP side, cargo-config2 and cargo-options for reading Cargo's configuration, indicatif for progress bars, and clap with the env feature so the environment variables in that table are first class options.
Editorial conclusion
Adopt cargo-xwin when your Linux or macOS build machine has to produce Windows MSVC binaries and you do not want to run a Windows host or install Visual Studio on it. Do not adopt it if your project depends on a build step that needs a real Windows environment, since the tool supplies headers and libraries rather than the OS. Verify first that your Rust version is at least 1.89 and that the edition of your project is one cargo-xwin supports, that clang and the llvm-tools component are installed, and that you cache the CRT and SDK in your CI image so the build does not download them on every run.
Frequently asked questions
What do I need installed to use cargo-xwin?
clang, and on macOS that means brew install llvm. For assembly dependencies you need the llvm-tools component via rustup component add llvm-tools, or an LLVM installation. A full LLVM installation is recommended to avoid possible issues. You also need the Rust Windows MSVC target, for example rustup target add x86_64-pc-windows-msvc.
How do I install cargo-xwin?
With cargo install --locked cargo-xwin, or with pip install cargo-xwin, which works because the project ships a Python package built with maturin. There is also a Docker image, messense/cargo-xwin, with wine pre-installed alongside cargo-xwin and Rust.
How do I cache the Windows SDK for CI?
Run cargo xwin cache xwin to cache the Microsoft CRT and Windows SDK for the clang-cl backend, or cargo xwin cache windows-msvc-sysroot for the clang backend. The README describes this as useful for Docker images or CI/CD pipelines where you want to cache dependencies ahead of time.
Can cargo-xwin build crates that depend on C or C++ libraries?
Yes, if ninja is installed. Some Rust crates use the cmake crate to build C/C++ dependencies, and cargo-xwin generates a CMake toolchain file automatically so cross compilation works out of the box. Ninja is required to enable that support.
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/rust-cross-cargo-xwin)Was this useful?