Properly propagate props from the command line + Some code cleanup.
This commit is contained in:
parent
6dd4a49043
commit
81f10de24e
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
105
cutekit/model.py
105
cutekit/model.py
|
@ -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, ""
|
||||||
|
|
|
@ -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())) + ["."]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue