blob: 194431936bb6f7fba53996617129bf0b64137352 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2024 Richard Hughes <richard@hughsie.com>
#
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# pylint: disable=invalid-name,missing-docstring,too-few-public-methods
from typing import Dict
import os
import glob
import sys
import subprocess
import json
import argparse
from collections import defaultdict
from termcolor import colored
def _scan_file(fn: str) -> Dict[str, int]:
new_map: Dict[str, int] = defaultdict(int)
try:
print(f"loading {fn}…")
args = [
"./src/fwupdtool",
"firmware-parse",
fn,
"ifd-firmware",
"--json",
"--no-timestamp",
]
p = subprocess.run(args, check=True, capture_output=True)
except subprocess.CalledProcessError as e:
print(f"{' '.join(args)}: {e}")
else:
for line in p.stdout.decode().split("\n"):
new_map["Lines"] += 1
if line.find("gtype=") == -1:
continue
sections = line.split('"')
if not sections[1].startswith("Fu"):
continue
new_map[sections[1]] += 1
for line in p.stderr.decode().split("\n"):
if not line:
continue
new_map["WarningLines"] += 1
print(line)
return new_map
def _scan_dir(path: str, force_save: bool = False) -> bool:
all_okay: bool = True
needs_save: bool = False
results: Dict[str, Dict[str, int]] = {}
# support folders or paths
if os.path.isdir(path):
for fn in glob.glob(f"{path}/*.bin"):
results[fn] = _scan_file(fn)
else:
results[path] = _scan_file(path)
# go through each result
print(f" {os.path.basename(sys.argv[0])}:")
for fn, new_map in results.items():
try:
with open(f"{fn}.json", "rb") as f:
old_map = json.loads(f.read().decode())
except FileNotFoundError:
old_map = {}
print(f" {fn}")
for key in sorted(set(list(old_map.keys()) + list(new_map.keys()))):
cnt_old = old_map.get(key, 0)
cnt_new = new_map.get(key, 0)
if cnt_new > cnt_old:
key_str: str = colored(key, "green")
needs_save = True
elif cnt_new < cnt_old:
key_str = colored(key, "red")
if key != "WarningLines":
all_okay = False
else:
continue
print(f" {key_str:36}: {cnt_old} -> {cnt_new}")
# save new results if all better
if (needs_save and all_okay) or force_save:
for fn, new_map in results.items():
with open(f"{fn}.json", "wb") as f:
f.write(json.dumps(new_map, sort_keys=True, indent=4).encode())
return all_okay
if __name__ == "__main__":
rc: int = 0
parser = argparse.ArgumentParser(
prog="check-ifd-firmware", description="Check IFD firmware parsing"
)
parser.add_argument("paths", nargs="+")
parser.add_argument(
"--force-save", action="store_true", help="always save the json reports"
)
_args = parser.parse_args()
for _path in _args.paths:
if not _scan_dir(_path, force_save=_args.force_save):
rc = 1
sys.exit(rc)