Update
This commit is contained in:
parent
cc88242cef
commit
ea0e5613e8
12 changed files with 221 additions and 79 deletions
|
@ -11,6 +11,7 @@ from . import (
|
||||||
plugins,
|
plugins,
|
||||||
pods, # noqa: F401 this is imported for side effects
|
pods, # noqa: F401 this is imported for side effects
|
||||||
vt100,
|
vt100,
|
||||||
|
shell,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,10 +41,7 @@ def setupLogger(verbose: bool):
|
||||||
if projectRoot is not None:
|
if projectRoot is not None:
|
||||||
logFile = os.path.join(projectRoot.dirname(), const.PROJECT_LOG_FILE)
|
logFile = os.path.join(projectRoot.dirname(), const.PROJECT_LOG_FILE)
|
||||||
|
|
||||||
# create the directory if it doesn't exist
|
shell.mkdir(os.path.dirname(logFile))
|
||||||
logDir = os.path.dirname(logFile)
|
|
||||||
if not os.path.isdir(logDir):
|
|
||||||
os.makedirs(logDir)
|
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
|
@ -56,21 +54,21 @@ def setupLogger(verbose: bool):
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
try:
|
try:
|
||||||
|
shell.mkdir(const.GLOBAL_CK_DIR)
|
||||||
args = cli.parse(sys.argv[1:])
|
args = cli.parse(sys.argv[1:])
|
||||||
setupLogger(args.consumeOpt("verbose", False) is True)
|
setupLogger(args.consumeOpt("verbose", False) is True)
|
||||||
safemode = args.consumeOpt("safemode", False) is True
|
|
||||||
if not safemode:
|
const.setup()
|
||||||
plugins.loadAll()
|
plugins.setup(args)
|
||||||
pods.reincarnate(args)
|
pods.setup(args)
|
||||||
cli.exec(args)
|
cli.exec(args)
|
||||||
print()
|
|
||||||
return 0
|
return 0
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
cli.error(str(e))
|
cli.error(str(e))
|
||||||
cli.usage()
|
cli.usage()
|
||||||
print()
|
|
||||||
return 1
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print()
|
print()
|
||||||
return 1
|
|
||||||
|
return 1
|
||||||
|
|
62
cutekit/bootstrap.sh
Normal file
62
cutekit/bootstrap.sh
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#!/bin/env bash
|
||||||
|
# version: {version}
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$CUTEKIT_NOVENV" == "1" ]; then
|
||||||
|
echo "CUTEKIT_NOVENV is set, skipping virtual environment setup."
|
||||||
|
exec cutekit $@
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" == "tools" -a "$2" == "nuke" ]; then
|
||||||
|
rm -rf .cutekit/tools .cutekit/venv
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f .cutekit/tools/ready ]; then
|
||||||
|
if [ ! \( "$1" == "tools" -a "$2" == "setup" \) ]; then
|
||||||
|
echo "CuteKit is not installed."
|
||||||
|
echo "This script will install cutekit into $PWD/.cutekit"
|
||||||
|
|
||||||
|
read -p "Do you want to continue? [Y/n] " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Installing CuteKit..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p .cutekit
|
||||||
|
if [ ! -d .cutekit/venv ]; then
|
||||||
|
echo "Setting up Python virtual environment..."
|
||||||
|
python3 -m venv .cutekit/venv
|
||||||
|
fi
|
||||||
|
source .cutekit/venv/bin/activate
|
||||||
|
|
||||||
|
echo "Downloading CuteKit..."
|
||||||
|
if [ ! -d .cutekit/tools/cutekit ]; then
|
||||||
|
git clone --depth 1 https://github.com/cute-engineering/cutekit .cutekit/tools/cutekit --branch "0.7-dev"
|
||||||
|
else
|
||||||
|
echo "CuteKit already downloaded."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing Tools..."
|
||||||
|
pip3 install -e .cutekit/tools/cutekit
|
||||||
|
pip3 install -r meta/plugins/requirements.txt
|
||||||
|
|
||||||
|
touch .cutekit/tools/ready
|
||||||
|
echo "Done!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" == "tools" -a "$2" == "setup" ]; then
|
||||||
|
echo "Tools already installed."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
source .cutekit/venv/bin/activate
|
||||||
|
export PATH="$PATH:.cutekit/venv/bin"
|
||||||
|
|
||||||
|
cutekit $@
|
||||||
|
|
|
@ -11,7 +11,23 @@ _logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dt.dataclass
|
@dt.dataclass
|
||||||
class TargetScope:
|
class Scope:
|
||||||
|
registry: model.Registry
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def use(args: cli.Args) -> "Scope":
|
||||||
|
registry = model.Registry.use(args)
|
||||||
|
return Scope(registry)
|
||||||
|
|
||||||
|
def key(self) -> str:
|
||||||
|
return self.registry.project.id
|
||||||
|
|
||||||
|
def openTargetScope(self, t: model.Target):
|
||||||
|
return TargetScope(self.registry, t)
|
||||||
|
|
||||||
|
|
||||||
|
@dt.dataclass
|
||||||
|
class TargetScope(Scope):
|
||||||
registry: model.Registry
|
registry: model.Registry
|
||||||
target: model.Target
|
target: model.Target
|
||||||
|
|
||||||
|
@ -21,6 +37,9 @@ class TargetScope:
|
||||||
target = model.Target.use(args)
|
target = model.Target.use(args)
|
||||||
return TargetScope(registry, target)
|
return TargetScope(registry, target)
|
||||||
|
|
||||||
|
def key(self) -> str:
|
||||||
|
return super().key() + "/" + self.target.id + "/" + self.target.hashid
|
||||||
|
|
||||||
def openComponentScope(self, c: model.Component):
|
def openComponentScope(self, c: model.Component):
|
||||||
return ComponentScope(self.registry, self.target, c)
|
return ComponentScope(self.registry, self.target, c)
|
||||||
|
|
||||||
|
@ -29,6 +48,9 @@ class TargetScope:
|
||||||
class ComponentScope(TargetScope):
|
class ComponentScope(TargetScope):
|
||||||
component: model.Component
|
component: model.Component
|
||||||
|
|
||||||
|
def key(self) -> str:
|
||||||
|
return super().key() + "/" + self.component.id
|
||||||
|
|
||||||
def openComponentScope(self, c: model.Component):
|
def openComponentScope(self, c: model.Component):
|
||||||
return ComponentScope(self.registry, self.target, c)
|
return ComponentScope(self.registry, self.target, c)
|
||||||
|
|
||||||
|
@ -76,6 +98,8 @@ def _computeCinc(scope: TargetScope) -> str:
|
||||||
for c in scope.registry.iterEnabled(scope.target):
|
for c in scope.registry.iterEnabled(scope.target):
|
||||||
if "cpp-root-include" in c.props:
|
if "cpp-root-include" in c.props:
|
||||||
res.add(c.dirname())
|
res.add(c.dirname())
|
||||||
|
elif "cpp-excluded" in c.props:
|
||||||
|
pass
|
||||||
elif c.type == model.Kind.LIB:
|
elif c.type == model.Kind.LIB:
|
||||||
res.add(str(Path(c.dirname()).parent))
|
res.add(str(Path(c.dirname()).parent))
|
||||||
|
|
||||||
|
@ -108,18 +132,16 @@ def buildpath(scope: ComponentScope, path) -> Path:
|
||||||
|
|
||||||
|
|
||||||
def subdirs(scope: ComponentScope) -> list[str]:
|
def subdirs(scope: ComponentScope) -> list[str]:
|
||||||
registry = scope.registry
|
|
||||||
target = scope.target
|
|
||||||
component = scope.component
|
component = scope.component
|
||||||
result = [component.dirname()]
|
result = [component.dirname()]
|
||||||
|
|
||||||
for subs in component.subdirs:
|
for subs in component.subdirs:
|
||||||
result.append(os.path.join(component.dirname(), subs))
|
result.append(os.path.join(component.dirname(), subs))
|
||||||
|
|
||||||
for inj in component.resolved[target.id].injected:
|
# for inj in component.resolved[target.id].injected:
|
||||||
injected = registry.lookup(inj, model.Component)
|
# injected = registry.lookup(inj, model.Component)
|
||||||
assert injected is not None # model.Resolver has already checked this
|
# assert injected is not None # model.Resolver has already checked this
|
||||||
result.extend(subdirs(scope))
|
# result.extend(subdirs(scope.openComponentScope(injected)))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -134,13 +156,31 @@ def compile(
|
||||||
res: list[str] = []
|
res: list[str] = []
|
||||||
for src in srcs:
|
for src in srcs:
|
||||||
rel = Path(src).relative_to(scope.component.dirname())
|
rel = Path(src).relative_to(scope.component.dirname())
|
||||||
dest = buildpath(scope, "obj") / rel.with_suffix(".o")
|
dest = buildpath(scope, path="obj") / rel.with_suffix(".o")
|
||||||
t = scope.target.tools[rule]
|
t = scope.target.tools[rule]
|
||||||
w.build(str(dest), rule, inputs=src, order_only=t.files)
|
w.build(str(dest), rule, inputs=src, order_only=t.files)
|
||||||
res.append(str(dest))
|
res.append(str(dest))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def compileObjs(w: ninja.Writer, scope: ComponentScope) -> list[str]:
|
||||||
|
objs = []
|
||||||
|
objs += compile(w, scope, "cc", wilcard(scope, ["*.c"]))
|
||||||
|
objs += compile(
|
||||||
|
w,
|
||||||
|
scope,
|
||||||
|
"cxx",
|
||||||
|
wilcard(scope, ["*.cpp", "*.cc", "*.cxx"]),
|
||||||
|
)
|
||||||
|
objs += compile(
|
||||||
|
w,
|
||||||
|
scope,
|
||||||
|
"as",
|
||||||
|
wilcard(scope, ["*.s", "*.asm", "*.S"]),
|
||||||
|
)
|
||||||
|
return objs
|
||||||
|
|
||||||
|
|
||||||
# --- Ressources ------------------------------------------------------------- #
|
# --- Ressources ------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,26 +235,12 @@ def link(
|
||||||
w.newline()
|
w.newline()
|
||||||
out = outfile(scope)
|
out = outfile(scope)
|
||||||
|
|
||||||
objs = []
|
|
||||||
objs += compile(w, scope, "cc", wilcard(scope, ["*.c"]))
|
|
||||||
objs += compile(
|
|
||||||
w,
|
|
||||||
scope,
|
|
||||||
"cxx",
|
|
||||||
wilcard(scope, ["*.cpp", "*.cc", "*.cxx"]),
|
|
||||||
)
|
|
||||||
objs += compile(
|
|
||||||
w,
|
|
||||||
scope,
|
|
||||||
"as",
|
|
||||||
wilcard(scope, ["*.s", "*.asm", "*.S"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
res = compileRes(w, scope)
|
res = compileRes(w, scope)
|
||||||
libs = collectLibs(scope)
|
objs = compileObjs(w, scope)
|
||||||
if scope.component.type == model.Kind.LIB:
|
if scope.component.type == model.Kind.LIB:
|
||||||
w.build(out, "ar", objs, implicit=res)
|
w.build(out, "ar", objs, implicit=res)
|
||||||
else:
|
else:
|
||||||
|
libs = collectLibs(scope)
|
||||||
w.build(out, "ld", objs + libs, implicit=res)
|
w.build(out, "ld", objs + libs, implicit=res)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -300,12 +326,12 @@ def build(
|
||||||
|
|
||||||
|
|
||||||
@cli.command("b", "builder", "Build/Run/Clean a component or all components")
|
@cli.command("b", "builder", "Build/Run/Clean a component or all components")
|
||||||
def buildCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@cli.command("b", "builder/build", "Build a component or all components")
|
@cli.command("b", "builder/build", "Build a component or all components")
|
||||||
def buildBuildCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
scope = TargetScope.use(args)
|
scope = TargetScope.use(args)
|
||||||
componentSpec = args.consumeArg()
|
componentSpec = args.consumeArg()
|
||||||
component = None
|
component = None
|
||||||
|
@ -315,7 +341,7 @@ def buildBuildCmd(args: cli.Args):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("r", "builder/run", "Run a component")
|
@cli.command("r", "builder/run", "Run a component")
|
||||||
def buildRunCmd(args: cli.Args):
|
def runCmd(args: cli.Args):
|
||||||
scope = TargetScope.use(args)
|
scope = TargetScope.use(args)
|
||||||
debug = args.consumeOpt("debug", False) is True
|
debug = args.consumeOpt("debug", False) is True
|
||||||
|
|
||||||
|
@ -337,28 +363,28 @@ def buildRunCmd(args: cli.Args):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("t", "builder/test", "Run all test targets")
|
@cli.command("t", "builder/test", "Run all test targets")
|
||||||
def buildTestCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
# This is just a wrapper around the `run` command that try
|
# This is just a wrapper around the `run` command that try
|
||||||
# to run a special hook component named __tests__.
|
# to run a special hook component named __tests__.
|
||||||
args.args.insert(0, "__tests__")
|
args.args.insert(0, "__tests__")
|
||||||
buildRunCmd(args)
|
runCmd(args)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("d", "builder/debug", "Debug a component")
|
@cli.command("d", "builder/debug", "Debug a component")
|
||||||
def buildDebugCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
# This is just a wrapper around the `run` command that
|
# This is just a wrapper around the `run` command that
|
||||||
# always enable debug mode.
|
# always enable debug mode.
|
||||||
args.opts["debug"] = True
|
args.opts["debug"] = True
|
||||||
buildRunCmd(args)
|
runCmd(args)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("c", "builder/clean", "Clean build files")
|
@cli.command("c", "builder/clean", "Clean build files")
|
||||||
def buildCleanCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
model.Project.use(args)
|
model.Project.use(args)
|
||||||
shell.rmrf(const.BUILD_DIR)
|
shell.rmrf(const.BUILD_DIR)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("n", "builder/nuke", "Clean all build files and caches")
|
@cli.command("n", "builder/nuke", "Clean all build files and caches")
|
||||||
def buildNukeCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
model.Project.use(args)
|
model.Project.use(args)
|
||||||
shell.rmrf(const.PROJECT_CK_DIR)
|
shell.rmrf(const.PROJECT_CK_DIR)
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Uninitialized:
|
||||||
|
def __repr__(self):
|
||||||
|
raise Exception("Uninitialized constant")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
raise Exception("Uninitialized constant")
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
raise Exception("Uninitialized constant")
|
||||||
|
|
||||||
|
|
||||||
VERSION = (0, 7, 0, "dev")
|
VERSION = (0, 7, 0, "dev")
|
||||||
VERSION_STR = f"{VERSION[0]}.{VERSION[1]}.{VERSION[2]}{'-' + VERSION[3] if len(VERSION) >= 4 else ''}"
|
VERSION_STR = f"{VERSION[0]}.{VERSION[1]}.{VERSION[2]}{'-' + VERSION[3] if len(VERSION) >= 4 else ''}"
|
||||||
MODULE_DIR = os.path.dirname(os.path.realpath(__file__))
|
MODULE_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
@ -16,4 +30,17 @@ TARGETS_DIR = os.path.join(META_DIR, "targets")
|
||||||
DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates"
|
DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates"
|
||||||
DESCRIPTION = "A build system and package manager for low-level software development"
|
DESCRIPTION = "A build system and package manager for low-level software development"
|
||||||
PROJECT_LOG_FILE = os.path.join(PROJECT_CK_DIR, "cutekit.log")
|
PROJECT_LOG_FILE = os.path.join(PROJECT_CK_DIR, "cutekit.log")
|
||||||
GLOBAL_LOG_FILE = os.path.join(os.path.expanduser("~"), ".cutekit", "cutekit.log")
|
GLOBAL_LOG_FILE: str = os.path.join(os.path.expanduser("~"), ".cutekit", "cutekit.log")
|
||||||
|
HOSTID: str | Uninitialized = Uninitialized()
|
||||||
|
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
global HOSTID
|
||||||
|
hostIdPath = GLOBAL_CK_DIR + "/hostid"
|
||||||
|
if os.path.exists(hostIdPath):
|
||||||
|
with open(hostIdPath, "r") as f:
|
||||||
|
HOSTID = f.read().strip()
|
||||||
|
else:
|
||||||
|
HOSTID = utils.randomHash()
|
||||||
|
with open(hostIdPath, "w") as f:
|
||||||
|
f.write(HOSTID)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#!/bin/env bash
|
#!/bin/env bash
|
||||||
|
|
||||||
|
# This script makes sure that the virtual environment is
|
||||||
|
# set up and that the plugins requirements are installed.
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
export PY=python3.11
|
export PY=python3.11
|
||||||
|
@ -30,5 +33,6 @@ else
|
||||||
source /tools/venv/bin/activate
|
source /tools/venv/bin/activate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cd /project
|
||||||
export PYTHONPATH=/tools
|
export PYTHONPATH=/tools
|
||||||
$PY -m cutekit "$@"
|
$PY -m cutekit $@
|
||||||
|
|
|
@ -46,7 +46,7 @@ def view(
|
||||||
|
|
||||||
g.node(
|
g.node(
|
||||||
component.id,
|
component.id,
|
||||||
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.decription, 40,newline='<BR/>')}>",
|
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.description, 40,newline='<BR/>')}>",
|
||||||
shape=shape,
|
shape=shape,
|
||||||
style="filled",
|
style="filled",
|
||||||
fillcolor=fillcolor,
|
fillcolor=fillcolor,
|
||||||
|
@ -67,7 +67,7 @@ def view(
|
||||||
elif showDisabled:
|
elif showDisabled:
|
||||||
g.node(
|
g.node(
|
||||||
component.id,
|
component.id,
|
||||||
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.decription, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(str(component.resolved[target.id].reason), 40,newline='<BR/>')}</I>>",
|
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.description, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(str(component.resolved[target.id].reason), 40,newline='<BR/>')}</I>>",
|
||||||
shape="plaintext",
|
shape="plaintext",
|
||||||
style="filled",
|
style="filled",
|
||||||
fontcolor="#999999",
|
fontcolor="#999999",
|
||||||
|
@ -84,7 +84,7 @@ def view(
|
||||||
|
|
||||||
|
|
||||||
@cli.command("g", "graph", "Show the dependency graph")
|
@cli.command("g", "graph", "Show the dependency graph")
|
||||||
def graphCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
registry = model.Registry.use(args)
|
registry = model.Registry.use(args)
|
||||||
target = model.Target.use(args)
|
target = model.Target.use(args)
|
||||||
|
|
||||||
|
|
|
@ -174,18 +174,18 @@ class Project(Manifest):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("m", "model", "Manage the model")
|
@cli.command("m", "model", "Manage the model")
|
||||||
def modelCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@cli.command("i", "model/install", "Install required external packages")
|
@cli.command("i", "model/install", "Install required external packages")
|
||||||
def modelInstallCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
project = Project.use(args)
|
project = Project.use(args)
|
||||||
Project.fetchs(project.extern)
|
Project.fetchs(project.extern)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("I", "model/init", "Initialize a new project")
|
@cli.command("I", "model/init", "Initialize a new project")
|
||||||
def modelInitCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES)
|
repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES)
|
||||||
|
@ -275,7 +275,10 @@ class Target(Manifest):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def builddir(self) -> str:
|
def builddir(self) -> str:
|
||||||
return os.path.join(const.BUILD_DIR, f"{self.id}-{self.hashid[:8]}")
|
postfix = f"-{self.hashid[:8]}"
|
||||||
|
if self.props.get("host"):
|
||||||
|
postfix += f"-{str(const.HOSTID)[:8]}"
|
||||||
|
return os.path.join(const.BUILD_DIR, f"{self.id}{postfix}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def use(args: cli.Args) -> "Target":
|
def use(args: cli.Args) -> "Target":
|
||||||
|
@ -310,7 +313,7 @@ class Resolved:
|
||||||
|
|
||||||
@dt.dataclass
|
@dt.dataclass
|
||||||
class Component(Manifest):
|
class Component(Manifest):
|
||||||
decription: str = dt.field(default="(No description)")
|
description: str = dt.field(default="(No description)")
|
||||||
props: Props = dt.field(default_factory=dict)
|
props: Props = dt.field(default_factory=dict)
|
||||||
tools: Tools = dt.field(default_factory=dict)
|
tools: Tools = dt.field(default_factory=dict)
|
||||||
enableIf: dict[str, list[Any]] = dt.field(default_factory=dict)
|
enableIf: dict[str, list[Any]] = dt.field(default_factory=dict)
|
||||||
|
@ -593,7 +596,7 @@ class Registry(DataClassJsonMixin):
|
||||||
for c in r.iter(Component):
|
for c in r.iter(Component):
|
||||||
if c.isEnabled(target)[0]:
|
if c.isEnabled(target)[0]:
|
||||||
for inject in c.injects:
|
for inject in c.injects:
|
||||||
victim = r.lookup(inject, Component)
|
victim = r.lookup(inject, Component, includeProvides=True)
|
||||||
if not victim:
|
if not victim:
|
||||||
raise RuntimeError(f"Cannot find component '{inject}'")
|
raise RuntimeError(f"Cannot find component '{inject}'")
|
||||||
victim.resolved[target.id].injected.append(c.id)
|
victim.resolved[target.id].injected.append(c.id)
|
||||||
|
@ -622,7 +625,7 @@ class Registry(DataClassJsonMixin):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("l", "model/list", "List all components and targets")
|
@cli.command("l", "model/list", "List all components and targets")
|
||||||
def modelListCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
registry = Registry.use(args)
|
registry = Registry.use(args)
|
||||||
|
|
||||||
components = list(registry.iter(Component))
|
components = list(registry.iter(Component))
|
||||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from . import shell, model, const
|
from . import shell, model, const, cli
|
||||||
|
|
||||||
import importlib.util as importlib
|
import importlib.util as importlib
|
||||||
|
|
||||||
|
@ -43,3 +43,8 @@ def loadAll():
|
||||||
for files in shell.readdir(pluginDir):
|
for files in shell.readdir(pluginDir):
|
||||||
if files.endswith(".py"):
|
if files.endswith(".py"):
|
||||||
load(os.path.join(pluginDir, files))
|
load(os.path.join(pluginDir, files))
|
||||||
|
|
||||||
|
|
||||||
|
def setup(args: cli.Args):
|
||||||
|
if not bool(args.consumeOpt("safemode", False)):
|
||||||
|
loadAll()
|
||||||
|
|
|
@ -7,7 +7,7 @@ from . import cli, model, shell, vt100
|
||||||
|
|
||||||
|
|
||||||
podPrefix = "CK__"
|
podPrefix = "CK__"
|
||||||
projectRoot = "/self"
|
projectRoot = "/project"
|
||||||
toolingRoot = "/tools"
|
toolingRoot = "/tools"
|
||||||
defaultPodName = f"{podPrefix}default"
|
defaultPodName = f"{podPrefix}default"
|
||||||
defaultPodImage = "ubuntu"
|
defaultPodImage = "ubuntu"
|
||||||
|
@ -16,8 +16,9 @@ defaultPodImage = "ubuntu"
|
||||||
@dt.dataclass
|
@dt.dataclass
|
||||||
class Image:
|
class Image:
|
||||||
id: str
|
id: str
|
||||||
|
like: str
|
||||||
image: str
|
image: str
|
||||||
init: list[str]
|
setup: list[str]
|
||||||
|
|
||||||
|
|
||||||
@dt.dataclass
|
@dt.dataclass
|
||||||
|
@ -28,6 +29,7 @@ class Pod:
|
||||||
|
|
||||||
IMAGES: dict[str, Image] = {
|
IMAGES: dict[str, Image] = {
|
||||||
"ubuntu": Image(
|
"ubuntu": Image(
|
||||||
|
"ubuntu",
|
||||||
"ubuntu",
|
"ubuntu",
|
||||||
"ubuntu:jammy",
|
"ubuntu:jammy",
|
||||||
[
|
[
|
||||||
|
@ -36,6 +38,7 @@ IMAGES: dict[str, Image] = {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
"debian": Image(
|
"debian": Image(
|
||||||
|
"debian",
|
||||||
"debian",
|
"debian",
|
||||||
"debian:bookworm",
|
"debian:bookworm",
|
||||||
[
|
[
|
||||||
|
@ -44,6 +47,7 @@ IMAGES: dict[str, Image] = {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
"alpine": Image(
|
"alpine": Image(
|
||||||
|
"alpine",
|
||||||
"alpine",
|
"alpine",
|
||||||
"alpine:3.18",
|
"alpine:3.18",
|
||||||
[
|
[
|
||||||
|
@ -52,6 +56,7 @@ IMAGES: dict[str, Image] = {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
"arch": Image(
|
"arch": Image(
|
||||||
|
"arch",
|
||||||
"arch",
|
"arch",
|
||||||
"archlinux:latest",
|
"archlinux:latest",
|
||||||
[
|
[
|
||||||
|
@ -60,6 +65,7 @@ IMAGES: dict[str, Image] = {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
"fedora": Image(
|
"fedora": Image(
|
||||||
|
"fedora",
|
||||||
"fedora",
|
"fedora",
|
||||||
"fedora:39",
|
"fedora:39",
|
||||||
[
|
[
|
||||||
|
@ -70,7 +76,7 @@ IMAGES: dict[str, Image] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def reincarnate(args: cli.Args):
|
def setup(args: cli.Args):
|
||||||
"""
|
"""
|
||||||
Reincarnate cutekit within a docker container, this is
|
Reincarnate cutekit within a docker container, this is
|
||||||
useful for cross-compiling
|
useful for cross-compiling
|
||||||
|
@ -95,6 +101,7 @@ def reincarnate(args: cli.Args):
|
||||||
"-it",
|
"-it",
|
||||||
pod,
|
pod,
|
||||||
"/tools/cutekit/entrypoint.sh",
|
"/tools/cutekit/entrypoint.sh",
|
||||||
|
"--reincarnated",
|
||||||
*args.args,
|
*args.args,
|
||||||
)
|
)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -103,15 +110,15 @@ def reincarnate(args: cli.Args):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("p", "pod", "Manage pods")
|
@cli.command("p", "pod", "Manage pods")
|
||||||
def podCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@cli.command("c", "pod/create", "Create a new pod")
|
@cli.command("c", "pod/create", "Create a new pod")
|
||||||
def podCreateCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
"""
|
"""
|
||||||
Create a new development pod with cutekit installed and the current
|
Create a new development pod with cutekit installed and the current
|
||||||
project mounted at /self
|
project mounted at /project
|
||||||
"""
|
"""
|
||||||
project = model.Project.ensure()
|
project = model.Project.ensure()
|
||||||
|
|
||||||
|
@ -144,7 +151,7 @@ def podCreateCmd(args: cli.Args):
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Initializing pod '{name[len(podPrefix) :]}'...")
|
print(f"Initializing pod '{name[len(podPrefix) :]}'...")
|
||||||
for cmd in image.init:
|
for cmd in image.setup:
|
||||||
print(vt100.p(cmd))
|
print(vt100.p(cmd))
|
||||||
exitCode, ouput = container.exec_run(f"/bin/bash -c '{cmd}'", demux=True)
|
exitCode, ouput = container.exec_run(f"/bin/bash -c '{cmd}'", demux=True)
|
||||||
if exitCode != 0:
|
if exitCode != 0:
|
||||||
|
@ -154,7 +161,7 @@ def podCreateCmd(args: cli.Args):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("k", "pod/kill", "Stop and remove a pod")
|
@cli.command("k", "pod/kill", "Stop and remove a pod")
|
||||||
def podKillCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
client = docker.from_env()
|
client = docker.from_env()
|
||||||
name = str(args.consumeOpt("name", defaultPodName))
|
name = str(args.consumeOpt("name", defaultPodName))
|
||||||
if not name.startswith(podPrefix):
|
if not name.startswith(podPrefix):
|
||||||
|
@ -170,13 +177,13 @@ def podKillCmd(args: cli.Args):
|
||||||
|
|
||||||
|
|
||||||
@cli.command("s", "pod/shell", "Open a shell in a pod")
|
@cli.command("s", "pod/shell", "Open a shell in a pod")
|
||||||
def podShellCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
args.args.insert(0, "/bin/bash")
|
args.args.insert(0, "/bin/bash")
|
||||||
podExecCmd(args)
|
podExecCmd(args)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("l", "pod/list", "List all pods")
|
@cli.command("l", "pod/list", "List all pods")
|
||||||
def podListCmd(args: cli.Args):
|
def _(args: cli.Args):
|
||||||
client = docker.from_env()
|
client = docker.from_env()
|
||||||
hasPods = False
|
hasPods = False
|
||||||
for container in client.containers.list(all=True):
|
for container in client.containers.list(all=True):
|
||||||
|
|
|
@ -10,7 +10,7 @@ import fnmatch
|
||||||
import platform
|
import platform
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import dataclasses as dt
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from . import const
|
from . import const
|
||||||
|
@ -18,15 +18,13 @@ from . import const
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dt.dataclass
|
||||||
class Uname:
|
class Uname:
|
||||||
def __init__(
|
sysname: str
|
||||||
self, sysname: str, nodename: str, release: str, version: str, machine: str
|
nodename: str
|
||||||
):
|
release: str
|
||||||
self.sysname = sysname
|
version: str
|
||||||
self.nodename = nodename
|
machine: str
|
||||||
self.release = release
|
|
||||||
self.version = version
|
|
||||||
self.machine = machine
|
|
||||||
|
|
||||||
|
|
||||||
def uname() -> Uname:
|
def uname() -> Uname:
|
||||||
|
@ -131,12 +129,13 @@ def wget(url: str, path: Optional[str] = None) -> str:
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def exec(*args: str, quiet: bool = False) -> bool:
|
def exec(*args: str, quiet: bool = False, cwd: str | None = None) -> bool:
|
||||||
_logger.debug(f"Executing {args}")
|
_logger.debug(f"Executing {args}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
args,
|
args,
|
||||||
|
cwd=cwd,
|
||||||
stdout=sys.stdout if not quiet else subprocess.PIPE,
|
stdout=sys.stdout if not quiet else subprocess.PIPE,
|
||||||
stderr=sys.stderr if not quiet else subprocess.PIPE,
|
stderr=sys.stderr if not quiet else subprocess.PIPE,
|
||||||
)
|
)
|
||||||
|
@ -274,3 +273,10 @@ def which(cmd: str) -> Optional[str]:
|
||||||
Find the path of a command
|
Find the path of a command
|
||||||
"""
|
"""
|
||||||
return shutil.which(cmd)
|
return shutil.which(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def nproc() -> int:
|
||||||
|
"""
|
||||||
|
Return the number of processors
|
||||||
|
"""
|
||||||
|
return os.cpu_count() or 1
|
||||||
|
|
|
@ -29,6 +29,10 @@ def hash(
|
||||||
return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def randomHash() -> str:
|
||||||
|
return hashlib.sha256(os.urandom(32)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def camelCase(s: str) -> str:
|
def camelCase(s: str) -> str:
|
||||||
s = "".join(x for x in s.title() if x != "_" and x != "-")
|
s = "".join(x for x in s.title() if x != "_" and x != "-")
|
||||||
s = s[0].lower() + s[1:]
|
s = s[0].lower() + s[1:]
|
||||||
|
|
|
@ -11,7 +11,7 @@ For example you can add a new command to the CLI:
|
||||||
from cutekit import cli
|
from cutekit import cli
|
||||||
|
|
||||||
@cli.command("h", "hello", "Print hello world")
|
@cli.command("h", "hello", "Print hello world")
|
||||||
def bootCmd(args: cli.Args) -> None:
|
def _(args: cli.Args) -> None:
|
||||||
print("Hello world!")
|
print("Hello world!")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue