meta: Renamed osdk -> cutekit + improved logging + better error handling.
This commit is contained in:
parent
00537aa34e
commit
f345f94471
1
.github/workflows/python-publish.yml
vendored
1
.github/workflows/python-publish.yml
vendored
|
@ -17,7 +17,6 @@ permissions:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
59
README.md
59
README.md
|
@ -1,30 +1,39 @@
|
||||||
# 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)
|
- [`tools`](#tools)
|
||||||
- [`tools`](#tools)
|
|
||||||
|
|
||||||
|
|
||||||
## Macros
|
## 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`
|
### `tools`
|
||||||
|
|
||||||
|
|
50
cutekit/__init__.py
Normal file
50
cutekit/__init__.py
Normal 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
|
|
@ -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())
|
|
@ -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
33
cutekit/compat.py
Normal 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
19
cutekit/const.py
Normal 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")
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
|
@ -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)
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
|
@ -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
17
cutekit/project.py
Normal 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)
|
|
@ -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]
|
|
@ -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
|
|
|
@ -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"
|
|
13
setup.py
13
setup.py
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue