For regular folk, there are two operating systems available for computers: Mac or Windows. But for technical folk, things are much more colorful. Whether on servers, on Virtual Machines, or our private computers, we run OSes such as Linux, various BSD versions, Solaris, or even other, more exotic systems, like IBM AIX, for example. Each can show its strength in a particular situation. What they have in common is that they all need software to run on them.
After all, an OS is merely a vessel for shipping applications that serve business use cases. This is where things start to get problematic for us: we need to install the desired apps on our OS. Even though there are some standards of software redistribution, generally you cannot install two applications in the same way. First of all, the standards are too many; second, not everyone adheres to standards.
Package Managers Explained
This is where package managers enter the picture. They offer a unified interface for installing various software packages, hence the moniker “manager”. No matter what installation method any given piece of software uses, on a Debian GNU/Linux system, you would install it by issuing apt-get install $WHATEVER. Any modern Linux distro has similar means available as well. Much better than needing to figure out the installation method by hand each time, but it’s still not perfect.
You see, the apt-get method works on some systems, others may require yum or dnf. Also, package names may differ so a NodeJS binaries may exist as either node or nodejs depending on which Linux distribution you’re running. Confusing, I know. Let’s try to make it a little less so.
The Story Behind Homebrew
Situation for MacOS was completely different. Since the release of Mac OS X (based on FreeBSD), Mac started to get traction as a development platform of choice. It accelerated even further with the arrival of iOS. One problem developers faced, though, was that the OS itself lacked a proper package manager. The first approaches to the problem relied on the existing managers for other platforms: Fink (based on dpkg/APT) and MacPorts (based on FreeBSD Ports). Together with the rise of Ruby on Rails came a new solution written from the ground up: Homebrew. Since then, Homebrew rose as the de facto standard package manager for the Mac. Even with the advent of Mac App Store it still holds its position, especially with developers using command-line apps.
Problems You Might Face
OK, so every variation of an OS has its packaging system, but you only need to learn the one you use, right? Well, it depends on what you are doing. If you are working alone and on your own machine, it’s simple. But as a freelancer or a member of an agile team lacking homogeneous IT infrastructure, it’s not so simple. When you write a manual on how to install dependencies of your application, who will you write it for? Same goes for developers of open-source applications. If you want people to use your project, you need to write the installation manual. Preparing packages and instructions for every platform out there, however, will quickly become too much of a burden.
There are several solutions to overcome the problem of incompatible package managers. Containers, either in the form of lxc or Docker, encapsulate all of the required software (OS and above) as artifacts with a standard interface. Containers don’t solve the package management problem, of course, but only the container’s maintainer needs to worry about it. Other users are free to do their job.
Language-specific package managers are another option. Though they were originally invented to help in installing third-party libraries, pip, npm and Ruby Gems tend to work as distribution channels for applications as well. Since they rely on the system much less than native languages (like C or C++) and allow installation in home directories, they are quite popular in this space. But each is limited to only one programming language: you likely will need to manage software installed by several of them, and it quickly starts to look like a mess.
More close to the original native package manager are ideas such as PackageKit which abstract away the underlying managers, such as APT or dnf. Unfortunately, it doesn’t seem to have much traction. Projects such as Flatpak, Snappy or AppImage borrow some concepts from containers by bundling together an application with all its dependencies. While a nice concept, they don’t solve the problem of compatibility between different platforms as they target primary desktop Linux.
How Linuxbrew Is Different
The task isn’t completely hopeless, though. The already mentioned Homebrew, although written with Mac in mind, has also been ported to Linux as Linuxbrew. It uses the same Domain Specific Language to describe packages which makes it easy to create new ones. Plus, since Homebrew is open and decentralized, anyone can make their Tap (which is a package repository in Homebrew lingo) and host it on GitHub or by some other means. Even if your project requires the use of software that does not have Homebrew/Linuxbrew packages available, you can create your own and host it internally for your team’s use. Another interesting fact is that Linuxbrew can even run on WSL if you run Microsoft Windows. (Using Linuxbrew this way isn’t officially supported, mind you!)
Since Linuxbrew does not substitute a system package manager and all the software is installed in home directories, it can run on most recent GNU/Linux distributions. What’s more, you can have the same program installed through Linuxbrew and the native package manager. Which of them will run will depend on your system configuration (specifically: the $PATH variable). This means you can have a set of stable packages provided by your OS, but at the same time try out bleeding edge releases obtained with Linuxbrew. No problem with that!
This makes Homebrew/Linuxbrew great for managing binary dependencies of a project. Regardless of Linux distribution you’re running or if you’re running Mac you can store the dependencies in a` Brewfile`. This should be portable thanks to Homebrew on Mac and Linuxbrew on Linux. Those team members that can’t or won’t use `brew install` can still read the file and install the required software by other means. I consider it a step up from elaborate installation manuals that either explain thing for one OS only or try to handle everything at once and still fail to satisfy all its recipients.
The Ultimate Package Manager?
For the projects I work on, whenever I encounter the need to handle dependencies that fall outside Docker’s use cases, I use Homebrew to manage them. This way a repository can have its own
Brewfile with a list of software needed to build or maintain it, much like requirements.txt for Python or Gemfile for Ruby. Code is the best form of documentation as it is understandable by both humans and machines.
But even though Homebrew or Linuxbrew are potent in what they do, there are certain use cases where they are not appropriate. Packages with their own device drivers or otherwise integrating deeply into the operating system itself will not work in Linuxbrew’s case. Neither Virtualbox nor Docker daemon will work correctly, so you still need to handle them in the usual way.
Keeping this in mind, I still encourage you to try it if only for the separation of the actual OS from user’s software.