setup.py 11.3 KB
Newer Older
1
#!/usr/bin/env python
2 3 4 5
try:
    from setuptools import setup, Extension
except ImportError:
    from distutils.core import setup, Extension
Stefan Behnel's avatar
Stefan Behnel committed
6
import os
7
import stat
8
import subprocess
9
import textwrap
Robert Bradshaw's avatar
Robert Bradshaw committed
10
import sys
William Stein's avatar
William Stein committed
11

12 13
import platform
is_cpython = platform.python_implementation() == 'CPython'
14

15 16 17
# Suppress warning.
sys.modules[Extension.__module__].have_pyrex = lambda : True

18 19 20 21 22
if sys.platform == "darwin":
    # Don't create resource files on OS X tar.
    os.environ['COPY_EXTENDED_ATTRIBUTES_DISABLE'] = 'true'
    os.environ['COPYFILE_DISABLE'] = 'true'

23 24
setup_args = {}

25 26 27 28 29
def add_command_class(name, cls):
    cmdclasses = setup_args.get('cmdclass', {})
    cmdclasses[name] = cls
    setup_args['cmdclass'] = cmdclasses

30 31 32
from distutils.command.sdist import sdist as sdist_orig
class sdist(sdist_orig):
    def run(self):
33
        self.force_manifest = 1
34
        if (sys.platform != "win32" and
35
            os.path.isdir('.git')):
36
            assert os.system("git rev-parse --verify HEAD > .gitrev") == 0
37 38 39
        sdist_orig.run(self)
add_command_class('sdist', sdist)

40
if sys.version_info[:2] == (3, 2):
41 42 43 44
    import lib2to3.refactor
    from distutils.command.build_py \
         import build_py_2to3 as build_py
    # need to convert sources to Py3 on installation
45 46
    with open('2to3-fixers.txt') as f:
        fixers = [line.strip() for line in f if line.strip()]
47
    build_py.fixer_names = fixers
48
    add_command_class("build_py", build_py)
49

50 51 52
pxd_include_dirs = [
    directory for directory, dirs, files in os.walk('Cython/Includes')
    if '__init__.pyx' in files or '__init__.pxd' in files
53
    or directory == 'Cython/Includes' or directory == 'Cython/Includes/Deprecated']
54 55 56 57

pxd_include_patterns = [
    p+'/*.pxd' for p in pxd_include_dirs ] + [
    p+'/*.pyx' for p in pxd_include_dirs ]
58

59 60 61 62 63 64 65
setup_args['package_data'] = {
    'Cython.Plex'     : ['*.pxd'],
    'Cython.Compiler' : ['*.pxd'],
    'Cython.Runtime'  : ['*.pyx', '*.pxd'],
    'Cython.Utility'  : ['*.pyx', '*.pxd', '*.c', '*.h', '*.cpp'],
    'Cython'          : [ p[7:] for p in pxd_include_patterns ],
    }
William Stein's avatar
William Stein committed
66

67
# This dict is used for passing extra arguments that are setuptools
68 69 70
# specific to setup
setuptools_extra_args = {}

71
# tells whether to include cygdb (the script and the Cython.Debugger package
72
include_debugger = sys.version_info[:2] > (2, 5)
73

74 75 76 77 78
if 'setuptools' in sys.modules:
    setuptools_extra_args['zip_safe'] = False
    setuptools_extra_args['entry_points'] = {
        'console_scripts': [
            'cython = Cython.Compiler.Main:setuptools_main',
79
            'cythonize = Cython.Build.Cythonize:main'
80 81 82
        ]
    }
    scripts = []
William Stein's avatar
William Stein committed
83
else:
84
    if os.name == "posix":
85
        scripts = ["bin/cython", 'bin/cythonize']
86
    else:
87
        scripts = ["cython.py", "cythonize.py"]
88 89

if include_debugger:
90
    if 'setuptools' in sys.modules:
Gabi Davar's avatar
Gabi Davar committed
91 92
        setuptools_extra_args['entry_points']['console_scripts'].append(
            'cygdb = Cython.Debugger.Cygdb:main')
93
    else:
94 95 96 97
        if os.name == "posix":
            scripts.append('bin/cygdb')
        else:
            scripts.append('cygdb.py')
98

99

100
def compile_cython_modules(profile=False, compile_more=False, cython_with_refnanny=False):
101
    source_root = os.path.abspath(os.path.dirname(__file__))
