Posts Tagged ‘fink’

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:

  1. if [ -n "$SW" ]
  2. then export CFLAGS="-I$SW/include"
  3. export LDFLAGS="-L$SW/lib"
  4. export CXXFLAGS="$CFLAGS"
  5. export CPPFLAGS="$CXXFLAGS"
  6. export ACLOCAL_FLAGS="-I \"$SW/share/aclocal\""
  7. export PKG_CONFIG_PATH="$SW/lib/pkgconfig"
  8. export PS1="[$SW_DISPNAME \\W@$(hostname -s)]\\\$ "
  9. . "$SW/bin/init.sh"
  10. export PATH=~/bin:"$PATH"
  11. 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:

  1. #!/bin/bash
  2.  
  3. FINK=${1:-main}
  4.  
  5. case "$FINK" in
  6. main)
  7. SW=/Volumes/SandBox/fink/sw
  8. SW_DISPNAME="fink"
  9. ;;
  10. dev)
  11. SW=/Volumes/SandBox/fink/dev-sw
  12. SW_DISPNAME="fink-dev"
  13. ;;
  14. *) echo "Unknown fink install '$FINK'" >&2 ; exit 1
  15. esac
  16.  
  17. export SW SW_DISPNAME
  18. 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.


  1. 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-mysql as opposed to compiling it by hand.) 

  2. 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.dylib will always use it, even if /sw/lib/libexpat.1.dylib exists. 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 to ls

  3. 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.