野良 homebrew Formula の作成 - M1 MacBook Air インストール覚書(9)

はじめに

昨日、homebrew-cask の方は簡単にいったのですが、本家の Formula はだいぶ苦戦しました。すでにあるものをコピーして修正しても、その Formula があることをうまく認識してくれない状況が改善しませんでした。

ということで、基本に忠実にHomebrewでformulaの作成 | ottijp blogを参考に 1 から作って今までのものに似せていく形にしてみます。

(最後まで書いてからの追記: まさか Homebrew 本体まで手を入れることになるとは、この時点では思っていませんでした)

Formula の作成

昨日と同様に GitHub に作っておいた hkob/m1-beta-brew を tap しておきます。

hkob@thin ~> brew tap hkob/m1-beta-brew
==> Tapping hkob/m1-beta-brew
Cloning into '/usr/local/Homebrew/Library/Taps/hkob/homebrew-m1-beta-brew'...
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
Tapped (29 files, 25.6KB).

記事のように Formula を作ってみようとしたのですが、--set-name は --cask の時しか設定できないと言われてできなかったので、無指定です。無指定だと、ダウンロードした tar.gz から sshfs にされてしまいました。エディタが開きますが、修正せずに終了します。

brew create --tap hkob/m1-beta-brew https://github.com/libfuse/sshfs/releases/download/sshfs-2.10/sshfs-2.10.tar.gz

==> Downloading https://github.com/libfuse/sshfs/releases/download/sshfs-2.10/sshfs-2.10.tar.gz
Already downloaded: /Users/hkob/Library/Caches/Homebrew/downloads/530b903f02c905bfdb15b1859c391f8383cfaac570656f7396ed07ca0817fd93--sshfs-2.10.tar.gz
Warning: Cannot verify integrity of '530b903f02c905bfdb15b1859c391f8383cfaac570656f7396ed07ca0817fd93--sshfs-2.10.tar.gz'.
No checksum was provided for this resource.
For your reference, the checksum is:
  sha256 "70845dde2d70606aa207db5edfe878e266f9c193f1956dd10ba1b7e9a3c8d101"
Please run `brew audit --new sshfs` before submitting, thanks.
Editing /usr/local/Homebrew/Library/Taps/hkob/homebrew-m1-beta-brew/sshfs.rb
Warning: Using vim because no editor was set in the environment.
This may change in the future, so we recommend setting EDITOR,
or HOMEBREW_EDITOR to your preferred text editor.

ここに書いてあるように brew audit をしてみます。既存ファイルを書き換えただけだと、Error: No available formula or cask with the name になってしまっていました。sshfs だとオリジナルのものと区別ができないので、tap 名も付けて audit してみます。

hkob@thin ~> brew audit --new hkob/m1-beta-brew/sshfs
hkob/m1-beta-brew/sshfs:
  * 1: col 1: Please remove default template comments
  * 3: col 1: Please remove default template comments
  * 5: col 9: Description shouldn't start with an article.
  * 6: col 3: Formula should have a homepage.
  * 11: col 3: Commented-out dependency "cmake" => :build
  * 14: col 5: Please remove default template comments
  * 15: col 5: Please remove default template comments
  * 20: col 5: Please remove default template comments
  * Formula sshfs contains deprecated SPDX licenses: ["GPL-2.0"].
    You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
    For a list of valid licenses check: https://spdx.org/licenses/
Error: 9 problems in 1 formula detected

ちゃんと認識していました。名前を変えてみます。

mv (brew --repository)/Library/Taps/hkob/homebrew-m1-beta-brew/{sshfs,m1-beta-sshfs}.rb

名前を変えた場合には、Ruby の Class 名も修正する必要があります。 昨日は直接 vi で編集してしまいましたが、brew edit という便利な命令があるのを忘れていました。tap されているので、tap 名は省略できます。ちゃんと認識されていますね。

brew edit m1-beta-sshfs

class 名の部分をファイル名に合わせて、Camel Case 型で記載します。「-」の次の文字が大文字になる感じです。

class M1BetaSshfs < Formula

名前が unique になったので、Formula 名だけで audit もできました。

