配方手冊

配方是用 Ruby 編寫的套件定義。可以使用 brew create <URL> 建立配方,其中 <URL> 是 zip 或 tarball,可以使用 brew install <formula> 安裝配方,並使用 brew install --debug --verbose <formula> 除錯配方。配方使用 配方 API,其中提供各種 Homebrew 專屬的輔助工具。

Homebrew 術語

術語 說明 範例
公式 從上游來源建置的 Homebrew 套件定義 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb
cask 安裝 macOS 原生應用程式的 Homebrew 套件定義 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask/Casks/b/bar.rb
前綴 Homebrew 安裝的路徑 /usr/local
keg 特定 公式版本的安裝目的地目錄 /usr/local/Cellar/foo/0.1
rack 包含一個或多個版本化 keg 的目錄 /usr/local/Cellar/foo
僅 keg 如果 公式 未連結至 Homebrew 的前綴,則為 僅 keg openjdk 公式
opt 前綴 連結至 keg 的有效版本的符號連結 /usr/local/opt/foo
Cellar 包含一個或多個命名 rack 的目錄 /usr/local/Cellar
Caskroom 包含一個或多個命名 casks 的目錄 /usr/local/Caskroom
外部命令 在 Homebrew/brew GitHub 儲存庫外部定義的 brew 子命令 brew 別名
tap 公式casks 和/或 外部命令 的目錄(通常為 Git 儲存庫) /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core
bottle 預先建置的 keg,倒入 Cellarrack 中,而不是從上游來源建置 qt--6.5.1.ventura.bottle.tar.gz
標籤 關於 keg 的資訊,例如它是從 bottle 倒入的還是從來源建置的 /usr/local/Cellar/foo/0.1/INSTALL_RECEIPT.json
Brew Bundle Homebrew 的 擴充功能,用於描述相依性 brew 'myservice', restart_service: true
Brew Services Homebrew 的 擴充功能,用於管理服務 brew services start myservice

簡介

Homebrew 使用 Git 來儲存公式並為專案做出貢獻。

Homebrew 4.0.0 起,公式會從 https://formulae.brew.sh 以 JSON 格式下載,而 Homebrew/formulae.brew.sh 的排程工作會自動從 Homebrew/homebrew-core 儲存庫的 master 分支重新產生。

Homebrew 會將公式安裝到 $(brew --cellar) 的 Cellar,然後將部分安裝項目符號連結到 $(brew --prefix) (例如 /opt/homebrew) 的前置詞,以便其他程式可以看到正在進行的作業。建議您對 Cellar 中的幾個 keg 執行 brew ls,以查看其如何排列。

套件會根據其公式進行安裝。閱讀一個簡單的公式,例如 brew edit etl (或 etl.rb) 或一個較進階的公式,例如 brew edit git (或 git.rb).

基本說明

開始之前,請務必執行 brew update。這可確保您的 Homebrew 安裝是一個 Git 儲存庫。

若要建立或編輯本機公式,您需要先 輕點 homebrew/core (如果您之前尚未輕點)。這會將 Homebrew/homebrew-core Git 儲存庫複製到 $(brew --repository homebrew/core)。在開發過程中,您還需要在 shell 環境中設定 HOMEBREW_NO_INSTALL_FROM_API=1,或在任何 installreinstallupgrade 指令之前設定,以強制 brew 使用本機儲存庫,而不是 API。

在提交新公式之前,請確認您的套件

在提交新公式之前,請務必閱讀我們的 貢獻指南

取得 URL

使用來源 tarball 的 URL 執行 brew create

brew create https://example.com/foo-0.1.tar.gz

