JPMartha's Pancake

This blog is the way to brush up my poor English.

Xcode の Build Settings について調べたこと 🤔

2016年2月度Cocoa勉強会関西ビギナーズ : ATND に参加しました。

この勉強会は基本自習で 以前から取り組んでいる コマンドラインツールの Build Settings について調べました。

Carthage をベースに とりあえず動くもの はできたのですがなぜそのように設定する必要があるのかを考えました。

github.com

f:id:JPMartha:20160221142024j:plain

概要

  • Carthage と同様に Build Settings などを設定
  • フレームワークや dylib の格納先を確認
  • それらを突き合わせて検証

前提

  • Carthage と同様に Build Settings などを設定した 自作ツール をサンプルとします。
  • コマンドラインツールpancake )は Build Configuration を Release に設定しています。
  • デフォルトから変更した箇所を対象とします。どのように変更したかは こちらの記事 と同じです。
  • 変数の説明については途中で書くと見づらいので後回しにしています。
  • Makefileextract-toolxcconfigs が関係しますが少しでも簡潔にするためこの記事では触れません。
  • 説明用に行番号をつけていますが実際の設定にはありません。

github.com

Build Settings

コマンドラインツール(pancake)の設定

Run Search Paths
1-1: @executable_path/.
1-2: @executable_path/PancakeKit.framework/Versions/Current/Frameworks
1-3: /Library/Frameworks
1-4: /Library/Frameworks/PancakeKit.framework/Versions/Current/Frameworks
1-5: $(inherited)

フレームワーク(PancakeKit)の設定

Run Search Paths
2-1: $(inherited)
2-2: @executable_path/../Frameworks
2-3: @loader_path/../Frameworks
Dynamic Library Install Name
3-1: @rpath/$(PRODUCT_NAME).$(WRAPPER_EXTENSION)/$(PRODUCT_NAME)

👉 @rpath/PancakeKit.framework/PancakeKit になります。

Xcode の Build Settings で設定すると変換されます。

格納先

フレームワークや dylib がどこに格納されるのかを確認します。

コマンドラインツール(pancake)

ビルド時

👉 /…/Release/pancake.app/Contents/MacOS/ pancake

インストール時

👉 /usr/local/bin/ pancake

フレームワーク(PancakeKit)

ビルド時

👉 /…/Release/ PancakeKit.framework

インストール時

👉 /Library/Frameworks/ PancakeKit.framework

Build Settings と格納先の突き合わせ

Build Settings の設定内容、ビルド時とインストール時それぞれの格納先を突き合わせどこを指すのかを確認します。

コマンドラインツール(pancake)

Run Search Paths

1-1:

@executable_path/.

👉 /…/Release/

ビルド時フレームワークを指します。

  • PancakeKit.framework

f:id:JPMartha:20160221201906p:plain

1-2:

@executable_path/PancakeKit.framework/Versions/Current/Frameworks

👉 /…/Release/PancakeKit.framework/Versions/Current/Frameworks

ビルド時 の dylib を指します。

  • libswiftCore.dylib
  • libswiftCoreGraphics.dylib
  • libswiftDarwin.dylib
  • libswiftDispatch.dylib
  • libswiftFoundation.dylib
  • libswiftObjectiveC.dylib

f:id:JPMartha:20160221141603p:plain

ここで Embedded Content Contains Swift Code が関係します。

デフォルトでは No ですが フレームワークPancakeKit)では Yes にしています。

No だとこのように dylib が格納されません。

f:id:JPMartha:20160222073628p:plain

1-3:

/Library/Frameworks

インストール時フレームワークを指します。

  • PancakeKit.framework

f:id:JPMartha:20160221203101p:plain

1-4:

/Library/Frameworks/PancakeKit.framework/Versions/Current/Frameworks

インストール時 の dylib を指します。

  • libswiftCore.dylib
  • libswiftCoreGraphics.dylib
  • libswiftDarwin.dylib
  • libswiftDispatch.dylib
  • libswiftFoundation.dylib
  • libswiftObjectiveC.dylib

f:id:JPMartha:20160221141603p:plain

1-5:

$(inherited)

継承については こちらの記事 で書きました。

公式情報は今のところ見当たりませんが「 XCodeのBuild Settingsで、値が決まっていくルール - Qiita 」によれば親の設定を継承するそうです。

フレームワーク(PancakeKit)

Run Search Paths

2-1:

$(inherited)

継承については こちらの記事 で書きました。

⚠️以下推測です。

設定なし(コマンドで確認すると半角スペース)は /.../Release を指す?

なぜならここに ビルド時フレームワークがあるからです。

  • Commandant.framework
  • Himotoki.framework
  • Result.framework
  • SourceKittenFramework.framework
  • SWXMLHash.framework

f:id:JPMartha:20160221171537p:plain

2-2:

@executable_path/../Frameworks

👉 /.../Release/pancake.app/Contents/Frameworks

ビルド時 の dylib を指します。

  • libswiftCore.dylib
  • libswiftCoreGraphics.dylib
  • libswiftDarwin.dylib
  • libswiftDispatch.dylib
  • libswiftFoundation.dylib
  • libswiftObjectiveC.dylib

f:id:JPMartha:20160222093040p:plain

2-3:

@loader_path/../Frameworks

👉 /Library/Frameworks/PancakeKit.framework/Versions/A/Frameworks

インストール時フレームワークと dylib を指します。

  • Commandant.framework
  • Himotoki.framework
  • Result.framework
  • SourceKittenFramework.framework
  • SWXMLHash.framework
  • libswiftCore.dylib
  • libswiftCoreGraphics.dylib
  • libswiftDarwin.dylib
  • libswiftDispatch.dylib
  • libswiftFoundation.dylib
  • libswiftObjectiveC.dylib

