em2native: Node.js ビルドの復活
いろいろ考えたけど、ちゃんとしたnpmモジュールにするのは時期尚早ということで、CMakeでビルド時にモジュールを生成してしまう方向にした。
Visual Studioデバッガ用のjsonとかも生成にしたいがちょっと良いアイデアがない(ソースコードと同じ場所に置く必要がある)。
標準libcがリンクされない問題
Visual StudioではSDLが /NODEFAULTLIB
しているので、それにリンクすると一緒に指定されてしまう問題。
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __vcrt_initialize が関数 __scrt_initialize_crt で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __vcrt_uninitialize が関数 __scrt_initialize_crt で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __vcrt_uninitialize_critical が関数 __scrt_dllmain_uninitialize_critical で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __vcrt_thread_attach が関数 __scrt_dllmain_crt_thread_attach で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __vcrt_thread_detach が関数 __scrt_dllmain_crt_thread_attach で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル _is_c_termination_complete が関数 __scrt_dllmain_uninitialize_c で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __acrt_initialize が関数 __scrt_initialize_crt で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __acrt_uninitialize が関数 __scrt_uninitialize_crt で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __acrt_uninitialize_critical が関数 __scrt_dllmain_uninitialize_critical で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __acrt_thread_attach が関数 __scrt_dllmain_crt_thread_attach で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\msvcrtd.lib(utility.obj) : error LNK2019: 未解決の外部シンボル __acrt_thread_detach が関数 __scrt_dllmain_crt_thread_detach で参照されました
C:\cygwin64\home\oku\repos\em2c\nodejs\out\build\x64-Debug\yfrm\yfrm.dll : fatal error LNK1120: 11 件の未解決の外部参照
_is_c_termination_complete
など見たことないシンボルのエラーが出る。
結局、SDLでもlibcを使うようにして回避してしまった。
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8721ed..7b16ac7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,6 +57,7 @@ endif()
# Configure SDL
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
set(SDL_STATIC ON CACHE BOOL "" FORCE)
+set(LIBC ON CACHE BOOL "" FORCE)
add_subdirectory(ext/platform/SDL sdl)
今やWindowsもUCRT(Universal C Runtime)が標準添付になったので、C標準ライブラリが付属してこないOSは無いといっても良いのではないだろうか。。
SDLは依存DLLを減らすためにlibcの一部を自前で実装しているが↑のようにSDLを静的リンクしたい場合に問題になることがある。これはちょっと機序が複雑で、
- Visual Studioのコンパイラは最適化で
memset
等の呼出しを生成してしまうことがある -
LIBC
なしでビルドされたSDLにはmemset
が無いのでリンクエラーになる → memsetを自前で実装する - SDLを静的リンクした場合、
memset
は標準libcにも存在するので、それと衝突してしまう
GCCでも似たような問題がある:
デバッグ用にDLLをコピーする
diff --git a/javascript/node-nccc/CMakeLists.txt b/javascript/node-nccc/CMakeLists.txt
index 9ed4f34..8ef3c20 100644
--- a/javascript/node-nccc/CMakeLists.txt
+++ b/javascript/node-nccc/CMakeLists.txt
@@ -32,3 +32,10 @@ set_target_properties(node-nccc
PREFIX ""
SUFFIX ".node")
+if(NODENCCC_DEBUG_COPY)
+ add_custom_command(TARGET node-nccc POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ "$<TARGET_FILE:node-nccc>"
+ "${NODENCCC_DEBUG_COPY}")
+endif()
個人的には POST_BUILD
でコピーするのをよく使ってるけど、そもそもコピーとか考えるのが面倒なので本当にマジで必要でない限りは共有ライブラリにしないようにしている。。
復活した
一発完動でよかった。
DLLのパス指定方法でちょっと悩んだが、とりあえずデバッグ用と割り切って、ネイティブコードをビルドしたディレクトリで、JavaScript側のランチャーを相対パスで呼ぶということにした。
oku@stripe ~/repos/em2c/nodejs/out/build/x64-Debug
$ node ../../../../runtime/run_nodejs.js
現状のNode.jsはESMに対応しているので、JavaScript側はコミットされているものそのままで動作する。
__dirname
が使えない対策
ESMでは ESMなNode.jsには __dirname
が無いため、 import.meta.url
でURL 文字列 を取得し、適当に加工してURLオブジェクトに変換して使っている。
import launch from "./launcher.js";
const path = require("path");
const TESTAPPROOT = path.dirname(import.meta.url) + "/../app/testapp";
const BOOTPROTOCOL = "plain";
const BOOTSTRAP = new URL(TESTAPPROOT + "/app1/example_emscripten_opengl3.js");
const BOOTWASM = new URL(TESTAPPROOT + "/app1/example_emscripten_opengl3.wasm");
Node.jsの fs
APIにはURLオブジェクトが渡せるが、 file://
で始まる文字列ではダメなのでどうしても変換は必要になってしまう。