ottijp blog

Homebrewでformulaの作成

2020-05-23 Tags: Homebrew

前回の記事でHomebrewを完全に理解したので,formulaを作ってみました.

対象

前にPDFの綴じ方向を変換するツールを作ったので,これをHomebrewのパッケージにしてみました.

cf. ottijp/pdf-r2l: PDFを右綴じにするツール

Homebrew/core にPR送るのはちょっとハードルがあったのと, tapでもbrew install ottijp/tap/<formula>という感じで Homebrew/core のパッケージとほぼ同じようにインストールできるので, 自分のリポジトリにtapを作りました.

作り方

まず,自分のリポジトリにtapを作ります. ottijp/tapというtapは,https://github.com/ottijp/homebrew-tap に配置することになります.

tapはformula単位ではなく,GitHubのユーザ単位で作るのがよいです. (tapリポジトリの下に複数のformulaを配置します.)

# Tapの作成
$ brew tap-new ottijp/tap
==> Created ottijp/tap
/usr/local/Homebrew/Library/Taps/ottijp/homebrew-tap

次に新しくformulaを作ります. ソースコードのURLを指定して作るので,GitHubにリリースを切り,tar.gzのURLを指定しました.

# Formulaの作成
$ brew create --tap ottijp/tap --set-name pdf-r2l https://github.com/ottijp/pdf-r2l/archive/v1.0.0.tar.gz
==> Downloading https://github.com/ottijp/pdf-r2l/archive/v1.0.0.tar.gz
==> Downloading from https://codeload.github.com/ottijp/pdf-r2l/tar.gz/v1.0.0
######################################################################## 100.0%
Warning: Cannot verify integrity of 270c2c05fa8241fb5dd498df6b762dbcbde47fa9fff62e7e0e0dc9e650872ff0--pdf-r2l-1.0.0.tar.gz
A checksum was not provided for this resource.
For your reference the SHA-256 is: f6232237a9cdfa743e9fe947c9e1b361a24210d402bad407dc30e17b033f4c1e
Please run `brew audit --new-formula pdf-r2l` before submitting, thanks.
Editing /usr/local/Homebrew/Library/Taps/ottijp/homebrew-tap/Formula/pdf-r2l.rb

ここでエディタが開くので,formulaを編集します. もし閉じてしまった場合はbrew edit ottijp/tap/pdf-r2lで開けます.

/usr/local/Homebrew/Library/Taps/ottijp/homebrew-tap/Formula/pdf-r2l.rb
class PdfR2l < Formula
  desc "Change PDF reading direction to Right-to-Left (R2L)"
  homepage "https://github.com/ottijp/pdf-r2l"
  url "https://github.com/ottijp/pdf-r2l/archive/v1.0.2.tar.gz"
  sha256 "421c44dae937bb010e5268ffc061fee3841ec1c668a208c06dd73dcfa9b62ab7"

  depends_on "gradle" => :build

  def install
    system "gradle", "-q", "installDist"

    bin.install "build/install/pdf-r2l/bin/pdf-r2l"
    libexec.install Dir["build/install/pdf-r2l/lib/*"]
    system "sed", "-i", "", "s/\\$APP_HOME\\/lib\\//\\$APP_HOME\\/libexec\\//g", "#{bin}/pdf-r2l"
  end

  test do
    system "false"
  end
end

元はdockerの gradle:6.4-jdk11 でビルドしているのですが, Homebrewでのビルドは,dockerの環境がないユーザのほうが多いと思うので, Homebrewのgradleを依存パッケージとして指定しました.

これでビルドしてインストールできるformulaは完成なのですが,一応bottleも作ってみました. bottleは--build-bottleを付けてインストールし,その後brew bottleでbottleを作ります.

# bottle用にビルド
$ brew install --build-bottle ottijp/tap/pdf-r2l
==> Installing pdf-r2l from ottijp/tap
==> Downloading https://github.com/ottijp/pdf-r2l/archive/v1.0.1.tar.gz
Already downloaded: /Users/otti/Library/Caches/Homebrew/downloads/fd61b4a7c052d24e9b1af32852c8a15343182be3b93c4b126f28bcd522a91919--pdf-r2l-1.0.1.tar.gz
==> gradle -q installDist
==> Not running post_install as we're building a bottle
You can run it manually using `brew postinstall ottijp/tap/pdf-r2l`
🍺  /usr/local/Cellar/pdf-r2l/1.0.1: 22 files, 12.3MB, built in 14 seconds

