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, arch, manifest_file): import sys from os.path import join from ninja.ninja_syntax import Writer from .config import generate_configs config_deps = generate_configs(root, output, config, arch, modules.targets, modules.kinds) with open(output / "build.ninja", "w") as buildfile: build = Writer(buildfile) default_builds = [] 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.variable("build_config", config) build.newline() build.include(root / "assets" / "build" / arch / "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() build.variable("cogflags", [ "-I", "${source_root}/scripts", "-D", "definitions_path=${source_root}/definitions", ]) build.newline() for target in modules.targets: build.subninja(output / target / "target.ninja") build.newline() build.build( rule = "touch", outputs = "${build_root}/.all_headers", implicit = [f"${{build_root}}/include/{m.name}/.headers.phony" for m in modules.used.values() if m.public_headers], ) build.build( rule = "phony", outputs = ["all-headers"], inputs = ["${build_root}/.all_headers"]) from .manifest import Manifest manifest = Manifest(manifest_file, modules) debugroot = output / ".debug" debugroot.mkdir(exist_ok=True) fatroot = output / "fatroot" fatroot.mkdir(exist_ok=True) (fatroot / manifest.location).mkdir(exist_ok=True) initrdroot = output / "initrd_root" initrdroot.mkdir(exist_ok=True) image_content = {'initrd_root': [], 'fatroot': []} def add_image_content(image, path, name): output = join(path, name) image_output = f"${{build_root}}/{image}/{output}" build.build( rule = "cp", outputs = [image_output], inputs = [f"${{build_root}}/{name}"], variables = { "description": f"Installing {name}", }) image_content[image].append(image_output) build.newline() def add_image_content_exe(image, path, entry): input_path = f"${{build_root}}/{entry.target}/{entry.output}" intermediary = f"${{build_root}}/{entry.output}" build.build( rule = "strip", outputs = [intermediary], inputs = [input_path], implicit = [f"{input_path}.dump"], variables = { "name": f"Stripping {entry.module}", "debug": f"${{build_root}}/.debug/{entry.output}.debug", }) build.build( rule = "phony", outputs = [entry.output], inputs = [intermediary]) add_image_content(image, path, entry.output) if 'kernel' in modules.used: add_image_content_exe("fatroot", manifest.location, manifest.kernel) syms = manifest.add_data("symbol_table.dat", "Symbol table", ("symbols",)) syms_file = "jsix.symbols" syms_path = join(manifest.location, syms_file ); syms_out = join(fatroot, syms_path) build.build( rule = "makest", outputs = [syms_out], inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"], ) image_content['fatroot'].append(syms_out) manifest.symbols = syms_file build.newline() default_builds.append("${build_root}/jsix.img") def try_add_manifest_module(entry, section, image, path="jsix"): if entry.module in modules.used: add_image_content_exe(image, path, entry) elif entry.module not in modules: raise BonnibelError(f'unknown {section} module in manifest: {entry.module}') try_add_manifest_module(manifest.init, "init", "fatroot") for program in manifest.panics: try_add_manifest_module(program, "panic", "fatroot") for program in manifest.services: try_add_manifest_module(program, "services", "initrd_root", "jsix/services") for program in manifest.drivers: try_add_manifest_module(program, "drivers", "initrd_root", "jsix/drivers") for program in manifest.libs: try_add_manifest_module(program, "libs", "initrd_root", "jsix/lib") extra_regen_outputs = [] if 'boot' in modules.used: bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi" build.build( rule = "cp", outputs = [bootloader], inputs = ["${build_root}/boot/boot.efi"], variables = { "description": "Installing bootloader", }) build.newline() boot_config = join(fatroot, "jsix", "boot.conf") manifest.write_boot_config(boot_config) extra_regen_outputs.append(boot_config) initrd = str(fatroot / manifest.location / manifest.initrd["name"]) build.build( rule = "makeinitrd", outputs = [initrd], inputs = [str(initrdroot)], implicit = image_content['initrd_root'] + ["${source_root}/scripts/mkj6romfs.py"], variables = {"format": manifest.initrd["format"]}, ) build.newline() image_content['fatroot'].append(initrd) build.build( rule = "makefat", outputs = ["${build_root}/jsix.img"], inputs = ["${source_root}/assets/diskbase.img"], implicit = image_content['fatroot'] + [bootloader], 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.newline() default_builds.append(out) compdb = "${source_root}/compile_commands.json" 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.used.values()] regen_implicits += list(map(str, config_deps)) build.build( rule = "compdb", outputs = [compdb], implicit = regen_implicits, ) build.newline() default_builds.append(compdb) build.build( rule = "regen", outputs = ['build.ninja'], implicit = regen_implicits, implicit_outputs = [f"module.{mod.name}.ninja" for mod in modules.used.values()] + [f"{target}/target.ninja" for target in modules.targets] + extra_regen_outputs, ) build.newline() build.default(default_builds) for target in modules.targets: mods = modules.target_mods(target) targetdir = output / target 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) build.variable("target_dir", output / target) build.newline() build.include(f"{target}/config.ninja") build.newline() for kind in ('defs', 'run'): for lang in ('c', 'cpp'): deffile = str(output / target / 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"module.{mod.name}.ninja")