react-electron-ffi-(native dll/so/dylib) プロジェクトを作るメモ (2020-03-19版)
(1/2) react-electron
ここは今回のメモの本編ではないものの、"やり方"がころころ変わるようなので一応現時点での方法をついで程度に整理します。
(1) プロジェクトのディレクトリー(=リポジトリー)を create-react-app
で生成:
npx create-react-app myapp
cd myapp
(2) electron 系のパッケージを追加:
yarn add electron electron-builder wait-on concurrently --dev
yarn add electron-is-dev
(3) public/electron.js を作る:
const electron = require('electron'); const app = electron.app; const BrowserWindow = electron.BrowserWindow; const path = require('path'); const isDev = require('electron-is-dev'); let mainWindow; function createWindow() { app.allowRendererProcessReuse = true; mainWindow = new BrowserWindow({width: 1920/4, height: 1080/4, webPreferences:{ nodeIntegration: true } }); mainWindow.loadURL(isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`); mainWindow.on('closed', () => mainWindow = null); } app.on('ready', createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (mainWindow === null) { createWindow(); } });
(4) package.json に electron 用のお約束を追記:
(以下のコードはmergeする追記分のみ)
{ "main": "public/electron.js" , "scripts": { "dev": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"" } }
ここまでの時点で yarn dev
で package.json に追記した scripts の dev を呼ぶと一応 react-scripts を介して react-electron が期待動作してくれるはず。しなかったらこのメモの作成時点とは何か変化が必要にどこかが変わっている可能性があります。 Node.js, electron, Chromium など要素技術の変化はかなり激しいので、そういう事があっても慌てず問題を探りましょう、未来のわたしへ。
(5) rescripts を導入し eject せず create-react-app を扱える様に調整:
(5-1) rescripts を install
yarn add @rescripts/cli @rescripts/rescript-env --dev
(5-2) package.json / scripts の start
, build
, test
を react-scripts
から resccripts
へ変更:
(以下のコードは該当部分の変更後のみ)
{ "scripts": { "start": "rescripts start" , "build": "rescripts build" , "test": "rescripts test" } }
(5-3) package.json / scripts に postinstall
, preelectron-pack
, electron-pack
を追加:
(以下のコードはmergeする追記分のみ)
{ "scripts": { "postinstall": "electron-builder install-app-deps" , "preelectron-pack": "yarn build" , "electron-pack": "electron-builder build -w" } }
ここで electron-pack
の electron-builder build -w
の -w
は Windows 向けのパッケージングを有効にする引数。OSX向けを有効にしたければ -m
、GNU/Linux 向けを有効にしたければ -l
、Windows と OSX と GNU/Linux 向けを同時に有効にしたければ -wml
のように定義でき、プロジェクトに応じて変更します。必要ならプラットフォームのアーキテクチャーも --ia32
, --x64
で設定できます。
(5-4) package.json / scripts から eject
を削除
rescripts を使う場合 eject を使う必要は無いし、残しておいても事故の元になるだけなので scripts から完全に消し去ります。
(5-5) package.json に rescripts を追記:
(以下のコードはmergeする追記分のみ)
{ "rescripts": [ "env" ] }
これを書かないと electron のウィンドウ(=client-side)は動いても react な中身(=server-side)が期待動作しなくなります。
(5-6) .webpack.config.js を追加:
module.exports = config => { config.target = 'electron-renderer'; return config; }
(5-7) .rescripts.js を追加:
module.exports = [require.resolve('./.webpack.config.js')]
(6) package.json にパッケージング用のメタデータを追加:
{ "author": { "name": "Author Name" , "email": "aurhot.email@example.com" , "url": "https://author-website.example.com" } , "build": { "appId": "com.my-website.my-app" , "productName": "MyApp" , "copyright": "Copyright © 2020 ${author}", , "mac": { "category": "public.app-category.utilities" } , "files": [ "build/**/*" , "node_modules/**/*" ] , "directories": { "buildResources": "assets" } }
(7) assets / icon.png を作成
以上で react-electron の 2020-03-19 頃の準備手順は完了です。 yarn dev
で開発版を実行、 yarn electron-pack
でリリース版パッケージを dist ディレクトリーへ生成できるようになっています。なっていなければ、このメモの作成時点とは何か変化が必要にどこかが変わっている可能性があります。 Node.js, electron, Chromium など要素技術の変化はかなり激しいので、そういう事があっても慌てず問題を探りましょう、未来のわたしへ。
動作を確認したら次の react-electron を react-electron-ffi-(native dll/so/dylib) へ進みましょう。
(2/2) react-electron から react-electron-ffi-(native dll/so/dylib) へ
react-electron を使わない Node.js で FFI して native dll/so/dylib を呼ぶ方法は1つ前回書いた Node.js と FFI の 2020-03-18 時点でのメモ; node-ffi 系 → node-ffi-napi 系 - C++ ときどき ごはん、わりとてぃーぶれいく☆ のように node-ffi
系ではなく node-ffi-napi
系を使う事だけ知っていればわりと簡単に yarn add ffi-napi ref-napi
して、
var ffi = require('ffi-napi') var ref = require('ref-napi') var s = ref.types.CString var nyanko = ffi.Library( 'nyanko.dll',{ 'f': [ s, [] ] } ) console.log( nyanko.f() )
のように使えばよいだけです。これを踏まえて、 (1/2) で作成した react-electron のプロジェクトで、
yarn add ffi-napi ref-napi
して- src/App.js で前述のような FFI コードを記述して実行
すると:
こんな具合で死にます。☠ Node.js, react, electron に慣れていない初心者にはわけがわからない上にググらビリティーに問題を抱えたエラーの様に感じられますが、わかってしまえばどうという事はなくなります:
(1) public/electron.js の new BrowserWindow
で webPreferences.nodeIntegration
を明示的に true
に変更する:
mainWindow = new BrowserWindow({width: 1920/4, height: 1080/4, webPreferences:{ nodeIntegration: false } });
これをやらないと次の(2)だけやっても "window.require is not a function" とか electron のウィンドウにエラーが表示される事になります。nodeIntegration
を true
にしないと Node.js 部分の機能が有効にならないため、 fs
も使えないとかそういう事が起こります。そうなると ffi
も使えません。(2/2) の最初の死亡状態で無いと言われていた exists
はたぶん fs
の exists
でしょう。そこに気がつければこの問題と原因と解決方法へ繋がります。
(2) src/App.js では window.require('electron').remote
で require
して FFI する
const remote = window.require('electron').remote; const ffi = remote.require( 'ffi-napi' ); const ref = remote.require( 'ref-napi' );
こんな具合で期待動作します。これで例えば:
let s = ref.types.CString; let nyanko = ffi.Library( '../fuga/nyanko.dll',{ 'f': [ s, [] ] } ); // 例として前回適当に作った文字列値を返すだけのライブラリーを束縛 let mewing = nyanko.f();
としておいて、 function App()
の return
に {mewing}
を:
Learn React {mewing} !
など仕込んで実行すると:
こうなります。成功👍
もし、 yarn dev
する場合とパッケージングして実行する場合では native dll/so/dylib のパスが変わる場合は:
const is_dev = remote.require( 'electron-is-dev' ); const nyanko_path = is_dev ? '../fuga/nyanko.dll' : 'nyanko.dll'; let nyanko = ffi.Library( nyanko_path,{ 'f': [ s, [] ] } )
と electron-is-dev を使うと簡単です。呼び出す native dll/so/dylib もオリジナルに作ったものを使用したいプロジェクトではこのように対応します。あるいは、たぶん実行時の引数かシェル変数で渡す事にしておいて dev では package.json の scripts/start で特殊化しても対応できそうですが、私は今回 electron-is-dev で満足したのでその方法については思いついただけに留まります。
ついで、蛇足となりますが自分用メモとして、 electron-is-dev の値は Boolean
なので、これを文字列で表示したい場合は {is_dev}
ではなく {is_dev.toString()}
しないと文字列では見れません。このメモを書いた時に一度「あれ、文字列には…」と期待動作しない状態を見てしまったので自分用備忘録。
参考
- How to build an Electron app using Create React App and Electron Builder | Codementor
- note: このメモの(1/2)の参考としてとても有用でした。但し、参照する場合はいくつかの誤り、設定の不足や間違いが含まれる事に注意して下さい。
- Multi Platform Build - electron-builder
- Electron+React(create-react-app)のビルドでつまづいたこと - Qiita
- GitHub - sindresorhus/electron-is-dev: Check if Electron is running in development