CARVIEW |
Formula Cookbook
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 |
Basic Instructions
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.
Formula we don’t accept
The mxcl fork doesn’t dupe stuff that comes with OS X or stuff that is provided by RubyGems, CPAN or PyPi. There are good reasons for this:
1. Duplicate libraries regularly break builds
2. Subtle bugs emerge with duplicate libraries, and to a lesser extent, duplicate tools
3. We want you to try harder to make your formula work with what OS X comes with
We currently have a few exceptions:
Ruby | People need version 1.9 |
Python | People need version 2.6 |
Bash | Version 4 is cool |
Subversion | For some tasks Homebrew requires a newer version than OS X 10.5 provides |
Zsh | This was a mistake, but it’s a bit late to remove it |
libpng | The version that is bundled with OS X has some notable crash bugs |
libxml2 | The version that is bundled with OS X has some notable crash bugs |
If you find other dupes. They should be reported and removed.
Will your formula be exempted? Possibly. Submit and justify it.
We’re hoping other people will maintain forks that contain dupes. Currently AdamV is maintaining a duplicates branch.
Some Quick Examples Before You Get Started
Formulae aren’t that complicated. wget is as simple as it gets.
And then Git and flac show more advanced functionality.
Grab the URL
All you need to make a formula is a URL to the tarball.
brew create https://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 'https://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
Fill in the Homepage
We don’t accept formula 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.
Fill in the MD5
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.
Check the build system
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).
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 (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.
Specifying other formula as dependencies
class Foo < Formula depends_on 'jpeg' end
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 what libraries a binary link 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)
Specifying gems, etc. as dependencies
Homebrew doesn’t package the already packaged. If you 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
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.
A Quick Word on Naming
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 with the aka
function.
Commit
Everything is built on git, so contribution is easy:
brew install git brew update # required in more ways than you think 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 formula they are interested in.
Push
Now you just need to push back to Github. If you haven’t forked Homebrew yet, then the easiest way to do that is with the github gem:
gem install github github fork git push myname master
Now all that remains is to tell a Homebrew collaborator about your formula so it can be merged.
We prefer that you create an issue rather than send a pull request, as this allows some discussion about the patch before it is accepted (if required, usually it’s fine as is), and more easily allows us to delegate which collaborator will apply the merge.
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
Convenience Tools
bin.install(‘foo’)
You’ll see stuff like that in other formula. This installs the file foo into the Formula’s bin directory (`/usr/local/Cellar/pkg/0.1/bin`).
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 |contents| contents.gsub! /foo/, 'bar' end
Make sure you modify `contents` — 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 |contents| contents.remove_make_var! %w[CFLAGS LDFLAGS CC LD] contents.change_make_var! "CC", ENV.cc end
Patches
Return an array or string from your re-implemented patches function:
def patches # fixes doc install on OS X 10.6 "https://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 => "https://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) :p2 => 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.
Creating the diff
We use git for everything don’t you know?
AdamV came up with this ingenious method:
brew install -i foo … git init git add -A … git diff | pbcopy brew edit foo
Now just paste into the formula after __END__
.
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!
HEAD formula
HEAD formula 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, cvs and hg URLs.
Specifying the Download Strategy explicitly
Override the Formula::download_strategy function. Return the class type. Eg:
class Foo < Formula def download_strategy GitDownloadStrategy end end
This can be useful when used with a local repo, where specifying a url would not let you specify how to access it. For example:
class Bar < Formula def download_strategy; GitDownloadStrategy; end head '/users/abc/src/git.git' end