Cleanups imports and got ride of cutekit.project
This commit is contained in:
		
							parent
							
								
									3a78537dff
								
							
						
					
					
						commit
						8d1ca3095a
					
				
					 13 changed files with 224 additions and 222 deletions
				
			
		|  | @ -2,7 +2,7 @@ import sys | ||||||
| import os | import os | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from cutekit import const, project, vt100, plugins, cmds, cli | from . import const, model, vt100, plugins, cmds, cli | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def setupLogger(verbose: bool): | def setupLogger(verbose: bool): | ||||||
|  | @ -13,7 +13,7 @@ def setupLogger(verbose: bool): | ||||||
|             datefmt="%Y-%m-%d %H:%M:%S", |             datefmt="%Y-%m-%d %H:%M:%S", | ||||||
|         ) |         ) | ||||||
|     else: |     else: | ||||||
|         projectRoot = project.root() |         projectRoot = model.Project.root() | ||||||
|         logFile = const.GLOBAL_LOG_FILE |         logFile = const.GLOBAL_LOG_FILE | ||||||
|         if projectRoot is not None: |         if projectRoot is not None: | ||||||
|             logFile = os.path.join(projectRoot, const.PROJECT_LOG_FILE) |             logFile = os.path.join(projectRoot, const.PROJECT_LOG_FILE) | ||||||
|  |  | ||||||
|  | @ -2,16 +2,13 @@ import os | ||||||
| import logging | import logging | ||||||
| from typing import TextIO | from typing import TextIO | ||||||
| 
 | 
 | ||||||
| from cutekit.model import Props | from . import shell, rules, model, ninja, context | ||||||
| from cutekit.ninja import Writer |  | ||||||
| from cutekit.context import ComponentInstance, Context, contextFor |  | ||||||
| from cutekit import shell, rules |  | ||||||
| 
 | 
 | ||||||
| _logger = logging.getLogger(__name__) | _logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def gen(out: TextIO, context: Context): | def gen(out: TextIO, context: context.Context): | ||||||
|     writer = Writer(out) |     writer = ninja.Writer(out) | ||||||
| 
 | 
 | ||||||
|     target = context.target |     target = context.target | ||||||
| 
 | 
 | ||||||
|  | @ -102,16 +99,18 @@ def gen(out: TextIO, context: Context): | ||||||
|     writer.default("all") |     writer.default("all") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def build(componentSpec: str, targetSpec: str, props: Props = {}) -> ComponentInstance: | def build( | ||||||
|     context = contextFor(targetSpec, props) |     componentSpec: str, targetSpec: str, props: model.Props = {} | ||||||
|  | ) -> context.ComponentInstance: | ||||||
|  |     ctx = context.contextFor(targetSpec, props) | ||||||
| 
 | 
 | ||||||
|     shell.mkdir(context.builddir()) |     shell.mkdir(ctx.builddir()) | ||||||
|     ninjaPath = os.path.join(context.builddir(), "build.ninja") |     ninjaPath = os.path.join(ctx.builddir(), "build.ninja") | ||||||
| 
 | 
 | ||||||
|     with open(ninjaPath, "w") as f: |     with open(ninjaPath, "w") as f: | ||||||
|         gen(f, context) |         gen(f, ctx) | ||||||
| 
 | 
 | ||||||
|     instance = context.componentByName(componentSpec) |     instance = ctx.componentByName(componentSpec) | ||||||
| 
 | 
 | ||||||
|     if instance is None: |     if instance is None: | ||||||
|         raise RuntimeError(f"Component {componentSpec} not found") |         raise RuntimeError(f"Component {componentSpec} not found") | ||||||
|  | @ -137,32 +136,32 @@ class Paths: | ||||||
|         self.obj = obj |         self.obj = obj | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def buildAll(targetSpec: str, props: Props = {}) -> Context: | def buildAll(targetSpec: str, props: model.Props = {}) -> context.Context: | ||||||
|     context = contextFor(targetSpec, props) |     ctx = context.contextFor(targetSpec, props) | ||||||
| 
 | 
 | ||||||
|     shell.mkdir(context.builddir()) |     shell.mkdir(ctx.builddir()) | ||||||
|     ninjaPath = os.path.join(context.builddir(), "build.ninja") |     ninjaPath = os.path.join(ctx.builddir(), "build.ninja") | ||||||
| 
 | 
 | ||||||
|     with open(ninjaPath, "w") as f: |     with open(ninjaPath, "w") as f: | ||||||
|         gen(f, context) |         gen(f, ctx) | ||||||
| 
 | 
 | ||||||
|     shell.exec("ninja", "-v", "-f", ninjaPath) |     shell.exec("ninja", "-v", "-f", ninjaPath) | ||||||
| 
 | 
 | ||||||
|     return context |     return ctx | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def testAll(targetSpec: str): | def testAll(targetSpec: str): | ||||||
|     context = contextFor(targetSpec) |     ctx = context.contextFor(targetSpec) | ||||||
| 
 | 
 | ||||||
|     shell.mkdir(context.builddir()) |     shell.mkdir(ctx.builddir()) | ||||||
|     ninjaPath = os.path.join(context.builddir(), "build.ninja") |     ninjaPath = os.path.join(ctx.builddir(), "build.ninja") | ||||||
| 
 | 
 | ||||||
|     with open(ninjaPath, "w") as f: |     with open(ninjaPath, "w") as f: | ||||||
|         gen(f, context) |         gen(f, ctx) | ||||||
| 
 | 
 | ||||||
|     shell.exec("ninja", "-v", "-f", ninjaPath, "all") |     shell.exec("ninja", "-v", "-f", ninjaPath, "all") | ||||||
| 
 | 
 | ||||||
|     for instance in context.enabledInstances(): |     for instance in ctx.enabledInstances(): | ||||||
|         if instance.isLib(): |         if instance.isLib(): | ||||||
|             continue |             continue | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,13 +3,12 @@ import os | ||||||
| import sys | import sys | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| from cutekit import ( | from . import ( | ||||||
|     context, |     context, | ||||||
|     shell, |     shell, | ||||||
|     const, |     const, | ||||||
|     vt100, |     vt100, | ||||||
|     builder, |     builder, | ||||||
|     project, |  | ||||||
|     cli, |     cli, | ||||||
|     model, |     model, | ||||||
|     jexpr, |     jexpr, | ||||||
|  | @ -21,7 +20,7 @@ _logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| @cli.command("p", "project", "Show project information") | @cli.command("p", "project", "Show project information") | ||||||
| def runCmd(args: cli.Args): | def runCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 | 
 | ||||||
|     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) |     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||||
|     props = args.consumePrefix("prop:") |     props = args.consumePrefix("prop:") | ||||||
|  | @ -42,7 +41,7 @@ def runCmd(args: cli.Args): | ||||||
| 
 | 
 | ||||||
| @cli.command("t", "test", "Run all test targets") | @cli.command("t", "test", "Run all test targets") | ||||||
| def testCmd(args: cli.Args): | def testCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 | 
 | ||||||
|     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) |     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||||
|     builder.testAll(targetSpec) |     builder.testAll(targetSpec) | ||||||
|  | @ -50,7 +49,7 @@ def testCmd(args: cli.Args): | ||||||
| 
 | 
 | ||||||