hkob@thin ~ [1]> brew audit --new m1-beta-sshfs
hkob/m1-beta-brew/m1-beta-sshfs:
  * 1: col 1: Please remove default template comments
  * 3: col 1: Please remove default template comments
  * 5: col 9: Description shouldn't start with an article.
  * 6: col 3: Formula should have a homepage.
  * 11: col 3: Commented-out dependency "cmake" => :build
  * 14: col 5: Please remove default template comments
  * 15: col 5: Please remove default template comments
  * 20: col 5: Please remove default template comments
  * Formula m1-beta-sshfs contains deprecated SPDX licenses: ["GPL-2.0"].
    You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
    For a list of valid licenses check: https://spdx.org/licenses/
Error: 9 problems in 1 formula detected

とりあえず、真似て修正したのがこんな感じです。昨日ダメだったのは、:osxfuse を Cask 名に直していたのがダメだったみたいです。:osxfuse 自体は dependency_collector.rb の中で特殊処理する形になっていました。これが macFUSE で動くのかどうかというところです。ダメなら Homebrew の本体自体にプルリクエストを送らないといけない状況ですね。だから簡単に 4.0.4 に上げていないのかもしれません。

class M1BetaSshfs < Formula
  desc "File system client based on SSH File Transfer Protocol"
  homepage "https://osxfuse.github.io/"
  url "https://github.com/libfuse/sshfs/releases/download/sshfs-2.10/sshfs-2.10.tar.gz"
  sha256 "70845dde2d70606aa207db5edfe878e266f9c193f1956dd10ba1b7e9a3c8d101"
  license "GPL-2.0-or-later"

  depends_on "autoconf" => :build
  depends_on "automake" => :build
  depends_on "libtool" => :build
  depends_on "pkg-config" => :build
  depends_on "glib"
  depends_on :osxfuse

  patch do
    url "https://github.com/libfuse/sshfs/commit/667cf34622e2e873db776791df275c7a582d6295.patch?full_index=1"
    sha256 "ab2aa697d66457bf8a3f469e89572165b58edb0771aa1e9c2070f54071fad5f6"
  end

  def install
    system "./configure", "--disable-dependency-tracking", "--prefix=#{prefix}"
    system "make", "install"
  end

  test do
    system "#{bin}/sshfs", "--version"
  end
end

ということで、audit も無事スルーしました。

hkob@thin ~> brew audit --new m1-beta-sshfs

sshfs のビルド

早速ですが、brew install してみます。デバッグ確認のために、「-dv」のオプションを付けてみます。

hkob@thin ~> brew install m1-brew-sshfs -dv
==> Installing m1-beta-sshfs from hkob/m1-beta-brew
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/autoconf.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/automake.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/libtool.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/pkg-config.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/glib.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/meson.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/ninja.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/python@3.9.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/gdbm.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/openssl@1.1.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/readline.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/sqlite.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/xz.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/gettext.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/libffi.rb
/usr/local/Homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/pcre.rb
m1-beta-sshfs: FUSE for macOS is required for this software. OsxfuseRequirement unsatisfied!
You can install the necessary cask with:
  brew install --cask osxfuse
You can download from:
  https://osxfuse.github.io/