這會建立 $(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb,並在您的 EDITOR 中開啟它。

傳入 --ruby--python 將填入各種預設值,通常對使用這些語言撰寫的專案很有用。

如果 brew 在執行 create 步驟時顯示 警告:無法從 URL 判斷版本,您需要明確將正確的 version 新增到公式,然後儲存公式。

Homebrew 會嘗試從其 URL 猜測公式的名稱。如果無法猜測,您可以使用 brew create <URL> --set-name <name> 覆寫此名稱。

填寫 homepage

我們不接受沒有 homepage 的公式!

如果有的話,建議使用 SSL/TLS (https) homepage

嘗試從 homepage 總結公式在 desccription 中執行的動作。請注意,列印時會自動在 description 前面加上公式名稱。

填寫 license

我們不接受沒有 license 的新公式進入 Homebrew/homebrew-core!

我們只接受使用 Debian 自由軟體指引授權 或根據 DFSG 公有領域軟體指引 發布到公有領域的公式。

使用 SPDX 授權清單 中的授權識別碼,例如 license "BSD-2-Clause",或對公有領域軟體使用 license :public_domain

使用 :any_of:all_of:with 來描述複雜的授權表達式。當使用者可以選擇要使用的授權時,應使用 :any_of。當使用者必須使用所有授權時,應使用 :all_of。應使用 :with 來指定有效的 SPDX 例外。新增 + 到識別碼,表示公式可以在相同授權的後續版本下獲得授權。

請查看 授權指南,了解 Homebrew 配方中複雜授權表達式的範例。

檢查建置系統

HOMEBREW_NO_INSTALL_FROM_API=1 brew install --interactive foo

現在您會看到新的提示,其中 tarball 已解壓縮到暫時沙盒中。

查看套件的 README。套件是否使用 ./configurecmake 或其他方式安裝?如果套件使用 ./configure,請刪除註解掉的 cmake 行。

檢查依賴關係

README 可能會告訴您相依性,而 Homebrew 或 macOS 可能已經具備這些相依性。您可以使用 brew search 檢查 Homebrew 相依性。macOS 附帶的一些常見相依性

還有許多其他相依性;請檢查 /usr/lib 以尋找它們。

我們通常會盡量避免在核心 Homebrew 中重複系統函式庫和複雜工具,但我們確實會重複一些常用的工具。

特別的例外是 OpenSSL 和 LibreSSL。使用其中任何一種的項目使用 Homebrew 附帶的等效項目進行建置,而我們的 BrewTestBot 安裝後 audit 會在偵測到您尚未執行此操作時發出警告。

重要: $(brew --prefix)/bin 在配方安裝期間不在 PATH 中。如果您在建置時有相依性,您必須指定它們,而 brew 會將它們新增到 PATH 或建立 Requirement

指定其他配方作為依賴關係

class Foo < Formula
  # ...

  depends_on "httpd" => [:build, :test]
  depends_on xcode: ["9.3", :build]
  depends_on arch: :x86_64
  depends_on "jpeg"
  depends_on macos: :high_sierra
  depends_on "pkg-config"
  depends_on "readline" => :recommended
  depends_on "gtk+" => :optional

  # ...
end

一個 字串(例如 "jpeg")指定配方相依性。

一個 符號(例如 :xcode)指定 Requirement,以將安裝限制在符合特定條件的系統,這些條件可以由一個或多個配方、cask 或其他系統範圍內安裝的軟體(例如 Xcode)滿足。某些 Requirement 也可以採用字串或符號來指定其配方所依賴的最低版本。

一個 雜湊(例如 =>)會將資訊新增到相依性中。給定字串或符號,其值可以是下列值中的其中一個或多個

指定與其他配方的衝突

有時公式之間會發生難以避免或無法使用 keg_only 迴避或解決的嚴重衝突。

次要衝突的一個好範例是 mbedtls 公式,它會傳送並編譯一個「Hello World」可執行檔。這顯然對 mbedtls 的功能而言並非必要,而且由於與熱門的 GNU hello 公式發生衝突會造成過度反應,我們只會在安裝過程中 移除它

pdftohtml 提供了一個嚴重衝突的範例,其中每個列出的公式都會傳送一個名稱相同的二進位檔,而此二進位檔對功能而言至關重要,因此較適合使用 conflicts_with

一般來說,conflicts_with 應作為最後的手段。這是一個相當直接的工具。

無法解決的衝突語法為

conflicts_with "blueduck", because: "yellowduck also ships a duck binary"

配方修訂

在 Homebrew 中,我們有時會接受不包含版本升級的公式更新。這些更新包括資源更新、新的修補程式或修正公式中的安全性問題。

偶爾,這些更新需要強制重新編譯公式本身或其依賴項,以確保公式繼續按預期運作或關閉安全性問題。這種強制重新編譯稱為 revision,並插入在 homepage/url/sha256 區塊下方。

當公式的依賴項無法建置於該依賴項的新版本時,它必須接收 revision。此類失敗的範例在 此問題報告其修正程式 中。

revision 也用於從系統 OpenSSL 移至 Homebrew 提供的 OpenSSL 的公式,而沒有對該公式進行任何其他變更。這可確保使用者不會暴露於過時 OpenSSL 的潛在安全性問題中。在 此提交 中可以看到此範例。

版本配置變更

有時公式有版本配置會變更,使得兩個版本之間的直接比較不再產生正確的結果。例如,專案版本可能是 13,然後決定變更為 1.0.0。由於 13 會由我們的版本控制系統預設轉換為 13.0.0,因此這需要介入。

當公式的版本配置無法辨識新版本為較新版本時,它必須接收 version_scheme。在 此拉取請求 中可以看到此範例。

再次檢查依賴關係

當您已經安裝許多公式時,很容易遺漏常見的依賴項。您可以使用 otool 指令再次確認二進位連結到哪些函式庫(您可能需要使用 xcrun otool

$ otool -L /usr/local/bin/ldapvi
/usr/local/bin/ldapvi:
    /usr/local/opt/openssl/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/local/lib/libglib-2.0.0.dylib (compatibility version 4201.0.0, current version 4201.0.0)
    /usr/local/opt/gettext/lib/libintl.8.dylib (compatibility version 10.0.0, current version 10.2.0)
    /usr/local/opt/readline/lib/libreadline.6.dylib (compatibility version 6.0.0, current version 6.3.0)
    /usr/local/lib/libpopt.0.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
    /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP (compatibility version 1.0.0, current version 2.4.0)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)

指定 macOS 元件作為依賴關係

如果所有平台都需要公式依賴項,但可以由 macOS 附帶的元件處理,請使用 uses_from_macos 指定。在 Linux 上,它會像 depends_on 一樣運作,而在 macOS 上,它會被忽略,除非主機系統早於選用的 since: 參數。

例如,要在 Linux 上需要 bzip2 公式,同時依賴 macOS 上內建的 bzip2

uses_from_macos "bzip2"

要在 Linux 上建置或測試時才需要 perl 公式

uses_from_macos "perl" => [:build, :test]

要在 Linux 和 macOS 12 之前需要 curl 公式

uses_from_macos "curl", since: :monterey

指定 gem、Python 模組、Go 專案等作為依賴關係

Homebrew 沒有封裝已經封裝的特定語言函式庫。這些函式庫應直接從 gem/cpan/pip 等安裝。

Ruby Gem 依賴關係

安裝 gem 依賴項的首選機制是使用 bundler 搭配上游的 Gemfile.lock。這需要上游在他們的 Gemfile.lock 中進行檢查,因此如果他們沒有,最好提出問題並要求他們這樣做。假設他們有一個,這就像

ENV["GEM_HOME"] = libexec
system "bundle", "install", "--without", "development"

從那裡,您可以建置並安裝專案本身

system "gem", "build", "<project>.gemspec"
system "gem", "install", "--ignore-dependencies", "<project>-#{version}.gem"

並安裝任何 bin,並使用以下方式整理他們的 shebang 行

bin.install libexec/"bin/<bin>"
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV.fetch("GEM_HOME", nil))

Python 依賴關係

對於 python,我們使用 resource 作為依賴項,並且有自動化功能可以為您產生這些依賴項。執行 brew update-python-resources <formula> 將自動為您的 Python 應用程式的依賴項新增必要的 resource 節。請注意,如果您傳遞 --python 開關,brew update-python-resources 會由 brew create 自動執行。如果 brew update-python-resources 無法確定正確的 resource 節,homebrew-pypi-poet 是可能有所幫助的第三方替代方案。

