Update cutekit CLI to use new cli module
This commit is contained in:
		
							parent
							
								
									31ca0b19e8
								
							
						
					
					
						commit
						e77e787547
					
				
					 4 changed files with 141 additions and 112 deletions
				
			
		|  | @ -2,8 +2,7 @@ import sys | |||
| import os | ||||
| import logging | ||||
| 
 | ||||
| from cutekit import const, project, vt100, plugins, cmds | ||||
| from cutekit.args import parse | ||||
| from cutekit import const, project, vt100, plugins, cmds, cli | ||||
| 
 | ||||
| 
 | ||||
| def setupLogger(verbose: bool): | ||||
|  | @ -35,7 +34,7 @@ def setupLogger(verbose: bool): | |||
| 
 | ||||
| def main() -> int: | ||||
|     try: | ||||
|         a = parse(sys.argv[1:]) | ||||
|         a = cli.parse(sys.argv[1:]) | ||||
|         setupLogger(a.consumeOpt("verbose", False) is True) | ||||
|         plugins.loadAll() | ||||
|         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] | ||||
|  | @ -58,3 +61,37 @@ def parse(args: list[str]) -> Args: | |||
|             result.args.append(arg) | ||||
| 
 | ||||
|     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 | ||||
							
								
								
									
										128
									
								
								cutekit/cmds.py
									
										
									
									
									
								
							
							
						
						
									
										128
									
								
								cutekit/cmds.py
									
										
									
									
									
								
							|  | @ -1,58 +1,29 @@ | |||
| import inspect | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
| 
 | ||||
| from dataclasses import dataclass | ||||
| from typing import Callable, cast, Optional | ||||
| 
 | ||||
| from cutekit import context, shell, const, vt100, builder, graph, project | ||||
| from cutekit.args import Args | ||||
| from cutekit.context import contextFor | ||||
| from cutekit.jexpr import Json | ||||
| from cutekit.model import Extern | ||||
| from cutekit import ( | ||||
|     context, | ||||
|     shell, | ||||
|     const, | ||||
|     vt100, | ||||
|     builder, | ||||
|     project, | ||||
|     cli, | ||||
|     model, | ||||
|     jexpr, | ||||
| ) | ||||
| 
 | ||||
| Callback = Callable[[Args], None] | ||||
| 
 | ||||
| _logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| @dataclass | ||||
| class Cmd: | ||||
|     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): | ||||
| @cli.command("p", "project", "Show project information") | ||||
| def runCmd(args: cli.Args): | ||||
|     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:") | ||||
| 
 | ||||
|     componentSpec = args.consumeArg() | ||||
|  | @ -69,19 +40,19 @@ def runCmd(args: Args): | |||
|     shell.exec(component.outfile(), *args.args) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("t", "test", "Run all test targets") | ||||
| def testCmd(args: Args): | ||||
| @cli.command("t", "test", "Run all test targets") | ||||
| def testCmd(args: cli.Args): | ||||
|     project.chdir() | ||||
| 
 | ||||
|     targetSpec = cast(str, args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||
|     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||
|     builder.testAll(targetSpec) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("d", "debug", "Debug a component") | ||||
| def debugCmd(args: Args): | ||||
| @cli.command("d", "debug", "Debug a component") | ||||
| def debugCmd(args: cli.Args): | ||||
|     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:") | ||||
| 
 | ||||
|     componentSpec = args.consumeArg() | ||||
|  | @ -98,11 +69,11 @@ def debugCmd(args: Args): | |||
|     shell.exec("lldb", "-o", "run", component.outfile(), *args.args) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("b", "build", "Build a component or all components") | ||||
| def buildCmd(args: Args): | ||||
| @cli.command("b", "build", "Build a component or all components") | ||||
| def buildCmd(args: cli.Args): | ||||
|     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:") | ||||
|     componentSpec = args.consumeArg() | ||||
| 
 | ||||
|  | @ -112,8 +83,8 @@ def buildCmd(args: Args): | |||
|         builder.build(componentSpec, targetSpec, props) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("l", "list", "List all components and targets") | ||||
| def listCmd(args: Args): | ||||
| @cli.command("l", "list", "List all components and targets") | ||||
| def listCmd(args: cli.Args): | ||||
|     project.chdir() | ||||
| 
 | ||||
|     components = context.loadAllComponents() | ||||
|  | @ -136,20 +107,20 @@ def listCmd(args: Args): | |||
|     print() | ||||
| 
 | ||||
| 
 | ||||
| @cmd("c", "clean", "Clean build files") | ||||
| def cleanCmd(args: Args): | ||||
| @cli.command("c", "clean", "Clean build files") | ||||
| def cleanCmd(args: cli.Args): | ||||
|     project.chdir() | ||||
|     shell.rmrf(const.BUILD_DIR) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("n", "nuke", "Clean all build files and caches") | ||||
| def nukeCmd(args: Args): | ||||
| @cli.command("n", "nuke", "Clean all build files and caches") | ||||
| def nukeCmd(args: cli.Args): | ||||
|     project.chdir() | ||||
|     shell.rmrf(const.PROJECT_CK_DIR) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("h", "help", "Show this help message") | ||||
| def helpCmd(args: Args): | ||||
| @cli.command("h", "help", "Show this help message") | ||||
| def helpCmd(args: cli.Args): | ||||
|     usage() | ||||
| 
 | ||||
|     print() | ||||
|  | @ -159,7 +130,7 @@ def helpCmd(args: Args): | |||
| 
 | ||||
|     print() | ||||
|     vt100.title("Commands") | ||||
|     for cmd in cmds: | ||||
|     for cmd in cli.commands: | ||||
|         pluginText = "" | ||||
|         if cmd.isPlugin: | ||||
|             pluginText = f"{vt100.CYAN}(plugin){vt100.RESET}" | ||||
|  | @ -175,27 +146,12 @@ def helpCmd(args: Args): | |||
|     print(f"     - {const.GLOBAL_LOG_FILE}") | ||||
| 
 | ||||
| 
 | ||||
| @cmd("v", "version", "Show current version") | ||||
| def versionCmd(args: Args): | ||||
| @cli.command("v", "version", "Show current version") | ||||
| def versionCmd(args: cli.Args): | ||||
|     print(f"CuteKit v{const.VERSION_STR}\n") | ||||
| 
 | ||||
| 
 | ||||
| @cmd("g", "graph", "Show the dependency graph") | ||||
| 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]): | ||||
| def grabExtern(extern: dict[str, model.Extern]): | ||||
|     for extSpec, ext in extern.items(): | ||||
|         extPath = os.path.join(const.EXTERN_DIR, extSpec) | ||||
| 
 | ||||