| @cli.command("d", "debug", "Debug a component") | @cli.command("d", "debug", "Debug a component") | ||||||
| def debugCmd(args: cli.Args): | def debugCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 | 
 | ||||||
|     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) |     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||||
|     props = args.consumePrefix("prop:") |     props = args.consumePrefix("prop:") | ||||||
|  | @ -71,7 +70,7 @@ def debugCmd(args: cli.Args): | ||||||
| 
 | 
 | ||||||
| @cli.command("b", "build", "Build a component or all components") | @cli.command("b", "build", "Build a component or all components") | ||||||
| def buildCmd(args: cli.Args): | def buildCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 | 
 | ||||||
|     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) |     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||||
|     props = args.consumePrefix("prop:") |     props = args.consumePrefix("prop:") | ||||||
|  | @ -85,7 +84,7 @@ def buildCmd(args: cli.Args): | ||||||
| 
 | 
 | ||||||
| @cli.command("l", "list", "List all components and targets") | @cli.command("l", "list", "List all components and targets") | ||||||
| def listCmd(args: cli.Args): | def listCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 | 
 | ||||||
|     components = context.loadAllComponents() |     components = context.loadAllComponents() | ||||||
|     targets = context.loadAllTargets() |     targets = context.loadAllTargets() | ||||||
|  | @ -109,13 +108,13 @@ def listCmd(args: cli.Args): | ||||||
| 
 | 
 | ||||||
| @cli.command("c", "clean", "Clean build files") | @cli.command("c", "clean", "Clean build files") | ||||||
| def cleanCmd(args: cli.Args): | def cleanCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
|     shell.rmrf(const.BUILD_DIR) |     shell.rmrf(const.BUILD_DIR) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @cli.command("n", "nuke", "Clean all build files and caches") | @cli.command("n", "nuke", "Clean all build files and caches") | ||||||
| def nukeCmd(args: cli.Args): | def nukeCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
|     shell.rmrf(const.PROJECT_CK_DIR) |     shell.rmrf(const.PROJECT_CK_DIR) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -170,8 +169,7 @@ def grabExtern(extern: dict[str, model.Extern]): | ||||||
| 
 | 
 | ||||||
| @cli.command("i", "install", "Install required external packages") | @cli.command("i", "install", "Install required external packages") | ||||||
| def installCmd(args: cli.Args): | def installCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 |  | ||||||
|     pj = context.loadProject(".") |     pj = context.loadProject(".") | ||||||
|     grabExtern(pj.extern) |     grabExtern(pj.extern) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,22 +4,14 @@ from pathlib import Path | ||||||
| import os | import os | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from cutekit.model import ( | 
 | ||||||
|     Project, | from . import const, shell, jexpr, utils, rules, mixins, project, model | ||||||
|     Target, |  | ||||||
|     Component, |  | ||||||
|     Props, |  | ||||||
|     Type, |  | ||||||
|     Tool, |  | ||||||
|     Tools, |  | ||||||
| ) |  | ||||||
| from cutekit import const, shell, jexpr, utils, rules, mixins, project |  | ||||||
| 
 | 
 | ||||||
| _logger = logging.getLogger(__name__) | _logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IContext(Protocol): | class IContext(Protocol): | ||||||
|     target: Target |     target: model.Target | ||||||
| 
 | 
 | ||||||
|     def builddir(self) -> str: |     def builddir(self) -> str: | ||||||
|         ... |         ... | ||||||
|  | @ -28,7 +20,7 @@ class IContext(Protocol): | ||||||
| class ComponentInstance: | class ComponentInstance: | ||||||
|     enabled: bool = True |     enabled: bool = True | ||||||
|     disableReason = "" |     disableReason = "" | ||||||
|     manifest: Component |     manifest: model.Component | ||||||
|     sources: list[str] = [] |     sources: list[str] = [] | ||||||
|     res: list[str] = [] |     res: list[str] = [] | ||||||
|     resolved: list[str] = [] |     resolved: list[str] = [] | ||||||
|  | @ -38,7 +30,7 @@ class ComponentInstance: | ||||||
|         self, |         self, | ||||||
|         enabled: bool, |         enabled: bool, | ||||||
|         disableReason: str, |         disableReason: str, | ||||||
|         manifest: Component, |         manifest: model.Component, | ||||||
|         sources: list[str], |         sources: list[str], | ||||||
|         res: list[str], |         res: list[str], | ||||||
|         resolved: list[str], |         resolved: list[str], | ||||||
|  | @ -54,7 +46,7 @@ class ComponentInstance: | ||||||
|         return self.manifest.id |         return self.manifest.id | ||||||
| 
 | 
 | ||||||
|     def isLib(self): |     def isLib(self): | ||||||
|         return self.manifest.type == Type.LIB |         return self.manifest.type == model.Type.LIB | ||||||
| 
 | 
 | ||||||
|     def objdir(self) -> str: |     def objdir(self) -> str: | ||||||
|         return os.path.join(self.context.builddir(), f"{self.manifest.id}/obj") |         return os.path.join(self.context.builddir(), f"{self.manifest.id}/obj") | ||||||
|  | @ -96,22 +88,25 @@ class ComponentInstance: | ||||||
|     def cinclude(self) -> str: |     def cinclude(self) -> str: | ||||||
|         if "cpp-root-include" in self.manifest.props: |         if "cpp-root-include" in self.manifest.props: | ||||||
|             return self.manifest.dirname() |             return self.manifest.dirname() | ||||||
|         elif self.manifest.type == Type.LIB: |         elif self.manifest.type == model.Type.LIB: | ||||||
|             return str(Path(self.manifest.dirname()).parent) |             return str(Path(self.manifest.dirname()).parent) | ||||||
|         else: |         else: | ||||||
|             return "" |             return "" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Context(IContext): | class Context(IContext): | ||||||
|     target: Target |     target: model.Target | ||||||
|     instances: list[ComponentInstance] |     instances: list[ComponentInstance] | ||||||
|     tools: Tools |     tools: model.Tools | ||||||
| 
 | 
 | ||||||
