luarocks(lua5.2 DLL) on VS2015(windows)

前に失敗したのでリベンジ。
VC(VS2013)にluasocketの.makeみながら全ソースぶっ込んで作ったことはあったのですが、monnscript使おうと思ったらモジュールの依存関係があってこれは辛いなぁ…ってことでluarocksさん。

luarocksと言われても…

lua用モジュール管理ツールです。と言われてもちょっとピンときませんよね。
luaが必要最小限の機能しか有していないので、拡張はlua言語ではなくDLLに頼ることになります。となるとコンパイルが必要。その環境依存性の高いコンパイルを吸収してくれる筈のツールになります。筈。
目的のモジュール名をお願いすると、ダウンロードしてきてコンパイルしてへいおまち!してくれる筈の夢のツールです。ありがたい!

とってもかんたんだよ(windows除く)

linuxなどでは特に問題なくluarocksがapt-getやyumなんかで出来ちゃうのですが、windowsだとそうもいきません。具体的にはコンパイルluaのライブラリ「lua.lib」が必要なのですが、これは自力で作る必要があります。
コンパイルが成功するとluaのあるところに「share」と「lib」というフォルダを作ってluaソースと作ったDLLをそれぞれ別々に出力します。
って説明が無いので、最初迷いますよねこれ…。

まずはlua.lib

いきなりですがluaそのものをコンパイルする環境構築が必要です。自分はとりあえずVC2015で試してみます。
説明は省きますが、luaの公式ページからソースを一式ダウンロードして、空のライブラリプロジェクトに全ソースファイル突っ込んでエラーを見てちょっと抜いて、コンパイルして終わりです。詳しくは長くなるので割愛。
必要最低限な設定では以下のような感じ。

構成の種類:スタティック ライブラリ (.lib)
マルチ バイト文字セットを使用する:マルチ バイト文字セットを使用する(luaはこれ重要)
ターゲットの拡張子:.lib
プリプロセッサ:LUA_BUILD_AS_DLL;_CRT_SECURE_NO_WARNINGS
SDLチェック:いいえ
すべての既定ライブラリの無視:はい (/NODEFAULTLIB)

それほど難しくありませんが、後記のエラーのためもうちょっと細かい設定が必要になりますが、今のところはパスします。

ほんとにこのライブラリ大丈夫?

これはやらなくて大丈夫です。
exeでもいいのですが、自分はDLLを使うプログラムがあったのでそれで整合性をチェックしました。新しく空のプロジェクトを作ってDLLに設定、「LUALIB_API,LUA_API」があるソースをぶち込んでコンパイル。アプリに組み込み後、相当でかいlauコードが動いたので大丈夫でしょう。

luarocksダウンロードしてインストール!と思ったら5.1入ってるんですけど…

最初のインストールに使うインストーラー的なものなので問題ありません。が、そのままインストールすると5.1になっちゃうので、別途lua5.2が必要です。上の工程でexeも作っていればそれを、無ければ公式からバイナリが取得できます。
以下のフォルダ構成フォーマットに従ってファイルをコピーして、大元「c:\lua52\bin\」をwindows環境変数「path」に登録します。

c:\lua52\bin\lua52.exe
c:\lua52\bin\lua52.lib
c:\lua52\bin\lua52.dll
c:\lua52\include\*.h    //ソースファイルのヘッダだけ抜き出したもの(本当はもうちょっと絞り込むんですが)

落としたluarocksのinstall.batでインストール。「linstall /?」と打ち込むと設定が出てくるので以下のようなかんじで指定の場所にインストールします。(programfilesは権限が必要になるので今回は回避しておきます)

install /LV 5.2 /LUA c:\lua52\ /P c:\LuaRocks52

/LV:luaバージョン
/LUA:luaのあるところ
/P:luarocksのインストール先

ベコベコベコとファイルのサーチが始まります。この時にベースのluaのバージョンが決定します。なので5.3を用意してこのインストーラでまたインストールすれば5.3のluarocksが共存できます。
途中で止まった場合はそのパスを確認してください。現段階ではファイルコピーと設定ファイルのパス訂正ぐらいしかしてませんので、パスが合っていれば概ね大丈夫です。

ではさっそくモジュールげっと

最初にゲットするものといえばluasocketというのが世の習わしです。pokemonでいうところのpikachuuuuです。

c:\luarocks52\>luarocks install luasocket

わくわく感もつかの間、エラーが出ます。clってなに?
忘れてました、VCのコンパイラを使うので「デベロッパコマンドプロンプト」で開き直して再度チャレンジしましょう。
するとまたエラーが出るはずです。LINK1107!なにやらDLLを組み込んでコンパイルしようとしてます。そこはlibつかってくださいluarockさん。

config-5.2.luaの修正

「config-5.2.lua」がluaだけど設定ファイルになります。中身はテーブルです。ってluaそのものです。こんな感じにDLLになってる「LUALIB」のところを修正します。

