Skip to content
jacknagel edited this page Dec 2, 2011 · 23 revisions

h1. 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.

h2. Terminology

| 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 |

h2. Homebrew: An Introduction

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 @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 @/usr/local/Library/Formula@. Check some out. You can view any formula at anytime with eg. @brew edit wget@.

h2. Basic Instructions

Ensure you've run brew update at least once ever 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:

Make sure you search thoroughly (all aliases!) We don't want you to waste your time.

h3. Will we merge your formula?

Probably. But we have rules to keep the quality and goals of Homebrew intact: Please read Acceptable Formula.

h3. Some Quick Examples Before You Get Started

Formulae aren't that complicated. "etl":https://github.com/mxcl/homebrew/blob/master/Library/Formula/etl.rb and "browser":https://github.com/mxcl/homebrew/blob/master/Library/Formula/browser.rb are as simple as it gets.

And then "Git":http://github.com/mxcl/homebrew/tree/master/Library/Formula/git.rb and "flac":http://github.com/mxcl/homebrew/tree/master/Library/Formula/flac.rb show more advanced functionality.

h3. Grab the URL

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 '1234567890ABCDEF1234567890ABCDEF'

# depends_on 'cmake'

  def install
    system "./configure", "--prefix=#{prefix}", "--disable-debug", "--disable-dependency-tracking"
#   system "cmake . #{std_cmake_parameters}"
    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.)

h3. Fill in the Homepage

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. To satisfy the description, we're going to invent a new packaging microformat and persuade everyone to publish it on their homepage.

h3. Check the build system

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).

h3. Check for dependencies

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 (e.g. @scons@). But generally we avoid dupes because it's one of Homebrew's foundations. (And it causes build and usage problems!)

We encourage you to maintain your own fork with dupes as you require them.

h3. Specifying other formulae as dependencies

class Foo < Formula
  depends_on 'jpeg'
end

Note that you cannot (yet) depend on specific versions of a formula, or building with certain options. There is the concept of an optional dependency, but it's not implemented yet (even though some formulae already sport them).

h3. Double-check for dependencies

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)

h3. Specifying gems, etc. as dependencies

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

h3. Check your "system" calls

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.

h3. Test the formula

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.

h2. Manuals

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.

h3. A Quick Word on Naming

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 stick 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.

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 file 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@.

h3. Audit the formula

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.

h3. Commit

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 for Git commit messages is:

  • the first line is a commit summary of 50 characters or less, then
  • two newlines, 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'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":http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html.

Ensure you reference any relevant GitHub tickets 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.

h3. Push

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, simply 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.)

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@ your commits. For this reason, you should create 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.)

h2. Overview of the Formula Install Process

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

h2. Convenience Tools

h3. Messaging

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

h3. bin.install('foo')

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).

h3. inreplace

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

h3. Patches

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, function return values, 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 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 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 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 @@
…

h3. Creating the diff

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.

h2. Advanced Formula Tricks

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!

h3. HEAD formula

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 a specific commit from a git repository, specify head with the :tag option, like so:

class Foo < Formula
  head 'git://github.com/some/package.git.', :tag => '090930930295adslfknsdfsdaffnasd13'
end

h3. Specifying the Download Strategy explicitly

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

h3. Just copying some files

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['*']

h3. Variables for directory locations

| 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/localCellar/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.

h3. Installation without linking to /usr/local

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.

h3. Adding optional steps

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":http://github.com/mxcl/homebrew/tree/master/Library/Formula/emacs.rb formula for an example.

h3. File level operations

You can use the file utilities provided by Ruby.

They are documented "here":http://www.ruby-doc.org/stdlib/libdoc/fileutils/rdoc/index.html

h3. Handling files that should persist over formula upgrades

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.

h2. Troubleshooting for people writing new formulae

h3. Version detection fails

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

h3. Binaries fail when trying to do dynamic library loading at runtime

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.)

h3. Bad Makefiles

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":http://github.com/mxcl/homebrew/issues so that we can fix it for everyone.

h3. Still won't work?

Check out what macports and fink do:

brew -S --macports foo

brew -S --fink foo

Clone this wiki locally