読者です 読者をやめる 読者になる 読者になる

monodevelopのmercurial addinを作ってみた(作りかけだけど)

MonoDevelopにはバージョン管理サポートがアドインとして入っていて、gitとsubversionがサポートされている。svnサポートは使ったことがないので、gitしか分からないのだけど、既に任意のリビジョンに復元できたり、blameできたり、新旧のリビジョンを比較できたり、ローカルの変更を追跡できたりする。マウスクリックひとつで、余計な変更を差し戻したりとか、割と便利だ。

一方、codeplexなんかには、Mercurialベースのリポジトリがそれなりにある。たまにそれらを見ると、「MonoDevelopでもMercurialリポジトリを引っ張ってきていじれるようになればそれなりに便利なんじゃね?」と思うようになった。というわけで、試しに、この既存のバージョン管理アドインをベースに、独自のaddinを作ってみることにした。

https://github.com/atsushieno/monodevelop/tree/mercurial

自分ではgithubを使っているので、現在のアドインだけで十分に満足している。そんなわけで、Mercurialはほとんど使ったことがない(特に自分のコードをMercurialで管理したことは一度もない)。今回は主にMonoDevelopでどうやってアドインを作るのかを勉強するために手を出したようなものだ。

というわけで、今回はこのアドインを作ってビルド出来るところまでの説明を書こうと思う。ただ、今回はこれで完成というものでもない。あんまし資料的価値のあるまとまった話にもならないと思う。

MonoDevelop.VersionControl.Mercurial sshot: file diffs MonoDevelop.VersionControl.Mercurial sshot: file log view MonoDevelop.VersionControl.Mercurial sshot: push changeset diffs

バージョン管理ライブラリについて

MercurialC#からいじるものとしては、javahgをSharpenで移植するとか、いろいろ考えたけど、今回はMercurial.Netを活用することにした。まだWindowsでは試していないけど、Mercurial.NetはWindowsのみで開発されていたようだし(そのためLinuxではまともに動作しなかった)、うちのアドインもWindowsで動くはずと思っている。

Mercurial.Netは、ローカルにあるhg*1のexecutable*2を標準入出力でパイプしながら実行することで、Mercurialの機能を呼び出している。MercurialGPLで配布されているため、これが非GPLコードで呼び出すための方法としてMercurial側から推奨されている。これはsubversionのサポートを担っているSharpSvnでも同じようだ。

