⚒️

Linux(WSL2)からTauriアプリをWindows向けにクロスコンパイルする(Experimental)

2024/01/26に公開

※今回の時点でのTauriのバージョンはv1.5です

※あとTauriそのものの説明は省きます

TL;DR;

Tauriのクロスプラットフォームビルドは2024年1月時点で未サポートであり保証外だが、
rust-crosscross-rsによるツールを活用することで意外と楽にLinux上でWindows向けクロスコンパイルが可能。

targetがx86_64-pc-windows-msvcの場合、cargo-xwinが便利

https://github.com/rust-cross/cargo-xwin

# Linux上で実行

rustup target add x86_64-pc-windows-msvc
cargo install cargo-xwin

# Windows向けクロスコンパイル用のコマンド:
cargo xwin build --release \
    --features custom-protocol \
    --target x86_64-pc-windows-msvc

targetがx86_64-pc-windows-gnuの場合、crossが便利

https://github.com/cross-rs/cross

# Linux上で実行

cargo install cross

# Windows向けクロスコンパイル用のコマンド:
cross build --release \
    --features custom-protocol \
    --target x86_64-pc-windows-gnu

はじめに

公式ドキュメント Cross-Platform Compilation冒頭部分:

(...中略), so meaningful cross-compilation is not possible at the current moment.

と記載されているように、
執筆時点(2024年1月25日)でクロスコンパイルは未サポート、というか基本的に不可能とされている。
なので、真面目な用途であれば現時点では本記事の内容は無意味であり、GitHub Actionsなどサポートされているまともなやり方を採用すべきである。

一方で同ページを下までスクロールしていくと、
Experimental: Build Windows apps on Linux and macOSという項目があり、色々とサポート外だが一応方法がある旨が記されている。
が、かなり面倒なのでrust-crosscross-rsによるツールを活用してなるべく楽にビルドを試みる。

https://github.com/rust-cross
https://github.com/cross-rs

実行

(検証環境)

Windows11のWSL2上で動作しているLinuxで検証を実施した。

詳細情報は以下:

(検証環境詳細)
# OS情報

uname -a
# Linux <My-Machine-Name> 5.15.133.1-microsoft-standard-WSL2 #1 SMP Thu Oct 5 21:02:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

$ head /etc/os-release -n 5
# PRETTY_NAME="Ubuntu 23.10"
# NAME="Ubuntu"
# VERSION_ID="23.10"
# VERSION="23.10 (Mantic Minotaur)"
# VERSION_CODENAME=mantic


# Nodejs

$ node --version
# v20.11.0

$ pnpm --version
# 8.14.3


# Rust

$ cargo --version
# cargo 1.75.0 (1d8b05cdd 2023-11-20)

$ rustup --version
# rustup 1.26.0 (5af9b9484 2023-04-05)

$ apt list --installed | grep build-essential
# build-essential/mantic,now 12.10ubuntu1 amd64 [installed]

$ cargo xwin --version
# cargo-xwin-xwin 0.16.3

$ cross --version
# cross 0.2.5

$ docker --version
# Docker version 25.0.1, build 29cf629
# (↑crossでDockerコンテナを実行するため)

↑上記はUbuntuだが、他にもFedora39などでも同様に動くことは確認済み

(Tauriプロジェクト作成)

公式記載のQuick Startを使う。

割と何でも良いはずだが、今回はvite + Reactベースのものにした。
あとパッケージマネージャーも何でも良いがpnpmにしている。

