From 0e9d9f4727ec8883a03536885e09b662f90b70a5 Mon Sep 17 00:00:00 2001 From: Florian Schroegendorfer Date: Tue, 4 Jun 2024 02:07:17 +0200 Subject: [PATCH] added ESP command line option --- README.md | 32 +++++------ ekernel.py | 122 ++++++++++++++++++++++++++---------------- tests/data/kernel.py | 14 ++--- tests/test_clean.py | 13 +++++ tests/test_install.py | 16 ++++-- tests/test_update.py | 17 ++++++ 6 files changed, 140 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 579d525..9039ac4 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ The following variables will be used throughout this description: `version` - new kernel's version string -###

configure`ekernel-configure`

+### `ekernel-configure` Runs `make menuconfig` if the current config file is missing, or no other kernel is installed @@ -58,7 +58,7 @@ cd ${new} make oldconfig || exit ``` -###

build`ekernel-build`

+### `ekernel-build` Build and install modules, using the given number of jobs. @@ -66,7 +66,7 @@ Build and install modules, using the given number of jobs. make -j ${jobs} && make modules_install ``` -###

install`ekernel-install`

+### `ekernel-install` Update symlink ``/usr/src/linux`` to the new source directory. @@ -88,18 +88,7 @@ Rebuild external modules. emerge @module-rebuild ``` -###

commit`ekernel-commit`

- -Commit the new kernel config with a detailed commit message. - -```sh -git add -f /usr/src/linux/.config -git commit -S -m "${msg}" -``` - -The message will not only contain the version change, but also details about the newly added or removed options. - -###

clean`ekernel-clean`

