T-CREATOR

Electron Mac 向けビルド:Universal(Intel/Apple Silicon)を作る手順

Electron Mac 向けビルド:Universal(Intel/Apple Silicon)を作る手順

Electron アプリを Mac 向けにリリースする際、Intel チップと Apple Silicon(M1/M2/M3)の両方に対応させたいと思ったことはありませんか。Universal Binary を使えば、1 つのアプリで両アーキテクチャをサポートできます。

この記事では、Electron アプリを Mac の Intel と Apple Silicon の両方で動作する Universal Binary としてビルドする手順を、初心者の方にもわかりやすく解説します。実際のコード例を交えながら、electron-builder を使った具体的な設定方法をご紹介しますね。

背景

Apple の Mac アーキテクチャ変遷

Apple は 2020 年から Mac のプロセッサを Intel から独自開発の Apple Silicon(M シリーズチップ)へ移行し始めました。この大きな変化により、Mac アプリの開発者は新しい課題に直面することになります。

Apple Silicon は ARM64 アーキテクチャを採用しており、従来の Intel Mac が使用していた x86_64 アーキテクチャとは根本的に異なるのです。

以下の図は、Apple の Mac プロセッサの変遷と、それぞれのアーキテクチャを示しています。

mermaidflowchart TB
  subgraph old["2020年以前"]
    intel["Intel Mac<br/>アーキテクチャ: x86_64"]
  end
  subgraph new["2020年以降"]
    m1["Apple Silicon Mac<br/>アーキテクチャ: arm64"]
  end
  subgraph transition["移行期間"]
    both["Intel と Apple Silicon<br/>が共存"]
  end

  old --> transition
  transition --> new

  style intel fill:#e3f2fd
  style m1 fill:#f3e5f5
  style both fill:#fff3e0

この図からわかるように、現在は移行期間であり、Intel Mac と Apple Silicon Mac の両方が市場に存在しています。

Rosetta 2 の役割

Apple Silicon Mac では、Intel 向けにビルドされたアプリも「Rosetta 2」という変換レイヤーを通じて実行できます。しかし、Rosetta 2 を経由すると、ネイティブ実行と比べてパフォーマンスが低下してしまうのです。

ユーザーに最高のパフォーマンスを提供するには、各アーキテクチャに最適化されたネイティブコードを提供することが重要になります。

Universal Binary とは

Universal Binary は、複数のアーキテクチャ(Intel の x86_64 と Apple Silicon の arm64)のバイナリコードを 1 つのファイルに含めた実行ファイル形式です。

この形式を採用すると、ユーザーは自分の Mac のアーキテクチャを気にすることなく、同じアプリをダウンロードして使えるようになります。システムが自動的に適切なバイナリを選択して実行してくれるのですね。

課題

別々のビルドを配布する問題点

Electron アプリを Intel 用と Apple Silicon 用に別々にビルドして配布する場合、以下のような課題が発生します。

ユーザー体験の低下

ユーザーは自分の Mac がどちらのアーキテクチャなのかを確認し、正しいバージョンを選択してダウンロードしなければなりません。この手間は、特に技術に詳しくないユーザーにとって大きな障壁となってしまいます。

配布管理の複雑化

開発者側も、2 つの異なるビルドを管理し、それぞれのダウンロードリンクを提供する必要があります。バージョンアップの際も、両方のビルドを更新しなければなりません。

サポートコストの増加

どちらのバージョンをダウンロードすべきか、という問い合わせが増える可能性があります。また、間違ったバージョンをダウンロードしたユーザーへの対応も必要になるでしょう。

以下の図は、別々にビルドを配布する場合の問題点を示しています。

