Use dataclasses for the model
This commit is contained in:
		
							parent
							
								
									b1415cce16
								
							
						
					
					
						commit
						8f4d19c98e
					
				
					 6 changed files with 112 additions and 250 deletions
				
			
		|  | @ -1,3 +1,4 @@ | ||||||
|  | from pathlib import Path | ||||||
| from typing import Any | from typing import Any | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -5,10 +6,11 @@ SUPPORTED_MANIFEST = [ | ||||||
|     "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1", |     "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.project.v1", | ||||||
|     "https://schemas.cute.engineering/stable/cutekit.manifest.target.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" | OSDK_MANIFEST_NOT_SUPPORTED = ( | ||||||
|  |     "OSDK manifests are not supported by CuteKit. Please use CuteKit manifest instead" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| UNSUPORTED_MANIFEST = { | UNSUPORTED_MANIFEST = { | ||||||
|     "https://schemas.cute.engineering/stable/osdk.manifest.component.v1": OSDK_MANIFEST_NOT_SUPPORTED, |     "https://schemas.cute.engineering/stable/osdk.manifest.component.v1": OSDK_MANIFEST_NOT_SUPPORTED, | ||||||
|  | @ -20,14 +22,16 @@ UNSUPORTED_MANIFEST = { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def ensureSupportedManifest(manifest: Any, path: str): | def ensureSupportedManifest(manifest: Any, path: Path): | ||||||
|     if "$schema" not in manifest: |     if "$schema" not in manifest: | ||||||
|         raise RuntimeError(f"Missing $schema in {path}") |         raise RuntimeError(f"Missing $schema in {path}") | ||||||
| 
 | 
 | ||||||
|     if manifest["$schema"] in UNSUPORTED_MANIFEST: |     if manifest["$schema"] in UNSUPORTED_MANIFEST: | ||||||
|         raise RuntimeError( |         raise RuntimeError( | ||||||
|             f"Unsupported manifest schema {manifest['$schema']} in {path}: {UNSUPORTED_MANIFEST[manifest['$schema']]}") |             f"Unsupported manifest schema {manifest['$schema']} in {path}: {UNSUPORTED_MANIFEST[manifest['$schema']]}" | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     if manifest["$schema"] not in SUPPORTED_MANIFEST: |     if manifest["$schema"] not in SUPPORTED_MANIFEST: | ||||||
|         raise RuntimeError( |         raise RuntimeError( | ||||||
|             f"Unsupported manifest schema {manifest['$schema']} in {path}") |             f"Unsupported manifest schema {manifest['$schema']} in {path}" | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| 
 | 
 | ||||||
| VERSION = (0, 5, 4) | VERSION = (0, 6, 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__)) | ||||||
| ARGV0 = os.path.basename(sys.argv[0]) | ARGV0 = os.path.basename(sys.argv[0]) | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ class ComponentInstance: | ||||||
|         return self.manifest.id |         return self.manifest.id | ||||||
| 
 | 
 | ||||||
|     def isLib(self): |     def isLib(self): | ||||||
|         return self.manifest.type == model.Type.LIB |         return self.manifest.type == model.Kind.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") | ||||||
|  | @ -88,7 +88,7 @@ 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 == model.Type.LIB: |         elif self.manifest.type == model.Kind.LIB: | ||||||
|             return str(Path(self.manifest.dirname()).parent) |             return str(Path(self.manifest.dirname()).parent) | ||||||
|         else: |         else: | ||||||
|             return "" |             return "" | ||||||
|  | @ -131,7 +131,7 @@ class Context(IContext): | ||||||
| 
 | 
 | ||||||
|     def hashid(self) -> str: |     def hashid(self) -> str: | ||||||
|         return utils.hash( |         return utils.hash( | ||||||
|             (self.target.props, [self.tools[t].toJson() for t in self.tools]) |             (self.target.props, [self.tools[t].to_dict() for t in self.tools]) | ||||||
|         )[0:8] |         )[0:8] | ||||||
| 
 | 
 | ||||||
|     def builddir(self) -> str: |     def builddir(self) -> str: | ||||||
|  | @ -154,14 +154,19 @@ def loadAllTargets() -> list[model.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: model.Target(jexpr.evalRead(path), path), files)) |         ret += list( | ||||||
|  |             map( | ||||||
|  |                 lambda path: model.Manifest.load(Path(path)).ensureType(model.Target), | ||||||
|  |                 files, | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     return ret |     return ret | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def loadProject(path: str) -> model.Project: | def loadProject(path: str) -> model.Project: | ||||||
|     path = os.path.join(path, "project.json") |     path = os.path.join(path, "project.json") | ||||||
|     return model.Project(jexpr.evalRead(path), path) |     return model.Manifest.load(Path(path)).ensureType(model.Project) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def loadTarget(id: str) -> model.Target: | def loadTarget(id: str) -> model.Target: | ||||||
|  | @ -175,7 +180,12 @@ 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: model.Component(jexpr.evalRead(path), path), files)) |     return list( | ||||||
|  |         map( | ||||||
|  |             lambda path: model.Manifest.load(Path(path)).ensureType(model.Component), | ||||||
|  |             files, | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def filterDisabled( | def filterDisabled( | ||||||
|  | @ -242,7 +252,7 @@ def resolveDeps( | ||||||
| 
 | 
 | ||||||
|     enabled, unresolvedReason, resolved = resolveInner(componentSpec) |     enabled, unresolvedReason, resolved = resolveInner(componentSpec) | ||||||
| 
 | 
 | ||||||
|     return enabled, unresolvedReason, resolved |     return enabled, unresolvedReason, utils.uniq(resolved) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def instanciate( | def instanciate( | ||||||
|  | @ -250,7 +260,10 @@ def instanciate( | ||||||
| ) -> 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()))) | ||||||
|     sources = shell.find(manifest.subdirs, list(wildcards), recusive=False) |     dirs = [manifest.dirname()] + list( | ||||||
|  |         map(lambda d: os.path.join(manifest.dirname(), d), manifest.subdirs) | ||||||
|  |     ) | ||||||
|  |     sources = shell.find(dirs, list(wildcards), recusive=False) | ||||||
| 
 | 
 | ||||||
|     res = shell.find(os.path.join(manifest.dirname(), "res")) |     res = shell.find(os.path.join(manifest.dirname(), "res")) | ||||||
| 
 | 
 | ||||||
|  | @ -299,9 +312,7 @@ def contextFor(targetSpec: str, props: model.Props = {}) -> Context: | ||||||
|     for toolSpec in target.tools: |     for toolSpec in target.tools: | ||||||
|         tool = target.tools[toolSpec] |         tool = target.tools[toolSpec] | ||||||
| 
 | 
 | ||||||
|         tools[toolSpec] = model.Tool( |         tools[toolSpec] = model.Tool(cmd=tool.cmd, args=tool.args, files=tool.files) | ||||||
|             strict=False, cmd=tool.cmd, args=tool.args, files=tool.files |  | ||||||
|         ) |  | ||||||
| 
 | 
 | ||||||
|         tools[toolSpec].args += rules.rules[toolSpec].args |         tools[toolSpec].args += rules.rules[toolSpec].args | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| import os | import os | ||||||
| import json | import json | ||||||
|  | from pathlib import Path | ||||||
| 
 | 
 | ||||||
| from typing import Any, cast, Callable, Final | from typing import Any, cast, Callable, Final | ||||||
| from . import shell, compat | from . import shell, compat | ||||||
|  | @ -9,8 +10,8 @@ Builtin = Callable[..., Json] | ||||||
| 
 | 
 | ||||||
| BUILTINS: Final[dict[str, Builtin]] = { | BUILTINS: Final[dict[str, Builtin]] = { | ||||||
|     "uname": lambda arg, ctx: getattr(shell.uname(), arg).lower(), |     "uname": lambda arg, ctx: getattr(shell.uname(), arg).lower(), | ||||||
|     "include": lambda arg, ctx: evalRead(arg, compatibilityCheck=False), |     "include": lambda arg, ctx: evalRead(arg), | ||||||
|     "evalRead": lambda arg, ctx: evalRead(arg, compatibilityCheck=False), |     "evalRead": lambda arg, ctx: evalRead(arg), | ||||||
|     "join": lambda lhs, rhs, ctx: cast( |     "join": lambda lhs, rhs, ctx: cast( | ||||||
|         Json, {**lhs, **rhs} if isinstance(lhs, dict) else lhs + rhs |         Json, {**lhs, **rhs} if isinstance(lhs, dict) else lhs + rhs | ||||||
|     ), |     ), | ||||||
|  | @ -27,7 +28,7 @@ BUILTINS: Final[dict[str, Builtin]] = { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def eval(jexpr: Json, filePath: str) -> Json: | def eval(jexpr: Json, filePath: Path) -> Json: | ||||||
|     if isinstance(jexpr, dict): |     if isinstance(jexpr, dict): | ||||||
|         result = {} |         result = {} | ||||||
|         for k in cast(dict[str, Json], jexpr): |         for k in cast(dict[str, Json], jexpr): | ||||||
|  | @ -49,7 +50,7 @@ def eval(jexpr: Json, filePath: str) -> Json: | ||||||
|         return jexpr |         return jexpr | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def read(path: str) -> Json: | def read(path: Path) -> Json: | ||||||
|     try: |     try: | ||||||
|         with open(path, "r") as f: |         with open(path, "r") as f: | ||||||
|             return json.load(f) |             return json.load(f) | ||||||
|  | @ -57,8 +58,6 @@ def read(path: str) -> Json: | ||||||
|         raise RuntimeError(f"Failed to read {path}") |         raise RuntimeError(f"Failed to read {path}") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def evalRead(path: str, compatibilityCheck: bool = True) -> Json: | def evalRead(path: Path) -> Json: | ||||||
|     data = read(path) |     data = read(path) | ||||||
|     if compatibilityCheck: |  | ||||||
|         compat.ensureSupportedManifest(data, path) |  | ||||||
|     return eval(data, path) |     return eval(data, path) | ||||||
|  |  | ||||||
							
								
								
									
										284
									
								
								cutekit/model.py
									
										
									
									
									
								
							
							
						
						
									
										284
									
								
								cutekit/model.py
									
										
									
									
									
								
							|  | @ -1,18 +1,21 @@ | ||||||
| import os | import os | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from enum import Enum |  | ||||||
| from typing import Any |  | ||||||
| from pathlib import Path |  | ||||||
| from . import jexpr |  | ||||||
| 
 | 
 | ||||||
|  | from enum import Enum | ||||||
|  | from typing import Any, Type, cast | ||||||
|  | from pathlib import Path | ||||||
|  | from dataclasses_json import DataClassJsonMixin, config | ||||||
|  | from dataclasses import dataclass, field | ||||||
|  | 
 | ||||||
|  | from . import jexpr, compat, utils | ||||||
| 
 | 
 | ||||||
| _logger = logging.getLogger(__name__) | _logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| Props = dict[str, Any] | Props = dict[str, Any] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Type(Enum): | class Kind(Enum): | ||||||
|     UNKNOWN = "unknown" |     UNKNOWN = "unknown" | ||||||
|     PROJECT = "project" |     PROJECT = "project" | ||||||
|     TARGET = "target" |     TARGET = "target" | ||||||
|  | @ -20,115 +23,46 @@ class Type(Enum): | ||||||
|     EXE = "exe" |     EXE = "exe" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Manifest: | @dataclass | ||||||
|     id: str = "" | class Manifest(DataClassJsonMixin): | ||||||
|     type: Type = Type.UNKNOWN |     id: str | ||||||
|     path: str = "" |     type: Kind = field(default=Kind.UNKNOWN) | ||||||
|  |     path: str = field(default="") | ||||||
| 
 | 
 | ||||||
|     def __init__( |     @staticmethod | ||||||
|         self, |     def parse(path: Path, data: dict[str, Any]) -> "Manifest": | ||||||
|         json: jexpr.Json = None, |         compat.ensureSupportedManifest(data, path) | ||||||
|         path: str = "", |         kind = Kind(data["type"]) | ||||||
|         strict: bool = True, |         del data["$schema"] | ||||||
|         **kwargs: Any, |         obj = KINDS[kind].from_dict(data) | ||||||
|     ): |         obj.path = str(path) | ||||||
|         if json is not None: |         return obj | ||||||
|             if "id" not in json: |  | ||||||
|                 raise RuntimeError("Missing id") |  | ||||||
| 
 | 
 | ||||||
|             self.id = json["id"] |     @staticmethod | ||||||
| 
 |     def load(path: Path) -> "Manifest": | ||||||
|             if "type" not in json and strict: |         return Manifest.parse(path, jexpr.evalRead(path)) | ||||||
|                 raise RuntimeError("Missing type") |  | ||||||
| 
 |  | ||||||
|             self.type = Type(json["type"]) |  | ||||||
| 
 |  | ||||||
|             self.path = path |  | ||||||
|         elif strict: |  | ||||||
|             raise RuntimeError("Missing json") |  | ||||||
| 
 |  | ||||||
|         for key in kwargs: |  | ||||||
|             setattr(self, key, kwargs[key]) |  | ||||||
| 
 |  | ||||||
|     def toJson(self) -> jexpr.Json: |  | ||||||
|         return {"id": self.id, "type": self.type.value, "path": self.path} |  | ||||||
| 
 |  | ||||||
|     def __str__(self): |  | ||||||
|         return f"Manifest(id={self.id}, type={self.type}, path={self.path})" |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f"Manifest({id})" |  | ||||||
| 
 | 
 | ||||||
|     def dirname(self) -> str: |     def dirname(self) -> str: | ||||||
|         return os.path.dirname(self.path) |         return os.path.dirname(self.path) | ||||||
| 
 | 
 | ||||||
| 
 |     def ensureType(self, t: Type[utils.T]) -> utils.T: | ||||||
| class Extern: |         if not isinstance(self, t): | ||||||
|     git: str = "" |             raise RuntimeError( | ||||||
|     tag: str = "" |                 f"{self.path} should be a {type.__name__} manifest but is a {self.__class__.__name__} manifest" | ||||||
| 
 |             ) | ||||||
|     def __init__(self, json: jexpr.Json = None, strict: bool = True, **kwargs: Any): |         return cast(utils.T, self) | ||||||
|         if json is not None: |  | ||||||
|             if "git" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing git") |  | ||||||
| 
 |  | ||||||
|             self.git = json["git"] |  | ||||||
| 
 |  | ||||||
|             if "tag" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing tag") |  | ||||||
| 
 |  | ||||||
|             self.tag = json["tag"] |  | ||||||
|         elif strict: |  | ||||||
|             raise RuntimeError("Missing json") |  | ||||||
| 
 |  | ||||||
|         for key in kwargs: |  | ||||||
|             setattr(self, key, kwargs[key]) |  | ||||||
| 
 |  | ||||||
|     def toJson(self) -> jexpr.Json: |  | ||||||
|         return {"git": self.git, "tag": self.tag} |  | ||||||
| 
 |  | ||||||
|     def __str__(self): |  | ||||||
|         return f"Extern(git={self.git}, tag={self.tag})" |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f"Extern({self.git})" |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @dataclass | ||||||
|  | class Extern(DataClassJsonMixin): | ||||||
|  |     git: str | ||||||
|  |     tag: str | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @dataclass | ||||||
| class Project(Manifest): | class Project(Manifest): | ||||||
|     description: str = "" |     description: str = field(default="(No description)") | ||||||
|     extern: dict[str, Extern] = {} |     extern: dict[str, Extern] = field(default_factory=dict) | ||||||
| 
 |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         json: jexpr.Json = None, |  | ||||||
|         path: str = "", |  | ||||||
|         strict: bool = True, |  | ||||||
|         **kwargs: Any, |  | ||||||
|     ): |  | ||||||
|         if json is not None: |  | ||||||
|             if "description" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing description") |  | ||||||
| 
 |  | ||||||
|             self.description = json["description"] |  | ||||||
| 
 |  | ||||||
|             self.extern = {k: Extern(v) for k, v in json.get("extern", {}).items()} |  | ||||||
|         elif strict: |  | ||||||
|             raise RuntimeError("Missing json") |  | ||||||
| 
 |  | ||||||
|         super().__init__(json, path, strict, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def toJson(self) -> jexpr.Json: |  | ||||||
|         return { |  | ||||||
|             **super().toJson(), |  | ||||||
|             "description": self.description, |  | ||||||
|             "extern": {k: v.toJson() for k, v in self.extern.items()}, |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def __str__(self): |  | ||||||
|         return f"ProjectManifest(id={self.id}, type={self.type}, path={self.path}, description={self.description}, extern={self.extern})" |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f"ProjectManifest({self.id})" |  | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def root() -> str | None: |     def root() -> str | None: | ||||||
|  | @ -149,80 +83,21 @@ class Project(Manifest): | ||||||
|         os.chdir(path) |         os.chdir(path) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Tool: | @dataclass | ||||||
|     cmd: str = "" | class Tool(DataClassJsonMixin): | ||||||
|     args: list[str] = [] |     cmd: str = field(default="") | ||||||
|     files: list[str] = [] |     args: list[str] = field(default_factory=list) | ||||||
| 
 |     files: list[str] = field(default_factory=list) | ||||||
|     def __init__(self, json: jexpr.Json = None, strict: bool = True, **kwargs: Any): |  | ||||||
|         if json is not None: |  | ||||||
|             if "cmd" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing cmd") |  | ||||||
| 
 |  | ||||||
|             self.cmd = json.get("cmd", self.cmd) |  | ||||||
| 
 |  | ||||||
|             if "args" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing args") |  | ||||||
| 
 |  | ||||||
|             self.args = json.get("args", []) |  | ||||||
| 
 |  | ||||||
|             self.files = json.get("files", []) |  | ||||||
|         elif strict: |  | ||||||
|             raise RuntimeError("Missing json") |  | ||||||
| 
 |  | ||||||
|         for key in kwargs: |  | ||||||
|             setattr(self, key, kwargs[key]) |  | ||||||
| 
 |  | ||||||
|     def toJson(self) -> jexpr.Json: |  | ||||||
|         return {"cmd": self.cmd, "args": self.args, "files": self.files} |  | ||||||
| 
 |  | ||||||
|     def __str__(self): |  | ||||||
|         return f"Tool(cmd={self.cmd}, args={self.args}, files={self.files})" |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f"Tool({self.cmd})" |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Tools = dict[str, Tool] | Tools = dict[str, Tool] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @dataclass | ||||||
| class Target(Manifest): | class Target(Manifest): | ||||||
|     props: Props |     props: Props = field(default_factory=dict) | ||||||
|     tools: Tools |     tools: Tools = field(default_factory=dict) | ||||||
|     routing: dict[str, str] |     routing: dict[str, str] = field(default_factory=dict) | ||||||
| 
 |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         json: jexpr.Json = None, |  | ||||||
|         path: str = "", |  | ||||||
|         strict: bool = True, |  | ||||||
|         **kwargs: Any, |  | ||||||
|     ): |  | ||||||
|         if json is not None: |  | ||||||
|             if "props" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing props") |  | ||||||
| 
 |  | ||||||
|             self.props = json["props"] |  | ||||||
| 
 |  | ||||||
|             if "tools" not in json and strict: |  | ||||||
|                 raise RuntimeError("Missing tools") |  | ||||||
| 
 |  | ||||||
|             self.tools = {k: Tool(v) for k, v in json["tools"].items()} |  | ||||||
| 
 |  | ||||||
|             self.routing = json.get("routing", {}) |  | ||||||
| 
 |  | ||||||
|         super().__init__(json, path, strict, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def toJson(self) -> jexpr.Json: |  | ||||||
|         return { |  | ||||||
|             **super().toJson(), |  | ||||||
|             "props": self.props, |  | ||||||
|             "tools": {k: v.toJson() for k, v in self.tools.items()}, |  | ||||||
|             "routing": self.routing, |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f"TargetManifest({self.id})" |  | ||||||
| 
 | 
 | ||||||
|     def route(self, componentSpec: str): |     def route(self, componentSpec: str): | ||||||
|         return ( |         return ( | ||||||
|  | @ -250,54 +125,15 @@ class Target(Manifest): | ||||||
|         return defines |         return defines | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @dataclass | ||||||
| class Component(Manifest): | class Component(Manifest): | ||||||
|     decription: str = "(No description)" |     decription: str = field(default="(No description)") | ||||||
|     props: Props = {} |     props: Props = field(default_factory=dict) | ||||||
|     tools: Tools = {} |     tools: Tools = field(default_factory=dict) | ||||||
|     enableIf: dict[str, list[Any]] = {} |     enableIf: dict[str, list[Any]] = field(default_factory=dict) | ||||||
|     requires: list[str] = [] |     requires: list[str] = field(default_factory=list) | ||||||
|     provides: list[str] = [] |     provides: list[str] = field(default_factory=list) | ||||||
|     subdirs: list[str] = [] |     subdirs: list[str] = field(default_factory=list) | ||||||
| 
 |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         json: jexpr.Json = None, |  | ||||||
|         path: str = "", |  | ||||||
|         strict: bool = True, |  | ||||||
|         **kwargs: Any, |  | ||||||
|     ): |  | ||||||
|         if json is not None: |  | ||||||
|             self.decription = json.get("description", self.decription) |  | ||||||
|             self.props = json.get("props", self.props) |  | ||||||
|             self.tools = { |  | ||||||
|                 k: Tool(v, strict=False) for k, v in json.get("tools", {}).items() |  | ||||||
|             } |  | ||||||
|             self.enableIf = json.get("enableIf", self.enableIf) |  | ||||||
|             self.requires = json.get("requires", self.requires) |  | ||||||
|             self.provides = json.get("provides", self.provides) |  | ||||||
|             self.subdirs = list( |  | ||||||
|                 map( |  | ||||||
|                     lambda x: os.path.join(os.path.dirname(path), x), |  | ||||||
|                     json.get("subdirs", [""]), |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|         super().__init__(json, path, strict, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def toJson(self) -> jexpr.Json: |  | ||||||
|         return { |  | ||||||
|             **super().toJson(), |  | ||||||
|             "description": self.decription, |  | ||||||
|             "props": self.props, |  | ||||||
|             "tools": {k: v.toJson() for k, v in self.tools.items()}, |  | ||||||
|             "enableIf": self.enableIf, |  | ||||||
|             "requires": self.requires, |  | ||||||
|             "provides": self.provides, |  | ||||||
|             "subdirs": self.subdirs, |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f"ComponentManifest({self.id})" |  | ||||||
| 
 | 
 | ||||||
|     def isEnabled(self, target: Target) -> tuple[bool, str]: |     def isEnabled(self, target: Target) -> tuple[bool, str]: | ||||||
|         for k, v in self.enableIf.items(): |         for k, v in self.enableIf.items(): | ||||||
|  | @ -316,3 +152,11 @@ class Component(Manifest): | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
|         return True, "" |         return True, "" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | KINDS: dict[Kind, Type[Manifest]] = { | ||||||
|  |     Kind.PROJECT: Project, | ||||||
|  |     Kind.TARGET: Target, | ||||||
|  |     Kind.LIB: Component, | ||||||
|  |     Kind.EXE: Component, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -3,11 +3,11 @@ from typing import Any, TypeVar, cast, Optional, Union | ||||||
| import json | import json | ||||||
| import hashlib | import hashlib | ||||||
| 
 | 
 | ||||||
| T = TypeVar('T') | T = TypeVar("T") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def uniq(l: list[str]) -> list[str]: | def uniq(l: list[T]) -> list[T]: | ||||||
|     result: list[str] = [] |     result: list[T] = [] | ||||||
|     for i in l: |     for i in l: | ||||||
|         if i in result: |         if i in result: | ||||||
|             result.remove(i) |             result.remove(i) | ||||||
|  | @ -15,7 +15,9 @@ def uniq(l: list[str]) -> list[str]: | ||||||
|     return result |     return result | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def hash(obj: Any, keys: list[str] = [], cls: Optional[type[json.JSONEncoder]] = None) -> str: | def hash( | ||||||
|  |     obj: Any, keys: list[str] = [], cls: Optional[type[json.JSONEncoder]] = None | ||||||
|  | ) -> str: | ||||||
|     toHash = {} |     toHash = {} | ||||||
|     if len(keys) == 0: |     if len(keys) == 0: | ||||||
|         toHash = obj |         toHash = obj | ||||||
|  | @ -28,7 +30,7 @@ def hash(obj: Any, keys: list[str] = [], cls: Optional[type[json.JSONEncoder]] = | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 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:] | ||||||
|     return s |     return s | ||||||
| 
 | 
 | ||||||
|  | @ -60,4 +62,6 @@ def asList(i: Optional[Union[T, list[T]]]) -> list[T]: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def isNewer(path1: str, path2: str) -> bool: | def isNewer(path1: str, path2: str) -> bool: | ||||||
|     return not os.path.exists(path2) or os.path.getmtime(path1) > os.path.getmtime(path2) |     return not os.path.exists(path2) or os.path.getmtime(path1) > os.path.getmtime( | ||||||
|  |         path2 | ||||||
|  |     ) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue