1#!/usr/bin/env python
2
3import ez_setup
4ez_setup.use_setuptools()
5
6import sys
7import os
8import glob
9import site
10import platform
11
12if 'MallocStackLogging' in os.environ:
13    del os.environ['MallocStackLogging']
14if 'MallocStackLoggingNoCompact' in os.environ:
15    del os.environ['MallocStackLoggingNoCompact']
16
17# See the news file:
18#os.environ['MACOSX_DEPLOYMENT_TARGET']='10.5'
19
20# We need at least Python 2.3
21MIN_PYTHON = (2, 3)
22
23if sys.version_info < MIN_PYTHON:
24    vstr = '.'.join(map(str, MIN_PYTHON))
25    raise SystemExit('PyObjC: Need at least Python ' + vstr)
26
27USE_SYSTEM_FFI = False
28if int(os.uname()[2].split('.')[0]) >= 10:
29	USE_SYSTEM_FFI = True
30
31SDKROOT = os.environ.get('SDKROOT')
32if SDKROOT is None or SDKROOT is '':
33    SDKROOT = '/'
34
35
36# Some PiPy stuff
37LONG_DESCRIPTION="""
38PyObjC is a bridge between Python and Objective-C.  It allows full
39featured Cocoa applications to be written in pure Python.  It is also
40easy to use other frameworks containing Objective-C class libraries
41from Python and to mix in Objective-C, C and C++ source.
42
43Python is a highly dynamic programming language with a shallow learning
44curve.  It combines remarkable power with very clear syntax.
45
46The installer package installs a number of Xcode templates for
47easily creating new Cocoa-Python projects.
48
49PyObjC also supports full introspection of Objective-C classes and
50direct invocation of Objective-C APIs from the interactive interpreter.
51
52PyObjC requires MacOS X 10.4 or later.  This beta release requires
53MacOS X 10.5.
54"""
55
56from setuptools import setup, Extension, find_packages
57from setuptools.command import build_ext, install_lib
58import os
59
60class pyobjc_install_lib (install_lib.install_lib):
61    def get_exclusions(self):
62        result = install_lib.install_lib.get_exclusions(self)
63        for fn in install_lib._install_lib.get_outputs(self):
64	    if 'PyObjCTest' in fn:
65                result[fn] = 1
66
67	return result
68
69class pyobjc_build_ext (build_ext.build_ext):
70    def run(self):
71        build_ext.build_ext.run(self)
72        extensions = self.extensions
73        self.extensions = [
74                e for e in extensions if e.name.startswith('PyObjCTest') ]
75        self.copy_extensions_to_source()
76        self.extensions = extensions
77
78def frameworks(*args):
79    lst = []
80    for arg in args:
81        lst.extend(['-framework', arg])
82    return lst
83
84def IfFrameWork(name, packages, extensions, headername=None):
85    """
86    Return the packages and extensions if the framework exists, or
87    two empty lists if not.
88    """
89    import os
90    for pth in ('/System/Library/Frameworks', '/Library/Frameworks'):
91        basedir = os.path.join(pth, name)
92        if os.path.exists(basedir):
93            if (headername is None) or os.path.exists(os.path.join(basedir, "Headers", headername)):
94                return packages, extensions
95    return [], []
96
97# Double-check
98if sys.platform != 'darwin':
99    print "You're not running on MacOS X, and don't use GNUstep"
100    print "I don't know how to build PyObjC on such a platform."
101    print "Please read the ReadMe."
102    print ""
103    raise SystemExit("ObjC runtime not found")
104
105from distutils.sysconfig import get_config_var
106cc = get_config_var('CC')
107
108CFLAGS=[ ]
109
110if cc == 'XXXgcc':
111    # This is experimental code that tries to avoid refering to files in
112    # /Library/Frameworks or /usr/local.
113    #
114    # NOTE: This is not enabled by default because the linker will still look
115    # in /usr/local/lib and /Library/Frameworks...
116
117    fp = os.popen('cpp -v </dev/null 2>&1', 'r')
118    dirs = []
119    started = False
120    for ln in fp:
121        if not started:
122            if ln.startswith('#include <...> search starts here:'):
123                started=True
124            continue
125
126        else:
127            ln = ln.strip()
128            if not ln.startswith('/'):
129                break
130
131            if ln == '/usr/local/include':
132                continue
133
134            elif ln == '/Library/Frameworks':
135                continue
136
137            if ln.endswith('(framework directory)'):
138                dirs.append(('framework', ln.split()[0]))
139            else:
140                dirs.append(('system', ln))
141
142    if dirs:
143        CFLAGS.append('-nostdinc')
144        for k, d in dirs:
145            CFLAGS.append('-i%s%s'%(k,d))
146
147# Enable 'PyObjC_STRICT_DEBUGGING' to enable some costly internal
148# assertions.
149CFLAGS.extend([
150
151# The following flags are an attempt at getting rid of /usr/local
152# in the compiler search path.
153    "-DPyObjC_STRICT_DEBUGGING",
154    "-DMACOSX", # For libffi
155    "-DPyObjC_BUILD_RELEASE=%02d%02d"%(tuple(map(int, platform.mac_ver()[0].split('.')[:2]))),
156    #"-Wno-long-double",
157    #"-Wselector",
158    #"-Wstrict-overflow",
159    "-g",
160    #"-fobjc-gc",
161    "-fexceptions",
162
163    ## Arghh, a stupid compiler flag can cause problems. Don't
164    ## enable -O0 if you value your sanity. With -O0 PyObjC will crash
165    ## on i386 systems when a method returns a struct that isn't returned
166    ## in registers.
167    #"-O0",
168    "-O1",
169    #"-O2",
170    #"-O3",
171    #'-arch', 'x86_64', '-arch', 'ppc64',
172
173    # Loads of warning flags
174    "-Wall", "-Wstrict-prototypes", "-Wmissing-prototypes",
175    "-Wformat=2", "-W", "-Wshadow",
176    "-Wpointer-arith", #"-Wwrite-strings",
177    "-Wmissing-declarations",
178    "-Wnested-externs",
179    "-Wno-long-long",
180    #"-Wfloat-equal",
181
182    # These two are fairly useless:
183    #"-Wunreachable-code",
184    #"-pedantic",
185
186    "-Wno-import",
187    #"-Werror",
188
189    # use the same optimization as Python, probably -O3,
190    # but can be overrided by one of the following:
191
192    # no optimization, for debugging
193    #"-O0",
194
195    # g4 optimized
196    #"-fast", "-fPIC", "-mcpu=7450",
197
198    # g5 optimized
199    #"-fast", "-fPIC",
200    ])
201
202
203OBJC_LDFLAGS = frameworks('CoreFoundation', 'Foundation', 'Carbon')
204
205if not os.path.exists(os.path.join(SDKROOT, 'usr/include/objc/runtime.h')):
206    CFLAGS.append('-DNO_OBJC2_RUNTIME')
207
208else:
209    # Force compilation with the local SDK, compilation of PyObC will result in
210    # a binary that runs on other releases of the OS without using a particular SDK.
211    CFLAGS.extend([])
212    OBJC_LDFLAGS.extend([])
213
214
215# We're using xml2, check for the flags to use:
216def xml2config(arg):
217    import os, shlex
218    ln = os.popen('xml2-config %s'%(arg,), 'r').readline()
219    ln = ln.strip()
220
221    return shlex.split(ln)
222
223CFLAGS.extend(xml2config('--cflags'))
224OBJC_LDFLAGS.extend(xml2config('--libs'))
225
226
227
228CFLAGS.append('-Ibuild/codegen/')
229
230# Patch distutils: it needs to compile .S files as well.
231from distutils.unixccompiler import UnixCCompiler
232UnixCCompiler.src_extensions.append('.S')
233del UnixCCompiler
234
235
236#
237# Support for an embedded copy of libffi
238#
239FFI_CFLAGS=['-Ilibffi-src/include', '-Ilibffi-src/powerpc']
240
241# The list below includes the source files for all CPU types that we run on
242# this makes it easier to build fat binaries on Mac OS X.
243FFI_SOURCE=[
244    "libffi-src/ffi.c",
245    "libffi-src/types.c",
246    "libffi-src/powerpc/ppc-darwin.S",
247    "libffi-src/powerpc/ppc-darwin_closure.S",
248    "libffi-src/powerpc/ppc-ffi_darwin.c",
249    "libffi-src/powerpc/ppc64-darwin_closure.S",
250    "libffi-src/x86/darwin64.S",
251    "libffi-src/x86/x86-darwin.S",
252    "libffi-src/x86/x86-ffi64.c",
253    "libffi-src/x86/x86-ffi_darwin.c",
254]
255
256
257
258#
259# Calculate the list of extensions: objc._objc + extensions for the unittests
260#
261
262if USE_SYSTEM_FFI:
263	ExtensionList =  [
264	    Extension("objc._objc",
265		list(glob.glob(os.path.join('Modules', 'objc', '*.m'))),
266		extra_compile_args=CFLAGS + ['-I' + os.path.join(SDKROOT, "usr/include/ffi")],
267		extra_link_args=OBJC_LDFLAGS + ["-lffi"],
268	    )
269	]
270
271else:
272	ExtensionList =  [
273	    Extension("objc._objc",
274		FFI_SOURCE + list(glob.glob(os.path.join('Modules', 'objc', '*.m'))),
275		extra_compile_args=CFLAGS + FFI_CFLAGS,
276		extra_link_args=OBJC_LDFLAGS,
277	    )
278	]
279
280for test_source in glob.glob(os.path.join('Modules', 'objc', 'test', '*.m')):
281    name, ext = os.path.splitext(os.path.basename(test_source))
282
283    ExtensionList.append(Extension('PyObjCTest.' + name,
284        [test_source],
285        extra_compile_args=['-IModules/objc'] + CFLAGS,
286        extra_link_args=OBJC_LDFLAGS))
287
288def package_version():
289    fp = open('Modules/objc/pyobjc.h', 'r')
290    for ln in fp.readlines():
291        if ln.startswith('#define OBJC_VERSION'):
292            fp.close()
293            return ln.split()[-1][1:-1]
294
295    raise ValueError, "Version not found"
296
297CLASSIFIERS = filter(None,
298"""
299Development Status :: 5 - Production/Stable
300Environment :: Console
301Environment :: MacOS X :: Cocoa
302Intended Audience :: Developers
303License :: OSI Approved :: MIT License
304Natural Language :: English
305Operating System :: MacOS :: MacOS X
306Programming Language :: Python
307Programming Language :: Objective C
308Topic :: Software Development :: Libraries :: Python Modules
309Topic :: Software Development :: User Interfaces
310""".splitlines())
311
312dist = setup(
313    name = "pyobjc-core",
314    version = package_version(),
315    description = "Python<->ObjC Interoperability Module",
316    long_description = LONG_DESCRIPTION,
317    author = "Ronald Oussoren, bbum, SteveM, LeleG, many others stretching back through the reaches of time...",
318    author_email = "pyobjc-dev@lists.sourceforge.net",
319    url = "http://pyobjc.sourceforge.net/",
320    platforms = [ 'MacOS X' ],
321    ext_modules = ExtensionList,
322    packages = [ 'objc', 'PyObjCTools' ],
323    #namespace_packages = ['PyObjCTools'],
324    package_dir = { '': 'Lib', 'PyObjCTest': 'PyObjCTest' },
325    extra_path = "PyObjC",
326    cmdclass = {'build_ext': pyobjc_build_ext, 'install_lib': pyobjc_install_lib },
327    options = {'egg_info': {'egg_base': 'Lib'}},
328    classifiers = CLASSIFIERS,
329    license = 'MIT License',
330    download_url = 'http://pyobjc.sourceforge.net/software/index.php',
331    test_suite='PyObjCTest.loader.makeTestSuite',
332    zip_safe = False,
333)
334