Source code for openalea.sconsx.f2py

"""f2py Tool

Tool-specific initialization for f2py.

There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.

"""

import os
from os.path import join as pjoin, dirname as pdirname, \
                    basename as pbasename, splitext
import re
import sys
import subprocess

import SCons.Action
import SCons.Scanner
import SCons.Tool
import SCons.Util
from SCons.Node.FS import default_fs

CGEN_TEMPLATE   = '%smodule'
FOBJECT_FILE    = 'fortranobject.c'
FWRAP_TEMPLATE  = '%s-f2pywrappers.f'

# Those regex are copied from build_src in numpy.distutils.command
F2PY_MODNAME_MATCH = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]+)',
                                re.I).match
F2PY_UMODNAME_MATCH = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]*?'\
                                     '__user__[\w_]*)',re.I).match
# End of copy

[docs] def get_f2py_modulename_from_txt(source): """This returns the name of the module from the pyf source file. source is expected to be one string, containing the whole source file code.""" name = None for line in source.splitlines(): m = F2PY_MODNAME_MATCH(line) if m: if F2PY_UMODNAME_MATCH(line): # skip *__user__* names continue name = m.group('name') break return name
[docs] def get_f2py_modulename_from_node(source): """This function returns the module name of the pyf file. The argument should be a scons node. This should work even if the node is generated from another scons builder.""" # See email on scons-users from 6th April 2008 (Dmitry Mikhin). name = None node = source.rfile() if node.exists() or not node.is_derived(): name = get_f2py_modulename_from_txt(node.get_contents()) else: try: # XXX: I don't understand this part snode = source.sources[0].rfile() if snode.is_derived(): snode = snode.sources[0].rfile() cnt = snode.get_contents() name = get_f2py_modulename_from_txt(cnt) except AttributeError: pass if name is None: raise ValueError("f2py file %s not found ?" % str(source)) return name
[docs] def F2pyEmitter(target, source, env): build_dir = pdirname(str(target[0])) if is_pyf(str(source[0])): # target is (in this order): # - the C file which will contain the generated code # - the fortranobject.c file # - the f2py fortran wrapper basename = get_f2py_modulename_from_node(source[0]) ntarget = [] cgename = (CGEN_TEMPLATE % basename) + env['CFILESUFFIX'] ntarget.append(default_fs.Entry(pjoin(build_dir, cgename))) fobj = pjoin(build_dir, mangle_fortranobject(basename, FOBJECT_FILE)) ntarget.append(default_fs.Entry(fobj)) f2pywrap = pjoin(build_dir, FWRAP_TEMPLATE % basename) ntarget.append(default_fs.Entry(f2pywrap)) else: ntarget = target fobj = pjoin(build_dir, mangle_fortranobject(str(target[0]), FOBJECT_FILE)) ntarget.append(default_fs.Entry(fobj)) return (ntarget, source)
[docs] def mangle_fortranobject(targetname, filename): basename = pbasename(targetname).split('module')[0] return '%s_%s' % (basename, filename)
[docs] def is_pyf(source_file): return splitext(source_file)[1] == '.pyf'
[docs] def f2py_cmd_exec(cmd): """Executes a f2py command. The point to execute f2py in a new process instead of using f2py.mainto avoid race issues when using multible jobs with scons. cmd should be a sequence. """ f2py_cmd = [sys.executable, '-c', '"from numpy.f2py.f2py2e import run_main;run_main(%s)"' \ % repr(cmd)] p = subprocess.Popen(" ".join(f2py_cmd), shell = True, stdout = subprocess.PIPE) for i in p.stdout.readlines(): print(i.rstrip('\n')) return p.wait()
[docs] def pyf2c(target, source, env): import numpy.f2py import shutil # We need filenames from source/target for path handling target_file_names = [str(i) for i in target] source_file_names = [str(i) for i in source] # Get source files necessary for f2py generated modules d = os.path.dirname(numpy.f2py.__file__) source_c = pjoin(d, 'src', FOBJECT_FILE) # Copy source files for f2py generated modules in the build dir build_dir = pdirname(target_file_names[0]) # XXX: blah if build_dir == '': build_dir = '.' try: cpi = mangle_fortranobject(target_file_names[0], FOBJECT_FILE) shutil.copy(source_c, pjoin(build_dir, cpi)) except IOError as e: msg = "Error while copying fortran source files (error was %s)" % str(e) raise IOError(msg) basename = os.path.basename(str(target[0]).split('module')[0]) # XXX: handle F2PYOPTIONS being a string instead of a list if is_pyf(source_file_names[0]): # XXX: scons has a way to force buidler to only use one source file if len(source_file_names) > 1: raise NotImplementedError("FIXME: multiple source files") wrapper = pjoin(build_dir, FWRAP_TEMPLATE % basename) cmd = env['F2PYOPTIONS'] + \ [source_file_names[0], '--build-dir', build_dir] st = f2py_cmd_exec(cmd) if not os.path.exists(wrapper): f = open(wrapper, 'w') f.close() else: cmd = env['F2PYOPTIONS'] + source_file_names + \ ['--build-dir', build_dir] # fortran files, we need to give the module name cmd.extend(['--lower', '-m', basename]) st = f2py_cmd_exec(cmd) return 0
[docs] def generate(env): """Add Builders and construction variables for swig to an Environment.""" import numpy.f2py d = pdirname(numpy.f2py.__file__) f2pyac = SCons.Action.Action(pyf2c, '$F2PYCOMSTR') c_file, cxx_file = SCons.Tool.createCFileBuilders(env) c_file.add_action('.pyf', SCons.Action.Action(f2pyac)) c_file.add_emitter('.pyf', F2pyEmitter) env['F2PYOPTIONS'] = SCons.Util.CLVar('--quiet') env['F2PYBUILDDIR'] = '' env['F2PYINCLUDEDIR'] = pjoin(d, 'src') if not ("F2PY_NOT_ADD_INCLUDEDIR" in env and env["F2PY_NOT_ADD_INCLUDEDIR"]): env.Prepend(CPPPATH = env["F2PYINCLUDEDIR"]) if 'F2PYCOMSTR' not in env: env['F2PYCOMSTR'] = "f2py: generating $TARGET from $SOURCE" # XXX: adding a scanner using c_file.add_scanner does not work... expr = '(<)include_file=(\S+)>' scanner = SCons.Scanner.ClassicCPP("F2PYScan", ".pyf", "F2PYPATH", expr) env.Append(SCANNERS = scanner) env['BUILDERS']['F2py'] = SCons.Builder.Builder(action = f2pyac, emitter = [F2pyEmitter])
[docs] def exists(env): try: import numpy.f2py st = 1 except Exception as e: #print "Warning : f2py tool not found, error was %s" % e st = 0 return st