How to Fix a Cross Compile a Rust Application from Linux to Windows

I was working on a small Rust project the other day when I decided I wanted to generate a Windows .exe but I was on Linux.
At first, I thought, “How hard could it be, Just tell rustc to target Windows and done.” Turns out, there’s a bit more to it.

Let me walk you through the exact process I used what went wrong at first, how I fixed it, and how I built something a bit more interesting.

The Minimal Program

Here where I started:

fn main() {
    println!("Hello, and bye.");
}

A simple “Hello, and bye.” nothing fancy.
Then I tried running:

rustc --target=i686_pc_windows_gnu -C linker=i686-w64-mingw32-gcc main.rs

And I got:

error: can't find crate for `std`

Why That Error Happen

Rust has a standard library (std) for each target.
When you cross-compile, rustc needs the standard library for your target platform, not your host system.

If you’ve never installed the Windows std for Rust, it simply doesn’t exist on your system and rustc can’t build even the smallest program.

The Fix

The solution was pretty straightforward:

  1. Install the target’s standard library via rustup.
  2. Use a Windows linker (MinGW-w64) to actually link the final .exe.

Step by Step Setup (One-Time)

I chose the GNU Windows target:
x86_64-pc-windows-gnu (64-bit) or i686-pc-windows-gnu (32-bit).

Install Rustup

curl https://sh.rustup.rs -sSf | sh
source ~/.cargo/env

Add the Windows target

# 64-bit
rustup target add x86_64-pc-windows-gnu

# Optional: 32-bit
rustup target add i686-pc-windows-gnu

Install MinGW-w64

Debian/Ubuntu:

sudo apt update
sudo apt install mingw-w64

Fedora:

sudo dnf install mingw64-gcc mingw32-gcc

Arch:

sudo pacman -S mingw-w64-gcc

Building with Cargo

I switched to Cargo instead of raw rustc way simpler.

cargo new hello-windows
cd hello-windows

src/main.rs:

fn main() {
    println!("Hello, and bye.");
}

.cargo/config.toml:

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"

[target.i686-pc-windows-gnu]
linker = "i686-w64-mingw32-gcc"

Build for Windows:

cargo build --target x86_64-pc-windows-gnu --release

That gives me:

target/x86_64-pc-windows-gnu/release/hello-windows.exe

No more missing std errors.

A More Interesting Practice App

Once I got the basic .exe building, I decided to make it do a bit more.

src/main.rs:

use std::env;
use std::fs;
use std::path::MAIN_SEPARATOR;

fn main() {
    // 1) Basic greeting
    println!("Hello, and bye.");

    // 2) Show compile-time target info
    println!("Target OS: {}", std::env::consts::OS);
    println!("Target ARCH: {}", std::env::consts::ARCH);
    println!("Path separator: '{}'", MAIN_SEPARATOR);

    // 3) Optional name from args
    let name = env::args().nth(1).unwrap_or_else(|| "friend".to_string());
    println!("Nice to meet you, {name}!");

    // 4) Write a file with platform-specific line endings
    let message = if cfg!(target_os = "windows") {
        "This file uses CRLF line endings.\r\n"
    } else {
        "This file uses LF line endings.\n"
    };

    let out = format!("Hello {name}! {message}");
    fs::write("greeting.txt", out).expect("Failed to write file");

    // 5) Tip based on platform
    print_platform_tip();
}

#[cfg(target_os = "windows")]
fn print_platform_tip() {
    println!("Tip: You're building for Windows (GNU). Your binary is a .exe.");
}

#[cfg(not(target_os = "windows"))]
fn print_platform_tip() {
    println!("Tip: You're cross-compiling to Windows from Linux.");
}

Testing It

Run on Linux:

cargo run

Cross-compile for Windows:

cargo build --target x86_64-pc-windows-gnu --release

hello-windows.exe to a Windows machine (or run with Wine) and it works perfectly.
greeting.txt even uses Windows-style CRLF endings.

Try:

cargo run --target x86_64-pc-windows-gnu -- "Ada"

and it’ll greet Ada.

Common Pitfalls

  • can't find crate for 'std' → Run rustup target add ….
  • linker 'x86_64-w64-mingw32-gcc' not found → Install MinGW-w64.
  • crt2.o: No such file → Reinstall MinGW-w64.
  • Wrong architecture → Match linker and target (i686 vs x86_64).

Final Thought

Cross-compiling Rust from Linux to Windows isn’t as intimidating as it looks. The key is making sure Rust has the std for your target and your system has a linker that can build Windows binaries. Once you have that, you can produce .exe files straight from Linux no Windows VM needed. Next time I’ll probably try integrating this into a CI pipeline so every push automatically builds Windows, Linux, and macOS versions of my app. But for now, I’m just happy my Linux box can speak .exe.

Related blog posts