4 # Thomas Nagy 2008-2010 (ita)
7 from waflib import Task, Logs
8 from waflib.TaskGen import extension, feature, after_method
9 from waflib.Configure import conf
10 from waflib.Tools import c_preproc
13 tasks have to be added dynamically:
14 - swig interface files may be created at runtime
15 - the module name may be unknown in advance
18 SWIG_EXTS = ['.swig', '.i']
20 re_module = re.compile('%module(?:\s*\(.*\))?\s+(.+)', re.M)
22 re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M)
23 re_2 = re.compile('[#%]include [<"](.*)[">]', re.M)
25 class swig(Task.Task):
27 run_str = '${SWIG} ${SWIGFLAGS} ${SWIGPATH_ST:INCPATHS} ${SWIGDEF_ST:DEFINES} ${SRC}'
28 ext_out = ['.h'] # might produce .h files although it is not mandatory
29 vars = ['SWIG_VERSION', 'SWIGDEPS']
31 def runnable_status(self):
32 for t in self.run_after:
36 if not getattr(self, 'init_outputs', None):
37 self.init_outputs = True
38 if not getattr(self, 'module', None):
39 # search the module name
40 txt = self.inputs[0].read()
41 m = re_module.search(txt)
43 raise ValueError("could not find the swig module name")
44 self.module = m.group(1)
48 # add the language-specific output files as nodes
49 # call funs in the dict swig_langs
50 for x in self.env['SWIGFLAGS']:
60 return super(swig, self).runnable_status()
63 "scan for swig dependencies, climb the .i files"
68 to_see = [self.inputs[0]]
79 code = c_preproc.re_nl.sub('', code)
80 code = c_preproc.re_cpp.sub(c_preproc.repl, code)
82 # find .i files and project headers
83 names = re_2.findall(code)
85 for d in self.generator.includes_nodes + [node.parent]:
86 u = d.find_resource(n)
92 return (lst_src, missing)
94 # provide additional language processing
97 swig_langs[fun.__name__.replace('swig_', '')] = fun
102 ext = '.swigwrap_%d.c' % self.generator.idx
103 flags = self.env['SWIGFLAGS']
106 out_node = self.inputs[0].parent.find_or_declare(self.module + ext)
109 c_tsk = self.generator.cxx_hook(out_node)
111 c_tsk = self.generator.c_hook(out_node)
113 c_tsk.set_run_after(self)
115 # transfer weights from swig task to c task
116 if getattr(self, 'weight', None):
117 c_tsk.weight = self.weight
118 if getattr(self, 'tree_weight', None):
119 c_tsk.tree_weight = self.tree_weight
122 self.more_tasks.append(c_tsk)
123 except AttributeError:
124 self.more_tasks = [c_tsk]
127 ltask = self.generator.link_task
128 except AttributeError:
131 ltask.set_run_after(c_tsk)
132 # setting input nodes does not declare the build order
133 # because the build already started, but it sets
134 # the dependency to enable rebuilds
135 ltask.inputs.append(c_tsk.outputs[0])
137 self.outputs.append(out_node)
139 if not '-o' in self.env['SWIGFLAGS']:
140 self.env.append_value('SWIGFLAGS', ['-o', self.outputs[0].abspath()])
143 def swig_python(tsk):
144 node = tsk.inputs[0].parent
147 tsk.set_outputs(node.find_or_declare(tsk.module+'.py'))
151 node = tsk.inputs[0].parent
154 tsk.set_outputs(node.find_or_declare(tsk.module+'.ml'))
155 tsk.set_outputs(node.find_or_declare(tsk.module+'.mli'))
157 @extension(*SWIG_EXTS)
158 def i_file(self, node):
160 tsk = self.create_task('swig')
162 tsk.module = getattr(self, 'swig_module', None)
164 flags = self.to_list(getattr(self, 'swig_flags', []))
165 tsk.env.append_value('SWIGFLAGS', flags)
168 if '-outdir' in flags:
169 outdir = flags[flags.index('-outdir')+1]
170 outdir = tsk.generator.bld.bldnode.make_node(outdir)
174 @feature('c', 'cxx', 'd', 'fc', 'asm')
175 @after_method('apply_link', 'process_source')
176 def enforce_swig_before_link(self):
178 link_task = self.link_task
179 except AttributeError:
183 if x.__class__.__name__ == 'swig':
184 link_task.run_after.add(x)
187 def check_swig_version(conf, minver=None):
189 Check if the swig tool is found matching a given minimum version.
190 minver should be a tuple, eg. to check for swig >= 1.3.28 pass (1,3,28) as minver.
192 If successful, SWIG_VERSION is defined as 'MAJOR.MINOR'
193 (eg. '1.3') of the actual swig version found.
195 :param minver: minimum version
196 :type minver: tuple of int
197 :return: swig version
200 assert minver is None or isinstance(minver, tuple)
201 swigbin = conf.env['SWIG']
203 conf.fatal('could not find the swig executable')
205 # Get swig version string
206 cmd = swigbin + ['-version']
207 Logs.debug('swig: Running swig command %r', cmd)
208 reg_swig = re.compile(r'SWIG Version\s(.*)', re.M)
209 swig_out = conf.cmd_and_log(cmd)
210 swigver_tuple = tuple([int(s) for s in reg_swig.findall(swig_out)[0].split('.')])
212 # Compare swig version with the minimum required
213 result = (minver is None) or (swigver_tuple >= minver)
216 # Define useful environment variables
217 swigver = '.'.join([str(x) for x in swigver_tuple[:2]])
218 conf.env['SWIG_VERSION'] = swigver
221 swigver_full = '.'.join(map(str, swigver_tuple[:3]))
223 conf.msg('Checking for swig version', swigver_full)
225 minver_str = '.'.join(map(str, minver))
226 conf.msg('Checking for swig version >= %s' % (minver_str,), swigver_full, color=result and 'GREEN' or 'YELLOW')
229 conf.fatal('The swig version is too old, expecting %r' % (minver,))
234 conf.find_program('swig', var='SWIG')
235 conf.env.SWIGPATH_ST = '-I%s'
236 conf.env.SWIGDEF_ST = '-D%s'