-
Notifications
You must be signed in to change notification settings - Fork 0
Formula Cookbook
Making a formula is easy. Just brew create URL
and then brew install FOO
(perhaps with --debug --verbose
). Basically, a formula is a Ruby file. You can place it anywhere you want (local or remote) and install it by pointing to the file or URL.
We want your formula to be awesome, and the cookbook will tell you how.
Formula | The package description | /usr/local/Library/Formula/foo.rb |
Keg | The installation prefix of a Formula | /usr/local/Cellar/foo/0.1 |
Cellar | All kegs are installed in here | /usr/local/Cellar |
Tap | An optional repository (git) of Formulae | /usr/local/Library/Taps |
More general: brew --prefix
and brew --repository
instead of /usr/local
but let's KISS.
Did you see /usr/local/.git
? Homebrew is built on Git (as long as you did brew update
at least once). This means you can just do your work in /usr/local
and merge
in upstream updates as you go.
Homebrew installs to the Cellar
, it then symlinks some of the installation into /usr/local
so that other programs can see what's going on. We suggest you brew ls
a few of the kegs in your Cellar to see how it is all arranged.
Packages are installed according to their formulae, which live in $(brew --repository)/Library/Formula
. Check some out. You can view any formula at anytime; e.g. brew edit wget
.
Ensure you’ve run brew update
at least once even before you start. This turns your Homebrew prefix into a Git repository. (Homebrew is built on top of Git. You did know that, right?)
Before contributing, make sure your package:
- isn't already waiting to be merged (check the issue tracker)
- isn't already in Homebrew (check
brew search foo
) - isn't in another Homebrew tap.
Make sure you search thoroughly (all aliases!) We don’t want you to waste your time.
Probably. But we have rules to keep the quality and goals of Homebrew intact: Please read Acceptable Formulae.
Formulae aren’t that complicated. etl and browser are as simple as it gets.
And then Git and flac show more advanced functionality.
All you need to make a formula is a URL to the tarball.
brew create http://example.com/foo-0.1.tar.gz
This creates:
/usr/local/Library/Formula/foo.rb
And opens it in your $EDITOR
. It'll look like:
require 'formula'
class Foo < Formula
url 'http://example.com/foo-0.1.tar.gz'
homepage ''
sha1 '1234567890ABCDEF1234567890ABCDEF'
# depends_on 'cmake' => :build
def install
system "./configure", "--prefix=#{prefix}", "--disable-debug", "--disable-dependency-tracking"
# system "cmake", ".", *std_cmake_args
system "make install"
end
end
Note: 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 with version 'foo'
and then save the formula. brew install
should then proceed without any trouble.
Note: If brew
said No formula found for "php54-timezonedb". Searching open pull requests...
and you are writing a Tap, you should run brew tap --repair
.
We don’t accept formulae without homepages!
Homebrew doesn’t have a description field because the homepage is always up to date, and Homebrew is not. That’s way less maintenance for us. Try brew home qt
.
brew install -i foo
You’re now at new prompt with the tarball extracted to a temporary sandbox.
Check the package’s README
. Does the package install with autotools
, cmake
, or something else? Delete the commented out cmake lines if the package uses autotools (i.e. if it has a configure
script).
The README
probably tells you about dependencies. Homebrew or OS X probably already has them. You can check for Homebrew deps with brew search
. These are the common deps that OS X comes with:
Freetype
libexpat
libGL
libiconv
libpcap
libpng
libxml2
Python
Ruby
There are plenty of others. Check /usr/lib
to see.
We try to not duplicate libraries and complicated tools in core Homebrew. We dupe some common tools though. But generally, we avoid dupes because it’s one of Homebrew’s foundations. (And it causes build and usage problems!)
However, we maintain a special tap that provides dupes.
Important: Since the introduction of superenv
, brew --prefix
/bin is NOT on the $PATH
during formula installation. If you have dependencies at build time, you must specify them and brew will add them to the $PATH
. You can test around this with --env=std
.
class Foo < Formula
depends_on 'jpeg'
depends_on 'gtk+' => :optional
depends_on 'readline' => :recommended
depends_on 'boost' => 'with-icu'
depends_on :x11
end
A String specifies a formula dependency.
A Symbol specifies a special conditional dependency, such as X11.
A Hash specifies a formula dependency with some additional information. Given a single string key, the value can take several forms:
-
a Symbol (currently one of
:build
,:optional
,:recommended
).-
:build
tags that dependency as a build-time only dependency, meaning it can be safely ignored when installing from a bottle and when listing missing dependencies usingbrew missing
. -
:optional
generates an implicitwith-foo
option for the formula. This means that, givendepends_on 'foo' => :optional
, the user must pass--with-foo
in order to enable the dependency. -
:recommended
generates an implicitwithout-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 normal option syntax:
depends_on 'foo' => :optional # Generated description is "Build with foo support" option 'with-foo', 'Compile with foo bindings' # This overrides the generated description if you want to
-
-
a String or an Array String values are interpreted as options to be passed to the dependency. You can also pass an array of strings, or an array of symbols and strings, in which case the symbols are interpreted as described above, and the strings are passed to the dependency as options.
depends_on 'foo' => 'with-bar' depends_on 'foo' => %w{with-bar with-baz} depends_on 'foo' => [:optional, 'with-bar']
When you already have a lot of brews installed, its easy to miss a common dependency like glib
or gettext
.
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/lib/libssl.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
/usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3)
/usr/local/Cellar/glib/2.22.4/lib/libglib-2.0.0.dylib (compatibility version 2201.0.0, current version 2201.4.0)
/usr/local/Cellar/gettext/0.17/lib/libintl.8.dylib (compatibility version 9.0.0, current version 9.2.0)
/usr/local/Cellar/readline/6.0/lib/libreadline.6.0.dylib (compatibility version 6.0.0, current version 6.0.0)
/usr/local/Cellar/popt/1.15/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.2.0)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 38.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.0)
Homebrew doesn’t package the already-packaged. If your formula needs a gem or python module, you’ll need to check for these external dependencies:
class Foo < Formula
depends_on 'mg' => :ruby
depends_on 'json' => :python
# New style (but only implemented for Python):
depends_on :python => 'json'
depends_on :python3 => 'json'
end
Sometimes Python or Ruby package names don't match the way that the dependency should be checked. For instance, Python's PyYAML
installs the package, but its existence is checked with python -c "import yaml"
:
class Foo < Formula
depends_on :python => ['yaml' => 'PyYAML']
# for other languanges we need to use the old style like so:
depends_on LanguageModuleDependency.new :python, 'PyYAML', 'yaml'
end
Exit out of the interactive shell.
brew install --verbose --debug foo
Debug will ask you to open an interactive shell when the build fails so you can try to figure out what went wrong.
Check the top of the ./configure
output (if applicable)! Some configure scripts do not recognize --disable-debug
. If you see a warning about it, remove the option from the formula.
Homebrew expects to find man 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.
THE NAME IS VERY IMPORTANT!
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 the homepage, and check the Wikipedia page.
ALSO CHECK WHAT DEBIAN CALLS IT!
If you’re still not sure, just commit. I’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.
Add aliases by creating symlinks in Library/Aliases
.
You can run brew audit
to test formulae for adherence to Homebrew house style. This includes warnings for trailing whitespace, preferred URLs for certain source hosts, and a lot of other style issues. Fixing these warnings before committing will make the process a lot smoother for us.
Everything is built on Git, so contribution is easy:
brew install git # if you already have git installed, skip this command
brew update # required in more ways than you think (initializes the brew git repository if you don't already have it)
cd `brew --repository`
# 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>
git add Library/Formula/foo.rb
git commit
The established standard for Git commit messages is:
- the first line is a commit summary of 50 characters or less, then
- two (2) newlines, then
- explain the commit throughly
At Homebrew, we like to put the name of the formula upfront like so "foobar: 7.3 - tool to do foo". 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-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.
Ensure you reference any relevant GitHub issue #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 back to GitHub.
If you haven’t forked Homebrew yet, go to the repo 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 mxcl/homebrew master):
git push [email protected]:myname/homebrew.git <what-you-called-your-branch>
Now, please open a Pull Request (on your github repo page) for new and updated brews.
- One formula per commit; one commit per formula
- Keep merge commits out of the request
- If you have any merge or mixup commits, please squash them.
If a commit touches multiple files, or isn’t one logical bug fix, or a file is touched in multiple commits, we’ll probably ask you to rebase
and squash
your commits. For this reason, you should avoid pushing to your master
branch. Note, after rebase and/or squash, you'll need to push with --force
to your remote.
- The result of
Formula.download_strategy
is instantiated. -
DownloadStrategy.fetch
is called (downloads tarball, checks out git repository, etc.) - A temporary sandbox is created in
/tmp/homebrew
-
DownloadStrategy.stage
is called (extracts tarball to above sandbox, exports git repository to sandbox, etc.) - Patches are applied
- Current directory is changed to the stage root (so when you
system make
, it works) -
Formula.install
is called - Anything installed to the keg is cleaned (see later)
- The keg is symlinked into Homebrew’s prefix
- Caveats are displayed
Three commands are provided for displaying informational messages to the user:
-
ohai
for general info -
opoo
for warning messages -
onoe
for error messages
In particular, when a test needs to be performed before installation use onoe
to bail out gracefully. For example:
if some_test?
system "make install"
else
onoe 'Error! Something is wrong.'
end
You’ll see stuff like that in other formulae. This installs the file foo into the Formula’s bin
directory (/usr/local/Cellar/pkg/0.1/bin
) and makes it executable (chmod +x foo
).
A convenience function that can edit files in-place. For example:
inreplace 'path', before, after
before
and after
can be strings or regexps. You can also use the block form:
inreplace 'path' do |s|
s.gsub! /foo/, 'bar'
end
Make sure you modify s
! This block ignores the returned value.
There are some special functions you can use inside the block that makes it easier to modify Makefiles
:
inreplace 'Makefile' do |s|
s.remove_make_var! %w[CFLAGS LDFLAGS CC LD]
s.change_make_var! "CC", ENV.cc
end
NOTE: We really prefer that you use patches. Patches fail to apply if the code changes too much, and this is good! in replace
could theoretically continue to apply long after it has subtly broken the code.
While patches should generally be avoided, sometimes they are necessary.
Patches to make the software’s build system respect Homebrew’s installation hierarchy are usually fine and don’t necessitate an upstream bug report, but if the patch is more complex (i.e. fixing header file inclusion, fixing compiler warnings, etc.), the first thing to do is check whether or not the upstream project is aware of the issue. If not, file a bug report and/or submit your patch for inclusion. We will normally still accept your patches, 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, always, always justify a patch with a 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).
Return a string or array from your re-implemented patches function:
def patches
# fixes doc install on OS X 10.6
"http://gist.github.com/623/my.patch/raw/"
end
# or ...
def patches
# fixes multiple problems
[
"http://gist.github.com/623/my-1.patch/raw/",
"http://gist.github.com/623/my-2.patch/raw/",
"http://gist.github.com/623/my-3.patch/raw/"
]
end
Patches can be gzipped
...or not. If the patches are not p1, return a Hash:
def patches
# back ported fix for memory leak in drawing functions
{ :p0 => "http://gist.github.com/623/my.patch/raw/" }
end
Small patches should be put in the formula:
class Foo < Formula
def patches
# fixes something small
DATA
end
end
__END__
diff --git a/foo/showfigfonts b/foo/showfigfonts
index 643c60b..543379c 100644
--- a/foo/showfigfonts
+++ b/foo/showfigfonts
@@ -14,6 +14,7 @@
…
The magic variable HOMEBREW_PREFIX
is expanded to whatever the Homebrew prefix is before the patch is applied.
We use git for everything...Didn’t you know?
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 that the diff is not touched (eg. white space removal, indentation, etc.)
If anything isn’t clear, you can usually figure it out with some grep
and the Library/Formula
directory. Please amend this wiki page if you think it will help!
Formulae can specify alternate downloads for the upstream project’s devel
release (unstable but not trunk
) or HEAD
(master/trunk
).
HEAD URLs (activated by passing --HEAD
) build the development cutting edge. Specifying it is easy:
class Foo < Formula
head 'https://github.com/mxcl/lastfm-cocoa.git'
end
Homebrew understands git
, svn
, and hg
URLs, and has a way to specify cvs
repositories as a URL as well. You can test whether the HEAD
is being built with build.head?
.
To use a specific commit, tag, or branch from a repository, specify head with the :revision
, :tag
, or :branch
option, like so:
class Foo < Formula
head 'https://github.com/some/package.git', :revision => '090930930295adslfknsdfsdaffnasd13'
# or :branch => 'develop'
# or :tag => '1_0_release'
end
The 'devel' spec (activated by passing --devel
) is used for a project’s unstable releases. It is specified in a block:
devel do
url 'https://foo.com/foo-0.1.tar.gz'
sha1 'deadbeefdeadbeefdeadbeafdeadbeefdeadbeef'
end
You can test if the 'devel' spec is in use with build.devel?
.
With a simple if/else statement, you can use a preferred toolchain if available, and if not, fall back to a secondary toolchain. For example, the following install
stanza will check whether Xcode is installed and use xcodebuild
if it is, falling back to autoconf
if necessary.
def install
if MacOS::Xcode.installed?
system "xcodebuild install DSTROOT=#{prefix}"
else
system "autoconf"
system "./configure", "--prefix=#{prefix}"
system "make install"
end
end
Sometimes a package fails to build when using a certain compiler. Since recent XCode no longer includes a vanilla GCC compiler, we cannot simply force the use of GCC. Instead, the correct way to declare this is 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 :llvm do
build 2335
cause <<-EOS.undent
The 'cause' field should include a short summary of the error. Include
the URLs of any relevant information, such as upstream bug reports. Wrap
the text at a sensible boundary (~72-80 characters), but do not break
URLs over multiple lines.
EOS
end
build
takes a Fixnum (you can find this number in your brew --config
output). 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).
To use one of Homebrew’s built-in download strategies, specify the :using =>
flag on a url
or head
. For example:
class Sip < Formula
url 'http://www.riverbankcomputing.co.uk/hg/sip/archive/4.11'
md5 'dbafd7101a4e7caee6f529912a1356e5'
head 'http://www.riverbankcomputing.co.uk/hg/sip', :using => :hg
homepage 'http://www.riverbankcomputing.co.uk/software/sip'
The downloaders offered by Homebrew are:
Value of :using
|
Corresponds To |
---|---|
:bzr |
BazaarDownloadStrategy |
:curl |
CurlDownloadStrategy |
:cvs |
CVSDownloadStrategy |
:git |
GitDownloadStrategy |
:hg |
MercurialDownloadStrategy |
: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 using the Formula#download_strategy
function. Return the class type. For example:
class MyDownloadStrategy < SomeHomebrewDownloadStrategy
# Does something cool
end
class Foo < Formula
url "something", :using => MyDownloadStrategy
end
Specifying download strategies can be useful when used with a local repo, where a plain URL would not let you specify how to access it. For example:
class Bar < Formula
head '/users/abc/src/git.git', :using => :git
end
When your code in the install function is run, the current working directory is set to the extracted tarball.
So it is easy to just copy some files:
prefix.install 'file1', 'file2'
Or everything:
prefix.install Dir['*']
Again, in your mind, replace /usr/local
by brew --prefix
:
Name | Default | E.g. |
---|---|---|
name | [formula name] | foo |
HOMEBREW_PREFIX |
/usr/local |
|
prefix |
#{HOMEBREW_PREFIX}/Cellar/#{name}/#{version} |
/usr/local/Cellar/foo/0.1 |
bin |
#{prefix}/bin |
/usr/local/Cellar/foo/0.1/bin |
doc |
#{prefix}/share/doc/foo |
/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 |
etc |
#{HOMEBREW_PREFIX}/etc |
/usr/local/etc |
var |
#{HOMEBREW_PREFIX}/var |
/usr/local/var |
These can be used, for instance, in code such as
bin.install Dir['*']
to install binaries into their correct location into the cellar, and
man.mkpath
to create the directory structure to the man location.
To install man pages into specific locations, use man1.install ['foo.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
.
If you only need a program for a dependency and it does not need to be linked for public use in /usr/local
, specify
keg_only "This is my rationale."
in the Formula class.
If you want to add an option:
class Yourformula < Formula
...
option 'with-ham', "Description of the option"
option 'without-spam', "Another description"
depends_on 'foo' => :optional # will automatically add a with-foo option
...
And then to define the effects the options 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
if build.include? 'with-ham'
# the old style, mostly useful for options other than `with`/`without` style
end
Option names should usually be prefixed with one of the words with
, without
, no
, or a verb in the imperative tense describing the action to be taken. 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 should be named --enable-shared
rather than --shared
.
See the graphviz formula for an example.
You can use the file utilities provided by Ruby (FileUtils
). These are included in the Formula
class, so you do not need the FileUtils.
prefix to use them.
They are documented here
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 better a configure option.
Homebrew provides two Formula methods for launchd plist files. plist_name
will return homebrew.mxcl.<formula>
, and plist_path
will return, for example, /usr/local/Cellar/foo/0.1/homebrew.mxcl.foo.plist
.
Homebrew tries to maintain a consistent ruby style across all formulae. In case of doubt, please consult existing formulae and do the same.
- The order of methods in a formula should be consistent with other formulae (eg.:
def patches
goes beforedef install
) - An empty line is required before the
__END__
line
Homebrew tries to automatically determine the version from the URL in order to save on duplication. If the tarball has a funny name though, you may have to assign the version number:
class Foobar
version "0.7"
end
Not all projects have makefiles that will run in parallel so try to deparallelize:
brew edit foo
Add all this to the formula (so there will already be a class line, don’t add another or change that, and there’s already an install function, don't add another one, add the lines in the install function below to the top of the problem formula’s install function).
class Foo < Formula
skip_clean :all
def install
ENV.deparallelize
ENV.no_optimization
system "make" # separate make and make install steps
system "make install"
end
end
If that fixes it, please open an issue so that we can fix it for everyone.
Check out what macports and fink do:
brew -S --macports foo
brew -S --fink foo
superenv
is a "super" environment that tries to improve reliability for the general case. But it does make making formula harder.
To not use superenv
, install with --env=std
.
Superenv isolates builds by removing /usr/local/bin
and all user-PATHs that are not determined to be essential to the build. It does this because other PATHs are full of stuff that breaks builds. (We have 15,000 tickets as testament!)
superenv
tries to remove 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 superenv troubles you, try to brew install --env=std
and report to us if that fixes it.
Some software requires a Fortran compiler. This can be declared by adding depends_on :fortran
to a formula. :fortran
is a special dependency that does several things.
First, it looks to see if you have set the FC
environment variable. If it is set, Homebrew will use this value during compilation. If it is not set, it will check to see if gfortran
is found in PATH
. If it is, Homebrew will use its location as the value of FC
. Otherwise, the gfortran
formula will be treated as a dependency and installed prior to compilation.
If you have set FC
to a custom Fortran compiler, you may additionally set FCFLAGS
and FFLAGS
. Alternatively, you can pass --default-fortran-flags
to brew install
to use Homebrew's standard CFLAGS
.
When using Homebrew's own gfortran compiler, the standard CFLAGS
are used and user-supplied values of FCFLAGS
and FFLAGS
are ignored for consistency and reproducibility reasons.