f:id:JPMartha:20160221171740p:plain

Dynamic Library Install Name

3-1:

@rpath/$(PRODUCT_NAME).$(WRAPPER_EXTENSION)/$(PRODUCT_NAME)

前述のとおり @rpath/PancakeKit.framework/PancakeKit になります。

👉 ビルド時 は /.../Release/pancake.app/Contents/Frameworks/PancakeKit.framework/ PancakeKitエイリアス)を指します。

f:id:JPMartha:20160221162657p:plain

その実体は /.../Release/pancake.app/Contents/Frameworks/PancakeKit.framework/Versions/A/ PancakeKit です。

f:id:JPMartha:20160221161128p:plain

👉 インストール時 は /Library/Frameworks/PancakeKit.framework/ PancakeKitエイリアス)を指します。

f:id:JPMartha:20160221162636p:plain

その実体は /Library/Frameworks/PancakeKit.framework/Versions/A/ PancakeKit です。

f:id:JPMartha:20160221161128p:plain

デフォルトは次のとおりですがこれだとエラーになります。

$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)

👉 @rpath/PancakeKit.framework/Versions/A/PancakeKit

変数の説明

Run Search Paths (LD_RUNPATH_SEARCH_PATHS)

依存するライブラリを検索するパスです。複数指定できます。(雑)

Space-separated list of directory paths. Specifies the run-path locations at which the dynamic loader searches for the product’s run-path dependent libraries.

Build Setting Reference

Dynamic Library Install Name (LD_DYLIB_INSTALL_NAME)

ダイナミックライブラリのインストール名です。(雑)

File path. Specifies the install name of a dynamic library.

Build Setting Reference

$(inherited)

継承については こちらの記事 で書きました。

公式情報は今のところ見当たりませんが「 XCodeのBuild Settingsで、値が決まっていくルール - Qiita 」によれば親の設定を継承するそうです。

@executable_path

main executable( pancake )を含むディレクトリのパスです。(雑)

dyld(1) Mac OS X Manual Page の説明に当てはめて考えると /…/Release/ を指します。

@executable_path/

This variable is replaced with the path to the directory containing the main executable for the process. This is useful for loading dylibs/frameworks embedded in a .app directory. If the main executable file is at /some/path/My.app/Contents/MacOS/My and a framework dylib file is at /some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo, then the framework load path could be encoded as @executable_path/../Frameworks/Foo.framework/Versions/A/Foo and the .app directory could be moved around in the file system and dyld will still be able to load the embedded framework.

dyld(1) Mac OS X Manual Page

@loader_path

mach-o binary を含むディレクトリのパスです。バイナリによって異なります。(雑)

dyld(1) Mac OS X Manual Page の説明に当てはめて考えます。

ビルド時

👉 /.../Release を指します。

インストール時
  • /Library/Frameworks/ に PancakeKit.framework があります。
  • /Library/Frameworks/PancakeKit.framework/Versions/A/Frameworks に PancakeKit.framework が依存するフレームワークと dylib があります。
    • AエイリアスCurrent なので次のように置き換えることができます。
    • /Library/Frameworks/PancakeKit.framework/Versions/Current/Frameworks

👉 /Library/Frameworks/PancakeKit.framework を指します。

@loader_path/

This variable is replaced with the path to the directory containing the mach-o binary which contains the load command using @loader_path. Thus, in every binary, @loader_path resolves to a different path, whereas @executable_path always resolves to the same path. @loader_path is useful as the load path for a framework/dylib embedded in a plug-in, if the final file system location of the plugin-in unknown (so absolute paths cannot be used) or if the plug-in is used by multiple applications (so @executable_path cannot be used). If the plug-in mach-o file is at /some/path/Myfilter.plugin/Contents/MacOS/Myfilter and a framework dylib file is at /some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo, then the frame-work framework work load path could be encoded as @loader_path/../Frameworks/Foo.framework/Versions/A/Foo and the Myfilter.plugin directory could be moved around in the file system and dyld will still be able to load the embedded framework.

dyld(1) Mac OS X Manual Page

@rpath

実行時に決まる相対パスです。(雑)

A run-path-relative pathname uses the @rpath macro to specify a path relative to a directory to be determined at runtime.

Run-Path Dependent Libraries


@rpath/

Dyld maintains a current stack of paths called the run path list. When @rpath is encountered it is substituted with each path in the run path list until a loadable dylib if found. The run path stack is built from the LC_RPATH load commands in the depencency chain that lead to the current dylib load. You can add an LC_RPATH load command to an image with the -rpath option to ld(1). You can even add a LC_RPATH load command path that starts with @loader_path/, and it will push a path on the run path stack that relative to the image containing the LC_RPATH. The use of @rpath is most useful when you have a complex directory structure of programs and dylibs which can be installed anywhere, but keep their relative positions. This scenario could be implemented using @loader_path, but every client of a dylib could need a different load path because its relative position in the file system is different. The use of @rpath introduces a level of indirection that simplies things. You pick a location in your directory structure as an anchor point. Each dylib then gets an install path that starts with @rpath and is the path to the dylib relative to the anchor point. Each main executable is linked with -rpath @loader_path/zzz, where zzz is the path from the executable to the anchor point. At runtime dyld sets it run path to be the anchor point, then each dylib is found relative to the anchor point.

dyld(1) Mac OS X Manual Page

その他

Build Settings やライブラリ設定などに関する Apple のドキュメント、また著名なライブラリでよく利用される xcconfigs についても調べましたが別の機会でまとめます。

参考