-
Notifications
You must be signed in to change notification settings - Fork 5
Formula Cookbook
Making formula is easy. Just `brew create URL` and then `brew install FOO`. If you want mxcl/master to merge your new formula then you should read the rest of this document because 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 |
Before contributing, check that the package you are about to create isn’t waiting to be merged by doing a search at the issue tracker.
Naturally check it already isn’t in Homebrew with a search: brew search foo
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 the Acceptable Formula wiki.
Formulae aren’t that complicated. wget is 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 '' md5 '' # depends_on 'cmake' def install system "./configure", "--prefix=#{prefix}", "--disable-debug", "--disable-dependency-tracking" # system "cmake . #{std_cmake_parameters}" system "make install" end end
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. Thus it’s less maintenance for us. To satisfy the description we’re going to invent a new packaging microformat and persuade everyone to publish it on their homepage.
brew install -i foo
This downloads the formula and extracts it to a sandbox. Homebrew tells you the MD5 as it does this. Fill the formula in.
(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.)
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 (ie. 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 |
X11 |
There are plenty of others. Check /usr/lib, /usr/X11/lib to see.
We try to never duplicate libraries and complicated tools. We dupe some common tools (eg scons). But generally we avoid dupes because it is a foundation of Homebrew, and because it causes build and usage problems.
We encourage you to maintain your own fork with dupes as you require them.
class Foo < Formula depends_on 'jpeg' end
Note that you cannot (yet) depend on specific versions of a formula or it being built with certain options. There is the concept of an optional dependency but it has not been implemented yet even though some formulae already sport them.
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:
$ 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 egg, you’ll need to check for these external dependencies:
class Foo < Formula depends_on 'mg' => :ruby depends_on 'json' => :python end
If you call OS X provided binaries, such as bsdmake
, be sure to use the full system path:
system "/usr/bin/bsdmake"
This will help prevent errors in case another version of the same tool appears in the user’s PATH.
Exit out of the interactive shell.
brew install -vd foo
The v is for verbose, the d debug. Debug mode dumps you to an interactive shell if 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 outputs and add a "--mandir=#{man}"
to the ./configure
line if needed.
Name the formula like the project markets the product. So it is pkg-config not pkgconfig, it is sdl_mixer not sdl-mixer or sdlmixer. The exception is eg. Apache Ant. Apache stick Apache in front of everything, but we use the formula name “ant”. We only include the prefix in cases like: GNUplot and GNU Go. The former because it is part of the name. The latter is because everyone calls it GNU go, nobody refers to GNU’s Go as merely 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. If you’re still not sure then just commit I’ll apply some arbitrary rule and make a decision ;)
When importing classes Homebrew will require the formula file and then create an instance of the class. It does this by assuming the formula name can be directly converted the 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 --prefix` git add Library/Formula/foo.rb git commit
The established standard with Git is for commit messages to have a first line that is 50 characters or less and then add two newlines, and then explain the commit throughly.
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 probably are trying to commit two commits as one.
Ensure you reference any GitHub tickets if relevant in the commit message. Homebrew’s history is the first thing future contributors will look to when they are trying to understand the current state of formulae they are 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
Now all that remains is to tell a Homebrew collaborator about your formula so it can be merged.
We prefer that you create a Pull Request for new and changed brews. Do not open both an issue and a Pull Request; Pull Requests automagically create their own issues. One formula to a commit, one commit to a formula. Keep merge commits out of the request. If you have any or fixed up commits then please squash your commits. If more than one file is touched in a commit, and the commit isn’t one logical bug fix, or the same file is touched in multiple commits then we will probably ask you to rebase your commits. For this reason you should a new remote branch in your fork for each pull request rather than using your fork’s master branch. You can delete it after we have pulled your commit.
(Keep Issues for reporting bugs, problems and suggestions.)
- The result of Formula.download_strategy is instantiated.
- DownloadStrategy.fetch is called — eg. downloads tarball, checks out git repository, etc.
- A temporary sandbox is created in /tmp/homebrew
- DownloadStrategy.stage is called — eg. 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`).
A convenience function that can edit files in-place. Eg:
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
We prefer to not patch wherever possible. Please submit patches upstream at the same time as adding the patch to the formula. We prefer accepted patches from an upstream version control system when possible.
Return an array or string 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
Patches can 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 your mum (she was b0rked) 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 @@ …
Always, always, always justify the patch with a comment! Otherwise nobody will ever know if it’s safe to remove the patch, or if it’s safe to leave it in when upgrading the formula.
We use git for everything don’t you know?
brew install -i --git foo … (make some edits) … git diff | pbcopy brew edit foo
Now just paste into the formula after __END__
.
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!
HEAD formulae have an alternate URL that users can choose with --HEAD
. This URL points to trunk/master/HEAD. It will build the development cutting edge. Specifying it is easy:
class Foo < Formula head 'git://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 build with ARGV.build_head?
.
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. Eg:
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['*']
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 |
lib | #{prefix}/lib | etc. |
share | #{prefix}/share | |
man | #{prefix}/share/man |
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.
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 ... def options [ ['--option-name', "Description of the option"], ['--another-option', "Another description"] ] end
And then to define the effects the options have:
if ARGV.include? '--option-name' ... end
See the emacs formula for an example.
You can use the file utilities provided by Ruby.
They are documented here
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 member:
class Foobar version "0.7" end
Homebrew aggressively strips symbols from built binaries. In most cases the only effect of this is smaller resulting binaries, but some times stripping can break executables that use dynamic loading at runtime. In this case, your formula can specify that a given binary should not be stripped; see the Python formula for an example. (When stripped, the python executable fails to load .so based modules.)
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 def skip_clean? path true end 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
Please don’t copy what they do unless you need to. We’re stricter on patching upstream than they are.
The list of variables misses the commonly used variables var and etc defined in formula.rb:
Name | Default | E.g. |
etc | #{HOMEBREW_PREFIX}/etc | /usr/local/etc |
var | #{HOMEBREW_PREFIX}/var | /usr/local/var |