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