From d61146eff7ae5d785f1dc6348efc42df46cbfb6e Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Tue, 5 Mar 2013 10:45:41 -0800 Subject: [PATCH] Bug 836208 - Part 1: Factor resolve_target_to_make out of mozbuild.mach_commands. r=glandium --- python/mozbuild/mozbuild/mach_commands.py | 48 +-------------- python/mozbuild/mozbuild/test/data/Makefile | 0 .../mozbuild/test/data/test-dir/Makefile | 0 .../mozbuild/test/data/test-dir/with/Makefile | 0 .../data/test-dir/with/without/with/Makefile | 0 .../test/data/test-dir/without/with/Makefile | 0 python/mozbuild/mozbuild/test/test_util.py | 37 ++++++++++++ python/mozbuild/mozbuild/util.py | 58 +++++++++++++++++++ 8 files changed, 98 insertions(+), 45 deletions(-) create mode 100644 python/mozbuild/mozbuild/test/data/Makefile create mode 100644 python/mozbuild/mozbuild/test/data/test-dir/Makefile create mode 100644 python/mozbuild/mozbuild/test/data/test-dir/with/Makefile create mode 100644 python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile create mode 100644 python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index be334b8b366..8a05a517756 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -35,6 +35,7 @@ class Build(MachCommandBase): # 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() @@ -60,50 +61,6 @@ class Build(MachCommandBase): 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): @@ -112,7 +69,8 @@ class Build(MachCommandBase): 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 diff --git a/python/mozbuild/mozbuild/test/data/Makefile b/python/mozbuild/mozbuild/test/data/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/mozbuild/mozbuild/test/data/test-dir/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/mozbuild/mozbuild/test/data/test-dir/with/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/with/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/mozbuild/mozbuild/test/test_util.py b/python/mozbuild/mozbuild/test/test_util.py index 395de17e676..324c5452fb5 100644 --- a/python/mozbuild/mozbuild/test/test_util.py +++ b/python/mozbuild/mozbuild/test/test_util.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import hashlib +import os import unittest from mozfile.mozfile import NamedTemporaryFile @@ -16,9 +17,14 @@ from mozunit import ( 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.""" @@ -93,5 +99,36 @@ class TestFileAvoidWrite(unittest.TestCase): 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() diff --git a/python/mozbuild/mozbuild/util.py b/python/mozbuild/mozbuild/util.py index 60da43a6efa..5591fe7fd99 100644 --- a/python/mozbuild/mozbuild/util.py +++ b/python/mozbuild/mozbuild/util.py @@ -147,3 +147,61 @@ class FileAvoidWrite(StringIO): 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)