所有其他情況

如果其他方法都失敗了,您將需要使用 resource 作為所有其他特定語言依賴項。這需要您同時指定特定版本的 URL 和 sha256 校驗和以確保安全性。以下是一個範例

class Foo < Formula
  # ...
  url "https://example.com/foo-1.0.tar.gz"

  resource "pycrypto" do
    url "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz"
    sha256 "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
  end

  def install
    resource("pycrypto").stage { system "python", *Language::Python.setup_install_args(libexec/"vendor") }
  end
end

jrnl 是執行良好的範例程式碼。最終結果表示使用者不必使用 pip 或 Python,只要執行 jrnl 即可。

安裝配方

HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug foo

--debug 會在建置失敗時要求您開啟互動式外殼程式,以便嘗試找出錯誤原因。

查看例如 ./configure 輸出的頂端。有些組態指令碼無法辨識例如 --disable-debug。如果您看到相關警告,請從程式碼中移除該選項。

新增測試到配方

將有效的測試加入程式碼的 test do 區塊。這會由 brew test fooBrewTestBot 執行。

test do 區塊會自動建立暫時目錄並切換至該目錄,並在執行後刪除該目錄。您可以使用 Pathname 函數 testpath 存取此 Pathname。環境變數 HOME 會在 test do 區塊中設定為 testpath

我們需要不需要任何使用者輸入,就能測試應用程式基本功能的測試。例如 foo build-foo input.foo 是良好的測試,而 foo --versionfoo --help 則是糟糕的測試(儘管它們廣泛使用)。不過,糟糕的測試總比沒有測試好。

請參閱 cmake 程式碼,以取得良好測試的範例。它會將基本的 CMakeLists.txt 檔案寫入測試目錄,然後呼叫 CMake 來產生 Makefiles。此測試會檢查 CMake 是否在基本操作期間發生區段錯誤等問題。

您可以使用 程式碼斷言 中的 assert_equalassert_match 檢查輸出是否符合預期,例如 envv 程式碼中的此範例

assert_equal "mylist=A:C; export mylist", shell_output("#{bin}/envv del mylist B").strip

您也可以檢查是否已建立輸出檔案

assert_predicate testpath/"output.txt", :exist?

針對特定案例的一些建議

test do
  resource "testdata" do
    url "https://example.com/input.foo"
    sha256 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
  end

  resource("testdata").stage do
    assert_match "OK", shell_output("#{bin}/foo build-foo input.foo")
  end
end

手冊

Homebrew 預期在 #{prefix}/share/man/... 中找到手冊頁面,而不是在 #{prefix}/man/... 中。

有些軟體會安裝到 man 而不是 share/man,因此請檢查輸出,並在需要時將 "--mandir=#{man}" 新增到 ./configure 行中。

注意事項

如果 Homebrew 封裝有任何特定問題(與從其他來源安裝軟體的方式相比),可以在公式中新增 caveats 區塊來警告使用者。這可以指出非標準的安裝路徑,例如 ruby 公式中的這個範例

==> Caveats
By default, binaries installed by gem will be placed into:
  /usr/local/lib/ruby/gems/bin

You may want to add this to your PATH.

關於命名的一點建議

為公式命名時,請採用專案行銷產品的方式。因此它是 pkg-config,而不是 pkgconfigsdl_mixer,而不是 sdl-mixersdlmixer

唯一的例外是像「Apache Ant」之類的東西。Apache 將「Apache」放在所有東西前面,但我們使用公式名稱 ant。我們只在像 gnuplot(因為它是名稱的一部分)和 gnu-go(因為每個人都稱它為「GNU Go」——沒有人只稱它為「Go」)這樣的案例中包含前綴。這個字「Go」太普遍了,而且有太多它的實作。

如果您不確定名稱,請查看它的首頁、維基百科頁面和 Debian 稱它為什麼

當 Homebrew 已經有一個稱為 foo 的公式時,我們通常不接受將該公式替換為其他也稱為 foo 的東西的請求。這是為了避免混淆和出乎用戶意料。

當兩個公式共用一個上游名稱時,例如 AESCryptAES Crypt,較新的公式通常必須調整其名稱以避免與目前的公式衝突。

如果您仍然不確定,那就提交吧。我們會套用一些任意規則並做出決定 😉。

當匯入類別時,Homebrew 將需要公式,然後建立類別的實例。它假設公式名稱可以使用 regexp 直接轉換為類別名稱。規則很簡單

因此,如果您變更類別的名稱,您也必須重新命名檔案。檔案名稱應全部使用小寫,而類別名稱應為嚴格的 CamelCase 等效名稱,例如公式 gnu-gosdl_mixer 變成類別 GnuGoSdlMixer,即使它們名稱的一部分是縮寫。

透過在 tap 根目錄的 Aliases 目錄中建立符號連結來新增別名。

稽核配方

您可以執行 brew audit --strict --online 來測試公式是否符合 Homebrew house 風格,它大致基於 Ruby 風格指南audit 指令包含尾隨空白、某些來源主機的首選 URL 以及許多其他風格問題的警告。在提交前修正這些警告將使每個人的流程快很多。

提交給 Homebrew 的新公式應執行 brew audit --new --formula foo。此指令由 BrewTestBot 在新的提交中執行,作為自動建置和測試流程的一部分,並強調比標準稽核更多的潛在問題。

使用 brew info 並檢查 Homebrew 從 URL 猜測的版本是否正確。如果不是,請新增明確的 version

提交

一切都建立在 Git 上,因此很容易貢獻

brew update # required in more ways than you think (initialises the Homebrew/brew Git repository if you don't already have it)
cd "$(brew --repository homebrew/core)"
# Create a new git branch for your formula so your pull request is easy to
# modify if any changes come up during review.
git checkout -b <some-descriptive-name> origin/master
git add Formula/f/foo.rb
git commit

Git commit 訊息的既定標準是

在 Homebrew,我們需要在最前面放上公式名稱,如下所示:foobar 7.3 (new formula)

這看起來可能很短,但你會發現強制自己總結 commit 會鼓勵你簡潔扼要。如果你無法在 50 到 80 個字元內總結,你可能試圖將兩個 commit 當成一個 commit。如需更詳細的說明,請閱讀 Tim Pope 出色的部落格文章,關於 Git Commit 訊息的注意事項

簡單版本更新所需的 commit 訊息格式為 foobar 7.3,而修正的格式為 foobar: fix flibble matrix.。請將你的 commit 壓縮成具有此訊息格式的單一 commit,否則你的 PR 將會被我們的自動壓縮工作流程取代。

確保你參考任何相關的 GitHub 問題,例如 Closes #12345 在 commit 訊息中。Homebrew 的歷史是未來貢獻者在試圖了解他們感興趣的公式的當前狀態時會首先查看的內容。

推送

現在你只需要將你的 commit 推送到 GitHub。

如果你尚未 fork Homebrew,請前往 Homebrew/homebrew-core 儲存庫並按下 Fork 按鈕

如果你已經在 GitHub 上 fork 了 Homebrew,那麼你可以手動推送(只要確保你已從 Homebrew/homebrew-core 主程式中提取)

git push https://github.com/myname/homebrew-core/ <what-you-named-your-branch>

現在,為你的變更開啟一個 pull request

便利工具

訊息傳遞

提供三個命令來向使用者顯示資訊訊息

當你需要出於任何原因優雅地退出公式時,請使用 odie。例如

if build.head?
  lib_jar = Dir["cfr-*-SNAPSHOT.jar"]
  doc_jar = Dir["cfr-*-SNAPSHOT-javadoc.jar"]
  odie "Unexpected number of artifacts!" if (lib_jar.length != 1) || (doc_jar.length != 1)
end

標準參數

對於使用知名建置系統的任何公式,都會有應該在編譯期間傳遞的參數,以使其建置符合 Homebrew 標準。這些已收集到一組 std_*_args 方法中(例如 std_configure_argsstd_cmake_args,如 brew create 輸出 中所示),這些方法會設定建置類型和安裝路徑,以及任何其他適用的選項。

這些方法大部分都接受參數來自訂其輸出。例如,要將安裝前置詞設定為 libexec,以供 configurecmake

system "./configure", *std_configure_args(prefix: libexec)
system "cmake", "-S", ".", "-B", "build", *std_cmake_args(install_prefix: libexec)

bin.install "foo"

您會在某些公式中看到類似這樣的內容。這會將檔案 foo 移到公式的 bin 目錄(/usr/local/Cellar/pkg/0.1/bin)並使其可執行(chmod 0555 foo)。

您也可以在安裝過程中重新命名檔案。這對於為二進位檔新增前置詞(否則會與其他公式產生衝突)或移除檔案副檔名非常有用。例如,要將 foo.py 安裝到公式的 bin 目錄(/usr/local/Cellar/pkg/0.1/bin)中,只要將 foo.py 設為 foo 即可

bin.install "foo.py" => "foo"

inreplace

inreplace 是一個方便的函式,可以用來編輯檔案。例如

inreplace "path", before, after

beforeafter 可以是字串或正規表示式。如果您需要在檔案中進行多重取代,則應使用區塊形式

inreplace "path" do |s|
  s.gsub!(/foo/, "bar")
  s.gsub! "123", "456"
end

請務必修改 s!此區塊會忽略傳回值。

inreplace 應在修補上游永遠不會接受的項目時使用,例如讓軟體的建置系統尊重 Homebrew 的安裝層級。如果影響 Homebrew 和 MacPorts(也就是 macOS 專屬)的項目,則應轉換成上游提交的修補程式。

如果您需要修改 Makefile 中的變數,請嘗試將它們傳遞為 make 的引數,而不是在 inreplace 中使用 change_make_var!

system "make", "target", "VAR2=value1", "VAR2=value2", "VAR3=values can have spaces"
system "make", "CC=#{ENV.cc}", "PREFIX=#{prefix}"

請注意,如果您使用 system 的多個引數形式,則值可以包含未跳脫的空白。

修補程式

雖然通常應避免使用 patch,但有時暫時有必要。

在進行 patch(例如修正標頭檔包含、修正編譯器警告等)時,首先要檢查上游專案是否已知悉此問題。如果不是,請提交錯誤報告和/或提交您的修補程式以供納入。我們有時仍可能在您提交上游之前接受您的修補程式,但透過開始著手修正上游問題,您可以減少我們必須攜帶修補程式的時間。

務必使用程式碼註解來證明 patch 的合理性!否則,沒有人會知道何時可以安全移除修補程式,或在更新配方時可以安全保留修補程式。註解應包含相關上游問題的連結。

可以使用資源樣式區塊宣告外部 patch

patch do
  url "https://example.com/example_patch.diff"
  sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end

假設剝離層級為 -p1。可以使用符號引數覆寫此層級。

patch :p0 do
  url "https://example.com/example_patch.diff"
  sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end

patch 可以宣告在 stablehead 區塊中。請務必使用區塊而非條件式,也就是使用 stable do ... end 而不是 if build.stable? then ... end

stable do
  # ...

  patch do
    url "https://example.com/example_patch.diff"
    sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
  end
end

內嵌 (END) 修補程式宣告方式如下

patch :DATA
patch :p0, :DATA

檔案結尾包含修補程式資料

__END__
diff --git a/foo/showfigfonts b/foo/showfigfonts
index 643c60b..543379c 100644
--- a/foo/showfigfonts
+++ b/foo/showfigfonts
@@ -14,6 +14,7 @@
…

也可以傳遞字串來內嵌修補程式。這樣可以提供多個內嵌修補程式,同時只讓其中一些有條件。

patch :p0, "..."

