1## @file
2# This file is for installed package information database operations
3#
4# Copyright (c) 2011 - 2014, 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 a DP depex satisfied by current workspace
188    #  (excluding the original distribution's packages to be replaced) for Replace
189    #
190    # @param DpObj:  A distribution object
191    # @param OrigDpGuid: The original distribution's Guid
192    # @param OrigDpVersion: The original distribution's Version
193    #
194    def ReplaceCheckNewDpDepex(self, DpObj, OrigDpGuid, OrigDpVersion):
195        self.PkgsToBeDepend = [(PkgInfo[1], PkgInfo[2]) for PkgInfo in self.WsPkgList]
196        OrigDpPackageList = self.IpiDb.GetPackageListFromDp(OrigDpGuid, OrigDpVersion)
197        for OrigPkgInfo in OrigDpPackageList:
198            Guid, Version = OrigPkgInfo[0], OrigPkgInfo[1]
199            if (Guid, Version) in self.PkgsToBeDepend:
200                self.PkgsToBeDepend.remove((Guid, Version))
201        return self.CheckDpDepexSatisfied(DpObj)
202
203    ## Check whether a DP depex satisfied by current workspace.
204    #
205    # @param DpObj:  A distribution object
206    #
207    def CheckDpDepexSatisfied(self, DpObj):
208        for PkgKey in DpObj.PackageSurfaceArea.keys():
209            PkgObj = DpObj.PackageSurfaceArea[PkgKey]
210            if self.CheckPackageDepexSatisfied(PkgObj, DpObj):
211                continue
212            else:
213                return False
214
215        for ModKey in DpObj.ModuleSurfaceArea.keys():
216            ModObj = DpObj.ModuleSurfaceArea[ModKey]
217            if self.CheckModuleDepexSatisfied(ModObj, DpObj):
218                continue
219            else:
220                return False
221
222        return True
223
224    ## Check whether a DP could be removed from current workspace.
225    #
226    # @param DpGuid:  File's guid
227    # @param DpVersion: File's version
228    # @retval Removable: True if distribution could be removed, False Else
229    # @retval DependModuleList: the list of modules that make distribution can not be removed
230    #
231    def CheckDpDepexForRemove(self, DpGuid, DpVersion):
232        Removable = True
233        DependModuleList = []
234        WsModuleList = self.WsModuleList
235        #
236        # remove modules that included in current DP
237        # List of item (FilePath)
238        DpModuleList = self.IpiDb.GetDpModuleList(DpGuid, DpVersion)
239        for Module in DpModuleList:
240            if Module in WsModuleList:
241                WsModuleList.remove(Module)
242            else:
243                Logger.Warn("UPT\n",
244                            ST.ERR_MODULE_NOT_INSTALLED % Module)
245        #
246        # get packages in current Dp and find the install path
247        # List of item (PkgGuid, PkgVersion, InstallPath)
248        DpPackageList = self.IpiDb.GetPackageListFromDp(DpGuid, DpVersion)
249        DpPackagePathList = []
250        WorkSP = GlobalData.gWORKSPACE
251        for (PkgName, PkgGuid, PkgVersion, DecFile) in self.WsPkgList:
252            if PkgName:
253                pass
254            DecPath = dirname(DecFile)
255            if DecPath.find(WorkSP) > -1:
256                InstallPath = GetRelativePath(DecPath,WorkSP)
257                DecFileRelaPath = GetRelativePath(DecFile,WorkSP)
258            else:
259                InstallPath = DecPath
260                DecFileRelaPath = DecFile
261
262            if (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
263                DpPackagePathList.append(DecFileRelaPath)
264                DpPackageList.remove((PkgGuid, PkgVersion, InstallPath))
265
266        #
267        # the left items in DpPackageList are the packages that installed but not found anymore
268        #
269        for (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
270            Logger.Warn("UPT",
271                        ST.WARN_INSTALLED_PACKAGE_NOT_FOUND%(PkgGuid, PkgVersion, InstallPath))
272
273        #
274        # check modules to see if has dependency on package of current DP
275        #
276        for Module in WsModuleList:
277            if (not VerifyRemoveModuleDep(Module, DpPackagePathList)):
278                Removable = False
279                DependModuleList.append(Module)
280        return (Removable, DependModuleList)
281
282
283    ## Check whether a DP could be replaced by a distribution containing NewDpPkgList
284    # from current workspace.
285    #
286    # @param OrigDpGuid:  original Dp's Guid
287    # @param OrigDpVersion: original Dp's version
288    # @param NewDpPkgList: a list of package information (Guid, Version) in new Dp
289    # @retval Replaceable: True if distribution could be replaced, False Else
290    # @retval DependModuleList: the list of modules that make distribution can not be replaced
291    #
292    def CheckDpDepexForReplace(self, OrigDpGuid, OrigDpVersion, NewDpPkgList):
293        Replaceable = True
294        DependModuleList = []
295        WsModuleList = self.WsModuleList
296        #
297        # remove modules that included in current DP
298        # List of item (FilePath)
299        DpModuleList = self.IpiDb.GetDpModuleList(OrigDpGuid, OrigDpVersion)
300        for Module in DpModuleList:
301            if Module in WsModuleList:
302                WsModuleList.remove(Module)
303            else:
304                Logger.Warn("UPT\n",
305                            ST.ERR_MODULE_NOT_INSTALLED % Module)
306
307        OtherPkgList = NewDpPkgList
308        #
309        # get packages in current Dp and find the install path
310        # List of item (PkgGuid, PkgVersion, InstallPath)
311        DpPackageList = self.IpiDb.GetPackageListFromDp(OrigDpGuid, OrigDpVersion)
312        DpPackagePathList = []
313        WorkSP = GlobalData.gWORKSPACE
314        for (PkgName, PkgGuid, PkgVersion, DecFile) in self.WsPkgList:
315            if PkgName:
316                pass
317            DecPath = dirname(DecFile)
318            if DecPath.find(WorkSP) > -1:
319                InstallPath = GetRelativePath(DecPath,WorkSP)
320                DecFileRelaPath = GetRelativePath(DecFile,WorkSP)
321            else:
322                InstallPath = DecPath
323                DecFileRelaPath = DecFile
324
325            if (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
326                DpPackagePathList.append(DecFileRelaPath)
327                DpPackageList.remove((PkgGuid, PkgVersion, InstallPath))
328            else:
329                OtherPkgList.append((PkgGuid, PkgVersion))
330
331        #
332        # the left items in DpPackageList are the packages that installed but not found anymore
333        #
334        for (PkgGuid, PkgVersion, InstallPath) in DpPackageList:
335            Logger.Warn("UPT",
336                        ST.WARN_INSTALLED_PACKAGE_NOT_FOUND%(PkgGuid, PkgVersion, InstallPath))
337
338        #
339        # check modules to see if it can be satisfied by package not belong to removed DP
340        #
341        for Module in WsModuleList:
342            if (not VerifyReplaceModuleDep(Module, DpPackagePathList, OtherPkgList)):
343                Replaceable = False
344                DependModuleList.append(Module)
345        return (Replaceable, DependModuleList)
346
347
348## check whether module depends on packages in DpPackagePathList, return True
349# if found, False else
350#
351# @param Path: a module path
352# @param DpPackagePathList: a list of Package Paths
353# @retval:  False: module depends on package in DpPackagePathList
354#           True:  module doesn't depend on package in DpPackagePathList
355#
356def VerifyRemoveModuleDep(Path, DpPackagePathList):
357    WorkSP = GlobalData.gWORKSPACE
358
359    try:
360        PomAli = InfPomAlignment(Path, WorkSP, Skip=True)
361
362        for Item in PomAli.GetPackageDependencyList():
363            if Item.GetPackageFilePath() in DpPackagePathList:
364                Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, Item.GetPackageFilePath()))
365                return False
366        else:
367            return True
368    except FatalError, ErrCode:
369        if ErrCode.message == EDK1_INF_ERROR:
370            Logger.Warn("UPT",
371                        ST.WRN_EDK1_INF_FOUND%Path)
372            return True
373        else:
374            return True
375
376## check whether module depends on packages in DpPackagePathList and can not be satisfied by OtherPkgList
377#
378# @param Path: a module path
379# @param DpPackagePathList:  a list of Package Paths
380# @param OtherPkgList:       a list of Package Information (Guid, Version)
381# @retval:  False: module depends on package in DpPackagePathList and can not be satisfied by OtherPkgList
382#           True:  either module doesn't depend on DpPackagePathList or module depends on DpPackagePathList
383#                 but can be satisfied by OtherPkgList
384#
385def VerifyReplaceModuleDep(Path, DpPackagePathList, OtherPkgList):
386    WorkSP = GlobalData.gWORKSPACE
387
388    try:
389        PomAli = InfPomAlignment(Path, WorkSP, Skip=True)
390
391        for Item in PomAli.GetPackageDependencyList():
392            if Item.GetPackageFilePath() in DpPackagePathList:
393                Guid, Version = Item.GetGuid(), Item.GetVersion()
394                if (Guid, Version) not in OtherPkgList:
395                    Logger.Info(ST.MSG_MODULE_DEPEND_ON % (Path, Item.GetPackageFilePath()))
396                    return False
397        else:
398            return True
399    except FatalError, ErrCode:
400        if ErrCode.message == EDK1_INF_ERROR:
401            Logger.Warn("UPT",
402                        ST.WRN_EDK1_INF_FOUND%Path)
403            return True
404        else:
405            return True
406
407
408
409