[plain text]

#!/usr/bin/env python

import ez_setup

import sys
import os
import glob
import site
import platform

if 'MallocStackLogging' in os.environ:
    del os.environ['MallocStackLogging']
if 'MallocStackLoggingNoCompact' in os.environ:
    del os.environ['MallocStackLoggingNoCompact']

# See the news file:

# We need at least Python 2.3
MIN_PYTHON = (2, 3)

if sys.version_info < MIN_PYTHON:
    vstr = '.'.join(map(str, MIN_PYTHON))
    raise SystemExit('PyObjC: Need at least Python ' + vstr)

if int(os.uname()[2].split('.')[0]) >= 10:

# Some PiPy stuff
PyObjC is a bridge between Python and Objective-C.  It allows full
featured Cocoa applications to be written in pure Python.  It is also
easy to use other frameworks containing Objective-C class libraries
from Python and to mix in Objective-C, C and C++ source.

Python is a highly dynamic programming language with a shallow learning
curve.  It combines remarkable power with very clear syntax.

The installer package installs a number of Xcode templates for
easily creating new Cocoa-Python projects.

PyObjC also supports full introspection of Objective-C classes and
direct invocation of Objective-C APIs from the interactive interpreter.

PyObjC requires MacOS X 10.4 or later.  This beta release requires
MacOS X 10.5.

from setuptools import setup, Extension, find_packages
from setuptools.command import build_ext, install_lib
import os

class pyobjc_install_lib (install_lib.install_lib):
    def get_exclusions(self):
        result = install_lib.install_lib.get_exclusions(self)
        for fn in install_lib._install_lib.get_outputs(self):
	    if 'PyObjCTest' in fn:
                result[fn] = 1

	return result

class pyobjc_build_ext (build_ext.build_ext):
    def run(self):
        extensions = self.extensions
        self.extensions = [
                e for e in extensions if'PyObjCTest') ]
        self.extensions = extensions

def frameworks(*args):
    lst = []
    for arg in args:
        lst.extend(['-framework', arg])
    return lst

def IfFrameWork(name, packages, extensions, headername=None):
    Return the packages and extensions if the framework exists, or
    two empty lists if not.
    import os
    for pth in ('/System/Library/Frameworks', '/Library/Frameworks'):
        basedir = os.path.join(pth, name)
        if os.path.exists(basedir):
            if (headername is None) or os.path.exists(os.path.join(basedir, "Headers", headername)):
                return packages, extensions
    return [], []

# Double-check
if sys.platform != 'darwin':
    print "You're not running on MacOS X, and don't use GNUstep"
    print "I don't know how to build PyObjC on such a platform."
    print "Please read the ReadMe."
    print ""
    raise SystemExit("ObjC runtime not found")

from distutils.sysconfig import get_config_var
cc = get_config_var('CC')


if cc == 'XXXgcc':
    # This is experimental code that tries to avoid refering to files in 
    # /Library/Frameworks or /usr/local.
    # NOTE: This is not enabled by default because the linker will still look
    # in /usr/local/lib and /Library/Frameworks...

    fp = os.popen('cpp -v </dev/null 2>&1', 'r')
    dirs = []
    started = False
    for ln in fp:
        if not started:
            if ln.startswith('#include <...> search starts here:'):

            ln = ln.strip()
            if not ln.startswith('/'):

            if ln == '/usr/local/include':

            elif ln == '/Library/Frameworks':

            if ln.endswith('(framework directory)'):
                dirs.append(('framework', ln.split()[0]))
                dirs.append(('system', ln))

    if dirs:
        for k, d in dirs:

# Enable 'PyObjC_STRICT_DEBUGGING' to enable some costly internal 
# assertions. 

