from textwrap import dedent
from typing import Callable, ContextManager, Optional, TextIO, TypeVar, Union, cast
-from mkosi.config import GenericVersion, MkosiArgs, MkosiConfig, MkosiConfigParser
+from mkosi.config import (
+ ConfigFeature,
+ GenericVersion,
+ MkosiArgs,
+ MkosiConfig,
+ MkosiConfigParser,
+)
from mkosi.install import add_dropin_config_from_resource, copy_path, flock
from mkosi.log import Style, color_error, complete_step, die, log_step
from mkosi.manifest import Manifest
package manager is present in the image.
"""
- assert state.config.clean_package_metadata in (False, True, None)
- if state.config.clean_package_metadata is False:
+ if state.config.clean_package_metadata == ConfigFeature.disabled:
return
# we try then all: metadata will only be touched if any of them are in the
# final image
- always = state.config.clean_package_metadata is True
+ always = state.config.clean_package_metadata == ConfigFeature.enabled
clean_dnf_metadata(state.root, always=always)
clean_yum_metadata(state.root, always=always)
clean_rpm_metadata(state.root, always=always)
def install_boot_loader(state: MkosiState) -> None:
- if state.config.bootable is False:
+ if state.config.bootable == ConfigFeature.disabled:
return
- if state.config.output_format == OutputFormat.cpio and state.config.bootable is None:
+ if state.config.output_format == OutputFormat.cpio and state.config.bootable == ConfigFeature.auto:
return
directory = state.root / "usr/lib/systemd/boot/efi"
if not directory.exists() or not any(directory.iterdir()):
- if state.config.bootable is True:
+ if state.config.bootable == ConfigFeature.enabled:
die("A bootable image was requested but systemd-boot was not found at "
f"{directory.relative_to(state.root)}")
return
# benefit that they can be signed like normal EFI binaries, and can encode everything necessary to boot a
# specific root device, including the root hash.
- if state.config.bootable is False:
+ if state.config.bootable == ConfigFeature.disabled:
return
for kver, kimg in gen_kernel_images(state):
copy_path(state.root / kimg, state.staging / state.config.output_split_kernel)
break
- if state.config.output_format == OutputFormat.cpio and state.config.bootable is None:
+ if state.config.output_format == OutputFormat.cpio and state.config.bootable == ConfigFeature.auto:
return
initrds = []
if not state.staging.joinpath(state.config.output_split_uki).exists():
copy_path(boot_binary, state.staging / state.config.output_split_uki)
- if state.config.bootable is True and not state.staging.joinpath(state.config.output_split_uki).exists():
+ if state.config.bootable == ConfigFeature.enabled and not state.staging.joinpath(state.config.output_split_uki).exists():
die("A bootable image was requested but no kernel was found")
die(f"Output path {f} exists already. (Consider invocation with --force.)")
-def yes_no(b: Optional[bool]) -> str:
+def yes_no(b: bool) -> str:
return "yes" if b else "no"
-def yes_no_auto(b: Optional[bool]) -> str:
- return "auto" if b is None else yes_no(b)
+def yes_no_auto(f: ConfigFeature) -> str:
+ return "auto" if f is ConfigFeature.auto else yes_no(f == ConfigFeature.enabled)
def none_to_na(s: Optional[T]) -> Union[T, str]:
def run_depmod(state: MkosiState) -> None:
- if state.config.bootable is False:
+ if state.config.bootable == ConfigFeature.disabled:
return
for kver, _ in gen_kernel_images(state):
bootdir = state.root.joinpath("boot/EFI/BOOT")
# If Bootable=auto and we have at least one UKI and a bootloader, let's generate an ESP partition.
- add = (state.config.bootable is True or
- (state.config.bootable is None and
+ add = (state.config.bootable == ConfigFeature.enabled or
+ (state.config.bootable == ConfigFeature.auto and
bootdir.exists() and
any(bootdir.iterdir()) and
any(gen_kernel_images(state))))
ConfigDefaultCallback = Callable[[argparse.Namespace], Any]
+class ConfigFeature(enum.Enum):
+ auto = "auto"
+ enabled = "enabled"
+ disabled = "disabled"
+
+ def __str__(self) -> str:
+ return str(self.value).lower()
+
+
def parse_boolean(s: str) -> bool:
"Parse 1/true/yes/y/t/on as true and 0/false/no/n/f/off/None as false"
s_l = s.lower()
return cast(bool, getattr(namespace, dest) == parse_boolean(value))
-def config_parse_feature(dest: str, value: Optional[str], namespace: argparse.Namespace) -> Optional[bool]:
+def config_parse_feature(dest: str, value: Optional[str], namespace: argparse.Namespace) -> ConfigFeature:
if dest in namespace:
return getattr(namespace, dest) # type: ignore
- if value and value == "auto":
- return None
+ if not value or value == ConfigFeature.auto.value:
+ return ConfigFeature.auto
- return parse_boolean(value) if value else None
+ return ConfigFeature.enabled if parse_boolean(value) else ConfigFeature.disabled
def config_parse_compression(dest: str, value: Optional[str], namespace: argparse.Namespace) -> Optional[Compression]:
base_trees: list[Path]
extra_trees: list[tuple[Path, Optional[Path]]]
skeleton_trees: list[tuple[Path, Optional[Path]]]
- clean_package_metadata: Optional[bool]
+ clean_package_metadata: ConfigFeature
remove_files: list[str]
environment: dict[str, str]
build_sources: Path
make_initrd: bool
kernel_command_line_extra: list[str]
acl: bool
- bootable: Optional[bool]
+ bootable: ConfigFeature
# QEMU-specific options
qemu_gui: bool
):
die("--repo-dir is only supported on DNF/Debian based distributions and Arch")
- if args.qemu_kvm is True and not qemu_check_kvm_support():
+ if args.qemu_kvm == ConfigFeature.enabled and not qemu_check_kvm_support():
die("Sorry, the host machine does not support KVM acceleration.")
- if args.qemu_kvm is None:
+ if args.qemu_kvm == ConfigFeature.auto:
args.qemu_kvm = qemu_check_kvm_support()
+ else:
+ args.qemu_kvm = args.qemu_kvm == ConfigFeature.enabled
if args.repositories and not (is_dnf_distribution(args.distribution) or is_apt_distribution(args.distribution)):
die("Sorry, the --repositories option is only supported on DNF/Debian based distributions")