# bottleの作成
$ brew bottle --root-url https://github.com/ottijp/pdf-r2l/releases/download/v1.0.1 ottijp/tap/pdf-r2l
==> Determining ottijp/tap/pdf-r2l bottle rebuild...
==> Bottling pdf-r2l--1.0.1.catalina.bottle.tar.gz...
==> Detecting if pdf-r2l--1.0.1.catalina.bottle.tar.gz is relocatable...
./pdf-r2l--1.0.1.catalina.bottle.tar.gz
  bottle do
    root_url "https://github.com/ottijp/pdf-r2l/releases/download/v1.0.1"
    cellar :any_skip_relocation
    sha256 "763118b39d3822b3ee3df8e9709332849d29499a120b9be1ad81f8594e6d08d7" => :catalina
  end

ここで最後に出力されたDSLをformulaの定義ファイルに追加します.

ただ,pdf-r2l—1.0.1.catalina.bottle.tar.gz というファイル名でbottleが作成されたのですが, brew install ottijp/tap/pdf-r2lとしたとき,なぜか pdf-r2l-1.0.1.catalina.bottle.tar.gz を ダウンロードしようとしてしまうので,うまくいきませんでした.)

そこで,ファイル名を pdf-r2l-1.0.1.catalina.bottle.tar.gz に変えて, GitHubのリリースに添付ファイルとして追加しました.

$ mv pdf-r2l--1.0.1.catalina.bottle.tar.gz pdf-r2l-1.0.1.catalina.bottle.tar.gz

あとはformulaの検証をします. いくつか問題が報告されたので,言われたとおりに修正しました.

$ brew audit --new-formula ottijp/tap/pdf-r2l

さいごに,formulaをGitHubにコミットすれば完了です.

インストールと実行

次のコマンドでパッケージのインストールができます. (bottleからインストールされた例.)

# インストール
$ brew install ottijp/tap/pdf-r2l
==> Installing pdf-r2l from ottijp/tap
==> Downloading https://github.com/ottijp/pdf-r2l/releases/download/v1.0.1/pdf-r2l-1.0.1.catalina.bottle.tar.gz
Already downloaded: /Users/otti/Library/Caches/Homebrew/downloads/1f482cbb0f254e403f9ee41b83118c522be0617ba46f6aa339e86603a9780eb1--pdf-r2l-1.0.1.catalina.bottle.tar.gz
==> Pouring pdf-r2l-1.0.1.catalina.bottle.tar.gz
🍺  /usr/local/Cellar/pdf-r2l/1.0.1: 22 files, 12.3MB

# 実行
$ pdf-r2l
Usage: pdf-r2l R2L|L2R input-file outout-file

引っかかったところ

libexec

brew auditをしたときに,このアプリケーションを実行するために必要なjarファイルを, libではなくlibexecに置くように言われましたが,Gradleのビルドで出力先を変更する方法がわかりませんでした.

そこで,Homebrewでのビルド時のみ,libexecに配置し,実行時のCLASSPATHを変更するようにしました.

/usr/local/Homebrew/Library/Taps/ottijp/homebrew-tap/Formula/pdf-r2l.rb
  def install
    system "gradle", "-q", "installDist"

    bin.install "build/install/pdf-r2l/bin/pdf-r2l"
    libexec.install Dir["build/install/pdf-r2l/lib/*"]
    system "sed", "-i", "", "s/\\$APP_HOME\\/lib\\//\\$APP_HOME\\/libexec\\//g", "#{bin}/pdf-r2l"
  end

cf. Gradle Application プラグインで生成したスクリプトが実行できないことがある · kaakaa blog

gradleのプロジェクト名

brew build時のディレクトリ名が pdf-r2l-1.0.0などどなるため, これがプロジェクト名になり,Gradleのビルド出力先が build/install/pdf-r2l-1.0.0 などとなってしまうため, bin.installのコピー元を特定することができませんでした.

そこで,settings.gradle を追加し,プロジェクト名を固定しました.

settings.gradle
rootProject.name = 'pdf-r2l'

cf. gradle installDist したときのディレクトリ名と実行ファイル名の設定 - たごもりすメモ

Javaの互換性

もともとのツールはJDK11で作っていますが,dockerでの実行なので,JREを指定できていて特に問題はありませんでした. しかし,Homebrewでインストールした時は,ユーザの実行環境に依存してしまうので, せめてJava8はサポートしたいと思いました.

そこで,Javaのソースコードを書き換え,Gradleで互換性の設定を入れました.

build.gradle
java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

cf. Building Java & JVM projects

その他注意点

環境ごとのbottleの作成

各環境(macOSバージョン)用のbottleの作成は,それぞれ環境を用意しないといけないので,VMか何かで別途作り方を考える必要があります.

依存パッケージのバージョン

Homebrewにバージョニングの機構はあるのですが,まだ一部のFormulaが対応しているのみなので, 特定バージョンのパッケージに依存している場合は,依存性解決のやり方を個別に考える必要があります.

cf. How do I depend on a specific version of a homebrew formula - Ask Different


ottijp
Satoshi SAKAO (@ottijp)

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

...