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