# The following flags are an attempt at getting rid of /usr/local
# in the compiler search path.
    "-DMACOSX", # For libffi
    "-DPyObjC_BUILD_RELEASE=%02d%02d"%(tuple(map(int, platform.mac_ver()[0].split('.')[:2]))),

    ## Arghh, a stupid compiler flag can cause problems. Don't 
    ## enable -O0 if you value your sanity. With -O0 PyObjC will crash
    ## on i386 systems when a method returns a struct that isn't returned
    ## in registers. 
    #'-arch', 'x86_64', '-arch', 'ppc64',

    # Loads of warning flags
    "-Wall", "-Wstrict-prototypes", "-Wmissing-prototypes",
    "-Wformat=2", "-W", "-Wshadow",
    "-Wpointer-arith", #"-Wwrite-strings",

    # These two are fairly useless:


    # use the same optimization as Python, probably -O3,
    # but can be overrided by one of the following:

    # no optimization, for debugging

    # g4 optimized
    #"-fast", "-fPIC", "-mcpu=7450",

    # g5 optimized
    #"-fast", "-fPIC",

OBJC_LDFLAGS = frameworks('CoreFoundation', 'Foundation', 'Carbon')

if not os.path.exists('/usr/include/objc/runtime.h'):

    # Force compilation with the local SDK, compilation of PyObC will result in
    # a binary that runs on other releases of the OS without using a particular SDK.
    CFLAGS.extend(['-isysroot', '/'])
    OBJC_LDFLAGS.extend(['-isysroot', '/'])

# We're using xml2, check for the flags to use:
def xml2config(arg):
    import os, shlex
    ln = os.popen('xml2-config %s'%(arg,), 'r').readline()
    ln = ln.strip()

    return shlex.split(ln)



# Patch distutils: it needs to compile .S files as well.
from distutils.unixccompiler import UnixCCompiler
del UnixCCompiler

# Support for an embedded copy of libffi
FFI_CFLAGS=['-Ilibffi-src/include', '-Ilibffi-src/powerpc']

# The list below includes the source files for all CPU types that we run on
# this makes it easier to build fat binaries on Mac OS X.

# Calculate the list of extensions: objc._objc + extensions for the unittests

	ExtensionList =  [ 
		list(glob.glob(os.path.join('Modules', 'objc', '*.m'))),
		extra_compile_args=CFLAGS + ["-I/usr/include/ffi"],
		extra_link_args=OBJC_LDFLAGS + ["-lffi"],

	ExtensionList =  [ 
		FFI_SOURCE + list(glob.glob(os.path.join('Modules', 'objc', '*.m'))),
		extra_compile_args=CFLAGS + FFI_CFLAGS,

for test_source in glob.glob(os.path.join('Modules', 'objc', 'test', '*.m')):
    name, ext = os.path.splitext(os.path.basename(test_source))

    ExtensionList.append(Extension('PyObjCTest.' + name,
        extra_compile_args=['-IModules/objc'] + CFLAGS,

def package_version():
    fp = open('Modules/objc/pyobjc.h', 'r')
    for ln in fp.readlines():
        if ln.startswith('#define OBJC_VERSION'):
            return ln.split()[-1][1:-1]

    raise ValueError, "Version not found"

CLASSIFIERS = filter(None,
Development Status :: 5 - Production/Stable
Environment :: Console
Environment :: MacOS X :: Cocoa
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Natural Language :: English
Operating System :: MacOS :: MacOS X
Programming Language :: Python
Programming Language :: Objective C
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Software Development :: User Interfaces

dist = setup(
    name = "pyobjc-core", 
    version = package_version(),
    description = "Python<->ObjC Interoperability Module",
    long_description = LONG_DESCRIPTION,
    author = "Ronald Oussoren, bbum, SteveM, LeleG, many others stretching back through the reaches of time...",
    author_email = "",
    url = "",
    platforms = [ 'MacOS X' ],
    ext_modules = ExtensionList,
    packages = [ 'objc', 'PyObjCTools' ], 
    #namespace_packages = ['PyObjCTools'],
    package_dir = { '': 'Lib', 'PyObjCTest': 'PyObjCTest' },
    extra_path = "PyObjC",
    cmdclass = {'build_ext': pyobjc_build_ext, 'install_lib': pyobjc_install_lib },
    options = {'egg_info': {'egg_base': 'Lib'}},
    classifiers = CLASSIFIERS,
    license = 'MIT License',
    download_url = '',
    zip_safe = False,