dom/bindings/mozwebidlcodegen/__init__.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/bindings/mozwebidlcodegen/__init__.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,568 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +# This module contains code for managing WebIDL files and bindings for
     1.9 +# the build system.
    1.10 +
    1.11 +from __future__ import unicode_literals
    1.12 +
    1.13 +import errno
    1.14 +import hashlib
    1.15 +import json
    1.16 +import logging
    1.17 +import os
    1.18 +
    1.19 +from copy import deepcopy
    1.20 +
    1.21 +from mach.mixin.logging import LoggingMixin
    1.22 +
    1.23 +from mozbuild.base import MozbuildObject
    1.24 +from mozbuild.makeutil import Makefile
    1.25 +from mozbuild.pythonutil import iter_modules_in_path
    1.26 +from mozbuild.util import FileAvoidWrite
    1.27 +
    1.28 +import mozpack.path as mozpath
    1.29 +
    1.30 +# There are various imports in this file in functions to avoid adding
    1.31 +# dependencies to config.status. See bug 949875.
    1.32 +
    1.33 +
    1.34 +class BuildResult(object):
    1.35 +    """Represents the result of processing WebIDL files.
    1.36 +
    1.37 +    This holds a summary of output file generation during code generation.
    1.38 +    """
    1.39 +
    1.40 +    def __init__(self):
    1.41 +        # The .webidl files that had their outputs regenerated.
    1.42 +        self.inputs = set()
    1.43 +
    1.44 +        # The output files that were created.
    1.45 +        self.created = set()
    1.46 +
    1.47 +        # The output files that changed.
    1.48 +        self.updated = set()
    1.49 +
    1.50 +        # The output files that didn't change.
    1.51 +        self.unchanged = set()
    1.52 +
    1.53 +
    1.54 +class WebIDLCodegenManagerState(dict):
    1.55 +    """Holds state for the WebIDL code generation manager.
    1.56 +
    1.57 +    State is currently just an extended dict. The internal implementation of
    1.58 +    state should be considered a black box to everyone except
    1.59 +    WebIDLCodegenManager. But we'll still document it.
    1.60 +
    1.61 +    Fields:
    1.62 +
    1.63 +    version
    1.64 +       The integer version of the format. This is to detect incompatible
    1.65 +       changes between state. It should be bumped whenever the format
    1.66 +       changes or semantics change.
    1.67 +
    1.68 +    webidls
    1.69 +       A dictionary holding information about every known WebIDL input.
    1.70 +       Keys are the basenames of input WebIDL files. Values are dicts of
    1.71 +       metadata. Keys in those dicts are:
    1.72 +
    1.73 +       * filename - The full path to the input filename.
    1.74 +       * inputs - A set of full paths to other webidl files this webidl
    1.75 +         depends on.
    1.76 +       * outputs - Set of full output paths that are created/derived from
    1.77 +         this file.
    1.78 +       * sha1 - The hexidecimal SHA-1 of the input filename from the last
    1.79 +         processing time.
    1.80 +
    1.81 +    global_inputs
    1.82 +       A dictionary defining files that influence all processing. Keys
    1.83 +       are full filenames. Values are hexidecimal SHA-1 from the last
    1.84 +       processing time.
    1.85 +    """
    1.86 +
    1.87 +    VERSION = 1
    1.88 +
    1.89 +    def __init__(self, fh=None):
    1.90 +        self['version'] = self.VERSION
    1.91 +        self['webidls'] = {}
    1.92 +        self['global_depends'] = {}
    1.93 +
    1.94 +        if not fh:
    1.95 +            return
    1.96 +
    1.97 +        state = json.load(fh)
    1.98 +        if state['version'] != self.VERSION:
    1.99 +            raise Exception('Unknown state version: %s' % state['version'])
   1.100 +
   1.101 +        self['version'] = state['version']
   1.102 +        self['global_depends'] = state['global_depends']
   1.103 +
   1.104 +        for k, v in state['webidls'].items():
   1.105 +            self['webidls'][k] = v
   1.106 +
   1.107 +            # Sets are converted to lists for serialization because JSON
   1.108 +            # doesn't support sets.
   1.109 +            self['webidls'][k]['inputs'] = set(v['inputs'])
   1.110 +            self['webidls'][k]['outputs'] = set(v['outputs'])
   1.111 +
   1.112 +    def dump(self, fh):
   1.113 +        """Dump serialized state to a file handle."""
   1.114 +        normalized = deepcopy(self)
   1.115 +
   1.116 +        for k, v in self['webidls'].items():
   1.117 +            # Convert sets to lists because JSON doesn't support sets.
   1.118 +            normalized['webidls'][k]['outputs'] = sorted(v['outputs'])
   1.119 +            normalized['webidls'][k]['inputs'] = sorted(v['inputs'])
   1.120 +
   1.121 +        json.dump(normalized, fh, sort_keys=True)
   1.122 +
   1.123 +
   1.124 +class WebIDLCodegenManager(LoggingMixin):
   1.125 +    """Manages all code generation around WebIDL.
   1.126 +
   1.127 +    To facilitate testing, this object is meant to be generic and reusable.
   1.128 +    Paths, etc should be parameters and not hardcoded.
   1.129 +    """
   1.130 +
   1.131 +    # Global parser derived declaration files.
   1.132 +    GLOBAL_DECLARE_FILES = {
   1.133 +        'GeneratedAtomList.h',
   1.134 +        'PrototypeList.h',
   1.135 +        'RegisterBindings.h',
   1.136 +        'UnionConversions.h',
   1.137 +        'UnionTypes.h',
   1.138 +    }
   1.139 +
   1.140 +    # Global parser derived definition files.
   1.141 +    GLOBAL_DEFINE_FILES = {
   1.142 +        'RegisterBindings.cpp',
   1.143 +        'UnionTypes.cpp',
   1.144 +        'PrototypeList.cpp',
   1.145 +    }
   1.146 +
   1.147 +    def __init__(self, config_path, inputs, exported_header_dir,
   1.148 +        codegen_dir, state_path, cache_dir=None, make_deps_path=None,
   1.149 +        make_deps_target=None):
   1.150 +        """Create an instance that manages WebIDLs in the build system.
   1.151 +
   1.152 +        config_path refers to a WebIDL config file (e.g. Bindings.conf).
   1.153 +        inputs is a 4-tuple describing the input .webidl files and how to
   1.154 +        process them. Members are:
   1.155 +            (set(.webidl files), set(basenames of exported files),
   1.156 +                set(basenames of generated events files),
   1.157 +                set(example interface names))
   1.158 +
   1.159 +        exported_header_dir and codegen_dir are directories where generated
   1.160 +        files will be written to.
   1.161 +        state_path is the path to a file that will receive JSON state from our
   1.162 +        actions.
   1.163 +        make_deps_path is the path to a make dependency file that we can
   1.164 +        optionally write.
   1.165 +        make_deps_target is the target that receives the make dependencies. It
   1.166 +        must be defined if using make_deps_path.
   1.167 +        """
   1.168 +        self.populate_logger()
   1.169 +
   1.170 +        input_paths, exported_stems, generated_events_stems, example_interfaces = inputs
   1.171 +
   1.172 +        self._config_path = config_path
   1.173 +        self._input_paths = set(input_paths)
   1.174 +        self._exported_stems = set(exported_stems)
   1.175 +        self._generated_events_stems = set(generated_events_stems)
   1.176 +        self._example_interfaces = set(example_interfaces)
   1.177 +        self._exported_header_dir = exported_header_dir
   1.178 +        self._codegen_dir = codegen_dir
   1.179 +        self._state_path = state_path
   1.180 +        self._cache_dir = cache_dir
   1.181 +        self._make_deps_path = make_deps_path
   1.182 +        self._make_deps_target = make_deps_target
   1.183 +
   1.184 +        if (make_deps_path and not make_deps_target) or (not make_deps_path and
   1.185 +            make_deps_target):
   1.186 +            raise Exception('Must define both make_deps_path and make_deps_target '
   1.187 +                'if one is defined.')
   1.188 +
   1.189 +        self._parser_results = None
   1.190 +        self._config = None
   1.191 +        self._state = WebIDLCodegenManagerState()
   1.192 +
   1.193 +        if os.path.exists(state_path):
   1.194 +            with open(state_path, 'rb') as fh:
   1.195 +                try:
   1.196 +                    self._state = WebIDLCodegenManagerState(fh=fh)
   1.197 +                except Exception as e:
   1.198 +                    self.log(logging.WARN, 'webidl_bad_state', {'msg': str(e)},
   1.199 +                        'Bad WebIDL state: {msg}')
   1.200 +
   1.201 +    @property
   1.202 +    def config(self):
   1.203 +        if not self._config:
   1.204 +            self._parse_webidl()
   1.205 +
   1.206 +        return self._config
   1.207 +
   1.208 +    def generate_build_files(self):
   1.209 +        """Generate files required for the build.
   1.210 +
   1.211 +        This function is in charge of generating all the .h/.cpp files derived
   1.212 +        from input .webidl files. Please note that there are build actions
   1.213 +        required to produce .webidl files and these build actions are
   1.214 +        explicitly not captured here: this function assumes all .webidl files
   1.215 +        are present and up to date.
   1.216 +
   1.217 +        This routine is called as part of the build to ensure files that need
   1.218 +        to exist are present and up to date. This routine may not be called if
   1.219 +        the build dependencies (generated as a result of calling this the first
   1.220 +        time) say everything is up to date.
   1.221 +
   1.222 +        Because reprocessing outputs for every .webidl on every invocation
   1.223 +        is expensive, we only regenerate the minimal set of files on every
   1.224 +        invocation. The rules for deciding what needs done are roughly as
   1.225 +        follows:
   1.226 +
   1.227 +        1. If any .webidl changes, reparse all .webidl files and regenerate
   1.228 +           the global derived files. Only regenerate output files (.h/.cpp)
   1.229 +           impacted by the modified .webidl files.
   1.230 +        2. If an non-.webidl dependency (Python files, config file) changes,
   1.231 +           assume everything is out of date and regenerate the world. This
   1.232 +           is because changes in those could globally impact every output
   1.233 +           file.
   1.234 +        3. If an output file is missing, ensure it is present by performing
   1.235 +           necessary regeneration.
   1.236 +        """
   1.237 +        # Despite #1 above, we assume the build system is smart enough to not
   1.238 +        # invoke us if nothing has changed. Therefore, any invocation means
   1.239 +        # something has changed. And, if anything has changed, we need to
   1.240 +        # parse the WebIDL.
   1.241 +        self._parse_webidl()
   1.242 +
   1.243 +        result = BuildResult()
   1.244 +
   1.245 +        # If we parse, we always update globals - they are cheap and it is
   1.246 +        # easier that way.
   1.247 +        created, updated, unchanged = self._write_global_derived()
   1.248 +        result.created |= created
   1.249 +        result.updated |= updated
   1.250 +        result.unchanged |= unchanged
   1.251 +
   1.252 +        # If any of the extra dependencies changed, regenerate the world.
   1.253 +        global_changed, global_hashes = self._global_dependencies_changed()
   1.254 +        if global_changed:
   1.255 +            # Make a copy because we may modify.
   1.256 +            changed_inputs = set(self._input_paths)
   1.257 +        else:
   1.258 +            changed_inputs = self._compute_changed_inputs()
   1.259 +
   1.260 +        self._state['global_depends'] = global_hashes
   1.261 +
   1.262 +        # Generate bindings from .webidl files.
   1.263 +        for filename in sorted(changed_inputs):
   1.264 +            basename = mozpath.basename(filename)
   1.265 +            result.inputs.add(filename)
   1.266 +            written, deps = self._generate_build_files_for_webidl(filename)
   1.267 +            result.created |= written[0]
   1.268 +            result.updated |= written[1]
   1.269 +            result.unchanged |= written[2]
   1.270 +
   1.271 +            self._state['webidls'][basename] = dict(
   1.272 +                filename=filename,
   1.273 +                outputs=written[0] | written[1] | written[2],
   1.274 +                inputs=set(deps),
   1.275 +                sha1=self._input_hashes[filename],
   1.276 +            )
   1.277 +
   1.278 +        # Process some special interfaces required for testing.
   1.279 +        for interface in self._example_interfaces:
   1.280 +            written = self.generate_example_files(interface)
   1.281 +            result.created |= written[0]
   1.282 +            result.updated |= written[1]
   1.283 +            result.unchanged |= written[2]
   1.284 +
   1.285 +        # Generate a make dependency file.
   1.286 +        if self._make_deps_path:
   1.287 +            mk = Makefile()
   1.288 +            codegen_rule = mk.create_rule([self._make_deps_target])
   1.289 +            codegen_rule.add_dependencies(global_hashes.keys())
   1.290 +            codegen_rule.add_dependencies(self._input_paths)
   1.291 +
   1.292 +            with FileAvoidWrite(self._make_deps_path) as fh:
   1.293 +                mk.dump(fh)
   1.294 +
   1.295 +        self._save_state()
   1.296 +
   1.297 +        return result
   1.298 +
   1.299 +    def generate_example_files(self, interface):
   1.300 +        """Generates example files for a given interface."""
   1.301 +        from Codegen import CGExampleRoot
   1.302 +
   1.303 +        root = CGExampleRoot(self.config, interface)
   1.304 +
   1.305 +        return self._maybe_write_codegen(root, *self._example_paths(interface))
   1.306 +
   1.307 +    def _parse_webidl(self):
   1.308 +        import WebIDL
   1.309 +        from Configuration import Configuration
   1.310 +
   1.311 +        self.log(logging.INFO, 'webidl_parse',
   1.312 +            {'count': len(self._input_paths)},
   1.313 +            'Parsing {count} WebIDL files.')
   1.314 +
   1.315 +        hashes = {}
   1.316 +        parser = WebIDL.Parser(self._cache_dir)
   1.317 +
   1.318 +        for path in sorted(self._input_paths):
   1.319 +            with open(path, 'rb') as fh:
   1.320 +                data = fh.read()
   1.321 +                hashes[path] = hashlib.sha1(data).hexdigest()
   1.322 +                parser.parse(data, path)
   1.323 +
   1.324 +        self._parser_results = parser.finish()
   1.325 +        self._config = Configuration(self._config_path, self._parser_results)
   1.326 +        self._input_hashes = hashes
   1.327 +
   1.328 +    def _write_global_derived(self):
   1.329 +        from Codegen import GlobalGenRoots
   1.330 +
   1.331 +        things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES]
   1.332 +        things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES)
   1.333 +
   1.334 +        result = (set(), set(), set())
   1.335 +
   1.336 +        for what, filename in things:
   1.337 +            stem = mozpath.splitext(filename)[0]
   1.338 +            root = getattr(GlobalGenRoots, stem)(self._config)
   1.339 +
   1.340 +            if what == 'declare':
   1.341 +                code = root.declare()
   1.342 +                output_root = self._exported_header_dir
   1.343 +            elif what == 'define':
   1.344 +                code = root.define()
   1.345 +                output_root = self._codegen_dir
   1.346 +            else:
   1.347 +                raise Exception('Unknown global gen type: %s' % what)
   1.348 +
   1.349 +            output_path = mozpath.join(output_root, filename)
   1.350 +            self._maybe_write_file(output_path, code, result)
   1.351 +
   1.352 +        return result
   1.353 +
   1.354 +    def _compute_changed_inputs(self):
   1.355 +        """Compute the set of input files that need to be regenerated."""
   1.356 +        changed_inputs = set()
   1.357 +        expected_outputs = self.expected_build_output_files()
   1.358 +
   1.359 +        # Look for missing output files.
   1.360 +        if any(not os.path.exists(f) for f in expected_outputs):
   1.361 +            # FUTURE Bug 940469 Only regenerate minimum set.
   1.362 +            changed_inputs |= self._input_paths
   1.363 +
   1.364 +        # That's it for examining output files. We /could/ examine SHA-1's of
   1.365 +        # output files from a previous run to detect modifications. But that's
   1.366 +        # a lot of extra work and most build systems don't do that anyway.
   1.367 +
   1.368 +        # Now we move on to the input files.
   1.369 +        old_hashes = {v['filename']: v['sha1']
   1.370 +            for v in self._state['webidls'].values()}
   1.371 +
   1.372 +        old_filenames = set(old_hashes.keys())
   1.373 +        new_filenames = self._input_paths
   1.374 +
   1.375 +        # If an old file has disappeared or a new file has arrived, mark
   1.376 +        # it.
   1.377 +        changed_inputs |= old_filenames ^ new_filenames
   1.378 +
   1.379 +        # For the files in common between runs, compare content. If the file
   1.380 +        # has changed, mark it. We don't need to perform mtime comparisons
   1.381 +        # because content is a stronger validator.
   1.382 +        for filename in old_filenames & new_filenames:
   1.383 +            if old_hashes[filename] != self._input_hashes[filename]:
   1.384 +                changed_inputs.add(filename)
   1.385 +
   1.386 +        # We've now populated the base set of inputs that have changed.
   1.387 +
   1.388 +        # Inherit dependencies from previous run. The full set of dependencies
   1.389 +        # is associated with each record, so we don't need to perform any fancy
   1.390 +        # graph traversal.
   1.391 +        for v in self._state['webidls'].values():
   1.392 +            if any(dep for dep in v['inputs'] if dep in changed_inputs):
   1.393 +                changed_inputs.add(v['filename'])
   1.394 +
   1.395 +        # Only use paths that are known to our current state.
   1.396 +        # This filters out files that were deleted or changed type (e.g. from
   1.397 +        # static to preprocessed).
   1.398 +        return changed_inputs & self._input_paths
   1.399 +
   1.400 +    def _binding_info(self, p):
   1.401 +        """Compute binding metadata for an input path.
   1.402 +
   1.403 +        Returns a tuple of:
   1.404 +
   1.405 +          (stem, binding_stem, is_event, output_files)
   1.406 +
   1.407 +        output_files is itself a tuple. The first two items are the binding
   1.408 +        header and C++ paths, respectively. The 2nd pair are the event header
   1.409 +        and C++ paths or None if this isn't an event binding.
   1.410 +        """
   1.411 +        basename = mozpath.basename(p)
   1.412 +        stem = mozpath.splitext(basename)[0]
   1.413 +        binding_stem = '%sBinding' % stem
   1.414 +
   1.415 +        if stem in self._exported_stems:
   1.416 +            header_dir = self._exported_header_dir
   1.417 +        else:
   1.418 +            header_dir = self._codegen_dir
   1.419 +
   1.420 +        is_event = stem in self._generated_events_stems
   1.421 +
   1.422 +        files = (
   1.423 +            mozpath.join(header_dir, '%s.h' % binding_stem),
   1.424 +            mozpath.join(self._codegen_dir, '%s.cpp' % binding_stem),
   1.425 +            mozpath.join(header_dir, '%s.h' % stem) if is_event else None,
   1.426 +            mozpath.join(self._codegen_dir, '%s.cpp' % stem) if is_event else None,
   1.427 +        )
   1.428 +
   1.429 +        return stem, binding_stem, is_event, header_dir, files
   1.430 +
   1.431 +    def _example_paths(self, interface):
   1.432 +        return (
   1.433 +            mozpath.join(self._codegen_dir, '%s-example.h' % interface),
   1.434 +            mozpath.join(self._codegen_dir, '%s-example.cpp' % interface))
   1.435 +
   1.436 +    def expected_build_output_files(self):
   1.437 +        """Obtain the set of files generate_build_files() should write."""
   1.438 +        paths = set()
   1.439 +
   1.440 +        # Account for global generation.
   1.441 +        for p in self.GLOBAL_DECLARE_FILES:
   1.442 +            paths.add(mozpath.join(self._exported_header_dir, p))
   1.443 +        for p in self.GLOBAL_DEFINE_FILES:
   1.444 +            paths.add(mozpath.join(self._codegen_dir, p))
   1.445 +
   1.446 +        for p in self._input_paths:
   1.447 +            stem, binding_stem, is_event, header_dir, files = self._binding_info(p)
   1.448 +            paths |= {f for f in files if f}
   1.449 +
   1.450 +        for interface in self._example_interfaces:
   1.451 +            for p in self._example_paths(interface):
   1.452 +                paths.add(p)
   1.453 +
   1.454 +        return paths
   1.455 +
   1.456 +    def _generate_build_files_for_webidl(self, filename):
   1.457 +        from Codegen import (
   1.458 +            CGBindingRoot,
   1.459 +            CGEventRoot,
   1.460 +        )
   1.461 +
   1.462 +        self.log(logging.INFO, 'webidl_generate_build_for_input',
   1.463 +            {'filename': filename},
   1.464 +            'Generating WebIDL files derived from {filename}')
   1.465 +
   1.466 +        stem, binding_stem, is_event, header_dir, files = self._binding_info(filename)
   1.467 +        root = CGBindingRoot(self._config, binding_stem, filename)
   1.468 +
   1.469 +        result = self._maybe_write_codegen(root, files[0], files[1])
   1.470 +
   1.471 +        if is_event:
   1.472 +            generated_event = CGEventRoot(self._config, stem)
   1.473 +            result = self._maybe_write_codegen(generated_event, files[2],
   1.474 +                files[3], result)
   1.475 +
   1.476 +        return result, root.deps()
   1.477 +
   1.478 +    def _global_dependencies_changed(self):
   1.479 +        """Determine whether the global dependencies have changed."""
   1.480 +        current_files = set(iter_modules_in_path(mozpath.dirname(__file__)))
   1.481 +
   1.482 +        # We need to catch other .py files from /dom/bindings. We assume these
   1.483 +        # are in the same directory as the config file.
   1.484 +        current_files |= set(iter_modules_in_path(mozpath.dirname(self._config_path)))
   1.485 +
   1.486 +        current_files.add(self._config_path)
   1.487 +
   1.488 +        current_hashes = {}
   1.489 +        for f in current_files:
   1.490 +            # This will fail if the file doesn't exist. If a current global
   1.491 +            # dependency doesn't exist, something else is wrong.
   1.492 +            with open(f, 'rb') as fh:
   1.493 +                current_hashes[f] = hashlib.sha1(fh.read()).hexdigest()
   1.494 +
   1.495 +        # The set of files has changed.
   1.496 +        if current_files ^ set(self._state['global_depends'].keys()):
   1.497 +            return True, current_hashes
   1.498 +
   1.499 +        # Compare hashes.
   1.500 +        for f, sha1 in current_hashes.items():
   1.501 +            if sha1 != self._state['global_depends'][f]:
   1.502 +                return True, current_hashes
   1.503 +
   1.504 +        return False, current_hashes
   1.505 +
   1.506 +    def _save_state(self):
   1.507 +        with open(self._state_path, 'wb') as fh:
   1.508 +            self._state.dump(fh)
   1.509 +
   1.510 +    def _maybe_write_codegen(self, obj, declare_path, define_path, result=None):
   1.511 +        assert declare_path and define_path
   1.512 +        if not result:
   1.513 +            result = (set(), set(), set())
   1.514 +
   1.515 +        self._maybe_write_file(declare_path, obj.declare(), result)
   1.516 +        self._maybe_write_file(define_path, obj.define(), result)
   1.517 +
   1.518 +        return result
   1.519 +
   1.520 +    def _maybe_write_file(self, path, content, result):
   1.521 +        fh = FileAvoidWrite(path)
   1.522 +        fh.write(content)
   1.523 +        existed, updated = fh.close()
   1.524 +
   1.525 +        if not existed:
   1.526 +            result[0].add(path)
   1.527 +        elif updated:
   1.528 +            result[1].add(path)
   1.529 +        else:
   1.530 +            result[2].add(path)
   1.531 +
   1.532 +
   1.533 +def create_build_system_manager(topsrcdir, topobjdir, dist_dir):
   1.534 +    """Create a WebIDLCodegenManager for use by the build system."""
   1.535 +    src_dir = os.path.join(topsrcdir, 'dom', 'bindings')
   1.536 +    obj_dir = os.path.join(topobjdir, 'dom', 'bindings')
   1.537 +
   1.538 +    with open(os.path.join(obj_dir, 'file-lists.json'), 'rb') as fh:
   1.539 +        files = json.load(fh)
   1.540 +
   1.541 +    inputs = (files['webidls'], files['exported_stems'],
   1.542 +        files['generated_events_stems'], files['example_interfaces'])
   1.543 +
   1.544 +    cache_dir = os.path.join(obj_dir, '_cache')
   1.545 +    try:
   1.546 +        os.makedirs(cache_dir)
   1.547 +    except OSError as e:
   1.548 +        if e.errno != errno.EEXIST:
   1.549 +            raise
   1.550 +
   1.551 +    return WebIDLCodegenManager(
   1.552 +        os.path.join(src_dir, 'Bindings.conf'),
   1.553 +        inputs,
   1.554 +        os.path.join(dist_dir, 'include', 'mozilla', 'dom'),
   1.555 +        obj_dir,
   1.556 +        os.path.join(obj_dir, 'codegen.json'),
   1.557 +        cache_dir=cache_dir,
   1.558 +        # The make rules include a codegen.pp file containing dependencies.
   1.559 +        make_deps_path=os.path.join(obj_dir, 'codegen.pp'),
   1.560 +        make_deps_target='codegen.pp',
   1.561 +    )
   1.562 +
   1.563 +
   1.564 +class BuildSystemWebIDL(MozbuildObject):
   1.565 +    @property
   1.566 +    def manager(self):
   1.567 +        if not hasattr(self, '_webidl_manager'):
   1.568 +            self._webidl_manager = create_build_system_manager(
   1.569 +                self.topsrcdir, self.topobjdir, self.distdir)
   1.570 +
   1.571 +        return self._webidl_manager

mercurial