🔨

xopen-cli でプロジェクトにあった Xcode を使う

2021/12/28に公開

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 を探す

LSCopyApplicationURLsForURLurlsForApplications(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 がインストールされていない場合にそのバージョンをダウンロード/インストールする

まとめ

複数のプロジェクトでいろんな Xcode のバージョンを使っている人はぜひ使ってみてください。

Discussion