arch-image-builder/builder/disk/abootimg.py
BigfootACA 4ad6592d3e builder: disk: add initial Android Boot Image supports
Signed-off-by: BigfootACA <bigfoot@classfun.cn>
2024-05-21 11:27:40 +08:00

124 lines
3.9 KiB
Python

import os
import io
import gzip
from logging import getLogger
from tempfile import mkstemp
from builder.disk.content import ImageContentBuilder
from builder.lib.config import ArchBuilderConfigError
from external.mkbootimg import main as mkbootimg
log = getLogger(__name__)
class AndroidBootBuilder(ImageContentBuilder):
temps: list[str] = []
def to_list(self, val: str | list[str] | None) -> list[str]:
if type(val) is str: return [val]
if type(val) is None: return []
if type(val) is list[str]: return val
raise TypeError("bad type for list")
def get_input_file(self, name: str):
ctx = self.builder.ctx
if name.startswith("/"): return name
in_root = os.path.join(ctx.get_rootfs(), name)
in_out = os.path.join(ctx.get_output(), name)
return in_root if os.path.exists(in_root) else in_out
def parse_config(self, cfg: dict) -> list:
ret: list[str] = []
def add_option(key: str, file: bool = False):
if key not in cfg: return
val = str(cfg[key])
key = "--" + key.replace("-", "_")
if file: val = self.get_input_file(val)
ret.extend([key, val])
file_in = [
"kernel", "ramdisk", "second", "dtb", "recovery-dtbo",
"recovery-acpio", "vendor-ramdisk", "vendor-bootconfig",
]
fields = [
"header-version", "cmdline", "vendor-cmdline", "base",
"kernel-offset", "ramdisk-offset", "second-offset",
"dtb-offset", "os-version", "os-patch-level", "tags-offset",
"board", "pagesize", "header-version",
]
for arg in fields: add_option(arg)
for arg in file_in: add_option(arg, True)
return ret
def resolve_kernel(self, files: list[str] | str) -> list[str]:
ret: list[str] = []
if files is None: return ret
ctx = self.builder.ctx
rootfs = ctx.get_rootfs()
path: str = ctx.get("kernel.path", "")
if path.startswith("/"): path = path[1:]
for file in self.to_list(files):
if file.startswith("/"): file = file[1:]
ret.append(os.path.join(rootfs, path, file))
return ret
def merge_fp(self, files: list[str], fp: io.FileIO):
for f in files:
inp = self.get_input_file(f)
with open(inp, "rb") as fi:
fp.write(fi.read())
fp.flush()
def merge_files(self, files: list[str]):
fd, file = mkstemp()
with os.fdopen(fd, "wb") as fo:
self.merge_fp(files, fo)
return file
def load_initramfs(self, cfg: dict):
ctx = self.builder.ctx
initramfs = self.resolve_kernel(ctx.get("kernel.initramfs"))
if "ramdisk" in cfg: initramfs = self.to_list(cfg["ramdisk"])
if len(initramfs) <= 0: return
file = self.merge_files(initramfs)
self.temps.append(file)
cfg["ramdisk"] = file
def load_kernel(self, cfg: dict):
ctx = self.builder.ctx
if "image-gzip-dtb" not in cfg or not cfg["image-gzip-dtb"]: return
kernel = self.resolve_kernel(ctx.get("kernel.kernel"))
dtb = self.resolve_kernel(ctx.get("kernel.devicetree"))
if "kernel" in cfg: kernel = self.to_list(cfg["kernel"])
if "dtb" in cfg: dtb = self.to_list(cfg["dtb"])
if len(kernel) != 1: raise ArchBuilderConfigError("bad number of kernel")
if len(dtb) < 1: raise ArchBuilderConfigError("no device tree found")
fd, file = mkstemp()
log.debug("creating Image.gz-dtb...")
with os.fdopen(fd, "wb") as fo:
with open(kernel[0], "rb") as fi:
fo.write(gzip.compress(fi.read()))
for d in dtb:
with open(d, "rb") as fi:
fo.write(fi.read())
size = fo.tell()
log.debug("size: %u bytes", size)
cfg.pop("dtb", None)
cfg["kernel"] = file
self.temps.append(file)
def build(self):
try:
self.temps = []
cfg = self.builder.config.copy()
self.load_initramfs(cfg)
self.load_kernel(cfg)
args = self.parse_config(cfg)
match self.builder.type:
case "aboot": key = "--output"
case "avndboot": key = "--vendor_boot"
case _: raise ArchBuilderConfigError("bad type for abootimg")
args.extend([key, self.builder.device])
log.debug("run mkbootimg with %s", " ".join(args))
mkbootimg(args)
finally:
for temp in self.temps:
os.remove(temp)