cutekit/cutekit/builder.py

161 lines
4.3 KiB
Python

import os
import logging
from typing import TextIO
from cutekit.model import Props
from cutekit.ninja import Writer
from cutekit.context import ComponentInstance, Context, contextFor
from cutekit import shell, rules
logger = logging.getLogger(__name__)
def gen(out: TextIO, context: Context):
writer = Writer(out)
target = context.target
writer.comment("File generated by the build system, do not edit")
writer.newline()
writer.variable("builddir", context.builddir())
writer.separator("Tools")
writer.variable("cincs", " ".join(
map(lambda i: f"-I{i}", context.cincls())))
writer.variable("cdefs", " ".join(context.cdefs()))
writer.newline()
writer.rule("cp", "cp $in $out")
writer.newline()
for i in target.tools:
tool = target.tools[i]
rule = rules.rules[i]
writer.variable(i, tool.cmd)
writer.variable(
i + "flags", " ".join(rule.args + tool.args))
writer.rule(
i, f"{tool.cmd} {rule.rule.replace('$flags',f'${i}flags')}", depfile=rule.deps)
writer.newline()
writer.separator("Components")
all: list[str] = []
for instance in context.enabledInstances():
objects = instance.objsfiles()
assets = instance.resfiles()
writer.comment(f"Component: {instance.manifest.id}")
writer.comment(f"Resolved: {', '.join(instance.resolved)}")
for obj in objects:
r = rules.byFileIn(obj[0])
if r is None:
raise RuntimeError(f"Unknown rule for file {obj[0]}")
t = target.tools[r.id]
writer.build(obj[1], r.id, obj[0], order_only=t.files)
for asset in assets:
writer.build(asset[1], "cp", asset[0])
writer.newline()
if instance.isLib():
writer.build(instance.outfile(), "ar",
list(map(lambda o: o[1], objects)), implicit=list(map(lambda o: o[1], assets)))
else:
libraries: list[str] = []
for req in instance.resolved:
reqInstance = context.componentByName(req)
if reqInstance is None:
raise RuntimeError(f"Component {req} not found")
if not reqInstance.isLib():
raise RuntimeError(f"Component {req} is not a library")
libraries.append(reqInstance.outfile())
writer.build(instance.outfile(), "ld", list(
map(lambda o: o[1], objects)) + libraries, implicit=list(map(lambda o: o[1], assets)))
all.append(instance.outfile())
writer.newline()
writer.separator("Phony targets")
writer.build("all", "phony", all)
writer.default("all")
def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentInstance:
context = contextFor(targetSpec, props)
shell.mkdir(context.builddir())
ninjaPath = os.path.join(context.builddir(), "build.ninja")
with open(ninjaPath, "w") as f:
gen(f, context)
instance = context.componentByName(componentSpec)
if instance is None:
raise RuntimeError(f"Component {componentSpec} not found")
if not instance.enabled:
raise RuntimeError(
f"Component {componentSpec} is disabled: {instance.disableReason}")
shell.exec(f"ninja", "-f", ninjaPath, instance.outfile())
return instance
class Paths:
bin: str
lib: str
obj: str
def __init__(self, bin: str, lib: str, obj: str):
self.bin = bin
self.lib = lib
self.obj = obj
def buildAll(targetSpec: str) -> Context:
context = contextFor(targetSpec)
shell.mkdir(context.builddir())
ninjaPath = os.path.join(context.builddir(), "build.ninja")
with open(ninjaPath, "w") as f:
gen(f, context)
shell.exec(f"ninja", "-v", "-f", ninjaPath)
return context
def testAll(targetSpec: str):
context = contextFor(targetSpec)
shell.mkdir(context.builddir())
ninjaPath = os.path.join(context.builddir(), "build.ninja")
with open(ninjaPath, "w") as f:
gen(f, context)
shell.exec(f"ninja", "-v", "-f", ninjaPath, "all")
for instance in context.enabledInstances():
if instance.isLib():
continue
if instance.id().endswith("-tests"):
print(f"Running {instance.id()}")
shell.exec(instance.outfile())