Homebrew: Formula Development Guide
Table of Contents
Section titled “Table of Contents”- Homebrew Formulas
- Creating Formulas
- Testing and Building
- Development Workflow
- Advanced Formula Patterns
- Dependencies
- Installation Strategies
- Testing Formulas
- Troubleshooting
- Publishing Formulas
- Versioning and Updates
- Best Practices
- Useful Commands
- Resources
Homebrew Formulas
Section titled “Homebrew Formulas”Guide to creating, testing, and maintaining Homebrew formulas for macOS package management.
Creating Formulas
Section titled “Creating Formulas”Generate Formula Template
Section titled “Generate Formula Template”brew create https://example.com/foo-1.0.tar.gzThis creates a formula template in:
/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/
Basic Formula Structure
Section titled “Basic Formula Structure”class Foo < Formula desc "Description of foo" homepage "https://example.com/foo" url "https://example.com/foo-1.0.tar.gz" sha256 "abc123..." license "MIT"
depends_on "cmake" => :build depends_on "openssl@3"
def install system "./configure", "--prefix=#{prefix}" system "make", "install" end
test do system "#{bin}/foo", "--version" endendTesting and Building
Section titled “Testing and Building”Build from Source
Section titled “Build from Source”HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source ./iproute2.rbFlags:
HOMEBREW_NO_INSTALL_FROM_API=1- Disable bottle API, force local formula--build-from-source- Compile from source instead of using prebuilt bottles
Run Audits
Section titled “Run Audits”Check formula for common issues:
brew audit --strict iproute2Options:
--strict- Stricter checks for official submission--online- Check URLs are reachable--new-formula- Checks specific to new formulas
Run Tests
Section titled “Run Tests”brew test iproute2This runs the test do block defined in the formula.
Style Check
Section titled “Style Check”brew style iproute2Checks Ruby code style compliance using RuboCop.
Fix style issues automatically:
brew style --fix iproute2Development Workflow
Section titled “Development Workflow”Edit Existing Formula
Section titled “Edit Existing Formula”brew edit iproute2Opens formula in $EDITOR.
Install with Debugging
Section titled “Install with Debugging”brew install --debug --verbose iproute2--debug- Drop into debugger on error--verbose- Show compilation output
Force Reinstall
Section titled “Force Reinstall”brew reinstall iproute2Uninstall
Section titled “Uninstall”brew uninstall iproute2Advanced Formula Patterns
Section titled “Advanced Formula Patterns”Multiple Versions
Section titled “Multiple Versions”class FooAT2 < Formula desc "Foo version 2.x" url "https://example.com/foo-2.0.tar.gz" # ...
keg_only :versioned_formulaendPython Formulas
Section titled “Python Formulas”class MyPythonApp < Formula include Language::Python::Virtualenv
depends_on "python@3.11"
resource "requests" do url "https://files.pythonhosted.org/..." sha256 "abc123..." end
def install virtualenv_install_with_resources endendNode.js Formulas
Section titled “Node.js Formulas”class MyNodeApp < Formula depends_on "node"
def install system "npm", "install", *Language::Node.std_npm_install_args(libexec) bin.install_symlink Dir["#{libexec}/bin/*"] endendGo Formulas
Section titled “Go Formulas”class MyGoApp < Formula depends_on "go" => :build
def install system "go", "build", *std_go_args(ldflags: "-s -w") endendDependencies
Section titled “Dependencies”Types of Dependencies
Section titled “Types of Dependencies”depends_on "cmake" => :build # Build-time onlydepends_on "pkg-config" => :builddepends_on "openssl@3" # Runtime dependencydepends_on "python@3.11" => [:build, :test]depends_on :macos => :big_sur # Minimum macOS versiondepends_on :xcode => "12.0" # Xcode requirementOptional Dependencies
Section titled “Optional Dependencies”depends_on "readline" => :optionalInstallation Strategies
Section titled “Installation Strategies”Standard Install
Section titled “Standard Install”def install system "./configure", "--prefix=#{prefix}" system "make", "install"enddef install system "cmake", "-S", ".", "-B", "build", *std_cmake_args system "cmake", "--build", "build" system "cmake", "--install", "build"endMeson/Ninja
Section titled “Meson/Ninja”def install system "meson", "setup", "build", *std_meson_args system "meson", "compile", "-C", "build", "--verbose" system "meson", "install", "-C", "build"endManual File Placement
Section titled “Manual File Placement”def install bin.install "myapp" lib.install Dir["lib/*"] (etc/"myapp").install "config.yml" doc.install "README.md"endTesting Formulas
Section titled “Testing Formulas”Test Block
Section titled “Test Block”test do # Test version output assert_match version.to_s, shell_output("#{bin}/foo --version")
# Test functionality (testpath/"test.txt").write("content") assert_equal "content", shell_output("#{bin}/foo read test.txt").strip
# Test compilation system ENV.cc, "test.c", "-o", "test", "-L#{lib}", "-lfoo" system "./test"endTest Assertions
Section titled “Test Assertions”assert_match /pattern/, stringassert_equal expected, actualassert_predicate path, :exist?refute_predicate path, :exist?Troubleshooting
Section titled “Troubleshooting”Check Formula Syntax
Section titled “Check Formula Syntax”brew audit --strict --online ./formula.rbDebug Installation
Section titled “Debug Installation”brew install --debug --verbose --build-from-source ./formula.rbWhen build fails, you’re dropped into shell in build directory.
Common Issues
Section titled “Common Issues”SHA256 mismatch:
# Calculate correct SHA256shasum -a 256 downloaded_file.tar.gz
# Update formula with correct hashMissing dependencies:
# Check what libraries the binary needsotool -L /usr/local/bin/myapp
# Add missing dependencies to formulaBuild errors:
# Check config.log for autotools projectscat config.log
# Enable verbose make outputsystem "make", "V=1", "install"Publishing Formulas
Section titled “Publishing Formulas”Tap Structure
Section titled “Tap Structure”Create a custom tap:
brew tap-new username/repoDirectory structure:
homebrew-repo/└── Formula/ ├── foo.rb └── bar.rbInstall from Custom Tap
Section titled “Install from Custom Tap”brew tap username/repobrew install fooSubmit to Homebrew Core
Section titled “Submit to Homebrew Core”- Fork homebrew-core repository
- Create formula in
Formula/directory - Test thoroughly:
Terminal window brew audit --strict --online foobrew test foobrew style foo - Submit pull request
Bottle (Binary Package) Creation
Section titled “Bottle (Binary Package) Creation”brew install --build-bottle foobrew bottle fooThis creates a .tar.gz bottle file and JSON with bottle DSL.
Add bottle block to formula:
bottle do sha256 cellar: :any, arm64_ventura: "abc123..." sha256 cellar: :any, ventura: "def456..."endVersioning and Updates
Section titled “Versioning and Updates”Update Formula Version
Section titled “Update Formula Version”brew bump-formula-pr foo \ --url=https://example.com/foo-2.0.tar.gz \ --sha256=abc123...Check for Outdated Formulas
Section titled “Check for Outdated Formulas”brew outdatedbrew upgrade fooBest Practices
Section titled “Best Practices”-
Use standard helpers:
std_cmake_argsstd_meson_argsstd_configure_args
-
Test thoroughly:
- Run audit before submitting
- Test on multiple macOS versions
- Include meaningful test block
-
Handle edge cases:
- Different architectures (ARM vs Intel)
- macOS version differences
- Optional features
-
Documentation:
- Clear
descandhomepage - Document caveats in
caveatsblock - Add deprecation notices if needed
- Clear
-
Respect conventions:
- Follow naming conventions
- Use proper dependency types
- Include license information
Useful Commands
Section titled “Useful Commands”# Formula informationbrew info foobrew desc foo
# Dependenciesbrew deps foobrew uses --installed foo
# Installation locationsbrew --prefix foobrew --cellar foo
# Cleanupbrew cleanup foobrew unlink foobrew link foo
# Formula managementbrew pin foo # Prevent updatesbrew unpin foobrew switch foo 1.0 # Switch versions