Friday, July 14, 2006

fink, apt and dpkg

Under OS X there doesn't appear to be any official (or de facto standard) full-featured package-management system for third-party software. Instead, there's the minimal "installer" command supplied by Apple, and several third-party alternatives. Installer lacks several desirable features:
  • It's not aware of dependencies
  • It provides no mechanism for un-installing packages
  • It provides no easy way of finding information about installed packages (although it does maintain such metadata in /Library/Receipts)
Among the alternatives to installer, one that stands out is the "fink" project.

The fink project consists of
  1. A port of the Debian "apt" and "dpkg" package-management tools to OS X
  2. A set of tools for automating the creation of binary packages in .deb format from source code
  3. A set of repositories of packages available for OS X
In the following I'll talk about installing fink, using a few of the commands it provides, and integrating fink into our general-purpose nightly update system.

The fink project provides a binary installer for PowerPC- and Intel-based OS X, or you can install from source code. I opted for the latter. The source code can be downloaded from the fink project source download page. In order to compile it (and in order to later use fink to build binary packages) you'll need to have Apple's Xcode development environment installed. By default, fink will be installed in a new directory tree called "/sw". This tree will contain the fink binaries (fink, apt, dpkg, etc.), configuration files, and most (but not quite all) of the software you install using fink (the notable exception is X11, which gets installed directly under /usr.). Executables installed through fink will (again , mostly but not quite always) be put into /sw/bin, so you'll need to add this to your search path. Some tools used to build and install packages will live in /sw/sbin, so this should be in root's search path.

The Debian apt and dpkg commands are analogous to yum and rpm in the Red Hat/Fedora world I'm more familiar with. Dpkg is a tool for installing, removing and reporting information about individual packages, and apt provides a framework for automatically resolving dependencies and fetching packages from repositories. The fink command provides an additional layer, capable of fetching source code from repositories and creating binary packages from it.

Here are a few useful dpkg commands:
  • dpkg -l will list all of the currently-installed packages
  • dpkg -L package will list the files installed by package
  • dpkg -s package will show meta-information about an installed package
  • dpkg -i package.deb will install package.deb
  • dpkg -r package will uninstall package

The most useful fink command for our purposes is "fink build package", which will create a binary .deb package from source code fetched from a repository. For example, the command "fink build tree" will fetch the source code for the useful "tree" command, then compile it and pack into a .deb file under /sw/fink/dists/stable/main/binary-darwin-i386/utils . This binary package could then be installed (on this or any other machine) with "dpkg -i".

Using only these tools, we can now add fink-produced .deb packages to our local package repository, along with dmg, app and pkg packages. To do this, I just wrote a trivial script called "deb-install" that looks like this:
exec dpkg -i $*
This will automatically be invoked when our pkgupdate script encounters a .deb file.

Notice that I've completely skipped apt for now. By doing this, I lose a couple of things:
  • Automatic dependency resolution. I have to manually sort out the packages, and make sure dependencies are satisfied
  • Automatic updates. I need to keep an eye out for updated packages, and add them to my repository
I think we can live without these for a while. The positive points of this solution:
  • I can keep all packages in one place
  • I can manage the complete list of packages (of all kinds) from one place
  • I have complete control over what is and isn't installed
Ultimately, I'll probably move apt into the process, by creating my own apt repository (or, if possible, making the current "universal" repostory apt-compatible), but I'll probably always want to check updated packages before I release them onto production machines.

I've now added many .deb packages to the repository and /common/manager/update.pkglist, and things seem to be working fine.

No comments: