JavaScript
Emscripten
webpack
parcel
parceljs

emscriptenのメモ・parcelでどうしようか

概要

https://github.com/taktod/emscripten-webpack-loader
これをparcelのpluginとしたい。

つくりたかったものができました。
https://qiita.com/taktod/items/f2be7e2cfd9fcbc96722

なぜこんなものが必要か

例として
emscriptenでvp8変換とopus変換したいとする
vp8処理をする動作用のjsをvp8.c -> vp8.jsとして作成
opus処理をする動作用のjsをopus.c -> opus.jsとして作成

htmlファイルからscriptタグでこれら2つのjsファイルを読み込んで処理させようとすると

「動作しません」

なんかメモリー用のデータ部が干渉するらしい。

なのでうまく動作させたい場合は

vp8.c opus.c -> enc.jsとして作成する
emscriptenの出力のjsは1つにまとめる必要があります。

emscripten-webpack-loaderではどうやったか

まずソースファイルとして以下のemc.tsとemc.jsを定義した。

https://github.com/taktod/ttLibJsEmc_next/blob/c84d3e081b698fbeed352a1ad62d286077921096/src/emcts/encoder/opusEncoder.emc.ts

こんな感じ

ソースコードの先頭にjsで矛盾がない形で
コンパイルの元になるsource
js側から利用したいfunc
コンパイル時に必要になるcflagsとldflags
を定義することにした。

あとはwebpackのpluginの動作で
webpackの結合時にすべてのemc.ts、emc.jsを検索してこれら4つの情報をすべて収集
収集したデータを元にコンパイル実行

できたデータをwebpackの次のpluginに通常のjsとして渡すことで解決しています。

parcelでどうしようか

https://parceljs.org/getting_started.html
ここのadavancedによると

parcelのpluginをつくるとAssetとPackagerの2つが定義できるみたいですね。

動作はざっくりこんな感じ

pluginを書くと・・・
拡張子(1文字目を排除する)とMyAssetを紐づける
Assetでの定義typeとMyPackagerを紐づける
という動作になる模様

ファイルが更新や追加されると拡張子から対応するAssetに処理を託す
https://parceljs.org/asset_types.html

基本的にgenerateの応答のjsはjs packager cssはcss packagerといったふうに渡される
対応するものがない場合はraw packagerに渡されるらしい

処理順メモ

まずはasset

Apretransform
Aparse
AcollectDependencies
Atransform
Agenerate

これをそれぞれのファイルについて実施

assetの結果は.cacheに記録される模様
元になるファイルを更新しないとcacheは更新されないみたい。
pluginが更新されても、そのままな模様

cacheのデータは
dependencies 依存しているファイル記録 jsで別のjsをrequireしてるとか
generated 作成したデータのjsonの形かな?
hash hash値

次にpackager

Pstart
PaddAsset
Pend

startでhead情報を書き出し
addAssetでassetをベースに1つづつデータを書き出す。
endでtailの情報を書き出し

packagerはparcelでコードを書き直す場合に毎回callされる

というイメージ

ということからするとparcelでの動作はこんな感じにすべきかな。

.emc.ts
.emc.js
と独自assetを紐付け(*1)
assetの処理でsourceと紐付け

通常のjsとおなじように出力を実施しつつ
emscripten用の情報をgeneratedに登録しておく

js -> jsPackagerで処理
emc -> 独自packagerでemscriptenのコンパイルを実施しておく
html -> htmlPackagerでコンパイルしてemc.jsを読み込めるようにしとく

webpackとはちがいmem.jsつかえそうですね。

さて、プログラミングしてみるか・・・

*1: この拡張子を.emc.tsとかにするとtypescriptと判定されて処理するみたいですね。
処理pluginとしてはちゃんと登録されるけど、マッチ検索のときに.tsが優先される模様。
この動作になる原因はpath.extnameの挙動による模様。
path.extnameの動作を書き換えて.emc.js .emc.tsの場合は・・・というのを書けばよさげだな。
-> だめっぽい。packagerの動作の部分でawait asyncを使っている部分があるもようだが、この部分でpath.extnameの動作が戻されてしまう模様。うーん。
tsとMyAssetを紐づければオリジナルの動作をオーバーライドできるみたいなので
そうやってから、拡張子がemc.tsならこうして、tsならこうして・・・というコードを各方向でいってみよう。
-> この方法を使うと別のpluginが同じことをしているとどっちかのライブラリが無効になる可能性があるわけか・・・
これは困るな・・・
-> この方法だとダメなこと、その2:pluginのロードが本家のロードの後になるとは限らないみたいですね。こりゃ困ったわ
emcjsとemctsを定義して、プラグイン自体はtsやjsで書いてそのなかで特殊データとしてemcという拡張子のデータを読み込むみたいなことにしておくといいかな。

assetのループで処理してくれるのって、 dependenciesで紐づいているもののみっぽいですね。
手元の動作テストだと
index.html -> index.ts -> hogehoge.ext
という依存関係になってて、独自のデータがある場合にindex.htmlにscriptタグをかかせようとしてもうまくいかないみたい。
どうしたものか・・・

.cacheの中身のjsonをみてみたところ、cacheをさぐっていってindex.htmlに依存データを書き込む・・・ということはできないもよう。
index.htmlにいい感じにscriptタグを自動で挿入したいけど・・・うーん。

あ、そうか、 scriptタグを動的につくって、追加するようにすればいいかな。
emscriptenの動作でのmem.jsの読み込みってどうやってるのかな。

emscriptenのjs.memはみてみたところXMLHttpRequestで取得してりようしてる模様。
今回の動作では、js側でrequire(import)したときにscriptタグをつくって追記するのが一番かな。と思った。