亀岡的プログラマ日記

京都のベッドタウン、亀岡よりだらだらとお送りいたします。

Groovyから.NETのアセンブリを呼び出したりしてみた

某勉強会で発表できなかったので、現状報告 & メモがてらに。

動機とか

まぁ、一言でいうと「Spock」使いたい!!なんですが。

もっと言うと、「表現力の高いテストライブラリが使いたい」、になるんですが。 C#だとSpecFlowという手もあるし、個人的には嫌いではないんですが、いかんせん二種類のファイルを使わないといけない辛みとか、そこらへんでガンガン進めるまでには至ってないな~、と。 んで、ふとJVMの世界を見ると、やっぱSpockが魅力的に見えるんですよねー。

というわけで、大きな目的は、SpockでC#のテストを書くなんですが、そこはまぁ一旦おいておいて、それの大前提となる、Groovy上から.NETのライブラリを使う、というのに対してのアプローチをご紹介。します。

Groovy上で.NETのライブラリを使う

JVMから.NETのライブラリを使えるようにする

結局JVM上で.NETのライブラリを使う。と言葉に出しただけでなんだか不思議な響きです。

逆はもう皆さんご存知、IKVMを使えば可能です。jarをdllに変換して呼ぶ、なんてのは割と簡単にできます。

しかし、IKVMはそれで終わるようなタマではございません。その逆も、ちゃんとサポートしているのです。

つまり、IKVMStubの出番です。

IKVMStub

IKVMStubは、IKVMCの逆、つまりdllをjarに変換します。

実態はexeなので、使用方法はやはり簡単で、

ikvmstub hoge.dll

hoge.jarが作成されます。すげえ。

とまぁ、すごいんですが、以前書いたように、これで作ったjarIKVM上、つまり.NET上に構築されたJVMでしか動作しません。 つまり単純なラッパーなんですな。まぁIKVMStubなんて名前から察すべし。

じゃあ練習として、mscorlib.jarSystem.Windows.Forms.jarを作成しておきます。Windowsなら、C:\Windows\Microsoft.NET\Framework\とかにあるはず。 とりあえず、.NET 4の方を使っておきましょうか。

ikvmstub -out:System.Windows.Forms.jar "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Windows.Forms.dll"

はい、エラーが出ます。

Error: unable to load assembly 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Windows.Forms.dll' as it depends on a higher version of mscorlib than the one currently loaded

これは、ikvmstubのconfigファイルがこうなってるからですね。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v2.0.50727" />
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

これだと.NET 2.0系のランタイムが入ってるとエラーでちゃうので、スタートアップ要素を逆転させて

    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
        <supportedRuntime version="v2.0.50727" />
    </startup>

これで、OKですね。

IKVMStubにかぎらず、全てのIKVM系のexeは.NET 2.0を優先するので、使用する際はご注意を。

当然、このjarをロードするプロセスも.NET 4でないといけません。

...書いていてわけわかんなくなりますね。

さて、じゃあこれをGroovyから呼んでみましょう。

Groovyで書いてみる

実行時のことは一旦忘れて、Groovyで.NETのライブラリを呼び出してみましょう。

前提として、mscorlib.jarSystem.Windows.Forms.jarを作成しておきます。Windowsなら、C:\Windows\Microsoft.NET\Framework\とかにあるはず。 とりあえず、.NET 4の方を使っておきましょうか。

んで、とりあえずjarへのパスは通っているものとしておきます。 すると、こんなかんじに書けるはず。

import cli.System.IO.*
import cli.System.Windows.Forms.*

def ret =  Directory.class.toString() + 
           "\n\n" +
           Directory.GetFiles(".").collect()

println(ret)

def msg = MessageBox.Show("hoge")

def form = new Form()
form.set_Text("Hoge")
println ( form.ShowDialog())

System.IO.DirectorySystem.Windows.Forms.Formなどを使うものになっていますね。

GroovyをCLI上で起動する

上で書いたように、こいつはIKVM上で立ち上げなくてはいけません。 どうするのかってーと、.NETのネイティブ言語(C#とか)からGroovyを起動してやる方法と、そもそもGroovyの起動をjava.exeではなくIKVM.exeに変更して、直接起動するという方法があります。

まずは前者から行ってみましょう。

C#から起動

C#から起動する場合、今度はjardllに変換しておいてやる必要があります。これに使うのはIKVMCですね。 groovy-all.jarを使うのが手っ取り早くていいです。詳しくは以下の記事を参考に。

DLLさえロードできれば、例えば以下でGroovyConsoleが立ち上がります。

class Program
{
    static void Main(string[] args)
    {
        groovy.ui.Console.main(new string[]{});
    }
}

あとはコンソール上でjarへのパスを通すなり、起動引数に与えるなりしてやれば、この通り。

GroovyをIKVMで直接起動

これはポイントさえ抑えればハードルはそう高くないです。 結局やることってのは、java.exeではなくIKVM.exeに変更する、それだけです。IKVM単体はjavaと互換なので、これで問題ないのです。

んじゃあどこをカスタマイズするかってえと、直接ikvm -jar してもいいのですが、僕は起動バッチ(startGroovy.bat)のjava.exeを置き換えてしまいました。 ちなみに、上で書いたようにIKVMも標準は.net 2.0を優先するので、configファイルも設定します。

こうしとくと、例えばcli_test.groovyにさっきのコードを書いておいて

groovy -cp System.Windows.Forms.jar cli_test.groovy

でちゃんと動いてくれるようになります。素敵素敵。

まぁ、とりあえずこっちの方針で行くほうが本来のGroovyを崩さなくていいと思いますよね。*2

さて、こっから先

というわけで、IKVMへの切り替えオプションどうしようかなぁというのはありつつも、何とかなりそうな気がしてきました。 んで、こっから先はどちらかというとGroovy/Spock周りの知識が足りない、ということになりそうです。

さて、勉強しよう、両方。 だれかおせーて。

*1:これGroovy公式記事だったりします。

*2:@irofさんにも優しく諭された