diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index bdaab28..77e9483 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -17,7 +17,6 @@ permissions: jobs: deploy: - runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index d0f02e4..975b4e2 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,39 @@ -# osdk - -The operating system development kit +
+
+
+

+ +

+

CuteKit

+

+ The Cute build system and package manager +

+
+
+
## Table of contents -- [osdk](#osdk) - - [Table of contents](#table-of-contents) - - [Macros](#macros) - - [`@latest`](#latest) - - [`@uname`](#uname) - - [`@include`](#include) - - [`@join`](#join) - - [`@concat`](#concat) - - [`@exec`](#exec) - - [Manifest file format](#manifest-file-format) - - [`id`](#id) - - [`type`](#type) - - [`description`](#description) - - [`enabledIf`](#enabledif) - - [`requires`](#requires) - - [`provides`](#provides) - - [Target file format](#target-file-format) - - [`id`](#id-1) - - [`type`](#type-1) - - [`props`](#props) - - [`tools`](#tools) +- [Table of contents](#table-of-contents) +- [Macros](#macros) + - [`@latest`](#latest) + - [`@uname`](#uname) + - [`@include`](#include) + - [`@join`](#join) + - [`@concat`](#concat) + - [`@exec`](#exec) +- [Manifest file format](#manifest-file-format) + - [`id`](#id) + - [`type`](#type) + - [`description`](#description) + - [`enabledIf`](#enabledif) + - [`requires`](#requires) + - [`provides`](#provides) +- [Target file format](#target-file-format) + - [`id`](#id-1) + - [`type`](#type-1) + - [`props`](#props) + - [`tools`](#tools) ## Macros @@ -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` diff --git a/cutekit/__init__.py b/cutekit/__init__.py new file mode 100644 index 0000000..124251a --- /dev/null +++ b/cutekit/__init__.py @@ -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 \ No newline at end of file diff --git a/osdk/__main__.py b/cutekit/__main__.py similarity index 100% rename from osdk/__main__.py rename to cutekit/__main__.py diff --git a/osdk/args.py b/cutekit/args.py similarity index 100% rename from osdk/args.py rename to cutekit/args.py diff --git a/osdk/builder.py b/cutekit/builder.py similarity index 89% rename from osdk/builder.py rename to cutekit/builder.py index 2e4bc7b..f505ec0 100644 --- a/osdk/builder.py +++ b/cutekit/builder.py @@ -2,10 +2,10 @@ import os import logging from typing import TextIO -from osdk.model import Props -from osdk.ninja import Writer -from osdk.context import ComponentInstance, Context, contextFor -from osdk import shell, rules +from cutekit.model import Props +from cutekit.ninja import Writer +from cutekit.context import ComponentInstance, Context, contextFor +from cutekit import shell, rules logger = logging.getLogger(__name__) @@ -54,7 +54,7 @@ def gen(out: TextIO, context: Context): for obj in objects: r = rules.byFileIn(obj[0]) 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] 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) if reqInstance is None: - raise Exception(f"Component {req} not found") + raise RuntimeError(f"Component {req} not found") 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()) @@ -105,10 +105,10 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentIn instance = context.componentByName(componentSpec) if instance is None: - raise Exception(f"Component {componentSpec} not found") + raise RuntimeError(f"Component {componentSpec} not found") if not instance.enabled: - raise Exception( + raise RuntimeError( f"Component {componentSpec} is disabled: {instance.disableReason}") shell.exec(f"ninja", "-v", "-f", ninjaPath, instance.outfile()) diff --git a/osdk/cmds.py b/cutekit/cmds.py similarity index 70% rename from osdk/cmds.py rename to cutekit/cmds.py index d6b2c25..6662374 100644 --- a/osdk/cmds.py +++ b/cutekit/cmds.py @@ -3,17 +3,19 @@ import json import logging import tempfile import requests +import sys from typing import Callable, cast -from osdk import context, shell, const, vt100, builder, graph -from osdk.args import Args -from osdk.context import contextFor +from cutekit import context, shell, const, vt100, builder, graph, project +from cutekit.args import Args +from cutekit.context import contextFor Callback = Callable[[Args], None] logger = logging.getLogger(__name__) + class Cmd: shortName: str | None longName: str @@ -38,19 +40,21 @@ def append(cmd: Cmd): def runCmd(args: Args): + project.chdir() + targetSpec = cast(str, args.consumeOpt( "target", "host-" + shell.uname().machine)) componentSpec = args.consumeArg() if componentSpec is None: - raise Exception("Component not specified") + raise RuntimeError("Component not specified") component = builder.build(componentSpec, targetSpec) - os.environ["OSDK_TARGET"] = component.context.target.id - os.environ["OSDK_COMPONENT"] = component.id() - os.environ["OSDK_BUILDDIR"] = component.context.builddir() + os.environ["CK_TARGET"] = component.context.target.id + os.environ["CK_COMPONENT"] = component.id() + os.environ["CK_BUILDDIR"] = component.context.builddir() shell.exec(component.outfile(), *args.args) @@ -59,6 +63,8 @@ cmds += [Cmd("r", "run", "Run the target", runCmd)] def testCmd(args: Args): + project.chdir() + targetSpec = cast(str, args.consumeOpt( "target", "host-" + shell.uname().machine)) builder.testAll(targetSpec) @@ -68,19 +74,21 @@ cmds += [Cmd("t", "test", "Run all test targets", testCmd)] def debugCmd(args: Args): + project.chdir() + targetSpec = cast(str, args.consumeOpt( "target", "host-" + shell.uname().machine)) componentSpec = args.consumeArg() if componentSpec is None: - raise Exception("Component not specified") + raise RuntimeError("Component not specified") component = builder.build(componentSpec, targetSpec) - os.environ["OSDK_TARGET"] = component.context.target.id - os.environ["OSDK_COMPONENT"] = component.id() - os.environ["OSDK_BUILDDIR"] = component.context.builddir() + os.environ["CK_TARGET"] = component.context.target.id + os.environ["CK_COMPONENT"] = component.id() + os.environ["CK_BUILDDIR"] = component.context.builddir() 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): + project.chdir() + targetSpec = cast(str, args.consumeOpt( "target", "host-" + shell.uname().machine)) @@ -104,6 +114,8 @@ cmds += [Cmd("b", "build", "Build the target", buildCmd)] def listCmd(args: Args): + project.chdir() + components = context.loadAllComponents() targets = context.loadAllTargets() @@ -129,6 +141,7 @@ cmds += [Cmd("l", "list", "List the targets", listCmd)] def cleanCmd(args: Args): + project.chdir() shell.rmrf(const.BUILD_DIR) @@ -136,7 +149,8 @@ cmds += [Cmd("c", "clean", "Clean the build directory", cleanCmd)] 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)] @@ -148,7 +162,7 @@ def helpCmd(args: Args): print() vt100.title("Description") - print(" Operating System Development Kit.") + print(f" {const.DESCRIPTION}") print() vt100.title("Commands") @@ -161,19 +175,25 @@ def helpCmd(args: Args): 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: Args): - print(f"OSDK v{const.VERSION}\n") + print(f"CuteKit v{const.VERSION_STR}\n") cmds += [Cmd("v", "version", "Show current version", versionCmd)] def graphCmd(args: Args): + project.chdir() + targetSpec = cast(str, args.consumeOpt( "target", "host-" + shell.uname().machine)) @@ -191,10 +211,12 @@ cmds += [Cmd("g", "graph", "Show dependency graph", graphCmd)] def installCmd(args: Args): - project = context.loadProject(".") + project.chdir() - for extSpec in project.extern: - ext = project.extern[extSpec] + pj = context.loadProject(".") + + for extSpec in pj.extern: + ext = pj.extern[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): template = args.consumeArg() + + if template is None: + template = "default" + repo = const.DEFAULT_REPO_TEMPLATES if not "repo" in args.opts else args.opts["repo"] list = "list" in args.opts if list: 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: logger.error('Failed to fetch registry') 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: 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", "-C", os.path.join(tmp, "osdk-repo"), "sparse-checkout", "set", "--no-cone", template, "-q"]) - shell.exec(*["git", "-C", os.path.join(tmp, "osdk-repo"), "checkout", "-q"]) - shell.mv(os.path.join(tmp, "osdk-repo", template), os.path.join(".", template)) + shell.exec(*["git", "clone", "-n", "--depth=1", + "--filter=tree:0", f"https://github.com/{repo}", tmp, "-q"]) + shell.exec(*["git", "-C", tmp, "sparse-checkout", + "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(): print(f"Usage: {const.ARGV0} [args...]") +def error(msg: str) -> None: + print(f"{vt100.RED}Error:{vt100.RESET} {msg}\n", file=sys.stderr) + + def exec(args: Args): cmd = args.consumeArg() if cmd is None: - raise Exception("No command specified") + raise RuntimeError("No command specified") for c in cmds: if c.shortName == cmd or c.longName == cmd: c.callback(args) return - raise Exception(f"Unknown command {cmd}") + raise RuntimeError(f"Unknown command {cmd}") diff --git a/cutekit/compat.py b/cutekit/compat.py new file mode 100644 index 0000000..8da16ce --- /dev/null +++ b/cutekit/compat.py @@ -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}") diff --git a/cutekit/const.py b/cutekit/const.py new file mode 100644 index 0000000..879cd42 --- /dev/null +++ b/cutekit/const.py @@ -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") diff --git a/osdk/context.py b/cutekit/context.py similarity index 96% rename from osdk/context.py rename to cutekit/context.py index 9fe67dc..c9fd5de 100644 --- a/osdk/context.py +++ b/cutekit/context.py @@ -4,8 +4,8 @@ from pathlib import Path import os import logging -from osdk.model import ProjectManifest, TargetManifest, ComponentManifest, Props, Type, Tool, Tools -from osdk import const, shell, jexpr, utils, rules, mixins +from cutekit.model import ProjectManifest, TargetManifest, ComponentManifest, Props, Type, Tool, Tools +from cutekit import const, shell, jexpr, utils, rules, mixins logger = logging.getLogger(__name__) @@ -131,7 +131,7 @@ def loadTarget(id: str) -> TargetManifest: try: return next(filter(lambda t: t.id == id, loadAllTargets())) except StopIteration: - raise Exception(f"Target '{id}' not found") + raise RuntimeError(f"Target '{id}' not found") def loadAllComponents() -> list[ComponentManifest]: @@ -181,7 +181,7 @@ def resolveDeps(componentSpec: str, components: list[ComponentManifest], target: return False, unresolvedReason, [] if resolved in stack: - raise Exception(f"Dependency loop: {stack} -> {resolved}") + raise RuntimeError(f"Dependency loop: {stack} -> {resolved}") stack.append(resolved) diff --git a/osdk/graph.py b/cutekit/graph.py similarity index 97% rename from osdk/graph.py rename to cutekit/graph.py index 4759a9a..ab4d65e 100644 --- a/osdk/graph.py +++ b/cutekit/graph.py @@ -1,7 +1,7 @@ import os -from osdk.context import Context -from osdk import vt100 +from cutekit.context import Context +from cutekit import vt100 def view(context: Context, scope: str | None = None, showExe: bool = True, showDisabled: bool = False): diff --git a/osdk/jexpr.py b/cutekit/jexpr.py similarity index 82% rename from osdk/jexpr.py rename to cutekit/jexpr.py index 7e0e6c0..ab3d7d8 100644 --- a/osdk/jexpr.py +++ b/cutekit/jexpr.py @@ -1,7 +1,8 @@ from typing import Any, cast, Callable, Final import json -import osdk.shell as shell +import cutekit.shell as shell +from cutekit.compat import ensureSupportedManifest Json = Any Builtin = Callable[..., Json] @@ -32,7 +33,7 @@ def eval(jexpr: Json) -> Json: if funcName in BUILTINS: return BUILTINS[funcName](*eval(jexpr[1:])) - raise Exception(f"Unknown macro {funcName}") + raise RuntimeError(f"Unknown macro {funcName}") else: return list(map(eval, jexpr)) else: @@ -44,8 +45,10 @@ def read(path: str) -> Json: with open(path, "r") as f: return json.load(f) except: - raise Exception(f"Failed to read {path}") + raise RuntimeError(f"Failed to read {path}") def evalRead(path: str) -> Json: - return eval(read(path)) + data = read(path) + ensureSupportedManifest(data, path) + return eval(data) diff --git a/osdk/mixins.py b/cutekit/mixins.py similarity index 97% rename from osdk/mixins.py rename to cutekit/mixins.py index b996ac9..ed7ed54 100644 --- a/osdk/mixins.py +++ b/cutekit/mixins.py @@ -1,5 +1,5 @@ from typing import Callable -from osdk.model import TargetManifest, Tools +from cutekit.model import TargetManifest, Tools Mixin = Callable[[TargetManifest, Tools], Tools] diff --git a/osdk/model.py b/cutekit/model.py similarity index 89% rename from osdk/model.py rename to cutekit/model.py index 0ab153e..b8ec441 100644 --- a/osdk/model.py +++ b/cutekit/model.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Any import logging -from osdk.jexpr import Json +from cutekit.jexpr import Json logger = logging.getLogger(__name__) @@ -27,18 +27,18 @@ class Manifest: def __init__(self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any): if json is not None: if not "id" in json: - raise ValueError("Missing id") + raise RuntimeError("Missing id") self.id = json["id"] if not "type" in json and strict: - raise ValueError("Missing type") + raise RuntimeError("Missing type") self.type = Type(json["type"]) self.path = path elif strict: - raise ValueError("Missing json") + raise RuntimeError("Missing json") for key in kwargs: setattr(self, key, kwargs[key]) @@ -67,16 +67,16 @@ class Extern: def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any): if json is not None: if not "git" in json and strict: - raise ValueError("Missing git") + raise RuntimeError("Missing git") self.git = json["git"] if not "tag" in json and strict: - raise ValueError("Missing tag") + raise RuntimeError("Missing tag") self.tag = json["tag"] elif strict: - raise ValueError("Missing json") + raise RuntimeError("Missing json") for key in kwargs: 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): if json is not None: if not "description" in json and strict: - raise ValueError("Missing description") + raise RuntimeError("Missing description") self.description = json["description"] self.extern = {k: Extern(v) for k, v in json.get("extern", {}).items()} elif strict: - raise ValueError("Missing json") + raise RuntimeError("Missing json") super().__init__(json, path, strict, **kwargs) @@ -134,18 +134,18 @@ class Tool: def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any): if json is not None: if not "cmd" in json and strict: - raise ValueError("Missing cmd") + raise RuntimeError("Missing cmd") self.cmd = json.get("cmd", self.cmd) if not "args" in json and strict: - raise ValueError("Missing args") + raise RuntimeError("Missing args") self.args = json.get("args", []) self.files = json.get("files", []) elif strict: - raise ValueError("Missing json") + raise RuntimeError("Missing json") for key in kwargs: 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): if json is not None: if not "props" in json and strict: - raise ValueError("Missing props") + raise RuntimeError("Missing props") self.props = json["props"] 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()} @@ -211,9 +211,9 @@ class TargetManifest(Manifest): macrovalue = str(prop).lower().replace(" ", "_").replace("-", "_") if isinstance(prop, bool): if prop: - defines += [f"-D__osdk_{macroname}__"] + defines += [f"-D__ck_{macroname}__"] else: - defines += [f"-D__osdk_{macroname}_{macrovalue}__"] + defines += [f"-D__ck_{macroname}_{macrovalue}__"] return defines diff --git a/osdk/ninja.py b/cutekit/ninja.py similarity index 99% rename from osdk/ninja.py rename to cutekit/ninja.py index 6148f82..a24698c 100644 --- a/osdk/ninja.py +++ b/cutekit/ninja.py @@ -25,7 +25,7 @@ use Python. import textwrap from typing import TextIO, Union -from osdk.utils import asList +from cutekit.utils import asList def escapePath(word: str) -> str: diff --git a/osdk/plugins.py b/cutekit/plugins.py similarity index 64% rename from osdk/plugins.py rename to cutekit/plugins.py index 0e8e9b7..dff9fb1 100644 --- a/osdk/plugins.py +++ b/cutekit/plugins.py @@ -1,8 +1,9 @@ import os import logging +from cutekit import shell, project + import importlib.util as importlib -from osdk.shell import readdir logger = logging.getLogger(__name__) @@ -20,9 +21,17 @@ def load(path: str): def loadAll(): 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"): - plugin = load(os.path.join("meta", "plugins", files)) + plugin = load(os.path.join(pluginDir, files)) if plugin: print(f"Loaded plugin {plugin.name}") diff --git a/cutekit/project.py b/cutekit/project.py new file mode 100644 index 0000000..979fb7b --- /dev/null +++ b/cutekit/project.py @@ -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) \ No newline at end of file diff --git a/osdk/rules.py b/cutekit/rules.py similarity index 100% rename from osdk/rules.py rename to cutekit/rules.py diff --git a/osdk/shell.py b/cutekit/shell.py similarity index 92% rename from osdk/shell.py rename to cutekit/shell.py index 5efb615..012e49b 100644 --- a/osdk/shell.py +++ b/cutekit/shell.py @@ -10,7 +10,7 @@ import fnmatch import platform import logging -from osdk import const +from cutekit import const logger = logging.getLogger(__name__) @@ -131,16 +131,16 @@ def exec(*args: str): proc = subprocess.run(args) except FileNotFoundError: - raise Exception(f"{args[0]}: Command not found") + raise RuntimeError(f"{args[0]}: Command not found") except KeyboardInterrupt: - raise Exception(f"{args[0]}: Interrupted") + raise RuntimeError(f"{args[0]}: Interrupted") if proc.returncode == -signal.SIGSEGV: - raise Exception(f"{args[0]}: Segmentation fault") + raise RuntimeError(f"{args[0]}: Segmentation fault") if proc.returncode != 0: - raise Exception( + raise RuntimeError( f"{args[0]}: Process exited with code {proc.returncode}") return True @@ -152,13 +152,13 @@ def popen(*args: str) -> str: try: proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=sys.stderr) except FileNotFoundError: - raise Exception(f"{args[0]}: Command not found") + raise RuntimeError(f"{args[0]}: Command not found") if proc.returncode == -signal.SIGSEGV: - raise Exception(f"{args[0]}: Segmentation fault") + raise RuntimeError(f"{args[0]}: Segmentation fault") if proc.returncode != 0: - raise Exception( + raise RuntimeError( f"{args[0]}: Process exited with code {proc.returncode}") return proc.stdout.decode('utf-8') @@ -221,7 +221,7 @@ def latest(cmd: str) -> str: versions.append(f) if len(versions) == 0: - raise Exception(f"{cmd} not found") + raise RuntimeError(f"{cmd} not found") versions.sort() chosen = versions[-1] diff --git a/osdk/utils.py b/cutekit/utils.py similarity index 100% rename from osdk/utils.py rename to cutekit/utils.py diff --git a/osdk/vt100.py b/cutekit/vt100.py similarity index 100% rename from osdk/vt100.py rename to cutekit/vt100.py diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..b103240 Binary files /dev/null and b/logo.png differ diff --git a/osdk/__init__.py b/osdk/__init__.py deleted file mode 100644 index 71f8363..0000000 --- a/osdk/__init__.py +++ /dev/null @@ -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 diff --git a/osdk/const.py b/osdk/const.py deleted file mode 100644 index 1211fec..0000000 --- a/osdk/const.py +++ /dev/null @@ -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" \ No newline at end of file diff --git a/setup.py b/setup.py index 474aa62..50e48af 100644 --- a/setup.py +++ b/setup.py @@ -1,22 +1,23 @@ from setuptools import setup -from osdk.const import VERSION +from cutekit.const import VERSION_STR, DESCRIPTION setup( - name="osdk", - version=VERSION, + name="cutekit", + version=VERSION_STR, python_requires='>=3.10', - description="Operating System Development Kit", + description=DESCRIPTION, author="Cute Engineering", author_email="contact@cute.engineering", url="https://cute.engineering/", - packages=["osdk"], + packages=["cutekit"], install_requires=[ "requests", "graphviz" ], entry_points={ "console_scripts": [ - "osdk = osdk:main", + "ck = cutekit:main", + "cutekit = cutekit:main", ], }, license="MIT",