Bash on WindowsでFirstStepReVIEW-v2をビルドする Posted on Feb 16, 2017


Re:VIEWで同人誌の原稿を書いている場合、TechBooster/C89-FirstStepReVIEW-v2 がテンプレートとして役に立つことかと思います。一度セットアップしてしまえば、以降はコマンド一発でPDFファイルがビルドできるようになっているリポジトリで、これ自体Re:VIEWを使った本の執筆環境を解説するものです。 ただ、ビルド環境はMacOSやLinuxなどのUnix互換環境を前提としているようです。Windows環境でのビルド方法は自明ではありません。今回はWindows 10 Anniversary Update以降で追加されたBash on Windows (WSL) を使って、ユーザーでもこのリポジトリが使えるようにする方法を簡単にまとめます。 (Re:VIEW自体のビルドはcygwinで出来るかもしれませんが、ここではRe:VIEWの原稿を揃えるだけでPDFファイルのビルドまで出来るようにテンプレートが整備されているこのリポジトリを対象とします。) まず、筆者はBash on WindowsをUbuntu 16.04環境に切り替えているので、これを前提とします。Ubuntu 14.04ベースのBash on Windowsでも、同様の操作は可能かもしれませんが、パッケージは存在していない可能性もあります。Windows 10 build 14951以降のBash on Windowsであれば、Ubuntu 16.04になっているようです。それ以前のビルドであれば、手動でアップグレードが可能です。(ただ、問題が発生しないとは言えないので、いざという時に自力で調べて復元できる自信が無ければ、やめておいたほうが賢明でしょう。) さて、Bash on Windowsの環境が整った後は、大して難しいことはないはずです。ただし、review環境はtexliveの巨大なパッケージのインストールを必要とするもので、ディスクスペースは2GBくらい消費すると思います(正確には調べていません)。 まず、github上のリポジトリをcloneします。(sudo apt install git でgitをインストールしてからgit cloneするのが手っ取り早いでしょう。) 次に、リポジトリのビルドに必要になるパッケージをインストールします。以下の内容でたぶん大丈夫だと思います。(足りないものがあったらコメントで教えてください。) 後はほぼ README.md にある内容を実行するだけです。 sudo apt install ruby nodejs texlive texlive-latex-extra texlive-binaries texlive-lang-japanese ln -s /usr/bin/nodejs /usr/bin/node sudo gem install bundler npm install npm run pdf 一つ注意すべきことですが、Ubuntuでインストールされるnode.jsのコマンドは /usr/bin/nodejs となっています。(Ubuntuにはnode.js以前から node というパッケージとコマンドが存在していたので、後から登場したnode.jsの node コマンドは改名せざるを得なかったわけです。) FirstStepReVIEW-v2で使われているパッケージの何かが、node を実行しようとして失敗するので、今回は(やっつけですが)ダミーの /usr/bin/node を作成してやり過ごします。

Read More

Xamarin.AndroidでもInstant Run (cold swap) がしたい! Posted on Jan 3, 2017


