Properly propagate props from the command line + Some code cleanup.

This commit is contained in:
Sleepy Monax 2023-10-22 14:19:59 +02:00
parent 6dd4a49043
commit 81f10de24e
10 changed files with 159 additions and 120 deletions

View file

@ -27,14 +27,14 @@ def setupLogger(verbose: bool):
level=logging.INFO, level=logging.INFO,
filename=logFile, filename=logFile,
filemode="w", filemode="w",
format=f"%(asctime)s %(levelname)s %(name)s: %(message)s", format="%(asctime)s %(levelname)s %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S", datefmt="%Y-%m-%d %H:%M:%S",
) )
def main() -> int: def main() -> int:
try: try:
a = parse(sys.argv[1:]) a = parse(sys.argv[1:])
setupLogger(a.consumeOpt("verbose", False) == True) setupLogger(a.consumeOpt("verbose", False) is True)
plugins.loadAll() plugins.loadAll()
cmds.exec(a) cmds.exec(a)
print() print()
@ -47,4 +47,4 @@ def main() -> int:
return 1 return 1
except KeyboardInterrupt: except KeyboardInterrupt:
print() print()
return 1 return 1

View file

@ -14,9 +14,10 @@ class Args:
def consumePrefix(self, prefix: str) -> dict[str, Value]: def consumePrefix(self, prefix: str) -> dict[str, Value]:
result: dict[str, Value] = {} result: dict[str, Value] = {}
for key, value in self.opts.items(): copy = self.opts.copy()
for key, value in copy.items():
if key.startswith(prefix): if key.startswith(prefix):
result[key[len(prefix):]] = value result[key[len(prefix) :]] = value
del self.opts[key] del self.opts[key]
return result return result

View file

@ -21,8 +21,7 @@ def gen(out: TextIO, context: Context):
writer.separator("Tools") writer.separator("Tools")
writer.variable("cincs", " ".join( writer.variable("cincs", " ".join(map(lambda i: f"-I{i}", context.cincls())))
map(lambda i: f"-I{i}", context.cincls())))
writer.variable("cdefs", " ".join(context.cdefs())) writer.variable("cdefs", " ".join(context.cdefs()))
@ -35,10 +34,12 @@ def gen(out: TextIO, context: Context):
tool = target.tools[i] tool = target.tools[i]
rule = rules.rules[i] rule = rules.rules[i]
writer.variable(i, tool.cmd) writer.variable(i, tool.cmd)
writer.variable( writer.variable(i + "flags", " ".join(rule.args + tool.args))
i + "flags", " ".join(rule.args + tool.args))
writer.rule( writer.rule(
i, f"{tool.cmd} {rule.rule.replace('$flags',f'${i}flags')}", depfile=rule.deps) i,
f"{tool.cmd} {rule.rule.replace('$flags',f'${i}flags')}",
depfile=rule.deps,
)
writer.newline() writer.newline()
writer.separator("Components") writer.separator("Components")
@ -56,7 +57,7 @@ def gen(out: TextIO, context: Context):
if r is None: if r is None:
raise RuntimeError(f"Unknown rule for file {obj[0]}") raise RuntimeError(f"Unknown rule for file {obj[0]}")
t = target.tools[r.id] t = target.tools[r.id]
writer.build(obj[1], r.id, obj[0], order_only=t.files) writer.build(obj[1], r.id, obj[0], order_only=t.files)
for asset in assets: for asset in assets:
writer.build(asset[1], "cp", asset[0]) writer.build(asset[1], "cp", asset[0])
@ -64,8 +65,12 @@ def gen(out: TextIO, context: Context):
writer.newline() writer.newline()
if instance.isLib(): if instance.isLib():
writer.build(instance.outfile(), "ar", writer.build(
list(map(lambda o: o[1], objects)), implicit=list(map(lambda o: o[1], assets))) instance.outfile(),
"ar",
list(map(lambda o: o[1], objects)),
implicit=list(map(lambda o: o[1], assets)),
)
else: else:
libraries: list[str] = [] libraries: list[str] = []
@ -80,8 +85,12 @@ def gen(out: TextIO, context: Context):
libraries.append(reqInstance.outfile()) libraries.append(reqInstance.outfile())
writer.build(instance.outfile(), "ld", list( writer.build(
map(lambda o: o[1], objects)) + libraries, implicit=list(map(lambda o: o[1], assets))) instance.outfile(),
"ld",
list(map(lambda o: o[1], objects)) + libraries,
implicit=list(map(lambda o: o[1], assets)),
)
all.append(instance.outfile()) all.append(instance.outfile())
@ -109,9 +118,10 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentIn
if not instance.enabled: if not instance.enabled:
raise RuntimeError( raise RuntimeError(
f"Component {componentSpec} is disabled: {instance.disableReason}") f"Component {componentSpec} is disabled: {instance.disableReason}"
)
shell.exec(f"ninja", "-f", ninjaPath, instance.outfile()) shell.exec("ninja", "-f", ninjaPath, instance.outfile())
return instance return instance
@ -127,8 +137,8 @@ class Paths:
self.obj = obj self.obj = obj
def buildAll(targetSpec: str) -> Context: def buildAll(targetSpec: str, props: Props = {}) -> Context:
context = contextFor(targetSpec) context = contextFor(targetSpec, props)
shell.mkdir(context.builddir()) shell.mkdir(context.builddir())
ninjaPath = os.path.join(context.builddir(), "build.ninja") ninjaPath = os.path.join(context.builddir(), "build.ninja")
@ -136,7 +146,7 @@ def buildAll(targetSpec: str) -> Context:
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("ninja", "-v", "-f", ninjaPath)
return context return context
@ -150,7 +160,7 @@ def testAll(targetSpec: str):
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, "all") shell.exec("ninja", "-v", "-f", ninjaPath, "all")
for instance in context.enabledInstances(): for instance in context.enabledInstances():
if instance.isLib(): if instance.isLib():

View file

@ -22,7 +22,13 @@ class Cmd:
callback: Callable[[Args], NoReturn] callback: Callable[[Args], NoReturn]
isPlugin: bool = False isPlugin: bool = False
def __init__(self, shortName: Optional[str], longName: str, helpText: str, callback: Callable[[Args], NoReturn]): def __init__(
self,
shortName: Optional[str],
longName: str,
helpText: str,
callback: Callable[[Args], NoReturn],
):
self.shortName = shortName self.shortName = shortName
self.longName = longName self.longName = longName
self.helpText = helpText self.helpText = helpText
@ -41,15 +47,15 @@ def append(cmd: Cmd):
def runCmd(args: Args): def runCmd(args: Args):
project.chdir() project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
"target", "host-" + shell.uname().machine)) props = args.consumePrefix("prop:")
componentSpec = args.consumeArg() componentSpec = args.consumeArg()
if componentSpec is None: if componentSpec is None:
raise RuntimeError("Component not specified") raise RuntimeError("Component not specified")
component = builder.build(componentSpec, targetSpec) component = builder.build(componentSpec, targetSpec, props)
os.environ["CK_TARGET"] = component.context.target.id os.environ["CK_TARGET"] = component.context.target.id
os.environ["CK_COMPONENT"] = component.id() os.environ["CK_COMPONENT"] = component.id()
@ -64,8 +70,7 @@ cmds += [Cmd("r", "run", "Run the target", runCmd)]
def testCmd(args: Args): def testCmd(args: Args):
project.chdir() project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
"target", "host-" + shell.uname().machine))
builder.testAll(targetSpec) builder.testAll(targetSpec)
@ -75,15 +80,15 @@ cmds += [Cmd("t", "test", "Run all test targets", testCmd)]
def debugCmd(args: Args): def debugCmd(args: Args):
project.chdir() project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
"target", "host-" + shell.uname().machine)) props = args.consumePrefix("prop:")
componentSpec = args.consumeArg() componentSpec = args.consumeArg()
if componentSpec is None: if componentSpec is None:
raise RuntimeError("Component not specified") raise RuntimeError("Component not specified")
component = builder.build(componentSpec, targetSpec) component = builder.build(componentSpec, targetSpec, props)
os.environ["CK_TARGET"] = component.context.target.id os.environ["CK_TARGET"] = component.context.target.id
os.environ["CK_COMPONENT"] = component.id() os.environ["CK_COMPONENT"] = component.id()
@ -98,15 +103,14 @@ cmds += [Cmd("d", "debug", "Debug the target", debugCmd)]
def buildCmd(args: Args): def buildCmd(args: Args):
project.chdir() project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
"target", "host-" + shell.uname().machine)) props = args.consumePrefix("prop:")
componentSpec = args.consumeArg() componentSpec = args.consumeArg()
if componentSpec is None: if componentSpec is None:
builder.buildAll(targetSpec) builder.buildAll(targetSpec, props)
else: else:
builder.build(componentSpec, targetSpec) builder.build(componentSpec, targetSpec, props)
cmds += [Cmd("b", "build", "Build the target", buildCmd)] cmds += [Cmd("b", "build", "Build the target", buildCmd)]
@ -120,16 +124,15 @@ def listCmd(args: Args):
vt100.title("Components") vt100.title("Components")
if len(components) == 0: if len(components) == 0:
print(f" (No components available)") print(" (No components available)")
else: else:
print(vt100.indent(vt100.wordwrap( print(vt100.indent(vt100.wordwrap(", ".join(map(lambda m: m.id, components)))))
", ".join(map(lambda m: m.id, components)))))
print() print()
vt100.title("Targets") vt100.title("Targets")
if len(targets) == 0: if len(targets) == 0:
print(f" (No targets available)") print(" (No targets available)")
else: else:
print(vt100.indent(vt100.wordwrap(", ".join(map(lambda m: m.id, targets))))) print(vt100.indent(vt100.wordwrap(", ".join(map(lambda m: m.id, targets)))))
@ -171,11 +174,12 @@ def helpCmd(args: Args):
pluginText = f"{vt100.CYAN}(plugin){vt100.RESET}" pluginText = f"{vt100.CYAN}(plugin){vt100.RESET}"
print( print(
f" {vt100.GREEN}{cmd.shortName or ' '}{vt100.RESET} {cmd.longName} - {cmd.helpText} {pluginText}") f" {vt100.GREEN}{cmd.shortName or ' '}{vt100.RESET} {cmd.longName} - {cmd.helpText} {pluginText}"
)
print() print()
vt100.title("Logging") vt100.title("Logging")
print(f" Logs are stored in:") print(" Logs are stored in:")
print(f" - {const.PROJECT_LOG_FILE}") print(f" - {const.PROJECT_LOG_FILE}")
print(f" - {const.GLOBAL_LOG_FILE}") print(f" - {const.GLOBAL_LOG_FILE}")
@ -193,17 +197,15 @@ cmds += [Cmd("v", "version", "Show current version", versionCmd)]
def graphCmd(args: Args): def graphCmd(args: Args):
project.chdir() project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
"target", "host-" + shell.uname().machine))
scope: Optional[str] = cast(Optional[str], args.tryConsumeOpt("scope")) scope: Optional[str] = cast(Optional[str], args.tryConsumeOpt("scope"))
onlyLibs: bool = args.consumeOpt("only-libs", False) == True onlyLibs: bool = args.consumeOpt("only-libs", False) is True
showDisabled: bool = args.consumeOpt("show-disabled", False) == True showDisabled: bool = args.consumeOpt("show-disabled", False) is True
context = contextFor(targetSpec) context = contextFor(targetSpec)
graph.view(context, scope=scope, showExe=not onlyLibs, graph.view(context, scope=scope, showExe=not onlyLibs, showDisabled=showDisabled)
showDisabled=showDisabled)
cmds += [Cmd("g", "graph", "Show dependency graph", graphCmd)] cmds += [Cmd("g", "graph", "Show dependency graph", graphCmd)]
@ -218,8 +220,9 @@ def grabExtern(extern: dict[str, Extern]):
continue continue
print(f"Installing {extSpec}-{ext.tag} from {ext.git}...") print(f"Installing {extSpec}-{ext.tag} from {ext.git}...")
shell.popen("git", "clone", "--depth", "1", "--branch", shell.popen(
ext.tag, ext.git, extPath) "git", "clone", "--depth", "1", "--branch", ext.tag, ext.git, extPath
)
if os.path.exists(os.path.join(extPath, "project.json")): if os.path.exists(os.path.join(extPath, "project.json")):
grabExtern(context.loadProject(extPath).extern) grabExtern(context.loadProject(extPath).extern)
@ -238,31 +241,33 @@ cmds += [Cmd("i", "install", "Install all the external packages", installCmd)]
def initCmd(args: Args): def initCmd(args: Args):
import requests import requests
repo = args.consumeOpt('repo', const.DEFAULT_REPO_TEMPLATES) repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES)
list = args.consumeOpt('list') list = args.consumeOpt("list")
template = args.consumeArg() template = args.consumeArg()
name = args.consumeArg() name = args.consumeArg()
logger.info("Fetching registry...") logger.info("Fetching registry...")
r = requests.get( r = requests.get(f"https://raw.githubusercontent.com/{repo}/main/registry.json")
f'https://raw.githubusercontent.com/{repo}/main/registry.json')
if r.status_code != 200: if r.status_code != 200:
logger.error('Failed to fetch registry') logger.error("Failed to fetch registry")
exit(1) exit(1)
registry = r.json() registry = r.json()
if list: if list:
print('\n'.join( print(
f"* {entry['id']} - {entry['description']}" for entry in registry)) "\n".join(f"* {entry['id']} - {entry['description']}" for entry in registry)
)
return return
if not template: if not template:
raise RuntimeError('Template not specified') raise RuntimeError("Template not specified")
def template_match(t: Json) -> str:
return t["id"] == template
template_match: Callable[[Json], str] = lambda t: t['id'] == template
if not any(filter(template_match, registry)): if not any(filter(template_match, registry)):
raise LookupError(f"Couldn't find a template named {template}") raise LookupError(f"Couldn't find a template named {template}")
@ -279,9 +284,12 @@ def initCmd(args: Args):
print("We suggest that you begin by typing:") print("We suggest that you begin by typing:")
print(f" {vt100.GREEN}cd {name}{vt100.RESET}") print(f" {vt100.GREEN}cd {name}{vt100.RESET}")
print(f" {vt100.GREEN}cutekit install{vt100.BRIGHT_BLACK} # Install external packages{vt100.RESET}")
print( print(
f" {vt100.GREEN}cutekit build{vt100.BRIGHT_BLACK} # Build the project{vt100.RESET}") f" {vt100.GREEN}cutekit install{vt100.BRIGHT_BLACK} # Install external packages{vt100.RESET}"
)
print(
f" {vt100.GREEN}cutekit build{vt100.BRIGHT_BLACK} # Build the project{vt100.RESET}"
)
cmds += [Cmd("I", "init", "Initialize a new project", initCmd)] cmds += [Cmd("I", "init", "Initialize a new project", initCmd)]

