はじめに
macOSとLinuxで利用できるパッケージ管理ツールのHomebrewでは、Formulaと呼ばれるパッケージの構成ファイルをRubyのコードで書くことで、新しいパッケージを追加することができます。
作成したFormulaを公開することでパッケージを世界中の人に使ってもらうことができます。
本稿ではFormulaの基本的な作成方法については省略して、macOSとLinuxの両方に対応したFormulaの作成・公開方法にフォーカスします。
単一プラットフォーム向けのFormulaがそのまま使えることもある
結論を一部先に述べる形になりますが、特にマルチプラットフォーム向けの対応をしなくても同じFormulaがそのまま使える場合もあります。
例:
- そもそもプラットフォーム依存のないソフトウェアである場合。例えば、ポータブルな形で書かれたシェルスクリプトとか
- ビルドツールがマルチプラットフォームに対応している場合。Go言語など
- ビルドスクリプトでプラットフォーム差異を吸収しているので、インストールコマンドがプラットフォームによって変わらない場合
Linuxbrewの場合、デフォルトのFormulaリポジトリとしてはlinuxbrew-coreを参照していますが、こちらには1日1回程度のペースで本家(?)であるhomebrew-coreの差分がマージされています。1
なので、元々プラットフォーム依存のないソフトウェア・Formulaであれば、作者が特に意識して対応していなくても、両方のプラットフォームで利用できる可能性はなくはないです。
(実際、私はLinuxbrewを使っていますが、公式にはLinux対応と書かれていないパッケージが使えたことがあります。)
1. Formulaを作る
1.1. 雛形の作成
普通にFormulaを作る場合と変わりません。
brew create
コマンドで作成できます。
# 構文
brew create [Options] URL
# 例
## カスタムのTAPを指定し、Go言語プロジェクト向けの雛形を作る
brew create --go --tap progrhyme/taps -v https://github.com/progrhyme/shelp/archive/v0.5.0.tar.gz
この brew create
コマンドには様々なオプションがあり、各種ビルド方式に対応した雛形があるので、一度 brew create --help
を見てみると良いでしょう。
1.2. LinuxとmacOSで必要な分岐処理を書く
LinuxとmacOSで処理を分けるためには、以下の4つのメソッドが利用できます:
利用イメージはこんな感じです:
class Foo < Formula
on_macos do
# macOS向けのレシピ
end
on_linux do
# Linux向けのレシピ
end
def install
if OS.mac?
# macOS向けのインストール処理
elsif OS.linux?
# Linux向けのインストール処理
end
end
end
Homebrew FormulaのRuby APIには、これ以外にもプラットフォーム固有の処理を行う上で有用そうなモジュールやメソッドがあるので、調べてみるとよさそうです。
サンプル① linuxbrew-core/rust.rb
RustコンパイラやcargoのFormulaである
linuxbrew-core/rust.rbで、上に挙げたメソッドがいくつか使われているので、抜粋して見てみます:
class Rust < Formula
desc "Safe, concurrent, practical language"
homepage "https://www.rust-lang.org/"
stable do
url "https://static.rust-lang.org/dist/rustc-1.44.0-src.tar.gz"
sha256 "bf2df62317e533e84167c5bc7d4351a99fdab1f9cd6e6ba09f51996ad8561100"
resource "cargo" do
url "https://github.com/rust-lang/cargo.git",
:tag => "0.45.0",
:revision => "05d080faa4f2bc1e389ea7c4fd8f30ed2b733a7f"
end
end
bottle do
: # 略
end
depends_on "cmake" => :build
: # 略
on_linux do
depends_on "binutils"
end
uses_from_macos "curl"
uses_from_macos "zlib"
resource "cargobootstrap" do
if OS.mac?
url "https://static.rust-lang.org/dist/2020-05-07/cargo-0.44.0-x86_64-apple-darwin.tar.gz"
sha256 "1071c520204a9e8fe4dd0de66a07a083f06abba16ac88f1df72231328a6395e6"
end
unless OS.mac?
url "https://static.rust-lang.org/dist/2020-05-07/cargo-0.44.0-x86_64-unknown-linux-gnu.tar.gz"
sha256 "e4c8533670a64a85ee1a367e4bb9d63a2d6b3e9949e116ec956cec15df9a67bd"
end
end
def install
ENV.prepend_path "PATH", Formula["python@3.8"].opt_libexec/"bin"
ENV["MACOSX_DEPLOYMENT_TARGET"] = MacOS.version if OS.mac?
ENV["OPENSSL_DIR"] = Formula["openssl@1.1"].opt_prefix
ENV["SDKROOT"] = MacOS.sdk_path if OS.mac?
args = ["--prefix=#{prefix}"]
: # 略
system "./configure", *args
system "make"
system "make", "install"
: # 略
end
end
ここでは上に挙げた4つのメソッド以外に、以下のメソッド、モジュールが使われています:
-
Formula.uses_from_macos ... ドキュメントによれば、macOSでは何もせず(システムライブラリを使う)、Linuxでは
depends_on
と同じ動きをするそうです - module MacOS ... これはmodule OS::Macの別名として定義されていました。2 macOSマシンの各種情報を取得するメソッドが用意されています
サンプル② progrhyme/taps/shelp.rb
自前でTapを用意する場合のサンプルとして、拙作のFormulaを紹介します:
class Shelp < Formula
Version = '0.5.3'
version Version
if OS.mac?
Binary = "shelp_#{Version}_darwin_x86_64"
sha256 "dffb6ccb90395d2e9d90b3dc7f80736a30d6b5be42c99e99b6b3df87e965e30c"
elsif OS.linux?
Binary = "shelp_#{Version}_linux_x86_64"
sha256 "7f35438588860e8f372ce4b3dbed96abc848ec35cb4a7826df49f033d595277d"
end
url "https://github.com/progrhyme/shelp/releases/download/v#{Version}/#{Binary}"
desc 'Git-based package manager for shell scripts written in Go'
homepage 'https://github.com/progrhyme/shelp'
head 'https://github.com/progrhyme/shelp.git'
def install
bin.install Binary => 'shelp'
end
test do
system "#{bin}/shelp", '--version'
end
end
Go言語でビルドする形でFormulaを書けば特に分岐を作る必要もありませんが、わざわざビルドしてもらうためにGoをインストールしてもらうのも気が引けるので、GitHub Releaseにアップロード済みのバイナリを配布する形にしています。
野良Tapなのでbottleで配布できないので、このようにOSを判定してダウンロードURLを分けています。
2. Formulaを公開する
基本的に、次のいずれかの方法を取ることになると思います。
- 公式のhomebrew-coreとlinuxbrew-coreに取り込んでもらう
- 自前でTapを用意して配布する
これらのやり方については既に多くの解説記事が書かれており、マルチプラットフォーム対応する場合もそれほど手続きに差異はありません。
ので、共通する部分はさらっと行きます。
公式リポジトリに取り込んでもらう
通常と同様に、homebrew-coreにプルリクエストを送って取り込んでもらう形になります。
先にも述べたように、linuxbrew-coreには1日1回程度のペースで最新のhomebrew-coreがマージされているようなので、基本的にこちらにプルリクエストを送る必要はありません。
公式のガイドは下になります:
自前でTapを用意して配布する
Tapとは「Third-Party Repositories」のことで、非公式のFormulaリポジトリをbrewの仕組みで管理できるようにするためのものです。3
brew tap
してインストールするアレです。
公式のhomebrew-coreにプルリクエストを送るのがハードルが高い場合、こちらのやり方を採るとよいです。
こちらについても、やり方については既に巷に多くの記事が書かれているので、詳細は割愛します。
私の場合、上にも挙げたprogrhyme/homebrew-tapsというリポジトリを作ったので、基本的にこちらにまとめていこうと考えています。(参考記事のやり方を参考にしました。)
まとめ
本稿では、HomebrewでLinuxとmacOSの両方に対応したFormulaを作って、公開する方法を紹介しました。
参考
- RustのソフトウェアをHomebrewのパッケージにする - Qiita ... この当時はlinuxbrew-coreに取り込んでもらった後、bottleを生成してもらうために手動でイシューを立てる必要があったそうです
- Homebrewで自作ツールを簡単にインストール可能にする | おそらくはそれさえも平凡な日々