🗂

Homebrewのmysql@5.7のformulaを読んでみた

に公開

1. はじめに

皆さん、こんにちは。株式会社BTMの風間と申します。

先日、Macのローカル環境でMySQL5.7を利用する必要があり、いつものごとくHomebrewでインストールしようとしたら、エラーが表示されました。。。

$ brew install mysql@5.7
Error: mysql@5.7 has been disabled because it is not supported upstream! It was disabled on 2024-08-01.

とある事情でDockerを利用できない状況だったので困りました。

brew install -fで強制的にインストールはできたのですが、メンテナンスされていないものを利用するのも怖いなと思い、どうすべきか悩みました。

Macで利用する開発ツールはHomebrewで完結させておきたかったので、「知らないことは知れば良い」ということで、Homebrewの仕組みを理解することにしました。

今回の記事は、その時の備忘録となります。

2. 環境

本記事は、Apple Silicon Macでの環境を想定しています。
Apple Silicon MacとIntel MacではHomebrewのディレクトリ構成が変わってきます。

Intel Macの方は以下の表記を次のように読み替えてください。
/opt/homebrew -> /usr/local

※Homebrewのインストール先を変えられている方は、brew --prefixで表示されるパスに置き換えて読んでください。

3. 本ブログを読むのに必要な知識

以下の知識があるとスムーズに読めると思います。

  • Homebrewの基本的なコマンドを使ったことがある
  • Rubyを触ったことがある
  • cmakeやmakeを実行したことがある
  • mysqlを触ったことがある

4. Homebrewについて

参考:Homebrewのドキュメント(brew manual page)

  • Homebrewは、macOS(およびLinux)用のパッケージマネージャー

  • パッケージマネージャーとは、ソフトウェアのインストール、アンインストール、アップデートなどを簡単に行うためのツール

  • Homebrewで、macOSに同梱されていないUNIXツールをインストールできる

  • Homebrewの仕組みを理解する上で欠かせない用語

    用語 読み 単語の意味 Homebrewでの役割
    formula フォーミュラ 調理法 コマンドラインツールのインストール方法を定義した設計図
    tap タップ 蛇口 Homebrewの公式以外のformula置き場(リポジトリ)にアクセスする
    bottle ボトル ビルド済みのバイナリパッケージ。これをダウンロードすることで、時間のかかるビルドを省略できる
    keg ケグ 小さい樽 formulaからインストールした先のディレクトリ。各バージョン毎に管理される
    Cellar セラー 貯蔵室 Homebrewでインストールした全パッケージ(keg)が保管される大元のディレクトリ

Homebrewの"brew"には「ビールを醸造する」という意味があります。Homebrewは自家醸造を意味し、関連用語も醸造を連想できる言葉で統一されています。なぜこのような用語が使われているのかは、インストールの流れを追うと納得できます。

brew installを実行すると、以下のように進みます。

  1. Homebrewはまずformula(設計図)を探します
  2. formulaに自分の環境に合ったbottle(ビルド済みパッケージ)が書かれていれば、それをダウンロードしてkeg(インストール先のディレクトリ)にいれます
  3. bottleがなければ、formulaの指示通りにソースコードからビルドを行い、ビルドしたものをkegに入れます
  4. 最後に作ったkegをCellar(全パッケージを管理するディレクトリ)に移動します

ソフトウェアをビルドしてインストールする一連の流れを、ビールの醸造プロセスに見立てているんですね。アイコンがビールジョッキなのも頷けます!

※tapを使う場面
公式(homebrew-core)以外のformulaを使いたい場合は、brew tapコマンドでサードパーティのformula置き場(リポジトリ)に接続し、そこからformulaをとってきます。tap(蛇口)を切り替えることでkeg(樽)に注がれるもの(formulaでできあがるもの)が変わる感じだと思います。

※他の用語(caskとCaskroom)は今回使わないので割愛しています。

5. Homebrewのformulaについて

参考:Homebrewのドキュメント(Formula Cookbook)

formulaには、Homebrewがソフトウェアをインストールするのに必要な手順がRubyで書かれています。
Formulaというクラスを継承して作られ、主に以下のようなメソッドで構成されています。

  • desc: パッケージの説明
  • homepage: 公式サイトのURL
  • url: ソースコードのダウンロードURL
  • sha256: ダウンロードしたファイルが正しいものか検証するためのハッシュ値
  • depends_on: インストールに必要な他のパッケージ(依存関係)
  • install: インストール処理の具体的な手順

