超聖域: iOSアプリのビルドツール

iOSアプリ開発担当のふじしげ((@nakiwo)です。

7/1 から 7/7 にかけて社内で行われた「超聖域」 の結果レポートをまとめました。

結論からいいますと、紆余曲折あって最終的は”DSLな設定ファイルを利用するRubyGemを作ってみよう”という取り組みになっています。

フィードテイラーのiOSアプリビルドには、当初itok作のMakefileが利用されていました。xcodebuildを初めとするXcode付属のコマンドラインツールを組み合わせて、ビルドからデプロイを行うものです。Xcodeツール類のバージョンアップに伴う修正や新しい要件に対応するためこのMakefileの改造に着手。気づけば自分がメンテナンス担当に。

日々細かい改良を施すというよりは、必要な時に少しまとまった時間をとって最低限の改良を行うにとどまる事が多かったのですが、超聖域という事で今まで気になりつつも放置していた部分に大きく手を入れよう試みを行いました。

これまでの状況

これまでの状況はざっくりと以下のような感じです。

  • Makefileに改造が必要になる。iOS2時代から綿々と受け継がれる秘伝のタレのレシピを紐解く。
  • そのうちMakefileで何をやってるか分かってきたので改造開始。
  • Makefile内のシェル芸の理解にかかる時間が多くなってきたので、慣れているRubyベースでRakefile化 (シェル苦手)
  • Rakefileをメンテナンス

Xcodeビルドツール類の情報は、現在ではネット上に沢山あるので基本的に困る事はないと思います。ツールとしてまとまったものとしては、SHENZHENや、新しいところではfastlaneが有名どころです。(これらのツールのコードを読むと色々参考になるのでオススメ)

社内のRakefileでは、xcodebuildを直接触って動作確認しつつ、ネット上の各種情報や既存の実装を参考に必要なエッセンスを取り込むような事をしていました。

fastlane

先にあげましたが、新しめのツールとして fastlaneがあります。iOSアプリ開発にまつわる様々なタスクをなんでも自動化してしまおうという壮大なプロジェクト。試してみる価値大では?という印象を受けました。

超聖域は、自前ツールのメンテナンスをやめてfastlaneで既存のタスクを置き換えようという試みからスタートする事に。

設定ファイルであるFastfileで各種ツールの組み合わせを柔軟にカスタマイズ可能で、これを極めて今後はこれ一本で!と勢い勇んで取り掛かりました。しかしいざやってみると細かい挙動を理解するのに一苦労。

fastlaneはそれぞれ単体で利用可能なツール類を組み合わせて利用する仕組みになっています。細かい挙動を知るにはそれら単体ツールの挙動をよく知る必要が有ります。それに加えてfastlane自体の挙動もよく理解している必要があり、思ったより学習コストが高い印象を受けました。

ドキュメントに記載されていない挙動を理解するためにfastlaneツール類のソースコードを読む事しばし。そのうちに色々知見が得られました。残念ながらfastlane自体を使いこなすにはいたりませんでしたが、得られた知見を元に元の社内ツールを改良するというアプローチに方向転換しました。

(fastlane自体はやりたい事にうまくはまると良いツールだと思いますので今後も注目していきたいと思っています)

Gem化したコマンドラインツールへの置き換え

社内Rakefileは必要最低限の機能しかない規模の小さいもので見通しは良い状態にあります。ただ、いくつか問題があったのでそれを潰していく事にしました。

まず既存のRakefileで抱えていた問題点から。

  • 1つのRakefileだけで構成。gitで管理してはいるものの、バージョン管理が曖昧な状態。
  • Rakefile内に全ての必要なコードが書かれている。別途Gemを取り込むということができない構造
  • Rakefileのタスクの挙動を変更するために別途YAMLの設定ファイルを利用している。Ruby DSLな設定ファイルが使えればもう少し柔軟な記述ができるのに..。

問題点を解消するために以下のような構成に変更することにしました。

  • Rakefileではなく、Gem化した専用コマンドツール化する。バージョン管理と依存Gemの取り込みが容易になる。
  • 設定ファイルはYAMLではなくDSL化した専用ファイルにする。設定ファイル内の記述が柔軟に。

ここからはiOS周りの話ではなく、”GemやRuby DSLを作ったことが無い人間によるコマンドラインツール作成”、という内容になります。

作ったコマンドは”ftbuild”と命名しました。(今の所一般公開予定はありません)

Gemの作成

bundlerでGemのひな形を作成することができるのでこれを利用。(bundle gem)

gempsec自体じっくり見るのは初めてでしたが、cocoapodsのpodspecと似た構造なのですんなり理解できました。

(cocoapodsはRubyGemsやBundlerを真似て作られているので似ていて当然)

Gem化する事で、ツールのバージョン管理と、依存Gemの取り込みが一般的な方法でできるようになりました。

このGemは一般公開する予定が無いので、privateなgitリポジトリに置きます。

利用する際は、bundlerのGemfileで以下のようにリポジトリを直接指定します。

gem "ftbuild", :git => 'url/to/ftbuild.git'

次にgemspecに依存Gemを追加。今回は以下のようなGemを利用しました。

  • Thor

    サブコマンドを持つコマンドラインインターフェースを簡単に作成できる。

  • colored

    ターミナルへのテキスト色付け。

  • highline

    ユーザとの対話。パスワード伏字機能アリ。

  • xcpretty

    xcodebuildの出力をいい感じに整形。

  • CFPropertyList

    plistの操作。

  • aws-sdk-resources

    s3の操作用。

あとは元のRakefileに実装していた諸々機能をThorベースでコマンド化していきます。

DSL

昨今の開発シーンでは、RubyベースのDSLを設定ファイルとして利用する事がよくあります。(Gemfile、Podfile、Rakefile、Fastfile、etc..)

fastlaneのソースを読んでいる際、DSLの設定ファイルの取り扱いのコードが案外あっさりしている事がわかったので実装してみました。(参考にしたのはこのあたり)

基本はRubyで書かれた設定ファイルを読み込んでinstance_evalするだけです。

content = File.read(path)
self.instance_eval(content)

あとは上の例でselfとなっているオブジェクトに、設定項目に対応するメソッドを用意します。

例えば設定ファイルに

hogehoge "xyz"

という項目を作りたい場合は、

def hogehoge(val)
  # valを保存
end

というメソッドを作って値を保存するなり好きなように料理します。

単純な値設定のみの項目であれば、method_missing を実装して処理を共通化するとすっきりします。(参考コード)

ここまでの段階だけでも設定ファイル側では、

dir_path = "path_to_dir"

hogehoge dir_path + "_1"
hogehoge dir_path + "_2"

のような記述が可能になり、YAMLの設定ファイルより記述力が格段に増します。

まとめ

  • fastlaneは要注目。
  • fastlane配下の各種ツールは単体で使える。必要なものだけつまみ食いするのも有効。
  • RubyGem・Ruby DSLの作成は思ったより敷居が低かった。

当初の作戦(fastlaneへの移行)とは違う方向になりましたが、結果的には社内ツールが利用・メンテナンス共にすっきりした状態に移行できました。

このエントリーをはてなブックマークに追加