Recently I had the need to create a homebrew external command to extend homebrew default behavior. My goal is to extend brew deps
command by creating a new command brew deps-group
. Executing the command will list all top-level packages in homebrew that is related to each other by common dependencies, doing so will let me know if it safe for me to actually delete the package without breaking other packages.
Structure
To achieve this, the overall file structure consist of:
- Formula - this tells homebrew about where to get file for your packages and their dependencies.
- Application - actual implementation and I wrote my simple app in Python.
homebrew-deps-group/
|------ Formula/
| |--- brew-deps-group.rb
|------ brew-deps-group.rb
|------ brew-deps-group.py
I noticed that your package needs to start with homebrew-app-name
.
Formula
Explanation in comments
require "formula"
# this will translate to brew-deps-group as the formula name
class BrewDepsGroup < Formula
homepage "https://github.com/shulhi/homebrew-deps-group"
# this is the source of your package
# when you run brew install, it will be installed from this source
url "https://github.com/shulhi/homebrew-deps-group.git"
# my package depends on python
depends_on :python if MacOS.version <= :snow_leopard
# this is the dependency for my formula
resource "networkx" do
url "https://pypi.python.org/packages/source/n/networkx/networkx-1.9.1.tar.gz"
sha1 "ac2db3b29c7c4d16435f2a7ebe90fc8bd687b59c"
end
# skip cleaning path in formula
skip_clean "bin"
def install
# since my python application has other dependency, we need to install it
# and ensure it is installed in /vendor
# to avoid pollution with system packages
ENV.prepend_create_path "PYTHONPATH", libexec/"vendor/lib/python2.7/site-packages"
%w[networkx].each do |r|
resource(r).stage do
system "python", *Language::Python.setup_install_args(libexec/"vendor")
end
end
# if your python application has setup.py, then uncomment this line
# this will install your application in the correct location
# ENV.prepend_create_path "PYTHONPATH", libexec/"lib/python2.7/site-packages"
# system "python", *Language::Python.setup_install_args(libexec)
# copies your python application to bin
# since we don't have any, I don't need this
# instead, we are just going to copy the actual file (see next section below)
# bin.install Dir[libexec/"bin/*"]
# bin.env_script_all_files(libexec/"bin", :PYTHONPATH => ENV["PYTHONPATH"])
# copies all files to bin so we can execute the files from other scripts
bin.install 'brew-deps-group.py'
bin.install 'brew-deps-group.rb'
(bin + 'brew-deps-group.py').chmod 0755
(bin + 'brew-deps-group.rb').chmod 0755
end
end
Entry point
So our application/command needs an entry point, i.e. when I run brew deps-group
, some file should be executed right? In our case, this will call brew-deps-group.rb
which in turn will call up brew-deps-group.py
.
require 'keg'
require 'formula'
module BrewDepsGroup
module_function
def run
if ['-h', '?', '--help'].include? ARGV.first
puts USAGE
exit 0
end
if ['-a', '--all'].include? ARGV.first
# call our python executable file
# we don't need python file.py since the file is already executable and is in our path
system "brew-deps-group.py", "all"
end
end
end
BrewDepsGroup.run
exit 0
#! /usr/bin/env python
# our dependency we declared in our formula can now be used
import networkx as nx
def main(argv):
option = argv[0]
# do something
print "hello world"
if __name__ == '__main__':
main(sys.argv[1:])
If you need the full source code, you can find it here