より詳しくは、公式ドキュメントのCookbookを見てください。

6. Homebrewのmysql@5.7のformulaファイルの確認

参考:Homebrew Ruby API(Formula)

実際にMySQL5.7用のformulaを見ていきます。

公式が提供しているformulaは、homebrew-coreというリポジトリのformula置き場に入っています。

今回確認するformulaはmysql@5.7.rbとなります。

mysql@5.7.rb
class MysqlAT57 < Formula
  desc "Open source relational database management system"
  homepage "https://dev.mysql.com/doc/refman/5.7/en/"
  url "https://cdn.mysql.com/Downloads/MySQL-5.7/mysql-boost-5.7.44.tar.gz"
  sha256 "b8fe262c4679cb7bbc379a3f1addc723844db168628ce2acf78d33906849e491"
  license "GPL-2.0-only"
  revision 1

  bottle do
    sha256 arm64_sonoma:   "ca2e5c8b98bd92843578ffeae0e6280d3066afc33c814cb1ba49299fe9285f50"
    sha256 arm64_ventura:  "c0ff4905882e49d8baf0446652ee9fa6158b00bcd0d17ef2c1a017d0875c5ae5"
    sha256 arm64_monterey: "326f59da12d2b0f0c18465085d6539930cb75dc500856e7dd05ecc734b91a3f4"
    sha256 sonoma:         "8676947218acdace558ac56f01a14c4499963201aa5834041146f8b3efe6eea0"
    sha256 ventura:        "6e9e12bd8918e60560b4c95f3d3b4135bffba820bcdb7b671e7a43af3e948f5c"
    sha256 monterey:       "de04d1a5af2794e0ee6ff76f3f17882ce4e8bbe81705483c7bfb799d42227266"
    sha256 x86_64_linux:   "cf11c67e97a5a88a6788a07e7088f4525979f7eb4192dd41948ddcac6f1016c2"
  end

  keg_only :versioned_formula

  # https://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf
  disable! date: "2024-08-01", because: :unsupported

  depends_on "cmake" => :build
  depends_on "libevent"
  depends_on "lz4"
  depends_on "openssl@1.1"
  depends_on "protobuf"

  uses_from_macos "curl"
  uses_from_macos "cyrus-sasl"
  uses_from_macos "libedit"

  on_linux do
    depends_on "pkgconf" => :build
    depends_on "libtirpc"
  end

  def datadir
    var/"mysql"
  end

  # Fixes loading of VERSION file, backported from mysql/mysql-server@51675dd
  patch :DATA

  def install
    if OS.linux?
      # Fix libmysqlgcs.a(gcs_logging.cc.o): relocation R_X86_64_32
      # against `_ZN17Gcs_debug_options12m_debug_noneB5cxx11E' can not be used when making
      # a shared object; recompile with -fPIC
      ENV.append_to_cflags "-fPIC"
    end

    # Fixes loading of VERSION file; used in conjunction with patch
    File.rename "VERSION", "MYSQL_VERSION"

    # -DINSTALL_* are relative to `CMAKE_INSTALL_PREFIX` (`prefix`)
    args = %W[
      -DCOMPILATION_COMMENT=Homebrew
      -DDEFAULT_CHARSET=utf8
      -DDEFAULT_COLLATION=utf8_general_ci
      -DINSTALL_DOCDIR=share/doc/#{name}
      -DINSTALL_INCLUDEDIR=include/mysql
      -DINSTALL_INFODIR=share/info
      -DINSTALL_MANDIR=share/man
      -DINSTALL_MYSQLSHAREDIR=share/mysql
      -DINSTALL_PLUGINDIR=lib/plugin
      -DMYSQL_DATADIR=#{datadir}
      -DSYSCONFDIR=#{etc}
      -DWITH_BOOST=boost
      -DWITH_EDITLINE=system
      -DWITH_SSL=yes
      -DWITH_NUMA=OFF
      -DWITH_UNIT_TESTS=OFF
      -DWITH_EMBEDDED_SERVER=ON
    ]

    args << if OS.mac?
      "-DWITH_INNODB_MEMCACHED=ON" # InnoDB memcached plugin build fails on Linux
    else
      "-DENABLE_DTRACE=0"
    end

    system "cmake", ".", *std_cmake_args, *args
    system "make"
    system "make", "install"

    (prefix/"mysql-test").cd do
      system "./mysql-test-run.pl", "status", "--vardir=#{Dir.mktmpdir}"
    end

    # Remove the tests directory
    rm_r(prefix/"mysql-test")

    # Don't create databases inside of the prefix!
    # See: https://github.com/Homebrew/homebrew/issues/4975
    rm_r(prefix/"data")

    # Fix up the control script and link into bin.
    inreplace "#{prefix}/support-files/mysql.server",
              /^(PATH=".*)(")/,
              "\\1:#{HOMEBREW_PREFIX}/bin\\2"
    bin.install_symlink prefix/"support-files/mysql.server"

    # Install my.cnf that binds to 127.0.0.1 by default
    (buildpath/"my.cnf").write <<~INI
      # Default Homebrew MySQL server config
      [mysqld]
      # Only allow connections from localhost
      bind-address = 127.0.0.1
    INI
    etc.install "my.cnf"
  end

  def post_install
    # Make sure the var/mysql directory exists
    (var/"mysql").mkpath

    if (my_cnf = ["/etc/my.cnf", "/etc/mysql/my.cnf"].find { |x| File.exist? x })
      opoo <<~EOS
        A "#{my_cnf}" from another install may interfere with a Homebrew-built
        server starting up correctly.
      EOS
    end

    # Don't initialize database, it clashes when testing other MySQL-like implementations.
    return if ENV["HOMEBREW_GITHUB_ACTIONS"]

    unless (datadir/"mysql/general_log.CSM").exist?
      ENV["TMPDIR"] = nil
      system bin/"mysqld", "--initialize-insecure", "--user=#{ENV["USER"]}",
        "--basedir=#{prefix}", "--datadir=#{datadir}", "--tmpdir=/tmp"
    end
  end

  def caveats
    <<~EOS
      We've installed your MySQL database without a root password. To secure it run:
          mysql_secure_installation

      MySQL is configured to only allow connections from localhost by default

      To connect run:
          mysql -uroot
    EOS
  end

  service do
    run [opt_bin/"mysqld_safe", "--datadir=#{var}/mysql"]
    keep_alive true
    working_dir var/"mysql"
  end

  test do
    (testpath/"mysql").mkpath
    (testpath/"tmp").mkpath
    system bin/"mysqld", "--no-defaults", "--initialize-insecure", "--user=#{ENV["USER"]}",
      "--basedir=#{prefix}", "--datadir=#{testpath}/mysql", "--tmpdir=#{testpath}/tmp"
    port = free_port
    fork do
      system bin/"mysqld", "--no-defaults", "--user=#{ENV["USER"]}",
        "--datadir=#{testpath}/mysql", "--port=#{port}", "--tmpdir=#{testpath}/tmp"
    end
    sleep 5
    assert_match "information_schema",
      shell_output("#{bin}/mysql --port=#{port} --user=root --password= --execute='show databases;'")
    system bin/"mysqladmin", "--port=#{port}", "--user=root", "--password=", "shutdown"
  end
