Linux(WSL2)からTauriアプリをWindows向けにクロスコンパイルする(Experimental)
※末尾の方に改善点やv2での注意点などを追記(2024-10-16)
※記事中で紹介したツールcargo-xwin
について、(筆者個人としてはライセンス的に不安があるため)一旦推奨しない旨を追記→詳細は末尾に記載(2024-10-23)
※今回の時点でのTauriのバージョンはv1.5です
※あとTauriそのものの説明は省きます
TL;DR;
Tauriのクロスプラットフォームビルドは2024年1月時点で未サポートであり保証外だが、
rust-crossやcross-rsによるツールを活用することで意外と楽にLinux上でWindows向けクロスコンパイルが可能。
targetがx86_64-pc-windows-msvc
の場合、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
が便利
# 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-crossや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のドキュメント↓などを参照のこと
ここで、先の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
を参照すると、
{
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm build",
"devPath": "http://localhost:1420",
"distDir": "../dist"
}
}
などと書いてある。これにより、アプリのビルド時は予めpnpm build
でフロントエンド部分をビルドしておき(beforeBuildCommand)、
アプリに表示するページの中身として../dist
(pnpm build
で生成されたアセット)を指定していることが伺える。
次に、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
フラグを指定することで指定が可能:
ここで、先ほどのコマンド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
を使う。
インストール:
cargo install cross
# バージョン指定
cargo install cross@0.2.5
Dockerコンテナを使ってビルドしてくれるのでホスト側に色々入れたりする必要が無いのだが、
先程のtauri build
の挙動を把握しておかないと詰まる。
すなわち、ビルド時にコンテナにカレントディレクトリをマウントするのだが、
さっき作ったプロジェクトの場合だとフロントエンドのビルドアセットがCargo.tomlが置いてあるsrc-tauri
から見て親ディレクトリに存在するため、コンテナにマウントされておらずビルドが失敗してしまう。
そこで、cross
を使う場合はフロントエンドのビルドアセットが吐き出される場所を少し調整すると良い。
今回はviteを使っているので、
を参考に、
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のビルド時に参照するフロントエンドアセットのパスも合わせて調整しておく:
{
"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.exe
とWebView2Loader.dll
の2つのファイルがあればアプリを実行できる:
x86_64-pc-windows-msvc
のときは5MBくらいだったのに比べると若干サイズが大きい(20MB以上)←それでもElectronよりはだいぶコンパクト
実行結果:
先ほどの場合と同様のWindowsアプリが立ち上がっており、
ファイルサイズの大きさや追加のdllファイルが必要なこと以外にすぐ分かる差異は無く、問題なく動いていそうな感じ。
まとめ
ここまでで作ったものを一応GitHubに保存しておいた:
今回使った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なはず。
追記 2024-10-16
ビルド方法の改善
後から気付いたが、
今回の記事でやろうとしていることはtauri-cli
のbuild
サブコマンドで--runner
オプションを設定することで達成できるので、そちらの方が良いと思われる。
すなわち、
# ※npmやyarnの場合は適宜読み替えてください
# cargo-xwinを使ってx86_64-pc-windows-msvcをターゲットにビルド
pnpm tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc
# または、crossを使ってx86_64-pc-windows-gnuをターゲットにビルド
pnpm tauri build --runner cross --target x86_64-pc-windows-gnu
のような感じにすると、tauri build
コマンドで内部的に使われるコマンドをcargo
から代替コマンドに変更することができ、より自然にクロスコンパイルすることができる。
※ただ、この記事で書いた内容と全く同じ挙動にはならないので内容の修正はしていない。
Tauri v2.0での対応
ここまでで書いた方法は最近リリースされたtauri v2でもほぼ同様に使える:
(↑同様に--runner
オプションが存在する。ただし、その他のオプションは一部変わっていたりもする)
ただ、v2だとビルドに必要な依存関係が微妙に変わっていたりして、v1でビルドできてもv2ではビルド出来ない可能性がある。
そのため、
や、Windows向けにクロスコンパイルする際は
を参考に、ビルドに必要な依存関係が足りているかどうか見直すと良い。
(インストーラー(msi)を作らない場合でも、llvmのインストールなどは必要。)
v2に上げると何故かコンパイルがうまくいかずハマる、みたいなことが起こったので概要だけメモしておく。
追記 2024-10-23
記事で紹介しているツール: cargo-xwinに関して、READMEに使用許諾について
By using this software you are consented to accept the license at https://go.microsoft.com/fwlink/?LinkId=2086102
(リンク先↓)
とある(内部的にxwinが利用しているMicrosoft CRTおよびWindows SDKに対応)が、そこには
- インストールと使用権。
お客様は、Visual Studio Community、Visual Studio Professional、Visual Studio Enterprise と共に使用する場合に限り、本ソフトウェアの任意の数の複製をインストールして使用し、お客様のアプリケーションを開発およびテストすることができます。
などと書かれている。
つまり、冷静に見返してみると、
Visual Studioを使わずに、Linux上からターゲットをx86_64-pc-windows-msvc
向けにクロスコンパイルしようとするcargo-xwin
(というか、内部で使われているxwin
)について、そもそも利用許諾条件に合っているのか?について、筆者は自信が持てなくなっている。
といいつつ、本当にNGならそもそもそういうツールが公開・配布されるのか、Tauriのドキュメントで言及されるのか、という気もするので、杞憂の可能性もある。
(もし杞憂であれば、xwinの作者のブログなどにもあるようにmsvcターゲットでビルド出来る方がたいてい実用的なので嬉しい)
しかし、筆者は専門家ではなく断言しかねるので、確信が持てない方はもう一つの方法(targetをx86_64-pc-windows-gnu
にする方)を使う、あるいは初めからWindows上でコンパイルすることを個人的には推奨する。
(というか、この辺詳しい方がいらっしゃったら教えて頂けるとありがたいです。。。)
x86_64-pc-windows-gnu
にしたときのビルド方法の補足
targetを--target x86_64-pc-windows-gnu
を指定してクロスコンパイルする方法について、
記事中ではcross
による方法を紹介したが、
cross
(docker)を使わない方法として、mingw64-gcc
(+α)をインストールしてビルドするのも良いと思われる。
Tauri 2.0.Xに関して、
Ubuntu(24.04LTS noble)であれば
sudo apt install gcc-mingw-w64-x86-64 llvm
Fedora40などであれば
sudo dnf install mingw64-gcc mingw64-llvm
あたりを追加すると、
# pnpmは環境に応じて適宜npm runやyarnに置き換え
pnpm tauri build --target x86_64-pc-windows-gnu
のようにして、
--runner cross
オプションを指定しなくても(通常のcargo
を使って)クロスコンパイルが通ったりする。
※通常のx86_64-unknown-linux-gnuターゲットは普通にビルドできるくらいには必要なものをインストール済みな前提
※厳密な環境再現まではやっていないので、若干過不足はあるかも
cross
はDockerを使っているからこそ使用環境によらず安定感があって良いが(環境毎に追加で必要な依存関係を調べたりバージョンを管理する必要がなくなる)、
イメージサイズが重くて使いたくないときもあるので、追加の選択肢としてメモしておく。
Discussion