mermaidflowchart TD
  dev["開発者"] -->|ビルド| intel_build["Intel 向けビルド<br/>x86_64"]
  dev -->|ビルド| arm_build["Apple Silicon 向けビルド<br/>arm64"]

  intel_build -->|配布| server1["配布サーバー<br/>Intel版"]
  arm_build -->|配布| server2["配布サーバー<br/>ARM版"]

  user["ユーザー"] -->|判断が必要| choice{"自分の Mac は<br/>どちら?"}

  choice -->|Intel| server1
  choice -->|Apple Silicon| server2
  choice -->|わからない| confusion["混乱・エラー"]

  style confusion fill:#ffebee
  style choice fill:#fff3e0

図からわかるように、ユーザーが自分で判断しなければならない点が、大きな課題となっています。

ネイティブモジュールの対応

Electron アプリで Node.js のネイティブモジュール(C/C++ で書かれたアドオン)を使用している場合、さらに複雑になります。

これらのモジュールも各アーキテクチャ向けにコンパイルする必要があり、Universal Binary の作成時には両方のバージョンを含める必要があるのです。

解決策

Universal Binary で一本化

Universal Binary を作成することで、これらの課題を一気に解決できます。1 つのアプリファイルに両アーキテクチャのコードを含めることで、ユーザーは何も考えずにアプリをダウンロードして使えるようになるのです。

以下の図は、Universal Binary のビルドと配布のフローを示しています。

mermaidflowchart LR
  dev["開発者"] -->|ビルド| builder["electron-builder<br/>Universal設定"]

  builder -->|コンパイル| intel_code["x86_64 バイナリ"]
  builder -->|コンパイル| arm_code["arm64 バイナリ"]

  intel_code -->|結合| universal["Universal Binary<br/>両方を含む"]
  arm_code -->|結合| universal

  universal -->|配布| server["配布サーバー<br/>1つのファイル"]

  server -->|ダウンロード| user["ユーザー"]
  user -->|実行| auto["システムが自動選択"]

  style universal fill:#c8e6c9
  style auto fill:#c8e6c9

この図からわかるように、ユーザーは何も判断する必要がなく、システムが自動的に適切なバイナリを選択して実行してくれます。

electron-builder の活用

Electron アプリのビルドツールとして広く使われている electron-builder は、Universal Binary の作成をサポートしています。設定ファイルに適切なオプションを追加するだけで、簡単に Universal Binary をビルドできるのです。

electron-builder の導入

まず、プロジェクトに electron-builder をインストールしましょう。

bash# electron-builder をプロジェクトに追加
yarn add --dev electron-builder

このコマンドで、electron-builder が開発用の依存関係として追加されます。

基本的な設定方針

Universal Binary を作成するための設定は、主に以下の 3 つのポイントに注目します。

  1. ターゲットアーキテクチャの指定: x86_64 と arm64 の両方を指定
  2. ビルド環境の準備: Apple Silicon Mac でのビルドを推奨
  3. ネイティブモジュールの処理: 両アーキテクチャ向けにリビルド

これらの設定を順番に見ていきましょう。

具体例

package.json の設定

まず、package.json にビルド設定を追加します。ここで Universal Binary を作成するための基本設定を行うのです。

以下は、package.json の基本構造です。

json{
  "name": "my-electron-app",
  "version": "1.0.0",
  "description": "Electron アプリのサンプル",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "build": "electron-builder"
  }
}

この基本構造に、electron-builder の設定を追加していきます。

build セクションの追加

package.json に build セクションを追加し、Mac 向けの設定を記述しましょう。

json{
  "build": {
    "appId": "com.example.myapp",
    "productName": "MyElectronApp",
    "mac": {
      "target": {
        "target": "default",
        "arch": ["x64", "arm64"]
      },
      "category": "public.app-category.utilities"
    }
  }
}

arch 配列に x64(Intel 用)と arm64(Apple Silicon 用)の両方を指定することで、Universal Binary が作成されます。

DMG パッケージの設定

Mac アプリの配布形式として一般的な DMG ファイルの設定も追加しましょう。

