Bug 1261283 - allow GENERATED_FILES to write to multiple outputs; r=glandium
authorMike Shal <mshal@mozilla.com>
Fri, 01 Apr 2016 10:38:22 -0400
changeset 291889 305f923c6f9c953518c9e8bcfb70bcae3c7f8af8
parent 291888 a65e1d378c8e8f66fa373266db9b2167c06e66c1
child 291890 aa65acd9650aa99e5452dd7344ff06d387c6e3f9
push id74714
push usermshal@mozilla.com
push dateWed, 06 Apr 2016 14:17:26 +0000
treeherdermozilla-inbound@305f923c6f9c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1261283
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1261283 - allow GENERATED_FILES to write to multiple outputs; r=glandium MozReview-Commit-ID: DbBoZZnasTo
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/frontend/data.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/test/frontend/data/generated-files/moz.build
python/mozbuild/mozbuild/test/frontend/test_emitter.py
python/mozbuild/mozbuild/util.py
xpcom/idl-parser/xpidl/moz.build
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -504,26 +504,30 @@ class RecursiveMakeBackend(CommonBackend
         elif isinstance(obj, HostDefines):
             self._process_defines(obj, backend_file, which='HOST_DEFINES')
         elif isinstance(obj, Defines):
             self._process_defines(obj, backend_file)
 
         elif isinstance(obj, GeneratedFile):
             tier = 'misc' if any(isinstance(f, ObjDirPath) for f in obj.inputs) else 'export'
             self._no_skip[tier].add(backend_file.relobjdir)
-            dep_file = "%s.pp" % obj.output
-            backend_file.write('%s:: %s\n' % (tier, obj.output))
-            backend_file.write('GARBAGE += %s\n' % obj.output)
+            first_output = obj.outputs[0]
+            dep_file = "%s.pp" % first_output
+            backend_file.write('%s:: %s\n' % (tier, first_output))
+            for output in obj.outputs:
+                if output != first_output:
+                    backend_file.write('%s: %s ;\n' % (output, first_output))
+                backend_file.write('GARBAGE += %s\n' % output)
             backend_file.write('EXTRA_MDDEPEND_FILES += %s\n' % dep_file)
             if obj.script:
                 backend_file.write("""{output}: {script}{inputs}{backend}
 \t$(REPORT_BUILD)
 \t$(call py_action,file_generate,{script} {method} {output} $(MDDEPDIR)/{dep_file}{inputs}{flags})
 
-""".format(output=obj.output,
+""".format(output=first_output,
            dep_file=dep_file,
            inputs=' ' + ' '.join([f.full_path for f in obj.inputs]) if obj.inputs else '',
            flags=' ' + ' '.join(obj.flags) if obj.flags else '',
            backend=' backend.mk' if obj.flags else '',
            script=obj.script,
            method=obj.method))
 
         elif isinstance(obj, JARManifest):
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -907,26 +907,26 @@ class SdkFiles(FinalTargetFiles):
 
 
 class GeneratedFile(ContextDerived):
     """Represents a generated file."""
 
     __slots__ = (
         'script',
         'method',
-        'output',
+        'outputs',
         'inputs',
         'flags',
     )
 
-    def __init__(self, context, script, method, output, inputs, flags=()):
+    def __init__(self, context, script, method, outputs, inputs, flags=()):
         ContextDerived.__init__(self, context)
         self.script = script
         self.method = method
-        self.output = output
+        self.outputs = outputs if isinstance(outputs, tuple) else (outputs,)
         self.inputs = inputs
         self.flags = flags
 
 
 class ClassPathEntry(object):
     """Represents a classpathentry in an Android Eclipse project."""
 
     __slots__ = (
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -780,17 +780,18 @@ class TreeMetadataEmitter(LoggingMixin):
             if (context.config.substs.get('MOZ_DEBUG') and
                     not context.config.substs.get('MOZ_NO_DEBUG_RTL')):
                 rtl_flag += 'd'
             # Use a list, like MOZBUILD_*FLAGS variables
             passthru.variables['RTL_FLAGS'] = [rtl_flag]
 
         generated_files = set()
         for obj in self._process_generated_files(context):
-            generated_files.add(obj.output)
+            for f in obj.outputs:
+                generated_files.add(f)
             yield obj
 
         for path in context['CONFIGURE_SUBST_FILES']:
             sub = self._create_substitution(ConfigFileSubstitution, context,
                 path)
             generated_files.add(str(sub.relpath))
             yield sub
 
@@ -1000,17 +1001,17 @@ class TreeMetadataEmitter(LoggingMixin):
                                 [Path(context, path + '.in')])
 
         generated_files = context.get('GENERATED_FILES')
         if not generated_files:
             return
 
         for f in generated_files:
             flags = generated_files[f]
-            output = f
+            outputs = f
             inputs = []
             if flags.script:
                 method = "main"
                 script = SourcePath(context, flags.script).full_path
 
                 # Deal with cases like "C:\\path\\to\\script.py:function".
                 if '.py:' in script:
                     script, method = script.rsplit('.py:', 1)
@@ -1031,17 +1032,17 @@ class TreeMetadataEmitter(LoggingMixin):
                             not os.path.exists(p.full_path)):
                         raise SandboxValidationError(
                             'Input for generating %s does not exist: %s'
                             % (f, p.full_path), context)
                     inputs.append(p)
             else:
                 script = None
                 method = None
-            yield GeneratedFile(context, script, method, output, inputs)
+            yield GeneratedFile(context, script, method, outputs, inputs)
 
     def _process_test_manifests(self, context):
         for prefix, info in TEST_MANIFESTS.items():
             for path, manifest in context.get('%s_MANIFESTS' % prefix, []):
                 for obj in self._process_test_manifest(context, info, path, manifest):
                     yield obj
 
         for flavor in REFTEST_FLAVORS:
--- a/python/mozbuild/mozbuild/test/frontend/data/generated-files/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files/moz.build
@@ -1,5 +1,5 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # Any copyright is dedicated to the Public Domain.
 # http://creativecommons.org/publicdomain/zero/1.0/
 
-GENERATED_FILES += [ 'bar.c', 'foo.c' ]
+GENERATED_FILES += [ 'bar.c', 'foo.c', ('xpidllex.py', 'xpidlyacc.py'), ]
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -224,51 +224,52 @@ class TestEmitterBasic(unittest.TestCase
                           'AS_DASH_C_FLAG': ''})
         self.maxDiff = maxDiff
 
 
     def test_generated_files(self):
         reader = self.reader('generated-files')
         objs = self.read_topsrcdir(reader)
 
-        self.assertEqual(len(objs), 2)
+        self.assertEqual(len(objs), 3)
         for o in objs:
             self.assertIsInstance(o, GeneratedFile)
 
-        expected = ['bar.c', 'foo.c']
-        for o, expected_filename in zip(objs, expected):
-            self.assertEqual(o.output, expected_filename)
+        expected = ['bar.c', 'foo.c', ('xpidllex.py', 'xpidlyacc.py'), ]
+        for o, f in zip(objs, expected):
+            expected_filename = f if isinstance(f, tuple) else (f,)
+            self.assertEqual(o.outputs, expected_filename)
             self.assertEqual(o.script, None)
             self.assertEqual(o.method, None)
             self.assertEqual(o.inputs, [])
 
     def test_generated_files_method_names(self):
         reader = self.reader('generated-files-method-names')
         objs = self.read_topsrcdir(reader)
 
         self.assertEqual(len(objs), 2)
         for o in objs:
             self.assertIsInstance(o, GeneratedFile)
 
         expected = ['bar.c', 'foo.c']
         expected_method_names = ['make_bar', 'main']
         for o, expected_filename, expected_method in zip(objs, expected, expected_method_names):
-            self.assertEqual(o.output, expected_filename)
+            self.assertEqual(o.outputs, (expected_filename,))
             self.assertEqual(o.method, expected_method)
             self.assertEqual(o.inputs, [])
 
     def test_generated_files_absolute_script(self):
         reader = self.reader('generated-files-absolute-script')
         objs = self.read_topsrcdir(reader)
 
         self.assertEqual(len(objs), 1)
 
         o = objs[0]
         self.assertIsInstance(o, GeneratedFile)
-        self.assertEqual(o.output, 'bar.c')
+        self.assertEqual(o.outputs, ('bar.c',))
         self.assertRegexpMatches(o.script, 'script.py$')
         self.assertEqual(o.method, 'make_bar')
         self.assertEqual(o.inputs, [])
 
     def test_generated_files_no_script(self):
         reader = self.reader('generated-files-no-script')
         with self.assertRaisesRegexp(SandboxValidationError,
             'Script for generating bar.c does not exist'):
--- a/python/mozbuild/mozbuild/util.py
+++ b/python/mozbuild/mozbuild/util.py
@@ -411,17 +411,21 @@ class UnsortedError(Exception):
 
 
 class StrictOrderingOnAppendListMixin(object):
     @staticmethod
     def ensure_sorted(l):
         if isinstance(l, StrictOrderingOnAppendList):
             return
 
-        srtd = sorted(l, key=lambda x: x.lower())
+        def _first_element(e):
+            # If the list entry is a tuple, we sort based on the first element
+            # in the tuple.
+            return e[0] if isinstance(e, tuple) else e
+        srtd = sorted(l, key=lambda x: _first_element(x).lower())
 
         if srtd != l:
             raise UnsortedError(srtd, l)
 
     def __init__(self, iterable=None, **kwargs):
         if iterable is None:
             iterable = []
 
--- a/xpcom/idl-parser/xpidl/moz.build
+++ b/xpcom/idl-parser/xpidl/moz.build
@@ -4,21 +4,20 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 PYTHON_UNIT_TESTS += [
     'runtests.py',
 ]
 
 GENERATED_FILES += [
-    'xpidllex.py',
-    'xpidlyacc.py',
+    ('xpidllex.py', 'xpidlyacc.py'),
 ]
 
-GENERATED_FILES['xpidllex.py'].script = 'header.py:main'
+GENERATED_FILES[('xpidllex.py', 'xpidlyacc.py')].script = 'header.py:main'
 
 SDK_FILES.bin += [
     '!xpidllex.py',
     '!xpidlyacc.py',
     'header.py',
     'typelib.py',
     'xpidl.py',
 ]