Coverage for pyEDAA/ToolSetup/__init__.py: 67%

157 statements  

« prev     ^ index     » next       coverage.py v7.15.1, created at 2026-07-13 18:11 +0000

1# ==================================================================================================================== # 

2# _____ ____ _ _ _____ _ ____ _ # 

3# _ __ _ _| ____| _ \ / \ / \ |_ _|__ ___ | / ___| ___| |_ _ _ _ __ # 

4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |/ _ \ / _ \| \___ \ / _ \ __| | | | '_ \ # 

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| | (_) | (_) | |___) | __/ |_| |_| | |_) | # 

6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_|\___/ \___/|_|____/ \___|\__|\__,_| .__/ # 

7# |_| |___/ |_| # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2021-2026 Patrick Lehmann - Boetzingen, Germany # 

15# # 

16# Licensed under the Apache License, Version 2.0 (the "License"); # 

17# you may not use this file except in compliance with the License. # 

18# You may obtain a copy of the License at # 

19# # 

20# http://www.apache.org/licenses/LICENSE-2.0 # 

21# # 

22# Unless required by applicable law or agreed to in writing, software # 

23# distributed under the License is distributed on an "AS IS" BASIS, # 

24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

25# See the License for the specific language governing permissions and # 

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31"""Package to support configuring EDA tools for usage with pyEDAA.CLITool.""" 

32__author__ = "Patrick Lehmann" 

33__email__ = "Paebbels@gmail.com" 

34__copyright__ = "2014-2026, Patrick Lehmann" 

35__license__ = "Apache License, Version 2.0" 

36__version__ = "0.5.1" 

37__keywords__ = ["configuration", "eda", "installation", "selection"] 

38__project_url__ = "https://github.com/edaa-org/pyEDAA.ToolSetup" 

39__documentation_url__ = "https://edaa-org.github.io/pyEDAA.ToolSetup" 

40__issue_tracker_url__ = "https://GitHub.com/edaa-org/pyEDAA.ToolSetup/issues" 

41 

42 

43from pathlib import Path 

44from typing import Dict, ClassVar, cast 

45 

46from pyTooling.Decorators import export 

47from pyTooling.Exceptions import ExceptionBase 

48from pyTooling.Configuration import Dictionary 

49from pyTooling.Configuration.YAML import Configuration 

50 

51from .DataModel import ( 

52 Installation as DM_Installation, 

53 Vendor as DM_Vendor, 

54 Tool as DM_Tool, 

55 ToolInstance as DM_ToolInstance 

56) 

57 

58 

59class ToolChainException(ExceptionBase): 

60 """Base-class for all pyEDAA.ToolSetup specific exceptions.""" 

61 

62class ConfigurationException(ExceptionBase): 

63 """``ConfigurationException`` is raise while running configuration or database 

64 tasks in pyIPCMI 

65 """ 

66 

67class SkipConfigurationException(ExceptionBase): 

68 """``SkipConfigurationException`` is a :py:exc:`ConfigurationException`, 

69 which can be skipped. 

70 """ 

71 

72class ConfigurationMixIn: 

73 _config: Dictionary 

74 

75 def __init__(self, config: Dictionary) -> None: 

76 self._config = config 

77 

78 

79@export 

80class ToolInstance(DM_ToolInstance, ConfigurationMixIn): 

81 def __init__(self, config: Dictionary, parent: "Tool") -> None: 

82 name = config.Key 

83 installationDirectory = Path(config["InstallationDirectory"]) 

84 binaryDirectory = Path(config["BinaryDirectory"]) 

85 version = config["Version"] 

86 

87 super().__init__(installationDirectory, binaryDirectory, version, parent=parent) 

88 ConfigurationMixIn.__init__(self, config) 

89 

90 

91@export 

92class Tool(DM_Tool, ConfigurationMixIn): 

93 def __init__(self, config: Dictionary, parent: "Vendor") -> None: 

94 name = config.Key 

95 

96 super().__init__(name, parent=parent) 

97 ConfigurationMixIn.__init__(self, config) 

98 

99 @property 

100 def Default(self) -> ToolInstance: 

101 return self._LoadVariant("Default") 

102 

103 def _LoadVariant(self, key: str) -> ToolInstance: 

104 if key not in self._variants: 104 ↛ 108line 104 didn't jump to line 108 because the condition on line 104 was always true

105 instance = self._instanceClass(self._config[key], parent=self) 

106 self._variants[key] = instance 

107 else: 

108 instance = self._variants[key] 

109 

110 return instance 

111 

112 def _LoadAllVariants(self) -> None: 

113 if self._allLoaded: 

114 return 

115 

116 for key in self._config: 

117 if key not in self._variants: 

118 self._variants[key] = ToolInstance(self._config[key], parent=self) 

119 

120 self._allLoaded = True 

121 

122 

123@export 

124class Vendor(DM_Vendor, ConfigurationMixIn): 

125 _toolClasses: ClassVar[Dict[str, Tool]] 

126 

127 def __init__(self, config: Dictionary, parent: "Installations") -> None: 

128 name = config.Key 

129 installationDirectory = Path(config["InstallationDirectory"]) 

130 

131 super().__init__(name, installationDirectory, parent=parent) 

132 ConfigurationMixIn.__init__(self, config) 

133 