end

__END__
diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake
index 43d731e..3031258 100644
--- a/cmake/mysql_version.cmake
+++ b/cmake/mysql_version.cmake
@@ -31,7 +31,7 @@ SET(DOT_FRM_VERSION "6")
 
 # Generate "something" to trigger cmake rerun when VERSION changes
 CONFIGURE_FILE(
-  ${CMAKE_SOURCE_DIR}/VERSION
+  ${CMAKE_SOURCE_DIR}/MYSQL_VERSION
   ${CMAKE_BINARY_DIR}/VERSION.dep
 )
 
@@ -39,7 +39,7 @@ CONFIGURE_FILE(
 
 MACRO(MYSQL_GET_CONFIG_VALUE keyword var)
  IF(NOT ${var})
-   FILE (STRINGS ${CMAKE_SOURCE_DIR}/VERSION str REGEX "^[ ]*${keyword}=")
+   FILE (STRINGS ${CMAKE_SOURCE_DIR}/MYSQL_VERSION str REGEX "^[ ]*${keyword}=")
    IF(str)
      STRING(REPLACE "${keyword}=" "" str ${str})
      STRING(REGEX REPLACE  "[ ].*" ""  str "${str}")

6.1. classの定義

class MysqlAT57 < Formula

class定義をみると、Formulaクラスを継承しているのが分かります。
(クラス名は、@が使えないためかATで代用されているようです)

6.2. 基本情報

  desc "Open source relational database management system"
  homepage "https://dev.mysql.com/doc/refman/5.7/en/"
  • desc: brew info mysql@5.7で表示されるパッケージの短い説明です
  • homepage: このformulaでインストールされるソフトウェアの公式サイトのURLです。ここではMySQL5.7のドキュメントのURLとなっています
  url "https://cdn.mysql.com/Downloads/MySQL-5.7/mysql-boost-5.7.44.tar.gz"
  sha256 "b8fe262c4679cb7bbc379a3f1addc723844db168628ce2acf78d33906849e491"
  • url: インストールするMySQL5.7のソースコード(Boostライブラリ同梱版)のダウンロードURLです
  • sha256: urlからダウンロードしたファイルが正しいか検証するためのハッシュ値です

せっかくなので、MySQL5.7のページからダウンロードできるものと一致するか確認してみます。

ソースのダウンロード

MySQLのダウンロードページから所定のものを探します。
MySQL公式からのたどり方: Downloads>MySQL Community(GPL) Download > Download Archives>MySQL Community Server)

