annotate DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/distutils/command/build_src.py @ 118:770eb830ec19 emscripten

Typo fix
author Chris Cannam
date Wed, 18 May 2016 16:14:08 +0100
parents 2a2c65a20a8b
children
rev   line source
Chris@87 1 """ Build swig, f2py, pyrex sources.
Chris@87 2 """
Chris@87 3 from __future__ import division, absolute_import, print_function
Chris@87 4
Chris@87 5 import os
Chris@87 6 import re
Chris@87 7 import sys
Chris@87 8 import shlex
Chris@87 9 import copy
Chris@87 10
Chris@87 11 from distutils.command import build_ext
Chris@87 12 from distutils.dep_util import newer_group, newer
Chris@87 13 from distutils.util import get_platform
Chris@87 14 from distutils.errors import DistutilsError, DistutilsSetupError
Chris@87 15
Chris@87 16 def have_pyrex():
Chris@87 17 try:
Chris@87 18 import Pyrex.Compiler.Main
Chris@87 19 return True
Chris@87 20 except ImportError:
Chris@87 21 return False
Chris@87 22
Chris@87 23 # this import can't be done here, as it uses numpy stuff only available
Chris@87 24 # after it's installed
Chris@87 25 #import numpy.f2py
Chris@87 26 from numpy.distutils import log
Chris@87 27 from numpy.distutils.misc_util import fortran_ext_match, \
Chris@87 28 appendpath, is_string, is_sequence, get_cmd
Chris@87 29 from numpy.distutils.from_template import process_file as process_f_file
Chris@87 30 from numpy.distutils.conv_template import process_file as process_c_file
Chris@87 31
Chris@87 32 def subst_vars(target, source, d):
Chris@87 33 """Substitute any occurence of @foo@ by d['foo'] from source file into
Chris@87 34 target."""
Chris@87 35 var = re.compile('@([a-zA-Z_]+)@')
Chris@87 36 fs = open(source, 'r')
Chris@87 37 try:
Chris@87 38 ft = open(target, 'w')
Chris@87 39 try:
Chris@87 40 for l in fs:
Chris@87 41 m = var.search(l)
Chris@87 42 if m:
Chris@87 43 ft.write(l.replace('@%s@' % m.group(1), d[m.group(1)]))
Chris@87 44 else:
Chris@87 45 ft.write(l)
Chris@87 46 finally:
Chris@87 47 ft.close()
Chris@87 48 finally:
Chris@87 49 fs.close()
Chris@87 50
Chris@87 51 class build_src(build_ext.build_ext):
Chris@87 52
Chris@87 53 description = "build sources from SWIG, F2PY files or a function"
Chris@87 54
Chris@87 55 user_options = [
Chris@87 56 ('build-src=', 'd', "directory to \"build\" sources to"),
Chris@87 57 ('f2py-opts=', None, "list of f2py command line options"),
Chris@87 58 ('swig=', None, "path to the SWIG executable"),
Chris@87 59 ('swig-opts=', None, "list of SWIG command line options"),
Chris@87 60 ('swig-cpp', None, "make SWIG create C++ files (default is autodetected from sources)"),
Chris@87 61 ('f2pyflags=', None, "additional flags to f2py (use --f2py-opts= instead)"), # obsolete
Chris@87 62 ('swigflags=', None, "additional flags to swig (use --swig-opts= instead)"), # obsolete
Chris@87 63 ('force', 'f', "forcibly build everything (ignore file timestamps)"),
Chris@87 64 ('inplace', 'i',
Chris@87 65 "ignore build-lib and put compiled extensions into the source " +
Chris@87 66 "directory alongside your pure Python modules"),
Chris@87 67 ]
Chris@87 68
Chris@87 69 boolean_options = ['force', 'inplace']
Chris@87 70
Chris@87 71 help_options = []
Chris@87 72
Chris@87 73 def initialize_options(self):
Chris@87 74 self.extensions = None
Chris@87 75 self.package = None
Chris@87 76 self.py_modules = None
Chris@87 77 self.py_modules_dict = None
Chris@87 78 self.build_src = None
Chris@87 79 self.build_lib = None
Chris@87 80 self.build_base = None
Chris@87 81 self.force = None
Chris@87 82 self.inplace = None
Chris@87 83 self.package_dir = None
Chris@87 84 self.f2pyflags = None # obsolete
Chris@87 85 self.f2py_opts = None
Chris@87 86 self.swigflags = None # obsolete
Chris@87 87 self.swig_opts = None
Chris@87 88 self.swig_cpp = None
Chris@87 89 self.swig = None
Chris@87 90
Chris@87 91 def finalize_options(self):
Chris@87 92 self.set_undefined_options('build',
Chris@87 93 ('build_base', 'build_base'),
Chris@87 94 ('build_lib', 'build_lib'),
Chris@87 95 ('force', 'force'))
Chris@87 96 if self.package is None:
Chris@87 97 self.package = self.distribution.ext_package
Chris@87 98 self.extensions = self.distribution.ext_modules
Chris@87 99 self.libraries = self.distribution.libraries or []
Chris@87 100 self.py_modules = self.distribution.py_modules or []
Chris@87 101 self.data_files = self.distribution.data_files or []
Chris@87 102
Chris@87 103 if self.build_src is None:
Chris@87 104 plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3])
Chris@87 105 self.build_src = os.path.join(self.build_base, 'src'+plat_specifier)
Chris@87 106
Chris@87 107 # py_modules_dict is used in build_py.find_package_modules
Chris@87 108 self.py_modules_dict = {}
Chris@87 109
Chris@87 110 if self.f2pyflags:
Chris@87 111 if self.f2py_opts:
Chris@87 112 log.warn('ignoring --f2pyflags as --f2py-opts already used')
Chris@87 113 else:
Chris@87 114 self.f2py_opts = self.f2pyflags
Chris@87 115 self.f2pyflags = None
Chris@87 116 if self.f2py_opts is None:
Chris@87 117 self.f2py_opts = []
Chris@87 118 else:
Chris@87 119 self.f2py_opts = shlex.split(self.f2py_opts)
Chris@87 120
Chris@87 121 if self.swigflags:
Chris@87 122 if self.swig_opts:
Chris@87 123 log.warn('ignoring --swigflags as --swig-opts already used')
Chris@87 124 else:
Chris@87 125 self.swig_opts = self.swigflags
Chris@87 126 self.swigflags = None
Chris@87 127
Chris@87 128 if self.swig_opts is None:
Chris@87 129 self.swig_opts = []
Chris@87 130 else:
Chris@87 131 self.swig_opts = shlex.split(self.swig_opts)
Chris@87 132
Chris@87 133 # use options from build_ext command
Chris@87 134 build_ext = self.get_finalized_command('build_ext')
Chris@87 135 if self.inplace is None:
Chris@87 136 self.inplace = build_ext.inplace
Chris@87 137 if self.swig_cpp is None:
Chris@87 138 self.swig_cpp = build_ext.swig_cpp
Chris@87 139 for c in ['swig', 'swig_opt']:
Chris@87 140 o = '--'+c.replace('_', '-')
Chris@87 141 v = getattr(build_ext, c, None)
Chris@87 142 if v:
Chris@87 143 if getattr(self, c):
Chris@87 144 log.warn('both build_src and build_ext define %s option' % (o))
Chris@87 145 else:
Chris@87 146 log.info('using "%s=%s" option from build_ext command' % (o, v))
Chris@87 147 setattr(self, c, v)
Chris@87 148
Chris@87 149 def run(self):
Chris@87 150 log.info("build_src")
Chris@87 151 if not (self.extensions or self.libraries):
Chris@87 152 return
Chris@87 153 self.build_sources()
Chris@87 154
Chris@87 155 def build_sources(self):
Chris@87 156
Chris@87 157 if self.inplace:
Chris@87 158 self.get_package_dir = \
Chris@87 159 self.get_finalized_command('build_py').get_package_dir
Chris@87 160
Chris@87 161 self.build_py_modules_sources()
Chris@87 162
Chris@87 163 for libname_info in self.libraries:
Chris@87 164 self.build_library_sources(*libname_info)
Chris@87 165
Chris@87 166 if self.extensions:
Chris@87 167 self.check_extensions_list(self.extensions)
Chris@87 168
Chris@87 169 for ext in self.extensions:
Chris@87 170 self.build_extension_sources(ext)
Chris@87 171
Chris@87 172 self.build_data_files_sources()
Chris@87 173 self.build_npy_pkg_config()
Chris@87 174
Chris@87 175 def build_data_files_sources(self):
Chris@87 176 if not self.data_files:
Chris@87 177 return
Chris@87 178 log.info('building data_files sources')
Chris@87 179 from numpy.distutils.misc_util import get_data_files
Chris@87 180 new_data_files = []
Chris@87 181 for data in self.data_files:
Chris@87 182 if isinstance(data, str):
Chris@87 183 new_data_files.append(data)
Chris@87 184 elif isinstance(data, tuple):
Chris@87 185 d, files = data
Chris@87 186 if self.inplace:
Chris@87 187 build_dir = self.get_package_dir('.'.join(d.split(os.sep)))
Chris@87 188 else:
Chris@87 189 build_dir = os.path.join(self.build_src, d)
Chris@87 190 funcs = [f for f in files if hasattr(f, '__call__')]
Chris@87 191 files = [f for f in files if not hasattr(f, '__call__')]
Chris@87 192 for f in funcs:
Chris@87 193 if f.__code__.co_argcount==1:
Chris@87 194 s = f(build_dir)
Chris@87 195 else:
Chris@87 196 s = f()
Chris@87 197 if s is not None:
Chris@87 198 if isinstance(s, list):
Chris@87 199 files.extend(s)
Chris@87 200 elif isinstance(s, str):
Chris@87 201 files.append(s)
Chris@87 202 else:
Chris@87 203 raise TypeError(repr(s))
Chris@87 204 filenames = get_data_files((d, files))
Chris@87 205 new_data_files.append((d, filenames))
Chris@87 206 else:
Chris@87 207 raise TypeError(repr(data))
Chris@87 208 self.data_files[:] = new_data_files
Chris@87 209
Chris@87 210
Chris@87 211 def _build_npy_pkg_config(self, info, gd):
Chris@87 212 import shutil
Chris@87 213 template, install_dir, subst_dict = info
Chris@87 214 template_dir = os.path.dirname(template)
Chris@87 215 for k, v in gd.items():
Chris@87 216 subst_dict[k] = v
Chris@87 217
Chris@87 218 if self.inplace == 1:
Chris@87 219 generated_dir = os.path.join(template_dir, install_dir)
Chris@87 220 else:
Chris@87 221 generated_dir = os.path.join(self.build_src, template_dir,
Chris@87 222 install_dir)
Chris@87 223 generated = os.path.basename(os.path.splitext(template)[0])
Chris@87 224 generated_path = os.path.join(generated_dir, generated)
Chris@87 225 if not os.path.exists(generated_dir):
Chris@87 226 os.makedirs(generated_dir)
Chris@87 227
Chris@87 228 subst_vars(generated_path, template, subst_dict)
Chris@87 229
Chris@87 230 # Where to install relatively to install prefix
Chris@87 231 full_install_dir = os.path.join(template_dir, install_dir)
Chris@87 232 return full_install_dir, generated_path
Chris@87 233
Chris@87 234 def build_npy_pkg_config(self):
Chris@87 235 log.info('build_src: building npy-pkg config files')
Chris@87 236
Chris@87 237 # XXX: another ugly workaround to circumvent distutils brain damage. We
Chris@87 238 # need the install prefix here, but finalizing the options of the
Chris@87 239 # install command when only building sources cause error. Instead, we
Chris@87 240 # copy the install command instance, and finalize the copy so that it
Chris@87 241 # does not disrupt how distutils want to do things when with the
Chris@87 242 # original install command instance.
Chris@87 243 install_cmd = copy.copy(get_cmd('install'))
Chris@87 244 if not install_cmd.finalized == 1:
Chris@87 245 install_cmd.finalize_options()
Chris@87 246 build_npkg = False
Chris@87 247 gd = {}
Chris@87 248 if self.inplace == 1:
Chris@87 249 top_prefix = '.'
Chris@87 250 build_npkg = True
Chris@87 251 elif hasattr(install_cmd, 'install_libbase'):
Chris@87 252 top_prefix = install_cmd.install_libbase
Chris@87 253 build_npkg = True
Chris@87 254
Chris@87 255 if build_npkg:
Chris@87 256 for pkg, infos in self.distribution.installed_pkg_config.items():
Chris@87 257 pkg_path = self.distribution.package_dir[pkg]
Chris@87 258 prefix = os.path.join(os.path.abspath(top_prefix), pkg_path)
Chris@87 259 d = {'prefix': prefix}
Chris@87 260 for info in infos:
Chris@87 261 install_dir, generated = self._build_npy_pkg_config(info, d)
Chris@87 262 self.distribution.data_files.append((install_dir,
Chris@87 263 [generated]))
Chris@87 264
Chris@87 265 def build_py_modules_sources(self):
Chris@87 266 if not self.py_modules:
Chris@87 267 return
Chris@87 268 log.info('building py_modules sources')
Chris@87 269 new_py_modules = []
Chris@87 270 for source in self.py_modules:
Chris@87 271 if is_sequence(source) and len(source)==3:
Chris@87 272 package, module_base, source = source
Chris@87 273 if self.inplace:
Chris@87 274 build_dir = self.get_package_dir(package)
Chris@87 275 else:
Chris@87 276 build_dir = os.path.join(self.build_src,
Chris@87 277 os.path.join(*package.split('.')))
Chris@87 278 if hasattr(source, '__call__'):
Chris@87 279 target = os.path.join(build_dir, module_base + '.py')
Chris@87 280 source = source(target)
Chris@87 281 if source is None:
Chris@87 282 continue
Chris@87 283 modules = [(package, module_base, source)]
Chris@87 284 if package not in self.py_modules_dict:
Chris@87 285 self.py_modules_dict[package] = []
Chris@87 286 self.py_modules_dict[package] += modules
Chris@87 287 else:
Chris@87 288 new_py_modules.append(source)
Chris@87 289 self.py_modules[:] = new_py_modules
Chris@87 290
Chris@87 291 def build_library_sources(self, lib_name, build_info):
Chris@87 292 sources = list(build_info.get('sources', []))
Chris@87 293
Chris@87 294 if not sources:
Chris@87 295 return
Chris@87 296
Chris@87 297 log.info('building library "%s" sources' % (lib_name))
Chris@87 298
Chris@87 299 sources = self.generate_sources(sources, (lib_name, build_info))
Chris@87 300
Chris@87 301 sources = self.template_sources(sources, (lib_name, build_info))
Chris@87 302
Chris@87 303 sources, h_files = self.filter_h_files(sources)
Chris@87 304
Chris@87 305 if h_files:
Chris@87 306 log.info('%s - nothing done with h_files = %s',
Chris@87 307 self.package, h_files)
Chris@87 308
Chris@87 309 #for f in h_files:
Chris@87 310 # self.distribution.headers.append((lib_name,f))
Chris@87 311
Chris@87 312 build_info['sources'] = sources
Chris@87 313 return
Chris@87 314
Chris@87 315 def build_extension_sources(self, ext):
Chris@87 316
Chris@87 317 sources = list(ext.sources)
Chris@87 318
Chris@87 319 log.info('building extension "%s" sources' % (ext.name))
Chris@87 320
Chris@87 321 fullname = self.get_ext_fullname(ext.name)
Chris@87 322
Chris@87 323 modpath = fullname.split('.')
Chris@87 324 package = '.'.join(modpath[0:-1])
Chris@87 325
Chris@87 326 if self.inplace:
Chris@87 327 self.ext_target_dir = self.get_package_dir(package)
Chris@87 328
Chris@87 329 sources = self.generate_sources(sources, ext)
Chris@87 330
Chris@87 331 sources = self.template_sources(sources, ext)
Chris@87 332
Chris@87 333 sources = self.swig_sources(sources, ext)
Chris@87 334
Chris@87 335 sources = self.f2py_sources(sources, ext)
Chris@87 336
Chris@87 337 sources = self.pyrex_sources(sources, ext)
Chris@87 338
Chris@87 339 sources, py_files = self.filter_py_files(sources)
Chris@87 340
Chris@87 341 if package not in self.py_modules_dict:
Chris@87 342 self.py_modules_dict[package] = []
Chris@87 343 modules = []
Chris@87 344 for f in py_files:
Chris@87 345 module = os.path.splitext(os.path.basename(f))[0]
Chris@87 346 modules.append((package, module, f))
Chris@87 347 self.py_modules_dict[package] += modules
Chris@87 348
Chris@87 349 sources, h_files = self.filter_h_files(sources)
Chris@87 350
Chris@87 351 if h_files:
Chris@87 352 log.info('%s - nothing done with h_files = %s',
Chris@87 353 package, h_files)
Chris@87 354 #for f in h_files:
Chris@87 355 # self.distribution.headers.append((package,f))
Chris@87 356
Chris@87 357 ext.sources = sources
Chris@87 358
Chris@87 359 def generate_sources(self, sources, extension):
Chris@87 360 new_sources = []
Chris@87 361 func_sources = []
Chris@87 362 for source in sources:
Chris@87 363 if is_string(source):
Chris@87 364 new_sources.append(source)
Chris@87 365 else:
Chris@87 366 func_sources.append(source)
Chris@87 367 if not func_sources:
Chris@87 368 return new_sources
Chris@87 369 if self.inplace and not is_sequence(extension):
Chris@87 370 build_dir = self.ext_target_dir
Chris@87 371 else:
Chris@87 372 if is_sequence(extension):
Chris@87 373 name = extension[0]
Chris@87 374 # if 'include_dirs' not in extension[1]:
Chris@87 375 # extension[1]['include_dirs'] = []
Chris@87 376 # incl_dirs = extension[1]['include_dirs']
Chris@87 377 else:
Chris@87 378 name = extension.name
Chris@87 379 # incl_dirs = extension.include_dirs
Chris@87 380 #if self.build_src not in incl_dirs:
Chris@87 381 # incl_dirs.append(self.build_src)
Chris@87 382 build_dir = os.path.join(*([self.build_src]\
Chris@87 383 +name.split('.')[:-1]))
Chris@87 384 self.mkpath(build_dir)
Chris@87 385 for func in func_sources:
Chris@87 386 source = func(extension, build_dir)
Chris@87 387 if not source:
Chris@87 388 continue
Chris@87 389 if is_sequence(source):
Chris@87 390 [log.info(" adding '%s' to sources." % (s,)) for s in source]
Chris@87 391 new_sources.extend(source)
Chris@87 392 else:
Chris@87 393 log.info(" adding '%s' to sources." % (source,))
Chris@87 394 new_sources.append(source)
Chris@87 395
Chris@87 396 return new_sources
Chris@87 397
Chris@87 398 def filter_py_files(self, sources):
Chris@87 399 return self.filter_files(sources, ['.py'])
Chris@87 400
Chris@87 401 def filter_h_files(self, sources):
Chris@87 402 return self.filter_files(sources, ['.h', '.hpp', '.inc'])
Chris@87 403
Chris@87 404 def filter_files(self, sources, exts = []):
Chris@87 405 new_sources = []
Chris@87 406 files = []
Chris@87 407 for source in sources:
Chris@87 408 (base, ext) = os.path.splitext(source)
Chris@87 409 if ext in exts:
Chris@87 410 files.append(source)
Chris@87 411 else:
Chris@87 412 new_sources.append(source)
Chris@87 413 return new_sources, files
Chris@87 414
Chris@87 415 def template_sources(self, sources, extension):
Chris@87 416 new_sources = []
Chris@87 417 if is_sequence(extension):
Chris@87 418 depends = extension[1].get('depends')
Chris@87 419 include_dirs = extension[1].get('include_dirs')
Chris@87 420 else:
Chris@87 421 depends = extension.depends
Chris@87 422 include_dirs = extension.include_dirs
Chris@87 423 for source in sources:
Chris@87 424 (base, ext) = os.path.splitext(source)
Chris@87 425 if ext == '.src': # Template file
Chris@87 426 if self.inplace:
Chris@87 427 target_dir = os.path.dirname(base)
Chris@87 428 else:
Chris@87 429 target_dir = appendpath(self.build_src, os.path.dirname(base))
Chris@87 430 self.mkpath(target_dir)
Chris@87 431 target_file = os.path.join(target_dir, os.path.basename(base))
Chris@87 432 if (self.force or newer_group([source] + depends, target_file)):
Chris@87 433 if _f_pyf_ext_match(base):
Chris@87 434 log.info("from_template:> %s" % (target_file))
Chris@87 435 outstr = process_f_file(source)
Chris@87 436 else:
Chris@87 437 log.info("conv_template:> %s" % (target_file))
Chris@87 438 outstr = process_c_file(source)
Chris@87 439 fid = open(target_file, 'w')
Chris@87 440 fid.write(outstr)
Chris@87 441 fid.close()
Chris@87 442 if _header_ext_match(target_file):
Chris@87 443 d = os.path.dirname(target_file)
Chris@87 444 if d not in include_dirs:
Chris@87 445 log.info(" adding '%s' to include_dirs." % (d))
Chris@87 446 include_dirs.append(d)
Chris@87 447 new_sources.append(target_file)
Chris@87 448 else:
Chris@87 449 new_sources.append(source)
Chris@87 450 return new_sources
Chris@87 451
Chris@87 452 def pyrex_sources(self, sources, extension):
Chris@87 453 new_sources = []
Chris@87 454 ext_name = extension.name.split('.')[-1]
Chris@87 455 for source in sources:
Chris@87 456 (base, ext) = os.path.splitext(source)
Chris@87 457 if ext == '.pyx':
Chris@87 458 target_file = self.generate_a_pyrex_source(base, ext_name,
Chris@87 459 source,
Chris@87 460 extension)
Chris@87 461 new_sources.append(target_file)
Chris@87 462 else:
Chris@87 463 new_sources.append(source)
Chris@87 464 return new_sources
Chris@87 465
Chris@87 466 def generate_a_pyrex_source(self, base, ext_name, source, extension):
Chris@87 467 if self.inplace or not have_pyrex():
Chris@87 468 target_dir = os.path.dirname(base)
Chris@87 469 else:
Chris@87 470 target_dir = appendpath(self.build_src, os.path.dirname(base))
Chris@87 471 target_file = os.path.join(target_dir, ext_name + '.c')
Chris@87 472 depends = [source] + extension.depends
Chris@87 473 if self.force or newer_group(depends, target_file, 'newer'):
Chris@87 474 if have_pyrex():
Chris@87 475 import Pyrex.Compiler.Main
Chris@87 476 log.info("pyrexc:> %s" % (target_file))
Chris@87 477 self.mkpath(target_dir)
Chris@87 478 options = Pyrex.Compiler.Main.CompilationOptions(
Chris@87 479 defaults=Pyrex.Compiler.Main.default_options,
Chris@87 480 include_path=extension.include_dirs,
Chris@87 481 output_file=target_file)
Chris@87 482 pyrex_result = Pyrex.Compiler.Main.compile(source,
Chris@87 483 options=options)
Chris@87 484 if pyrex_result.num_errors != 0:
Chris@87 485 raise DistutilsError("%d errors while compiling %r with Pyrex" \
Chris@87 486 % (pyrex_result.num_errors, source))
Chris@87 487 elif os.path.isfile(target_file):
Chris@87 488 log.warn("Pyrex required for compiling %r but not available,"\
Chris@87 489 " using old target %r"\
Chris@87 490 % (source, target_file))
Chris@87 491 else:
Chris@87 492 raise DistutilsError("Pyrex required for compiling %r"\
Chris@87 493 " but notavailable" % (source,))
Chris@87 494 return target_file
Chris@87 495
Chris@87 496 def f2py_sources(self, sources, extension):
Chris@87 497 new_sources = []
Chris@87 498 f2py_sources = []
Chris@87 499 f_sources = []
Chris@87 500 f2py_targets = {}
Chris@87 501 target_dirs = []
Chris@87 502 ext_name = extension.name.split('.')[-1]
Chris@87 503 skip_f2py = 0
Chris@87 504
Chris@87 505 for source in sources:
Chris@87 506 (base, ext) = os.path.splitext(source)
Chris@87 507 if ext == '.pyf': # F2PY interface file
Chris@87 508 if self.inplace:
Chris@87 509 target_dir = os.path.dirname(base)
Chris@87 510 else:
Chris@87 511 target_dir = appendpath(self.build_src, os.path.dirname(base))
Chris@87 512 if os.path.isfile(source):
Chris@87 513 name = get_f2py_modulename(source)
Chris@87 514 if name != ext_name:
Chris@87 515 raise DistutilsSetupError('mismatch of extension names: %s '
Chris@87 516 'provides %r but expected %r' % (
Chris@87 517 source, name, ext_name))
Chris@87 518 target_file = os.path.join(target_dir, name+'module.c')
Chris@87 519 else:
Chris@87 520 log.debug(' source %s does not exist: skipping f2py\'ing.' \
Chris@87 521 % (source))
Chris@87 522 name = ext_name
Chris@87 523 skip_f2py = 1
Chris@87 524 target_file = os.path.join(target_dir, name+'module.c')
Chris@87 525 if not os.path.isfile(target_file):
Chris@87 526 log.warn(' target %s does not exist:\n '\
Chris@87 527 'Assuming %smodule.c was generated with '\
Chris@87 528 '"build_src --inplace" command.' \
Chris@87 529 % (target_file, name))
Chris@87 530 target_dir = os.path.dirname(base)
Chris@87 531 target_file = os.path.join(target_dir, name+'module.c')
Chris@87 532 if not os.path.isfile(target_file):
Chris@87 533 raise DistutilsSetupError("%r missing" % (target_file,))
Chris@87 534 log.info(' Yes! Using %r as up-to-date target.' \
Chris@87 535 % (target_file))
Chris@87 536 target_dirs.append(target_dir)
Chris@87 537 f2py_sources.append(source)
Chris@87 538 f2py_targets[source] = target_file
Chris@87 539 new_sources.append(target_file)
Chris@87 540 elif fortran_ext_match(ext):
Chris@87 541 f_sources.append(source)
Chris@87 542 else:
Chris@87 543 new_sources.append(source)
Chris@87 544
Chris@87 545 if not (f2py_sources or f_sources):
Chris@87 546 return new_sources
Chris@87 547
Chris@87 548 for d in target_dirs:
Chris@87 549 self.mkpath(d)
Chris@87 550
Chris@87 551 f2py_options = extension.f2py_options + self.f2py_opts
Chris@87 552
Chris@87 553 if self.distribution.libraries:
Chris@87 554 for name, build_info in self.distribution.libraries:
Chris@87 555 if name in extension.libraries:
Chris@87 556 f2py_options.extend(build_info.get('f2py_options', []))
Chris@87 557
Chris@87 558 log.info("f2py options: %s" % (f2py_options))
Chris@87 559
Chris@87 560 if f2py_sources:
Chris@87 561 if len(f2py_sources) != 1:
Chris@87 562 raise DistutilsSetupError(
Chris@87 563 'only one .pyf file is allowed per extension module but got'\
Chris@87 564 ' more: %r' % (f2py_sources,))
Chris@87 565 source = f2py_sources[0]
Chris@87 566 target_file = f2py_targets[source]
Chris@87 567 target_dir = os.path.dirname(target_file) or '.'
Chris@87 568 depends = [source] + extension.depends
Chris@87 569 if (self.force or newer_group(depends, target_file, 'newer')) \
Chris@87 570 and not skip_f2py:
Chris@87 571 log.info("f2py: %s" % (source))
Chris@87 572 import numpy.f2py
Chris@87 573 numpy.f2py.run_main(f2py_options
Chris@87 574 + ['--build-dir', target_dir, source])
Chris@87 575 else:
Chris@87 576 log.debug(" skipping '%s' f2py interface (up-to-date)" % (source))
Chris@87 577 else:
Chris@87 578 #XXX TODO: --inplace support for sdist command
Chris@87 579 if is_sequence(extension):
Chris@87 580 name = extension[0]
Chris@87 581 else: name = extension.name
Chris@87 582 target_dir = os.path.join(*([self.build_src]\
Chris@87 583 +name.split('.')[:-1]))
Chris@87 584 target_file = os.path.join(target_dir, ext_name + 'module.c')
Chris@87 585 new_sources.append(target_file)
Chris@87 586 depends = f_sources + extension.depends
Chris@87 587 if (self.force or newer_group(depends, target_file, 'newer')) \
Chris@87 588 and not skip_f2py:
Chris@87 589 log.info("f2py:> %s" % (target_file))
Chris@87 590 self.mkpath(target_dir)
Chris@87 591 import numpy.f2py
Chris@87 592 numpy.f2py.run_main(f2py_options + ['--lower',
Chris@87 593 '--build-dir', target_dir]+\
Chris@87 594 ['-m', ext_name]+f_sources)
Chris@87 595 else:
Chris@87 596 log.debug(" skipping f2py fortran files for '%s' (up-to-date)"\
Chris@87 597 % (target_file))
Chris@87 598
Chris@87 599 if not os.path.isfile(target_file):
Chris@87 600 raise DistutilsError("f2py target file %r not generated" % (target_file,))
Chris@87 601
Chris@87 602 target_c = os.path.join(self.build_src, 'fortranobject.c')
Chris@87 603 target_h = os.path.join(self.build_src, 'fortranobject.h')
Chris@87 604 log.info(" adding '%s' to sources." % (target_c))
Chris@87 605 new_sources.append(target_c)
Chris@87 606 if self.build_src not in extension.include_dirs:
Chris@87 607 log.info(" adding '%s' to include_dirs." \
Chris@87 608 % (self.build_src))
Chris@87 609 extension.include_dirs.append(self.build_src)
Chris@87 610
Chris@87 611 if not skip_f2py:
Chris@87 612 import numpy.f2py
Chris@87 613 d = os.path.dirname(numpy.f2py.__file__)
Chris@87 614 source_c = os.path.join(d, 'src', 'fortranobject.c')
Chris@87 615 source_h = os.path.join(d, 'src', 'fortranobject.h')
Chris@87 616 if newer(source_c, target_c) or newer(source_h, target_h):
Chris@87 617 self.mkpath(os.path.dirname(target_c))
Chris@87 618 self.copy_file(source_c, target_c)
Chris@87 619 self.copy_file(source_h, target_h)
Chris@87 620 else:
Chris@87 621 if not os.path.isfile(target_c):
Chris@87 622 raise DistutilsSetupError("f2py target_c file %r not found" % (target_c,))
Chris@87 623 if not os.path.isfile(target_h):
Chris@87 624 raise DistutilsSetupError("f2py target_h file %r not found" % (target_h,))
Chris@87 625
Chris@87 626 for name_ext in ['-f2pywrappers.f', '-f2pywrappers2.f90']:
Chris@87 627 filename = os.path.join(target_dir, ext_name + name_ext)
Chris@87 628 if os.path.isfile(filename):
Chris@87 629 log.info(" adding '%s' to sources." % (filename))
Chris@87 630 f_sources.append(filename)
Chris@87 631
Chris@87 632 return new_sources + f_sources
Chris@87 633
Chris@87 634 def swig_sources(self, sources, extension):
Chris@87 635 # Assuming SWIG 1.3.14 or later. See compatibility note in
Chris@87 636 # http://www.swig.org/Doc1.3/Python.html#Python_nn6
Chris@87 637
Chris@87 638 new_sources = []
Chris@87 639 swig_sources = []
Chris@87 640 swig_targets = {}
Chris@87 641 target_dirs = []
Chris@87 642 py_files = [] # swig generated .py files
Chris@87 643 target_ext = '.c'
Chris@87 644 if '-c++' in extension.swig_opts:
Chris@87 645 typ = 'c++'
Chris@87 646 is_cpp = True
Chris@87 647 extension.swig_opts.remove('-c++')
Chris@87 648 elif self.swig_cpp:
Chris@87 649 typ = 'c++'
Chris@87 650 is_cpp = True
Chris@87 651 else:
Chris@87 652 typ = None
Chris@87 653 is_cpp = False
Chris@87 654 skip_swig = 0
Chris@87 655 ext_name = extension.name.split('.')[-1]
Chris@87 656
Chris@87 657 for source in sources:
Chris@87 658 (base, ext) = os.path.splitext(source)
Chris@87 659 if ext == '.i': # SWIG interface file
Chris@87 660 # the code below assumes that the sources list
Chris@87 661 # contains not more than one .i SWIG interface file
Chris@87 662 if self.inplace:
Chris@87 663 target_dir = os.path.dirname(base)
Chris@87 664 py_target_dir = self.ext_target_dir
Chris@87 665 else:
Chris@87 666 target_dir = appendpath(self.build_src, os.path.dirname(base))
Chris@87 667 py_target_dir = target_dir
Chris@87 668 if os.path.isfile(source):
Chris@87 669 name = get_swig_modulename(source)
Chris@87 670 if name != ext_name[1:]:
Chris@87 671 raise DistutilsSetupError(
Chris@87 672 'mismatch of extension names: %s provides %r'
Chris@87 673 ' but expected %r' % (source, name, ext_name[1:]))
Chris@87 674 if typ is None:
Chris@87 675 typ = get_swig_target(source)
Chris@87 676 is_cpp = typ=='c++'
Chris@87 677 else:
Chris@87 678 typ2 = get_swig_target(source)
Chris@87 679 if typ2 is None:
Chris@87 680 log.warn('source %r does not define swig target, assuming %s swig target' \
Chris@87 681 % (source, typ))
Chris@87 682 elif typ!=typ2:
Chris@87 683 log.warn('expected %r but source %r defines %r swig target' \
Chris@87 684 % (typ, source, typ2))
Chris@87 685 if typ2=='c++':
Chris@87 686 log.warn('resetting swig target to c++ (some targets may have .c extension)')
Chris@87 687 is_cpp = True
Chris@87 688 else:
Chris@87 689 log.warn('assuming that %r has c++ swig target' % (source))
Chris@87 690 if is_cpp:
Chris@87 691 target_ext = '.cpp'
Chris@87 692 target_file = os.path.join(target_dir, '%s_wrap%s' \
Chris@87 693 % (name, target_ext))
Chris@87 694 else:
Chris@87 695 log.warn(' source %s does not exist: skipping swig\'ing.' \
Chris@87 696 % (source))
Chris@87 697 name = ext_name[1:]
Chris@87 698 skip_swig = 1
Chris@87 699 target_file = _find_swig_target(target_dir, name)
Chris@87 700 if not os.path.isfile(target_file):
Chris@87 701 log.warn(' target %s does not exist:\n '\
Chris@87 702 'Assuming %s_wrap.{c,cpp} was generated with '\
Chris@87 703 '"build_src --inplace" command.' \
Chris@87 704 % (target_file, name))
Chris@87 705 target_dir = os.path.dirname(base)
Chris@87 706 target_file = _find_swig_target(target_dir, name)
Chris@87 707 if not os.path.isfile(target_file):
Chris@87 708 raise DistutilsSetupError("%r missing" % (target_file,))
Chris@87 709 log.warn(' Yes! Using %r as up-to-date target.' \
Chris@87 710 % (target_file))
Chris@87 711 target_dirs.append(target_dir)
Chris@87 712 new_sources.append(target_file)
Chris@87 713 py_files.append(os.path.join(py_target_dir, name+'.py'))
Chris@87 714 swig_sources.append(source)
Chris@87 715 swig_targets[source] = new_sources[-1]
Chris@87 716 else:
Chris@87 717 new_sources.append(source)
Chris@87 718
Chris@87 719 if not swig_sources:
Chris@87 720 return new_sources
Chris@87 721
Chris@87 722 if skip_swig:
Chris@87 723 return new_sources + py_files
Chris@87 724
Chris@87 725 for d in target_dirs:
Chris@87 726 self.mkpath(d)
Chris@87 727
Chris@87 728 swig = self.swig or self.find_swig()
Chris@87 729 swig_cmd = [swig, "-python"] + extension.swig_opts
Chris@87 730 if is_cpp:
Chris@87 731 swig_cmd.append('-c++')
Chris@87 732 for d in extension.include_dirs:
Chris@87 733 swig_cmd.append('-I'+d)
Chris@87 734 for source in swig_sources:
Chris@87 735 target = swig_targets[source]
Chris@87 736 depends = [source] + extension.depends
Chris@87 737 if self.force or newer_group(depends, target, 'newer'):
Chris@87 738 log.info("%s: %s" % (os.path.basename(swig) \
Chris@87 739 + (is_cpp and '++' or ''), source))
Chris@87 740 self.spawn(swig_cmd + self.swig_opts \
Chris@87 741 + ["-o", target, '-outdir', py_target_dir, source])
Chris@87 742 else:
Chris@87 743 log.debug(" skipping '%s' swig interface (up-to-date)" \
Chris@87 744 % (source))
Chris@87 745
Chris@87 746 return new_sources + py_files
Chris@87 747
Chris@87 748 _f_pyf_ext_match = re.compile(r'.*[.](f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match
Chris@87 749 _header_ext_match = re.compile(r'.*[.](inc|h|hpp)\Z', re.I).match
Chris@87 750
Chris@87 751 #### SWIG related auxiliary functions ####
Chris@87 752 _swig_module_name_match = re.compile(r'\s*%module\s*(.*\(\s*package\s*=\s*"(?P<package>[\w_]+)".*\)|)\s*(?P<name>[\w_]+)',
Chris@87 753 re.I).match
Chris@87 754 _has_c_header = re.compile(r'-[*]-\s*c\s*-[*]-', re.I).search
Chris@87 755 _has_cpp_header = re.compile(r'-[*]-\s*c[+][+]\s*-[*]-', re.I).search
Chris@87 756
Chris@87 757 def get_swig_target(source):
Chris@87 758 f = open(source, 'r')
Chris@87 759 result = None
Chris@87 760 line = f.readline()
Chris@87 761 if _has_cpp_header(line):
Chris@87 762 result = 'c++'
Chris@87 763 if _has_c_header(line):
Chris@87 764 result = 'c'
Chris@87 765 f.close()
Chris@87 766 return result
Chris@87 767
Chris@87 768 def get_swig_modulename(source):
Chris@87 769 f = open(source, 'r')
Chris@87 770 name = None
Chris@87 771 for line in f:
Chris@87 772 m = _swig_module_name_match(line)
Chris@87 773 if m:
Chris@87 774 name = m.group('name')
Chris@87 775 break
Chris@87 776 f.close()
Chris@87 777 return name
Chris@87 778
Chris@87 779 def _find_swig_target(target_dir, name):
Chris@87 780 for ext in ['.cpp', '.c']:
Chris@87 781 target = os.path.join(target_dir, '%s_wrap%s' % (name, ext))
Chris@87 782 if os.path.isfile(target):
Chris@87 783 break
Chris@87 784 return target
Chris@87 785
Chris@87 786 #### F2PY related auxiliary functions ####
Chris@87 787
Chris@87 788 _f2py_module_name_match = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]+)',
Chris@87 789 re.I).match
Chris@87 790 _f2py_user_module_name_match = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]*?'\
Chris@87 791 '__user__[\w_]*)', re.I).match
Chris@87 792
Chris@87 793 def get_f2py_modulename(source):
Chris@87 794 name = None
Chris@87 795 f = open(source)
Chris@87 796 for line in f:
Chris@87 797 m = _f2py_module_name_match(line)
Chris@87 798 if m:
Chris@87 799 if _f2py_user_module_name_match(line): # skip *__user__* names
Chris@87 800 continue
Chris@87 801 name = m.group('name')
Chris@87 802 break
Chris@87 803 f.close()
Chris@87 804 return name
Chris@87 805
Chris@87 806 ##########################################