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

これは[技術書典#1](https://techbookfest.github.io/)で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>

ビルドしてみましょう。

	~$ xbuild x.proj /nologo

	Build started 5/25/2016 3:08:28 PM.
	__________________________________________________
	Project "/home/atsushi/Desktop/ManualProject/ManualProject/x.proj" (default target(s)):
		Target x:
			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.0638150

ただし、同じ名前のターゲットは、後から内容を差し替えることが出来てしまうので、このサンプルのTargetをコピーして下に追加した上で、内容を変更すると、その内容が（最初のターゲットの内容を書き換えて）実行されます。

	~$ cat x.proj 
	<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	  <Target>
		<Message Text="Hello, MSBuild" />
	  </Target>
	  <Target>
		<Message Text="Hello, MSBuild 2" />
	  </Target>
	</Project>

ビルドしてみましょう。

	~$ xbuild x.proj /nologo

	Build started 5/25/2016 3:09:55 PM.
	__________________________________________________
	Project "/home/atsushi/Desktop/ManualProject/ManualProject/x.proj" (default target(s)):
		Target :
			Hello, MSBuild 2
	Done building project "/home/atsushi/Desktop/ManualProject/ManualProject/x.proj".

	Build succeeded.
		 0 Warning(s)
		 0 Error(s)

	Time Elapsed 00:00:00.0639620

## C#のプロジェクト

さて、通常われわれはC#/.NETのプロジェクトを作ります（F#でもかまいませんが、今日は言語によって内容が変わるので、C#に固定します）。C#プロジェクトは、通常C#プロジェクトをビルドするために必要な`Microoft.CSharp.targets`というファイルをインポートして作成します。いったんこのファイルをインポートすると、Compileというビルドアイテムで、C#ソースファイルを指定することが出来ます。

	~$ cat y.proj 
	<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	  <ItemGroup>
		<Compile Include="test.cs" />
	  </ItemGroup>

	  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
	</Project>

このインポートを宣言した後は、もうターゲットを定義する必要はありません。このインポートされるファイルの中で定義されているためです。一方、`ItemGroup`という新しい要素が出てきましたが、これはビルドに使用される各種ファイルを指定するために使用される、MSBuildで定義されている要素です。ここでは、`test.cs`というファイルが`Compile`という要素で定義されていますね。この`ItemGroup`の内容は、「ビルド アクション」を要素名に、ファイル名をInclude属性に、それぞれ指定したものになります。

	~$ cat test.cs
	public class Test
	{
		public static void Main ()
		{
			System.Console.WriteLine ("Hello, C#");
		}
	}

ではビルドして実行してみましょう。

	~$ xbuild y.proj /nologo

	Build started 5/25/2016 3:14:14 PM.
	__________________________________________________
	Project "/home/atsushi/Desktop/ManualProject/ManualProject/y.proj" (default target(s)):
		Target PrepareForBuild:
			Configuration: Debug Platform: AnyCPU
		Target GenerateSatelliteAssemblies:
		No input files were specified for target GenerateSatelliteAssemblies, skipping.
		Target GenerateTargetFrameworkMonikerAttribute:
		Skipping target "GenerateTargetFrameworkMonikerAttribute" because its outputs are up-to-date.
		Target CoreCompile:
		Skipping target "CoreCompile" because its outputs are up-to-date.
	Done building project "/home/atsushi/Desktop/ManualProject/ManualProject/y.proj".

	Build succeeded.
		 0 Warning(s)
		 0 Error(s)

	Time Elapsed 00:00:00.1992210
	~$ mono bin/Debug/y.exe
	Hello, C#

簡単に出来ましたね。ちなみに、アセンブリ参照を追加したい場合は、これも`ItemGroup`の内容として追加します。要素名は`Reference`で、ファイル名はパス無しのアセンブリ名で大丈夫です。

	  <ItemGroup>
		<Compile Include="MainActivity.cs" />
		<Reference Include="System" />
		<Reference Include="System.Core" />
	  </ItemGroup>

プロジェクトファイルの全容は省略しましたが、この部分以外は先の例と同じです。Linqを使ってみましょう。

	~$ cat test.cs
	using System.Linq;
	public class Test
	{
		public static void Main ()
		{
			System.Console.WriteLine ("Hello, C#".Last ());
		}
	}

	~$ xbuild y.proj /nologo

	Build started 5/25/2016 3:29:15 PM.
	__________________________________________________
	Project "/home/atsushi/Desktop/ManualProject/ManualProject/y.proj" (default target(s)):
		Target PrepareForBuild:
			Configuration: Debug Platform: AnyCPU
		Target GenerateSatelliteAssemblies:
		No input files were specified for target GenerateSatelliteAssemblies, skipping.
		Target GenerateTargetFrameworkMonikerAttribute:
		Skipping target "GenerateTargetFrameworkMonikerAttribute" because its outputs are up-to-date.
		Target CoreCompile:
		Skipping target "CoreCompile" because its outputs are up-to-date.
	Done building project "/home/atsushi/Desktop/ManualProject/ManualProject/y.proj".

	Build succeeded.
		 0 Warning(s)
		 0 Error(s)

	Time Elapsed 00:00:00.2009900
	~$ mono bin/Debug/y.exe
	#

無事動きました。

## Xamarin.Androidのプロジェクト

さて、いよいよXamarin.Androidプロジェクトを作ってビルドしてみましょう。少しややこしくなりますが、（ほぼ）最小の構成は以下のようになるでしょう。

	~$ cat ManualProject.csproj 
	<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	  <PropertyGroup>
		<Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
		<OutputType>Library</OutputType>
		<!-- AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk -->
		<AssemblyName>ManualProject</AssemblyName>
		<AndroidApplication>True</AndroidApplication>
	  </PropertyGroup>
	  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
		<OutputPath>bin\Debug</OutputPath>
	  </PropertyGroup>
	  <PropertyGroup Condition="'$(Configuration)'=='Release'">
		<OutputPath>bin\Release</OutputPath>
	  </PropertyGroup>
	  <ItemGroup>
		<Compile Include="MainActivity.cs" />
		<Reference Include="System" />
		<Reference Include="Mono.Android" />
	  </ItemGroup>
	  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
	</Project>

新しく`PropertyGroup`という要素が出現しましたが、これはMSBuildプロパティを定義するために使われる、MSBuildが最初から用意している要素です。この中に、プロジェクトのオプションなどがXML要素として格納されます。

OutputType（ライブラリまたは実行形式）, OutputPath（出力パス）, RootNamespace（デフォルトのC#ネームスペース）, AssemblyName（出力アセンブリ名）は、実のところ通常のC#プロジェクトでも使われるものです。OutputPathは、通常のC#プロジェクトでは「無くても良い」のですが、Xamarin.Androidでは必要になります。

Xamarin.Androidプロジェクトでは、インポートされるのは`Xamarin.Android.CSharp.targets`になります。これは、Androidプロジェクトをビルドするために必要なビルド構成要素を定義しているものです。

このプロジェクトでは、使用するAndroidのAPI Levelを指定していないのですが、その場合は（現在は）`10`になります。

apkをビルドする際には、ただの`Build`ターゲットではなく、`SignAndroidPackage`を呼び出します。

	~/$ xbuild ManualProject.csproj /t:SignAndroidPackage /nologo >/dev/null
	~/$ find . -name *.apk
	./bin/Debug/ManualProject.ManualProject.apk
	./bin/Debug/ManualProject.ManualProject-Signed.apk
	./obj/Debug/android/bin/ManualProject.ManualProject.apk

どうでしょうか? Xamarin.Androidプロジェクトも、割と簡単に出来てしまいましたね。こんな感じで、IDEが無い環境でも、Androidプロジェクトを作ることは、一応は可能です。