From 90a0eb3c5334764a275619566770de4c51297601 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 23 Nov 2025 23:21:12 -0800 Subject: [PATCH] [build] first pass at multiarch support Changing bonnibel to respect the --arch flag to configure. This requires some reworking of modules, mostly in the addition of the ModuleList class instead of just a dict of modules. --- assets/build/{ => amd64}/config.debug.yaml | 0 assets/build/{ => amd64}/config.release.yaml | 0 assets/build/{ => amd64}/global.yaml | 0 assets/build/{ => amd64}/rules.ninja | 0 assets/build/{ => amd64}/target.boot.yaml | 0 assets/build/{ => amd64}/target.init.yaml | 0 assets/build/{ => amd64}/target.kernel.yaml | 0 assets/build/{ => amd64}/target.user.exe.yaml | 0 .../build/{ => amd64}/target.user.shared.yaml | 0 assets/build/{ => amd64}/target.user.yaml | 0 configure | 32 +- qemu.sh | 2 +- scripts/bonnibel/config.py | 8 +- scripts/bonnibel/manifest.py | 19 +- scripts/bonnibel/module.py | 512 ++++++++++-------- scripts/bonnibel/project.py | 236 ++++---- src/boot/boot.module | 2 +- src/kernel/kernel.module | 2 +- src/kernel/panic.serial/serial.module | 2 +- src/libraries/j6/j6.module | 6 +- src/user/6s/6s.module | 2 +- src/user/drv.uart/uart.module | 2 +- src/user/drv.uefi_fb/uefi_fb.module | 2 +- src/user/ld.so/ld.so.module | 2 +- src/user/srv.init/init.module | 2 +- src/user/srv.logger/logger.module | 2 +- src/user/test_runner/test_runner.module | 2 +- src/user/testapp/testapp.module | 2 +- 28 files changed, 446 insertions(+), 391 deletions(-) rename assets/build/{ => amd64}/config.debug.yaml (100%) rename assets/build/{ => amd64}/config.release.yaml (100%) rename assets/build/{ => amd64}/global.yaml (100%) rename assets/build/{ => amd64}/rules.ninja (100%) rename assets/build/{ => amd64}/target.boot.yaml (100%) rename assets/build/{ => amd64}/target.init.yaml (100%) rename assets/build/{ => amd64}/target.kernel.yaml (100%) rename assets/build/{ => amd64}/target.user.exe.yaml (100%) rename assets/build/{ => amd64}/target.user.shared.yaml (100%) rename assets/build/{ => amd64}/target.user.yaml (100%) diff --git a/assets/build/config.debug.yaml b/assets/build/amd64/config.debug.yaml similarity index 100% rename from assets/build/config.debug.yaml rename to assets/build/amd64/config.debug.yaml diff --git a/assets/build/config.release.yaml b/assets/build/amd64/config.release.yaml similarity index 100% rename from assets/build/config.release.yaml rename to assets/build/amd64/config.release.yaml diff --git a/assets/build/global.yaml b/assets/build/amd64/global.yaml similarity index 100% rename from assets/build/global.yaml rename to assets/build/amd64/global.yaml diff --git a/assets/build/rules.ninja b/assets/build/amd64/rules.ninja similarity index 100% rename from assets/build/rules.ninja rename to assets/build/amd64/rules.ninja diff --git a/assets/build/target.boot.yaml b/assets/build/amd64/target.boot.yaml similarity index 100% rename from assets/build/target.boot.yaml rename to assets/build/amd64/target.boot.yaml diff --git a/assets/build/target.init.yaml b/assets/build/amd64/target.init.yaml similarity index 100% rename from assets/build/target.init.yaml rename to assets/build/amd64/target.init.yaml diff --git a/assets/build/target.kernel.yaml b/assets/build/amd64/target.kernel.yaml similarity index 100% rename from assets/build/target.kernel.yaml rename to assets/build/amd64/target.kernel.yaml diff --git a/assets/build/target.user.exe.yaml b/assets/build/amd64/target.user.exe.yaml similarity index 100% rename from assets/build/target.user.exe.yaml rename to assets/build/amd64/target.user.exe.yaml diff --git a/assets/build/target.user.shared.yaml b/assets/build/amd64/target.user.shared.yaml similarity index 100% rename from assets/build/target.user.shared.yaml rename to assets/build/amd64/target.user.shared.yaml diff --git a/assets/build/target.user.yaml b/assets/build/amd64/target.user.yaml similarity index 100% rename from assets/build/target.user.yaml rename to assets/build/amd64/target.user.yaml diff --git a/configure b/configure index 117cb92..b17a657 100755 --- a/configure +++ b/configure @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -def generate(output, config, manifest): +def generate(output, config, arch, manifest): from os import makedirs from glob import iglob from pathlib import Path - from bonnibel.module import Module + from bonnibel.module import Module, ModuleList from bonnibel.project import Project root = Path(__file__).parent.resolve() @@ -18,16 +18,17 @@ def generate(output, config, manifest): str(root / "external/*.module"), ] - modules = {} + modules = ModuleList(arch) for source in sources: for modfile in iglob(source, recursive=True): - path = Path(modfile).parent + modfile = Path(modfile) + path = modfile.parent def module_init(name, **kwargs): if not "root" in kwargs: kwargs["root"] = path m = Module(name, modfile, **kwargs) - modules[m.name] = m + modules.add(m) return m glo = { @@ -36,18 +37,16 @@ def generate(output, config, manifest): "build_root": output, "module_root": path, "config": config, + "arch": arch, } code = compile(open(modfile, 'r').read(), modfile, "exec") loc = {} exec(code, glo, loc) - Module.update(modules) - makedirs(output.resolve(), exist_ok=True) - project.generate(root, output, modules, config, manifest) - for mod in modules.values(): - mod.generate(output) + project.generate(root, output, modules, config, arch, manifest) + modules.generate(output) if __name__ == "__main__": import sys @@ -57,18 +56,25 @@ if __name__ == "__main__": from argparse import ArgumentParser from bonnibel import BonnibelError + default_arch = "amd64" + p = ArgumentParser(description="Generate jsix build files") p.add_argument("--manifest", "-m", metavar="FILE", default="assets/manifests/default.yaml", help="File to use as the system manifest") - p.add_argument("--config", "-c", metavar="NAME", default="debug", + p.add_argument("--conf", "-c", metavar="NAME", default="debug", help="Configuration to build (eg, 'debug' or 'release')") - p.add_argument("output", metavar="DIR", default="build", nargs='?', + p.add_argument("--arch", "-a", metavar="NAME", default=default_arch, + help="Architecture to build (eg, 'amd64' or 'linux')") + p.add_argument("--verbose", "-v", action='count', default=0, + help="More verbose log output") + p.add_argument("output", metavar="DIR", default=None, nargs='?', help="Where to create the build root") args = p.parse_args() + output = args.output or f"build.{args.arch}" try: - generate(args.output, args.config, args.manifest) + generate(output, args.conf, args.arch, args.manifest) except BonnibelError as be: import sys diff --git a/qemu.sh b/qemu.sh index 91c1ad7..797b25b 100755 --- a/qemu.sh +++ b/qemu.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash root=$(dirname $0) -build="${root}/build" +build="${root}/build.amd64" assets="${root}/assets" no_build="" diff --git a/scripts/bonnibel/config.py b/scripts/bonnibel/config.py index 5bb4b94..e2b9d5d 100644 --- a/scripts/bonnibel/config.py +++ b/scripts/bonnibel/config.py @@ -43,10 +43,10 @@ def _make_ninja_config(outfile, config, files): build.variable(k, v) -def generate_configs(root, output, config, targets, kinds): +def generate_configs(root, output, buildconfig, arch, targets, kinds): - assets = root / "assets" / "build" - base = ["global.yaml", f"config.{config}.yaml"] + assets = root / "assets" / "build" / arch + base = ["global.yaml", f"config.{buildconfig}.yaml"] depfiles = set() @@ -65,4 +65,4 @@ def generate_configs(root, output, config, targets, kinds): outfile = output / target / f"config.{kind}.ninja" _make_ninja_config(outfile, config, files) - return depfiles \ No newline at end of file + return depfiles diff --git a/scripts/bonnibel/manifest.py b/scripts/bonnibel/manifest.py index a441be1..63f83bc 100644 --- a/scripts/bonnibel/manifest.py +++ b/scripts/bonnibel/manifest.py @@ -37,9 +37,15 @@ class Manifest: self.drivers = [self.__build_entry(modules, i) for i in config.get("drivers", tuple())] - libs = set(config.get("libs", tuple())) - libs.update(self.__libdeps([modules[e.module] for e in self.services])) - libs.update(self.__libdeps([modules[e.module] for e in self.drivers])) + def get_libdeps(names): + libmods = modules.get_mods(names) + deps = modules.all_deps(libmods, stop_at_static=True) + deps = [m.name for m in deps if m.kind == "lib"] + return deps + + libs = set(get_libdeps(config.get("libs", tuple()))) + libs.update(get_libdeps([e.module for e in self.services])) + libs.update(get_libdeps([e.module for e in self.drivers])) self.libs = [self.__build_entry(modules, i) for i in libs] @@ -80,13 +86,6 @@ class Manifest: return Manifest.Entry(name, target, mod.get_output(), flags) - def __libdeps(self, modules): - deps = set([m.name for m in modules if m.kind == "lib"]) - for m in modules: - if m.static: continue - deps.update(self.__libdeps(m.depmods)) - return deps - def add_data(self, output, desc, flags=tuple()): e = Manifest.Entry(None, None, output, flags) self.data.append(e) diff --git a/scripts/bonnibel/module.py b/scripts/bonnibel/module.py index 8070d47..f77304e 100644 --- a/scripts/bonnibel/module.py +++ b/scripts/bonnibel/module.py @@ -39,7 +39,7 @@ class Module: "kind": (str, "exe"), "outfile": (str, None), "basename": (str, None), - "targets": (set, ()), + "target": (str, None), "deps": (set, ()), "public_headers": (set, ()), "copy_headers": (bool, False), @@ -53,6 +53,8 @@ class Module: "no_libc": (bool, False), "ld_script": (str, None), "static": (bool, False), + "arch_source": (dict, ()), + "skip_arches": (tuple, ()), } def __init__(self, name, modfile, root, **kwargs): @@ -81,15 +83,14 @@ class Module: # Turn strings into real Source objects self.sources = [make_source(root, f) for f in self.sources] + for arch in self.arch_source: + self.arch_source[arch] = [make_source(root, f) for f in self.arch_source[arch]] header_source = lambda f: make_source(root, Path("include") / f) if self.copy_headers: header_source = lambda f: make_copy_source(root, f, "include") self.public_headers = [header_source(f) for f in self.public_headers] - # Filled by Module.update - self.depmods = set() - def __repr__(self): return f"" @@ -115,227 +116,6 @@ class Module: ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so")) return self.basename + ext.get(self.kind, "") - @classmethod - def update(cls, mods): - from . import BonnibelError - - def resolve(source, modlist): - resolved = set() - for dep in modlist: - if not dep in mods: - raise BonnibelError(f"module '{source.name}' references unknown module '{dep}'") - mod = mods[dep] - resolved.add(mod) - return resolved - - for mod in mods.values(): - mod.depmods = resolve(mod, mod.deps) - - target_mods = [mod for mod in mods.values() if mod.targets] - for mod in target_mods: - closed = set() - children = set(mod.depmods) - while children: - child = children.pop() - closed.add(child) - child.targets |= mod.targets - children |= {m for m in child.depmods if not m in closed} - - def generate(self, output): - from pathlib import Path - from collections import defaultdict - from ninja.ninja_syntax import Writer - - def walk_deps(deps, static, results): - for mod in deps: - if static or mod.name not in results: - results[mod.name] = (mod, static) - walk_deps(mod.depmods, static or mod.static, results) - - all_deps = {} - walk_deps(self.depmods, self.static, all_deps) - all_deps = all_deps.values() - - def gather_phony(build, deps, child_rel): - phony = ".headers.phony" - child_phony = [child_rel(phony, module=c.name) - for c, _ in all_deps] - - build.build( - rule = "touch", - outputs = [mod_rel(phony)], - implicit = child_phony, - order_only = list(map(mod_rel, deps)), - ) - - filename = str(output / f"module.{self.name}.ninja") - with open(filename, "w") as buildfile: - build = Writer(buildfile) - - build.comment("This file is automatically generated by bonnibel") - build.newline() - - build.variable("module_dir", target_rel(self.name + ".dir")) - build.variable("module_kind", self.kind) - build.newline() - - build.include(f"${{target_dir}}/config.{self.kind}.ninja") - build.newline() - - modopts = BuildOptions( - local = [self.root, "${module_dir}"], - ld_script = self.ld_script and self.root / self.ld_script, - ) - if self.public_headers: - modopts.includes += [ - self.root / "include", - f"${{target_dir}}/{self.name}.dir/include", - ] - - for key, value in self.variables.items(): - build.variable(key, value) - build.newline() - - for include in self.includes: - p = Path(include) - if p.is_absolute(): - if not p in modopts.includes: - modopts.includes.append(str(p.resolve())) - elif include != ".": - incpath = self.root / p - destpath = mod_rel(p) - for header in incpath.rglob("*.h"): - dest_header = f"{destpath}/" + str(header.relative_to(incpath)) - modopts.includes.append(str(incpath)) - modopts.includes.append(destpath) - - for dep, static in all_deps: - if dep.public_headers: - if dep.include_phase == "normal": - modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"] - elif dep.include_phase == "late": - modopts.late += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"] - else: - from . import BonnibelError - raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}") - - if dep.kind == "headers": - continue - elif dep.kind == "lib": - modopts.libs.append((target_rel(dep.get_output(static)), static)) - else: - modopts.order_only.append(target_rel(dep.get_output(static))) - - cc_includes = [] - if modopts.local: - cc_includes += [f"-iquote{i}" for i in modopts.local] - - if modopts.includes: - cc_includes += [f"-I{i}" for i in modopts.includes] - - if modopts.late: - cc_includes += [f"-idirafter{i}" for i in modopts.late] - - if cc_includes: - build.variable("ccflags", ["${ccflags}"] + cc_includes) - - as_includes = [f"-I{d}" for d in modopts.local + modopts.includes + modopts.late] - if as_includes: - build.variable("asflags", ["${asflags}"] + as_includes) - - if modopts.libs: - build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args) - - if modopts.ld_script: - build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script]) - - header_deps = [] - inputs = [] - headers = set(self.public_headers) - while headers: - source = headers.pop() - headers.update(source.next) - - if source.action: - build.newline() - build.build(rule=source.action, **source.args) - - if source.gather: - header_deps += list(source.outputs) - - if source.input: - inputs.extend(map(mod_rel, source.outputs)) - - build.newline() - - inputs = [] - sources = set(self.sources) - while sources: - source = sources.pop() - sources.update(source.next) - - if source.action: - build.newline() - build.build(rule=source.action, **source.args) - - if source.gather: - header_deps += list(source.outputs) - - if source.input: - inputs.extend(map(mod_rel, source.outputs)) - - gather_phony(build, header_deps, target_rel) - - if self.kind == "headers": - # Header-only, don't output a build rule - return - - output = target_rel(self.get_output()) - build.newline() - build.build( - rule = self.kind, - outputs = output, - inputs = inputs, - implicit = modopts.implicit, - order_only = modopts.order_only, - variables = {"name": self.name, - "soname": self.get_output()}, - ) - - dump = output + ".dump" - build.newline() - build.build( - rule = "dump", - outputs = dump, - inputs = output, - variables = {"name": self.name}, - ) - - s_output = target_rel(self.get_output(static=True)) - if s_output != output: - build.newline() - build.build( - rule = self.kind + "_static", - outputs = s_output, - inputs = inputs, - order_only = modopts.order_only, - variables = {"name": self.name}, - ) - - dump = s_output + ".dump" - build.newline() - build.build( - rule = "dump", - outputs = dump, - inputs = s_output, - variables = {"name": self.name}, - ) - - if self.default: - build.newline() - build.default(output) - build.default(dump) - def add_input(self, path, **kwargs): from .source import make_source s = make_source(self.root, path, **kwargs) @@ -350,3 +130,285 @@ class Module: for source in self.public_headers: if source.path in paths: source.add_deps(deps) + + +class ModuleList: + def __init__(self, arch): + self.__arch = arch + self.__mods = {} + self.__used = {} + self.__targets = frozenset() + self.__kinds = frozenset() + + def __getitem__(self, name): + return self.__mods.get(name) + + def __contains__(self, name): + """Return if the module name is known.""" + return name in self.__mods + + def __iter__(self): + """Iterate over _non-skipped_ modules.""" + return self.used.__iter__() + + def __skip(self, mod): + if self.__arch in mod.skip_arches: return True + return False + + @property + def used(self): + if self.__used is None: + self.__used = {n:m for n,m in self.__mods.items() if not self.__skip(m)} + return self.__used + + @property + def targets(self): + if self.__targets is None: + self.__targets = frozenset([m.target for m in self.used.values() if m.target]) + return self.__targets + + @property + def kinds(self): + if self.__kinds is None: + self.__kinds = frozenset([m.kind for m in self.used.values()]) + return self.__kinds + + def get(self, name): + return self.__mods.get(name) + + def add(self, mod): + if mod.name in self.__mods: + raise BonnibelError(f"re-adding module '{mod.name}' to this ModuleList") + self.__mods[mod.name] = mod + self.__used = None + self.__targets = None + self.__kinds = None + + def get_mods(self, names, filt=None): + return {self[n] for n in names + if n in self.used + and ((not filt) or filt(self[n]))} + + def all_deps(self, mods, stop_at_static=False): + search = set(mods) + closed = list() + + while search: + mod = search.pop() + if mod in closed: continue + closed.append(mod) + + if stop_at_static and mod.static: + continue + + for dep in mod.deps: + if not dep in self.__mods: + raise BonnibelError(f"module '{mod.name}' references unknown module '{dep}'") + if dep in self.used: + search.add(self.used[dep]) + + return closed + + def target_mods(self, target): + return self.all_deps([m for m in self.used.values() if m.target == target]) + + def generate(self, output): + from pathlib import Path + from collections import defaultdict + from ninja.ninja_syntax import Writer + + def walk_deps(deps, static, results): + for modname in deps: + mod = self.used.get(modname) + if not mod: continue # skipped + if static or modname not in results: + results[modname] = (mod, static) + walk_deps(mod.deps, static or mod.static, results) + + for mod in self.used.values(): + all_deps = {} + walk_deps(mod.deps, mod.static, all_deps) + all_deps = all_deps.values() + + def gather_phony(build, deps, child_rel): + phony = ".headers.phony" + child_phony = [child_rel(phony, module=c.name) + for c, _ in all_deps] + + build.build( + rule = "touch", + outputs = [mod_rel(phony)], + implicit = child_phony, + order_only = list(map(mod_rel, deps)), + ) + + filename = str(output / f"module.{mod.name}.ninja") + with open(filename, "w") as buildfile: + build = Writer(buildfile) + + build.comment("This file is automatically generated by bonnibel") + build.newline() + + build.variable("module_dir", target_rel(mod.name + ".dir")) + build.variable("module_kind", mod.kind) + build.newline() + + build.include(f"${{target_dir}}/config.{mod.kind}.ninja") + build.newline() + + modopts = BuildOptions( + local = [mod.root, "${module_dir}"], + ld_script = mod.ld_script and mod.root / mod.ld_script, + ) + if mod.public_headers: + modopts.includes += [ + mod.root / "include", + f"${{target_dir}}/{mod.name}.dir/include", + ] + + for key, value in mod.variables.items(): + build.variable(key, value) + build.newline() + + for include in mod.includes: + p = Path(include) + if p.is_absolute(): + if not p in modopts.includes: + modopts.includes.append(str(p.resolve())) + elif include != ".": + incpath = mod.root / p + destpath = mod_rel(p) + for header in incpath.rglob("*.h"): + dest_header = f"{destpath}/" + str(header.relative_to(incpath)) + modopts.includes.append(str(incpath)) + modopts.includes.append(destpath) + + for dep, static in all_deps: + if dep.public_headers: + if dep.include_phase == "normal": + modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"] + elif dep.include_phase == "late": + modopts.late += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"] + else: + from . import BonnibelError + raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}") + + if dep.kind == "headers": + continue + elif dep.kind == "lib": + modopts.libs.append((target_rel(dep.get_output(static)), static)) + else: + modopts.order_only.append(target_rel(dep.get_output(static))) + + cc_includes = [] + if modopts.local: + cc_includes += [f"-iquote{i}" for i in modopts.local] + + if modopts.includes: + cc_includes += [f"-I{i}" for i in modopts.includes] + + if modopts.late: + cc_includes += [f"-idirafter{i}" for i in modopts.late] + + if cc_includes: + build.variable("ccflags", ["${ccflags}"] + cc_includes) + + as_includes = [f"-I{d}" for d in modopts.local + modopts.includes + modopts.late] + if as_includes: + build.variable("asflags", ["${asflags}"] + as_includes) + + if modopts.libs: + build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args) + + if modopts.ld_script: + build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script]) + + header_deps = [] + inputs = [] + headers = set(mod.public_headers) + while headers: + source = headers.pop() + headers.update(source.next) + + if source.action: + build.newline() + build.build(rule=source.action, **source.args) + + if source.gather: + header_deps += list(source.outputs) + + if source.input: + inputs.extend(map(mod_rel, source.outputs)) + + build.newline() + + inputs = [] + sources = set(mod.sources) + sources.update(set(mod.arch_source.get(self.__arch, tuple()))) + while sources: + source = sources.pop() + sources.update(source.next) + + if source.action: + build.newline() + build.build(rule=source.action, **source.args) + + if source.gather: + header_deps += list(source.outputs) + + if source.input: + inputs.extend(map(mod_rel, source.outputs)) + + gather_phony(build, header_deps, target_rel) + + if mod.kind == "headers": + # Header-only, don't output a build rule + continue + + mod_output = target_rel(mod.get_output()) + build.newline() + build.build( + rule = mod.kind, + outputs = mod_output, + inputs = inputs, + implicit = modopts.implicit, + order_only = modopts.order_only, + variables = {"name": mod.name, + "soname": mod.get_output()}, + ) + + dump = mod_output + ".dump" + build.newline() + build.build( + rule = "dump", + outputs = dump, + inputs = mod_output, + variables = {"name": mod.name}, + ) + + s_output = target_rel(mod.get_output(static=True)) + if s_output != mod_output: + build.newline() + build.build( + rule = mod.kind + "_static", + outputs = s_output, + inputs = inputs, + order_only = modopts.order_only, + variables = {"name": mod.name}, + ) + + dump = s_output + ".dump" + build.newline() + build.build( + rule = "dump", + outputs = dump, + inputs = s_output, + variables = {"name": mod.name}, + ) + + if mod.default: + build.newline() + build.default(mod_output) + build.default(dump) + + diff --git a/scripts/bonnibel/project.py b/scripts/bonnibel/project.py index 9770dd8..d81a15c 100644 --- a/scripts/bonnibel/project.py +++ b/scripts/bonnibel/project.py @@ -10,22 +10,17 @@ class Project: 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): + def generate(self, root, output, modules, config, arch, manifest_file): import sys from os.path import join from ninja.ninja_syntax import Writer - targets = set() - kinds = set() - for mod in modules.values(): - targets.update(mod.targets) - kinds.add(mod.kind) - from .config import generate_configs - config_deps = generate_configs(root, output, config, targets, kinds) + 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") @@ -34,7 +29,7 @@ class Project: build.variable("build_config", config) build.newline() - build.include(root / "assets/build/rules.ninja") + build.include(root / "assets" / "build" / arch / "rules.ninja") build.newline() build.variable("version_major", self.version.major) @@ -49,7 +44,7 @@ class Project: ]) build.newline() - for target in targets: + for target in modules.targets: build.subninja(output / target / "target.ninja") build.newline() @@ -57,7 +52,7 @@ class Project: rule = "touch", outputs = "${build_root}/.all_headers", implicit = [f"${{build_root}}/include/{m.name}/.headers.phony" - for m in modules.values() if m.public_headers], + for m in modules.used.values() if m.public_headers], ) build.build( rule = "phony", @@ -78,56 +73,24 @@ class Project: initrdroot = output / "initrd_root" initrdroot.mkdir(exist_ok=True) - fatroot_content = [] - initrd_content = [] + image_content = {'initrd_root': [], 'fatroot': []} - def add_fatroot(source, name): - output = join(manifest.location, name) - fatroot_output = f"${{build_root}}/fatroot/{output}" + def add_image_content(image, path, name): + output = join(path, name) + image_output = f"${{build_root}}/{image}/{output}" build.build( rule = "cp", - outputs = [fatroot_output], - inputs = [source], - variables = { - "description": f"Installing {output}", - }) - - fatroot_content.append(fatroot_output) - build.newline() - - def add_fatroot_exe(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", - }) - - add_fatroot(intermediary, entry.output) - - def add_initrd_content(root, name): - output = join(root, name) - initrd_output = f"${{build_root}}/initrd_root/{output}" - - build.build( - rule = "cp", - outputs = [initrd_output], + outputs = [image_output], inputs = [f"${{build_root}}/{name}"], variables = { "description": f"Installing {name}", }) - initrd_content.append(initrd_output) + image_content[image].append(image_output) build.newline() - def add_initrd_stripped(root, entry): + def add_image_content_exe(image, path, entry): input_path = f"${{build_root}}/{entry.target}/{entry.output}" intermediary = f"${{build_root}}/{entry.output}" @@ -141,87 +104,108 @@ class Project: "debug": f"${{build_root}}/.debug/{entry.output}.debug", }) - add_initrd_content(root, entry.output) + 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") - add_fatroot_exe(manifest.kernel) - add_fatroot_exe(manifest.init) for program in manifest.panics: - add_fatroot_exe(program) + try_add_manifest_module(program, "panic", "fatroot") for program in manifest.services: - add_initrd_stripped("jsix/services", program) + try_add_manifest_module(program, "services", "initrd_root", "jsix/services") for program in manifest.drivers: - add_initrd_stripped("jsix/drivers", program) + try_add_manifest_module(program, "drivers", "initrd_root", "jsix/drivers") for program in manifest.libs: - add_initrd_stripped("jsix/lib", program) + try_add_manifest_module(program, "libs", "initrd_root", "jsix/lib") - syms = manifest.add_data("symbol_table.dat", - "Symbol table", ("symbols",)) + extra_regen_outputs = [] - 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)}"], - ) - fatroot_content.append(syms_out) - manifest.symbols = syms_file - - 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) - - initrd = str(fatroot / manifest.location / manifest.initrd["name"]) - build.build( - rule = "makeinitrd", - outputs = [initrd], - inputs = [str(initrdroot)], - implicit = initrd_content + ["${source_root}/scripts/mkj6romfs.py"], - variables = {"format": manifest.initrd["format"]}, - ) - build.newline() - - fatroot_content.append(initrd) - - build.build( - rule = "makefat", - outputs = ["${build_root}/jsix.img"], - inputs = ["${source_root}/assets/diskbase.img"], - implicit = fatroot_content + [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]}" + if 'boot' in modules.used: + bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi" build.build( rule = "cp", - outputs = [out], - inputs = [str(p)], - variables = {"name": name}, - ) - build.default([out]) + 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", @@ -233,7 +217,7 @@ class Project: regen_implicits = \ [f"{self.root}/configure", str(manifest_file)] + \ - [str(mod.modfile) for mod in modules.values()] + [str(mod.modfile) for mod in modules.used.values()] regen_implicits += list(map(str, config_deps)) @@ -242,24 +226,24 @@ class Project: outputs = [compdb], implicit = regen_implicits, ) - build.default([compdb]) 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.values()] + - [f"{target}/target.ninja" for target in targets] + - [boot_config], + [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(["${build_root}/jsix.img"]) + build.default(default_builds) - for target in targets: - mods = [m.name for m in modules.values() if target in m.targets] + for target in modules.targets: + mods = modules.target_mods(target) targetdir = output / target targetdir.mkdir(exist_ok=True) @@ -290,4 +274,4 @@ class Project: build.newline() for mod in mods: - build.subninja(f"module.{mod}.ninja") + build.subninja(f"module.{mod.name}.ninja") diff --git a/src/boot/boot.module b/src/boot/boot.module index 82c6bad..e4fb459 100644 --- a/src/boot/boot.module +++ b/src/boot/boot.module @@ -3,7 +3,7 @@ boot = module("boot", kind = "exe", outfile = "boot.efi", - targets = [ "boot" ], + target = "boot", deps = [ "cpu", "elf", "util", "bootproto" ], static = True, sources = [ diff --git a/src/kernel/kernel.module b/src/kernel/kernel.module index f7010dc..051a627 100644 --- a/src/kernel/kernel.module +++ b/src/kernel/kernel.module @@ -3,7 +3,7 @@ kernel = module("kernel", default = True, basename = "jsix", - targets = [ "kernel" ], + target = "kernel", description = "jsix kernel", deps = [ "util", "cpu", "bootproto", "j6", "acpi" ], static = True, diff --git a/src/kernel/panic.serial/serial.module b/src/kernel/panic.serial/serial.module index 88a6306..1f5cc5f 100644 --- a/src/kernel/panic.serial/serial.module +++ b/src/kernel/panic.serial/serial.module @@ -1,7 +1,7 @@ # vim: ft=python panic = module("panic.serial", - targets = [ "kernel" ], + target = "kernel", deps = [ "util", "elf", "kernel" ], static = True, includes = [ ".." ], diff --git a/src/libraries/j6/j6.module b/src/libraries/j6/j6.module index 4cea203..714c952 100644 --- a/src/libraries/j6/j6.module +++ b/src/libraries/j6/j6.module @@ -14,10 +14,14 @@ j6 = module("j6", "protocols/service_locator.cpp", "protocols/vfs.cpp", "ring_buffer.cpp", - "syscalls.s.cog", "sysconf.cpp.cog", "syslog.cpp", ], + arch_source = { + "amd64": [ + "syscalls.s.cog", + ], + }, public_headers = [ "j6/cap_flags.h.cog", "j6/channel.hh", diff --git a/src/user/6s/6s.module b/src/user/6s/6s.module index b9a908f..c5c71d9 100644 --- a/src/user/6s/6s.module +++ b/src/user/6s/6s.module @@ -1,7 +1,7 @@ # vim: ft=python module("6s", - targets = [ "user" ], + target = "user", deps = [ "libc", "edit" ], description = "j6 shell", sources = [ diff --git a/src/user/drv.uart/uart.module b/src/user/drv.uart/uart.module index 9f2562f..9390d6d 100644 --- a/src/user/drv.uart/uart.module +++ b/src/user/drv.uart/uart.module @@ -1,7 +1,7 @@ # vim: ft=python module("drv.uart", - targets = [ "user" ], + target = "user", deps = [ "libc", "util" ], description = "UART driver", drivers = [ "pc.uart" ], diff --git a/src/user/drv.uefi_fb/uefi_fb.module b/src/user/drv.uefi_fb/uefi_fb.module index d3afb8e..0e1d0b0 100644 --- a/src/user/drv.uefi_fb/uefi_fb.module +++ b/src/user/drv.uefi_fb/uefi_fb.module @@ -1,7 +1,7 @@ # vim: ft=python module("drv.uefi_fb", - targets = [ "user" ], + target = "user", deps = [ "libc", "bootproto" ], description = "UEFI framebuffer driver", drivers = [ "uefi.fb" ], diff --git a/src/user/ld.so/ld.so.module b/src/user/ld.so/ld.so.module index 5942274..50c0ab3 100644 --- a/src/user/ld.so/ld.so.module +++ b/src/user/ld.so/ld.so.module @@ -4,7 +4,7 @@ ldso = module("ld.so", kind = "lib", static = True, basename = "ld", - targets = [ "user" ], + target = "user", deps = [ "libc", "util", "elf" ], description = "Dynamic Linker", sources = [ diff --git a/src/user/srv.init/init.module b/src/user/srv.init/init.module index 6ce82a7..b6da096 100644 --- a/src/user/srv.init/init.module +++ b/src/user/srv.init/init.module @@ -1,7 +1,7 @@ # vim: ft=python init = module("srv.init", - targets = [ "init" ], + target = "init", deps = [ "libc", "elf", "bootproto", "zstd", "acpi", "pci" ], static = True, description = "Init server", diff --git a/src/user/srv.logger/logger.module b/src/user/srv.logger/logger.module index b369d6a..a89d002 100644 --- a/src/user/srv.logger/logger.module +++ b/src/user/srv.logger/logger.module @@ -1,7 +1,7 @@ # vim: ft=python module("srv.logger", - targets = [ "user" ], + target = "user", deps = [ "libc" ], description = "Logging server", sources = [ diff --git a/src/user/test_runner/test_runner.module b/src/user/test_runner/test_runner.module index 7e68c05..99bb976 100644 --- a/src/user/test_runner/test_runner.module +++ b/src/user/test_runner/test_runner.module @@ -1,7 +1,7 @@ # vim: ft=python module("test_runner", - targets = [ "user" ], + target = "user", deps = [ "libc", "util" ], description = "Unit test runner", sources = [ diff --git a/src/user/testapp/testapp.module b/src/user/testapp/testapp.module index 3a3a99a..2e57242 100644 --- a/src/user/testapp/testapp.module +++ b/src/user/testapp/testapp.module @@ -1,7 +1,7 @@ # vim: ft=python module("testapp", - targets = [ "user" ], + target = "user", deps = [ "libc" ], description = "Testbed app", sources = [