T-CREATOR

Apple Silicon 最適化 Tauri セットアップ:Universal Binary/arm64 対応の実践

Apple Silicon 最適化 Tauri セットアップ:Universal Binary/arm64 対応の実践

Tauri でデスクトップアプリケーションを開発する際、Apple Silicon(M1、M2、M3 シリーズ)への対応は避けて通れない重要な課題となっています。Intel ベースの Mac から Apple Silicon への移行が進む中、開発者は新しいアーキテクチャに最適化されたアプリケーションを提供する必要があるでしょう。

本記事では、Tauri アプリケーションを Apple Silicon に最適化し、Universal Binary や arm64 ネイティブビルドを実現する具体的な手順を解説します。実際のプロジェクトで直面する課題と、その解決策を段階的に紹介していきますね。

背景

Apple Silicon の登場とアーキテクチャの変化

2020 年、Apple は自社設計の ARM ベースプロセッサ「Apple Silicon」を搭載した Mac を発表しました。これにより、Mac のアーキテクチャは Intel の x86_64 から ARM の arm64(aarch64)へと移行を始めたのです。

この移行は開発者にとって大きな転換点となりました。従来の x86_64 向けにビルドされたアプリケーションは、Rosetta 2 という互換レイヤーを通じて動作しますが、パフォーマンスの面では最適とは言えません。

アーキテクチャの違いによる影響

以下の表は、両アーキテクチャの主な違いを示しています。

#項目x86_64(Intel)arm64(Apple Silicon)
1命令セットCISCRISC
2消費電力高い低い
3発熱量多い少ない
4パフォーマンス効率標準高効率
5ネイティブ実行Intel Mac のみApple Silicon Mac のみ

Tauri とアーキテクチャ対応

Tauri は Rust で書かれたバックエンドと、Web 技術で構築されたフロントエンドを組み合わせたデスクトップアプリケーションフレームワークです。Rust 自体は ARM アーキテクチャをサポートしているため、適切な設定を行えば Apple Silicon ネイティブのアプリケーションを構築できます。

次の図は、Tauri アプリケーションのアーキテクチャとビルドターゲットの関係を示しています。

mermaidflowchart TB
    source["Tauri プロジェクト<br/>(Rust + Web)"]

    subgraph build["ビルドプロセス"]
        cargo["Cargo ビルド"]
        target["ターゲット指定"]
    end

    subgraph output["ビルド成果物"]
        x86["x86_64 バイナリ<br/>(Intel Mac)"]
        arm["arm64 バイナリ<br/>(Apple Silicon)"]
        universal["Universal Binary<br/>(両対応)"]
    end

    source --> cargo
    cargo --> target
    target --> x86
    target --> arm
    target --> universal

    x86 -.->|Rosetta 2| arm_mac["Apple Silicon Mac"]
    arm --> arm_mac
    universal --> arm_mac
    universal --> intel_mac["Intel Mac"]

図のポイント:Tauri プロジェクトは適切なターゲット指定により、複数のアーキテクチャ向けにビルドできることがわかります。

課題

Universal Binary とは何か

Universal Binary は、複数のアーキテクチャ向けのバイナリコードを 1 つのファイルに含めた実行ファイル形式です。macOS では、Intel Mac でも Apple Silicon Mac でも同じアプリケーションファイルをネイティブ実行できるため、ユーザー体験が向上します。

Tauri 開発で直面する具体的な課題

Tauri アプリケーションを Apple Silicon に対応させる際、以下のような課題に直面することがあります。

#課題説明影響
1ターゲット設定の不足デフォルトではホスト環境のアーキテクチャのみ他のアーキテクチャで動作しない
2依存関係の互換性Rust クレートや npm パッケージが arm64 未対応ビルドエラーの発生
3ビルド時間の増加複数ターゲットのビルドで時間がかかる開発効率の低下
4バイナリサイズの増大Universal Binary はサイズが大きくなる配布ファイルの肥大化
5テスト環境の確保両アーキテクチャでのテストが必要CI/CD の複雑化

ビルドプロセスの問題点

