mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
Since we have a DSL for specifying syscalls, we can create a verificaton method for each syscall that can cover most argument (and eventually capability) verification instead of doing it piecemeal in each syscall implementation, which can be more error-prone. Now a new _syscall_verify_* function exists for every syscall, which calls the real implementation. The syscall table for the syscall handler now maps to these verify functions. Other changes: - Updated the definition grammar to allow options to have a "key:value" style, to eventually support capabilities. - Added an "optional" option for parameters that says a syscall will accept a null value. - Some bonnibel fixes, as definition file changes weren't always properly causing updates in the build dep graph. - The syscall implementation function signatures are no longer exposed in syscall.h. Also, the unused syscall enum has been removed.
219 lines
6.9 KiB
Python
219 lines
6.9 KiB
Python
def resolve(path):
|
|
if path.startswith('/') or path.startswith('$'):
|
|
return path
|
|
from pathlib import Path
|
|
return str(Path(path).resolve())
|
|
|
|
class Module:
|
|
__fields = {
|
|
# name: (type, default)
|
|
"kind": (str, "exe"),
|
|
"output": (str, None),
|
|
"targets": (set, ()),
|
|
"deps": (set, ()),
|
|
"includes": (tuple, ()),
|
|
"sources": (tuple, ()),
|
|
"variables": (dict, ()),
|
|
"default": (bool, False),
|
|
"location": (str, "jsix"),
|
|
"description": (str, None),
|
|
}
|
|
|
|
def __init__(self, name, modfile, root, **kwargs):
|
|
from .source import Source
|
|
|
|
# Required fields
|
|
self.root = root
|
|
self.name = name
|
|
self.modfile = modfile
|
|
|
|
for name, data in self.__fields.items():
|
|
ctor, default = data
|
|
value = kwargs.get(name, default)
|
|
if value is not None:
|
|
value = ctor(value)
|
|
|
|
setattr(self, name, value)
|
|
|
|
for name in kwargs:
|
|
if not name in self.__fields:
|
|
raise AttributeError(f"No attribute named {name} on Module")
|
|
|
|
# Turn strings into real Source objects
|
|
self.sources = [Source(root, f) for f in self.sources]
|
|
|
|
# Filled by Module.update
|
|
self.depmods = set()
|
|
|
|
def __str__(self):
|
|
return "Module {} {}\n\t{}".format(self.kind, self.name, "\n\t".join(map(str, self.sources)))
|
|
|
|
@property
|
|
def output(self):
|
|
if self.__output is not None:
|
|
return self.__output
|
|
|
|
if self.kind == "lib":
|
|
return f"lib{self.name}.a"
|
|
else:
|
|
return f"{self.name}.elf"
|
|
|
|
@output.setter
|
|
def output(self, value):
|
|
self.__output = value
|
|
|
|
@classmethod
|
|
def update(cls, mods):
|
|
from . import BonnibelError
|
|
|
|
for mod in mods.values():
|
|
for dep in mod.deps:
|
|
if not dep in mods:
|
|
raise BonnibelError(f"module '{mod.name}' references unknown module '{dep}'")
|
|
|
|
depmod = mods[dep]
|
|
mod.depmods.add(depmod)
|
|
|
|
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):
|
|
filename = str(output / f"{self.name}.ninja")
|
|
|
|
with open(filename, "w") as buildfile:
|
|
from pathlib import Path
|
|
from ninja.ninja_syntax import Writer
|
|
|
|
build = Writer(buildfile)
|
|
|
|
build.comment("This file is automatically generated by bonnibel")
|
|
build.newline()
|
|
|
|
build.variable("module_dir", f"${{target_dir}}/{self.name}.dir")
|
|
|
|
for key, value in self.variables.items():
|
|
build.variable(key, value)
|
|
build.newline()
|
|
|
|
includes = [self.root, "${module_dir}"]
|
|
for include in self.includes:
|
|
p = Path(include)
|
|
if p.is_absolute():
|
|
if not p in includes:
|
|
includes.append(str(p.resolve()))
|
|
elif include != ".":
|
|
includes.append(str(self.root / p))
|
|
includes.append(f"${{module_dir}}/{p}")
|
|
|
|
libs = []
|
|
child_deps = []
|
|
order_only = []
|
|
closed = set()
|
|
children = set(self.depmods)
|
|
while children:
|
|
child = children.pop()
|
|
closed.add(child)
|
|
|
|
includes += [f"${{target_dir}}/{child.name}.dir/{i}" for i in child.includes]
|
|
includes += [f"{child.root}/{i}" for i in child.includes]
|
|
|
|
child_deps.append(f"${{target_dir}}/{child.name}.dir/.parse_dep.phony")
|
|
|
|
if child.kind == "lib":
|
|
libs.append(f"${{target_dir}}/{child.output}")
|
|
else:
|
|
order_only.append(f"${{target_dir}}/{child.output}")
|
|
children |= {m for m in child.depmods if not m in closed}
|
|
|
|
if includes:
|
|
build.variable("ccflags", ["${ccflags}"] + [f"-I{i}" for i in includes])
|
|
build.variable("asflags", ["${asflags}"] + [f"-I{i}" for i in includes])
|
|
|
|
if libs:
|
|
build.variable("libs", ["${libs}"] + libs)
|
|
|
|
inputs = []
|
|
parse_deps = []
|
|
parse_dep = "${module_dir}/.parse_dep.phony"
|
|
|
|
for start in self.sources:
|
|
source = start
|
|
|
|
while source and source.action:
|
|
output = source.output
|
|
|
|
if source.action.parse_deps:
|
|
oodeps = [parse_dep]
|
|
else:
|
|
oodeps = []
|
|
|
|
if source.action.rule:
|
|
build.newline()
|
|
build.build(
|
|
rule = source.action.rule,
|
|
outputs = output.input,
|
|
inputs = source.input,
|
|
implicit = list(map(resolve, source.deps)) +
|
|
list(source.action.deps),
|
|
order_only = oodeps,
|
|
variables = {"name": source.name},
|
|
)
|
|
|
|
elif source.action.implicit:
|
|
parse_deps.append(source.input)
|
|
|
|
else:
|
|
inputs.append(source.input)
|
|
|
|
source = output
|
|
|
|
build.newline()
|
|
build.build(
|
|
rule = "touch",
|
|
outputs = [parse_dep],
|
|
implicit = child_deps,
|
|
order_only = parse_deps,
|
|
)
|
|
|
|
output = f"${{target_dir}}/{self.output}"
|
|
dump = f"${{target_dir}}/{self.output}.dump"
|
|
build.newline()
|
|
build.build(
|
|
rule = self.kind,
|
|
outputs = output,
|
|
inputs = inputs,
|
|
implicit = libs,
|
|
order_only = order_only,
|
|
)
|
|
|
|
build.newline()
|
|
build.build(
|
|
rule = "dump",
|
|
outputs = dump,
|
|
inputs = 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 Source
|
|
s = Source(self.root, path, **kwargs)
|
|
self.sources.append(s)
|
|
return str(s.output)
|
|
|
|
def add_depends(self, paths, deps):
|
|
for source in self.sources:
|
|
if source.name in paths:
|
|
source.add_deps(deps)
|