Inject tools from components.

This commit is contained in:
Sleepy Monax 2023-02-07 12:40:00 +01:00
parent 6c8fdb8521
commit 09d8b65cfe
3 changed files with 144 additions and 152 deletions

View file

@ -39,7 +39,7 @@ def gen(out: TextIO, context: Context):
writer.separator("Components") writer.separator("Components")
for instance in context.instances: for instance in context.instances:
objects = instance.objsfiles() objects = instance.objsfiles(context)
writer.comment(f"Component: {instance.manifest.id}") writer.comment(f"Component: {instance.manifest.id}")
writer.comment(f"Resolved: {', '.join(instance.resolved)}") writer.comment(f"Resolved: {', '.join(instance.resolved)}")
@ -53,7 +53,7 @@ def gen(out: TextIO, context: Context):
writer.newline() writer.newline()
if instance.isLib(): if instance.isLib():
writer.build(instance.libfile(), "ar", writer.build(instance.libfile(context), "ar",
list(map(lambda o: o[1], objects))) list(map(lambda o: o[1], objects)))
else: else:
libraries: list[str] = [] libraries: list[str] = []
@ -67,9 +67,9 @@ def gen(out: TextIO, context: Context):
if not reqInstance.isLib(): if not reqInstance.isLib():
raise Exception(f"Component {req} is not a library") raise Exception(f"Component {req} is not a library")
libraries.append(reqInstance.outfile()) libraries.append(reqInstance.outfile(context))
writer.build(instance.binfile(), "ld", writer.build(instance.binfile(context), "ld",
list(map(lambda o: o[1], objects)) + libraries) list(map(lambda o: o[1], objects)) + libraries)
writer.newline() writer.newline()
@ -79,32 +79,47 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> str:
context = contextFor(targetSpec, props) context = contextFor(targetSpec, props)
target = context.target target = context.target
shell.mkdir(target.builddir()) shell.mkdir(context.builddir())
ninjaPath = f"{target.builddir()}/build.ninja" ninjaPath = f"{context.builddir()}/build.ninja"
with open(ninjaPath, "w") as f: with open(ninjaPath, "w") as f:
gen(f, context) gen(f, context)
component = context.componentByName(componentSpec) instance = context.componentByName(componentSpec)
if component is None: if instance is None:
raise Exception(f"Component {componentSpec} not found") raise Exception(f"Component {componentSpec} not found")
shell.exec(f"ninja", "-v", "-f", ninjaPath, component.outfile()) shell.exec(f"ninja", "-v", "-f", ninjaPath, instance.outfile(context))
return component.outfile() return instance.outfile(context)
def buildAll(targetSpec: str, props: Props = {}) -> str: 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, props: Props = {}) -> Paths:
context = contextFor(targetSpec, props) context = contextFor(targetSpec, props)
target = context.target target = context.target
shell.mkdir(target.builddir()) shell.mkdir(context.builddir())
ninjaPath = f"{target.builddir()}/build.ninja" ninjaPath = f"{context.builddir()}/build.ninja"
with open(ninjaPath, "w") as f: with open(ninjaPath, "w") as f:
gen(f, context) gen(f, context)
shell.exec(f"ninja", "-v", "-f", ninjaPath) shell.exec(f"ninja", "-v", "-f", ninjaPath)
return target.builddir() return Paths(
context.builddir() + "/bin",
context.builddir() + "/lib",
context.builddir() + "/obj",
)

View file