ソースのダウンロード.png

画像の通りMySQL5.7.44のソースは2種類あります。MySQL5.7.5以降はBoostを利用しているようで、Boost無しと有りのソースがあります。
formulaのurlはBoost有りなので、Boost有りをダウンロードします。

ダウンロードURLの確認.png

Chromeの開発者ツールを使ってリクエストしたURLを確認するとformulaに記載されているurlと一致するのが分かります。

ダウンロードしたファイルが一致するか

ダウンロードしたファイルが正しいか、MD5のチェックサムを確認するとダウンロードページの値と一致します。

$ md5 mysql-boost-5.7.44.tar.gz 
MD5 (mysql-boost-5.7.44.tar.gz) = 1a637fce4599d9bf5f1c81699f086274

SHA256のハッシュ値をチェックするとformulaのsha256と一致しています。

$ shasum -a 256 mysql-boost-5.7.44.tar.gz 
b8fe262c4679cb7bbc379a3f1addc723844db168628ce2acf78d33906849e491

formulaに指定されているurlとsha256は、公式が提供しているものだと分かりました。

6.3. Bottleブロック (ビルド済みパッケージ)

  bottle do
    sha256 arm64_sonoma:   "ca2e5c8b98bd92843578ffeae0e6280d3066afc33c814cb1ba49299fe9285f50"
    sha256 arm64_ventura:  "c0ff4905882e49d8baf0446652ee9fa6158b00bcd0d17ef2c1a017d0875c5ae5"
    sha256 arm64_monterey: "326f59da12d2b0f0c18465085d6539930cb75dc500856e7dd05ecc734b91a3f4"
    sha256 sonoma:         "8676947218acdace558ac56f01a14c4499963201aa5834041146f8b3efe6eea0"
    sha256 ventura:        "6e9e12bd8918e60560b4c95f3d3b4135bffba820bcdb7b671e7a43af3e948f5c"
    sha256 monterey:       "de04d1a5af2794e0ee6ff76f3f17882ce4e8bbe81705483c7bfb799d42227266"
    sha256 x86_64_linux:   "cf11c67e97a5a88a6788a07e7088f4525979f7eb4192dd41948ddcac6f1016c2"
  end
  • bottle: ビルド済みパッケージ(Bottle)の情報を定義するブロックです。各OSバージョン・CPUごとにBottleのsha256ハッシュ値が記載されています。Homebrewはこれを見て、自分の環境に合ったBottleをダウンロードします

2024年8月でMySQL5.7のサポートが終了したので、その時点の最新macOSから三世代前までのビルド済みパッケージが用意されていたことが分かります。Apple Silicon用とIntel用の両方が準備されていた点も見て取れます。

