]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Rework architecture handling
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 29 May 2023 20:41:31 +0000 (22:41 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 30 May 2023 10:17:30 +0000 (12:17 +0200)
Let's lock architectures down by making it an enum instead of a
free form string. We also introduce a bunch of mapping functions
to map the Architecture enum to qemu, distribution arches, efi
arches. We only support the architectures defined in the discoverable
partitions spec and use those architectures as the default representation.

16 files changed:
mkosi.md
mkosi/__init__.py
mkosi/architecture.py [new file with mode: 0644]
mkosi/config.py
mkosi/distributions/__init__.py
mkosi/distributions/arch.py
mkosi/distributions/centos.py
mkosi/distributions/debian.py
mkosi/distributions/fedora.py
mkosi/distributions/gentoo.py
mkosi/distributions/mageia.py
mkosi/distributions/openmandriva.py
mkosi/distributions/opensuse.py
mkosi/distributions/ubuntu.py
mkosi/manifest.py
mkosi/qemu.py

index 3aa70c2a551926b747b984615cde6388bee27575..de4048dbbd46e21ae562effd4558063f92c04350 100644 (file)
--- a/mkosi.md
+++ b/mkosi.md
@@ -402,10 +402,33 @@ a boolean argument: either "1", "yes", or "true" to enable, or "0",
 
 `Architecture=`, `--architecture=`
 
-: The architecture to build the image for. Note that this currently
-  only works for architectures compatible with the host's
+: The architecture to build the image for. A number of architectures can be specified, but which ones are
+  actually supported depends on the distribution used and whether a bootable image is requested or not. When
+  building for a foreign architecture, you'll also need to install and register a user mode emulator for that
   architecture.
 
+  The following architectures can be specified:
+
+  - alpha
+  - arc
+  - arm
+  - arm64
+  - ia64
+  - loongarch64
+  - mips64-le
+  - mips-le
+  - parisc
+  - ppc
+  - ppc64
+  - ppc64-le
+  - riscv32
+  - riscv64
+  - s390
+  - s390x
+  - tilegx
+  - x86
+  - x86-64
+
 ### [Output] Section
 
 `Format=`, `--format=`, `-t`
index e3a7a6e198321f3f5f0e7f5546a70152ff20bb5d..17b3f38e2cbb6bd5c2d8ba7e619dbcf87d6f3480 100644 (file)
@@ -59,16 +59,6 @@ MKOSI_COMMANDS_SUDO = (Verb.shell, Verb.boot)
 T = TypeVar("T")
 
 
-# EFI has its own conventions too
-EFI_ARCHITECTURES = {
-    "x86_64": "x64",
-    "x86": "ia32",
-    "aarch64": "aa64",
-    "armhfp": "arm",
-    "riscv64:": "riscv64",
-}
-
-
 def format_bytes(num_bytes: int) -> str:
     if num_bytes >= 1024 * 1024 * 1024:
         return f"{num_bytes/1024**3 :0.1f}G"
@@ -833,7 +823,7 @@ def install_unified_kernel(state: MkosiState, roothash: Optional[str]) -> None:
                 "--directory", "",
                 "--distribution", str(state.config.distribution),
                 "--release", state.config.release,
-                "--architecture", state.config.architecture,
+                "--architecture", str(state.config.architecture),
                 *(["--mirror", state.config.mirror] if state.config.mirror else []),
                 "--repository-key-check", yes_no(state.config.repository_key_check),
                 "--repositories", ",".join(state.config.repositories),
@@ -906,7 +896,7 @@ def install_unified_kernel(state: MkosiState, roothash: Optional[str]) -> None:
             # nul terminators in argv so let's communicate the cmdline via a file instead.
             state.workspace.joinpath("cmdline").write_text(f"{' '.join(cmdline).strip()}\x00")
 
-            stub = state.root / f"usr/lib/systemd/boot/efi/linux{EFI_ARCHITECTURES[state.config.architecture]}.efi.stub"
+            stub = state.root / f"usr/lib/systemd/boot/efi/linux{state.config.architecture.to_efi()}.efi.stub"
             if not stub.exists():
                 die(f"sd-stub not found at /{stub.relative_to(state.root)} in the image")
 
@@ -916,7 +906,7 @@ def install_unified_kernel(state: MkosiState, roothash: Optional[str]) -> None:
                 "--os-release", f"@{state.root / 'usr/lib/os-release'}",
                 "--stub", stub,
                 "--output", boot_binary,
-                "--efi-arch", EFI_ARCHITECTURES[state.config.architecture],
+                "--efi-arch", state.config.architecture.to_efi(),
             ]
 
             for p in state.config.extra_search_paths:
