merged in Vitja's tab removals
[cython.git] / setup.py
index 2e5b8cdcfb9a68033fd54a26ea8097b33c74d009..782dea25edf0bd376c7809fceb1e68fe6b30dec1 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -3,17 +3,6 @@ from distutils.sysconfig import get_python_lib
 import os, os.path
 import sys
 
-if 'sdist' in sys.argv and sys.platform != "win32" and sys.version_info >= (2,4):
-    # Record the current revision in .hgrev
-    import subprocess # os.popen is cleaner but deprecated
-    changset = subprocess.Popen("hg log --rev tip | grep changeset", 
-                                shell=True,
-                                stdout=subprocess.PIPE).stdout.read()
-    rev = changset.decode('ISO-8859-1').split(':')[-1].strip()
-    hgrev = open('.hgrev', 'w')
-    hgrev.write(rev)
-    hgrev.close()
-
 if sys.platform == "darwin":
     # Don't create resource files on OS X tar.
     os.environ['COPY_EXTENDED_ATTRIBUTES_DISABLE'] = 'true'
@@ -26,6 +15,16 @@ def add_command_class(name, cls):
     cmdclasses[name] = cls
     setup_args['cmdclass'] = cmdclasses
 
+from distutils.command.sdist import sdist as sdist_orig
+class sdist(sdist_orig):
+    def run(self):
+        self.force_manifest = 1
+        if (sys.platform != "win32" and 
+            os.path.isdir('.git')):
+            assert os.system("git show-ref -s HEAD > .gitrev") == 0
+        sdist_orig.run(self)
+add_command_class('sdist', sdist)
+
 if sys.version_info[0] >= 3:
     import lib2to3.refactor
     from distutils.command.build_py \
@@ -37,26 +36,43 @@ if sys.version_info[0] >= 3:
     build_py.fixer_names = fixers
     add_command_class("build_py", build_py)
 
+pxd_include_dirs = [
+    directory for directory, dirs, files in os.walk('Cython/Includes')
+    if '__init__.pyx' in files or '__init__.pxd' in files
+    or directory == 'Cython/Includes' or directory == 'Cython/Includes/Deprecated']
+
+pxd_include_patterns = [
+    p+'/*.pxd' for p in pxd_include_dirs ] + [
+    p+'/*.pyx' for p in pxd_include_dirs ]
 
 if sys.version_info < (2,4):
+    install_base_dir = get_python_lib(prefix='')
     import glob
+    patterns = pxd_include_patterns + [
+        'Cython/Plex/*.pxd',
+        'Cython/Compiler/*.pxd',
+        'Cython/Runtime/*.pyx'
+        ]
     setup_args['data_files'] = [
-        (os.path.dirname(pattern), [ f for f in glob.glob(pattern) ])
-        for pattern in ['Cython/Includes/*.pxd',
-                        'Cython/Plex/*.pxd',
-                        'Cython/Compiler/*.pxd',
-                        'Cython/Runtime/*.pyx']
+        (os.path.dirname(os.path.join(install_base_dir, pattern)),
+         [ f for f in glob.glob(pattern) ])
+        for pattern in patterns
         ]
 else:
-    setup_args['package_data'] = {'Cython' : ['Includes/*.pxd',
-                                              'Plex/*.pxd',
-                                              'Compiler/*.pxd',
-                                              'Runtime/*.pyx']}
+    setup_args['package_data'] = {
+        'Cython.Plex'     : ['*.pxd'],
+        'Cython.Compiler' : ['*.pxd'],
+        'Cython.Runtime'  : ['*.pyx', '*.pxd'],
+        'Cython'          : [ p[7:] for p in pxd_include_patterns ],
+        }
 
-# This dict is used for passing extra arguments that are setuptools 
+# This dict is used for passing extra arguments that are setuptools
 # specific to setup
 setuptools_extra_args = {}
 
+# tells whether to include cygdb (the script and the Cython.Debugger package
+include_debugger = sys.version_info[:2] > (2, 5)
+
 if 'setuptools' in sys.modules:
     setuptools_extra_args['zip_safe'] = False
     setuptools_extra_args['entry_points'] = {
@@ -68,18 +84,37 @@ if 'setuptools' in sys.modules:
 else:
     if os.name == "posix":
         scripts = ["bin/cython"]
+        if include_debugger:
+            scripts.append('bin/cygdb')
     else:
         scripts = ["cython.py"]
+        if include_debugger:
+            scripts.append('cygdb.py')
 
-def compile_cython_modules():
+def compile_cython_modules(profile=False, compile_more=False, cython_with_refnanny=False):
     source_root = os.path.abspath(os.path.dirname(__file__))
     compiled_modules = ["Cython.Plex.Scanners",
+                        "Cython.Plex.Actions",
+                        "Cython.Compiler.Lexicon",
                         "Cython.Compiler.Scanning",
                         "Cython.Compiler.Parsing",
                         "Cython.Compiler.Visitor",
-                        "Cython.Runtime.refnanny"]
-    extensions = []
+                        "Cython.Compiler.Code",
+                        "Cython.Runtime.refnanny",]
+    if compile_more:
+        compiled_modules.extend([
+            "Cython.Compiler.ParseTreeTransforms",
+            "Cython.Compiler.Nodes",
+            "Cython.Compiler.ExprNodes",
+            "Cython.Compiler.ModuleNode",
+            "Cython.Compiler.Optimize",
+            ])
+
+    defines = []
+    if cython_with_refnanny:
+        defines.append(('CYTHON_REFNANNY', '1'))
 
+    extensions = []
     if sys.version_info[0] >= 3:
         from Cython.Distutils import build_ext as build_ext_orig
         for module in compiled_modules:
