Use Both Homebrew and Macports on Your OS X

Homebrew and Macports are two excellent package managers on OS X. At most of the time, Homebrew is fair enough: it has a large package collections. But sometimes, there are just some packages not available in Homebrew while they are in Macports. Although having both of them installed is not recommended, I still want to give it a try.

The basic rules here are using Homebrew packages as much as possible. When one package is not available in Homebrew, install it from Macports (you will soon see why). We will wrap the executables installed by Macports with suitable Environmental Variables.

Please note that this post only presents a workaround. It is not garanteed to work, and it is possible that it does not work under some certain circumstances.

Install Homebrew and Macports

Following the installation instructions on their websites (Homebrew and Macports) to install both of them. But remember not to modify environmental variables related to Macports, such as PATH, CPATH.

Wrap Macports Executables with Appropriate Environmental Variables When You Use them

Before running any executables, we need to prepend $MACPORTS_PREFIX/bin:$MACPORTS_PREFIX/sbin to the PATH environmental variable, etc. where $MACPORTS_PREFIX is the installation prefix of Macports (by default it is /opt/local. To do this, I wrote a wrapper script use_macports.sh:

#!/bin/bash

## Wrap Macports command (any executables installed by Macports).

if [ "$#" -le 0 ]; then
  echo "Usage: $0 command [arg1, arg2, ...]" >&2
  exit 1
fi

if [[ -z $MACPORTS_PREFIX ]]; then
  MACPORTS_PREFIX='/opt/local'
fi


export PATH="$MACPORTS_PREFIX/bin:$MACPORTS_PREFIX/sbin:$PATH"
export CPATH="$MACPORTS_PREFIX/include:$CPATH"

command=$1

shift

exec $command $*

Copy this script to any directory in your PATH environmental variable. Then, to wrap any executables installed by Macports, just run:

use_macports.sh executable args1 args2 ...

For example, you need to run port command to install texlive:

use_macports.sh port install texlive

For convenience, if you want to run port (or any other executables installed from MacPorts, e.g. pdflatex if texlive is installed from MacPorts) directly without the lengthy command above, you can wrap frequently used commands into scripts. For example, assuming ~/bin is in your PATH environmental variable:

echo 'exec use_macports.sh port $*' >~/bin/port
chmod +x ~/bin/port

Executing the above line will give you a new “port” command which is actually a wrapper. So now you see, although it’s not a big deal, Macports packages require more energy to set up. That’s why I try to use Homebrew if the package is available there.

When Using Homebrew to Install Packages …

When using Homebrew to install packages, one noticeable thing is that sometimes /opt/local may interference the build of the packages. In this case, you might want to try to run brew install --env=std package_name and brew install --env=super package_name to see whether the build works, or you even need to rename /opt/local temporarily.

Myself

I’ve used Macports to install evince and texlive, and there is no problem to use this method by far. If you have any problems or concerns please feel free to comment.

Update: There is now a known issue when using kpsewhich with this method. It seems that kpsewhich cannot detect its parameters correctly using this method.

10 thoughts on “Use Both Homebrew and Macports on Your OS X

  1. Dan Jones

    Why in the world would you create a script when you could just modify your PATH in bashrc?
    Also, that last line could be exec “$@”.
    Using $* doesn’t work if you have spaces in any of your arguments as well.
    But all of that doesn’t matter because the whole thing is ridiculous.

    Just add your export lines to your .bashrc, and you’re good.

    Reply
    1. Hong Post author

      It’s been quite a while, but I remember that not everything works if only PATH is updated. TeXLive, if I remember correctly, is one of such package.

      Reply
  2. jonquimbly

    Wrap the execution of CLI utils installed by Macports with a lengthy wrapper script? A brilliant idea that could only come from the anti-Macports community of Homebrew.

    I was hoping to find equal coexistence on this here, considering that I use Macports and its installed binaries throughout the day, and wanted to install a single binary from brew. Instead, I find prophylaxis like Macports was teh HIV -but, that’s what we’ve come to expect Homebrewers.

    Reply
  3. Clemens

    Please do not set DYLD_LIBRARY_PATH on OS X. It’s very likely it doesn’t do what you expect it to and is really only meant to be a tool for developers to test compatibility against newer library versions. It’s important to know that this variables does *not* behave like LD_LIBRARY_PATH on Linux.

    All executables should already work without DYLD_LIBRARY_PATH set *even* in the presence of homebrew, unless you did also set DYLD_LIBRARY_PATH for homebrew (which should not be required there either, but I’m no pro on that).

    In the remote case that a binary doesn’t work, you should set DYLD_FALLBACK_LIBRARY_PATH. Please see man 1 dyld for more information on what these variables do and how to set them correctly.

    As rationale: Libraries are referenced using absolute paths on OS X. Setting DYLD_LIBRARY_PATH will make the loader ignore those absolute paths and always use the version form DYLD_LIBRARY_PATH. If e.g. a binary links against /usr/lib/libcurl.4.dylib and you have curl installed via MacPorts and you set DYLD_LIBRARY_PATH to the MacPorts prefix, that binary might load an incompatible version of libcurl, whereas it would have worked fine without the variable. DYLD_FALLBACK_PATH on the other hand is only used if the library isn’t found using the path baked into the binary.

    I’m not sure what you’re trying to achieve by setting CPATH, either – MacPorts will ignore your current environment setup anyway, so unless you run use_macports.sh clang, it won’t have an effect at all.

    Note that some builds in MacPorts will still fail using this setup, because it is virtually impossible to stop compilers from looking for headers in /usr/local/include and libraries in /usr/local/lib, which is where homebrew might have put some of it’s stuff. The only safe way to avoid that at the moment is to move /usr/local aside while installing ports from MacPorts – that’s the reason why MacPorts generally thinks putting stuff into /usr/local is a bad idea – you cannot possibly ignore it without *a lot* of effort. I have done that effort for MacPorts 2.3, though, which will contain a sandbox that will hide /usr/local (and all other non-standard files) from the build systems within MacPorts, which should make builds in the presence of homebrew more robust. This feature is called “trace mode” and is activated using the -t switch. Please wait until 2.3 is released before using it, though, because it’s broken in the current version.

    Reply
    1. Hong Xu

      Thanks for your comments here.

      According to the manual,

      “This (DYLD_LIBRARY_PATH) is a colon separated list of directories that contain libraries. The dynamic linker searches these directories before it searches the default locations for libraries. It allows you to test new versions of existing libraries.

      “For each library that a program uses, the dynamic linker looks for it in each directory in DYLD_LIBRARY_PATH in turn. If it still can’t find the library, it then searches DYLD_FALLBACK_FRAMEWORK_PATH and DYLD_FALLBACK_LIBRARY_PATH in turn.”

      In this case, what we need to do is to search for the libraries first, then other locations, since the binaries installed by MacPorts “prefers” depending on the libraries in the MacPorts directory. Most MacPorts binaries would not link against the system libraries unless necessary — they always tend to use their own libraries. See the [FAQ](https://trac.macports.org/wiki/FAQ#syslibs) section “Will MacPorts link to system libraries rather than its own?”. That’s why I use DYLD_LIBRARY_PATH.

      CPATH — I’m trying to cover as many environmental variables as possible.

      Yes, I agree with you that this setup won’t work for all situations. But this is a simple workaround, for those people who need to run both of the two package managers together. If it works, it’s great; if it doesn’t work, we then try something else.

      Thanks for your work on MacPorts — I’ll wait for the release of 2.3.

      Reply
      1. Clemens

        The whole point of linking on OS X is that you don’t need to tell the load where the libraries needed by a binary are because the libraries use absolute paths.

        Take for example the geoipupdate binary installed by the libgeoip port. It links against the following libraries:

        $> otool -L geoipupdate
        geoipupdate:
            /opt/local/lib/libGeoIPUpdate.0.dylib (compatibility version 1.0.0, current version 1.0.0)
            /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
            /opt/local/lib/libGeoIP.1.dylib (compatibility version 7.0.0, current version 7.1.0)
            /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
        

        If you start this binary without

        DYLD_LIBRARY_PATH

        every one of the libraries will be found as expected, since they are all referenced using absolute paths. Setting the variable is thus not required for the binaries to work as expected.

        If you start this binary with

        DYLD_LIBRARY_PATH=/opt/local/lib

        the loader will actually ignore the component of the path leading up to the basename and replace it with the value of

        DYLD_LIBRARY_PATH

        . In this case, it will not load

        libz.1.dylib

        from

        /usr/lib

        like it should, but it will replace this library with the MacPorts version, even though that one might not be compatible, crashing the program.

        There should never be any cases where libraries aren’t referenced using absolute paths in MacPorts-installed binaries, because MacPorts has a check phase called “rev-upgrade” that would detect such problems and consider them an error.

        You also quoted one of the reasons why

        DYLD_LIBRARY_PATH

        shouldn’t be used:

        The dynamic linker searches these directories before it searches the default locations for libraries. It allows you to test new versions of existing libraries.

        Testing is really the only reason why this variable exists – it should never be used in deployment and be users.

        Reply

Leave a Reply

Your email address will not be published.