View file

@ -21,13 +21,13 @@ UNSUPORTED_MANIFEST = {
def ensureSupportedManifest(manifest: Any, path: str): def ensureSupportedManifest(manifest: Any, path: str):
if not "$schema" in manifest: if "$schema" not in manifest:
raise RuntimeError(f"Missing $schema in {path}") raise RuntimeError(f"Missing $schema in {path}")
if manifest["$schema"] in UNSUPORTED_MANIFEST: if manifest["$schema"] in UNSUPORTED_MANIFEST:
raise RuntimeError( raise RuntimeError(
f"Unsupported manifest schema {manifest['$schema']} in {path}: {UNSUPORTED_MANIFEST[manifest['$schema']]}") f"Unsupported manifest schema {manifest['$schema']} in {path}: {UNSUPORTED_MANIFEST[manifest['$schema']]}")
if not manifest["$schema"] in SUPPORTED_MANIFEST: if manifest["$schema"] not in SUPPORTED_MANIFEST:
raise RuntimeError( raise RuntimeError(
f"Unsupported manifest schema {manifest['$schema']} in {path}") f"Unsupported manifest schema {manifest['$schema']} in {path}")

View file

@ -11,7 +11,7 @@ BUILD_DIR = os.path.join(PROJECT_CK_DIR, "build")
CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache") CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache")
EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern") EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern")
SRC_DIR = "src" SRC_DIR = "src"
META_DIR = f"meta" META_DIR = "meta"
TARGETS_DIR = os.path.join(META_DIR, "targets") TARGETS_DIR = os.path.join(META_DIR, "targets")
DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates" DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates"
DESCRIPTION = "A build system and package manager for low-level software development" DESCRIPTION = "A build system and package manager for low-level software development"

View file

@ -288,7 +288,7 @@ def contextFor(targetSpec: str, props: Props = {}) -> Context:
instances: list[ComponentInstance] = list( instances: list[ComponentInstance] = list(
map(lambda c: instanciateDisabled(c, target), disabled)) map(lambda c: instanciateDisabled(c, target), disabled))
instances += cast(list[ComponentInstance], list(filter(lambda e: e != None, map(lambda c: instanciate( instances += cast(list[ComponentInstance], list(filter(lambda e: e is not None, map(lambda c: instanciate(
c.id, components, target), components)))) c.id, components, target), components))))
context[targetSpec] = Context( context[targetSpec] = Context(

View file

@ -10,15 +10,21 @@ Builtin = Callable[..., Json]
BUILTINS: Final[dict[str, Builtin]] = { BUILTINS: Final[dict[str, Builtin]] = {
"uname": lambda arg, ctx: getattr(shell.uname(), arg).lower(), "uname": lambda arg, ctx: getattr(shell.uname(), arg).lower(),
"include": lambda arg, ctx: evalRead(arg), "include": lambda arg, ctx: evalRead(arg, compatibilityCheck=False),
"evalRead": lambda arg, ctx: evalRead(arg), "evalRead": lambda arg, ctx: evalRead(arg, compatibilityCheck=False),
"join": lambda lhs, rhs, ctx: cast(Json, {**lhs, **rhs} if isinstance(lhs, dict) else lhs + rhs), "join": lambda lhs, rhs, ctx: cast(
Json, {**lhs, **rhs} if isinstance(lhs, dict) else lhs + rhs
),
"concat": lambda *args, ctx: "".join(args), "concat": lambda *args, ctx: "".join(args),
"first": lambda arg, ctx: arg[0],
"last": lambda arg, ctx: arg[-1],
"eval": lambda arg, ctx: eval(arg, ctx["filepath"]), "eval": lambda arg, ctx: eval(arg, ctx["filepath"]),
"read": lambda arg, ctx: read(arg), "read": lambda arg, ctx: read(arg),
"exec": lambda *args, ctx: shell.popen(*args).splitlines(), "exec": lambda *args, ctx: shell.popen(*args).splitlines(),
"latest": lambda arg, ctx: shell.latest(arg), "latest": lambda arg, ctx: shell.latest(arg),
"abspath": lambda *args, ctx: os.path.normpath(os.path.join(os.path.dirname(ctx["filepath"]), *args)) "abspath": lambda *args, ctx: os.path.normpath(
os.path.join(os.path.dirname(ctx["filepath"]), *args)
),
} }
@ -33,7 +39,9 @@ def eval(jexpr: Json, filePath: str) -> Json:
if len(jexpr) > 0 and isinstance(jexpr[0], str) and jexpr[0].startswith("@"): if len(jexpr) > 0 and isinstance(jexpr[0], str) and jexpr[0].startswith("@"):
funcName = jexpr[0][1:] funcName = jexpr[0][1:]
if funcName in BUILTINS: if funcName in BUILTINS:
return BUILTINS[funcName](*eval(jexpr[1:], filePath), ctx={"filepath": filePath}) return BUILTINS[funcName](
*eval(jexpr[1:], filePath), ctx={"filepath": filePath}
)
raise RuntimeError(f"Unknown macro {funcName}") raise RuntimeError(f"Unknown macro {funcName}")
else: else:
@ -50,7 +58,8 @@ def read(path: str) -> Json:
raise RuntimeError(f"Failed to read {path}") raise RuntimeError(f"Failed to read {path}")
def evalRead(path: str) -> Json: def evalRead(path: str, compatibilityCheck: bool = True) -> Json:
data = read(path) data = read(path)
ensureSupportedManifest(data, path) if compatibilityCheck:
ensureSupportedManifest(data, path)
return eval(data, path) return eval(data, path)

View file

@ -24,14 +24,16 @@ class Manifest:
type: Type = Type.UNKNOWN type: Type = Type.UNKNOWN
path: str = "" path: str = ""
def __init__(self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any): def __init__(
self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any
):
if json is not None: if json is not None:
if not "id" in json: if "id" not in json:
raise RuntimeError("Missing id") raise RuntimeError("Missing id")
self.id = json["id"] self.id = json["id"]
if not "type" in json and strict: if "type" not in json and strict:
raise RuntimeError("Missing type") raise RuntimeError("Missing type")
self.type = Type(json["type"]) self.type = Type(json["type"])
@ -44,11 +46,7 @@ class Manifest:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
def toJson(self) -> Json: def toJson(self) -> Json:
return { return {"id": self.id, "type": self.type.value, "path": self.path}
"id": self.id,
"type": self.type.value,
"path": self.path
}
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})"
@ -66,12 +64,12 @@ class Extern:
def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any): def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any):
if json is not None: if json is not None:
if not "git" in json and strict: if "git" not in json and strict:
raise RuntimeError("Missing git") raise RuntimeError("Missing git")
self.git = json["git"] self.git = json["git"]
if not "tag" in json and strict: if "tag" not in json and strict:
raise RuntimeError("Missing tag") raise RuntimeError("Missing tag")
self.tag = json["tag"] self.tag = json["tag"]
@ -82,10 +80,7 @@ class Extern:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
def toJson(self) -> Json: def toJson(self) -> Json:
return { return {"git": self.git, "tag": self.tag}
"git": self.git,
"tag": self.tag
}
def __str__(self): def __str__(self):
return f"Extern(git={self.git}, tag={self.tag})" return f"Extern(git={self.git}, tag={self.tag})"
@ -98,15 +93,16 @@ class ProjectManifest(Manifest):
description: str = "" description: str = ""
extern: dict[str, Extern] = {} extern: dict[str, Extern] = {}
def __init__(self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any): def __init__(
self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any
):
if json is not None: if json is not None:
if not "description" in json and strict: if "description" not in json and strict:
raise RuntimeError("Missing description") raise RuntimeError("Missing description")
self.description = json["description"] self.description = json["description"]
self.extern = {k: Extern(v) self.extern = {k: Extern(v) for k, v in json.get("extern", {}).items()}
for k, v in json.get("extern", {}).items()}
elif strict: elif strict:
raise RuntimeError("Missing json") raise RuntimeError("Missing json")
@ -116,7 +112,7 @@ class ProjectManifest(Manifest):
return { return {
**super().toJson(), **super().toJson(),
"description": self.description, "description": self.description,
"extern": {k: v.toJson() for k, v in self.extern.items()} "extern": {k: v.toJson() for k, v in self.extern.items()},
} }
def __str__(self): def __str__(self):
@ -133,12 +129,12 @@ class Tool:
def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any): def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any):
if json is not None: if json is not None:
if not "cmd" in json and strict: if "cmd" not in json and strict:
raise RuntimeError("Missing cmd") raise RuntimeError("Missing cmd")
self.cmd = json.get("cmd", self.cmd) self.cmd = json.get("cmd", self.cmd)
if not "args" in json and strict: if "args" not in json and strict:
raise RuntimeError("Missing args") raise RuntimeError("Missing args")
self.args = json.get("args", []) self.args = json.get("args", [])
@ -151,11 +147,7 @@ class Tool:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
def toJson(self) -> Json: def toJson(self) -> Json:
return { return {"cmd": self.cmd, "args": self.args, "files": self.files}
"cmd": self.cmd,
"args": self.args,
"files": self.files
}
def __str__(self): def __str__(self):
return f"Tool(cmd={self.cmd}, args={self.args}, files={self.files})" return f"Tool(cmd={self.cmd}, args={self.args}, files={self.files})"
@ -172,14 +164,16 @@ class TargetManifest(Manifest):
tools: Tools tools: Tools
routing: dict[str, str] routing: dict[str, str]
def __init__(self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any): def __init__(
self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any
):
if json is not None: if json is not None:
if not "props" in json and strict: if "props" not in json and strict:
raise RuntimeError("Missing props") raise RuntimeError("Missing props")
self.props = json["props"] self.props = json["props"]
if not "tools" in json and strict: if "tools" not in json and strict:
raise RuntimeError("Missing tools") raise RuntimeError("Missing tools")
self.tools = {k: Tool(v) for k, v in json["tools"].items()} self.tools = {k: Tool(v) for k, v in json["tools"].items()}
@ -193,27 +187,34 @@ class TargetManifest(Manifest):
**super().toJson(), **super().toJson(),
"props": self.props, "props": self.props,
"tools": {k: v.toJson() for k, v in self.tools.items()}, "tools": {k: v.toJson() for k, v in self.tools.items()},
"routing": self.routing "routing": self.routing,
} }
def __repr__(self): def __repr__(self):
return f"TargetManifest({self.id})" return f"TargetManifest({self.id})"
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 cdefs(self) -> list[str]: def cdefs(self) -> list[str]:
defines: list[str] = [] defines: list[str] = []
def sanatize(s: str) -> str:
return s.lower().replace(" ", "_").replace("-", "_").replace(".", "_")
for key in self.props: for key in self.props:
macroname = key.lower().replace("-", "_")
prop = self.props[key] prop = self.props[key]
macrovalue = str(prop).lower().replace(" ", "_").replace("-", "_") propStr = str(prop)
if isinstance(prop, bool): if isinstance(prop, bool):
if prop: if prop:
defines += [f"-D__ck_{macroname}__"] defines += [f"-D__ck_{sanatize(key)}__"]
else: else:
defines += [f"-D__ck_{macroname}_{macrovalue}__"] defines += [f"-D__ck_{sanatize(key)}_{sanatize(propStr)}__"]
defines += [f"-D__ck_{sanatize(key)}_value={propStr}"]
return defines return defines
@ -227,17 +228,24 @@ class ComponentManifest(Manifest):
provides: list[str] = [] provides: list[str] = []
subdirs: list[str] = [] subdirs: list[str] = []
def __init__(self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any): def __init__(
self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any
):
if json is not None: if json is not None:
self.decription = json.get("description", self.decription) self.decription = json.get("description", self.decription)
self.props = json.get("props", self.props) self.props = json.get("props", self.props)
self.tools = {k: Tool(v, strict=False) self.tools = {
for k, v in json.get("tools", {}).items()} k: Tool(v, strict=False) for k, v in json.get("tools", {}).items()
}
self.enableIf = json.get("enableIf", self.enableIf) self.enableIf = json.get("enableIf", self.enableIf)
self.requires = json.get("requires", self.requires) self.requires = json.get("requires", self.requires)
self.provides = json.get("provides", self.provides) self.provides = json.get("provides", self.provides)
self.subdirs = list(map(lambda x: os.path.join(os.path.dirname( self.subdirs = list(
path), x), json.get("subdirs", [""]))) map(
lambda x: os.path.join(os.path.dirname(path), x),
json.get("subdirs", [""]),
)
)
super().__init__(json, path, strict, **kwargs) super().__init__(json, path, strict, **kwargs)
@ -250,7 +258,7 @@ class ComponentManifest(Manifest):
"enableIf": self.enableIf, "enableIf": self.enableIf,
"requires": self.requires, "requires": self.requires,
"provides": self.provides, "provides": self.provides,
"subdirs": self.subdirs "subdirs": self.subdirs,
} }
def __repr__(self): def __repr__(self):
@ -258,15 +266,18 @@ class ComponentManifest(Manifest):
def isEnabled(self, target: TargetManifest) -> tuple[bool, str]: def isEnabled(self, target: TargetManifest) -> tuple[bool, str]:
for k, v in self.enableIf.items(): for k, v in self.enableIf.items():
if not k in target.props: if k not in target.props:
logger.info( logger.info(f"Component {self.id} disabled by missing {k} in target")
f"Component {self.id} disabled by missing {k} in target")
return False, f"Missing props '{k}' in target" return False, f"Missing props '{k}' in target"
if not target.props[k] in v: if target.props[k] not in v:
vStrs = [f"'{str(x)}'" for x in v] vStrs = [f"'{str(x)}'" for x in v]
logger.info( logger.info(
f"Component {self.id} disabled by {k}={target.props[k]} not in {v}") f"Component {self.id} disabled by {k}={target.props[k]} not in {v}"
return False, f"Props missmatch for '{k}': Got '{target.props[k]}' but expected {', '.join(vStrs)}" )
return (
False,
f"Props missmatch for '{k}': Got '{target.props[k]}' but expected {', '.join(vStrs)}",
)
return True, "" return True, ""

View file

@ -23,11 +23,11 @@ def loadAll():
logger.info("Loading plugins...") logger.info("Loading plugins...")
projectRoot = project.root() projectRoot = project.root()
if projectRoot is None: if projectRoot is None:
logger.info("Not in project, skipping plugin loading") logger.info("Not in project, skipping plugin loading")
return return
pj = context.loadProject(projectRoot) pj = context.loadProject(projectRoot)
paths = list(map(lambda e: os.path.join(const.EXTERN_DIR, e), pj.extern.keys())) + ["."] paths = list(map(lambda e: os.path.join(const.EXTERN_DIR, e), pj.extern.keys())) + ["."]