Stefan Behnel's avatar
Stefan Behnel committed
102 103 104 105 106 107 108 109 110 111 112 113 114
    compiled_modules = [
        "Cython.Plex.Scanners",
        "Cython.Plex.Actions",
        "Cython.Compiler.Lexicon",
        "Cython.Compiler.Scanning",
        "Cython.Compiler.Parsing",
        "Cython.Compiler.Visitor",
        "Cython.Compiler.FlowControl",
        "Cython.Compiler.Code",
        "Cython.Runtime.refnanny",
        # "Cython.Compiler.FusedNode",
        "Cython.Tempita._tempita",
    ]
115 116
    if compile_more:
        compiled_modules.extend([
117
            "Cython.Build.Dependencies",
118 119 120 121 122 123
            "Cython.Compiler.ParseTreeTransforms",
            "Cython.Compiler.Nodes",
            "Cython.Compiler.ExprNodes",
            "Cython.Compiler.ModuleNode",
            "Cython.Compiler.Optimize",
            ])
William Stein's avatar
William Stein committed
124

125 126 127 128 129
    from distutils.spawn import find_executable
    from distutils.sysconfig import get_python_inc
    pgen = find_executable(
        'pgen', os.pathsep.join([os.environ['PATH'], os.path.join(get_python_inc(), '..', 'Parser')]))
    if not pgen:
Robert Bradshaw's avatar
Robert Bradshaw committed
130
        print ("Unable to find pgen, not compiling formal grammar.")
131 132
    else:
        parser_dir = os.path.join(os.path.dirname(__file__), 'Cython', 'Parser')
133
        grammar = os.path.join(parser_dir, 'Grammar')
134 135
        subprocess.check_call([
            pgen,
136
            os.path.join(grammar),
137 138 139
            os.path.join(parser_dir, 'graminit.h'),
            os.path.join(parser_dir, 'graminit.c'),
            ])
140 141 142 143
        cst_pyx = os.path.join(parser_dir, 'ConcreteSyntaxTree.pyx')
        if os.stat(grammar)[stat.ST_MTIME] > os.stat(cst_pyx)[stat.ST_MTIME]:
            mtime = os.stat(grammar)[stat.ST_MTIME]
            os.utime(cst_pyx, (mtime, mtime))
144 145 146 147
        compiled_modules.extend([
                "Cython.Parser.ConcreteSyntaxTree",
            ])

148 149 150
    defines = []
    if cython_with_refnanny:
        defines.append(('CYTHON_REFNANNY', '1'))
William Stein's avatar
William Stein committed
151

152
    extensions = []
153 154 155 156 157 158 159 160 161 162 163 164 165
    for module in compiled_modules:
        source_file = os.path.join(source_root, *module.split('.'))
        if os.path.exists(source_file + ".py"):
            pyx_source_file = source_file + ".py"
        else:
            pyx_source_file = source_file + ".pyx"
        dep_files = []
        if os.path.exists(source_file + '.pxd'):
            dep_files.append(source_file + '.pxd')
        if '.refnanny' in module:
            defines_for_module = []
        else:
            defines_for_module = defines
166 167 168 169 170
        extensions.append(Extension(
            module, sources=[pyx_source_file],
            define_macros=defines_for_module,
            depends=dep_files))
        # XXX hack around setuptools quirk for '*.pyx' sources
171 172
        extensions[-1].sources[0] = pyx_source_file

173
    from Cython.Build import build_ext
174 175
    if sys.version_info[:2] == (3, 2):
        # Python 3.2: can only run Cython *after* running 2to3
176 177 178 179 180
        build_ext = _defer_cython_import_in_py32(build_ext, source_root, profile)
    elif profile:
        from Cython.Compiler.Options import directive_defaults
        directive_defaults['profile'] = True
        print("Enabled profiling for the Cython binary modules")
181

182 183
    # not using cythonize() here to let distutils decide whether building extensions was requested
    add_command_class("build_ext", build_ext)
184 185 186
    setup_args['ext_modules'] = extensions


187
def _defer_cython_import_in_py32(build_ext_orig, source_root, profile=False):
188
    # Python 3.2: can only run Cython *after* running 2to3
189
    # => re-import Cython inside of build_ext
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    class build_ext(build_ext_orig):
        # we must keep the original modules alive to make sure
        # their code keeps working when we remove them from
        # sys.modules
        dead_modules = []

        def build_extensions(self):
            # add path where 2to3 installed the transformed sources
            # and make sure Python (re-)imports them from there
            already_imported = [
                module for module in sys.modules
                if module == 'Cython' or module.startswith('Cython.')
            ]
            keep_alive = self.dead_modules.append
            for module in already_imported:
                keep_alive(sys.modules[module])
                del sys.modules[module]
            sys.path.insert(0, os.path.join(source_root, self.build_lib))

