29
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

YUMEMI Advent CalendarAdvent Calendar 2021

Day 16

M1 環境で Homebrew 導入コマンドを Xcode から叩けない場合の対処法

Last updated at Posted at 2021-12-15

この記事は YUMEMI Advent Calendar 2021 16 日目の記事です。

背景

M1 (Apple Silicon) 環境の Homebrew については、デフォルトのインストール先が /opt/homebrew/bin (+ /opt/homebrew/sbin) へと変更されました。

この設定で Homebrew で導入したコマンドを Xcode の Build Phases 中で叩くと、 command not found のエラーが発生してしまいます。

Xcode のデフォルトの設定のままだと Build Phases 内部の Run Script において PATH に /opt/homebrew/bin が通っていない状態となってしまっているのが原因なので、これを何とか解消しようというのが今回の記事となります。

対象 画像
Build Phases print_path.png
ログ print_path_result.png

対処法

その1. Run Script 内部で PATH を更新

Run Script での呼び出し直前に PATH を明示的に更新してしまうというシンプルな対応です。

Run Script
if [${PATH を更新する場合の条件}]; then
  # PATH の更新
fi

# 本来の Run Script 処理

ググってみると、おそらくこの対処法が一番ポピュラーなのですが、今回はこれを汎用的に利用できるように対応していきます。

まず、プロジェクトルートに .envrc ファイル (.envrc なのは direnv 等での間接利用狙い) を用意し、 .gitignore にこれを追加。

.envrc には、各開発者それぞれが必要な環境変数を記述してもらうようフローを構築します。

.envrc
# for M1 mac
export PATH=$PATH:/opt/homebrew/bin:/opt/homebrew/sbin
# other env vars
export OTHER_ENV=e.g.credential_information

次に、 Build Phases 内の各 Run Script それぞれに次の処理を追加してきます。

Run Script
if [ -f "$SRCROOT/.envrc" ]; then
  source "$SRCROOT/.envrc"
fi

# 本来の Run Script 処理

これで、それぞれの Run Script の実行前に PATH (+ その他の環境変数) を設定、上書きしていまいます。

Pros

  • プロジェクト外の環境を汚さない
  • PATH 以外の環境変数にも対応可能
    • クレデンシャルな値を環境変数を利用して外部から注入している場合等

Cons

  • プロジェクトの修正作業が発生する
    • 本来はプロジェクト側の問題とは言い難いため、筋の悪さを感じる
  • Run Script 毎に対応する必要があり、記述が冗長 + 煩雑 になる

その2. UseSanitizedBuildSystemEnvironment を無効化

試してみると分かるのですが、不思議なことにシェルから

Xcode をシェルから起動
$ open XcodeProject.xcodeproj

のように Xcode を起動してみても、本記事冒頭のスクリーンショット画像のように Build Phases 内の Run Script では、 PATH を引き継いでくれないように見えます。

実はこれ、引き継いでくれないのではなく、 Xcode 側の隠し設定 (UseSanitizedBuildSystemEnvironment) によって PATH で設定されている値がフィルタリングされた結果となっています。 (たぶん)

該当の設定は 以下の default コマンドで無効化できるため、このコマンドを実行することによって、シェルから Xcode を起動した場合においては、問題が解消できます。

$ defaults write com.apple.dt.Xcode UseSanitizedBuildSystemEnvironment -bool NO

Pros

  • プロジェクト側の修正は不要
  • 設定自体が defaults コマンドを実行するだけで簡単

Cons

  • Finder や Spotlight 等から Xcode を起動された場合に、 PATH 設定が効かない
    • 開発時には、 open して Xcode を起動するよう注意喚起が必要な可能性
    • ケアする場合は、こういった対応が必要
  • Xcode のデフォルト設定からの変更
    • Sanitize を意図しているので、セキュリティ的な懸念が前提にありそう
      • 想定されている expolit フローは不明だが、変な環境変数を追加されることを恐れている?
      • とはいえ、 CUI 上での xcodebuild 等では、シェル上の環境変数が利用される

その3. プロジェクトローカルに該当コマンドを用意

そもそも、プロジェクトのビルドに必要なものを外部環境のコマンドに依存しているのがおかしいということで、プロジェクト内でコマンドを管理してしまう方法です。

のように、コマンドの実体を用意してしまう方法もあるのですが、今回はシンボリックリンクを利用します。

まずはプロジェクトルートに、 bin/ ディレクトリを用意し、この中に利用するコマンドへのシンボリックリンクを用意します。

作業イメージ
$ cd ${プロジェクトルート}
$ mkdir bin && cd bin
$ ln -s /opt/homebrew/bin/${利用コマンド} ${利用コマンド名}

ディレクトリ構成と .gitignore は以下のような形となる想定です。

ディレクトリ構成
bin
├── .gitkeep
└── ${利用コマンド名} -> /opt/homebrew/bin/${利用コマンド}
.gitignore
/bin/*        # シンボリックリンクの設定は開発者環境毎に異なるため、 Git 管理から外す
!.gitkeep

プロジェクトローカルに利用コマンドを用意した後、 Run Script 側を修正して、プロジェクトローカルに配置したコマンド (${SRCROOT}/bin/${利用コマンド名}) を呼び出します。

Pros

  • プロジェクト外の環境を汚さない
  • ビルドで利用するコマンドの依存性が整理される

Cons

  • プロジェクトの修正作業が発生する
  • 対応手順がやや複雑
    • CI / CD 環境に対する対応も発生
    • 開発者がシンボリックリンクの設定で戸惑う可能性がある (特に iOS 開発エンジニアだと)

まとめ

上記の 3 つの対応どれでも対処自体は可能なはずなのですが、オススメとしては

その1. Run Script 内部で PATH を更新

の対応です。

PATH 以外の環境変数にも対応可能、かつ設定自体が明示的なので、いざというときのトラブルシュートも比較的容易な認識です。

特に、チーム開発の場合には開発者間の環境設定差異等もあるので、これを .envrc の記述 1 箇所で吸収してくれるというのは大きなメリットだと思っています。

29
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?