⚒️

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

2024/01/26に公開

末尾の方に改善点やv2での注意点などを追記(2024-10-16)

※記事中で紹介したツールcargo-xwinについて、(筆者個人としてはライセンス的に不安があるため)一旦推奨しない旨を追記→詳細は末尾に記載(2024-10-23)


※今回の時点での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なはず。


追記 2024-10-16

ビルド方法の改善

後から気付いたが、
今回の記事でやろうとしていることはtauri-clibuildサブコマンドで--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でもほぼ同様に使える:

https://v2.tauri.app/reference/cli/#build

(↑同様に--runnerオプションが存在する。ただし、その他のオプションは一部変わっていたりもする)

ただ、v2だとビルドに必要な依存関係が微妙に変わっていたりして、v1でビルドできてもv2ではビルド出来ない可能性がある。
そのため、

https://v2.tauri.app/start/prerequisites/

や、Windows向けにクロスコンパイルする際は

https://v2.tauri.app/distribute/windows-installer/#install-llvm-and-the-lld-linker

を参考に、ビルドに必要な依存関係が足りているかどうか見直すと良い。
(インストーラー(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

(リンク先↓)

https://go.microsoft.com/fwlink/?LinkId=2086102

とある(内部的にxwinが利用しているMicrosoft CRTおよびWindows SDKに対応)が、そこには

  1. インストールと使用権。

お客様は、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上でコンパイルすることを個人的には推奨する。
(というか、この辺詳しい方がいらっしゃったら教えて頂けるとありがたいです。。。)

targetをx86_64-pc-windows-gnuにしたときのビルド方法の補足

--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を使っているからこそ使用環境によらず安定感があって良いが(環境毎に追加で必要な依存関係を調べたりバージョンを管理する必要がなくなる)、
イメージサイズが重くて使いたくないときもあるので、追加の選択肢としてメモしておく。

GitHubで編集を提案

Discussion