wip
This commit is contained in:
parent
515893de5f
commit
aae3ccd06e
|
@ -3,7 +3,7 @@ import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from cutekit import const, project, vt100, plugins, cmds
|
from cutekit import const, project, vt100, plugins, cmds
|
||||||
from cutekit.args import parse
|
|
||||||
|
|
||||||
def setupLogger(verbose: bool):
|
def setupLogger(verbose: bool):
|
||||||
if verbose:
|
if verbose:
|
||||||
|
@ -31,12 +31,12 @@ def setupLogger(verbose: bool):
|
||||||
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:])
|
setupLogger(False)
|
||||||
setupLogger(a.consumeOpt("verbose", False) == True)
|
|
||||||
plugins.loadAll()
|
plugins.loadAll()
|
||||||
cmds.exec(a)
|
cmds.exec(sys.argv)
|
||||||
print()
|
print()
|
||||||
return 0
|
return 0
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
|
@ -47,4 +47,4 @@ def main() -> int:
|
||||||
return 1
|
return 1
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print()
|
print()
|
||||||
return 1
|
return 1
|
||||||
|
|
651
cutekit/args2.py
Normal file
651
cutekit/args2.py
Normal file
|
@ -0,0 +1,651 @@
|
||||||
|
from typing import TypeVar, Generic, Optional, Callable, Any
|
||||||
|
import sys
|
||||||
|
from cutekit import vt100
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
|
# --- Base ------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
class Scan:
|
||||||
|
_argv: list[str]
|
||||||
|
_off: int = 0
|
||||||
|
|
||||||
|
def __init__(self, argv: list[str]):
|
||||||
|
self._argv = argv
|
||||||
|
|
||||||
|
def any(self) -> bool:
|
||||||
|
return self._off < len(self._argv)
|
||||||
|
|
||||||
|
def peek(self) -> str:
|
||||||
|
return self._argv[self._off]
|
||||||
|
|
||||||
|
def tryPeek(self, default: str = "") -> str:
|
||||||
|
if self.any():
|
||||||
|
return self.peek()
|
||||||
|
return default
|
||||||
|
|
||||||
|
def isHelp(self) -> bool:
|
||||||
|
return self.tryPeek() == "-h" or self.tryPeek() == "--help"
|
||||||
|
|
||||||
|
def next(self) -> str:
|
||||||
|
result = self._argv[self._off]
|
||||||
|
self._off += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def consumed(self) -> list[str]:
|
||||||
|
return self._argv[:self._off]
|
||||||
|
|
||||||
|
|
||||||
|
class Emit:
|
||||||
|
_buf: str = ""
|
||||||
|
_ident: int = 0
|
||||||
|
_newline: int = 0
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ident(self) -> None:
|
||||||
|
self._ident += 1
|
||||||
|
|
||||||
|
def unident(self) -> None:
|
||||||
|
self._ident -= 1
|
||||||
|
|
||||||
|
def emit(self, text: str) -> None:
|
||||||
|
for c in text:
|
||||||
|
if c == "\n":
|
||||||
|
self._newline += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if c != "\n" and self._newline:
|
||||||
|
self.flushNewline()
|
||||||
|
self._buf += " " * self._ident
|
||||||
|
|
||||||
|
self._buf += c
|
||||||
|
|
||||||
|
def newline(self) -> None:
|
||||||
|
self._newline += 1
|
||||||
|
|
||||||
|
def flushNewline(self) -> None:
|
||||||
|
if self._newline:
|
||||||
|
self._buf += "\n" * min(2, self._newline)
|
||||||
|
self._newline = 0
|
||||||
|
|
||||||
|
def finish(self) -> str:
|
||||||
|
self._newline = min(1, self._newline)
|
||||||
|
self.flushNewline()
|
||||||
|
if self._ident:
|
||||||
|
raise RuntimeError("Unbalanced ident")
|
||||||
|
return self._buf
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
# --- Quatifiers ------------------------------------------------------------ #
|
||||||
|
|
||||||
|
class AllOf(Node):
|
||||||
|
parts: list[Node]
|
||||||
|
|
||||||
|
def __init__(self, *parts: Node):
|
||||||
|
self.parts = list(parts)
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if s.isHelp():
|
||||||
|
e = Emit()
|
||||||
|
self.help(e)
|
||||||
|
print(e.finish())
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
for part in self.parts:
|
||||||
|
part.eval(s, args)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
if len(self.parts) == 1:
|
||||||
|
self.parts[0].usage(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
e.emit("(")
|
||||||
|
first = True
|
||||||
|
for part in self.parts:
|
||||||
|
if not first:
|
||||||
|
e.emit(" ")
|
||||||
|
first = False
|
||||||
|
part.usage(e)
|
||||||
|
e.emit(")")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
for part in self.parts:
|
||||||
|
part.help(e)
|
||||||
|
|
||||||
|
|
||||||
|
class OneOf(Node):
|
||||||
|
parts: list[Node]
|
||||||
|
|
||||||
|
def __init__(self, *parts: Node, title: Optional[str] = None):
|
||||||
|
self.parts = list(parts)
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if s.isHelp():
|
||||||
|
e = Emit()
|
||||||
|
self.help(e)
|
||||||
|
print(e.finish())
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
for part in self.parts:
|
||||||
|
if part.eval(s, args):
|
||||||
|
return True
|
||||||
|
|
||||||
|
e = Emit()
|
||||||
|
self.usage(e)
|
||||||
|
if not s.any():
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Unexpected end of input, expected one of " + e.finish())
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Unexpected {s.peek()}, expected one of " + e.finish())
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
e.emit("(")
|
||||||
|
first = True
|
||||||
|
for part in self.parts:
|
||||||
|
if not first:
|
||||||
|
e.emit(" | ")
|
||||||
|
first = False
|
||||||
|
part.usage(e)
|
||||||
|
e.emit(")")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
for part in self.parts:
|
||||||
|
part.help(e)
|
||||||
|
|
||||||
|
|
||||||
|
class ZeroOrMoreOf(Node):
|
||||||
|
operands: list[Node]
|
||||||
|
|
||||||
|
def __init__(self, *operands: Node):
|
||||||
|
self.operands = list(operands)
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if s.isHelp():
|
||||||
|
e = Emit()
|
||||||
|
self.help(e)
|
||||||
|
print(e.finish())
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
any = True
|
||||||
|
while s.any() and any:
|
||||||
|
any = False
|
||||||
|
for part in self.operands:
|
||||||
|
if not part.eval(s, args):
|
||||||
|
break
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
if len(self.operands) == 1:
|
||||||
|
e.emit("[")
|
||||||
|
self.operands[0].usage(e)
|
||||||
|
e.emit("...]")
|
||||||
|
return
|
||||||
|
|
||||||
|
e.emit("[")
|
||||||
|
first = True
|
||||||
|
for part in self.operands:
|
||||||
|
if not first:
|
||||||
|
e.emit(" | ")
|
||||||
|
first = False
|
||||||
|
part.usage(e)
|
||||||
|
e.emit("]+")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
for part in self.operands:
|
||||||
|
part.help(e)
|
||||||
|
|
||||||
|
|
||||||
|
class OneOrMoreOf(Node):
|
||||||
|
operands: list[Node]
|
||||||
|
|
||||||
|
def __init__(self, *operands: Node):
|
||||||
|
self.operands = list(operands)
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if s.isHelp():
|
||||||
|
e = Emit()
|
||||||
|
self.help(e)
|
||||||
|
print(e.finish())
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
any = False
|
||||||
|
while s.any():
|
||||||
|
for operand in self.operands:
|
||||||
|
if operand.eval(s, args):
|
||||||
|
any = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not any:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not any:
|
||||||
|
e = Emit()
|
||||||
|
self.usage(e)
|
||||||
|
if not s.any():
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Unexpected end of input, expected one or more {vt100.BOLD + vt100.GREEN}{e.finish()}{vt100.RESET}")
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Unexpected {vt100.BOLD + vt100.BROWN}{s.peek()}{vt100.RESET}, expected one or more {vt100.BOLD + vt100.GREEN}{e.finish()}{vt100.RESET}")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
if len(self.operands) == 1:
|
||||||
|
e.emit("")
|
||||||
|
self.operands[0].usage(e)
|
||||||
|
e.emit("+")
|
||||||
|
return
|
||||||
|
|
||||||
|
e.emit("[")
|
||||||
|
first = True
|
||||||
|
for operand in self.operands:
|
||||||
|
if not first:
|
||||||
|
e.emit(" | ")
|
||||||
|
first = False
|
||||||
|
e.emit("[")
|
||||||
|
operand.usage(e)
|
||||||
|
e.emit("]")
|
||||||
|
e.emit("]+")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
for operand in self.operands:
|
||||||
|
operand.help(e)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Keywords, Options and Operands ---------------------------------------- #
|
||||||
|
|
||||||
|
class Keyword(Node):
|
||||||
|
name: str
|
||||||
|
shorthand: str
|
||||||
|
description: str
|
||||||
|
syntax: Optional[Node]
|
||||||
|
run: Optional[Callable[[Any], None]]
|
||||||
|
|
||||||
|
def __init__(self, name: str, shorthand: str = "", description: str = "", syntax: Optional[Node] = None, run: Optional[Callable[[Any], None]] = None):
|
||||||
|
self.name = name
|
||||||
|
self.shorthand = shorthand
|
||||||
|
self.description = description
|
||||||
|
self.syntax = syntax
|
||||||
|
self.run = run
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if s.any() and (s.peek() == self.name or s.peek() == self.shorthand):
|
||||||
|
s.next()
|
||||||
|
if self.syntax:
|
||||||
|
try:
|
||||||
|
self.syntax.eval(s, args)
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(f"{vt100.RED}Error:{vt100.RESET} {e}")
|
||||||
|
print("Try: --help for more information")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
if self.run:
|
||||||
|
self.run(args)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
if self.shorthand:
|
||||||
|
e.emit(f'({self.shorthand} | ')
|
||||||
|
|
||||||
|
e.emit(self.name)
|
||||||
|
|
||||||
|
if self.shorthand:
|
||||||
|
e.emit(")")
|
||||||
|
|
||||||
|
if self.syntax:
|
||||||
|
e.emit(" ")
|
||||||
|
self.syntax.usage(e)
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
if self.shorthand:
|
||||||
|
e.emit(f" {vt100.BOLD + vt100.GREEN}{self.shorthand}{vt100.RESET} ")
|
||||||
|
else:
|
||||||
|
e.emit(" ")
|
||||||
|
e.emit(f"{self.name} -")
|
||||||
|
e.emit(f" {self.description}\n")
|
||||||
|
|
||||||
|
e.ident()
|
||||||
|
e.newline()
|
||||||
|
e.emit(f"{vt100.BOLD}Usage:{vt100.RESET}")
|
||||||
|
e.newline()
|
||||||
|
self.usage(e)
|
||||||
|
e.newline()
|
||||||
|
|
||||||
|
if self.syntax:
|
||||||
|
self.syntax.help(e)
|
||||||
|
e.unident()
|
||||||
|
e.newline()
|
||||||
|
|
||||||
|
|
||||||
|
class Option(Node, Generic[T]):
|
||||||
|
name: str
|
||||||
|
key: str
|
||||||
|
shorthand: str
|
||||||
|
description: str
|
||||||
|
default: Optional[T]
|
||||||
|
|
||||||
|
def __init__(self, name: str, key: Optional[str] = None, shorthand: str = "", description: str = "", default: Optional[T] = None):
|
||||||
|
self.name = name
|
||||||
|
self.key = key or name
|
||||||
|
self.shorthand = shorthand
|
||||||
|
self.description = description
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if not s.any():
|
||||||
|
return False
|
||||||
|
|
||||||
|
opt = s.peek()
|
||||||
|
if s.peek().startswith('--' + self.name) or s.peek().startswith('-' + self.shorthand):
|
||||||
|
s.next()
|
||||||
|
if '=' in opt:
|
||||||
|
value = opt.split('=')[1]
|
||||||
|
else:
|
||||||
|
if not s.any():
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Missing value for {vt100.BOLD + vt100.BROWN}{opt}{vt100.RESET}")
|
||||||
|
value = s.next()
|
||||||
|
args[self.key] = value
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
e.emit("(")
|
||||||
|
if self.shorthand:
|
||||||
|
e.emit(f'(-{self.shorthand} | ')
|
||||||
|
|
||||||
|
e.emit(f'--{self.name}')
|
||||||
|
|
||||||
|
if self.shorthand:
|
||||||
|
e.emit(")")
|
||||||
|
|
||||||
|
if self.default:
|
||||||
|
e.emit(f'=<{self.key}>')
|
||||||
|
|
||||||
|
e.emit(")")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
if self.shorthand:
|
||||||
|
e.emit(f"-{self.shorthand}, ")
|
||||||
|
else:
|
||||||
|
e.emit(" ")
|
||||||
|
e.emit(f"--{self.name}")
|
||||||
|
e.emit(f" {self.description}")
|
||||||
|
|
||||||
|
if self.default:
|
||||||
|
e.emit(f" (default: {self.default})")
|
||||||
|
e.emit("\n")
|
||||||
|
|
||||||
|
|
||||||
|
class Operand(Node, Generic[T]):
|
||||||
|
name: str
|
||||||
|
key: str
|
||||||
|
description: str
|
||||||
|
default: Optional[T]
|
||||||
|
|
||||||
|
def __init__(self, name: str, key: Optional[str] = None, description: str = "", default: Optional[T] = None):
|
||||||
|
self.name = name
|
||||||
|
self.key = key or name
|
||||||
|
self.description = description
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if s.any() and not s.peek().startswith('-'):
|
||||||
|
args[self.key] = s.next()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
e.emit(f"<{self.key}>")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.emit(f"{vt100.WHITE}<{self.name}>{vt100.RESET}")
|
||||||
|
e.emit(f" {self.description}\n")
|
||||||
|
if self.default:
|
||||||
|
e.emit(f" (default: {self.default})")
|
||||||
|
|
||||||
|
|
||||||
|
class Operands(Node, Generic[T]):
|
||||||
|
name: str
|
||||||
|
key: str
|
||||||
|
description: str
|
||||||
|
default: list[T]
|
||||||
|
|
||||||
|
def __init__(self, name: str, key: Optional[str] = None, description: str = "", default: list[T] = []):
|
||||||
|
self.name = name
|
||||||
|
self.key = key or name
|
||||||
|
self.description = description
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if self.key not in args:
|
||||||
|
args[self.key] = []
|
||||||
|
|
||||||
|
any = False
|
||||||
|
while s.any() and not s.peek().startswith('-'):
|
||||||
|
args[self.key] += [s.next()]
|
||||||
|
any = True
|
||||||
|
|
||||||
|
if not any and len(args[self.key]) == 0:
|
||||||
|
args[self.key] += self.default
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
e.emit(f"<{self.key}...>")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.emit(f"{vt100.WHITE}<{self.name}>{vt100.RESET}")
|
||||||
|
e.emit(f" {self.description}\n")
|
||||||
|
if self.default:
|
||||||
|
e.emit(f" (default: {self.default})")
|
||||||
|
|
||||||
|
|
||||||
|
class Sink(Node):
|
||||||
|
name: str
|
||||||
|
key: str
|
||||||
|
description: str
|
||||||
|
|
||||||
|
def __init__(self, name: str, key: Optional[str] = None, description: str = ""):
|
||||||
|
self.name = name
|
||||||
|
self.key = key or name
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
if self.key not in args:
|
||||||
|
args[self.key] = []
|
||||||
|
while s.any():
|
||||||
|
args[self.key].append(s.next())
|
||||||
|
return True
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
e.emit(f"<{self.name}>...")
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.emit(f"{vt100.WHITE}<{self.name}...>{vt100.RESET}")
|
||||||
|
e.emit(f" {self.description}\n")
|
||||||
|
e.emit("\n")
|
||||||
|
|
||||||
|
# --- Help ------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
class Title(Node):
|
||||||
|
title: str
|
||||||
|
|
||||||
|
def __init__(self, title: str):
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.emit(f"{vt100.BOLD}{self.title}{vt100.RESET}\n")
|
||||||
|
|
||||||
|
|
||||||
|
class Heading(Node):
|
||||||
|
title: str
|
||||||
|
|
||||||
|
def __init__(self, title: str):
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.emit(f"{vt100.BOLD}{self.title}:{vt100.RESET}\n")
|
||||||
|
|
||||||
|
|
||||||
|
class Paragraph(Node):
|
||||||
|
_text: str
|
||||||
|
|
||||||
|
def __init__(self, text: str):
|
||||||
|
self._text = text
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.emit(f"{self._text}\n")
|
||||||
|
|
||||||
|
|
||||||
|
class Section(Node):
|
||||||
|
_inner: Node
|
||||||
|
|
||||||
|
def __init__(self, inner: Node):
|
||||||
|
self._inner = inner
|
||||||
|
|
||||||
|
def eval(self, s: Scan, args: Any) -> bool:
|
||||||
|
return self._inner.eval(s, args)
|
||||||
|
|
||||||
|
def usage(self, e: Emit) -> None:
|
||||||
|
self._inner.usage(e)
|
||||||
|
|
||||||
|
def help(self, e: Emit) -> None:
|
||||||
|
e.newline()
|
||||||
|
self._inner.help(e)
|
||||||
|
e.newline()
|
||||||
|
|
||||||
|
|
||||||
|
def Options(*nodes: Node) -> Node:
|
||||||
|
return Section(ZeroOrMoreOf(
|
||||||
|
Heading("Options"),
|
||||||
|
Paragraph("Options can be specified in any order."),
|
||||||
|
ZeroOrMoreOf(
|
||||||
|
*nodes,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
cmds = None
|
||||||
|
|
||||||
|
|
||||||
|
def helpCmd(args):
|
||||||
|
global cmds
|
||||||
|
if cmds:
|
||||||
|
e = Emit()
|
||||||
|
cmds.help(e)
|
||||||
|
print(e.finish())
|
||||||
|
|
||||||
|
cmds = Section(OneOf(
|
||||||
|
Title("CuteKit"),
|
||||||
|
Paragraph(
|
||||||
|
"A build system and package manager for low-level software development"),
|
||||||
|
|
||||||
|
Section(
|
||||||
|
ZeroOrMoreOf(
|
||||||
|
Heading("Usage"),
|
||||||
|
Paragraph("ck <command> [args...]")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
Section(
|
||||||
|
OneOf(
|
||||||
|
Heading("Commands"),
|
||||||
|
Keyword(
|
||||||
|
"help",
|
||||||
|
shorthand="h",
|
||||||
|
description="Show help",
|
||||||
|
syntax=AllOf(
|
||||||
|
Operand("command", description="Command to show help for"),
|
||||||
|
),
|
||||||
|
run=helpCmd,
|
||||||
|
),
|
||||||
|
Keyword(
|
||||||
|
"build",
|
||||||
|
shorthand="b",
|
||||||
|
description="Build the project",
|
||||||
|
syntax=AllOf(
|
||||||
|
Options(
|
||||||
|
Option[str](
|
||||||
|
"target",
|
||||||
|
shorthand="t",
|
||||||
|
description="Build target",
|
||||||
|
default="default",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Operands(
|
||||||
|
"components",
|
||||||
|
description="Components to build",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
run=lambda args: print("build", args),
|
||||||
|
),
|
||||||
|
Keyword(
|
||||||
|
"run",
|
||||||
|
shorthand="r",
|
||||||
|
description="Run the project",
|
||||||
|
syntax=AllOf(
|
||||||
|
Options(
|
||||||
|
Option[str](
|
||||||
|
"target",
|
||||||
|
shorthand="t",
|
||||||
|
description="Build target",
|
||||||
|
default="default",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Operand("component", description="Component to run"),
|
||||||
|
Sink("args", description="Arguments to pass to the component")
|
||||||
|
),
|
||||||
|
run=lambda args: print("run", args),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmds.eval(Scan(sys.argv[1:]), {})
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(f"{vt100.RED}Error:{vt100.RESET} {e}")
|
||||||
|
print("Try: --help for more information")
|
||||||
|
raise SystemExit(1)
|
||||||
|
"""
|
|
@ -93,7 +93,7 @@ def gen(out: TextIO, context: Context):
|
||||||
writer.default("all")
|
writer.default("all")
|
||||||
|
|
||||||
|
|
||||||
def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentInstance:
|
def buildMany(componentSpec: list[str], targetSpec: str, props: Props = {}) -> list[ComponentInstance]:
|
||||||
context = contextFor(targetSpec, props)
|
context = contextFor(targetSpec, props)
|
||||||
|
|
||||||
shell.mkdir(context.builddir())
|
shell.mkdir(context.builddir())
|
||||||
|
@ -102,18 +102,21 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentIn
|
||||||
with open(ninjaPath, "w") as f:
|
with open(ninjaPath, "w") as f:
|
||||||
gen(f, context)
|
gen(f, context)
|
||||||
|
|
||||||
instance = context.componentByName(componentSpec)
|
instances = map(lambda i: context.componentByName(i), componentSpec)
|
||||||
|
|
||||||
if instance is None:
|
for instance in instances:
|
||||||
raise RuntimeError(f"Component {componentSpec} not found")
|
if not instance.enabled:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Component {componentSpec} is disabled: {instance.disableReason}")
|
||||||
|
|
||||||
if not instance.enabled:
|
shell.exec(f"ninja", "-v", "-f", ninjaPath,
|
||||||
raise RuntimeError(
|
*map(lambda i: i.outfile(), instances))
|
||||||
f"Component {componentSpec} is disabled: {instance.disableReason}")
|
|
||||||
|
|
||||||
shell.exec(f"ninja", "-v", "-f", ninjaPath, instance.outfile())
|
return [instance]
|
||||||
|
|
||||||
return instance
|
|
||||||
|
def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentInstance:
|
||||||
|
return buildMany([componentSpec], targetSpec, props)[0]
|
||||||
|
|
||||||
|
|
||||||
class Paths:
|
class Paths:
|
||||||
|
|
473
cutekit/cmds.py
473
cutekit/cmds.py
|
@ -2,212 +2,328 @@ import os
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import sys
|
import sys
|
||||||
|
import git
|
||||||
|
|
||||||
from typing import Callable, cast, Optional, NoReturn
|
from typing import cast, Optional, Any
|
||||||
|
|
||||||
from cutekit import context, shell, const, vt100, builder, graph, project
|
from cutekit import context, shell, const, vt100, builder, graph, project, args2
|
||||||
from cutekit.args import Args
|
|
||||||
from cutekit.jexpr import Json
|
|
||||||
from cutekit.model import Extern
|
from cutekit.model import Extern
|
||||||
from cutekit.context import contextFor
|
from cutekit.context import contextFor
|
||||||
|
|
||||||
Callback = Callable[[Args], None]
|
|
||||||
|
# === Commons ================================================================ #
|
||||||
|
|
||||||
|
TARGET_OPTION = args2.Option[str](
|
||||||
|
"target",
|
||||||
|
shorthand="t",
|
||||||
|
description="Build target",
|
||||||
|
default="host-" + shell.uname().machine
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
root: Optional[args2.Node] = None
|
||||||
class Cmd:
|
cmds: list[args2.Node] = []
|
||||||
shortName: Optional[str]
|
|
||||||
longName: str
|
|
||||||
helpText: str
|
|
||||||
callback: Callable[[Args], NoReturn]
|
|
||||||
isPlugin: bool = False
|
|
||||||
|
|
||||||
def __init__(self, shortName: Optional[str], longName: str, helpText: str, callback: Callable[[Args], NoReturn]):
|
|
||||||
self.shortName = shortName
|
|
||||||
self.longName = longName
|
|
||||||
self.helpText = helpText
|
|
||||||
self.callback = callback
|
|
||||||
|
|
||||||
|
|
||||||
cmds: list[Cmd] = []
|
def append(cmd: args2.Node):
|
||||||
|
global cmds
|
||||||
|
|
||||||
def append(cmd: Cmd):
|
|
||||||
cmd.isPlugin = True
|
|
||||||
cmds.append(cmd)
|
cmds.append(cmd)
|
||||||
cmds.sort(key=lambda c: c.shortName or c.longName)
|
|
||||||
|
|
||||||
|
|
||||||
def runCmd(args: Args):
|
def usage():
|
||||||
|
print(f"Usage: {const.ARGV0} <command> [args...]")
|
||||||
|
|
||||||
|
|
||||||
|
def error(msg: str) -> None:
|
||||||
|
print(f"{vt100.RED}Error:{vt100.RESET} {msg}\n", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def exec(args: list[str]):
|
||||||
|
global root, cmds
|
||||||
|
|
||||||
|
if root is None:
|
||||||
|
root = args2.Section(args2.OneOf(
|
||||||
|
args2.Title("CuteKit"),
|
||||||
|
args2.Paragraph(
|
||||||
|
"A build system and package manager for low-level software development"),
|
||||||
|
|
||||||
|
args2.Section(
|
||||||
|
args2.ZeroOrMoreOf(
|
||||||
|
args2.Heading("Usage"),
|
||||||
|
args2.Paragraph("ck <command> [args...]")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
args2.Section(
|
||||||
|
args2.OneOf(
|
||||||
|
args2.Heading("Commands"),
|
||||||
|
*cmds
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
try:
|
||||||
|
root.eval(args2.Scan(args[1:]), {})
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(f"{vt100.RED}Error:{vt100.RESET} {e}")
|
||||||
|
print("Try: --help for more information")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
|
# === Commands =============================================================== #
|
||||||
|
|
||||||
|
# --- Help Command ----------------------------------------------------------- #
|
||||||
|
|
||||||
|
def helpCmd(args: Any):
|
||||||
|
global root
|
||||||
|
if root:
|
||||||
|
e = args.Emit()
|
||||||
|
root.help(e)
|
||||||
|
print(e.finish())
|
||||||
|
|
||||||
|
|
||||||
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"help",
|
||||||
|
shorthand="h",
|
||||||
|
description="Show help",
|
||||||
|
run=helpCmd,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Run Command ------------------------------------------------------------ #
|
||||||
|
|
||||||
|
|
||||||
|
def runCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
component = builder.build(args['component'], args['target'])
|
||||||
targetSpec = cast(str, args.consumeOpt(
|
|
||||||
"target", "host-" + shell.uname().machine))
|
|
||||||
|
|
||||||
componentSpec = args.consumeArg()
|
|
||||||
|
|
||||||
if componentSpec is None:
|
|
||||||
raise RuntimeError("Component not specified")
|
|
||||||
|
|
||||||
component = builder.build(componentSpec, targetSpec)
|
|
||||||
|
|
||||||
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()
|
||||||
os.environ["CK_BUILDDIR"] = component.context.builddir()
|
os.environ["CK_BUILDDIR"] = component.context.builddir()
|
||||||
|
shell.exec(component.outfile(), *args["args"])
|
||||||
shell.exec(component.outfile(), *args.args)
|
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("r", "run", "Run the target", runCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"run",
|
||||||
|
shorthand="r",
|
||||||
|
description="Run the target",
|
||||||
|
run=runCmd,
|
||||||
|
syntax=args2.AllOf(
|
||||||
|
args2.Options(
|
||||||
|
TARGET_OPTION
|
||||||
|
),
|
||||||
|
args2.Operand("component", description="Component to run"),
|
||||||
|
args2.Sink("args", description="Arguments to pass to the component")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def testCmd(args: Args):
|
# --- Test Command ----------------------------------------------------------- #
|
||||||
|
|
||||||
|
def testCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
builder.testAll(args['target'])
|
||||||
targetSpec = cast(str, args.consumeOpt(
|
|
||||||
"target", "host-" + shell.uname().machine))
|
|
||||||
builder.testAll(targetSpec)
|
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("t", "test", "Run all test targets", testCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"test",
|
||||||
|
shorthand="t",
|
||||||
|
description="Run all test",
|
||||||
|
run=testCmd,
|
||||||
|
syntax=args2.AllOf(
|
||||||
|
args2.Options(
|
||||||
|
TARGET_OPTION
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def debugCmd(args: Args):
|
# --- Debug Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
def debugCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
component = builder.build(args['component'], args['target'])
|
||||||
targetSpec = cast(str, args.consumeOpt(
|
|
||||||
"target", "host-" + shell.uname().machine))
|
|
||||||
|
|
||||||
componentSpec = args.consumeArg()
|
|
||||||
|
|
||||||
if componentSpec is None:
|
|
||||||
raise RuntimeError("Component not specified")
|
|
||||||
|
|
||||||
component = builder.build(componentSpec, targetSpec)
|
|
||||||
|
|
||||||
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()
|
||||||
os.environ["CK_BUILDDIR"] = component.context.builddir()
|
os.environ["CK_BUILDDIR"] = component.context.builddir()
|
||||||
|
shell.exec("lldb", "-o", "run", component.outfile(), *args["args"])
|
||||||
shell.exec("lldb", "-o", "run", component.outfile(), *args.args)
|
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("d", "debug", "Debug the target", debugCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"debug",
|
||||||
|
shorthand="d",
|
||||||
|
description="Run a component in the debugger",
|
||||||
|
run=debugCmd,
|
||||||
|
syntax=args2.AllOf(
|
||||||
|
args2.Options(
|
||||||
|
TARGET_OPTION
|
||||||
|
),
|
||||||
|
args2.Operand("component", description="Component to debug"),
|
||||||
|
args2.Sink("args", description="Arguments to pass to the component")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def buildCmd(args: Args):
|
# --- Build Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
def buildCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
if len(args['components']) == 0:
|
||||||
targetSpec = cast(str, args.consumeOpt(
|
builder.buildAll(args['target'])
|
||||||
"target", "host-" + shell.uname().machine))
|
|
||||||
|
|
||||||
componentSpec = args.consumeArg()
|
|
||||||
|
|
||||||
if componentSpec is None:
|
|
||||||
builder.buildAll(targetSpec)
|
|
||||||
else:
|
else:
|
||||||
builder.build(componentSpec, targetSpec)
|
builder.buildMany(args['components'], args['target'])
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("b", "build", "Build the target", buildCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"build",
|
||||||
|
shorthand="b",
|
||||||
|
description="Build specified component or all components",
|
||||||
|
run=buildCmd,
|
||||||
|
syntax=args2.AllOf(
|
||||||
|
args2.Options(
|
||||||
|
TARGET_OPTION
|
||||||
|
),
|
||||||
|
args2.Operands(
|
||||||
|
"components", description="Components to build", default=[])
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def listCmd(args: Args):
|
# --- List Command ----------------------------------------------------------- #
|
||||||
project.chdir()
|
|
||||||
|
|
||||||
components = context.loadAllComponents()
|
def listWithTitle(title: str, elements: list[str]):
|
||||||
targets = context.loadAllTargets()
|
vt100.title(title + " (" + str(len(elements)) + ")")
|
||||||
|
if len(elements) == 0:
|
||||||
vt100.title("Components")
|
print(f" (No {title.lower()} available)")
|
||||||
if len(components) == 0:
|
|
||||||
print(f" (No components available)")
|
|
||||||
else:
|
else:
|
||||||
print(vt100.indent(vt100.wordwrap(
|
print(vt100.indent(vt100.wordwrap(
|
||||||
", ".join(map(lambda m: m.id, components)))))
|
", ".join(elements))))
|
||||||
print()
|
|
||||||
|
|
||||||
vt100.title("Targets")
|
|
||||||
|
|
||||||
if len(targets) == 0:
|
|
||||||
print(f" (No targets available)")
|
|
||||||
else:
|
|
||||||
print(vt100.indent(vt100.wordwrap(", ".join(map(lambda m: m.id, targets)))))
|
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("l", "list", "List the targets", listCmd)]
|
def listCmd(args: Any):
|
||||||
|
project.chdir()
|
||||||
|
components = context.loadAllComponents()
|
||||||
|
targets = context.loadAllTargets()
|
||||||
|
listWithTitle("Components", list(map(lambda m: m.id, components)))
|
||||||
|
listWithTitle("Targets", list(map(lambda m: m.id, targets)))
|
||||||
|
|
||||||
|
|
||||||
def cleanCmd(args: Args):
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"list",
|
||||||
|
shorthand="l",
|
||||||
|
description="List all targets and components",
|
||||||
|
run=listCmd,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Clean Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def cleanCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
shell.rmrf(const.BUILD_DIR)
|
shell.rmrf(const.BUILD_DIR)
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("c", "clean", "Clean the build directory", cleanCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"clean",
|
||||||
|
shorthand="c",
|
||||||
|
description="Clean the build directory",
|
||||||
|
run=cleanCmd,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Nuke Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
def nukeCmd(args: Args):
|
def nukeCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
shell.rmrf(const.PROJECT_CK_DIR)
|
shell.rmrf(const.PROJECT_CK_DIR)
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("n", "nuke", "Clean the build directory and cache", nukeCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"nuke",
|
||||||
|
shorthand="n",
|
||||||
|
description="Clean the build and cache directories",
|
||||||
|
run=nukeCmd,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def helpCmd(args: Args):
|
# --- Version Command ---------------------------------------------------------- #
|
||||||
usage()
|
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
vt100.title("Description")
|
|
||||||
print(f" {const.DESCRIPTION}")
|
|
||||||
|
|
||||||
print()
|
|
||||||
vt100.title("Commands")
|
|
||||||
for cmd in cmds:
|
|
||||||
pluginText = ""
|
|
||||||
if cmd.isPlugin:
|
|
||||||
pluginText = f"{vt100.CYAN}(plugin){vt100.RESET}"
|
|
||||||
|
|
||||||
print(
|
|
||||||
f" {vt100.GREEN}{cmd.shortName or ' '}{vt100.RESET} {cmd.longName} - {cmd.helpText} {pluginText}")
|
|
||||||
|
|
||||||
print()
|
|
||||||
vt100.title("Logging")
|
|
||||||
print(f" Logs are stored in:")
|
|
||||||
print(f" - {const.PROJECT_LOG_FILE}")
|
|
||||||
print(f" - {const.GLOBAL_LOG_FILE}")
|
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("h", "help", "Show this help message", helpCmd)]
|
def versionCmd(args: Any):
|
||||||
|
|
||||||
|
|
||||||
def versionCmd(args: Args):
|
|
||||||
print(f"CuteKit v{const.VERSION_STR}\n")
|
print(f"CuteKit v{const.VERSION_STR}\n")
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("v", "version", "Show current version", versionCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"version",
|
||||||
|
shorthand="v",
|
||||||
|
description="Show current version",
|
||||||
|
run=versionCmd,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Version Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
def graphCmd(args: Args):
|
def graphCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
context = contextFor(args["target"])
|
||||||
targetSpec = cast(str, args.consumeOpt(
|
graph.view(
|
||||||
"target", "host-" + shell.uname().machine))
|
context,
|
||||||
|
scope=args["scope"],
|
||||||
scope: Optional[str] = cast(Optional[str], args.tryConsumeOpt("scope"))
|
showExe=not args["only-libs"],
|
||||||
onlyLibs: bool = args.consumeOpt("only-libs", False) == True
|
showDisabled=args["show-disabled"])
|
||||||
showDisabled: bool = args.consumeOpt("show-disabled", False) == True
|
|
||||||
|
|
||||||
context = contextFor(targetSpec)
|
|
||||||
|
|
||||||
graph.view(context, scope=scope, showExe=not onlyLibs,
|
|
||||||
showDisabled=showDisabled)
|
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("g", "graph", "Show dependency graph", graphCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"graph",
|
||||||
|
shorthand="g",
|
||||||
|
description="Show dependency graph",
|
||||||
|
run=graphCmd,
|
||||||
|
syntax=args2.AllOf(
|
||||||
|
args2.Options(
|
||||||
|
TARGET_OPTION,
|
||||||
|
args2.Option[str](
|
||||||
|
"scope",
|
||||||
|
shorthand="s",
|
||||||
|
description="Scope to show (default: all)",
|
||||||
|
default=None,
|
||||||
|
),
|
||||||
|
args2.Option[bool](
|
||||||
|
"only-libs",
|
||||||
|
shorthand="l",
|
||||||
|
description="Only show libraries",
|
||||||
|
default=False,
|
||||||
|
),
|
||||||
|
args2.Option[bool](
|
||||||
|
"show-disabled",
|
||||||
|
shorthand="d",
|
||||||
|
description="Show disabled components",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Install Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
def grabExtern(extern: dict[str, Extern]):
|
def grabExtern(extern: dict[str, Extern]):
|
||||||
|
@ -225,22 +341,30 @@ def grabExtern(extern: dict[str, Extern]):
|
||||||
grabExtern(context.loadProject(extPath).extern)
|
grabExtern(context.loadProject(extPath).extern)
|
||||||
|
|
||||||
|
|
||||||
def installCmd(args: Args):
|
def installCmd(args: Any):
|
||||||
project.chdir()
|
project.chdir()
|
||||||
|
p = context.loadProject(".")
|
||||||
pj = context.loadProject(".")
|
grabExtern(p.extern)
|
||||||
grabExtern(pj.extern)
|
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("i", "install", "Install all the external packages", installCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"install",
|
||||||
|
shorthand="i",
|
||||||
|
description="Install external dependencies",
|
||||||
|
run=installCmd,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Init Command ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
def initCmd(args: Args):
|
def initCmd(args: Any):
|
||||||
repo = args.consumeOpt('repo', const.DEFAULT_REPO_TEMPLATES)
|
repo = args['repo']
|
||||||
list = args.consumeOpt('list')
|
list = args['list']
|
||||||
|
|
||||||
template = args.consumeArg()
|
template = args['template']
|
||||||
name = args.consumeArg()
|
name = args['name']
|
||||||
|
|
||||||
logger.info("Fetching registry...")
|
logger.info("Fetching registry...")
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
|
@ -261,7 +385,7 @@ def initCmd(args: Args):
|
||||||
raise RuntimeError('Failed to fetch registry')
|
raise RuntimeError('Failed to fetch registry')
|
||||||
|
|
||||||
print('\n'.join(
|
print('\n'.join(
|
||||||
f"* {entry['id']} - {entry['description']}" for entry in json.loads(r.text)))
|
f"* {entry['id']} - {entry['description']}" for entry in registry))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not template:
|
if not template:
|
||||||
|
@ -284,26 +408,35 @@ def initCmd(args: Args):
|
||||||
f" {vt100.GREEN}cutekit build{vt100.BRIGHT_BLACK} # Build the project{vt100.RESET}")
|
f" {vt100.GREEN}cutekit build{vt100.BRIGHT_BLACK} # Build the project{vt100.RESET}")
|
||||||
|
|
||||||
|
|
||||||
cmds += [Cmd("I", "init", "Initialize a new project", initCmd)]
|
append(
|
||||||
|
args2.Keyword(
|
||||||
|
"init",
|
||||||
def usage():
|
shorthand="I",
|
||||||
print(f"Usage: {const.ARGV0} <command> [args...]")
|
description="Initialize a new project",
|
||||||
|
run=installCmd,
|
||||||
|
syntax=args2.AllOf(
|
||||||
def error(msg: str) -> None:
|
args2.Options(
|
||||||
print(f"{vt100.RED}Error:{vt100.RESET} {msg}\n", file=sys.stderr)
|
args2.Option[str](
|
||||||
|
"repo",
|
||||||
|
shorthand="r",
|
||||||
def exec(args: Args):
|
description="Repository to fetch templates from",
|
||||||
cmd = args.consumeArg()
|
default=const.DEFAULT_REPO_TEMPLATES,
|
||||||
|
),
|
||||||
if cmd is None:
|
args2.Option[bool](
|
||||||
raise RuntimeError("No command specified")
|
"list",
|
||||||
|
shorthand="l",
|
||||||
for c in cmds:
|
description="List available templates",
|
||||||
if c.shortName == cmd or c.longName == cmd:
|
default=False,
|
||||||
c.callback(args)
|
),
|
||||||
return
|
),
|
||||||
|
args2.Operand[str](
|
||||||
raise RuntimeError(f"Unknown command {cmd}")
|
"template",
|
||||||
|
description="Template to use",
|
||||||
|
),
|
||||||
|
args2.Operand[str](
|
||||||
|
"name",
|
||||||
|
description="Name of the project",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -95,10 +95,10 @@ class Context(IContext):
|
||||||
self.instances = instances
|
self.instances = instances
|
||||||
self.tools = tools
|
self.tools = tools
|
||||||
|
|
||||||
def componentByName(self, name: str) -> Optional[ComponentInstance]:
|
def componentByName(self, name: str) -> ComponentInstance:
|
||||||
result = list(filter(lambda x: x.manifest.id == name, self.instances))
|
result = list(filter(lambda x: x.manifest.id == name, self.instances))
|
||||||
if len(result) == 0:
|
if len(result) == 0:
|
||||||
return None
|
raise RuntimeError(f"Component '{name}' not found")
|
||||||
return result[0]
|
return result[0]
|
||||||
|
|
||||||
def cincls(self) -> list[str]:
|
def cincls(self) -> list[str]:
|
||||||
|
|
Loading…
Reference in a new issue