@@ -1652,7 +1642,7 @@ def invoke_repart(state: MkosiState, skip: Sequence[str] = [], split: bool = Fal
                 dedent(
                     f"""\
                     [Partition]
-                    Type=root
+                    Type=root-{state.config.architecture}
                     Format={state.installer.filesystem()}
                     CopyFiles=/
                     Minimize=guess
diff --git a/mkosi/architecture.py b/mkosi/architecture.py
new file mode 100644 (file)
index 0000000..00a577f
--- /dev/null
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+import enum
+import platform
+
+from mkosi.log import die
+
+
+class Architecture(enum.Enum):
+    alpha       = "alpha"
+    arc         = "arc"
+    arm         = "arm"
+    arm64       = "arm64"
+    ia64        = "ia64"
+    loongarch64 = "loongarch64"
+    mips_le     = "mips-le"
+    mips64_le   = "mips64-le"
+    parisc      = "parisc"
+    ppc         = "ppc"
+    ppc64       = "ppc64"
+    ppc64_le    = "ppc64-le"
+    riscv32     = "riscv32"
+    riscv64     = "riscv64"
+    s390        = "s390"
+    s390x       = "s390x"
+    tilegx      = "tilegx"
+    x86         = "x86"
+    x86_64      = "x86-64"
+
+    def __str__(self) -> str:
+        return self.value
+
+    @staticmethod
+    def from_uname(s: str) -> "Architecture":
+        a = {
+            "aarch64"     : Architecture.arm64,
+            "aarch64_be"  : Architecture.arm64,
+            "armv8l"      : Architecture.arm,
+            "armv8b"      : Architecture.arm,
+            "armv7ml"     : Architecture.arm,
+            "armv7mb"     : Architecture.arm,
+            "armv7l"      : Architecture.arm,
+            "armv7b"      : Architecture.arm,
+            "armv6l"      : Architecture.arm,
+            "armv6b"      : Architecture.arm,
+            "armv5tl"     : Architecture.arm,
+            "armv5tel"    : Architecture.arm,
+            "armv5tejl"   : Architecture.arm,
+            "armv5tejb"   : Architecture.arm,
+            "armv5teb"    : Architecture.arm,
+            "armv5tb"     : Architecture.arm,
+            "armv4tl"     : Architecture.arm,
+            "armv4tb"     : Architecture.arm,
+            "armv4l"      : Architecture.arm,
+            "armv4b"      : Architecture.arm,
+            "alpha"       : Architecture.alpha,
+            "arc"         : Architecture.arc,
+            "arceb"       : Architecture.arc,
+            "x86_64"      : Architecture.x86_64,
+            "i686"        : Architecture.x86,
+            "i586"        : Architecture.x86,
+            "i486"        : Architecture.x86,
+            "i386"        : Architecture.x86,
+            "ia64"        : Architecture.ia64,
+            "parisc64"    : Architecture.parisc,
+            "parisc"      : Architecture.parisc,
+            "loongarch64" : Architecture.loongarch64,
+            "mips64"      : Architecture.mips64_le,
+            "mips"        : Architecture.mips_le,
+            "ppc64le"     : Architecture.ppc64_le,
+            "ppc64"       : Architecture.ppc64,
+            "ppc"         : Architecture.ppc,
+            "riscv64"     : Architecture.riscv64,
+            "riscv32"     : Architecture.riscv32,
+            "riscv"       : Architecture.riscv64,
+            "s390x"       : Architecture.s390x,
+            "s390"        : Architecture.s390,
+            "tilegx"      : Architecture.tilegx,
+        }.get(s)
+
+        if not a:
+            die(f"Architecture {a} is not supported")
+
+        return a
+
+    def to_efi(self) -> str:
+        a = {
+            Architecture.x86_64      : "x64",
+            Architecture.x86         : "ia32",
+            Architecture.arm64       : "aa64",
+            Architecture.arm         : "arm",
+            Architecture.riscv64     : "riscv64",
+            Architecture.loongarch64 : "loongarch64",
+        }.get(self)
+
+        if not a:
+            die(f"Architecture {self} does not support UEFI")
+
+        return a
+
+    def to_qemu(self) -> str:
+        a = {
+            Architecture.alpha: "alpha",
+            Architecture.arm: "arm",
+            Architecture.arm64: "aarch64",
+            Architecture.loongarch64: "loongarch64",
+            Architecture.mips64_le: "mips",
+            Architecture.mips_le: "mips",
+            Architecture.parisc: "hppa",
+            Architecture.ppc: "ppc",
+            Architecture.ppc64: "ppc",
+            Architecture.ppc64_le: "ppc",
+            Architecture.riscv32: "riscv32",
+            Architecture.riscv64: "riscv64",
+            Architecture.s390x: "s390x",
+            Architecture.x86: "i386",
+            Architecture.x86_64: "x86_64",
+        }.get(self)
+
+        if not a:
+            die(f"Architecture {self} not supported by QEMU")
+
+        return a
+
+    def is_native(self) -> bool:
+        return self == self.native()
+
+    @classmethod
+    def native(cls) -> "Architecture":
+        return cls.from_uname(platform.machine())
+
index 16f6914a7fbaacbf11986ea4d1ef3536b716f44c..75cd4e3a0ca0085fa8e047f31cdca52ae28e16e9 100644 (file)
@@ -20,6 +20,7 @@ from collections.abc import Sequence
 from pathlib import Path
 from typing import Any, Callable, Optional, Type, Union, cast
 
+from mkosi.architecture import Architecture
 from mkosi.log import ARG_DEBUG, ARG_DEBUG_SHELL, Style, die
 from mkosi.pager import page
 from mkosi.run import run
@@ -218,7 +219,7 @@ def config_default_mirror(namespace: argparse.Namespace) -> Optional[str]:
     if "distribution" not in namespace:
         setattr(namespace, "distribution", detect_distribution()[0])
     if "architecture" not in namespace:
-        setattr(namespace, "architecture", platform.machine())
+        setattr(namespace, "architecture", Architecture.native())
 
     d = getattr(namespace, "distribution")
     a = getattr(namespace, "architecture")
@@ -226,12 +227,12 @@ def config_default_mirror(namespace: argparse.Namespace) -> Optional[str]:
     if d == Distribution.debian:
         return "http://deb.debian.org/debian"
     elif d == Distribution.ubuntu:
-        if a == "x86" or a == "x86_64":
+        if a == Architecture.x86 or a == Architecture.x86_64:
             return "http://archive.ubuntu.com/ubuntu"
         else:
             return "http://ports.ubuntu.com"
     elif d == Distribution.arch:
-        if a == "aarch64":
+        if a == Architecture.arm64:
             return "http://mirror.archlinuxarm.org"
         else:
             return "https://geo.mirror.pkgbuild.com"
@@ -244,9 +245,9 @@ def config_default_mirror(namespace: argparse.Namespace) -> Optional[str]:
 def make_enum_parser(type: Type[enum.Enum]) -> Callable[[str], enum.Enum]:
     def parse_enum(value: str) -> enum.Enum:
         try:
-            return type[value]
-        except KeyError:
-            die(f"Invalid {type.__name__} value \"{value}\"")
+            return type(value)
+        except ValueError:
+            die(f"'{value}' is not a valid {type.__name__}")
 
     return parse_enum
 
@@ -594,7 +595,7 @@ class MkosiConfig:
     repo_dirs: list[Path]
     repart_dirs: list[Path]
     overlay: bool
-    architecture: str
+    architecture: Architecture
     output_format: OutputFormat
     manifest_format: list[ManifestFormat]
     output: str
@@ -682,9 +683,6 @@ class MkosiConfig:
             if k in inspect.signature(cls).parameters
         })
 
-    def architecture_is_native(self) -> bool:
-        return self.architecture == platform.machine()
-
     @property
     def output_with_version(self) -> str:
         output = self.output
@@ -775,7 +773,8 @@ class MkosiConfigParser:
         MkosiConfigSetting(
             dest="architecture",
             section="Distribution",
-            default=platform.machine(),
+            parse=config_make_enum_parser(Architecture),
+            default=Architecture.native(),
         ),
         MkosiConfigSetting(
             dest="mirror",
index bdc1b656816e5537e5625fc6288d75bcee221097..51f51b3566ae0f7f7329d6e9a1f20f9f8d58a9c1 100644 (file)
@@ -4,6 +4,8 @@ from collections.abc import Sequence
 from pathlib import Path
 from typing import TYPE_CHECKING
 
+from mkosi.architecture import Architecture
+
 if TYPE_CHECKING:
     from mkosi.state import MkosiState
 
@@ -11,23 +13,23 @@ if TYPE_CHECKING:
 class DistributionInstaller:
     @classmethod
     def install(cls, state: "MkosiState") -> None:
-        raise NotImplementedError
+        raise NotImplementedError()
 
     @staticmethod
-    def kernel_image(kver: str, architecture: str) -> Path:
+    def kernel_image(kver: str, architecture: Architecture) -> Path:
         return Path("usr/lib/modules") / kver / "vmlinuz"
 
     @classmethod
     def install_packages(cls, state: "MkosiState", packages: Sequence[str]) -> None:
-        raise NotImplementedError
+        raise NotImplementedError()
 
     @classmethod
     def remove_packages(cls, state: "MkosiState", packages: Sequence[str]) -> None:
-        raise NotImplementedError
+        raise NotImplementedError()
 
     @classmethod
     def filesystem(cls) -> str:
-        raise NotImplementedError
+        raise NotImplementedError()
 
     @classmethod
     def filesystem_options(cls, state: "MkosiState") -> dict[str, list[str]]:
@@ -36,3 +38,7 @@ class DistributionInstaller:
     @staticmethod
     def kernel_command_line(state: "MkosiState") -> list[str]:
         return []
+
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        raise NotImplementedError()
index 1dbd6772c8ca39ccbd67d4c4781c51ef94171ee9..b22fd04a7176d3a6b97c886a022f6cbec3f4dbf2 100644 (file)
@@ -3,8 +3,10 @@
 from collections.abc import Sequence
 from textwrap import dedent
 
+from mkosi.architecture import Architecture
 from mkosi.config import ConfigFeature
 from mkosi.distributions import DistributionInstaller
+from mkosi.log import die
 from mkosi.run import bwrap
 from mkosi.state import MkosiState
 from mkosi.types import PathString
@@ -27,7 +29,7 @@ class ArchInstaller(DistributionInstaller):
         if state.config.local_mirror:
             server = f"Server = {state.config.local_mirror}"
         else:
-            if state.config.architecture == "aarch64":
+            if state.config.architecture == Architecture.arm64:
                 server = f"Server = {state.config.mirror}/$arch/$repo"
             else:
                 server = f"Server = {state.config.mirror}/$repo/os/$arch"
@@ -54,7 +56,7 @@ class ArchInstaller(DistributionInstaller):
                     GPGDir = /etc/pacman.d/gnupg/
                     HookDir = {state.root}/etc/pacman.d/hooks/
                     HoldPkg = pacman glibc
-                    Architecture = {state.config.architecture}
+                    Architecture = {state.installer.architecture(state.config.architecture)}
                     Color
                     CheckSpace
                     SigLevel = {sig_level}
@@ -85,6 +87,18 @@ class ArchInstaller(DistributionInstaller):
 
         return invoke_pacman(state, packages, apivfs=apivfs)
 
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.x86_64 : "x86_64",
+            Architecture.arm64  : "aarch64",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by Arch Linux")
+
+        return a
+
 
 def invoke_pacman(state: MkosiState, packages: Sequence[str], apivfs: bool = True) -> None:
     cmdline: list[PathString] = [
index c9b3e6a3e322ccf215a6e204ffcd8588d48b14ad..b2046a4fb89b747f19a79156e713d05b2263d2d5 100644 (file)
@@ -5,6 +5,7 @@ import shutil
 from collections.abc import Sequence
 from pathlib import Path
 
+from mkosi.architecture import Architecture
 from mkosi.config import MkosiConfig
 from mkosi.distributions import DistributionInstaller
 from mkosi.distributions.fedora import Repo, invoke_dnf, setup_dnf
@@ -109,6 +110,20 @@ class CentosInstaller(DistributionInstaller):
     def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_dnf(state, "remove", packages)
 
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.x86_64   : "x86_64",
+            Architecture.ppc64_le : "ppc64le",
+            Architecture.s390x    : "s390x",
+            Architecture.arm64    : "aarch64",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by CentOS")
+
+        return a
+
     @staticmethod
     def _gpgurl(release: int) -> str:
         return "https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official"
index db2262016d3525f9a95b6db74f3f7ddd2471f968..5ee962a26d31555e269a0aa098b3c2243aa3db93 100644 (file)
@@ -6,7 +6,9 @@ from collections.abc import Sequence
 from pathlib import Path
 from textwrap import dedent
 
+from mkosi.architecture import Architecture
 from mkosi.distributions import DistributionInstaller
+from mkosi.log import die
 from mkosi.run import bwrap, run
 from mkosi.state import MkosiState
 from mkosi.types import CompletedProcess, PathString
@@ -18,7 +20,7 @@ class DebianInstaller(DistributionInstaller):
         return "ext4"
 
     @staticmethod
-    def kernel_image(name: str, architecture: str) -> Path:
+    def kernel_image(name: str, architecture: Architecture) -> Path:
         return Path(f"boot/vmlinuz-{name}")
 
     @staticmethod
@@ -68,7 +70,7 @@ class DebianInstaller(DistributionInstaller):
             "sparc"       : ["lib64"],
             "sparc64"     : ["lib32", "lib64"],
             "x32"         : ["lib32", "lib64", "libx32"],
-        }.get(DEBIAN_ARCHITECTURES[state.config.architecture], [])
+        }.get(state.installer.architecture(state.config.architecture), [])
 
         state.root.joinpath("usr").mkdir(mode=0o755, exist_ok=True)
         for d in subdirs:
@@ -130,24 +132,30 @@ class DebianInstaller(DistributionInstaller):
     def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_apt(state, "purge", packages)
 
-
-# Debian calls their architectures differently, so when calling apt we will have to map to their names.
-# uname -m -> dpkg --print-architecture
-DEBIAN_ARCHITECTURES = {
-    "aarch64": "arm64",
-    "armhfp": "armhf",
-    "armv7l": "armhf",
-    "ia64": "ia64",
-    "mips64": "mipsel",
-    "m68k": "m68k",
-    "parisc64": "hppa",
-    "ppc64": "ppc64",
-    "ppc64le": "ppc64el",
-    "riscv64:": "riscv64",
-    "s390x": "s390x",
-    "x86": "i386",
-    "x86_64": "amd64",
-}
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.arm64       : "arm64",
+            Architecture.arm         : "armhf",
+            Architecture.alpha       : "alpha",
+            Architecture.x86_64      : "amd64",
+            Architecture.x86         : "i386",
+            Architecture.ia64        : "ia64",
+            Architecture.loongarch64 : "loongarch64",
+            Architecture.mips64_le   : "mips64el",
+            Architecture.mips_le     : "mipsel",
+            Architecture.parisc      : "hppa",
+            Architecture.ppc64_le    : "ppc64el",
+            Architecture.ppc64       : "ppc64",
+            Architecture.riscv64     : "riscv64",
+            Architecture.s390x       : "s390x",
+            Architecture.s390        : "s390",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {arch} is not supported by Debian")
+
+        return a
 
 
 def setup_apt(state: MkosiState, repos: Sequence[str]) -> None:
@@ -164,7 +172,7 @@ def setup_apt(state: MkosiState, repos: Sequence[str]) -> None:
     state.root.joinpath("var/lib/dpkg/status").touch()
 
     config = state.workspace / "apt/apt.conf"
-    debarch = DEBIAN_ARCHITECTURES[state.config.architecture]
+    debarch = state.installer.architecture(state.config.architecture)
 
     config.write_text(
         dedent(
index 821b460f45efbb16d491cf5b1211f081e60e71ac..db42e647fa859ad070e8ee1fc14ceac8f24edcdd 100644 (file)
@@ -10,7 +10,9 @@ from pathlib import Path
 from textwrap import dedent
 from typing import Any, NamedTuple
 
+from mkosi.architecture import Architecture
 from mkosi.distributions import DistributionInstaller
+from mkosi.log import die
 from mkosi.remove import unlink_try_hard
 from mkosi.run import bwrap
 from mkosi.state import MkosiState
@@ -35,7 +37,7 @@ class FedoraInstaller(DistributionInstaller):
             updates_url = None
         elif state.config.mirror:
             baseurl = urllib.parse.urljoin(state.config.mirror, f"releases/{release}/Everything/$basearch/os/")
-            media = urllib.parse.urljoin(baseurl.replace("$basearch", state.config.architecture), "media.repo")
+            media = urllib.parse.urljoin(baseurl.replace("$basearch", state.installer.architecture(state.config.architecture)), "media.repo")
             if not url_exists(media):
                 baseurl = urllib.parse.urljoin(state.config.mirror, f"development/{release}/Everything/$basearch/os/")
 
@@ -66,6 +68,25 @@ class FedoraInstaller(DistributionInstaller):
     def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_dnf(state, "remove", packages)
 
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.arm64     : "aarch64",
+            Architecture.ia64      : "ia64",
+            Architecture.mips64_le : "mips64el",
+            Architecture.mips_le   : "mipsel",
+            Architecture.parisc    : "parisc64",
+            Architecture.ppc64_le  : "ppc64le",
+            Architecture.riscv64   : "riscv64",
+            Architecture.s390x     : "s390x",
+            Architecture.x86_64    : "x86_64",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by Fedora")
+
+        return a
+
 
 def parse_fedora_release(release: str) -> str:
     # The release can be specified as 'rawhide-<version>'. We don't make use
@@ -178,8 +199,8 @@ def invoke_dnf(
     if state.config.cache_only and not state.config.local_mirror:
         cmdline += ["-C"]
 
-    if not state.config.architecture_is_native():
-        cmdline += [f"--forcearch={state.config.architecture}"]
+    if not state.config.architecture.is_native():
+        cmdline += [f"--forcearch={state.installer.architecture(state.config.architecture)}"]
 
     if not state.config.with_docs:
         cmdline += ["--no-docs" if dnf.endswith("dnf5") else "--nodocs"]
index 7756a6a8921fadf2cb169375fac002459af1ae31..b455119ceba7d2fb7686ff1682b0d0da29673530 100644 (file)
@@ -9,6 +9,7 @@ from collections.abc import Sequence
 from pathlib import Path
 from textwrap import dedent
 
+from mkosi.architecture import Architecture
 from mkosi.distributions import DistributionInstaller
 from mkosi.install import copy_path, flock
 from mkosi.log import ARG_DEBUG, complete_step, die, log_step
@@ -16,14 +17,6 @@ from mkosi.remove import unlink_try_hard
 from mkosi.run import run, run_workspace_command
 from mkosi.state import MkosiState
 
-ARCHITECTURES = {
-    "x86_64": ("amd64", "arch/x86/boot/bzImage"),
-    # TODO:
-    "aarch64": ("arm64", "arch/arm64/boot/Image.gz"),
-    # TODO:
-    "armv7l": ("arm", "arch/arm/boot/zImage"),
-}
-
 
 def invoke_emerge(
     state: MkosiState,
@@ -172,7 +165,7 @@ class Gentoo:
 
         self.portage_cfg_dir.mkdir(parents=True, exist_ok=True)
 
-        self.arch, _ = ARCHITECTURES[state.config.architecture or "x86_64"]
+        self.arch, _ = state.installer.architecture(state.config.architecture)
         self.arch_profile = Path(f"default/linux/{self.arch}/{state.config.release}/no-multilib/systemd/merged-usr")
         self.pkgs['sys'] = ["@world"]
 
@@ -321,8 +314,13 @@ class GentooInstaller(DistributionInstaller):
         return "ext4"
 
     @staticmethod
-    def kernel_image(name: str, architecture: str) -> Path:
-        _, kimg_path = ARCHITECTURES[architecture]
+    def kernel_image(name: str, architecture: Architecture) -> Path:
+        kimg_path = {
+            Architecture.x86_64: "arch/x86/boot/bzImage",
+            Architecture.arm64: "arch/arm64/boot/Image.gz",
+            Architecture.arm: "arch/arm/boot/zImage",
+        }[architecture]
+
         return Path(f"usr/src/linux-{name}") / kimg_path
 
     @classmethod
@@ -332,3 +330,16 @@ class GentooInstaller(DistributionInstaller):
     @classmethod
     def install_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_emerge(state, packages)
+
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.x86_64 : "amd64",
+            Architecture.arm64  : "arm64",
+            Architecture.arm    : "arm",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by Gentoo")
+
+        return a
index 73432bdb92d564c2172d724dfa54f935e538565f..54ca55d8c62454a8257e54b537ca6c04a9b75e37 100644 (file)
@@ -2,8 +2,10 @@
 
 from collections.abc import Sequence
 
+from mkosi.architecture import Architecture
 from mkosi.distributions import DistributionInstaller
 from mkosi.distributions.fedora import Repo, invoke_dnf, setup_dnf
+from mkosi.log import die
 from mkosi.state import MkosiState
 
 
@@ -19,26 +21,27 @@ class MageiaInstaller(DistributionInstaller):
     @classmethod
     def install_packages(cls, state: MkosiState, packages: Sequence[str], apivfs: bool = True) -> None:
         release = state.config.release.strip("'")
+        arch = state.installer.architecture(state.config.architecture)
 
         if state.config.local_mirror:
             release_url = f"baseurl={state.config.local_mirror}"
             updates_url = None
         elif state.config.mirror:
-            baseurl = f"{state.config.mirror}/distrib/{release}/{state.config.architecture}/media/core/"
+            baseurl = f"{state.config.mirror}/distrib/{release}/{arch}/media/core/"
             release_url = f"baseurl={baseurl}/release/"
             if release == "cauldron":
                 updates_url = None
             else:
                 updates_url = f"baseurl={baseurl}/updates/"
         else:
-            baseurl = f"https://www.mageia.org/mirrorlist/?release={release}&arch={state.config.architecture}&section=core"
+            baseurl = f"https://www.mageia.org/mirrorlist/?release={release}&arch={arch}&section=core"
             release_url = f"mirrorlist={baseurl}&repo=release"
             if release == "cauldron":
                 updates_url = None
             else:
                 updates_url = f"mirrorlist={baseurl}&repo=updates"
 
-        gpgurl = f"https://mirrors.kernel.org/mageia/distrib/{release}/{state.config.architecture}/media/core/release/media_info/pubkey"
+        gpgurl = f"https://mirrors.kernel.org/mageia/distrib/{release}/{arch}/media/core/release/media_info/pubkey"
 
         repos = [Repo(f"mageia-{release}", release_url, [gpgurl])]
         if updates_url is not None:
@@ -50,3 +53,14 @@ class MageiaInstaller(DistributionInstaller):
     @classmethod
     def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_dnf(state, "remove", packages)
+
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.x86_64 : "x86_64",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by Mageia")
+
+        return a
index dd7f9f42c986b075abafd2f064f74bc63316cbfb..a0bfbe02d6604189e2a8167854cbe16c3f3e0422 100644 (file)
@@ -2,8 +2,10 @@
 
 from collections.abc import Sequence
 
+from mkosi.architecture import Architecture
 from mkosi.distributions import DistributionInstaller
 from mkosi.distributions.fedora import Repo, invoke_dnf, setup_dnf
+from mkosi.log import die
 from mkosi.state import MkosiState
 
 
@@ -19,6 +21,7 @@ class OpenmandrivaInstaller(DistributionInstaller):
     @classmethod
     def install_packages(cls, state: MkosiState, packages: Sequence[str], apivfs: bool = True) -> None:
         release = state.config.release.strip("'")
+        arch = state.installer.architecture(state.config.architecture)
 
         if release[0].isdigit():
             release_model = "rock"
@@ -31,11 +34,11 @@ class OpenmandrivaInstaller(DistributionInstaller):
             release_url = f"baseurl={state.config.local_mirror}"
             updates_url = None
         elif state.config.mirror:
-            baseurl = f"{state.config.mirror}/{release_model}/repository/{state.config.architecture}/main"
+            baseurl = f"{state.config.mirror}/{release_model}/repository/{arch}/main"
             release_url = f"baseurl={baseurl}/release/"
             updates_url = f"baseurl={baseurl}/updates/"
         else:
-            baseurl = f"http://mirrors.openmandriva.org/mirrors.php?platform={release_model}&arch={state.config.architecture}&repo=main"
+            baseurl = f"http://mirrors.openmandriva.org/mirrors.php?platform={release_model}&arch={arch}&repo=main"
             release_url = f"mirrorlist={baseurl}&release=release"
             updates_url = f"mirrorlist={baseurl}&release=updates"
 
@@ -51,3 +54,14 @@ class OpenmandrivaInstaller(DistributionInstaller):
     @classmethod
     def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_dnf(state, "remove", packages)
+
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.x86_64 : "x86_64",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by OpenMandriva")
+
+        return a
index 700280a2f5f2ea10ca8a70cb83c01e8a1ab44f28..d28ed0ed2bfa71f3581bec513ac700ff0f571617 100644 (file)
@@ -4,6 +4,7 @@ import urllib.request
 import xml.etree.ElementTree as ElementTree
 from collections.abc import Sequence
 
+from mkosi.architecture import Architecture
 from mkosi.distributions import DistributionInstaller
 from mkosi.distributions.fedora import Repo, invoke_dnf, setup_dnf
 from mkosi.log import die
@@ -51,6 +52,17 @@ class OpensuseInstaller(DistributionInstaller):
     def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
         invoke_dnf(state, "remove", packages)
 
+    @staticmethod
+    def architecture(arch: Architecture) -> str:
+        a = {
+            Architecture.x86_64 : "x86_64",
+        }.get(arch)
+
+        if not a:
+            die(f"Architecture {a} is not supported by OpenSUSE")
+
+        return a
+
 
 def fetch_gpgurls(repourl: str) -> list[str]:
     gpgurls = [f"{repourl}/repodata/repomd.xml.key"]
index 39504aacd3d4a4e9f94688a4f5087a9fd24484b8..72ec1daf226ca8c897a53bc88c119ca7b5a5f437 100644 (file)
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
+from mkosi.architecture import Architecture
 from mkosi.distributions.debian import DebianInstaller
 from mkosi.state import MkosiState
 
@@ -16,7 +17,7 @@ class UbuntuInstaller(DebianInstaller):
         updates = f"deb {state.config.mirror} {state.config.release}-updates {repos}"
 
         # Security updates repos are never mirrored. But !x86 are on the ports server.
-        if state.config.architecture in ["x86", "x86_64"]:
+        if state.config.architecture in [Architecture.x86, Architecture.x86_64]:
             security = f"deb http://security.ubuntu.com/ubuntu/ {state.config.release}-security {repos}"
         else:
             security = f"deb http://ports.ubuntu.com/ {state.config.release}-security {repos}"
index 89917049216a8262261b9beba0d79d9dc2fa99ab..4e95bbec47eed9b4f033b4348cf678c62c295085 100644 (file)
@@ -255,8 +255,8 @@ class Manifest:
     def as_dict(self) -> dict[str, Any]:
         config = {
             "name": self.config.image_id or "image",
-            "distribution": self.config.distribution.name,
-            "architecture": self.config.architecture,
+            "distribution": str(self.config.distribution),
+            "architecture": str(self.config.architecture),
         }
         if self.config.image_version is not None:
             config["version"] = self.config.image_version
index 175bea43af5a0f6a18bdd8b3040bd4449de09c90..2541348cd81b8534822456e6a682df29e1d277f9 100644 (file)
@@ -14,6 +14,7 @@ import tempfile
 from pathlib import Path
 from typing import Iterator, Optional
 
+from mkosi.architecture import Architecture
 from mkosi.config import ConfigFeature, MkosiArgs, MkosiConfig
 from mkosi.install import copy_path
 from mkosi.log import die
@@ -35,7 +36,7 @@ def machine_cid(config: MkosiConfig) -> int:
 
 
 def find_qemu_binary(config: MkosiConfig) -> str:
-    binaries = ["qemu", "qemu-kvm", f"qemu-system-{config.architecture}"]
+    binaries = ["qemu", "qemu-kvm", f"qemu-system-{config.architecture.to_qemu()}"]
     for binary in binaries:
         if shutil.which(binary) is not None:
             return binary
@@ -45,8 +46,8 @@ def find_qemu_binary(config: MkosiConfig) -> str:
 
 def find_qemu_firmware(config: MkosiConfig) -> tuple[Path, bool]:
     FIRMWARE_LOCATIONS = {
-        "x86_64": ["/usr/share/ovmf/x64/OVMF_CODE.secboot.fd"],
-        "i386": [
+        Architecture.x86_64: ["/usr/share/ovmf/x64/OVMF_CODE.secboot.fd"],
+        Architecture.x86: [
             "/usr/share/edk2/ovmf-ia32/OVMF_CODE.secboot.fd",
             "/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd"
         ],
@@ -57,14 +58,14 @@ def find_qemu_firmware(config: MkosiConfig) -> tuple[Path, bool]:
             return Path(firmware), True
 
     FIRMWARE_LOCATIONS = {
-        "x86_64": [
+        Architecture.x86_64: [
             "/usr/share/ovmf/ovmf_code_x64.bin",
             "/usr/share/ovmf/x64/OVMF_CODE.fd",
             "/usr/share/qemu/ovmf-x86_64.bin",
         ],
-        "i386": ["/usr/share/ovmf/ovmf_code_ia32.bin", "/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd"],
-        "aarch64": ["/usr/share/AAVMF/AAVMF_CODE.fd"],
-        "armhfp": ["/usr/share/AAVMF/AAVMF32_CODE.fd"],
+        Architecture.x86: ["/usr/share/ovmf/ovmf_code_ia32.bin", "/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd"],
+        Architecture.arm64: ["/usr/share/AAVMF/AAVMF_CODE.fd"],
+        Architecture.arm: ["/usr/share/AAVMF/AAVMF32_CODE.fd"],
     }.get(config.architecture, [])
 
     for firmware in FIRMWARE_LOCATIONS:
@@ -107,16 +108,16 @@ def find_qemu_firmware(config: MkosiConfig) -> tuple[Path, bool]:
 def find_ovmf_vars(config: MkosiConfig) -> Path:
     OVMF_VARS_LOCATIONS = []
 
-    if config.architecture == "x86_64":
+    if config.architecture == Architecture.x86_64:
         OVMF_VARS_LOCATIONS += ["/usr/share/ovmf/x64/OVMF_VARS.fd"]
-    elif config.architecture == "i386":
+    elif config.architecture == Architecture.x86:
         OVMF_VARS_LOCATIONS += [
             "/usr/share/edk2/ovmf-ia32/OVMF_VARS.fd",
             "/usr/share/OVMF/OVMF32_VARS_4M.fd",
         ]
-    elif config.architecture == "armhfp":
+    elif config.architecture == Architecture.arm:
         OVMF_VARS_LOCATIONS += ["/usr/share/AAVMF/AAVMF32_VARS.fd"]
-    elif config.architecture == "aarch64":
+    elif config.architecture == Architecture.arm64:
         OVMF_VARS_LOCATIONS += ["/usr/share/AAVMF/AAVMF_VARS.fd"]
 
     OVMF_VARS_LOCATIONS += ["/usr/share/edk2/ovmf/OVMF_VARS.fd",
@@ -199,7 +200,7 @@ def run_qemu(args: MkosiArgs, config: MkosiConfig) -> None:
     firmware, fw_supports_sb = find_qemu_firmware(config)
     smm = "on" if fw_supports_sb else "off"
 
-    if config.architecture == "aarch64":
+    if config.architecture == Architecture.arm64:
         machine = f"type=virt,accel={accel}"
     else:
         machine = f"type=q35,accel={accel},smm={smm}"
@@ -292,9 +293,9 @@ def run_qemu(args: MkosiArgs, config: MkosiConfig) -> None:
             cmdline += ["-chardev", f"socket,id=chrtpm,path={swtpm_socket}",
                         "-tpmdev", "emulator,id=tpm0,chardev=chrtpm"]
 
-            if config.architecture == "x86_64":
+            if config.architecture == Architecture.x86_64:
                 cmdline += ["-device", "tpm-tis,tpmdev=tpm0"]
-            elif config.architecture == "aarch64":
+            elif config.architecture == Architecture.arm64:
                 cmdline += ["-device", "tpm-tis-device,tpmdev=tpm0"]
 
         if use_vsock: