Go言語のcgoは静的リンクが上手く扱えないらしい(@Windows)んだけど
最近はどうなんだろう?と思って試してみました。
ここでは参考までにmrubyを静的リンクしてみようと思います。
動作環境
Go | 1.4.1 | 32bit |
gcc | 4.9.2(TDM) | 32bit |
OS | Windows8.1 | 64bit |
cgoで静的リンク
普段C言語を書く人は、cgoでライブラリを使う場合、以下のように書くと思います。
// #cgo CFLAGS: -Ihoge/include
// #cgo LDFLAGS: -Lhoge/lib -lhoge
// #include <hoge.h>
import "C"
しかし、これではリンクできませんでした。
以下2つはちゃんとリンクできた記述です。
// #cgo CFLAGS: -Ihoge/include
// #cgo LDFLAGS: hoge/lib/libhoge.a
// #include <hoge.h>
import "C"
// #cgo CFLAGS: -Ihoge/include
// #cgo LDFLAGS: -Lhoge/lib -Wl,-lhoge
// #include <hoge.h>
import "C"
1つ目の例とやってることは同じはずなのに、なぜこれでリンクできるのかは調べられていません。
cgoの#cgoディレクティブのパース部分がどうなってるかコードを読む必要あるかも…
mrubyをリンクする(失敗編)
とりあえず、ライブラリの静的リンクの方法が分かったので本題のmrubyのリンクです。
mrubyのビルドは割愛しますが、make
するだけで特別なオプションなどは必要ありません。
さて、以下のコード(コード片)はエラー出てビルドできません。
これでいけると思ったのですがダメでした。
package main
// #cgo CFLAGS: -II:/FreeSpace/C/mruby/include
// #cgo LDFLAGS: -LI:/FreeSpace/C/mruby/build/host/lib -Wl,-lmruby
// #include <windows.h>
// #include <math.h>
// #include <mruby.h>
// #include <mruby/compile.h>
import "C"
出力されるエラーは
main(.text): hypot: not defined
main(.text): _get_output_format: not defined
main(.text): undefined: hypot
main(.text): undefined: _get_output_format
hypotと_get_output_formatがundefinedって言われてますね。
調べたのですが原因が分からなかったので
吐き出されるエラーをdirtyに潰してコンパイルを通します!(なんだって!?)
###追記
割と大切な事を書き忘れてました。
上記の2つの関数はmsvcrtがexportしてるので、-lmsvcrt
を追加するとコンパイルは出るのですが
実行時にエラーが出てクラッシュしてします。
コンパイルエラーを潰す
まず、_get_output_format
を握り潰します
// int _get_output_format( void ){ return 0; }
_get_output_format mingw
でググると、いくつか似た症例が見つかること、
Mingw64でビルドしたらエラーが出なかったのでMingw側の問題かもしれません。
次にhypot
を握り潰しますが、_hypotを使ってくれとあるので_hypotを呼び出すよう実装します。
// double hypot( double a, double b ){ return _hypot(a, b); }
ちなみにhypot
はhypot(a,b) = √(a*a+b*b)
。三平方の定理のアレっすね。
mrubyをリンクする(完成編)
エラーを握り潰すコードとmrubyのサンプルコードを追加した
package main
// #cgo CFLAGS: -II:/FreeSpace/C/mruby/include
// #cgo LDFLAGS: -LI:/FreeSpace/C/mruby/build/host/lib -Wl,-lmruby
// #include <windows.h>
// #include <math.h>
// #include <mruby.h>
// #include <mruby/compile.h>
// int _get_output_format( void ){ return 0; }
// double hypot( double a, double b ){ return _hypot(a, b); }
import "C"
import "unsafe"
func main() {
mrb := C.mrb_open()
defer C.mrb_close(mrb)
cxt := C.mrbc_context_new(mrb)
defer C.mrbc_context_free(mrb, cxt)
mrb_code0 := C.CString("puts 'Hello World ' + 'From mruby.'; m = 3")
defer C.free(unsafe.Pointer(mrb_code0))
mrb_code1 := C.CString("puts 'hypot(m, m+1) = ' + Math.hypot(m,m+1).to_s")
defer C.free(unsafe.Pointer(mrb_code1))
C.mrb_load_string_cxt(mrb, mrb_code0, cxt)
C.mrb_load_string_cxt(mrb, mrb_code1, cxt)
}
Hello World From mruby.
hypot(m, m+1) = 5.0
おわりに
とりあえず、リンクできましたが
いまいち動作が分かってないけど静的リンクできるようになった点、
なぜ、いくつかの関数がundefinedになってしまうか分かってない点
を踏まえると、かなり実験レベルの内容です。
サンプルのmrubyコード以外は動かない可能性もありますが、その辺はご了承ください。
ところでdefer
の使い方ってこれであってるのかな?
mattnさんにご教示頂き
mrb_close
とmrbc_context_free
もdefer
で処理するよう修正しました。