6
9

More than 3 years have passed since last update.

HomebrewでLinuxとmacOSの両方に対応したFormulaを作って公開する

Posted at

はじめに

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で、上に挙げたメソッドがいくつか使われているので、抜粋して見てみます:

Formula/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を紹介します:

Formula/shelp.rb
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には1日1回程度のペースで最新のhomebrew-coreがマージされているようなので、基本的にこちらにプルリクエストを送る必要はありません。

公式のガイドは下になります:

自前でTapを用意して配布する

Tapとは「Third-Party Repositories」のことで、非公式のFormulaリポジトリをbrewの仕組みで管理できるようにするためのものです。3
brew tap してインストールするアレです。

公式のhomebrew-coreにプルリクエストを送るのがハードルが高い場合、こちらのやり方を採るとよいです。

こちらについても、やり方については既に巷に多くの記事が書かれているので、詳細は割愛します。

私の場合、上にも挙げたprogrhyme/homebrew-tapsというリポジトリを作ったので、基本的にこちらにまとめていこうと考えています。(参考記事のやり方を参考にしました。)

まとめ

本稿では、HomebrewでLinuxとmacOSの両方に対応したFormulaを作って、公開する方法を紹介しました。

参考

6
9
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
6
9