@@ -88,20 +123,40 @@ def compile_cython_modules():
                 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
             extensions.append(
-                Extension(module, sources = [pyx_source_file])
+                Extension(module, sources = [pyx_source_file],
+                          define_macros = defines_for_module,
+                          depends = dep_files)
                 )
 
         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))
 
+                if profile:
+                    from Cython.Compiler.Options import directive_defaults
+                    directive_defaults['profile'] = True
+                    print("Enabled profiling for the Cython binary modules")
                 build_ext_orig.build_extensions(self)
 
         setup_args['ext_modules'] = extensions
@@ -118,6 +173,10 @@ def compile_cython_modules():
                         print("Compilation of '%s' failed" % ext.sources[0])
             from Cython.Compiler.Main import compile
             from Cython import Utils
+            if profile:
+                from Cython.Compiler.Options import directive_defaults
+                directive_defaults['profile'] = True
+                print("Enabled profiling for the Cython binary modules")
             source_root = os.path.dirname(__file__)
             for module in compiled_modules:
                 source_file = os.path.join(source_root, *module.split('.'))
@@ -126,15 +185,35 @@ def compile_cython_modules():
                 else:
                     pyx_source_file = source_file + ".pyx"
                 c_source_file = source_file + ".c"
-                if not os.path.exists(c_source_file) or \
-                   Utils.file_newer_than(pyx_source_file,
-                                         Utils.modification_time(c_source_file)):
+                source_is_newer = False
+                if not os.path.exists(c_source_file):
+                    source_is_newer = True
+                else:
+                    c_last_modified = Utils.modification_time(c_source_file)
+                    if Utils.file_newer_than(pyx_source_file, c_last_modified):
+                        source_is_newer = True
+                    else:
+                        pxd_source_file = source_file + ".pxd"
+                        if os.path.exists(pxd_source_file) and Utils.file_newer_than(pxd_source_file, c_last_modified):
+                            source_is_newer = True
+                if source_is_newer:
                     print("Compiling module %s ..." % module)
                     result = compile(pyx_source_file)
                     c_source_file = result.c_file
                 if c_source_file:
+                    # Py2 distutils can't handle unicode file paths
+                    if isinstance(c_source_file, unicode):
+                        filename_encoding = sys.getfilesystemencoding()
+                        if filename_encoding is None:
+                            filename_encoding = sys.getdefaultencoding()
+                        c_source_file = c_source_file.encode(filename_encoding)
+                    if '.refnanny' in module:
+                        defines_for_module = []
+                    else:
+                        defines_for_module = defines
                     extensions.append(
-                        Extension(module, sources = [c_source_file])
+                        Extension(module, sources = [c_source_file],
+                                  define_macros = defines_for_module)
                         )
                 else:
                     print("Compilation failed")
@@ -142,24 +221,70 @@ def compile_cython_modules():
                 setup_args['ext_modules'] = extensions
                 add_command_class("build_ext", build_ext)
         except Exception:
-            print("ERROR: %s" % sys.exc_info()[1])
-            print("Extension module compilation failed, using plain Python implementation")
+            print('''
+ERROR: %s
+
+Extension module compilation failed, looks like Cython cannot run
+properly on this system.  To work around this, pass the option
+"--no-cython-compile".  This will install a pure Python version of
+Cython without compiling its own sources.
+''' % sys.exc_info()[1])
+            raise
+
+cython_profile = '--cython-profile' in sys.argv
+if cython_profile:
+    sys.argv.remove('--cython-profile')
+
+try:
+    sys.argv.remove("--cython-compile-all")
+    cython_compile_more = True
+except ValueError:
+    cython_compile_more = False
+
+try:
+    sys.argv.remove("--cython-with-refnanny")
+    cython_with_refnanny = True
+except ValueError:
+    cython_with_refnanny = False
 
 try:
     sys.argv.remove("--no-cython-compile")
+    compile_cython_itself = False
 except ValueError:
-    compile_cython_modules()
+    compile_cython_itself = True
+
+if compile_cython_itself:
+    compile_cython_modules(cython_profile, cython_compile_more, cython_with_refnanny)
 
 setup_args.update(setuptools_extra_args)
 
-from Cython.Compiler.Version import version
+from Cython import __version__ as version
+
+packages = [
+    'Cython',
+    'Cython.Build',
+    'Cython.Compiler',
+    'Cython.Runtime',
+    'Cython.Distutils',
+    'Cython.Plex',
+    'Cython.Tests',
+    'Cython.Build.Tests',
+    'Cython.Compiler.Tests',
+]
+
+if include_debugger:
+    packages.append('Cython.Debugger')
+    packages.append('Cython.Debugger.Tests')
+    # it's enough to do this for Py2.5+:
+    setup_args['package_data']['Cython.Debugger.Tests'] = ['codefile', 'cfuncs.c']
+
 
 setup(
   name = 'Cython',
   version = version,
   url = 'http://www.cython.org',
-  author = 'Greg Ewing, Robert Bradshaw, Stefan Behnel, Dag Seljebotn, et al.',
-  author_email = 'cython-dev@codespeak.net',
+  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 = """\
   The Cython language makes writing C extensions for the Python language as
@@ -194,16 +319,7 @@ setup(
   ],
 
   scripts = scripts,
-  packages=[
-    'Cython',
-    'Cython.Compiler',
-    'Cython.Runtime',
-    'Cython.Distutils',
-    'Cython.Plex',
-
-    'Cython.Tests',
-    'Cython.Compiler.Tests',
-    ],
+  packages=packages,
 
   # pyximport
   py_modules = ["pyximport/__init__",