pnpm create tauri-app
# ↓入力・選択した各項目
✔ Project name · tauri-app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · React - (https://reactjs.org/)
✔ Choose your UI flavor · TypeScript

出来上がったプロジェクトの構成はこんな感じ↓

tauri-app ディレクトリ構成
$ tree --gitignore
.
├── index.html
├── package.json
├── pnpm-lock.yaml
├── public
│   ├── tauri.svg
│   └── vite.svg
├── README.md
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── main.tsx
│   ├── styles.css
│   └── vite-env.d.ts
├── src-tauri
│   ├── build.rs
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── icons
│   │   └── (省略, pngファイルなど)
│   ├── src
│   │   └── main.rs
│   └── tauri.conf.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

今回だとnodeプロジェクトの中にrustプロジェクト(src-tauri/以下)が入っているような構成になっている。

これをLinux上からWindows向けのtargetにコンパイルを行っていく。

1. x86_64-pc-windows-msvc向けコンパイル

targetをx86_64-pc-windows-msvcにしてコンパイルする。
公式のBuild Windows apps on Linux and macOSもmsvcをtargetにする方を記載しており、
おそらくx86_64-pc-windows-gnuよりこちらの方がベターと思われる。

なお、それぞれのtargetの違いについてはrustupのドキュメント↓などを参照のこと
https://rust-lang.github.io/rustup/installation/windows.html

ここで、先のBuild Windows apps on Linux and macOSの記載手順はかなり複雑で辛そうなので、冒頭に書いた[rust-cross/cargo-xwin]を使ってビルドする。

必要ツールのセットアップは以下のような感じ:

# targetにx86_64-pc-windows-msvcを追加
rustup target add x86_64-pc-windows-msvc

# cargo-xwinコマンドを使えるようにする:
cargo install cargo-xwin

# バージョン指定してインストールする場合:
cargo install cargo-xwin@0.16.3

ここで、さっき作ったtauriプロジェクトでアプリのビルドを行う際、
普通だったら

# ※npm, yarnを使うひとは適宜読み替えてください(以降も同様)

# ※↓今回(Windows向けクロスコンパイル時)は実行しない
pnpm tauri build

などでビルドを実施する。
これで開発環境と同じプラットフォーム向け(ここではLinux)のビルドが実施されるが、
今回はここでtargetをwindows-msvcに変えて実施したい。
ターゲットオプション--targetを指定して上記コマンドを実行することは可能だが、
そのままだと内部的に普通にcargo buildが走るためさっきのcargo-xwinなどの代替ツールを使うことが出来ないため、途中でビルドが失敗してしまう。

そこで、頑張ってpnpm tauri buildの実行内容を推測してみる。
(ピンポイントで知りたい情報がすぐ見つからなかったので、雰囲気で推測している。)

まずtauri.conf.jsonを参照すると、

src-tauri/tauri.conf.json(抜粋)
{
  "build": {
    "beforeDevCommand": "pnpm dev",
    "beforeBuildCommand": "pnpm build",
    "devPath": "http://localhost:1420",
    "distDir": "../dist"
  }
}

などと書いてある。これにより、アプリのビルド時は予めpnpm buildでフロントエンド部分をビルドしておき(beforeBuildCommand)、
アプリに表示するページの中身として../dist(pnpm buildで生成されたアセット)を指定していることが伺える。

次に、Cargo.tomlの方を見てみる:

src-tauri/Cargo.toml(抜粋)
[features]
# this feature is used for production builds or when `devPath` points to the filesystem
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]

features部分を参照することで、
開発用ビルド(pnpm tauri devのとき)と本番用ビルド(pnpm tauri buildのとき)の挙動の差に効いていることが伺える。
cargoコマンドを実行する際は、--featuresフラグを指定することで指定が可能:

https://doc.rust-lang.org/cargo/reference/features.html

ここで、先ほどのコマンドpnpm tauri buildは下記の内容と大体同じになると考えられる:

# フロントエンド(今回はvite + React)側のビルド・静的アセットの生成
pnpm build

cd tauri-src

cargo build --release \
    --features custom-protocol

ここで、cargo build部分をcargo xwin buildに置き換え、更にtargetをx86_64-pc-windows-msvcに指定することでビルドができると考えられる。

# クロスコンパイルのために実行するコマンド:

# フロントエンド側のビルド
pnpm build

cd tauri-src

cargo xwin build --release \
    --features custom-protocol \
    --target x86_64-pc-windows-msvc

これで、src-tauri/target/x86_64-pc-windows-msvc/release下にビルドされたアプリが置かれる:

ls src-tauri/target/x86_64-pc-windows-msvc/release/
# build/  deps/  examples/  incremental/  tauri-app.d  tauri-app.exe*  tauri_app.pdb

今回の場合、tauri-app.exeの単一ファイルさえあればアプリを実行できる:

