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