Add a new function to test if a DIST file list one by one to see if they can meet the requirement of Dependency. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hess Chen <hesheng.chen@intel.com> Reviewed-by: Yonghong Zhu <yonghong.zhu@intel.com>
		
			
				
	
	
		
			428 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
## @file
 | 
						|
# This file is for installed package information database operations
 | 
						|
#
 | 
						|
# Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
 | 
						|
#
 | 
						|
# This program and the accompanying materials are licensed and made available 
 | 
						|
# under the terms and conditions of the BSD License which accompanies this 
 | 
						|
# distribution. The full text of the license may be found at 
 | 
						|
# http://opensource.org/licenses/bsd-license.php
 | 
						|
#
 | 
						|
#
 | 
						|
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
#
 | 
						|
 | 
						|
'''
 | 
						|
Dependency
 | 
						|
'''
 | 
						|
 | 
						|
##
 | 
						|
# Import Modules
 | 
						|
#
 | 
						|
from os.path import dirname
 | 
						|
 | 
						|
import Logger.Log as Logger
 | 
						|
from Logger import StringTable as ST
 | 
						|
from Library.Parsing import GetWorkspacePackage
 | 
						|
from Library.Parsing import GetWorkspaceModule
 | 
						|
from Library.Misc import GetRelativePath
 | 
						|
from Library import GlobalData
 | 
						|
from PomAdapter.InfPomAlignment import InfPomAlignment
 | 
						|
from Logger.ToolError import FatalError
 | 
						|
from Logger.ToolError import EDK1_INF_ERROR
 | 
						|
from Logger.ToolError import UNKNOWN_ERROR
 | 
						|
(DEPEX_CHECK_SUCCESS, DEPEX_CHECK_MODULE_NOT_FOUND, \
 | 
						|
DEPEX_CHECK_PACKAGE_NOT_FOUND, DEPEX_CHECK_DP_NOT_FOUND) = (0, 1, 2, 3)
 | 
						|
 | 
						|
 | 
						|
## DependencyRules
 | 
						|
#
 | 
						|
# This class represents the dependency rule check mechanism
 | 
						|
# 
 | 
						|
# @param object:      Inherited from object class
 | 
						|
#
 | 
						|
