diff mbox series

[v2,2/2] support: add a kernel version configuration helper script

Message ID 20241108231236.2838-2-git@jdknight.me
State New
Headers show
Series [v2,1/2] linux: support each linux latest lts version | expand

Commit Message

James Knight Nov. 8, 2024, 11:12 p.m. UTC
Provides a helper script which can be used to quickly synchronize the
kernel versions defined in the Linux's `Config.in` file based on the
contents populated inside `linux.hash`.

The goal of this script is to help the maintenance effort required when
possibly supporting all active Linux LTS versions in the tree at a
given time.

Signed-off-by: James Knight <git@jdknight.me>
---
Changes v1 -> v2:
  - Introduced in the v2 of this series.

This is optional. No worries if not desired.
---
 support/scripts/sync-kernel-versions.py | 142 ++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
 create mode 100755 support/scripts/sync-kernel-versions.py
diff mbox series

Patch

diff --git a/support/scripts/sync-kernel-versions.py b/support/scripts/sync-kernel-versions.py
new file mode 100755
index 0000000000000000000000000000000000000000..2684b052657b0b98ac0265cd1b74a514d03ba9ca
--- /dev/null
+++ b/support/scripts/sync-kernel-versions.py
@@ -0,0 +1,142 @@ 
+#!/usr/bin/env python3
+#
+# Provides a script to allow the automatic updating of a Linux
+# package's `Config.in` file based on the version information detailed
+# in a `linux.hash` file.
+
+from pathlib import Path
+import argparse
+import re
+
+
+# searching for a version pattern inside a `linux.hash` file
+LINUX_VERSION_PATTERN = r'.*-([1-9]+.*)\.t.*'
+
+
+def main():
+    # argument parsing
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--dry-run', action='store_true',
+                        help='Do not change any files')
+    parser.add_argument('--verbose', action='store_true',
+                        help='Output additional information')
+    args = parser.parse_args()
+
+    # helpers
+    def verbose(msg):
+        if args.verbose:
+            print(f'(verbose) {msg}')
+
+    # find the linux package folder
+    br_dir = Path(__file__).parent.parent.parent
+
+    linux_pkg_dir = br_dir / 'linux'
+    if not linux_pkg_dir.is_dir():
+        print('unable to find the linux package folder')
+        return 1
+
+    linux_conf = linux_pkg_dir / 'Config.in'
+    if not linux_conf.is_file():
+        print('unable to find the linux configuration file')
+        return 1
+
+    linux_hash = linux_pkg_dir / 'linux.hash'
+    if not linux_hash.is_file():
+        print('unable to find the linux hash file')
+        return 1
+
+    # extract expected linux entries from the hash file
+    try:
+        with linux_hash.open(encoding='utf-8') as f:
+            raw_hash_output = f.readlines()
+    except OSError as e:
+        print(f'error reading file {linux_hash}: {e}')
+        return 1
+
+    entries = {}
+    for hash_entry in raw_hash_output:
+        if hash_entry.startswith('sha'):
+            _, _, fname = hash_entry.strip().split(maxsplit=2)
+            if fname.startswith('linux-'):
+                verbose(f'detected linux entry: {fname}')
+
+                match = re.search(LINUX_VERSION_PATTERN, fname)
+                if match:
+                    vnum = match.group(1)
+                    ventry = extract_ventry(vnum)
+                    entries[ventry] = vnum
+                    verbose(f'detected linux version ({fname}): {vnum}')
+                else:
+                    verbose(f'unable to detect linux version: {fname}')
+
+    # read the existing Linux configuration file
+    conf_contents = []
+    has_updated = False
+    process_cfg = None
+    try:
+        with linux_conf.open(encoding='utf-8') as f:
+            lines = f.read().splitlines()
+            for line in lines:
+                # wait until we are processing the kernel version section
+                if process_cfg is None:
+                    if 'config BR2_LINUX_KERNEL_VERSION' in line:
+                        process_cfg = True
+
+                # if we are processing, check each version entry for any
+                # updates that are required
+                elif process_cfg:
+                    # stop if the section is done
+                    if not line:
+                        process_cfg = False
+                    # check if this is a linux configuration entry that
+                    # might need to be updated
+                    elif 'default "' in line:
+                        front, raw_version, tail = line.split(maxsplit=2)
+                        vnum = raw_version.replace('"', '')
+                        if '.' in vnum:
+                            ventry = extract_ventry(vnum)
+                            if ventry in entries:
+                                new_vnum = entries[ventry]
+                                # if the version is outdated, replace the line
+                                if vnum != new_vnum:
+                                    print(f'Sync: {vnum} -> {new_vnum}')
+                                    line = f'\t{front} "{new_vnum}" {tail}'
+                                    has_updated = True
+
+                conf_contents.append(line)
+
+    except OSError as e:
+        print(f'error reading file {linux_conf}: {e}')
+        return 1
+
+    # update the linux configuration file if we have new changes
+    if has_updated and not args.dry_run:
+        print(f'Updating: {linux_conf}')
+        try:
+            with linux_conf.open('w', encoding='utf-8', newline='') as f:
+                for line in conf_contents:
+                    f.write(f'{line}\n')
+
+        except OSError as e:
+            print(f'error writing file {linux_conf}: {e}')
+            return 1
+
+    if not has_updated:
+        print('No outdated versions.')
+
+    return 0
+
+
+def extract_ventry(vnum):
+    vmaj, vmin, _ = vnum.split('.', maxsplit=2)
+
+    ventry = f'{vmaj}.{vmin}'
+    if '-' in vnum:
+        _, postfix = vnum.split('-', maxsplit=1)
+        ventry += f'-{postfix}'
+
+    return ventry
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())