在內嵌修補程式中,字串「HOMEBREW_PREFIX」會在套用修補程式前替換為常數 HOMEBREW_PREFIX 的值。

建立 diff

HOMEBREW_NO_INSTALL_FROM_API=1 brew install --interactive --git foo
# (make some edits)
git diff | pbcopy
brew edit foo

現在只要貼到 __END__ 之後的公式即可。

對於某些編輯器,使用 git diff >> path/to/your/formula/foo.rb 而不是 git diff | pbcopy 可能有助於確保修補程式未變更,例如移除空白、變更縮排等。

進階配方技巧

請參閱 公式 API 以取得公式中可用的方法完整清單。如果仍有不清楚之處,通常可以透過 grepping $(brew --repository homebrew/core) 目錄來找出範例。如果您認為這份文件有幫助,請提交拉取請求來修改!

處理不同的系統組態

公式通常需要不同的相依性、資源、修補程式、衝突、不建議使用或 keg_only 狀態,具體取決於不同的作業系統和架構。在這些情況下,元件可以巢狀在 on_macoson_linuxon_armon_intel 區塊中。例如,以下是將 gcc 新增為僅限 Linux 的相依性的方法

on_linux do
  depends_on "gcc"
end

元件也可以宣告為特定 macOS 版本或版本範圍。例如,若要僅在 High Sierra 上宣告相依性,請將 depends_on 呼叫巢狀在 on_high_sierra 區塊中。將 :or_older:or_newer 參數新增到 on_high_sierra 方法,以將相依性新增到符合條件的所有 macOS 版本。例如,若要在 Mojave 和所有更新的 macOS 版本上將 gettext 新增為建置相依性,請使用

on_mojave :or_newer do
  depends_on "gettext" => :build
end

有時,在特定 macOS 版本 Linux 上需要相依性。在這些情況下,可以使用特殊的 on_system 方法

on_system :linux, macos: :sierra_or_older do
  depends_on "gettext" => :build
end

若要檢查多個條件,請巢狀對應的區塊。例如,以下程式碼在 ARM macOS 上新增 gettext 建置相依性

on_macos do
  on_arm do
    depends_on "gettext" => :build
  end
end

def installtest do

def installtest do 中,請勿使用這些 on_* 方法。請改用 if 陳述式和下列條件

請參閱 icoutils 公式以取得範例。

livecheck 區塊

brew livecheck 無法辨識公式的版本時,我們可以使用 livecheck 區塊控制其行為。以下是一個簡單的範例,用於檢查頁面中包含類似 example-1.2.tar.gz 檔案名稱的連結

livecheck do
  url "https://www.example.com/downloads/"
  regex(/href=.*?example[._-]v?(\d+(?:\.\d+)+)\.t/i)
end

有關 url/regex 指南和額外的 livecheck 區塊範例,請參閱 brew livecheck 文件。有關 livecheck 區塊中所使用方法的更多技術資訊,請參閱 Livecheck 類別文件

不穩定版本 (head)

公式可以使用 head 指定上游專案開發尖端來源(例如 master/main/trunk)的替代下載,可以在安裝時透過傳遞 --HEAD 來啟用。指定的方式與 url 相同

class Foo < Formula
  # ...
  head "https://github.com/some/package.git", branch: "main" # the default is "master"
end

您也可以在 head do 區塊中組合 URL 和任何 head 特定的依賴項和資源。

class Foo < Formula
  # ...

  head do
    url "https://svn.code.sf.net/p/project/code/trunk"
    depends_on "pkg-config" => :build
  end
end

您可以在 install 方法中使用 build.head? 測試 head 是否正在使用 build.head? 建置。

URL 下載策略

當解析下載網址時,Homebrew 會自動偵測它指向的資源類型,無論是封存檔(例如 tarball、zip)或版本控制儲存庫(例如 Git、SVN、Mercurial),並選擇適當的下載策略。有些策略可以傳遞其他選項來變更下載的內容。例如,若要使用儲存庫中的特定提交、標籤或分支,請使用 urlhead 搭配 :tag:revision:revision:branch 選項,如下所示

class Foo < Formula
  # ...
  url "https://github.com/some/package.git",
      tag:      "v1.6.2",
      revision: "344cd2ee3463abab4c16ac0f9529a846314932a2"
end

如果無法推論,請使用 using: 選項指定要使用 Homebrew 的哪種內建下載策略。例如

class Nginx < Formula
  desc "HTTP(S) server and reverse proxy, and IMAP/POP3 proxy server"
  homepage "https://nginx.dev.org.tw/"
  url "https://nginx.dev.org.tw/download/nginx-1.23.2.tar.gz", using: :homebrew_curl
  sha256 "a80cc272d3d72aaee70aa8b517b4862a635c0256790434dbfc4d618a999b0b46"
  head "https://hg.nginx.org/nginx/", using: :hg
end

Homebrew 提供這些匿名下載策略。

:using 下載策略
:bzr BazaarDownloadStrategy
:curl CurlDownloadStrategy
:cvs CVSDownloadStrategy
:fossil FossilDownloadStrategy
:git GitDownloadStrategy
:hg MercurialDownloadStrategy
:homebrew_curl HomebrewCurlDownloadStrategy
:nounzip NoUnzipCurlDownloadStrategy
:post CurlPostDownloadStrategy
:svn SubversionDownloadStrategy

如果您需要更進一步控制檔案下載和分段的方式,您可以建立自訂下載策略,並使用 :using 選項指定它

class MyDownloadStrategy < SomeHomebrewDownloadStrategy
  def fetch(timeout: nil, **options)
    opoo "Unhandled options in #{self.class}#fetch: #{options.keys.join(", ")}" unless options.empty?

    # downloads output to `temporary_path`
  end
end

class Foo < Formula
  url "something", using: MyDownloadStrategy
end

編譯器選擇

有時,使用特定編譯器時,套件會無法建置。由於最近的 Xcode 版本 不再包含 GCC 編譯器,因此我們無法強制使用 GCC。相反地,宣告此項的正確方式是使用 fails_with DSL 方法。建構正確的 fails_with 區塊會記錄已知會導致編譯失敗的最新編譯器建置版本,以及失敗的原因。例如

