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