OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 |
| 3 import os |
| 4 import shutil |
| 5 import tempfile |
| 6 |
| 7 from distutils.core import setup |
| 8 from Cython.Build.Dependencies import cythonize, extended_iglob |
| 9 from Cython.Utils import is_package_dir |
| 10 from Cython.Compiler import Options |
| 11 |
| 12 try: |
| 13 import multiprocessing |
| 14 parallel_compiles = int(multiprocessing.cpu_count() * 1.5) |
| 15 except ImportError: |
| 16 multiprocessing = None |
| 17 parallel_compiles = 0 |
| 18 |
| 19 |
| 20 class _FakePool(object): |
| 21 def map_async(self, func, args): |
| 22 from itertools import imap |
| 23 for _ in imap(func, args): |
| 24 pass |
| 25 |
| 26 def close(self): pass |
| 27 def terminate(self): pass |
| 28 def join(self): pass |
| 29 |
| 30 |
| 31 def parse_directives(option, name, value, parser): |
| 32 dest = option.dest |
| 33 old_directives = dict(getattr(parser.values, dest, |
| 34 Options.directive_defaults)) |
| 35 directives = Options.parse_directive_list( |
| 36 value, relaxed_bool=True, current_settings=old_directives) |
| 37 setattr(parser.values, dest, directives) |
| 38 |
| 39 |
| 40 def parse_options(option, name, value, parser): |
| 41 dest = option.dest |
| 42 options = dict(getattr(parser.values, dest, {})) |
| 43 for opt in value.split(','): |
| 44 if '=' in opt: |
| 45 n, v = opt.split('=', 1) |
| 46 v = v.lower() not in ('false', 'f', '0', 'no') |
| 47 else: |
| 48 n, v = opt, True |
| 49 options[n] = v |
| 50 setattr(parser.values, dest, options) |
| 51 |
| 52 |
| 53 def find_package_base(path): |
| 54 base_dir, package_path = os.path.split(path) |
| 55 while os.path.isfile(os.path.join(base_dir, '__init__.py')): |
| 56 base_dir, parent = os.path.split(base_dir) |
| 57 package_path = '%s/%s' % (parent, package_path) |
| 58 return base_dir, package_path |
| 59 |
| 60 |
| 61 def cython_compile(path_pattern, options): |
| 62 pool = None |
| 63 paths = map(os.path.abspath, extended_iglob(path_pattern)) |
| 64 try: |
| 65 for path in paths: |
| 66 if options.build_inplace: |
| 67 base_dir = path |
| 68 while not os.path.isdir(base_dir) or is_package_dir(base_dir): |
| 69 base_dir = os.path.dirname(base_dir) |
| 70 else: |
| 71 base_dir = None |
| 72 |
| 73 if os.path.isdir(path): |
| 74 # recursively compiling a package |
| 75 paths = [os.path.join(path, '**', '*.%s' % ext) |
| 76 for ext in ('py', 'pyx')] |
| 77 else: |
| 78 # assume it's a file(-like thing) |
| 79 paths = [path] |
| 80 |
| 81 ext_modules = cythonize( |
| 82 paths, |
| 83 nthreads=options.parallel, |
| 84 exclude_failures=options.keep_going, |
| 85 exclude=options.excludes, |
| 86 compiler_directives=options.directives, |
| 87 force=options.force, |
| 88 quiet=options.quiet, |
| 89 **options.options) |
| 90 |
| 91 if ext_modules and options.build: |
| 92 if len(ext_modules) > 1 and options.parallel > 1: |
| 93 if pool is None: |
| 94 try: |
| 95 pool = multiprocessing.Pool(options.parallel) |
| 96 except OSError: |
| 97 pool = _FakePool() |
| 98 pool.map_async(run_distutils, [ |
| 99 (base_dir, [ext]) for ext in ext_modules]) |
| 100 else: |
| 101 run_distutils((base_dir, ext_modules)) |
| 102 except: |
| 103 if pool is not None: |
| 104 pool.terminate() |
| 105 raise |
| 106 else: |
| 107 if pool is not None: |
| 108 pool.close() |
| 109 pool.join() |
| 110 |
| 111 |
| 112 def run_distutils(args): |
| 113 base_dir, ext_modules = args |
| 114 script_args = ['build_ext', '-i'] |
| 115 cwd = os.getcwd() |
| 116 temp_dir = None |
| 117 try: |
| 118 if base_dir: |
| 119 os.chdir(base_dir) |
| 120 temp_dir = tempfile.mkdtemp(dir=base_dir) |
| 121 script_args.extend(['--build-temp', temp_dir]) |
| 122 setup( |
| 123 script_name='setup.py', |
| 124 script_args=script_args, |
| 125 ext_modules=ext_modules, |
| 126 ) |
| 127 finally: |
| 128 if base_dir: |
| 129 os.chdir(cwd) |
| 130 if temp_dir and os.path.isdir(temp_dir): |
| 131 shutil.rmtree(temp_dir) |
| 132 |
| 133 |
| 134 def parse_args(args): |
| 135 from optparse import OptionParser |
| 136 parser = OptionParser(usage='%prog [options] [sources and packages]+') |
| 137 |
| 138 parser.add_option('-X', '--directive', metavar='NAME=VALUE,...', dest='direc
tives', |
| 139 type=str, action='callback', callback=parse_directives, de
fault={}, |
| 140 help='set a compiler directive') |
| 141 parser.add_option('-s', '--option', metavar='NAME=VALUE', dest='options', |
| 142 type=str, action='callback', callback=parse_options, defau
lt={}, |
| 143 help='set a cythonize option') |
| 144 parser.add_option('-3', dest='python3_mode', action='store_true', |
| 145 help='use Python 3 syntax mode by default') |
| 146 |
| 147 parser.add_option('-x', '--exclude', metavar='PATTERN', dest='excludes', |
| 148 action='append', default=[], |
| 149 help='exclude certain file patterns from the compilation') |
| 150 |
| 151 parser.add_option('-b', '--build', dest='build', action='store_true', |
| 152 help='build extension modules using distutils') |
| 153 parser.add_option('-i', '--inplace', dest='build_inplace', action='store_tru
e', |
| 154 help='build extension modules in place using distutils (im
plies -b)') |
| 155 parser.add_option('-j', '--parallel', dest='parallel', metavar='N', |
| 156 type=int, default=parallel_compiles, |
| 157 help=('run builds in N parallel jobs (default: %d)' % |
| 158 parallel_compiles or 1)) |
| 159 parser.add_option('-f', '--force', dest='force', action='store_true', |
| 160 help='force recompilation') |
| 161 parser.add_option('-q', '--quiet', dest='quiet', action='store_true', |
| 162 help='be less verbose during compilation') |
| 163 |
| 164 parser.add_option('--lenient', dest='lenient', action='store_true', |
| 165 help='increase Python compatibility by ignoring some compi
le time errors') |
| 166 parser.add_option('-k', '--keep-going', dest='keep_going', action='store_tru
e', |
| 167 help='compile as much as possible, ignore compilation fail
ures') |
| 168 |
| 169 options, args = parser.parse_args(args) |
| 170 if not args: |
| 171 parser.error("no source files provided") |
| 172 if options.build_inplace: |
| 173 options.build = True |
| 174 if multiprocessing is None: |
| 175 options.parallel = 0 |
| 176 if options.python3_mode: |
| 177 options.options['language_level'] = 3 |
| 178 return options, args |
| 179 |
| 180 |
| 181 def main(args=None): |
| 182 options, paths = parse_args(args) |
| 183 |
| 184 if options.lenient: |
| 185 # increase Python compatibility by ignoring compile time errors |
| 186 Options.error_on_unknown_names = False |
| 187 Options.error_on_uninitialized = False |
| 188 |
| 189 for path in paths: |
| 190 cython_compile(path, options) |
| 191 |
| 192 |
| 193 if __name__ == '__main__': |
| 194 main() |
OLD | NEW |