1## @file 2# This file is for installed package information database operations 3# 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 5# 6# SPDX-License-Identifier: BSD-2-Clause-Patent 7# 8 9''' 10Dependency 11''' 12 13## 14# Import Modules 15# 16from os.path import dirname 17import os 18 19import Logger.Log as Logger 20from Logger import StringTable as ST 21from Library.Parsing import GetWorkspacePackage 22from Library.Parsing import GetWorkspaceModule 23from Library.Parsing import GetPkgInfoFromDec 24from Library.Misc import GetRelativePath 25from Library import GlobalData 26from Logger.ToolError import FatalError 27from Logger.ToolError import EDK1_INF_ERROR 28from Logger.ToolError import UNKNOWN_ERROR 29(DEPEX_CHECK_SUCCESS, DEPEX_CHECK_MODULE_NOT_FOUND, \ 30DEPEX_CHECK_PACKAGE_NOT_FOUND, DEPEX_CHECK_DP_NOT_FOUND) = (0, 1, 2, 3) 31 32 33## DependencyRules 34# 35# This class represents the dependency rule check mechanism 36# 37# @param object: Inherited from object class 38# 39class DependencyRules(object): 40 def __init__(self, Datab, ToBeInstalledPkgList=None): 41 self.IpiDb = Datab 42 self.WsPkgList = GetWorkspacePackage() 43 self.WsModuleList = GetWorkspaceModule() 44 45 self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList] 46 47 # Add package info from the DIST to be installed. 48 self.PkgsToBeDepend.extend(self.GenToBeInstalledPkgList(ToBeInstalledPkgList)) 49 50 def GenToBeInstalledPkgList(self, ToBeInstalledPkgList): 51 if not ToBeInstalledPkgList: 52 return [] 53 RtnList = [] 54 for Dist in ToBeInstalledPkgList: 55 for Package in Dist.PackageSurfaceArea: 56 RtnList.append((Package[0], Package[1])) 57 58 return RtnList 59 60 ## Check whether a module exists by checking the Guid+Version+Name+Path combination 61 # 62 # @param Guid: Guid of a module 63 # @param Version: Version of a module 64 # @param Name: Name of a module 65 # @param Path: Path of a module 66 # @return: True if module existed, else False 67 # 68 def CheckModuleExists(self, Guid, Version, Name, Path): 69 Logger.Verbose(ST.MSG_CHECK_MODULE_EXIST) 70 ModuleList = self.IpiDb.GetModInPackage(Guid, Version, Name, Path) 71 ModuleList.extend(self.IpiDb.GetStandaloneModule(Guid, Version, Name, Path)) 72 Logger.Verbose(ST.MSG_CHECK_MODULE_EXIST_FINISH) 73 if len(ModuleList) > 0: 74 return True 75 else: 76 return False 77 78 ## Check whether a module depex satisfied. 79 # 80 # @param ModuleObj: A module object 81 # @param DpObj: A distribution object 82 # @return: True if module depex satisfied 83 # False else 84 # 85 def CheckModuleDepexSatisfied(self, ModuleObj, DpObj=None): 86 Logger.Verbose(ST.MSG_CHECK_MODULE_DEPEX_START) 87 Result = True 88 Dep = None 89 if ModuleObj.GetPackageDependencyList(): 90 Dep = ModuleObj.GetPackageDependencyList()[0] 91 for Dep in ModuleObj.GetPackageDependencyList(): 92 # 93 # first check whether the dependency satisfied by current workspace 94 # 95 Exist = self.CheckPackageExists(Dep.GetGuid(), Dep.GetVersion()) 96 # 97 # check whether satisfied by current distribution 98 # 99 if not Exist: 100 if DpObj is None: 101 Result = False 102 break 103 for GuidVerPair in DpObj.PackageSurfaceArea.keys(): 104 if Dep.GetGuid() == GuidVerPair[0]: 105 if Dep.GetVersion() is None or \ 106 len(Dep.GetVersion()) == 0: 107 Result = True 108 break 109 if Dep.GetVersion() == GuidVerPair[1]: 110 Result = True 111 break 112 else: 113 Result = False 114 break 115 116 if not Result: 117 Logger.Error("CheckModuleDepex", UNKNOWN_ERROR, \ 118 ST.ERR_DEPENDENCY_NOT_MATCH % (ModuleObj.GetName(), \ 119 Dep.GetPackageFilePath(), \ 120 Dep.GetGuid(), \ 121 Dep.GetVersion())) 122 return Result 123 124 ## Check whether a package exists in a package list specified by PkgsToBeDepend. 125 # 126 # @param Guid: Guid of a package 127 # @param Version: Version of a package 128 # @return: True if package exist 129 # False else 130 # 131 def CheckPackageExists(self, Guid, Version): 132 Logger.Verbose(ST.MSG_CHECK_PACKAGE_START) 133 Found = False 134 for (PkgGuid, PkgVer) in self.PkgsToBeDepend: 135 if (PkgGuid == Guid): 136 # 137 # if version is not empty and not equal, then not match 138 # 139 if Version and (PkgVer != Version): 140 Found = False 141 break 142 else: 143 Found = True 144 break 145 else: 146 Found = False 147 148 Logger.Verbose(ST.MSG_CHECK_PACKAGE_FINISH) 149 return Found 150 151 ## Check whether a package depex satisfied. 152 # 153 # @param PkgObj: A package object 154 # @param DpObj: A distribution object 155 # @return: True if package depex satisfied 156 # False else 157 # 158 def CheckPackageDepexSatisfied(self, PkgObj, DpObj=None): 159 ModuleDict = PkgObj.GetModuleDict() 160 for ModKey in ModuleDict.keys(): 161 ModObj = ModuleDict[ModKey] 162 if self.CheckModuleDepexSatisfied(ModObj, DpObj): 163 continue 164 else: 165 return False 166 return True 167 168 ## Check whether a DP exists. 169 # 170 # @param Guid: Guid of a Distribution 171 # @param Version: Version of a Distribution 172 # @return: True if Distribution exist 173 # False else 174 def CheckDpExists(self, Guid, Version): 175 Logger.Verbose(ST.MSG_CHECK_DP_START) 176 DpList = self.IpiDb.GetDp(Guid, Version) 177 if len(DpList) > 0: 178 Found = True 179 else: 180 Found = False 181 182 Logger.Verbose(ST.MSG_CHECK_DP_FINISH) 183 return Found 184 185 ## Check whether a DP depex satisfied by current workspace for Install 186 # 187 # @param DpObj: A distribution object 188 # @return: True if distribution depex satisfied 189 # False else 190 # 191 def CheckInstallDpDepexSatisfied(self, DpObj): 192 return self.CheckDpDepexSatisfied(DpObj) 193 194 # # Check whether multiple DP depex satisfied by current workspace for Install 195 # 196 # @param DpObjList: A distribution object list 197 # @return: True if distribution depex satisfied 198 # False else 199 # 200 def CheckTestInstallPdDepexSatisfied(self, DpObjList): 201 for DpObj in DpObjList: 202 if self.CheckDpDepexSatisfied(DpObj): 203 for PkgKey in DpObj.PackageSurfaceArea.keys(): 204 PkgObj = DpObj.PackageSurfaceArea[PkgKey] 205 self.PkgsToBeDepend.append((PkgObj.Guid, PkgObj.Version)) 206 else: 207 return False, DpObj 208 209 return True, DpObj 210 211 212 ## Check whether a DP depex satisfied by current workspace 213 # (excluding the original distribution's packages to be replaced) for Replace 214 # 215 # @param DpObj: A distribution object 216 # @param OrigDpGuid: The original distribution's Guid 217 # @param OrigDpVersion: The original distribution's Version 218 # 219 def ReplaceCheckNewDpDepex(self, DpObj, OrigDpGuid, OrigDpVersion): 220 self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList] 221 OrigDpPackageList = self.IpiDb.GetPackageListFromDp(OrigDpGuid, OrigDpVersion) 222 for OrigPkgInfo in OrigDpPackageList: 223 Guid, Version = OrigPkgInfo[0], OrigPkgInfo[1] 224 if (Guid, Version) in self.PkgsToBeDepend: 225 self.PkgsToBeDepend.remove((Guid, Version)) 226 return self.CheckDpDepexSatisfied(DpObj) 227 228 ## Check whether a DP depex satisfied by current workspace. 229 # 230 # @param DpObj: A distribution object 231 # 232 def CheckDpDepexSatisfied(self, DpObj): 233 for PkgKey in DpObj.PackageSurfaceArea.keys(): 234 PkgObj = DpObj.PackageSurfaceArea[PkgKey] 235 if self.CheckPackageDepexSatisfied(PkgObj, DpObj): 236 continue 237 else: 238 return False 239 240 for ModKey in DpObj.ModuleSurfaceArea.keys(): 241 ModObj = DpObj.ModuleSurfaceArea[ModKey] 242 if self.CheckModuleDepexSatisfied(ModObj, DpObj): 243 continue 244 else: 245 return False 246 247 return True 248 249 ## Check whether a DP could be removed from current workspace. 250 # 251 # @param DpGuid: File's guid 252 # @param DpVersion: File's version 253 # @retval Removable: True if distribution could be removed, False Else 254 # @retval DependModuleList: the list of modules that make distribution can not be removed 255 # 256 def CheckDpDepexForRemove(self, DpGuid, DpVersion): 257 Removable = True 258 DependModuleList = [] 259 WsModuleList = self.WsModuleList 260 # 261 # remove modules that included in current DP 262 # List of item (FilePath) 263 DpModuleList = self.IpiDb.GetDpModuleList(DpGuid, DpVersion) 264 for Module in DpModuleList: 265 if Module in WsModuleList: 266 WsModuleList.remove(Module) 267 else: 268 Logger.Warn("UPT\n", 269 ST.ERR_MODULE_NOT_INSTALLED % Module) 270 # 271 # get packages in current Dp and find the install path 272 # List of item (PkgGuid, PkgVersion, InstallPath) 273 DpPackageList = self.IpiDb.GetPackageListFromDp(DpGuid, DpVersion) 274 DpPackagePathList = [] 275 WorkSP = GlobalData.gWORKSPACE 276 for (PkgName, PkgGuid, PkgVersion, DecFile) in self.WsPkgList: 277 if PkgName: 278 pass 279 DecPath = dirname(DecFile) 280 if DecPath.find(WorkSP) > -1: 281 InstallPath = GetRelativePath(DecPath, WorkSP) 282 DecFileRelaPath = GetRelativePath(DecFile, WorkSP) 283 else: 284 InstallPath = DecPath 285 DecFileRelaPath = DecFile 286 287 if (PkgGuid, PkgVersion, InstallPath) in DpPackageList: 288 DpPackagePathList.append(DecFileRelaPath) 289 DpPackageList.remove((PkgGuid, PkgVersion, InstallPath)) 290 291 # 292 # the left items in DpPackageList are the packages that installed but not found anymore 293 # 294 for (PkgGuid, PkgVersion, InstallPath) in DpPackageList: 295 Logger.Warn("UPT", 296 ST.WARN_INSTALLED_PACKAGE_NOT_FOUND%(PkgGuid, PkgVersion, InstallPath)) 297 298 # 299 # check modules to see if has dependency on package of current DP 300 # 301 for Module in WsModuleList: 302 if (not VerifyRemoveModuleDep(Module, DpPackagePathList)): 303 Removable = False 304 DependModuleList.append(Module) 305 return (Removable, DependModuleList) 306 307 308 ## Check whether a DP could be replaced by a distribution containing NewDpPkgList 309 # from current workspace. 310 # 311 # @param OrigDpGuid: original Dp's Guid 312 # @param OrigDpVersion: original Dp's version 313 # @param NewDpPkgList: a list of package information (Guid, Version) in new Dp 314 # @retval Replaceable: True if distribution could be replaced, False Else 315 # @retval DependModuleList: the list of modules that make distribution can not be replaced 316 # 317 def CheckDpDepexForReplace(self, OrigDpGuid, OrigDpVersion, NewDpPkgList): 318 Replaceable = True 319 DependModuleList = [] 320 WsModuleList = self.WsModuleList 321 # 322 # remove modules that included in current DP 323 # List of item (FilePath) 324 DpModuleList = self.IpiDb.GetDpModuleList(OrigDpGuid, OrigDpVersion) 325 for Module in DpModuleList: 326 if Module in WsModuleList: 327 WsModuleList.remove(Module) 328 else: 329 Logger.Warn("UPT\n", 330 ST.ERR_MODULE_NOT_INSTALLED % Module) 331 332 OtherPkgList = NewDpPkgList 333 # 334 # get packages in current Dp and find the install path 335 # List of item (PkgGuid, PkgVersion, InstallPath) 336 DpPackageList = self.IpiDb.GetPackageListFromDp(OrigDpGuid, OrigDpVersion) 337 DpPackagePathList = [] 338 WorkSP = GlobalData.gWORKSPACE 339 for (PkgName, PkgGuid, PkgVersion, DecFile) in self.WsPkgList: 340 if PkgName: 341 pass 342 DecPath = dirname(DecFile) 343 if DecPath.find(WorkSP) > -1: 344 InstallPath = GetRelativePath(DecPath, WorkSP) 345 DecFileRelaPath = GetRelativePath(DecFile, WorkSP) 346 else: 347 InstallPath = DecPath 348 DecFileRelaPath = DecFile 349 350 if (PkgGuid, PkgVersion, InstallPath) in DpPackageList: 351 DpPackagePathList.append(DecFileRelaPath) 352 DpPackageList.remove((PkgGuid, PkgVersion, InstallPath)) 353 else: 354 OtherPkgList.append((PkgGuid, PkgVersion)) 355 356 # 357 # the left items in DpPackageList are the packages that installed but not found anymore 358 # 359 for (PkgGuid, PkgVersion, InstallPath) in DpPackageList: 360 Logger.Warn("UPT", 361 ST.WARN_INSTALLED_PACKAGE_NOT_FOUND%(PkgGuid, PkgVersion, InstallPath)) 362 363 # 364 # check modules to see if it can be satisfied by package not belong to removed DP 365 # 366 for Module in WsModuleList: 367 if (not VerifyReplaceModuleDep(Module, DpPackagePathList, OtherPkgList)): 368 Replaceable = False 369 DependModuleList.append(Module) 370 return (Replaceable, DependModuleList) 371 372 373## check whether module depends on packages in DpPackagePathList, return True 374# if found, False else 375# 376# @param Path: a module path 377# @param DpPackagePathList: a list of Package Paths 378# @retval: False: module depends on package in DpPackagePathList 379# True: module doesn't depend on package in DpPackagePathList 380# 381def VerifyRemoveModuleDep(Path, DpPackagePathList): 382 try: 383 for Item in GetPackagePath(Path): 384 if Item in DpPackagePathList: 385 DecPath = os.path.normpath(os.path.join(GlobalData.gWORKSPACE, Item)) 386 Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, DecPath)) 387 return False 388 else: 389 return True 390 except FatalError as ErrCode: 391 if ErrCode.message == EDK1_INF_ERROR: 392 Logger.Warn("UPT", 393 ST.WRN_EDK1_INF_FOUND%Path) 394 return True 395 else: 396 return True 397 398# # GetPackagePath 399# 400# Get Dependency package path from an Inf file path 401# 402def GetPackagePath(InfPath): 403 PackagePath = [] 404 if os.path.exists(InfPath): 405 FindSection = False 406 for Line in open(InfPath).readlines(): 407 Line = Line.strip() 408 if not Line: 409 continue 410 if Line.startswith('#'): 411 continue 412 if Line.startswith('[Packages') and Line.endswith(']'): 413 FindSection = True 414 continue 415 if Line.startswith('[') and Line.endswith(']') and FindSection: 416 break 417 if FindSection: 418 PackagePath.append(os.path.normpath(Line)) 419 420 return PackagePath 421 422## check whether module depends on packages in DpPackagePathList and can not be satisfied by OtherPkgList 423# 424# @param Path: a module path 425# @param DpPackagePathList: a list of Package Paths 426# @param OtherPkgList: a list of Package Information (Guid, Version) 427# @retval: False: module depends on package in DpPackagePathList and can not be satisfied by OtherPkgList 428# True: either module doesn't depend on DpPackagePathList or module depends on DpPackagePathList 429# but can be satisfied by OtherPkgList 430# 431def VerifyReplaceModuleDep(Path, DpPackagePathList, OtherPkgList): 432 try: 433 for Item in GetPackagePath(Path): 434 if Item in DpPackagePathList: 435 DecPath = os.path.normpath(os.path.join(GlobalData.gWORKSPACE, Item)) 436 Name, Guid, Version = GetPkgInfoFromDec(DecPath) 437 if (Guid, Version) not in OtherPkgList: 438 Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, DecPath)) 439 return False 440 else: 441 return True 442 except FatalError as ErrCode: 443 if ErrCode.message == EDK1_INF_ERROR: 444 Logger.Warn("UPT", 445 ST.WRN_EDK1_INF_FOUND%Path) 446 return True 447 else: 448 return True 449