https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Bob Feng <bob.c.feng@intel.com>
		
			
				
	
	
		
			449 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
## @file
 | 
						|
# This file is for installed package information database operations
 | 
						|
#
 | 
						|
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
#
 | 
						|
# SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
#
 | 
						|
 | 
						|
'''
 | 
						|
Dependency
 | 
						|
'''
 | 
						|
 | 
						|
##
 | 
						|
# Import Modules
 | 
						|
#
 | 
						|
from os.path import dirname
 | 
						|
import os
 | 
						|
 | 
						|
import Logger.Log as Logger
 | 
						|
from Logger import StringTable as ST
 | 
						|
from Library.Parsing import GetWorkspacePackage
 | 
						|
from Library.Parsing import GetWorkspaceModule
 | 
						|
from Library.Parsing import GetPkgInfoFromDec
 | 
						|
from Library.Misc import GetRelativePath
 | 
						|
from Library import GlobalData
 | 
						|
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, ToBeInstalledPkgList=None):
 | 
						|
        self.IpiDb = Datab
 | 
						|
        self.WsPkgList = GetWorkspacePackage()
 | 
						|
        self.WsModuleList = GetWorkspaceModule()
 | 
						|
 | 
						|
        self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList]
 | 
						|
 | 
						|
        # Add package info from the DIST to be installed.
 | 
						|
        self.PkgsToBeDepend.extend(self.GenToBeInstalledPkgList(ToBeInstalledPkgList))
 | 
						|
 | 
						|
    def GenToBeInstalledPkgList(self, ToBeInstalledPkgList):
 | 
						|
        if not ToBeInstalledPkgList:
 | 
						|
            return []
 | 
						|
        RtnList = []
 | 
						|
        for Dist in ToBeInstalledPkgList:
 | 
						|
            for Package in Dist.PackageSurfaceArea:
 | 
						|
                RtnList.append((Package[0], Package[1]))
 | 
						|
 | 
						|
        return RtnList
 | 
						|
 | 
						|
    ## 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 is None:
 | 
						|
                    Result = False
 | 
						|
                    break
 | 
						|
                for GuidVerPair in DpObj.PackageSurfaceArea.keys():
 | 
						|
                    if Dep.GetGuid() == GuidVerPair[0]:
 | 
						|
                        if Dep.GetVersion() is 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 satisfied
 | 
						|
    #          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):
 | 
						|
        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):
 | 
						|
        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):
 | 
						|
    try:
 | 
						|
        for Item in GetPackagePath(Path):
 | 
						|
            if Item in DpPackagePathList:
 | 
						|
                DecPath = os.path.normpath(os.path.join(GlobalData.gWORKSPACE, Item))
 | 
						|
                Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, DecPath))
 | 
						|
                return False
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
    except FatalError as ErrCode:
 | 
						|
        if ErrCode.message == EDK1_INF_ERROR:
 | 
						|
            Logger.Warn("UPT",
 | 
						|
                        ST.WRN_EDK1_INF_FOUND%Path)
 | 
						|
            return True
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
 | 
						|
# # GetPackagePath
 | 
						|
#
 | 
						|
# Get Dependency package path from an Inf file path
 | 
						|
#
 | 
						|
def GetPackagePath(InfPath):
 | 
						|
    PackagePath = []
 | 
						|
    if os.path.exists(InfPath):
 | 
						|
        FindSection = False
 | 
						|
        for Line in open(InfPath).readlines():
 | 
						|
            Line = Line.strip()
 | 
						|
            if not Line:
 | 
						|
                continue
 | 
						|
            if Line.startswith('#'):
 | 
						|
                continue
 | 
						|
            if Line.startswith('[Packages') and Line.endswith(']'):
 | 
						|
                FindSection = True
 | 
						|
                continue
 | 
						|
            if Line.startswith('[') and Line.endswith(']') and FindSection:
 | 
						|
                break
 | 
						|
            if FindSection:
 | 
						|
                PackagePath.append(os.path.normpath(Line))
 | 
						|
 | 
						|
    return PackagePath
 | 
						|
 | 
						|
## 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):
 | 
						|
    try:
 | 
						|
        for Item in GetPackagePath(Path):
 | 
						|
            if Item in DpPackagePathList:
 | 
						|
                DecPath = os.path.normpath(os.path.join(GlobalData.gWORKSPACE, Item))
 | 
						|
                Name, Guid, Version = GetPkgInfoFromDec(DecPath)
 | 
						|
                if (Guid, Version) not in OtherPkgList:
 | 
						|
                    Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, DecPath))
 | 
						|
                    return False
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
    except FatalError as ErrCode:
 | 
						|
        if ErrCode.message == EDK1_INF_ERROR:
 | 
						|
            Logger.Warn("UPT",
 | 
						|
                        ST.WRN_EDK1_INF_FOUND%Path)
 | 
						|
            return True
 | 
						|
        else:
 | 
						|
            return True
 |