R.Swiftのアップデートによるエラーを解決するついでにRun ScriptやBuild phasesについて整理してみた
こんにちは。クラスメソッド福岡オフィス 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のビルドの仕組みや各種設定項目の内容を理解することで同様のエラーが返ってきた時にきちんと対応できると思います。
これからもトラブルやエラーが発生した時は原因と背景をきちんと理解して解決して、できればブログに書けるようにしたいと思います。