1## @file
2# This file is used to create/update/query/erase a meta file table
3#
4# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
5# SPDX-License-Identifier: BSD-2-Clause-Patent
6#
7
8##
9# Import Modules
10#
11from __future__ import absolute_import
12import uuid
13
14import Common.EdkLogger as EdkLogger
15from Common.BuildToolError import FORMAT_INVALID
16
17from CommonDataClass.DataClass import MODEL_FILE_DSC, MODEL_FILE_DEC, MODEL_FILE_INF, \
18                                      MODEL_FILE_OTHERS
19from Common.DataType import *
20
21class MetaFileTable():
22    # TRICK: use file ID as the part before '.'
23    _ID_STEP_ = 1
24    _ID_MAX_ = 99999999
25
26    ## Constructor
27    def __init__(self, DB, MetaFile, FileType, Temporary, FromItem=None):
28        self.MetaFile = MetaFile
29        self.TableName = ""
30        self.DB = DB
31        self._NumpyTab = None
32
33        self.CurrentContent = []
34        DB.TblFile.append([MetaFile.Name,
35                        MetaFile.Ext,
36                        MetaFile.Dir,
37                        MetaFile.Path,
38                        FileType,
39                        MetaFile.TimeStamp,
40                        FromItem])
41        self.FileId = len(DB.TblFile)
42        self.ID = self.FileId * 10**8
43        if Temporary:
44            self.TableName = "_%s_%s_%s" % (FileType, len(DB.TblFile), uuid.uuid4().hex)
45        else:
46            self.TableName = "_%s_%s" % (FileType, len(DB.TblFile))
47
48    def IsIntegrity(self):
49        try:
50            TimeStamp = self.MetaFile.TimeStamp
51            if not self.CurrentContent:
52                Result = False
53            else:
54                Result = self.CurrentContent[-1][0] < 0
55            if not Result:
56                # update the timestamp in database
57                self.DB.SetFileTimeStamp(self.FileId, TimeStamp)
58                return False
59
60            if TimeStamp != self.DB.GetFileTimeStamp(self.FileId):
61                # update the timestamp in database
62                self.DB.SetFileTimeStamp(self.FileId, TimeStamp)
63                return False
64        except Exception as Exc:
65            EdkLogger.debug(EdkLogger.DEBUG_5, str(Exc))
66            return False
67        return True
68
69    def SetEndFlag(self):
70        self.CurrentContent.append(self._DUMMY_)
71
72    def GetAll(self):
73        return [item for item in self.CurrentContent if item[0] >= 0 and item[-1]>=0]
74
75## Python class representation of table storing module data
76class ModuleTable(MetaFileTable):
77    _COLUMN_ = '''
78        ID REAL PRIMARY KEY,
79        Model INTEGER NOT NULL,
80        Value1 TEXT NOT NULL,
81        Value2 TEXT,
82        Value3 TEXT,
83        Scope1 TEXT,
84        Scope2 TEXT,
85        BelongsToItem REAL NOT NULL,
86        StartLine INTEGER NOT NULL,
87        StartColumn INTEGER NOT NULL,
88        EndLine INTEGER NOT NULL,
89        EndColumn INTEGER NOT NULL,
90        Enabled INTEGER DEFAULT 0
91        '''
92    # used as table end flag, in case the changes to database is not committed to db file
93    _DUMMY_ = [-1, -1, '====', '====', '====', '====', '====', -1, -1, -1, -1, -1, -1]
94
95    ## Constructor
96    def __init__(self, Db, MetaFile, Temporary):
97        MetaFileTable.__init__(self, Db, MetaFile, MODEL_FILE_INF, Temporary)
98
99    ## Insert a record into table Inf
100    #
101    # @param Model:          Model of a Inf item
102    # @param Value1:         Value1 of a Inf item
103    # @param Value2:         Value2 of a Inf item
104    # @param Value3:         Value3 of a Inf item
105    # @param Scope1:         Arch of a Inf item
106    # @param Scope2          Platform os a Inf item
107    # @param BelongsToItem:  The item belongs to which another item
108    # @param StartLine:      StartLine of a Inf item
109    # @param StartColumn:    StartColumn of a Inf item
110    # @param EndLine:        EndLine of a Inf item
111    # @param EndColumn:      EndColumn of a Inf item
112    # @param Enabled:        If this item enabled
113    #
114    def Insert(self, Model, Value1, Value2, Value3, Scope1=TAB_ARCH_COMMON, Scope2=TAB_COMMON,
115               BelongsToItem=-1, StartLine=-1, StartColumn=-1, EndLine=-1, EndColumn=-1, Enabled=0):
116
117        (Value1, Value2, Value3, Scope1, Scope2) = (Value1.strip(), Value2.strip(), Value3.strip(), Scope1.strip(), Scope2.strip())
118        self.ID = self.ID + self._ID_STEP_
119        if self.ID >= (MODEL_FILE_INF + self._ID_MAX_):
120            self.ID = MODEL_FILE_INF + self._ID_STEP_
121
122        row = [ self.ID,
123                Model,
124                Value1,
125                Value2,
126                Value3,
127                Scope1,
128                Scope2,
129                BelongsToItem,
130                StartLine,
131                StartColumn,
132                EndLine,
133                EndColumn,
134                Enabled
135            ]
136        self.CurrentContent.append(row)
137        return self.ID
138
139    ## Query table
140    #
141    # @param    Model:      The Model of Record
142    # @param    Arch:       The Arch attribute of Record
143    # @param    Platform    The Platform attribute of Record
144    #
145    # @retval:       A recordSet of all found records
146    #
147    def Query(self, Model, Arch=None, Platform=None, BelongsToItem=None):
148
149        QueryTab = self.CurrentContent
150        result = [item for item in QueryTab if item[1] == Model and item[-1]>=0 ]
151
152        if Arch is not None and Arch != TAB_ARCH_COMMON:
153            ArchList = set(['COMMON'])
154            ArchList.add(Arch)
155            result = [item for item in result if item[5] in ArchList]
156
157        if Platform is not None and Platform != TAB_COMMON:
158            Platformlist = set( ['COMMON','DEFAULT'])
159            Platformlist.add(Platform)
160            result = [item for item in result if item[6] in Platformlist]
161
162        if BelongsToItem is not None:
163            result = [item for item in result if item[7] == BelongsToItem]
164
165        result = [ [r[2],r[3],r[4],r[5],r[6],r[0],r[9]] for r in result ]
166        return result
167
168## Python class representation of table storing package data
169class PackageTable(MetaFileTable):
170    _COLUMN_ = '''
171        ID REAL PRIMARY KEY,
172        Model INTEGER NOT NULL,
173        Value1 TEXT NOT NULL,
174        Value2 TEXT,
175        Value3 TEXT,
176        Scope1 TEXT,
177        Scope2 TEXT,
178        BelongsToItem REAL NOT NULL,
179        StartLine INTEGER NOT NULL,
180        StartColumn INTEGER NOT NULL,
181        EndLine INTEGER NOT NULL,
182        EndColumn INTEGER NOT NULL,
183        Enabled INTEGER DEFAULT 0
184        '''
185    # used as table end flag, in case the changes to database is not committed to db file
186    _DUMMY_ = [-1, -1, '====', '====', '====', '====', '====', -1, -1, -1, -1, -1, -1]
187
188    ## Constructor
189    def __init__(self, Cursor, MetaFile, Temporary):
190        MetaFileTable.__init__(self, Cursor, MetaFile, MODEL_FILE_DEC, Temporary)
191
192    ## Insert table
193    #
194    # Insert a record into table Dec
195    #
196    # @param Model:          Model of a Dec item
197    # @param Value1:         Value1 of a Dec item
198    # @param Value2:         Value2 of a Dec item
199    # @param Value3:         Value3 of a Dec item
200    # @param Scope1:         Arch of a Dec item
201    # @param Scope2:         Module type of a Dec item
202    # @param BelongsToItem:  The item belongs to which another item
203    # @param StartLine:      StartLine of a Dec item
204    # @param StartColumn:    StartColumn of a Dec item
205    # @param EndLine:        EndLine of a Dec item
206    # @param EndColumn:      EndColumn of a Dec item
207    # @param Enabled:        If this item enabled
208    #
209    def Insert(self, Model, Value1, Value2, Value3, Scope1=TAB_ARCH_COMMON, Scope2=TAB_COMMON,
210               BelongsToItem=-1, StartLine=-1, StartColumn=-1, EndLine=-1, EndColumn=-1, Enabled=0):
211        (Value1, Value2, Value3, Scope1, Scope2) = (Value1.strip(), Value2.strip(), Value3.strip(), Scope1.strip(), Scope2.strip())
212        self.ID = self.ID + self._ID_STEP_
213
214        row = [ self.ID,
215                Model,
216                Value1,
217                Value2,
218                Value3,
219                Scope1,
220                Scope2,
221                BelongsToItem,
222                StartLine,
223                StartColumn,
224                EndLine,
225                EndColumn,
226                Enabled
227            ]
228        self.CurrentContent.append(row)
229        return self.ID
230
231    ## Query table
232    #
233    # @param    Model:  The Model of Record
234    # @param    Arch:   The Arch attribute of Record
235    #
236    # @retval:       A recordSet of all found records
237    #
238    def Query(self, Model, Arch=None):
239
240        QueryTab = self.CurrentContent
241        result = [item for item in QueryTab if item[1] == Model and item[-1]>=0 ]
242
243        if Arch is not None and Arch != TAB_ARCH_COMMON:
244            ArchList = set(['COMMON'])
245            ArchList.add(Arch)
246            result = [item for item in result if item[5] in ArchList]
247
248        return [[r[2], r[3], r[4], r[5], r[6], r[0], r[8]] for r in result]
249
250    def GetValidExpression(self, TokenSpaceGuid, PcdCName):
251
252        QueryTab = self.CurrentContent
253        result = [[item[2], item[8]] for item in QueryTab if item[3] == TokenSpaceGuid and item[4] == PcdCName]
254        validateranges = []
255        validlists = []
256        expressions = []
257        try:
258            for row in result:
259                comment = row[0]
260
261                LineNum = row[1]
262                comment = comment.strip("#")
263                comment = comment.strip()
264                oricomment = comment
265                if comment.startswith("@ValidRange"):
266                    comment = comment.replace("@ValidRange", "", 1)
267                    validateranges.append(comment.split("|")[1].strip())
268                if comment.startswith("@ValidList"):
269                    comment = comment.replace("@ValidList", "", 1)
270                    validlists.append(comment.split("|")[1].strip())
271                if comment.startswith("@Expression"):
272                    comment = comment.replace("@Expression", "", 1)
273                    expressions.append(comment.split("|")[1].strip())
274        except Exception as Exc:
275            ValidType = ""
276            if oricomment.startswith("@ValidRange"):
277                ValidType = "@ValidRange"
278            if oricomment.startswith("@ValidList"):
279                ValidType = "@ValidList"
280            if oricomment.startswith("@Expression"):
281                ValidType = "@Expression"
282            EdkLogger.error('Parser', FORMAT_INVALID, "The syntax for %s of PCD %s.%s is incorrect" % (ValidType, TokenSpaceGuid, PcdCName),
283                            ExtraData=oricomment, File=self.MetaFile, Line=LineNum)
284            return set(), set(), set()
285        return set(validateranges), set(validlists), set(expressions)
286
287## Python class representation of table storing platform data
288class PlatformTable(MetaFileTable):
289    _COLUMN_ = '''
290        ID REAL PRIMARY KEY,
291        Model INTEGER NOT NULL,
292        Value1 TEXT NOT NULL,
293        Value2 TEXT,
294        Value3 TEXT,
295        Scope1 TEXT,
296        Scope2 TEXT,
297        Scope3 TEXT,
298        BelongsToItem REAL NOT NULL,
299        FromItem REAL NOT NULL,
300        StartLine INTEGER NOT NULL,
301        StartColumn INTEGER NOT NULL,
302        EndLine INTEGER NOT NULL,
303        EndColumn INTEGER NOT NULL,
304        Enabled INTEGER DEFAULT 0
305        '''
306    # used as table end flag, in case the changes to database is not committed to db file
307    _DUMMY_ = [-1, -1, '====', '====', '====', '====', '====','====', -1, -1, -1, -1, -1, -1, -1]
308
309    ## Constructor
310    def __init__(self, Cursor, MetaFile, Temporary, FromItem=0):
311        MetaFileTable.__init__(self, Cursor, MetaFile, MODEL_FILE_DSC, Temporary, FromItem)
312
313    ## Insert table
314    #
315    # Insert a record into table Dsc
316    #
317    # @param Model:          Model of a Dsc item
318    # @param Value1:         Value1 of a Dsc item
319    # @param Value2:         Value2 of a Dsc item
320    # @param Value3:         Value3 of a Dsc item
321    # @param Scope1:         Arch of a Dsc item
322    # @param Scope2:         Module type of a Dsc item
323    # @param BelongsToItem:  The item belongs to which another item
324    # @param FromItem:       The item belongs to which dsc file
325    # @param StartLine:      StartLine of a Dsc item
326    # @param StartColumn:    StartColumn of a Dsc item
327    # @param EndLine:        EndLine of a Dsc item
328    # @param EndColumn:      EndColumn of a Dsc item
329    # @param Enabled:        If this item enabled
330    #
331    def Insert(self, Model, Value1, Value2, Value3, Scope1=TAB_ARCH_COMMON, Scope2=TAB_COMMON, Scope3=TAB_DEFAULT_STORES_DEFAULT,BelongsToItem=-1,
332               FromItem=-1, StartLine=-1, StartColumn=-1, EndLine=-1, EndColumn=-1, Enabled=1):
333        (Value1, Value2, Value3, Scope1, Scope2, Scope3) = (Value1.strip(), Value2.strip(), Value3.strip(), Scope1.strip(), Scope2.strip(), Scope3.strip())
334        self.ID = self.ID + self._ID_STEP_
335
336        row = [ self.ID,
337                Model,
338                Value1,
339                Value2,
340                Value3,
341                Scope1,
342                Scope2,
343                Scope3,
344                BelongsToItem,
345                FromItem,
346                StartLine,
347                StartColumn,
348                EndLine,
349                EndColumn,
350                Enabled
351            ]
352        self.CurrentContent.append(row)
353        return self.ID
354
355
356    ## Query table
357    #
358    # @param Model:          The Model of Record
359    # @param Scope1:         Arch of a Dsc item
360    # @param Scope2:         Module type of a Dsc item
361    # @param BelongsToItem:  The item belongs to which another item
362    # @param FromItem:       The item belongs to which dsc file
363    #
364    # @retval:       A recordSet of all found records
365    #
366    def Query(self, Model, Scope1=None, Scope2=None, BelongsToItem=None, FromItem=None):
367
368        QueryTab = self.CurrentContent
369        result = [item for item in QueryTab if item[1] == Model and item[-1]>0 ]
370        if Scope1 is not None and Scope1 != TAB_ARCH_COMMON:
371            Sc1 = set(['COMMON'])
372            Sc1.add(Scope1)
373            result = [item for item in result if item[5] in Sc1]
374        Sc2 = set( ['COMMON','DEFAULT'])
375        if Scope2 and Scope2 != TAB_COMMON:
376            if '.' in Scope2:
377                Index = Scope2.index('.')
378                NewScope = TAB_COMMON + Scope2[Index:]
379                Sc2.add(NewScope)
380            Sc2.add(Scope2)
381            result = [item for item in result if item[6] in Sc2]
382
383        if BelongsToItem is not None:
384            result = [item for item in result if item[8] == BelongsToItem]
385        else:
386            result = [item for item in result if item[8] < 0]
387        if FromItem is not None:
388            result = [item for item in result if item[9] == FromItem]
389
390        result = [ [r[2],r[3],r[4],r[5],r[6],r[7],r[0],r[10]] for r in result ]
391        return result
392
393    def DisableComponent(self,comp_id):
394        for item in self.CurrentContent:
395            if item[0] == comp_id or item[8] == comp_id:
396                item[-1] = -1
397
398## Factory class to produce different storage for different type of meta-file
399class MetaFileStorage(object):
400    _FILE_TABLE_ = {
401        MODEL_FILE_INF      :   ModuleTable,
402        MODEL_FILE_DEC      :   PackageTable,
403        MODEL_FILE_DSC      :   PlatformTable,
404        MODEL_FILE_OTHERS   :   MetaFileTable,
405    }
406
407    _FILE_TYPE_ = {
408        ".inf"  : MODEL_FILE_INF,
409        ".dec"  : MODEL_FILE_DEC,
410        ".dsc"  : MODEL_FILE_DSC,
411    }
412    _ObjectCache = {}
413    ## Constructor
414    def __new__(Class, Cursor, MetaFile, FileType=None, Temporary=False, FromItem=None):
415        # no type given, try to find one
416        key = (MetaFile.Path, FileType,Temporary,FromItem)
417        if key in Class._ObjectCache:
418            return Class._ObjectCache[key]
419        if not FileType:
420            if MetaFile.Type in self._FILE_TYPE_:
421                FileType = Class._FILE_TYPE_[MetaFile.Type]
422            else:
423                FileType = MODEL_FILE_OTHERS
424
425        # don't pass the type around if it's well known
426        if FileType == MODEL_FILE_OTHERS:
427            Args = (Cursor, MetaFile, FileType, Temporary)
428        else:
429            Args = (Cursor, MetaFile, Temporary)
430        if FromItem:
431            Args = Args + (FromItem,)
432
433        # create the storage object and return it to caller
434        reval = Class._FILE_TABLE_[FileType](*Args)
435        if not Temporary:
436            Class._ObjectCache[key] = reval
437        return reval
438
439