Chris@87: """ Build swig, f2py, pyrex sources. Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import os Chris@87: import re Chris@87: import sys Chris@87: import shlex Chris@87: import copy Chris@87: Chris@87: from distutils.command import build_ext Chris@87: from distutils.dep_util import newer_group, newer Chris@87: from distutils.util import get_platform Chris@87: from distutils.errors import DistutilsError, DistutilsSetupError Chris@87: Chris@87: def have_pyrex(): Chris@87: try: Chris@87: import Pyrex.Compiler.Main Chris@87: return True Chris@87: except ImportError: Chris@87: return False Chris@87: Chris@87: # this import can't be done here, as it uses numpy stuff only available Chris@87: # after it's installed Chris@87: #import numpy.f2py Chris@87: from numpy.distutils import log Chris@87: from numpy.distutils.misc_util import fortran_ext_match, \ Chris@87: appendpath, is_string, is_sequence, get_cmd Chris@87: from numpy.distutils.from_template import process_file as process_f_file Chris@87: from numpy.distutils.conv_template import process_file as process_c_file Chris@87: Chris@87: def subst_vars(target, source, d): Chris@87: """Substitute any occurence of @foo@ by d['foo'] from source file into Chris@87: target.""" Chris@87: var = re.compile('@([a-zA-Z_]+)@') Chris@87: fs = open(source, 'r') Chris@87: try: Chris@87: ft = open(target, 'w') Chris@87: try: Chris@87: for l in fs: Chris@87: m = var.search(l) Chris@87: if m: Chris@87: ft.write(l.replace('@%s@' % m.group(1), d[m.group(1)])) Chris@87: else: Chris@87: ft.write(l) Chris@87: finally: Chris@87: ft.close() Chris@87: finally: Chris@87: fs.close() Chris@87: Chris@87: class build_src(build_ext.build_ext): Chris@87: Chris@87: description = "build sources from SWIG, F2PY files or a function" Chris@87: Chris@87: user_options = [ Chris@87: ('build-src=', 'd', "directory to \"build\" sources to"), Chris@87: ('f2py-opts=', None, "list of f2py command line options"), Chris@87: ('swig=', None, "path to the SWIG executable"), Chris@87: ('swig-opts=', None, "list of SWIG command line options"), Chris@87: ('swig-cpp', None, "make SWIG create C++ files (default is autodetected from sources)"), Chris@87: ('f2pyflags=', None, "additional flags to f2py (use --f2py-opts= instead)"), # obsolete Chris@87: ('swigflags=', None, "additional flags to swig (use --swig-opts= instead)"), # obsolete Chris@87: ('force', 'f', "forcibly build everything (ignore file timestamps)"), Chris@87: ('inplace', 'i', Chris@87: "ignore build-lib and put compiled extensions into the source " + Chris@87: "directory alongside your pure Python modules"), Chris@87: ] Chris@87: Chris@87: boolean_options = ['force', 'inplace'] Chris@87: Chris@87: help_options = [] Chris@87: Chris@87: def initialize_options(self): Chris@87: self.extensions = None Chris@87: self.package = None Chris@87: self.py_modules = None Chris@87: self.py_modules_dict = None Chris@87: self.build_src = None Chris@87: self.build_lib = None Chris@87: self.build_base = None Chris@87: self.force = None Chris@87: self.inplace = None Chris@87: self.package_dir = None Chris@87: self.f2pyflags = None # obsolete Chris@87: self.f2py_opts = None Chris@87: self.swigflags = None # obsolete Chris@87: self.swig_opts = None Chris@87: self.swig_cpp = None Chris@87: self.swig = None Chris@87: Chris@87: def finalize_options(self): Chris@87: self.set_undefined_options('build', Chris@87: ('build_base', 'build_base'), Chris@87: ('build_lib', 'build_lib'), Chris@87: ('force', 'force')) Chris@87: if self.package is None: Chris@87: self.package = self.distribution.ext_package Chris@87: self.extensions = self.distribution.ext_modules Chris@87: self.libraries = self.distribution.libraries or [] Chris@87: self.py_modules = self.distribution.py_modules or [] Chris@87: self.data_files = self.distribution.data_files or [] Chris@87: Chris@87: if self.build_src is None: Chris@87: plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) Chris@87: self.build_src = os.path.join(self.build_base, 'src'+plat_specifier) Chris@87: Chris@87: # py_modules_dict is used in build_py.find_package_modules Chris@87: self.py_modules_dict = {} Chris@87: Chris@87: if self.f2pyflags: Chris@87: if self.f2py_opts: Chris@87: log.warn('ignoring --f2pyflags as --f2py-opts already used') Chris@87: else: Chris@87: self.f2py_opts = self.f2pyflags Chris@87: self.f2pyflags = None Chris@87: if self.f2py_opts is None: Chris@87: self.f2py_opts = [] Chris@87: else: Chris@87: self.f2py_opts = shlex.split(self.f2py_opts) Chris@87: Chris@87: if self.swigflags: Chris@87: if self.swig_opts: Chris@87: log.warn('ignoring --swigflags as --swig-opts already used') Chris@87: else: Chris@87: self.swig_opts = self.swigflags Chris@87: self.swigflags = None Chris@87: Chris@87: if self.swig_opts is None: Chris@87: self.swig_opts = [] Chris@87: else: Chris@87: self.swig_opts = shlex.split(self.swig_opts) Chris@87: Chris@87: # use options from build_ext command Chris@87: build_ext = self.get_finalized_command('build_ext') Chris@87: if self.inplace is None: Chris@87: self.inplace = build_ext.inplace Chris@87: if self.swig_cpp is None: Chris@87: self.swig_cpp = build_ext.swig_cpp Chris@87: for c in ['swig', 'swig_opt']: Chris@87: o = '--'+c.replace('_', '-') Chris@87: v = getattr(build_ext, c, None) Chris@87: if v: Chris@87: if getattr(self, c): Chris@87: log.warn('both build_src and build_ext define %s option' % (o)) Chris@87: else: Chris@87: log.info('using "%s=%s" option from build_ext command' % (o, v)) Chris@87: setattr(self, c, v) Chris@87: Chris@87: def run(self): Chris@87: log.info("build_src") Chris@87: if not (self.extensions or self.libraries): Chris@87: return Chris@87: self.build_sources() Chris@87: Chris@87: def build_sources(self): Chris@87: Chris@87: if self.inplace: Chris@87: self.get_package_dir = \ Chris@87: self.get_finalized_command('build_py').get_package_dir Chris@87: Chris@87: self.build_py_modules_sources() Chris@87: Chris@87: for libname_info in self.libraries: Chris@87: self.build_library_sources(*libname_info) Chris@87: Chris@87: if self.extensions: Chris@87: self.check_extensions_list(self.extensions) Chris@87: Chris@87: for ext in self.extensions: Chris@87: self.build_extension_sources(ext) Chris@87: Chris@87: self.build_data_files_sources() Chris@87: self.build_npy_pkg_config() Chris@87: Chris@87: def build_data_files_sources(self): Chris@87: if not self.data_files: Chris@87: return Chris@87: log.info('building data_files sources') Chris@87: from numpy.distutils.misc_util import get_data_files Chris@87: new_data_files = [] Chris@87: for data in self.data_files: Chris@87: if isinstance(data, str): Chris@87: new_data_files.append(data) Chris@87: elif isinstance(data, tuple): Chris@87: d, files = data Chris@87: if self.inplace: Chris@87: build_dir = self.get_package_dir('.'.join(d.split(os.sep))) Chris@87: else: Chris@87: build_dir = os.path.join(self.build_src, d) Chris@87: funcs = [f for f in files if hasattr(f, '__call__')] Chris@87: files = [f for f in files if not hasattr(f, '__call__')] Chris@87: for f in funcs: Chris@87: if f.__code__.co_argcount==1: Chris@87: s = f(build_dir) Chris@87: else: Chris@87: s = f() Chris@87: if s is not None: Chris@87: if isinstance(s, list): Chris@87: files.extend(s) Chris@87: elif isinstance(s, str): Chris@87: files.append(s) Chris@87: else: Chris@87: raise TypeError(repr(s)) Chris@87: filenames = get_data_files((d, files)) Chris@87: new_data_files.append((d, filenames)) Chris@87: else: Chris@87: raise TypeError(repr(data)) Chris@87: self.data_files[:] = new_data_files Chris@87: Chris@87: Chris@87: def _build_npy_pkg_config(self, info, gd): Chris@87: import shutil Chris@87: template, install_dir, subst_dict = info Chris@87: template_dir = os.path.dirname(template) Chris@87: for k, v in gd.items(): Chris@87: subst_dict[k] = v Chris@87: Chris@87: if self.inplace == 1: Chris@87: generated_dir = os.path.join(template_dir, install_dir) Chris@87: else: Chris@87: generated_dir = os.path.join(self.build_src, template_dir, Chris@87: install_dir) Chris@87: generated = os.path.basename(os.path.splitext(template)[0]) Chris@87: generated_path = os.path.join(generated_dir, generated) Chris@87: if not os.path.exists(generated_dir): Chris@87: os.makedirs(generated_dir) Chris@87: Chris@87: subst_vars(generated_path, template, subst_dict) Chris@87: Chris@87: # Where to install relatively to install prefix Chris@87: full_install_dir = os.path.join(template_dir, install_dir) Chris@87: return full_install_dir, generated_path Chris@87: Chris@87: def build_npy_pkg_config(self): Chris@87: log.info('build_src: building npy-pkg config files') Chris@87: Chris@87: # XXX: another ugly workaround to circumvent distutils brain damage. We Chris@87: # need the install prefix here, but finalizing the options of the Chris@87: # install command when only building sources cause error. Instead, we Chris@87: # copy the install command instance, and finalize the copy so that it Chris@87: # does not disrupt how distutils want to do things when with the Chris@87: # original install command instance. Chris@87: install_cmd = copy.copy(get_cmd('install')) Chris@87: if not install_cmd.finalized == 1: Chris@87: install_cmd.finalize_options() Chris@87: build_npkg = False Chris@87: gd = {} Chris@87: if self.inplace == 1: Chris@87: top_prefix = '.' Chris@87: build_npkg = True Chris@87: elif hasattr(install_cmd, 'install_libbase'): Chris@87: top_prefix = install_cmd.install_libbase Chris@87: build_npkg = True Chris@87: Chris@87: if build_npkg: Chris@87: for pkg, infos in self.distribution.installed_pkg_config.items(): Chris@87: pkg_path = self.distribution.package_dir[pkg] Chris@87: prefix = os.path.join(os.path.abspath(top_prefix), pkg_path) Chris@87: d = {'prefix': prefix} Chris@87: for info in infos: Chris@87: install_dir, generated = self._build_npy_pkg_config(info, d) Chris@87: self.distribution.data_files.append((install_dir, Chris@87: [generated])) Chris@87: Chris@87: def build_py_modules_sources(self): Chris@87: if not self.py_modules: Chris@87: return Chris@87: log.info('building py_modules sources') Chris@87: new_py_modules = [] Chris@87: for source in self.py_modules: Chris@87: if is_sequence(source) and len(source)==3: Chris@87: package, module_base, source = source Chris@87: if self.inplace: Chris@87: build_dir = self.get_package_dir(package) Chris@87: else: Chris@87: build_dir = os.path.join(self.build_src, Chris@87: os.path.join(*package.split('.'))) Chris@87: if hasattr(source, '__call__'): Chris@87: target = os.path.join(build_dir, module_base + '.py') Chris@87: source = source(target) Chris@87: if source is None: Chris@87: continue Chris@87: modules = [(package, module_base, source)] Chris@87: if package not in self.py_modules_dict: Chris@87: self.py_modules_dict[package] = [] Chris@87: self.py_modules_dict[package] += modules Chris@87: else: Chris@87: new_py_modules.append(source) Chris@87: self.py_modules[:] = new_py_modules Chris@87: Chris@87: def build_library_sources(self, lib_name, build_info): Chris@87: sources = list(build_info.get('sources', [])) Chris@87: Chris@87: if not sources: Chris@87: return Chris@87: Chris@87: log.info('building library "%s" sources' % (lib_name)) Chris@87: Chris@87: sources = self.generate_sources(sources, (lib_name, build_info)) Chris@87: Chris@87: sources = self.template_sources(sources, (lib_name, build_info)) Chris@87: Chris@87: sources, h_files = self.filter_h_files(sources) Chris@87: Chris@87: if h_files: Chris@87: log.info('%s - nothing done with h_files = %s', Chris@87: self.package, h_files) Chris@87: Chris@87: #for f in h_files: Chris@87: # self.distribution.headers.append((lib_name,f)) Chris@87: Chris@87: build_info['sources'] = sources Chris@87: return Chris@87: Chris@87: def build_extension_sources(self, ext): Chris@87: Chris@87: sources = list(ext.sources) Chris@87: Chris@87: log.info('building extension "%s" sources' % (ext.name)) Chris@87: Chris@87: fullname = self.get_ext_fullname(ext.name) Chris@87: Chris@87: modpath = fullname.split('.') Chris@87: package = '.'.join(modpath[0:-1]) Chris@87: Chris@87: if self.inplace: Chris@87: self.ext_target_dir = self.get_package_dir(package) Chris@87: Chris@87: sources = self.generate_sources(sources, ext) Chris@87: Chris@87: sources = self.template_sources(sources, ext) Chris@87: Chris@87: sources = self.swig_sources(sources, ext) Chris@87: Chris@87: sources = self.f2py_sources(sources, ext) Chris@87: Chris@87: sources = self.pyrex_sources(sources, ext) Chris@87: Chris@87: sources, py_files = self.filter_py_files(sources) Chris@87: Chris@87: if package not in self.py_modules_dict: Chris@87: self.py_modules_dict[package] = [] Chris@87: modules = [] Chris@87: for f in py_files: Chris@87: module = os.path.splitext(os.path.basename(f))[0] Chris@87: modules.append((package, module, f)) Chris@87: self.py_modules_dict[package] += modules Chris@87: Chris@87: sources, h_files = self.filter_h_files(sources) Chris@87: Chris@87: if h_files: Chris@87: log.info('%s - nothing done with h_files = %s', Chris@87: package, h_files) Chris@87: #for f in h_files: Chris@87: # self.distribution.headers.append((package,f)) Chris@87: Chris@87: ext.sources = sources Chris@87: Chris@87: def generate_sources(self, sources, extension): Chris@87: new_sources = [] Chris@87: func_sources = [] Chris@87: for source in sources: Chris@87: if is_string(source): Chris@87: new_sources.append(source) Chris@87: else: Chris@87: func_sources.append(source) Chris@87: if not func_sources: Chris@87: return new_sources Chris@87: if self.inplace and not is_sequence(extension): Chris@87: build_dir = self.ext_target_dir Chris@87: else: Chris@87: if is_sequence(extension): Chris@87: name = extension[0] Chris@87: # if 'include_dirs' not in extension[1]: Chris@87: # extension[1]['include_dirs'] = [] Chris@87: # incl_dirs = extension[1]['include_dirs'] Chris@87: else: Chris@87: name = extension.name Chris@87: # incl_dirs = extension.include_dirs Chris@87: #if self.build_src not in incl_dirs: Chris@87: # incl_dirs.append(self.build_src) Chris@87: build_dir = os.path.join(*([self.build_src]\ Chris@87: +name.split('.')[:-1])) Chris@87: self.mkpath(build_dir) Chris@87: for func in func_sources: Chris@87: source = func(extension, build_dir) Chris@87: if not source: Chris@87: continue Chris@87: if is_sequence(source): Chris@87: [log.info(" adding '%s' to sources." % (s,)) for s in source] Chris@87: new_sources.extend(source) Chris@87: else: Chris@87: log.info(" adding '%s' to sources." % (source,)) Chris@87: new_sources.append(source) Chris@87: Chris@87: return new_sources Chris@87: Chris@87: def filter_py_files(self, sources): Chris@87: return self.filter_files(sources, ['.py']) Chris@87: Chris@87: def filter_h_files(self, sources): Chris@87: return self.filter_files(sources, ['.h', '.hpp', '.inc']) Chris@87: Chris@87: def filter_files(self, sources, exts = []): Chris@87: new_sources = [] Chris@87: files = [] Chris@87: for source in sources: Chris@87: (base, ext) = os.path.splitext(source) Chris@87: if ext in exts: Chris@87: files.append(source) Chris@87: else: Chris@87: new_sources.append(source) Chris@87: return new_sources, files Chris@87: Chris@87: def template_sources(self, sources, extension): Chris@87: new_sources = [] Chris@87: if is_sequence(extension): Chris@87: depends = extension[1].get('depends') Chris@87: include_dirs = extension[1].get('include_dirs') Chris@87: else: Chris@87: depends = extension.depends Chris@87: include_dirs = extension.include_dirs Chris@87: for source in sources: Chris@87: (base, ext) = os.path.splitext(source) Chris@87: if ext == '.src': # Template file Chris@87: if self.inplace: Chris@87: target_dir = os.path.dirname(base) Chris@87: else: Chris@87: target_dir = appendpath(self.build_src, os.path.dirname(base)) Chris@87: self.mkpath(target_dir) Chris@87: target_file = os.path.join(target_dir, os.path.basename(base)) Chris@87: if (self.force or newer_group([source] + depends, target_file)): Chris@87: if _f_pyf_ext_match(base): Chris@87: log.info("from_template:> %s" % (target_file)) Chris@87: outstr = process_f_file(source) Chris@87: else: Chris@87: log.info("conv_template:> %s" % (target_file)) Chris@87: outstr = process_c_file(source) Chris@87: fid = open(target_file, 'w') Chris@87: fid.write(outstr) Chris@87: fid.close() Chris@87: if _header_ext_match(target_file): Chris@87: d = os.path.dirname(target_file) Chris@87: if d not in include_dirs: Chris@87: log.info(" adding '%s' to include_dirs." % (d)) Chris@87: include_dirs.append(d) Chris@87: new_sources.append(target_file) Chris@87: else: Chris@87: new_sources.append(source) Chris@87: return new_sources Chris@87: Chris@87: def pyrex_sources(self, sources, extension): Chris@87: new_sources = [] Chris@87: ext_name = extension.name.split('.')[-1] Chris@87: for source in sources: Chris@87: (base, ext) = os.path.splitext(source) Chris@87: if ext == '.pyx': Chris@87: target_file = self.generate_a_pyrex_source(base, ext_name, Chris@87: source, Chris@87: extension) Chris@87: new_sources.append(target_file) Chris@87: else: Chris@87: new_sources.append(source) Chris@87: return new_sources Chris@87: Chris@87: def generate_a_pyrex_source(self, base, ext_name, source, extension): Chris@87: if self.inplace or not have_pyrex(): Chris@87: target_dir = os.path.dirname(base) Chris@87: else: Chris@87: target_dir = appendpath(self.build_src, os.path.dirname(base)) Chris@87: target_file = os.path.join(target_dir, ext_name + '.c') Chris@87: depends = [source] + extension.depends Chris@87: if self.force or newer_group(depends, target_file, 'newer'): Chris@87: if have_pyrex(): Chris@87: import Pyrex.Compiler.Main Chris@87: log.info("pyrexc:> %s" % (target_file)) Chris@87: self.mkpath(target_dir) Chris@87: options = Pyrex.Compiler.Main.CompilationOptions( Chris@87: defaults=Pyrex.Compiler.Main.default_options, Chris@87: include_path=extension.include_dirs, Chris@87: output_file=target_file) Chris@87: pyrex_result = Pyrex.Compiler.Main.compile(source, Chris@87: options=options) Chris@87: if pyrex_result.num_errors != 0: Chris@87: raise DistutilsError("%d errors while compiling %r with Pyrex" \ Chris@87: % (pyrex_result.num_errors, source)) Chris@87: elif os.path.isfile(target_file): Chris@87: log.warn("Pyrex required for compiling %r but not available,"\ Chris@87: " using old target %r"\ Chris@87: % (source, target_file)) Chris@87: else: Chris@87: raise DistutilsError("Pyrex required for compiling %r"\ Chris@87: " but notavailable" % (source,)) Chris@87: return target_file Chris@87: Chris@87: def f2py_sources(self, sources, extension): Chris@87: new_sources = [] Chris@87: f2py_sources = [] Chris@87: f_sources = [] Chris@87: f2py_targets = {} Chris@87: target_dirs = [] Chris@87: ext_name = extension.name.split('.')[-1] Chris@87: skip_f2py = 0 Chris@87: Chris@87: for source in sources: Chris@87: (base, ext) = os.path.splitext(source) Chris@87: if ext == '.pyf': # F2PY interface file Chris@87: if self.inplace: Chris@87: target_dir = os.path.dirname(base) Chris@87: else: Chris@87: target_dir = appendpath(self.build_src, os.path.dirname(base)) Chris@87: if os.path.isfile(source): Chris@87: name = get_f2py_modulename(source) Chris@87: if name != ext_name: Chris@87: raise DistutilsSetupError('mismatch of extension names: %s ' Chris@87: 'provides %r but expected %r' % ( Chris@87: source, name, ext_name)) Chris@87: target_file = os.path.join(target_dir, name+'module.c') Chris@87: else: Chris@87: log.debug(' source %s does not exist: skipping f2py\'ing.' \ Chris@87: % (source)) Chris@87: name = ext_name Chris@87: skip_f2py = 1 Chris@87: target_file = os.path.join(target_dir, name+'module.c') Chris@87: if not os.path.isfile(target_file): Chris@87: log.warn(' target %s does not exist:\n '\ Chris@87: 'Assuming %smodule.c was generated with '\ Chris@87: '"build_src --inplace" command.' \ Chris@87: % (target_file, name)) Chris@87: target_dir = os.path.dirname(base) Chris@87: target_file = os.path.join(target_dir, name+'module.c') Chris@87: if not os.path.isfile(target_file): Chris@87: raise DistutilsSetupError("%r missing" % (target_file,)) Chris@87: log.info(' Yes! Using %r as up-to-date target.' \ Chris@87: % (target_file)) Chris@87: target_dirs.append(target_dir) Chris@87: f2py_sources.append(source) Chris@87: f2py_targets[source] = target_file Chris@87: new_sources.append(target_file) Chris@87: elif fortran_ext_match(ext): Chris@87: f_sources.append(source) Chris@87: else: Chris@87: new_sources.append(source) Chris@87: Chris@87: if not (f2py_sources or f_sources): Chris@87: return new_sources Chris@87: Chris@87: for d in target_dirs: Chris@87: self.mkpath(d) Chris@87: Chris@87: f2py_options = extension.f2py_options + self.f2py_opts Chris@87: Chris@87: if self.distribution.libraries: Chris@87: for name, build_info in self.distribution.libraries: Chris@87: if name in extension.libraries: Chris@87: f2py_options.extend(build_info.get('f2py_options', [])) Chris@87: Chris@87: log.info("f2py options: %s" % (f2py_options)) Chris@87: Chris@87: if f2py_sources: Chris@87: if len(f2py_sources) != 1: Chris@87: raise DistutilsSetupError( Chris@87: 'only one .pyf file is allowed per extension module but got'\ Chris@87: ' more: %r' % (f2py_sources,)) Chris@87: source = f2py_sources[0] Chris@87: target_file = f2py_targets[source] Chris@87: target_dir = os.path.dirname(target_file) or '.' Chris@87: depends = [source] + extension.depends Chris@87: if (self.force or newer_group(depends, target_file, 'newer')) \ Chris@87: and not skip_f2py: Chris@87: log.info("f2py: %s" % (source)) Chris@87: import numpy.f2py Chris@87: numpy.f2py.run_main(f2py_options Chris@87: + ['--build-dir', target_dir, source]) Chris@87: else: Chris@87: log.debug(" skipping '%s' f2py interface (up-to-date)" % (source)) Chris@87: else: Chris@87: #XXX TODO: --inplace support for sdist command Chris@87: if is_sequence(extension): Chris@87: name = extension[0] Chris@87: else: name = extension.name Chris@87: target_dir = os.path.join(*([self.build_src]\ Chris@87: +name.split('.')[:-1])) Chris@87: target_file = os.path.join(target_dir, ext_name + 'module.c') Chris@87: new_sources.append(target_file) Chris@87: depends = f_sources + extension.depends Chris@87: if (self.force or newer_group(depends, target_file, 'newer')) \ Chris@87: and not skip_f2py: Chris@87: log.info("f2py:> %s" % (target_file)) Chris@87: self.mkpath(target_dir) Chris@87: import numpy.f2py Chris@87: numpy.f2py.run_main(f2py_options + ['--lower', Chris@87: '--build-dir', target_dir]+\ Chris@87: ['-m', ext_name]+f_sources) Chris@87: else: Chris@87: log.debug(" skipping f2py fortran files for '%s' (up-to-date)"\ Chris@87: % (target_file)) Chris@87: Chris@87: if not os.path.isfile(target_file): Chris@87: raise DistutilsError("f2py target file %r not generated" % (target_file,)) Chris@87: Chris@87: target_c = os.path.join(self.build_src, 'fortranobject.c') Chris@87: target_h = os.path.join(self.build_src, 'fortranobject.h') Chris@87: log.info(" adding '%s' to sources." % (target_c)) Chris@87: new_sources.append(target_c) Chris@87: if self.build_src not in extension.include_dirs: Chris@87: log.info(" adding '%s' to include_dirs." \ Chris@87: % (self.build_src)) Chris@87: extension.include_dirs.append(self.build_src) Chris@87: Chris@87: if not skip_f2py: Chris@87: import numpy.f2py Chris@87: d = os.path.dirname(numpy.f2py.__file__) Chris@87: source_c = os.path.join(d, 'src', 'fortranobject.c') Chris@87: source_h = os.path.join(d, 'src', 'fortranobject.h') Chris@87: if newer(source_c, target_c) or newer(source_h, target_h): Chris@87: self.mkpath(os.path.dirname(target_c)) Chris@87: self.copy_file(source_c, target_c) Chris@87: self.copy_file(source_h, target_h) Chris@87: else: Chris@87: if not os.path.isfile(target_c): Chris@87: raise DistutilsSetupError("f2py target_c file %r not found" % (target_c,)) Chris@87: if not os.path.isfile(target_h): Chris@87: raise DistutilsSetupError("f2py target_h file %r not found" % (target_h,)) Chris@87: Chris@87: for name_ext in ['-f2pywrappers.f', '-f2pywrappers2.f90']: Chris@87: filename = os.path.join(target_dir, ext_name + name_ext) Chris@87: if os.path.isfile(filename): Chris@87: log.info(" adding '%s' to sources." % (filename)) Chris@87: f_sources.append(filename) Chris@87: Chris@87: return new_sources + f_sources Chris@87: Chris@87: def swig_sources(self, sources, extension): Chris@87: # Assuming SWIG 1.3.14 or later. See compatibility note in Chris@87: # http://www.swig.org/Doc1.3/Python.html#Python_nn6 Chris@87: Chris@87: new_sources = [] Chris@87: swig_sources = [] Chris@87: swig_targets = {} Chris@87: target_dirs = [] Chris@87: py_files = [] # swig generated .py files Chris@87: target_ext = '.c' Chris@87: if '-c++' in extension.swig_opts: Chris@87: typ = 'c++' Chris@87: is_cpp = True Chris@87: extension.swig_opts.remove('-c++') Chris@87: elif self.swig_cpp: Chris@87: typ = 'c++' Chris@87: is_cpp = True Chris@87: else: Chris@87: typ = None Chris@87: is_cpp = False Chris@87: skip_swig = 0 Chris@87: ext_name = extension.name.split('.')[-1] Chris@87: Chris@87: for source in sources: Chris@87: (base, ext) = os.path.splitext(source) Chris@87: if ext == '.i': # SWIG interface file Chris@87: # the code below assumes that the sources list Chris@87: # contains not more than one .i SWIG interface file Chris@87: if self.inplace: Chris@87: target_dir = os.path.dirname(base) Chris@87: py_target_dir = self.ext_target_dir Chris@87: else: Chris@87: target_dir = appendpath(self.build_src, os.path.dirname(base)) Chris@87: py_target_dir = target_dir Chris@87: if os.path.isfile(source): Chris@87: name = get_swig_modulename(source) Chris@87: if name != ext_name[1:]: Chris@87: raise DistutilsSetupError( Chris@87: 'mismatch of extension names: %s provides %r' Chris@87: ' but expected %r' % (source, name, ext_name[1:])) Chris@87: if typ is None: Chris@87: typ = get_swig_target(source) Chris@87: is_cpp = typ=='c++' Chris@87: else: Chris@87: typ2 = get_swig_target(source) Chris@87: if typ2 is None: Chris@87: log.warn('source %r does not define swig target, assuming %s swig target' \ Chris@87: % (source, typ)) Chris@87: elif typ!=typ2: Chris@87: log.warn('expected %r but source %r defines %r swig target' \ Chris@87: % (typ, source, typ2)) Chris@87: if typ2=='c++': Chris@87: log.warn('resetting swig target to c++ (some targets may have .c extension)') Chris@87: is_cpp = True Chris@87: else: Chris@87: log.warn('assuming that %r has c++ swig target' % (source)) Chris@87: if is_cpp: Chris@87: target_ext = '.cpp' Chris@87: target_file = os.path.join(target_dir, '%s_wrap%s' \ Chris@87: % (name, target_ext)) Chris@87: else: Chris@87: log.warn(' source %s does not exist: skipping swig\'ing.' \ Chris@87: % (source)) Chris@87: name = ext_name[1:] Chris@87: skip_swig = 1 Chris@87: target_file = _find_swig_target(target_dir, name) Chris@87: if not os.path.isfile(target_file): Chris@87: log.warn(' target %s does not exist:\n '\ Chris@87: 'Assuming %s_wrap.{c,cpp} was generated with '\ Chris@87: '"build_src --inplace" command.' \ Chris@87: % (target_file, name)) Chris@87: target_dir = os.path.dirname(base) Chris@87: target_file = _find_swig_target(target_dir, name) Chris@87: if not os.path.isfile(target_file): Chris@87: raise DistutilsSetupError("%r missing" % (target_file,)) Chris@87: log.warn(' Yes! Using %r as up-to-date target.' \ Chris@87: % (target_file)) Chris@87: target_dirs.append(target_dir) Chris@87: new_sources.append(target_file) Chris@87: py_files.append(os.path.join(py_target_dir, name+'.py')) Chris@87: swig_sources.append(source) Chris@87: swig_targets[source] = new_sources[-1] Chris@87: else: Chris@87: new_sources.append(source) Chris@87: Chris@87: if not swig_sources: Chris@87: return new_sources Chris@87: Chris@87: if skip_swig: Chris@87: return new_sources + py_files Chris@87: Chris@87: for d in target_dirs: Chris@87: self.mkpath(d) Chris@87: Chris@87: swig = self.swig or self.find_swig() Chris@87: swig_cmd = [swig, "-python"] + extension.swig_opts Chris@87: if is_cpp: Chris@87: swig_cmd.append('-c++') Chris@87: for d in extension.include_dirs: Chris@87: swig_cmd.append('-I'+d) Chris@87: for source in swig_sources: Chris@87: target = swig_targets[source] Chris@87: depends = [source] + extension.depends Chris@87: if self.force or newer_group(depends, target, 'newer'): Chris@87: log.info("%s: %s" % (os.path.basename(swig) \ Chris@87: + (is_cpp and '++' or ''), source)) Chris@87: self.spawn(swig_cmd + self.swig_opts \ Chris@87: + ["-o", target, '-outdir', py_target_dir, source]) Chris@87: else: Chris@87: log.debug(" skipping '%s' swig interface (up-to-date)" \ Chris@87: % (source)) Chris@87: Chris@87: return new_sources + py_files Chris@87: Chris@87: _f_pyf_ext_match = re.compile(r'.*[.](f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match Chris@87: _header_ext_match = re.compile(r'.*[.](inc|h|hpp)\Z', re.I).match Chris@87: Chris@87: #### SWIG related auxiliary functions #### Chris@87: _swig_module_name_match = re.compile(r'\s*%module\s*(.*\(\s*package\s*=\s*"(?P[\w_]+)".*\)|)\s*(?P[\w_]+)', Chris@87: re.I).match Chris@87: _has_c_header = re.compile(r'-[*]-\s*c\s*-[*]-', re.I).search Chris@87: _has_cpp_header = re.compile(r'-[*]-\s*c[+][+]\s*-[*]-', re.I).search Chris@87: Chris@87: def get_swig_target(source): Chris@87: f = open(source, 'r') Chris@87: result = None Chris@87: line = f.readline() Chris@87: if _has_cpp_header(line): Chris@87: result = 'c++' Chris@87: if _has_c_header(line): Chris@87: result = 'c' Chris@87: f.close() Chris@87: return result Chris@87: Chris@87: def get_swig_modulename(source): Chris@87: f = open(source, 'r') Chris@87: name = None Chris@87: for line in f: Chris@87: m = _swig_module_name_match(line) Chris@87: if m: Chris@87: name = m.group('name') Chris@87: break Chris@87: f.close() Chris@87: return name Chris@87: Chris@87: def _find_swig_target(target_dir, name): Chris@87: for ext in ['.cpp', '.c']: Chris@87: target = os.path.join(target_dir, '%s_wrap%s' % (name, ext)) Chris@87: if os.path.isfile(target): Chris@87: break Chris@87: return target Chris@87: Chris@87: #### F2PY related auxiliary functions #### Chris@87: Chris@87: _f2py_module_name_match = re.compile(r'\s*python\s*module\s*(?P[\w_]+)', Chris@87: re.I).match Chris@87: _f2py_user_module_name_match = re.compile(r'\s*python\s*module\s*(?P[\w_]*?'\ Chris@87: '__user__[\w_]*)', re.I).match Chris@87: Chris@87: def get_f2py_modulename(source): Chris@87: name = None Chris@87: f = open(source) Chris@87: for line in f: Chris@87: m = _f2py_module_name_match(line) Chris@87: if m: Chris@87: if _f2py_user_module_name_match(line): # skip *__user__* names Chris@87: continue Chris@87: name = m.group('name') Chris@87: break Chris@87: f.close() Chris@87: return name Chris@87: Chris@87: ##########################################