diff options
Diffstat (limited to 'extras/swig.py')
-rw-r--r-- | extras/swig.py | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/extras/swig.py b/extras/swig.py new file mode 100644 index 00000000..fd3d6d2c --- /dev/null +++ b/extras/swig.py @@ -0,0 +1,237 @@ +#! /usr/bin/env python +# encoding: UTF-8 +# Petar Forai +# Thomas Nagy 2008-2010 (ita) + +import re +from waflib import Task, Logs +from waflib.TaskGen import extension, feature, after_method +from waflib.Configure import conf +from waflib.Tools import c_preproc + +""" +tasks have to be added dynamically: +- swig interface files may be created at runtime +- the module name may be unknown in advance +""" + +SWIG_EXTS = ['.swig', '.i'] + +re_module = re.compile('%module(?:\s*\(.*\))?\s+(.+)', re.M) + +re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M) +re_2 = re.compile('[#%]include [<"](.*)[">]', re.M) + +class swig(Task.Task): + color = 'BLUE' + run_str = '${SWIG} ${SWIGFLAGS} ${SWIGPATH_ST:INCPATHS} ${SWIGDEF_ST:DEFINES} ${SRC}' + ext_out = ['.h'] # might produce .h files although it is not mandatory + vars = ['SWIG_VERSION', 'SWIGDEPS'] + + def runnable_status(self): + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + if not getattr(self, 'init_outputs', None): + self.init_outputs = True + if not getattr(self, 'module', None): + # search the module name + txt = self.inputs[0].read() + m = re_module.search(txt) + if not m: + raise ValueError("could not find the swig module name") + self.module = m.group(1) + + swig_c(self) + + # add the language-specific output files as nodes + # call funs in the dict swig_langs + for x in self.env['SWIGFLAGS']: + # obtain the language + x = x[1:] + try: + fun = swig_langs[x] + except KeyError: + pass + else: + fun(self) + + return super(swig, self).runnable_status() + + def scan(self): + "scan for swig dependencies, climb the .i files" + lst_src = [] + + seen = [] + missing = [] + to_see = [self.inputs[0]] + + while to_see: + node = to_see.pop(0) + if node in seen: + continue + seen.append(node) + lst_src.append(node) + + # read the file + code = node.read() + code = c_preproc.re_nl.sub('', code) + code = c_preproc.re_cpp.sub(c_preproc.repl, code) + + # find .i files and project headers + names = re_2.findall(code) + for n in names: + for d in self.generator.includes_nodes + [node.parent]: + u = d.find_resource(n) + if u: + to_see.append(u) + break + else: + missing.append(n) + return (lst_src, missing) + +# provide additional language processing +swig_langs = {} +def swigf(fun): + swig_langs[fun.__name__.replace('swig_', '')] = fun + return fun +swig.swigf = swigf + +def swig_c(self): + ext = '.swigwrap_%d.c' % self.generator.idx + flags = self.env['SWIGFLAGS'] + if '-c++' in flags: + ext += 'xx' + out_node = self.inputs[0].parent.find_or_declare(self.module + ext) + + if '-c++' in flags: + c_tsk = self.generator.cxx_hook(out_node) + else: + c_tsk = self.generator.c_hook(out_node) + + c_tsk.set_run_after(self) + + # transfer weights from swig task to c task + if getattr(self, 'weight', None): + c_tsk.weight = self.weight + if getattr(self, 'tree_weight', None): + c_tsk.tree_weight = self.tree_weight + + try: + self.more_tasks.append(c_tsk) + except AttributeError: + self.more_tasks = [c_tsk] + + try: + ltask = self.generator.link_task + except AttributeError: + pass + else: + ltask.set_run_after(c_tsk) + # setting input nodes does not declare the build order + # because the build already started, but it sets + # the dependency to enable rebuilds + ltask.inputs.append(c_tsk.outputs[0]) + + self.outputs.append(out_node) + + if not '-o' in self.env['SWIGFLAGS']: + self.env.append_value('SWIGFLAGS', ['-o', self.outputs[0].abspath()]) + +@swigf +def swig_python(tsk): + node = tsk.inputs[0].parent + if tsk.outdir: + node = tsk.outdir + tsk.set_outputs(node.find_or_declare(tsk.module+'.py')) + +@swigf +def swig_ocaml(tsk): + node = tsk.inputs[0].parent + if tsk.outdir: + node = tsk.outdir + tsk.set_outputs(node.find_or_declare(tsk.module+'.ml')) + tsk.set_outputs(node.find_or_declare(tsk.module+'.mli')) + +@extension(*SWIG_EXTS) +def i_file(self, node): + # the task instance + tsk = self.create_task('swig') + tsk.set_inputs(node) + tsk.module = getattr(self, 'swig_module', None) + + flags = self.to_list(getattr(self, 'swig_flags', [])) + tsk.env.append_value('SWIGFLAGS', flags) + + tsk.outdir = None + if '-outdir' in flags: + outdir = flags[flags.index('-outdir')+1] + outdir = tsk.generator.bld.bldnode.make_node(outdir) + outdir.mkdir() + tsk.outdir = outdir + +@feature('c', 'cxx', 'd', 'fc', 'asm') +@after_method('apply_link', 'process_source') +def enforce_swig_before_link(self): + try: + link_task = self.link_task + except AttributeError: + pass + else: + for x in self.tasks: + if x.__class__.__name__ == 'swig': + link_task.run_after.add(x) + +@conf +def check_swig_version(conf, minver=None): + """ + Check if the swig tool is found matching a given minimum version. + minver should be a tuple, eg. to check for swig >= 1.3.28 pass (1,3,28) as minver. + + If successful, SWIG_VERSION is defined as 'MAJOR.MINOR' + (eg. '1.3') of the actual swig version found. + + :param minver: minimum version + :type minver: tuple of int + :return: swig version + :rtype: tuple of int + """ + assert minver is None or isinstance(minver, tuple) + swigbin = conf.env['SWIG'] + if not swigbin: + conf.fatal('could not find the swig executable') + + # Get swig version string + cmd = swigbin + ['-version'] + Logs.debug('swig: Running swig command %r', cmd) + reg_swig = re.compile(r'SWIG Version\s(.*)', re.M) + swig_out = conf.cmd_and_log(cmd) + swigver_tuple = tuple([int(s) for s in reg_swig.findall(swig_out)[0].split('.')]) + + # Compare swig version with the minimum required + result = (minver is None) or (swigver_tuple >= minver) + + if result: + # Define useful environment variables + swigver = '.'.join([str(x) for x in swigver_tuple[:2]]) + conf.env['SWIG_VERSION'] = swigver + + # Feedback + swigver_full = '.'.join(map(str, swigver_tuple[:3])) + if minver is None: + conf.msg('Checking for swig version', swigver_full) + else: + minver_str = '.'.join(map(str, minver)) + conf.msg('Checking for swig version >= %s' % (minver_str,), swigver_full, color=result and 'GREEN' or 'YELLOW') + + if not result: + conf.fatal('The swig version is too old, expecting %r' % (minver,)) + + return swigver_tuple + +def configure(conf): + conf.find_program('swig', var='SWIG') + conf.env.SWIGPATH_ST = '-I%s' + conf.env.SWIGDEF_ST = '-D%s' + |