mirror of
https://gitlab.com/kernel-firmware/linux-firmware.git
synced 2025-12-10 07:44:48 +01:00
Add a script and helper library that convert the WHENCE file to YAML format and write the data to WHENCE.yaml. This can we be used as a starting point for making the WHENCE data better machine-parseable and also serves as a foundation for adding more relavant data for firmware consumers like distros. Signed-off-by: Juerg Haefliger <juerg.haefliger@canonical.com>
179 lines
5.1 KiB
Python
179 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
#
|
|
# WHENCE data classes
|
|
#
|
|
|
|
import json
|
|
import re
|
|
from dataclasses import asdict, dataclass, field
|
|
from pathlib import Path
|
|
|
|
from ruamel.yaml import YAML
|
|
|
|
|
|
def strip(obj: list) -> list:
|
|
"""Remove leading and trailing empty strings from a list of strings"""
|
|
if obj:
|
|
while obj[0] == "":
|
|
obj = obj[1:]
|
|
while obj[-1] == "":
|
|
obj = obj[:-1]
|
|
return obj
|
|
|
|
|
|
@dataclass
|
|
class BaseWhence:
|
|
"""Base WHENCE data class"""
|
|
|
|
def __str__(self):
|
|
return json.dumps(asdict(self), indent=2)
|
|
|
|
|
|
@dataclass
|
|
class Entry(BaseWhence):
|
|
"""WHENCE file/link entry class"""
|
|
|
|
type: str = "" # File or RawFile or Link
|
|
path: str = "" # Firmware file or link
|
|
target: str = "" # Link target
|
|
sources: list[str] = field(default_factory=list) # List of source files
|
|
version: str = ""
|
|
info: str = ""
|
|
origin: str = ""
|
|
|
|
|
|
@dataclass
|
|
class Section(BaseWhence):
|
|
"""WHENCE driver section class"""
|
|
|
|
driver: str = ""
|
|
module: str = ""
|
|
description: str = ""
|
|
licence: list[str] = field(default_factory=list)
|
|
licence_file: str = ""
|
|
notes: list[str] = field(default_factory=list)
|
|
entries: list[Entry] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class Whence(BaseWhence):
|
|
"""Main WHENCE data class"""
|
|
|
|
header: list[str] = field(default_factory=list)
|
|
sections: list[Section] = field(default_factory=list)
|
|
|
|
def load_whence(self, whence_file: Path) -> None:
|
|
"""Load data from a WHENCE file"""
|
|
self.sections = []
|
|
|
|
HEADER = 0
|
|
SECTION = 1
|
|
NOTES = 2
|
|
LICENCE = 3
|
|
|
|
with open(whence_file, encoding="utf-8") as fh:
|
|
whence_type = HEADER
|
|
for line in fh:
|
|
line = line.rstrip()
|
|
|
|
if ":" in line:
|
|
key, val = line.split(":", 1)
|
|
val = val.strip()
|
|
else:
|
|
key = None
|
|
val = None
|
|
|
|
if line.startswith("----------"):
|
|
whence_type = SECTION
|
|
section = Section()
|
|
self.sections.append(section)
|
|
continue
|
|
|
|
if key == "Driver":
|
|
section.driver = val
|
|
if " " in val:
|
|
module, desc = val.split(" ", 1)
|
|
desc = desc.strip(" -")
|
|
else:
|
|
module = val
|
|
desc = ""
|
|
section.module = module
|
|
section.description = desc
|
|
continue
|
|
|
|
if key in ("File", "RawFile"):
|
|
entry = Entry(type=key, path=val)
|
|
section.entries.append(entry)
|
|
continue
|
|
|
|
if key == "Version":
|
|
entry.version = val
|
|
continue
|
|
|
|
if key == "Link":
|
|
path, _, target = val.partition(" -> ")
|
|
entry = Entry(type=key, path=path, target=target)
|
|
section.entries.append(entry)
|
|
continue
|
|
|
|
if key == "Source":
|
|
entry.sources.append(val)
|
|
continue
|
|
|
|
if key == "Info":
|
|
entry.info = val
|
|
continue
|
|
|
|
if key == "Origin":
|
|
entry.origin = val
|
|
continue
|
|
|
|
if key in ("Licence", "License"):
|
|
whence_type = LICENCE
|
|
section.licence.append(val or "--")
|
|
m = re.search(r"(LICEN[CS]E\.[^ ]+) for details", line)
|
|
if m:
|
|
section.licence_file = m.group(1)
|
|
continue
|
|
|
|
if whence_type == HEADER:
|
|
self.header.append(line)
|
|
continue
|
|
|
|
if whence_type == NOTES or (whence_type == SECTION and line):
|
|
whence_type = NOTES
|
|
section.notes.append(line)
|
|
continue
|
|
|
|
if whence_type == LICENCE:
|
|
section.licence.append(line)
|
|
continue
|
|
|
|
if line:
|
|
raise Exception(f"Failed to parse line: {line}")
|
|
|
|
# Strip leading and trailing empty lines
|
|
self.header = strip(self.header)
|
|
for s in self.sections:
|
|
s.licence = strip(s.licence)
|
|
s.notes = strip(s.notes)
|
|
|
|
def save_yaml(self, whence_yaml: Path, remove_empty: bool = False) -> None:
|
|
"""Save the WHENCE data to a YAML file"""
|
|
d = asdict(self)
|
|
|
|
if remove_empty:
|
|
# Hack: Remove empty items to reduce clutter
|
|
for s in d["sections"]:
|
|
for key in list(s):
|
|
if not s[key]:
|
|
del s[key]
|
|
for e in s["entries"]:
|
|
for key in list(e):
|
|
if not e[key]:
|
|
del e[key]
|
|
|
|
with open(whence_yaml, mode="w", encoding="utf-8") as fh:
|
|
yaml = YAML()
|
|
yaml.dump(d, fh)
|