6.4. 重要ディレクティブ

  keg_only :versioned_formula
  • keg_only: このformulaをインストールしても、mysqlコマンドなどが置かれる/opt/homebrew/binに直接リンク(シンボリックリンク)を作成しないようにする設定です。mysql@8.0など他のバージョンと競合するのを防ぐ目的で利用されます
  disable! date: "2024-08-01", because: :unsupported
  • disable!: dateに指定された日付以降、formulaを無効にします。これがインストールできない直接的な原因となります。becauseに理由があり、unsupported(開発元サポート終了)と明記されています

6.5. 依存関係

MySQLをビルドまたは実行するために必要な他のパッケージがここで指定されています。

  depends_on "cmake" => :build
  depends_on "libevent"
  depends_on "lz4"
  depends_on "openssl@1.1"
  depends_on "protobuf"

  uses_from_macos "curl"
  uses_from_macos "cyrus-sasl"
  uses_from_macos "libedit"

  on_linux do
    depends_on "pkgconf" => :build
    depends_on "libtirpc"
  end
  • depends_on: Homebrewで管理されている他のformulaに依存することを示します。installされていないものはHomebrewが自動でインストールします
  • uses_from_macos: macOSに入っているものを利用することを示します
  • on_linux do...end: HomebrewをLinuxで使う場合にのみ必要となる依存関係を定義しています
  • => :buildは、ビルド時に必要なものとなります

MySQL5.7が依存しているものはMySQL5.7のドキュメント(Source Installation Prerequisites)に記載があります。

ただ、lz4などは上記に記載がなく、MySQL5.7のドキュメント(MySQL Source-Configuration Options)を見ると気づくことができます。

例:lz4ライブラリ

lzライブラリ.png

6.6. パッチ適用

  patch :DATA
  • patch: ソースコードをビルドする前に、コードの一部を自動的に修正するための「パッチ」を適用します

パッチの内容はformulaの下部にある以下の部分となります。

__END__
diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake
#... (パッチの内容)

パッチの内容は、MySQLのビルドする時にCMakeがVERSIONというファイルの代わりにMYSQL_VERSIONというファイルを参照するように設定を書き換えています。

なぜこのパッチを当てるのか分かっていないのですが、もしかしたらこの問題の対応で、他のビルド環境との競合を避けているのかもしれません。

6.7. インストール

参考:MySQL5.7のドキュメント( Installing MySQL Using a Standard Source Distribution)

ここからがformulaの中心部分です。ソースコードからMySQLをビルドし、インストールするまでの具体的な手順が書かれています。

  def install
  • install: ここからがインストールの手順となります。 このinstallメソッド内の処理を上から順に実行します
    if OS.linux?
      ENV.append_to_cflags "-fPIC"
    end
  • if OS.linux?: Linux環境での設定を行っています
    File.rename "VERSION", "MYSQL_VERSION"

ファイル名のリネームをしています。先ほどのパッチが正しく機能するように実際のファイル名を変更しています。

    args = %W[
      -DCOMPILATION_COMMENT=Homebrew
      -DDEFAULT_CHARSET=utf8
      -DDEFAULT_COLLATION=utf8_general_ci
      -DINSTALL_DOCDIR=share/doc/#{name}
      -DINSTALL_INCLUDEDIR=include/mysql
      -DINSTALL_INFODIR=share/info
      -DINSTALL_MANDIR=share/man
      -DINSTALL_MYSQLSHAREDIR=share/mysql
      -DINSTALL_PLUGINDIR=lib/plugin
      -DMYSQL_DATADIR=#{datadir}
      -DSYSCONFDIR=#{etc}
      -DWITH_BOOST=boost
      -DWITH_EDITLINE=system
      -DWITH_SSL=yes
      -DWITH_NUMA=OFF
      -DWITH_UNIT_TESTS=OFF
      -DWITH_EMBEDDED_SERVER=ON
    ]

CMakeに渡す詳細なオプションを定義します。
各値の詳細は、MySQL5.7のドキュメント(MySQL Source-Configuration Options)を参照してください。

    args << if OS.mac?
      "-DWITH_INNODB_MEMCACHED=ON" # InnoDB memcached plugin build fails on Linux
    else
      "-DENABLE_DTRACE=0"
    end

