オープンソース化されたMSBuildについて

昨日のdotnetconf2015でMSBuildオープンソースになりましたね。

github.com

Miguelがゲストでセッションに参加していて、もしかして何か来るかなと思っていましたが、アタリでした。

このMSBuildのソース、基本的に新しいビルドエンジンのみが対象で、逆に旧エンジンしか無いmonoでは*1、新しいものにdllだけ差し替えるというより、別のツールにするといいかなあと個人的には思っています(xbuildじゃなくてmsbuildにする)が、既に移植を担当する新人ハカー*2がいるので、その辺はそちらの采配次第です。旧エンジンも新エンジンも同じファイル名のtargetsを使っているので、分けて使うのも簡単ではないかもしれませんし。

このMSのコード、昨日仲間内で眺めていたのですが、やはりだいぶWindows依存のコードが多く("DllImport" とか、それで引っかかる "NativeMethods" をさらに検索してみると大まかにわかります)、やはり簡単に取り込めるというわけにはいかなそうです。

ただ、referencesourceと異なり、こちらはMSもクロスプラットフォームなビルドツールにしていく方向性であるようです。

http://blogs.msdn.com/b/dotnet/archive/2015/03/18/msbuild-engine-is-now-open-source-on-github.aspx

どちらかというと coreclr, corefx, roslyn などと同じ、継続して開発・公開していくスタイルということになりそうですね。githubリポジトリも、masterの他にxplatというブランチが切られていて、早速さまざまなpull requestが取り込まれているようです。

新しいMSBuildエンジンが使えるようになると、新機能(というほど新しくもないのですが)を無理やり旧エンジンに埋め込むことなく使えるようになりますし、ビルドのパフォーマンスもある程度向上するのではないかと期待しています。

…というわけで、msbuildの取り込みは、短期的には実現しないでしょうが、それほど遠くないうちにmonoでも使えるようになるだろうとは思います。

*1:新しいMicrosoft.Build.dllのエンジンは、わたしがちょっとだけ実装していたこともあるのですが、時間が取れないままで結局使えませんでした

*2:って言ってもだいぶ前にmonoをいじっていた昔の仲間ですが

Mono x referencesource updates

最近ほとんど告知とまとまったネタしか書いていなかったので、ひさびさにmonoの細々としたアップデートを、主にreferencesource絡みでいろいろ書いてみようと思います。

最近monoの中で主に行われているのは、Microsoftから公開されたreferencesourceの取り込み作業で、大部分はわたしを含む3人くらいでやっています。他の2人がmscorlib.dllとSystem.dllの内容を適宜置き換えている間に、わたしも適当に参戦しつつ(EncodingとかSystem.Diagnostics.Traceとかを置き換えました)、この前ようやくSystem.Xml.dllの大部分(XmlSerializer以外)とSystem.Xml.Linq.dllの全てを置き換えました。つまりわたしのコードはもうSystem.Xmlにはほとんどありません(!)

XmlSerializerが古いままなのは、先日GoAzureでもしゃべりましたが、referencesourceのXmlSerializerが動的コード生成に依存していてiOSで使えなくなる、という問題のせいです。これは誰かがreferencesourceの実装に沿ったインタープリターを書けば取り込めるかもしれません(!) なんか、個人的に「オマエでもいいんだけどカネ払ってもいいからやってくれる人いないか?」とか頼まれたりしているので、誰かやりたい人がいたら繋ぎます。(わたしは一応フルタイムで仕事しているしそんなのやる気は…w)

mscorlib.dllやSystem.dllについては、ソースコードのどの部分がreferencesourceに置き換えられているか、ビルドのレスポンスファイルのソースを見てみると、大まかな状況が掴めてくるのではないかと思います。collectionsとかcomponentmodelとかは置き換えられていますね。

わたしはそれに続いてWCFの取り込みにも目を向けているのですが、当初われわれ(主にMiguelとわたし)が「マネージドコードだし簡単なんじゃね?」と言っていたSystem.Runtime.Serializationがガチガチの動的コード生成仕様だったので、これはどうにかせんとなあという感じです。コレが取り込めないとSystem.ServiceModel.dllも取り込めないので、困っちゃいますね。あといろいろ足りない部分があって、ビルド出来るところまで持っていくのがひと苦労です。まあこれは11日にreferencesourceにSMDiagnosticsが追加されて、少し緩和されたかもしれませんが。

