Skip to main content

TL;DR: Bevy Windows Wsl2



Here's a quick setup for cross-compiling Bevy games from WSL2 to Windows with quick iteration cycles. My 2020 laptop with Intel graphics compiles and starts both of my bevy demos in a few seconds for quick edit / try it turnarounds.

This setup leans into two things: dynamic linking bevy to make development builds quicker, and launching the Windows exe from the WSL shell. While WSL2 can do some graphics I find it flaky and this setup gets me the best of both.

Here are some of the steps. I'm assuming you have a WSL2 instance that's already able to build your Bevy 0.16 project. The main thing you'll need is xwin, which takes most of the pain out of cross-compiling to Windows.

# Install xwin for Windows SDK access
cargo install xwin cargo-xwin

# Download Windows CRT/SDK (one-time setup)
xwin --accept-license splat --output ~/.xwin

Ensure your bevy dependencies in Cargo.toml includes dynamic_linking. Mine looks like this.

[dependencies]
bevy = { version = "0.16", default-features = false, features = [
    "bevy_asset", "bevy_gilrs", "bevy_scene", "bevy_winit", 
    "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render",
    "bevy_sprite", "bevy_text", "bevy_ui", "multi_threaded", 
    "dynamic_linking"
] }

I use a build-windows.sh script to wrap up the build. This script sets RUSTFLAGS to ensure the exe's directory is included in the defaulit DLL search path.

#!/bin/bash

set -e

target="x86_64-pc-windows-msvc"
cargo xwin build --target $target

exe_path="target/$target/debug/bloblins-rust.exe"
required_dlls=$(objdump -p "$exe_path" | awk '/DLL Name/{print $3}')

sysroot=$(rustc --print sysroot)
rust_lib_dir="$sysroot/lib/rustlib/$target/lib"

for dll_name in $required_dlls; do
    # skip system DLLs
    if [[ "$dll_name" =~ ^(KERNEL32|USER32|ADVAPI32).*\.dll$ ]]; then
        continue
    fi
    
    # copy dlls to the exe directory
    if [[ -f "target/$target/debug/deps/$dll_name" ]]; then
        cp "target/$target/debug/deps/$dll_name" "target/$target/debug/"
    elif [[ -f "$rust_lib_dir/$dll_name" ]]; then
        cp "$rust_lib_dir/$dll_name" "target/$target/debug/"
    fi
done

And a simple run.sh for launching:

#!/bin/bash
cd target/x86_64-pc-windows-msvc/debug
./bloblins-rust.exe "$@"

Daily workflow looks like:

# Quick syntax check
cargo c

# Build with DLL management
./build.sh

# Run with arguments
./run.sh --headless --fps 60

# Lint before commit
cargo clippy
cargo fmt

This gets me sub-10-second iteration cycles for most changes. The key tricks are using cargo c for quick validation, automatic DLL management in the build script, and optimizing dependencies separately from my main code. Dynamic linking on Linux keeps builds fast, while the cross-compilation handles the Windows target without leaving WSL2.