|     def enabledInstances(self) -> Iterable[ComponentInstance]: |     def enabledInstances(self) -> Iterable[ComponentInstance]: | ||||||
|         return filter(lambda x: x.enabled, self.instances) |         return filter(lambda x: x.enabled, self.instances) | ||||||
| 
 | 
 | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, target: Target, instances: list[ComponentInstance], tools: Tools |         self, | ||||||
|  |         target: model.Target, | ||||||
|  |         instances: list[ComponentInstance], | ||||||
|  |         tools: model.Tools, | ||||||
|     ): |     ): | ||||||
|         self.target = target |         self.target = target | ||||||
|         self.instances = instances |         self.instances = instances | ||||||
|  | @ -143,8 +138,8 @@ class Context(IContext): | ||||||
|         return os.path.join(const.BUILD_DIR, f"{self.target.id}-{self.hashid()[:8]}") |         return os.path.join(const.BUILD_DIR, f"{self.target.id}-{self.hashid()[:8]}") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def loadAllTargets() -> list[Target]: | def loadAllTargets() -> list[model.Target]: | ||||||
|     projectRoot = project.root() |     projectRoot = model.Project.root() | ||||||
|     if projectRoot is None: |     if projectRoot is None: | ||||||
|         return [] |         return [] | ||||||
| 
 | 
 | ||||||
|  | @ -159,40 +154,42 @@ def loadAllTargets() -> list[Target]: | ||||||
|     ret = [] |     ret = [] | ||||||
|     for entry in paths: |     for entry in paths: | ||||||
|         files = shell.find(entry, ["*.json"]) |         files = shell.find(entry, ["*.json"]) | ||||||
|         ret += list(map(lambda path: Target(jexpr.evalRead(path), path), files)) |         ret += list(map(lambda path: model.Target(jexpr.evalRead(path), path), files)) | ||||||
| 
 | 
 | ||||||
|     return ret |     return ret | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def loadProject(path: str) -> Project: | def loadProject(path: str) -> model.Project: | ||||||
|     path = os.path.join(path, "project.json") |     path = os.path.join(path, "project.json") | ||||||
|     return Project(jexpr.evalRead(path), path) |     return model.Project(jexpr.evalRead(path), path) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def loadTarget(id: str) -> Target: | def loadTarget(id: str) -> model.Target: | ||||||
|     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 RuntimeError(f"Target '{id}' not found") |         raise RuntimeError(f"Target '{id}' not found") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def loadAllComponents() -> list[Component]: | def loadAllComponents() -> list[model.Component]: | ||||||
|     files = shell.find(const.SRC_DIR, ["manifest.json"]) |     files = shell.find(const.SRC_DIR, ["manifest.json"]) | ||||||
|     files += shell.find(const.EXTERN_DIR, ["manifest.json"]) |     files += shell.find(const.EXTERN_DIR, ["manifest.json"]) | ||||||
| 
 | 
 | ||||||
