third_party/waf: upgrade to waf 2.0.8
[samba.git] / third_party / waf / waflib / extras / swig.py
1 #! /usr/bin/env python
2 # encoding: UTF-8
3 # Petar Forai
4 # Thomas Nagy 2008-2010 (ita)
5
6 import re
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
11
12 """
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
16 """
17
18 SWIG_EXTS = ['.swig', '.i']
19
20 re_module = re.compile('%module(?:\s*\(.*\))?\s+(.+)', re.M)
21
22 re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M)
23 re_2 = re.compile('[#%]include [<"](.*)[">]', re.M)
24
25 class swig(Task.Task):
26         color   = 'BLUE'
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']
30
31         def runnable_status(self):
32                 for t in self.run_after:
33                         if not t.hasrun:
34                                 return Task.ASK_LATER
35
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)
42                                 if not m:
43                                         raise ValueError("could not find the swig module name")
44                                 self.module = m.group(1)
45
46                         swig_c(self)
47
48                         # add the language-specific output files as nodes
49                         # call funs in the dict swig_langs
50                         for x in self.env['SWIGFLAGS']:
51                                 # obtain the language
52                                 x = x[1:]
53                                 try:
54                                         fun = swig_langs[x]
55                                 except KeyError:
56                                         pass
57                                 else:
58                                         fun(self)
59
60                 return super(swig, self).runnable_status()
61
62         def scan(self):
63                 "scan for swig dependencies, climb the .i files"
64                 lst_src = []
65
66                 seen = []
67                 missing = []
68                 to_see = [self.inputs[0]]
69
70                 while to_see:
71                         node = to_see.pop(0)
72                         if node in seen:
73                                 continue
74                         seen.append(node)
75                         lst_src.append(node)
76
77                         # read the file
78                         code = node.read()
79                         code = c_preproc.re_nl.sub('', code)
80                         code = c_preproc.re_cpp.sub(c_preproc.repl, code)
81
82                         # find .i files and project headers
83                         names = re_2.findall(code)
84                         for n in names:
85                                 for d in self.generator.includes_nodes + [node.parent]:
86                                         u = d.find_resource(n)
87                                         if u:
88                                                 to_see.append(u)
89                                                 break
90                                 else:
91                                         missing.append(n)
92                 return (lst_src, missing)
93
94 # provide additional language processing
95 swig_langs = {}
96 def swigf(fun):
97         swig_langs[fun.__name__.replace('swig_', '')] = fun
98         return fun
99 swig.swigf = swigf
100
101 def swig_c(self):
102         ext = '.swigwrap_%d.c' % self.generator.idx
103         flags = self.env['SWIGFLAGS']
104         if '-c++' in flags:
105                 ext += 'xx'
106         out_node = self.inputs[0].parent.find_or_declare(self.module + ext)
107
108         if '-c++' in flags:
109                 c_tsk = self.generator.cxx_hook(out_node)
110         else:
111                 c_tsk = self.generator.c_hook(out_node)
112
113         c_tsk.set_run_after(self)
114
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
120
121         try:
122                 self.more_tasks.append(c_tsk)
123         except AttributeError:
124                 self.more_tasks = [c_tsk]
125
126         try:
127                 ltask = self.generator.link_task
128         except AttributeError:
129                 pass
130         else:
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])
136
137         self.outputs.append(out_node)
138
139         if not '-o' in self.env['SWIGFLAGS']:
140                 self.env.append_value('SWIGFLAGS', ['-o', self.outputs[0].abspath()])
141
142 @swigf
143 def swig_python(tsk):
144         node = tsk.inputs[0].parent
145         if tsk.outdir:
146                 node = tsk.outdir
147         tsk.set_outputs(node.find_or_declare(tsk.module+'.py'))
148
149 @swigf
150 def swig_ocaml(tsk):
151         node = tsk.inputs[0].parent
152         if tsk.outdir:
153                 node = tsk.outdir
154         tsk.set_outputs(node.find_or_declare(tsk.module+'.ml'))
155         tsk.set_outputs(node.find_or_declare(tsk.module+'.mli'))
156
157 @extension(*SWIG_EXTS)
158 def i_file(self, node):
159         # the task instance
160         tsk = self.create_task('swig')
161         tsk.set_inputs(node)
162         tsk.module = getattr(self, 'swig_module', None)
163
164         flags = self.to_list(getattr(self, 'swig_flags', []))
165         tsk.env.append_value('SWIGFLAGS', flags)
166
167         tsk.outdir = None
168         if '-outdir' in flags:
169                 outdir = flags[flags.index('-outdir')+1]
170                 outdir = tsk.generator.bld.bldnode.make_node(outdir)
171                 outdir.mkdir()
172                 tsk.outdir = outdir
173
174 @feature('c', 'cxx', 'd', 'fc', 'asm')
175 @after_method('apply_link', 'process_source')
176 def enforce_swig_before_link(self):
177         try:
178                 link_task = self.link_task
179         except AttributeError:
180                 pass
181         else:
182                 for x in self.tasks:
183                         if x.__class__.__name__ == 'swig':
184                                 link_task.run_after.add(x)
185
186 @conf
187 def check_swig_version(conf, minver=None):
188         """
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.
191
192         If successful, SWIG_VERSION is defined as 'MAJOR.MINOR'
193         (eg. '1.3') of the actual swig version found.
194
195         :param minver: minimum version
196         :type minver: tuple of int
197         :return: swig version
198         :rtype: tuple of int
199         """
200         assert minver is None or isinstance(minver, tuple)
201         swigbin = conf.env['SWIG']
202         if not swigbin:
203                 conf.fatal('could not find the swig executable')
204
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('.')])
211
212         # Compare swig version with the minimum required
213         result = (minver is None) or (swigver_tuple >= minver)
214
215         if result:
216                 # Define useful environment variables
217                 swigver = '.'.join([str(x) for x in swigver_tuple[:2]])
218                 conf.env['SWIG_VERSION'] = swigver
219
220         # Feedback
221         swigver_full = '.'.join(map(str, swigver_tuple[:3]))
222         if minver is None:
223                 conf.msg('Checking for swig version', swigver_full)
224         else:
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')
227
228         if not result:
229                 conf.fatal('The swig version is too old, expecting %r' % (minver,))
230
231         return swigver_tuple
232
233 def configure(conf):
234         conf.find_program('swig', var='SWIG')
235         conf.env.SWIGPATH_ST = '-I%s'
236         conf.env.SWIGDEF_ST = '-D%s'
237