Xcodeのビルド/バージョン番号をagvtoolで管理

Xcode 8にプロジェクトを変えたところで、あるプロジェクトで、PlistBuddyでバージョン番号をアップしていたのがあるのですが、スクリプトが引き継がれなくなったので、今風のやり方はないかと思って検索したら、agvtoolがあるなということで、使い方を考えてみました。Apple Generic Versioning Toolの略だそうです。ただ、いろいろ調べたり、結構詳しいmanを見て考えました。一番素直な使い方はどんなのだろうか?Xcodeのプロジェクト1つだけの場合と、別のプロジェクトを組み込んだ場合で考えて見ました。

使い方の前に、ここでいうVersioningとは、XcodeのプロジェクトのGeneralの設定にある「バージョン」と「ビルド」です。英語版しか今はないので画面通りに記述すると、IndentityのカテゴリにあるVersionとBuildです。

shot3250x

このうち、Finderで表示されるいわゆるバージョンはここのVersionですが、Buildは一般には参照できません。Finderのバージョン表記に出てくる場合もありますが、それはさらにこれらとは別のバージョン表記です。バージョンに関する情報はアプリケーションバンドルのInfo.plistに記述されますが、次のような関係にあります。

短いバージョン 長いバージョン ビルド番号
Finderの情報ウインドウ バージョン バージョン 非表示
Finderウインドウ内 バージョン 非表示 非表示
WebのApp Store
iOSでのApp Storeアプリ
バージョン  非表示  非表示
Xcodeでの設定 Version Info.plistに設定する Build
Info.plistのキー CFBundleShortVersionString CFBundleGetInfoString CFBundleVersion

Finderの情報ウインドウに見えるバージョンには、Info.plistに長いバージョンつまり、CFBundleGetInfoStringがあればそれを表示しなければ短いバージョンつまり、CFBundleShortVersionStringを表示します。Finderウインドウをカラム表示にした時に右端に表示されるアイコン下の属性は、常に短いバージョンつまりCFBundleShortVersionStringが表示されます。

一方、iOSについては、Web上のApp StoreやiOSでのApp Storeアプリで、短いバージョンすなわちCFBundleShortVersionStringが見えていますが、iTunesでは見えていません。

表の最初の3行は一般ユーザーの目に触れるところで、最後の2行はXcodeやInfo.plistの世界です。つまり、ここではCFBundleShortVersionStringがバージョンで、CFBundleVersionがビルドであるということを認識してください。どっちもキーのキーワードにVersionとあり、一見するとCFBundleVersionがバージョン番号のように思えてしまうところが引っかかるところかと思います。以下、「バージョン」と「ビルド」としてそれぞれの設定を参照します。

agvtoolを使う基本的な設定

プロジェクトのビルド設定にあるBuild Settingsには多数の設定項目があります。そこのVersioningのカテゴリを見てください。Versioning Systemの右側の値が入っているところをクリックすると、ポップアップニュが出てくるので、「Apple Generic」を選択します。まず、この設定を行います。

shot3249

それから、上の図にはありませんが、同じVersioningのカテゴリにあるCurrent Project Versionに何か数字を入れてください。この数字はビルド番号の初期値となります。キー名に「Version」とありますが、このキーはビルドの値をおぼえておくために使われています。なお、その他のキーを見るとPrefixやSuffixなどがあり、ビルド番号を構築する時にこれらの文字列を前後につけることができるようになっています。

他のプロジェクトを読み込んでいないプロジェクト

まず、単純なプロジェクト、つまり、他のプロジェクトを読み込んでいないようなプロジェクトを考えます。この場合、ターゲットは通常は1つであるので、アプリケーションのバージョンはそのターゲットを1箇所変えるだけです。そのために、わざわざコマンドを動かすよりかは、Xcodeの画面上で手作業で変えた方がいいでしょう。ということで、バージョンについては自動化の必要はないと考えました。

一方、ビルドの方は、本当にビルドをした時、あるいはProductメニューからArchiveを選んでアーカイブを作った時など、ある程度自動的に設定をしたいと考えるでしょう。そのためには、タイツルバーのプロジェクト名の見えている部分をクリックして、Edit Schemeを選択します。そして、左側のBuildやArchiveの設定に、以下のようなスクリプトを追加します。Edit Schemeを選択してシートが出てきます。BuildあるいはArchiveのPre-actionsないしはPost-actionsを選択します。おそらく、最初は何もないと思われます。シートの下にある「+」をクリックして、New Run Script Actionを選択すると、以下のような画面になります。ここで、2行のシェルスクリプトを入れると同時に、Provide build settings fromのところで、アプリケーションのターゲットを選択しておきます。

shot3259

cd "${PROJECT_DIR}"
agvtool next-version -all

これでCloseをクリックします。上記の場合だと、command+Bでビルドすると、ビルドの番号がアップするはずです。現在の番号は、Build SettingsにあるCurrent Project Versionの値を取り出してアップして、「ビルド」の枠に設定します。現在、ビルドの枠に見えている数字がアップするのではありません。ちなみに、このagvtoolは、プロジェクトのルートをカレントにして動くようになっており、引数でプロジェクトのディレクトリを設定するようになっていません。そこで、ビルド時に作成される環境変数のPROJECT_DIRを利用してカレントディレクトリを移動し、コマンドを入力しています。agvtoolの引数はプロジェクトや値に関わらず、常にこの通りです。

プロジェクトを読み込んでいるプロジェクト

プロジェクトを分けていて、あるプロジェクトで統合している場合、まず、ビルド番号は前に示したような方法で、各プロジェクトに設定するしかないと思われます。ビルドの数はプロジェクトごとに違うでしょうから、それはそれでいいかと思います。

一方、バージョンの方は、以下のように一気にまとめて設定ができるようになっています。統合したプロジェクトのディレクトリをカレントにして、「agvtool new-marketing-version」に続いてバージョン番号を指定します。このマーケティングバージョンというのが、「バージョン」になるということです。

 

$ agvtool new-marketing-version 5.1
Setting CFBundleShortVersionString of project DLS to: 
    5.1.

Updating CFBundleShortVersionString in Info.plist(s)...

Updated CFBundleShortVersionString in "DLS.xcodeproj/../DLSTests/Info.plist" to 5.1
Updated CFBundleShortVersionString in "DLS.xcodeproj/../DLSUITests/Info.plist" to 5.1
Updated CFBundleShortVersionString in "DLS.xcodeproj/../DLS/Info.plist" to 5.1
Updated CFBundleShortVersionString in "DLS.xcodeproj/../Installer/Info.plist" to 5.1
Updated CFBundleShortVersionString in "DLS.xcodeproj/../Utility/Info.plist" to 5.1

ここで、読み込んでいるプロジェクトのInfo.plistも設定されることをメッセージで確認しましょう。

ただ、いくつか試してみた限りでは、読み込んだプロジェクト側にバージョン番号が反映される場合と反映されない場合がありました。以前から読み込みをしてあったプロジェクトは全てに反映されるのに、新たに作ったプロジェクトはだめでした。また、agvtool next-version -allの呼び出しをPre-actionsに設定すると、読み込み側のプロジェクトのビルドがキャンセルされることも見られたので、その場合はPost-actions側に設定することでビルドは全部流れるようになりました。

この辺りは、理由が分かれば追記しようと思います。

何れにしても、agvtoolのパラーメータは多数ありますが、ここで紹介したように、next-versionのものをプロジェクトにスクリプトとして仕組むことと、バージョンの設定のためにnew-marcketing-versionを使うことで概ねのことは賄えると思います。まずはこの線から始めてみてはどうでしょうか?