Package managers and dependency managers are essentially the same thing : They both install software you need. If you want to load an application onto your computer, you need a package manager. If you are developing software and want to import some libraries, you need a dependency manager. Both do mostly the same thing. They install software for you, automatically find and download dependencies, update to the latest version with an easy command, and generally make managing software installation much easier.

I now have enough of these managers on my development machine that I am starting to become bewildered : bower, brew, npm, maven, gem, cocoapods, pkgutil, and maybe some that I have missed. Each of these differ in scope, purpose, and behavior. I run them all on the Mac OSX command line.

pkgutil is the built-in package manager for Mac OSX, and is responsible for installing the very operating system itself. If you happen to have a .pkg file (somewhat rare), you can use this utility to install it. You’re more likely to use this tool on the command line to see what Apple put on your computer. pkutil --pkgs will give you the complete list of packages installed on your computer by Apple through your OS or applications from the App Store. Pick a package name like Essentials and you can see what files were installed using pkgutil --files com.apple.pkg.Essentials. An even more power feature is the ability to tell which package a particular file belongs to. Try it with pkgutil --file-info /bin/ls.

Sidequest! Let’s see if I can figure out which package manager I used to install NodeJS on my computer! pkgutil --file-info /usr/local/bin/node tells me… it was not installed using pkgutil. Well, the prefix /usr/local should have been a clue. Homebrew puts its installations there, as do other package managers.

Homebrew and MacPorts are package managers backed by a central database containing thousands of apps. Homebrew has a sexier web page, so I use it. I heard that it is implemented on top of git, and git rocks my world. Putting an application like NodeJS on your system with Homebrew is as simple as typing brew install node. You can upgrade all of your applications using brew upgrade, or view a list of all installed apps using brew list. You could also see them in /usr/local/Cellar. Brew uses beer puns like that. To a fault.

Homebrew can be installed using ruby, which, like gem, is installed on Mac OSX in the package Essentials.  Speaking of gem: gem is used to install ruby programs. You can use gem list to see a list of all installed packages, or gem list --remote to see a list of all available packages.

And it seems like neither gem, brew, nor pkgutil were used to install my local version of node. Hmm…

When using the package manager npm, it is important to distinguish between global and local installations. A global installation acts like a package manager for system commands, while a local installation acts like a dependency manager for development projects. Several other dependency managers have this split between global and local mode, including bower.

A local npm installation starts with npm init, which creates the package.json file used to track dependencies between frameworks. npm install fontawesome -save will create a node_modules folder containing fontawesome and all dependencies and update the package.json file. Additionally, npm will save the raw package files in your home folder under .npm as a kind of local cache to speed retrieval if you decide to use the same library for another project. This tutorial shows how to install the build tool Gulp using this method.

Global npm installations are intended for system commands, and they are really very different from dependency frameworks. Most npm commands can be followed with -g to make them global installations. npm install bower -g will install bower in /usr/local using subfolders and soft links. In more detail :

?? cd /usr/local/lib/node_modules/bower/

?? ls

CHANGELOG.md README.md lib

LICENSE bin package.json

?? cd /usr/local/bin

?? ls -l bower

bower -> ../lib/node_modules/bower/bin/bower

Bower is a package manager aimed at web development. It is actually built on top of node and npm, so we need those before installing bower. It seems redundant in purpose to npm, but it adds features. Look at a typical bower installation command :

?  ? bower install jquery
bower not-cached    https://github.com/jquery/jquery-dist.git#*
bower resolve       https://github.com/jquery/jquery-dist.git#*
bower checkout      jquery#3.1.0
bower resolved      https://github.com/jquery/jquery-dist.git#3.1.0
bower install       jquery#3.1.0

jquery#3.1.0 bower_components/jquery

Notice how verbose and clear everything is? However, bower does not handle nested dependencies (frameworks that require other frameworks), so that is something that must be handled by manually by the developer.

OK, I have no idea where my /usr/local/bin/node came from, but it’s time to update! brew install node followed by brew link --overwrite node brings node under my management umbrella, and now everything is up to date. Yay!

I’m going to save CocoaPods for another post.

 

One thought on “Too Many Dependency Managers

  1. Project dependencies are more nuanced than I described here. It turns out that npm distinguishes between dependencies (needed for the application), devDependencies (needed for development), and optionalDependencies (not needed, but a minimum version required for optional features to work). npm install should be used with –save, –save-dev, or –save-optional, depending on what is intended.

Leave a Reply