json{
  "build": {
    "mac": {
      "target": {
        "target": "dmg",
        "arch": ["x64", "arm64"]
      }
    },
    "dmg": {
      "contents": [
        {
          "x": 130,
          "y": 220
        },
        {
          "x": 410,
          "y": 220,
          "type": "link",
          "path": "/Applications"
        }
      ],
      "window": {
        "width": 540,
        "height": 380
      }
    }
  }
}

この設定により、ユーザーフレンドリーな DMG インストーラーが作成されます。

electron-builder.yml での設定

package.json が大きくなるのを避けたい場合は、専用の設定ファイル electron-builder.yml を作成する方法もあります。

プロジェクトのルートディレクトリに electron-builder.yml ファイルを作成しましょう。

yaml# アプリケーション ID とプロダクト名の設定
appId: com.example.myapp
productName: MyElectronApp

# ビルドに含めるファイル・除外するファイル
files:
  - '**/*'
  - '!**/*.ts'
  - '!*.md'
  - '!.gitignore'

# ビルドから除外するディレクトリ
directories:
  buildResources: build
  output: dist

この基本設定では、TypeScript ファイルやマークダウンファイルなど、配布に不要なファイルを除外しています。

Mac 向けの詳細設定

次に、Mac 専用の設定を追加します。

yaml# Mac 向けの設定
mac:
  # ターゲットの指定(DMG 形式で Universal Binary を作成)
  target:
    - target: dmg
      arch:
        - x64 # Intel Mac 用
        - arm64 # Apple Silicon Mac 用

  # アプリケーションカテゴリ
  category: public.app-category.utilities

  # アイコンの設定
  icon: build/icon.icns

  # 署名の設定(必要に応じて)
  identity: null

archx64arm64 の両方を指定することで、Universal Binary が作成されます。

DMG の外観設定

DMG ファイルを開いたときの見た目を設定します。

yaml# DMG の設定
dmg:
  # DMG ウィンドウのサイズ
  window:
    width: 540
    height: 380

  # アイコンの配置
  contents:
    - x: 130
      y: 220
    - x: 410
      y: 220
      type: link
      path: /Applications

  # 背景画像(オプション)
  background: build/background.png

この設定により、ユーザーがアプリを Applications フォルダにドラッグ&ドロップしやすいレイアウトが作成されます。

ネイティブモジュールの対応

Electron アプリで Node.js のネイティブモジュールを使用している場合、特別な処理が必要になります。

electron-rebuild の使用

ネイティブモジュールを各アーキテクチャ向けに再ビルドするには、electron-rebuild を使用します。

bash# electron-rebuild をインストール
yarn add --dev @electron/rebuild

インストール後、package.json にスクリプトを追加しましょう。

json{
  "scripts": {
    "rebuild": "electron-rebuild -f -w ネイティブモジュール名",
    "postinstall": "electron-rebuild"
  }
}

postinstall スクリプトを追加することで、yarn install 実行時に自動的にネイティブモジュールが再ビルドされます。

両アーキテクチャ向けのビルド

Universal Binary を作成する際、electron-builder は自動的に両アーキテクチャ向けにネイティブモジュールをビルドしてくれます。

ただし、Apple Silicon Mac 上でビルドを実行することを強く推奨します。Apple Silicon Mac では Rosetta 2 を使って Intel 向けのコードもビルドできますが、Intel Mac では ARM64 向けのコードをビルドできないからです。

ビルドスクリプトの最適化

ビルドプロセスを効率化するため、package.json に便利なスクリプトを追加しましょう。

json{
  "scripts": {
    "start": "electron .",
    "build:mac": "electron-builder --mac",
    "build:mac:universal": "electron-builder --mac --x64 --arm64",
    "build:all": "electron-builder -mwl",
    "clean": "rm -rf dist"
  }
}

各スクリプトの役割を説明します。

#スクリプト名説明
1start開発モードでアプリを起動
2build:macMac 向けビルド(現在のアーキテクチャのみ)
3build:mac:universalUniversal Binary を作成
4build:allMac・Windows・Linux 向けにビルド
5cleanビルド成果物を削除

ビルドの実行