通常の Tauri ビルドでは、実行しているマシンのアーキテクチャに合わせたバイナリのみが生成されます。つまり、Intel Mac でビルドすれば x86_64 のみ、Apple Silicon Mac でビルドすれば arm64 のみが生成されるのです。

次の図は、標準的なビルドプロセスでの制約を示しています。

mermaidstateDiagram-v2
    [*] --> BuildStart: ビルド開始

    BuildStart --> DetectArch: アーキテクチャ検出

    DetectArch --> IntelBuild: Intel Mac
    DetectArch --> ArmBuild: Apple Silicon Mac

    IntelBuild --> X86Binary: x86_64 バイナリのみ生成
    ArmBuild --> Arm64Binary: arm64 バイナリのみ生成

    X86Binary --> Problem1: Apple Silicon で<br/>Rosetta 2 が必要
    Arm64Binary --> Problem2: Intel Mac で<br/>実行不可

    Problem1 --> [*]
    Problem2 --> [*]

この図から、標準ビルドでは片方のアーキテクチャしかカバーできないことがわかりますね。

依存関係の互換性問題

Tauri アプリケーションは、Rust のクレートと Node.js のパッケージの両方に依存します。これらの依存関係が arm64 に対応していない場合、ビルドが失敗したり、実行時エラーが発生したりする可能性があるのです。

特に、ネイティブモジュールを含む npm パッケージは注意が必要です。例えば、以下のようなパッケージは arm64 対応を確認する必要があります。

  • node-gyp を使用するパッケージ
  • C/C++ バインディングを持つパッケージ
  • プリビルドバイナリを配布しているパッケージ

解決策

Rust ターゲットの追加

Tauri で Universal Binary や arm64 ネイティブビルドを実現するには、まず Rust のクロスコンパイルターゲットを追加する必要があります。これにより、異なるアーキテクチャ向けのバイナリを生成できるようになるのです。

ターゲットの追加方法

rustup を使用して、必要なターゲットを追加します。

bash# arm64(Apple Silicon)ターゲットの追加
rustup target add aarch64-apple-darwin

# x86_64(Intel)ターゲットの追加
rustup target add x86_64-apple-darwin

このコマンドを実行すると、Rust コンパイラが両方のアーキテクチャ向けにコードをコンパイルできるようになります。

tauri.conf.json の設定

Tauri の設定ファイルを編集し、ビルドターゲットを指定します。この設定により、アプリケーションのビルド時に適切なアーキテクチャが選択されるのです。

基本的な設定

json{
  "build": {
    "beforeBuildCommand": "yarn build",
    "beforeDevCommand": "yarn dev",
    "devPath": "http://localhost:3000",
    "distDir": "../dist"
  }
}

上記は Tauri 設定ファイルの基本構造です。build セクションには、ビルド前に実行するコマンドや出力先を指定します。

バンドル設定の追加

json{
  "bundle": {
    "active": true,
    "targets": ["dmg", "app"],
    "identifier": "com.example.app",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  }
}

bundle セクションでは、アプリケーションのバンドル形式やアイコンを設定します。targetsdmgapp を指定することで、macOS 用のインストーラーが生成されますね。

macOS 固有の設定

json{
  "bundle": {
    "macOS": {
      "frameworks": [],
      "minimumSystemVersion": "10.13",
      "exceptionDomain": "",
      "signingIdentity": null,
      "entitlements": null
    }
  }
}

macOS セクションでは、macOS 固有の設定を行います。minimumSystemVersion で最小対応バージョンを指定できます。

Universal Binary のビルド手順

Universal Binary を作成するには、両方のアーキテクチャ向けにビルドした後、lipo コマンドで結合します。この処理を自動化するスクリプトを作成すると便利でしょう。

ビルドスクリプトの作成

package.json にビルドスクリプトを追加します。

json{
  "scripts": {
    "dev": "tauri dev",
    "build": "tauri build",
    "build:universal": "node scripts/build-universal.js"
  }
}

このスクリプト定義により、yarn build:universal コマンドで Universal Binary ビルドが実行できるようになります。

