🖥️
グローバル環境を可能な限り汚染しないXcodeの開発環境構築
追記
解決したい課題
- チーム開発時に各メンバーの環境にインストールしてあるツールのバージョンの差異が気になる
- Podfile.lockでversionの差分が出るのがすごく気になる。気にしない人はそのままcommitするし…
- READMEに環境構築の手順を書く手間を減らしたい
- 環境構築に関する問い合わせも減らしたい
結論
- ローカルのグローバルなところにインストールするツールはHomebrewとbundlerのみ
- Swift系のツールはMint経由でインストール、Ruby系のツールはbundlerでプロジェクト直下にインストール
- 各ツールの依存関係は以下になる
Homebrew
└─ Mint
├─ Carthage
├─ xcodegen
├─ swiftgen
└─ swiftlint
bundler
└─ CocoaPods
- 環境構築するためのMakefileを用意する
- サンプルはyusuga/xcode-setup
解決方法
ツールのインストール
Homebrew
brew "mint"
-
brew bundle --no-upgrade --no-lock
でBrewfileに記載のツールをインストール-
--no-lock
をつけるとBrewfile.lock.json
が生成されなくなる - lockファイルの有無は迷ったが、brewやXcodeのバージョンの差分も出てしまうのが嫌なので生成しないようにした
-
- これで各ツールの依存関係は以下になる
Homebrew
└─ Mint
Mint
carthage/carthage@0.35.0
yonaskolb/xcodegen@2.17.0
swiftgen/swiftgen@6.3.0
realm/swiftlint@0.40.0
- Mintはlockファイルはなく、Mintfileに直接バージョンを記述する形式
- Mintは異なるバージョンのツールをインストールできる
-
mint bootstrap
でlinkなしでインストール-
mint install
でもいいと思いますが、linkしたくないのでbootstrapにした
-
- これで各ツールの依存関係は以下になる
Homebrew
└─ Mint
├─ Carthage
├─ xcodegen
├─ swiftgen
└─ swiftlint
- mintでインストールしたツールは
mint run carthage bootstrap
のように使用することになる- XcodeプロジェクトのBuild Phasesで実行するCarthageのスクリプトも
mint run carthage copy-frameworks
になることに注意
- XcodeプロジェクトのBuild Phasesで実行するCarthageのスクリプトも
bundler
-
bundlerをインストール
- バージョン指定でインストールしたい場合は
sudo gem install bundler:2.0.2
な感じ
- バージョン指定でインストールしたい場合は
- bundler経由でインストールしたいツールを記述したGemfileを用意
source 'https://rubygems.org'
gem 'cocoapods'
-
bundle install --path vendor/bundle
または .bundle/configを用意してbundle install
し、プロジェクトのディレクトリ直下にツールをインストール - これでツールの依存関係は以下になる
Homebrew
└─ Mint
├─ Carthage
├─ xcodegen
├─ swiftgen
└─ swiftlint
bundler
└─ CocoaPods
- bundleでインストールしたツールは
bundle exec pod install
のように使用することになる
環境構築方法
- 各種環境構築を記述したMakefileを用意
- makeの本来の用途であるファイルを作るというよりかは、単純なタスクランナーとして使う
APP_NAME := App
DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
MAKEFILE_PATH := ${DIR}/Makefile
MINTFILE_PATH := ${DIR}/Mintfile
GEMFILE_PATH := ${DIR}/../Gemfile
make := make -f ${MAKEFILE_PATH}
mint-run := mint run -m ${MINTFILE_PATH}
USES_CACHE := false
.PHONY: setup setup-uses-cache
.PHONY: setup-tools setup-carthage setup-cocoapods
.PHONY: generate-asset-files generate-xcodeproj open
.PHONY: clean
default: setup
setup:
${make} setup-tools
${make} generate-asset-files
${make} setup-carthage
${make} generate-xcodeproj
${make} open
setup-uses-cache:
${make} setup USES_CACHE=true
setup-tools:
brew bundle --no-upgrade --no-lock --file ${DIR}/Brewfile
mint bootstrap -m ${MINTFILE_PATH}
bundle install --gemfile ${GEMFILE_PATH}
setup-carthage:
ifeq ($(USES_CACHE),true)
${mint-run} carthage bootstrap --platform iOS --no-use-binaries --cache-builds
else
${mint-run} carthage bootstrap --platform iOS --no-use-binaries
endif
setup-cocoapods:
bundle exec --gemfile ${GEMFILE_PATH} \
pod install --project-directory=${DIR}
generate-asset-files:
${mint-run} swiftgen config run --config ${DIR}/swiftgen.yml
generate-xcodeproj:
${mint-run} xcodegen -s ${DIR}/project.yml
${make} setup-cocoapods # Xcodeプロジェクトが生成された後に実行する必要がある
open:
open ${DIR}/${APP_NAME}.xcworkspace
clean:
rm -rf ${DIR}/../vendor
rm -rf ${DIR}/${APP_NAME}.xcodeproj
rm -rf ${DIR}/${APP_NAME}.xcworkspace
rm -rf ${DIR}/Carthage
rm -rf ${DIR}/Pods
-
make
を実行すれば各種ツールのインストールからXcodeの環境構築まですべて終わる- CarthageやCocoaPodsのライブラリ更新はMakefileではサポートしないで、わかる人が適切にCartfile.resolvedやPodfile.lockを更新する形にしている
- サンプルはyusuga/xcode-setup
Makefileについて
- もうちょっとシンプルに書くのは可能ですが、makeはCIから呼んだり別のMakefileからも呼ぶこともあるので、都度パスの指定を入れている
- GemfileがAppディレクトリの一つ上の階層にありますが、CIを使うときは以下のディレクトリ構成でやってて、CIでもGemfileを使うのでこういう構成にしている
.
├── App
│ ├── App
│ ├── App.xcodeproj
│ ├── App.xcworkspace
│ ├── Brewfile
│ ├── Cartfile
│ ├── Cartfile.private
│ ├── Cartfile.resolved
│ ├── Carthage
│ ├── Makefile
│ ├── Mintfile
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ ├── project.yml
│ └── swiftgen.yml
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── fastlane
│ ├── Appfile
│ ├── Fastfile
│ ├── Matchfile
│ └── README.md
└── vendor
└── bundle
- Makefileは賛否両論みたいだけど(参考: また make の話してる(2020年9月14日))、個人的には複雑なことをせず用法容量をちゃんと守ったMakefile + 複雑なことは別途スクリプトに書いてそれをMakefileから呼び出す、という形に落ちついた
- CarthageのMakefileくらいな感じ
今後解決したい課題
-
brew bundle --no-upgrade
やmint bootstrap
で、2回目以降の処理時間をもっと減らしたい - Homebrew、Mint、bundlerもローカルにインストールしたくない
- ローカルにインストールしているとHomebrewやMintのバージョン違いで問題が起きるケースをカバーできない(ほぼなさそうだけど)
- Dockerで解決できるかな?
Discussion