from . import BonnibelError class Project: def __init__(self, root): from .version import git_version self.root = root self.version = git_version(root) def __str__(self): return f"{self.name} {self.version.major}.{self.version.minor}.{self.version.patch}-{self.version.sha}" def generate(self, root, output, modules, config, manifest_file): import sys import bonnibel from os.path import join from ninja.ninja_syntax import Writer from . import load_config from .target import Target targets = set() for mod in modules.values(): targets.update({Target.load(root, t, config) for t in mod.targets}) with open(output / "build.ninja", "w") as buildfile: build = Writer(buildfile) build.comment("This file is automatically generated by bonnibel") build.variable("ninja_required_version", "1.3") build.variable("build_root", output) build.variable("source_root", root) build.newline() build.include(root / "configs" / "rules.ninja") build.newline() build.variable("version_major", self.version.major) build.variable("version_minor", self.version.minor) build.variable("version_patch", self.version.patch) build.variable("version_sha", self.version.sha) build.newline() for target in targets: build.subninja(output / target.name / "target.ninja") build.newline() debugroot = output / ".debug" debugroot.mkdir(exist_ok=True) fatroot = output / "fatroot" fatroot.mkdir(exist_ok=True) fatroot_content = [] def add_fatroot(name, target): if not name in modules: raise BonnibelError(f"Manifest item '{name}' is not a known module") mod = modules[name] intermediary = f"${{build_root}}/{mod.output}" fatroot_output = f"${{build_root}}/fatroot/{mod.output}" build.build( rule = "strip", outputs = [intermediary], inputs = [f"${{build_root}}/{target}/{mod.output}"], implicit = [f"${{build_root}}/{target}/{mod.output}.dump"], variables = { "name": f"Stripping {name}", "debug": f"${{build_root}}/.debug/{mod.output}.debug", }) build.build( rule = "cp", outputs = [fatroot_output], inputs = [intermediary], variables = { "name": f"Installing {name}", }) fatroot_content.append(fatroot_output) build.newline() manifest = load_config(manifest_file) programs = manifest.get("programs", tuple()) kernel = manifest.get("kernel", dict()) add_fatroot( kernel.get("name", "kernel"), kernel.get("target", "kernel")) for program in programs: name = program["name"] target = program.get("target", "user") add_fatroot(name, target) symbol_table = "${build_root}/fatroot/symbol_table.dat" build.build( rule = "makest", outputs = [symbol_table], inputs = ["${build_root}/fatroot/jsix.elf"], ) fatroot_content.append(symbol_table) build.newline() bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi" build.build( rule = "cp", outputs = [bootloader], inputs = ["${build_root}/boot/boot.efi"], variables = { "name": "Installing bootloader", }) fatroot_content.append(bootloader) build.newline() build.build( rule = "makefat", outputs = ["${build_root}/jsix.img"], inputs = ["${source_root}/assets/diskbase.img"], implicit = fatroot_content, variables = {"name": "jsix.img"}, ) build.newline() default_assets = { "UEFI Variables": ("ovmf/x64/ovmf_vars.fd", "ovmf_vars.fd"), "GDB Debug Helpers": ("debugging/jsix.elf-gdb.py", "jsix.elf-gdb.py"), } for name, assets in default_assets.items(): p = root / "assets" / assets[0] out = f"${{build_root}}/{assets[1]}" build.build( rule = "cp", outputs = [out], inputs = [str(p)], variables = {"name": name}, ) build.default([out]) build.newline() build.rule("regen", command = " ".join([str(root / 'configure')] + sys.argv[1:]), description = "Regenerate build files", generator = True, ) build.newline() regen_implicits = \ [f"{self.root}/configure", str(manifest_file)] + \ [str(mod.modfile) for mod in modules.values()] for target in targets: regen_implicits += target.depfiles build.build( rule = "regen", outputs = ['build.ninja'], implicit = regen_implicits, implicit_outputs = [f"{mod.name}.ninja" for mod in modules.values()] + [f"{target.name}/target.ninja" for target in targets], ) build.newline() build.default(["${build_root}/jsix.img"]) for target in targets: mods = [m.name for m in modules.values() if target.name in m.targets] targetdir = output / target.name targetdir.mkdir(exist_ok=True) buildfilename = str(targetdir / "target.ninja") with open(buildfilename, "w") as buildfile: build = Writer(buildfile) build.comment("This file is automatically generated by bonnibel") build.newline() build.variable("target", target.name) build.variable("target_dir", output / target.name) build.newline() for name, value in target.items(): build.variable(name, value) build.newline() for kind in ('defs', 'run'): for lang in ('c', 'cpp'): deffile = str(output / target.name / f"{lang}.{kind}") build.build( rule = f"dump_{lang}_{kind}", outputs = [deffile], implicit = [buildfilename], ) build.default(deffile) build.newline() for mod in mods: build.subninja(f"{mod}.ninja")