Files
jsix/scripts/bonnibel/project.py
Justin C. Miller c9d713fc7f [build] Move to yaml-based build config and manifest
Overall, I believe TOML to be a superior configuration format than YAML
in many situations, but it gets ugly quickly when nesting data
structures. The build configs were fine in TOML, but the manifest (and
my future plans for it) got unwieldy. I also did not want different
formats for each kind of configuration on top of also having a custom
DSL for interface definitions, so I've switched all the TOML to YAML.

Also of note is that this change actually adds structure to the manifest
file, which was little more than a CSV previously.
2021-09-05 13:07:09 -07:00

189 lines
6.6 KiB
Python

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()
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]
fatroot_output = f"${{build_root}}/fatroot/{mod.output}"
build.build(
rule = "strip",
outputs = [fatroot_output],
inputs = [f"${{build_root}}/{target}/{mod.output}"],
variables = {
"name": f"Installing {name}",
"debug": f"${{build_root}}/{mod.output}.debug",
})
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 = {
"ovmf/x64/ovmf_vars.fd": "UEFI Variables",
"debugging/jsix.elf-gdb.py": "GDB Debug Helpers",
}
for asset, name in default_assets.items():
p = root / "assets" / asset
out = f"${{build_root}}/{p.name}"
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")