1#!/usr/bin/env python
2try:
3    from setuptools import setup, Extension
4except ImportError:
5    from distutils.core import setup, Extension
6import os
7import sys
8
9try:
10    import platform
11    is_cpython = not hasattr(platform, 'python_implementation') or platform.python_implementation() == 'CPython'
12except (ImportError, NameError):
13    is_cpython = True # CPython < 2.6
14
15if sys.platform == "darwin":
16    # Don't create resource files on OS X tar.
17    os.environ['COPY_EXTENDED_ATTRIBUTES_DISABLE'] = 'true'
18    os.environ['COPYFILE_DISABLE'] = 'true'
19
20setup_args = {}
21
22def add_command_class(name, cls):
23    cmdclasses = setup_args.get('cmdclass', {})
24    cmdclasses[name] = cls
25    setup_args['cmdclass'] = cmdclasses
26
27from distutils.command.sdist import sdist as sdist_orig
28class sdist(sdist_orig):
29    def run(self):
30        self.force_manifest = 1
31        if (sys.platform != "win32" and
32            os.path.isdir('.git')):
33            assert os.system("git rev-parse --verify HEAD > .gitrev") == 0
34        sdist_orig.run(self)
35add_command_class('sdist', sdist)
36
37if sys.version_info[0] >= 3:
38    import lib2to3.refactor
39    from distutils.command.build_py \
40         import build_py_2to3 as build_py
41    # need to convert sources to Py3 on installation
42    fixers = [ fix for fix in lib2to3.refactor.get_fixers_from_package("lib2to3.fixes")
43               if fix.split('fix_')[-1] not in ('next',)
44               ]
45    build_py.fixer_names = fixers
46    add_command_class("build_py", build_py)
47
48pxd_include_dirs = [
49    directory for directory, dirs, files in os.walk('Cython/Includes')
50    if '__init__.pyx' in files or '__init__.pxd' in files
51    or directory == 'Cython/Includes' or directory == 'Cython/Includes/Deprecated']
52
53pxd_include_patterns = [
54    p+'/*.pxd' for p in pxd_include_dirs ] + [
55    p+'/*.pyx' for p in pxd_include_dirs ]
56
57setup_args['package_data'] = {
58    'Cython.Plex'     : ['*.pxd'],
59    'Cython.Compiler' : ['*.pxd'],
60    'Cython.Runtime'  : ['*.pyx', '*.pxd'],
61    'Cython.Utility'  : ['*.pyx', '*.pxd', '*.c', '*.h', '*.cpp'],
62    'Cython'          : [ p[7:] for p in pxd_include_patterns ],
63    }
64
65# This dict is used for passing extra arguments that are setuptools
66# specific to setup
67setuptools_extra_args = {}
68
69# tells whether to include cygdb (the script and the Cython.Debugger package
70include_debugger = sys.version_info[:2] > (2, 5)
71
72if 'setuptools' in sys.modules:
73    setuptools_extra_args['zip_safe'] = False
74    setuptools_extra_args['entry_points'] = {
75        'console_scripts': [
76            'cython = Cython.Compiler.Main:setuptools_main',
77        ]
78    }
79    scripts = []
80else:
81    if os.name == "posix":
82        scripts = ["bin/cython"]
83    else:
84        scripts = ["cython.py"]
85
86if include_debugger:
87    if 'setuptools' in sys.modules:
88        setuptools_extra_args['entry_points']['console_scripts'].append(
89            'cygdb = Cython.Debugger.Cygdb:main')
90    else:
91        if os.name == "posix":
92            scripts.append('bin/cygdb')
93        else:
94            scripts.append('cygdb.py')
95
96
97def compile_cython_modules(profile=False, compile_more=False, cython_with_refnanny=False):
98    source_root = os.path.abspath(os.path.dirname(__file__))
99    compiled_modules = ["Cython.Plex.Scanners",
100                        "Cython.Plex.Actions",
101                        "Cython.Compiler.Lexicon",
102                        "Cython.Compiler.Scanning",
103                        "Cython.Compiler.Parsing",
104                        "Cython.Compiler.Visitor",
105                        "Cython.Compiler.FlowControl",
106                        "Cython.Compiler.Code",
107                        "Cython.Runtime.refnanny",
108                        # "Cython.Compiler.FusedNode",
109                        "Cython.Tempita._tempita",
110                        ]
111    if compile_more:
112        compiled_modules.extend([
113            "Cython.Build.Dependencies",
114            "Cython.Compiler.ParseTreeTransforms",
115            "Cython.Compiler.Nodes",
116            "Cython.Compiler.ExprNodes",
117            "Cython.Compiler.ModuleNode",
118            "Cython.Compiler.Optimize",
119            ])
120
121    defines = []
122    if cython_with_refnanny:
123        defines.append(('CYTHON_REFNANNY', '1'))
124
125    extensions = []
126    if sys.version_info[0] >= 3:
127        from Cython.Distutils import build_ext as build_ext_orig
128        for module in compiled_modules:
129            source_file = os.path.join(source_root, *module.split('.'))
130            if os.path.exists(source_file + ".py"):
131                pyx_source_file = source_file + ".py"
132            else:
133                pyx_source_file = source_file + ".pyx"
134            dep_files = []
135            if os.path.exists(source_file + '.pxd'):
136                dep_files.append(source_file + '.pxd')
137            if '.refnanny' in module:
138                defines_for_module = []
139            else:
140                defines_for_module = defines
141            extensions.append(
142                Extension(module, sources = [pyx_source_file],
143                          define_macros = defines_for_module,
144                          depends = dep_files)
145                )
146
147        class build_ext(build_ext_orig):
148            # we must keep the original modules alive to make sure
149            # their code keeps working when we remove them from
150            # sys.modules
151            dead_modules = []
152
153            def build_extensions(self):
154                # add path where 2to3 installed the transformed sources
155                # and make sure Python (re-)imports them from there
156                already_imported = [ module for module in sys.modules
157                                     if module == 'Cython' or module.startswith('Cython.') ]
158                keep_alive = self.dead_modules.append
159                for module in already_imported:
160                    keep_alive(sys.modules[module])
161                    del sys.modules[module]
162                sys.path.insert(0, os.path.join(source_root, self.build_lib))
163
164                if profile:
165                    from Cython.Compiler.Options import directive_defaults
166                    directive_defaults['profile'] = True
167                    print("Enabled profiling for the Cython binary modules")
168                build_ext_orig.build_extensions(self)
169
170        setup_args['ext_modules'] = extensions
171        add_command_class("build_ext", build_ext)
172
173    else: # Python 2.x
174        from distutils.command.build_ext import build_ext as build_ext_orig
175        try:
176            class build_ext(build_ext_orig):
177                def build_extension(self, ext, *args, **kargs):
178                    try:
179                        build_ext_orig.build_extension(self, ext, *args, **kargs)
180                    except StandardError:
181                        print("Compilation of '%s' failed" % ext.sources[0])
182            from Cython.Compiler.Main import compile
183            from Cython import Utils
184            if profile:
185                from Cython.Compiler.Options import directive_defaults
186                directive_defaults['profile'] = True
187                print("Enabled profiling for the Cython binary modules")
188            source_root = os.path.dirname(__file__)
189            for module in compiled_modules:
190                source_file = os.path.join(source_root, *module.split('.'))
191                if os.path.exists(source_file + ".py"):
192                    pyx_source_file = source_file + ".py"
193                else:
194                    pyx_source_file = source_file + ".pyx"
195                c_source_file = source_file + ".c"
196                source_is_newer = False
197                if not os.path.exists(c_source_file):
198                    source_is_newer = True
199                else:
200                    c_last_modified = Utils.modification_time(c_source_file)
201                    if Utils.file_newer_than(pyx_source_file, c_last_modified):
202                        source_is_newer = True
203                    else:
204                        pxd_source_file = source_file + ".pxd"
205                        if os.path.exists(pxd_source_file) and Utils.file_newer_than(pxd_source_file, c_last_modified):
206                            source_is_newer = True
207                if source_is_newer:
208                    print("Compiling module %s ..." % module)
209                    result = compile(pyx_source_file)
210                    c_source_file = result.c_file
211                if c_source_file:
212                    # Py2 distutils can't handle unicode file paths
213                    if isinstance(c_source_file, unicode):
214                        filename_encoding = sys.getfilesystemencoding()
215                        if filename_encoding is None:
216                            filename_encoding = sys.getdefaultencoding()
217                        c_source_file = c_source_file.encode(filename_encoding)
218                    if '.refnanny' in module:
219                        defines_for_module = []
220                    else:
221                        defines_for_module = defines
222                    extensions.append(
223                        Extension(module, sources = [c_source_file],
224                                  define_macros = defines_for_module)
225                        )
226                else:
227                    print("Compilation failed")
228            if extensions:
229                setup_args['ext_modules'] = extensions
230                add_command_class("build_ext", build_ext)
231        except Exception:
232            print('''
233ERROR: %s
234
235Extension module compilation failed, looks like Cython cannot run
236properly on this system.  To work around this, pass the option
237"--no-cython-compile".  This will install a pure Python version of
238Cython without compiling its own sources.
239''' % sys.exc_info()[1])
240            raise
241
242cython_profile = '--cython-profile' in sys.argv
243if cython_profile:
244    sys.argv.remove('--cython-profile')
245
246try:
247    sys.argv.remove("--cython-compile-all")
248    cython_compile_more = True
249except ValueError:
250    cython_compile_more = False
251
252try:
253    sys.argv.remove("--cython-with-refnanny")
254    cython_with_refnanny = True
255except ValueError:
256    cython_with_refnanny = False
257
258try:
259    sys.argv.remove("--no-cython-compile")
260    compile_cython_itself = False
261except ValueError:
262    compile_cython_itself = True
263
264if compile_cython_itself and (is_cpython or cython_compile_more):
265    compile_cython_modules(cython_profile, cython_compile_more, cython_with_refnanny)
266
267setup_args.update(setuptools_extra_args)
268
269from Cython import __version__ as version
270
271packages = [
272    'Cython',
273    'Cython.Build',
274    'Cython.Compiler',
275    'Cython.Runtime',
276    'Cython.Distutils',
277    'Cython.Plex',
278    'Cython.Tests',
279    'Cython.Build.Tests',
280    'Cython.Compiler.Tests',
281    'Cython.Utility',
282    'Cython.Tempita',
283    'pyximport',
284]
285
286if include_debugger:
287    packages.append('Cython.Debugger')
288    packages.append('Cython.Debugger.Tests')
289    # it's enough to do this for Py2.5+:
290    setup_args['package_data']['Cython.Debugger.Tests'] = ['codefile', 'cfuncs.c']
291
292setup(
293  name = 'Cython',
294  version = version,
295  url = 'http://www.cython.org',
296  author = 'Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.',
297  author_email = 'cython-devel@python.org',
298  description = "The Cython compiler for writing C extensions for the Python language.",
299  long_description = """\
300  The Cython language makes writing C extensions for the Python language as
301  easy as Python itself.  Cython is a source code translator based on the
302  well-known Pyrex_, but supports more cutting edge functionality and
303  optimizations.
304
305  The Cython language is very close to the Python language (and most Python
306  code is also valid Cython code), but Cython additionally supports calling C
307  functions and declaring C types on variables and class attributes. This
308  allows the compiler to generate very efficient C code from Cython code.
309
310  This makes Cython the ideal language for writing glue code for external C
311  libraries, and for fast C modules that speed up the execution of Python
312  code.
313
314  .. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/
315  """,
316  classifiers = [
317    "Development Status :: 5 - Production/Stable",
318    "Intended Audience :: Developers",
319    "License :: OSI Approved :: Apache Software License",
320    "Operating System :: OS Independent",
321    "Programming Language :: Python",
322    "Programming Language :: Python :: 2",
323    "Programming Language :: Python :: 3",
324    "Programming Language :: C",
325    "Programming Language :: Cython",
326    "Topic :: Software Development :: Code Generators",
327    "Topic :: Software Development :: Compilers",
328    "Topic :: Software Development :: Libraries :: Python Modules"
329  ],
330
331  scripts = scripts,
332  packages=packages,
333
334  py_modules = ["cython"],
335
336  **setup_args
337  )
338