設定が完了したら、実際にビルドを実行してみましょう。

bash# Universal Binary をビルド
yarn build:mac:universal

このコマンドを実行すると、electron-builder が以下の処理を順番に行います。

  1. 依存関係の確認とインストール
  2. x64(Intel)向けのバイナリをコンパイル
  3. arm64(Apple Silicon)向けのバイナリをコンパイル
  4. 両バイナリを 1 つの Universal Binary に結合
  5. DMG パッケージを作成

ビルドが完了すると、dist ディレクトリに DMG ファイルが作成されているはずです。

ビルド結果の確認

ビルドが成功したら、作成された Universal Binary が正しく両アーキテクチャに対応しているか確認しましょう。

lipo コマンドでの確認

macOS の lipo コマンドを使うと、バイナリに含まれるアーキテクチャを確認できます。

bash# アプリ内のバイナリを確認
lipo -archs dist/mac-universal/MyElectronApp.app/Contents/MacOS/MyElectronApp

正しく Universal Binary が作成されていれば、以下のように表示されます。

x86_64 arm64

この出力は、バイナリが Intel(x86_64)と Apple Silicon(arm64)の両方に対応していることを示しています。

file コマンドでの詳細確認

file コマンドを使うと、さらに詳細な情報を確認できます。

bash# バイナリの詳細情報を表示
file dist/mac-universal/MyElectronApp.app/Contents/MacOS/MyElectronApp

出力例は以下のようになります。

vbnetMyElectronApp: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64:Mach-O 64-bit executable arm64]

「universal binary with 2 architectures」と表示されていれば、正しく Universal Binary が作成されています。

コード署名と公証の設定

Mac アプリを一般配布する際は、Apple の開発者証明書で署名し、公証(notarization)を受ける必要があります。

署名の設定

electron-builder.yml に署名の設定を追加しましょう。

yamlmac:
  # 署名の設定
  identity: 'Developer ID Application: Your Name (TEAM_ID)'

  # Hardened Runtime の有効化(公証に必要)
  hardenedRuntime: true
  gatekeeperAssess: false

  # エンタイトルメントの設定
  entitlements: build/entitlements.mac.plist
  entitlementsInherit: build/entitlements.mac.plist

identity には、Apple Developer アカウントの証明書名を指定します。

エンタイトルメントファイルの作成

build​/​entitlements.mac.plist ファイルを作成し、必要な権限を定義します。

xml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <!-- JIT コンパイルを許可 -->
  <key>com.apple.security.cs.allow-jit</key>
  <true/>

  <!-- 署名されていないコードの実行を許可 -->
  <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
  <true/>

  <!-- Hardened Runtime を有効化 -->
  <key>com.apple.security.cs.disable-library-validation</key>
  <true/>
</dict>
</plist>

これらの設定は、Electron アプリが正常に動作するために必要な権限です。

公証の自動化

electron-builder は、Apple の公証プロセスも自動化できます。

yamlmac:
  # 公証の設定
  notarize:
    teamId: YOUR_TEAM_ID

afterSign: scripts/notarize.js

環境変数に Apple ID とアプリパスワードを設定しておく必要があります。

bash# 環境変数の設定(.env ファイルなどで管理)
export APPLE_ID="your-apple-id@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"

これらの情報は機密情報なので、決してコードリポジトリにコミットしないよう注意してください。

トラブルシューティング

Universal Binary のビルド時によくあるエラーと解決方法をご紹介します。

エラー: Error: Cannot build for arm64 on x64

このエラーは、Intel Mac で arm64 向けのビルドを試みた場合に発生します。

解決方法: Apple Silicon Mac でビルドを実行するか、CI/CD 環境で Apple Silicon ランナーを使用してください。

エラー: native module not compatible

ネイティブモジュールが特定のアーキテクチャに対応していない場合に発生します。

解決方法: 以下のコマンドでモジュールを再ビルドします。

bash# 両アーキテクチャ向けに再ビルド
yarn add @electron/rebuild
yarn electron-rebuild

それでも解決しない場合は、モジュールが Universal Binary に対応しているかドキュメントを確認しましょう。

ビルドサイズが大きくなる問題

Universal Binary は 2 つのアーキテクチャのコードを含むため、ファイルサイズが約 2 倍になります。

対策方法: 以下のような最適化を検討してください。

  1. 不要な依存関係の削除: package.json の dependencies を見直し
  2. アセットの最適化: 画像や動画ファイルを圧縮
  3. asar アーカイブの使用: コードをアーカイブ化してサイズを削減

electron-builder.yml に asar の設定を追加しましょう。

yaml# asar アーカイブの設定
asar: true
asarUnpack:
  - '**/*.node' # ネイティブモジュールは展開

この設定により、ネイティブモジュール以外のファイルがアーカイブ化され、サイズが削減されます。

CI/CD での自動ビルド

GitHub Actions を使って、Universal Binary のビルドを自動化することもできます。

GitHub Actions ワークフローの設定

.github​/​workflows​/​build.yml ファイルを作成します。

yamlname: Build Universal Binary

# トリガーの設定(タグがプッシュされたとき)
on:
  push:
    tags:
      - 'v*'

jobs:
  build-mac:
    # macOS ランナーを使用(Apple Silicon 対応)
    runs-on: macos-latest

    steps:
      # リポジトリをチェックアウト
      - name: Checkout code
        uses: actions/checkout@v3

まず、リポジトリのコードをチェックアウトします。

Node.js のセットアップ

次に、Node.js 環境をセットアップしましょう。

yaml# Node.js のセットアップ
- name: Setup Node.js
  uses: actions/setup-node@v3
  with:
    node-version: '18'
    cache: 'yarn'

キャッシュを有効にすることで、2 回目以降のビルドが高速になります。

依存関係のインストールとビルド

依存関係をインストールし、ビルドを実行します。

yaml# 依存関係のインストール
- name: Install dependencies
  run: yarn install --frozen-lockfile

# Universal Binary のビルド
- name: Build Universal Binary
  run: yarn build:mac:universal
  env:
    # Apple 署名用の環境変数(GitHub Secrets から取得)
    APPLE_ID: ${{ secrets.APPLE_ID }}
    APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}

機密情報は GitHub Secrets に登録し、環境変数として参照します。

ビルド成果物のアップロード

ビルドした DMG ファイルを GitHub Release にアップロードしましょう。

yaml# Release へのアップロード
- name: Upload to Release
  uses: softprops/action-gh-release@v1
  with:
    files: dist/*.dmg
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

これで、タグをプッシュするだけで自動的に Universal Binary がビルドされ、Release に公開されるようになります。

まとめ

Electron アプリを Mac の Intel と Apple Silicon の両方に対応させる Universal Binary の作成方法をご紹介しました。

electron-builder を使えば、設定ファイルに arch: ["x64", "arm64"] を追加するだけで、簡単に Universal Binary を作成できます。これにより、ユーザーは自分の Mac のアーキテクチャを気にすることなく、同じアプリをダウンロードして最高のパフォーマンスで使用できるようになるのです。

重要なポイントをまとめると、以下のようになります。

  1. アーキテクチャの指定: package.json または electron-builder.yml で x64 と arm64 を指定
  2. ビルド環境: Apple Silicon Mac でのビルドを推奨
  3. ネイティブモジュール: electron-rebuild で両アーキテクチャ向けに再ビルド
  4. 署名と公証: 一般配布には Apple の開発者証明書と公証が必要
  5. 確認方法: lipo コマンドで Universal Binary を検証

Universal Binary を採用することで、ユーザー体験が向上し、配布管理も簡素化されます。Mac アプリをリリースする際は、ぜひ Universal Binary での配布を検討してみてください。

Apple Silicon への移行はまだ進行中ですが、Universal Binary を提供することで、すべてのユーザーに最適な体験を届けることができるでしょう。

関連リンク