third_party:waf: update to upstream 2.0.4 release
[sfrench/samba-autobuild/.git] / third_party / waf / waflib / Tools / vala.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Ali Sabil, 2007
8 # Radosław Szkodziński, 2010
9
10 """
11 At this point, vala is still unstable, so do not expect
12 this tool to be too stable either (apis, etc)
13 """
14
15 import re
16 from waflib import Build, Context, Errors, Logs, Node, Options, Task, Utils
17 from waflib.TaskGen import extension, taskgen_method
18 from waflib.Configure import conf
19
20 class valac(Task.Task):
21         """
22         Compiles vala files
23         """
24         #run_str = "${VALAC} ${VALAFLAGS}" # ideally
25         #vars = ['VALAC_VERSION']
26         vars = ["VALAC", "VALAC_VERSION", "VALAFLAGS"]
27         ext_out = ['.h']
28
29         def run(self):
30                 cmd = self.env.VALAC + self.env.VALAFLAGS
31                 resources = getattr(self, 'vala_exclude', [])
32                 cmd.extend([a.abspath() for a in self.inputs if a not in resources])
33                 ret = self.exec_command(cmd, cwd=self.vala_dir_node.abspath())
34
35                 if ret:
36                         return ret
37
38                 if self.generator.dump_deps_node:
39                         self.generator.dump_deps_node.write('\n'.join(self.generator.packages))
40
41                 return ret
42
43 @taskgen_method
44 def init_vala_task(self):
45         """
46         Initializes the vala task with the relevant data (acts as a constructor)
47         """
48         self.profile = getattr(self, 'profile', 'gobject')
49
50         self.packages = packages = Utils.to_list(getattr(self, 'packages', []))
51         self.use = Utils.to_list(getattr(self, 'use', []))
52         if packages and not self.use:
53                 self.use = packages[:] # copy
54
55         if self.profile == 'gobject':
56                 if not 'GOBJECT' in self.use:
57                         self.use.append('GOBJECT')
58
59         def addflags(flags):
60                 self.env.append_value('VALAFLAGS', flags)
61
62         if self.profile:
63                 addflags('--profile=%s' % self.profile)
64
65         valatask = self.valatask
66
67         # output directory
68         if hasattr(self, 'vala_dir'):
69                 if isinstance(self.vala_dir, str):
70                         valatask.vala_dir_node = self.path.get_bld().make_node(self.vala_dir)
71                         try:
72                                 valatask.vala_dir_node.mkdir()
73                         except OSError:
74                                 raise self.bld.fatal('Cannot create the vala dir %r' % valatask.vala_dir_node)
75                 else:
76                         valatask.vala_dir_node = self.vala_dir
77         else:
78                 valatask.vala_dir_node = self.path.get_bld()
79         addflags('--directory=%s' % valatask.vala_dir_node.abspath())
80
81         if hasattr(self, 'thread'):
82                 if self.profile == 'gobject':
83                         if not 'GTHREAD' in self.use:
84                                 self.use.append('GTHREAD')
85                 else:
86                         #Vala doesn't have threading support for dova nor posix
87                         Logs.warn('Profile %s means no threading support', self.profile)
88                         self.thread = False
89
90                 if self.thread:
91                         addflags('--thread')
92
93         self.is_lib = 'cprogram' not in self.features
94         if self.is_lib:
95                 addflags('--library=%s' % self.target)
96
97                 h_node = valatask.vala_dir_node.find_or_declare('%s.h' % self.target)
98                 valatask.outputs.append(h_node)
99                 addflags('--header=%s' % h_node.name)
100
101                 valatask.outputs.append(valatask.vala_dir_node.find_or_declare('%s.vapi' % self.target))
102
103                 if getattr(self, 'gir', None):
104                         gir_node = valatask.vala_dir_node.find_or_declare('%s.gir' % self.gir)
105                         addflags('--gir=%s' % gir_node.name)
106                         valatask.outputs.append(gir_node)
107
108         self.vala_target_glib = getattr(self, 'vala_target_glib', getattr(Options.options, 'vala_target_glib', None))
109         if self.vala_target_glib:
110                 addflags('--target-glib=%s' % self.vala_target_glib)
111
112         addflags(['--define=%s' % x for x in Utils.to_list(getattr(self, 'vala_defines', []))])
113
114         packages_private = Utils.to_list(getattr(self, 'packages_private', []))
115         addflags(['--pkg=%s' % x for x in packages_private])
116
117         def _get_api_version():
118                 api_version = '1.0'
119                 if hasattr(Context.g_module, 'API_VERSION'):
120                         version = Context.g_module.API_VERSION.split(".")
121                         if version[0] == "0":
122                                 api_version = "0." + version[1]
123                         else:
124                                 api_version = version[0] + ".0"
125                 return api_version
126
127         self.includes = Utils.to_list(getattr(self, 'includes', []))
128         valatask.install_path = getattr(self, 'install_path', '')
129
130         valatask.vapi_path = getattr(self, 'vapi_path', '${DATAROOTDIR}/vala/vapi')
131         valatask.pkg_name = getattr(self, 'pkg_name', self.env.PACKAGE)
132         valatask.header_path = getattr(self, 'header_path', '${INCLUDEDIR}/%s-%s' % (valatask.pkg_name, _get_api_version()))
133         valatask.install_binding = getattr(self, 'install_binding', True)
134
135         self.vapi_dirs = vapi_dirs = Utils.to_list(getattr(self, 'vapi_dirs', []))
136         #includes =  []
137
138         if hasattr(self, 'use'):
139                 local_packages = Utils.to_list(self.use)[:] # make sure to have a copy
140                 seen = []
141                 while len(local_packages) > 0:
142                         package = local_packages.pop()
143                         if package in seen:
144                                 continue
145                         seen.append(package)
146
147                         # check if the package exists
148                         try:
149                                 package_obj = self.bld.get_tgen_by_name(package)
150                         except Errors.WafError:
151                                 continue
152
153                         # in practice the other task is already processed
154                         # but this makes it explicit
155                         package_obj.post()
156                         package_name = package_obj.target
157                         task = getattr(package_obj, 'valatask', None)
158                         if task:
159                                 for output in task.outputs:
160                                         if output.name == package_name + ".vapi":
161                                                 valatask.set_run_after(task)
162                                                 if package_name not in packages:
163                                                         packages.append(package_name)
164                                                 if output.parent not in vapi_dirs:
165                                                         vapi_dirs.append(output.parent)
166                                                 if output.parent not in self.includes:
167                                                         self.includes.append(output.parent)
168
169                         if hasattr(package_obj, 'use'):
170                                 lst = self.to_list(package_obj.use)
171                                 lst.reverse()
172                                 local_packages = [pkg for pkg in lst if pkg not in seen] + local_packages
173
174         addflags(['--pkg=%s' % p for p in packages])
175
176         for vapi_dir in vapi_dirs:
177                 if isinstance(vapi_dir, Node.Node):
178                         v_node = vapi_dir
179                 else:
180                         v_node = self.path.find_dir(vapi_dir)
181                 if not v_node:
182                         Logs.warn('Unable to locate Vala API directory: %r', vapi_dir)
183                 else:
184                         addflags('--vapidir=%s' % v_node.abspath())
185
186         self.dump_deps_node = None
187         if self.is_lib and self.packages:
188                 self.dump_deps_node = valatask.vala_dir_node.find_or_declare('%s.deps' % self.target)
189                 valatask.outputs.append(self.dump_deps_node)
190
191         if self.is_lib and valatask.install_binding:
192                 headers_list = [o for o in valatask.outputs if o.suffix() == ".h"]
193                 if headers_list:
194                         self.install_vheader = self.add_install_files(install_to=valatask.header_path, install_from=headers_list)
195
196                 vapi_list = [o for o in valatask.outputs if (o.suffix() in (".vapi", ".deps"))]
197                 if vapi_list:
198                         self.install_vapi = self.add_install_files(install_to=valatask.vapi_path, install_from=vapi_list)
199
200                 gir_list = [o for o in valatask.outputs if o.suffix() == '.gir']
201                 if gir_list:
202                         self.install_gir = self.add_install_files(
203                                 install_to=getattr(self, 'gir_path', '${DATAROOTDIR}/gir-1.0'), install_from=gir_list)
204
205         if hasattr(self, 'vala_resources'):
206                 nodes = self.to_nodes(self.vala_resources)
207                 valatask.vala_exclude = getattr(valatask, 'vala_exclude', []) + nodes
208                 valatask.inputs.extend(nodes)
209                 for x in nodes:
210                         addflags(['--gresources', x.abspath()])
211
212 @extension('.vala', '.gs')
213 def vala_file(self, node):
214         """
215         Compile a vala file and bind the task to *self.valatask*. If an existing vala task is already set, add the node
216         to its inputs. The typical example is::
217
218                 def build(bld):
219                         bld.program(
220                                 packages      = 'gtk+-2.0',
221                                 target        = 'vala-gtk-example',
222                                 use           = 'GTK GLIB',
223                                 source        = 'vala-gtk-example.vala foo.vala',
224                                 vala_defines  = ['DEBUG'] # adds --define=<xyz> values to the command-line
225
226                                 # the following arguments are for libraries
227                                 #gir          = 'hello-1.0',
228                                 #gir_path     = '/tmp',
229                                 #vapi_path = '/tmp',
230                                 #pkg_name = 'hello'
231                                 # disable installing of gir, vapi and header
232                                 #install_binding = False
233
234                                 # profile     = 'xyz' # adds --profile=<xyz> to enable profiling
235                                 # thread      = True, # adds --thread, except if profile is on or not on 'gobject'
236                                 # vala_target_glib = 'xyz' # adds --target-glib=<xyz>, can be given through the command-line option --vala-target-glib=<xyz>
237                         )
238
239
240         :param node: vala file
241         :type node: :py:class:`waflib.Node.Node`
242         """
243
244         try:
245                 valatask = self.valatask
246         except AttributeError:
247                 valatask = self.valatask = self.create_task('valac')
248                 self.init_vala_task()
249
250         valatask.inputs.append(node)
251         name = node.name[:node.name.rfind('.')] + '.c'
252         c_node = valatask.vala_dir_node.find_or_declare(name)
253         valatask.outputs.append(c_node)
254         self.source.append(c_node)
255
256 @extension('.vapi')
257 def vapi_file(self, node):
258         try:
259                 valatask = self.valatask
260         except AttributeError:
261                 valatask = self.valatask = self.create_task('valac')
262                 self.init_vala_task()
263         valatask.inputs.append(node)
264
265 @conf
266 def find_valac(self, valac_name, min_version):
267         """
268         Find the valac program, and execute it to store the version
269         number in *conf.env.VALAC_VERSION*
270
271         :param valac_name: program name
272         :type valac_name: string or list of string
273         :param min_version: minimum version acceptable
274         :type min_version: tuple of int
275         """
276         valac = self.find_program(valac_name, var='VALAC')
277         try:
278                 output = self.cmd_and_log(valac + ['--version'])
279         except Errors.WafError:
280                 valac_version = None
281         else:
282                 ver = re.search(r'\d+.\d+.\d+', output).group().split('.')
283                 valac_version = tuple([int(x) for x in ver])
284
285         self.msg('Checking for %s version >= %r' % (valac_name, min_version),
286                  valac_version, valac_version and valac_version >= min_version)
287         if valac and valac_version < min_version:
288                 self.fatal("%s version %r is too old, need >= %r" % (valac_name, valac_version, min_version))
289
290         self.env.VALAC_VERSION = valac_version
291         return valac
292
293 @conf
294 def check_vala(self, min_version=(0,8,0), branch=None):
295         """
296         Check if vala compiler from a given branch exists of at least a given
297         version.
298
299         :param min_version: minimum version acceptable (0.8.0)
300         :type min_version: tuple
301         :param branch: first part of the version number, in case a snapshot is used (0, 8)
302         :type branch: tuple of int
303         """
304         if self.env.VALA_MINVER:
305                 min_version = self.env.VALA_MINVER
306         if self.env.VALA_MINVER_BRANCH:
307                 branch = self.env.VALA_MINVER_BRANCH
308         if not branch:
309                 branch = min_version[:2]
310         try:
311                 find_valac(self, 'valac-%d.%d' % (branch[0], branch[1]), min_version)
312         except self.errors.ConfigurationError:
313                 find_valac(self, 'valac', min_version)
314
315 @conf
316 def check_vala_deps(self):
317         """
318         Load the gobject and gthread packages if they are missing.
319         """
320         if not self.env.HAVE_GOBJECT:
321                 pkg_args = {'package':      'gobject-2.0',
322                             'uselib_store': 'GOBJECT',
323                             'args':         '--cflags --libs'}
324                 if getattr(Options.options, 'vala_target_glib', None):
325                         pkg_args['atleast_version'] = Options.options.vala_target_glib
326                 self.check_cfg(**pkg_args)
327
328         if not self.env.HAVE_GTHREAD:
329                 pkg_args = {'package':      'gthread-2.0',
330                             'uselib_store': 'GTHREAD',
331                             'args':         '--cflags --libs'}
332                 if getattr(Options.options, 'vala_target_glib', None):
333                         pkg_args['atleast_version'] = Options.options.vala_target_glib
334                 self.check_cfg(**pkg_args)
335
336 def configure(self):
337         """
338         Use the following to enforce minimum vala version::
339
340                 def configure(conf):
341                         conf.env.VALA_MINVER = (0, 10, 0)
342                         conf.load('vala')
343         """
344         self.load('gnu_dirs')
345         self.check_vala_deps()
346         self.check_vala()
347         self.add_os_flags('VALAFLAGS')
348         self.env.append_unique('VALAFLAGS', ['-C'])
349
350 def options(opt):
351         """
352         Load the :py:mod:`waflib.Tools.gnu_dirs` tool and add the ``--vala-target-glib`` command-line option
353         """
354         opt.load('gnu_dirs')
355         valaopts = opt.add_option_group('Vala Compiler Options')
356         valaopts.add_option('--vala-target-glib', default=None,
357                 dest='vala_target_glib', metavar='MAJOR.MINOR',
358                 help='Target version of glib for Vala GObject code generation')
359