fails_with :clang do
  build 211
  cause "Miscompilation resulting in segfault on queries"
end

fails_with :gcc do
  version "5" # fails with GCC 5.x and earlier
  cause "Requires C++17 support"
end

fails_with gcc: "7" do
  version "7.1" # fails with GCC 7.0 and 7.1 but not 7.2, or any other major GCC version
  cause <<-EOS
    warning: dereferencing type-punned pointer will break strict-aliasing rules
    Fixed in GCC 7.2, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42136
  EOS
end

對於 :clangbuild 會取得一個整數(您可以在 brew --config 輸出中找到此數字),而 :gcc 則只使用 version,它會取得一個字串來表示最後一個有問題的 GCC 版本,或將主版本參數與 version 結合使用,以找出特定 GCC 版本的範圍。 cause 會取得一個字串,建議使用 here 文件來改善可讀性,並允許更全面的文件說明。

fails_with 宣告可以用在 :gcc:llvm:clang 中的任何一個。Homebrew 會使用這些資訊來選擇可用的編譯器(如果有)。

僅移動部分檔案

當在安裝函式中執行你的程式碼時,目前的工作目錄會設定為已解壓縮的 tarball。這使得移動一些檔案變得容易

prefix.install "file1", "file2"

或所有檔案

prefix.install Dir["output/*"]

或僅移動 tarball 的頂層檔案,例如 README、LICENSE 等。

prefix.install_metafiles

通常我們希望你明確指出需要安裝哪些檔案或目錄,而不是安裝所有檔案。

目錄位置的變數

名稱 預設路徑 範例
HOMEBREW_PREFIX $(brew --prefix) 的輸出 /usr/local
前綴 #{HOMEBREW_PREFIX}/Cellar/#{name}/#{version} /usr/local/Cellar/foo/0.1
opt_prefix #{HOMEBREW_PREFIX}/opt/#{name} /usr/local/opt/foo
bin #{prefix}/bin /usr/local/Cellar/foo/0.1/bin
doc #{prefix}/share/doc/#{name} /usr/local/Cellar/foo/0.1/share/doc/foo
include #{prefix}/include /usr/local/Cellar/foo/0.1/include
info #{prefix}/share/info /usr/local/Cellar/foo/0.1/share/info
lib #{prefix}/lib /usr/local/Cellar/foo/0.1/lib
libexec #{prefix}/libexec /usr/local/Cellar/foo/0.1/libexec
man #{prefix}/share/man /usr/local/Cellar/foo/0.1/share/man
man[1-8] #{prefix}/share/man/man[1-8] /usr/local/Cellar/foo/0.1/share/man/man[1-8]
sbin #{prefix}/sbin /usr/local/Cellar/foo/0.1/sbin
share #{prefix}/share /usr/local/Cellar/foo/0.1/share
pkgshare #{prefix}/share/#{name} /usr/local/Cellar/foo/0.1/share/foo
elisp #{prefix}/share/emacs/site-lisp/#{name} /usr/local/Cellar/foo/0.1/share/emacs/site-lisp/foo
frameworks #{prefix}/Frameworks /usr/local/Cellar/foo/0.1/Frameworks
kext_prefix #{prefix}/Library/Extensions /usr/local/Cellar/foo/0.1/Library/Extensions
zsh_function #{prefix}/share/zsh/site-functions /usr/local/Cellar/foo/0.1/share/zsh/site-functions
fish_function #{prefix}/share/fish/vendor_functions /usr/local/Cellar/foo/0.1/share/fish/vendor_functions
bash_completion #{prefix}/etc/bash_completion.d /usr/local/Cellar/foo/0.1/etc/bash_completion.d
zsh_completion #{prefix}/share/zsh/site-functions /usr/local/Cellar/foo/0.1/share/zsh/site-functions
fish_completion #{prefix}/share/fish/vendor_completions.d /usr/local/Cellar/foo/0.1/share/fish/vendor_completions.d
etc #{HOMEBREW_PREFIX}/etc /usr/local/etc
pkgetc #{HOMEBREW_PREFIX}/etc/#{name} /usr/local/etc/foo
var #{HOMEBREW_PREFIX}/var /usr/local/var
buildpath 系統中某處的暫時目錄 /private/tmp/[formula-name]-0q2b/[formula-name]

例如,這些路徑可用於以下程式碼中

bin.install Dir["output/*"]

將二進位檔移至 Cellar 中正確的位置,以及

man.mkpath

建立說明頁面位置的目錄結構。

若要將說明頁面安裝到特定位置,請使用 man1.install "foo.1", "bar.1"man2.install "foo.2" 等。

請注意,在 Homebrew 的背景下,libexec 是保留給公式私下使用的,因此不會連結到 HOMEBREW_PREFIX

檔案層級作業

你可以使用 Ruby 的 FileUtils 所提供的檔案工具程式。這些程式包含在 Formula 類別中,因此你不必使用 FileUtils. 前綴即可使用它們。

建立符號連結時,請特別注意確保它們是相對符號連結。這有助於建立可重新定位的 Bottle。例如,若要在 bin 中建立符號連結至 libexec 中的可執行檔,請使用

bin.install_symlink libexec/"name"

而非

ln_s libexec/"name", bin

install_symlink 建立的符號連結保證是相對的。只有在給定相對路徑時,ln_s 才會產生相對符號連結。

Ruby 的 Pathname 提供的幾個其他工具程式可以簡化一些常見的操作。

改寫指令碼 shebang

有些公式會安裝使用 Python 或 Perl 等直譯語言撰寫的可執行指令碼。Homebrew 提供 rewrite_shebang 方法來改寫指令碼的 shebang。這會將指令碼的原始直譯器路徑替換為公式所依賴的路徑。這可確保在執行時使用正確的直譯器。如果建置系統已處理此問題(例如,通常使用 pip 或 Perl ExtUtils::MakeMaker),則不需要此方法。