Universal Binary 生成スクリプト

以下は、Universal Binary を生成するための Node.js スクリプトです。

javascript// scripts/build-universal.js
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

// ビルド設定
const APP_NAME = 'YourApp';
const BUNDLE_NAME = `${APP_NAME}.app`;

まず、必要なモジュールをインポートし、アプリケーション名を定義します。execSync は外部コマンドを同期的に実行するために使用するのです。

javascript// ビルド関数
function buildForTarget(target) {
  console.log(`Building for ${target}...`);

  // Cargo でターゲット指定してビルド
  execSync(`cargo build --release --target ${target}`, {
    cwd: path.join(__dirname, '..', 'src-tauri'),
    stdio: 'inherit',
  });
}

この関数は、指定されたターゲット向けに Cargo ビルドを実行します。stdio: 'inherit' により、ビルドの進捗がコンソールに表示されますね。

javascript// Universal Binary の作成
function createUniversalBinary() {
  const x86Binary = path.join(
    __dirname, '..',
    'src-tauri/target/x86_64-apple-darwin/release',
    APP_NAME
  );

  const armBinary = path.join(
    __dirname, '..',
    'src-tauri/target/aarch64-apple-darwin/release',
    APP_NAME
  );

  const outputBinary = path.join(
    __dirname, '..',
    'src-tauri/target/universal-apple-darwin/release',
    APP_NAME
  );

各アーキテクチャのバイナリパスを定義します。x86_64 と aarch64 のバイナリを、最終的な Universal Binary に結合するのです。

javascript  // 出力ディレクトリの作成
  const outputDir = path.dirname(outputBinary);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // lipo コマンドで結合
  console.log('Creating Universal Binary...');
  execSync(
    `lipo -create "${x86Binary}" "${armBinary}" -output "${outputBinary}"`,
    { stdio: 'inherit' }
  );

  console.log('Universal Binary created successfully!');
}

lipo コマンドは macOS に標準で含まれるツールで、複数のアーキテクチャのバイナリを 1 つのファイルに結合できます。

javascript// メイン処理
async function main() {
  try {
    // 各ターゲットのビルド
    buildForTarget('x86_64-apple-darwin');
    buildForTarget('aarch64-apple-darwin');

    // Universal Binary の作成
    createUniversalBinary();
  } catch (error) {
    console.error('Build failed:', error.message);
    process.exit(1);
  }
}

main();

メイン処理では、両方のターゲット向けにビルドを実行した後、Universal Binary を作成します。エラーハンドリングも忘れずに実装しましょう。

CI/CD での自動化

GitHub Actions を使用して、Universal Binary のビルドを自動化できます。これにより、リリース時に自動的に両方のアーキテクチャに対応したアプリケーションが生成されるのです。

ワークフローファイルの作成

.github​/​workflows​/​build.yml に以下の設定を記述します。

yamlname: Build Universal Binary

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:

このワークフローは、v で始まるタグがプッシュされた時、または手動で実行された時にトリガーされます。

yamljobs:
  build-universal:
    runs-on: macos-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Setup Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          profile: minimal

まず、コードをチェックアウトし、Node.js と Rust の環境をセットアップします。macOS ランナーを使用することで、macOS 向けのビルドが可能になりますね。

yaml- name: Add Rust targets
  run: |
    rustup target add aarch64-apple-darwin
    rustup target add x86_64-apple-darwin

- name: Install dependencies
  run: yarn install

- name: Build Universal Binary
  run: yarn build:universal

Rust のターゲットを追加し、依存関係をインストールした後、Universal Binary のビルドを実行します。

