Carthageの作り方を紐解いてみる~Makefile編~

SwiftLinuxで動かせるようになりはや数ヶ月、そろそろSwift製のツールを自分で作って使って行きたいなぁと思っています。

そこでSwift製のプロダクトで一番使われているんじゃないか?と思われるCarthageの作り方について調べてみました。

github.com

Makefileを見る

なにはともあれまずはMakefileを読んでみます。

とりあえず $ make install すると使えるようになりそうな雰囲気を感じるので一旦インストールしてみます。

$ make install
rm -f "Carthage.pkg"
rm -f "CarthageKit.framework.zip"
rm -rf "/tmp/Carthage.dst"
xcodebuild -workspace 'Carthage.xcworkspace' -scheme 'carthage' DSTROOT=/tmp/Carthage.dst clean
Build settings from command line:
    DSTROOT = /tmp/Carthage.dst
〜(中略)〜
Sudo installer -pkg Carthage.pkg -target /
Password:
installer: Package name is Carthage
installer: Installing at base path /
installer: The install was successful.

xcodebuildpkgbuildが走り、生成物であるCarthage.pkgをインストールしました。

$ carthage
Available commands:

   archive           Archives built frameworks into a zip that Carthage can use
   bootstrap         Check out and build the project's dependencies
   build             Build the project's dependencies
   checkout          Check out the project's dependencies
   copy-frameworks   In a Run Script build phase, copies each framework specified by a SCRIPT_INPUT_FILE environment variable into the built app bundle
   fetch             Clones or fetches a Git repository ahead of time
   help              Display general or command-specific help
   update            Update and rebuild the project's dependencies
   version           Display the current version of Carthage

正しくインストールされていますね。

中で何が起きているか追うためにMakefileの依存関係を整理してみます。

install

install: package
    sudo installer -pkg Carthage.pkg -target /

installpackageに依存していて、最終的にinstallerを走らせてCarthage.pkgをインストールするためのターゲットのようです。

package

package: installables
    pkgbuild \
        --component-plist "$(COMPONENTS_PLIST)" \
        --identifier "org.carthage.carthage" \
        --install-location "/" \
        --root "$(TEMPORARY_FOLDER)" \
        --version "$(VERSION_STRING)" \
        "$(OUTPUT_PACKAGE)"

    (cd "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)" && zip -q -r --symlinks - "$(OUTPUT_FRAMEWORK)") > "$(OUTPUT_FRAMEWORK_ZIP)"

packageinstallablesに依存していて、pkgbuildCarthage.pkgを生成するためのターゲットのようです。

引数のTEMPORARY_FOLDERにはinstallablesでビルドされた生成物が配置されてそうです。

ちなみに最終行のCarthageKit.framework.zipは何目的で作っているのかよくわかりませんでした。

intallables

installables: clean bootstrap
    $(BUILD_TOOL) $(XCODEFLAGS) install

    mkdir -p "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)"
    mv -f "$(CARTHAGEKIT_BUNDLE)" "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)/$(OUTPUT_FRAMEWORK)"
    mv -f "$(CARTHAGE_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)/carthage"
    rm -rf "$(BUILT_BUNDLE)"

installablescleanbootstrapに依存していて、pkgbuildで利用するコマンドやフレームワークを生成し、必要な箇所に配置するためのターゲットのようです。

実際に叩かれてるコマンドは xcodebuild -workspace 'Carthage.xcworkspace' -scheme 'carthage' DSTROOT=/tmp/Carthage.dst install で、xcodebuildinstallは指定されたDSTROOTに対してビルドしたターゲットをインストールするアクションです。

今回で言うと /tmp/Carthage.dstCarthageのビルド結果であるCarthageKit.frameworkと、carthageのバイナリ本体を配置しています。

clean & bootstrap

bootstrap:
    script/bootstrap

clean:
    rm -f "$(OUTPUT_PACKAGE)"
    rm -f "$(OUTPUT_FRAMEWORK_ZIP)"
    rm -rf "$(TEMPORARY_FOLDER)"
    $(BUILD_TOOL) $(XCODEFLAGS) clean

cleanは過去の生成物の削除、bootstrapはルート以下のscript/bootstrapを叩いており、どちらも他のターゲットには依存していませんでした。 script/bootstrapは、Carthageプロジェクトが利用している外部ライブラリをgitsubmoduleで管理するためのスクリプトでした。

まとめ

フローを整理します。

  1. 過去の生成物を削除する
  2. Carthageのビルドに必要なライブラリを取得/更新する
  3. xcodebuildを利用してCarthageをビルドし、CarthageKit.frameworkcarthageのバイナリを生成し特定の箇所に配置する
  4. 特定の箇所に配置された生成物をCarthage.pkgにパッケージ化する
  5. Carthage.pkgをインストールする

以上のフローで、Carthageが手元で利用できるようになりました。 次回はCarthageをビルドする周りを詳しく見てみようと思います。