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")
for instance in context.instances:
objects = instance.objsfiles()
objects = instance.objsfiles(context)
writer.comment(f"Component: {instance.manifest.id}")
writer.comment(f"Resolved: {', '.join(instance.resolved)}")
@ -53,7 +53,7 @@ def gen(out: TextIO, context: Context):
writer.newline()
if instance.isLib():
writer.build(instance.libfile(), "ar",
writer.build(instance.libfile(context), "ar",
list(map(lambda o: o[1], objects)))
else:
libraries: list[str] = []
@ -67,9 +67,9 @@ def gen(out: TextIO, context: Context):
if not reqInstance.isLib():
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)
writer.newline()
@ -79,32 +79,47 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> str:
context = contextFor(targetSpec, props)
target = context.target
shell.mkdir(target.builddir())
ninjaPath = f"{target.builddir()}/build.ninja"
shell.mkdir(context.builddir())
ninjaPath = f"{context.builddir()}/build.ninja"
with open(ninjaPath, "w") as f:
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")
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)
target = context.target
shell.mkdir(target.builddir())
ninjaPath = f"{target.builddir()}/build.ninja"
shell.mkdir(context.builddir())
ninjaPath = f"{context.builddir()}/build.ninja"
with open(ninjaPath, "w") as f:
gen(f, context)
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 osdk.model import TargetManifest, ComponentManifest, Props, Type
from osdk.model import TargetManifest, ComponentManifest, Props, Type, Tool
from osdk.logger import Logger
from osdk import const, shell, jexpr, utils
from osdk import const, shell, jexpr, utils, rules
logger = Logger("context")
class IContext(Protocol):
def builddir(self) -> str:
...
class ComponentInstance:
target: TargetManifest
manifest: ComponentManifest
sources: list[str] = []
resolved: list[str] = []
def __init__(
self,
target: TargetManifest,
manifest: ComponentManifest,
sources: list[str],
resolved: list[str]):
self.target = target
self.manifest = manifest
self.sources = sources
self.resolved = resolved
@ -29,26 +31,27 @@ class ComponentInstance:
def isLib(self):
return self.manifest.type == Type.LIB
def binfile(self) -> str:
return f"{self.target.builddir()}/bin/{self.manifest.id}.out"
def binfile(self, context: IContext) -> str:
return f"{context.builddir()}/bin/{self.manifest.id}.out"
def objdir(self) -> str:
return f"{self.target.builddir()}/obj/{self.manifest.id}"
def objdir(self, context: IContext) -> str:
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(
map(
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))
def libfile(self) -> str:
return f"{self.target.builddir()}/lib/{self.manifest.id}.a"
def libfile(self, context: IContext) -> str:
return f"{context.builddir()}/lib/{self.manifest.id}.a"
def outfile(self) -> str:
def outfile(self, context: IContext) -> str:
if self.isLib():
return self.libfile()
return self.binfile()
return self.libfile(context)
else:
return self.binfile(context)
def cinclude(self) -> str:
if "cpp-root-include" in self.manifest.props:
@ -57,13 +60,15 @@ class ComponentInstance:
return str(Path(self.manifest.dirname()).parent)
class Context:
class Context(IContext):
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.instances = instances
self.tools = tools
def componentByName(self, name: str) -> ComponentInstance | None:
result = list(filter(lambda x: x.manifest.id == name, self.instances))
@ -79,8 +84,11 @@ class Context:
def cdefs(self) -> list[str]:
return self.target.cdefs()
def hashid(self) -> str:
return utils.hash((self.target.props, str(self.tools)))[0:8]
def builddir(self) -> str:
return self.target.builddir()
return f"{const.BUILD_DIR}/{self.target.id}-{self.hashid()[:8]}"
def loadAllTargets() -> list[TargetManifest]:
@ -172,7 +180,7 @@ def instanciate(componentSpec: str, components: list[ComponentManifest], target:
if not enabled:
return None
return ComponentInstance(target, manifest, sources, resolved[1:])
return ComponentInstance(manifest, sources, resolved[1:])
def contextFor(targetSpec: str, props: Props) -> Context:
@ -180,6 +188,24 @@ def contextFor(targetSpec: str, props: Props) -> Context:
target = loadTarget(targetSpec)
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)
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(
target,
instances
instances,
tools,
)

View file

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