ottijp blog

Homebrew完全に理解した

2020-05-23 Tags: Homebrew

macOSで開発をする人の999割が使っているであろうHomeBrewですが, HomeBrewで入れているopensslのバージョンをごちゃごちゃにしてしまったことがちょっと前にあったので,仕組みをちゃんと知ろうと思い調べてみました.

基本的にはHomeBrewの公式ページman brew, man brew-caskを読んでまとめました.

Homebrewの基本的な使い方などはここでは説明しないので,公式ページのドキュメントを見てください.

各用語の意味

Homebrewはその名の通り,自家醸造をモチーフにした用語を使っています. これらについて調べてみました.

全体の関係図

whole

formula

formulaとは「調理法,調合方法」という意味で,パッケージ定義を表します.これには,

  • ソースのURL
  • bottle(後述)
  • 依存パッケージ
  • インストールスクリプト
  • テストスクリプト

などの情報が書いてあり,実体はパッケージ定義が書いてあるrubyファイルです. 具体的には次のようなものです.(gradleの例)

$ brew cat gradle
class Gradle < Formula
  desc "Open-source build automation tool based on the Groovy and Kotlin DSL"
  homepage "https://www.gradle.org/"
  url "https://services.gradle.org/distributions/gradle-6.4.1-all.zip"
  sha256 "3fd824892df8ad5847be6e4fb7d3600068437de172939fd657cc280a1a629f63"

  bottle :unneeded

  depends_on "openjdk"

  def install
    rm_f Dir["bin/*.bat"]
    libexec.install %w[bin docs lib src]
    (bin/"gradle").write_env_script libexec/"bin/gradle",
      :JAVA_HOME => "${JAVA_HOME:-#{Formula["openjdk"].opt_prefix}}"
  end

  test do
    assert_match version.to_s, shell_output("#{bin}/gradle --version")

    (testpath/"build.gradle").write <<~EOS
      println "gradle works!"
    EOS
    gradle_output = shell_output("#{bin}/gradle build --no-daemon")
    assert_includes gradle_output, "gradle works!"
  end
end

ちなみに複数形はformulae(フォーミュリ)です.

bottle

bottleとはそのまま「ボトル」で,ビルド済みのバイナリを表します. keg(後述)と同じファイル構成をtar.gzにしたもので,環境に合うbottleがformulaに含まれていれば,ビルドせずにインストール可能です. (合うものがなければ,formulaの定義に従いビルドされてインストールされます.)

bottleを作るときは,一旦開発環境でbottle用にビルドしてから作成します.

$ brew install --build-bottle <formula> && brew bottle <formula>

これを行うと,bottleが作られると同時にformula定義ファイル用のDSLも生成してくれます.

keg

kegとは「小樽」という意味で,インストールされたformulaもしくはbottleを表します. 実行可能ファイルやライブラリの実体がここに置かれ,/usr/local/bin などからシンボリックリンクが貼られます.

# wgetの実体とシンボリックリンクの例
$ ls -l $(which wget)
lrwxr-xr-x  1 otti  admin  30 Nov 14  2018 /usr/local/bin/wget@ -> ../Cellar/wget/1.19.5/bin/wget

この場合 /usr/local/Cellar/wget/1.19.5 がkegです.

cellar

cellarとは「貯蔵室」という意味(ワインセラーとかのセラー)で,kegが置かれる場所を表します.

# Cellarの場所
$ brew --cellar
/usr/local/Cellar

# kegのリスト
$ find $(brew --cellar) -depth 2 | head -5
/usr/local/Cellar/pstoedit/3.74
/usr/local/Cellar/rubberband/1.8.2_1
/usr/local/Cellar/avrdude/6.3_1
/usr/local/Cellar/mosquitto/1.5.4
/usr/local/Cellar/libpsl/0.20.2

tap

tapとは「蛇口」という意味で,formulaを提供するリポジトリへの接続口を表します.

brew tap https://github.com/user/homebrew-repoにtapする場合は,brew tap user/repoと省略形で書けます. gitが解釈できるURLなら,フルのURLを指定することで他の場所からもtapできます.

Homebrew/coreはデフォルトでtapされているので,公式リポジトリに取り込まれているパッケージは,Homebrewをインストールした時点でtapすることなくインストールできます. Homebrew/coreへ自分のformulaを取り込んでほしい場合は,PRを送って取り込んでもらう必要がありますが,PRを作ったりするためのコマンドもbrewに用意されているので,brewのエコシステムの中だけでできるようになってます. ただし,取り込んでもらうためには,Acceptable Formulaeを満たす必要があります.

cask

caskとは「大樽」という意味で,バイナリのみ提供されるパッケージを意味します.例えば次のようなものです.

  • macOSアプリケーション
  • フォント
  • プラグイン
  • その他のOSSではないソフトウェア

caskはHomebrew(自家醸造)できないもので,Homebrewとしては推奨しておらず,拡張扱いです. Homebrew/coreにもマージしてもらえません.

コマンドも拡張形式(brew cask hoge)ですし, インストール先はcellarとは異なる場所(/usr/local/CaskRoom)になります.

Homebrewのパッケージ情報のupdate

Homebrewの本体はここに入っています.

$ brew --repository
/usr/local/Homebrew

これはHomebrewのGitHubリポジトリをcloneしたもので,brew updateした時にpullされてパッケージ情報などが更新されます.

$ cd $(brew --repository) && git remote get-url origin
https://github.com/Homebrew/brew

では,パッケージをインストールする前に毎回brew updateをしないといけないかというと,その必要はありません. 以下のHomebrewの設定によって,インストール時に自動でupdateしてくれます.(デフォルトでは,前回のupdateから300秒経っていたら自動でやってくれます.)

  • HOMEBREWAUTOUPDATE_SECS (Default: 300)
  • HOMEBREWNOAUTO_UPDATE (Default: 0)

逆に勝手にやってほしくない場合は,これらの設定を変えることで制御できます.

cf. Is it possible to `brew install` without updating? · Issue #1670 · Homebrew/brew

また,brew upgradeをすると,全部のパッケージがupgradeされてしまいますが, upgradeさせたくないパッケージがある場合は,次のコマンドで固定することができます.

$ brew pin <formula>
$ brew unpin <formula>

ottijp
Satoshi SAKAO (@ottijp)

都内でアプリケーションエンジニアをしています

...