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:
- Install the target’s standard library via
rustup
. - 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'
→ Runrustup 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
vsx86_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
.