Error: An unsatisfied requirement failed this build.
/usr/local/Homebrew/Library/Homebrew/formula_installer.rb:524:in `check_requirements'
/usr/local/Homebrew/Library/Homebrew/formula_installer.rb:487:in `compute_dependencies'
/usr/local/Homebrew/Library/Homebrew/formula_installer.rb:213:in `verify_deps_exist'
/usr/local/Homebrew/Library/Homebrew/formula_installer.rb:204:in `prelude'
/usr/local/Homebrew/Library/Homebrew/cmd/install.rb:382:in `install_formula'
/usr/local/Homebrew/Library/Homebrew/cmd/install.rb:297:in `block in install'
/usr/local/Homebrew/Library/Homebrew/cmd/install.rb:295:in `each'
/usr/local/Homebrew/Library/Homebrew/cmd/install.rb:295:in `install'
/usr/local/Homebrew/Library/Homebrew/brew.rb:119:in `<main>'

やはり macFUSE は osxfuse と認識してくれない問題でした。パス名が面倒なので、Library/Homebrew に移動しましょう。

cd (brew --repository)/Library/Homebrew

ここにあるdependency_collector.rb の 185行目で作成されている OsxfuseRequirement.new(tags) を直さないとですね。

    when :osxfuse       then OsxfuseRequirement.new(tags)

OsxfuseRequirement を探しましょう。requirements の下にいました。

hkob@thin /u/l/H/L/Homebrew (master)> grep OsxfuseRequirement */*.rb
requirements/osxfuse_requirement.rb:class OsxfuseRequirement < Requirement

requirements/osxfuse_requirement.rb をみます。cask "osxfuse" が書かれていますね。cask 名は osxfuse のままの方が互換性が取れそうです。ここを直すわけにはいかないので、バイナリがインストールされているかを確認する部分をチェックします。extend/os/requirements/osxfuse_requirementにありそうです。

# typed: strict
# frozen_string_literal: true

require "requirement"

# A requirement on FUSE for macOS.
#
# @api private
class OsxfuseRequirement < Requirement
  extend T::Sig
  cask "osxfuse"
  fatal true

  sig { returns(String) }
  def display_s
    "FUSE"
  end
end

require "extend/os/requirements/osxfuse_requirement"

extend/os/requirements/osxfuse_requirement.rb を確認します。linuxbrew もあるので、ここで機種判断ですね。先に進みます。

# typed: strict
# frozen_string_literal: true

if OS.mac?
  require "extend/os/mac/requirements/osxfuse_requirement"
elsif OS.linux?
  require "extend/os/linux/requirements/osxfuse_requirement"
end

さらに、extend/os/mac/requirements/osxfuse_requirement.rbをみます。/usr/local/include/osxfuse/fuse.h をチェックしています。昨日の記事の pkgutil の結果を確認すると./usr/local/include/fuse になっていましたね。この部分を修正すればよさそうです。

# typed: false
# frozen_string_literal: true

require "requirement"

class OsxfuseRequirement < Requirement
  extend T::Sig

  download "https://osxfuse.github.io/"

  satisfy(build_env: false) { self.class.binary_osxfuse_installed? }

  sig { returns(T::Boolean) }
  def self.binary_osxfuse_installed?
    File.exist?("/usr/local/include/osxfuse/fuse.h") &&
      !File.symlink?("/usr/local/include/osxfuse")
  end

  env do
    ENV.append_path "PKG_CONFIG_PATH", HOMEBREW_LIBRARY/"Homebrew/os/mac/pkgconfig/fuse"

    unless HOMEBREW_PREFIX.to_s == "/usr/local"
      ENV.append_path "HOMEBREW_LIBRARY_PATHS", "/usr/local/lib"
      ENV.append_path "HOMEBREW_INCLUDE_PATHS", "/usr/local/include/osxfuse"
    end
  end

  def message
    "FUSE for macOS is required for this software. #{super}"
  end
end

とりあえず、修正してみました。

  def self.exist_include_osxfuse?
    File.exist?("/usr/local/include/osxfuse/fuse.h") &&
      !File.symlink?("/usr/local/include/osxfuse")
  end

  def self.exist_include_fuse?
    File.exist?("/usr/local/include/fuse/fuse.h") &&
      !File.symlink?("/usr/local/include/fuse")
  end

  def self.binary_osxfuse_installed?
    exist_include_osxfuse? || exist_include_fuse?
  end

  env do
    ENV.append_path "PKG_CONFIG_PATH", HOMEBREW_LIBRARY/"Homebrew/os/mac/pkgconfig/fuse"

    unless HOMEBREW_PREFIX.to_s == "/usr/local"
      ENV.append_path "HOMEBREW_LIBRARY_PATHS", "/usr/local/lib"
      ENV.append_path "HOMEBREW_INCLUDE_PATHS",
        "/usr/local/include/#{self.exist_include_osxfuse? ? "osxfuse" : "fuse"}"
    end
  end

これでコンパイルが実行できるようになりました。ただし、コンパイルエラーです。/usr/local/include/fuse の下には fuse_darwin.h は存在しませんでした。

sshfs.c:18:12: fatal error: 'fuse_darwin.h' file not found
#  include <fuse_darwin.h>

osxfuse 3.11.2 のマシンで fuse_darwin.h を確認してみます。osxfuse_version のプロトタイプくらいしかないですね。なくてもコンパイルできそうな気がします。

/*
 * Copyright (c) 2006-2008 Amit Singh/Google Inc.
 * Copyright (c) 2011-2017 Benjamin Fleischer
 */

#ifdef __APPLE__

#ifndef _FUSE_DARWIN_H_
#define _FUSE_DARWIN_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

const char *osxfuse_version(void);

#ifdef __cplusplus
}
#endif

#endif /* _FUSE_DARWIN_H_ */

#endif /* __APPLE__ */

とりあえず、fuse_darwin.h の include 部分を外す patch を作ってみます。

cp sshfs.c sshfs.c.orig
vi sshfs.c
diff -u sshfs.c.orig sshfs.c > remove_include.patch

作成されたパッチはこんな感じ

--- sshfs.c.orig 2020-12-03 10:32:25.000000000 +0900
+++ sshfs.c 2020-12-03 10:32:42.000000000 +0900
@@ -14,9 +14,6 @@
 #if !defined(__CYGWIN__)
 #include <fuse_lowlevel.h>
 #endif
-#ifdef __APPLE__
-#  include <fuse_darwin.h>
-#endif
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>

今回、patch は短いので Formula の最後にヒアドキュメントで付けてしまいました。

class M1BetaSshfs < Formula
  desc "File system client based on SSH File Transfer Protocol"
  homepage "https://osxfuse.github.io/"
  url "https://github.com/libfuse/sshfs/releases/download/sshfs-2.10/sshfs-2.10.tar.gz"
  sha256 "70845dde2d70606aa207db5edfe878e266f9c193f1956dd10ba1b7e9a3c8d101"
  license "GPL-2.0-or-later"

  depends_on "autoconf" => :build
  depends_on "automake" => :build
  depends_on "libtool" => :build
  depends_on "pkg-config" => :build
  depends_on "glib"
  depends_on :osxfuse

  patch do
    url "https://github.com/libfuse/sshfs/commit/667cf34622e2e873db776791df275c7a582d6295.patch?full_index=1"
    sha256 "ab2aa697d66457bf8a3f469e89572165b58edb0771aa1e9c2070f54071fad5f6"
  end

  patch :p0, :DATA

  def install
    system "./configure", "--disable-dependency-tracking", "--prefix=#{prefix}"
    system "make", "install"
  end

  test do
    system "#{bin}/sshfs", "--version"
  end
end
__END__
--- sshfs.c.orig  2020-12-03 10:32:25.000000000 +0900
+++ sshfs.c 2020-12-03 10:32:42.000000000 +0900
@@ -14,9 +14,6 @@
 #if !defined(__CYGWIN__)
 #include <fuse_lowlevel.h>
 #endif
-#ifdef __APPLE__
-#  include <fuse_darwin.h>
-#endif
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>

これでコンパイルが通りました。試しにデバッグつけずに reinstall した結果がこちら。

hkob@thin ~> brew reinstall m1-beta-sshfs
==> Downloading https://github.com/libfuse/sshfs/commit/667cf34622e2e873db776791df275c7a582d6295.patch?full_index=1
Already downloaded: /Users/hkob/Library/Caches/Homebrew/downloads/cdcad74dd3989c7992d0df5c9d06cabde2b6d7523ddbb551712a3733e0ba4b02--667cf34622e2e873db776791df275c7a582d6295.patch
==> Downloading https://github.com/libfuse/sshfs/releases/download/sshfs-2.10/sshfs-2.10.tar.gz
Already downloaded: /Users/hkob/Library/Caches/Homebrew/downloads/530b903f02c905bfdb15b1859c391f8383cfaac570656f7396ed07ca0817fd93--sshfs-2.10.tar.gz
==> Reinstalling hkob/m1-beta-brew/m1-beta-sshfs
==> Patching
==> Applying 667cf34622e2e873db776791df275c7a582d6295.patch
patching file sshfs.c
Hunk #1 succeeded at 3862 (offset -58 lines).
==> ./configure --prefix=/usr/local/Cellar/m1-beta-sshfs/2.10
==> make install
🍺  /usr/local/Cellar/m1-beta-sshfs/2.10: 6 files, 138KB, built in 12 seconds

ちゃんと作れている。sshfs --version もちゃんと出ます。

hkob@thin ~> sshfs --version
SSHFS version 2.10
FUSE library version: 2.9.9
fuse: no mount point
hkob@thin ~>

パッチあてとか久しぶりすぎる。Homebrew 本体の osxfuse のプルリクエストとか送るとみんな幸せになれそうな気がするんだけど、送った方がいいのかな。

次の記事: tmcit-brew tap の作成 - M1 MacBook Air インストール覚書(10) - hkob’s blog 前の記事: 野良 homebrew-cask の作成 - M1 MacBook Air インストール覚書(8) - hkob’s blog

hkob.hatenablog.com