配方手册

配方是用 Ruby 编写的软件包定义。可以使用 brew create <URL> 创建配方,其中 <URL> 是 zip 或 tarball,可以使用 brew install <formula> 安装配方,可以使用 brew install --debug --verbose <formula> 调试配方。配方使用 配方 API,该 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-only 如果公式未符号链接到 Homebrew 的前缀,则该公式keg-only openjdk 公式
opt 前缀 指向keg 活动版本的符号链接 /usr/local/opt/foo
Cellar 包含一个或多个命名rack 的目录 /usr/local/Cellar
Caskroom 包含一个或多个命名cask 的目录 /usr/local/Caskroom
外部命令 在 Homebrew/brew GitHub 存储库外部定义的 brew 子命令 brew 别名
tap 公式cask 和/或外部命令 的目录(通常为 Git 存储库) /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core
bottle 预构建的keg,倒入Cellarrack 中,而不是从上游源构建 qt--6.5.1.ventura.bottle.tar.gz
tab 有关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 开始,配方以 JSON 格式从 https://formulae.brew.sh.cn 下载,后者由 Homebrew/formulae.brew.sh 的计划任务从 Homebrew/homebrew-core 存储库的 master 分支自动重新生成。

Homebrew 将配方安装到 $(brew --cellar) 中的酒窖,然后将部分安装符号链接到 $(brew --prefix)(例如 /opt/homebrew)中的前缀,以便其他程序可以看到正在发生的事情。我们建议对酒窖中的几个木桶运行 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 环境中或在任何 installreinstallupgrade 命令之前设置 HOMEBREW_NO_INSTALL_FROM_API=1,以强制 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 中总结配方在 description 中的作用。请注意,打印时 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

一个 String(例如 "jpeg")指定一个公式依赖项。

一个 Symbol(例如 :xcode)指定一个 Requirement,以将安装限制在满足特定条件的系统中,这些条件可以通过一个或多个公式、cask 或其他系统范围安装的软件(例如 Xcode)来满足。一些 Requirement 还可以采用一个字符串或符号,指定公式所依赖的最低版本。

一个 Hash(例如 =>)向依赖项添加信息。给定一个字符串或符号,该值可以是以下一个或多个值

指定与其他配方的冲突

有时,公式之间存在无法避免或通过 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 和 pre-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 如果构建失败,会要求你打开一个交互式 shell,以便你可以尝试找出出错的原因。

检查 ./configure 输出的顶部。一些配置脚本无法识别 --disable-debug。如果你看到有关它的警告,请从公式中删除该选项。

向配方添加测试

向公式的 test do 块添加一个有效的测试。这将由 brew test fooBrewTestBot 运行。

test do 块会自动创建并更改到一个临时目录,该目录会在运行后删除。你可以使用 testpath 函数访问此 Pathname。环境变量 HOMEtest do 块中被设置为 testpath

我们想要不需要任何用户输入且测试应用程序基本功能的测试。例如,foo build-foo input.foo 是一个好的测试,而(尽管它们被广泛使用)foo --versionfoo --help 是不好的测试。但是,一个不好的测试总比没有测试好。

请参阅 cmake 公式,了解一个好的测试示例。它将一个基本的 CMakeLists.txt 文件写入测试目录,然后调用 CMake 来生成 Makefile。此测试检查 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 直接转换为类名称来执行此操作。规则很简单

因此,如果你更改了类的名称,还必须重命名文件。文件名应全部小写,类名应为严格的驼峰式等效形式,例如配方 gnu-gosdl_mixer 变为类 GnuGoSdlMixer,即使其名称的一部分是首字母缩写。

通过在 tap 根目录中的 Aliases 目录中创建符号链接来添加别名。

审核配方

你可以运行 brew audit --strict --online 来测试配方是否符合 Homebrew 的内部风格,该风格大致基于 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 提交消息的既定标准是

在 Homebrew 中,我们要求预先提供配方名称,如下所示:foobar 7.3 (new formula)

这看起来可能非常简短,但你会发现,强迫自己总结提交会鼓励你保持原子性和简洁性。如果你无法用 50 到 80 个字符总结,你可能试图将两次提交作为一次提交。有关更全面的解释,请阅读 Tim Pope 出色的博客文章,关于 Git 提交消息的说明

简单版本更新所需的提交消息格式为 foobar 7.3,修复所需的格式为 foobar: fix flibble matrix.。请将你的提交压缩成一条,并采用此消息格式,否则你的 PR 将被我们的自动压缩工作流替换。

确保引用任何相关的 GitHub 问题,例如在提交消息中 Closes #12345。当试图了解他们感兴趣的公式的当前状态时,Homebrew 的历史是未来贡献者首先会查看的内容。

