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