Bug 836208 - Part 1: Factor resolve_target_to_make out of mozbuild.mach_commands. r=glandium
☠☠ backed out by bdf1f0890e2f ☠ ☠
authorNick Alexander <nalexander@mozilla.com>
Tue, 05 Mar 2013 10:45:41 -0800
changeset 123865 1e2fc9c3b3c5d004337f943df347995d2e8ec588
parent 123864 60eb333fc671ae7821ad53b7b940a36d31cfc2ee
child 123866 64bad42e13b783e2ef292b40a563f852d43a2af8
push id1401
push userpastithas@mozilla.com
push dateThu, 07 Mar 2013 07:26:45 +0000
treeherderfx-team@ee4879719f78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs836208
milestone22.0a1
Bug 836208 - Part 1: Factor resolve_target_to_make out of mozbuild.mach_commands. r=glandium
python/mozbuild/mozbuild/mach_commands.py
python/mozbuild/mozbuild/test/data/Makefile
python/mozbuild/mozbuild/test/data/test-dir/Makefile
python/mozbuild/mozbuild/test/data/test-dir/with/Makefile
python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile
python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile
python/mozbuild/mozbuild/test/test_util.py
python/mozbuild/mozbuild/util.py
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -30,16 +30,17 @@ class Build(MachCommandBase):
 
     @Command('build', help='Build the tree.')
     @CommandArgument('what', default=None, nargs='*', help=BUILD_WHAT_HELP)
     def build(self, what=None):
         # This code is only meant to be temporary until the more robust tree
         # building code in bug 780329 lands.
         from mozbuild.compilation.warnings import WarningsCollector
         from mozbuild.compilation.warnings import WarningsDatabase
+        from mozbuild.util import resolve_target_to_make
 
         warnings_path = self._get_state_filename('warnings.json')
         warnings_database = WarningsDatabase()
 
         if os.path.exists(warnings_path):
             try:
                 warnings_database.load_from_file(warnings_path)
             except ValueError:
@@ -55,69 +56,26 @@ class Build(MachCommandBase):
                     self.log(logging.INFO, 'compiler_warning', warning,
                         'Warning: {flag} in {filename}: {message}')
             except:
                 # This will get logged in the more robust implementation.
                 pass
 
             self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
 
-        def resolve_target_to_make(target):
-            if os.path.isabs(target):
-                print('Absolute paths for make targets are not allowed.')
-                return (None, None)
-
-            target = target.replace(os.sep, '/')
-
-            abs_target = os.path.join(self.topobjdir, target)
-
-            # For directories, run |make -C dir|. If the directory does not
-            # contain a Makefile, check parents until we find one. At worst,
-            # this will terminate at the root.
-            if os.path.isdir(abs_target):
-                current = abs_target
-
-                while True:
-                    make_path = os.path.join(current, 'Makefile')
-                    if os.path.exists(make_path):
-                        return (current[len(self.topobjdir) + 1:], None)
-
-                    current = os.path.dirname(current)
-
-            # If it's not in a directory, this is probably a top-level make
-            # target. Treat it as such.
-            if '/' not in target:
-                return (None, target)
-
-            # We have a relative path within the tree. We look for a Makefile
-            # as far into the path as possible. Then, we compute the make
-            # target as relative to that directory.
-            reldir = os.path.dirname(target)
-            target = os.path.basename(target)
-
-            while True:
-                make_path = os.path.join(self.topobjdir, reldir, 'Makefile')
-
-                if os.path.exists(make_path):
-                    return (reldir, target)
-
-                target = os.path.join(os.path.basename(reldir), target)
-                reldir = os.path.dirname(reldir)
-
-        # End of resolve_target_to_make.
-
         if what:
             top_make = os.path.join(self.topobjdir, 'Makefile')
             if not os.path.exists(top_make):
                 print('Your tree has not been configured yet. Please run '
                     '|mach build| with no arguments.')
                 return 1
 
             for target in what:
-                make_dir, make_target = resolve_target_to_make(target)
+                make_dir, make_target = resolve_target_to_make(self.topobjdir,
+                    target)
 
                 if make_dir is None and make_target is None:
                     return 1
 
                 status = self._run_make(directory=make_dir, target=make_target,
                     line_handler=on_line, log=False, print_directory=False,
                     ensure_exit_code=False)
 
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/test_util.py
+++ b/python/mozbuild/mozbuild/test/test_util.py
@@ -1,29 +1,35 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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/.
 
 from __future__ import unicode_literals
 
 import hashlib
+import os
 import unittest
 
 from mozfile.mozfile import NamedTemporaryFile
 from mozunit import (
     main,
     MockedOpen,
 )
 
 from mozbuild.util import (
     FileAvoidWrite,
     hash_file,
+    resolve_target_to_make,
 )
 
 
