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