インサイドXamarin(4)
Monoのモバイル化の流れ ― Xamarin.iOS/Xamarin.Androidの誕生
デスクトップ環境での動作を主眼に開発された「.NET」のオープンソース実装である「Mono」は、どのようにモバイル開発に向かって流れていくことになったのか。
前回まではMonoについて詳しく説明した。Monoは.NET Frameworkのオープンソース実装としての側面が強く、.NET Frameworkはデスクトップで使用されることを主眼に開発されていた。それがどのようにモバイル開発に向かって流れていくことになったのだろうか。今回は、Xamarin.iOSやXamarin.Androidが誕生するきっかけとなった「Monoのモバイル化の流れ」について説明する。
Silverlightの登場
最初の大きな動きは、Silverlightだ。Silverlightは、Webブラウザーのプラグインとして「Webブラウザー上で、HTMLより機能豊富なアプリケーションを実現する」という目的で作られた。HTMLというよりFlashを標的にしていたというべきだろうか。もともと、.NET Frameworkでも、Webブラウザー経由でコードを実行できるようにしたいという動機が見られることは、しばしばあった。.NET Framework 1.0 SP1までは、ブラウザーからコードをダウンロードしてJavaScriptと同様のアクセス権限で実行できたし(「ノータッチデプロイメント」などと呼ばれていた)、後に「ClickOnce」というWeb経由のデプロイ技術も登場した)。
Silverlightの登場が.NET開発にもたらしたインパクトとして、以下の2点を挙げておきたい。
- それまでWindows専用だった、マイクロソフトの.NETのコードが、一部とはいえMac OS X上でも動作するようになった
- ブラウザープラグインとして別途インストールを必要とするために、極小の構成であることが求められた結果、デスクトップの.NET Frameworkから機能を大幅に削り落としたものが普及することになり、開発でも意識されるようになった
Silverlightでは、マイクロソフトが自らクロスプラットフォームを実現することになり、そこには「対象プラットフォームでできる範囲のことをやり、無理なWindows化はしない」という暗黙の哲学が含まれていた(例えばMac上のSilverlightで文字列比較しても、Windowsと同じ結果は得られない。.NETの文字列比較は、あまりにもWindowsに固有の実装になっているからだ)。これはMonoの開発にも多かれ少なかれ影響している。それまでは「.NETの開発者はWindowsのような動作を期待するから、そのように機能を実現する」ということが、しばしば過剰に要求されていたからだ。
Silverlight 2.0以降に搭載された.NET相当のランタイムは、「CoreCLR」と呼ばれていた(※2015年に公開された.NET Coreのランタイムに、同じ名前が再利用されている。2016年現在でCoreCLRと言ったら、こちらを指すものと考えたほうがよい)。SilverlightのCoreCLRは、.NETの機能を大幅に削り落とした、「.NET 2.1」*1と呼ばれることもあるクラスライブラリ群を伴っていた*2。機能を削り落としたというよりも、WPFとWCFのサブセットを公開するために必要最小限の機能を含めた、と書いた方が実態に近いかもしれない。非ジェネリックのコレクションの(System.Collections名前空間の)クラスと、それに依存する全ての「古い」ライブラリは、全て含まれていない。Windows APIべったりだったライブラリも消滅した。また、ランタイムでは、新しいCoreCLRセキュリティという機能が使われるようになり、煩雑さが不評だったコード・アクセス・セキュリティ(CAS)も消滅した。安全なプラットフォームでなければならないブラウザープラグインでは、ファイルI/Oは一時領域に限定され、P/Invokeのような機能も当然に利用不可能になった。
- *1 「.NET 2.1」という呼称は、現在の.NETコミュニティでは見当たらないのだが、Monoチームでは広く使われていたので、以降もこの呼称を使用する。
- *2 .NET Frameworkの機能を削り落としたプロファイルとしては、以前にも「Compact Framework」というものがあったが、Windows Mobileの普及レベルでしか普及せず、少なくともMonoとは完全に無関係だった。
Moonlightの実装、そしてモバイルプロファイルへの流れ
さて、MonoのリーダーであるMiguel de Icazaは、このSilverlightの話を聞くと、その構想が非常にMonoのアプローチと親和的であることに気付き、マイクロソフトの技術者と対話して、さまざまなヒントを教わり、Monoチームのメンバーと相談して、LinuxでもこのSilverlightを実装することにした。それが「Moonlight」である。なお、このMoonlightは、Monoプロジェクトの開発がモバイル中心のXamarinに移行した際に中止となっている。
もともと、Silverlight 1.0にはCoreCLRが含まれておらず、単なる「JavaScriptと統合したWPFベースのUIフレームワーク」であったため、わずか3週間の実装期間で、Moonlightでも基本的な実装を公開できた。
CoreCLRの実現とMoonlightへの統合は、短くて長い道のりだったといえる。2つの方向からのAPI実装が必要になった。
「GUIにバインドするAPIの実装」と
「.NET Frameworkから削り落とされたコアライブラリの実現」
である。Xamarin製品との関わりでは、後者が重要だ。
前者は、Dependency ObjectなどのC#ベースのクラス群を、「libmoon」というMoonlightのUIをC++で実装したコードと相互運用する作業となった(このアプローチはマイクロソフトのチームから教わった当時の最適解だった。ただ、現在では、「これは過剰なマーシャリングを伴うため、必ずしも最適ではなかった」と、Miguel de Icazaはほのめかしている)。
後者については、Monoチームでは、2段階の手順で実現することにした。まず既存のソースコードから、.NET 2.1でのみコンパイルされる部分のみを#if NET_2_1
というディレクティブでマークし、「だいたい.NET 2.1」を実現した.dllファイルをビルドするようにした。そして、「cecil(Mono.Cecil)」というCIL操作ライブラリを活用した「Cecil Linker」というツールを開発した。これは、API定義記述ファイルを参照して、入力dllから不要なpublic
メンバーをnonpublicにした.dllファイルを生成できる。これによって、比較的少ない量の#if
ディレクティブの追加作業で、膨大なクラスライブラリの「正確な焼き直し版」が実現された。このLinkerの技術が、その後、Xamarin.iOS、Xamarin.Androidで大きく活用されることになる。
Moonlightの成果から、Monoチームは1つの大きな可能性を見いだすことになった。CoreCLRと.NET 2.1の「小さい」プロファイルは、CPU/メモリリソースの少ない組み込み環境で、フルスタックの.NETよりも効率的にコードを実行できる、ということだ。
折しも、2008年にiPhone SDKが登場し、Maemo、Moblin、Meegoと、Linuxベースのモバイルフレームワークが登場するたびに、日常的に誰かが「monoをこれで動かした人はいるか?」とか「○○を動かしてみた」という話題を起こしていたMonoコミュニティでは、ごく自然に「mono on iPhone」という話も浮かんできたのだった。
そして恐らく、これと近い時期に、マイクロソフトでもこのSilverlightのAPIプロファイルを基に、「Windows Phoneを開発しよう」という動きが出ていたはずである。結果的に、現在のXamarin製品のAPIとWindows Phoneで使えるAPIは、割と類似しているし、いずれもこの「.NET 2.1」のAPIに基づいている。
MonoTouchプロファイルとMonoDroidプロファイルの誕生
このような歴史の流れを経て誕生した「MonoTouch」こと「Xamarin.iOS」は、基本的に.NET 2.1相当のAPIに基づいている。ただ、iOSアプリは、誰が書いたのかも分からない(悪意かもしれない)ままに動作するアプリケーションではなく、開発者が自ら書き、開発者の名声、提供者(アップル)によるレビュー、ユーザーによる明示的なインストール作業などが、安全性を担保するアプリケーションだ。ここでは、「ネットワークを張る」「ファイルを読み書きする」「外部のライブラリを実行する」といった処理を防ぐ理由は無い。そういうわけで、System.IO
やSystem.Net
などの名前空間が提供する基本的な機能は復活した。P/Invokeも復活して、iOSやAndroidの機能の呼び出しを実現している。
Xamarin.iOSでは、古いコレクションクラスと、それに依存するライブラリも、復活を果たしている。SilverlightのAPI削除は、時としてかなり過激なもので、例えばXPathやXmlSerializer
クラスを含む、System.Xml.dll
ファイルのAPIはほぼ消滅していた(WCFでXMLを操作することはあったはずである)。これらはXamarin.iOSでは復活している。さらに、SOAPクライアントのAPIとしてのSystem.Web.Services.dll
ファイルも、デスクトップ版からサーバー機能を除外した形で復活した。
これはSilverlightで実現したスリムダウンに逆行する動きだが、SilverlightとXamarin.iOSでは状況が異なる。Xamarin.iOSで重要なのは、最終的にユーザーに配布されるアプリケーションのサイズが小さいことである。Silverlightの場合、コアライブラリはインストーラーで配布される必要があり、これは極小であるべき存在だった。Xamarin.iOSでは、先のCecil Linkerの機能を活用しており、最終的に配布するappパッケージに含まれる.dllファイルは、パッケージされる前に、使われていないコードを「リンク」して消去してしまう。そうであれば、不必要にSystem.Collections
名前空間や古いAPIに依存するライブラリを、再利用の輪から外すだけもったいない、というわけだ。Monoコミュニティは、「古いAPIは有害だ」という教条主義的な主張をユーザーに強制することはしないし、特にXamarin.Androidは、ABIレベルでジェネリクスの無いJavaと相互運用する関係で、これらを除外することはできない。
なお、組み込みランタイムが小さければ良いことに変わりはなく、例えばWindows準拠の文字列照合(System.Globalization.CompareInfo
クラス)に必要なリソースなどは削除されている。
SilverlightのAPIに修正を加えたiOS用のAPIは、その後、「MonoDroid」という名前でXamarin.Androidの開発が始められた時にも利用された。ただし、一般的に、iOSはAndroidよりも実行できるコードの制約が大きい。その最たるものは動的コード生成の禁止であり、具体的にはMonoTouchにはSystem.Reflection.Emit
名前空間に属するコードジェネレーターが存在しない。System.Linq.Expressions
名前空間の、いわゆるExpression Treeも、コードを生成して実行する動作はiOSの制約に引っかかるので、現在は削除されている。C#のdynamic
型やDLR(Dynamic Language Runtime)もおおかた、ご想像の通り、これらに依存するため使用できない。詳しくはXamarin.iOSの回で詳しく説明しよう。
Androidには、動的コード生成の禁止のような制約はないので、MonoランタイムはJITの力を最大限活用している。C#のdynamic型も使用できる(前々回説明した通り、dynamic
型の使用にはMicrosoft.CSharp.dll
ファイルを参照する必要がある)。もっとも、AndroidにもDalvikランタイムのレベルでJavaの動的コード生成には制約がある。筆者はXamarin.Android環境で動的なコードを書くことはお勧めしない。もともとリソースの小さいモバイル環境で動かすことを前提としていないAPI設計であり、アセンブリのリンクとも相性が悪く、DLRの追加によってアプリケーションのサイズを肥大化させることにもなるからだ。
最後に、monoのソースコードから、これらのMonoTouch/MonoDroidモバイルプロファイルのライブラリをビルドできることを示唆して、この節は終わりにしよう。mcsコマンドおよびクラスライブラリには、これらのモバイル環境のビルド用に、monotouchとmonodroidというプロファイルがある。monoをconfigure
するときに、--with-monotouch=yes
あるいは--with-monodroid=yes
というオプションを指定すると、これらがビルドされるようになる。
モバイルプロファイルで利用できるライブラリ、コンパイラー
Xamarinのモバイルプロファイルで既存の.NETライブラリを使用する場合に注意すべきことは、それらのライブラリがそれぞれのモバイルプロファイルで参照できるようにビルドされていなければならない、ということだ。
そのライブラリが.NET 4.xでビルドしたアセンブリであれば、いかにそのアセンブリがモバイルプロファイルに含まれている機能のみを使用しているとしても、それを使用することはできない。それらのアセンブリは、モバイルプロファイルと異なるSystem.dll
などのアセンブリを参照していて、アプリケーションのパッケージング時に必要な参照アセンブリを発見できずに失敗するからだ。次の画面は、Xamarin.iOSアプリケーションにおける参照アセンブリを辿ってmscorlib.dll
の内容を表示したものである。これは先のmonotouchプロファイルでビルドされていて、デスクトップの.NET Frameworkのmscorlib.dll
とは内容が異なっている。
SilverlightやWindows Phoneなど、いわゆる「.NET 2.1」のプロファイルでビルドでき、WPFベースのUIを使用していないものであれば、おおむねAndroidでも利用できるだろう。さらに、動的コード制約に引っかからなければ、iOSでも利用できる可能性は高い。また、2013年末ごろから、新たにiOSやAndroidを対象に含むポータブル・クラス・ライブラリ(PCL)も使用できるようになった(PCLについては回を改めて論じたい)。
ライブラリと同様、.NET Frameworkでは、さまざまな開発言語のコンパイラーが使用できる。しかし、これはデスクトップの.NET Frameworkに限られた話だ。SilverlightやWindows Phoneには、これは当てはまらない。とはいえ、F#やVB(Visual Basic)などは、Silverlightでも使用できるし、XamarinではF#をサポートしている。このように、他の言語をXamarinのフレームワークに合わせて使用することも、不可能ではない。
しかしながら、「アプリケーション」のビルドは、単なる.dllファイルのビルドだけでは済まず、相当に入り組んだ作業を併せて行っている。従って、Xamarinのビルドの仕組みに詳しくないと、他の言語を活用することはまず実現できないだろう(そしてXamarinが他の言語のビルドをサポートすることはない)。現実的には、Xamarinのモバイルプロファイル上で使用しても動作するライブラリを作成する、というレベルで「対応する」方が良いだろう。
C#、F#以外の言語でXamarinのプロファイルをサポートするには、その言語コンパイラーが、「.NET 2.1」のプロファイルに基づいてアセンブリをビルドできるかどうかが鍵だ。正確にはXamarin.iOSとXamarin.Andridの各プロファイルだが、Silverlightに対応できて、iOSの制限に引っかからなければ、おおむね問題ないだろう。例えばMonoのVBコンパイラーであるvbnc
とクラスライブラリ(Microsoft.VisualBasic.dll
ファイル)を含む「mono-basic」は、Moonlight開発時に.NET 2.1ビルドをサポートしていた時期があり、VBの依存ライブラリであるMicrosoft.VisualBasic.dll
ファイルをビルドすることもできたので、Xamarinと組み合わせて使用することも可能かもしれない(monoのVBサポートはほぼメンテナンスされておらず、現在、ビルドできる保証もない)。
また、前々回も言及したが、コンパイラー自身がSystem.Reflection
アセンブリのAPIを使用してコードを生成していると、対象プロファイルで動作する必要が生じてしまう。Mono.CecilやIKVM.Reflectionなどを使った方がよいだろう。
■
次回は、Xamarin.iOSについて説明する。
※以下では、本稿の前後を合わせて5回分(第1回~第5回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
1. Xamarinを構成するソフトウェア。その主要な10要素とは?
Xamarinは何を提供しているのか? その主要なソフトウェア構成要素として、Mono、Gtk#、MonoDevelopとXamarin Studio、Xamarin.iOS、Xamarin.Android、Xamarin.Mac、Visual Studioアドイン、Xamarin.Forms、Xamarinコンポーネント、Xamarin Test Cloudなどについて紹介。
2. Xamarinの基盤となっている「Mono」と、C#コンパイラー「mcs」
Xamarinにおけるソフトウェアの基盤であるMonoを深く理解すれば、Xamarin製品の理解はもっと深まる。今回はMonoの成り立ちから、そのソフトウェア構成、C#コンパイラーの内容までを解説する。
3. Xamarinの基盤「Mono」のmonoランタイムとクラスライブラリ
Xamarinにおけるソフトウェアの基盤であるMonoを深く理解すれば、Xamarin製品の理解はもっと深まる。今回はmonoランタイムと、Monoのクラスライブラリについて解説する。