xopen-cli でプロジェクトにあった Xcode を使う
xopen という CLI ツールを公開しました。
概要
$ cd <your repository's root directory>
$ xopen
ターミナルで普段 Xcode で開発しているアプリケーションのリポジトリのルートに移動して、xopen
を叩くだけで、それなりによしなに Xcode でプロジェクトファイルを開いてくれます。
作った背景
僕は普段仕事で iOS アプリケーションの開発をしています。プライベートでも勉強がてら iOS アプリケーションだったり、CLI ツールだったりを書いています。
特に仕事で複数のプロジェクトに関わっていると、最新の Xcode を使っているプロジェクトもあれば、2 年ほど開発環境をアップデートできていなくて古い Xcode を使っているモノもあったりします。比較的新しいプロジェクトであっても QA テストフェイズに入ると Xcode のバージョンをフリーズさせるので最新版ではなくなることもあります。プロジェクトを行ったり来たりするたびに「このプロジェクトはバージョンいくつで開くんだっけ?」と README やらを確認していて面倒でした。たまに勘違いして新しいバージョンの Xcode でコードを書いてしまってビルドが通らなくなったこともあります。
バージョンの問題と同時に面倒だったのが、Xcode で開くべき xcworkspace や xcodeproj ファイルの場所です。普段はリポジトリのルートディレクトリをスッキリさせたいのでルートにはこれらのファイルがなく、一段下のディレクトリあったりします。ghq + peco でサクッとリポジトリのルートディレクトリには移動できても、肝心の xcworkspace を開くためにさらに cd するためにタイピング。プロジェクトファイル名も当然ながらプロジェクトによってバラバラです。
ファイル名自体を把握していなくても xed を使えば簡単に開けますが、ディレクトリをトラバースはしてくれないので、明示的に対象ファイルがあるディレクトリまでは移動する必要がありました。
「適切な Xcode のバージョン」で「プロジェクトファイル名を明示しなくてもよしなに」開くことができたら良いのになぁという思いからこのツールを作りました。
内部のお話
バージョン指定
.ruby-version のようにディレクトリ内に不可視ファイルとしてバージョン番号が指定されていたら、そのバージョンで開くようにしました。
調べてみると .xcode-version
を使っている例があったのでそれにある程度合わせるようにしました。
基本的にはセマンティックバージョニングで 3 桁表記に対応しています。基本的にゼロも含めて 3 桁表記がベストですが、 13.0.0
じゃなく 13.0
と書いてもマッチングできるようにしています。
インストールされている Xcode を探す
LSCopyApplicationURLsForURL と urlsForApplications(toOpen:) を併用しています。(LSCopyApplicationURLsForURL が Deprecated になっている)
Xcode で開くファイルを見つける
BFS (幅優先探索) で以下のファイルを探しています。
- 拡張子
xcworkspace
xcodeproj
- ファイル名
Package.swift
僕の環境では Package.swift
があるリポジトリでは、このファイルがルートに置かれていて、xcXXX 系の拡張子のファイルはさらに深い位置にあることが多いです。このような構成の場合は僕は Xcode で Package.swift
を開くので今の実装でちょうどよい感じになっています。
swiftpm
バンドルにはまだ対応していません。開くときの優先順位をどうするのが無難かどうかのノウハウもないのでいったん放置しようと思います。
機能紹介
xopen
には以下のサブコマンドがあります。
open
history
open
サブコマンドのショートハンドとして、サブコマンドなしでも実行できるようにしています。内部的には defaultOpen
が設定されているのですが、ヘルプには出てこないようになっています。
open サブコマンド
無駄にオプションを増やしていった結果ちょっとややこしくなったので実際の例を交えながら説明します。
以下のようなディレクトリ構成のリポジトリのルートディレクトリにいるとします。
.
├── .git
├── .gitignore
├── .swiftpm
├── .xcode-version
├── Makefile
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
└── Tests
ここで以下のように実行します。
xopen open Package.swift
こうすると .xcode-version ファイルの中身で指定されたバージョンの Xcode で Package.swift を開きます。指定されたバージョンがインストールされていない場合は以下のようなエラーが表示されます。
Error: notInstalled("12.5.0")
Xcode you selected is not installed
.xcode-version ファイルの指定を上書きして、指定したい場合は --use
オプションで
- beta
- latest(Beta 版を除いた正式版と思われる Xcode の中で最新)
- バージョン指定(ex: 13.2.1)
のいずれかを指定することで、そちらを優先して利用します。
.xcode-version が存在しない場合は、「とにかくインストール済みの最新の Xcode で開く」か「--use-fallback オプションの指定に従う」のどちらかになります。
ややこしいのでフローチャートにしてみました。
history サブコマンド
その名の通り xopen
で開いた履歴が直近のものが先頭で列挙されます。これの何がうれしいかと言えば、peco のようなフィルタリングツールと併用すると、ターミナル上でどこにいても過去に xopen
で開いたことがあるプロジェクトを開けます。
僕は以下のような設定をしています。
## 過去に xopen-cli or xopen.app で開いたプロジェクトから開く
function peco-xopen () {
local selected_dir=$((xopen history) | peco --query "$LBUFFER")
if [ -n "$selected_dir" ]; then
BUFFER="xopen open ${selected_dir}"
zle accept-line
fi
zle clear-screen
}
zle -N peco-xopen
bindkey '^@' peco-xopen
xopen コマンド(サブコマンドなし)
このアーティクルの概要で紹介した実行方法です。僕は基本的にこれしか使ってません。1 番楽なので。
内部的には、以下のコマンドと同等の処理をしています。
xopen open --auto-discovery --use-fallback latest
このオプション指定が長いので作りました。
help
xopen --help
xopen <サブコマンド> --help
でみられるようになっています。
zsh completion
xopen --generate-completion-script zsh >~/.zsh/completion/_xopen
上記のようにすると completion が設定できます。できるはずなのですが、default subcommand を設定してからこれがちゃんと動かなくなりました。
この機能自体は apple/swift-argument-parser が提供してくれている機能です。zsh の仕様なのか、もしくはこの機能のバグを踏んでしまっているかもしれません。
他にも Bash, Fish に対応しているらしいので詳しくは公式ドキュメントを参照ください。
インストール
Homebrew で以下のようにすればインストールできるはずです。Intel と Apple Silicon に対応した Fat binary です。署名もしているので問題なく動かせられると思います。
brew install griffin-stewie/formulae/xopen-cli
xopen でやらないこと
- 指定されていた Xcode がインストールされていない場合にそのバージョンをダウンロード/インストールする
- コードが複雑になりすぎるかなと思うので対応する予定はありません。
- RobotsAndPencils/xcodes か RobotsAndPencils/XcodesApp をおすすめします。
まとめ
複数のプロジェクトでいろんな Xcode のバージョンを使っている人はぜひ使ってみてください。
Discussion