From e5970d631cbda9335a0f24d30c646ac605c97421 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Mon, 17 Oct 2022 15:06:25 -0500 Subject: [PATCH] Minor fixes for map key/value assignments. --- docker/android/android/soong.py | 61 +++++++++++++++++++++++--- docker/android/scripts/build-system.py | 9 ++-- docker/android/tests/Android.bp | 4 ++ docker/android/tests/test_soong.py | 32 +++++++++++--- docker/android/tox.ini | 7 ++- 5 files changed, 94 insertions(+), 19 deletions(-) diff --git a/docker/android/android/soong.py b/docker/android/android/soong.py index 51bbb3758..861153bb7 100644 --- a/docker/android/android/soong.py +++ b/docker/android/android/soong.py @@ -196,9 +196,9 @@ def pairs(self, prod): def pairs(self, prod): return [prod.pair] - @_('ident COLON expr') + @_('ident COLON expr', 'ident EQUALS expr') def pair(self, prod): - return (prod.ident, prod.expr) + return (prod.ident, MapValue(prod[1], prod.expr)) @_('ident', 'binary_operator', 'map', 'list', 'string', 'integer', 'bool') def expr(self, prod): @@ -272,6 +272,9 @@ def is_map(self): def is_list(self): return False + def is_map_value(self): + return False + def is_ident(self): return False @@ -390,6 +393,12 @@ def to_str(self, pretty=True, indent=4, depth=0): def is_binary_operator(self): return True + def str_op(self, cmp): + return ( + (self.lhs.is_string() and self.lhs.str_op(cmp)) + or (self.rhs.is_string() and self.rhs.str_op(cmp)) + ) + def __eq__(self, other): return (self.lhs, self.op, self.rhs) == (other.lhs, other.op, other.rhs) @@ -434,7 +443,7 @@ def __str__(self): def to_str(self, pretty=True, indent=4, depth=0): fmt = lambda x: x.to_str(pretty, indent, depth + 1) result = '{' - pairs = [f'{fmt(k)}: {fmt(v)}' for k, v in self.items()] + pairs = [f'{fmt(k)}{fmt(v)}' for k, v in self.items()] if len(self) == 0: result += '}' elif pretty: @@ -454,13 +463,13 @@ def is_test(self): name = self.get('name') if name is None: return False - return 'test' in name.lower() + return 'test' in name.value.lower() def is_benchmark(self): name = self.get('name') if name is None: return False - return 'benchmark' in name.lower() + return 'benchmark' in name.value.lower() def is_dev(self): return self.is_test() or self.is_benchmark() @@ -475,8 +484,8 @@ def recurse(self, max_depth=-1, depth=0): if depth != max_depth: for key, value in self.items(): yield (key, value, depth + 1, self) - if value.is_map(): - yield from value.recurse(max_depth, depth + 1) + if value.value.is_map(): + yield from value.value.recurse(max_depth, depth + 1) class List(list, Node): @@ -507,6 +516,41 @@ def filter(self, op): self[:] = [i for i in self if op(i)] +class MapValue(Node): + def __init__(self, delimiter, value): + # map key/value separators can be `:` or `=`. + assert delimiter in (':', '=') + self.delimiter = delimiter + self.value = value + + def __repr__(self): + return f'MapValue({str(self)})' + + def __str__(self): + return self.to_str(False) + + def __eq__(self, other): + # delimiter doesn't matter for equality comparison + if isinstance(other, MapValue): + return self.value == other.value + return self.value == other + + def __len__(self): + return len(self.value) + + def to_str(self, pretty=True, indent=4, depth=0): + value = self.value.to_str(pretty, indent, depth) + if self.delimiter == '=': + return f' = {value}' + return f': {value}' + + def is_map_value(self): + return True + + def filter(self, op): + self.value.filter(op) + + class Ident(str, Node): def __repr__(self): return f'Ident({str(self)})' @@ -528,6 +572,9 @@ def __repr__(self): def to_str(self, *_, **__): return f'{super().__str__()}' + def str_op(self, cmp): + return cmp(self) + def __str__(self): # `"target"` should be shown as `'target'`, not `'"target"'` return super().__str__()[1:-1] diff --git a/docker/android/scripts/build-system.py b/docker/android/scripts/build-system.py index d2f297700..0629799cd 100644 --- a/docker/android/scripts/build-system.py +++ b/docker/android/scripts/build-system.py @@ -50,10 +50,11 @@ def remove_soong_tests(path, args, *_): # or those with `{name: "test"}`, etc. ast.filter(lambda x: not (x.is_scope() and x.is_dev())) # need to remove test and benchmark subdirs + test_names = ('test', 'benchmark') subdirs = [i for i in ast if i.name == 'subdirs'] - for subdir in subdirs: - assert type(subdir.expr) is android.soong.List - subdir.expr.filter(lambda x: not any(i in x.lower() for i in ('test', 'benchmark'))) + for sub in subdirs: + assert type(sub.expr) is android.soong.List + sub.expr.filter(lambda x: x.str_op(lambda y: not any(i in y.lower() for i in test_names))) # remove gtest dependencies from regular targets. for node in ast: map = None @@ -65,7 +66,7 @@ def remove_soong_tests(path, args, *_): map = node.expr for key, value, *_ in map.recurse(): if value.is_list(): - value.filter(lambda x: 'libgtest' not in x.lower()) + value.filter(lambda x: x.str_op(lambda y: 'libgtest' not in y.lower())) with open(path, 'w') as file: ast.dump(file) diff --git a/docker/android/tests/Android.bp b/docker/android/tests/Android.bp index b0e83ca6b..531f7b5af 100644 --- a/docker/android/tests/Android.bp +++ b/docker/android/tests/Android.bp @@ -64,4 +64,8 @@ cc_test_host { "-l" + "z", ], shared_libs: [], + cflags = [ + "-Wall", + "-fstrict-aliasing", + ], } diff --git a/docker/android/tests/test_soong.py b/docker/android/tests/test_soong.py index 81ffa6477..502f467a7 100644 --- a/docker/android/tests/test_soong.py +++ b/docker/android/tests/test_soong.py @@ -133,6 +133,11 @@ def test_addition(): assert ast[26].expr[2].rhs == 'test' assert ast[26].expr[2].rhs.is_ident() + # test a few binops, just in case + binop = ast[26].expr[1] + assert binop.str_op(lambda x: 'test' in x.lower()) + assert binop.lhs.str_op(lambda x: 'test' in x.lower()) + def test_empty(): path = os.path.join(TEST_DIR, 'Empty.bp') @@ -143,19 +148,20 @@ def test_empty(): def test_ast(): array = soong.List([soong.String('"value1"'), soong.String('"value2"')]) assignment = soong.Assignment(soong.Ident('name'), array) - map = soong.Map({soong.Ident('key'): soong.String('"value"')}) + value = soong.MapValue('=', soong.String('"value"')) + map = soong.Map({soong.Ident('key'): value}) scope = soong.Scope(soong.Ident('name'), map) ast = soong.Ast([assignment, scope]) assert repr(ast) == '''Ast(name = ["value1", "value2"] -name {key: "value"})''' +name {key = "value"})''' assert str(ast) == '''name = ["value1", "value2"] -name {key: "value"}''' +name {key = "value"}''' assert ast.to_str() == '''name = [ "value1", "value2", ] name { - key: "value", + key = "value", }''' @@ -196,7 +202,8 @@ def test_binary_operator(): def test_scope(): - map = soong.Map({soong.Ident('key'): soong.String('"value"')}) + value = soong.MapValue(':', soong.String('"value"')) + map = soong.Map({soong.Ident('key'): value}) scope = soong.Scope(soong.Ident('name'), map) assert repr(scope) == 'Scope(name {key: "value"})' assert str(scope) == 'name {key: "value"}' @@ -210,7 +217,8 @@ def test_scope(): def test_map(): - map = soong.Map({soong.Ident('key'): soong.String('"value"')}) + value = soong.MapValue(':', soong.String('"value"')) + map = soong.Map({soong.Ident('key'): value}) assert repr(map) == 'Map({key: "value"})' assert str(map) == '{key: "value"}' assert map.to_str(pretty=False) == '{key: "value"}' @@ -266,6 +274,18 @@ def test_list(): assert sequence.to_str() == '[]' +def test_map_value(): + value = soong.MapValue(':', soong.String('"value"')) + assert repr(value) == 'MapValue(: "value")' + assert str(value) == ': "value"' + assert value.to_str() == ': "value"' + + value = soong.MapValue('=', soong.String('"value"')) + assert repr(value) == 'MapValue( = "value")' + assert str(value) == ' = "value"' + assert value.to_str() == ' = "value"' + + def test_ident(): ident = soong.Ident('name') assert repr(ident) == 'Ident(name)' diff --git a/docker/android/tox.ini b/docker/android/tox.ini index 091ac6929..c5886d373 100644 --- a/docker/android/tox.ini +++ b/docker/android/tox.ini @@ -16,8 +16,11 @@ passenv = [flake8] max-line-length = 100 -# we use lambdas for short, one-line conditions and formatters -ignore = E731 +ignore = + # we use lambdas for short, one-line conditions and formatters + E731 + # opt-in to new behavior with operators after line breaks + W503 per-file-ignores = # the sly grammar uses variables before they are defined via a metaclass # likewise, it uses redefinitions to extend parsers via SLR grammar