Update cutekit CLI to use new cli module
This commit is contained in:
parent
2c9a7c5fc6
commit
9a962e825e
|
@ -2,8 +2,7 @@ import sys
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from cutekit import const, project, vt100, plugins, cmds
|
from cutekit import const, project, vt100, plugins, cmds, cli
|
||||||
from cutekit.args import parse
|
|
||||||
|
|
||||||
|
|
||||||
def setupLogger(verbose: bool):
|
def setupLogger(verbose: bool):
|
||||||
|
@ -35,7 +34,7 @@ def setupLogger(verbose: bool):
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
try:
|
try:
|
||||||
a = parse(sys.argv[1:])
|
a = cli.parse(sys.argv[1:])
|
||||||
setupLogger(a.consumeOpt("verbose", False) is True)
|
setupLogger(a.consumeOpt("verbose", False) is True)
|
||||||
plugins.loadAll()
|
plugins.loadAll()
|
||||||
cmds.exec(a)
|
cmds.exec(a)
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from typing import Optional, Union
|
import inspect
|
||||||
|
|
||||||
|
from typing import Optional, Union, Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
Value = Union[str, bool, int]
|
Value = Union[str, bool, int]
|
||||||
|
@ -58,3 +61,37 @@ def parse(args: list[str]) -> Args:
|
||||||
result.args.append(arg)
|
result.args.append(arg)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
Callback = Callable[[Args], None]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Command:
|
||||||
|
shortName: str
|
||||||
|
longName: str
|
||||||
|
helpText: str
|
||||||
|
isPlugin: bool
|
||||||
|
callback: Callback
|
||||||
|
|
||||||
|
|
||||||
|
commands: list[Command] = []
|
||||||
|
|
||||||
|
|
||||||
|
def append(command: Command):
|
||||||
|
command.isPlugin = True
|
||||||
|
commands.append(command)
|
||||||
|
commands.sort(key=lambda c: c.shortName or c.longName)
|
||||||
|
|
||||||
|
|
||||||
|
def command(shortName: str, longName: str, helpText: str):
|
||||||
|
curframe = inspect.currentframe()
|
||||||
|
calframe = inspect.getouterframes(curframe, 2)
|
||||||
|
|
||||||
|
def wrap(fn: Callable[[Args], None]):
|
||||||
|
commands.append(
|
||||||
|
Command(shortName, longName, helpText, calframe[1].filename != __file__, fn)
|
||||||
|
)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
return wrap
|
126
cutekit/cmds.py
126
cutekit/cmds.py
|
@ -1,58 +1,29 @@
|
||||||
import inspect
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Callable, cast, Optional
|
|
||||||
|
|
||||||
from cutekit import context, shell, const, vt100, builder, graph, project
|
from cutekit import (
|
||||||
from cutekit.args import Args
|
context,
|
||||||
from cutekit.context import contextFor
|
shell,
|
||||||
from cutekit.jexpr import Json
|
const,
|
||||||
from cutekit.model import Extern
|
vt100,
|
||||||
|
builder,
|
||||||
|
project,
|
||||||
|
cli,
|
||||||
|
model,
|
||||||
|
jexpr,
|
||||||
|
)
|
||||||
|
|
||||||
Callback = Callable[[Args], None]
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@cli.command("p", "project", "Show project information")
|
||||||
class Cmd:
|
def runCmd(args: cli.Args):
|
||||||
shortName: str
|
|
||||||
longName: str
|
|
||||||
helpText: str
|
|
||||||
isPlugin: bool
|
|
||||||
callback: Callback
|
|
||||||
|
|
||||||
|
|
||||||
cmds: list[Cmd] = []
|
|
||||||
|
|
||||||
|
|
||||||
def append(cmd: Cmd):
|
|
||||||
cmd.isPlugin = True
|
|
||||||
cmds.append(cmd)
|
|
||||||
cmds.sort(key=lambda c: c.shortName or c.longName)
|
|
||||||
|
|
||||||
|
|
||||||
def cmd(shortName: str, longName: str, helpText: str):
|
|
||||||
curframe = inspect.currentframe()
|
|
||||||
calframe = inspect.getouterframes(curframe, 2)
|
|
||||||
|
|
||||||
def wrap(fn: Callable[[Args], None]):
|
|
||||||
cmds.append(
|
|
||||||
Cmd(shortName, longName, helpText, calframe[1].filename != __file__, fn)
|
|
||||||
)
|
|
||||||
return fn
|
|
||||||
|
|
||||||
return wrap
|
|
||||||
|
|
||||||
|
|
||||||
@cmd("p", "project", "Show project information")
|
|
||||||
def runCmd(args: Args):
|
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
|
||||||
targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
|
targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine))
|
||||||
props = args.consumePrefix("prop:")
|
props = args.consumePrefix("prop:")
|
||||||
|
|
||||||
componentSpec = args.consumeArg()
|
componentSpec = args.consumeArg()
|
||||||
|
@ -69,19 +40,19 @@ def runCmd(args: Args):
|
||||||
shell.exec(component.outfile(), *args.args)
|
shell.exec(component.outfile(), *args.args)
|
||||||
|
|
||||||
|
|
||||||
@cmd("t", "test", "Run all test targets")
|
@cli.command("t", "test", "Run all test targets")
|
||||||
def testCmd(args: Args):
|
def testCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
|
||||||
targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
|
targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine))
|
||||||
builder.testAll(targetSpec)
|
builder.testAll(targetSpec)
|
||||||
|
|
||||||
|
|
||||||
@cmd("d", "debug", "Debug a component")
|
@cli.command("d", "debug", "Debug a component")
|
||||||
def debugCmd(args: Args):
|
def debugCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
|
||||||
targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
|
targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine))
|
||||||
props = args.consumePrefix("prop:")
|
props = args.consumePrefix("prop:")
|
||||||
|
|
||||||
componentSpec = args.consumeArg()
|
componentSpec = args.consumeArg()
|
||||||
|
@ -98,11 +69,11 @@ def debugCmd(args: Args):
|
||||||
shell.exec("lldb", "-o", "run", component.outfile(), *args.args)
|
shell.exec("lldb", "-o", "run", component.outfile(), *args.args)
|
||||||
|
|
||||||
|
|
||||||
@cmd("b", "build", "Build a component or all components")
|
@cli.command("b", "build", "Build a component or all components")
|
||||||
def buildCmd(args: Args):
|
def buildCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
|
||||||
targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
|
targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine))
|
||||||
props = args.consumePrefix("prop:")
|
props = args.consumePrefix("prop:")
|
||||||
componentSpec = args.consumeArg()
|
componentSpec = args.consumeArg()
|
||||||
|
|
||||||
|
@ -112,8 +83,8 @@ def buildCmd(args: Args):
|
||||||
builder.build(componentSpec, targetSpec, props)
|
builder.build(componentSpec, targetSpec, props)
|
||||||
|
|
||||||
|
|
||||||
@cmd("l", "list", "List all components and targets")
|
@cli.command("l", "list", "List all components and targets")
|
||||||
def listCmd(args: Args):
|
def listCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
|
||||||
components = context.loadAllComponents()
|
components = context.loadAllComponents()
|
||||||
|
@ -136,20 +107,20 @@ def listCmd(args: Args):
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
@cmd("c", "clean", "Clean build files")
|
@cli.command("c", "clean", "Clean build files")
|
||||||
def cleanCmd(args: Args):
|
def cleanCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
shell.rmrf(const.BUILD_DIR)
|
shell.rmrf(const.BUILD_DIR)
|
||||||
|
|
||||||
|
|
||||||
@cmd("n", "nuke", "Clean all build files and caches")
|
@cli.command("n", "nuke", "Clean all build files and caches")
|
||||||
def nukeCmd(args: Args):
|
def nukeCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
shell.rmrf(const.PROJECT_CK_DIR)
|
shell.rmrf(const.PROJECT_CK_DIR)
|
||||||
|
|
||||||
|
|
||||||
@cmd("h", "help", "Show this help message")
|
@cli.command("h", "help", "Show this help message")
|
||||||
def helpCmd(args: Args):
|
def helpCmd(args: cli.Args):
|
||||||
usage()
|
usage()
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
@ -159,7 +130,7 @@ def helpCmd(args: Args):
|
||||||
|
|
||||||
print()
|
print()
|
||||||
vt100.title("Commands")
|
vt100.title("Commands")
|
||||||
for cmd in cmds:
|
for cmd in cli.commands:
|
||||||
pluginText = ""
|
pluginText = ""
|
||||||
if cmd.isPlugin:
|
if cmd.isPlugin:
|
||||||
pluginText = f"{vt100.CYAN}(plugin){vt100.RESET}"
|
pluginText = f"{vt100.CYAN}(plugin){vt100.RESET}"
|
||||||
|
@ -176,26 +147,11 @@ def helpCmd(args: Args):
|
||||||
|
|
||||||
|
|
||||||
@cmd("v", "version", "Show current version")
|
@cmd("v", "version", "Show current version")
|
||||||
def versionCmd(args: Args):
|
def versionCmd(args: cli.Args):
|
||||||
print(f"CuteKit v{const.VERSION_STR}")
|
print(f"CuteKit v{const.VERSION_STR}")
|
||||||
|
|
||||||
|
|
||||||
@cmd("g", "graph", "Show the dependency graph")
|
def grabExtern(extern: dict[str, model.Extern]):
|
||||||
def graphCmd(args: Args):
|
|
||||||
project.chdir()
|
|
||||||
|
|
||||||
targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine))
|
|
||||||
|
|
||||||
scope: Optional[str] = cast(Optional[str], args.tryConsumeOpt("scope"))
|
|
||||||
onlyLibs: bool = args.consumeOpt("only-libs", False) is True
|
|
||||||
showDisabled: bool = args.consumeOpt("show-disabled", False) is True
|
|
||||||
|
|
||||||
context = contextFor(targetSpec)
|
|
||||||
|
|
||||||
graph.view(context, scope=scope, showExe=not onlyLibs, showDisabled=showDisabled)
|
|
||||||
|
|
||||||
|
|
||||||
def grabExtern(extern: dict[str, Extern]):
|
|
||||||
for extSpec, ext in extern.items():
|
for extSpec, ext in extern.items():
|
||||||
extPath = os.path.join(const.EXTERN_DIR, extSpec)
|
extPath = os.path.join(const.EXTERN_DIR, extSpec)
|
||||||
|
|
||||||
|
@ -212,16 +168,16 @@ def grabExtern(extern: dict[str, Extern]):
|
||||||
grabExtern(context.loadProject(extPath).extern)
|
grabExtern(context.loadProject(extPath).extern)
|
||||||
|
|
||||||
|
|
||||||
@cmd("i", "install", "Install required external packages")
|
@cli.command("i", "install", "Install required external packages")
|
||||||
def installCmd(args: Args):
|
def installCmd(args: cli.Args):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
|
||||||
pj = context.loadProject(".")
|
pj = context.loadProject(".")
|
||||||
grabExtern(pj.extern)
|
grabExtern(pj.extern)
|
||||||
|
|
||||||
|
|
||||||
@cmd("I", "init", "Initialize a new project")
|
@cli.command("I", "init", "Initialize a new project")
|
||||||
def initCmd(args: Args):
|
def initCmd(args: cli.Args):
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES)
|
repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES)
|
||||||
|
@ -248,7 +204,7 @@ def initCmd(args: Args):
|
||||||
if not template:
|
if not template:
|
||||||
raise RuntimeError("Template not specified")
|
raise RuntimeError("Template not specified")
|
||||||
|
|
||||||
def template_match(t: Json) -> str:
|
def template_match(t: jexpr.Json) -> str:
|
||||||
return t["id"] == template
|
return t["id"] == template
|
||||||
|
|
||||||
if not any(filter(template_match, registry)):
|
if not any(filter(template_match, registry)):
|
||||||
|
@ -283,13 +239,13 @@ def error(msg: str) -> None:
|
||||||
print(f"{vt100.RED}Error:{vt100.RESET} {msg}\n", file=sys.stderr)
|
print(f"{vt100.RED}Error:{vt100.RESET} {msg}\n", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def exec(args: Args):
|
def exec(args: cli.Args):
|
||||||
cmd = args.consumeArg()
|
cmd = args.consumeArg()
|
||||||
|
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
raise RuntimeError("No command specified")
|
raise RuntimeError("No command specified")
|
||||||
|
|
||||||
for c in cmds:
|
for c in cli.commands:
|
||||||
if c.shortName == cmd or c.longName == cmd:
|
if c.shortName == cmd or c.longName == cmd:
|
||||||
c.callback(args)
|
c.callback(args)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from typing import Optional
|
from typing import cast
|
||||||
from cutekit.context import Context
|
from . import vt100, context, project, cli, shell
|
||||||
from cutekit import vt100
|
|
||||||
|
|
||||||
|
|
||||||
def view(context: Context, scope: Optional[str] = None, showExe: bool = True, showDisabled: bool = False):
|
def view(
|
||||||
|
context: context.Context,
|
||||||
|
scope: str | None = None,
|
||||||
|
showExe: bool = True,
|
||||||
|
showDisabled: bool = False,
|
||||||
|
):
|
||||||
from graphviz import Digraph
|
from graphviz import Digraph
|
||||||
|
|
||||||
g = Digraph(context.target.id, filename='graph.gv')
|
g = Digraph(context.target.id, filename="graph.gv")
|
||||||
|
|
||||||
g.attr('graph', splines='ortho', rankdir='BT', ranksep='1.5')
|
g.attr("graph", splines="ortho", rankdir="BT", ranksep="1.5")
|
||||||
g.attr('node', shape='ellipse')
|
g.attr("node", shape="ellipse")
|
||||||
g.attr(
|
g.attr(
|
||||||
'graph', label=f"<<B>{scope or 'Full Dependency Graph'}</B><BR/>{context.target.id}>", labelloc='t')
|
"graph",
|
||||||
|
label=f"<<B>{scope or 'Full Dependency Graph'}</B><BR/>{context.target.id}>",
|
||||||
|
labelloc="t",
|
||||||
|
)
|
||||||
|
|
||||||
scopeInstance = None
|
scopeInstance = None
|
||||||
|
|
||||||
|
@ -24,36 +31,66 @@ def view(context: Context, scope: Optional[str] = None, showExe: bool = True, sh
|
||||||
if not instance.isLib() and not showExe:
|
if not instance.isLib() and not showExe:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if scopeInstance is not None and \
|
if (
|
||||||
instance.manifest.id != scope and \
|
scopeInstance is not None
|
||||||
instance.manifest.id not in scopeInstance.resolved:
|
and instance.manifest.id != scope
|
||||||
|
and instance.manifest.id not in scopeInstance.resolved
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if instance.enabled:
|
if instance.enabled:
|
||||||
fillcolor = "lightgrey" if instance.isLib() else "lightblue"
|
fillcolor = "lightgrey" if instance.isLib() else "lightblue"
|
||||||
shape = "plaintext" if not scope == instance.manifest.id else 'box'
|
shape = "plaintext" if not scope == instance.manifest.id else "box"
|
||||||
|
|
||||||
g.node(instance.manifest.id, f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}>",
|
g.node(
|
||||||
shape=shape, style="filled", fillcolor=fillcolor)
|
instance.manifest.id,
|
||||||
|
f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}>",
|
||||||
|
shape=shape,
|
||||||
|
style="filled",
|
||||||
|
fillcolor=fillcolor,
|
||||||
|
)
|
||||||
|
|
||||||
for req in instance.manifest.requires:
|
for req in instance.manifest.requires:
|
||||||
g.edge(instance.manifest.id, req)
|
g.edge(instance.manifest.id, req)
|
||||||
|
|
||||||
for req in instance.manifest.provides:
|
for req in instance.manifest.provides:
|
||||||
isChosen = context.target.routing.get(
|
isChosen = context.target.routing.get(req, None) == instance.manifest.id
|
||||||
req, None) == instance.manifest.id
|
|
||||||
|
|
||||||
g.edge(req, instance.manifest.id, arrowhead="none", color=(
|
g.edge(
|
||||||
"blue" if isChosen else "black"))
|
req,
|
||||||
|
instance.manifest.id,
|
||||||
|
arrowhead="none",
|
||||||
|
color=("blue" if isChosen else "black"),
|
||||||
|
)
|
||||||
elif showDisabled:
|
elif showDisabled:
|
||||||
g.node(instance.manifest.id, f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(instance.disableReason, 40,newline='<BR/>')}</I>>",
|
g.node(
|
||||||
shape="plaintext", style="filled", fontcolor="#999999", fillcolor="#eeeeee")
|
instance.manifest.id,
|
||||||
|
f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(instance.disableReason, 40,newline='<BR/>')}</I>>",
|
||||||
|
shape="plaintext",
|
||||||
|
style="filled",
|
||||||
|
fontcolor="#999999",
|
||||||
|
fillcolor="#eeeeee",
|
||||||
|
)
|
||||||
|
|
||||||
for req in instance.manifest.requires:
|
for req in instance.manifest.requires:
|
||||||
g.edge(instance.manifest.id, req, color="#aaaaaa")
|
g.edge(instance.manifest.id, req, color="#aaaaaa")
|
||||||
|
|
||||||
for req in instance.manifest.provides:
|
for req in instance.manifest.provides:
|
||||||
g.edge(req, instance.manifest.id,
|
g.edge(req, instance.manifest.id, arrowhead="none", color="#aaaaaa")
|
||||||
arrowhead="none", color="#aaaaaa")
|
|
||||||
|
|
||||||
g.view(filename=os.path.join(context.builddir(), "graph.gv"))
|
g.view(filename=os.path.join(context.builddir(), "graph.gv"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("g", "graph", "Show the dependency graph")
|
||||||
|
def graphCmd(args: cli.Args):
|
||||||
|
project.chdir()
|
||||||
|
|
||||||
|
targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine))
|
||||||
|
|
||||||
|
scope: str | None = cast(str | None, args.tryConsumeOpt("scope"))
|
||||||
|
onlyLibs: bool = args.consumeOpt("only-libs", False) is True
|
||||||
|
showDisabled: bool = args.consumeOpt("show-disabled", False) is True
|
||||||
|
|
||||||
|
ctx = context.contextFor(targetSpec)
|
||||||
|
|
||||||
|
view(ctx, scope=scope, showExe=not onlyLibs, showDisabled=showDisabled)
|
||||||
|
|
Loading…
Reference in a new issue