Bug 1304125 - Perform WebIDL code generation in the tup backend; r?gps draft
authorMike Shal <mshal@mozilla.com>
Mon, 19 Sep 2016 13:30:03 -0400
changeset 415651 43322e320f4a0c385c6ca682dc01766add835ccc
parent 415650 79a9d255c750157d34cf1b7847cd1b8ee36b8e00
child 531659 e182c305562c4a2bb5f11c1d6bbd9c04ccc63bb3
push id29923
push userbmo:mshal@mozilla.com
push dateTue, 20 Sep 2016 19:29:15 +0000
reviewersgps
bugs1304125
milestone52.0a1
Bug 1304125 - Perform WebIDL code generation in the tup backend; r?gps MozReview-Commit-ID: Fnw8380zyGU
python/mozbuild/mozbuild/backend/tup.py
--- a/python/mozbuild/mozbuild/backend/tup.py
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -10,17 +10,19 @@ import mozpack.path as mozpath
 from mozbuild.base import MozbuildObject
 from mozbuild.backend.base import PartialBackend, HybridBackend
 from mozbuild.backend.recursivemake import RecursiveMakeBackend
 from mozbuild.shellutil import quote as shell_quote
 
 from .common import CommonBackend
 from ..frontend.data import (
     ContextDerived,
+    Defines,
     GeneratedFile,
+    HostDefines,
 )
 from ..util import (
     FileAvoidWrite,
 )
 
 
 class BackendTupfile(object):
     """Represents a generated Tupfile.
@@ -30,36 +32,51 @@ class BackendTupfile(object):
         self.topsrcdir = topsrcdir
         self.srcdir = srcdir
         self.objdir = objdir
         self.relobjdir = mozpath.relpath(objdir, topobjdir)
         self.environment = environment
         self.name = mozpath.join(objdir, 'Tupfile')
         self.rules_included = False
         self.shell_exported = False
+        self.defines = []
+        self.host_defines = []
 
         self.fh = FileAvoidWrite(self.name, capture_diff=True)
         self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
         self.fh.write('\n')
 
     def write(self, buf):
         self.fh.write(buf)
 
     def include_rules(self):
         if not self.rules_included:
             self.write('include_rules\n')
             self.rules_included = True
 
-    def rule(self, cmd, inputs=None, outputs=None, display=None, extra_outputs=None):
+    def rule(self, cmd, inputs=None, outputs=None, display=None, extra_outputs=None, check_unchanged=False):
         inputs = inputs or []
         outputs = outputs or []
+        display = display or ""
         self.include_rules()
+        flags = ""
+        if check_unchanged:
+            # This flag causes tup to compare the outputs with the previous run
+            # of the command, and skip the rest of the DAG for any that are the
+            # same.
+            flags += "o"
+
+        if display:
+            caret_text = flags + ' ' + display
+        else:
+            caret_text = flags
+
         self.write(': %(inputs)s |> %(display)s%(cmd)s |> %(outputs)s%(extra_outputs)s\n' % {
             'inputs': ' '.join(inputs),
-            'display': '^ %s^ ' % display if display else '',
+            'display': '^%s^ ' % caret_text if caret_text else '',
             'cmd': ' '.join(cmd),
             'outputs': ' '.join(outputs),
             'extra_outputs': ' | ' + ' '.join(extra_outputs) if extra_outputs else '',
         })
 
     def export_shell(self):
         if not self.shell_exported:
             # These are used by mach/mixin/process.py to determine the current
@@ -146,16 +163,20 @@ class TupOnly(CommonBackend, PartialBack
                 outputs.append('%s.pp' % obj.outputs[0])
 
                 backend_file.rule(
                     display='python {script}:{method} -> [%o]'.format(script=obj.script, method=obj.method),
                     cmd=cmd,
                     inputs=full_inputs,
                     outputs=outputs,
                 )
+        elif isinstance(obj, Defines):
+            self._process_defines(backend_file, obj)
+        elif isinstance(obj, HostDefines):
+            self._process_defines(backend_file, obj, host=True)
 
         return True
 
     def consume_finished(self):
         CommonBackend.consume_finished(self)
 
         for objdir, backend_file in sorted(self._backend_files.items()):
             with self._write_file(fh=backend_file):
@@ -179,16 +200,24 @@ class TupOnly(CommonBackend, PartialBack
             fh.write('IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser\n')
             fh.write('IDL_PARSER_CACHE_DIR = $(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl\n')
 
         # Run 'tup init' if necessary.
         if not os.path.exists(mozpath.join(self.environment.topsrcdir, ".tup")):
             tup = self.environment.substs.get('TUP', 'tup')
             self._cmd.run_process(cwd=self.environment.topsrcdir, log_name='tup', args=[tup, 'init'])
 
+    def _process_defines(self, backend_file, obj, host=False):
+        defines = list(obj.get_defines())
+        if defines:
+            if host:
+                backend_file.host_defines = defines
+            else:
+                backend_file.defines = defines
+
     def _handle_idl_manager(self, manager):
         backend_file = self._get_backend_file('xpcom/xpidl')
         backend_file.export_shell()
 
         for module, data in sorted(manager.modules.iteritems()):
             dest, idls = data
             cmd = [
                 '$(PYTHON_PATH)',
@@ -211,28 +240,64 @@ class TupOnly(CommonBackend, PartialBack
                     '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidllex.py',
                     '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidlyacc.py',
                 ],
                 display='XPIDL %s' % module,
                 cmd=cmd,
                 outputs=outputs,
             )
 
+    def _preprocess(self, backend_file, input_file):
+        cmd = self._py_action('preprocessor')
+        cmd.extend(backend_file.defines)
+        cmd.extend(['$(ACDEFINES)', '%f', '-o', '%o'])
+
+        backend_file.rule(
+            inputs=[input_file],
+            display='Preprocess %o',
+            cmd=cmd,
+            outputs=[mozpath.basename(input_file)],
+        )
+
     def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources,
                              unified_ipdl_cppsrcs_mapping):
         # TODO: This isn't implemented yet in the tup backend, but it is called
         # by the CommonBackend.
         pass
 
     def _handle_webidl_build(self, bindings_dir, unified_source_mapping,
                              webidls, expected_build_output_files,
                              global_define_files):
-        # TODO: This isn't implemented yet in the tup backend, but it is called
-        # by the CommonBackend.
-        pass
+        backend_file = self._get_backend_file('dom/bindings')
+        backend_file.export_shell()
+
+        for source in sorted(webidls.all_preprocessed_sources()):
+            self._preprocess(backend_file, source)
+
+        cmd = self._py_action('webidl')
+        cmd.append(mozpath.join(self.environment.topsrcdir, 'dom', 'bindings'))
+
+        # The WebIDLCodegenManager knows all of the .cpp and .h files that will
+        # be created (expected_build_output_files), but there are a few
+        # additional files that are also created by the webidl py_action.
+        outputs = [
+            '_cache/webidlyacc.py',
+            'codegen.json',
+            'codegen.pp',
+            'parser.out',
+        ]
+        outputs.extend(expected_build_output_files)
+
+        backend_file.rule(
+            display='WebIDL code generation',
+            cmd=cmd,
+            inputs=webidls.all_non_static_basenames(),
+            outputs=outputs,
+            check_unchanged=True,
+        )
 
 
 class TupBackend(HybridBackend(TupOnly, RecursiveMakeBackend)):
     def build(self, config, output, jobs, verbose):
         status = config._run_make(directory=self.environment.topobjdir, target='tup',
                                   line_handler=output.on_line, log=False, print_directory=False,
                                   ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
                                   append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})