これはXamarin Advent Calendarの空白の最終日を埋めるべく書かれたエントリーです。 今回はXamarin.Android 7.0に新しく追加された…ことになっている…Improved Fast Deploymentについて解説します。 ちなみに、Android Studio 2.0のInstant Runの仕組みを解読するの内容を把握している人向けにひと言で書くと、今回のImproved Fast Deploymentとは、これまで出来ていなかった…ことになっている…dex事前コンパイルと分割アップロードをXamarin.Androidでも実現した、という(だけの)ことです。 まあ、わたしの知る限り、Xamarin.Androidを使っていて、かつ、きちんとInstant Runの挙動まで把握している人は、おそらくほぼ皆無なので(知っていたYO! という人はぜひ教えてください! トモダチになりましょう(!?) )、引き続き読み進めてください。 そもそも、Fast Deploymentとは何か? まずFast Deploymentについて説明しましょう。 Xamarin.Androidには、デバッグビルドとリリースビルドが存在します。これらは、通常の.NET Frameworkアプリケーションでは、ほとんど違いが意識されることが無かったと思います(デバッグシンボルの有無やコードの最適化オプションくらいでしょうか)。Xamarin.Androidでは、実はこれらは全くと言っていいほど異なるものです。(Xamarin.iOSも、これらのビルドの挙動は大きく異なるのですが、話が広がりすぎるので今回は議論しません。) なぜそのような違いが生じるのでしょうか? その答えは、Androidアプリケーションのフルビルドが、とにかく長いことにあります。開発者は、通常のAndroidアプリケーションapkのビルドを、数十秒の単位で待たなければなりません。あまりにも長いので、ビルドを開発用に特化させてビルド時間を短縮できないか、ということになるわけです。ちなみにこれはXamarin特有の問題ではなく、Android Java開発にも当てはまる話です。 なぜAndroidアプリケーションのビルドが長いのかというと、ビルドのために行う作業が多数あって、最終的にインストールしてデバッグするアプリケーションのapkをビルドするためには、全てを実行しなければならないからです。Google Playで配布できるようなXamarin.Androidアプリケーションのapkには、(1)AndroidManifest.xml、(2)マネージドコードのdll、(3)monodroidランタイム(monoランタイムを含む)、(4)Java Callable WrapperのjavaクラスなどをDalvikバイトコードに変換したclasses.dex、(5)Androidリソースをコンパイルしてzipしたパッケージ、(6)その他の依存ネイティブライブラリ…などが必要になります。これらのどれかひとつでも更新されたら(そしてdllはC#コンパイラでビルドするたびに更新されます)、apkをビルドしなければなりません。 もうひとつ、アプリケーションをデバッグするためには、そのアプリケーションをデプロイ(インストール)しなければなりません。Xamarin.Androidのアプリケーションは、時には数十MBにもなります。数十MBのapkをAndroidターゲットに転送してインストール処理を実行するのにかかる時間は、ばかになりません。assembly linkerを使えばこれを縮小することが出来るのですが、リンク処理には時間がかかります。今はビルド時間を削減したいのですから、転送時間を減らすためにサイズを減らすためにビルド時間を伸ばすのでは、本末転倒です。 もちろん、ビルドシステムも盲目的にビルド作業を先頭から実行するわけではありません。たとえば、dllをビルドする時に、ソースファイルの最終更新時間が、コンパイル結果となるdllの最終更新時間よりも古い場合は、ソースファイルに何ら変更は加えられていないので以前のビルド結果を使い回すことが出来るはずです。という感じで、モダンなビルドシステムには通常、各ビルドタスクの入力ファイルとビルドの出力ファイルをタイムスタンプで比較して、出力のほうが新しければスキップする、という機能が入っています。 MSBuildの場合、ビルド対象は「プロジェクトファイル」(csprojとかvbprojとか。slnはちょっと特別で、複数のプロジェクトをまとめてビルドしているだけ)で、通常はその中で指定されているImport要素が指定する.targets と(慣例的に)名付けられたファイルで、それらのビルドタスクが定義されています。そして各ビルドタスクには、InputsとOutputsが定義されています。Inputsで指定されたファイルがOutputsで指定されたファイルより古い場合は、そのタスクはスキップされます。基本的には、無駄なビルドタスクは行われないように設計されています。 しかし、いずれにしろ、毎回ビルドしてデバッグするたびにこのデプロイメント作業が発生するのでは、時間がかかりすぎてしまいます。 Xamarin.AndroidのFast Deploymentとは、この「遅すぎるビルド」問題を、トリッキーな方法によって解決する仕組みなのです。 これまでのFast Deploymentの仕組み では、ここからは、Fast Deploymentがどのような仕組みで実現しているかを見ていきます。再び先のInstant Runの解説記事に戻りますが、これまでのFast Deploymentは、基本的にdllをInstant Runのdexのようにアップロードする仕組みです。コレで分かった人はこの節を読む必要はありません。 Fast Deploymentが有効になっていると、アプリケーションの累積的なビルドの際に、apkをビルドしてインストールする場面が少なくなります。 Xamarin.Androidアプリケーションのロジックの実体はdllとなってアップロードされますが、AndroidフレームワークがDalvikバイトコードをロードする機構ではなく、mondroidランタイムがマネージドコードをロードする機構については、Xamarinがコントロールする余地が大いにあります(何しろ自分たちで作っているわけですから)。具体的には、monodroidランタイムは、apkの中身だけではなく、任意のファイルパスにあるdllファイルをロードするように、動作を調整できます。もしそれが可能なら、apkの中には、アプリケーションのdllをパッケージする必要がなくなって、アプリケーションをデバッグする時は、apkを再インストールする代わりに、更新されたdllファイルだけをAndroidターゲットにアップロードすれば良いのです。これは、毎回のデバッグに際して行われる転送量と作業時間を、大いに削減してくれます。 さらに、mscorlib.dllやSystem.dll、Mono.Android.dllなどについては、アプリケーションごとに転送する必要すら無いはずです。mscorlib.dllやSystem.dllはどのアプリケーションでも同じですし、所定のTargetPlatformを使っているアプリケーションについては、同一のMono.Android.dllを使用しているはずだからです。Xamarin.AndroidにおけるDebug Runtime APKやPlatform APKと呼ばれているパッケージは、実のところこのためにあります。これらは、共通のdllを他のアプリケーションから要求された時に提供するためのパッケージとして機能しているのです。 もちろん、この仕組みで実際にアプリケーションをデバッグモードで動かすためには、更新が必要なファイルを検出して、apkインストール後に転送するデバッグ プロトコルが必要になります。これらは残念ながらxamarin-android (OSS) には含まれていません。(わたしはOSSコミュニティがデバッグ機能を改善でき、カスタムビルドしたxamarin-androidもIDEと組み合わせて使えるようにするために、これもOSSに含めるべきだと主張しているのですが…) ともあれ、これがFast Deploymentの実態です。正確に言えば、従来のFast Deploymentの実態です。 新しいFast Deploymentは何が「改善された」のか? Xamarin.Androidにおけるアプリケーション ロジックはマネージドコードであり、dllが更新されてもapkの再インストールは発生しない、ということは、C#のソースにどのような変更を加えてもapkが再インストールされることはない、ということでしょうか? 実はそうではないのです。 Xamarin.Androidアプリケーションが動作する仕組みを思い出して下さい。.NETのアプリケーション ロジックでは、JavaのAPIを呼び出しますし、逆にAndroidフレームワークがアプリケーションで実装されている(はずの)Java APIを呼び出すこともあります。Androidフレームワークからのマネージドコードの呼び出しを正しく処理できるように、Xamarin.Androidはアプリケーションのビルド時に「Javaスタブコード」を生成します。Java Callable Wrapper、JCWとも言われるやつです。

Read More

Xamarin.Formsに新しいプラットフォームを追加する: 前哨戦 Posted on Dec 24, 2016


Advent Calendar用まえがき これはXamarin Advent Calendar24日目の投稿です。 今回はXamarin.Formsに新しいプラットフォーム サポートを追加するためには、どんな作業が求められているのか、調べてみたいと思って始めました(Xamarin.Formsに関してはほとんど使ったこともなく知識もあまり無いので…)。ただ、残念ながら現状動くものはほぼ何もありません(!) というわけでこれも半ば経過報告に近い内容です。いずれ更新する前提でqiitaに書くことにしました(今日も残り時間で更新するかも)。あるいは同人誌の原稿ネタにするかも。 まえがきはこのくらいにしておいて、本編に入りましょう。 デスクトップ プラットフォームを追加する Xamarin.FormsはクロスプラットフォームのUIツールキットを自称し、Android, iOS, MacOS, UWPなど実際にさまざまなプラットフォームをサポートしていますが、世の中には他にも幅広く使われているGUIツールキットやプラットフォームが存在します。わたしのデスクトップ環境はUbuntu LinuxのGNOME環境であり、ここで動作するアプリケーションは主にGtk+で書かれています。KDEであればQtで書かれたアプリケーションが動作することでしょう。また、Windowsアプリケーションであっても、UWPとして動作してほしいアプリケーションもあれば、そうでない、WPFのように通常のWindowsアプリケーションとして動作してほしいものもあるはずです。 Xamarin.Formsが現在サポートしていない環境で動作するためには、Xamarin.Formsの「プラットフォーム」実装を用意しなければなりません。今回は、新しくプラットフォームを実装してみることで、新しいプラットフォームの追加に際して、どのような作業が必要になるかを調べてみようと思いました。 今回は、わたしのデスクトップでXamarin.Formsアプリケーションを動作させることを目的として、Gtk#…ではなく、クロスプラットフォームのデスクトップGUIツールキットであるXwtを使って、これを実現するつもりで着手しました。WPFのバックエンドもあるので、うまく行けばWPFでも動作するかもしれません。 EtoやAvaloniaも検討しましたが、いずれもモバイルまで包摂しようとするGUIフレームワークなので、そうなるとむしろデスクトップ専用のAPIよりも実装に際しての自由度が低くなるのではないかという(抽象的な)懸念があり、またXwtであればXamarin.Formsと同様にプラットフォームに特化した拡張が可能なので、いざという時にGtk#に「逃げる」ことが可能であろう、という判断から、今回はXwtにしました。 実装編(1) Xwtでウィドウを表示するまで プラットフォーム実装のアセンブリとControlGalleryのプラットフォーム実装を追加 Xamarin.Formsのライブラリの中心部分は、PCLで実装されているXamarin.Forms.Core.dllと、Xamarin.Forms.Platform.iOS.dll、Xamarin.Forms.Platform.Android.dll、…といったプラットフォーム別のアセンブリという構成から成っています。最近はTizenが追加されてアツい状況(?)ですね。 また、Xamarin.Formsを使用したアプリケーションは、PCLで作成できるクロスプラットフォームのGUIアプリケーション共通dll(MyAppとします)と、それをプラットフォーム別の手法で起動するプラットフォーム別のアプリケーション プロジェクト群(MyApp.iOS, MyApp.Android, …)から成っています。これらの内容は、新規Xamarin.Formsアプリケーションを作成した時に、見ることができる内容です。 というわけで、まずXamarin.Forms.Platform.Xwt.dllを追加します。 それと、アプリケーションのプロジェクトがどのような構成になるかを確認するために、ControlGalleryのプラットフォーム別実装も追加します。そのプラットフォームのプロジェクトを作成します(Xwtの場合は単なるコンソール アプリケーション)。 両方出来たら、まずアプリケーションプロジェクトのほうを作成します。アプリケーションがXamarin.Formsをブートストラップするプロセスは短いはずです。AndroidならMainActivityとしてFormsApplicationActivityの派生クラスが作成され、iOSならAppDelegateとしてFormsApplicationDelegateの派生クラスが作成されるように、そのGUIツールキットのブートストラップの手法に沿った初期化を行うようにします。 今回使用するXwtの場合、Xwt.Application.Initialize()を呼び出してから、XwtのWindowを表示するなどして、それからXwt.Application.Run()でアプリケーションループを開始します。 Xamarin.Forms.Applicationの初期化 さて、Xamarin.Formsの方にフォーカスを移しましょう。Xamarin.Forms.Applicationクラスのインスタンスを生成しようとすると、Xamarin.Forms内部でのさまざまな初期化プロセスが走ります。これを何も考えずに行うとこうなります。 “You MUST call Xamarin.Forms.Init(); prior to using it.” と言われています。Xamarin.Forms.InitなどというAPIは実は存在しないのですが(!)、どうやらXamarin.Forms.Applicationのコンストラクタの実行中に、Device.PlatformServicesへのアクセスが発生して、このstaticプロパティが設定されていないために、例外が投げられているようです。Applicationのインスタンスを作成する前に、このIPlatformServicesの実装を設定する必要がありそうです。 もちろん、このIPlatformServicesは、プラットフォームに合わせて実装する必要があります。ようやくXamarin.Formsを実装しているという雰囲気が出てきましたね! ところで、IPlatformServices、これを執筆中のXamarin.Forms v2.3.3の段階では、Xamarin.Forms.Core.dllに含まれる非公開インターフェースなのです。この型にアクセスできるようにするには、Xamarin.Forms.Core.dllにInternalsVisibleToAttributeを追加して(つまりXamarin.Forms.Core.dllに変更を加えて)、自分のPlatformアセンブリを指定する必要があります。実のところ、iOSやAndroidなど各プラットフォームアセンブリについて、AssemblyInfo.csの中で指定されています。 Xamarin.Formsのプラットフォーム別実装では、慣例的に、Xamarin.Forms.Formsクラス(これはCLSCompliantなネーミングでは無いのであまり良くないと思いますが)をプラットフォームDLL上で定義していて、その中でこのDevice.PlatformServicesも設定しています。Device.Info、Ticker.Defaultといった、プラットフォームに依存しそうな各種グローバル(static)プロパティも設定されています。新しいプラットフォーム向けには、既存のForms.csのいずれかを適当にコピーしつつ、似たようなInit()メソッドの実装を追加すると良いでしょう。 Forms.Init()(と慣習的に命名された初期化メソッド)では、プラットフォームに特化したIPlatformServicesの実装も必要になるので、それを他のプラットフォームから適当にコピーしてきて実装します。XwtPlatformServicesはWindowsBasePlatformServicesをベースに実装しました。最初のうちは、実装の面倒そうな部分は throw new NotImplementedException(); とでも書いておけば良いでしょう。他のメンバーも適当に実装クラスを作って設定します。 さて、ここまでやったら、アプリケーションが実行できるようになるでしょうか? なりません。どうやらISystemResourcesProviderのDependencyServiceが必要になるようです。この問題にハマるケースは2つあって、(1) IPlatformServicesのGetAssemblies()メソッドをちゃんと実装していない場合と、(2) 対象DependencyServiceがきちんとアセンブリに定義されていない場合です。後者については、DependencyServiceの正しい使い方を知っている読者には分かると思いますが、Properties/AssemblyInfo.cs に次のような行を追加すると良いでしょう。 [assembly: Xamarin.Forms.Dependency (typeof (ResourcesProvider))] 最低限のカスタムレンダラー構造の追加 ControlGalleryのプラットフォーム実装には、PageRendererとLabelRendererのカスタム実装が含まれています。カスタムレンダラー(実際には標準レンダラーというべきでしょうが)はプラットフォームでいずれ実装しなければならない存在なので、ここで軽く実装しておくのが良いでしょう。 カスタムレンダラーのクラス階層構造は、厳密にはプラットフォームによって異なりますが、概ね同様の構成に基づいていると言えます(メンバー構成はプラットフォームによって大きく異なります)。新しいプラットフォームでは、基本的に共通している階層構造を倣いつつ、プラットフォームに合わせたかたちで実装するのが良いでしょう。以下は今回のXwt実装における階層構造です。 namespace Xamarin.Forms.Platform.XwtBackend { public class LabelRenderer : ViewRenderer<Xamarin.

Read More

Visual Studio for Macの.NET Coreサポートの実装について Posted on Dec 1, 2016


はじめに 本稿は.NET Core Advent Calendar1日目の投稿です。トップバッターなので、あまり.NET Coreの深い部分には立ち入らずに済ませようと思います。 Microsoft Connect 2016();で、新しいVSブランドの製品としてVisual Studio for Mac(以下VSMac)が公開され、プレビュー版が試せるようになりました。 お分かりの方も多いかと思いますが、VSMacは概ねXamarin Studioと言っても差し支えない存在です。MonoDevelopには元々UI上の「ブランディング」を簡単に変更する仕組みが用意されており、実態においてMonoDevelopとその上のアドインの集合体であるにすぎないXamarin Studioは、簡単に表示をXamarin Studioとしていられるわけです(かつてPlayStationSuiteという開発環境もありましたね)。VSMacもおそらく同様の仕組みに基づいて、UIを変更しているのでしょう。 というわけで、VSMacについて詳しく知りたければ、Xamarin StudioひいてはMonoDevelopについて調べればよいということになるわけですが、VSMacには、少なくとも現状のXamarin Studioには搭載されていない機能がいくつか存在します。そのひとつが本稿で言及する.NET Coreサポートです。 ちなみにVSMac自体についての基本的な使い方は、既に導入記事がいくつかあるので(この辺とか)、それらを参考にしてもらえればと思います。 .NET Core toolingとバージョンの関係 2016年10月に公表されたところによると、VS2017(当該ブログで記述されているVS “15”は、VS2017となったようです)における.NET CoreプロジェクトのビルドツールはMSBuildということになって、project.jsonや.xprojファイルは.csprojファイルに移行することになったようです。古いプロジェクトはdotnet migrateコマンドで移行できるようです。 エェー、あの.csprojファイルかよ…と思われたかもしれませんが、新しいファイルはだいぶすっきりしているので、そんなに悪くないかもしれませんよ? <Project> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" /> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp1.0</TargetFramework> </PropertyGroup> <ItemGroup> <Compile Include="**\*.cs" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.NETCore.App" Version="1.0.0" /> <PackageReference Include="Microsoft.NET.SDK" Version="1.0.0" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project> とはいえ、残念ながら、今回リリースされたVSMac previewがサポートしているのは、.NET Core 1.0ベースのプロジェクトなので、VSMacの新規プロジェクトで作成された.csprojは、このスタイルにはなっていません。VS2017とMonoDevelopでは、プロジェクトモデルの実装が異なるので(いきなりプロジェクトのファイル追加・削除操作がワイルドカード ベースになったりはしない)、しばらくは従来型のプロジェクト ファイル構成になるのではないでしょうか。 現状のVSMacは、あくまでpreviewなので、VS2017の正式版が出てtoolingが.NET Core 1.1ベースに切り替わる頃には、こちらもアップデートされる可能性が十分にあると想像します。 .NET Coreをサポートするパッケージ管理 VSMacのアプリケーションの内容を見ていくと、./Contents/Resources/lib/monodevelop/AddIns 以下に、おなじみのmonodevelopのアドイン群が入っています。今回は(Xamarin.

Read More

language server protocolについて (前編) Posted on Aug 19, 2016


(今のところ前後編に分ける予定ですが、追記したり構成が変更になったりするかもしれません。予定は未定。) 2016年6月に、Microsoftがlanguage server protocolという仕様を公開しました。 https://github.com/Microsoft/language-server-protocol 本稿では、このlanguage server protocolの存在意義や具体的な実現方法について解説します。 language server protocolの存在意義 language serverとは、IDEが必要とするプログラムのプロジェクト ソースを解析して情報を提供する機能を、サービスとして実現するものです。language serverがサポートされたIDEでは、型やメンバーの自動補完、変数やメンバーの定義参照、変数やメンバーの利用箇所の検索、コードの自動フォーマット、コードのエラー分析や修正案の提示といった、さまざまな機能を実現できます。 Microsoftの技術で言えば、Roslynが最も近い存在であると言えます。あるいは、TypeScriptは、最初からlanguage serverの実現を意図しながら開発された言語でした。 language server protocolとは、このlanguage serverとそのクライアントを接続する仕組みを、プロトコルとして規定したものです。サーバーの実体は主にプログラミング言語環境、クライアントの実体は主にテキストエディタやIDEということになります。プロトコルはJSON-RPCで規定されています。(これが2000年代だったらSOAPで規定されていたかもしれませんね…!) 今回Microsoftがこのlanguage server protocolで提案したのは、language serverの機能というものは、プログラミング言語のいずれかを問わず、コンパイラー インフラストラクチャーの類にある程度は共通しているので、それを取り決めてしまおう、というものです。これが実現すれば、どのIDEやテキストエディタでも、この仕様で定めるプロトコルのクライアントを実装すれば、その接続先にあるサーバーがいかなる言語を取り扱っていても処理できるようになります。逆に、どのプログラミング言語でも、この仕様が定めるプロトコルのサーバーを実装すれば、どんなクライアントが接続してきても、画一的に対応できるようになります。 Microsoftの中でこのイニシアティブの原動力となったのは、Visual Studio Codeです。VSCodeは本質的にはelectronを使用した「単なるエディタ」を拡張する(アドイン・エクステンションを取り込む)ことで実現していくスタイルのIDEです。 (本稿におけるテキストエディタとは、単なるメモ帳のようなものではなく、拡張機能のサポートを前提としたものであり、この意味では、IDEとはテキストエディタにプロジェクトモデルやコンパイラー ツールチェインを統合したものであると考えることができます。以降も、「(テキスト)エディタ」と「IDE」という語句を使って解説していきますが、本稿においては、これらの間に本質的な違いはありません。) 通常、IDEのアドイン開発は、それぞれのIDEの内部構造に密結合する(strongly-typedな)機構に基づいています。MEFを活用したVisual Studio SDK、Mono.Addinsを活用したMonoDevelop Addin、Eclipse Platform、 NetBeans Platformといった仕組みでもあり、emacs, vim, atom, vscodeといったエディタでも、それぞれ独自のプラグイン機構が存在しています。これらにおいて、サポートされていない言語環境を新たにサポートすることになった場合は、それぞれの環境に合わせたアドインを、それぞれのエディタのユーザーが開発します。すなわち、エディタの数だけ、言語の数だけ、それらを繋ぐアドインの開発が必要になっていたということです。 vscodeに限らず、IDEでエクステンション開発を促進するには、その基盤が確立されていることが重要です。前述の通り、通常、IDEのアドイン機構は強い型付けに基づいており、IDE別のAPIを使用して実装します。そして、これはIDEに限った話ではありませんが、アドイン機構で安定したAPIを提供するというのは、なかなか実現しない夢のような目標です。自分が使っていたFirefoxのアドインやChrome拡張が、ブラウザをバージョンアップしたら機能しなくなった、という経験のある人は多いのではないでしょうか。そして、APIが安定しているということは、むしろ発展が止まったということでもあります。 また、IDEごとに実装言語が異なるため、複数のIDEをひとつの言語でサポートするというのは現実的ではありません。 このような場面では、強い型付けによるAPIに基づいてアドインを実装するより、実装言語を問わずに実現できるプロトコルとして規定する、というのは、かしこい選択であると言えるでしょう。 language server protocolの内容 language server protocolは具体的に定義されたプロトコルです。 https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md このプロトコルの内容が大まかに分かると、このプロトコルを実装したエディタや言語環境でどんな機能が実現できるのか、が分かるようになるでしょう。また、自分が普段使っているIDEにどんな機能があるのか(あるいはどんなことが出来る可能性があるのか)を知る、いい機会となるでしょう。 これはmarkdownで1600行にもわたる長大なドキュメントですが、大半はコードによる例示であり、実際に読むべきところは多くありません。前半部分はJSONメッセージの構造を説明しており、これだけを読んでも何ら得られるものはありません。後半になると、具体的なリクエストとレスポンスを規定する内容になっており、これらを俯瞰すれば、このプロトコルで何が実現できるか見えてきます。”Actual Protocol”というセクション以降が主な内容です。 (本稿では、2016年6月に公開された「バージョン2.0」に基づいて解説しています。これより後のバージョンでは、説明が当てはまらない部分もあるかもしれません。) このプロトコルのメッセージには、クライアントからリクエストを送ってレスポンスを受け取るタイプのものと、サーバーからメッセージを通知のかたちで送るタイプのものがあります。 クライアントから送信するリクエストの中には、解析対象ソースコードの追加やエディタ上で編集内容を送る、といった、language serverが機能するために必要だけど面白みのないものと、自動補完や定義参照などlanguage serverと呼ぶにふさわしい機能を実現するものとがあります。ここでは面白みのある部分を取り上げて眺めてみましょう。language server protocolでは、以下のようなリクエストが定義されています(JSON-RPCにおけるmethodの名前を取り上げます)。 リクエスト 説明 textDocument/completion 自動補完 completionItem/resolve 自動補完候補の選択 textDocument/hover ヒント(ホバー)の表示 textDocument/signatureHelp メンバー定義(signature)候補の表示 textDocument/definition シンボルの定義の位置を取得 textDocument/references シンボルの利用(参照)位置のリスト取得 textDocument/documentHighlight ハイライト対象シンボルの利用(参照)のリスト取得 textDocument/documentSymbol ドキュメント中で定義されている全シンボルの取得 workspace/symbol ワークスペース全体からクエリ条件に合致するシンボルの取得 textDocument/codeAction コード アクションのリストの取得 textDocument/codeLens code lensのリストの取得 codeLens/resolve code lensの処理の実行 textDocument/formatting ドキュメントの整形 textDocument/rangeFormatting 選択範囲の整形 textDocument/onTypeFormatting タイプ時の整形 textDocument/rename 識別子の変更 以下、それぞれのリクエストについて、少しずつ補足します。(各種機能の具体例としてスクリーンショットをいくつか挙げますが、このlanguage server protocolを実装したものには限定しません。)

Read More

cerberoを使ってlibfluidsynthをAndroid用にビルドする Posted on Jun 22, 2016


TL;DR 試行錯誤の末、fluidsynthをビルドするために、cerberoのソースに追加できるrecipeを作成した。 https://gist.github.com/atsushieno/a1c7af0537f1ce353e4e やや冗長な導入 fluidsynthはLinux環境で使われる仮想MIDIシンセサイザのひとつだ。同様のソフトウェアにはtimidityやZynAddSubFxなどがある。 fluidsynthは、ALSAのMIDIデバイスとしても使用するのが一般的であろうと思われるが、C APIがそれなりに整備されていて(fluidsynth自体もCで書かれている)、プログラムから直接操作することも出来る。C APIが用意されていると、Cライブラリとのインターフェースを実現している、perl、python、node.JS、C#/.NETのような言語環境とも相性が良い。筆者は実際C#バインディングを開発したことがある。(自分では実際のアプリで使ったことがないのに、ユーザーから問い合わせや使用レポートを受け取ってもいる地雷ライブラリである。) https://github.com/atsushieno/nfluidsynth そんなfluidsynthを、今回はAndroid用にビルドしてみた。これがあまりストレートにはいかなかったので、それについて説明したい。 ちなみに、Androidの主なアプリケーション開発言語はJavaであり、Cのライブラリをビルドするだけでは、直ちに使用できるわけではなく、JNIを使用したJava用のエントリポイントをいろいろ作成してやらなければならないのが一般的だが、筆者はそもそもC#環境で使うことを念頭に置いている(そのための上記nfluidsynthである)ので、fluidsynthのAPIのそれぞれについてバインディングを作成するなどという面倒な作業は行っていない。Java環境にも.NETのP/Invokeに相当する機能を実現する仕組みはいくつかあって、有名なのはSun Microsystems(当時)が開発したJNAだろうが、筆者のお気に入りはBridjというライブラリである。これについては、筆者が数年前にAndroidサポートをcontributeした時に、詳しい解説を書いたので、興味のある人はそれを参照されたい。 http://d.hatena.ne.jp/atsushieno/20110628/p1 android-cmakeによるビルドの試行(おまけ) さて、Android NDKを使用したネイティブライブラリのビルドにおいては、Android.mkという独自フォーマットのビルド記述ファイルが使用されるが、fluidsynthが主にサポートしているビルド環境はcmakeである。最新のstableバージョンであるcmake 3.2.*系列では、Androidビルドをサポートするための何かしらのモジュールファイルが追加されているが、それらはAndroid NDKのtoolchainを使用したビルドを実現するためのものではないようだ(必要なモジュールかもしれないが、cmake単体でNDKによるビルドをサポートするものではなさそうだ)。 cmakeには、拡張モジュールによってツールチェインの設定などを簡単に行える機構があり、Android NDKでもcmakeベースのプロジェクトをビルドできるように、この拡張モジュールを作成している人たちが何人かいるようだ。筆者がgithubで検索して最初に発見したのがこちらである。 https://github.com/taka-no-me/android-cmake やったー!と思って、このREADMEにある通りfluidsynthをビルドしてみると… $ cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a with NEON" . ... CMake Error at /usr/share/cmake-3.2/Modules/FindPackageHandleStandardArgs.cmake:138 (message): Could NOT find Pthreads (missing: PTHREADS_LIBRARY) 上手く行かない。 このエラーメッセージでgoogle searchをかけてヒットする情報が既に古く、筆者がcmakeにも詳しいこともないので、結局これを解決する方法はよく分からなかった。また、後述する別の問題もあるので、cmakeによる解決はとりあえずあきらめた。 autotoolsによるビルドの試行 実は、fluidsynthにはautotoolsベースのビルドも用意されている。これを使えば、cmakeに頼る必要もないわけだ。 もちろん、autotoolsで得られるのはmakeベースのビルドであり、ndk-build一発で完了するAndroid.mkが得られるわけではない。しかし一般的なautotoolsベースのプロジェクトでは、クロスコンパイル用のビルドスクリプトが作成されるものだし、実際にautotoolsベースのプロジェクトをAndroid NDKでビルドするためのTipsは存在している。 https://warpedtimes.wordpress.com/2010/02/03/building-open-source-libraries-with-android-ndk/ ただ、flluidsynthのビルドに関しては、ひとつ厄介な問題があった。fluidsynthはglibを参照しているのである。このglibをAndroid用にビルドするのは、不可能ではないはずだが(glibを使用しているライブラリはAOSPの中に含まれているはずである)、glibパッケージの解決にはpkg-configが使用されるので面倒くさいし(クロスコンパイルでpkg-configの参照を解決するということは、INSTALL PREFIXとして指定できるような、対象ターゲット環境のディレクトリが必要になるはずだ)、普通のLinuxデスクトップ用のビルドと同様にというわけにはいかなそうだ。 (ただし、後で判明したことだが、fluidsynthのビルドでautogen.shを使用する場合は、pkg-configが存在しない環境でも動作するようだ。しかし、いずれにしろglibのビルドが必要になることに変わりはない)。 (ちなみに、Makefile.amに比較的短いビルドステップの記述を追加することでAndroid.mkを簡単に作成できるandrogenizerというプロジェクトも存在するが、いずれにしろglibの依存解決が必要になるので、通常のautotoolsビルドはあきらめた。) cerberoによるビルド 今回のglibもそうだが、この「パッケージ参照を解決する仕組み」がネックになってビルドできないプロジェクトというのは、いくつもありそうだ。 Android NDKのtoolchainにpkg-configが含まれるだけでも、autotoolsベースのプロジェクトの取り込みがだいぶ楽になると思うし、個人的にもAndroid NDK用のビルドシステムを構築したほうが良いんじゃないかとは思っている。(実のところ、それで開発したビルドサーバのようなものを作成したこともあるのだが…) ともあれ、Android NDKに無い以上、この種の多大な依存ライブラリをビルドできるような、代替の仕組みがどこかにあるのではないか。そう思って探してみたもののひとつがgstreamerである。gstreamerのビルドが存在するであろうことは、VLC for Androidが存在することから推測していた。 このgstreamerのAndroidビルドを実現しているのが、cerberoという、gstreamer独自のビルドシステムである。 http://docs.gstreamer.com/display/GstSDK/Building+from+source+using+Cerbero このgstreamerは、主にGNOME環境で開発されてきたもので、GNOME関係の依存関係が多大に含まれる。先のページからリンクされているfreedesktopのgitサーバから、チェックアウトして、以下のコマンドを実行してみると、およそ180種類のライブラリやツールがビルドできることが分かる。 ./cerbero-uninstalled list glibはGNOMEでは非常にessentialなライブラリなので、もちろんこの中に含まれている。

Read More

Xamarin.Androidプロジェクトをテキストエディタで作ってみよう Posted on May 25, 2016


これは技術書典#1でTechBoosterが発行するAndroid本向けにわたしが書いているXamarin.Android SDKネタ、のスピンオフのようなものです。何となくどんな内容になるのか、雰囲気を感じ取って興味を持ってもらえればと思います。 MSBuildはじめの一歩 Xamarin.Androidプロジェクトは、Xamarin StudioあるいはVisual StudioでサポートされているMSBuild形式に従っています。 MSBuildは、JavaにおけるAntのような、XML形式のビルド記述方式を採用しています。 最も簡単なMSBuildスタイルのビルドスクリプトを書いてみましょう。簡単な内容なので、テキストエディタで編集できます。 ~$ cat x.proj <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target> <Message Text="Hello, MSBuild" /> </Target> </Project> ビルドしてみましょう。 ~$ xbuild x.proj /nologo Build started 5/25/2016 2:55:08 PM. __________________________________________________ Project "/home/atsushi/Desktop/ManualProject/ManualProject/x.proj" (default target(s)): Target : Hello, MSBuild Done building project "/home/atsushi/Desktop/ManualProject/ManualProject/x.proj". Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:00.0672030 どうでしょう? catで示された通り、内容はメッセージを表示するだけのビルドターゲットを含んでいます。http://schemas.microsoft.com/developer/msbuild/2003というXML名前空間さえ覚えておけば、何とか手書きでも作れる内容ではないでしょうか。 MSBuildは、ターゲットが1つしかない場合は、そのターゲットを実行します。複数ある場合は、「最初のターゲット」が実行されます。通常は、ターゲットにName属性で名前を付けます。 ~$ cat x.proj <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target Name="x"> <Message Text="Hello, MSBuild" /> </Target> <Target Name="y"> <Message Text="Hello, MSBuild 2" /> </Target> </Project> ビルドしてみましょう。

Read More

Android Studio 2.0のInstant Runの仕組みを解読する Posted on Feb 21, 2016


これは、DroidKaigi 2016のセッション「Instant Runを実現する仕組み」と連動するエントリです。セッションは30分という短い時間で行われるので、細かい説明はこちらでまとめていきます。 Part I: Instant Runを理解するためのAndroidビルド概説 はじめに 2015年11月に、Android Studio 2.0 previewと同時に公開されたInstant Runは、開発中のアプリケーションのAndroidデバイスあるいはエミュレーター(以下”target”)へのデプロイメントを高速化して、また実行中のアプリケーションを再起動することなくコードを置き換えることを可能にして、デバッグ開始までの待ち時間を劇的に減らすことが可能になりました。開発者がIDE上でデバッグを指示してから、実際のデバッグ開始まで、場合によっては1秒程度で出来てしまう、というものです。これはAndroid 4.0以降の任意のtargetで実行可能です。 このような劇的な改善はどのようにして可能になったのでしょうか? 今回は、Instant Runを実現する仕組みについて解説していきます。 Instant Runの仕組みを理解すると、アプリケーションのビルドや実行に、ある種のフックを仕掛けるなど、ある程度素直でないことを行う場合に、どのような点に気をつければ良いのかが、見えてくるかもしれません。 Instant Runを実現する技術要素は、主にビルドシステムの改善ですが、Android 4.0以降のフレームワークの事情もある程度関連してきます。GoogleがInstant Runと呼ぶ技術はGradleの上に成り立っていますが、技術的には必ずしもGradleに依存するものではありません。いずれにしろ、その辺りの領域に関心のある人向けの議論です。 ビルドとデプロイメント - デバッグ作業をスローダウンさせる要因 Perl、PHP、Python、RubyなどでWebアプリケーションを開発したことがあれば、アプリケーションをデバッグ実行するのは、ストレスの大きい作業ではないことが分かるでしょう。アプリケーションは概ね直ちに実行開始できます。C#でWindowsアプリケーションを開発している時も、ビルドにはちょっと時間はかかるかもしれませんが、アプリケーションのデバッグ実行はすぐに開始します。 一方、Android Studioを起動して、新しいアプリケーションを作成し、デバッグを指示してから、Android Studio上で実際にデバッグが行えるようになるには、かなりの時間がかかります。いったい何に時間がかかっているのでしょうか? Android Studioが新規アプリケーションのテンプレートを展開したり、Gradleのダウンロード、依存ライブラリのダウンロード、Androidエミュレーターの起動や実行など、一度しか発生しないタスクや、本質的に回避しようのないものを除外して考えると、デバッグ実行のたびに、反復的に必要になるのは、大まかには次の2つです。 アプリケーションのビルド アプリケーションのデプロイメント Androidアプリケーションのビルドは特に時間がかかります。なぜかというと、ビルドの際に行う作業が多いためです。その主な内容を列挙すると… aaptを呼び出してリソースを処理して、9-patchを施し、リソースIDをインデックス化して、バイナリデータとR.javaを生成します JavaソースコードをJavaバイトコード(*.class)にコンパイルします もし指定されていたら、proguardを使用して不要なJavaバイトコードを削ります 場合によっては、multidexに対応するためにJavaバイトコードを全てスキャンして設定ファイルを生成します JavaバイトコードをDalvikバイトコード(*.dex)に変換します これらをzip圧縮してapkにまとめ、zipalignでアラインメントを整理します apkに署名を施します ビルドが終わっても、直ちにデバッグを開始できるわけではありません。デバッガーを起動するホストと、アプリケーションをtargetは別のマシンであり、デバッグする前に、実行するAndroidアプリケーションのapkパッケージをtargetに転送、すなわちデプロイしなければならないのです。このデプロイメント作業が、もうひとつの大きな遅延要因です。なぜかというと… アプリケーションのapkパッケージをtargetにTCP経由で転送しなければなりません アプリケーションが既にインストールされている場合は、一度アンインストールする作業が発生します アプリケーションのapkパッケージをインストールすると、中では適切なパーミッションの設定(アプリケーションごとにLinuxユーザーアカウントが作成される)などの追加作業が発生します Android 5.0以降(あるいはランタイムにARTを選択したAndroid 4.4)であれば、target側でアプリケーション コードのネイティブ変換 (dex2oat)が行われます これらが終わってから、ようやくIDEがアプリケーションに対してinstrumentationを使用してデバッガーのセッションを開いてデバッグ処理を行えるようになる、というわけです。 いま、上記のような細かい事柄を把握している必要はありません。ポイントは、Androidアプリケーションのビルドとデプロイメントは、数多くのタスクの積み重ねによって遅くなっている、という部分です。 もし、仮にこれらのビルドタスクを、いくつか省略することが出来るとしたら、どうでしょう? ビルドタスクが多すぎるなら、それらを可能な限り減らしてやれば、ビルドやデプロイメントを高速化できるのではないでしょうか? Instant Runは、これを(いくらか)実現したものなのです。 ビルド キャッシュ ここまでは、ビルドとデプロイメントの高速化が重要だ、という話を書きました。では、ビルドを高速化するには、どうしたら良いでしょうか? まず、おおまかな説明から始めようと思います。 Androidアプリケーションのビルドに限らず、あらゆるビルドシステム - make, ant, cmake, ndk-build, bazel, MSBuildなど - に共通する機能として、ビルド出力のキャッシュと再利用が挙げられます。

Read More

デスクトップのC#プロジェクトをPCLプロジェクトに手作業で変換する Posted on Jan 3, 2016


小ネタだけど、日本語で書かれた資料が見当たらなかったので、とりあえず知っていることを書いておこう。(英語では SOに同旨の質問があった。) 通常のC#プロジェクト (.csproj) をPCLのC#プロジェクト (.csproj) に変換するには、以下の(1)〜(3)を行えば良い。 (1) 最初のPropertyGroup要素の下に、以下を追加する。 <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> (実際には、最初のPropertyGroupである必然性は全然なくて、とにかく余計なConditionが無く必ず適用されるPropertyGroupであれば良い。) 他のProjectTypeGuidsがあれば、追加はせず、代わりにその内容を置き換える。 ちなみに、最初のGuidはPCLを、次のGuidはC#であることを示している。だから、もしPCLをサポートする他の言語のプロジェクトにしたい場合は、この値をその言語のGuidにする。 (2) 同じPropertyGroupの下に、以下を追加する: <TargetFrameworkProfile>Profile78</TargetFrameworkProfile> TargetFrameworkVersion要素と並んでいると明確で良い。 TargetFrameworkProfileの値は、指定したいPCLプロファイルの名前にする。 ここで注意すべきは、それぞれのプロファイル名は、それぞれ、特定のTargetFrameworkVersionの値を前提としているということだ。Profile78なら、TargetFrameworkVersionは v4.5 となる。 (3) ルートProject要素の最後の要素として、以下のデフォルトのImport要素 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> を削除し、代わりに以下を追加する: <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> 以上で完了だ。分かってしまえば、スクリプトで自動的に.csprojを生成することも出来るはずだ。

Read More

fluidsynthをAndroid MIDI APIの出力デバイスとして使用する Posted on Dec 15, 2015


Xamarin Advent Calendar 15日目は、Android 6.0で追加されたMIDI APIを経由して、OpenSLESサポートを組み込んだFluidsynthをP/InvokeしてXamarin.Androidから音を鳴らす方法について説明します。 Xamarinを使ってはいますが、一番手間と時間がかかっている部分はXamarinほぼ関係無いです。でもまあXamarinでなければ正直やりたくない作業でもあります。 今回の内容は概ねこのリポジトリで作っているものについてです。(とは言っても、このリポジトリのsubmoduleとそのsubmoduleがメインではありますが…) https://github.com/atsushieno/fluidsynth-midi-service Android MIDIに関心のない人は(99%くらいはそうかと思いますが)、おまけでXamarin.AndroidからNDKでビルドしてP/Invokeで呼び出されるネイティブライブラリをgdbでデバッグする方法について書いておきましたので、そこだけ読まれると良いと思います。 Android MIDI APIとは? Android MIDI APIについては、(別のところでも書いたとおり)今年の夏コミ(C88)でTechBoosterが出したAndroid Masters!という冊子に詳しく寄稿したので、それを見ていただくのが一番なのですが、ここでも簡潔に… Android MIDI APIは、Android 6.0に新しく追加された、AndroidとMIDI楽器を接続してメッセージを送受信するためのAPIです。機能的にはデバイスリストの取得と入出力だけをサポートする、低レベルMIDI APIと呼ぶに相応しいもので、ALSA Raw MIDIやWinMMのMIDI API、あるいはWeb MIDI APIが近い内容でしょう。ALSA sequencer APIみたいな抽象レイヤーはありません。 2015年にもなってMIDIを使うことがあるのか?と思われるかもしれませんが、MIDIメッセージは汎用性が高く、単なる楽器の接続以外でも、IoTなどのメッセージング機構として使われたりします。また、昨今ではBluetooth接続が可能になったことで、楽器とPCやモバイルデバイスをハンディに接続できるようになって、使い勝手が向上したということもあります。iOSではサポートされていて、音楽ソフトではAndroidはオーディオ・レイテンシーなどの問題から大きく出遅れているので、何とかリカバーしたいということもあるでしょう。 Android MIDI APIには、MIDIデバイスに接続するクライアントAPIと、自分でMIDIデバイスを公開する場合に使用するデバイスサービスAPIがあります。MIDIアプリケーションの開発者は、その99%が単にクライアントAPIを使用するだけでしょう。今回はこのクライアントAPIの話は(ほぼ)しません。これを読んでる皆さんには最先端を行っていただきます。 さて、デバイスサービスAPIとは何でしょうか? Android MIDI APIの実装では、USB-MIDIとBluetooth MIDIがデフォルトでサポートされていますが、これに加えて、仮想的なMIDIデバイスを実装することもできるようになっています。デバイスサービスAPIは、このために存在しています。独自のデバイスを開発している人や、バーチャル シンセサイザーを公開したい人は、このAPIを実装することで実現できるというわけです。 今回は、このデバイスサービスAPIを応用して、FluidsynthをAndroidのMIDIデバイスとして使用できるようにしたいと思います。 Fluidsynthとは? Fluidsynthは、主にLinux環境で使われている(けどクロスプラットフォームの)バーチャルMIDIシンセサイザーです。 http://www.fluidsynth.org/ バーチャルMIDIシンセサイザーは、その音色をどこかから調達しなければならないわけですが、Fluidsynthの場合は、「サウンドフォント」と呼ばれる音色定義ファイルの仕組みを活用しています。これはサンプリングデータを音色のバンクとして集めてアーカイブしたもので、sfzやsf2という形式でWebのさまざまな場所で公開・配布されています。音色バンクはGM準拠のものもあれば、そうでないものも数多くあります。MIDI音色用に作成されているというよりは、特定の楽器として作成されVSTのように使用されているほうが多いです。 サウンドフォントのライセンス(というのはまた実体があやふやなものではありますが)がMITなどOSS準拠のものもあり、UbuntuではFluidsynthのパッケージと同様、サウンドフォントのパッケージも公式リポジトリに含まれています。(FluidR3_GM.sf2) FluidsynthはCで開発されており、概ねクロスプラットフォームなのですが、Androidはサポートされているプラットフォームに含まれていませんでした。AndroidでFluidsynthをビルドしている人は何人かいるようなのですが、Fluidsynthがサポートする音源の「ドライバー」までAndroidでPCM再生ができるところまで手を加えた人はいないようです。Fluidsynthには「ファイルに書き出す」機能があるので、それだけ使っていたりするのかもしれません(その程度なら既存のソースをビルド出来るところまで書き換えれば出来ますし)。 cerbero - Fluidsynthをビルドする(完成版) 以下、しばらくXamarinとはほぼ無関係な話が続きます(というか、ここまで1ミリも関わってない)。libfluidsynth.soがAndroid上に存在していることに何の疑問も感じない・感じる必要がないという人は、マネージドコードから操作する部分まで飛ばし読みすると良いでしょう。 さて、このfluidsynth、LinuxやMacOS Xであれば、cmake で普通にビルドできるのですが(のはず)、Androidではそうはいきません。そこで、Android用にはcerberoというビルドシステムを活用します。cerberoは今やや唐突に登場しましたが、ここは http://qiita.com/atsushieno/items/331b76b08b7f9b4aca0f で書いた内容とかぶるので、詳しくはそちらを見て下さい。ここでは、cerberoを使うことは前提として話を進めます。) cerberoを使う最大の理由は、glibを問題なくビルドしてくれることです。Android用にglibをビルドするのは大変面倒なので、cerberoに全力で丸投げします。 ちなみに、上記エントリを書いた時は、gstreamer-sdkのサイトでリンクされていたcerberoのリポジトリをチェックアウトしてビルドしていました。このリンクが実は古い内容を指していて、わたしがコードをハックし始めた時は今年の2月くらいから更新が無かったのですが、実はgstreamer-sdk/cerberoとは別にgstreamer/cerberoというリポジトリがあって、こっちは毎日のように更新されていて、わたしがまともなAndroid対応のために加えた修正がほぼ重複していて、自分の苦労は一体何だったんだろうと…愚痴は見なかったことにして話を進めましょう。 cerberoに、多少ビルドシステムに手を加えた上で、fluidsynthのビルド「レシピ」を追加したものは、以下で公開してあります。(gstreamerで使わないモジュールを追加したものなのと、多少やっつけが入っているのと、Android以外で確認していないのとで、現状では本家から単にforkしています…) https://github.com/atsushieno/cerbero (古) まともに使えるlibfluidsynth.soをビルドする ここは多分少し情報が古くて、おそらくもう必要ないのですが(全て上記gstreamer-sdkのサイトの情報が古かったのが悪い!)、cerberoでビルドされる共有ライブラリ(*.so)は、通常のLinux環境でautotoolsを使用してビルドして出来る共有ライブラリと同様、.soファイルはバージョン番号付きの実体(*.so.1など)へのシンボリックリンクになっていました。これは実はAndroidのライブラリローダーと相性が悪く、シンボリックリンクが解決されずにそのまま読み込まれてエラーになってしまいます。 これを解決するには、gccのオプションで正しい”SONAME”を使用するように指定するのが筋なのですが、cerberoをそれなりにきちんと理解しなければならないのと、glibのビルドをきちんと理解しなければならないのとで、面倒だったので、とりあえずso.Xを含むSONAMEと、それを参照しているであろう部分を全てバイナリハックで書き換えて対応しました(やっつけです)。これは思いつきでやったのではなくて、同じことをやっていた先人がいたためです。 https://github.com/MysticTreeGames/Boost-for-Android/issues/44 最新のcerberoを使用したAndroidビルドでは、SONAMEにバージョン番号が入らないような修正が加えられているようなので、このステップは多分不要です(libiconvやlibcharsetは未だにバージョン番号が付いていたので、そこはかとなく不安がありますが)。 ndk-buildを併用して、シングルバイナリの共有ライブラリをビルドする glibの面倒な点は、実体となるライブラリがlibglib-2.0.soだけでなく、libgthread.soなど多岐にわたる点です。これは面倒なので、出来ればlibfluidsynth.soという1つのファイルに全てまとめてしまいたいところです。 というわけで、少し方針を変えて、fluidsynthはcerberoでビルドせずにndk-buildを使用することにしました。そのためにはAndroid.mkを手書きする必要があったわけですが、fluidsynthのソースツリーの構造は、ソースをいじっていて概ね理解していたので、あまり違和感なく書けました。 https://github.

Read More