R.Swiftのアップデートによるエラーを解決するついでにRun ScriptやBuild phasesについて整理してみた

Run ScriptなどのBuild Phaseについて、ライブラリの導入で必要になった時にしか触らない項目で概要をきちんと理解しているとは言い難かったので整理してみました。
2019.07.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。クラスメソッド福岡オフィス CX事業本部でiOSアプリエンジニアとして働いている田辺です。手元のプロジェクトがのSwiftのバージョンが4.0なのですが、5.0にアップデートしたくて少しずつ進めていました。

プロジェクトの依存ライブラリの一つにR.Swiftがあり、ビルドが通らなかったのでエラーメッセージを見ていると

error: [R.swift] Output path must specify a file, it should not be a directory.
error: [R.swift] Build phase Intput Files does not contain '$TEMP_DIR/rswift-lastrun'.
error: [R.swift] Build phase Output Files do not contain '$SRCROOT/R.generated.swift'.

というエラーありました。

R.Swiftのこのエラーは以下の画像のように、Run Scriptの各項目を変更する必要があります。

また、変更の手順はR.swift/Migration.md at master · mac-cain13/R.swiftを参照するようエラーで示されています。

このエラーを直す時にRun ScriptなどのBuild Phaseについてきちんと理解していないとBuild Phasesについて触れる時は指示通り従うだけになってしまうため、良い機会なので調べてみました。

Build Phases

Xcodeでは、Build PhasesセクションのRun Script Phaseを使用して、ビルドプロセスの一部としてユーザーが定義したコードを実行することができます。

Build Phasesではターゲットをビルドする時に様々なタスクを実行できるよう設定できます。

Run ScriptはそんなBuild Phasesのうちのひとつです。

  • Compile sources
  • Copy bundle resources
  • Copy files
  • Headers
  • Link binary with libraries
  • Run script
  • Target dependencies

Compile sources

Compile sourcesではターゲットを構築する過程でどのソースファイルをコンパイルすべきかをコンパイラに知らせることができます。

ライブラリにはサンプルコードや特定のAppleのフレームワークをサポートするファイルなどが含まれている場合があります。必要ないことが確定している場合Compile sourcesで除外するよう設定できます。

また、ファイルを追加した時にtargetに追加するオプションを選択するのを忘れた時に後になってコンパイルするファイルに含める時に使用することができます。正直こちらの用途でお世話になることの方が多そうだなと感じます。

Copy bundle resources

Build PhasesのCopy bundle resourcesにあるものはリソースとして内部から扱うことができるようにコピーされます。リソースはターゲットに関連付けられ、ビルド後のプロダクトのResourcesサブフォルダにコピーされます。

プロジェクト内のファイルを使用する際にBundle.main.path(forResource:ofType:)というメソッドを使ってファイルまでのパスを取得する時があります。Copy bundle resourceで指定したファイルにはこのメソッドでアクセスできます。

Copy files

プロジェクトファイルと他のターゲットのプロダクトをターゲットと関連付けます。Copy filesというBuild phaseは、すべてのビルドに対して有効にすることも、特定のビルドにのみ有効にすることもできます。

Headers

まず、Appleでのフレームワークの定義を確認します。AppleのFrame work Programming Guideを引用すると

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

とあります。ヘッダーファイルはフレームワークのリソースの一つと考えて良さそうです。

また、Wikipediaによると、ヘッダーファイルはコンパイラが別のソースファイルの一部として自動的に展開して使用するファイルとあります。

HeadersというBuild PhaseではPublic 、Private、Projectのヘッダーファイルをターゲットに関連付けることができます。 パブリックヘッダーとプライベートヘッダーは、他のクライアントによる使用を目的としたAPIを定義し、インストール用にプロダクトにコピーされます。

ライブラリ、フレームワークをプロジェクトの導入する時にで触る場合があります。

Link binary with libraries

ライブラリやフレームワークの導入の際に使います。ビルドのタイミングでアプリケーションにリンクされるライブラリやフレームワークの導入です。

Xcodeで外部から導入できるモジュールにはStatic Library、Dynamic Library、Frameworkがあります。Static Libraryは文字通り静的に組み込まれるライブラリのことです。コンパイル時にリンクされます。Dynamic Libraryはランタイム時に実行されます。.dylibという拡張位をみたことがある人もいるかと思います。

.frameworkという拡張子がFrameworkです。画像やxibを含むことができます。XcodeのNewからプロジェクトを新規作成してフレームワークを作るとframeworkが作れます。

アプリケーション内でFrameworkやLibraryのAPIを参照したい時はこちらで設定していきます。

Target dependencies

現在のターゲットを構築する前に構築する必要があるもう1つのターゲットをここで指定します。 たとえば、アプリターゲットとフレームワークターゲットがある場合、アプリのターゲットはフレームワークに「依存」しています。ライブラリを追加する時などにLink binary with librariesと共に設定する必要があります。

Run scriptの新規作成

Build Phasesそれぞれについて説明したのでRun scriptに戻ります。

ProjectのBuild Phasesで「+」ボタンをタップして、New Run Script Phaseを選択する。Run Scriptは上から順番に実行されていきます。文字列を出力するだけのスクリプトを書いてBuildしてみます。

Report Navigatorの直近のBuildをクリックして先ほど入力した文字列を探しみると確かに出力されているのがわかります。

Scriptの実行を特定の構成に制限したい

そういう場合は以下のようにします。

if [ "${CONFIGURATION}" = "ここに任意の構成" ]; then
    echo "このブロックはifで指定した構成でしか実行されない"
fi

環境変数

ドキュメントはこちらになります。

*それぞれの環境変数が何を指すか確認できる方法があります。

Add Run Script Build PhaseでRun Scriptを追加して、Shell下の入力欄に ' env > ./env.txt' と入力後ビルドするとenv.txtファイルが生成されます

ビルドすると指定したディレクトリにenv.txtファイルが生成されていて開くと環境変数の中身が全て見れます。非常に長いのでスクリーンショットを貼ります。

Xcode10以降のRun Script

Xcodeのビルドシステムが変わり、ファイル管理の方法も変わりました。

実は今回の記事のきっかけになったR.Swiftのエラーもそれに関係するものです。

シェルスクリプトによって生成されるファイルがビルド時に別のどこかでInput fileとして使われる場合、その際に生成されるファイルをOutputとして明示的に設定される必要があります。ない場合はビルドエラーが発生します。

まとめ

この辺りはライブラリの導入周りで触れるぐらいで通常の開発で頻繁に目にする設定項目ではありませんでした。XCodeのビルドの仕組みや各種設定項目の内容を理解することで同様のエラーが返ってきた時にきちんと対応できると思います。

これからもトラブルやエラーが発生した時は原因と背景をきちんと理解して解決して、できればブログに書けるようにしたいと思います。

参考