Homebrewで自作のプロジェクト（ライブラリ）を公開する


## はじめに

Homebrewは自作のライブラリを公開することが割と楽な機能を提供してくれている。しかしながら、その作業全体についてちゃんと書いてくれているブログが少ないので、備忘録として自分の作業を晒しておく。なお、自分のgithubアカウントくらいは持っている人向けの内容になっているので、各自補完してほしい。

## Tapの作成

Homebrewの本番リポジトリに入れるのは嫌なので、ライブラリ用のtapの作成から入る。まず、githubでhomebrew-\<XXX>といった名前の公開リポジトリを用意する。このリポジトリに各種インストールスクリプトを作っていく。空のリポジトリの作成が終わったら、

```
$ brew tap hogehoge/homebrew-<XXX>
```

として、空のリポジトリのcloneをローカル環境に作成する。このとき、”hogehoge”は、githubのアカウント名、homebrew-\<XXX>が公開リポジトリ名になる。同じ名前のリポジトリがあったとしても、ユーザ名が変わるので別に構わない。何も言わずに普通にgithubからcloneしてきてくれるのが嬉しい。リポジトリの展開したロケーションは

```
$ cd `brew prefix`/Library/Taps/hogehoge/homebrew-<XXX>
```

である。

## インストールスクリプト（Formula）の作成

続いて、Formulaの作成に移る。とはいえ、こちらは、brewのコマンドで用意されているので、実行するだけ、

```
$ brew create http://hogehoge.com/path/to/libhoge.tar.gz
```

上記のように、プロジェクトのtarballのロケーションを指定して実行すると、Formulaのテンプレートをデフォルトエディタで開いてくれる。

```libhoge.rb
require "formula"

# Documentation: https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Formula-Cookbook.md
#                /usr/local/Library/Contributions/example-formula.rb
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!

class Libhoge < Formula
  homepage ""
  url "http://hogehoge.com/path/to/libhoge.tar.gz"
  sha1 ""

  # depends_on "cmake" => :build
  depends_on :x11 # if your formula requires any X11/XQuartz components

  def install
    # ENV.deparallelize  # if your formula fails when building in parallel

    # Remove unrecognized options if warned by configure
    system "./configure", "--disable-debug",
                          "--disable-dependency-tracking",
                          "--disable-silent-rules",
                          "--prefix=#{prefix}"
    # system "cmake", ".", *std_cmake_args
    system "make", "install" # if this fails, try separate make/make install steps
  end

  test do
    # `test do` will create, run in and delete a temporary directory.
    #
    # This test will fail and we won't accept that! It's enough to just replace
    # "false" with the main program this formula installs, but it'd be nice if you
    # were more thorough. Run the test with `brew test libhoge`. Options passed
    # to `brew install` such as `--HEAD` also need to be provided to `brew test`.
    #
    # The installed folder is not in the path, so use the entire path to any
    # executables being tested: `system "#{bin}/program", "do", "something"`.
    system "false"
  end
end

```

このスクリプトを編集して、tapのディレクトリに保存する。なお、デフォルトでは、brewのメインリポジトリに保存されるため、必ず、スクリプトをTapのリポジトリに移動させよう。同時にsha1は忘れずに書き込もう。OS Xでsha1を取るなら、

```
$ openssl sha1 libhoge.tar.gz
```

を実行すると取得できる。

最後に、githubのリポジトリにこの内容をpushする。

```
$ cd `brew prefix`/Library/Taps/hogehoge/homebrew-<XXX>
$ git add —all
$ git commit -a -m "initial import"
$ git push
```

後は、

```
$ brew untap hogehoge/homebrew-<XXX>
$ brew tap hogehoge/homebrew-<XXX>
$ brew install libhoge
```

などとして動作確認すれば良い。

ここまでで基本は終了。これからは、あまり他のブログではまとまっていない部分になる。

## バイナリインストール

homebrewの利点は、すべてのソースをビルドしないで、バイナリがあるものはバイナリでインストールしてくれることなのだが、それを自分のカスタムリポジトリでやろうとするとどう設定したら良いのかわからなくなる。

brewのバイナリインストールはBottleという概念で行われ、スクリプト中に以下の関数を実装することで行う。

```libhoge.rb
  bottle do
    root_url "https://hogehoge.com/dirpath/to“ # Optional root to calculate bottle URLs
    sha1 "" => :yosemite
  end

  def pour_bottle?
    # Only needed if this formula has to check if using the pre-built
    # bottle is fine.
    true
  end
```

ここでroot_urlはバイナリが置いてあるURLを、sha1はそのバイナリのハッシュコードを表す。
バイナリは、

```
<package_name>-<version>.<osx name>.bottle.tar.gz
```

