#!/usr/bin/env python3 import argparse import os import shutil import subprocess import sys from pathlib import Path SRC_DIR = Path('/usr/src') BOOT_DIR = Path('/boot') def update_config(old_dir: Path, new_dir: Path, make_cmd: list[str]) -> None: if old_dir == new_dir: return old_config = old_dir / '.config' new_config = new_dir / '.config' while new_config.is_file(): response = input('New config present. Overwrite? [y/N]\n').strip().lower() if response == 'y': break elif response in ('', 'n'): return else: print('unrecognized option {}', response) print(f'Copying config from {old_config} to {new_config}') shutil.copy2(old_config, new_config) print(f'Setting symlink to {new_dir}') (SRC_DIR / 'linux').symlink_to(new_dir, target_is_directory=True) print('Migrating config options') migrate = [*make_cmd, '--directory', new_dir.as_posix(), 'oldconfig'] subprocess.run(migrate, check=True) menuconfig = [*make_cmd, '--directory', new_dir.as_posix(), 'menuconfig'] while True: subprocess.run(menuconfig, check=True) response = input('Stop editing? [Y/n]').strip().lower() if response in ('', 'y'): break elif response == 'n': continue else: print('unrecognized option {}', response) def compile_kernel(new_dir: Path, make_cmd: list[str]) -> None: cc = [*make_cmd, '--directory', new_dir.as_posix()] subprocess.run(cc, check=True) def install_kernel(kernel_dir: Path, make_cmd: list[str], kver: str) -> None: install_modules = [*make_cmd, '--directory', kernel_dir.as_posix(), 'modules_install'] subprocess.run(install_modules, check=True) kver = kver[6:] # drop "linux-" prefix # assumes proper dracut config in /etc, incl. signing keys dracut = ['dracut', f'--kver={kver}', '--force', '--no-machineid'] res = subprocess.run(dracut, check=True) print(res) def linux_folder(src_dir: Path, version: str) -> Path: revision = '' version = version.split('-') if len(version) > 1: revision = '-' + version[1] version = version[0] return src_dir / (f'linux-{version}-gentoo{revision}') def update_kernel(args: argparse.Namespace) -> None: old_dir = linux_folder(SRC_DIR, args.old_version) new_dir = linux_folder(SRC_DIR, args.new_version) new_version = new_dir.name make_cmd = ['make', f'--jobs={len(os.sched_getaffinity(0))}'] if args.llvm: # https://docs.kernel.org/kbuild/llvm.html make_cmd.extend(['LLVM=1']) none_selected = not any((args.config, args.compile, args.install)) if none_selected: update_config(old_dir, new_dir, make_cmd) compile_kernel(new_dir, make_cmd) install_kernel(new_dir, make_cmd, new_version) if args.config: update_config(old_dir, new_dir, make_cmd) if args.compile: compile_kernel(new_dir, make_cmd) if args.install: install_kernel(new_dir, make_cmd, new_version) def main() -> None: parser = argparse.ArgumentParser(description='Helper for manual kernel updates', usage=f'{sys.argv[0]}') parser.add_argument('--llvm', '-l', action='store_true', help='Use clang/llvm to compile kernel') parser.add_argument('--config', '-C', action='store_true', help='Migrate config from old kernel') parser.add_argument('--compile', '-c', action='store_true', help='Compile new kernel') parser.add_argument('--install', '-i', action='store_true', help='Install new kernel') parser.add_argument('old_version', help='Old kernel version (optional)', nargs='?', default=None) parser.add_argument('new_version', help='New kernel version (optional)', nargs='?', default=None) args = parser.parse_args() if args.old_version and not args.new_version: args.new_version = args.old_version args.old_version = None if not args.old_version: args.old_version = (SRC_DIR / 'linux').readlink().name.split('-')[1] if not args.new_version: subdirs = Path(SRC_DIR).glob('linux-*-gentoo*') # too broad but good enough, blame fnmatch args.new_version = sorted(subdir.name for subdir in subdirs if subdir.is_dir())[-1].split('-')[1] update_kernel(args) if __name__ == '__main__': main()