Coverage for klayout_pex/env.py: 95%
61 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-08 12:43 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-08 12:43 +0000
1#! /usr/bin/env python3
2#
3# --------------------------------------------------------------------------------
4# SPDX-FileCopyrightText: 2024-2025 Martin Jan Köhler and Harald Pretl
5# Johannes Kepler University, Institute for Integrated Circuits.
6#
7# This file is part of KPEX
8# (see https://github.com/iic-jku/klayout-pex).
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22# SPDX-License-Identifier: GPL-3.0-or-later
23# --------------------------------------------------------------------------------
24#
26from __future__ import annotations
28from enum import StrEnum
29import os
30from typing import *
32from .pdk_config import PDK
33from .log import warning
36class EnvVar(StrEnum):
37 FASTCAP_EXE = 'KPEX_FASTCAP_EXE'
38 FASTERCAP_EXE = 'KPEX_FASTERCAP_EXE'
39 KLAYOUT_EXE = 'KPEX_KLAYOUT_EXE'
40 MAGIC_EXE = 'KPEX_MAGIC_EXE'
41 PDK_ROOT = 'PDK_ROOT'
42 PDK = 'PDK'
44 @property
45 def default_value(self) -> Optional[str]:
46 match self:
47 case EnvVar.FASTCAP_EXE: return 'fastcap'
48 case EnvVar.FASTERCAP_EXE: return 'FasterCap'
49 case EnvVar.KLAYOUT_EXE:
50 return 'klayout_app' if os.name == 'nt' \
51 else 'klayout'
52 case EnvVar.MAGIC_EXE: return 'magic'
53 case EnvVar.PDK_ROOT: return None
54 case EnvVar.PDK: return None
55 case _: raise NotImplementedError(f"Unexpected env var '{self.name}'")
57 @classmethod
58 def help_epilog_table(cls) -> str:
59 return f"""
60| Variable | Description |
61| ------------------ | ----------------------------------------------------------------------------- |
62| KPEX_FASTCAP_EXE | Path to FastCap2 Executable. Defaults to '{cls.FASTCAP_EXE.default_value}' |
63| KPEX_FASTERCAP_EXE | Path to FasterCap Executable. Defaults to '{cls.FASTERCAP_EXE.default_value}' |
64| KPEX_KLAYOUT_EXE | Path to KLayout Executable. Defaults to '{cls.KLAYOUT_EXE.default_value}' |
65| KPEX_MAGIC_EXE | Path to MAGIC Executable. Defaults to '{cls.MAGIC_EXE.default_value}' |
66| PDK_ROOT | Optional (required for default magicrc), e.g. $HOME/.volare |
67| PDK | Optional (required for default magicrc), (e.g. sky130A) |
68"""
71class Env:
72 def __init__(self, env_dict: Dict[str, Optional[str]]):
73 self._data = env_dict
75 @classmethod
76 def from_os_environ(cls) -> Env:
77 d = {}
78 for env_var in EnvVar:
79 value = os.environ.get(env_var.value, None)
80 if value is None:
81 value = env_var.default_value
82 d[env_var] = value
83 return Env(d)
85 @property
86 def default_magicrc_path(self) -> Optional[str]:
87 PDK_ROOT = self[EnvVar.PDK_ROOT]
88 PDK = self[EnvVar.PDK]
89 default_magicrc_path = \
90 None if PDK_ROOT is None or PDK is None \
91 else os.path.abspath(f"{PDK_ROOT}/{PDK}/libs.tech/magic/{PDK}.magicrc")
92 return default_magicrc_path
94 @property
95 def default_pdk(self) -> Optional[PDK]:
96 pdk_str = self[EnvVar.PDK]
97 if pdk_str is None:
98 return None
99 try:
100 return PDK.from_string(pdk_str)
101 except ValueError:
102 warning(f"Ignoring invalid PDK specified in environment variable {EnvVar.PDK.value}: '{pdk_str}'")
103 return None
105 def __getitem__(self, env_var: EnvVar) -> Optional[str]:
106 return self._data[env_var]
108 def __contains__(self, env_var):
109 return env_var in self._data
111 def __repr__(self):
112 return f"Env({self._data})"