Mercurial > hg > vamp-build-and-test
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 ########################################## |