Installing python-ldap in Mac OS X Lion

The Problem

Normally, you could install python-ldap like this:

sudo pip install python-ldap

That will appear to work fine, but then when you try to use the ldap module, you get this:

% python -c 'import ldap'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Python/2.7/site-packages/ldap/__init__.py", line 22, in <module>
    from _ldap import *
ImportError: dlopen(/Library/Python/2.7/site-packages/_ldap.so, 2): Symbol not found: _ldap_create_assertion_control_value
  Referenced from: /Library/Python/2.7/site-packages/_ldap.so
  Expected in: flat namespace
 in /Library/Python/2.7/site-packages/_ldap.so

In a nutshell, /usr/include/ldap.h is a lie. It's the header for OpenLDAP 2.4.23, which is what comes bundled with Lion. All the binaries like ldapsearch, slapd, etc. are also at this version, but one thing was overlooked: the libraries.

% otool -L /usr/lib/libldap_r.dylib
/usr/lib/libldap_r.dylib:
    /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP (compatibility version 1.0.0, current version 2.2.0)
    /usr/lib/libsasl2.2.dylib (compatibility version 3.0.0, current version 3.15.0)
    /usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
    /usr/lib/libssl.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 46.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 633.0.0)
    /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55010.0.0)
    /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 395.6.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.0.0)

That's OpenLDAP 2.2.0, which is God knows how old. WTF, Apple?

The Work-around

One way around this is to use the older 2.3.13 version of python-ldap, but who wants that, right?

Install the OpenLDAP Libraries

What I did was set up Homebrew to install only the libraries and headers from OpenLDAP 2.4.23, then build python-ldap against them. If you don't use Homebrew for some reason, I'm sure you can figure out how to do things by hand after looking at the example.

Here's the formula for Homebrew. Save it to /usr/local/Library/Formula/openldap-libs.rb.

require 'formula'

class OpenldapLibs < Formula
  url 'ftp://ftp.OpenLDAP.org/pub/OpenLDAP/openldap-stable/openldap-stable-20100719.tgz'
  homepage 'http://www.openldap.org/'
  md5 '90150b8c0d0192e10b30157e68844ddf'
  version '2.4.23'

  def install
    system "./configure", "--disable-debug", "--prefix=#{prefix}",
                          "--disable-slapd", "--disable-slurpd"

    # empty Makefiles to prevent unnecessary installation attempts
    makefile = "all:\ninstall:\n"
    unwanted_paths = ['clients', 'servers', 'tests', 'doc']
    unwanted_paths.each do |upath|
      File.open(Dir.getwd + '/' + upath + '/Makefile', 'w') {|f| f.write(makefile)}
    end

    system "make install"
    File.rename("#{prefix}/etc/openldap/ldap.conf", "#{prefix}/etc/openldap/ldap.conf.backup")
    File.symlink('/etc/openldap/ldap.conf', "#{prefix}/etc/openldap/ldap.conf")
  end
end

Then install it:

% brew install openldap-libs
==> Downloading ftp://ftp.OpenLDAP.org/pub/OpenLDAP/openldap-stable/openldap-stable-20100719
==> ./configure --disable-debug --prefix=/usr/local/Cellar/openldap-libs/2.4.23 --disable-sl
==> make install
/usr/local/Cellar/openldap-libs/2.4.23: 19 files, 1.5M, built in 49 seconds

Things to be aware of:

  • This does some hacky things to prevent installation of duplicate client tools and man pages. The ones you have bundled with the OS are fine. We just need the libraries.
  • This symlinks /usr/local/etc/openldap/ldap.conf to /etc/openldap/ldap.conf. That way, you don't have to duplicate the settings that you've likely already put there. If you happened to have something in /usr/local/etc/openldap/ldap.conf already, it'll get backed up as ldap.conf.backup. (I originally tried to just have the libraries point to the existing system's config files, but it attempts to install them, which is unnecessary and requires root privileges which Homebrew typically shouldn't have.)

Install python-ldap

You need to modify the default setup.cfg, so installing python-ldap is a manual process. Download the latest tarball from http://pypi.python.org/pypi/python-ldap/. Extract it and modify the _ldap section of setup.cfg to match what's shown below.

[_ldap]
library_dirs = /usr/local/lib
include_dirs = /usr/include/sasl
extra_compile_args = -g -arch x86_64
extra_objects = 
libs = ldap_r lber sasl2 ssl crypto

Now build and install:

% python setup.py build
extra_compile_args: -g -arch x86_64
extra_objects: 
include_dirs: /usr/include/sasl
library_dirs: /usr/local/lib
libs: ldap_r lber sasl2 ssl crypto
running build
running build_py
…blah blah blah…
% sudo python setup.py install
…blah blah blah…

You should have a working module at this point.

% python -c 'import ldap; print ldap.__version__'
2.4.3

And for what it's worth, I'm assuming something similar should work for Snow Leopard.

blog comments powered by Disqus