comparison DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/distutils/command/build_src.py @ 87:2a2c65a20a8b

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