xcrun simctl launchは、シミュレータにインストール済みのアプリをbundle identifier指定で起動するサブコマンドである。
単純な起動だけでなく、起動時の引数や環境変数の指定、デバッガのアタッチ待ちなど、開発・デバッグ用途のオプションを備える。
基本的な使い方
launch <device> <app bundle identifier> [<argv 1> <argv 2> ...]の形式で実行する。
$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 3104
標準出力には<bundle identifier>: <PID>の形式で、起動したプロセスのPIDが出力される。
既にそのアプリが起動中の場合、launchは新しいプロセスを起動せず、実行中のプロセスのPIDをそのまま返す。
$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4433
$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4433
末尾の引数はそのままプロセスのargvとして渡る
<app bundle identifier>の後ろに指定した引数は、起動するプロセスのargvにそのまま渡る。
psでプロセスのコマンドラインを確認すると、指定した引数がそのまま付与されていることが分かる。
$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari --til-test-arg foo
com.apple.mobilesafari: 3602
$ ps -p 3602 -o command=
/Library/Developer/.../MobileSafari.app/MobileSafari --til-test-arg foo
SIMCTL_CHILD_接頭辞を付けた環境変数が、起動したアプリの環境変数として渡る
ヘルプに記載の通り、simctl launchを実行するシェルの環境変数にSIMCTL_CHILD_接頭辞を付けて設定すると、接頭辞を除いた名前で起動したアプリのプロセスに渡る。
ps ewwでプロセスの環境変数を確認すると、接頭辞なしの変数として存在することが分かる。
$ SIMCTL_CHILD_TIL_TEST_VAR="hello-from-til" xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 3386
$ ps eww 3386 | grep -o "TIL_TEST_VAR=[^ ]*"
TIL_TEST_VAR=hello-from-til
--terminate-running-processで実行中のプロセスを終了してから起動し直す
前述の通り、既に起動中のアプリに対してlaunchを実行しても新しいプロセスにはならないが、--terminate-running-processを付けると実行中のプロセスを終了してから改めて起動し直す。
起動し直された後のPIDは、それまで実行中だったプロセスのPIDとは異なる値になる。
$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4433
$ xcrun simctl launch --terminate-running-process "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4691
--wait-for-debuggerはデバッガがアタッチするまでプロセスを一時停止させる
--wait-for-debuggerを付けて起動すると、プロセスは生成されるがすぐには実行を開始せず、デバッガのアタッチを待つ状態になる。
psでプロセスの状態(STAT列)を確認すると、通常起動時は実行中を示すRsだが、--wait-for-debugger時は一時停止を示すTsになっていることが確認できる。
$ xcrun simctl launch --wait-for-debugger "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4308
$ ps -p 4308 -o pid,stat,command
PID STAT COMMAND
4308 Ts /Library/Developer/.../MobileSafari.app/MobileSafari
(比較: --wait-for-debuggerなしの通常起動)
$ ps -p 4433 -o pid,stat,command
PID STAT COMMAND
4433 Rs /Library/Developer/.../MobileSafari.app/MobileSafari
Xcodeを介さずlldb等を直接アタッチしてデバッグしたい場面での利用が想定される。
短縮形-wは使えない
ヘルプのUsageには-wが--wait-for-debuggerの短縮形として記載されているが、実際に-wを指定すると引数解析に失敗し、ヘルプが再表示されるだけのエラーになる。
$ xcrun simctl launch -w "TIL Launch iPhone" com.apple.mobilesafari
Launch an application by identifier on a device.
Usage: simctl launch [-w | --wait-for-debugger] ...
(以下ヘルプ全体が表示される)
長い形式の--wait-for-debuggerを使えば問題なく動作するため、デバッガのアタッチ待ちをする場合はこちらを使う。
--consoleは接続したまま標準出力・標準エラー出力を表示し続ける
--consoleを付けると、launchコマンド自体がフォアグラウンドでブロックし、アプリの標準出力・標準エラー出力をそのまま端末に表示し続ける。
$ xcrun simctl launch --console "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 5501
(Ctrl-Cで打ち切るまでブロックし続ける)
--console-ptyも同様にブロックするが、PTY経由で出力する点が異なる。
どちらも--stdout・--stderrとの併用はできない。
--stdout・--stderrに指定するパスは、ホストではなくシミュレータ内のファイルシステムを指す
--stdout=<path>・--stderr=<path>で、アプリの標準出力・標準エラー出力をファイルにリダイレクトできる。
このとき指定するパスは、実行しているMac(ホスト)のファイルシステムではなく、シミュレータ内から見たファイルシステムのパスとして解釈される。
$ xcrun simctl launch --stdout=/tmp/til-out.log --stderr=/tmp/til-err.log "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 5094
/tmp/til-out.logを指定しても、ホスト側の/tmp/til-out.logにはファイルが作成されない。
lsofでプロセスの標準出力・標準エラー出力のファイルディスクリプタを確認すると、実際にはデバイスのデータディレクトリ配下に作成されていることが分かる。
$ lsof -a -p 5094 -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
MobileSaf 5094 sue 0r CHR 3,2 0t0 336 /dev/null
MobileSaf 5094 sue 1u REG 1,15 0 ... /Users/xxx/Library/Developer/CoreSimulator/Devices/<UDID>/data/tmp/til-out.log
MobileSaf 5094 sue 2u REG 1,15 0 ... /Users/xxx/Library/Developer/CoreSimulator/Devices/<UDID>/data/tmp/til-err.log
シミュレータの/tmpは、ホスト側の~/Library/Developer/CoreSimulator/Devices/<UDID>/data/tmpに対応している。
出力先ファイルを直接確認したい場合は、指定したパスをホストのパスとして探すのではなく、対象デバイスのデータディレクトリ配下を確認する必要がある。