(NGitは、実はこの点で大きく異なっていて、gitツールを呼び出すようには出来ていない。元はgitリポジトリを操作するpure Java実装で、それをSharpenでC#に変換している。)

Mercurial.Netについては、他にもMonoDevelopアドインとして必要な機能を実現するために、いろいろなパッチを作ってcodeplexに投げている。ちなみにMercurial.Netの開発リソースは各所に分散しているようだけど、ソースとしては、codeplexにあるStableソースではなく、kilnhgのUnstableソースを、MonoDevelopのcontribソースとして取り込んだ。

アドインのコードベースについて

Mercurialアドインは、MonoDevelop.VersionControl.Mercurial.dllとしてビルドされる。これは、gitアドインであるMonoDevelop.VersionControl.Git.dllと大部分が同じ構造になっている。

最初に、MonoDevelop.VersionControl.Git.addin.xmlをもとに、MonoDevelop.VersionControl.Mercurial.addin.xmlを作成した。それから、中身で指定されているGitサポート用のクラスを、Mercurialサポート用のクラスとして作成し登録することにした。具体的には、addin.xmlの中では型名からGitをMercurialに置き換えて、それぞれクラス名と思われる属性(たとえば Command要素のdefaultHandler属性)に対応するクラスを新規作成して、中身を対応するGitのコードで書き換える、といった作業だ。

addin.xmlは、embedded resourceとして追加しないと、アドインとして認識してもらえない。また、monodevelopのアドインはMONO_ADDINS_REGISTRY環境変数で指定されたパスの中にある必要がある。今回はmonodevelop本体の一部としてビルドしたので、この環境変数の問題は無かったのだけど、代わりにアセンブリのビルド先のパスがデフォルトのbin/Debugではなかったためにロードされないという問題にハマった。

中にはデザイナが呼び出されるクラス(Gtk widgetsなど)があって、その場合はデザイナ上のオブジェクトも新規作成ファイル上にコピーしてやると、コードの丸コピがそのまま動くので楽だ。Gtk.Binがベースにあるクラスのデザインは、全体をまるごとコピーして自分のクラスに追加することができるが、Gtk.Dialogがベースにあるクラスのデザインは、ダイアログの内容のみがコピーできる(OK / キャンセル等のボタンも含むコピーは、個別に行う必要がある)。

ダイアログのUI要素はどうせgitのオリジナルとは一致しないので(機能が一致しないのだから当然)、後でいろいろ隠すことになる。

ソースコードの管理

このバージョン管理アドインのソースコードは、まず動くようになった時に公開したのだけど、それからしばらくはzipアーカイブでバージョン管理・バックアップされていた(一体何のアドインを作っていたのかという話である)。

これではまともにコードをいじれないので(ソースに変更を加えるたびに何かが動かなくなったりしたので)、とりあえずmonodevelopをgithubからforkして、独自のブランチを作ることにした(順番としては、先にcheckoutしていたmasterからbranchして、そのremoteを自分のブランチにしたのだけど)。これでとりあえず自分のコードは安心してcommitしてpush出来るようになった。

しばらくすると、Mercurial.Netに対する変更が増えすぎて、だんだんdiffを追えなくなってきた(パッチは全部codeplex上にフィードバックしてあるのだけど、同じファイルに対する複数の変更が出てきたりして厄介になった)ので、これもmonodevelopのcontribの下にコピーを作って、そこで管理することにした。hgでcloneしてきたものをまるごとcontribの下に持ってきている(hgのファイルもgit上に上がっている)。同じディレクトリでhg updateしながらgit commit/pushできる。

ちなみに、monodevelopは、monoのリポジトリにあるモジュールについてはgit externalを使ったり(cecilとか)、configureでpkgconfigでパッケージをチェックしてビルドしたりしているが(gtk-sharpとか)、monoリポジトリの外部にある依存ライブラリはcontribに置いてある。

あと、monodevelopのメインに含まれるアドインの依存ライブラリと、メインに含まれないアドインは、また扱いが異なる。最新のmonodevelopは、もはやMain.slnだけでメイン部分をビルド出来るようになっているのだけど、これにはextrasのアドインは含まれない(何がextrasになって何がmainに残るかは、今後しばらくのうちにいくつか変更される可能性がある)。

本来、今回作っているようなアドインは、monodevelopのコードの一部としてビルドされるべきではなく、個別にアドインとしてビルドして、MonoDevelop.VersionControl.dllなど一部のライブラリにのみ依存するかたちで公開されるのが筋だ(と思う)。本当はそういうかたちでまとめたかったのだけど、現在アドインのinstallable package (*.mpack) をビルドする方法が存在しない(バグっていて機能していない)ようなので、それが使えるようになってから考えたい。

機能的な制約

このアドインは、残念ながら作り始めてから実質まだ4日しか経っておらず、使えない/確認していない機能がたくさんある。現在の機能一覧のようなものは以下にまとめている。

https://gist.github.com/3116679

機能サポートについては、Mercurial.Netに不足している部分については、自前のforkではどんどん追加しているので、ここでは問題にはならない。一方で、hg本体に機能追加が必要な部分もあって(たとえばblameが実装できないのはhg annotateの出力をstyle指定できないことに由来している)、これは今後気が向いたら自分で実装するかもしれない(pythonで書かれたhgのソースを読んで書く作業になる)し、他の人が実装してくれるのを待つかもしれない。

その他にも単に試していない機能がいくつかあって(mergeやbranchまわり)、基本的にはまだ期待通りに動かないと思った方が良いと思う(特に更新系の機能には触れないほうが安全だ)。

あと一番問題なのが、割とよく謎のクラッシュ終了を起こすことだ。普段VCSサポートを活用していないので、確かなこととは言えないけど、gitサポートがクラッシュエラーを引き起こすという話は知らない。

今後のバージョン

今日で連休が終わってしまったので、しばらく開発は進まない(か、進んでもノロノロだ)と思う。もしかしたら、今あるバージョンが最後になるかもしれない。というのも、他にmonodevelop-hgを作っている人がいることに今日気付いたので…(試していないけどそっちの方が安定しているに違いない)

独自アドインの作り方を勉強するという目的はそこそこ達成されたので、その意味では成功だったと思っている。