micro:bit を Scratch 2 + Bluetooth で使う（ビルド編2：Electron + noble-uwp）

# micro:bit を Scratch 2 + Bluetooth で使う（ビルド編2）

以下のような話題についてまとめます。

1. ネイティブアドオンを使う Electron アプリケーションのビルドと配布可能なパッケージ化の方法
1. Scratch2（オフライン版）から Bluetooth Low Energy 経由で BBC micro:bit を使うためのヘルパーアプリケーション作成方法

[ビルド編1](https://qiita.com/memakura/items/11a0426f9060da1ded7e)にて、ネイティブアドオンの node-gyp ビルドが絡んだ Node のアプリケーションについて説明しましたが、今回は後半の、Electron 用のビルドとそのパッケージ化の話です。UWP (Universal Windows Platform) 関係のビルドが入るため、通常の node-gyp の準備に加え、Windows SDK のインストールが必要になります。

# electron-builder の使い方

## electron-builder

Electron のパッケージ化には、electron-builder や electron-packager、electron-forge など[いろいろとある](https://stackoverflow.com/questions/37113815/electron-builder-vs-electron-packager)ようです。いずれも設定次第なのかもしれませんが、ここではあまり深入りせず、ひとまずデフォルトのまま使ったときに比較的コンパクトな配布パッケージを生成した electron-builder を使ってみます。

インストーラの生成には、Windows では NSIS というツールが使用されます。初回にダウンロードされるため、別途インストールする必要はないようです。

なお、electron-builder では npm ではなくて [yarn](https://yarnpkg.com/ja/) が強く推奨されているので、ここから先は yarn を使ってみます。（といいつつ、npm でもおそらくOKだと思います。）コマンドの説明は[こちら](https://yarnpkg.com/lang/en/docs/cli/install/)にありますが、`npm install` は `yarn install`、`npm --save` は `yarn add`、`npm --save-dev` は `yarn add --dev` となります。バージョンのロックには `yarn.lock` というファイルが生成されます。npm よりパッケージのインストールが早いらしいです。確かに早い気がします。

### 参考ページ

以下のページが参考になります。

- https://www.electron.build/
    - https://www.electron.build/configuration/configuration
    - https://www.electron.build/configuration/nsis
- https://uqtimes.blogspot.jp/2016/12/electron-buildersample.html

## フォルダの準備

以下のような階層にしておきます。electron-builder の version 8 以降は[必ずしも二階層にしなくてよい](https://www.electron.build/tutorials/two-package-structure)ようですが、整理のために、ここでは以前通りトップフォルダがビルド用、一つ下にアプリケーション用 app フォルダを用意し、それぞれに package.json を置きます。node_modules や dist は勝手に生成されるので作成不要です。build もなくて構いませんが、独自にアイコンを置く場合などは用意しておきます。以下では、このプロジェクト用のフォルダが project_root という名前だとします。

```
project_root
 │
 ├─ package.json (パッケージビルド用)
 │
 ├─ app
 │   ├─ package.json (アプリ用)
 │   ├─ main.js (メインコード)
 │   ├─ index.html (メインページ)
 │   └─ node_modules/  (yarn install で作成される)
 │
 ├─ build (なくてもよい)
 │   ├─ icon.ico  (Windows用)
 │   └─ icon.icns (Mac用)
 │
 ├─ node_modules/  (yarn install で作成される)
 │
 └─ dist/ (electron-builder で生成される)
```

## アプリケーション用の package.json の準備

アプリケーション用の package.json を `yarn init` などで生成するか、適当に作成します。 

```json:app/package.json(アプリ用)
{
  "name": "mytest",
  "version": "1.0.0",
  "main": "main.js",
  "license": "MIT"
}
```

app フォルダに入って `yarn add` もしくは `npm install --save` します。

```
> cd app
> yarn add express
```

すると、node_modules へモジュールがインストールされ、`dependencies` のフィールドが追加されます。

### アプリケーションサンプル

main.js および index.html は、たとえば electron hello world などで検索すれば様々なサンプルがウェブで見つかります。例として、[以前の記事で解説した Node.js + 簡易Webサーバ (express)](https://qiita.com/memakura/items/ca1fff4d7dd33164bb7e) の Electron化のテストを[こちら (Github)](https://github.com/memakura/scratch2-ex-node-electron) に置きます。

## インストーラビルド用の package.json と electron-builder

インストーラビルド用の package.json を準備します。

```json:package.json
{
  "build": {
    "appId": "com.electron.mytest"
  }
}
```

本来は[このページのように](https://www.electron.build/configuration/configuration)いろいろと設定できますが、まずは最低限にしてみます。
appId はUUID代わりの適当な文字列で、この文字列が一致しているならば、二回目にインストールしたときに同じアプリケーションとして認識され、アップデートされます。別の文字列にすると別のアプリケーションとして認識され、別途インストールされます。

続けてこの package.json があるフォルダ (project_root) で electron および electron-builder をインストールします。(npm であれば --save-dev ですがここでは yarn を使います。）

```
> cd ..
> yarn add --dev electron
> yarn add --dev electron-builder
```

これで node_modules にモジュールが追加されるとともに、package.json に `devDependencies` が追加されます。

```json:package.json
{
  "build": {
    "appId": "mytest"
  },
  "devDependencies": {
    "electron": "^1.8.4",
    "electron-builder": "^20.8.1"
  }
}
```

### Electron の実行テスト

一度 electron を実行してみます。project_root にいる場合は

```
> .\node_modules\.bin\electron app
```

一つ下の app フォルダにいるときは

```
> ..\node_modules\.bin\electron .
```

で確認できます。

## electron-builder の実行

```
> .\node_modules\.bin\build
```

を実行すると

```
  • electron-builder version=20.8.1
  • loaded configuration file=package.json ("build" field)
  • writing effective config file=dist\electron-builder-effective-config.yaml
  • no native production dependencies
  • packaging       platform=win32 arch=x64 electron=1.8.4 appOutDir=dist\win-unpacked
  • default Electron icon is used reason=application icon is not set
  • building        target=nsis file=dist\<nameフィールド> Setup <versionフィールド>.exe archs=x64 oneClick=true
  • building block map blockMapFile=dist\<nameフィールド> Setup <versionフィールド>.exe.blockmap
```

のように表示され、dist フォルダ内にインストーラができます。初回はビルドの際にNSISなどのダウンロードが伴うため、二回目以降に比べて結構時間がかかります。

- `no native production dependencies` は、このパッケージではネイティブアドオンが絡まないことを意味しています。
- `<nameフィールド>` と `<versionフィールド>` はパッケージビルド用の package.json で設定した文字列（nameについては[少し修正（サニタイズ）](https://www.npmjs.com/package/sanitize-filename)されたもの）が入ります。


## インストーラの実行テストとNSISの設定

インストーラを実行すると、以下のユーザ領域にインストールされ、いきなり実行されます。

```
C:\Users\<ユーザ名>\AppData\Local\Programs\<nameフィールド>
```

インストール先などもう少し選びたいところです。パッケージビルド用の package.json を編集します。

```json:package.json
{
  "build": {
    "appId": "mytest",
    "artifactName": "${productName}_installer_${version}.${ext}",
    "win": {
      "target": "nsis"
    },
    "nsis": {
      "oneClick": false,
      "perMachine": false,
      "allowElevation": true,
      "allowToChangeInstallationDirectory": true
    }
  },
  "devDependencies": {
    "electron": "^1.8.4",
    "electron-builder": "^20.8.1"
  }
}
```

- `artifactname` では生成されるインストーラの名前を指定できます。詳しくは[こちら](https://www.electron.build/file-patterns#file-macros)を確認してください。
- `nsis` でインストーラ生成ツールNSIS の各種設定ができます。詳しくは[こちら](https://www.electron.build/configuration/nsis)を確認してください。
    - `oneClick` : インストールから実行まで一気に行います。（デフォルトは `true` ）
    - `perMachine` : `oneClick` が `true` のときに、ユーザ領域へのインストールするには `false`、全ユーザ用にインストールするには `true`（デフォルトは `false`）
    - `allowElevation` : 管理者権限でのインストールに移行するためのダイアログを開きます。（デフォルトは `true`）
    - `allowToChangeInstallationDirectory` : インストール先の変更を許します。（デフォルトは `false`）

以上が electron-builder の基本的な使い方でした。次はネイティブアドオンのビルドが絡む場合です。


# Electron用のネイティブアドオンをビルド

## UWP が絡まないネイティブアドオンのビルド

Node用のネイティブアドオンは、`yarn install` (`npm install`) すればリモートでダウンロードされることも多いですが、Electron用には別途ビルドが必要になります。

### 方法1: 手動で npm rebuild する場合

app フォルダで以下のコマンドを実行します。

```
> npm rebuild --runtime=electron --target=1.8.4 --arch=x64 --rebuild --disturl=https://atom.io/download/electron --build_from_source=true
```
これにより node-pre-gyp や node-gyp が走ります。`--target` には `.\node_modules\.bin\electron -v` で表示される Electron のバージョンを入れます。

ネイティブアドオンのビルドが終われば、UWP無しの場合と同様に、project_root のフォルダにある package.json の`build` に、`"npmRebuild": false` を加えておきます。（記載がなければデフォルトは `true` になります。）その後、`.\node_modules\.bin\build` すればインストーラが生成されます。


### 方法2: electron-builder に任せる場合

electron-builder のデフォルトの設定では内部で、自動的にこの rebuild を行います。しかし、場合によっては明示的に方法1が必要な場合もあるようです。方法2でエラーが起きる場合は方法1を試してみるのもよいかもしれません。

二回目以降のインストーラ生成時など、再度ネイティブアドオンをビルドする必要がなければ package.json の `npmBuild` を `false` にしておきます。

### 補足（yarn + node-pre-gyp まわりのエラー）

以下の問題は、以前は起きなかったので一時的な現象かもしれませんが、はまったのでメモしておきます。

自分の環境 (node v8.10.0, npm v5.6.0, yarn v1.5.1) では、なぜか `yarn install noble-uwp` では Node用ビルド済みバインディング (noble-uwp) がリモートからはダウンロードできず、ローカルでビルドされるようになりました。（`npm install noble-uwp` ではリモートからダウンロードされます。）さらに、node-pre-gyp まわりの設定がおかしくなっているのか、node-pre-gyp 0.9.0 に依存するようにインストールされてしまい、したがって、インストーラでインストールすると、node-pre-gyp がないという実行時エラーまで出てしまいます。

いずれも、`app/package.json` の `dependencies` に `"node-pre-gyp": "0.6.39"` を加えると解決されました。（なお、`npm install` を使う場合は `dependencies` への追加は不要でした。）


## UWP が絡むネイティブアドオンのビルドは Windows SDK が必要

UWPなどが絡む一部のネイティブアドオンでは、[ビルド編1](https://qiita.com/memakura/items/11a0426f9060da1ded7e)で説明した node-gyp の準備（Python 2.7 と Visual C++ Build Tools 2015）だけでは不足で、`fatal error C1107: could not find assembly 'Windows.winmd'` などが出てしまいます。

ビルドするネイティブアドオンの設定に合ったバージョンの Windows 10 SDK が必要になります。必要なバージョンは、モジュールの説明ページの README（たとえば Requirements）などに書かれていたりします。
[noble-uwp](https://github.com/jasongin/noble-uwp) は Windows 10 の Creators Update に依存するため、必要な バージョンは Windows 10 SDK build 10.0.15063 です。

これは Electron 用ビルドに限りません。[ビルド編1](https://qiita.com/memakura/items/11a0426f9060da1ded7e) の noble-uwp では、ビルド済みバインディングをリモートからダウンロードしたため SDKが不要でしたが、ローカルでビルドしようとすると同様に SDK をインストールしておく必要があります。

1. Visual Studio 2017 がインストール済みの場合、Visual Studio Installer を実行して「変更」ボタンを押し、右側の「概要」>「C++によるデスクトップ開発」で必要なバージョンにチェックを入れます。今回は「デスクトップ C++用 Windows 10 SDK (10.0.15063.0) [x86 および x64]」をインストールします。
1. Visual Studio 2017 が未インストールの場合、試していませんがおそらく [Windows SDK のアーカイブ](https://developer.microsoft.com/ja-jp/windows/downloads/sdk-archive)からダウンロードすればよさそうです。

これで UWP が絡む場合のビルドもエラーが出なくなります。

## usbモジュールでのエラーの対処

Nodeアプリケーション用の場合はリモートからダウンロードされていた usb モジュールですが、ビルド時にロシア語周りで以下のエラーが出ます。

```
..\libusb\libusb\strerror.c : warning C4819: The file contains a character that cannot be represented in the current co
de page (932). Save the file in Unicode format to prevent data loss [c:\hiroaki\coder\ScratchMicrobit\s2microbit-ble\ap
p\node_modules\usb\build\libusb.vcxproj]
  threads_windows.c
..\libusb\libusb\strerror.c(108): error C2001: newline in constant [c:\hiroaki\coder\ScratchMicrobit\s2microbit-ble\app
\node_modules\usb\build\libusb.vcxproj]
..\libusb\libusb\strerror.c(114): error C2001: newline in constant [c:\hiroaki\coder\ScratchMicrobit\s2microbit-ble\app
```

[このlibusbのissuesにあるように](https://github.com/libusb/libusb/issues/207)、簡易的な対処としてはロシア語の該当文字列の後ろに半角スペースを入れるようです。簡単なパッチを用意しておきます。

## 実行時にモジュール依存関係のエラーが起きた時のヒント

Electron のアプリケーションが依存するスクリプトやモジュールは `dist\win-unpacked\resources\app.asar` にたばねられています。`asar` コマンドで展開できます。最後の `app` は展開先のフォルダ名です。

```
> npm install -g asar
> cd dist\win-unpacked\resources
> asar extract app.asar app
```

インストール後にも展開することができます。ユーザ領域に入れた場合は `c:\Users\<ユーザ名>\AppData\Local\Programs\<nameフィールド>\resources` にあります。

もし直接変更を加えた後に再度たばねるなら以下のコマンドです。

```
> asar pack app app.asar
```

## appフォルダの外のファイルをインストーラへ追加したいとき

ネイティブアドオンの文脈から外れますが、app.asar と関連して、electron-builder でインストーラにファイルを追加する方法をまとめておきます。

electron-builder はデフォルトだと project_root/app フォルダを app.asar としてたばねますが、project_root/build にあるアイコンをアプリ内や index.html でも使いたいなど、project_root 直下のフォルダ（appにはないフォルダ）をインストーラに加えたい場合があります。このときの package.json の記述は [electron-builder のマニュアル](https://www.electron.build/configuration/contents#extrafiles)に詳しく書かれていますが、コピー先をどこにしたいのかで、`files`、`extraResources`、`extraFiles` を使い分けます。インストーラの app フォルダの中に加えるのであれば `files` を使います。

project_root/00microbit フォルダとその中にある `.hex` 拡張子のついたファイルをインストーラの app/00microbit へコピーしてみます。マニュアルによれば、デフォルトの指定は `**/*` で、これも加えておく必要があるとのことで、`package.json` に加える記述は以下のようになります。

```package.json    
    "files": [
      "**/*",
      {
        "from": "../00microbit",
        "to": "00microbit",
        "filter": ["*.hex"]
      }
    ],
```

これで build コマンドを実行すれば、インストーラ生成時に、インストーラの app の中に 00microbit フォルダが、さらにその中に `.hex` ファイルがコピーされます。

- `files` では `from` や `to` は app フォルダが起点になります。
- OS非依存なら package.json の `"build"` 以下に加えます。OS依存の場合は `"win"` や `"mac"` などの下に書いても良いそうです。`extraResources`や`extraFiles`などでは特に必要になるかもしれません。
- インストーラ生成時にコピーされるだけで、本来の project_root/app は影響を受けません。

`asar extract` コマンドを使って展開すると、実際に app/00microbit があるのが分かります。

# まとめ: s2microbit-ble のインストーラの作成方法

前回の[ビルド編1](https://qiita.com/memakura/items/11a0426f9060da1ded7e) で紹介した [s2microbit-ble-console](https://github.com/memakura/s2microbit-ble-console) を使うには Node, Python, VC++ のビルド環境が必要でした。さすがにこれだけの環境を子供のPCにそれぞれインストールするのは大変です。

そこで、いずれの環境もインストールせずに済むように、s2microbit-ble-console を Electron アプリケーション化し、インストーラとともに [s2microbit-ble](https://github.com/memakura/s2microbit-ble) で公開することにします。

以下ではこのインストーラの生成方法をまとめます。

## 要件

- Python 2.7 および VC++ Build Tools 2015（[windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) を入れておく）
- Windows 10 SDK build 10.0.15063（[詳しくはこちら](https://qiita.com/memakura/items/dc5cf2ff39d24ceb53ff#UWP が絡むネイティブアドオンのビルドは Windows SDK が必要)）

## インストーラの生成手順

以下の手順でインストーラを作ることができます。 `npm rebuild` の `target` は Electronのバージョンに合わせます。


```
> git clone https://github.com/memakura/s2microbit-ble.git
> cd s2microbit-ble\app
> yarn install （もしくは npm install）
> npm rebuild --runtime=electron --target=1.8.4 --arch=x64 --rebuild --disturl=https://atom.io/download/electron --build_from_source=true
> (Python 2.7 の環境にしておく。npm config で python にパスが通っていれば設定は不要)
> cd ..
> yarn install （もしくは npm install）
> .\node_modules\.bin\electron app （テストが必要な場合）
> .\node_modules\.bin\build
```

これで dist 以下にインストーラが生成されます。

インストール方法とアプリケーションの使用方法は [s2microbit-ble (Github)](https://github.com/memakura/s2microbit-ble) で確認できます。

次回は、s2microbit-ble で何ができるか、および bbc-microbit の API を一部説明します。

つづく：[まとめ編](https://qiita.com/memakura/items/1acab55a37651e9081b4)