88 lines
3.3 KiB
Python
88 lines
3.3 KiB
Python
import os
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
import yaml
|
|
|
|
|
|
@dataclass
|
|
class PdkAsset:
|
|
component: str
|
|
yaml_path: Optional[str] = None
|
|
gds_path: Optional[str] = None
|
|
metadata: Optional[dict] = None
|
|
|
|
|
|
class PdkRegistry:
|
|
"""Resolve public PDK component names to metadata and public GDS assets."""
|
|
|
|
def __init__(self, public_root: str):
|
|
self.public_root = os.path.abspath(public_root)
|
|
self._asset_cache = {}
|
|
|
|
def resolve(self, component: str) -> PdkAsset:
|
|
key = (component or "").strip().replace("\\", "/").strip("/")
|
|
if not key:
|
|
return PdkAsset(component=component)
|
|
if key in self._asset_cache:
|
|
return self._asset_cache[key]
|
|
|
|
yaml_path = self._find_yaml(key)
|
|
gds_path = self._find_gds(key, yaml_path)
|
|
metadata = self._load_yaml(yaml_path) if yaml_path else None
|
|
asset = PdkAsset(component=component, yaml_path=yaml_path, gds_path=gds_path, metadata=metadata)
|
|
self._asset_cache[key] = asset
|
|
return asset
|
|
|
|
def _find_yaml(self, key: str) -> Optional[str]:
|
|
direct = os.path.join(self.public_root, *key.split("/"))
|
|
candidates = []
|
|
if direct.lower().endswith((".yml", ".yaml")):
|
|
candidates.append(direct)
|
|
else:
|
|
name = key.split("/")[-1]
|
|
candidates.append(os.path.join(direct, f"{name}.yml"))
|
|
candidates.append(os.path.join(direct, f"{name}.yaml"))
|
|
|
|
for candidate in candidates:
|
|
if self._inside_root(candidate) and os.path.exists(candidate):
|
|
return os.path.abspath(candidate)
|
|
|
|
name = key.split("/")[-1]
|
|
for root, dirs, files in os.walk(self.public_root):
|
|
if os.path.basename(root) == name:
|
|
for filename in files:
|
|
if filename.lower().endswith((".yml", ".yaml")):
|
|
return os.path.join(root, filename)
|
|
dirs.clear()
|
|
return None
|
|
|
|
def _find_gds(self, key: str, yaml_path: Optional[str]) -> Optional[str]:
|
|
search_dir = os.path.dirname(yaml_path) if yaml_path else os.path.join(self.public_root, *key.split("/"))
|
|
name = key.split("/")[-1]
|
|
candidates = [
|
|
os.path.join(search_dir, f"{name}_BB.gds"),
|
|
os.path.join(search_dir, f"{name}.gds"),
|
|
]
|
|
for candidate in candidates:
|
|
if self._inside_root(candidate) and os.path.exists(candidate):
|
|
return os.path.abspath(candidate)
|
|
if os.path.isdir(search_dir):
|
|
for filename in sorted(os.listdir(search_dir)):
|
|
if filename.lower().endswith("_bb.gds"):
|
|
return os.path.join(search_dir, filename)
|
|
for filename in sorted(os.listdir(search_dir)):
|
|
if filename.lower().endswith(".gds"):
|
|
return os.path.join(search_dir, filename)
|
|
return None
|
|
|
|
def _load_yaml(self, yaml_path: Optional[str]) -> Optional[dict]:
|
|
if not yaml_path:
|
|
return None
|
|
with open(yaml_path, "r", encoding="utf-8") as file:
|
|
return yaml.safe_load(file) or {}
|
|
|
|
def _inside_root(self, path: str) -> bool:
|
|
target = os.path.abspath(path)
|
|
return target == self.public_root or target.startswith(self.public_root + os.sep)
|