例如,icdiff 公式使用此工具程式。請注意,必須將此工具程式包含在公式中;例如,對於 Python,必須使用 include Language::Python::Shebang

新增選用步驟

注意: option 不允許在 Homebrew/homebrew-core 中使用,因為 CI 沒有對其進行測試。

如果您想新增 option

class Yourformula < Formula
  # ...
  url "https://example.com/yourformula-1.0.tar.gz"
  sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
  # ...
  option "with-ham", "Description of the option"
  option "without-spam", "Another description"

  depends_on "bar" => :recommended
  depends_on "foo" => :optional # automatically adds a with-foo option # automatically adds a without-bar option
  # ...
end

然後定義 option 的效果

if build.with? "ham"
  # note, no "with" in the option name (it is added by the build.with? method)
end

if build.without? "ham"
  # works as you'd expect. True if `--without-ham` was given.
end

option 名稱應加上 withwithout 字首。例如,執行測試套件的選項應命名為 --with-test--with-check,而不是 --test,而啟用共享函式庫的選項應命名為 --with-shared,而不是 --shared--enable-shared。請參閱 替代的 ffmpeg 公式以取得範例。

不是 build.with?build.without?option 應使用 deprecated_option 標示為已棄用。請參閱 wget 公式以取得歷史範例。

在安裝後執行指令

任何不一定是安裝程序一部分的初始化步驟,都可以放在 post_install 區塊中,例如設定指令或資料目錄建立。這個區塊可以用 brew postinstall <formula> 重新個別執行。

class Foo < Formula
  # ...
  url "https://example.com/foo-1.0.tar.gz"

  def post_install
    rm_f pkgetc/"cert.pem"
    pkgetc.install_symlink Formula["ca-certificates"].pkgetc/"cert.pem"
  end
  # ...
end

在上述範例中,libressl 公式會用一個符號連結取代其證書庫存清單,指向 ca-certificates 公式的證書庫存清單。

處理應在配方升級時持續存在的檔案

例如,Ruby 1.9 的寶石應該安裝到 var/lib/ruby/,這樣在升級 Ruby 時就不需要重新安裝寶石。你通常可以用符號連結技巧,或(理想情況下)一個設定選項來做到這一點。

另一個範例會是組態檔案,這些檔案不應該在套件升級時被覆寫。如果你在安裝後發現需要持續存在的組態檔案並未被複製,而是從 Cellar 符號連結/usr/local/etc/,這通常可以透過傳遞一個適當的引數給套件的設定指令碼來修正。那個引數會根據給定的套件的設定指令碼和/或 Makefile 而有所不同,但一個範例可能是:--sysconfdir=#{etc}

服務檔案

有兩種方法可以將 launchd plist 和 systemd 服務加入一個公式,這樣 brew services 就可以選取它們

  1. 如果套件已經提供一個服務檔案,公式可以透過名稱來參照它

    service do
      name macos: "custom.launchd.name",
        linux: "custom.systemd.name"
    end
    

    為了找到檔案,我們會在 launchd 服務名稱後面加上 .plist,在 systemd 服務名稱後面加上 .service

  2. 如果公式沒有提供服務檔案,你可以使用以下節段產生一個

    # 1. An individual command
    service do
      run opt_bin/"script"
    end
    
    # 2. A command with arguments
    service do
      run [opt_bin/"script", "--config", etc/"dir/config.yml"]
    end
    
    # 3. OS specific commands (If you omit one, the service file won't get generated for that OS.)
    service do
      run macos: [opt_bin/"macos_script", "standalone"],
       linux: var/"special_linux_script"
    end
    

服務區塊方法

此表格列出您可以在 service 區塊中設定的選項。 runname 欄位必須在服務區塊中定義。如果 name 在沒有 run 的情況下定義,則 Homebrew 就不會嘗試根據這些欄位變更套件提供的服務檔案。 run 欄位指出要執行的指令,指示 Homebrew 使用區塊中設定的選項建立服務描述檔案,因此在使用 namerequire_root 以外的欄位之前,此欄位是必要的。

方法 預設值 macOS Linux 說明
run - 要執行的指令:包含引數或路徑的陣列
run_type :immediate 服務類型::immediate:interval:cron
interval - 控制啟動間隔,:interval 類型需要
cron - 控制觸發時間,:cron 類型需要
keep_alive 設定情境,服務將在其中讓程序持續執行
launch_only_once 指令是否只應執行一次
require_root 服務是否需要 root 存取權。如果為 true,Homebrew 會提示在各種情況下使用 sudo,但不會強制執行
environment_variables - 要設定的變數雜湊
working_dir - 要從中執行的目錄
root_dir - 要作為程序 chroot 的目錄
input_path - 要作為程序輸入的路徑
log_path - 要寫入 stdout 的路徑
error_log_path - 要寫入 stderr 的路徑
restart_delay - 重新啟動程序前要延遲的秒數
process_type - no-op 要管理的程序類型::background:standard:interactive:adaptive
macos_legacy_timers - no-op 除非設定此選項,否則 launchd 工作建立的計時器會合併
sockets - no-op 作為服務存取點建立的 socket
名稱 - 在 macOS 上的 launchd 服務名稱和/或在 Linux 上的 systemd 服務名稱的雜湊。如果沒有此雜湊,Homebrew 會為服務檔案產生預設名稱

對於在啟動後保持運作的服務,您可以使用預設 run_type

service do
  run [opt_bin/"beanstalkd", "test"]
  keep_alive true
  run_type :immediate # This should be omitted since it's the default
end

如果服務需要在區間中執行,請使用 run_type :interval 並指定區間

service do
  run [opt_bin/"beanstalkd", "test"]
  run_type :interval
  interval 500
end

如果服務需要在特定時間執行,請使用 run_type :cron 並使用 crontab 語法指定時間

service do
  run [opt_bin/"beanstalkd", "test"]
  run_type :cron
  cron "5 * * * *"