209 210 211 212
            if profile:
                from Cython.Compiler.Options import directive_defaults
                directive_defaults['profile'] = True
                print("Enabled profiling for the Cython binary modules")
213 214
            build_ext_orig.build_extensions(self)

215
    return build_ext
216 217


Stefan Behnel's avatar
Stefan Behnel committed
218 219 220 221
cython_profile = '--cython-profile' in sys.argv
if cython_profile:
    sys.argv.remove('--cython-profile')

222 223 224 225 226 227
try:
    sys.argv.remove("--cython-compile-all")
    cython_compile_more = True
except ValueError:
    cython_compile_more = False

228 229 230 231 232 233
try:
    sys.argv.remove("--cython-with-refnanny")
    cython_with_refnanny = True
except ValueError:
    cython_with_refnanny = False

234 235
try:
    sys.argv.remove("--no-cython-compile")
236
    compile_cython_itself = False
237
except ValueError:
238 239
    compile_cython_itself = True

240
if compile_cython_itself and (is_cpython or cython_compile_more):
241
    compile_cython_modules(cython_profile, cython_compile_more, cython_with_refnanny)
242

243
setup_args.update(setuptools_extra_args)
244

245
from Cython import __version__ as version
246

247 248 249 250 251 252 253 254 255 256 257 258

def dev_status():
    if 'b' in version or 'c' in version:
        # 1b1, 1beta1, 2rc1, ...
        return 'Development Status :: 4 - Beta'
    elif 'a' in version:
        # 1a1, 1alpha1, ...
        return 'Development Status :: 3 - Alpha'
    else:
        return 'Development Status :: 5 - Production/Stable'


259 260 261 262 263 264 265 266
packages = [
    'Cython',
    'Cython.Build',
    'Cython.Compiler',
    'Cython.Runtime',
    'Cython.Distutils',
    'Cython.Plex',
    'Cython.Tests',
267
    'Cython.Build.Tests',
268
    'Cython.Compiler.Tests',
269
    'Cython.Utility',
Mark Florisson's avatar
Mark Florisson committed
270
    'Cython.Tempita',
271
    'pyximport',
272 273 274 275
]

if include_debugger:
    packages.append('Cython.Debugger')
276
    packages.append('Cython.Debugger.Tests')
277 278 279
    # it's enough to do this for Py2.5+:
    setup_args['package_data']['Cython.Debugger.Tests'] = ['codefile', 'cfuncs.c']

William Stein's avatar
William Stein committed
280
setup(
281 282
    name='Cython',
    version=version,
Stefan Behnel's avatar
Stefan Behnel committed
283
    url='http://cython.org/',
284 285 286 287 288 289 290 291
    author='Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.',
    author_email='cython-devel@python.org',
    description="The Cython compiler for writing C extensions for the Python language.",
    long_description=textwrap.dedent("""\
    The Cython language makes writing C extensions for the Python language as
    easy as Python itself.  Cython is a source code translator based on Pyrex_,
    but supports more cutting edge functionality and optimizations.

292 293 294 295 296
    The Cython language is a superset of the Python language (almost all Python
    code is also valid Cython code), but Cython additionally supports optional
    static typing to natively call C functions, operate with C++ classes and
    declare fast C types on variables and class attributes.  This allows the
    compiler to generate very efficient C code from Cython code.
297

298 299 300
    This makes Cython the ideal language for writing glue code for external
    C/C++ libraries, and for fast C modules that speed up the execution of
    Python code.
301

302 303 304
    Note that for one-time builds, e.g. for CI/testing, on platforms that are not
    covered by one of the wheel packages provided on PyPI, it is substantially faster
    than a full source build to install an uncompiled (slower) version of Cython with::
305 306 307

        pip install Cython --install-option="--no-cython-compile"

308 309
    .. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/
    """),
310
    license='Apache',
311
    classifiers=[
312
        dev_status(),
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
        "Intended Audience :: Developers",
        "License :: OSI Approved :: Apache Software License",
        "Operating System :: OS Independent",
        "Programming Language :: Python",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 3",
        "Programming Language :: C",
        "Programming Language :: Cython",
        "Topic :: Software Development :: Code Generators",
        "Topic :: Software Development :: Compilers",
        "Topic :: Software Development :: Libraries :: Python Modules"
    ],

    scripts=scripts,
    packages=packages,
    py_modules=["cython"],
    **setup_args
)