yaml- name: Upload artifact
  uses: actions/upload-artifact@v4
  with:
    name: universal-binary
    path: |
      src-tauri/target/universal-apple-darwin/release/*.app
      src-tauri/target/universal-apple-darwin/release/*.dmg

ビルドされたアプリケーションファイルを GitHub Actions のアーティファクトとしてアップロードします。これにより、ビルド結果をダウンロードできるようになるのです。

具体例

実際のプロジェクトでの実装

ここでは、シンプルな Tauri アプリケーションを例に、Apple Silicon 対応の実装手順を段階的に解説します。実際の開発フローに沿って進めていきましょう。

プロジェクトのセットアップ

新規プロジェクトの作成

Tauri プロジェクトを新規作成します。

bash# Tauri CLI のインストール(グローバル)
yarn global add @tauri-apps/cli

# プロジェクト作成
yarn create tauri-app

対話的なプロンプトに従って、プロジェクト名やフレームワークを選択します。ここでは Next.js を選択したと仮定して進めますね。

プロジェクト構造の確認

bash# プロジェクトディレクトリに移動
cd your-app-name

# ディレクトリ構造の確認
ls -la

Tauri プロジェクトは、フロントエンド用のディレクトリと src-tauri ディレクトリで構成されます。

プロジェクト構造を図で示すと、以下のようになります。

mermaidflowchart TD
    root["プロジェクトルート"]

    root --> frontend["フロントエンド<br/>(Next.js / React)"]
    root --> tauri["src-tauri<br/>(Rust バックエンド)"]
    root --> scripts["scripts<br/>(ビルドスクリプト)"]

    frontend --> src_fe["src/"]
    frontend --> public["public/"]
    frontend --> package["package.json"]

    tauri --> src_rs["src/"]
    tauri --> cargo["Cargo.toml"]
    tauri --> conf["tauri.conf.json"]

    scripts --> build_script["build-universal.js"]

各ディレクトリの役割が明確に分かれていることが重要なポイントです。

依存関係のインストール

Node.js パッケージのインストール

bash# 依存関係のインストール
yarn install

# Tauri CLI の追加(ローカル)
yarn add -D @tauri-apps/cli

開発用の依存関係として Tauri CLI をローカルにもインストールします。これにより、プロジェクトごとに CLI のバージョンを管理できるのです。

Rust の依存関係確認

src-tauri​/​Cargo.toml を確認します。

toml[package]
name = "your-app"
version = "0.1.0"
edition = "2021"

[build-dependencies]
tauri-build = { version = "1.5", features = [] }

パッケージメタデータとビルド依存関係を定義します。edition = "2021" は Rust の言語エディションを指定しているのです。

toml[dependencies]
tauri = { version = "1.5", features = ["shell-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

アプリケーションの実行時依存関係を指定します。tauri クレートは必須で、serde は JSON のシリアライズ・デシリアライズに使用しますね。

Rust ターゲットの設定

ターゲットの追加

両方のアーキテクチャをサポートするため、ターゲットを追加します。

bash# Apple Silicon(arm64)ターゲット
rustup target add aarch64-apple-darwin

# Intel(x86_64)ターゲット
rustup target add x86_64-apple-darwin

# インストールされたターゲットの確認
rustup target list --installed

最後のコマンドで、正しくターゲットが追加されたことを確認できます。

Tauri 設定の最適化

tauri.conf.json の編集

src-tauri​/​tauri.conf.json を開き、以下のように設定します。

json{
  "build": {
    "beforeBuildCommand": "yarn build",
    "beforeDevCommand": "yarn dev",
    "devPath": "http://localhost:3000",
    "distDir": "../out"
  }
}

Next.js の場合、ビルド出力は out ディレクトリになることが一般的です。devPath は開発サーバーの URL を指定します。

json{
  "package": {
    "productName": "YourApp",
    "version": "0.1.0"
  },
  "tauri": {
    "allowlist": {
      "all": false,
      "shell": {
        "all": false,
        "open": true
      }
    }
  }
}

セキュリティのため、allowlist では必要な機能のみを有効化します。shell.open は外部リンクを開くために有効化しているのです。

json{
  "tauri": {
    "bundle": {
      "active": true,
      "targets": ["dmg", "app"],
      "identifier": "com.yourcompany.yourapp",
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/128x128@2x.png",
        "icons/icon.icns",
        "icons/icon.ico"
      ],
      "resources": [],
      "copyright": "",
      "category": "DeveloperTool"
    }
  }
}

バンドル設定では、アプリケーションの識別子やアイコン、カテゴリーを指定します。identifier はアプリケーションを一意に識別する文字列ですね。

json{
  "tauri": {
    "bundle": {
      "macOS": {
        "frameworks": [],
        "minimumSystemVersion": "10.13",
        "exceptionDomain": "",
        "signingIdentity": null,
        "entitlements": null
      }
    }
  }
}

macOS 固有の設定では、最小システムバージョンを指定します。Apple Silicon は macOS 11.0(Big Sur)以降でサポートされますが、互換性のため 10.13 を指定しても問題ありません。

Universal Binary ビルドスクリプトの実装

先ほど紹介したビルドスクリプトをプロジェクトに追加し、実際に動作させます。

スクリプトファイルの配置

bash# scripts ディレクトリの作成
mkdir scripts

# ビルドスクリプトの作成
touch scripts/build-universal.js

# 実行権限の付与
chmod +x scripts/build-universal.js

スクリプトファイルを作成し、実行可能にします。

完全なビルドスクリプト

以下は、エラーハンドリングや検証機能を含めた完全版のスクリプトです。

javascript#!/usr/bin/env node

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const APP_NAME = 'your-app';
const TAURI_DIR = path.join(__dirname, '..', 'src-tauri');

console.log(
  '🚀 Starting Universal Binary build process...\n'
);

スクリプトの冒頭では、必要な定数を定義し、ビルドプロセスの開始を通知します。

javascript// フロントエンドのビルド
function buildFrontend() {
  console.log('📦 Building frontend...');
  try {
    execSync('yarn build', {
      cwd: path.join(__dirname, '..'),
      stdio: 'inherit',
    });
    console.log('✅ Frontend build completed\n');
  } catch (error) {
    console.error('❌ Frontend build failed');
    throw error;
  }
}

フロントエンドのビルドを実行します。エラーが発生した場合は、わかりやすいメッセージを表示して処理を中断するのです。

javascript// Rust バックエンドのビルド
function buildBackend(target) {
  console.log(`🔨 Building Rust backend for ${target}...`);
  try {
    execSync(`cargo build --release --target ${target}`, {
      cwd: TAURI_DIR,
      stdio: 'inherit',
    });
    console.log(`✅ ${target} build completed\n`);
  } catch (error) {
    console.error(`❌ ${target} build failed`);
    throw error;
  }
}

指定されたターゲット向けに Rust バックエンドをビルドします。各ターゲットのビルドには数分かかることがあるため、進捗を表示することが重要ですね。

javascript// バイナリの存在確認
function verifyBinary(binaryPath) {
  if (!fs.existsSync(binaryPath)) {
    throw new Error(`Binary not found: ${binaryPath}`);
  }

  const stats = fs.statSync(binaryPath);
  console.log(
    `📊 Binary size: ${(stats.size / 1024 / 1024).toFixed(
      2
    )} MB`
  );
}

ビルドされたバイナリが実際に存在するか確認し、ファイルサイズも表示します。

javascript// Universal Binary の作成
function createUniversalBinary() {
  console.log('🔄 Creating Universal Binary...');

  const x86Binary = path.join(
    TAURI_DIR,
    'target/x86_64-apple-darwin/release',
    APP_NAME
  );

  const armBinary = path.join(
    TAURI_DIR,
    'target/aarch64-apple-darwin/release',
    APP_NAME
  );

  const outputDir = path.join(
    TAURI_DIR,
    'target/universal-apple-darwin/release'
  );

  const outputBinary = path.join(outputDir, APP_NAME);

各バイナリのパスを定義します。

javascript// バイナリの存在確認
console.log('🔍 Verifying binaries...');
verifyBinary(x86Binary);
verifyBinary(armBinary);

// 出力ディレクトリの作成
if (!fs.existsSync(outputDir)) {
  fs.mkdirSync(outputDir, { recursive: true });
}

// lipo コマンドで結合
try {
  execSync(
    `lipo -create "${x86Binary}" "${armBinary}" -output "${outputBinary}"`,
    { stdio: 'inherit' }
  );
  console.log('✅ Universal Binary created\n');
} catch (error) {
  console.error('❌ Failed to create Universal Binary');
  throw error;
}

lipo コマンドで両方のバイナリを結合し、Universal Binary を作成します。

javascript  // 作成されたバイナリの検証
  console.log('🔍 Verifying Universal Binary...');
  verifyBinary(outputBinary);

  // アーキテクチャの確認
  console.log('📋 Architectures in binary:');
  execSync(`lipo -info "${outputBinary}"`, { stdio: 'inherit' });

  return outputBinary;
}

作成された Universal Binary に両方のアーキテクチャが含まれているか、lipo -info コマンドで確認します。

javascript// メイン処理
async function main() {
  const startTime = Date.now();

  try {
    // フロントエンドのビルド
    buildFrontend();

    // 各アーキテクチャのビルド
    buildBackend('x86_64-apple-darwin');
    buildBackend('aarch64-apple-darwin');

    // Universal Binary の作成
    const universalBinary = createUniversalBinary();

    const elapsed = (
      (Date.now() - startTime) /
      1000
    ).toFixed(2);
    console.log(`\n🎉 Build completed in ${elapsed}s`);
    console.log(`📦 Output: ${universalBinary}`);
  } catch (error) {
    console.error(
      '\n💥 Build process failed:',
      error.message
    );
    process.exit(1);
  }
}

main();

メイン処理では、全体の流れを管理し、実行時間も計測します。エラーハンドリングにより、問題が発生した場合も適切に対処できるのです。

package.json への登録

作成したスクリプトを package.json に登録します。

json{
  "name": "your-app",
  "version": "0.1.0",
  "scripts": {
    "dev": "next dev",
    "build": "next build && next export",
    "tauri:dev": "tauri dev",
    "tauri:build": "tauri build",
    "tauri:build:universal": "node scripts/build-universal.js"
  }
}

これで、yarn tauri:build:universal コマンドで Universal Binary のビルドが実行できるようになりました。

ビルドの実行とテスト

ビルドの実行

bash# Universal Binary のビルド
yarn tauri:build:universal

このコマンドを実行すると、フロントエンドのビルド、各アーキテクチャの Rust バックエンドのビルド、そして Universal Binary の作成が順次実行されます。

ビルド結果の確認

bash# バイナリの確認
ls -lh src-tauri/target/universal-apple-darwin/release/

# アーキテクチャの確認
lipo -info src-tauri/target/universal-apple-darwin/release/your-app

lipo -info コマンドの出力は以下のようになります:

sqlArchitectures in the fat file: your-app are: x86_64 arm64

両方のアーキテクチャが含まれていることが確認できますね。

パフォーマンスの検証

Universal Binary が正しく動作し、適切なパフォーマンスを発揮しているか確認することも重要です。

アーキテクチャ別の実行確認

bash# 現在のアーキテクチャを確認
uname -m

# アプリケーションの実行(デバッグモード)
./src-tauri/target/universal-apple-darwin/release/your-app

Apple Silicon Mac では arm64、Intel Mac では x86_64 と表示されます。

以下の図は、Universal Binary の実行フローを示しています。

mermaidsequenceDiagram
    participant User as ユーザー
    participant OS as macOS
    participant Binary as Universal Binary
    participant Arch as アーキテクチャ選択
    participant Runtime as 実行環境

    User->>OS: アプリを起動
    OS->>Binary: バイナリを読み込み
    Binary->>Arch: CPU アーキテクチャを検出

    alt Apple Silicon
        Arch->>Runtime: arm64 コードを選択
        Runtime->>User: ネイティブ実行<br/>(高パフォーマンス)
    else Intel Mac
        Arch->>Runtime: x86_64 コードを選択
        Runtime->>User: ネイティブ実行<br/>(最適化済み)
    end

このシーケンス図から、Universal Binary が実行時に自動的に適切なアーキテクチャを選択することがわかります。

トラブルシューティング

よくあるエラーと解決方法

実装中に遭遇しやすいエラーとその対処法を紹介します。

Error: linking with 'cc' failed

このエラーは、クロスコンパイルに必要なツールチェーンが不足している場合に発生します。

bash# Xcode Command Line Tools の確認
xcode-select -p

# インストールされていない場合
xcode-select --install

Xcode Command Line Tools がインストールされていることを確認しましょう。

Error: error: cannot find -lSystem

このエラーは、macOS SDK が見つからない場合に発生します。

bash# SDK のパスを確認
xcrun --show-sdk-path

# 環境変数を設定(必要に応じて)
export SDKROOT=$(xcrun --show-sdk-path)

SDK のパスを環境変数に設定することで解決できます。

Error: バイナリサイズが異常に大きい

デバッグシンボルが含まれている可能性があります。

bash# バイナリをストリップ
strip src-tauri/target/universal-apple-darwin/release/your-app

strip コマンドでデバッグシンボルを削除し、バイナリサイズを削減できるのです。

最適化のポイント

ビルド時間の短縮

以下の方法でビルド時間を短縮できます。

#方法説明効果
1キャッシュの活用Cargo のビルドキャッシュを保持2 回目以降のビルドが高速化
2並列ビルド可能であれば両ターゲットを並列ビルドビルド時間が約半分に
3LTO の調整Link Time Optimization の設定調整バイナリサイズと時間のバランス
4依存関係の最小化不要なクレートを削除コンパイル時間の削減

Cargo.toml の最適化設定

toml[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true

リリースビルドの最適化設定を調整します。opt-level = "z" はサイズ優先の最適化、lto = true は Link Time Optimization を有効化し、strip = true でデバッグシンボルを自動削除するのです。

まとめ

Tauri アプリケーションを Apple Silicon に最適化し、Universal Binary や arm64 ネイティブビルドを実現する方法を解説してきました。重要なポイントを振り返ってみましょう。

実装の要点

Apple Silicon 対応の実装には、以下の手順が必要です:

  1. Rust ターゲットの追加 - rustup target add で両アーキテクチャのサポートを追加
  2. Tauri 設定の最適化 - tauri.conf.json でバンドル設定を適切に構成
  3. ビルドスクリプトの作成 - 両ターゲットのビルドと lipo による結合を自動化
  4. CI/CD の設定 - GitHub Actions で自動ビルドパイプラインを構築

これらを適切に実装することで、Intel Mac でも Apple Silicon Mac でもネイティブパフォーマンスを発揮するアプリケーションを配布できるようになります。

Universal Binary のメリット

Universal Binary を採用することで、以下のメリットが得られるのです:

  • ユーザー体験の向上 - どちらのアーキテクチャでもネイティブ実行
  • 配布の簡素化 - 1 つのインストーラーで両方に対応
  • パフォーマンスの最大化 - Rosetta 2 を介さない直接実行
  • 将来性 - Apple Silicon への移行期間中も安心して使用可能

注意すべきポイント

実装時には、以下の点に注意が必要です:

  • バイナリサイズの増加 - Universal Binary は単一アーキテクチャの約 2 倍のサイズになる
  • ビルド時間の増加 - 両方のターゲットをビルドするため時間がかかる
  • 依存関係の互換性 - すべての依存クレートが arm64 に対応していることを確認
  • テストの徹底 - 可能であれば両方のアーキテクチャで動作確認を実施

今後の展望

Apple Silicon はこれからの Mac の標準となっていきます。Intel Mac のサポートがいつまで続くかは不明ですが、移行期間中は Universal Binary が最適な選択肢でしょう。

将来的に Intel Mac のサポートを終了する場合は、arm64 のみのビルドに切り替えることで、バイナリサイズを削減し、ビルド時間を短縮できます。その際は、tauri build --target aarch64-apple-darwin というシンプルなコマンドで対応可能なのです。

Tauri は進化を続けており、今後さらに Apple Silicon への最適化が進むことが期待されます。本記事で紹介した手法を基盤として、プロジェクトに合わせたカスタマイズを行い、最高のユーザー体験を提供するアプリケーションを開発してください。

関連リンク

本記事の実装に役立つ公式ドキュメントやリソースをまとめました。