Vagrant で Mozc for Android を作る

先日、Google 日本語入力の Android 版のソースコードが Mozc プロジェクトで公開されました。いろいろ不満の多い Android の日本語入力環境で、オープンソースで慣れ親しみのあるプロジェクトでソースコードが公開1されることでにわかに良さげなハックが生まれる機運があります。

以前より Mozc にパッチを当てやすいように Github にミラーしていたのですが、今回もちゃんと追従したので、さて Android 向けにビルドしてみるかと思った次第です。しかし公開されたドキュメントには非情にも

開発環境 Ubuntu 12.04 で開発できることを確認しています。今のところ、Mac、Windows での開発には対応しておりません。

との記載が。あえてイバラの道になるだろう、Mac OS X でビルドできるようにするという選択肢はあるにせよひとまずは推奨環境でビルドしようと VMware の Ubuntu 12.04 環境を用意したり頑張ったのですが2如何せん面倒すぎる上に入れるべきパッケージが多くてこれはかなりビルドが大変になったと言わざるを得ない状況でした。

そこで今回は簡単に Mozc for Android を作ることのできる環境を作ってみようと思います。実際のスクリプトなどは Github の Mozc のミラー においてあります。

Vagrant って流行ってるらしいよ

Vagrant

最近この界隈でやたらと Vagrant についての話題が上がっていて、インストールだけはしていたのだけどマジメに使ってなかったのですが、まさに今回の問題解決向けではないかということで真面目に使ってみました。

Vagrant は仮想マシン(や、AWS のインスタンス)のフロントエンドとして動き、開発環境の構築とアクセスを自動化してくれるアプリケーションです。裏側の仮想マシン相当の環境は Provider と呼ばれており、デフォルトは VirtualBox を利用して、有償で VMware にも対応しています。

使い方はかなり簡単で、Vagrant のサイトにわずか3行の Getting Started があるとおり、任意のプロジェクトのディレクトリでそれらのコマンドを発行すれば確かに仮想マシンが動いてSSHとかが提供されます。

vagrant box add base http://files.vagrantup.com/lucid32.box
vagrant init
vagrant up

vagrant initVagrantfile が作成され、.vagrant にインスタンスの設定が保持されます。 プロジェクトのディレクトリは /vagrant にマウントされます。

$ vagrant ssh
% mount
...
vagrant-root on /vagrant type vboxsf (uid=1000,gid=1000,rw)
...

作られる仮想マシンの実態は VirtualBox の場合、Virtual Box の 仮想マシン保存先(デフォルトでは ~/VirtualBox VMs かな)に作成されます。

環境設定を自動化する Puppet とか Chef とか

Vagrant が使う Vagrantfile には仮想マシンの設定を記述できますが、仮想マシン内部の環境設定、例えばどのパッケージを入れるかとか /etc をどうするかとかの、環境の恒常性の維持には別途 Provisioning ツールを使います。 Vagrant は業界のデファクトらしい Puppet とか Chef に対応していて、 vagrant up した時や vagrant provision することで恒常性を維持してくれます。

Puppet や Chef にはひょっとしたら馴染みがないかもしれないのですが、大規模なホストの恒常性の維持に平日の業務では使っていて Ops の人たちとの会話はこれらのツールむけのスクリプトで行われると言っても過言ではなかったりします。

これらのツールの目的はホストのリソース(ファイルとかパッケージとか)を自動である状態に保つことにありますが、仮想環境の初期設定などでも力を発揮するわけですね。

Mozc for Android の開発環境を整える

最近はいろんな日本語ブログで Chef については良く見ると思いますので、ここではあまり話題になっていない、でも使ったことのある Puppet を使ってみようかと思います。

Puppet は大規模化する際には Module を定義しまくって、独自のヘンテコDSLでマジカルに記述できるのですが、そこまで正規化する必要もないので一つの Manifest で済ませます。Vagrant はデフォルトで Puppet の Manifest がある場所を指定すると、default.ppをエントリポイントとして使いますのでこのファイルにいろいろ書いていきます。

Vagrant.configure("2") do |config|
  ...
  # Varantfile の中でこう書くと、./manifests/default.pp が使われる
  config.vm.provision :puppet do |puppet|
    puppet.manifests_path = "manifests"
  end
  ...
end

Mozc for Android のビルドにはいくつかのパッケージ(GCCとか)と、Android SDK 及び Android NDK が必要です。この Android 向けのツールが曲者で、OSのパッケージ管理の外にあって、Google から tar.gz や zip で公開されているので手動で取得してくる必要があります。

さらに、Android SDK は SDK 自体にパッケージ管理機能があるのでさらに複雑なことになります。主要な部分を抜粋して書くとこんな感じ。

# インストール先を準備
file {
  "/opt/android":
    ensure => directory,
    owner  => "vagrant",
    group  => "vagrant",
    mode   => 0755;
}

exec {
  # Android SDK の tar.gz をダウンロードして展開する
  "download android-sdk":
    command => "/usr/bin/curl 'http://dl.google.com/android/android-sdk_r21.1-linux.tgz' | /bin/tar -x -z -C /opt/android",
    # 展開すると android-sdk-linux ができるから、これがあったら実行しない
    creates => "/opt/android/android-sdk-linux",
    user    => "vagrant",
    require => [
      Package["curl"],
      File["/opt/android"]
    ];

  # Android SDK を最新の状態にする
  "update android-sdk":
    # android コマンドを -u で GUI なしで実行
    # -t でアップデートするパッケージを限定。
    # エミュレータを動かす必要がないというのと、 android コマンドのバグで
    # system-image は取得済みでも毎回更新してしまうので除外。
    command => "/opt/android/android-sdk-linux/tools/android update sdk -u -t platform,tool,platform-tool,extra-android-support",
    user    => "vagrant",
    # すげー時間がかかるのでタイムアウトしないようにする
    timeout => 0,
    require => [
      Package["default-jdk"],
      Exec["download android-sdk"]
    ];
}

このような Puppet の Manifest と、 Vagrant の設定を用意しておくと、あら不思議。 プロジェクトのレポジトリを Github から Clone して vagrant up するだけで 30 分くらいでビルド環境ができてしまいます。

開発環境ができたあとは

Mozc for Android のビルド手順にしたがってビルドしていけば問題ないのですが、面倒なのでスクリプトを書きました。

sh build_android.sh

を実行して 30 分くらいで apk が出来上がります。-f をつけると全消しして最初からやりなおしてくれます。

真っ白なキーボード

以上で簡単に Mozc for Android をつくって遊ぶ環境が整ったので、Fork してパッチを書いて真っ白なキーボードに文字を置いたりして遊びましょう!

  1. Mozc は現状、通常のオープンソースとは言いがたい状況です。あくまでもソース公開であって、コントリビュートは出来ない状態なのです。 ↩︎

  2. 途中で / の空き容量がなくなって大変なことになりました。 ↩︎