134 def _LoadTool(self, key: str) -> Tool: 

135 if key not in self._tools: 135 ↛ 140line 135 didn't jump to line 140 because the condition on line 135 was always true

136 cls = self._toolClasses[key] 

137 tool = cls(self._config[key], parent=self) 

138 self._tools[key] = tool 

139 else: 

140 tool = self._tools[key] 

141 

142 return tool 

143 

144 def _LoadAllTools(self) -> None: 

145 if self._allLoaded: 

146 return 

147 

148 for key in self._config: 

149 if key not in self._tools: 

150 cls = self._toolClasses[key] 

151 self._tools[key] = cls(self._config[key], parent=self) 

152 

153 self._allLoaded = True 

154 

155 

156@export 

157class Installations(DM_Installation): 

158 from .Aldec import ActiveHDL, RivieraPRO, Aldec 

159 from .OpenSource.GHDL import GHDL 

160 from .IntelFPGA import Quartus, Altera, IntelFPGA 

161 from .Lattice import Diamond, Lattice 

162 from .OpenSource import OpenSource 

163 from .OpenSource.GTKWave import GTKWave 

164 from .SiemensEDA import ModelSim, QuestaSim, MentorGraphics 

165 from .SystemTools import Git, SystemTools 

166 from .Xilinx import ISE, Vivado, VivadoSDK, Vitis, Xilinx 

167 

168 _config: Configuration 

169 _vendorClasses: Dict[str, Vendor] = { 

170 "Aldec": Aldec, 

171 "Altera": Altera, 

172 "IntelFPGA": IntelFPGA, 

173 "Lattice": Lattice, 

174 "MentorGraphics": MentorGraphics, 

175 "Xilinx": Xilinx, 

176 "SystemTools": SystemTools, 

177 "OpenSource": OpenSource 

178 } 

179 

180 def __init__(self, yamlFile: Path) -> None: 

181 super().__init__() 

182 self._config = Configuration(yamlFile) 

183 

184 def _LoadVendor(self, key: str) -> Vendor: 

185 cls = self._vendorClasses[key] 

186 vendor = cls(self._config["Installations"][key], parent=self) 

187 self._vendors[key] = vendor 

188 

189 return vendor 

190 

191 def _LoadAllVendors(self) -> None: 

192 if self._allLoaded: 

193 return 

194 

195 for key in self._config["Installations"]: 

196 if key not in self._vendors: 

197 cls = self._vendorClasses[key] 

198 self._vendors[key] = cls(self._config["Installations"][key], parent=self) 

199 

200 self._allLoaded = True 

201 

202 @property 

203 def Aldec(self) -> Aldec: 

204 from .Aldec import Aldec 

205 return cast(Aldec, self.__getitem__("Aldec")) 

206 

207 @property 

208 def Altera(self) -> Altera: 

209 from .IntelFPGA import Altera 

210 return cast(Altera, self.__getitem__("Altera")) 

211 

212 @property 

213 def IntelFPGA(self) -> IntelFPGA: 

214 from .IntelFPGA import IntelFPGA 

215 return cast(IntelFPGA, self.__getitem__("IntelFPGA")) 

216 

217 @property 

218 def Lattice(self) -> Lattice: 

219 from .Lattice import Lattice 

220 return cast(Lattice, self.__getitem__("Lattice")) 

221 

222 @property 

223 def MentorGraphics(self) -> MentorGraphics: 

224 from .SiemensEDA import MentorGraphics 

225 return cast(MentorGraphics, self.__getitem__("MentorGraphics")) 

226 

227 @property 

228 def OpenSource(self) -> OpenSource: 

229 from .OpenSource import OpenSource 

230 return cast(OpenSource, self.__getitem__("OpenSource")) 

231 

232 @property 

233 def SiemensEDA(self) -> SiemensEDA: 

234 from .SiemensEDA import SiemensEDA 

235 return cast(SiemensEDA, self.__getitem__("SiemensEDA")) 

236 

237 @property 

238 def SystemTools(self) -> SystemTools: 

239 from .SystemTools import SystemTools 

240 return cast(SystemTools, self.__getitem__("SystemTools")) 

241 

242 @property 

243 def Xilinx(self) -> Xilinx: 

244 from .Xilinx import Xilinx 

245 return cast(Xilinx, self.__getitem__("Xilinx")) 

246 

247 @property 

248 def ActiveHDL(self) -> ActiveHDL: 

249 raise NotImplementedError() 

250 

251 @property 

252 def RivieraPRO(self) -> RivieraPRO: 

253 raise NotImplementedError() 

254 

255 @property 

256 def Diamond(self) -> Diamond: 

257 raise NotImplementedError() 

258 

259 @property 

260 def Quartus(self) -> Quartus: 

261 raise NotImplementedError() 

262 

263 @property 

264 def ModelSim(self) -> ModelSim: 

265 raise NotImplementedError() 

266 

267 @property 

268 def QuestaSim(self) -> QuestaSim: 

269 raise NotImplementedError() 

270 

271 @property 

272 def Vivado(self) -> Vivado: 

273 raise NotImplementedError() 

274 

275 @property 

276 def VivadoSDK(self) -> VivadoSDK: 

277 raise NotImplementedError() 

278 

279 @property 

280 def Vitis(self) -> Vitis: 

281 raise NotImplementedError()