ミニマルな機能だと5MBくらいで済んでおり、思ったよりだいぶコンパクト。
(例えばElectronなどはChromiumとNodejsを同梱するため機能ゼロでも180MBくらいのサイズになる)

上記のexeファイルを実行すると、

期待通りにちゃんとTauriアプリがWindows上で起動しており、@tauri-apps/apiを使ったフロントエンド側とRust側の連携部分も動いていることが確認できる。

2. x86_64-pc-windows-gnu向けコンパイル(おまけ)

targetをx86_64-pc-windows-gnuにしてビルドすることも一応できたので参考に書いておく。

今度はcross-rs/crossを使う。

https://github.com/cross-rs/cross
https://github.com/cross-rs/cross/wiki/Getting-Started

インストール:

cargo install cross

# バージョン指定
cargo install cross@0.2.5

Dockerコンテナを使ってビルドしてくれるのでホスト側に色々入れたりする必要が無いのだが、
先程のtauri buildの挙動を把握しておかないと詰まる。
すなわち、ビルド時にコンテナにカレントディレクトリをマウントするのだが、
さっき作ったプロジェクトの場合だとフロントエンドのビルドアセットがCargo.tomlが置いてあるsrc-tauriから見て親ディレクトリに存在するため、コンテナにマウントされておらずビルドが失敗してしまう。

そこで、crossを使う場合はフロントエンドのビルドアセットが吐き出される場所を少し調整すると良い。

今回はviteを使っているので、

https://ja.vitejs.dev/config/build-options.html#build-outdir

を参考に、

vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig(async () => ({
  // ...
  // 以下: build.outDirを追記
+ build: {
+   outDir: "src-tauri/dist",
+ },
}));

のように追記する。

また、tauriのビルド時に参照するフロントエンドアセットのパスも合わせて調整しておく:

src-tauri/tauri.conf.json
{
  "build": {
    // ...
-   "distDir": "../dist"
+   "distDir": "./dist"
  },
  // ...
}

これでsrc-tauriディレクトリをコンテナにマウントしてもフロントエンドのビルドアセットが見えるようになった。

これで、先ほどのようにフロントエンドのビルドとRust部分のビルドを順に行っていく。

# フロントエンドのビルド
pnpm build
# ↑src-tauri/dist にビルドアセットが格納

cd src-tauri

# crossを使ってTauriアプリ(Rust)をクロスコンパイル
cross build --release \
    --features custom-protocol \
    --target x86_64-pc-windows-gnu

これで、src-tauri/target/x86_64-pc-windows-gnu/release下にビルドされたアプリが置かれる:

ls src-tauri/target/x86_64-pc-windows-gnu/release/
# build  deps  examples  incremental  tauri-app.d  tauri-app.exe  WebView2Loader.dll

今回の場合、tauri-app.exeWebView2Loader.dllの2つのファイルがあればアプリを実行できる:

x86_64-pc-windows-msvcのときは5MBくらいだったのに比べると若干サイズが大きい(20MB以上)←それでもElectronよりはだいぶコンパクト

実行結果:

先ほどの場合と同様のWindowsアプリが立ち上がっており、
ファイルサイズの大きさや追加のdllファイルが必要なこと以外にすぐ分かる差異は無く、問題なく動いていそうな感じ。

まとめ

ここまでで作ったものを一応GitHubに保存しておいた:

https://github.com/junkor-1011/tauri-cross-compilation-example-2024-01/tree/zenn_2024-01-26_2

今回使ったTauriのバージョンや、その他プロジェクトで使われているパッケージのバージョン詳細も↑を参照のこと。

ビルド用のコマンドが長いので、適当過ぎる感じはあるがnpm-scriptのdist:win-msvcおよびdist:win-gnuにそれぞれ登録している。

# x86_64-pc-windows-msvc向けビルド
pnpm dist:win-msvc

# x86_64-pc-windows-gnu向けビルド
pnpm dist:win-gnu

これで遊びに動かす程度であれば問題なさそうなセットアップを実現できた。

あくまで保証外なので開発を進めていくとうまく動かなくなる可能性もあるが、
アプリのソースコードは一切変えていないので、それこそ問題が起きたら公式が推奨・サポートしている正規のやり方でビルドするようにすればOKなはず。

GitHubで編集を提案

Discussion