ただ、個人的には先にSystem.Data.dllを片付けたいという気もしなくはないところです。アレはメンテナーがほぼ壊滅状態なので…まあそれを言ったらWCFも(ry

あと最近referencesourceにWorkflow Foundationも追加されたので、これも取り込みたいところですが、こいつはWCFに依存している部分がある程度あるので、それもどうにかすべきところですね。WFにはSystem.Xaml.HostingとかXamlBuildTasksとかあるくせに、System.Xamlのソースはまだ公開されていないという…(アレが置き換えられるとわたしの成果がまたひとつ消せるのですがw)

ちなみにもう1人は新しいTLS実装に取り組んでいるみたい。この辺はreferencesourceがWindows に依存していて役に立たない部分であるわけです。

SocketとかHTTPSの実装まわりは、いろいろバグが出てくることが多いので、この辺はうまく置き換えられるようになると良いなあとは思います(Socketは難しいかな)。

とりあえず現在進行形の作業はこんなところでしょうか。ちなみにreferencesource統合作業については、パブリックなtrelloボードがあって、ここに計画や(部分的に)進捗なども記録されているので、興味がある人は見てみるといいと思います。referencesourceだけでなく、coreclrからGCのコードを引っ張ってくる、みたいな野望もリストアップされていたりして、割と面白いかもしれません。

https://trello.com/b/vRPTMfdz/net-framework-integration-into-mono

あと、先日Google Summer of Code 2015が発表されたのですが、referencesourceを取り込む作業もこの一部として含まれています。これももし興味のある人がいたらぜひどうぞ…まあGSoCって日本の大学の夏休みと合わないんですけどね…

http://monosoc.blogspot.tw/2015/03/mono-summer-of-code-2015.html

とまあ、ざっとこんなところでしょうか。本当はこういう話はこの辺のイベントに顔を出していろいろしゃべると良いのでしょうけど、たまたま裏番組でしゃべることになってしまったので、泣く泣くこうして落書きにして出した次第です。

この辺の作業については、またしばらくしてまとまった進捗が得られたら && 気が向いたら書こうと思います。

GoAzure2015 - オープンソース.NETと Mono / Xamarin の今後について

今日(いやtechnicallyもう昨日ですが)GoAzureのセッションに参加していただいた皆様 & お招きいただいたJAZUGの皆様、ありがとうございました。

今回のスライドはこちらに上げておきます:

[GoAzure2015] オープンソース.NETと Mono / Xamarin の今後について - Google スライド

今回はデモなしでしゃべりっぱなしでしたが、時間オーバーしちゃったのと最後少し巻いてしまった以外は、まあ過不足ない内容になったのではないかと思います。

会場でも、ここ1年くらいの.NET/Xamarin系の繋がりにある東京近辺の人々と、だいぶ総ざらいに近い感じで話せたので、満足しています。

そんなわけで、今後の機会にもよろしくお願いします。

2014年の終わりに感謝する作品集

元々クリスマスプレゼント的に書こうよっていう話だったはずなのに年末に書いて意味あるのかよくわかりませんが、今年1年の間に出会った作品に感謝し紹介するエントリです。例によって念のために書いておきますが、今年出た作品には限りません。

(1) monochromia "monograph"


monochromia(モノクロミア) 『monograph (モノグラフ)』GINGA | ::UMAA Inc.::

今年一番聴いたのはコレでした。去年45:64について書いたので、改めて書くことはないと思います(今年は586,401のほうがよく聴いていたかも)。thorndique, lemmのお2人はもっと売れてこないかなあ。

lemm作品ではnoctilucaをよく聴いていました。もうしばらく作品発表されないのかな? また聴けるようになる日を楽しみにしたいところです。


【lasah】"I LOVE YOU,"【クロスフェード】 - YouTube

あとOPHANILがえらく大掛かりでびびりましたね。


重厚な世界観に吸い込まれそう──西島尊大×深水チエ『OPHANIL』

(2) millstones "セカイカタログ" ... の各「歌ってみた」カバーたち

オリジナルは多分このジャンルでは有名だと思うのですが、わたしはvocaloidの歌より歌い手がカバーした曲の方ばかり聴いています(特にこのアルバムの頃のvocaloid使いの技術力は全体的にまだまだで、日本語らしい歌い方はあまり出来ていないですしね…)。ベスト1を選べなかったので、いくつか並べます。


『The Embedded Blue』歌ってみた 蘭子* ‐ ニコニコ動画:GINZA


【クロライドに没む】歌ってみたver.祭屋 ‐ ニコニコ動画:GINZA


計画都市 歌ってみた 【リツカ】 ‐ ニコニコ動画:GINZA


【F9】カガリビト 歌ってみた ‐ ニコニコ動画:GINZA

 

全体的に楽曲のレベルが高く安定していることもあって、これだけ質の高いカバーがあるのもすごいですね。

(3) cadencii


cadencii/cadencii · GitHub

3つ目までvocaloid関係ですが、音楽ではなくソフトウェアです。vocaloidといいUTAUといい、昔から日本の音楽ソフトはプロプロエタリなものばかりで全体的に残念感があるわけですが、そんな中でGPLv3で公開されている歌唱入力ソフトウェアです。素晴らしい。細かい部分まで作りこまれていて、Ubuntuでも使えるようになる日が来るのが楽しみです。(謎)

直接自分が使っているわけではないのですが、台湾でVocaloidのようなものを作ろうとしている知人のグループがありまして(まあ合成エンジンを作っているわけではなく、現状ほとんどUtauのようなものを使いまわしているだけですが)、彼らはこのCadenciiを使ってWindows上でvsqファイルを作成しているようです。しかもjcadenciiというJava移植版まであるんですね。

台湾でも今年になって夏語遙というUtauベースの音声DBが公開されて、これからは台湾でも消費するだけでなく生産する側にも回っていくといいんじゃないかなあと思っています。

(4) Roslyn


.NET Compiler Platform ("Roslyn") - Home

コードそのものというより、それをオープンソースにして公開するというかたちで、.NET開発者の期待と不安に最善のかたちで答えたことをまず賞賛したいです。.NETは本体もオープンソースにして出してきたりと、他にもここに並べ称えられるべきものがあるかと思いますが、ちゃんとビルドできて使えるソースをベストのタイミングで公開した点で、Roslynのインパクトは大きかったですね。monodevelopに統合されて使えるようになる日が来るのが楽しみです。

…と、今年はこのくらいにしておこうと思います*1。来年も素晴らしい作品に出会えることを楽しみにしておきます。それではまた来年。

*1:単に時間切れとも言う

Xamarin Monkeyの歴史を振り返る

どうも、Xamarin Advent Calendar 2014、2度目の登場です。昨年のクリスマスイヴにも、Xamarinの2年半を振り返るというゆるふわ話を書いて、今年はもうゆるふわ話はいいだろうと思っていたのですが、最終日に登録されていたはずのゆるふわ話が消えてしまったようなので、代わりにさらにゆるい話を書こう、ということで今日はXamarin Monkeyの歴史を振り返ります!!

あ、書くまでもないと思いますが、技術要素ゼロの100%ネタポストです。Rでサルを描画する関数とかは無いので期待しないでください! *1

Xamarin Monkey、Xamarin使いの皆さんはご存じの方も多いかと思いますが、こいつです。

f:id:atsushieno:20141225010953j:plain

サルですね。どう見てもサルです。

「われわれ」のマスコットキャラクターとしてのサルの歴史は長いです。もう15年くらいでしょうか。おそらくその起源はXamarin、Mono、Ximian (Helix Code)、GNOMEといった会社やプロジェクトを引っ張ってきたMiguel de Icazaが何気なくサルが好きだったということにあるはずです*2。Xamarinは設立わずか3年半ですが、XamarinはMonoの会社であって、Mono自体はXimianが始めたものであって、XimianLinuxデスクトップGNOMEを発展させるために創設された会社である…という歴史のほうが長いわけですね。

Mono Project FAQには次のような項目があります。

What does the name “Mono” mean?

Mono is the word for ‘monkey’ in Spanish. We like monkeys.

 

そう、Monoはスペイン語で「サル」という意味です。monoのググラビリティが低いのは日本だけじゃなかったんですね。

ちなみに、Ximian以降は、サルの名前が使われているものがいくつか散見されます。GNOMEでCORBAを元にしたコンポーネント オブジェクト モデルを実現するはずだったライブラリはBonoboという名前でしたが(BonoboについてはMicrosoftのサイトにMiguelが大昔に受けたインタビューで説明されています)、もちろんこれはボノボ猿からとった名前です。Ximianの名前は、類人猿を表すsimianという単語をもじっています。Xamarinもタマリンという猿の一種からもじっています*3。Xamarin関係で名前の由来がよくわからんものがあったら、猿に由来するものを疑ってみるのも一興でしょう。

Xamarin以前(HelixCode - Ximian - Novell時代)のサルは、この1種類で固定されていました。

f:id:atsushieno:20141225010944j:plain

この時代のサルにはRupertという名前があります。Xamarinのサルには確かXanderという名前が与えられています。この古いサルは実はXimianで作ったわけではなくて、単に他で売られているものに独自のTシャツを着せているだけです。同じサルがThinkGeekではTimmyという名前で売られています。Rupertは今は亡きOrkutにもアカウントをもっていて、その性格はbysexualでparty every nightなとんでもない奴だったのですが、まあもう昔の話ということで…

Xamarinのオフィスを見たことがある人もごくまれにいるかと思いますが、いたるところにチマチマとサル人形が飾られています。実のところ、ぬるいです。まだ。わたしは在籍していなかったのですが、Helix Codeの頃なんか、もっと遊んでたみたいです。わたしのお気に入りは、写真だとこの辺ですね。

https://www.flickr.com/photos/pteichman/sets/178561

https://www.flickr.com/photos/pteichman/sets/178545

しかし一番の傑作はやっぱりコレでしょうか。Knuthを読んでGtkプログラムを書くアイツが…


Code Monkey At Work - YouTube

これだ、これだよ! 人形作ってるだけじゃダメなんだ! 大事なのはスタイルの継承ではない、精神の継承である! (何)

さて戯言と昔話はこのくらいにしておいて、Xamarin猿に焦点を移しましょうか。Novell時代にはNovellのbudgetでしか仕事が出来なくて、サルの量産も出来ずに歯がゆい思いをしていたわれわれですが(!?)、Xamarinがローンチされて資金源も完全に独立したこともあり、その機会に新しくサルを独自にデザインし直しました。とはいえ初期の頃は割と迷走していたものです。迷走の歴史をごらんください。

f:id:atsushieno:20141225010946j:plain

確かこいつが初代型。ボミョーです。ほどなく2代目が出現します。

f:id:atsushieno:20141225010949j:plain

ちょっとかわいらしくなりましたね。ちなみに、この後今のバージョン(最初に出したもの)になったわけです。わずか3年で3代登場しているわけですね。

これらは皆、うちの部屋にいる連中です*4。かつては、これらを混ぜて配布していたように思いますが、今は黒いの1種類で固定されています。正直、初期のデザインはイマイチだったので、持ち歩くのには(!?)向いていなかったわけですが、今のはまあアリだと思います。ちなみに今のデザインにネタ元があるという話は聞いたことがありません。

Xanderには、カスタマー、パートナー、MVPなど、何だかんだでいろいろサルを配る機会があって、Rupertの頃と比べるとだいぶ希少価値が下がった感があります(まあThinkGeekで代替品が買えるという話は置いといて)。Rupertは実のところ少し再生産していて、絶滅危惧種ではなくなっている…というのは内緒なので見なかったことにしてください。

そんなわけで、こんくらい書けば、Xamarin Advent Calendarのシメにふさわしい、壮大なエントリーとなったんじゃないかと思いますので、前半はこの辺でシメさせていただきます。後半に続く!

----

というわけで後半です…になるはずだったのですが、ごめんなさい、「おまけ」です。

f:id:atsushieno:20141225163222j:plain

座間市のほうの ざまりん とコラボ写真を撮ってきました()

ご協力いただいた座間市役所の方、ありがとうございます。

ちょっといろいろ話を聞いて書く時間が無かったので、ざまりん の話はナシで、写真だけです。ざまりん人形は市役所のカスタマーになると送りつけられる…という話はなくて、市役所の売店で2000円で売られています(今年中は在庫切れだそうですが…)。 手元のXamarin猿とコラボ写真を撮りたい方はぜひどうぞ。

というわけで、24日まで真面目なネタで続いてきたXamarin Advent Calendarも、最後にとんでもないものが来てようやく終わりです。参加された皆様、特に立ち上げた あめいさん (@amay077)、おつかれさまでした! それではまた来年(?)

*1:参考: https://twitter.com/Akiyah/status/532331051620638720

*2:いま彼個人がどう思っているかは知りませんが、いったんそういう方向でいろいろ動き始めたので…

*3:MozillaJavaScriptエンジンでも使われていた名前ですし、Mozillaでも「ほげほげmonkey」という名前を多用していますね

*4:全く関係ないのですが、知人がわたしの不在時に我が家に泊まりに来たことがあって、「扉を開けたらこいつらが揃ってこっちを見ていて、まるで監視されているみたいだった」と感想をもらしていたのが印象的でした

mcsの最新情報と今後の展望について

C# Advent Calendar11日目は、C#開発環境の異界、Monoプロジェクト方面からお送りします。って10日目もそうだったみたいですが(しかもとっかかりなのにInternalCallとか、けっこう踏み込んでる!)

Monoを取り巻く環境は、この1年間で大きく変わりました。Microsoftは大々的に.NETをオープンソースで公開する方向に向かい、コミットし続けていて、4月にはC#VBコンパイラー インフラストラクチャーであるRoslynが、11月には.NET Frameworkの(一部の)ソースコードと、新しいモジュール指向のフレームワーク再編成となる.NET Coreが、それぞれ公開されました。.NET Coreのソースコード公開にはRyuJITも含まれるということで、これが実現すると、ランタイム、コンパイラ、クラスライブラリのフレームワーク3大要素がすべてオープンソース化されることになります。

そういった状況下にあって、もともと.NET Frameworkオープンソースで実装していくという起点から始まったMonoプロジェクトはどうなっていくのか、ということは当然ながらさまざまな人が疑問に思うところでしょうが、一言でいえば「.NETのうまいところを取り込みながら継続して開発されていく」、ということになります。その辺については、何やら来月行われるらしいGoAzureというイベントでちょろっと(50分くらい)しゃべりますんで、興味のある方はぜひ聞きに来てやってください。


Japan Azure User Group 【リニューアル準備中】 GoAzure 開催決定

さて今日はC# CalendarなのでC#コンパイラmcsの最新情報…と書くと無駄にカコイイですが、まあ現状ということですね…について(のみ)書こうと思います。まあ、最新情報だけだと知らない人(ほとんど)が光の速さで置いてきぼりになるので、適宜、過去を振り返りつつ…

mcsの変遷

mcsは、monoのC#コンパイラであり、monoの一部に含まれています。monoのソースツリーには、トップレベルに "mcs" というディレクトリが存在していますが、この中にはC#コンパイラであるmcsと、C#で書かれたクラスライブラリが存在しています。この一見妙なディレクトリ構成は、歴史的事情によります。もともと、monoのソースコードはCで書かれたランタイムを中心とする "mono" と、C#で書かれたコンパイラおよびライブラリを中心とする "mcs" の、2つのCVSリポジトリから成っていました。やがてSubversionに移行し、その後さらにgithubに移行する時点で、この "mono" の中に "mcs" を取り込むことになったのです(そうしないと2つのリポジトリの間でバージョンを合わせるのが面倒だったので)。

mcsは、開発当初からC#で書かれていました(MicrosoftcscC++だそうです)。当初はmcsでmcsをビルドすることは出来なかったので、Microsoftcscを使ってビルドしていました。もっとも、Monoのバージョン1.0が出る以前に、セルフホスト(自分で自分をビルド)出来るようにはなっていました。

mcsには、歴史的には特定の.NET Frameworkのバージョンに対応する、いくつかの派生物がありました(.NET 2.0に対応するgmcs、.NET 4.0に対応するdmcs、Silverlightやモバイルのプロファイル「.NET 2.1」に対応するsmcs)が、現在ではmcsで統一されており、gmcsやdmcsといったUnixスクリプトは、内部的にはmcs -langversion:* で代用されています。昔は実行バイナリそのものが異なっていましたが、それはmcsがSystem.Reflection.EmitのAPIを使用していて、このAPIでは(mcs自身を)実行中のmscorlibに対応するバージョンのILしか生成できなかったためです。現在では、IKVM.Reflectionというライブラリを使って(Cecilのようなものですが、APIはSystem.Reflection.Emitと概ね同等です)、この制約を受けることなくILバイナリを生成しています。

mcsのソースコードには、コンパイラスタンドアローン テストが含まれており、mcs/testsには正常系の、mcs/errorsには異常系の、テストが大量に存在します。

C# 6のサポート

mcsの開発は、Roslynが公開された後の現在でも、継続して行われています。githubにあるmasterブランチのmonoに含まれるmcsでは、C# 6.0の機能の多くが実装されています(C# 5までの機能は完成しています)。具体的には

  • indexed initializers
  • auto-property initializers
  • static using statements
  • nameof operator
  • async in catch / finally
  • expression-bodied members
  • null propagation
  • exception filters

あたりは実装されているようです。ていうかこれで全部じゃね? 上記以外だと、わたしの知る範囲では、string interpolationがC# 6仕様上どうなったのか、だけが気になるところです。mcs(C#コンパイラ)は、昨今ではかなり早い段階で新機能を実装できています。Microsoftが新機能の正式版をリリースする頃には、実装は終わっています。

しかも、以下のような、「C# 6に入れるつもりだったけど入れなかった」構文もちゃっかり実装されていて、ちゃんと「C#6にその機能はない」と言ってエラーにしてくれます。

  • declaration expression
  • primary constructor

ちなみにmasterのmcsでは、C# 6の機能はデフォルトで有効になっています。かつては -langversion:future というコマンドライン オプションを必要としていましたが、このオプション値はdeprecatedとなりました(futureのみです。-langversionそのものは有効)。masterにあるものに、これは必要ないということでしょう。これは、monodevelopC#プロジェクトを作った場合でも、対象フレームワークが.NET 4.5(以降)であれば、-langversionオプションが追加されず、これらの新機能がデフォルトで有効になる、ということでもあります。

Compiler as a Serviceとしてのmcs

mcsは、最初からC#で書かれていたので、Mono.CSharpというライブラリとして、他の.NETアプリケーションに組み込んで使うことが可能でした。

Mono.CSharpの利用例として、最初はC# shellというREPLコマンドラインツールが開発されました。monoがインストールされた環境であれば、csharp.exeもインストールされていると思います。このREPL機能は、今年になってXamarinがXamarin Sketchesという「インタラクティブ」機能を発表したことで、再度脚光を浴びたのではないかと思います。Xamarin Test Cloudにもインスタントにテストを書いて実行する機能が追加されて発表されましたね。csharpシェル、ちまちましたことを動作確認するのに便利です。わたしも「C#6の新機能どこまで使えるのかなー」とか確認するのに使ったばっかですしね。

Mono.CSharpの応用にはもうひとつ、NRefactoryというライブラリがあります。これは、オン ザ フライでソースコードを解析して結果を取得し、またソースを加工できる、いわゆるいわゆるCompiler as a Service、あるいはLanguage Serviceと呼ばれる機能を実現するものです。Roslynも本来的にはこれを実現するものです。NRefactoryとRoslynの関係については、後で詳しく書きます。

Roslynとmcs.exe

Monoチームは、Roslynのオープンソース化を歓迎しています。将来的にRoslyn単一のソースで.NET開発が.NET Framework上でもMono上でも行えるようになれば、開発コストもいろいろ下がることでしょう。

ただ、Roslynへの切り替えは、あくまで将来的な展望であって、今すぐにできることではありません。ひとつにはコンパイル速度の問題があります。mcsは何だかんだでもう10年以上の歴史があって、だいぶリソースが限られていた頃から、要所要所で最適化してきたコンパイラーです。今ではある程度改善されているかもしれませんが、Roslyn公開当初のコンパイラーはmcsの10倍遅かったそうです。

(個人的に覚えているのは、わたしがmcsをハックしていた時期がちょっとだけあって、mcsがまともに行・列番号をレポートしてくれないので、イチから実装し直しました。そのために最初はIXmlLineInfoみたいな構造を作って持っていたのですが、そのやり方にストップがかかります。「そのやり方だと重くなる。int1つにして、そこにファイルインデックス・行・列を保存してくれ。」 当時は「まじか…!!」と思いましたが、実際ソースは大量のトークンから成っているわけで、そうするとメモリ消費コストがだいぶ下がったんですね…。そんなわけで、今でも列番号は254が最大値です。)

そんなわけで、現状、コンパイラーとしてのmcsを置き換えることはありません。mcsはmonoの中でも割と優秀な部分で、フロー解析はわたしの知る限り完璧だし、エラーや警告のレポートもcscとほぼ違いはなく、言語の最新版にも追従していて、文句なく使える部分なので、これをRoslynで置き換えるユーザーレベルでのメリットは、ほとんど無いでしょう。

RoslynとNRefactory

ではRoslynは全然使われていないのかというと、そういうわけではありません。Roslynにはコンパイラー インフラストラクチャーとして、Compiler as a ServiceあるいはLanguage Serviceを提供している側面があります(というかそれが主要な機能ですが)。これはMonoDevelopSharpDevelopにおけるNRefactoryに相当する部分です。

XamarinでMonoDevelopとNRefactoryを開発しているハッカーが、NRefacotoryに "roslyn" および "roslyn-mono" というブランチを作って、そこにRoslynをNRefactoryに「取り込んだ」バージョンを公開しています。

https://github.com/ICSharpCode/NRefactory/tree/roslyn

…あれ? 取り込み? 置き換えじゃないの? と思われるかもしれませんが、RoslynはまだCompiler as a Serviceを実現するためのコア部分をブラッシュアップしている段階です…と書くと明快でないので、軽く説明しましょう。と言っても、2年前に同じ話を書いたことがあるので、その焼き直しということになりますが…

NRefactoryでもRoslynでも、その機能は(1)基本的な機能と(2)応用的な機能に大別できます(もっと細かくもできますが、今はとりあえず2つ)。編集中のソースコードに対して、オン ザ フライで解析をかけて、コンパイルエラーなどを報告する機能は必須(1)です。そうではなく、解析したソースコードの内容をもとに、変数名の変更(セマンティックに同一の識別子をもつ変数参照の変更を伴う)や、解析結果から一定のパターンを認識してソースを加工するリファクタリング機能(NRefactoryではCode Actionsと呼ばれます。Code Issuesでも加工機能があるかも)は、どちらかというとアドオン的なもので、(2)必須ではありません。

Roslynは、その基盤を安定させた上で、追加機能を実現しなければなりません(もうVS2015のリリースを意識した開発過程に入っているわけですし)。そのため、NRefactoryで実装されている大量のソース分析の機能が、実はRoslynにはあまり実装されていませんし、まだその実装に大量の開発リソースを費やすタイミングでもないのです。というわけで、NRefactory側としては、Roslynはあくまで一部を「取り込む」かたちで利用しています。

そして、NRefactoryのroslynブランチは、まだmonodevelop本体では利用されていません。これも、今すぐ取り込めるわけではないので、NRefactoryにRoslynを馴染ませながら、何らかの時点でそのブランチに切り替えることになるのではないかと思います(わたしの予測です)。NRefactoryとRoslynは、APIにはそれなりの違いがあり、MonoDevelopのtype system databaseはNRefactoryベースで作られているので、単純に「Roslynそのもの」への置き換えが効くわけでもありません。

もっとも、MonoDevelopで現状使われているNRefactoryでも、上記(2)の部分については、まだこれから実装の品質を改善していく必要があるところです。次のMonoDevelop 5.8では、Source Analysisがデフォルトで有効になる見通しですが、実は内部でわたしが個人的にクラッシュを頻繁に経験していて「もうちょっと待ってほしい」と抵抗した部分でもあります。(問題のあるコードを少し直してもらい、クラッシュはだいぶ減ったので、デフォルト有効にすることに同意しました。)

じゃあ早めにRoslynに移行しちゃったほうがいいんじゃね?と思われるかもしれませんが、問題の原点がパフォーマンスにある部分が多く、そうするとRoslynでは(現状)さらに状況が悪くなるので、やはりこれもVS2015に伴うRoslyn安定版のリリースに続く(と思いたい)最適化を待ったほうが良いところかなあ、といったところです。Visual Studio上でも、(重くなるとは言われるものの)現状でそれなりのパフォーマンスで動作している(であろう)ReSharperには、まだアドバンテージがあるかもしれませんね。

総括

以上のように、Roslynのオープンソース化が発表された後でも、monoはmcsから直ちにRoslynに移行できる状態ではなく、Roslynの今後の進化に期待しつつ、NRefactoryから準備をしていって、いずれは切り替える、という前提で、mcsの開発を継続していくことになります。幸い、mcsはもともとC#最新版への追従も早く、実装も安定しているので、しばらくはこのまま特に問題なく続いていくのではないかと思います。

Xamarin.Androidのproguardサポートについて

Xamarin Advent Calendar3日目のエントリーです。今年は全部埋まっていて気楽ですね! まあ去年も2回書いただけですが。

 

さて、もうだいぶ前になりましたが、Xamarin Evolve 2014のライトニングトークで、Xamarin.Androidでちょろっと実装していたproguardサポートについて話しました。今日はそのネタについて書こうと思います。LTで話したことに、必要になりそうな前提知識をちまちまと追加しただけで、その後追加された目新しいニュースはありません。

 

はじめに残念なお知らせですが、このproguardサポートの機能、秘密ではないのですが次のリリースでは大々的に告知されません。何が起こったかというと…IDEに必要なコードが何も追加されていなかった上に、QAが何もやってないことが判明したんですね(!)。そんなわけで、10月には「5.0に入れるよ!」としゃべったのに、告知はされないという何とも残念な事態です。 まあ個人的には「使えりゃいい」ですが…

Proguardとは何なのか?

proguard。聞いたことがない人も多いと思いますが、Android開発ではよく使われるSDKツールです。proguardが何をするかというと、主に2つの用途があります。

  1. 難読化 (obfuscation)
  2. 不要コードの削除 (dead code elimination)

Androidで使われるJavaライブラリの中を覗くと、たまにaだのbだのといった名前のクラスが散見されますが、これらはproguardで難読化されたものと思えば良いです。難読化については、今回は話題にしないので(Xamarinではサポートしません)、主に不要コード削除の話をしたいと思います。

 

不要コード削除が何のために行われるかというと、主に、アプリケーションサイズを縮小する、ということが挙げられます。最近では、Google Play Servicesのjarだけでも3MB近くあります。これがそのままアプリケーションに含まれるとなると、けっこうな負担増です。自分のアプリでは、Google Play Servicesのほんの一部の機能しか使いたくないのに。地図を出すだけなのに、GamesのGoogle Plusリーダーボードの機能なんかいらんですよね。

 

不要なコードが多く含まれると、他にも問題が生じてきます。

  • Javaコードはapk作成時にdex形式に変換され、さらにtarget上でdexから実機コード(DalvikのodexやARTのelf)に変換されますが、それぞれのコストが大きくなります。
  • Dalvik VMにはメソッド数が65535を越えられないという制約があって、特にGoogle Play Servicesを使ったアプリケーションなんかだと、この問題が顕現するようです。

Xamarin環境だと、伝統的に、まずmonoランタイムとマネージドコードの大きさが問題になっていました。Mono.Android.dllなんかは20MB近くあるわけです。これをそのままアプリケーションに入れるわけにはいかないので、Xamarin環境ではMonoがSilverlight対応(Moonlight)のために開発していたassembly linkerの機能を使って、dllから使われていないコードを削除するようにしています。やっていることはproguardと同じですね。

 

(Mono.Android.dllはandroid.jarに対するバインディングとなるわけですが、android.jarはターゲット側に既に入っているため、Java/Android開発において、android.jarのコードを削る工程は無いわけです。)

 

リンクされたMono.Android.dllと組み込みmonoランタイムは、HelloWorldレベルのアプリケーションでも、合わせて3MB以上はあります。伝統的には、proguardのコード削除機能は小さい規模での最適化だったため、「大きすぎるライブラリの問題」が最近になって顕現してくるまでは、相手にしてこなかったわけです。

XamarinでのProguardの使い方

さて、proguardの位置付けと必要性がわかってきたところで、Xamarin環境でproguardを「使う」方法について説明しましょう。Xamarin.Android 4.99以降が必要です。

 

有効にするためには、Xamarin.Androidのプロジェクト ファイル(*.csprojあるいは*.fsproj)を手作業で編集する必要があります*1。Releaseビルド用のPropertyGroup要素(Condition属性でConfiguration=='Release'として設定されているやつ)の中に、以下の要素を追加します。

 

<EnableProguard>True</EnableProguard>

 

これだけで、proguardはひとまず有効になります。

 

proguardはDebugビルドでも有効にできなくはないですが、assembly linkerがDebugビルドで無効になっているのと同様の理由で、fast deploymentと相容れる機能ではないので(無駄なファイル変更があるたびにapkを更新していたらfast deploymentの意味がない)、Releaseビルド専用と思っておけば間違いないです。

 

proguardはひとまず上記のプロパティで有効になりますが、実はこれではほとんど最適化は行われません。proguardを実効的に使うためには、もうひとつ重要な作業があります。それは

  • LinkerのモードをLink All Assembliesに設定する

ということです。これは少しややこしい理由によりますが、これは以降で詳しく説明します。

proguardの動作原理について

Link All Assembliesが必要になる理由を理解するためには、proguardがどのように「不要コード」を削除しているか、大まかに理解する必要があります。(理屈を理解しなくても、まあproguardを有効にすることはできますが、もし「調整」が必要になった場合は、proguardの挙動を理解しておかないと難しいでしょう。)

 

proguardは、「入り口」となるメソッドから、コードを探索して、中で使用されているメンバーをマークしていきます。マーキングが終わったら、全てのクラスの中から、マークされなかったメンバーを削除していきます。GCのマーク&スイープと似ていますね。

 

ちなみにこれは、マネージドコードのLinkerも概ね同様です。

 

そういうわけで、原理としては、各種「Androidフレームワークから呼びだされそうな」メソッドのエントリポイント(Acvitity.onCreate()など)から呼び出されるコードを全てマークすればいい、ということになるわけですが、実際にアプリケーションを作成していると、どうもそれだけでは済まない、自前でいろいろproguardに「これもマークしろ」といった指示を与えてやる必要が出てきます。proguardは、それを可能にするために、コマンドライン オプションをいろいろ用意しています。

 

実のところ、proguard自体は、Android専用ツールではないので(GoogleAndroid開発環境を構成するために、さまざまなオープンソース ツールを活用しているわけで、これもその一例です)、proguardはAndroid固有の事情を考慮しません(「固有の事情」というのは、android.jarを削る必要はなく、また呼び出されるアプリケーション コードのエントリポイントはstatic void main(string[])ではなく、さまざまなフレームワークAPIのオーバーライドである、といったところです)。そのため、Android SDK自身が、proguardにいくつかのデフォルト設定を与えています。android-sdkのtools/proguard/ディレクトリには、proguard-android*.txtというファイルがありますが、これらがその内容です。

 

Android SDKを使った開発では、典型的にはproguard.cfgというファイルを編集して、proguardに対するオプションを記述します。これはXamarin.Androidでも同じことができます。ProguardConfigurationというビルド アクションを指定されたファイルが存在すれば、Xamarin.Androidはデフォルトでその設定内容をproguardの呼び出しに反映させます。

 

(ちなみに、proguardとは関係ありませんが、マネージドコードのLinkerについても、LinkDescription.xmlというファイルで、linkerに対する指示をいろいろ加えることができます。)

 

Xamarin.Androidにおける設定ファイルの自動生成

さて、ここからが肝心の話ですが、Xamarin.AndroidJava呼び出しAPIは、全てJNIを経由して行われます。JNIを経由した呼び出しというのは、動的なJava APIの呼び出しであり、.NETで言えばリフレクションを使っているようなものです。動的な呼び出しは、proguardやlinkerのようなツールでは追跡できません。追跡できないけど、使われてはいるコードなので、削除されると実行時にNoSuchMethodExceptionになってしまいます。

 

というわけで、マネージドコードから呼びだされている機能については、明示的にマークして救済してやらないといけないわけです。これはユーザー任せにしていてはとても実現できないでしょう(「お前は今まで使ったAPIを全て覚えているか?」)。

 

幸いなことに、マネージドコードからのJava APIの呼び出しは、RegisterAttributeによって全てマークされています。というわけで、Xamarin.Androidでは、「アプリケーションのコードに含まれる全てのメソッドについて、RegisterAttributeをチェックして、もしあれば(そしてDoNotGenerateAcw="true"であれば)、それに対応するprogardオプションを動的に生成する」という荒業で、これに対応しています。

 

(もともとLinkerが同じようなことをやっているのと、最終的にproguardがかかってdex化するjavaバイトコードが減ることもあって、ビルドが遅くなる影響はあまりないようです。改善にもなりませんでしたが…)

 

そして、「アプリケーションのコード」と書きましたが、実のところこれは「RegisterAttributeの付いた全てのコード」に他なりません。全ての呼び出しは動的であり、特にフレームワーク側からACW(Android Callable Wrapper)経由で呼び出されるメソッドの呼び出しも含めると、何が実際に使われるコードであるかを判断するのは、動的な呼び出しをベースとしているXamarin環境では困難で、わたしには現状でこれ以上何が出来るかは、ちょっと思いつきません。まずは「概ね動作する機能を有効にする」かたちでのリリースを優先させたというわけです。

 

というわけで、ようやく本題に戻りますが、Link All Assembliesを指定するのは、「アプリケーションのコード」を減らすためです。Link SDK Assemblies Onlyを選択すると、Google Play ServicesやSupport Library(NuGetから入手できる最新のほう)などはlinkerがコードを削除しないため、全てのバインディングが残ったままになり、結果的にjarに含まれるほぼ全てのjavaコードが残ってしまうことになります。これではproguardの呼び出し損です。ビルド時間が長くなるだけです。

自動生成の設定ファイルを「外す」 - 高度なproguard設定

さて、以上のように、Xamarinにおけるproguardのサポートは、割と複雑な処理をはらんでいます。特に自動生成ファイルについては、本当にそれが常に存在すべきオプションだけを含むものなのか、わたしには確信がありません。(まあ主にkeepオプションを追加するだけなので、どちらかというと「安全」ではありますし、自前で作成するのはあまり現実味がない気もしますが…)

 

というわけで、場合によっては、この中間生成ファイルがむしろ効果的なproguardの邪魔になるかもしれない、だから場合によっては排除できるべきだ、とわたしは考えています。そこで、ProguardConfigFilesというMSBuildプロパティを用意して、もし必要であればカスタマイズできるようにしました。デフォルトの内容は次のようになっています。

       <ProguardConfigFiles Condition="'$(ProguardConfigFiles)' == ''">
               {sdk.dir}tools\proguard\proguard-android.txt;
               {intermediate.common.xamarin};
               {intermediate.references};
               {intermediate.application};
               @(ProguardConfiguration);
       </ProguardConfigFiles>

この{...}の部分には、自動生成ファイルなどが含まれることになります。たとえば、{intermediate.common.xamarin}に対応するのは、以下のような内容のファイルです:

# This is Xamarin-specific (and enhanced) configuration.
-dontobfuscate
-keep public class mono.MonoRuntimeProvider
-keep public class mono.MonoPackageManager
-keep public class mono.MonoPackageManager_Resources
-keep class mono.android.**
-keep public class mono.android.app.Application
-keep public class android.runtime.*

# Android's template misses fluent setters...
-keepclassmembers class * extends android.view.View {
  *** set*(***);
}

# also misses those inflated custom layout stuff from xml...
-keepclassmembers class * extends android.view.View {
  <init>(android.content.Context,android.util.AttributeSet);
  <init>(android.content.Context,android.util.AttributeSet,int);
}

これで、Xamarin環境で必要なクラスが残されることになります。また難読化も無効にされます(JNI呼び出しは動的に行われるので、難読化されたらまともに呼べなくなります)。

 

{intermediate.references}には、上記の、アプリケーションが参照するdllに含まれるRegisterAttributeを探索した処理の結果が含まれています。

 

{intermediate.application}には、アプリケーションのコードから自動生成されたACWについて、全てkeepとする設定が含まれています。(アプリケーションのdllには通常はRegisterAttributeが付いていないので、linkerの処理では対応できないわけです。)

 

これらは全て、ProguardConfigFilesというプロパティで既定の設定となっているがゆえに、読み込まれてproguardに渡されるようになっています。ですので、もしこれらの中に排除したいものがある場合は、csprojあるいはfsprojに、MSBuildプロパティを追加指定してやると、オーバーライドすることができます。

 

もちろん、単にオプションを「追加」したいだけであれば、ProguardConfigurationのビルドアクションをもつproguard.cfgなどをプロジェクトに追加して、そこに記述してやれば、ProjectConfigFilesに@(ProguardConfiguration)が含まれている通りで、そのままproguardに渡されます。

 

まとめ

Xamarin.Androidにおけるproguardサポートは、.csprojあるいは.fsprojファイルをいじって、MSBuildプロパティEnableProguardをtrueとして追加し、Link All Assembliesを指定することで、簡単に「使う」ことができます(リリースビルドだけにするのが望ましいところです)。

 

必要であればProguardConfigurationのビルド アクションをもつファイルにproguardオプションを追加すると良いでしょう。

 

ただし、中では、Xamarinのような環境でproguardを有効にするために、いくつかのオプションが自動生成されるなど、それなりに高度なことを行っています。もしそれらの自動生成ファイルが邪魔になったら、ProguardConfigFilesプロパティの内容を上書きして、好きなように指定すると良いでしょう。

 

場合によっては、自前でJavaクラスをkeepしておかないとけないこともあるはずなので、proguardがどのような原理で動作するものなのかは、proguardのドキュメンテーションなどを見て調べると良いでしょう。

*1:リリースチームに、IDEでこれを有効にするオプションを追加するよう頼まれて、コードを書いたのですが、頼んできた直後にリリースブランチを締め切られて、せっかく1時間で書いたのがまだ含まれていないという…