|     return list(map(lambda path: Component(jexpr.evalRead(path), path), files)) |     return list(map(lambda path: model.Component(jexpr.evalRead(path), path), files)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def filterDisabled( | def filterDisabled( | ||||||
|     components: list[Component], target: Target |     components: list[model.Component], target: model.Target | ||||||
| ) -> tuple[list[Component], list[Component]]: | ) -> tuple[list[model.Component], list[model.Component]]: | ||||||
|     return list(filter(lambda c: c.isEnabled(target)[0], components)), list( |     return list(filter(lambda c: c.isEnabled(target)[0], components)), list( | ||||||
|         filter(lambda c: not c.isEnabled(target)[0], components) |         filter(lambda c: not c.isEnabled(target)[0], components) | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def providerFor(what: str, components: list[Component]) -> tuple[Optional[str], str]: | def providerFor( | ||||||
|     result: list[Component] = list(filter(lambda c: c.id == what, components)) |     what: str, components: list[model.Component] | ||||||
|  | ) -> tuple[Optional[str], str]: | ||||||
|  |     result: list[model.Component] = list(filter(lambda c: c.id == what, components)) | ||||||
| 
 | 
 | ||||||
|     if len(result) == 0: |     if len(result) == 0: | ||||||
|         # Try to find a provider |         # Try to find a provider | ||||||
|  | @ -211,7 +208,7 @@ def providerFor(what: str, components: list[Component]) -> tuple[Optional[str], | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def resolveDeps( | def resolveDeps( | ||||||
|     componentSpec: str, components: list[Component], target: Target |     componentSpec: str, components: list[model.Component], target: model.Target | ||||||
| ) -> tuple[bool, str, list[str]]: | ) -> tuple[bool, str, list[str]]: | ||||||
|     mapping = dict(map(lambda c: (c.id, c), components)) |     mapping = dict(map(lambda c: (c.id, c), components)) | ||||||
| 
 | 
 | ||||||
|  | @ -249,7 +246,7 @@ def resolveDeps( | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def instanciate( | def instanciate( | ||||||
|     componentSpec: str, components: list[Component], target: Target |     componentSpec: str, components: list[model.Component], target: model.Target | ||||||
| ) -> Optional[ComponentInstance]: | ) -> Optional[ComponentInstance]: | ||||||
|     manifest = next(filter(lambda c: c.id == componentSpec, components)) |     manifest = next(filter(lambda c: c.id == componentSpec, components)) | ||||||
|     wildcards = set(chain(*map(lambda rule: rule.fileIn, rules.rules.values()))) |     wildcards = set(chain(*map(lambda rule: rule.fileIn, rules.rules.values()))) | ||||||
|  | @ -264,7 +261,9 @@ def instanciate( | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def instanciateDisabled(component: Component, target: Target) -> ComponentInstance: | def instanciateDisabled( | ||||||
|  |     component: model.Component, target: model.Target | ||||||
|  | ) -> ComponentInstance: | ||||||
|     return ComponentInstance( |     return ComponentInstance( | ||||||
|         enabled=False, |         enabled=False, | ||||||
|         disableReason=component.isEnabled(target)[1], |         disableReason=component.isEnabled(target)[1], | ||||||
|  | @ -278,7 +277,7 @@ def instanciateDisabled(component: Component, target: Target) -> ComponentInstan | ||||||
| context: dict[str, Context] = {} | context: dict[str, Context] = {} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def contextFor(targetSpec: str, props: Props = {}) -> Context: | def contextFor(targetSpec: str, props: model.Props = {}) -> Context: | ||||||
|     if targetSpec in context: |     if targetSpec in context: | ||||||
|         return context[targetSpec] |         return context[targetSpec] | ||||||
| 
 | 
 | ||||||
|  | @ -295,12 +294,12 @@ def contextFor(targetSpec: str, props: Props = {}) -> Context: | ||||||
|     components = loadAllComponents() |     components = loadAllComponents() | ||||||
|     components, disabled = filterDisabled(components, target) |     components, disabled = filterDisabled(components, target) | ||||||
| 
 | 
 | ||||||
|     tools: Tools = {} |     tools: model.Tools = {} | ||||||
| 
 | 
 | ||||||
|     for toolSpec in target.tools: |     for toolSpec in target.tools: | ||||||
|         tool = target.tools[toolSpec] |         tool = target.tools[toolSpec] | ||||||
| 
 | 
 | ||||||
|         tools[toolSpec] = Tool( |         tools[toolSpec] = model.Tool( | ||||||
|             strict=False, cmd=tool.cmd, args=tool.args, files=tool.files |             strict=False, cmd=tool.cmd, args=tool.args, files=tool.files | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import os | import os | ||||||
| 
 | 
 | ||||||
| from typing import cast | from typing import cast | ||||||
| from . import vt100, context, project, cli, shell | from . import vt100, context, cli, shell, model | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def view( | def view( | ||||||
|  | @ -10,7 +10,7 @@ def view( | ||||||
|     showExe: bool = True, |     showExe: bool = True, | ||||||
|     showDisabled: bool = False, |     showDisabled: bool = False, | ||||||
| ): | ): | ||||||
|     from graphviz import Digraph |     from graphviz import Digraph  # type: ignore | ||||||
| 
 | 
 | ||||||
|     g = Digraph(context.target.id, filename="graph.gv") |     g = Digraph(context.target.id, filename="graph.gv") | ||||||
| 
 | 
 | ||||||
|  | @ -83,7 +83,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 graphCmd(args: cli.Args): | ||||||
|     project.chdir() |     model.Project.chdir() | ||||||
| 
 | 
 | ||||||
|     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) |     targetSpec = str(args.consumeOpt("target", "host-" + shell.uname().machine)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,9 +1,8 @@ | ||||||
| import os | import os | ||||||
| from typing import Any, cast, Callable, Final |  | ||||||
| import json | import json | ||||||
| 
 | 
 | ||||||
| import cutekit.shell as shell | from typing import Any, cast, Callable, Final | ||||||
| from cutekit.compat import ensureSupportedManifest | from . import shell, compat | ||||||
| 
 | 
 | ||||||
| Json = Any | Json = Any | ||||||
| Builtin = Callable[..., Json] | Builtin = Callable[..., Json] | ||||||
|  | @ -61,5 +60,5 @@ def read(path: str) -> Json: | ||||||
| def evalRead(path: str, compatibilityCheck: bool = True) -> Json: | def evalRead(path: str, compatibilityCheck: bool = True) -> Json: | ||||||
|     data = read(path) |     data = read(path) | ||||||
|     if compatibilityCheck: |     if compatibilityCheck: | ||||||
|         ensureSupportedManifest(data, path) |         compat.ensureSupportedManifest(data, path) | ||||||
|     return eval(data, path) |     return eval(data, path) | ||||||
|  |  | ||||||
|  | @ -1,25 +1,26 @@ | ||||||
| from typing import Callable | from typing import Callable | ||||||
| from cutekit.model import Target, Tools |  | ||||||
| 
 | 
 | ||||||
| Mixin = Callable[[Target, Tools], Tools] | from . import model | ||||||
|  | 
 | ||||||
|  | Mixin = Callable[[model.Target, model.Tools], model.Tools] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def patchToolArgs(tools: Tools, toolSpec: str, args: list[str]): | def patchToolArgs(tools: model.Tools, toolSpec: str, args: list[str]): | ||||||
|     tools[toolSpec].args += args |     tools[toolSpec].args += args | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def prefixToolCmd(tools: Tools, toolSpec: str, prefix: str): | def prefixToolCmd(tools: model.Tools, toolSpec: str, prefix: str): | ||||||
|     tools[toolSpec].cmd = prefix + " " + tools[toolSpec].cmd |     tools[toolSpec].cmd = prefix + " " + tools[toolSpec].cmd | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def mixinCache(target: Target, tools: Tools) -> Tools: | def mixinCache(target: model.Target, tools: model.Tools) -> model.Tools: | ||||||
|     prefixToolCmd(tools, "cc", "ccache") |     prefixToolCmd(tools, "cc", "ccache") | ||||||
|     prefixToolCmd(tools, "cxx", "ccache") |     prefixToolCmd(tools, "cxx", "ccache") | ||||||
|     return tools |     return tools | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def makeMixinSan(san: str) -> Mixin: | def makeMixinSan(san: str) -> Mixin: | ||||||
|     def mixinSan(target: Target, tools: Tools) -> Tools: |     def mixinSan(target: model.Target, tools: model.Tools) -> model.Tools: | ||||||
|         patchToolArgs(tools, "cc", [f"-fsanitize={san}"]) |         patchToolArgs(tools, "cc", [f"-fsanitize={san}"]) | ||||||
|         patchToolArgs(tools, "cxx", [f"-fsanitize={san}"]) |         patchToolArgs(tools, "cxx", [f"-fsanitize={san}"]) | ||||||
|         patchToolArgs(tools, "ld", [f"-fsanitize={san}"]) |         patchToolArgs(tools, "ld", [f"-fsanitize={san}"]) | ||||||
|  | @ -30,7 +31,7 @@ def makeMixinSan(san: str) -> Mixin: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def makeMixinOptimize(level: str) -> Mixin: | def makeMixinOptimize(level: str) -> Mixin: | ||||||
|     def mixinOptimize(target: Target, tools: Tools) -> Tools: |     def mixinOptimize(target: model.Target, tools: model.Tools) -> model.Tools: | ||||||
|         patchToolArgs(tools, "cc", [f"-O{level}"]) |         patchToolArgs(tools, "cc", [f"-O{level}"]) | ||||||
|         patchToolArgs(tools, "cxx", [f"-O{level}"]) |         patchToolArgs(tools, "cxx", [f"-O{level}"]) | ||||||
| 
 | 
 | ||||||
|  | @ -39,7 +40,7 @@ def makeMixinOptimize(level: str) -> Mixin: | ||||||
|     return mixinOptimize |     return mixinOptimize | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def mixinDebug(target: Target, tools: Tools) -> Tools: | def mixinDebug(target: model.Target, tools: model.Tools) -> model.Tools: | ||||||
|     patchToolArgs(tools, "cc", ["-g", "-gdwarf-4"]) |     patchToolArgs(tools, "cc", ["-g", "-gdwarf-4"]) | ||||||
|     patchToolArgs(tools, "cxx", ["-g", "-gdwarf-4"]) |     patchToolArgs(tools, "cxx", ["-g", "-gdwarf-4"]) | ||||||
| 
 | 
 | ||||||
|  | @ -47,7 +48,7 @@ def mixinDebug(target: Target, tools: Tools) -> Tools: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def makeMixinTune(tune: str) -> Mixin: | def makeMixinTune(tune: str) -> Mixin: | ||||||
|     def mixinTune(target: Target, tools: Tools) -> Tools: |     def mixinTune(target: model.Target, tools: model.Tools) -> model.Tools: | ||||||
|         patchToolArgs(tools, "cc", [f"-mtune={tune}"]) |         patchToolArgs(tools, "cc", [f"-mtune={tune}"]) | ||||||
|         patchToolArgs(tools, "cxx", [f"-mtune={tune}"]) |         patchToolArgs(tools, "cxx", [f"-mtune={tune}"]) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,9 +1,10 @@ | ||||||
| import os | import os | ||||||
| from enum import Enum |  | ||||||
| from typing import Any |  | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from cutekit.jexpr import Json | from enum import Enum | ||||||
|  | from typing import Any | ||||||
|  | from pathlib import Path | ||||||
|  | from . import jexpr | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| _logger = logging.getLogger(__name__) | _logger = logging.getLogger(__name__) | ||||||
|  | @ -25,7 +26,11 @@ class Manifest: | ||||||
|     path: str = "" |     path: str = "" | ||||||
| 
 | 
 | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any |         self, | ||||||
|  |         json: jexpr.Json = None, | ||||||
|  |         path: str = "", | ||||||
|  |         strict: bool = True, | ||||||
|  |         **kwargs: Any, | ||||||
|     ): |     ): | ||||||
|         if json is not None: |         if json is not None: | ||||||
|             if "id" not in json: |             if "id" not in json: | ||||||
|  | @ -45,7 +50,7 @@ class Manifest: | ||||||
|         for key in kwargs: |         for key in kwargs: | ||||||
|             setattr(self, key, kwargs[key]) |             setattr(self, key, kwargs[key]) | ||||||
| 
 | 
 | ||||||
|     def toJson(self) -> Json: |     def toJson(self) -> jexpr.Json: | ||||||
|         return {"id": self.id, "type": self.type.value, "path": self.path} |         return {"id": self.id, "type": self.type.value, "path": self.path} | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|  | @ -62,7 +67,7 @@ class Extern: | ||||||
|     git: str = "" |     git: str = "" | ||||||
|     tag: str = "" |     tag: str = "" | ||||||
| 
 | 
 | ||||||
|     def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any): |     def __init__(self, json: jexpr.Json = None, strict: bool = True, **kwargs: Any): | ||||||
|         if json is not None: |         if json is not None: | ||||||
|             if "git" not in json and strict: |             if "git" not in json and strict: | ||||||
|                 raise RuntimeError("Missing git") |                 raise RuntimeError("Missing git") | ||||||
|  | @ -79,7 +84,7 @@ class Extern: | ||||||
|         for key in kwargs: |         for key in kwargs: | ||||||
|             setattr(self, key, kwargs[key]) |             setattr(self, key, kwargs[key]) | ||||||
| 
 | 
 | ||||||
|     def toJson(self) -> Json: |     def toJson(self) -> jexpr.Json: | ||||||
|         return {"git": self.git, "tag": self.tag} |         return {"git": self.git, "tag": self.tag} | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|  | @ -94,7 +99,11 @@ class Project(Manifest): | ||||||
|     extern: dict[str, Extern] = {} |     extern: dict[str, Extern] = {} | ||||||
| 
 | 
 | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any |         self, | ||||||
|  |         json: jexpr.Json = None, | ||||||
|  |         path: str = "", | ||||||
|  |         strict: bool = True, | ||||||
|  |         **kwargs: Any, | ||||||
|     ): |     ): | ||||||
|         if json is not None: |         if json is not None: | ||||||
|             if "description" not in json and strict: |             if "description" not in json and strict: | ||||||
|  | @ -108,7 +117,7 @@ class Project(Manifest): | ||||||
| 
 | 
 | ||||||
|         super().__init__(json, path, strict, **kwargs) |         super().__init__(json, path, strict, **kwargs) | ||||||
| 
 | 
 | ||||||
|     def toJson(self) -> Json: |     def toJson(self) -> jexpr.Json: | ||||||
|         return { |         return { | ||||||
|             **super().toJson(), |             **super().toJson(), | ||||||
|             "description": self.description, |             "description": self.description, | ||||||
|  | @ -121,13 +130,31 @@ class Project(Manifest): | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return f"ProjectManifest({self.id})" |         return f"ProjectManifest({self.id})" | ||||||
| 
 | 
 | ||||||
|  |     @staticmethod | ||||||
|  |     def root() -> str | None: | ||||||
|  |         cwd = Path.cwd() | ||||||
|  |         while str(cwd) != cwd.root: | ||||||
|  |             if (cwd / "project.json").is_file(): | ||||||
|  |                 return str(cwd) | ||||||
|  |             cwd = cwd.parent | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     @staticmethod | ||||||
|  |     def chdir() -> None: | ||||||
|  |         path = Project.root() | ||||||
|  |         if path is None: | ||||||
|  |             raise RuntimeError( | ||||||
|  |                 "No project.json found in this directory or any parent directory" | ||||||
|  |             ) | ||||||
|  |         os.chdir(path) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class Tool: | class Tool: | ||||||
|     cmd: str = "" |     cmd: str = "" | ||||||
|     args: list[str] = [] |     args: list[str] = [] | ||||||
|     files: list[str] = [] |     files: list[str] = [] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, json: Json = None, strict: bool = True, **kwargs: Any): |     def __init__(self, json: jexpr.Json = None, strict: bool = True, **kwargs: Any): | ||||||
|         if json is not None: |         if json is not None: | ||||||
|             if "cmd" not in json and strict: |             if "cmd" not in json and strict: | ||||||
|                 raise RuntimeError("Missing cmd") |                 raise RuntimeError("Missing cmd") | ||||||
|  | @ -146,7 +173,7 @@ class Tool: | ||||||
|         for key in kwargs: |         for key in kwargs: | ||||||
|             setattr(self, key, kwargs[key]) |             setattr(self, key, kwargs[key]) | ||||||
| 
 | 
 | ||||||
|     def toJson(self) -> Json: |     def toJson(self) -> jexpr.Json: | ||||||
|         return {"cmd": self.cmd, "args": self.args, "files": self.files} |         return {"cmd": self.cmd, "args": self.args, "files": self.files} | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|  | @ -165,7 +192,11 @@ class Target(Manifest): | ||||||
|     routing: dict[str, str] |     routing: dict[str, str] | ||||||
| 
 | 
 | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any |         self, | ||||||
|  |         json: jexpr.Json = None, | ||||||
|  |         path: str = "", | ||||||
|  |         strict: bool = True, | ||||||
|  |         **kwargs: Any, | ||||||
|     ): |     ): | ||||||
|         if json is not None: |         if json is not None: | ||||||
|             if "props" not in json and strict: |             if "props" not in json and strict: | ||||||
|  | @ -182,7 +213,7 @@ class Target(Manifest): | ||||||
| 
 | 
 | ||||||
|         super().__init__(json, path, strict, **kwargs) |         super().__init__(json, path, strict, **kwargs) | ||||||
| 
 | 
 | ||||||
|     def toJson(self) -> Json: |     def toJson(self) -> jexpr.Json: | ||||||
|         return { |         return { | ||||||
|             **super().toJson(), |             **super().toJson(), | ||||||
|             "props": self.props, |             "props": self.props, | ||||||
|  | @ -229,7 +260,11 @@ class Component(Manifest): | ||||||
|     subdirs: list[str] = [] |     subdirs: list[str] = [] | ||||||
| 
 | 
 | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, json: Json = None, path: str = "", strict: bool = True, **kwargs: Any |         self, | ||||||
|  |         json: jexpr.Json = None, | ||||||
|  |         path: str = "", | ||||||
|  |         strict: bool = True, | ||||||
|  |         **kwargs: Any, | ||||||
|     ): |     ): | ||||||
|         if json is not None: |         if json is not None: | ||||||
|             self.decription = json.get("description", self.decription) |             self.decription = json.get("description", self.decription) | ||||||
|  | @ -249,7 +284,7 @@ class Component(Manifest): | ||||||
| 
 | 
 | ||||||
|         super().__init__(json, path, strict, **kwargs) |         super().__init__(json, path, strict, **kwargs) | ||||||
| 
 | 
 | ||||||
|     def toJson(self) -> Json: |     def toJson(self) -> jexpr.Json: | ||||||
|         return { |         return { | ||||||
|             **super().toJson(), |             **super().toJson(), | ||||||
|             "description": self.decription, |             "description": self.decription, | ||||||
|  |  | ||||||
							
								
								
									
										159
									
								
								cutekit/ninja.py
									
										
									
									
									
								
							
							
						
						
									
										159
									
								
								cutekit/ninja.py
									
										
									
									
									
								
							|  | @ -23,13 +23,13 @@ use Python. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import textwrap | import textwrap | ||||||
| from typing import TextIO, Union |  | ||||||
| 
 | 
 | ||||||
| from cutekit.utils import asList | from typing import TextIO, Union | ||||||
|  | from . import utils | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def escapePath(word: str) -> str: | def escapePath(word: str) -> str: | ||||||
|     return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:') |     return word.replace("$ ", "$$ ").replace(" ", "$ ").replace(":", "$:") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| VarValue = Union[int, str, list[str], None] | VarValue = Union[int, str, list[str], None] | ||||||
|  | @ -42,92 +42,98 @@ class Writer(object): | ||||||
|         self.width = width |         self.width = width | ||||||
| 
 | 
 | ||||||
|     def newline(self) -> None: |     def newline(self) -> None: | ||||||
|         self.output.write('\n') |         self.output.write("\n") | ||||||
| 
 | 
 | ||||||
|     def comment(self, text: str) -> None: |     def comment(self, text: str) -> None: | ||||||
|         for line in textwrap.wrap(text, self.width - 2, break_long_words=False, |         for line in textwrap.wrap( | ||||||
|                                   break_on_hyphens=False): |             text, self.width - 2, break_long_words=False, break_on_hyphens=False | ||||||
|             self.output.write('# ' + line + '\n') |         ): | ||||||
|  |             self.output.write("# " + line + "\n") | ||||||
| 
 | 
 | ||||||
|     def separator(self, text : str) -> None: |     def separator(self, text: str) -> None: | ||||||
|         self.output.write(f"# --- {text} ---" + '-' * |         self.output.write( | ||||||
|                           (self.width - 10 - len(text)) + " #\n\n") |             f"# --- {text} ---" + "-" * (self.width - 10 - len(text)) + " #\n\n" | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     def variable(self, key: str, value: VarValue, indent: int = 0) -> None: |     def variable(self, key: str, value: VarValue, indent: int = 0) -> None: | ||||||
|         if value is None: |         if value is None: | ||||||
|             return |             return | ||||||
|         if isinstance(value, list): |         if isinstance(value, list): | ||||||
|             value = ' '.join(filter(None, value))  # Filter out empty strings. |             value = " ".join(filter(None, value))  # Filter out empty strings. | ||||||
|         self._line('%s = %s' % (key, value), indent) |         self._line("%s = %s" % (key, value), indent) | ||||||
| 
 | 
 | ||||||
|     def pool(self, name: str, depth: int) -> None: |     def pool(self, name: str, depth: int) -> None: | ||||||
|         self._line('pool %s' % name) |         self._line("pool %s" % name) | ||||||
|         self.variable('depth', depth, indent=1) |         self.variable("depth", depth, indent=1) | ||||||
| 
 | 
 | ||||||
|     def rule(self, |     def rule( | ||||||
|              name: str, |         self, | ||||||
|              command: VarValue, |         name: str, | ||||||
|              description: Union[str, None] = None, |         command: VarValue, | ||||||
|              depfile: VarValue = None, |         description: Union[str, None] = None, | ||||||
|              generator: VarValue = False, |         depfile: VarValue = None, | ||||||
|              pool: VarValue = None, |         generator: VarValue = False, | ||||||
|              restat: bool = False, |         pool: VarValue = None, | ||||||
|              rspfile: VarValue = None, |         restat: bool = False, | ||||||
|              rspfile_content: VarValue = None, |         rspfile: VarValue = None, | ||||||
|              deps: VarValue = None) -> None: |         rspfile_content: VarValue = None, | ||||||
|         self._line('rule %s' % name) |         deps: VarValue = None, | ||||||
|         self.variable('command', command, indent=1) |     ) -> None: | ||||||
|  |         self._line("rule %s" % name) | ||||||
|  |         self.variable("command", command, indent=1) | ||||||
|         if description: |         if description: | ||||||
|             self.variable('description', description, indent=1) |             self.variable("description", description, indent=1) | ||||||
|         if depfile: |         if depfile: | ||||||
|             self.variable('depfile', depfile, indent=1) |             self.variable("depfile", depfile, indent=1) | ||||||
|         if generator: |         if generator: | ||||||
|             self.variable('generator', '1', indent=1) |             self.variable("generator", "1", indent=1) | ||||||
|         if pool: |         if pool: | ||||||
|             self.variable('pool', pool, indent=1) |             self.variable("pool", pool, indent=1) | ||||||
|         if restat: |         if restat: | ||||||
|             self.variable('restat', '1', indent=1) |             self.variable("restat", "1", indent=1) | ||||||
|         if rspfile: |         if rspfile: | ||||||
|             self.variable('rspfile', rspfile, indent=1) |             self.variable("rspfile", rspfile, indent=1) | ||||||
|         if rspfile_content: |         if rspfile_content: | ||||||
|             self.variable('rspfile_content', rspfile_content, indent=1) |             self.variable("rspfile_content", rspfile_content, indent=1) | ||||||
|         if deps: |         if deps: | ||||||
|             self.variable('deps', deps, indent=1) |             self.variable("deps", deps, indent=1) | ||||||
| 
 | 
 | ||||||
|     def build(self, |     def build( | ||||||
|               outputs: Union[str, list[str]], |         self, | ||||||
|               rule: str, |         outputs: Union[str, list[str]], | ||||||
|               inputs: Union[VarPath, None], |         rule: str, | ||||||
|               implicit: VarPath = None, |         inputs: Union[VarPath, None], | ||||||
|               order_only: VarPath = None, |         implicit: VarPath = None, | ||||||
|               variables: Union[dict[str, str], None] = None, |         order_only: VarPath = None, | ||||||
|               implicit_outputs: VarPath = None, |         variables: Union[dict[str, str], None] = None, | ||||||
|               pool: Union[str, None] = None, |         implicit_outputs: VarPath = None, | ||||||
|               dyndep: Union[str, None] = None) -> list[str]: |         pool: Union[str, None] = None, | ||||||
|         outputs = asList(outputs) |         dyndep: Union[str, None] = None, | ||||||
|  |     ) -> list[str]: | ||||||
|  |         outputs = utils.asList(outputs) | ||||||
|         out_outputs = [escapePath(x) for x in outputs] |         out_outputs = [escapePath(x) for x in outputs] | ||||||
|         all_inputs = [escapePath(x) for x in asList(inputs)] |         all_inputs = [escapePath(x) for x in utils.asList(inputs)] | ||||||
| 
 | 
 | ||||||
|         if implicit: |         if implicit: | ||||||
|             implicit = [escapePath(x) for x in asList(implicit)] |             implicit = [escapePath(x) for x in utils.asList(implicit)] | ||||||
|             all_inputs.append('|') |             all_inputs.append("|") | ||||||
|             all_inputs.extend(implicit) |             all_inputs.extend(implicit) | ||||||
|         if order_only: |         if order_only: | ||||||
|             order_only = [escapePath(x) for x in asList(order_only)] |             order_only = [escapePath(x) for x in utils.asList(order_only)] | ||||||
|             all_inputs.append('||') |             all_inputs.append("||") | ||||||
|             all_inputs.extend(order_only) |             all_inputs.extend(order_only) | ||||||
|         if implicit_outputs: |         if implicit_outputs: | ||||||
|             implicit_outputs = [escapePath(x) |             implicit_outputs = [escapePath(x) for x in utils.asList(implicit_outputs)] | ||||||
|                                 for x in asList(implicit_outputs)] |             out_outputs.append("|") | ||||||
|             out_outputs.append('|') |  | ||||||
|             out_outputs.extend(implicit_outputs) |             out_outputs.extend(implicit_outputs) | ||||||
| 
 | 
 | ||||||
|         self._line('build %s: %s' % (' '.join(out_outputs), |         self._line( | ||||||
|                                      ' '.join([rule] + all_inputs))) |             "build %s: %s" % (" ".join(out_outputs), " ".join([rule] + all_inputs)) | ||||||
|  |         ) | ||||||
|         if pool is not None: |         if pool is not None: | ||||||
|             self._line('  pool = %s' % pool) |             self._line("  pool = %s" % pool) | ||||||
|         if dyndep is not None: |         if dyndep is not None: | ||||||
|             self._line('  dyndep = %s' % dyndep) |             self._line("  dyndep = %s" % dyndep) | ||||||
| 
 | 
 | ||||||
|         if variables: |         if variables: | ||||||
|             iterator = iter(variables.items()) |             iterator = iter(variables.items()) | ||||||
|  | @ -138,58 +144,59 @@ class Writer(object): | ||||||
|         return outputs |         return outputs | ||||||
| 
 | 
 | ||||||
|     def include(self, path: str) -> None: |     def include(self, path: str) -> None: | ||||||
|         self._line('include %s' % path) |         self._line("include %s" % path) | ||||||
| 
 | 
 | ||||||
|     def subninja(self, path: str) -> None: |     def subninja(self, path: str) -> None: | ||||||
|         self._line('subninja %s' % path) |         self._line("subninja %s" % path) | ||||||
| 
 | 
 | ||||||
|     def default(self, paths: VarPath) -> None: |     def default(self, paths: VarPath) -> None: | ||||||
|         self._line('default %s' % ' '.join(asList(paths))) |         self._line("default %s" % " ".join(utils.asList(paths))) | ||||||
| 
 | 
 | ||||||
|     def _count_dollars_before_index(self, s: str, i: int) -> int: |     def _count_dollars_before_index(self, s: str, i: int) -> int: | ||||||
|         """Returns the number of '$' characters right in front of s[i].""" |         """Returns the number of '$' characters right in front of s[i].""" | ||||||
|         dollar_count = 0 |         dollar_count = 0 | ||||||
|         dollar_index = i - 1 |         dollar_index = i - 1 | ||||||
|         while dollar_index > 0 and s[dollar_index] == '$': |         while dollar_index > 0 and s[dollar_index] == "$": | ||||||
|             dollar_count += 1 |             dollar_count += 1 | ||||||
|             dollar_index -= 1 |             dollar_index -= 1 | ||||||
|         return dollar_count |         return dollar_count | ||||||
| 
 | 
 | ||||||
|     def _line(self, text: str, indent: int = 0) -> None: |     def _line(self, text: str, indent: int = 0) -> None: | ||||||
|         """Write 'text' word-wrapped at self.width characters.""" |         """Write 'text' word-wrapped at self.width characters.""" | ||||||
|         leading_space = '  ' * indent |         leading_space = "  " * indent | ||||||
|         while len(leading_space) + len(text) > self.width: |         while len(leading_space) + len(text) > self.width: | ||||||
|             # The text is too wide; wrap if possible. |             # The text is too wide; wrap if possible. | ||||||
| 
 | 
 | ||||||
|             # Find the rightmost space that would obey our width constraint and |             # Find the rightmost space that would obey our width constraint and | ||||||
|             # that's not an escaped space. |             # that's not an escaped space. | ||||||
|             available_space = self.width - len(leading_space) - len(' $') |             available_space = self.width - len(leading_space) - len(" $") | ||||||
|             space = available_space |             space = available_space | ||||||
|             while True: |             while True: | ||||||
|                 space = text.rfind(' ', 0, space) |                 space = text.rfind(" ", 0, space) | ||||||
|                 if (space < 0 or |                 if space < 0 or self._count_dollars_before_index(text, space) % 2 == 0: | ||||||
|                         self._count_dollars_before_index(text, space) % 2 == 0): |  | ||||||
|                     break |                     break | ||||||
| 
 | 
 | ||||||
|             if space < 0: |             if space < 0: | ||||||
|                 # No such space; just use the first unescaped space we can find. |                 # No such space; just use the first unescaped space we can find. | ||||||
|                 space = available_space - 1 |                 space = available_space - 1 | ||||||
|                 while True: |                 while True: | ||||||
|                     space = text.find(' ', space + 1) |                     space = text.find(" ", space + 1) | ||||||
|                     if (space < 0 or |                     if ( | ||||||
|                             self._count_dollars_before_index(text, space) % 2 == 0): |                         space < 0 | ||||||
|  |                         or self._count_dollars_before_index(text, space) % 2 == 0 | ||||||
|  |                     ): | ||||||
|                         break |                         break | ||||||
|             if space < 0: |             if space < 0: | ||||||
|                 # Give up on breaking. |                 # Give up on breaking. | ||||||
|                 break |                 break | ||||||
| 
 | 
 | ||||||
|             self.output.write(leading_space + text[0:space] + ' $\n') |             self.output.write(leading_space + text[0:space] + " $\n") | ||||||
|             text = text[space+1:] |             text = text[space + 1 :] | ||||||
| 
 | 
 | ||||||
|             # Subsequent lines are continuations, so indent them. |             # Subsequent lines are continuations, so indent them. | ||||||
|             leading_space = '  ' * (indent+2) |             leading_space = "  " * (indent + 2) | ||||||
| 
 | 
 | ||||||
|         self.output.write(leading_space + text + '\n') |         self.output.write(leading_space + text + "\n") | ||||||
| 
 | 
 | ||||||
|     def close(self) -> None: |     def close(self) -> None: | ||||||
|         self.output.close() |         self.output.close() | ||||||
|  | @ -198,6 +205,6 @@ class Writer(object): | ||||||
| def escape(string: str) -> str: | def escape(string: str) -> str: | ||||||
|     """Escape a string such that it can be embedded into a Ninja file without |     """Escape a string such that it can be embedded into a Ninja file without | ||||||
|     further interpretation.""" |     further interpretation.""" | ||||||
|     assert '\n' not in string, 'Ninja syntax does not allow newlines' |     assert "\n" not in string, "Ninja syntax does not allow newlines" | ||||||
|     # We only have one special metacharacter: '$'. |     # We only have one special metacharacter: '$'. | ||||||
|     return string.replace('$', '$$') |     return string.replace("$", "$$") | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import os | import os | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from cutekit import shell, project, const, context | from . import shell, model, const, context | ||||||
| 
 | 
 | ||||||
| import importlib.util as importlib | import importlib.util as importlib | ||||||
| 
 | 
 | ||||||
|  | @ -23,7 +23,7 @@ def load(path: str): | ||||||
| def loadAll(): | def loadAll(): | ||||||
|     _logger.info("Loading plugins...") |     _logger.info("Loading plugins...") | ||||||
| 
 | 
 | ||||||
|     projectRoot = project.root() |     projectRoot = model.Project.root() | ||||||
| 
 | 
 | ||||||
|     if projectRoot is None: |     if projectRoot is None: | ||||||
|         _logger.info("Not in project, skipping plugin loading") |         _logger.info("Not in project, skipping plugin loading") | ||||||
|  |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| import os |  | ||||||
| 
 |  | ||||||
| from pathlib import Path |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def root() -> str | None: |  | ||||||
|     cwd = Path.cwd() |  | ||||||
|     while str(cwd) != cwd.root: |  | ||||||
|         if (cwd / "project.json").is_file(): |  | ||||||
|             return str(cwd) |  | ||||||
|         cwd = cwd.parent |  | ||||||
|     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) |  | ||||||
|  | @ -13,7 +13,7 @@ import tempfile | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| from typing import Optional | from typing import Optional | ||||||
| from cutekit import const | from . import const | ||||||
| 
 | 
 | ||||||
| _logger = logging.getLogger(__name__) | _logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| 
 |  | ||||||
| # Extending cutekit | # Extending cutekit | ||||||
| 
 | 
 | ||||||
| By writing custom Python plugins, you can extend Cutekit to do whatever you want. | By writing custom Python plugins, you can extend Cutekit to do whatever you want. | ||||||
|  | @ -9,24 +8,11 @@ Then you can import cutekit and change/add whatever you want. | ||||||
| For example you can add a new command to the CLI: | For example you can add a new command to the CLI: | ||||||
| 
 | 
 | ||||||
| ```python | ```python | ||||||
| import os | from cutekit import cli | ||||||
| import json |  | ||||||
| import magic |  | ||||||
| import logging |  | ||||||
| from pathlib import Path |  | ||||||
| 
 | 
 | ||||||
| 
 | @cli.command("h", "hello", "Print hello world") | ||||||
| from cutekit import shell, builder, const, project | def bootCmd(args: cli.Args) -> None: | ||||||
| from cutekit.cmds import Cmd, append |  | ||||||
| from cutekit.args import Args |  | ||||||
| from typing import Callable |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def bootCmd(args: Args) -> None: |  | ||||||
|     project.chdir() |  | ||||||
|     print("Hello world!") |     print("Hello world!") | ||||||
| 
 |  | ||||||
| append(Cmd("h", "hello", "Print hello world", bootCmd)) |  | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| This feature is used - for example - by [SkiftOS](https://github.com/skift-org/skift/blob/main/meta/plugins/start-cmd.py) to add the `start` command, that build packages and run a virtual machine. | This feature is used - for example - by [SkiftOS](https://github.com/skift-org/skift/blob/main/meta/plugins/start-cmd.py) to add the `start` command, that build packages and run a virtual machine. | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue