Bug 1132771 - API to return moz.build files relevant for a set of paths; r=glandium
☠☠ backed out by ce21e7a57cf8 ☠ ☠
authorGregory Szorc <gps@mozilla.com>
Wed, 25 Feb 2015 14:10:10 -0800
changeset 261574 4013d256b5910404cc04bb3caaf696b8ee551fc5
parent 261573 0fa6398b46b980ea673bf6e015a2bf067c97f0a7
child 261575 acdd5491f10ecf8ea4e1a14150f9a2e282e2cf5d
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1132771
milestone39.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 1132771 - API to return moz.build files relevant for a set of paths; r=glandium We have an eventual goal to store file-level metadata in moz.build files and to have this metadata "cascade" down directory hierarchies. e.g. metadata in the root directory will apply to all children directories. A prerequisite for this feature is a way to query which moz.build files are relevant to a given file. In this patch, we implement an API that returns this information.
python/mozbuild/mozbuild/frontend/reader.py
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file1
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file2
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/dir1/dir2/dir3/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/moz.build
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/file
python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/moz.build
python/mozbuild/mozbuild/test/frontend/test_reader.py
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -1057,8 +1057,55 @@ class BuildReader(object):
             if not descend:
                 continue
 
             for res in self.read_mozbuild(child_path, context.config,
                                           metadata=child_metadata):
                 yield res
 
         self._execution_stack.pop()
+
+    def _find_relevant_mozbuilds(self, paths):
+        """Given a set of filesystem paths, find all relevant moz.build files.
+
+        We assume that a moz.build file in the directory ancestry of a given path
+        is relevant to that path. Let's say we have the following files on disk::
+
+           moz.build
+           foo/moz.build
+           foo/baz/moz.build
+           foo/baz/file1
+           other/moz.build
+           other/file2
+
+        If ``foo/baz/file1`` is passed in, the relevant moz.build files are
+        ``moz.build``, ``foo/moz.build``, and ``foo/baz/moz.build``. For
+        ``other/file2``, the relevant moz.build files are ``moz.build`` and
+        ``other/moz.build``.
+
+        Returns a dict of input paths to a list of relevant moz.build files.
+        The root moz.build file is first and the leaf-most moz.build is last.
+        """
+        root = self.config.topsrcdir
+        result = {}
+
+        @memoize
+        def exists(path):
+            return os.path.exists(path)
+
+        def itermozbuild(path):
+            subpath = ''
+            yield 'moz.build'
+            for part in mozpath.split(path):
+                subpath = mozpath.join(subpath, part)
+                yield mozpath.join(subpath, 'moz.build')
+
+        for path in sorted(paths):
+            path = mozpath.normpath(path)
+            if os.path.isabs(path):
+                if not mozpath.basedir(path, [root]):
+                    raise Exception('Path outside topsrcdir: %s' % path)
+                path = mozpath.relpath(path, root)
+
+            result[path] = [p for p in itermozbuild(path)
+                              if exists(mozpath.join(root, p))]
+
+        return result
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
--- a/python/mozbuild/mozbuild/test/frontend/test_reader.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py
@@ -232,11 +232,72 @@ class TestBuildReader(unittest.TestCase)
         contexts = list(reader.read_topsrcdir())
 
         self.assertEqual(len(contexts), 4)
         self.assertEqual([context.relsrcdir for context in contexts],
             ['', 'foo', 'foo/baz', 'bar'])
         self.assertEqual([context['XPIDL_MODULE'] for context in contexts],
             ['foobar', 'foobar', 'baz', 'foobar'])
 
+    def test_find_relevant_mozbuilds(self):
+        reader = self.reader('reader-relevant-mozbuild')
+
+        # Absolute paths outside topsrcdir are rejected.
+        with self.assertRaises(Exception):
+            reader._find_relevant_mozbuilds(['/foo'])
+
+        # File in root directory.
+        paths = reader._find_relevant_mozbuilds(['file'])
+        self.assertEqual(paths, {'file': ['moz.build']})
+
+        # File in child directory.
+        paths = reader._find_relevant_mozbuilds(['d1/file1'])
+        self.assertEqual(paths, {'d1/file1': ['moz.build', 'd1/moz.build']})
+
+        # Multiple files in same directory.
+        paths = reader._find_relevant_mozbuilds(['d1/file1', 'd1/file2'])
+        self.assertEqual(paths, {
+            'd1/file1': ['moz.build', 'd1/moz.build'],
+            'd1/file2': ['moz.build', 'd1/moz.build']})
+
+        # Missing moz.build from missing intermediate directory.
+        paths = reader._find_relevant_mozbuilds(
+            ['d1/no-intermediate-moz-build/child/file'])
+        self.assertEqual(paths, {
+            'd1/no-intermediate-moz-build/child/file': [
+                'moz.build', 'd1/moz.build', 'd1/no-intermediate-moz-build/child/moz.build']})
+
+        # Lots of empty directories.
+        paths = reader._find_relevant_mozbuilds([
+            'd1/parent-is-far/dir1/dir2/dir3/file'])
+        self.assertEqual(paths, {
+            'd1/parent-is-far/dir1/dir2/dir3/file':
+                ['moz.build', 'd1/moz.build', 'd1/parent-is-far/moz.build']})
+
+        # Lots of levels.
+        paths = reader._find_relevant_mozbuilds([
+            'd1/every-level/a/file', 'd1/every-level/b/file'])
+        self.assertEqual(paths, {
+            'd1/every-level/a/file': [
+                'moz.build',
+                'd1/moz.build',
+                'd1/every-level/moz.build',
+                'd1/every-level/a/moz.build',
+            ],
+            'd1/every-level/b/file': [
+                'moz.build',
+                'd1/moz.build',
+                'd1/every-level/moz.build',
+                'd1/every-level/b/moz.build',
+            ],
+        })
+
+        # Different root directories.
+        paths = reader._find_relevant_mozbuilds(['d1/file', 'd2/file', 'file'])
+        self.assertEqual(paths, {
+            'file': ['moz.build'],
+            'd1/file': ['moz.build', 'd1/moz.build'],
+            'd2/file': ['moz.build', 'd2/moz.build'],
+        })
+
 
 if __name__ == '__main__':
     main()