end

環境變數可以用雜湊設定。對於 PATH,有輔助方法 std_service_path_env,它會傳回 #{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin,以便服務可以找到其他 brew 安裝的指令。

service do
  run opt_bin/"beanstalkd"
  environment_variables PATH: std_service_path_env
end

keep_alive 選項

標準選項會讓服務保持運作,不論任何狀態或情況

service do
  run [opt_bin/"beanstalkd", "test"]
  keep_alive true # or false
end

與上面相同,但以雜湊形式

service do
  run [opt_bin/"beanstalkd", "test"]
  keep_alive always: true
end

在服務以非零回傳碼退出前保持運作

service do
  run [opt_bin/"beanstalkd", "test"]
  keep_alive successful_exit: true
end

僅在工作中斷時保持運作

service do
  run [opt_bin/"beanstalkd", "test"]
  keep_alive crashed: true
end

只要檔案存在就保持運作

service do
  run [opt_bin/"beanstalkd", "test"]
  keep_alive path: "/some/path"
end

sockets 格式

sockets 方法接受格式化的 socket 定義為 <type>://<host>:<port>

請注意,socket 預設會在 IPv4 和 IPv6 位址上存取。

如果您只需要一個 socket,而且不關心名稱(預設為 listeners

service do
  run [opt_bin/"beanstalkd", "test"]
  sockets "tcp://127.0.0.1:80"
end

如果您需要多個 socket 和/或您想要指定名稱

service do
  run [opt_bin/"beanstalkd", "test"]
  sockets http: "tcp://0.0.0.0:80", https: "tcp://0.0.0.0:443"
end

使用環境變數

Homebrew 具有多個層級的環境變數過濾,會影響哪些變數可供公式使用。

首先,Homebrew 執行的整體 環境會經過過濾,以避免環境污染中斷原始碼建置。特別是,此程序會過濾所有變數(僅允許選定的變數清單),並允許任何以 HOMEBREW_ 為字首的變數。具體實作可在 bin/brew 中找到。

第二層過濾 會移除敏感的環境變數(例如金鑰、密碼或權杖等憑證),以防止惡意的子程序取得這些變數。這會防止任何此類變數傳遞到公式的 Ruby 程式碼,因為這些變數會在呼叫之前經過過濾。具體實作可在 ENV.clear_sensitive_environment! 方法 中找到。

總之,任何供公式使用的環境變數都需要符合這些過濾規則才能使用。

在安裝期間設定環境變數

您可以在公式的 installtest 區塊中使用 ENV["VARIABLE_NAME"] = "VALUE" 來設定環境變數。範例可以在 csound 公式中看到。

也可以使用 with_env 方法暫時設定環境變數;在呼叫該方法時定義的任何變數,都會在區塊結束時還原為其原始值。範例可以在 gh 公式中看到。

還有 ENV 輔助方法可供許多常見的環境變數設定和擷取作業使用,例如

完整的清單可在 SharedEnvExtensionSuperenv 模組文件檔中找到。

棄用和停用配方

請參閱我們的 廢棄、停用和移除公式 文件檔,以進一步了解如何以及何時廢棄或停用公式。

更新配方

當軟體的新版本發布時,請使用 brew bump-formula-pr 自動更新 urlsha256,移除任何 revision 行,並提交拉取請求。請參閱我們的 如何開啟 Homebrew 拉取請求 文件檔以進一步了解。

新配方的疑難排解

版本偵測失敗

Homebrew 會嘗試從 url 自動判斷 version 以避免重複。如果 tarball 有不尋常的名稱,您可能需要手動指定 version

錯誤的 makefile

如果專案的 makefile 無法並行執行,請嘗試將這些行新增至公式的 install 方法以解除並行化

ENV.deparallelize
system "make" # separate compilation and installation steps
system "make", "install"

如果這樣可以解決問題,請向上游專案開啟問題,以便我們為所有人修復它。

仍無法運作?

查看 MacPorts 和 Fink 的作法

brew search --macports foo
brew search --fink foo

Superenv 備註

superenv 是我們的「超級環境」,它透過移除 /usr/local/bin 和所有對建置而言並非必要的使用者 PATH 來隔離建置。它這麼做是因為使用者的 PATH 通常充斥著會中斷建置的內容。 superenv 也會從傳遞給 clang/gcc 的指令中移除不良標記,並插入其他標記(例如,所有 keg_only 相依項都會新增至 -I-L 標記)。

如果您在您新配方的本機 Homebrew 建置中看到 Operation not permitted 錯誤,這是因為您的新配方嘗試寫入沙盒區域外的磁碟。這在 macOS 上是由 sandbox-exec 強制執行的。

Fortran

有些軟體需要 Fortran 編譯器。這可以透過將 depends_on "gcc" 新增至配方來宣告。

MPI

需要 MPI 的套件應該透過將 depends_on "open-mpi" 新增至配方來使用 OpenMPI,而不是 MPICH。這些套件有衝突,而且提供相同的標準化介面。選擇預設實作並要求採用它,讓軟體可以連結到依賴 MPI 的多個函式庫,而不會因為不同的 MPI 執行時間而產生預期外的不相容性。

線性代數函式庫

需要 BLAS/LAPACK 線性代數介面的套件應該透過將 depends_on "openblas" 新增至配方,以及(如果使用 CMake 建置)將 -DBLA_VENDOR=OpenBLAS 傳遞給 CMake,來連結到 OpenBLAS,而不是 Apple 的 Accelerate 架構或預設參考 lapack 實作。Apple 的 BLAS/LAPACK 實作已過時,而且可能會造成難以除錯的問題。參考 lapack 配方沒問題,儘管它沒有積極維護或調整。

如何重新開始(重設至上游 master

您是否在 Git 中製造了一團糟,導致您無法建立您想提交給我們的 commit?您可能想要考慮從頭開始。您可以透過執行下列指令,重設您對 Homebrew master 分支的變更

git checkout -f master
git reset --hard origin/master
Fork me on GitHub