blob: 99c0af4fb09a25b1e77c59cb190cc8b07be69019 [file] [log] [blame]
#!/usr/bin/env python3.8
# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import json
import sys
# Removes |prefix| from |line|.
# A removeprefix() function is not available until 3.9.
def removeprefix(line, prefix):
if line.startswith(prefix):
return line[len(prefix):]
return line
# Create new dict/lists containers that are hashable, but first remove any
# |prefixes| from nested strings.
def make_hashable(item, prefixes):
if isinstance(item, dict):
item = hashabledict(item, prefixes)
elif isinstance(item, list):
item = hashablelist(item, prefixes)
elif isinstance(item, str):
for prefix in prefixes:
item = removeprefix(item, prefix)
return item
# A list that is hashable so it can be added to a set.
class hashablelist(list):
def __init__(self, list, prefixes):
for v in list:
self.append(make_hashable(v, prefixes))
def __hash__(self):
return hash(tuple(sorted(self)))
def __eq__(self, other):
return sorted(self) == sorted(other)
# A dictionary that is hashable so it can be added to a set.
class hashabledict(dict):
def __init__(self, dict, prefixes):
for k, v in dict.items():
self[k] = make_hashable(v, prefixes)
def __hash__(self):
return hash(tuple(sorted(self.items())))
# Loads a set of items from a json file and optionally, uses |key| to get the list.
# Any |prefixes| will be stripped from strings in each item.
def load_set_from_json(log, key, prefixes):
items = json.load(log)
if key:
items = items.get(key, [])
return set(make_hashable(i, prefixes) for i in items)
def main():
parser = argparse.ArgumentParser(
description=
'Compare two json lists generated by different assembly invocations'
'and assert that they are functionally equivalent')
parser.add_argument(
'--reference', type=argparse.FileType('r'), required=True)
parser.add_argument(
'--comparison', type=argparse.FileType('r'), required=True)
parser.add_argument('--stamp', type=argparse.FileType('w'), required=True)
parser.add_argument('--strip-prefix', nargs='*')
parser.add_argument('--list-key', type=str)
args = parser.parse_args()
reference = load_set_from_json(
args.reference, args.list_key, args.strip_prefix)
comparison = load_set_from_json(
args.comparison, args.list_key, args.strip_prefix)
# Checks for missing commands.
missing = reference.difference(comparison)
extra = comparison.difference(reference)
if missing:
print('Items in the reference were missing from the comparison')
for cmd in missing:
print('Missing: ' + str(cmd))
if extra:
print('Items in the comparison were not found in the reference')
for cmd in extra:
print('Extra: ' + str(cmd))
if missing or extra:
return len(missing) + len(extra)
# Writes an empty file because GN requires targets to have an output.
args.stamp.write("")
return 0
if __name__ == '__main__':
sys.exit(main())