という命名規則で用意する必要がある。例えば、package_nameがlibhoge, versionが0.0.1、osx name（対応OS）がyosemiteのバイナリの場合、そのパッケージ名は

```
libhoge-0.0.1.yosemite.bottle.tar.gz
```

となる。ではそのコンテンツはどうなっているのかというと、以下のようなディレクトリ構成になっている。

```
libhoge
└── 0.0.1
    ├── INSTALL_RECEIPT.json
    ├── LICENSE
    ├── include
    │   └── libhoge.h
    └── lib
        ├── libhoge.dylib
        └── pkgconfig
            └── libhoge.pc
```

0.0.1の下をprefixとして見れば、わかりやすいだろう。configure時、もしくは、cmakeの実行時にprefixとして/path/to/libhoge/0.0.1を指定してインストールすることで、基本的な構造は作れる。

大事なのは、INSTALL_RECEIPT.jsonで、これがないとbottleバイナリの検証が失敗してしまうので、必ず用意しよう。中身は定形なので、それほどガンバルことはない。

```INSTALL_RECEIPT.json

    "HEAD": "", 
    "built_as_bottle": true, 
    "compiler": "gcc", 
    "poured_from_bottle": true, 
    "stdlib": null, 
    "tapped_from": “<tap名>”, 
    "time": 0, 
    "unused_options": [], 
    "used_options": []
}
```

基本的には、tapped_fromに自身のtap名を書いておけば問題ないはずである。

以上を用意すると、brew installをするときにまずこのbottleバイナリを確認に行き、その検証が失敗する、ダウンロード出来ない場合にソースビルドに移行するという手順になる。

## 依存関係の定義

ライブラリが依存しているパッケージを指定する。

```libhoge.rb
class Libhoge < Formula
  homepage ""
  url "http://hogehoge.com/path/to/libhoge.tar.gz"
  sha1 ""

  depends_on "cmake"
```

上記のようにdepends_onでソフト名を指定すれば良い。より細かくオプショナルなどの指定もできる。
cmakeでビルドしたい場合は、depends_onにcmakeを指定しよう。

## パッチを当てる

改造したライブラリをインストールできるようにしておきたいといった場合、オリジナルのライブラリソースコードにパッチを当ててインストールするといった手順が必要になる。

```libhoge.rb
class Libhoge < Formula
  homepage ""
  url "http://hogehoge.com/path/to/libhoge.tar.gz"
  sha1 ""

  depends_on "cmake"

  patch :p0 do
    url 'https://raw.githubusercontent.com/hogehoge/homebrew-<XXX>/master/libhoge.patch'
    sha1 ‘XXXXXXXXXXXXXXXXXXXXXXXXXXX’
  end

  def install

```

パッチを当てるにはpatchコマンドを使用する。p0に当てたいといった場合は、:p0オプションを指定する。上記は、同じgithubのリポジトリにlibhoge.patchを置く場合の例である。

## 環境変数

homebrewでは、superenvといって、環境への依存性を低くするために環境を独自に構成して、ビルドを行う。これは便利な機能だが、PKG_CONFIG_PATHのような大事な環境変数まで失われてしまうため、必要な変数はインストール関数の中に組み込むと良い。

```libhoge.rb
  def install
    ENV['PKG_CONFIG_PATH'] = '/usr/local/lib/pkgconfig'
    system "cmake", "."
    system "make", "install"
  end
```

なお、自分の環境でビルドしたい場合、--env=stdオプションでビルドすると良い。

```
$ brew install libhoge —env=std
```

## デバッグ

インストールスクリプトが何の工夫もなく動けばここで終了となるが、特殊な設定をしてビルドしたり、複数のパッチを当てる、環境変数の関係が複雑といった場合には、一度のコンパイルで通ることはない。ここでは、そんな場合のデバッグ方法について簡単に触れる。一言でいうとデバッグオプション-vdを指定すると良い。

```
$ brew install libhoge -vd
```

brewは、コンパイルが具体的にどの部分で落ちたのか、トレースバックをする機能を提供してくれる。また、tmpで展開されているビルド途上のディレクトリの状態を、shellで確認するもできる。

以下の選択肢で5を選べば、シェルを実行して、/tmp/libhoge-XXXX下の展開中のビルドディレクトリにアクセスできる。

```
BuildError: Failed executing: cmake .
1. raise
2. ignore
3. backtrace
4. irb
5. shell
Choose an action: 
```

デバッグが終われば、bashスクリプトを終了(exit)し、上記の選択肢から、1を選べば良い。終了処理が開始され、/tmpに展開されたディレクトリもその時点で削除される。

## 参考資料

* https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Formula-Cookbook.md#formula-cookbook
* https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Bottles.md
* https://github.com/Homebrew/homebrew/blob/master/Library/Contributions/example-formula.rb