meta: Renamed osdk -> cutekit + improved logging + better error handling.

This commit is contained in:
Sleepy Monax 2023-05-28 11:28:52 +02:00 committed by Sleepy Monax
parent 00537aa34e
commit f345f94471
25 changed files with 283 additions and 154 deletions

View file

@ -17,7 +17,6 @@ permissions:
jobs: jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View file

@ -1,26 +1,35 @@
# osdk <br/>
<br/>
The operating system development kit <br/>
<p align="center">
<img src="logo.png" width="200" height="200">
</p>
<h1 align="center">CuteKit</h1>
<p align="center">
The Cute build system and package manager
</p>
<br/>
<br/>
<br/>
## Table of contents ## Table of contents
- [osdk](#osdk) - [Table of contents](#table-of-contents)
- [Table of contents](#table-of-contents) - [Macros](#macros)
- [Macros](#macros)
- [`@latest`](#latest) - [`@latest`](#latest)
- [`@uname`](#uname) - [`@uname`](#uname)
- [`@include`](#include) - [`@include`](#include)
- [`@join`](#join) - [`@join`](#join)
- [`@concat`](#concat) - [`@concat`](#concat)
- [`@exec`](#exec) - [`@exec`](#exec)
- [Manifest file format](#manifest-file-format) - [Manifest file format](#manifest-file-format)
- [`id`](#id) - [`id`](#id)
- [`type`](#type) - [`type`](#type)
- [`description`](#description) - [`description`](#description)
- [`enabledIf`](#enabledif) - [`enabledIf`](#enabledif)
- [`requires`](#requires) - [`requires`](#requires)
- [`provides`](#provides) - [`provides`](#provides)
- [Target file format](#target-file-format) - [Target file format](#target-file-format)
- [`id`](#id-1) - [`id`](#id-1)
- [`type`](#type-1) - [`type`](#type-1)
- [`props`](#props) - [`props`](#props)
@ -197,7 +206,7 @@ Exemple:
} }
``` ```
Theses values are exposed the translation unit as `__osdk_{prop}__`. Theses values are exposed the translation unit as `__ck_{prop}__`.
### `tools` ### `tools`

50
cutekit/__init__.py Normal file
View file

@ -0,0 +1,50 @@
import sys
import os
import logging
from cutekit import const, project, vt100, plugins, cmds
from cutekit.args import parse
def setupLogger(verbose: bool):
if verbose:
logging.basicConfig(
level=logging.INFO,
format=f"{vt100.CYAN}%(asctime)s{vt100.RESET} {vt100.YELLOW}%(levelname)s{vt100.RESET} %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
else:
projectRoot = project.root()
logFile = const.GLOBAL_LOG_FILE
if projectRoot is not None:
logFile = os.path.join(projectRoot, const.PROJECT_LOG_FILE)
# create the directory if it doesn't exist
logDir = os.path.dirname(logFile)
if not os.path.isdir(logDir):
os.makedirs(logDir)
logging.basicConfig(
level=logging.INFO,
filename=logFile,
filemode="w",
format=f"%(asctime)s %(levelname)s %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
def main() -> int:
try:
a = parse(sys.argv[1:])
setupLogger(a.consumeOpt("verbose", False) == True)
plugins.loadAll()
cmds.exec(a)
print()
return 0
except RuntimeError as e:
logging.exception(e)
cmds.error(str(e))
cmds.usage()
print()
return 1
except KeyboardInterrupt:
print()
return 1

View file

@ -2,10 +2,10 @@ import os
import logging import logging
from typing import TextIO from typing import TextIO
from osdk.model import Props from cutekit.model import Props
from osdk.ninja import Writer from cutekit.ninja import Writer
from osdk.context import ComponentInstance, Context, contextFor from cutekit.context import ComponentInstance, Context, contextFor
from osdk import shell, rules from cutekit import shell, rules
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -54,7 +54,7 @@ def gen(out: TextIO, context: Context):
for obj in objects: for obj in objects:
r = rules.byFileIn(obj[0]) r = rules.byFileIn(obj[0])
if r is None: if r is None:
raise Exception(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)
@ -73,10 +73,10 @@ def gen(out: TextIO, context: Context):
reqInstance = context.componentByName(req) reqInstance = context.componentByName(req)
if reqInstance is None: if reqInstance is None:
raise Exception(f"Component {req} not found") raise RuntimeError(f"Component {req} not found")
if not reqInstance.isLib(): if not reqInstance.isLib():
raise Exception(f"Component {req} is not a library") raise RuntimeError(f"Component {req} is not a library")
libraries.append(reqInstance.outfile()) libraries.append(reqInstance.outfile())
@ -105,10 +105,10 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentIn
instance = context.componentByName(componentSpec) instance = context.componentByName(componentSpec)
if instance is None: if instance is None:
raise Exception(f"Component {componentSpec} not found") raise RuntimeError(f"Component {componentSpec} not found")
if not instance.enabled: if not instance.enabled:
raise Exception( raise RuntimeError(
f"Component {componentSpec} is disabled: {instance.disableReason}") f"Component {componentSpec} is disabled: {instance.disableReason}")
shell.exec(f"ninja", "-v", "-f", ninjaPath, instance.outfile()) shell.exec(f"ninja", "-v", "-f", ninjaPath, instance.outfile())

View file

@ -3,17 +3,19 @@ import json
import logging import logging
import tempfile import tempfile
import requests import requests
import sys
from typing import Callable, cast from typing import Callable, cast
from osdk import context, shell, const, vt100, builder, graph from cutekit import context, shell, const, vt100, builder, graph, project
from osdk.args import Args from cutekit.args import Args
from osdk.context import contextFor from cutekit.context import contextFor
Callback = Callable[[Args], None] Callback = Callable[[Args], None]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Cmd: class Cmd:
shortName: str | None shortName: str | None
longName: str longName: str
@ -38,19 +40,21 @@ def append(cmd: Cmd):
def runCmd(args: Args): def runCmd(args: Args):
project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt(
"target", "host-" + shell.uname().machine)) "target", "host-" + shell.uname().machine))
componentSpec = args.consumeArg() componentSpec = args.consumeArg()
if componentSpec is None: if componentSpec is None:
raise Exception("Component not specified") raise RuntimeError("Component not specified")
component = builder.build(componentSpec, targetSpec) component = builder.build(componentSpec, targetSpec)
os.environ["OSDK_TARGET"] = component.context.target.id os.environ["CK_TARGET"] = component.context.target.id
os.environ["OSDK_COMPONENT"] = component.id() os.environ["CK_COMPONENT"] = component.id()
os.environ["OSDK_BUILDDIR"] = component.context.builddir() os.environ["CK_BUILDDIR"] = component.context.builddir()
shell.exec(component.outfile(), *args.args) shell.exec(component.outfile(), *args.args)
@ -59,6 +63,8 @@ cmds += [Cmd("r", "run", "Run the target", runCmd)]
def testCmd(args: Args): def testCmd(args: Args):
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)
@ -68,19 +74,21 @@ cmds += [Cmd("t", "test", "Run all test targets", testCmd)]
def debugCmd(args: Args): def debugCmd(args: Args):
project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt(
"target", "host-" + shell.uname().machine)) "target", "host-" + shell.uname().machine))
componentSpec = args.consumeArg() componentSpec = args.consumeArg()
if componentSpec is None: if componentSpec is None:
raise Exception("Component not specified") raise RuntimeError("Component not specified")
component = builder.build(componentSpec, targetSpec) component = builder.build(componentSpec, targetSpec)
os.environ["OSDK_TARGET"] = component.context.target.id os.environ["CK_TARGET"] = component.context.target.id
os.environ["OSDK_COMPONENT"] = component.id() os.environ["CK_COMPONENT"] = component.id()
os.environ["OSDK_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)
@ -89,6 +97,8 @@ cmds += [Cmd("d", "debug", "Debug the target", debugCmd)]
def buildCmd(args: Args): def buildCmd(args: Args):
project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt(
"target", "host-" + shell.uname().machine)) "target", "host-" + shell.uname().machine))
@ -104,6 +114,8 @@ cmds += [Cmd("b", "build", "Build the target", buildCmd)]
def listCmd(args: Args): def listCmd(args: Args):
project.chdir()
components = context.loadAllComponents() components = context.loadAllComponents()
targets = context.loadAllTargets() targets = context.loadAllTargets()
@ -129,6 +141,7 @@ cmds += [Cmd("l", "list", "List the targets", listCmd)]
def cleanCmd(args: Args): def cleanCmd(args: Args):
project.chdir()
shell.rmrf(const.BUILD_DIR) shell.rmrf(const.BUILD_DIR)
@ -136,7 +149,8 @@ cmds += [Cmd("c", "clean", "Clean the build directory", cleanCmd)]
def nukeCmd(args: Args): def nukeCmd(args: Args):
shell.rmrf(const.OSDK_DIR) project.chdir()
shell.rmrf(const.PROJECT_CK_DIR)
cmds += [Cmd("n", "nuke", "Clean the build directory and cache", nukeCmd)] cmds += [Cmd("n", "nuke", "Clean the build directory and cache", nukeCmd)]
@ -148,7 +162,7 @@ def helpCmd(args: Args):
print() print()
vt100.title("Description") vt100.title("Description")
print(" Operating System Development Kit.") print(f" {const.DESCRIPTION}")
print() print()
vt100.title("Commands") vt100.title("Commands")
@ -161,19 +175,25 @@ def helpCmd(args: Args):
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")
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)] cmds += [Cmd("h", "help", "Show this help message", helpCmd)]
def versionCmd(args: Args): def versionCmd(args: Args):
print(f"OSDK v{const.VERSION}\n") print(f"CuteKit v{const.VERSION_STR}\n")
cmds += [Cmd("v", "version", "Show current version", versionCmd)] cmds += [Cmd("v", "version", "Show current version", versionCmd)]
def graphCmd(args: Args): def graphCmd(args: Args):
project.chdir()
targetSpec = cast(str, args.consumeOpt( targetSpec = cast(str, args.consumeOpt(
"target", "host-" + shell.uname().machine)) "target", "host-" + shell.uname().machine))
@ -191,10 +211,12 @@ cmds += [Cmd("g", "graph", "Show dependency graph", graphCmd)]
def installCmd(args: Args): def installCmd(args: Args):
project = context.loadProject(".") project.chdir()
for extSpec in project.extern: pj = context.loadProject(".")
ext = project.extern[extSpec]
for extSpec in pj.extern:
ext = pj.extern[extSpec]
extPath = os.path.join(const.EXTERN_DIR, extSpec) extPath = os.path.join(const.EXTERN_DIR, extSpec)
@ -212,41 +234,54 @@ cmds += [Cmd("i", "install", "Install all the external packages", installCmd)]
def initCmd(args: Args): def initCmd(args: Args):
template = args.consumeArg() template = args.consumeArg()
if template is None:
template = "default"
repo = const.DEFAULT_REPO_TEMPLATES if not "repo" in args.opts else args.opts["repo"] repo = const.DEFAULT_REPO_TEMPLATES if not "repo" in args.opts else args.opts["repo"]
list = "list" in args.opts list = "list" in args.opts
if list: if list:
logger.info("Fetching registry...") logger.info("Fetching registry...")
r = requests.get(f'https://raw.githubusercontent.com/{repo}/main/registry.json') r = requests.get(
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)
print('\n'.join(f"* {entry['id']} - {entry['description']}" for entry in json.loads(r.text))) print('\n'.join(
f"* {entry['id']} - {entry['description']}" for entry in json.loads(r.text)))
else: else:
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
shell.exec(*["git", "clone", "-n", "--depth=1", "--filter=tree:0", f"https://github.com/{repo}", os.path.join(tmp, "osdk-repo"), "-q"]) shell.exec(*["git", "clone", "-n", "--depth=1",
shell.exec(*["git", "-C", os.path.join(tmp, "osdk-repo"), "sparse-checkout", "set", "--no-cone", template, "-q"]) "--filter=tree:0", f"https://github.com/{repo}", tmp, "-q"])
shell.exec(*["git", "-C", os.path.join(tmp, "osdk-repo"), "checkout", "-q"]) shell.exec(*["git", "-C", tmp, "sparse-checkout",
shell.mv(os.path.join(tmp, "osdk-repo", template), os.path.join(".", template)) "set", "--no-cone", template, "-q"])
shell.exec(*["git", "-C", tmp, "checkout", "-q"])
shell.mv(os.path.join(tmp, template), os.path.join(".", template))
cmds += [Cmd("I", "init", "Start a new project", initCmd)]
cmds += [Cmd("I", "init", "Initialize a new project", initCmd)]
def usage(): def usage():
print(f"Usage: {const.ARGV0} <command> [args...]") 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: Args): def exec(args: Args):
cmd = args.consumeArg() cmd = args.consumeArg()
if cmd is None: if cmd is None:
raise Exception("No command specified") raise RuntimeError("No command specified")
for c in cmds: for c in cmds:
if c.shortName == cmd or c.longName == cmd: if c.shortName == cmd or c.longName == cmd:
c.callback(args) c.callback(args)
return return
raise Exception(f"Unknown command {cmd}") raise RuntimeError(f"Unknown command {cmd}")

33
cutekit/compat.py Normal file
View file

@ -0,0 +1,33 @@
from typing import Any
SUPPORTED_MANIFEST = [
"https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"https://schemas.cute.engineering/stable/cutekit.manifest.project.v1",
"https://schemas.cute.engineering/stable/cutekit.manifest.target.v1",
]
OSDK_MANIFEST_NOT_SUPPORTED = "OSDK manifests are not supported by CuteKit. Please use CuteKit manifest instead"
UNSUPORTED_MANIFEST = {
"https://schemas.cute.engineering/stable/osdk.manifest.component.v1": OSDK_MANIFEST_NOT_SUPPORTED,
"https://schemas.cute.engineering/stable/osdk.manifest.project.v1": OSDK_MANIFEST_NOT_SUPPORTED,
"https://schemas.cute.engineering/stable/osdk.manifest.target.v1": OSDK_MANIFEST_NOT_SUPPORTED,
"https://schemas.cute.engineering/latest/osdk.manifest.component": OSDK_MANIFEST_NOT_SUPPORTED,
"https://schemas.cute.engineering/latest/osdk.manifest.project": OSDK_MANIFEST_NOT_SUPPORTED,
"https://schemas.cute.engineering/latest/osdk.manifest.target": OSDK_MANIFEST_NOT_SUPPORTED,
}
def ensureSupportedManifest(manifest: Any, path: str):
if not "$schema" in manifest:
raise RuntimeError(f"Missing $schema in {path}")
if manifest["$schema"] in UNSUPORTED_MANIFEST:
raise RuntimeError(
f"Unsupported manifest schema {manifest['$schema']} in {path}: {UNSUPORTED_MANIFEST[manifest['$schema']]}")
if not manifest["$schema"] in SUPPORTED_MANIFEST:
raise RuntimeError(
f"Unsupported manifest schema {manifest['$schema']} in {path}")

19
cutekit/const.py Normal file
View file

@ -0,0 +1,19 @@
import os
import sys
VERSION = (0, 5, 0, "dev")
VERSION_STR = f"{VERSION[0]}.{VERSION[1]}.{VERSION[2]}{'-' + VERSION[3] if VERSION[3] else ''}"
MODULE_DIR = os.path.dirname(os.path.realpath(__file__))
ARGV0 = os.path.basename(sys.argv[0])
PROJECT_CK_DIR = ".cutekit"
GLOBAL_CK_DIR = os.path.join(os.path.expanduser("~"), ".cutekit")
BUILD_DIR = os.path.join(PROJECT_CK_DIR, "build")
CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache")
EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern")
SRC_DIR = "src"
META_DIR = f"meta"
TARGETS_DIR = os.path.join(META_DIR, "targets")
DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates"
DESCRIPTION = "A build system and package manager for low-level software development"
PROJECT_LOG_FILE = os.path.join(PROJECT_CK_DIR, "cutekit.log")
GLOBAL_LOG_FILE = os.path.join(os.path.expanduser("~"), ".cutekit", "cutekit.log")

View file

@ -4,8 +4,8 @@ from pathlib import Path
import os import os
import logging import logging
from osdk.model import ProjectManifest, TargetManifest, ComponentManifest, Props, Type, Tool, Tools from cutekit.model import ProjectManifest, TargetManifest, ComponentManifest, Props, Type, Tool, Tools
from osdk import const, shell, jexpr, utils, rules, mixins from cutekit import const, shell, jexpr, utils, rules, mixins
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -131,7 +131,7 @@ def loadTarget(id: str) -> TargetManifest:
try: try:
return next(filter(lambda t: t.id == id, loadAllTargets())) return next(filter(lambda t: t.id == id, loadAllTargets()))
except StopIteration: except StopIteration:
raise Exception(f"Target '{id}' not found") raise RuntimeError(f"Target '{id}' not found")
def loadAllComponents() -> list[ComponentManifest]: def loadAllComponents() -> list[ComponentManifest]:
@ -181,7 +181,7 @@ def resolveDeps(componentSpec: str, components: list[ComponentManifest], target:
return False, unresolvedReason, [] return False, unresolvedReason, []
if resolved in stack: if resolved in stack:
raise Exception(f"Dependency loop: {stack} -> {resolved}") raise RuntimeError(f"Dependency loop: {stack} -> {resolved}")
stack.append(resolved) stack.append(resolved)

View file

@ -1,7 +1,7 @@
import os import os
from osdk.context import Context from cutekit.context import Context
from osdk import vt100 from cutekit import vt100
def view(context: Context, scope: str | None = None, showExe: bool = True, showDisabled: bool = False): def view(context: Context, scope: str | None = None, showExe: bool = True, showDisabled: bool = False):

View file

@ -1,7 +1,8 @@
from typing import Any, cast, Callable, Final from typing import Any, cast, Callable, Final
import json import json
import osdk.shell as shell import cutekit.shell as shell
from cutekit.compat import ensureSupportedManifest
Json = Any Json = Any
Builtin = Callable[..., Json] Builtin = Callable[..., Json]
@ -32,7 +33,7 @@ def eval(jexpr: Json) -> Json:
if funcName in BUILTINS: if funcName in BUILTINS:
return BUILTINS[funcName](*eval(jexpr[1:])) return BUILTINS[funcName](*eval(jexpr[1:]))
raise Exception(f"Unknown macro {funcName}") raise RuntimeError(f"Unknown macro {funcName}")
else: else:
return list(map(eval, jexpr)) return list(map(eval, jexpr))
else: else:
@ -44,8 +45,10 @@ def read(path: str) -> Json:
with open(path, "r") as f: with open(path, "r") as f:
return json.load(f) return json.load(f)
except: except:
raise Exception(f"Failed to read {path}") raise RuntimeError(f"Failed to read {path}")
def evalRead(path: str) -> Json: def evalRead(path: str) -> Json:
return eval(read(path)) data = read(path)
ensureSupportedManifest(data, path)
return eval(data)

View file

@ -1,5 +1,5 @@
from typing import Callable from typing import Callable
from osdk.model import TargetManifest, Tools from cutekit.model import TargetManifest, Tools
Mixin = Callable[[TargetManifest, Tools], Tools] Mixin = Callable[[TargetManifest, Tools], Tools]

View file

@ -3,7 +3,7 @@ from enum import Enum
from typing import Any from typing import Any
import logging import logging
from osdk.jexpr import Json from cutekit.jexpr import Json
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -27,18 +27,18 @@ class Manifest:
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 not "id" in json:
raise ValueError("Missing id") raise RuntimeError("Missing id")
self.id = json["id"] self.id = json["id"]
if not "type" in json and strict: if not "type" in json and strict:
raise ValueError("Missing type") raise RuntimeError("Missing type")
self.type = Type(json["type"]) self.type = Type(json["type"])
self.path = path self.path = path
elif strict: elif strict:
raise ValueError("Missing json") raise RuntimeError("Missing json")
for key in kwargs: for key in kwargs:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
@ -67,16 +67,16 @@ 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 not "git" in json and strict:
raise ValueError("Missing git") raise RuntimeError("Missing git")
self.git = json["git"] self.git = json["git"]
if not "tag" in json and strict: if not "tag" in json and strict:
raise ValueError("Missing tag") raise RuntimeError("Missing tag")
self.tag = json["tag"] self.tag = json["tag"]
elif strict: elif strict:
raise ValueError("Missing json") raise RuntimeError("Missing json")
for key in kwargs: for key in kwargs:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
@ -101,14 +101,14 @@ class ProjectManifest(Manifest):
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 not "description" in json and strict:
raise ValueError("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 ValueError("Missing json") raise RuntimeError("Missing json")
super().__init__(json, path, strict, **kwargs) super().__init__(json, path, strict, **kwargs)
@ -134,18 +134,18 @@ 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 not "cmd" in json and strict:
raise ValueError("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 not "args" in json and strict:
raise ValueError("Missing args") raise RuntimeError("Missing args")
self.args = json.get("args", []) self.args = json.get("args", [])
self.files = json.get("files", []) self.files = json.get("files", [])
elif strict: elif strict:
raise ValueError("Missing json") raise RuntimeError("Missing json")
for key in kwargs: for key in kwargs:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
@ -175,12 +175,12 @@ class TargetManifest(Manifest):
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 not "props" in json and strict:
raise ValueError("Missing props") raise RuntimeError("Missing props")
self.props = json["props"] self.props = json["props"]
if not "tools" in json and strict: if not "tools" in json and strict:
raise ValueError("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()}
@ -211,9 +211,9 @@ class TargetManifest(Manifest):
macrovalue = str(prop).lower().replace(" ", "_").replace("-", "_") macrovalue = str(prop).lower().replace(" ", "_").replace("-", "_")
if isinstance(prop, bool): if isinstance(prop, bool):
if prop: if prop:
defines += [f"-D__osdk_{macroname}__"] defines += [f"-D__ck_{macroname}__"]
else: else:
defines += [f"-D__osdk_{macroname}_{macrovalue}__"] defines += [f"-D__ck_{macroname}_{macrovalue}__"]
return defines return defines

View file

@ -25,7 +25,7 @@ use Python.
import textwrap import textwrap
from typing import TextIO, Union from typing import TextIO, Union
from osdk.utils import asList from cutekit.utils import asList
def escapePath(word: str) -> str: def escapePath(word: str) -> str:

View file

@ -1,8 +1,9 @@
import os import os
import logging import logging
from cutekit import shell, project
import importlib.util as importlib import importlib.util as importlib
from osdk.shell import readdir
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -20,9 +21,17 @@ def load(path: str):
def loadAll(): def loadAll():
logger.info("Loading plugins...") logger.info("Loading plugins...")
for files in readdir(os.path.join("meta", "plugins")):
projectRoot = project.root()
if projectRoot is None:
logger.info("Not in project, skipping plugin loading")
return
pluginDir = os.path.join(projectRoot, "meta/plugins")
for files in shell.readdir(pluginDir):
if files.endswith(".py"): if files.endswith(".py"):
plugin = load(os.path.join("meta", "plugins", files)) plugin = load(os.path.join(pluginDir, files))
if plugin: if plugin:
print(f"Loaded plugin {plugin.name}") print(f"Loaded plugin {plugin.name}")

17
cutekit/project.py Normal file
View file

@ -0,0 +1,17 @@
import os
def root() -> str | None:
cwd = os.getcwd()
while cwd != "/":
if os.path.isfile(os.path.join(cwd, "project.json")):
return cwd
cwd = os.path.dirname(cwd)
return None
def chdir() -> None:
projectRoot = root()
if projectRoot is None:
raise RuntimeError("No project.json found in this directory or any parent directory")
os.chdir(projectRoot)

View file

@ -10,7 +10,7 @@ import fnmatch
import platform import platform
import logging import logging
from osdk import const from cutekit import const
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -131,16 +131,16 @@ def exec(*args: str):
proc = subprocess.run(args) proc = subprocess.run(args)
except FileNotFoundError: except FileNotFoundError:
raise Exception(f"{args[0]}: Command not found") raise RuntimeError(f"{args[0]}: Command not found")
except KeyboardInterrupt: except KeyboardInterrupt:
raise Exception(f"{args[0]}: Interrupted") raise RuntimeError(f"{args[0]}: Interrupted")
if proc.returncode == -signal.SIGSEGV: if proc.returncode == -signal.SIGSEGV:
raise Exception(f"{args[0]}: Segmentation fault") raise RuntimeError(f"{args[0]}: Segmentation fault")
if proc.returncode != 0: if proc.returncode != 0:
raise Exception( raise RuntimeError(
f"{args[0]}: Process exited with code {proc.returncode}") f"{args[0]}: Process exited with code {proc.returncode}")
return True return True
@ -152,13 +152,13 @@ def popen(*args: str) -> str:
try: try:
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=sys.stderr) proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=sys.stderr)
except FileNotFoundError: except FileNotFoundError:
raise Exception(f"{args[0]}: Command not found") raise RuntimeError(f"{args[0]}: Command not found")
if proc.returncode == -signal.SIGSEGV: if proc.returncode == -signal.SIGSEGV:
raise Exception(f"{args[0]}: Segmentation fault") raise RuntimeError(f"{args[0]}: Segmentation fault")
if proc.returncode != 0: if proc.returncode != 0:
raise Exception( raise RuntimeError(
f"{args[0]}: Process exited with code {proc.returncode}") f"{args[0]}: Process exited with code {proc.returncode}")
return proc.stdout.decode('utf-8') return proc.stdout.decode('utf-8')
@ -221,7 +221,7 @@ def latest(cmd: str) -> str:
versions.append(f) versions.append(f)
if len(versions) == 0: if len(versions) == 0:
raise Exception(f"{cmd} not found") raise RuntimeError(f"{cmd} not found")
versions.sort() versions.sort()
chosen = versions[-1] chosen = versions[-1]

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1,32 +0,0 @@
import sys
import logging
from os.path import isdir
from osdk import const, shell
from osdk.args import parse
from osdk.cmds import exec, usage
from osdk.plugins import loadAll
import osdk.vt100 as vt100
def main() -> int:
logging.basicConfig(
level=logging.INFO,
format=f"{vt100.CYAN}%(asctime)s{vt100.RESET} {vt100.YELLOW}%(levelname)s{vt100.RESET} %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
a = parse(sys.argv[1:])
try:
loadAll()
exec(a)
return 0
except Exception as e:
logging.error(f"{vt100.RED}{e}{vt100.RESET}")
print()
usage()
print()
raise e

View file

@ -1,14 +0,0 @@
import os
import sys
VERSION = "0.4.1"
MODULE_DIR = os.path.dirname(os.path.realpath(__file__))
ARGV0 = os.path.basename(sys.argv[0])
OSDK_DIR = ".osdk"
BUILD_DIR = os.path.join(OSDK_DIR, "build")
CACHE_DIR = os.path.join(OSDK_DIR, "cache")
EXTERN_DIR = os.path.join(OSDK_DIR, "extern")
SRC_DIR = "src"
META_DIR = f"meta"
TARGETS_DIR = os.path.join(META_DIR, "targets")
DEFAULT_REPO_TEMPLATES = "cute-engineering/osdk-template"

View file

@ -1,22 +1,23 @@
from setuptools import setup from setuptools import setup
from osdk.const import VERSION from cutekit.const import VERSION_STR, DESCRIPTION
setup( setup(
name="osdk", name="cutekit",
version=VERSION, version=VERSION_STR,
python_requires='>=3.10', python_requires='>=3.10',
description="Operating System Development Kit", description=DESCRIPTION,
author="Cute Engineering", author="Cute Engineering",
author_email="contact@cute.engineering", author_email="contact@cute.engineering",
url="https://cute.engineering/", url="https://cute.engineering/",
packages=["osdk"], packages=["cutekit"],
install_requires=[ install_requires=[
"requests", "requests",
"graphviz" "graphviz"
], ],
entry_points={ entry_points={
"console_scripts": [ "console_scripts": [
"osdk = osdk:main", "ck = cutekit:main",
"cutekit = cutekit:main",
], ],
}, },
license="MIT", license="MIT",