summaryrefslogtreecommitdiffstats
path: root/Tools/vala.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/vala.py')
-rw-r--r--Tools/vala.py355
1 files changed, 355 insertions, 0 deletions
diff --git a/Tools/vala.py b/Tools/vala.py
new file mode 100644
index 0000000..822ec50
--- /dev/null
+++ b/Tools/vala.py
@@ -0,0 +1,355 @@
+#!/usr/bin/env python
+# encoding: utf-8
+# Ali Sabil, 2007
+# Radosław Szkodziński, 2010
+
+"""
+At this point, vala is still unstable, so do not expect
+this tool to be too stable either (apis, etc)
+"""
+
+import re
+from waflib import Build, Context, Errors, Logs, Node, Options, Task, Utils
+from waflib.TaskGen import extension, taskgen_method
+from waflib.Configure import conf
+
+class valac(Task.Task):
+ """
+ Compiles vala files
+ """
+ #run_str = "${VALAC} ${VALAFLAGS}" # ideally
+ #vars = ['VALAC_VERSION']
+ vars = ["VALAC", "VALAC_VERSION", "VALAFLAGS"]
+ ext_out = ['.h']
+
+ def run(self):
+ cmd = self.env.VALAC + self.env.VALAFLAGS
+ resources = getattr(self, 'vala_exclude', [])
+ cmd.extend([a.abspath() for a in self.inputs if a not in resources])
+ ret = self.exec_command(cmd, cwd=self.vala_dir_node.abspath())
+
+ if ret:
+ return ret
+
+ if self.generator.dump_deps_node:
+ self.generator.dump_deps_node.write('\n'.join(self.generator.packages))
+
+ return ret
+
+@taskgen_method
+def init_vala_task(self):
+ """
+ Initializes the vala task with the relevant data (acts as a constructor)
+ """
+ self.profile = getattr(self, 'profile', 'gobject')
+
+ self.packages = packages = Utils.to_list(getattr(self, 'packages', []))
+ self.use = Utils.to_list(getattr(self, 'use', []))
+ if packages and not self.use:
+ self.use = packages[:] # copy
+
+ if self.profile == 'gobject':
+ if not 'GOBJECT' in self.use:
+ self.use.append('GOBJECT')
+
+ def addflags(flags):
+ self.env.append_value('VALAFLAGS', flags)
+
+ if self.profile:
+ addflags('--profile=%s' % self.profile)
+
+ valatask = self.valatask
+
+ # output directory
+ if hasattr(self, 'vala_dir'):
+ if isinstance(self.vala_dir, str):
+ valatask.vala_dir_node = self.path.get_bld().make_node(self.vala_dir)
+ try:
+ valatask.vala_dir_node.mkdir()
+ except OSError:
+ raise self.bld.fatal('Cannot create the vala dir %r' % valatask.vala_dir_node)
+ else:
+ valatask.vala_dir_node = self.vala_dir
+ else:
+ valatask.vala_dir_node = self.path.get_bld()
+ addflags('--directory=%s' % valatask.vala_dir_node.abspath())
+
+ if hasattr(self, 'thread'):
+ if self.profile == 'gobject':
+ if not 'GTHREAD' in self.use:
+ self.use.append('GTHREAD')
+ else:
+ #Vala doesn't have threading support for dova nor posix
+ Logs.warn('Profile %s means no threading support', self.profile)
+ self.thread = False
+
+ if self.thread:
+ addflags('--thread')
+
+ self.is_lib = 'cprogram' not in self.features
+ if self.is_lib:
+ addflags('--library=%s' % self.target)
+
+ h_node = valatask.vala_dir_node.find_or_declare('%s.h' % self.target)
+ valatask.outputs.append(h_node)
+ addflags('--header=%s' % h_node.name)
+
+ valatask.outputs.append(valatask.vala_dir_node.find_or_declare('%s.vapi' % self.target))
+
+ if getattr(self, 'gir', None):
+ gir_node = valatask.vala_dir_node.find_or_declare('%s.gir' % self.gir)
+ addflags('--gir=%s' % gir_node.name)
+ valatask.outputs.append(gir_node)
+
+ self.vala_target_glib = getattr(self, 'vala_target_glib', getattr(Options.options, 'vala_target_glib', None))
+ if self.vala_target_glib:
+ addflags('--target-glib=%s' % self.vala_target_glib)
+
+ addflags(['--define=%s' % x for x in Utils.to_list(getattr(self, 'vala_defines', []))])
+
+ packages_private = Utils.to_list(getattr(self, 'packages_private', []))
+ addflags(['--pkg=%s' % x for x in packages_private])
+
+ def _get_api_version():
+ api_version = '1.0'
+ if hasattr(Context.g_module, 'API_VERSION'):
+ version = Context.g_module.API_VERSION.split(".")
+ if version[0] == "0":
+ api_version = "0." + version[1]
+ else:
+ api_version = version[0] + ".0"
+ return api_version
+
+ self.includes = Utils.to_list(getattr(self, 'includes', []))
+ valatask.install_path = getattr(self, 'install_path', '')
+
+ valatask.vapi_path = getattr(self, 'vapi_path', '${DATAROOTDIR}/vala/vapi')
+ valatask.pkg_name = getattr(self, 'pkg_name', self.env.PACKAGE)
+ valatask.header_path = getattr(self, 'header_path', '${INCLUDEDIR}/%s-%s' % (valatask.pkg_name, _get_api_version()))
+ valatask.install_binding = getattr(self, 'install_binding', True)
+
+ self.vapi_dirs = vapi_dirs = Utils.to_list(getattr(self, 'vapi_dirs', []))
+ #includes = []
+
+ if hasattr(self, 'use'):
+ local_packages = Utils.to_list(self.use)[:] # make sure to have a copy
+ seen = []
+ while len(local_packages) > 0:
+ package = local_packages.pop()
+ if package in seen:
+ continue
+ seen.append(package)
+
+ # check if the package exists
+ try:
+ package_obj = self.bld.get_tgen_by_name(package)
+ except Errors.WafError:
+ continue
+
+ # in practice the other task is already processed
+ # but this makes it explicit
+ package_obj.post()
+ package_name = package_obj.target
+ task = getattr(package_obj, 'valatask', None)
+ if task:
+ for output in task.outputs:
+ if output.name == package_name + ".vapi":
+ valatask.set_run_after(task)
+ if package_name not in packages:
+ packages.append(package_name)
+ if output.parent not in vapi_dirs:
+ vapi_dirs.append(output.parent)
+ if output.parent not in self.includes:
+ self.includes.append(output.parent)
+
+ if hasattr(package_obj, 'use'):
+ lst = self.to_list(package_obj.use)
+ lst.reverse()
+ local_packages = [pkg for pkg in lst if pkg not in seen] + local_packages
+
+ addflags(['--pkg=%s' % p for p in packages])
+
+ for vapi_dir in vapi_dirs:
+ if isinstance(vapi_dir, Node.Node):
+ v_node = vapi_dir
+ else:
+ v_node = self.path.find_dir(vapi_dir)
+ if not v_node:
+ Logs.warn('Unable to locate Vala API directory: %r', vapi_dir)
+ else:
+ addflags('--vapidir=%s' % v_node.abspath())
+
+ self.dump_deps_node = None
+ if self.is_lib and self.packages:
+ self.dump_deps_node = valatask.vala_dir_node.find_or_declare('%s.deps' % self.target)
+ valatask.outputs.append(self.dump_deps_node)
+
+ if self.is_lib and valatask.install_binding:
+ headers_list = [o for o in valatask.outputs if o.suffix() == ".h"]
+ if headers_list:
+ self.install_vheader = self.add_install_files(install_to=valatask.header_path, install_from=headers_list)
+
+ vapi_list = [o for o in valatask.outputs if (o.suffix() in (".vapi", ".deps"))]
+ if vapi_list:
+ self.install_vapi = self.add_install_files(install_to=valatask.vapi_path, install_from=vapi_list)
+
+ gir_list = [o for o in valatask.outputs if o.suffix() == '.gir']
+ if gir_list:
+ self.install_gir = self.add_install_files(
+ install_to=getattr(self, 'gir_path', '${DATAROOTDIR}/gir-1.0'), install_from=gir_list)
+
+ if hasattr(self, 'vala_resources'):
+ nodes = self.to_nodes(self.vala_resources)
+ valatask.vala_exclude = getattr(valatask, 'vala_exclude', []) + nodes
+ valatask.inputs.extend(nodes)
+ for x in nodes:
+ addflags(['--gresources', x.abspath()])
+
+@extension('.vala', '.gs')
+def vala_file(self, node):
+ """
+ Compile a vala file and bind the task to *self.valatask*. If an existing vala task is already set, add the node
+ to its inputs. The typical example is::
+
+ def build(bld):
+ bld.program(
+ packages = 'gtk+-2.0',
+ target = 'vala-gtk-example',
+ use = 'GTK GLIB',
+ source = 'vala-gtk-example.vala foo.vala',
+ vala_defines = ['DEBUG'] # adds --define=<xyz> values to the command-line
+
+ # the following arguments are for libraries
+ #gir = 'hello-1.0',
+ #gir_path = '/tmp',
+ #vapi_path = '/tmp',
+ #pkg_name = 'hello'
+ # disable installing of gir, vapi and header
+ #install_binding = False
+
+ # profile = 'xyz' # adds --profile=<xyz> to enable profiling
+ # thread = True, # adds --thread, except if profile is on or not on 'gobject'
+ # vala_target_glib = 'xyz' # adds --target-glib=<xyz>, can be given through the command-line option --vala-target-glib=<xyz>
+ )
+
+
+ :param node: vala file
+ :type node: :py:class:`waflib.Node.Node`
+ """
+
+ try:
+ valatask = self.valatask
+ except AttributeError:
+ valatask = self.valatask = self.create_task('valac')
+ self.init_vala_task()
+
+ valatask.inputs.append(node)
+ name = node.name[:node.name.rfind('.')] + '.c'
+ c_node = valatask.vala_dir_node.find_or_declare(name)
+ valatask.outputs.append(c_node)
+ self.source.append(c_node)
+
+@extension('.vapi')
+def vapi_file(self, node):
+ try:
+ valatask = self.valatask
+ except AttributeError:
+ valatask = self.valatask = self.create_task('valac')
+ self.init_vala_task()
+ valatask.inputs.append(node)
+
+@conf
+def find_valac(self, valac_name, min_version):
+ """
+ Find the valac program, and execute it to store the version
+ number in *conf.env.VALAC_VERSION*
+
+ :param valac_name: program name
+ :type valac_name: string or list of string
+ :param min_version: minimum version acceptable
+ :type min_version: tuple of int
+ """
+ valac = self.find_program(valac_name, var='VALAC')
+ try:
+ output = self.cmd_and_log(valac + ['--version'])
+ except Errors.WafError:
+ valac_version = None
+ else:
+ ver = re.search(r'\d+.\d+.\d+', output).group().split('.')
+ valac_version = tuple([int(x) for x in ver])
+
+ self.msg('Checking for %s version >= %r' % (valac_name, min_version),
+ valac_version, valac_version and valac_version >= min_version)
+ if valac and valac_version < min_version:
+ self.fatal("%s version %r is too old, need >= %r" % (valac_name, valac_version, min_version))
+
+ self.env.VALAC_VERSION = valac_version
+ return valac
+
+@conf
+def check_vala(self, min_version=(0,8,0), branch=None):
+ """
+ Check if vala compiler from a given branch exists of at least a given
+ version.
+
+ :param min_version: minimum version acceptable (0.8.0)
+ :type min_version: tuple
+ :param branch: first part of the version number, in case a snapshot is used (0, 8)
+ :type branch: tuple of int
+ """
+ if self.env.VALA_MINVER:
+ min_version = self.env.VALA_MINVER
+ if self.env.VALA_MINVER_BRANCH:
+ branch = self.env.VALA_MINVER_BRANCH
+ if not branch:
+ branch = min_version[:2]
+ try:
+ find_valac(self, 'valac-%d.%d' % (branch[0], branch[1]), min_version)
+ except self.errors.ConfigurationError:
+ find_valac(self, 'valac', min_version)
+
+@conf
+def check_vala_deps(self):
+ """
+ Load the gobject and gthread packages if they are missing.
+ """
+ if not self.env.HAVE_GOBJECT:
+ pkg_args = {'package': 'gobject-2.0',
+ 'uselib_store': 'GOBJECT',
+ 'args': '--cflags --libs'}
+ if getattr(Options.options, 'vala_target_glib', None):
+ pkg_args['atleast_version'] = Options.options.vala_target_glib
+ self.check_cfg(**pkg_args)
+
+ if not self.env.HAVE_GTHREAD:
+ pkg_args = {'package': 'gthread-2.0',
+ 'uselib_store': 'GTHREAD',
+ 'args': '--cflags --libs'}
+ if getattr(Options.options, 'vala_target_glib', None):
+ pkg_args['atleast_version'] = Options.options.vala_target_glib
+ self.check_cfg(**pkg_args)
+
+def configure(self):
+ """
+ Use the following to enforce minimum vala version::
+
+ def configure(conf):
+ conf.env.VALA_MINVER = (0, 10, 0)
+ conf.load('vala')
+ """
+ self.load('gnu_dirs')
+ self.check_vala_deps()
+ self.check_vala()
+ self.add_os_flags('VALAFLAGS')
+ self.env.append_unique('VALAFLAGS', ['-C'])
+
+def options(opt):
+ """
+ Load the :py:mod:`waflib.Tools.gnu_dirs` tool and add the ``--vala-target-glib`` command-line option
+ """
+ opt.load('gnu_dirs')
+ valaopts = opt.add_option_group('Vala Compiler Options')
+ valaopts.add_option('--vala-target-glib', default=None,
+ dest='vala_target_glib', metavar='MAJOR.MINOR',
+ help='Target version of glib for Vala GObject code generation')
+