class DependencyRules(object):
 | 
						|
    def __init__(self, Datab):
 | 
						|
        self.IpiDb = Datab
 | 
						|
        self.WsPkgList = GetWorkspacePackage()
 | 
						|
        self.WsModuleList = GetWorkspaceModule()
 | 
						|
        self.PkgsToBeDepend = []
 | 
						|
        
 | 
						|
    ## Check whether a module exists by checking the Guid+Version+Name+Path combination
 | 
						|
    #
 | 
						|
    # @param Guid:  Guid of a module
 | 
						|
    # @param Version: Version of a module
 | 
						|
    # @param Name: Name of a module
 | 
						|
    # @param Path: Path of a module
 | 
						|
    # @return:  True if module existed, else False
 | 
						|
    #
 | 
						|
    def CheckModuleExists(self, Guid, Version, Name, Path):
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_MODULE_EXIST)
 | 
						|
        ModuleList = self.IpiDb.GetModInPackage(Guid, Version, Name, Path)
 | 
						|
        ModuleList.extend(self.IpiDb.GetStandaloneModule(Guid, Version, Name, Path))
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_MODULE_EXIST_FINISH)
 | 
						|
        if len(ModuleList) > 0:
 | 
						|
            return True
 | 
						|
        else:
 | 
						|
            return False
 | 
						|
        
 | 
						|
    ## Check whether a module depex satisfied.
 | 
						|
    #
 | 
						|
    # @param ModuleObj: A module object
 | 
						|
    # @param DpObj: A distribution object
 | 
						|
    # @return: True if module depex satisfied
 | 
						|
    #          False else
 | 
						|
    #
 | 
						|
    def CheckModuleDepexSatisfied(self, ModuleObj, DpObj=None):
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_MODULE_DEPEX_START)
 | 
						|
        Result = True
 | 
						|
        Dep = None
 | 
						|
        if ModuleObj.GetPackageDependencyList():
 | 
						|
            Dep = ModuleObj.GetPackageDependencyList()[0]
 | 
						|
        for Dep in ModuleObj.GetPackageDependencyList():
 | 
						|
            #
 | 
						|
            # first check whether the dependency satisfied by current workspace
 | 
						|
            #
 | 
						|
            Exist = self.CheckPackageExists(Dep.GetGuid(), Dep.GetVersion())
 | 
						|
            #
 | 
						|
            # check whether satisfied by current distribution 
 | 
						|
            #
 | 
						|
            if not Exist:
 | 
						|
                if DpObj == None:
 | 
						|
                    Result = False
 | 
						|
                    break
 | 
						|
                for GuidVerPair in DpObj.PackageSurfaceArea.keys():
 | 
						|
                    if Dep.GetGuid() == GuidVerPair[0]:
 | 
						|
                        if Dep.GetVersion() == None or \
 | 
						|
                        len(Dep.GetVersion()) == 0:
 | 
						|
                            Result = True
 | 
						|
                            break
 | 
						|
                        if Dep.GetVersion() == GuidVerPair[1]:
 | 
						|
                            Result = True
 | 
						|
                            break
 | 
						|
                else:
 | 
						|
                    Result = False
 | 
						|
                    break
 | 
						|
        
 | 
						|
        if not Result:
 | 
						|
            Logger.Error("CheckModuleDepex", UNKNOWN_ERROR, \
 | 
						|
                         ST.ERR_DEPENDENCY_NOT_MATCH % (ModuleObj.GetName(), \
 | 
						|
                                                        Dep.GetPackageFilePath(), \
 | 
						|
                                                        Dep.GetGuid(), \
 | 
						|
                                                        Dep.GetVersion()))
 | 
						|
        return Result
 | 
						|
            
 | 
						|
    ## Check whether a package exists in a package list specified by PkgsToBeDepend.
 | 
						|
    #
 | 
						|
    # @param Guid: Guid of a package
 | 
						|
    # @param Version: Version of a package
 | 
						|
    # @return: True if package exist
 | 
						|
    #          False else
 | 
						|
    #
 | 
						|
    def CheckPackageExists(self, Guid, Version):
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_PACKAGE_START)
 | 
						|
        Found = False
 | 
						|
        for (PkgGuid, PkgVer) in self.PkgsToBeDepend:
 | 
						|
            if (PkgGuid == Guid):
 | 
						|
                #
 | 
						|
                # if version is not empty and not equal, then not match
 | 
						|
                #
 | 
						|
                if Version and (PkgVer != Version):
 | 
						|
                    Found = False
 | 
						|
                    break
 | 
						|
                else:
 | 
						|
                    Found = True
 | 
						|
                    break
 | 
						|
        else:
 | 
						|
            Found = False
 | 
						|
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_PACKAGE_FINISH)
 | 
						|
        return Found
 | 
						|
         
 | 
						|
    ## Check whether a package depex satisfied.
 | 
						|
    #
 | 
						|
    # @param PkgObj: A package object
 | 
						|
    # @param DpObj: A distribution object
 | 
						|
    # @return: True if package depex satisified
 | 
						|
    #          False else
 | 
						|
    #
 | 
						|
    def CheckPackageDepexSatisfied(self, PkgObj, DpObj=None):
 | 
						|
        ModuleDict = PkgObj.GetModuleDict()
 | 
						|
        for ModKey in ModuleDict.keys():
 | 
						|
            ModObj = ModuleDict[ModKey]
 | 
						|
            if self.CheckModuleDepexSatisfied(ModObj, DpObj):
 | 
						|
                continue
 | 
						|
            else:
 | 
						|
                return False
 | 
						|
        return True
 | 
						|
        
 | 
						|
    ## Check whether a DP exists.
 | 
						|
    #
 | 
						|
    # @param Guid: Guid of a Distribution
 | 
						|
    # @param Version: Version of a Distribution
 | 
						|
    # @return: True if Distribution exist
 | 
						|
    #          False else
 | 
						|
    def CheckDpExists(self, Guid, Version):
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_DP_START)
 | 
						|
        DpList = self.IpiDb.GetDp(Guid, Version)
 | 
						|
        if len(DpList) > 0:
 | 
						|
            Found = True
 | 
						|
        else:
 | 
						|
            Found = False
 | 
						|
 | 
						|
        Logger.Verbose(ST.MSG_CHECK_DP_FINISH)
 | 
						|
        return Found
 | 
						|
 | 
						|
    ## Check whether a DP depex satisfied by current workspace for Install
 | 
						|
    #
 | 
						|
    # @param DpObj:  A distribution object
 | 
						|
    # @return: True if distribution depex satisfied
 | 
						|
    #          False else
 | 
						|
    #
 | 
						|
    def CheckInstallDpDepexSatisfied(self, DpObj):
 | 
						|
        self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList]
 | 
						|
        return self.CheckDpDepexSatisfied(DpObj)
 | 
						|
 | 
						|
    # # Check whether multiple DP depex satisfied by current workspace for Install
 | 
						|
    #
 | 
						|
    # @param DpObjList:  A distribution object list
 | 
						|
    # @return: True if distribution depex satisfied
 | 
						|
    #          False else
 | 
						|
    #
 | 
						|
    def CheckTestInstallPdDepexSatisfied(self, DpObjList):
 | 
						|
        self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList]
 | 
						|
        for DpObj in DpObjList:
 | 
						|
            if self.CheckDpDepexSatisfied(DpObj):
 | 
						|
                for PkgKey in DpObj.PackageSurfaceArea.keys():
 | 
						|
                    PkgObj = DpObj.PackageSurfaceArea[PkgKey]
 | 
						|
                    self.PkgsToBeDepend.append((PkgObj.Guid, PkgObj.Version))
 | 
						|
            else:
 | 
						|
                return False, DpObj
 | 
						|
 | 
						|
        return True, DpObj
 | 
						|
 | 
						|
 | 
						|
    ## Check whether a DP depex satisfied by current workspace 
 | 
						|
    #  (excluding the original distribution's packages to be replaced) for Replace
 | 
						|
    #
 | 
						|
    # @param DpObj:  A distribution object
 | 
						|
    # @param OrigDpGuid: The original distribution's Guid
 | 
						|
    # @param OrigDpVersion: The original distribution's Version
 | 
						|
    #
 | 
						|
    def ReplaceCheckNewDpDepex(self, DpObj, OrigDpGuid, OrigDpVersion):
 | 
						|
        self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList]
 | 
						|
        OrigDpPackageList = self.IpiDb.GetPackageListFromDp(OrigDpGuid, OrigDpVersion)
 | 
						|
        for OrigPkgInfo in OrigDpPackageList:
 | 
						|
            Guid, Version = OrigPkgInfo[0], OrigPkgInfo[1]
 | 
						|
            if (Guid, Version) in self.PkgsToBeDepend:
 | 
						|
                self.PkgsToBeDepend.remove((Guid, Version))
 | 
						|
        return self.CheckDpDepexSatisfied(DpObj)
 | 
						|
 | 
						|
    ## Check whether a DP depex satisfied by current workspace.
 | 
						|
    #
 | 
						|
    # @param DpObj:  A distribution object
 | 
						|
    #
 | 
						|
    def CheckDpDepexSatisfied(self, DpObj):
 | 
						|
        for PkgKey in DpObj.PackageSurfaceArea.keys():
 | 
						|
            PkgObj = DpObj.PackageSurfaceArea[PkgKey]
 | 
						|
            if self.CheckPackageDepexSatisfied(PkgObj, DpObj):
 | 
						|
                continue
 | 
						|
            else:
 | 
						|
                return False
 | 
						|
            
 | 
						|
        for ModKey in DpObj.ModuleSurfaceArea.keys():
 | 
						|
            ModObj = DpObj.ModuleSurfaceArea[ModKey]
 | 
						|
            if self.CheckModuleDepexSatisfied(ModObj, DpObj):
 | 
						|
                continue
 | 
						|
            else:
 | 
						|
                return False
 | 
						|
        
 | 
						|
        return True
 | 
						|
    
 | 
						|
    ## Check whether a DP could be removed from current workspace. 
 | 
						|
    #
 | 
						|
    # @param DpGuid:  File's guid
 | 
						|
    # @param DpVersion: File's version
 | 
						|
    # @retval Removable: True if distribution could be removed, False Else
 | 
						|
    # @retval DependModuleList: the list of modules that make distribution can not be removed
 | 
						|
    #
 | 
						|
    def CheckDpDepexForRemove(self, DpGuid, DpVersion):
 | 
						|
        Removable = True
 | 
						|
        DependModuleList = []
 | 
						|
        WsModuleList = self.WsModuleList
 | 
						|
        #
 | 
						|
        # remove modules that included in current DP
 | 
						|
        # List of item (FilePath)
 | 
						|
        DpModuleList = self.IpiDb.GetDpModuleList(DpGuid, DpVersion) 
 | 
						|
        for Module in DpModuleList:
 | 
						|
            if Module in WsModuleList:
 | 
						|
                WsModuleList.remove(Module)
 | 
						|
            else:
 | 
						|
                Logger.Warn("UPT\n",
 | 
						|
                            ST.ERR_MODULE_NOT_INSTALLED % Module)
 | 
						|
        #
 | 
						|
        # get packages in current Dp and find the install path
 | 
						|
        # List of item (PkgGuid, PkgVersion, InstallPath)
 | 
						|
        DpPackageList = self.IpiDb.GetPackageListFromDp(DpGuid, DpVersion) 
 | 
						|
        DpPackagePathList = []
 | 
						|
        WorkSP = GlobalData.gWORKSPACE
 | 
						|
        for (PkgName, PkgGuid, PkgVersion, DecFile) in self.WsPkgList:
 | 
						|
            if PkgName:
 | 
						|
                pass
 | 
						|
            DecPath = dirname(DecFile)
 | 
						|
            if DecPath.find(WorkSP) > -1:
 | 
						|
                InstallPath = GetRelativePath(DecPath,WorkSP)
 | 
						|
                DecFileRelaPath = GetRelativePath(DecFile,WorkSP)
 | 
						|
            else:
 | 
						|
                InstallPath = DecPath
 | 
						|
                DecFileRelaPath = DecFile
 | 
						|
                
 | 
						|
            if (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
 | 
						|
                DpPackagePathList.append(DecFileRelaPath)
 | 
						|
                DpPackageList.remove((PkgGuid, PkgVersion, InstallPath))
 | 
						|
        
 | 
						|
        #
 | 
						|
        # the left items in DpPackageList are the packages that installed but not found anymore
 | 
						|
        #
 | 
						|
        for (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
 | 
						|
            Logger.Warn("UPT",
 | 
						|
                        ST.WARN_INSTALLED_PACKAGE_NOT_FOUND%(PkgGuid, PkgVersion, InstallPath))
 | 
						|
        
 | 
						|
        #
 | 
						|
        # check modules to see if has dependency on package of current DP
 | 
						|
        #
 | 
						|
        for Module in WsModuleList:
 | 
						|
            if (not VerifyRemoveModuleDep(Module, DpPackagePathList)):
 | 
						|
                Removable = False
 | 
						|
                DependModuleList.append(Module)
 | 
						|
        return (Removable, DependModuleList)
 | 
						|
 | 
						|
 | 
						|
    ## Check whether a DP could be replaced by a distribution containing NewDpPkgList
 | 
						|
    # from current workspace.
 | 
						|
    #
 | 
						|
    # @param OrigDpGuid:  original Dp's Guid
 | 
						|
    # @param OrigDpVersion: original Dp's version
 | 
						|
    # @param NewDpPkgList: a list of package information (Guid, Version) in new Dp
 | 
						|
    # @retval Replaceable: True if distribution could be replaced, False Else
 | 
						|
    # @retval DependModuleList: the list of modules that make distribution can not be replaced
 | 
						|
    # 
 | 
						|
    def CheckDpDepexForReplace(self, OrigDpGuid, OrigDpVersion, NewDpPkgList):
 | 
						|
        Replaceable = True
 | 
						|
        DependModuleList = []
 | 
						|
        WsModuleList = self.WsModuleList
 | 
						|
        #
 | 
						|
        # remove modules that included in current DP
 | 
						|
        # List of item (FilePath)
 | 
						|
        DpModuleList = self.IpiDb.GetDpModuleList(OrigDpGuid, OrigDpVersion) 
 | 
						|
        for Module in DpModuleList:
 | 
						|
            if Module in WsModuleList:
 | 
						|
                WsModuleList.remove(Module)
 | 
						|
            else:
 | 
						|
                Logger.Warn("UPT\n",
 | 
						|
                            ST.ERR_MODULE_NOT_INSTALLED % Module)
 | 
						|
        
 | 
						|
        OtherPkgList = NewDpPkgList
 | 
						|
        #
 | 
						|
        # get packages in current Dp and find the install path
 | 
						|
        # List of item (PkgGuid, PkgVersion, InstallPath)
 | 
						|
        DpPackageList = self.IpiDb.GetPackageListFromDp(OrigDpGuid, OrigDpVersion) 
 | 
						|
        DpPackagePathList = []
 | 
						|
        WorkSP = GlobalData.gWORKSPACE
 | 
						|
        for (PkgName, PkgGuid, PkgVersion, DecFile) in self.WsPkgList:
 | 
						|
            if PkgName:
 | 
						|
                pass
 | 
						|
            DecPath = dirname(DecFile)
 | 
						|
            if DecPath.find(WorkSP) > -1:
 | 
						|
                InstallPath = GetRelativePath(DecPath,WorkSP)
 | 
						|
                DecFileRelaPath = GetRelativePath(DecFile,WorkSP)
 | 
						|
            else:
 | 
						|
                InstallPath = DecPath
 | 
						|
                DecFileRelaPath = DecFile
 | 
						|
                
 | 
						|
            if (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
 | 
						|
                DpPackagePathList.append(DecFileRelaPath)
 | 
						|
                DpPackageList.remove((PkgGuid, PkgVersion, InstallPath))
 | 
						|
            else:
 | 
						|
                OtherPkgList.append((PkgGuid, PkgVersion))
 | 
						|
 | 
						|
        #
 | 
						|
        # the left items in DpPackageList are the packages that installed but not found anymore
 | 
						|
        #
 | 
						|
        for (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
 | 
						|
            Logger.Warn("UPT",
 | 
						|
                        ST.WARN_INSTALLED_PACKAGE_NOT_FOUND%(PkgGuid, PkgVersion, InstallPath))
 | 
						|
        
 | 
						|
        #
 | 
						|
        # check modules to see if it can be satisfied by package not belong to removed DP
 | 
						|
        #
 | 
						|
        for Module in WsModuleList:
 | 
						|
            if (not VerifyReplaceModuleDep(Module, DpPackagePathList, OtherPkgList)):
 | 
						|
                Replaceable = False
 | 
						|
                DependModuleList.append(Module)
 | 
						|
        return (Replaceable, DependModuleList)
 | 
						|
 | 
						|
    
 | 
						|
## check whether module depends on packages in DpPackagePathList, return True 
 | 
						|
# if found, False else
 | 
						|
#
 | 
						|
# @param Path: a module path
 | 
						|
# @param DpPackagePathList: a list of Package Paths
 | 
						|
# @retval:  False: module depends on package in DpPackagePathList
 | 
						|
#           True:  module doesn't depend on package in DpPackagePathList
 | 
						|
#
 | 
						|
def VerifyRemoveModuleDep(Path, DpPackagePathList):
 | 
						|
    WorkSP = GlobalData.gWORKSPACE
 | 
						|
    
 | 
						|
    try:
 | 
						|
        PomAli = InfPomAlignment(Path, WorkSP, Skip=True)
 | 
						|
 | 
						|
        for Item in PomAli.GetPackageDependencyList():
 | 
						|
            if Item.GetPackageFilePath() in DpPackagePathList:
 | 
						|
                Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, Item.GetPackageFilePath()))
 | 
						|
                return False
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
    except FatalError, ErrCode:
 | 
						|
        if ErrCode.message == EDK1_INF_ERROR:
 | 
						|
            Logger.Warn("UPT",
 | 
						|
                        ST.WRN_EDK1_INF_FOUND%Path)
 | 
						|
            return True
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
 | 
						|
## check whether module depends on packages in DpPackagePathList and can not be satisfied by OtherPkgList
 | 
						|
#
 | 
						|
# @param Path: a module path
 | 
						|
# @param DpPackagePathList:  a list of Package Paths
 | 
						|
# @param OtherPkgList:       a list of Package Information (Guid, Version)
 | 
						|
# @retval:  False: module depends on package in DpPackagePathList and can not be satisfied by OtherPkgList
 | 
						|
#           True:  either module doesn't depend on DpPackagePathList or module depends on DpPackagePathList 
 | 
						|
#                 but can be satisfied by OtherPkgList
 | 
						|
#
 | 
						|
def VerifyReplaceModuleDep(Path, DpPackagePathList, OtherPkgList):
 | 
						|
    WorkSP = GlobalData.gWORKSPACE
 | 
						|
    
 | 
						|
    try:
 | 
						|
        PomAli = InfPomAlignment(Path, WorkSP, Skip=True)
 | 
						|
 | 
						|
        for Item in PomAli.GetPackageDependencyList():
 | 
						|
            if Item.GetPackageFilePath() in DpPackagePathList:
 | 
						|
                Guid, Version = Item.GetGuid(), Item.GetVersion()
 | 
						|
                if (Guid, Version) not in OtherPkgList:
 | 
						|
                    Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, Item.GetPackageFilePath()))
 | 
						|
                    return False
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
    except FatalError, ErrCode:
 | 
						|
        if ErrCode.message == EDK1_INF_ERROR:
 | 
						|
            Logger.Warn("UPT",
 | 
						|
                        ST.WRN_EDK1_INF_FOUND%Path)
 | 
						|
            return True
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
   
 | 
						|
 | 
						|
 |