ここでは、OSに応じて異なるビルドオプションを追加しています。

    system "cmake", ".", *std_cmake_args, *args
    system "make"
    system "make", "install"

ここでビルドとインストールを実行します。

  • system "cmake", ".", *std_cmake_args, *args: CMakeを実行します。std_cmake_argsはHomebrewが提供する標準オプション群です。Homebrewのドキュメント(Formula Cookbook)をみると次のように記載されています
    std_cmake_args
    "-DCMAKE_INSTALL_PREFIX=#{install_prefix}"
    "-DCMAKE_INSTALL_LIBDIR=#{install_libdir}"
    "-DCMAKE_BUILD_TYPE=Release"
    "-DCMAKE_FIND_FRAMEWORK=#{find_framework}"
    "-DCMAKE_VERBOSE_MAKEFILE=ON"
    "-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=#{HOMEBREW_LIBRARY_PATH}/cmake/trap_fetchcontent_provider.cmake"
    "-Wno-dev"
    "-DBUILD_TESTING=OFF"
    
  • system "make" と system "make", "install": ソースコードをコンパイルし、指定された場所にインストールします
    (prefix/"mysql-test").cd do
      system "./mysql-test-run.pl", "status", "--vardir=#{Dir.mktmpdir}"
    end

インストール後にMySQLで用意されているテストを実行します。

  • (prefix/"mysql-test").cd: インストール先(Keg)にあるmysql-testというディレクトリに一時的に移動します
  • system "./mysql-test-run.pl", "status",...: MySQLに同梱されているmysql-test-run.plというテスト実行スクリプトを呼び出しています
  • "--vardir=#{Dir.mktmpdir}": 一時ディレクトリを作成し、そこでテスト実行時に作られるファイルを管理するようにしています
    # Remove the tests directory
    rm_r(prefix/"mysql-test")

テストの後処理としてテストのディレクトリを削除しています。

    # Don't create databases inside of the prefix!
    # See: https://github.com/Homebrew/homebrew/issues/4975
    rm_r(prefix/"data")

コメントのissueを確認すると、インストール先のディレクトリ内にデータが作成されるのを防ぐ対応のようです。

    # Fix up the control script and link into bin.
    inreplace "#{prefix}/support-files/mysql.server",
              /^(PATH=".*)(")/,
              "\\1:#{HOMEBREW_PREFIX}/bin\\2"
    bin.install_symlink prefix/"support-files/mysql.server"
  • inreplace "#{prefix}/support-files/mysql.server",...: mysql.serverの中に記載されているPATHにHomebrewのbinのパスを追加しています。これでmysql.server呼び出し時にHomebrewでインストールされているバイナリを参照できるようになります
  • bin.install_symlink prefix/"support-files/mysql.server": support-filesにあるmysql.serverへのシンボリックリンクをbinディレクトリに作成します
    # Install my.cnf that binds to 127.0.0.1 by default
    (buildpath/"my.cnf").write <<~INI
      # Default Homebrew MySQL server config
      [mysqld]
      # Only allow connections from localhost
      bind-address = 127.0.0.1
    INI
    etc.install "my.cnf"

my.cnfファイルを作成しています。ローカル環境からのアクセスのみ受け付けるようにしています。
そして、作成したファイルをHomebrewのetcにコピーしています。

bin.install_symlinketc.installが気になる方は、Homebrew Ruby API(Pathname)を参照してください

6.8. インストール後処理

参考:MySQL5.7のドキュメント(Postinstallation Setup and Testing)

