Ubuntu(WSL)上からWindows(msvc)のBevyアプリケーションのビルド&実行

WSL上のUbuntu24.04からWindows向けbevyアプリケーションをビルドし、実行するまでのなんやかや

ここでは Unofficial Bevy Cheat Book の下記にある手順をベースに、いくつかの改善や問題への対策を記録しておく。 https://bevy-cheatbook.github.io/setup/cross/linux-windows.html

x86_64-pc-windows-msvc を使うパターンの話。 x86_64-pc-windows-gnu を使う場合はいろいろ異なる。

出くわす問題:

  • ビルド環境作り
    • 開発環境の引っ越し時苦労しないよう、リポジトリ内に手順や設定を詰め込んでおく
  • target/debug下のwindowsのbevyアプリとassetsパス
    • ビルド成果物のexeは target/下に配置されるが、 assets/ はexeと同じ位置にコピーされるわけではない。 bevy はデフォルトでは同ディレクトリの assets/ にアクセスするため、exe実行に際してなんらかの対処が必要になる。

ビルド環境作り

リンカが必要(だったはず)。 lld使う場合: lldインストール( apt install lld )して .cargo/config.toml に下記書いておけば cargo build --target x86_64-pc-windows-msvc としたとき適用される。

[target.x86_64-pc-windows-msvc]
linker = "lld-link"

リンク対象となるWindows SDK のインストールを行う必要がある。 その際 Microsoftのライセンスを受け入れる必要あり。

インストールは xwin を使うと楽なので、これを使うことにする。しかし xwin のインストール、 xwin による SDKインストール後SDKのパスをリンカに渡せるようにする、などの必要がある。

ここでは build.rs を用いて半自動化する。以下は Geminiくんに作ってもらった build.rs

  • XWIN_CACHE_DIR が設定されていない場合、インストール手順を表示して終了する。
    • 手順通りやればSDKがインストールされる。インストール先はユーザ指定(例: /home/<user>/.xwin)可能とし、ユーザが XWIN_CACHE_DIR にパスを格納する前提としている。
  • XWIN_CACHE_DIR をもとに リンカに必要なパス設定を行う。
    • 一応パスが不正ならWarning表示( panic! の方がよいかも?)
// build.rs
fn main() {
    let target = std::env::var("TARGET").unwrap_or_default();

    // Windows MSVC ターゲットのみ対象
    if target.contains("msvc") {
        // 1. 環境変数のチェック
        let xwin_cache_env = "XWIN_CACHE_DIR";
        let xwin_path = match std::env::var(xwin_cache_env) {
            Ok(path) => path,
            Err(_) => {
                // 環境変数が設定されていない場合、エラーメッセージを表示して終了
                panic!(
                    "\n\n[Error] xwin のパスが設定されていません。\n\
                    以下の手順でセットアップを行ってください:\n\n\
                    1. xwin をインストール (未実行の場合):\n\
                       cargo install xwin\n\n\
                    2. MSVC SDK をダウンロード・展開:\n\
                       xwin --accept-license splat --output /path/to/xwin-data\n\n\
                    3. 環境変数をセット:\n\
                       export {}=/path/to/xwin-data\n\n\
                    注意: /path/to/xwin-data は絶対パスを指定してください。\n",
                    xwin_cache_env
                );
            }
        };

        // 2. ターゲットアーキテクチャの判定
        let arch = if target.contains("x86_64") {
            "x86_64"
        } else if target.contains("aarch64") {
            "aarch64"
        } else {
            "x86"
        };

        // 3. リンカパスの指定
        // xwin splat 後のディレクトリ構造 (sdk/lib/um, sdk/lib/ucrt, crt/lib)
        let lib_dirs = [
            format!("{}/sdk/lib/um/{}", xwin_path, arch),
            format!("{}/sdk/lib/ucrt/{}", xwin_path, arch),
            format!("{}/crt/lib/{}", xwin_path, arch),
        ];

        for dir in &lib_dirs {
            // ディレクトリの存在チェック(オプション)
            if !std::path::Path::new(dir).exists() {
                println!("cargo:warning=Directory not found: {}", dir);
            }
            println!("cargo:rustc-link-search=native={}", dir);
        }

        // 変更を検知するための再ビルドトリガー
        println!("cargo:rerun-if-env-changed={}", xwin_cache_env);
    }
}

以上の準備後、 cargo build --target x86_64-pc-windows-msvc にてビルド可能になる。

target/debug下のwindowsのbevyアプリとassetsパス

ビルド成果物のexeは target/下に配置されるが、 assets/ はexeと同じ位置にコピーされるわけではない。 bevy はデフォルトでは同ディレクトリの assets/ にアクセスするため、exe実行に際してなんらかの対処が必要になる。

実行時に exe を assets/ と同じディレクトリへコピーすることでも実行可能だが、 exeのサイズはそれなりに(MSVCなら150Mbytesくらい、GNUなら1GBytes超えたはず)大きくなるため避けたい。

AssetPlugin はアセットのディレクトリパスを指定可能 ( file_path ) であるため、 実行時にパスを書き換えられるようにする方法がある。

下記は環境変数 ASSETS_DIR が設定されている場合、 そのパスを file_path に設定するコード例。

fn main() {
    let asset_root_path = std::env::var("ASSETS_DIR").unwrap_or("assets".into());
    App::new()
        .add_plugins((
            DefaultPlugins.set(AssetPlugin {
                file_path: asset_root_path,
                ..Default::default()
            }),
        ))
    // ...
        .run();
}