+### `ekernel-clean` Remove unused kernel source directories, modules and boot images. @@ -113,9 +102,20 @@ rm -rf $(ls -1 ${esp} | sed -e '/${old}/d' -e '/${new}/d' -e '/bootx64/d') The default is to keep the previous kernel version in case something goes horribly wrong. +### `ekernel-commit` + +Commit the new kernel config with a detailed commit message. + +```sh +git add -f /usr/src/linux/.config +git commit -S -m "${msg}" +``` + +The message will not only contain the version change, but also details about the newly added or removed options. + ## Installation -TODO: add ebuild `app-admin/ekernel` +You may use the ebuild `app-admin/ekernel` from my [personal repository](https://github.com/phlo/phlo-portage). ## Requirements diff --git a/ekernel.py b/ekernel.py index 60e1d0d..d04c7e8 100644 --- a/ekernel.py +++ b/ekernel.py @@ -41,15 +41,12 @@ class Kernel: # kernel source symlink linux = src / "linux" - # EFI system partition - esp = pathlib.Path("/boot/EFI/Gentoo") - - # boot image - bootx64 = esp / "bootx64.efi" - # module directory modules = pathlib.Path("/lib/modules") + # EFI system partition + esp = pathlib.Path("/boot/EFI/Gentoo") + def __init__ (self, src): """Construct a Kernel based on a given source path.""" self.src = pathlib.Path(src) @@ -61,6 +58,7 @@ class Kernel: raise ValueError(f"illegal source: {src}") from e self.config = self.src / ".config" self.bzImage = self.src / "arch/x86_64/boot/bzImage" + self.bootx64 = self.esp / "bootx64.efi" self.efi = self.esp / f"gentoo-{self.version.base_version}.efi" self.modules = self.modules / f"{self.version.base_version}-gentoo" @@ -76,8 +74,8 @@ class Kernel: f"* src = {self.src}\n" f"* config = {self.config}\n" f"* bzImage = {self.bzImage}\n" - f"* efi = {self.efi}\n" f"* modules = {self.modules}\n" + f"* efi = {self.efi}\n" ) @classmethod @@ -211,9 +209,9 @@ def configure (argv): parser.add_argument( "-s", metavar="", - dest="kernel", - type=Kernel, - default=Kernel.latest(), + dest="src", + type=pathlib.Path, + default=Kernel.latest().src, help="kernel source directory (default: latest)" ) parser.add_argument( @@ -223,8 +221,9 @@ def configure (argv): help="be quiet" ) args = parser.parse_args(argv) + kernel = Kernel(args.src) out.quiet = args.quiet - newoptions = args.kernel.src / ".newoptions" + newoptions = kernel.src / ".newoptions" # check if current kernel config exists try: @@ -233,18 +232,18 @@ def configure (argv): oldconfig = Kernel.esp / "FILENOTFOUND" # change to source directory - os.chdir(args.kernel.src) + os.chdir(kernel.src) # delete config - reconfigure - if args.delete and args.kernel.config.exists(): - out.einfo(f"deleting {args.kernel.config}") - args.kernel.config.unlink() + if args.delete and kernel.config.exists(): + out.einfo(f"deleting {kernel.config}") + kernel.config.unlink() # make oldconfig - if not args.kernel.config.exists() and oldconfig.exists(): + if not kernel.config.exists() and oldconfig.exists(): # copy oldconfig out.einfo(f"copying {out.hilite(oldconfig)}") - shutil.copy(oldconfig, args.kernel.config) + shutil.copy(oldconfig, kernel.config) # store newly added options out.einfo(f"running {out.hilite('make listnewconfig')}") make = subprocess.run(["make", "listnewconfig"], capture_output=True) @@ -313,9 +312,9 @@ def build (argv): parser.add_argument( "-s", metavar="", - dest="kernel", - type=Kernel, - default=Kernel.latest(), + dest="src", + type=pathlib.Path, + default=Kernel.latest().src, help="kernel source directory (default: latest)" ) parser.add_argument( @@ -325,17 +324,18 @@ def build (argv): help="be quiet" ) args = parser.parse_args(argv) + kernel = Kernel(args.src) out.quiet = args.quiet # check if config exists - if not args.kernel.config.exists(): - raise FileNotFoundError(f"missing config: {args.kernel.config}") + if not kernel.config.exists(): + raise FileNotFoundError(f"missing config: {kernel.config}") # change directory - os.chdir(args.kernel.src) + os.chdir(kernel.src) # build and install modules - out.einfo(f"building {out.hilite(args.kernel.src.name)}") + out.einfo(f"building {out.hilite(kernel.src.name)}") subprocess.run(["make", "-j", str(args.jobs)], check=True) out.einfo("installing modules") subprocess.run(["make", "modules_install"], check=True) @@ -353,6 +353,9 @@ def install (argv): Command Line Arguments ---------------------- + ``-e `` + EFI bootloader directory (default: ``/boot/EFI/Gentoo``) + ``-s `` kernel source directory (default: latest) @@ -379,12 +382,19 @@ def install (argv): description="Install a kernel.", formatter_class=argparse.RawDescriptionHelpFormatter ) + parser.add_argument( + "-e", + metavar="", + dest="esp", + type=pathlib.Path, + help="EFI bootloader directory (default: /boot/EFI/Gentoo)" + ) parser.add_argument( "-s", metavar="", - dest="kernel", - type=Kernel, - default=Kernel.latest(), + dest="src", + type=pathlib.Path, + default=Kernel.latest().src, help="kernel source directory (default: latest)" ) parser.add_argument( @@ -394,29 +404,31 @@ def install (argv): help="be quiet" ) args = parser.parse_args(argv) + if args.esp: Kernel.esp = args.esp + kernel = Kernel(args.src) out.quiet = args.quiet # check if bzImage exists - if not args.kernel.bzImage.exists(): - raise FileNotFoundError(f"missing bzImage {args.kernel.bzImage}") + if not kernel.bzImage.exists(): + raise FileNotFoundError(f"missing bzImage {kernel.bzImage}") # update symlink to the new source directory out.einfo( "updating symlink " - f"{out.hilite(args.kernel.linux)} → {out.hilite(args.kernel.src)}" + f"{out.hilite(kernel.linux)} → {out.hilite(kernel.src)}" ) subprocess.run( - ["eselect", "kernel", "set", args.kernel.src.name], + ["eselect", "kernel", "set", kernel.src.name], check=True ) # copy boot image - out.einfo(f"creating boot image {out.hilite(args.kernel.bootx64)}") - shutil.copy(args.kernel.bzImage, args.kernel.bootx64) + out.einfo(f"creating boot image {out.hilite(kernel.bootx64)}") + shutil.copy(kernel.bzImage, kernel.bootx64) # create backup - out.einfo(f"creating backup image {out.hilite(args.kernel.efi)}") - shutil.copy(args.kernel.bzImage, args.kernel.efi) + out.einfo(f"creating backup image {out.hilite(kernel.efi)}") + shutil.copy(kernel.bzImage, kernel.efi) # rebuild external modules out.einfo(f"rebuilding external kernel modules") @@ -437,6 +449,9 @@ def clean (argv): Command Line Arguments ---------------------- + ``-e `` + EFI bootloader directory (default: ``/boot/EFI/Gentoo``) + ``-k `` keep the previous ```` kernels (default: 1) @@ -452,6 +467,14 @@ def clean (argv): description="Remove unused kernel leftovers.", formatter_class=argparse.RawDescriptionHelpFormatter ) + parser.add_argument( + "-e", + metavar="", + dest="esp", + type=pathlib.Path, + default=Kernel.esp, + help="EFI bootloader directory (default: /boot/EFI/Gentoo)" + ) parser.add_argument( "-k", metavar="", @@ -473,6 +496,7 @@ def clean (argv): help="be quiet" ) args = parser.parse_args(argv) + if args.esp: Kernel.esp = args.esp out.quiet = args.quiet if args.keep < 0: raise ValueError("invalid int value: must be greater equal zero") @@ -739,15 +763,20 @@ def update (argv): metavar="", dest="jobs", type=int, - default=int(jobs), help=f"number of parallel make jobs (default: {jobs})" ) + parser.add_argument( + "-e", + metavar="", + dest="esp", + type=pathlib.Path, + help="EFI bootloader directory (default: /boot/EFI/Gentoo)" + ) parser.add_argument( "-s", metavar="", - dest="kernel", - type=Kernel, - default=Kernel.latest(), + dest="src", + type=pathlib.Path, help="kernel source directory (default: latest)" ) parser.add_argument( @@ -755,7 +784,6 @@ def update (argv): metavar="", dest="keep", type=int, - default=1, help="keep the previous bootable kernels (default: 1)" ) parser.add_argument( @@ -763,7 +791,6 @@ def update (argv): metavar="", dest="msg", type=str, - default="", help="additional information for the commit message" ) parser.add_argument( @@ -773,14 +800,15 @@ def update (argv): help="be quiet" ) args = parser.parse_args(argv) - args.jobs = ["-j", str(args.jobs)] - args.src = ["-s", str(args.kernel.src)] - args.keep = ["-k", str(args.keep)] - args.msg = ["-m", args.msg] + args.jobs = ["-j", str(args.jobs)] if args.jobs else [] + args.esp = ["-e", str(args.esp)] if args.esp else [] + args.src = ["-s", str(args.src)] if args.src else [] + args.keep = ["-k", str(args.keep)] if args.keep is not None else [] + args.msg = ["-m", args.msg] if args.msg else [] args.quiet = ["-q"] if args.quiet else [] configure(args.quiet + args.src) build(args.quiet + args.jobs + args.src) - install(args.quiet + args.src) - clean(args.quiet + args.keep) + install(args.quiet + args.esp + args.src) + clean(args.quiet + args.esp + args.keep) commit(args.quiet + args.msg) diff --git a/tests/data/kernel.py b/tests/data/kernel.py index 622d549..a27c5d6 100644 --- a/tests/data/kernel.py +++ b/tests/data/kernel.py @@ -24,13 +24,6 @@ esp = root / "boot/EFI/Gentoo" # boot image bootx64 = esp / "bootx64.efi" -# change Kernel class' root directory -Kernel.src = src -Kernel.linux = linux -Kernel.modules = modules -Kernel.esp = esp -Kernel.bootx64 = bootx64 - # list of installed kernels kernels = [] sources = [ @@ -80,6 +73,13 @@ def setup (): for p in root.glob("*"): shutil.rmtree(p) + # change Kernel class' root directory + Kernel.src = src + Kernel.linux = linux + Kernel.modules = modules + Kernel.esp = esp + Kernel.bootx64 = bootx64 + # create EFI system partition esp.mkdir(parents=True) diff --git a/tests/test_clean.py b/tests/test_clean.py index d4bceb7..fd12899 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -93,6 +93,19 @@ class Tests (unittest.TestCase): self.assertEqual(run("-q", "-k", "10"), 0) self.check_clean(10) + def test_clean_esp (self): + esp = data.root / "boot/EFI/linux" + esp.mkdir(parents=True) + for k in data.kernels: + efi = esp / k.efi.name + if k.efi.exists(): + efi.touch() + k.efi.unlink() + k.efi = efi + data.esp.rmdir() + self.assertEqual(run("-q", "-e", str(esp)), 0) + self.check_clean() + @colorless @capture_stdout def test_clean_dry_run (self): diff --git a/tests/test_install.py b/tests/test_install.py index 2786440..6dea38c 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -24,6 +24,8 @@ class Tests (unittest.TestCase): self.interceptor.start() # setup test environment data.setup() + self.kernel = Kernel.latest() + self.kernel.bzImage.touch() def tearDown (self): # stop interceptor @@ -55,19 +57,25 @@ class Tests (unittest.TestCase): self.assertTrue(self.kernel.efi.exists()) def test_install (self): - self.kernel = Kernel.latest() - self.kernel.bzImage.touch() self.assertEqual(run("-q"), 0) self.check_install() - def test_install_version (self): + def test_install_esp (self): + esp = data.root / "boot/EFI/linux" + esp.mkdir(parents=True) + self.kernel.efi = esp / self.kernel.efi.name + self.kernel.bootx64 = esp / self.kernel.bootx64.name + self.assertEqual(run("-q", "-e", str(esp)), 0) + self.check_install() + + def test_install_source (self): self.kernel = Kernel.current() self.assertEqual(run("-q", "-s", str(data.current)), 0) self.check_install() @capture_stderr def test_install_missing_bzImage (self): - self.kernel = Kernel.latest() + self.kernel.bzImage.unlink() with self.assertRaises(SystemExit): self.assertEqual(run("-s", str(data.latest)), 1) self.assertRegex(sys.stderr.getvalue(), r"missing.*bzImage") diff --git a/tests/test_update.py b/tests/test_update.py index 9c39876..f438549 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -81,6 +81,21 @@ class Tests (unittest.TestCase): self.assertEqual(run("-q", "-j", "8"), 0) self.check_update() + def test_update_esp (self): + esp = data.root / "boot/EFI/linux" + esp.mkdir(parents=True) + for k in data.kernels: + efi = esp / k.efi.name + if k.efi.exists(): + efi.touch() + k.efi.unlink() + k.efi = efi + data.esp.rmdir() + self.latest.efi = esp / self.latest.efi.name + self.latest.bootx64 = esp / self.latest.bootx64.name + self.assertEqual(run("-q", "-e", str(esp)), 0) + self.check_update() + def test_update_source (self): self.assertEqual(run("-q", "-s", str(data.latest)), 0) self.check_update() @@ -90,6 +105,8 @@ class Tests (unittest.TestCase): self.assertEqual(run("-q", "-k", "0"), 0) self.check_update() self.assertFalse(current.src.exists()) + self.assertFalse(current.modules.exists()) + self.assertFalse(current.efi.exists()) @capture_stdout def test_update_message (self):