# HG changeset patch # User Ted Campbell # Date 1537408701 0 # Node ID 3d2a3eb077fff03a8863c1b487e6a84344b0720f # Parent c745122aefb1dc9627f699606b22beb79f3cbffc Bug 1138579 - Support multiple Files patterns in moz.build r=gps Add support for |with Files('a/**', 'b/**')| in mozbuild config files. MozReview-Commit-ID: IoM4qfEhXXc Differential Revision: https://phabricator.services.mozilla.com/D5315 diff --git a/config/tests/test_mozbuild_reading.py b/config/tests/test_mozbuild_reading.py --- a/config/tests/test_mozbuild_reading.py +++ b/config/tests/test_mozbuild_reading.py @@ -93,21 +93,22 @@ class TestMozbuildReading(unittest.TestC def pattern_exists(pat): return [p for p in finder.find(pat)] != [] for ctx in contexts: if not isinstance(ctx, Files): continue relsrcdir = ctx.relsrcdir - if not pattern_exists(os.path.join(relsrcdir, ctx.pattern)): - self.fail("The pattern '%s' in a Files() entry in " - "'%s' corresponds to no files in the tree.\n" - "Please update this entry." % - (ctx.pattern, ctx.main_path)) + for p in ctx.patterns: + if not pattern_exists(os.path.join(relsrcdir, p)): + self.fail("The pattern '%s' in a Files() entry in " + "'%s' corresponds to no files in the tree.\n" + "Please update this entry." % + (p, ctx.main_path)) test_files = ctx['IMPACTED_TESTS'].files for p in test_files: if not pattern_exists(os.path.relpath(p.full_path, config.topsrcdir)): self.fail("The pattern '%s' in a dependent tests entry " "in '%s' corresponds to no files in the tree.\n" "Please update this entry." % (p, ctx.main_path)) diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -1081,19 +1081,19 @@ class Files(SubContext): Exclusive components are named by setting `SCHEDULES.exclusive`: with Files('mobile/android/**'): SCHEDULES.exclusive = ['android'] """), } - def __init__(self, parent, pattern=None): + def __init__(self, parent, *patterns): super(Files, self).__init__(parent) - self.pattern = pattern + self.patterns = patterns self.finalized = set() self.test_files = set() self.test_tags = set() self.test_flavors = set() def __iadd__(self, other): assert isinstance(other, Files) diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -1330,16 +1330,25 @@ class BuildReader(object): if key not in defaults_cache: defaults_cache[key] = self.test_defaults_for_path(ctxs) return defaults_cache[key] r = {} + # Only do wildcard matching if the '*' character is present. + # Otherwise, mozpath.match will match directories, which we've + # arbitrarily chosen to not allow. + def path_matches_pattern(relpath, pattern): + if pattern == relpath: + return True + + return '*' in pattern and mozpath.match(relpath, pattern) + for path, ctxs in paths.items(): # Should be normalized by read_relevant_mozbuilds. assert '\\' not in path flags = Files(Context()) for ctx in ctxs: if not isinstance(ctx, Files): @@ -1352,23 +1361,17 @@ class BuildReader(object): # we implement an optimized version. ctx_rel_dir = ctx.relsrcdir if ctx_rel_dir: assert path.startswith(ctx_rel_dir) relpath = path[len(ctx_rel_dir) + 1:] else: relpath = path - pattern = ctx.pattern - - # Only do wildcard matching if the '*' character is present. - # Otherwise, mozpath.match will match directories, which we've - # arbitrarily chosen to not allow. - if pattern == relpath or \ - ('*' in pattern and mozpath.match(relpath, pattern)): + if any(path_matches_pattern(relpath, p) for p in ctx.patterns): flags += ctx if not any([flags.test_tags, flags.test_files, flags.test_flavors]): flags += test_defaults_for_path(ctxs) r[path] = flags return r diff --git a/python/mozbuild/mozbuild/test/frontend/test_context.py b/python/mozbuild/mozbuild/test/frontend/test_context.py --- a/python/mozbuild/mozbuild/test/frontend/test_context.py +++ b/python/mozbuild/mozbuild/test/frontend/test_context.py @@ -658,64 +658,80 @@ class TestTypedRecord(unittest.TestCase) with self.assertRaises(TypeError): inst.field2 = object() class TestFiles(unittest.TestCase): def test_aggregate_empty(self): c = Context({}) - files = {'moz.build': Files(c, pattern='**')} + files = {'moz.build': Files(c, '**')} self.assertEqual(Files.aggregate(files), { 'bug_component_counts': [], 'recommended_bug_component': None, }) def test_single_bug_component(self): c = Context({}) - f = Files(c, pattern='**') + f = Files(c, '**') f['BUG_COMPONENT'] = (u'Product1', u'Component1') files = {'moz.build': f} self.assertEqual(Files.aggregate(files), { 'bug_component_counts': [((u'Product1', u'Component1'), 1)], 'recommended_bug_component': (u'Product1', u'Component1'), }) def test_multiple_bug_components(self): c = Context({}) - f1 = Files(c, pattern='**') + f1 = Files(c, '**') f1['BUG_COMPONENT'] = (u'Product1', u'Component1') - f2 = Files(c, pattern='**') + f2 = Files(c, '**') f2['BUG_COMPONENT'] = (u'Product2', u'Component2') files = {'a': f1, 'b': f2, 'c': f1} self.assertEqual(Files.aggregate(files), { 'bug_component_counts': [ ((u'Product1', u'Component1'), 2), ((u'Product2', u'Component2'), 1), ], 'recommended_bug_component': (u'Product1', u'Component1'), }) def test_no_recommended_bug_component(self): """If there is no clear count winner, we don't recommend a bug component.""" c = Context({}) - f1 = Files(c, pattern='**') + f1 = Files(c, '**') f1['BUG_COMPONENT'] = (u'Product1', u'Component1') - f2 = Files(c, pattern='**') + f2 = Files(c, '**') f2['BUG_COMPONENT'] = (u'Product2', u'Component2') files = {'a': f1, 'b': f2} self.assertEqual(Files.aggregate(files), { 'bug_component_counts': [ ((u'Product1', u'Component1'), 1), ((u'Product2', u'Component2'), 1), ], 'recommended_bug_component': None, }) + def test_multiple_patterns(self): + c = Context({}) + f1 = Files(c, 'a/**') + f1['BUG_COMPONENT'] = (u'Product1', u'Component1') + f2 = Files(c, 'b/**', 'a/bar') + f2['BUG_COMPONENT'] = (u'Product2', u'Component2') + + files = {'a/foo': f1, 'a/bar' : f2, 'b/foo' : f2 } + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [ + ((u'Product2', u'Component2'), 2), + ((u'Product1', u'Component1'), 1), + ], + 'recommended_bug_component': (u'Product2', u'Component2'), + }) + if __name__ == '__main__': main()