...
variables = {
    MSVCRT = 'MSVCRT',
    LUALIB = 'lua52.lib'
}
...

これで一応コンパイルが通るとは思いますが、なにやらリンク時に「???が再読込されてる」とかなんとか言われます。
どうやらlib生成のコンパイラオプションがluarocksの時と異なるので怒られてます。なので、VCを開いてlibのプロジェクト設定を変更します。
luarocksのログを見ると

cl -nologo -MD -O2 -c ?????

となってますので、プロジェクト設定では以下に対応します。

著作権情報の非表示:はい (/nologo)
ランタイムライブラリ:マルチスレッドDLL (/MD)
最適化:実行速度の最大化 (/O2)

これでもう一回rualockさんにお願いすると〜

link -dll -def:core.def -out:socket/core.dll c:/lua5.2.4/bin/lua52.lib src/luasocket.obj src/timeout.obj src/buffer.obj src/io.obj src/auxiliar.obj src/options.obj src/inet.obj src/except.obj src/select.obj src/tcp.obj src/udp.obj src/wsocket.obj ws2_32.lib
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

   ライブラリ socket/core.lib とオブジェクト socket/core.exp を作成中
luasocket 3.0rc1-2 is now installed in c:\lua5.2.4\ (license: MIT)

わーい。エラー無く完了。お疲れ様です。
すっかり忘れている頃ですが、出力はluarocksではなくluaの方の「share」と「lib」にされています。

では使ってみよう…

出力されたファイル群をフォルダ構成を守ってluaを実行する所へコピペすれば準備完了です。
サンプルスクリプトを作って実行させましょう。luasocketのおまじないはこうです。

require("socket")

結果はこうです。

multiple vms detected.

深刻なエラーです。どうやらluaコアと読み込むDLLのlua内部情報が異なると出るそうです。
あぁー、先ほどlib更新したからDLLとEXEも更新しないとということで、この2つもプロジェクト設定を上記に従い再設定、再コンパイル、組み込みます。

multiple vms detected.

ぐえー。どうにもダメみたいです。

multiple vms detectedってなに?

疲れたのでluaの大元のソースをご開帳。

LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver) {
  const lua_Number *v = lua_version(L);
  if (v != lua_version(NULL))
    luaL_error(L, "multiple Lua VMs detected");
  else if (*v != ver)
    luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", ver, *v);
...

versionチェックなんですが、version値の比較の他に、アドレスの比較もやってるようです。
今回はVM Errorって言ってるのでそのアドレスで引っかかってるみたいです。何のアドレスを比較してるの?

LUA_API const lua_Number *lua_version (lua_State *L) {
  static const lua_Number version = LUA_VERSION_NUM;
  if (L == NULL) return &version;
  else return G(L)->version;
}

「static const」のアドレスですか…。きっとluasocket側でcheckversionをcallされてる弾かれてるんですね。luaの大元libをDLLに使ってるので一部2重コードになってますのでアドレスも違います。
つまりlua本体DLLから全命令をcallしなければなりません。なので最初の.confの設定にDLLが指定されていたのでしょう。エラーでますけど。
なーるほーどねーそういうことだったのか…。

まずDLLをコンパイルする

libだけでいけるかなーと思ったんですが、DLL生成が絶対必要になります。
厳密にはDLLじゃなくて、DLL出力した際に吐き出されるlibが必要です。とはいえ1:1対応の存在で別々に取って来てもダメなので、結局コンパイルして両方生成します。
DLLの生成方法の詳細は他のページでご覧ください!
一番簡単なのは全ソースぶち込んでエラー出たのを抜くのがてっとり早いです。(うえでは一端全部入りlibつくってからDLL吐き出してますが、必要なソースの選定に.make見ないとイケナイので面倒です)

lua52.dll    486kb
lua52.lib    30kb

容量は目安です。全部入りlua.libに比べてきわめて小さいlibが出来ます。このlibはDLLに命令をそのままリダイレクトさせるためだけのlibです。
これ使えって事だったの……名前一緒だしわからないですよね……。
この2つを「c:\lua52\bin\」にぶちこんで、「ruarocks install luasocket」してやります。

   ライブラリ socket/core.lib とオブジェクト socket/core.exp を作成中
luasocket 3.0rc1-2 is now installed in c:\lua5.2.4\ (license: MIT)

メッセージは上記と全く同じですが、出力されるcore.dllなどの容量が若干小さくなります。
これをluaの組み込み実行環境に持って行って

require("socket")

エラー無しで通ると思います。
終わり。疲れた…。

おまけ

ほんとは「moonscript」使いたかったんです。
試しに「luarocks install moonscript」して持って行ったら「multiple vms detected.」。え?なんで?
removeしてもinstallリストから除外されないしluarocksが壊れてるのかな?このモジュールは2つのモジュールの上に成り立っているので、それらが更新されていないとかそんな感じなのかな。一応リスト見て全部叩いては見たものの…。
結局再インストールして再コンパイルしたら上手くいきました。