推送

现在你只需要将你的提交推送到 GitHub。

如果你尚未 fork Homebrew,请转到 Homebrew/homebrew-core 存储库并点击 Fork 按钮

如果你已在 GitHub 上 fork Homebrew,则可以手动推送(只需确保你已从 Homebrew/homebrew-core master 中拉取)

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

现在,打开一个拉取请求以进行更改。

便捷工具

消息传递

提供了三个命令来向用户显示信息性消息

当你需要出于任何原因优雅地退出公式时,请使用 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 而不是 foo.py

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 中的变量,而不是在 inreplace 中使用 change_make_var!,请尝试将它们作为参数传递给 make

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 块中。向 on_high_sierra 方法添加 :or_older:or_newer 参数,以将依赖关系添加到满足条件的所有 macOS 版本。例如,要将 gettext 添加为 Mojave 及更高版本 macOS 的构建依赖关系,请使用

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

URL 下载策略

在解析下载 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://nginxserver.cn/"
  url "https://nginxserver.cn/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 将使用此信息选择一个可用的编译器(如果可用)。

只是移动一些文件

当安装函数中的代码运行时,当前工作目录将设置为提取的 tar 包。这使得移动一些文件变得很容易

prefix.install "file1", "file2"

或全部

prefix.install Dir["output/*"]

或仅 tar 包的顶级文件,如 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/*"]

将二进制文件移动到酒窖中的正确位置,以及

man.mkpath

为手册页位置创建目录结构。

要将手册页安装到特定位置,请使用 man1.install "foo.1", "bar.1"man2.install "foo.2" 等。

请注意,在 Homebrew 的上下文中,libexec 由配方保留供私下使用,因此不会符号链接到 HOMEBREW_PREFIX 中。

文件级操作

您可以使用 Ruby 的 FileUtils 提供的文件实用程序。这些实用程序包含在 Formula 类中,因此您无需 FileUtils. 前缀即可使用它们。

创建符号链接时,请特别注意确保它们是相对符号链接。这使得创建可重定位的瓶子变得更加容易。例如,要在 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 的 gem 应安装到 var/lib/ruby/,这样在升级 Ruby 时无需重新安装 gem。通常,你可以使用符号链接技巧或(理想情况下)配置选项来实现此目的。

另一个示例是不应在软件包升级时覆盖的配置文件。如果在安装后你发现要保留的配置文件未被复制,而是从 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
    

    为了找到文件,我们在内部将 .plist 附加到 launchd 服务名称,将 .service 附加到 systemd 服务名称。

  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 作为服务访问点的套接字
名称 - 哈希,其中包含 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 方法接受格式化的套接字定义,格式为 <type>://<host>:<port>

请注意,套接字默认情况下可以在 IPv4 和 IPv6 地址上访问。

如果你只需要一个套接字,并且不关心名称(默认值为 listeners

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

如果你需要多个套接字和/或你想指定名称

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! 方法 中找到。

总之,任何打算用于公式的环境变量都需要符合这些过滤规则才能使用。

在安装期间设置环境变量

你可以使用 ENV["VARIABLE_NAME"] = "VALUE" 在公式的 installtest 块中设置环境变量。可以在 csound 公式中看到示例。

还可以使用 with_env 方法临时设置环境变量;在对该方法的调用中定义的任何变量都将在块的末尾恢复到其原始值。可以在 gh 公式中看到示例。

还有 ENV 帮助程序方法可用于许多常见环境变量设置和检索操作,例如

可以在 SharedEnvExtensionSuperenv 模块文档中找到完整列表。

弃用和禁用配方

请参阅我们的 弃用、禁用和删除公式 文档,以了解有关如何以及何时弃用或禁用公式的更多信息。

更新配方

当软件的新版本发布时,请使用 brew bump-formula-pr 自动更新 urlsha256,删除任何 revision 行,并提交一个拉取请求。请参阅我们的 如何打开 Homebrew 拉取请求 文档以了解详细信息。

新配方的故障排除

版本检测失败

Homebrew 尝试从 version 自动确定 url 以避免重复。如果 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,而不是 Apple 的 Accelerate 框架或默认引用 lapack 实现,来链接到 OpenBLAS。Apple 的 BLAS/LAPACK 实现已过时,可能会引入难以调试的问题。引用 lapack 公式很好,尽管它并未得到积极维护或调整。

如何重新开始(重置为上游 master

您是否在 Git 中创建了一个真正的混乱,导致您无法创建要提交给我们的提交?您可能需要考虑从头开始。可以通过运行以下命令重置您对 Homebrew master 分支的更改

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