@ -1,27 +1,29 @@
from typing import cast from typing import cast, Protocol
from pathlib import Path from pathlib import Path
from osdk.model import TargetManifest, ComponentManifest, Props, Type from osdk.model import TargetManifest, ComponentManifest, Props, Type, Tool
from osdk.logger import Logger from osdk.logger import Logger
from osdk import const, shell, jexpr, utils from osdk import const, shell, jexpr, utils, rules
logger = Logger("context") logger = Logger("context")
class IContext(Protocol):
def builddir(self) -> str:
...
class ComponentInstance: class ComponentInstance:
target: TargetManifest
manifest: ComponentManifest manifest: ComponentManifest
sources: list[str] = [] sources: list[str] = []
resolved: list[str] = [] resolved: list[str] = []
def __init__( def __init__(
self, self,
target: TargetManifest,
manifest: ComponentManifest, manifest: ComponentManifest,
sources: list[str], sources: list[str],
resolved: list[str]): resolved: list[str]):
self.target = target
self.manifest = manifest self.manifest = manifest
self.sources = sources self.sources = sources
self.resolved = resolved self.resolved = resolved
@ -29,26 +31,27 @@ class ComponentInstance:
def isLib(self): def isLib(self):
return self.manifest.type == Type.LIB return self.manifest.type == Type.LIB
def binfile(self) -> str: def binfile(self, context: IContext) -> str:
return f"{self.target.builddir()}/bin/{self.manifest.id}.out" return f"{context.builddir()}/bin/{self.manifest.id}.out"
def objdir(self) -> str: def objdir(self, context: IContext) -> str:
return f"{self.target.builddir()}/obj/{self.manifest.id}" return f"{context.builddir()}/obj/{self.manifest.id}"
def objsfiles(self) -> list[tuple[str, str]]: def objsfiles(self, context: IContext) -> list[tuple[str, str]]:
return list( return list(
map( map(
lambda s: ( lambda s: (
s, f"{self.objdir()}/{s.replace(self.manifest.dirname() + '/', '')}.o"), s, f"{self.objdir(context)}/{s.replace(self.manifest.dirname() + '/', '')}.o"),
self.sources)) self.sources))
def libfile(self) -> str: def libfile(self, context: IContext) -> str:
return f"{self.target.builddir()}/lib/{self.manifest.id}.a" return f"{context.builddir()}/lib/{self.manifest.id}.a"
def outfile(self) -> str: def outfile(self, context: IContext) -> str:
if self.isLib(): if self.isLib():
return self.libfile() return self.libfile(context)
return self.binfile() else:
return self.binfile(context)
def cinclude(self) -> str: def cinclude(self) -> str:
if "cpp-root-include" in self.manifest.props: if "cpp-root-include" in self.manifest.props:
@ -57,13 +60,15 @@ class ComponentInstance:
return str(Path(self.manifest.dirname()).parent) return str(Path(self.manifest.dirname()).parent)
class Context: class Context(IContext):
target: TargetManifest target: TargetManifest
instances: list[ComponentInstance] = [] instances: list[ComponentInstance]
tools: dict[str, Tool]
def __init__(self, target: TargetManifest, instances: list[ComponentInstance]): def __init__(self, target: TargetManifest, instances: list[ComponentInstance], tools: dict[str, Tool]):
self.target = target self.target = target
self.instances = instances self.instances = instances
self.tools = tools
def componentByName(self, name: str) -> ComponentInstance | None: def componentByName(self, name: str) -> ComponentInstance | None:
result = list(filter(lambda x: x.manifest.id == name, self.instances)) result = list(filter(lambda x: x.manifest.id == name, self.instances))
@ -79,8 +84,11 @@ class Context:
def cdefs(self) -> list[str]: def cdefs(self) -> list[str]:
return self.target.cdefs() return self.target.cdefs()
def hashid(self) -> str:
return utils.hash((self.target.props, str(self.tools)))[0:8]
def builddir(self) -> str: def builddir(self) -> str:
return self.target.builddir() return f"{const.BUILD_DIR}/{self.target.id}-{self.hashid()[:8]}"
def loadAllTargets() -> list[TargetManifest]: def loadAllTargets() -> list[TargetManifest]:
@ -172,7 +180,7 @@ def instanciate(componentSpec: str, components: list[ComponentManifest], target:
if not enabled: if not enabled:
return None return None
return ComponentInstance(target, manifest, sources, resolved[1:]) return ComponentInstance(manifest, sources, resolved[1:])
def contextFor(targetSpec: str, props: Props) -> Context: def contextFor(targetSpec: str, props: Props) -> Context:
@ -180,6 +188,24 @@ def contextFor(targetSpec: str, props: Props) -> Context:
target = loadTarget(targetSpec) target = loadTarget(targetSpec)
components = loadAllComponents() components = loadAllComponents()
tools: dict[str, Tool] = {}
for toolSpec in target.tools:
tool = target.tools[toolSpec]
tools[toolSpec] = Tool(
strict=False,
cmd=tool.cmd,
args=tool.args,
files=tool.files)
tools[toolSpec].args += rules.rules[toolSpec].args
for component in components:
for toolSpec in component.tools:
tool = component.tools[toolSpec]
tools[toolSpec].args += tool.args
components = filterDisabled(components, target) components = filterDisabled(components, target)
instances = cast(list[ComponentInstance], list(filter(lambda e: e != None, map(lambda c: instanciate( instances = cast(list[ComponentInstance], list(filter(lambda e: e != None, map(lambda c: instanciate(
@ -187,5 +213,6 @@ def contextFor(targetSpec: str, props: Props) -> Context:
return Context( return Context(
target, target,
instances instances,
tools,
) )

View file

@ -14,28 +14,35 @@ Props = dict[str, Any]
class Type(Enum): class Type(Enum):
UNKNOWN = "unknown"
TARGET = "target" TARGET = "target"
LIB = "lib" LIB = "lib"
EXE = "exe" EXE = "exe"
class Manifest: class Manifest:
id: str id: str = ""
type: Type type: Type = Type.UNKNOWN
path: str = "" path: str = ""
def __init__(self, json: Json, path: str): def __init__(self, json: Json = None, path: str = "", strict=True, **kwargs):
if not "id" in json: if json is not None:
raise ValueError("Missing id") if not "id" in json:
raise ValueError("Missing id")
self.id = json["id"] self.id = json["id"]
if not "type" in json: if not "type" in json and strict:
raise ValueError("Missing type") raise ValueError("Missing type")
self.type = Type(json["type"]) self.type = Type(json["type"])
self.path = path self.path = path
elif strict:
raise ValueError("Missing json")
for key in kwargs:
setattr(self, key, kwargs[key])
def __str__(self): def __str__(self):
return f"Manifest(id={self.id}, type={self.type}, path={self.path})" return f"Manifest(id={self.id}, type={self.type}, path={self.path})"
@ -48,23 +55,34 @@ class Manifest:
class Tool: class Tool:
cmd: str cmd: str = ""
args: list[str] args: list[str] = []
files: list[str] = [] files: list[str] = []
def __init__(self, json: Json): def __init__(self, json: Json = None, strict=True, **kwargs):
if not "cmd" in json: if json is not None:
raise ValueError("Missing cmd") if not "cmd" in json and strict:
self.cmd = json["cmd"] raise ValueError("Missing cmd")
if not "args" in json: self.cmd = json.get("cmd", self.cmd)
raise ValueError("Missing args")
self.args = json["args"]
self.files = json.get("files", []) if not "args" in json and strict:
raise ValueError("Missing args")
self.args = json.get("args", [])
self.files = json.get("files", [])
elif strict:
raise ValueError("Missing json")
for key in kwargs:
setattr(self, key, kwargs[key])
def __str__(self): def __str__(self):
return f"Tool(cmd={self.cmd}, args={self.args})" return f"Tool(cmd={self.cmd}, args={self.args}, files={self.files})"
def __repr__(self):
return f"Tool({self.cmd})"
class TargetManifest(Manifest): class TargetManifest(Manifest):
@ -72,29 +90,21 @@ class TargetManifest(Manifest):
tools: dict[str, Tool] tools: dict[str, Tool]
routing: dict[str, str] routing: dict[str, str]
def __init__(self, json: Json, path: str): def __init__(self, json: Json = None, path: str = "", strict=True, **kwargs):
super().__init__(json, path) if json is not None:
if not "props" in json and strict:
raise ValueError("Missing props")
if not "props" in json: self.props = json["props"]
raise ValueError("Missing props")
self.props = json["props"] if not "tools" in json and strict:
raise ValueError("Missing tools")
if not "tools" in json: self.tools = {k: Tool(v) for k, v in json["tools"].items()}
raise ValueError("Missing tools")
self.tools = {k: Tool(v) for k, v in json["tools"].items()} self.routing = json.get("routing", {})
self.routing = json.get("routing", {}) super().__init__(json, path, strict, **kwargs)
def __str__(self):
return f"TargetManifest(" + \
"id={self.id}, " + \
"type={self.type}, " + \
"props={self.props}, " + \
"tools={self.tools}, " + \
"path={self.path}" + \
")"
def __repr__(self): def __repr__(self):
return f"TargetManifest({self.id})" return f"TargetManifest({self.id})"
@ -102,15 +112,6 @@ class TargetManifest(Manifest):
def route(self, componentSpec: str): def route(self, componentSpec: str):
return self.routing[componentSpec] if componentSpec in self.routing else componentSpec return self.routing[componentSpec] if componentSpec in self.routing else componentSpec
def hashid(self) -> str:
return utils.hash((self.props, self.tools), cls=ModelEncoder)
def builddir(self) -> str:
return f"{const.BUILD_DIR}/{self.id}-{self.hashid()[:8]}"
def patch(self, toolSpec: str, args: list[str]):
self.tools[toolSpec].args += args
def cdefs(self) -> list[str]: def cdefs(self) -> list[str]:
defines: list[str] = [] defines: list[str] = []
@ -128,31 +129,24 @@ class TargetManifest(Manifest):
class ComponentManifest(Manifest): class ComponentManifest(Manifest):
decription: str decription: str = "(No description)"
props: Props props: Props = {}
enableIf: dict[str, list[Any]] tools: dict[str, Tool] = {}
requires: list[str] enableIf: dict[str, list[Any]] = {}
provides: list[str] requires: list[str] = []
provides: list[str] = []
def __init__(self, json: Json, path: str): def __init__(self, json: Json = None, path: str = "", strict=True, **kwargs):
super().__init__(json, path) if json is not None:
self.decription = json.get("description", self.decription)
self.props = json.get("props", self.props)
self.tools = {k: Tool(v, strict=False)
for k, v in json.get("tools", {}).items()}
self.enableIf = json.get("enableIf", self.enableIf)
self.requires = json.get("requires", self.requires)
self.provides = json.get("provides", self.provides)
self.decription = json.get("description", "(No description)") super().__init__(json, path, strict, **kwargs)
self.props = json.get("props", {})
self.enableIf = json.get("enableIf", {})
self.requires = json.get("requires", [])
self.provides = json.get("provides", [])
def __str__(self):
return f"ComponentManifest(" + \
"id={self.id}, " + \
"type={self.type}, " + \
"description={self.decription}, " + \
"requires={self.requires}, " + \
"provides={self.provides}, " + \
"injects={self.injects}, " + \
"deps={self.deps}, " + \
"path={self.path})"
def __repr__(self): def __repr__(self):
return f"ComponentManifest({self.id})" return f"ComponentManifest({self.id})"
@ -170,47 +164,3 @@ class ComponentManifest(Manifest):
return False return False
return True return True
class ModelEncoder(JSONEncoder):
def default(self, o: Any):
if isinstance(o, Manifest):
return {
"id": o.id,
"type": o.type.value,
"path": o.path
}
if isinstance(o, Type):
return o.value
if isinstance(o, Tool):
return {
"cmd": o.cmd,
"args": o.args,
"files": o.files
}
if isinstance(o, TargetManifest):
return {
"id": o.id,
"type": o.type.value,
"props": o.props,
"tools": o.tools,
"routing": o.routing,
"path": o.path
}
if isinstance(o, ComponentManifest):
return {
"id": o.id,
"type": o.type.value,
"description": o.decription,
"props": o.props,
"enableIf": o.enableIf,
"requires": o.requires,
"provides": o.provides,
"path": o.path
}
return super().default(o)