scripts/kernel-update.py
Martin Matous 75d39f3233
kernel-update: fix symlink creation
Signed-off-by: Martin Matous <m@matous.dev>
2025-03-25 21:47:48 +01:00

117 lines
4 KiB
Python
Executable file

#!/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}')
new_symlink = SRC_DIR / 'linux.updated'
new_symlink.symlink_to(new_dir, target_is_directory=True)
new_symlink.rename(SRC_DIR / 'linux')
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()