これでexeの移動を行わず、代わりに assets パスを指定することでに実行可能になる。

例えばcargo workspace 下のプロジェクトappのルートディレクトリにてdevelopmentビルド→実行するコマンド例。

$ cargo build --target x86_64-pc-windows-msvc
$ ASSETS_DIR="$(pwd)/assets" WSLENV=ASSETS_DIR/p ../target/x86_64-pc-windows-msvc/debug/app.exe

blake3

bevy 0.12からの問題

下記のようなエラーが起こった場合、 blakc3featurepure を指定することで回避できる。

warning: blake3@1.8.3: GNU compiler is not supported for this target
warning: blake3@1.8.3: GNU compiler is not supported for this target
warning: blake3@1.8.3: The C compiler "cc" does not support /arch:AVX512.
warning: blake3@1.8.3: GNU compiler is not supported for this target
error: failed to run custom build command for `blake3 v1.8.3`

Caused by:
  process didn't exit successfully: `/home/mori/repos/bevy/learn-shader/target/debug/build/blake3-e4e57048ce5a5c89/build-script-build` (exit status: 1)
  --- stdout
  cargo:rustc-check-cfg=cfg(blake3_sse2_ffi, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_sse2_rust, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_sse41_ffi, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_sse41_rust, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_avx2_ffi, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_avx2_rust, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_avx512_ffi, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_neon, values(none()))
  cargo:rustc-check-cfg=cfg(blake3_wasm32_simd, values(none()))
  cargo:rerun-if-env-changed=CARGO_FEATURE_PURE
  cargo:rerun-if-env-changed=CARGO_FEATURE_NO_NEON
  OUT_DIR = Some(/home/mori/repos/bevy/learn-shader/target/x86_64-pc-windows-msvc/debug/build/blake3-b69351efbd66771c/out)
  TARGET = Some(x86_64-pc-windows-msvc)
  CARGO_ENCODED_RUSTFLAGS = Some()
  VCINSTALLDIR = None
  HOST = Some(x86_64-unknown-linux-gnu)
  CC_x86_64-pc-windows-msvc = None
  CC_x86_64_pc_windows_msvc = None
  TARGET_CC = None
  CC = None
  CROSS_COMPILE = None
  RUSTC_LINKER = Some(lld-link)
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
  RUSTC_WRAPPER = None
  cargo:warning=GNU compiler is not supported for this target
  OUT_DIR = Some(/home/mori/repos/bevy/learn-shader/target/x86_64-pc-windows-msvc/debug/build/blake3-b69351efbd66771c/out)
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
  CRATE_CC_NO_DEFAULTS = None
  TARGET = Some(x86_64-pc-windows-msvc)
  CARGO_CFG_TARGET_FEATURE = Some(cmpxchg16b,fxsr,sse,sse2,sse3)
  HOST = Some(x86_64-unknown-linux-gnu)
  CFLAGS = None
  TARGET_CFLAGS = None
  CFLAGS_x86_64_pc_windows_msvc = None
  CFLAGS_x86_64-pc-windows-msvc = None
  OPT_LEVEL = Some(0)
  cargo:warning=GNU compiler is not supported for this target
  CRATE_CC_NO_DEFAULTS = None
  DEBUG = Some(true)
  CARGO_CFG_TARGET_FEATURE = Some(cmpxchg16b,fxsr,sse,sse2,sse3)
  CFLAGS = None
  TARGET_CFLAGS = None
  CFLAGS_x86_64_pc_windows_msvc = None
  CFLAGS_x86_64-pc-windows-msvc = None
  cargo:warning=The C compiler "cc" does not support /arch:AVX512.
  cargo:rerun-if-env-changed=BLAKE3_CI
  cargo:rerun-if-env-changed=CARGO_FEATURE_PREFER_INTRINSICS
  cargo:rerun-if-env-changed=CARGO_FEATURE_PURE
  cargo:rustc-cfg=blake3_sse2_ffi
  cargo:rustc-cfg=blake3_sse41_ffi
  cargo:rustc-cfg=blake3_avx2_ffi
  OUT_DIR = Some(/home/mori/repos/bevy/learn-shader/target/x86_64-pc-windows-msvc/debug/build/blake3-b69351efbd66771c/out)
  OPT_LEVEL = Some(0)
  TARGET = Some(x86_64-pc-windows-msvc)
  CARGO_ENCODED_RUSTFLAGS = Some()
  VCINSTALLDIR = None
  HOST = Some(x86_64-unknown-linux-gnu)
  CC_x86_64-pc-windows-msvc = None
  CC_x86_64_pc_windows_msvc = None
  TARGET_CC = None
  CC = None
  CROSS_COMPILE = None
  RUSTC_LINKER = Some(lld-link)
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
  RUSTC_WRAPPER = None
  cargo:warning=GNU compiler is not supported for this target
  CRATE_CC_NO_DEFAULTS = None
  DEBUG = Some(true)
  CARGO_CFG_TARGET_FEATURE = Some(cmpxchg16b,fxsr,sse,sse2,sse3)
  CFLAGS = None
  TARGET_CFLAGS = None
  CFLAGS_x86_64_pc_windows_msvc = None
  CFLAGS_x86_64-pc-windows-msvc = None

  --- stderr


  error occurred in cc-rs: failed to find tool "ml64.exe": No such file or directory (os error 2)

Cargo.toml に下記のように記載する。

[dependencies.blake3]
version = "1.8"
features = ["pure"]

CARGO_FEATURE_PURE=1cargo build --target x86_64-pc-windows-msvc 時に指定することでも回避可能らしい。