mirror of
https://github.com/BigfootACA/arch-image-builder.git
synced 2024-11-11 07:57:53 +08:00
119 lines
3.2 KiB
Python
119 lines
3.2 KiB
Python
import os
|
|
import yaml
|
|
from logging import getLogger
|
|
from builder.lib import json
|
|
from builder.lib.cpu import cpu_arch_compatible
|
|
from builder.lib.context import ArchBuilderContext
|
|
log = getLogger(__name__)
|
|
|
|
|
|
class ArchBuilderConfigError(Exception):
|
|
pass
|
|
|
|
|
|
def _dict_merge(dst: dict, src: dict):
|
|
"""
|
|
Merge two dict with override
|
|
"""
|
|
for key in src.keys():
|
|
st = type(src[key])
|
|
if key in dst and st is type(dst[key]):
|
|
if st == list:
|
|
dst[key].extend(src[key])
|
|
continue
|
|
if st == dict:
|
|
_dict_merge(dst[key], src[key])
|
|
continue
|
|
dst[key] = src[key]
|
|
|
|
|
|
def load_config_file(ctx: ArchBuilderContext, path: str):
|
|
"""
|
|
Load one config (yaml/json) to context
|
|
"""
|
|
log.debug(f"try to open config {path}")
|
|
try:
|
|
with open(path, "r") as f:
|
|
if path.endswith((".yml", ".yaml")):
|
|
log.debug(f"load {path} as yaml")
|
|
loaded = yaml.safe_load(f)
|
|
elif path.endswith((".jsn", ".json")):
|
|
log.debug(f"load {path} as json")
|
|
loaded = json.load(f)
|
|
log.info(f"loaded config {path}")
|
|
except BaseException:
|
|
log.error(f"failed to load config {path}")
|
|
raise
|
|
def _proc_include(inc: str | list[str]):
|
|
pt = type(inc)
|
|
if pt is str: inc = [inc]
|
|
elif pt is list: pass
|
|
else: raise ArchBuilderConfigError("bad type for also")
|
|
load_configs(ctx, inc)
|
|
if loaded is None: return
|
|
if "+also" in loaded:
|
|
_proc_include(loaded["+also"])
|
|
loaded.pop("+also")
|
|
if ctx.config is None:
|
|
log.debug(f"use {path} as current config")
|
|
ctx.config = loaded
|
|
else:
|
|
log.debug(f"merge {path} into current config")
|
|
_dict_merge(ctx.config, loaded)
|
|
if "+then" in loaded:
|
|
_proc_include(loaded["+then"])
|
|
loaded.pop("+then")
|
|
|
|
|
|
def populate_config(ctx: ArchBuilderContext):
|
|
ctx.finish_config()
|
|
ctx.resolve_subscript()
|
|
if "target" not in ctx.config:
|
|
raise ArchBuilderConfigError("no target set")
|
|
if "arch" not in ctx.config:
|
|
raise ArchBuilderConfigError("no cpu arch set")
|
|
ctx.target = ctx.config["target"]
|
|
ctx.tgt_arch = ctx.config["arch"]
|
|
if ctx.tgt_arch == "any" or ctx.cur_arch == "any":
|
|
raise ArchBuilderConfigError("bad cpu arch value")
|
|
if not cpu_arch_compatible(ctx.tgt_arch, ctx.cur_arch):
|
|
log.warning(
|
|
f"current cpu arch {ctx.cur_arch} is not compatible to {ctx.tgt_arch}, "
|
|
"you may need qemu-user-static-binfmt to run incompatible executables",
|
|
)
|
|
jstr = json.dumps(ctx.config, indent=2)
|
|
log.debug(f"populated config:\n {jstr}")
|
|
|
|
|
|
def load_configs(ctx: ArchBuilderContext, configs: list[str]):
|
|
"""
|
|
Load multiple config to context
|
|
"""
|
|
loaded = 0
|
|
for config in configs:
|
|
success = False
|
|
for suffix in ["yml", "yaml", "jsn", "json"]:
|
|
fn = f"{config}.{suffix}"
|
|
path = os.path.join(ctx.dir, "configs", fn)
|
|
if os.path.exists(path):
|
|
load_config_file(ctx, path)
|
|
loaded += 1
|
|
success = True
|
|
if not success:
|
|
raise FileNotFoundError(f"config {config} not found")
|
|
if loaded > 0:
|
|
if ctx.config is None:
|
|
raise ArchBuilderConfigError("no any config loaded")
|
|
log.debug(f"loaded {loaded} configs")
|
|
|
|
|
|
def load_preset(ctx: ArchBuilderContext, preset: str):
|
|
file = preset
|
|
if not preset.startswith("/"):
|
|
file = "presets/" + preset
|
|
ctx.preset = True
|
|
load_configs(ctx, [file])
|
|
if "package" not in ctx.config:
|
|
raise ArchBuilderConfigError("bad preset config")
|
|
log.info(f"loaded preset {preset}")
|