Electron を試す 2 - パッケージ化におけるプラットフォーム固有処理とコンパイル分岐
Electron を試すで残された課題を解決したので、その内容を記録しておく。
- 2015/10/5 追記 本記事のはてブにて id:Pasta-Kさんより
.ico
ファイルを反映させるために wine が必要との指摘があった。試してみたところ OS X 環境でもアイコンとバージョン情報変更を反映した Windows 向けパッケージを生成できたので追記した。
プラットフォーム固有処理
electron-packager の --icon
オプションに .ico
ファイルを指定すると OS X でエラーになる問題だが Windows 環境で実行したらパッケージ化に成功。一方、Windows 環境だと OS X 版のパッケージ化がスキップされる。Linux 版は特別なオプションがないためか OS X と Windows のどちらでもパッケージ化できた。
この状況から察するに、アイコンの埋め込み処理などでプラットフォーム固有 API を利用している可能性がある。つまり完全なパッケージ化を実行する場合は対象プラットフォームと同じ環境でビルドしなければならない。
以上を踏まえリリース用の npm-scripts を再定義。
{
"scripts": {
"release:css": "stylus -c ./src/stylus/App.styl -o ./dist/src/bundle.css",
"release:js-main": "browserify -t babelify ./src/js/main/Main.js --im --no-detect-globals | uglifyjs -c warnings=false -d DEBUG=false > ./dist/src/main.js",
"release:js-renderer": "browserify -t babelify ./src/js/renderer/App.js | uglifyjs -c warnings=false -d DEBUG=false > ./dist/src/bundle.js",
"release:clean": "rimraf ./dist/src",
"release:copy": "cpx \"./src/**/{*.html,*.eot,*.svg,*.ttf,*.woff,package.json}\" ./dist/src",
"release:build": "npm-run-all -s release:clean release:copy -p release:css release:js-main release:js-renderer",
"release:pack-osx": "electron-packager ./dist/src Starter --out=dist/bin --cache=dist/cache --platform=darwin --arch=x64 --version=0.33.4 --overwrite --asar --icon=res/app.icns",
"release:pack-win": "electron-packager ./dist/src Starter --out=dist/bin --cache=dist/cache --platform=win32 --arch=x64 --version=0.33.4 --overwrite --asar --icon=res/app.ico --version-string.CompanyName=\"Company\" --version-string.LegalCopyright=\"Copylight (C) USERNAME, All right reserved.\" --version-string.FileDescription=\"Electron application\" --version-string.OriginalFilename=\"Starter.exe\" --version-string.FileVersion=\"1.0.1\" --version-string.ProductVersion=\"1.0.1\" --version-string.ProductName=\"Starter\" --version-string.InternalName=\"Starter\"",
"release:pack-linux": "electron-packager ./dist/src Starter --out=dist/bin --cache=dist/cache --platform=linux --arch=x64 --version=0.33.4 --overwrite --asar",
"release:osx": "npm-run-all -s release:build release:pack-osx",
"release:win": "npm-run-all -s release:build release:pack-win",
"release:linux": "npm-run-all -s release:build release:pack-linux"
}
}
従来は release:pack-XXXX
系だけ分岐して release
へ統合していた。新スクリプトではリリース処理もプラットフォームごとに定義するようにしている。実行も対象プラットフォーム上で個別におこなう必要あり。
アイコンのファイル形式自体には問題なかったのだが OS X 版も透過になっていなかったりしたので作りなおした。Windows 版のアイコン作成は久しぶりに @icon変換 を利用。複数の透過 PNG からマルチ アイコンを作成できる。
Windows 版については、他にバージョン情報も設定している。これらは --version-string.XXXX
オプションで指定。EXE ファイルの詳細タブに表示される。以下は参考画像。ダイアログの内容だけでなく左上のアイコンも Electron 標準ではなく独自のものに変更されていることを確認できる。
ファイルのバージョンは #129 version-string.FileVersion is not working on Windows のためか Electron の 0.33.4.0 になってしまうようだ。
Windows 環境で実行する場合は Windows で node-gyp を使った npm を動かすための環境構築を先に実施しておくこと。その前に npm install
してもビルドは成功するがアイコンとバージョン情報の書き変えに失敗する。そうなってしまったら環境構築した後に node_modules
を削除してから改めて npm install
することでビルド環境を正常化できる。
オマケとして OSX と Windows のアイコン作成方法を掲載しておく。
OS X
アイコンの元になる透過 PNG を以下のファイル名とサイズで用意。ファイル名の @2x
というのは Retina 用。これの前につくサイズの倍となる。詳しくは Mac Developer Library の Optimizing for High Resolution を参照のこと。
ファイル名 | サイズ |
---|---|
icon_16x16.png | 16x16 |
icon_16x16@2x.png | 32x32 |
icon_32x32.png | 32x32 |
icon_32x32@2x.png | 64x64 |
icon_128x128.png | 128x128 |
icon_128x128@2x.png | 256x256 |
icon_256x256.png | 256x256 |
icon_256x256@2x.png | 512x512 |
icon_512x512.png | 512x512 |
icon_512x512@2x.png | 1024x1024 |
これらを app.iconset
というフォルダに格納。Terminal でこれが置かれたフォルダーまで移動してから以下のコマンドを実行。
$ iconutil -c icns app.iconset
すると app.icns
という名前で OS X 用のアイコン ファイルが生成される。フォルダ名の app
部分がファイル名となるため、名前が決まっているなら別のものにしてもよい。
Windows
アイコンの元になる透過 PNG を用意する。OS X のものを使いまわしてもよい。必要なサイズは以下となる。
サイズ |
---|
16x16 |
24x24 |
32x32 |
48x48 |
64x64 |
128x128 |
256x256 |
次に @icon変換 を用意。最終バージョンは 2007/2/4 版と古いが現在でも実用十分なので気にしなくてよい。アプリを起動してウィンドウに画像ファイルをドラッグ & ドロップすると画面左のリストに登録される。リストの画像をすべて選択した状態 (スクリーンショットを参照のこと) にしておく。
メイン メニューから「ファイル」→「マルチiconとして保存」を選択するとリストされた画像を組み合わせたマルチ アイコン ファイルが生成される。
OS X 環境でアイコンとバージョン情報変更を反映した Windows 向けパッケージを生成する
本記事のはてブにて electron-packager でアイコン変更を含めた Windows 向けパッケージを生成するなら wine が必要という指摘があったので試してみた。wine は Homebrew からインストール。ただし X11 に依存しているらしく、先にこれを入れなさいとエラーが出る。
$ brew install wine
wine: XQuartz is required to install this formula.
You can install with Homebrew Cask:
brew install Caskroom/cask/xquartz
You can download from:
https://xquartz.macosforge.org
Error: An unsatisfied requirement failed this build.
最新の wine だと X11 なしで動くという話をどこかで聞いた気がする。しかしエラーでインストールを促されているので従う。
$ brew install Caskroom/cask/xquartz
次に wine をインストール。
$ brew install wine
==> Installing dependencies for wine: xz, libpng, freetype, jpeg, pkg-config, libtool, libusb, libusb-compat, fontconfig, libtiff, gd, libgphot
... 以下、略
依存パッケージが大量にあるため、すべてインストールするのに 30 分ぐらいかかった。wine を用意したので OS X 環境で Windows 向けパッケージを生成してみる。
$ npm run release:win
... 中略
> electron-starter@1.0.2 release:pack-win .../electron-starter
> electron-packager ./dist/src Starter --out=dist/bin --cache=dist/cache --platform=win32 --arch=x64 --version=0.33.6 --overwrite --asar --icon=res/app.ico --version-string.CompanyName="Company" --version-string.LegalCopyright="Copylight (C) USERNAME, All right reserved." --version-string.FileDescription="Electron application" --version-string.OriginalFilename="Starter.exe" --version-string.FileVersion="1.0.1" --version-string.ProductVersion="1.0.1" --version-string.ProductName="Starter" --version-string.InternalName="Starter"
Downloading electron-v0.33.6-win32-x64.zip
[============================================>] 100.0% of 48.85 MB (857 kB/s)
Packaging app for platform win32 x64 using electron v0.33.6
Wrote new app to dist/bin/Starter-win32-x64
ビルド成功。このパッケージを VMware FUSION 上の Windows 8.1 環境にコピーしてみたところアプリは正常動作。またアイコン変更とファイルのプロパティからバージョン情報変更が反映されていることも確認できた。
つまり OS X & wine な環境であれば全プラットフォーム向けのパッケージをまとめて生成できる。
コンパイル分岐
ソフトウェア開発においてデバッグとリリースで振るまいを変えたくなることがある。例えばデバッグ時にはログ出力や特殊なメニューを提供。リリース版ではそれらが顕在化しないようにコードごと削除する。
こうした処理を実現するためソースコードのコンパイル時におけるプリ プロセスがある。例えば Visual C++ ではコンパイル時の環境変数を定義する機能を利用可能。デバッグ版ではここに _DEBUG
を指定して #ifdef _DEBUG
から #endif
までの区間を固有処理にする。
プロジェクトをデバッグ用ビルドした場合、コンパイルより前に環境変数が定義されるため区間のコードは有効になる。リリース用ビルドでは環境変数が存在しないから区間が不要と判定され抹消される。
この設計を JavaScript で実現する方法について watchify と連携させる場合、Source Maps と同時に指定する方法がわからなかった。また watchify によるコンパイルの都度 uglify を走らせる実行コストも気になる。そこで以下のようなハックを試みた。
まず Main プロセスのエントリーポイントあたりで global.DEBUG
を true
に設定。
import App from 'app';
class Main {
constructor() {
this._mainWindow = null;
this._rendererIPC = null;
// Compile switch
global.DEBUG = true;
}
}
const main = new Main();
次にこの DEBUG
変数による分岐処理を定義。
class Main {
constructor() {
// 初期化処理...
}
onReady() {
if( DEBUG ) { console.log( 'Launched' ); }
// Window 作成などを実行...
}
onWindowAllClosed() {
if( DEBUG ) { console.log( 'Quit' ); }
App.quit();
}
}
const main = new Main();
App.on( 'ready', main.onReady );
App.on( 'window-all-closed', main.onWindowAllClosed );
このようにして以下の npm-scripts を実行すると global.DEBUG
が有効となり、これを判定している if
文の内容が処理される。開発用コンパイルの変更は不要。従来どおりでよい。代わりにリリース用コンパイルを以下のように定義する。
{
"scripts": {
"release:js-main": "browserify -t babelify ./src/js/main/Main.js --im --no-detect-globals | uglifyjs -c warnings=false -d DEBUG=false > ./dist/src/main.js",
"release:js-renderer": "browserify -t babelify ./src/js/renderer/App.js | uglifyjs -c warnings=false -d DEBUG=false > ./dist/src/bundle.js"
}
}
長いので肝要の uglify 部分だけ抜粋。ファイル名が異なるだけで Main/Renderer プロセスで設定は共通。
uglifyjs -c warnings=false -d DEBUG=false > ./dist/src/main.js
-c
は圧縮フラグ。warnings=false
は不要箇所の検出による警告を抑止するためのもの。
-d
または --define
オプションの後に NAME=VALUE
を指定すると、その内容でグローバル定数が宣言されているものとしてコンパイルされる。このスクリプトでは DEBUG=false
としているため、これを if
文で判定している部分はすべて無効。これらはコンパイル結果から除去されるため結果としてリリース版では余計なコードが抹消される。
ここで疑問がわくはず。コード上にあった global.DEBUG = true
という処理はどう扱われるのだ?と。これがあるならプリプロセス以降にグローバルな DEBUG
という名前の変数が true
に書き換えられ、これを判定している if
文は有効になってしまうのではないか?
実際に uglify されたファイルの当該箇所は以下のようになる。
global.DEBUG=!0
処理自体は残っており代入対象も !0
となっている。そのため判定部分は true になるだろう。しかしこの変換がおこなわれるのは uglify の処理時である。つまり global.DEBUG
自体の代入は有効な処理だが global
修飾なしに DEBUG
を参照している箇所は uglify 側の定数宣言と判定がおこなわれ無効化された。
...上記の内容はコンパイル結果からみた推測である。というのも多分そう動くだろうなぁと思って実装してみたらあっさり成功してしまい私のほうがビックリしたぐらい。そのためハックと表現した。しかし現時点の uglify なら有効であり、この処理によりデバッグ用メニューをリリース版から除去できたのでよしとする。
なお Main/Rederer ともに同じ方法で分岐可能。DEBUG
を有効にする処理はエントリー ポイントになるファイルへ仕込むのがよいだろう。
サンプル プロジェクト
今回の内容に加えてアイコン修正や全プラットフォーム同時生成スクリプトを追加した v1.0.2 をサンプルとして公開する。