A formula is a package definition written in Ruby. It can be created with brew create <URL>
where <URL>
is a zip or tarball, installed with brew install <formula>
, and debugged with brew install --debug --verbose <formula>
. Formulae use the Formula API which provides various Homebrew-specific helpers.
homepage
license
livecheck
blockshead
)master
)term | description | example |
---|---|---|
formula | Homebrew package definition that builds from upstream sources | /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb |
cask | Homebrew package definition that installs macOS native applications | /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask/Casks/b/bar.rb |
prefix | path in which Homebrew is installed | /usr/local |
keg | installation destination directory of a given formula version | /usr/local/Cellar/foo/0.1 |
rack | directory containing one or more versioned kegs | /usr/local/Cellar/foo |
keg-only | a formula is keg-only if it is not symlinked into Homebrew’s prefix | the openjdk formula |
opt prefix | a symlink to the active version of a keg | /usr/local/opt/foo |
Cellar | directory containing one or more named racks | /usr/local/Cellar |
Caskroom | directory containing one or more named casks | /usr/local/Caskroom |
external command | brew subcommand defined outside of the Homebrew/brew GitHub repository |
brew alias |
tap | directory (and usually Git repository) of formulae, casks and/or external commands | /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core |
bottle | pre-built keg poured into a rack of the Cellar instead of building from upstream sources | qt--6.5.1.ventura.bottle.tar.gz |
tab | information about a keg, e.g. whether it was poured from a bottle or built from source | /usr/local/Cellar/foo/0.1/INSTALL_RECEIPT.json |
Brew Bundle | an extension of Homebrew to describe dependencies | brew 'myservice', restart_service: true |
Brew Services | an extension of Homebrew to manage services | brew services start myservice |
Homebrew uses Git for storing formulae and contributing to the project.
As of Homebrew 4.0.0, formulae are downloaded as JSON from https://formulae.brew.sh which is automatically regenerated by a scheduled Homebrew/formulae.brew.sh job from the master
branch of the Homebrew/homebrew-core repository.
Homebrew installs formulae to the Cellar at $(brew --cellar)
and then symlinks some of the installation into the prefix at $(brew --prefix)
(e.g. /opt/homebrew
) so that other programs can see what’s going on. We suggest running brew ls
on a few of the kegs in your Cellar to see how it is all arranged.
Packages are installed according to their formulae. Read over a simple one, e.g. brew edit etl
(or etl.rb) or a more advanced one, e.g. brew edit git
(or git.rb).
Make sure you run brew update
before you start. This ensures your Homebrew installation is a Git repository.
To create or edit formulae locally, you’ll need to first tap homebrew/core
if you haven’t previously. This clones the Homebrew/homebrew-core Git repository to $(brew --repository homebrew/core)
. As you’re developing, you’ll also need to set HOMEBREW_NO_INSTALL_FROM_API=1
in your shell environment or before any install
, reinstall
or upgrade
commands to force brew
to use the local repository instead of the API.
Before submitting a new formula make sure your package:
brew search <formula>
)brew audit --new --formula <formula>
testsBefore submitting a new formula make sure you read over our contribution guidelines.
Run brew create
with a URL to the source tarball:
brew create https://example.com/foo-0.1.tar.gz
This creates $(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb
and opens it in your EDITOR
.
Passing in --ruby
or --python
will populate various defaults commonly useful for projects written in those languages.
If brew
said Warning: Version cannot be determined from URL
when doing the create
step, you’ll need to explicitly add the correct version
to the formula and then save the formula.
Homebrew will try to guess the formula’s name from its URL. If it fails to do so you can override this with brew create <URL> --set-name <name>
.
homepage
We don’t accept formulae without a homepage
!
An SSL/TLS (https) homepage
is preferred, if one is available.
Try to summarise from the homepage
what the formula does in the desc
ription. Note that the desc
ription is automatically prepended with the formula name when printed.
license
We don’t accept new formulae into Homebrew/homebrew-core without a license
!
We only accept formulae that use a Debian Free Software Guidelines license or are released into the public domain following DFSG Guidelines on Public Domain software.
Use the license identifier from the SPDX License List e.g. license "BSD-2-Clause"
, or use license :public_domain
for public domain software.
Use :any_of
, :all_of
or :with
to describe complex license expressions. :any_of
should be used when the user can choose which license to use. :all_of
should be used when the user must use all licenses. :with
should be used to specify a valid SPDX exception. Add +
to an identifier to indicate that the formulae can be licensed under later versions of the same license.
Check out the License Guidelines for examples of complex license expressions in Homebrew formulae.
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --interactive foo
You’re now at a new prompt with the tarball extracted to a temporary sandbox.
Check the package’s README
. Does the package install with ./configure
, cmake
, or something else? Delete the commented out cmake
lines if the package uses ./configure
.
The README
probably tells you about dependencies and Homebrew or macOS probably already has them. You can check for Homebrew dependencies with brew search
. Some common dependencies that macOS comes with:
libexpat
libGL
libiconv
libpcap
libxml2
python
ruby
There are plenty of others; check /usr/lib
for them.
We generally try not to duplicate system libraries and complicated tools in core Homebrew but we do duplicate some commonly used tools.
Special exceptions are OpenSSL and LibreSSL. Things that use either should be built using Homebrew’s shipped equivalent and our BrewTestBot’s post-install audit
will warn if it detects you haven’t done this.
Important: $(brew --prefix)/bin
is NOT in the PATH
during formula installation. If you have dependencies at build time, you must specify them and brew
will add them to the PATH
or create a 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
A String
(e.g. "jpeg"
) specifies a formula dependency.
A Symbol
(e.g. :xcode
) specifies a Requirement
to restrict installation to systems meeting certain criteria, which can be fulfilled by one or more formulae, casks or other system-wide installed software (e.g. Xcode). Some Requirement
s can also take a string or symbol specifying their minimum version that the formula depends on.
A Hash
(e.g. =>
) adds information to a dependency. Given a string or symbol, the value can be one or more of the following values:
:build
means this is a build-time only dependency so it can be skipped when installing from a bottle or when listing missing dependencies using brew missing
.:test
means this is only required when running brew test
.:optional
(not allowed in Homebrew/homebrew-core
) generates an implicit with-foo
option for the formula. This means that, given depends_on "foo" => :optional
, the user must pass --with-foo
to use the dependency.:recommended
(not allowed in Homebrew/homebrew-core
) generates an implicit without-foo
option, meaning that the dependency is enabled by default and the user must pass --without-foo
to disable this dependency. The default description can be overridden using the option
syntax (in this case, the option
declaration must precede the dependency):
option "with-foo", "Compile with foo bindings" # This overrides the generated description if you want to
depends_on "foo" => :optional # Generated description would otherwise be "Build with foo support"
"<option-name>"
(not allowed in Homebrew/homebrew-core
) requires a dependency to have been built with the specified option.Sometimes there’s a hard conflict between formulae that can’t be avoided or circumvented with keg_only
.
A good example for minor conflict is the mbedtls
formula, which ships and compiles a “Hello World” executable. This is obviously non-essential to mbedtls
’s functionality, and as conflict with the popular GNU hello
formula would be overkill, we just remove it during the installation process.
pdftohtml
provides an example of a serious conflict, where each listed formula ships an identically named binary that is essential to functionality, so a conflicts_with
is preferable.
As a general rule, conflicts_with
should be a last-resort option. It’s a fairly blunt instrument.
The syntax for a conflict that can’t be worked around is:
conflicts_with "blueduck", because: "yellowduck also ships a duck binary"
In Homebrew we sometimes accept formulae updates that don’t include a version bump. These include resource updates, new patches or fixing a security issue with a formula.
Occasionally, these updates require a forced-recompile of the formula itself or its dependents to either ensure formulae continue to function as expected or to close a security issue. This forced-recompile is known as a revision
and is inserted underneath the homepage
/url
/sha256
/license
block.
When a dependent of a formula fails to build against a new version of that dependency it must receive a revision
. An example of such failure is in this issue report and its fix.
revision
s are also used for formulae that move from the system OpenSSL to the Homebrew-shipped OpenSSL without any other changes to that formula. This ensures users aren’t left exposed to the potential security issues of the outdated OpenSSL. An example of this can be seen in this commit.
Sometimes formulae have version schemes that change such that a direct comparison between two versions no longer produces the correct result. For example, a project might be version 13
and then decide to become 1.0.0
. As 13
is translated to 13.0.0
by our versioning system by default this requires intervention.
When a version scheme of a formula fails to recognise a new version as newer it must receive a version_scheme
. An example of this can be seen in this pull request.
When you already have a lot of formulae installed, it’s easy to miss a common dependency. You can double-check which libraries a binary links to with the otool
command (perhaps you need to use 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)
If a formula dependency is required on all platforms but can be handled by a component that ships with macOS, specify it with uses_from_macos
. On Linux it acts like depends_on
, while on macOS it’s ignored unless the host system is older than the optional since:
parameter.
For example, to require the bzip2
formula on Linux while relying on built-in bzip2
on macOS:
uses_from_macos "bzip2"
To require the perl
formula only when building or testing on Linux:
uses_from_macos "perl" => [:build, :test]
To require the curl
formula on Linux and pre-macOS 12:
uses_from_macos "curl", since: :monterey
Homebrew doesn’t package already-packaged language-specific libraries. These should be installed directly from gem
/cpan
/pip
etc.
The preferred mechanism for installing gem dependencies is to use bundler
with the upstream’s Gemfile.lock
. This requires the upstream checks in their Gemfile.lock
, so if they don’t, it’s a good idea to file an issue and ask them to do so. Assuming they have one, this is as simple as:
ENV["GEM_HOME"] = libexec
system "bundle", "install", "--without", "development"
From there, you can build and install the project itself:
system "gem", "build", "<project>.gemspec"
system "gem", "install", "--ignore-dependencies", "<project>-#{version}.gem"
And install any bins, and munge their shebang lines, with:
bin.install libexec/"bin/<bin>"
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV.fetch("GEM_HOME", nil))
For python we use resource
s for dependencies and there’s automation to generate these for you. Running brew update-python-resources <formula>
will automatically add the necessary resource
stanzas for the dependencies of your Python application to the formula. Note that brew update-python-resources
is run automatically by brew create
if you pass the --python
switch. If brew update-python-resources
is unable to determine the correct resource
stanzas, homebrew-pypi-poet is a good third-party alternative that may help.
If all else fails, you’ll want to use resource
s for all other language-specific dependencies. This requires you to specify both a specific URL for a version and the sha256 checksum for security. Here’s an example:
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
is an example of a formula that does this well. The end result means the user doesn’t have to use pip
or Python and can just run jrnl
.
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug foo
--debug
will ask you to open an interactive shell if the build fails so you can try to figure out what went wrong.
Check the top of the e.g. ./configure
output. Some configure scripts do not recognise e.g. --disable-debug
. If you see a warning about it, remove the option from the formula.
Add a valid test to the test do
block of the formula. This will be run by brew test foo
and BrewTestBot.
The test do
block automatically creates and changes to a temporary directory which is deleted after run. You can access this Pathname
with the testpath
function. The environment variable HOME
is set to testpath
within the test do
block.
We want tests that don’t require any user input and test the basic functionality of the application. For example foo build-foo input.foo
is a good test and (despite their widespread use) foo --version
and foo --help
are bad tests. However, a bad test is better than no test at all.
See the cmake
formula for an example of a good test. It writes a basic CMakeLists.txt
file into the test directory then calls CMake to generate Makefiles. This test checks that CMake doesn’t e.g. segfault during basic operation.
You can check that the output is as expected with assert_equal
or assert_match
on the output of the Formula assertions such as in this example from the envv
formula:
assert_equal "mylist=A:C; export mylist", shell_output("#{bin}/envv del mylist B").strip
You can also check that an output file was created:
assert_predicate testpath/"output.txt", :exist?
Some advice for specific cases:
tinyxml2
’s test, which writes a small C++ source file into the test directory, compiles and links it against the tinyxml2 library and finally checks that the resulting program runs successfully.test_fixtures("test.svg")
.test
phase with a resource
block, like this: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 expects to find manual pages in #{prefix}/share/man/...
, and not in #{prefix}/man/...
.
Some software installs to man
instead of share/man
, so check the output and add a "--mandir=#{man}"
to the ./configure
line if needed.
In case there are specific issues with the Homebrew packaging (compared to how the software is installed from other sources) a caveats
block can be added to the formula to warn users. This can indicate non-standard install paths, like this example from the ruby
formula:
==> 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.
Name the formula like the project markets the product. So it’s pkg-config
, not pkgconfig
; sdl_mixer
, not sdl-mixer
or sdlmixer
.
The only exception is stuff like “Apache Ant”. Apache sticks “Apache” in front of everything, but we use the formula name ant
. We only include the prefix in cases like gnuplot
(because it’s part of the name) and gnu-go
(because everyone calls it “GNU Go”—nobody just calls it “Go”). The word “Go” is too common and there are too many implementations of it.
If you’re not sure about the name, check its homepage, Wikipedia page and what Debian calls it.
When Homebrew already has a formula called foo
we typically do not accept requests to replace that formula with something else also named foo
. This is to avoid both confusing and surprising users’ expectations.
When two formulae share an upstream name, e.g. AESCrypt and AES Crypt the newer formula must typically adapt its name to avoid conflict with the current formula.
If you’re still not sure, just commit. We’ll apply some arbitrary rule and make a decision 😉.
When importing classes, Homebrew will require the formula and then create an instance of the class. It does this by assuming the formula name can be directly converted to the class name using a regexp
. The rules are simple:
foo-bar.rb
=> FooBar
foobar.rb
=> Foobar
Thus, if you change the name of the class, you must also rename the file. Filenames should be all lowercase, and class names should be the strict CamelCase equivalent, e.g. formulae gnu-go
and sdl_mixer
become classes GnuGo
and SdlMixer
, even if part of their name is an acronym.
Add aliases by creating symlinks in an Aliases
directory in the tap root.
You can run brew audit --strict --online
to test formulae for adherence to Homebrew house style, which is loosely based on the Ruby Style Guide. The audit
command includes warnings for trailing whitespace, preferred URLs for certain source hosts, and many other style issues. Fixing these warnings before committing will make the process a lot quicker for everyone.
New formulae being submitted to Homebrew should run brew audit --new --formula foo
. This command is performed by BrewTestBot on new submissions as part of the automated build and test process, and highlights more potential issues than the standard audit.
Use brew info
and check if the version guessed by Homebrew from the URL is correct. Add an explicit version
if not.
Everything is built on Git, so contribution is easy:
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
The established standard for Git commit messages is:
At Homebrew, we require the name of the formula up front like so: foobar 7.3 (new formula)
.
This may seem crazy short, but you’ll find that forcing yourself to summarise the commit encourages you to be atomic and concise. If you can’t summarise it in 50 to 80 characters, you’re probably trying to commit two commits as one. For a more thorough explanation, please read Tim Pope’s excellent blog post, A Note About Git Commit Messages.
The required commit message format for simple version updates is foobar 7.3
and for fixes is foobar: fix flibble matrix.
. Please squash your commits into one with this message format, otherwise your PR will be replaced by our autosquash workflow.
Ensure you reference any relevant GitHub issue, e.g. Closes #12345
in the commit message. Homebrew’s history is the first thing future contributors will look to when trying to understand the current state of formulae they’re interested in.
Now you just need to push your commit to GitHub.
If you haven’t forked Homebrew yet, go to the Homebrew/homebrew-core repository and hit the Fork button.
If you have already forked Homebrew on GitHub, then you can manually push (just make sure you have been pulling from the Homebrew/homebrew-core
master):
git push https://github.com/myname/homebrew-core/ <what-you-named-your-branch>
Now, open a pull request for your changes.
Three commands are provided for displaying informational messages to the user:
ohai
for general infoopoo
for warning messagesodie
for error messages and immediately exitingUse odie
when you need to exit a formula gracefully for any reason. For example:
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
For any formula using certain well-known build systems, there will be arguments that should be passed during compilation so that the build conforms to Homebrew standards. These have been collected into a set of std_*_args
methods.
Most of these methods accept parameters to customize their output. For example, to set the install prefix to libexec
for configure
or cmake
:
system "./configure", *std_configure_args(prefix: libexec)
system "cmake", "-S", ".", "-B", "build", *std_cmake_args(install_prefix: libexec)
The std_*_args
methods, as well as the arguments they pass, are:
std_cabal_v2_args
"--jobs=#{ENV.make_jobs}"
"--max-backjumps=100000"
"--install-method=copy"
"--installdir=#{bin}"
std_cargo_args
"--locked"
"--root=#{root}"
"--path=#{path}"
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"
std_configure_args
"--disable-debug"
"--disable-dependency-tracking"
"--prefix=#{prefix}"
"--libdir=#{libdir}"
std_go_args
"-trimpath"
"-o=#{output}"
std_meson_args
"--prefix=#{prefix}"
"--libdir=#{lib}"
"--buildtype=release"
"--wrap-mode=nofallback"
std_npm_args
"-ddd"
"--global"
"--build-from-source"
"--cache=$(brew --cache)/npm_cache"
"--prefix=#{libexec}"
std_pip_args
"--verbose"
"--no-deps"
"--no-binary=:all:"
"--ignore-installed"
"--no-compile"
bin.install "foo"
You’ll see stuff like this in some formulae. This moves the file foo
into the formula’s bin
directory (/usr/local/Cellar/pkg/0.1/bin
) and makes it executable (chmod 0555 foo
).
You can also rename the file during the installation process. This can be useful for adding a prefix to binaries that would otherwise cause conflicts with another formula, or for removing a file extension. For example, to install foo.py
into the formula’s bin
directory (/usr/local/Cellar/pkg/0.1/bin
) as just foo
instead of foo.py
:
bin.install "foo.py" => "foo"
inreplace
inreplace
is a convenience function that can edit files in-place. For example:
inreplace "path", before, after
before
and after
can be strings or regular expressions. You should use the block form if you need to make multiple replacements in a file:
inreplace "path" do |s|
s.gsub!(/foo/, "bar")
s.gsub! "123", "456"
end
Make sure you modify s
! This block ignores the returned value.
inreplace
should be used instead of patches when patching something that will never be accepted upstream, e.g. making the software’s build system respect Homebrew’s installation hierarchy. If it’s something that affects both Homebrew and MacPorts (i.e. macOS specific) it should be turned into an upstream submitted patch instead.
If you need to modify variables in a Makefile
, rather than using change_make_var!
within an inreplace
, try passing them as arguments to make
:
system "make", "target", "VAR2=value1", "VAR2=value2", "VAR3=values can have spaces"
system "make", "CC=#{ENV.cc}", "PREFIX=#{prefix}"
Note that values can contain unescaped spaces if you use the multiple-argument form of system
.
While patch
es should generally be avoided, sometimes they are temporarily necessary.
When patch
ing (i.e. fixing header file inclusion, fixing compiler warnings, etc.) the first thing to do is check whether the upstream project is aware of the issue. If not, file a bug report and/or submit your patch for inclusion. We may sometimes still accept your patch before it was submitted upstream but by getting the ball rolling on fixing the upstream issue you reduce the length of time we have to carry the patch around.
Always justify a patch
with a code comment! Otherwise, nobody will know when it is safe to remove the patch, or safe to leave it in when updating the formula. The comment should include a link to the relevant upstream issue(s).
External patch
es can be declared using resource-style blocks:
patch do
url "https://example.com/example_patch.diff"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end
A strip level of -p1
is assumed. It can be overridden using a symbol argument:
patch :p0 do
url "https://example.com/example_patch.diff"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end
patch
es can be declared in stable
and head
blocks. Always use a block instead of a conditional, i.e. stable do ... end
instead of if build.stable? then ... end
.
stable do
# ...
patch do
url "https://example.com/example_patch.diff"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end
end
Embedded (END) patches can be declared like so:
patch :DATA
patch :p0, :DATA
with the patch data included at the end of the file:
__END__
diff --git a/foo/showfigfonts b/foo/showfigfonts
index 643c60b..543379c 100644
--- a/foo/showfigfonts
+++ b/foo/showfigfonts
@@ -14,6 +14,7 @@
…
Patches can also be embedded by passing a string. This makes it possible to provide multiple embedded patches while making only some of them conditional.
patch :p0, "..."
In embedded patches, the string “HOMEBREW_PREFIX” is replaced with the value of the constant HOMEBREW_PREFIX
before the patch is applied.
In external patches, the string “@@HOMEBREW_PREFIX@@” is replaced with the value of the constant HOMEBREW_PREFIX
before the patch is applied.
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --interactive --git foo
# (make some edits)
git diff | pbcopy
brew edit foo
Now just paste into the formula after __END__
.
Instead of git diff | pbcopy
, for some editors git diff >> path/to/your/formula/foo.rb
might help you ensure that the patch is not altered, e.g. whitespace removal, indentation changes, etc.
See the Formula API for the full list of methods available within a formula. If anything isn’t clear, you can usually figure it out by grep
ping the $(brew --repository homebrew/core)
directory for examples. Please submit a pull request to amend this document if you think it will help!
Often, formulae need different dependencies, resources, patches, conflicts, deprecations or keg_only
statuses on different OSes and architectures. In these cases, the components can be nested inside on_macos
, on_linux
, on_arm
or on_intel
blocks. For example, here’s how to add gcc
as a Linux-only dependency:
on_linux do
depends_on "gcc"
end
Components can also be declared for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the depends_on
call inside an on_high_sierra
block. Add an :or_older
or :or_newer
parameter to the on_high_sierra
method to add the dependency to all macOS versions that meet the condition. For example, to add gettext
as a build dependency on Mojave and all later macOS versions, use:
on_mojave :or_newer do
depends_on "gettext" => :build
end
Sometimes, a dependency is needed on certain macOS versions and on Linux. In these cases, a special on_system
method can be used:
on_system :linux, macos: :sierra_or_older do
depends_on "gettext" => :build
end
To check multiple conditions, nest the corresponding blocks. For example, the following code adds a gettext
build dependency when on ARM and macOS:
on_macos do
on_arm do
depends_on "gettext" => :build
end
end
def install
and test do
Inside def install
and test do
, don’t use these on_*
methods. Instead, use if
statements and the following conditionals:
OS.mac?
and OS.linux?
return true
or false
based on the OSHardware::CPU.intel?
and Hardware::CPU.arm?
return true
or false
based on the archMacOS.version
returns the current macOS version. Use ==
, <=
or >=
to compare to symbols corresponding to macOS versions (e.g. if MacOS.version >= :mojave
)See the icoutils
formula for an example.
livecheck
blocksWhen brew livecheck
is unable to identify versions for a formula, we can control its behavior using a livecheck
block. Here is a simple example to check a page for links containing a filename like example-1.2.tar.gz
:
livecheck do
url "https://www.example.com/downloads/"
regex(/href=.*?example[._-]v?(\d+(?:\.\d+)+)\.t/i)
end
For url
/regex
guidelines and additional livecheck
block examples, refer to the brew livecheck
documentation. For more technical information on the methods used in a livecheck
block, please refer to the Livecheck
class documentation.
head
)Formulae can specify an alternate download for the upstream project’s development cutting-edge source (e.g. master
/main
/trunk
) using head
, which can be activated by passing --HEAD
when installing. Specifying it is done in the same manner as url
:
class Foo < Formula
# ...
head "https://github.com/some/package.git", branch: "main" # the default is "master"
end
You can also bundle the URL and any head
-specific dependencies and resources in a head do
block.
class Foo < Formula
# ...
head do
url "https://svn.code.sf.net/p/project/code/trunk"
depends_on "pkg-config" => :build
end
end
You can test whether the head
is being built with build.head?
in the install
method.
When parsing a download URL, Homebrew auto-detects the resource type it points to, whether archive (e.g. tarball, zip) or version control repository (e.g. Git, SVN, Mercurial) and chooses an appropriate download strategy. Some strategies can be passed additional options to alter what’s downloaded. For example, to use a specific commit, tag, or branch from a repository, specify url
or head
with the :tag
and :revision
, :revision
, or :branch
options, like so:
class Foo < Formula
# ...
url "https://github.com/some/package.git",
tag: "v1.6.2",
revision: "344cd2ee3463abab4c16ac0f9529a846314932a2"
end
If not inferable, specify which of Homebrew’s built-in download strategies to use with the using:
option. For example:
class Nginx < Formula
desc "HTTP(S) server and reverse proxy, and IMAP/POP3 proxy server"
homepage "https://nginx.org/"
url "https://nginx.org/download/nginx-1.23.2.tar.gz", using: :homebrew_curl
sha256 "a80cc272d3d72aaee70aa8b517b4862a635c0256790434dbfc4d618a999b0b46"
head "https://hg.nginx.org/nginx/", using: :hg
end
Homebrew offers these anonymous download strategies.
:using value |
download strategy |
---|---|
:bzr |
BazaarDownloadStrategy |
:curl |
CurlDownloadStrategy |
:cvs |
CVSDownloadStrategy |
:fossil |
FossilDownloadStrategy |
:git |
GitDownloadStrategy |
:hg |
MercurialDownloadStrategy |
:homebrew_curl |
HomebrewCurlDownloadStrategy |
:nounzip |
NoUnzipCurlDownloadStrategy |
:post |
CurlPostDownloadStrategy |
:svn |
SubversionDownloadStrategy |
If you need more control over the way files are downloaded and staged, you can create a custom download strategy and specify it with the :using
option:
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
Sometimes a package fails to build when using a certain compiler. Since recent Xcode versions no longer include a GCC compiler we cannot simply force the use of GCC. Instead, the correct way to declare this is with the fails_with
DSL method. A properly constructed fails_with
block documents the latest compiler build version known to cause compilation to fail, and the cause of the failure. For example:
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
For :clang
, build
takes an integer (you can find this number in your brew --config
output), while :gcc
uses either just version
which takes a string to indicate the last problematic GCC version, or a major version argument combined with version
to single out a range of specific GCC releases. cause
takes a string, and the use of heredocs is encouraged to improve readability and allow for more comprehensive documentation.
fails_with
declarations can be used with any of :gcc
, :llvm
, and :clang
. Homebrew will use this information to select a working compiler (if one is available).
When your code in the install function is run, the current working directory is set to the extracted tarball. This makes it easy to just move some files:
prefix.install "file1", "file2"
Or everything:
prefix.install Dir["output/*"]
Or just the tarball’s top-level files like README, LICENSE etc.:
prefix.install_metafiles
Generally we’d rather you were specific about which files or directories need to be installed rather than installing everything.
name | default path | example |
---|---|---|
HOMEBREW_PREFIX |
output of $(brew --prefix) |
/usr/local |
prefix |
#{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 |
temporary directory somewhere on your system | /private/tmp/[formula-name]-0q2b/[formula-name] |
These can be used, for instance, in code such as:
bin.install Dir["output/*"]
to move binaries into their correct location within the Cellar, and:
man.mkpath
to create the directory structure for the manual page location.
To install man pages into specific locations, use man1.install "foo.1", "bar.1"
, man2.install "foo.2"
, etc.
Note that in the context of Homebrew, libexec
is reserved for private use by the formula and therefore is not symlinked into HOMEBREW_PREFIX
.
You can use the file utilities provided by Ruby’s FileUtils
. These are included in the Formula
class, so you do not need the FileUtils.
prefix to use them.
When creating symlinks, take special care to ensure they are relative symlinks. This makes it easier to create a relocatable bottle. For example, to create a symlink in bin
to an executable in libexec
, use:
bin.install_symlink libexec/"name"
instead of:
ln_s libexec/"name", bin
The symlinks created by install_symlink
are guaranteed to be relative. ln_s
will only produce a relative symlink when given a relative path.
Several other utilities for Ruby’s Pathname
can simplify some common operations.
To perform several operations within a directory, enclose them within a cd <path> do
block:
cd "src" do
system "./configure", "--disable-debug", "--prefix=#{prefix}"
system "make", "install"
end
To surface one or more binaries buried in libexec
or a macOS .app
package, use write_exec_script
or write_jar_script
:
bin.write_exec_script (libexec/"bin").children
bin.write_exec_script prefix/"Package.app/Contents/MacOS/package"
bin.write_jar_script libexec/jar_file, "jarfile", java_version: "11"
For binaries that require setting one or more environment variables to function properly, use write_env_script
or env_script_all_files
:
(bin/"package").write_env_script libexec/"package", PACKAGE_ROOT: libexec
bin.env_script_all_files(libexec/"bin", PERL5LIB: ENV.fetch("PERL5LIB", nil))
Some formulae install executable scripts written in an interpreted language such as Python or Perl. Homebrew provides a rewrite_shebang
method to rewrite the shebang of a script. This replaces a script’s original interpreter path with the one the formula depends on. This guarantees that the correct interpreter is used at execution time. This isn’t required if the build system already handles it (e.g. often with pip
or Perl ExtUtils::MakeMaker
).
For example, the icdiff
formula uses this utility. Note that it is necessary to include the utility in the formula; for example with Python one must use include Language::Python::Shebang
.
Note: option
s are not allowed in Homebrew/homebrew-core as they are not tested by CI.
If you want to add an 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
And then to define the effects the option
s have:
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
names should be prefixed with the words with
or without
. For example, an option to run a test suite should be named --with-test
or --with-check
rather than --test
, and an option to enable a shared library --with-shared
rather than --shared
or --enable-shared
. See the alternative ffmpeg
formula for examples.
option
s that aren’t build.with?
or build.without?
should be deprecated with deprecated_option
. See the wget
formula for a historical example.
Any initialization steps that aren’t necessarily part of the install process can be located in a post_install
block, such as setup commands or data directory creation. This block can be re-run separately with brew postinstall <formula>
.
class Foo < Formula
# ...
url "https://example.com/foo-1.0.tar.gz"
def post_install
rm pkgetc/"cert.pem" if File.exist?(pkgetc/"cert.pem")
pkgetc.install_symlink Formula["ca-certificates"].pkgetc/"cert.pem"
end
# ...
end
In the above example, the libressl
formula replaces its stock list of certificates with a symlink to that of the ca-certificates
formula.
For example, Ruby 1.9’s gems should be installed to var/lib/ruby/
so that gems don’t need to be reinstalled when upgrading Ruby. You can usually do this with symlink trickery, or (ideally) a configure option.
Another example would be configuration files that should not be overwritten on package upgrades. If after installation you find that to-be-persisted configuration files are not copied but instead symlinked into /usr/local/etc/
from the Cellar, this can often be rectified by passing an appropriate argument to the package’s configure script. That argument will vary depending on a given package’s configure script and/or Makefile, but one example might be: --sysconfdir=#{etc}
There are two ways to add launchd
plists and systemd
services to a formula, so that brew services
can pick them up:
If the package already provides a service file the formula can reference it by name:
service do
name macos: "custom.launchd.name",
linux: "custom.systemd.name"
end
To find the file we append .plist
to the launchd
service name and .service
to the systemd
service name internally.
If the formula does not provide a service file you can generate one using the following stanza:
# 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
This table lists the options you can set within a service
block. The run
or name
field must be defined inside the service block. If name
is defined without run
, then Homebrew makes no attempt to change the package-provided service file according these fields. The run
field indicates what command to run, instructs Homebrew to create a service description file using options set in the block, and therefore is required before using fields other than name
and require_root
.
method | default | macOS | Linux | description |
---|---|---|---|---|
run |
- | yes | yes | command to execute: an array with arguments or a path |
run_type |
:immediate |
yes | yes | type of service: :immediate , :interval or :cron |
interval |
- | yes | yes | controls the start interval, required for the :interval type |
cron |
- | yes | yes | controls the trigger times, required for the :cron type |
keep_alive |
false |
yes | yes | sets contexts in which the service will keep the process running |
launch_only_once |
false |
yes | yes | whether the command should only run once |
require_root |
false |
yes | yes | whether the service requires root access. If true, Homebrew hints at using sudo on various occasions, but does not enforce it |
environment_variables |
- | yes | yes | hash of variables to set |
working_dir |
- | yes | yes | directory to operate from |
root_dir |
- | yes | yes | directory to use as a chroot for the process |
input_path |
- | yes | yes | path to use as input for the process |
log_path |
- | yes | yes | path to write stdout to |
error_log_path |
- | yes | yes | path to write stderr to |
restart_delay |
- | yes | yes | number of seconds to delay before restarting a process |
process_type |
- | yes | no-op | type of process to manage: :background , :standard , :interactive or :adaptive |
macos_legacy_timers |
- | yes | no-op | timers created by launchd jobs are coalesced unless this is set |
sockets |
- | yes | no-op | socket that is created as an accesspoint to the service |
name |
- | yes | yes | a hash with the launchd service name on macOS and/or the systemd service name on Linux. Homebrew generates a default name for the service file if this is not present |
For services that are kept alive after starting you can use the default 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
If a service needs to run on an interval, use run_type :interval
and specify an interval:
service do
run [opt_bin/"beanstalkd", "test"]
run_type :interval
interval 500
end
If a service needs to run at certain times, use run_type :cron
and specify a time with the crontab syntax:
service do
run [opt_bin/"beanstalkd", "test"]
run_type :cron
cron "5 * * * *"
end
Environment variables can be set with a hash. For the PATH
there is the helper method std_service_path_env
which returns #{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin
so the service can find other brew
-installed commands.
service do
run opt_bin/"beanstalkd"
environment_variables PATH: std_service_path_env
end
keep_alive
optionsThe standard options keep the service alive regardless of any status or circumstances:
service do
run [opt_bin/"beanstalkd", "test"]
keep_alive true # or false
end
Same as above in hash form:
service do
run [opt_bin/"beanstalkd", "test"]
keep_alive always: true
end
Keep alive until the service exits with a non-zero return code:
service do
run [opt_bin/"beanstalkd", "test"]
keep_alive successful_exit: true
end
Keep alive only if the job crashed:
service do
run [opt_bin/"beanstalkd", "test"]
keep_alive crashed: true
end
Keep alive as long as a file exists:
service do
run [opt_bin/"beanstalkd", "test"]
keep_alive path: "/some/path"
end
sockets
formatThe sockets
method accepts a formatted socket definition as <type>://<host>:<port>
.
type
: udp
or tcp
host
: host to run the socket on, e.g. 0.0.0.0
port
: port number the socket should listen onPlease note that sockets will be accessible on IPv4 and IPv6 addresses by default.
If you only need one socket and you don’t care about the name (the default is listeners
):
service do
run [opt_bin/"beanstalkd", "test"]
sockets "tcp://127.0.0.1:80"
end
If you need multiple sockets and/or you want to specify the name:
service do
run [opt_bin/"beanstalkd", "test"]
sockets http: "tcp://0.0.0.0:80", https: "tcp://0.0.0.0:443"
end
Homebrew has multiple levels of environment variable filtering which affects which variables are available to formulae.
Firstly, the overall environment in which Homebrew runs is filtered to avoid environment contamination breaking from-source builds. In particular, this process filters all but a select list of variables, plus allowing any prefixed with HOMEBREW_
. The specific implementation is found in bin/brew
.
The second level of filtering removes sensitive environment variables (such as credentials like keys, passwords or tokens) to prevent malicious subprocesses from obtaining them. This has the effect of preventing any such variables from reaching a formula’s Ruby code since they are filtered before it is called. The specific implementation is found in the ENV.clear_sensitive_environment!
method.
In summary, any environment variables intended for use by a formula need to conform to these filtering rules in order to be available.
You can set environment variables in a formula’s install
or test
blocks using ENV["VARIABLE_NAME"] = "VALUE"
. An example can be seen in the csound
formula.
Environment variables can also be set temporarily using the with_env
method; any variables defined in the call to that method will be restored to their original values at the end of the block. An example can be seen in the gh
formula.
There are also ENV
helper methods available for many common environment variable setting and retrieval operations, such as:
ENV.cxx11
- compile with C++11 features enabledENV.deparallelize
- compile with only one job at a time; pass a block to have it only influence specific install stepsENV.O0
, ENV.O1
, ENV.O3
- set a specific compiler optimization level (default: macOS: -Os
, Linux: -O2
)ENV.runtime_cpu_detection
- account for formulae that detect CPU features at runtimeENV.append_to_cflags
- add a value to CFLAGS
CXXFLAGS
OBJCFLAGS
OBJCXXFLAGS
all at onceENV.prepend_create_path
- create and prepend a path to an existing list of pathsENV.remove
- remove a string from an environment variable valueENV.delete
- unset an environment variableThe full list can be found in the SharedEnvExtension and Superenv module documentation.
See our Deprecating, Disabling and Removing Formulae documentation for more information about how and when to deprecate or disable a formula.
When a new version of the software is released, use brew bump-formula-pr
to automatically update the url
and sha256
, remove any revision
lines, and submit a pull request. See our How to Open a Homebrew Pull Request documentation for more information.
Homebrew tries to automatically determine the version
from the url
to avoid duplication. If the tarball has an unusual name you may need to manually assign the version
.
If a project’s makefile will not run in parallel, try to deparallelize by adding these lines to the formula’s install
method:
ENV.deparallelize
system "make" # separate compilation and installation steps
system "make", "install"
If that fixes it, please open an issue with the upstream project so that we can fix it for everyone.
Check out what MacPorts and Fink do:
brew search --macports foo
brew search --fink foo
superenv
is our “super environment” that isolates builds by removing /usr/local/bin
and all user PATH
s that are not essential for the build. It does this because user PATH
s are often full of stuff that breaks builds. superenv
also removes bad flags from the commands passed to clang
/gcc
and injects others (for example all keg_only
dependencies are added to the -I
and -L
flags).
If in your local Homebrew build of your new formula, you see Operation not permitted
errors, this will be because your new formula tried to write to the disk outside of your sandbox area. This is enforced on macOS by sandbox-exec
.
Some software requires a Fortran compiler. This can be declared by adding depends_on "gcc"
to a formula.
Packages requiring MPI should use OpenMPI by adding depends_on "open-mpi"
to the formula, rather than MPICH. These packages have conflicts and provide the same standardised interfaces. Choosing a default implementation and requiring its adoption allows software to link against multiple libraries that rely on MPI without creating unanticipated incompatibilities due to differing MPI runtimes.
Packages requiring BLAS/LAPACK linear algebra interfaces should link to OpenBLAS by adding depends_on "openblas"
and (if built with CMake) passing -DBLA_VENDOR=OpenBLAS
to CMake, rather than Apple’s Accelerate framework or the default reference lapack
implementation. Apple’s implementation of BLAS/LAPACK is outdated and may introduce hard-to-debug problems. The reference lapack
formula is fine, although it is not actively maintained or tuned.
master
)Have you created a real mess in Git which stops you from creating a commit you want to submit to us? You might want to consider starting again from scratch. Your changes to the Homebrew master
branch can be reset by running:
git checkout -f master
git reset --hard origin/master