パッケージがインストールされた後に実行される処理で、MySQLのデータベースを初期化します。

  def post_install
    # Make sure the var/mysql directory exists
    (var/"mysql").mkpath

    if (my_cnf = ["/etc/my.cnf", "/etc/mysql/my.cnf"].find { |x| File.exist? x })
      opoo <<~EOS
        A "#{my_cnf}" from another install may interfere with a Homebrew-built
        server starting up correctly.
      EOS
    end

    # Don't initialize database, it clashes when testing other MySQL-like implementations.
    return if ENV["HOMEBREW_GITHUB_ACTIONS"]

    unless (datadir/"mysql/general_log.CSM").exist?
      ENV["TMPDIR"] = nil
      system bin/"mysqld", "--initialize-insecure", "--user=#{ENV["USER"]}",
        "--basedir=#{prefix}", "--datadir=#{datadir}", "--tmpdir=/tmp"
    end
  end
  • post_install: installメソッドの処理が完了した後に実行される処理です
  • (var/"mysql").mkpath: HomebrewのvarディレクトリにMySQLのデータベースなどのファイルを保存するディレクトリを作成します
  • if (my_cnf = ["/etc/my.cnf", ...end: Homebrewの管理外の場所にmy.cnfがあれば、ユーザーに警告を表示します
  • return if ENV["HOMEBREW_GITHUB_ACTIONS"]: HOMEBREW_GITHUB_ACTIONSの環境では、以降のデータベースの初期化をしないようにします
  • unless (datadir/"mysql/general_log.CSM").exist?: データベースが初期化済みであれば、再度初期化を行わないようにします
  • mysqld --initialize-insecure: rootユーザーのパスワードを設定せずにデータベースを初期化します
  def caveats
    <<~EOS
      We've installed your MySQL database without a root password. To secure it run:
          mysql_secure_installation

      MySQL is configured to only allow connections from localhost by default

      To connect run:
          mysql -uroot
    EOS
  end
  • caveats: インストール完了後、ターミナルに表示される注意事項です。「rootのパスワードが設定されてないので、mysql_secure_installationを実行してね」という親切なメッセージを表示します

6.9. サービス定義

参考: Homebrew Ruby API(Service)

brew servicesコマンドでMySQLをバックグラウンドサービスとして管理するための設定です 。

  service do
    run [opt_bin/"mysqld_safe", "--datadir=#{var}/mysql"]
    keep_alive true
    working_dir var/"mysql"
  end
  • run: サービス起動時に実行するコマンドを指定します
  • keep_alive: サービスを常駐させます
  • working_dir: サービスを実行する作業ディレクトリです

6.10. テストブロック

brew test mysql@5.7で、インストールが正しく行われたかを検証するための自己完結型テストです 。

  test do
    (testpath/"mysql").mkpath
    (testpath/"tmp").mkpath
    system bin/"mysqld", "--no-defaults", "--initialize-insecure", "--user=#{ENV["USER"]}",
      "--basedir=#{prefix}", "--datadir=#{testpath}/mysql", "--tmpdir=#{testpath}/tmp"
    port = free_port
    fork do
      system bin/"mysqld", "--no-defaults", "--user=#{ENV["USER"]}",
        "--datadir=#{testpath}/mysql", "--port=#{port}", "--tmpdir=#{testpath}/tmp"
    end
    sleep 5
    assert_match "information_schema",
      shell_output("#{bin}/mysql --port=#{port} --user=root --password= --execute='show databases;'")
    system bin/"mysqladmin", "--port=#{port}", "--user=root", "--password=", "shutdown"
  end

このテストは、ユーザー環境を汚さないよう一時的な場所で、以下の処理を行っています。

  1. テスト用のデータベースを初期化する
  2. 空いているポートを使ってテスト用のMySQLを起動する
    free_portの説明はHomebrew Ruby API(FreePort)
  3. テスト用のMySQLに接続し、show databasesクエリを実行する
  4. 結果にinformation_schemaが含まれていることを検証する
  5. テストサーバーをシャットダウンする

7. まとめ

HomebrewでインストールできなくなったMySQL5.7のformulaを見てみました。MySQL5.7のformulaでは、以下を行っていることが分かりました。

  • bottleがある場合はbottleをダウンロードしてインストール
  • bottleがなければ、ソースをダウンロードしビルドしてインストールしてテスト

そして、使えなくなったものは「自分でビルドするべし」という教訓を得ました。

また、昔の環境を使えるように準備しておくことは大切だと改めて実感しました。

mysql@5.7.rbを読むにあたり、自分でMySQL5.7のビルドを試してみたのですが、ビルド方法を調べたり依存関係を解決したりと大変でした(久しぶりのビルドで楽しかったですが!)。

Homebrewに限らず、日々使っているパッケージマネージャーやOSSをメンテナンスしてくれている方々には、本当に感謝しかありません。

初めてformulaを見たのですが、Homebrewの仕組みを少し深く知るだけで、パッケージをより自由に扱えるようになったと感じます。

知らないことや不安なことは調べるのが大切ですね!!

Discussion