+data_path = os.path.abspath(os.path.dirname(__file__))
+data_path = os.path.join(data_path, 'data')
+
+
 class TestHashing(unittest.TestCase):
     def test_hash_file_known_hash(self):
         """Ensure a known hash value is recreated."""
         data = b'The quick brown fox jumps over the lazy cog'
         expected = 'de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3'
 
         temp = NamedTemporaryFile()
         temp.write(data)
@@ -88,10 +94,41 @@ class TestFileAvoidWrite(unittest.TestCa
 
             # Check that no write actually happens when writing the
             # same content as what already is in the file
             faw = FileAvoidWrite('file')
             faw.write('content')
             self.assertEqual(faw.close(), (True, False))
 
 
+class TestResolveTargetToMake(unittest.TestCase):
+    def setUp(self):
+        self.topobjdir = data_path
+
+    def assertResolve(self, path, expected):
+        self.assertEqual(resolve_target_to_make(self.topobjdir, path), expected)
+
+    def test_absolute_path(self):
+        abspath = os.path.abspath(os.path.join(self.topobjdir, 'test-dir'))
+        self.assertResolve(abspath, (None, None))
+
+    def test_dir(self):
+        self.assertResolve('test-dir', ('test-dir', None))
+        self.assertResolve('test-dir/with', ('test-dir/with', None))
+        self.assertResolve('test-dir/with', ('test-dir/with', None))
+        self.assertResolve('test-dir/without', ('test-dir', None))
+        self.assertResolve('test-dir/without/with', ('test-dir/without/with', None))
+
+    def test_top_level(self):
+        self.assertResolve('package', (None, 'package'))
+
+    def test_regular_file(self):
+        self.assertResolve('test-dir/with/file', ('test-dir/with', 'file'))
+        self.assertResolve('test-dir/with/without/file', ('test-dir/with', 'without/file'))
+        self.assertResolve('test-dir/with/without/with/file', ('test-dir/with/without/with', 'file'))
+
+        self.assertResolve('test-dir/without/file', ('test-dir', 'without/file'))
+        self.assertResolve('test-dir/without/with/file', ('test-dir/without/with', 'file'))
+        self.assertResolve('test-dir/without/with/without/file', ('test-dir/without/with', 'without/file'))
+
+
 if __name__ == '__main__':
     main()
--- a/python/mozbuild/mozbuild/util.py
+++ b/python/mozbuild/mozbuild/util.py
@@ -142,8 +142,66 @@ class FileAvoidWrite(StringIO):
 
         return existed, True
 
     def __enter__(self):
         return self
     def __exit__(self, type, value, traceback):
         self.close()
 
+
+def resolve_target_to_make(topobjdir, target):
+    r'''
+    Resolve `target` (a target, directory, or file) to a make target.
+
+    `topobjdir` is the object directory; all make targets will be
+    rooted at or below the top-level Makefile in this directory.
+
+    Returns a pair `(reldir, target)` where `reldir` is a directory
+    relative to `topobjdir` containing a Makefile and `target` is a
+    make target (possibly `None`).
+
+    A directory resolves to the nearest directory at or above
+    containing a Makefile, and target `None`.
+
+    A file resolves to the nearest directory at or above the file
+    containing a Makefile, and an appropriate target.
+    '''
+    if os.path.isabs(target):
+        print('Absolute paths for make targets are not allowed.')
+        return (None, None)
+
+    target = target.replace(os.sep, '/')
+
+    abs_target = os.path.join(topobjdir, target)
+
+    # For directories, run |make -C dir|. If the directory does not
+    # contain a Makefile, check parents until we find one. At worst,
+    # this will terminate at the root.
+    if os.path.isdir(abs_target):
+        current = abs_target
+
+        while True:
+            make_path = os.path.join(current, 'Makefile')
+            if os.path.exists(make_path):
+                return (current[len(topobjdir) + 1:], None)
+
+            current = os.path.dirname(current)
+
+    # If it's not in a directory, this is probably a top-level make
+    # target. Treat it as such.
+    if '/' not in target:
+        return (None, target)
+
+    # We have a relative path within the tree. We look for a Makefile
+    # as far into the path as possible. Then, we compute the make
+    # target as relative to that directory.
+    reldir = os.path.dirname(target)
+    target = os.path.basename(target)
+
+    while True:
+        make_path = os.path.join(topobjdir, reldir, 'Makefile')
+
+        if os.path.exists(make_path):
+            return (reldir, target)
+
+        target = os.path.join(os.path.basename(reldir), target)
+        reldir = os.path.dirname(reldir)