Switching Finks
One of the open-source projects I contribute to is
Fink, a package manager for OS X; if you’ve
used apt-get or yum on Linux, it provides a similar facility,
allowing you to install, say, GnuPG by running fink install gnupg.
It installs things into its own directory tree, rooted at /sw by
default, to avoid interfering with things shipped by Apple (/,
/usr) or manually installed by the user (/usr/local.) That is, if
you have Fink installed, your system will have /sw/bin, /sw/lib,
/sw/etc, /sw/share/man, &c.
So that you can run things installed in these nonstandard locations,
Fink provides some shell commands in /sw/bin/init.sh which edit
environment variables like PATH and MANPATH to include the /sw/*
directories. Most Fink users have . /sw/bin/init.sh in their
~/.profile, so these commands will be invoked when their shell
starts.
Having my shell automatically pull in Fink at startup doesn’t work for me, though. It’s important to me to have a clean environment available. For instance, when I’m contributing to non-Fink open-source projects, trying to help someone who doesn’t have Fink installed troubleshoot something, or submitting a bug report for a program that interacts with other programs where I have the Fink version installed, but Apple ships a different version with the system.1 (Note that this is only an issue if program A interacts with program B by invoking it as a standalone process without using an absolute path.2)
Also, as a Fink developer, I actually have multiple Fink
installations at different paths,3 and I only want one loaded at a
time; I don’t want to activate /Volumes/SandBox/fink/dev-sw in an
environment where /Volumes/SandBox/fink/sw has already been pulled
in!
It’s much easier to pull Fink stuff in later when I
need it than to undo the changes that /sw/bin/init.sh makes to my
environment. My solution for making it easy to activate a particular Fink installation was to add the following to ~/.bashrc:
if [ -n "$SW" ] then export CFLAGS="-I$SW/include" export LDFLAGS="-L$SW/lib" export CXXFLAGS="$CFLAGS" export CPPFLAGS="$CXXFLAGS" export ACLOCAL_FLAGS="-I \"$SW/share/aclocal\"" export PKG_CONFIG_PATH="$SW/lib/pkgconfig" export PS1="[$SW_DISPNAME \\W@$(hostname -s)]\\\$ " . "$SW/bin/init.sh" export PATH=~/bin:"$PATH" fi
What this does is arranges it so that if I start a new shell with SW
and SW_DISPNAME set, it’ll pull in the Fink installation rooted at
the directory $SW and put $SW_DISPNAME in my shell prompt so that
I can see which environment I’m using. The extra environment
variables before . $SW/bin/init.sh set things up so that if I
compile things by hand, they’ll find and link against Fink-installed
libraries; the PATH setting at the end is because init.sh places
the Fink bin directory at the front of the PATH, and I want my
personal bin directory to come before it.
I run the following script (saved as ~/bin/finkinit) when I want to
pull in Fink:
#!/bin/bash FINK=${1:-main} case "$FINK" in main) SW=/Volumes/SandBox/fink/sw SW_DISPNAME="fink" ;; dev) SW=/Volumes/SandBox/fink/dev-sw SW_DISPNAME="fink-dev" ;; *) echo "Unknown fink install '$FINK'" >&2 ; exit 1 esac export SW SW_DISPNAME exec /bin/bash
This gives me a subshell with Fink turned on, which I can exit out of
when I want to return to a clean environment. If I run it as finkinit, I get my main Fink installation, or I can run finkinit dev to get an alternate Fink.
-
Yes, this actually happens somewhat frequently. Sometimes Fink has a newer version of a program than the OS (e.g. Subversion 1.4.6 vs. 1.4.4), or sometimes the Fink version has more extensions enabled (I use Fink’s Apache because it lets me use Fink’s PHP, which in turn lets me install PHP’s MySQL extension by doing
fink install php5-apache2-ssl-mysqlas opposed to compiling it by hand.) ↩ -
Unlike Linux, executables on Darwin have the absolute paths to their shared libraries hardcoded in the binary, so a program linked against
/usr/lib/libexpat.1.dylibwill always use it, even if/sw/lib/libexpat.1.dylibexists. Linux, on the other hand, uses a search path mechanism at runtime to find the libraries, similar to the way the shell figures out which program to invoke when you command it tols. ↩ -
At the moment, one for my personal use and a clean one for testing packages I maintain in an environment without extra packages installed. This is important for testing that you’ve declared all of the necessary dependencies. ↩
January 6th, 2008 at 09:27
Nice! I’ve occasionally been tripped up by having fink installed, and fink versions of things, and forgetting that. This is an excellent solution that lets you go about doing what you want to do without requiring a lot of extra work when you want to do it. Good example of frontloading the effort to save a lot of time and energy later. grin