Inject tools from components.
This commit is contained in:
parent
6c8fdb8521
commit
09d8b65cfe
|
@ -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",
|
||||||
|
)
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
180
osdk/model.py
180
osdk/model.py
|
@ -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)
|
|
||||||
|
|
Loading…
Reference in a new issue