Bug 844635 - Part 2: Don't require Makefile.in to exist; r=glandium
authorGregory Szorc <gps@mozilla.com>
Sun, 17 Mar 2013 18:01:10 -0700
changeset 131604 3c87e1fe617dacec9dafb5dc19d72e6082c26db1
parent 131603 a0336cae081545b6fb2ce58a30160776c75eed8d
child 131605 336b6586074ee8c550c47a15c717dc8ccc8ed47f
push idunknown
push userunknown
push dateunknown
reviewersglandium
bugs844635
milestone22.0a1
Bug 844635 - Part 2: Don't require Makefile.in to exist; r=glandium
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/test/backend/data/stub0/dir2/Makefile.in
python/mozbuild/mozbuild/test/backend/test_recursivemake.py
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -14,16 +14,29 @@ from ..frontend.data import (
     ConfigFileSubstitution,
     DirectoryTraversal,
     SandboxDerived,
     VariablePassthru,
 )
 from ..util import FileAvoidWrite
 
 
+STUB_MAKEFILE = '''
+# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.
+
+DEPTH          := {depth}
+topsrcdir      := {topsrc}
+srcdir         := {src}
+VPATH          := {src}
+relativesrcdir := {relsrc}
+
+include {topsrc}/config/rules.mk
+'''.lstrip()
+
+
 class BackendMakeFile(object):
     """Represents a generated backend.mk file.
 
     This is both a wrapper around a file handle as well as a container that
     holds accumulated state.
 
     It's worth taking a moment to explain the make dependencies. The
     generated backend.mk as well as the Makefile.in (if it exists) are in the
@@ -71,19 +84,27 @@ class BackendMakeFile(object):
         # Filenames that influenced the content of this file.
         self.inputs = set()
 
         self.fh = FileAvoidWrite(self.path)
         self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
         self.fh.write('\n')
         self.fh.write('MOZBUILD_DERIVED := 1\n')
 
-        # SUBSTITUTE_FILES handles Makefile.in -> Makefile conversion.
+        # SUBSTITUTE_FILES handles Makefile.in -> Makefile conversion. This
+        # also doubles to handle the case where there is no Makefile.in.
         self.fh.write('NO_MAKEFILE_RULE := 1\n')
 
+        # We can't blindly have a SUBMAKEFILES rule because some of the
+        # Makefile may not have a corresponding Makefile.in. For the case
+        # where a new directory is added, the mozbuild file referencing that
+        # new directory will need updated. This will cause a full backend
+        # scan and build, installing the new Makefile.
+        self.fh.write('NO_SUBMAKEFILES_RULE := 1\n')
+
 
     def write(self, buf):
         self.fh.write(buf)
 
     def close(self):
         if self.inputs:
             l = ' '.join(sorted(self.inputs))
             self.fh.write('BACKEND_INPUT_FILES += %s\n' % l)
@@ -194,28 +215,45 @@ class RecursiveMakeBackend(BuildBackend)
     def consume_finished(self):
         for srcdir in sorted(self._backend_files.keys()):
             bf = self._backend_files[srcdir]
 
             if not os.path.exists(bf.objdir):
                 os.makedirs(bf.objdir)
 
             makefile_in = os.path.join(srcdir, 'Makefile.in')
+            makefile = os.path.join(bf.objdir, 'Makefile')
 
-            if not os.path.exists(makefile_in):
-                raise Exception('Could not find Makefile.in: %s' % makefile_in)
+            # If Makefile.in exists, use it as a template. Otherwise, create a
+            # stub.
+            if os.path.exists(makefile_in):
+                self.log(logging.DEBUG, 'substitute_makefile',
+                    {'path': makefile}, 'Substituting makefile: {path}')
+
+                self._update_from_avoid_write(
+                    bf.environment.create_config_file(makefile))
+                self.summary.managed_count += 1
 
-            out_path = os.path.join(bf.objdir, 'Makefile')
-            self.log(logging.DEBUG, 'create_makefile', {'path': out_path},
-                'Generating makefile: {path}')
-            self._update_from_avoid_write(
-                bf.environment.create_config_file(out_path))
-            self.summary.managed_count += 1
+                bf.write('SUBSTITUTE_FILES += Makefile\n')
+            else:
+                self.log(logging.DEBUG, 'stub_makefile',
+                    {'path': makefile}, 'Creating stub Makefile: {path}')
 
-            bf.write('SUBSTITUTE_FILES += Makefile\n')
+                params = {
+                    'topsrc': bf.environment.get_top_srcdir(makefile),
+                    'src': bf.environment.get_file_srcdir(makefile),
+                    'depth': bf.environment.get_depth(makefile),
+                    'relsrc': bf.environment.get_relative_srcdir(makefile),
+                }
+
+                aw = FileAvoidWrite(makefile)
+                aw.write(STUB_MAKEFILE.format(**params))
+                self._update_from_avoid_write(aw.close())
+                self.summary.managed_count += 1
+
             self._update_from_avoid_write(bf.close())
             self.summary.managed_count += 1
 
     def _process_directory_traversal(self, obj, backend_file):
         """Process a data.DirectoryTraversal instance."""
         fh = backend_file.fh
 
         for tier, dirs in obj.tier_dirs.iteritems():
deleted file mode 100644
--- a/python/mozbuild/mozbuild/test/backend/data/stub0/dir2/Makefile.in
+++ /dev/null
@@ -1,12 +0,0 @@
-# Any copyright is dedicated to the Public Domain.
-# http://creativecommons.org/publicdomain/zero/1.0/
-
-DEPTH := @DEPTH@
-topsrcdir := @top_srcdir@
-srcdir := @srcdir@
-VPATH = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-include $(topsrcdir)/config/rules.mk
-
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -26,21 +26,19 @@ class TestRecursiveMakeBackend(BackendTe
 
     def test_output_files(self):
         """Ensure proper files are generated."""
         env = self._consume('stub0', RecursiveMakeBackend)
 
         expected = ['', 'dir1', 'dir2']
 
         for d in expected:
-            in_path = os.path.join(env.topsrcdir, d, 'Makefile.in')
             out_makefile = os.path.join(env.topobjdir, d, 'Makefile')
             out_backend = os.path.join(env.topobjdir, d, 'backend.mk')
 
-            self.assertTrue(os.path.exists(in_path))
             self.assertTrue(os.path.exists(out_makefile))
             self.assertTrue(os.path.exists(out_backend))
 
     def test_makefile_conversion(self):
         """Ensure Makefile.in is converted properly."""
         env = self._consume('stub0', RecursiveMakeBackend)
 
         p = os.path.join(env.topobjdir, 'Makefile')
@@ -52,26 +50,39 @@ class TestRecursiveMakeBackend(BackendTe
             'srcdir := %s' % env.topsrcdir,
             'VPATH = %s' % env.topsrcdir,
             '',
             'include $(DEPTH)/config/autoconf.mk',
             '',
             'include $(topsrcdir)/config/rules.mk'
         ])
 
+    def test_missing_makefile_in(self):
+        """Ensure missing Makefile.in results in Makefile creation."""
+        env = self._consume('stub0', RecursiveMakeBackend)
+
+        p = os.path.join(env.topobjdir, 'dir2', 'Makefile')
+        self.assertTrue(os.path.exists(p))
+
+        lines = [l.strip() for l in open(p, 'rt').readlines()]
+        self.assertEqual(len(lines), 9)
+
+        self.assertTrue(lines[0].startswith('# THIS FILE WAS AUTOMATICALLY'))
+
     def test_backend_mk(self):
         """Ensure backend.mk file is written out properly."""
         env = self._consume('stub0', RecursiveMakeBackend)
 
         p = os.path.join(env.topobjdir, 'backend.mk')
 
         lines = [l.strip() for l in open(p, 'rt').readlines()[2:-1]]
         self.assertEqual(lines, [
             'MOZBUILD_DERIVED := 1',
             'NO_MAKEFILE_RULE := 1',
+            'NO_SUBMAKEFILES_RULE := 1',
             'DIRS := dir1',
             'PARALLEL_DIRS := dir2',
             'TEST_DIRS := dir3',
             'SUBSTITUTE_FILES += Makefile',
         ])
 
     def test_mtime_no_change(self):
         """Ensure mtime is not updated if file content does not change."""
@@ -95,16 +106,17 @@ class TestRecursiveMakeBackend(BackendTe
         """Ensure we have make recursion into external make directories."""
         env = self._consume('external_make_dirs', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:-1]]
         self.assertEqual(lines, [
             'MOZBUILD_DERIVED := 1',
             'NO_MAKEFILE_RULE := 1',
+            'NO_SUBMAKEFILES_RULE := 1',
             'DIRS := dir',
             'PARALLEL_DIRS := p_dir',
             'DIRS += external',
             'PARALLEL_DIRS += p_external',
             'SUBSTITUTE_FILES += Makefile',
         ])
 
     def test_substitute_config_files(self):
@@ -119,23 +131,23 @@ class TestRecursiveMakeBackend(BackendTe
         ])
 
     def test_variable_passthru(self):
         """Ensure variable passthru is written out correctly."""
         env = self._consume('variable_passthru', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:-1]]
-        self.assertEqual(lines[2:5], [
+        self.assertEqual(lines[3:6], [
             'XPIDLSRCS += foo.idl',
             'XPIDLSRCS += bar.idl',
             'XPIDLSRCS += biz.idl',
         ])
-        self.assertEqual(lines[5:8], [
+        self.assertEqual(lines[6:9], [
             'XPIDL_FLAGS += -Idir1',
             'XPIDL_FLAGS += -Idir2',
             'XPIDL_FLAGS += -Idir3',
         ])
-        self.assertEqual(lines[8], 'XPIDL_MODULE := module_name')
+        self.assertEqual(lines[9], 'XPIDL_MODULE := module_name')
 
 
 if __name__ == '__main__':
     main()