|  | @ -212,16 +168,16 @@ def grabExtern(extern: dict[str, Extern]): | |||
|             grabExtern(context.loadProject(extPath).extern) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("i", "install", "Install required external packages") | ||||
| def installCmd(args: Args): | ||||
| @cli.command("i", "install", "Install required external packages") | ||||
| def installCmd(args: cli.Args): | ||||
|     project.chdir() | ||||
| 
 | ||||
|     pj = context.loadProject(".") | ||||
|     grabExtern(pj.extern) | ||||
| 
 | ||||
| 
 | ||||
| @cmd("I", "init", "Initialize a new project") | ||||
| def initCmd(args: Args): | ||||
| @cli.command("I", "init", "Initialize a new project") | ||||
| def initCmd(args: cli.Args): | ||||
|     import requests | ||||
| 
 | ||||
|     repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES) | ||||
|  | @ -248,7 +204,7 @@ def initCmd(args: Args): | |||
|     if not template: | ||||
|         raise RuntimeError("Template not specified") | ||||
| 
 | ||||
|     def template_match(t: Json) -> str: | ||||
|     def template_match(t: jexpr.Json) -> str: | ||||
|         return t["id"] == template | ||||
| 
 | ||||
|     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) | ||||
| 
 | ||||
| 
 | ||||
| def exec(args: Args): | ||||
| def exec(args: cli.Args): | ||||
|     cmd = args.consumeArg() | ||||
| 
 | ||||
|     if cmd is None: | ||||
|         raise RuntimeError("No command specified") | ||||
| 
 | ||||
|     for c in cmds: | ||||
|     for c in cli.commands: | ||||
|         if c.shortName == cmd or c.longName == cmd: | ||||
|             c.callback(args) | ||||
|             return | ||||
|  |  | |||
|  | @ -1,19 +1,26 @@ | |||
| import os | ||||
| 
 | ||||
| from typing import Optional | ||||
| from cutekit.context import Context | ||||
| from cutekit import vt100 | ||||
| from typing import cast | ||||
| from . import vt100, context, project, cli, shell | ||||
| 
 | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
|     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('node', shape='ellipse') | ||||
|     g.attr("graph", splines="ortho", rankdir="BT", ranksep="1.5") | ||||
|     g.attr("node", shape="ellipse") | ||||
|     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 | ||||
| 
 | ||||
|  | @ -24,36 +31,66 @@ def view(context: Context, scope: Optional[str] = None, showExe: bool = True, sh | |||
|         if not instance.isLib() and not showExe: | ||||
|             continue | ||||
| 
 | ||||
|         if scopeInstance is not None and \ | ||||
|             instance.manifest.id != scope and \ | ||||
|                 instance.manifest.id not in scopeInstance.resolved: | ||||
|         if ( | ||||
|             scopeInstance is not None | ||||
|             and instance.manifest.id != scope | ||||
|             and instance.manifest.id not in scopeInstance.resolved | ||||
|         ): | ||||
|             continue | ||||
| 
 | ||||
|         if instance.enabled: | ||||
|             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/>')}>", | ||||
|                    shape=shape, style="filled", fillcolor=fillcolor) | ||||
|             g.node( | ||||
|                 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: | ||||
|                 g.edge(instance.manifest.id, req) | ||||
| 
 | ||||
|             for req in instance.manifest.provides: | ||||
|                 isChosen = context.target.routing.get( | ||||
|                     req, None) == instance.manifest.id | ||||
|                 isChosen = context.target.routing.get(req, None) == instance.manifest.id | ||||
| 
 | ||||
|                 g.edge(req, instance.manifest.id, arrowhead="none", color=( | ||||
|                     "blue" if isChosen else "black")) | ||||
|                 g.edge( | ||||
|                     req, | ||||
|                     instance.manifest.id, | ||||
|                     arrowhead="none", | ||||
|                     color=("blue" if isChosen else "black"), | ||||
|                 ) | ||||
|         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>>", | ||||
|                    shape="plaintext", style="filled",   fontcolor="#999999", fillcolor="#eeeeee") | ||||
|             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>>", | ||||
|                 shape="plaintext", | ||||
|                 style="filled", | ||||
|                 fontcolor="#999999", | ||||
|                 fillcolor="#eeeeee", | ||||
|             ) | ||||
| 
 | ||||
|             for req in instance.manifest.requires: | ||||
|                 g.edge(instance.manifest.id, req, color="#aaaaaa") | ||||
| 
 | ||||
|             for req in instance.manifest.provides: | ||||
|                 g.edge(req, instance.manifest.id, | ||||
|                        arrowhead="none", color="#aaaaaa") | ||||
|                 g.edge(req, instance.manifest.id, arrowhead="none", color="#aaaaaa") | ||||
| 
 | ||||
|     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…
	
	Add table
		
		Reference in a new issue