diff options
Diffstat (limited to 'BaseTools/Source/Python/Ecc')
-rw-r--r-- | BaseTools/Source/Python/Ecc/Check.py | 175 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/Configuration.py | 10 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/Database.py | 12 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/Ecc.py | 29 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/MetaDataParser.py | 190 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaDataTable.py | 215 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileParser.py | 1849 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileTable.py | 332 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/MetaFileWorkspace/__init__.py | 15 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/c.py | 92 | ||||
-rw-r--r-- | BaseTools/Source/Python/Ecc/config.ini | 8 |
11 files changed, 2845 insertions, 82 deletions
diff --git a/BaseTools/Source/Python/Ecc/Check.py b/BaseTools/Source/Python/Ecc/Check.py index 6f5f9fd0b5..73d7318de0 100644 --- a/BaseTools/Source/Python/Ecc/Check.py +++ b/BaseTools/Source/Python/Ecc/Check.py @@ -15,6 +15,7 @@ import re from CommonDataClass.DataClass import *
from Common.DataType import SUP_MODULE_LIST_STRING, TAB_VALUE_SPLIT
from EccToolError import *
+from MetaDataParser import ParseHeaderCommentSection
import EccGlobalData
import c
@@ -48,7 +49,7 @@ class Check(object): if EccGlobalData.gConfig.GeneralCheckNonAcsii == '1' or EccGlobalData.gConfig.GeneralCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking Non-ACSII char in file ...")
SqlCommand = """select ID, FullPath, ExtName from File"""
- RecordSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
+ RecordSet = EccGlobalData.gDb.TblFile.Exec(SqlCommand)
for Record in RecordSet:
if Record[2].upper() not in EccGlobalData.gConfig.BinaryExtList:
op = open(Record[1]).readlines()
@@ -415,13 +416,81 @@ class Check(object): elif Ext in ('.inf', '.dec', '.dsc', '.fdf'):
FullName = os.path.join(Dirpath, F)
op = open(FullName).readlines()
- if not op[0].startswith('## @file') and op[6].startswith('## @file') and op[7].startswith('## @file'):
+ FileLinesList = op
+ LineNo = 0
+ CurrentSection = MODEL_UNKNOWN
+ HeaderSectionLines = []
+ HeaderCommentStart = False
+ HeaderCommentEnd = False
+
+ for Line in FileLinesList:
+ LineNo = LineNo + 1
+ Line = Line.strip()
+ if (LineNo < len(FileLinesList) - 1):
+ NextLine = FileLinesList[LineNo].strip()
+
+ #
+ # blank line
+ #
+ if (Line == '' or not Line) and LineNo == len(FileLinesList):
+ LastSectionFalg = True
+
+ #
+ # check whether file header comment section started
+ #
+ if Line.startswith('#') and \
+ (Line.find('@file') > -1) and \
+ not HeaderCommentStart:
+ if CurrentSection != MODEL_UNKNOWN:
+ SqlStatement = """ select ID from File where FullPath like '%s'""" % FullName
+ ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
+ for Result in ResultSet:
+ Msg = 'INF/DEC/DSC/FDF file header comment should begin with ""## @file"" or ""# @file""at the very top file'
+ EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
+
+ else:
+ CurrentSection = MODEL_IDENTIFIER_FILE_HEADER
+ #
+ # Append the first line to section lines.
+ #
+ HeaderSectionLines.append((Line, LineNo))
+ HeaderCommentStart = True
+ continue
+
+ #
+ # Collect Header content.
+ #
+ if (Line.startswith('#') and CurrentSection == MODEL_IDENTIFIER_FILE_HEADER) and\
+ HeaderCommentStart and not Line.startswith('##') and not\
+ HeaderCommentEnd and NextLine != '':
+ HeaderSectionLines.append((Line, LineNo))
+ continue
+ #
+ # Header content end
+ #
+ if (Line.startswith('##') or not Line.strip().startswith("#")) and HeaderCommentStart \
+ and not HeaderCommentEnd:
+ if Line.startswith('##'):
+ HeaderCommentEnd = True
+ HeaderSectionLines.append((Line, LineNo))
+ ParseHeaderCommentSection(HeaderSectionLines, FullName)
+ break
+ if HeaderCommentStart == False:
SqlStatement = """ select ID from File where FullPath like '%s'""" % FullName
ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
for Result in ResultSet:
- Msg = 'INF/DEC/DSC/FDF file header comment should begin with ""## @file""'
+ Msg = 'INF/DEC/DSC/FDF file header comment should begin with ""## @file"" or ""# @file"" at the very top file'
EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
+ if HeaderCommentEnd == False:
+ SqlStatement = """ select ID from File where FullPath like '%s'""" % FullName
+ ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
+ for Result in ResultSet:
+ Msg = 'INF/DEC/DSC/FDF file header comment should end with ""##"" at the end of file header comment block'
+ # Check whether File header Comment End with '##'
+ if EccGlobalData.gConfig.HeaderCheckFileCommentEnd == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
+ EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
+
# Check whether the function headers are followed Doxygen special documentation blocks in section 2.3.5
def DoxygenCheckFunctionHeader(self):
@@ -504,9 +573,9 @@ class Check(object): def MetaDataFileCheckLibraryInstance(self):
if EccGlobalData.gConfig.MetaDataFileCheckLibraryInstance == '1' or EccGlobalData.gConfig.MetaDataFileCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking for library instance type issue ...")
- SqlCommand = """select A.ID, A.Value2, B.Value2 from Inf as A left join Inf as B
- where A.Value1 = 'LIBRARY_CLASS' and A.Model = %s
- and B.Value1 = 'MODULE_TYPE' and B.Model = %s and A.BelongsToFile = B.BelongsToFile
+ SqlCommand = """select A.ID, A.Value3, B.Value3 from Inf as A left join Inf as B
+ where A.Value2 = 'LIBRARY_CLASS' and A.Model = %s
+ and B.Value2 = 'MODULE_TYPE' and B.Model = %s and A.BelongsToFile = B.BelongsToFile
group by A.BelongsToFile""" % (MODEL_META_DATA_HEADER, MODEL_META_DATA_HEADER)
RecordSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
LibraryClasses = {}
@@ -528,8 +597,8 @@ class Check(object): if Record[2] != 'BASE' and Record[2] not in SupModType:
EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_LIBRARY_INSTANCE_2, OtherMsg="The Library Class '%s' does not specify its supported module types" % (List[0]), BelongsToTable='Inf', BelongsToItem=Record[0])
- SqlCommand = """select A.ID, A.Value1, B.Value2 from Inf as A left join Inf as B
- where A.Model = %s and B.Value1 = '%s' and B.Model = %s
+ SqlCommand = """select A.ID, A.Value1, B.Value3 from Inf as A left join Inf as B
+ where A.Model = %s and B.Value2 = '%s' and B.Model = %s
and B.BelongsToFile = A.BelongsToFile""" \
% (MODEL_EFI_LIBRARY_CLASS, 'MODULE_TYPE', MODEL_META_DATA_HEADER)
RecordSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
@@ -558,11 +627,13 @@ class Check(object): SqlCommand = """select ID, Value1, Value2 from Dsc where Model = %s""" % MODEL_EFI_LIBRARY_CLASS
LibraryClasses = EccGlobalData.gDb.TblDsc.Exec(SqlCommand)
for LibraryClass in LibraryClasses:
- if LibraryClass[1].upper() != 'NULL':
+ if LibraryClass[1].upper() == 'NULL' or LibraryClass[1].startswith('!ifdef') or LibraryClass[1].startswith('!ifndef') or LibraryClass[1].endswith('!endif'):
+ continue
+ else:
LibraryIns = os.path.normpath(os.path.join(EccGlobalData.gWorkspace, LibraryClass[2]))
- SqlCommand = """select Value2 from Inf where BelongsToFile =
+ SqlCommand = """select Value3 from Inf where BelongsToFile =
(select ID from File where lower(FullPath) = lower('%s'))
- and Value1 = '%s'""" % (LibraryIns, 'LIBRARY_CLASS')
+ and Value2 = '%s'""" % (LibraryIns, 'LIBRARY_CLASS')
RecordSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
IsFound = False
for Record in RecordSet:
@@ -591,8 +662,8 @@ class Check(object): EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_LIBRARY_NO_USE, OtherMsg="The Library Class [%s] is not used in any platform" % (Record[1]), BelongsToTable='Inf', BelongsToItem=Record[0])
SqlCommand = """
select A.ID, A.Value1, A.BelongsToFile, A.StartLine, B.StartLine from Dsc as A left join Dsc as B
- where A.Model = %s and B.Model = %s and A.Value3 = B.Value3 and A.Arch = B.Arch and A.ID <> B.ID
- and A.Value1 = B.Value1 and A.StartLine <> B.StartLine and B.BelongsToFile = A.BelongsToFile""" \
+ where A.Model = %s and B.Model = %s and A.Scope1 = B.Scope1 and A.Scope2 = B.Scope2 and A.ID <> B.ID
+ and A.Value1 = B.Value1 and A.Value2 <> B.Value2 and A.BelongsToItem = -1 and B.BelongsToItem = -1 and A.StartLine <> B.StartLine and B.BelongsToFile = A.BelongsToFile""" \
% (MODEL_EFI_LIBRARY_CLASS, MODEL_EFI_LIBRARY_CLASS)
RecordSet = EccGlobalData.gDb.TblDsc.Exec(SqlCommand)
for Record in RecordSet:
@@ -631,9 +702,10 @@ class Check(object): if EccGlobalData.gConfig.MetaDataFileCheckPcdDuplicate == '1' or EccGlobalData.gConfig.MetaDataFileCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking for duplicate PCDs defined in both DSC and FDF files ...")
SqlCommand = """
- select A.ID, A.Value2, A.BelongsToFile, B.ID, B.Value2, B.BelongsToFile from Dsc as A, Fdf as B
+ select A.ID, A.Value1, A.Value2, A.BelongsToFile, B.ID, B.Value1, B.Value2, B.BelongsToFile from Dsc as A, Fdf as B
where A.Model >= %s and A.Model < %s
and B.Model >= %s and B.Model < %s
+ and A.Value1 = B.Value1
and A.Value2 = B.Value2
and A.Enabled > -1
and B.Enabled > -1
@@ -641,71 +713,74 @@ class Check(object): """ % (MODEL_PCD, MODEL_META_DATA_HEADER, MODEL_PCD, MODEL_META_DATA_HEADER)
RecordSet = EccGlobalData.gDb.TblDsc.Exec(SqlCommand)
for Record in RecordSet:
- SqlCommand1 = """select Name from File where ID = %s""" % Record[2]
- SqlCommand2 = """select Name from File where ID = %s""" % Record[5]
+ SqlCommand1 = """select Name from File where ID = %s""" % Record[3]
+ SqlCommand2 = """select Name from File where ID = %s""" % Record[7]
DscFileName = os.path.splitext(EccGlobalData.gDb.TblDsc.Exec(SqlCommand1)[0][0])[0]
FdfFileName = os.path.splitext(EccGlobalData.gDb.TblDsc.Exec(SqlCommand2)[0][0])[0]
if DscFileName != FdfFileName:
continue
- if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, Record[1]):
- EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, OtherMsg="The PCD [%s] is defined in both FDF file and DSC file" % (Record[1]), BelongsToTable='Dsc', BelongsToItem=Record[0])
- if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, Record[3]):
- EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, OtherMsg="The PCD [%s] is defined in both FDF file and DSC file" % (Record[4]), BelongsToTable='Fdf', BelongsToItem=Record[3])
+ if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, Record[1] + '.' + Record[2]):
+ EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, OtherMsg="The PCD [%s] is defined in both FDF file and DSC file" % (Record[1] + '.' + Record[2]), BelongsToTable='Dsc', BelongsToItem=Record[0])
+ if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, Record[5] + '.' + Record[6]):
+ EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, OtherMsg="The PCD [%s] is defined in both FDF file and DSC file" % (Record[5] + '.' + Record[6]), BelongsToTable='Fdf', BelongsToItem=Record[4])
EdkLogger.quiet("Checking for duplicate PCDs defined in DEC files ...")
SqlCommand = """
- select A.ID, A.Value2 from Dec as A, Dec as B
+ select A.ID, A.Value1, A.Value2, A.Model, B.Model from Dec as A left join Dec as B
where A.Model >= %s and A.Model < %s
and B.Model >= %s and B.Model < %s
+ and A.Value1 = B.Value1
and A.Value2 = B.Value2
- and ((A.Arch = B.Arch) and (A.Arch != 'COMMON' or B.Arch != 'COMMON'))
- and A.ID != B.ID
+ and A.Scope1 = B.Scope1
+ and A.ID <> B.ID
+ and A.Model = B.Model
and A.Enabled > -1
and B.Enabled > -1
and A.BelongsToFile = B.BelongsToFile
group by A.ID
""" % (MODEL_PCD, MODEL_META_DATA_HEADER, MODEL_PCD, MODEL_META_DATA_HEADER)
- RecordSet = EccGlobalData.gDb.TblDsc.Exec(SqlCommand)
+ RecordSet = EccGlobalData.gDb.TblDec.Exec(SqlCommand)
for Record in RecordSet:
- if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, Record[1]):
- EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, OtherMsg="The PCD [%s] is defined duplicated in DEC file" % (Record[1]), BelongsToTable='Dec', BelongsToItem=Record[0])
+ RecordCat = Record[1] + '.' + Record[2]
+ if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, RecordCat):
+ EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_DUPLICATE, OtherMsg="The PCD [%s] is defined duplicated in DEC file" % RecordCat, BelongsToTable='Dec', BelongsToItem=Record[0])
# Check whether PCD settings in the FDF file can only be related to flash.
def MetaDataFileCheckPcdFlash(self):
if EccGlobalData.gConfig.MetaDataFileCheckPcdFlash == '1' or EccGlobalData.gConfig.MetaDataFileCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking only Flash related PCDs are used in FDF ...")
SqlCommand = """
- select ID, Value2, BelongsToFile from Fdf as A
+ select ID, Value1, Value2, BelongsToFile from Fdf as A
where A.Model >= %s and Model < %s
and A.Enabled > -1
and A.Value2 not like '%%Flash%%'
""" % (MODEL_PCD, MODEL_META_DATA_HEADER)
RecordSet = EccGlobalData.gDb.TblFdf.Exec(SqlCommand)
for Record in RecordSet:
- if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_FLASH, Record[1]):
- EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_FLASH, OtherMsg="The PCD [%s] defined in FDF file is not related to Flash" % (Record[1]), BelongsToTable='Fdf', BelongsToItem=Record[0])
+ if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_FLASH, Record[1] + '.' + Record[2]):
+ EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_FLASH, OtherMsg="The PCD [%s] defined in FDF file is not related to Flash" % (Record[1] + '.' + Record[2]), BelongsToTable='Fdf', BelongsToItem=Record[0])
# Check whether PCDs used in Inf files but not specified in Dsc or FDF files
def MetaDataFileCheckPcdNoUse(self):
if EccGlobalData.gConfig.MetaDataFileCheckPcdNoUse == '1' or EccGlobalData.gConfig.MetaDataFileCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking for non-specified PCDs ...")
SqlCommand = """
- select ID, Value2, BelongsToFile from Inf as A
+ select ID, Value1, Value2, BelongsToFile from Inf as A
where A.Model >= %s and Model < %s
and A.Enabled > -1
- and A.Value2 not in
- (select Value2 from Dsc as B
+ and (A.Value1, A.Value2) not in
+ (select Value1, Value2 from Dsc as B
where B.Model >= %s and B.Model < %s
and B.Enabled > -1)
- and A.Value2 not in
- (select Value2 from Fdf as C
+ and (A.Value1, A.Value2) not in
+ (select Value1, Value2 from Fdf as C
where C.Model >= %s and C.Model < %s
and C.Enabled > -1)
""" % (MODEL_PCD, MODEL_META_DATA_HEADER, MODEL_PCD, MODEL_META_DATA_HEADER, MODEL_PCD, MODEL_META_DATA_HEADER)
RecordSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
for Record in RecordSet:
- if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_NO_USE, Record[1]):
- EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_NO_USE, OtherMsg="The PCD [%s] defined in INF file is not specified in either DSC or FDF files" % (Record[1]), BelongsToTable='Inf', BelongsToItem=Record[0])
+ if not EccGlobalData.gException.IsException(ERROR_META_DATA_FILE_CHECK_PCD_NO_USE, Record[1] + '.' + Record[2]):
+ EccGlobalData.gDb.TblReport.Insert(ERROR_META_DATA_FILE_CHECK_PCD_NO_USE, OtherMsg="The PCD [%s] defined in INF file is not specified in either DSC or FDF files" % (Record[1] + '.' + Record[2]), BelongsToTable='Inf', BelongsToItem=Record[0])
# Check whether having duplicate guids defined for Guid/Protocol/Ppi
def MetaDataFileCheckGuidDuplicate(self):
@@ -729,7 +804,7 @@ class Check(object): if EccGlobalData.gConfig.MetaDataFileCheckModuleFileNoUse == '1' or EccGlobalData.gConfig.MetaDataFileCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking for no used module files ...")
SqlCommand = """
- select upper(Path) from File where ID in (select BelongsToFile from INF where BelongsToFile != -1)
+ select upper(Path) from File where ID in (select BelongsToFile from Inf where BelongsToFile != -1)
"""
InfPathSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
InfPathList = []
@@ -756,15 +831,15 @@ class Check(object): if EccGlobalData.gConfig.MetaDataFileCheckPcdType == '1' or EccGlobalData.gConfig.MetaDataFileCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
EdkLogger.quiet("Checking for pcd type in c code function usage ...")
SqlCommand = """
- select ID, Model, Value1, BelongsToFile from INF where Model > %s and Model < %s
+ select ID, Model, Value1, Value2, BelongsToFile from INF where Model > %s and Model < %s
""" % (MODEL_PCD, MODEL_META_DATA_HEADER)
PcdSet = EccGlobalData.gDb.TblInf.Exec(SqlCommand)
for Pcd in PcdSet:
Model = Pcd[1]
PcdName = Pcd[2]
- if len(Pcd[2].split(".")) > 1:
- PcdName = Pcd[2].split(".")[1]
- BelongsToFile = Pcd[3]
+ if Pcd[3]:
+ PcdName = Pcd[3]
+ BelongsToFile = Pcd[4]
SqlCommand = """
select ID from File where FullPath in
(select B.Path || '\\' || A.Value1 from INF as A, File as B where A.Model = %s and A.BelongsToFile = %s
@@ -809,9 +884,9 @@ class Check(object): EdkLogger.quiet("Checking for pcd type in c code function usage ...")
Table = EccGlobalData.gDb.TblInf
SqlCommand = """
- select A.ID, A.Value2, A.BelongsToFile, B.BelongsToFile from %s as A, %s as B
- where A.Value1 = 'FILE_GUID' and B.Value1 = 'FILE_GUID' and
- A.Value2 = B.Value2 and A.ID <> B.ID group by A.ID
+ select A.ID, A.Value3, A.BelongsToFile, B.BelongsToFile from %s as A, %s as B
+ where A.Value2 = 'FILE_GUID' and B.Value2 = 'FILE_GUID' and
+ A.Value3 = B.Value3 and A.ID <> B.ID group by A.ID
""" % (Table.Table, Table.Table)
RecordSet = Table.Exec(SqlCommand)
for Record in RecordSet:
@@ -836,7 +911,7 @@ class Check(object): select A.ID, A.Value1 from %s as A, %s as B
where A.Model = %s and B.Model = %s
and A.Value1 = B.Value1 and A.ID <> B.ID
- and A.Arch = B.Arch
+ and A.Scope1 = B.Scope1
and A.Enabled > -1
and B.Enabled > -1
group by A.ID
@@ -857,16 +932,16 @@ class Check(object): if Model == MODEL_EFI_PPI:
Name = 'ppi'
SqlCommand = """
- select A.ID, A.Value2 from %s as A, %s as B
+ select A.ID, A.Value1, A.Value2 from %s as A, %s as B
where A.Model = %s and B.Model = %s
and A.Value2 = B.Value2 and A.ID <> B.ID
- and A.Arch = B.Arch
+ and A.Scope1 = B.Scope1 and A.Value1 <> B.Value1
group by A.ID
""" % (Table.Table, Table.Table, Model, Model)
RecordSet = Table.Exec(SqlCommand)
- for Record in RecordSet:
- if not EccGlobalData.gException.IsException(ErrorID, Record[1]):
- EccGlobalData.gDb.TblReport.Insert(ErrorID, OtherMsg="The %s value [%s] is used more than one time" % (Name.upper(), Record[1]), BelongsToTable=Table.Table, BelongsToItem=Record[0])
+ for Record in RecordSet:
+ if not EccGlobalData.gException.IsException(ErrorID, Record[1] + ':' + Record[2]):
+ EccGlobalData.gDb.TblReport.Insert(ErrorID, OtherMsg="The %s value [%s] is used more than one time" % (Name.upper(), Record[2]), BelongsToTable=Table.Table, BelongsToItem=Record[0])
# Naming Convention Check
def NamingConventionCheck(self):
diff --git a/BaseTools/Source/Python/Ecc/Configuration.py b/BaseTools/Source/Python/Ecc/Configuration.py index 1478ee6351..0ba8d732d7 100644 --- a/BaseTools/Source/Python/Ecc/Configuration.py +++ b/BaseTools/Source/Python/Ecc/Configuration.py @@ -100,7 +100,15 @@ class Configuration(object): self.HeaderCheckFile = 1
# Check whether Function header exists
self.HeaderCheckFunction = 1
-
+ # Check whether Meta data File header Comment End with '##'
+ self.HeaderCheckFileCommentEnd = 1
+ # Check whether C File header Comment content start with two spaces
+ self.HeaderCheckCFileCommentStartSpacesNum = 1
+ # Check whether C File header Comment's each reference at list should begin with a bullet character '-'
+ self.HeaderCheckCFileCommentReferenceFormat = 1
+ # Check whether C File header Comment have the License immediately after the ""Copyright"" line
+ self.HeaderCheckCFileCommentLicenseFormat = 1
+
## C Function Layout Checking
self.CFunctionLayoutCheckAll = 0
diff --git a/BaseTools/Source/Python/Ecc/Database.py b/BaseTools/Source/Python/Ecc/Database.py index 4b79cb708f..f3e2b5934a 100644 --- a/BaseTools/Source/Python/Ecc/Database.py +++ b/BaseTools/Source/Python/Ecc/Database.py @@ -26,9 +26,9 @@ from Table.TableFunction import TableFunction from Table.TablePcd import TablePcd
from Table.TableIdentifier import TableIdentifier
from Table.TableReport import TableReport
-from Table.TableInf import TableInf
-from Table.TableDec import TableDec
-from Table.TableDsc import TableDsc
+from MetaFileWorkspace.MetaFileTable import ModuleTable
+from MetaFileWorkspace.MetaFileTable import PackageTable
+from MetaFileWorkspace.MetaFileTable import PlatformTable
from Table.TableFdf import TableFdf
##
@@ -92,9 +92,9 @@ class Database(object): self.TblIdentifier = TableIdentifier(self.Cur)
self.TblPcd = TablePcd(self.Cur)
self.TblReport = TableReport(self.Cur)
- self.TblInf = TableInf(self.Cur)
- self.TblDec = TableDec(self.Cur)
- self.TblDsc = TableDsc(self.Cur)
+ self.TblInf = ModuleTable(self.Cur)
+ self.TblDec = PackageTable(self.Cur)
+ self.TblDsc = PlatformTable(self.Cur)
self.TblFdf = TableFdf(self.Cur)
#
diff --git a/BaseTools/Source/Python/Ecc/Ecc.py b/BaseTools/Source/Python/Ecc/Ecc.py index 05dba2568b..ab438c7e86 100644 --- a/BaseTools/Source/Python/Ecc/Ecc.py +++ b/BaseTools/Source/Python/Ecc/Ecc.py @@ -22,13 +22,17 @@ from MetaDataParser import * from optparse import OptionParser
from Configuration import Configuration
from Check import Check
-from Common.InfClassObject import Inf
-from Common.DecClassObject import Dec
-from Common.DscClassObject import Dsc
-from Common.FdfClassObject import Fdf
+
+
from Common.String import NormPath
from Common.BuildVersion import gBUILD_VERSION
from Common import BuildToolError
+
+from MetaFileWorkspace.MetaFileParser import DscParser
+from MetaFileWorkspace.MetaFileParser import DecParser
+from MetaFileWorkspace.MetaFileParser import InfParser
+from MetaFileWorkspace.MetaFileParser import Fdf
+from MetaFileWorkspace.MetaFileTable import MetaFileStorage
import c
import re, string
from Exception import *
@@ -53,6 +57,7 @@ class Ecc(object): self.IsInit = True
self.ScanSourceCode = True
self.ScanMetaData = True
+ self.MetaFile = ''
# Parse the options and args
self.ParseOption()
@@ -124,7 +129,6 @@ class Ecc(object): for Root, Dirs, Files in os.walk(EccGlobalData.gTarget):
if p.match(Root.upper()):
continue
-
for Dir in Dirs:
Dirname = os.path.join(Root, Dir)
if os.path.islink(Dirname):
@@ -139,19 +143,28 @@ class Ecc(object): Filename = os.path.normpath(os.path.join(Root, File))
EdkLogger.quiet("Parsing %s" % Filename)
Op.write("%s\r" % Filename)
- Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
+ #Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
+ self.MetaFile = DecParser(Filename, MODEL_FILE_DEC, EccGlobalData.gDb.TblDec)
+ self.MetaFile.Start()
continue
if len(File) > 4 and File[-4:].upper() == ".DSC":
Filename = os.path.normpath(os.path.join(Root, File))
EdkLogger.quiet("Parsing %s" % Filename)
Op.write("%s\r" % Filename)
- Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
+ #Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
+ self.MetaFile = DscParser(Filename, MODEL_FILE_DSC, MetaFileStorage(EccGlobalData.gDb.TblDsc.Cur, Filename, MODEL_FILE_DSC, True))
+ # alwasy do post-process, in case of macros change
+ self.MetaFile.DoPostProcess()
+ self.MetaFile.Start()
+ self.MetaFile._PostProcess()
continue
if len(File) > 4 and File[-4:].upper() == ".INF":
Filename = os.path.normpath(os.path.join(Root, File))
EdkLogger.quiet("Parsing %s" % Filename)
Op.write("%s\r" % Filename)
- Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
+ #Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
+ self.MetaFile = InfParser(Filename, MODEL_FILE_INF, EccGlobalData.gDb.TblInf)
+ self.MetaFile.Start()
continue
if len(File) > 4 and File[-4:].upper() == ".FDF":
Filename = os.path.normpath(os.path.join(Root, File))
diff --git a/BaseTools/Source/Python/Ecc/MetaDataParser.py b/BaseTools/Source/Python/Ecc/MetaDataParser.py index 4dda2e5360..bbafa00ddd 100644 --- a/BaseTools/Source/Python/Ecc/MetaDataParser.py +++ b/BaseTools/Source/Python/Ecc/MetaDataParser.py @@ -13,8 +13,9 @@ import os
from CommonDataClass.DataClass import *
-
-
+from EccToolError import *
+import EccGlobalData
+import re
## Get the inlcude path list for a source file
#
# 1. Find the source file belongs to which inf file
@@ -76,3 +77,188 @@ def GetTableList(FileModelList, Table, Db): return TableList
+## ParseHeaderCommentSection
+#
+# Parse Header comment section lines, extract Abstract, Description, Copyright
+# , License lines
+#
+# @param CommentList: List of (Comment, LineNumber)
+# @param FileName: FileName of the comment
+#
+def ParseHeaderCommentSection(CommentList, FileName = None):
+
+ Abstract = ''
+ Description = ''
+ Copyright = ''
+ License = ''
+ EndOfLine = "\n"
+ STR_HEADER_COMMENT_START = "@file"
+
+ #
+ # used to indicate the state of processing header comment section of dec,
+ # inf files
+ #
+ HEADER_COMMENT_NOT_STARTED = -1
+ HEADER_COMMENT_STARTED = 0
+ HEADER_COMMENT_FILE = 1
+ HEADER_COMMENT_ABSTRACT = 2
+ HEADER_COMMENT_DESCRIPTION = 3
+ HEADER_COMMENT_COPYRIGHT = 4
+ HEADER_COMMENT_LICENSE = 5
+ HEADER_COMMENT_END = 6
+ #
+ # first find the last copyright line
+ #
+ Last = 0
+ HeaderCommentStage = HEADER_COMMENT_NOT_STARTED
+ for Index in xrange(len(CommentList)-1, 0, -1):
+ Line = CommentList[Index][0]
+ if _IsCopyrightLine(Line):
+ Last = Index
+ break
+
+ for Item in CommentList:
+ Line = Item[0]
+ LineNo = Item[1]
+
+ if not Line.startswith('#') and Line:
+ SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
+ ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
+ for Result in ResultSet:
+ Msg = 'Comment must start with #'
+ EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
+ Comment = CleanString2(Line)[1]
+ Comment = Comment.strip()
+ #
+ # if there are blank lines between License or Description, keep them as they would be
+ # indication of different block; or in the position that Abstract should be, also keep it
+ # as it indicates that no abstract
+ #
+ if not Comment and HeaderCommentStage not in [HEADER_COMMENT_LICENSE, \
+ HEADER_COMMENT_DESCRIPTION, HEADER_COMMENT_ABSTRACT]:
+ continue
+
+ if HeaderCommentStage == HEADER_COMMENT_NOT_STARTED:
+ if Comment.startswith(STR_HEADER_COMMENT_START):
+ HeaderCommentStage = HEADER_COMMENT_ABSTRACT
+ else:
+ License += Comment + EndOfLine
+ else:
+ if HeaderCommentStage == HEADER_COMMENT_ABSTRACT:
+ #
+ # in case there is no abstract and description
+ #
+ if not Comment:
+ Abstract = ''
+ HeaderCommentStage = HEADER_COMMENT_DESCRIPTION
+ elif _IsCopyrightLine(Comment):
+ Copyright += Comment + EndOfLine
+ HeaderCommentStage = HEADER_COMMENT_COPYRIGHT
+ else:
+ Abstract += Comment + EndOfLine
+ HeaderCommentStage = HEADER_COMMENT_DESCRIPTION
+ elif HeaderCommentStage == HEADER_COMMENT_DESCRIPTION:
+ #
+ # in case there is no description
+ #
+ if _IsCopyrightLine(Comment):
+ Copyright += Comment + EndOfLine
+ HeaderCommentStage = HEADER_COMMENT_COPYRIGHT
+ else:
+ Description += Comment + EndOfLine
+ elif HeaderCommentStage == HEADER_COMMENT_COPYRIGHT:
+ if _IsCopyrightLine(Comment):
+ Copyright += Comment + EndOfLine
+ else:
+ #
+ # Contents after copyright line are license, those non-copyright lines in between
+ # copyright line will be discarded
+ #
+ if LineNo > Last:
+ if License:
+ License += EndOfLine
+ License += Comment + EndOfLine
+ HeaderCommentStage = HEADER_COMMENT_LICENSE
+ else:
+ if not Comment and not License:
+ continue
+ License += Comment + EndOfLine
+
+ if not Copyright:
+ SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
+ ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
+ for Result in ResultSet:
+ Msg = 'Header comment section must have copyright information'
+ EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
+
+ if not License:
+ SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
+ ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
+ for Result in ResultSet:
+ Msg = 'Header comment section must have license information'
+ EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
+
+ return Abstract.strip(), Description.strip(), Copyright.strip(), License.strip()
+
+## _IsCopyrightLine
+# check whether current line is copyright line, the criteria is whether there is case insensitive keyword "Copyright"
+# followed by zero or more white space characters followed by a "(" character
+#
+# @param LineContent: the line need to be checked
+# @return: True if current line is copyright line, False else
+#
+def _IsCopyrightLine (LineContent):
+ LineContent = LineContent.upper()
+ Result = False
+
+ ReIsCopyrightRe = re.compile(r"""(^|\s)COPYRIGHT *\(""", re.DOTALL)
+ if ReIsCopyrightRe.search(LineContent):
+ Result = True
+
+ return Result
+
+
+## CleanString2
+#
+# Split comments in a string
+# Remove spaces
+#
+# @param Line: The string to be cleaned
+# @param CommentCharacter: Comment char, used to ignore comment content,
+# default is DataType.TAB_COMMENT_SPLIT
+#
+def CleanString2(Line, CommentCharacter='#', AllowCppStyleComment=False):
+ #
+ # remove whitespace
+ #
+ Line = Line.strip()
+ #
+ # Replace EDK1's comment character
+ #
+ if AllowCppStyleComment:
+ Line = Line.replace('//', CommentCharacter)
+ #
+ # separate comments and statements
+ #
+ LineParts = Line.split(CommentCharacter, 1)
+ #
+ # remove whitespace again
+ #
+ Line = LineParts[0].strip()
+ if len(LineParts) > 1:
+ Comment = LineParts[1].strip()
+ #
+ # Remove prefixed and trailing comment characters
+ #
+ Start = 0
+ End = len(Comment)
+ while Start < End and Comment.startswith(CommentCharacter, Start, End):
+ Start += 1
+ while End >= 0 and Comment.endswith(CommentCharacter, Start, End):
+ End -= 1
+ Comment = Comment[Start:End]
+ Comment = Comment.strip()
+ else:
+ Comment = ''
+
+ return Line, Comment
diff --git a/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaDataTable.py b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaDataTable.py new file mode 100644 index 0000000000..3060698288 --- /dev/null +++ b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaDataTable.py @@ -0,0 +1,215 @@ +## @file
+# This file is used to create/update/query/erase table for files
+#
+# Copyright (c) 2008, 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.
+#
+
+##
+# Import Modules
+#
+import os
+
+import Common.EdkLogger as EdkLogger
+from CommonDataClass import DataClass
+from CommonDataClass.DataClass import FileClass
+
+## Convert to SQL required string format
+def ConvertToSqlString(StringList):
+ return map(lambda s: "'" + s.replace("'", "''") + "'", StringList)
+
+## TableFile
+#
+# This class defined a common table
+#
+# @param object: Inherited from object class
+#
+# @param Cursor: Cursor of the database
+# @param TableName: Name of the table
+#
+class Table(object):
+ _COLUMN_ = ''
+ _ID_STEP_ = 1
+ _ID_MAX_ = 0x80000000
+ _DUMMY_ = 0
+
+ def __init__(self, Cursor, Name='', IdBase=0, Temporary=False):
+ self.Cur = Cursor
+ self.Table = Name
+ self.IdBase = int(IdBase)
+ self.ID = int(IdBase)
+ self.Temporary = Temporary
+
+ def __str__(self):
+ return self.Table
+
+ ## Create table
+ #
+ # Create a table
+ #
+ def Create(self, NewTable=True):
+ if NewTable:
+ self.Drop()
+
+ if self.Temporary:
+ SqlCommand = """create temp table IF NOT EXISTS %s (%s)""" % (self.Table, self._COLUMN_)
+ else:
+ SqlCommand = """create table IF NOT EXISTS %s (%s)""" % (self.Table, self._COLUMN_)
+ EdkLogger.debug(EdkLogger.DEBUG_8, SqlCommand)
+ self.Cur.execute(SqlCommand)
+ self.ID = self.GetId()
+
+ ## Insert table
+ #
+ # Insert a record into a table
+ #
+ def Insert(self, *Args):
+ self.ID = self.ID + self._ID_STEP_
+ if self.ID >= (self.IdBase + self._ID_MAX_):
+ self.ID = self.IdBase + self._ID_STEP_
+ Values = ", ".join([str(Arg) for Arg in Args])
+ SqlCommand = "insert into %s values(%s, %s)" % (self.Table, self.ID, Values)
+ EdkLogger.debug(EdkLogger.DEBUG_5, SqlCommand)
+ self.Cur.execute(SqlCommand)
+ return self.ID
+
+ ## Query table
+ #
+ # Query all records of the table
+ #
+ def Query(self):
+ SqlCommand = """select * from %s""" % self.Table
+ self.Cur.execute(SqlCommand)
+ for Rs in self.Cur:
+ EdkLogger.verbose(str(Rs))
+ TotalCount = self.GetId()
+
+ ## Drop a table
+ #
+ # Drop the table
+ #
+ def Drop(self):
+ SqlCommand = """drop table IF EXISTS %s""" % self.Table
+ self.Cur.execute(SqlCommand)
+
+ ## Get count
+ #
+ # Get a count of all records of the table
+ #
+ # @retval Count: Total count of all records
+ #
+ def GetCount(self):
+ SqlCommand = """select count(ID) from %s""" % self.Table
+ Record = self.Cur.execute(SqlCommand).fetchall()
+ return Record[0][0]
+
+ def GetId(self):
+ SqlCommand = """select max(ID) from %s""" % self.Table
+ Record = self.Cur.execute(SqlCommand).fetchall()
+ Id = Record[0][0]
+ if Id == None:
+ Id = self.IdBase
+ return Id
+
+ ## Init the ID of the table
+ #
+ # Init the ID of the table
+ #
+ def InitID(self):
+ self.ID = self.GetId()
+
+ ## Exec
+ #
+ # Exec Sql Command, return result
+ #
+ # @param SqlCommand: The SqlCommand to be executed
+ #
+ # @retval RecordSet: The result after executed
+ #
+ def Exec(self, SqlCommand):
+ EdkLogger.debug(EdkLogger.DEBUG_5, SqlCommand)
+ self.Cur.execute(SqlCommand)
+ RecordSet = self.Cur.fetchall()
+ return RecordSet
+
+ def SetEndFlag(self):
+ pass
+
+ def IsIntegral(self):
+ Result = self.Exec("select min(ID) from %s" % (self.Table))
+ if Result[0][0] != -1:
+ return False
+ return True
+
+ def GetAll(self):
+ return self.Exec("select * from %s where ID > 0 order by ID" % (self.Table))
+
+
+## TableDataModel
+#
+# This class defined a table used for data model
+#
+# @param object: Inherited from object class
+#
+#
+class TableDataModel(Table):
+ _COLUMN_ = """
+ ID INTEGER PRIMARY KEY,
+ CrossIndex INTEGER NOT NULL,
+ Name VARCHAR NOT NULL,
+ Description VARCHAR
+ """
+ def __init__(self, Cursor):
+ Table.__init__(self, Cursor, 'DataModel')
+
+ ## Insert table
+ #
+ # Insert a record into table DataModel
+ #
+ # @param ID: ID of a ModelType
+ # @param CrossIndex: CrossIndex of a ModelType
+ # @param Name: Name of a ModelType
+ # @param Description: Description of a ModelType
+ #
+ def Insert(self, CrossIndex, Name, Description):
+ (Name, Description) = ConvertToSqlString((Name, Description))
+ return Table.Insert(self, CrossIndex, Name, Description)
+
+ ## Init table
+ #
+ # Create all default records of table DataModel
+ #
+ def InitTable(self):
+ EdkLogger.verbose("\nInitialize table DataModel started ...")
+ Count = self.GetCount()
+ if Count != None and Count != 0:
+ return
+ for Item in DataClass.MODEL_LIST:
+ CrossIndex = Item[1]
+ Name = Item[0]
+ Description = Item[0]
+ self.Insert(CrossIndex, Name, Description)
+ EdkLogger.verbose("Initialize table DataModel ... DONE!")
+
+ ## Get CrossIndex
+ #
+ # Get a model's cross index from its name
+ #
+ # @param ModelName: Name of the model
+ # @retval CrossIndex: CrossIndex of the model
+ #
+ def GetCrossIndex(self, ModelName):
+ CrossIndex = -1
+ SqlCommand = """select CrossIndex from DataModel where name = '""" + ModelName + """'"""
+ self.Cur.execute(SqlCommand)
+ for Item in self.Cur:
+ CrossIndex = Item[0]
+
+ return CrossIndex
+
diff --git a/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileParser.py b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileParser.py new file mode 100644 index 0000000000..fc29bafc58 --- /dev/null +++ b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileParser.py @@ -0,0 +1,1849 @@ +## @file +# This file is used to parse meta files +# +# Copyright (c) 2008 - 2010, 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. +# + +## +# Import Modules +# +import os +import re +import time +import copy + +import Common.EdkLogger as EdkLogger +import Common.GlobalData as GlobalData +import EccGlobalData + +from CommonDataClass.DataClass import * +from Common.DataType import * +from Common.String import * +from Common.Misc import GuidStructureStringToGuidString, CheckPcdDatum, PathClass, AnalyzePcdData +from Common.Expression import * +from CommonDataClass.Exceptions import * + +from MetaFileTable import MetaFileStorage +from GenFds.FdfParser import FdfParser + +## A decorator used to parse macro definition +def ParseMacro(Parser): + def MacroParser(self): + Match = gMacroDefPattern.match(self._CurrentLine) + if not Match: + # Not 'DEFINE/EDK_GLOBAL' statement, call decorated method + Parser(self) + return + + TokenList = GetSplitValueList(self._CurrentLine[Match.end(1):], TAB_EQUAL_SPLIT, 1) + # Syntax check + if not TokenList[0]: + EdkLogger.error('Parser', FORMAT_INVALID, "No macro name given", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + if len(TokenList) < 2: + TokenList.append('') + + Type = Match.group(1) + Name, Value = TokenList + # Global macros can be only defined via environment variable + if Name in GlobalData.gGlobalDefines: + EdkLogger.error('Parser', FORMAT_INVALID, "%s can only be defined via environment variable" % Name, + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + # Only upper case letters, digit and '_' are allowed + if not gMacroNamePattern.match(Name): + EdkLogger.error('Parser', FORMAT_INVALID, "The macro name must be in the pattern [A-Z][A-Z0-9_]*", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + + Value = ReplaceMacro(Value, self._Macros) + self._ItemType = MODEL_META_DATA_DEFINE + # DEFINE defined macros + if Type == TAB_DSC_DEFINES_DEFINE: + if type(self) == DecParser: + if MODEL_META_DATA_HEADER in self._SectionType: + self._FileLocalMacros[Name] = Value + else: + for Scope in self._Scope: + self._SectionsMacroDict.setdefault((Scope[2], Scope[0], Scope[1]), {})[Name] = Value + elif self._SectionType == MODEL_META_DATA_HEADER: + self._FileLocalMacros[Name] = Value + else: + SectionDictKey = self._SectionType, self._Scope[0][0], self._Scope[0][1] + if SectionDictKey not in self._SectionsMacroDict: + self._SectionsMacroDict[SectionDictKey] = {} + SectionLocalMacros = self._SectionsMacroDict[SectionDictKey] + SectionLocalMacros[Name] = Value + # EDK_GLOBAL defined macros + elif type(self) != DscParser: + EdkLogger.error('Parser', FORMAT_INVALID, "EDK_GLOBAL can only be used in .dsc file", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + elif self._SectionType != MODEL_META_DATA_HEADER: + EdkLogger.error('Parser', FORMAT_INVALID, "EDK_GLOBAL can only be used under [Defines] section", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + elif (Name in self._FileLocalMacros) and (self._FileLocalMacros[Name] != Value): + EdkLogger.error('Parser', FORMAT_INVALID, "EDK_GLOBAL defined a macro with the same name and different value as one defined by 'DEFINE'", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + + self._ValueList = [Type, Name, Value] + + return MacroParser + +## Base class of parser +# +# This class is used for derivation purpose. The specific parser for one kind +# type file must derive this class and implement some public interfaces. +# +# @param FilePath The path of platform description file +# @param FileType The raw data of DSC file +# @param Table Database used to retrieve module/package information +# @param Macros Macros used for replacement in file +# @param Owner Owner ID (for sub-section parsing) +# @param From ID from which the data comes (for !INCLUDE directive) +# +class MetaFileParser(object): + # data type (file content) for specific file type + DataType = {} + + # Parser objects used to implement singleton + MetaFiles = {} + + ## Factory method + # + # One file, one parser object. This factory method makes sure that there's + # only one object constructed for one meta file. + # + # @param Class class object of real AutoGen class + # (InfParser, DecParser or DscParser) + # @param FilePath The path of meta file + # @param *args The specific class related parameters + # @param **kwargs The specific class related dict parameters + # + def __new__(Class, FilePath, *args, **kwargs): + if FilePath in Class.MetaFiles: + return Class.MetaFiles[FilePath] + else: + ParserObject = super(MetaFileParser, Class).__new__(Class) + Class.MetaFiles[FilePath] = ParserObject + return ParserObject + + ## Constructor of MetaFileParser + # + # Initialize object of MetaFileParser + # + # @param FilePath The path of platform description file + # @param FileType The raw data of DSC file + # @param Table Database used to retrieve module/package information + # @param Macros Macros used for replacement in file + # @param Owner Owner ID (for sub-section parsing) + # @param From ID from which the data comes (for !INCLUDE directive) + # + def __init__(self, FilePath, FileType, Table, Owner=-1, From=-1): + self._Table = Table + self._RawTable = Table + self._FileType = FileType + self.MetaFile = FilePath + self._Defines = {} + self._FileLocalMacros = {} + self._SectionsMacroDict = {} + + # for recursive parsing + self._Owner = [Owner] + self._From = From + + # parsr status for parsing + self._ValueList = ['', '', '', '', ''] + self._Scope = [] + self._LineIndex = 0 + self._CurrentLine = '' + self._SectionType = MODEL_UNKNOWN + self._SectionName = '' + self._InSubsection = False + self._SubsectionType = MODEL_UNKNOWN + self._SubsectionName = '' + self._ItemType = MODEL_UNKNOWN + self._LastItem = -1 + self._Enabled = 0 + self._Finished = False + self._PostProcessed = False + # Different version of meta-file has different way to parse. + self._Version = 0 + + ## Store the parsed data in table + def _Store(self, *Args): + return self._Table.Insert(*Args) + + ## Virtual method for starting parse + def Start(self): + raise NotImplementedError + + ## Notify a post-process is needed + def DoPostProcess(self): + self._PostProcessed = False + + ## Set parsing complete flag in both class and table + def _Done(self): + self._Finished = True + ## Do not set end flag when processing included files + if self._From == -1: + self._Table.SetEndFlag() + + def _PostProcess(self): + self._PostProcessed = True + + ## Get the parse complete flag + def _GetFinished(self): + return self._Finished + + ## Set the complete flag + def _SetFinished(self, Value): + self._Finished = Value + + ## Use [] style to query data in table, just for readability + # + # DataInfo = [data_type, scope1(arch), scope2(platform/moduletype)] + # + def __getitem__(self, DataInfo): + if type(DataInfo) != type(()): + DataInfo = (DataInfo,) + + # Parse the file first, if necessary + if not self._Finished: + if self._RawTable.IsIntegrity(): + self._Finished = True + else: + self._Table = self._RawTable + self._PostProcessed = False + self.Start() + + # No specific ARCH or Platform given, use raw data + if self._RawTable and (len(DataInfo) == 1 or DataInfo[1] == None): + return self._RawTable.Query(*DataInfo) + + # Do post-process if necessary + if not self._PostProcessed: + self._PostProcess() + + return self._Table.Query(*DataInfo) + + ## Data parser for the common format in different type of file + # + # The common format in the meatfile is like + # + # xxx1 | xxx2 | xxx3 + # + @ParseMacro + def _CommonParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT) + self._ValueList[0:len(TokenList)] = TokenList + + ## Data parser for the format in which there's path + # + # Only path can have macro used. So we need to replace them before use. + # + @ParseMacro + def _PathParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT) + self._ValueList[0:len(TokenList)] = TokenList + # Don't do macro replacement for dsc file at this point + if type(self) != DscParser: + Macros = self._Macros + self._ValueList = [ReplaceMacro(Value, Macros) for Value in self._ValueList] + + ## Skip unsupported data + def _Skip(self): + EdkLogger.warn("Parser", "Unrecognized content", File=self.MetaFile, + Line=self._LineIndex+1, ExtraData=self._CurrentLine); + self._ValueList[0:1] = [self._CurrentLine] + + ## Section header parser + # + # The section header is always in following format: + # + # [section_name.arch<.platform|module_type>] + # + def _SectionHeaderParser(self): + self._Scope = [] + self._SectionName = '' + ArchList = set() + for Item in GetSplitValueList(self._CurrentLine[1:-1], TAB_COMMA_SPLIT): + if Item == '': + continue + ItemList = GetSplitValueList(Item, TAB_SPLIT) + # different section should not mix in one section + if self._SectionName != '' and self._SectionName != ItemList[0].upper(): + EdkLogger.error('Parser', FORMAT_INVALID, "Different section names in the same section", + File=self.MetaFile, Line=self._LineIndex+1, ExtraData=self._CurrentLine) + self._SectionName = ItemList[0].upper() + if self._SectionName in self.DataType: + self._SectionType = self.DataType[self._SectionName] + else: + self._SectionType = MODEL_UNKNOWN + EdkLogger.warn("Parser", "Unrecognized section", File=self.MetaFile, + Line=self._LineIndex+1, ExtraData=self._CurrentLine) + # S1 is always Arch + if len(ItemList) > 1: + S1 = ItemList[1].upper() + else: + S1 = 'COMMON' + ArchList.add(S1) + # S2 may be Platform or ModuleType + if len(ItemList) > 2: + S2 = ItemList[2].upper() + else: + S2 = 'COMMON' + self._Scope.append([S1, S2]) + + # 'COMMON' must not be used with specific ARCHs at the same section + if 'COMMON' in ArchList and len(ArchList) > 1: + EdkLogger.error('Parser', FORMAT_INVALID, "'common' ARCH must not be used with specific ARCHs", + File=self.MetaFile, Line=self._LineIndex+1, ExtraData=self._CurrentLine) + # If the section information is needed later, it should be stored in database + self._ValueList[0] = self._SectionName + + ## [defines] section parser + @ParseMacro + def _DefineParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_EQUAL_SPLIT, 1) + self._ValueList[1:len(TokenList)] = TokenList + if not self._ValueList[1]: + EdkLogger.error('Parser', FORMAT_INVALID, "No name specified", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + if not self._ValueList[2]: + EdkLogger.error('Parser', FORMAT_INVALID, "No value specified", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + + self._ValueList = [ReplaceMacro(Value, self._Macros) for Value in self._ValueList] + Name, Value = self._ValueList[1], self._ValueList[2] + # Sometimes, we need to make differences between EDK and EDK2 modules + if Name == 'INF_VERSION': + try: + self._Version = int(Value, 0) + except: + EdkLogger.error('Parser', FORMAT_INVALID, "Invalid version number", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + + if type(self) == InfParser and self._Version < 0x00010005: + # EDK module allows using defines as macros + self._FileLocalMacros[Name] = Value + self._Defines[Name] = Value + + ## [BuildOptions] section parser + @ParseMacro + def _BuildOptionParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_EQUAL_SPLIT, 1) + TokenList2 = GetSplitValueList(TokenList[0], ':', 1) + if len(TokenList2) == 2: + self._ValueList[0] = TokenList2[0] # toolchain family + self._ValueList[1] = TokenList2[1] # keys + else: + self._ValueList[1] = TokenList[0] + if len(TokenList) == 2 and type(self) != DscParser: # value + self._ValueList[2] = ReplaceMacro(TokenList[1], self._Macros) + + if self._ValueList[1].count('_') != 4: + EdkLogger.error( + 'Parser', + FORMAT_INVALID, + "'%s' must be in format of <TARGET>_<TOOLCHAIN>_<ARCH>_<TOOL>_FLAGS" % self._ValueList[1], + ExtraData=self._CurrentLine, + File=self.MetaFile, + Line=self._LineIndex+1 + ) + + def _GetMacros(self): + Macros = {} + Macros.update(self._FileLocalMacros) + Macros.update(self._GetApplicableSectionMacro()) + return Macros + + + ## Get section Macros that are applicable to current line, which may come from other sections + ## that share the same name while scope is wider + def _GetApplicableSectionMacro(self): + Macros = {} + for Scope1, Scope2 in [("COMMON", "COMMON"), ("COMMON", self._Scope[0][1]), + (self._Scope[0][0], "COMMON"), (self._Scope[0][0], self._Scope[0][1])]: + if (self._SectionType, Scope1, Scope2) in self._SectionsMacroDict: + Macros.update(self._SectionsMacroDict[(self._SectionType, Scope1, Scope2)]) + return Macros + + _SectionParser = {} + Finished = property(_GetFinished, _SetFinished) + _Macros = property(_GetMacros) + + +## INF file parser class +# +# @param FilePath The path of platform description file +# @param FileType The raw data of DSC file +# @param Table Database used to retrieve module/package information +# @param Macros Macros used for replacement in file +# +class InfParser(MetaFileParser): + # INF file supported data types (one type per section) + DataType = { + TAB_UNKNOWN.upper() : MODEL_UNKNOWN, + TAB_INF_DEFINES.upper() : MODEL_META_DATA_HEADER, + TAB_DSC_DEFINES_DEFINE : MODEL_META_DATA_DEFINE, + TAB_BUILD_OPTIONS.upper() : MODEL_META_DATA_BUILD_OPTION, + TAB_INCLUDES.upper() : MODEL_EFI_INCLUDE, + TAB_LIBRARIES.upper() : MODEL_EFI_LIBRARY_INSTANCE, + TAB_LIBRARY_CLASSES.upper() : MODEL_EFI_LIBRARY_CLASS, + TAB_PACKAGES.upper() : MODEL_META_DATA_PACKAGE, + TAB_NMAKE.upper() : MODEL_META_DATA_NMAKE, + TAB_INF_FIXED_PCD.upper() : MODEL_PCD_FIXED_AT_BUILD, + TAB_INF_PATCH_PCD.upper() : MODEL_PCD_PATCHABLE_IN_MODULE, + TAB_INF_FEATURE_PCD.upper() : MODEL_PCD_FEATURE_FLAG, + TAB_INF_PCD_EX.upper() : MODEL_PCD_DYNAMIC_EX, + TAB_INF_PCD.upper() : MODEL_PCD_DYNAMIC, + TAB_SOURCES.upper() : MODEL_EFI_SOURCE_FILE, + TAB_GUIDS.upper() : MODEL_EFI_GUID, + TAB_PROTOCOLS.upper() : MODEL_EFI_PROTOCOL, + TAB_PPIS.upper() : MODEL_EFI_PPI, + TAB_DEPEX.upper() : MODEL_EFI_DEPEX, + TAB_BINARIES.upper() : MODEL_EFI_BINARY_FILE, + TAB_USER_EXTENSIONS.upper() : MODEL_META_DATA_USER_EXTENSION + } + + ## Constructor of InfParser + # + # Initialize object of InfParser + # + # @param FilePath The path of module description file + # @param FileType The raw data of DSC file + # @param Table Database used to retrieve module/package information + # @param Macros Macros used for replacement in file + # + def __init__(self, FilePath, FileType, Table): + # prevent re-initialization + if hasattr(self, "_Table"): + return + MetaFileParser.__init__(self, FilePath, FileType, Table) + self.TblFile = EccGlobalData.gDb.TblFile + self.FileID = -1 + + ## Parser starter + def Start(self): + NmakeLine = '' + Content = '' + try: + Content = open(str(self.MetaFile), 'r').readlines() + except: + EdkLogger.error("Parser", FILE_READ_FAILURE, ExtraData=self.MetaFile) + # + # Insert a record for file + # + Filename = NormPath(self.MetaFile) + FileID = self.TblFile.GetFileId(Filename) + if FileID: + self.FileID = FileID + else: + self.FileID = self.TblFile.InsertFile(Filename, MODEL_FILE_INF) + + # parse the file line by line + IsFindBlockComment = False + + for Index in range(0, len(Content)): + # skip empty, commented, block commented lines + Line = CleanString(Content[Index], AllowCppStyleComment=True) + NextLine = '' + if Index + 1 < len(Content): + NextLine = CleanString(Content[Index + 1]) + if Line == '': + continue + if Line.find(DataType.TAB_COMMENT_EDK_START) > -1: + IsFindBlockComment = True + continue + if Line.find(DataType.TAB_COMMENT_EDK_END) > -1: + IsFindBlockComment = False + continue + if IsFindBlockComment: + continue + + self._LineIndex = Index + self._CurrentLine = Line + + # section header + if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END: + self._SectionHeaderParser() + # Check invalid sections + if self._Version < 0x00010005: + if self._SectionType in [MODEL_META_DATA_BUILD_OPTION, + MODEL_EFI_LIBRARY_CLASS, + MODEL_META_DATA_PACKAGE, + MODEL_PCD_FIXED_AT_BUILD, + MODEL_PCD_PATCHABLE_IN_MODULE, + MODEL_PCD_FEATURE_FLAG, + MODEL_PCD_DYNAMIC_EX, + MODEL_PCD_DYNAMIC, + MODEL_EFI_GUID, + MODEL_EFI_PROTOCOL, + MODEL_EFI_PPI, + MODEL_META_DATA_USER_EXTENSION]: + EdkLogger.error('Parser', FORMAT_INVALID, + "Section [%s] is not allowed in inf file without version" % (self._SectionName), + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + elif self._SectionType in [MODEL_EFI_INCLUDE, + MODEL_EFI_LIBRARY_INSTANCE, + MODEL_META_DATA_NMAKE]: + EdkLogger.error('Parser', FORMAT_INVALID, + "Section [%s] is not allowed in inf file with version 0x%08x" % (self._SectionName, self._Version), + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + continue + # merge two lines specified by '\' in section NMAKE + elif self._SectionType == MODEL_META_DATA_NMAKE: + if Line[-1] == '\\': + if NextLine == '': + self._CurrentLine = NmakeLine + Line[0:-1] + NmakeLine = '' + else: + if NextLine[0] == TAB_SECTION_START and NextLine[-1] == TAB_SECTION_END: + self._CurrentLine = NmakeLine + Line[0:-1] + NmakeLine = '' + else: + NmakeLine = NmakeLine + ' ' + Line[0:-1] + continue + else: + self._CurrentLine = NmakeLine + Line + NmakeLine = '' + + # section content + self._ValueList = ['','',''] + # parse current line, result will be put in self._ValueList + self._SectionParser[self._SectionType](self) + if self._ValueList == None or self._ItemType == MODEL_META_DATA_DEFINE: + self._ItemType = -1 + continue + # + # Model, Value1, Value2, Value3, Arch, Platform, BelongsToItem=-1, + # LineBegin=-1, ColumnBegin=-1, LineEnd=-1, ColumnEnd=-1, Enabled=-1 + # + self._ValueList[0] = self._ValueList[0].replace('/', '\\') + for Arch, Platform in self._Scope: + self._Store(self._SectionType, + self._ValueList[0], + self._ValueList[1], + self._ValueList[2], + Arch, + Platform, + self._Owner[-1], + self.FileID, + self._LineIndex+1, + -1, + self._LineIndex+1, + -1, + 0 + ) + if IsFindBlockComment: + EdkLogger.error("Parser", FORMAT_INVALID, "Open block comments (starting with /*) are expected to end with */", + File=self.MetaFile) + self._Done() + + ## Data parser for the format in which there's path + # + # Only path can have macro used. So we need to replace them before use. + # + def _IncludeParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT) + self._ValueList[0:len(TokenList)] = TokenList + Macros = self._Macros + if Macros: + for Index in range(0, len(self._ValueList)): + Value = self._ValueList[Index] + if not Value: + continue + + if Value.upper().find('$(EFI_SOURCE)\Edk'.upper()) > -1 or Value.upper().find('$(EFI_SOURCE)/Edk'.upper()) > -1: + Value = '$(EDK_SOURCE)' + Value[17:] + if Value.find('$(EFI_SOURCE)') > -1 or Value.find('$(EDK_SOURCE)') > -1: + pass + elif Value.startswith('.'): + pass + elif Value.startswith('$('): + pass + else: + Value = '$(EFI_SOURCE)/' + Value + + self._ValueList[Index] = ReplaceMacro(Value, Macros) + + ## Parse [Sources] section + # + # Only path can have macro used. So we need to replace them before use. + # + @ParseMacro + def _SourceFileParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT) + self._ValueList[0:len(TokenList)] = TokenList + Macros = self._Macros + # For Acpi tables, remove macro like ' TABLE_NAME=Sata1' + if 'COMPONENT_TYPE' in Macros: + if self._Defines['COMPONENT_TYPE'].upper() == 'ACPITABLE': + self._ValueList[0] = GetSplitValueList(self._ValueList[0], ' ', 1)[0] + if self._Defines['BASE_NAME'] == 'Microcode': + pass + self._ValueList = [ReplaceMacro(Value, Macros) for Value in self._ValueList] + + ## Parse [Binaries] section + # + # Only path can have macro used. So we need to replace them before use. + # + @ParseMacro + def _BinaryFileParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT, 2) + if len(TokenList) < 2: + EdkLogger.error('Parser', FORMAT_INVALID, "No file type or path specified", + ExtraData=self._CurrentLine + " (<FileType> | <FilePath> [| <Target>])", + File=self.MetaFile, Line=self._LineIndex+1) + if not TokenList[0]: + EdkLogger.error('Parser', FORMAT_INVALID, "No file type specified", + ExtraData=self._CurrentLine + " (<FileType> | <FilePath> [| <Target>])", + File=self.MetaFile, Line=self._LineIndex+1) + if not TokenList[1]: + EdkLogger.error('Parser', FORMAT_INVALID, "No file path specified", + ExtraData=self._CurrentLine + " (<FileType> | <FilePath> [| <Target>])", + File=self.MetaFile, Line=self._LineIndex+1) + self._ValueList[0:len(TokenList)] = TokenList + self._ValueList[1] = ReplaceMacro(self._ValueList[1], self._Macros) + + ## [nmake] section parser (Edk.x style only) + def _NmakeParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_EQUAL_SPLIT, 1) + self._ValueList[0:len(TokenList)] = TokenList + # remove macros + self._ValueList[1] = ReplaceMacro(self._ValueList[1], self._Macros) + # remove self-reference in macro setting + #self._ValueList[1] = ReplaceMacro(self._ValueList[1], {self._ValueList[0]:''}) + + ## [FixedPcd], [FeaturePcd], [PatchPcd], [Pcd] and [PcdEx] sections parser + @ParseMacro + def _PcdParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT, 1) + ValueList = GetSplitValueList(TokenList[0], TAB_SPLIT) + if len(ValueList) != 2: + EdkLogger.error('Parser', FORMAT_INVALID, "Illegal token space GUID and PCD name format", + ExtraData=self._CurrentLine + " (<TokenSpaceGuidCName>.<PcdCName>)", + File=self.MetaFile, Line=self._LineIndex+1) + self._ValueList[0:1] = ValueList + if len(TokenList) > 1: + self._ValueList[2] = TokenList[1] + if self._ValueList[0] == '' or self._ValueList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No token space GUID or PCD name specified", + ExtraData=self._CurrentLine + " (<TokenSpaceGuidCName>.<PcdCName>)", + File=self.MetaFile, Line=self._LineIndex+1) + + # if value are 'True', 'true', 'TRUE' or 'False', 'false', 'FALSE', replace with integer 1 or 0. + if self._ValueList[2] != '': + InfPcdValueList = GetSplitValueList(TokenList[1], TAB_VALUE_SPLIT, 1) + if InfPcdValueList[0] in ['True', 'true', 'TRUE']: + self._ValueList[2] = TokenList[1].replace(InfPcdValueList[0], '1', 1); + elif InfPcdValueList[0] in ['False', 'false', 'FALSE']: + self._ValueList[2] = TokenList[1].replace(InfPcdValueList[0], '0', 1); + + ## [depex] section parser + @ParseMacro + def _DepexParser(self): + self._ValueList[0:1] = [self._CurrentLine] + + _SectionParser = { + MODEL_UNKNOWN : MetaFileParser._Skip, + MODEL_META_DATA_HEADER : MetaFileParser._DefineParser, + MODEL_META_DATA_BUILD_OPTION : MetaFileParser._BuildOptionParser, + MODEL_EFI_INCLUDE : _IncludeParser, # for Edk.x modules + MODEL_EFI_LIBRARY_INSTANCE : MetaFileParser._CommonParser, # for Edk.x modules + MODEL_EFI_LIBRARY_CLASS : MetaFileParser._PathParser, + MODEL_META_DATA_PACKAGE : MetaFileParser._PathParser, + MODEL_META_DATA_NMAKE : _NmakeParser, # for Edk.x modules + MODEL_PCD_FIXED_AT_BUILD : _PcdParser, + MODEL_PCD_PATCHABLE_IN_MODULE : _PcdParser, + MODEL_PCD_FEATURE_FLAG : _PcdParser, + MODEL_PCD_DYNAMIC_EX : _PcdParser, + MODEL_PCD_DYNAMIC : _PcdParser, + MODEL_EFI_SOURCE_FILE : _SourceFileParser, + MODEL_EFI_GUID : MetaFileParser._CommonParser, + MODEL_EFI_PROTOCOL : MetaFileParser._CommonParser, + MODEL_EFI_PPI : MetaFileParser._CommonParser, + MODEL_EFI_DEPEX : _DepexParser, + MODEL_EFI_BINARY_FILE : _BinaryFileParser, + MODEL_META_DATA_USER_EXTENSION : MetaFileParser._Skip, + } + +## DSC file parser class +# +# @param FilePath The path of platform description file +# @param FileType The raw data of DSC file +# @param Table Database used to retrieve module/package information +# @param Macros Macros used for replacement in file +# @param Owner Owner ID (for sub-section parsing) +# @param From ID from which the data comes (for !INCLUDE directive) +# +class DscParser(MetaFileParser): + # DSC file supported data types (one type per section) + DataType = { + TAB_SKUIDS.upper() : MODEL_EFI_SKU_ID, + TAB_LIBRARIES.upper() : MODEL_EFI_LIBRARY_INSTANCE, + TAB_LIBRARY_CLASSES.upper() : MODEL_EFI_LIBRARY_CLASS, + TAB_BUILD_OPTIONS.upper() : MODEL_META_DATA_BUILD_OPTION, + TAB_PCDS_FIXED_AT_BUILD_NULL.upper() : MODEL_PCD_FIXED_AT_BUILD, + TAB_PCDS_PATCHABLE_IN_MODULE_NULL.upper() : MODEL_PCD_PATCHABLE_IN_MODULE, + TAB_PCDS_FEATURE_FLAG_NULL.upper() : MODEL_PCD_FEATURE_FLAG, + TAB_PCDS_DYNAMIC_DEFAULT_NULL.upper() : MODEL_PCD_DYNAMIC_DEFAULT, + TAB_PCDS_DYNAMIC_HII_NULL.upper() : MODEL_PCD_DYNAMIC_HII, + TAB_PCDS_DYNAMIC_VPD_NULL.upper() : MODEL_PCD_DYNAMIC_VPD, + TAB_PCDS_DYNAMIC_EX_DEFAULT_NULL.upper() : MODEL_PCD_DYNAMIC_EX_DEFAULT, + TAB_PCDS_DYNAMIC_EX_HII_NULL.upper() : MODEL_PCD_DYNAMIC_EX_HII, + TAB_PCDS_DYNAMIC_EX_VPD_NULL.upper() : MODEL_PCD_DYNAMIC_EX_VPD, + TAB_COMPONENTS.upper() : MODEL_META_DATA_COMPONENT, + TAB_COMPONENTS_SOURCE_OVERRIDE_PATH.upper() : MODEL_META_DATA_COMPONENT_SOURCE_OVERRIDE_PATH, + TAB_DSC_DEFINES.upper() : MODEL_META_DATA_HEADER, + TAB_DSC_DEFINES_DEFINE : MODEL_META_DATA_DEFINE, + TAB_DSC_DEFINES_EDKGLOBAL : MODEL_META_DATA_GLOBAL_DEFINE, + TAB_INCLUDE.upper() : MODEL_META_DATA_INCLUDE, + TAB_IF.upper() : MODEL_META_DATA_CONDITIONAL_STATEMENT_IF, + TAB_IF_DEF.upper() : MODEL_META_DATA_CONDITIONAL_STATEMENT_IFDEF, + TAB_IF_N_DEF.upper() : MODEL_META_DATA_CONDITIONAL_STATEMENT_IFNDEF, + TAB_ELSE_IF.upper() : MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSEIF, + TAB_ELSE.upper() : MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSE, + TAB_END_IF.upper() : MODEL_META_DATA_CONDITIONAL_STATEMENT_ENDIF, + } + + # Valid names in define section + DefineKeywords = [ + "DSC_SPECIFICATION", + "PLATFORM_NAME", + "PLATFORM_GUID", + "PLATFORM_VERSION", + "SKUID_IDENTIFIER", + "SUPPORTED_ARCHITECTURES", + "BUILD_TARGETS", + "OUTPUT_DIRECTORY", + "FLASH_DEFINITION", + "BUILD_NUMBER", + "RFC_LANGUAGES", + "ISO_LANGUAGES", + "TIME_STAMP_FILE", + "VPD_TOOL_GUID", + "FIX_LOAD_TOP_MEMORY_ADDRESS" + ] + + SymbolPattern = ValueExpression.SymbolPattern + + ## Constructor of DscParser + # + # Initialize object of DscParser + # + # @param FilePath The path of platform description file + # @param FileType The raw data of DSC file + # @param Table Database used to retrieve module/package information + # @param Macros Macros used for replacement in file + # @param Owner Owner ID (for sub-section parsing) + # @param From ID from which the data comes (for !INCLUDE directive) + # + def __init__(self, FilePath, FileType, Table, Owner=-1, From=-1): + # prevent re-initialization + if hasattr(self, "_Table"): + return + MetaFileParser.__init__(self, FilePath, FileType, Table, Owner, From) + self._Version = 0x00010005 # Only EDK2 dsc file is supported + # to store conditional directive evaluation result + self._DirectiveStack = [] + self._DirectiveEvalStack = [] + self._Enabled = 1 + + # Final valid replacable symbols + self._Symbols = {} + # + # Map the ID between the original table and new table to track + # the owner item + # + self._IdMapping = {-1:-1} + + self.TblFile = EccGlobalData.gDb.TblFile + self.FileID = -1 + + ## Parser starter + def Start(self): + Content = '' + try: + Content = open(str(self.MetaFile), 'r').readlines() + except: + EdkLogger.error("Parser", FILE_READ_FAILURE, ExtraData=self.MetaFile) + # + # Insert a record for file + # + Filename = NormPath(self.MetaFile) + FileID = self.TblFile.GetFileId(Filename) + if FileID: + self.FileID = FileID + else: + self.FileID = self.TblFile.InsertFile(Filename, MODEL_FILE_DSC) + + + for Index in range(0, len(Content)): + Line = CleanString(Content[Index]) + # skip empty line + if Line == '': + continue + + self._CurrentLine = Line + self._LineIndex = Index + if self._InSubsection and self._Owner[-1] == -1: + self._Owner.append(self._LastItem) + + # section header + if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END: + self._SectionType = MODEL_META_DATA_SECTION_HEADER + # subsection ending + elif Line[0] == '}' and self._InSubsection: + self._InSubsection = False + self._SubsectionType = MODEL_UNKNOWN + self._SubsectionName = '' + self._Owner[-1] = -1 + continue + # subsection header + elif Line[0] == TAB_OPTION_START and Line[-1] == TAB_OPTION_END: + self._SubsectionType = MODEL_META_DATA_SUBSECTION_HEADER + # directive line + elif Line[0] == '!': + self._DirectiveParser() + continue + + if self._InSubsection: + SectionType = self._SubsectionType + else: + SectionType = self._SectionType + self._ItemType = SectionType + + self._ValueList = ['', '', ''] + self._SectionParser[SectionType](self) + if self._ValueList == None: + continue + # + # Model, Value1, Value2, Value3, Arch, ModuleType, BelongsToItem=-1, BelongsToFile=-1, + # LineBegin=-1, ColumnBegin=-1, LineEnd=-1, ColumnEnd=-1, Enabled=-1 + # + for Arch, ModuleType in self._Scope: + self._LastItem = self._Store( + self._ItemType, + self._ValueList[0], + self._ValueList[1], + self._ValueList[2], + Arch, + ModuleType, + self._Owner[-1], + self.FileID, + self._From, + self._LineIndex+1, + -1, + self._LineIndex+1, + -1, + self._Enabled + ) + + if self._DirectiveStack: + Type, Line, Text = self._DirectiveStack[-1] + EdkLogger.error('Parser', FORMAT_INVALID, "No matching '!endif' found", + ExtraData=Text, File=self.MetaFile, Line=Line) + self._Done() + + ## <subsection_header> parser + def _SubsectionHeaderParser(self): + self._SubsectionName = self._CurrentLine[1:-1].upper() + if self._SubsectionName in self.DataType: + self._SubsectionType = self.DataType[self._SubsectionName] + else: + self._SubsectionType = MODEL_UNKNOWN + EdkLogger.warn("Parser", "Unrecognized sub-section", File=self.MetaFile, + Line=self._LineIndex+1, ExtraData=self._CurrentLine) + self._ValueList[0] = self._SubsectionName + + ## Directive statement parser + def _DirectiveParser(self): + self._ValueList = ['','',''] + TokenList = GetSplitValueList(self._CurrentLine, ' ', 1) + self._ValueList[0:len(TokenList)] = TokenList + + # Syntax check + DirectiveName = self._ValueList[0].upper() + if DirectiveName not in self.DataType: + EdkLogger.error("Parser", FORMAT_INVALID, "Unknown directive [%s]" % DirectiveName, + File=self.MetaFile, Line=self._LineIndex+1) + if DirectiveName in ['!IF', '!IFDEF', '!INCLUDE', '!IFNDEF', '!ELSEIF'] and self._ValueList[1] == '': + EdkLogger.error("Parser", FORMAT_INVALID, "Missing expression", + File=self.MetaFile, Line=self._LineIndex+1, + ExtraData=self._CurrentLine) + + ItemType = self.DataType[DirectiveName] + if ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_ENDIF: + # Remove all directives between !if and !endif, including themselves + while self._DirectiveStack: + # Remove any !else or !elseif + DirectiveInfo = self._DirectiveStack.pop() + if DirectiveInfo[0] in [MODEL_META_DATA_CONDITIONAL_STATEMENT_IF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFDEF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFNDEF]: + break + else: + EdkLogger.error("Parser", FORMAT_INVALID, "Redundant '!endif'", + File=self.MetaFile, Line=self._LineIndex+1, + ExtraData=self._CurrentLine) + elif ItemType != MODEL_META_DATA_INCLUDE: + # Break if there's a !else is followed by a !elseif + if ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSEIF and \ + self._DirectiveStack and \ + self._DirectiveStack[-1][0] == MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSE: + EdkLogger.error("Parser", FORMAT_INVALID, "'!elseif' after '!else'", + File=self.MetaFile, Line=self._LineIndex+1, + ExtraData=self._CurrentLine) + self._DirectiveStack.append((ItemType, self._LineIndex+1, self._CurrentLine)) + elif self._From > 0: + EdkLogger.error('Parser', FORMAT_INVALID, + "No '!include' allowed in included file", + ExtraData=self._CurrentLine, File=self.MetaFile, + Line=self._LineIndex+1) + + # + # Model, Value1, Value2, Value3, Arch, ModuleType, BelongsToItem=-1, BelongsToFile=-1, + # LineBegin=-1, ColumnBegin=-1, LineEnd=-1, ColumnEnd=-1, Enabled=-1 + # + self._LastItem = self._Store( + ItemType, + self._ValueList[0], + self._ValueList[1], + self._ValueList[2], + 'COMMON', + 'COMMON', + self._Owner[-1], + self.FileID, + self._From, + self._LineIndex+1, + -1, + self._LineIndex+1, + -1, + 0 + ) + + ## [defines] section parser + @ParseMacro + def _DefineParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_EQUAL_SPLIT, 1) + self._ValueList[1:len(TokenList)] = TokenList + + # Syntax check + if not self._ValueList[1]: + EdkLogger.error('Parser', FORMAT_INVALID, "No name specified", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + if not self._ValueList[2]: + EdkLogger.error('Parser', FORMAT_INVALID, "No value specified", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + if not self._ValueList[1] in self.DefineKeywords: + EdkLogger.error('Parser', FORMAT_INVALID, + "Unknown keyword found: %s. " + "If this is a macro you must " + "add it as a DEFINE in the DSC" % self._ValueList[1], + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + self._Defines[self._ValueList[1]] = self._ValueList[2] + self._ItemType = self.DataType[TAB_DSC_DEFINES.upper()] + + @ParseMacro + def _SkuIdParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT) + if len(TokenList) != 2: + EdkLogger.error('Parser', FORMAT_INVALID, "Correct format is '<Integer>|<UiName>'", + ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1) + self._ValueList[0:len(TokenList)] = TokenList + + ## Parse Edk style of library modules + def _LibraryInstanceParser(self): + self._ValueList[0] = self._CurrentLine + + ## PCD sections parser + # + # [PcdsFixedAtBuild] + # [PcdsPatchableInModule] + # [PcdsFeatureFlag] + # [PcdsDynamicEx + # [PcdsDynamicExDefault] + # [PcdsDynamicExVpd] + # [PcdsDynamicExHii] + # [PcdsDynamic] + # [PcdsDynamicDefault] + # [PcdsDynamicVpd] + # [PcdsDynamicHii] + # + @ParseMacro + def _PcdParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT, 1) + self._ValueList[0:1] = GetSplitValueList(TokenList[0], TAB_SPLIT) + if len(TokenList) == 2: + self._ValueList[2] = TokenList[1] + if self._ValueList[0] == '' or self._ValueList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No token space GUID or PCD name specified", + ExtraData=self._CurrentLine + " (<TokenSpaceGuidCName>.<TokenCName>|<PcdValue>)", + File=self.MetaFile, Line=self._LineIndex+1) + if self._ValueList[2] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No PCD value given", + ExtraData=self._CurrentLine + " (<TokenSpaceGuidCName>.<TokenCName>|<PcdValue>)", + File=self.MetaFile, Line=self._LineIndex+1) + # if value are 'True', 'true', 'TRUE' or 'False', 'false', 'FALSE', replace with integer 1 or 0. + DscPcdValueList = GetSplitValueList(TokenList[1], TAB_VALUE_SPLIT, 1) + if DscPcdValueList[0] in ['True', 'true', 'TRUE']: + self._ValueList[2] = TokenList[1].replace(DscPcdValueList[0], '1', 1); + elif DscPcdValueList[0] in ['False', 'false', 'FALSE']: + self._ValueList[2] = TokenList[1].replace(DscPcdValueList[0], '0', 1); + + ## [components] section parser + @ParseMacro + def _ComponentParser(self): + if self._CurrentLine[-1] == '{': + self._ValueList[0] = self._CurrentLine[0:-1].strip() + self._InSubsection = True + else: + self._ValueList[0] = self._CurrentLine + + ## [LibraryClasses] section + @ParseMacro + def _LibraryClassParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT) + if len(TokenList) < 2: + EdkLogger.error('Parser', FORMAT_INVALID, "No library class or instance specified", + ExtraData=self._CurrentLine + " (<LibraryClassName>|<LibraryInstancePath>)", + File=self.MetaFile, Line=self._LineIndex+1) + if TokenList[0] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No library class specified", + ExtraData=self._CurrentLine + " (<LibraryClassName>|<LibraryInstancePath>)", + File=self.MetaFile, Line=self._LineIndex+1) + if TokenList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No library instance specified", + ExtraData=self._CurrentLine + " (<LibraryClassName>|<LibraryInstancePath>)", + File=self.MetaFile, Line=self._LineIndex+1) + + self._ValueList[0:len(TokenList)] = TokenList + + def _CompponentSourceOverridePathParser(self): + self._ValueList[0] = self._CurrentLine + + ## [BuildOptions] section parser + @ParseMacro + def _BuildOptionParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_EQUAL_SPLIT, 1) + TokenList2 = GetSplitValueList(TokenList[0], ':', 1) + if len(TokenList2) == 2: + self._ValueList[0] = TokenList2[0] # toolchain family + self._ValueList[1] = TokenList2[1] # keys + else: + self._ValueList[1] = TokenList[0] + if len(TokenList) == 2: # value + self._ValueList[2] = TokenList[1] + + if self._ValueList[1].count('_') != 4: + EdkLogger.error( + 'Parser', + FORMAT_INVALID, + "'%s' must be in format of <TARGET>_<TOOLCHAIN>_<ARCH>_<TOOL>_FLAGS" % self._ValueList[1], + ExtraData=self._CurrentLine, + File=self.MetaFile, + Line=self._LineIndex+1 + ) + + ## Override parent's method since we'll do all macro replacements in parser + def _GetMacros(self): + Macros = {} + Macros.update(self._FileLocalMacros) + Macros.update(self._GetApplicableSectionMacro()) + Macros.update(GlobalData.gEdkGlobal) + Macros.update(GlobalData.gPlatformDefines) + Macros.update(GlobalData.gCommandLineDefines) + # PCD cannot be referenced in macro definition + if self._ItemType not in [MODEL_META_DATA_DEFINE, MODEL_META_DATA_GLOBAL_DEFINE]: + Macros.update(self._Symbols) + return Macros + + def _PostProcess(self): + Processer = { + MODEL_META_DATA_SECTION_HEADER : self.__ProcessSectionHeader, + MODEL_META_DATA_SUBSECTION_HEADER : self.__ProcessSubsectionHeader, + MODEL_META_DATA_HEADER : self.__ProcessDefine, + MODEL_META_DATA_DEFINE : self.__ProcessDefine, + MODEL_META_DATA_GLOBAL_DEFINE : self.__ProcessDefine, + MODEL_META_DATA_INCLUDE : self.__ProcessDirective, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IF : self.__ProcessDirective, + MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSE : self.__ProcessDirective, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFDEF : self.__ProcessDirective, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFNDEF : self.__ProcessDirective, + MODEL_META_DATA_CONDITIONAL_STATEMENT_ENDIF : self.__ProcessDirective, + MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSEIF : self.__ProcessDirective, + MODEL_EFI_SKU_ID : self.__ProcessSkuId, + MODEL_EFI_LIBRARY_INSTANCE : self.__ProcessLibraryInstance, + MODEL_EFI_LIBRARY_CLASS : self.__ProcessLibraryClass, + MODEL_PCD_FIXED_AT_BUILD : self.__ProcessPcd, + MODEL_PCD_PATCHABLE_IN_MODULE : self.__ProcessPcd, + MODEL_PCD_FEATURE_FLAG : self.__ProcessPcd, + MODEL_PCD_DYNAMIC_DEFAULT : self.__ProcessPcd, + MODEL_PCD_DYNAMIC_HII : self.__ProcessPcd, + MODEL_PCD_DYNAMIC_VPD : self.__ProcessPcd, + MODEL_PCD_DYNAMIC_EX_DEFAULT : self.__ProcessPcd, + MODEL_PCD_DYNAMIC_EX_HII : self.__ProcessPcd, + MODEL_PCD_DYNAMIC_EX_VPD : self.__ProcessPcd, + MODEL_META_DATA_COMPONENT : self.__ProcessComponent, + MODEL_META_DATA_COMPONENT_SOURCE_OVERRIDE_PATH : self.__ProcessSourceOverridePath, + MODEL_META_DATA_BUILD_OPTION : self.__ProcessBuildOption, + MODEL_UNKNOWN : self._Skip, + MODEL_META_DATA_USER_EXTENSION : self._Skip, + } + + self._RawTable = self._Table + self._Table = MetaFileStorage(self._RawTable.Cur, self.MetaFile, MODEL_FILE_DSC, True) + self._DirectiveStack = [] + self._DirectiveEvalStack = [] + self._FileWithError = self.MetaFile + self._FileLocalMacros = {} + self._SectionsMacroDict = {} + GlobalData.gPlatformDefines = {} + + # Get all macro and PCD which has straitforward value + self.__RetrievePcdValue() + self._Content = self._RawTable.GetAll() + self._ContentIndex = 0 + while self._ContentIndex < len(self._Content) : + Id, self._ItemType, V1, V2, V3, S1, S2, Owner, BelongsToFile, self._From, \ + LineStart, ColStart, LineEnd, ColEnd, Enabled = self._Content[self._ContentIndex] + + if self._From < 0: + self._FileWithError = self.MetaFile + + self._ContentIndex += 1 + + self._Scope = [[S1, S2]] + self._LineIndex = LineStart - 1 + self._ValueList = [V1, V2, V3] + + try: + Processer[self._ItemType]() + except EvaluationException, Excpt: + # + # Only catch expression evaluation error here. We need to report + # the precise number of line on which the error occurred + # + EdkLogger.error('Parser', FORMAT_INVALID, "Invalid expression: %s" % str(Excpt), + File=self._FileWithError, ExtraData=' '.join(self._ValueList), + Line=self._LineIndex+1) + except MacroException, Excpt: + EdkLogger.error('Parser', FORMAT_INVALID, str(Excpt), + File=self._FileWithError, ExtraData=' '.join(self._ValueList), + Line=self._LineIndex+1) + + if self._ValueList == None: + continue + + NewOwner = self._IdMapping.get(Owner, -1) + self._Enabled = int((not self._DirectiveEvalStack) or (False not in self._DirectiveEvalStack)) + self._LastItem = self._Store( + self._ItemType, + self._ValueList[0], + self._ValueList[1], + self._ValueList[2], + S1, + S2, + NewOwner, + BelongsToFile, + self._From, + self._LineIndex+1, + -1, + self._LineIndex+1, + -1, + self._Enabled + ) + self._IdMapping[Id] = self._LastItem + + RecordList = self._Table.GetAll() + for Record in RecordList: + EccGlobalData.gDb.TblDsc.Insert(Record[1],Record[2],Record[3],Record[4],Record[5],Record[6],Record[7],Record[8],Record[9],Record[10],Record[11],Record[12],Record[13],Record[14]) + GlobalData.gPlatformDefines.update(self._FileLocalMacros) + self._PostProcessed = True + self._Content = None + + def __ProcessSectionHeader(self): + self._SectionName = self._ValueList[0] + if self._SectionName in self.DataType: + self._SectionType = self.DataType[self._SectionName] + else: + self._SectionType = MODEL_UNKNOWN + + def __ProcessSubsectionHeader(self): + self._SubsectionName = self._ValueList[0] + if self._SubsectionName in self.DataType: + self._SubsectionType = self.DataType[self._SubsectionName] + else: + self._SubsectionType = MODEL_UNKNOWN + + def __RetrievePcdValue(self): + Records = self._RawTable.Query(MODEL_PCD_FEATURE_FLAG, BelongsToItem=-1.0) + for TokenSpaceGuid,PcdName,Value,Dummy2,Dummy3,ID,Line in Records: + Value, DatumType, MaxDatumSize = AnalyzePcdData(Value) + # Only use PCD whose value is straitforward (no macro and PCD) + if self.SymbolPattern.findall(Value): + continue + Name = TokenSpaceGuid + '.' + PcdName + # Don't use PCD with different values. + if Name in self._Symbols and self._Symbols[Name] != Value: + self._Symbols.pop(Name) + continue + self._Symbols[Name] = Value + + Records = self._RawTable.Query(MODEL_PCD_FIXED_AT_BUILD, BelongsToItem=-1.0) + for TokenSpaceGuid,PcdName,Value,Dummy2,Dummy3,ID,Line in Records: + Value, DatumType, MaxDatumSize = AnalyzePcdData(Value) + # Only use PCD whose value is straitforward (no macro and PCD) + if self.SymbolPattern.findall(Value): + continue + Name = TokenSpaceGuid+'.'+PcdName + # Don't use PCD with different values. + if Name in self._Symbols and self._Symbols[Name] != Value: + self._Symbols.pop(Name) + continue + self._Symbols[Name] = Value + + def __ProcessDefine(self): + if not self._Enabled: + return + + Type, Name, Value = self._ValueList + Value = ReplaceMacro(Value, self._Macros, False) + if self._ItemType == MODEL_META_DATA_DEFINE: + if self._SectionType == MODEL_META_DATA_HEADER: + self._FileLocalMacros[Name] = Value + else: + SectionDictKey = self._SectionType, self._Scope[0][0], self._Scope[0][1] + if SectionDictKey not in self._SectionsMacroDict: + self._SectionsMacroDict[SectionDictKey] = {} + SectionLocalMacros = self._SectionsMacroDict[SectionDictKey] + SectionLocalMacros[Name] = Value + elif self._ItemType == MODEL_META_DATA_GLOBAL_DEFINE: + GlobalData.gEdkGlobal[Name] = Value + + # + # Keyword in [Defines] section can be used as Macros + # + if (self._ItemType == MODEL_META_DATA_HEADER) and (self._SectionType == MODEL_META_DATA_HEADER): + self._FileLocalMacros[Name] = Value + + self._ValueList = [Type, Name, Value] + + def __ProcessDirective(self): + Result = None + if self._ItemType in [MODEL_META_DATA_CONDITIONAL_STATEMENT_IF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSEIF]: + Macros = self._Macros + Macros.update(GlobalData.gGlobalDefines) + try: + Result = ValueExpression(self._ValueList[1], Macros)() + except SymbolNotFound, Exc: + EdkLogger.debug(EdkLogger.DEBUG_5, str(Exc), self._ValueList[1]) + Result = False + except WrnExpression, Excpt: + # + # Catch expression evaluation warning here. We need to report + # the precise number of line and return the evaluation result + # + EdkLogger.warn('Parser', "Suspicious expression: %s" % str(Excpt), + File=self._FileWithError, ExtraData=' '.join(self._ValueList), + Line=self._LineIndex+1) + Result = Excpt.result + + if self._ItemType in [MODEL_META_DATA_CONDITIONAL_STATEMENT_IF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFDEF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFNDEF]: + self._DirectiveStack.append(self._ItemType) + if self._ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_IF: + Result = bool(Result) + else: + Macro = self._ValueList[1] + Macro = Macro[2:-1] if (Macro.startswith("$(") and Macro.endswith(")")) else Macro + Result = Macro in self._Macros + if self._ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_IFNDEF: + Result = not Result + self._DirectiveEvalStack.append(Result) + elif self._ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSEIF: + self._DirectiveStack.append(self._ItemType) + self._DirectiveEvalStack[-1] = not self._DirectiveEvalStack[-1] + self._DirectiveEvalStack.append(bool(Result)) + elif self._ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSE: + self._DirectiveStack[-1] = self._ItemType + self._DirectiveEvalStack[-1] = not self._DirectiveEvalStack[-1] + elif self._ItemType == MODEL_META_DATA_CONDITIONAL_STATEMENT_ENDIF: + # Back to the nearest !if/!ifdef/!ifndef + while self._DirectiveStack: + self._DirectiveEvalStack.pop() + Directive = self._DirectiveStack.pop() + if Directive in [MODEL_META_DATA_CONDITIONAL_STATEMENT_IF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFDEF, + MODEL_META_DATA_CONDITIONAL_STATEMENT_ELSE, + MODEL_META_DATA_CONDITIONAL_STATEMENT_IFNDEF]: + break + elif self._ItemType == MODEL_META_DATA_INCLUDE: + # The included file must be relative to workspace or same directory as DSC file + __IncludeMacros = {} + # + # Allow using system environment variables in path after !include + # + __IncludeMacros['WORKSPACE'] = GlobalData.gGlobalDefines['WORKSPACE'] + if "ECP_SOURCE" in GlobalData.gGlobalDefines.keys(): + __IncludeMacros['ECP_SOURCE'] = GlobalData.gGlobalDefines['ECP_SOURCE'] + # + # During GenFds phase call DSC parser, will go into this branch. + # + elif "ECP_SOURCE" in GlobalData.gCommandLineDefines.keys(): + __IncludeMacros['ECP_SOURCE'] = GlobalData.gCommandLineDefines['ECP_SOURCE'] + + __IncludeMacros['EFI_SOURCE'] = GlobalData.gGlobalDefines['EFI_SOURCE'] + __IncludeMacros['EDK_SOURCE'] = GlobalData.gGlobalDefines['EDK_SOURCE'] + # + # Allow using MACROs comes from [Defines] section to keep compatible. + # + __IncludeMacros.update(self._Macros) + + IncludedFile = NormPath(ReplaceMacro(self._ValueList[1], __IncludeMacros, RaiseError=True)) + # + # First search the include file under the same directory as DSC file + # + IncludedFile1 = PathClass(IncludedFile, self.MetaFile.Dir) + ErrorCode, ErrorInfo1 = IncludedFile1.Validate() + if ErrorCode != 0: + # + # Also search file under the WORKSPACE directory + # + IncludedFile1 = PathClass(IncludedFile, GlobalData.gWorkspace) + ErrorCode, ErrorInfo2 = IncludedFile1.Validate() + if ErrorCode != 0: + EdkLogger.error('parser', ErrorCode, File=self._FileWithError, + Line=self._LineIndex+1, ExtraData=ErrorInfo1 + "\n"+ ErrorInfo2) + + self._FileWithError = IncludedFile1 + + IncludedFileTable = MetaFileStorage(self._Table.Cur, IncludedFile1, MODEL_FILE_DSC, True) + Owner = self._Content[self._ContentIndex-1][0] + Parser = DscParser(IncludedFile1, self._FileType, IncludedFileTable, + Owner=Owner, From=Owner) + + # set the parser status with current status + Parser._SectionName = self._SectionName + Parser._SectionType = self._SectionType + Parser._Scope = self._Scope + Parser._Enabled = self._Enabled + # Parse the included file + Parser.Start() + + # update current status with sub-parser's status + self._SectionName = Parser._SectionName + self._SectionType = Parser._SectionType + self._Scope = Parser._Scope + self._Enabled = Parser._Enabled + + # Insert all records in the table for the included file into dsc file table + Records = IncludedFileTable.GetAll() + if Records: + self._Content[self._ContentIndex:self._ContentIndex] = Records + self._Content.pop(self._ContentIndex-1) + self._ValueList = None + self._ContentIndex -= 1 + + def __ProcessSkuId(self): + self._ValueList = [ReplaceMacro(Value, self._Macros, RaiseError=True) + for Value in self._ValueList] + + def __ProcessLibraryInstance(self): + self._ValueList = [ReplaceMacro(Value, self._Macros) for Value in self._ValueList] + + def __ProcessLibraryClass(self): + self._ValueList[1] = ReplaceMacro(self._ValueList[1], self._Macros, RaiseError=True) + + def __ProcessPcd(self): + ValueList = GetSplitValueList(self._ValueList[2]) + # + # PCD value can be an expression + # + if len(ValueList) > 1 and ValueList[1] == 'VOID*': + PcdValue = ValueList[0] + try: + ValueList[0] = ValueExpression(PcdValue, self._Macros)(True) + except WrnExpression, Value: + ValueList[0] = Value.result + else: + PcdValue = ValueList[-1] + try: + ValueList[-1] = ValueExpression(PcdValue, self._Macros)(True) + except WrnExpression, Value: + ValueList[-1] = Value.result + + if ValueList[-1] == 'True': + ValueList[-1] = '1' + if ValueList[-1] == 'False': + ValueList[-1] = '0' + + self._ValueList[2] = '|'.join(ValueList) + + def __ProcessComponent(self): + self._ValueList[0] = ReplaceMacro(self._ValueList[0], self._Macros) + + def __ProcessSourceOverridePath(self): + self._ValueList[0] = ReplaceMacro(self._ValueList[0], self._Macros) + + def __ProcessBuildOption(self): + self._ValueList = [ReplaceMacro(Value, self._Macros, RaiseError=False) + for Value in self._ValueList] + + _SectionParser = { + MODEL_META_DATA_HEADER : _DefineParser, + MODEL_EFI_SKU_ID : _SkuIdParser, + MODEL_EFI_LIBRARY_INSTANCE : _LibraryInstanceParser, + MODEL_EFI_LIBRARY_CLASS : _LibraryClassParser, + MODEL_PCD_FIXED_AT_BUILD : _PcdParser, + MODEL_PCD_PATCHABLE_IN_MODULE : _PcdParser, + MODEL_PCD_FEATURE_FLAG : _PcdParser, + MODEL_PCD_DYNAMIC_DEFAULT : _PcdParser, + MODEL_PCD_DYNAMIC_HII : _PcdParser, + MODEL_PCD_DYNAMIC_VPD : _PcdParser, + MODEL_PCD_DYNAMIC_EX_DEFAULT : _PcdParser, + MODEL_PCD_DYNAMIC_EX_HII : _PcdParser, + MODEL_PCD_DYNAMIC_EX_VPD : _PcdParser, + MODEL_META_DATA_COMPONENT : _ComponentParser, + MODEL_META_DATA_COMPONENT_SOURCE_OVERRIDE_PATH : _CompponentSourceOverridePathParser, + MODEL_META_DATA_BUILD_OPTION : _BuildOptionParser, + MODEL_UNKNOWN : MetaFileParser._Skip, + MODEL_META_DATA_USER_EXTENSION : MetaFileParser._Skip, + MODEL_META_DATA_SECTION_HEADER : MetaFileParser._SectionHeaderParser, + MODEL_META_DATA_SUBSECTION_HEADER : _SubsectionHeaderParser, + } + + _Macros = property(_GetMacros) + +## DEC file parser class +# +# @param FilePath The path of platform description file +# @param FileType The raw data of DSC file +# @param Table Database used to retrieve module/package information +# @param Macros Macros used for replacement in file +# +class DecParser(MetaFileParser): + # DEC file supported data types (one type per section) + DataType = { + TAB_DEC_DEFINES.upper() : MODEL_META_DATA_HEADER, + TAB_DSC_DEFINES_DEFINE : MODEL_META_DATA_DEFINE, + TAB_INCLUDES.upper() : MODEL_EFI_INCLUDE, + TAB_LIBRARY_CLASSES.upper() : MODEL_EFI_LIBRARY_CLASS, + TAB_GUIDS.upper() : MODEL_EFI_GUID, + TAB_PPIS.upper() : MODEL_EFI_PPI, + TAB_PROTOCOLS.upper() : MODEL_EFI_PROTOCOL, + TAB_PCDS_FIXED_AT_BUILD_NULL.upper() : MODEL_PCD_FIXED_AT_BUILD, + TAB_PCDS_PATCHABLE_IN_MODULE_NULL.upper() : MODEL_PCD_PATCHABLE_IN_MODULE, + TAB_PCDS_FEATURE_FLAG_NULL.upper() : MODEL_PCD_FEATURE_FLAG, + TAB_PCDS_DYNAMIC_NULL.upper() : MODEL_PCD_DYNAMIC, + TAB_PCDS_DYNAMIC_EX_NULL.upper() : MODEL_PCD_DYNAMIC_EX, + } + + ## Constructor of DecParser + # + # Initialize object of DecParser + # + # @param FilePath The path of platform description file + # @param FileType The raw data of DSC file + # @param Table Database used to retrieve module/package information + # @param Macros Macros used for replacement in file + # + def __init__(self, FilePath, FileType, Table): + # prevent re-initialization + if hasattr(self, "_Table"): + return + MetaFileParser.__init__(self, FilePath, FileType, Table) + self._Comments = [] + self._Version = 0x00010005 # Only EDK2 dec file is supported + self.TblFile = EccGlobalData.gDb.TblFile + self.FileID = -1 + + ## Parser starter + def Start(self): + Content = '' + try: + Content = open(str(self.MetaFile), 'r').readlines() + except: + EdkLogger.error("Parser", FILE_READ_FAILURE, ExtraData=self.MetaFile) + + # + # Insert a record for file + # + Filename = NormPath(self.MetaFile) + FileID = self.TblFile.GetFileId(Filename) + if FileID: + self.FileID = FileID + else: + self.FileID = self.TblFile.InsertFile(Filename, MODEL_FILE_DEC) + + for Index in range(0, len(Content)): + Line, Comment = CleanString2(Content[Index]) + self._CurrentLine = Line + self._LineIndex = Index + + # save comment for later use + if Comment: + self._Comments.append((Comment, self._LineIndex+1)) + # skip empty line + if Line == '': + continue + + # section header + if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END: + self._SectionHeaderParser() + self._Comments = [] + continue + elif len(self._SectionType) == 0: + self._Comments = [] + continue + + # section content + self._ValueList = ['','',''] + self._SectionParser[self._SectionType[0]](self) + if self._ValueList == None or self._ItemType == MODEL_META_DATA_DEFINE: + self._ItemType = -1 + self._Comments = [] + continue + + # + # Model, Value1, Value2, Value3, Arch, BelongsToItem=-1, LineBegin=-1, + # ColumnBegin=-1, LineEnd=-1, ColumnEnd=-1, FeatureFlag='', Enabled=-1 + # + for Arch, ModuleType, Type in self._Scope: + self._LastItem = self._Store( + Type, + self._ValueList[0], + self._ValueList[1], + self._ValueList[2], + Arch, + ModuleType, + self._Owner[-1], + self.FileID, + self._LineIndex+1, + -1, + self._LineIndex+1, + -1, + 0 + ) + for Comment, LineNo in self._Comments: + self._Store( + MODEL_META_DATA_COMMENT, + Comment, + self._ValueList[0], + self._ValueList[1], + Arch, + ModuleType, + self._LastItem, + self.FileID, + LineNo, + -1, + LineNo, + -1, + 0 + ) + self._Comments = [] + self._Done() + + def _GetApplicableSectionMacro(self): + Macros = {} + for S1, S2, SectionType in self._Scope: + for Scope1, Scope2 in [("COMMON", "COMMON"), ("COMMON", S2), (S1, "COMMON"), (S1, S2)]: + if (SectionType, Scope1, Scope2) in self._SectionsMacroDict: + Macros.update(self._SectionsMacroDict[(SectionType, Scope1, Scope2)]) + return Macros + + ## Section header parser + # + # The section header is always in following format: + # + # [section_name.arch<.platform|module_type>] + # + def _SectionHeaderParser(self): + self._Scope = [] + self._SectionName = '' + self._SectionType = [] + ArchList = set() + for Item in GetSplitValueList(self._CurrentLine[1:-1], TAB_COMMA_SPLIT): + if Item == '': + continue + ItemList = GetSplitValueList(Item, TAB_SPLIT) + + # different types of PCD are permissible in one section + self._SectionName = ItemList[0].upper() + if self._SectionName in self.DataType: + if self.DataType[self._SectionName] not in self._SectionType: + self._SectionType.append(self.DataType[self._SectionName]) + else: + EdkLogger.warn("Parser", "Unrecognized section", File=self.MetaFile, + Line=self._LineIndex+1, ExtraData=self._CurrentLine) + continue + + if MODEL_PCD_FEATURE_FLAG in self._SectionType and len(self._SectionType) > 1: + EdkLogger.error( + 'Parser', + FORMAT_INVALID, + "%s must not be in the same section of other types of PCD" % TAB_PCDS_FEATURE_FLAG_NULL, + File=self.MetaFile, + Line=self._LineIndex+1, + ExtraData=self._CurrentLine + ) + # S1 is always Arch + if len(ItemList) > 1: + S1 = ItemList[1].upper() + else: + S1 = 'COMMON' + ArchList.add(S1) + # S2 may be Platform or ModuleType + if len(ItemList) > 2: + S2 = ItemList[2].upper() + else: + S2 = 'COMMON' + if [S1, S2, self.DataType[self._SectionName]] not in self._Scope: + self._Scope.append([S1, S2, self.DataType[self._SectionName]]) + + # 'COMMON' must not be used with specific ARCHs at the same section + if 'COMMON' in ArchList and len(ArchList) > 1: + EdkLogger.error('Parser', FORMAT_INVALID, "'common' ARCH must not be used with specific ARCHs", + File=self.MetaFile, Line=self._LineIndex+1, ExtraData=self._CurrentLine) + + ## [guids], [ppis] and [protocols] section parser + @ParseMacro + def _GuidParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_EQUAL_SPLIT, 1) + if len(TokenList) < 2: + EdkLogger.error('Parser', FORMAT_INVALID, "No GUID name or value specified", + ExtraData=self._CurrentLine + " (<CName> = <GuidValueInCFormat>)", + File=self.MetaFile, Line=self._LineIndex+1) + if TokenList[0] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No GUID name specified", + ExtraData=self._CurrentLine + " (<CName> = <GuidValueInCFormat>)", + File=self.MetaFile, Line=self._LineIndex+1) + if TokenList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No GUID value specified", + ExtraData=self._CurrentLine + " (<CName> = <GuidValueInCFormat>)", + File=self.MetaFile, Line=self._LineIndex+1) + if TokenList[1][0] != '{' or TokenList[1][-1] != '}' or GuidStructureStringToGuidString(TokenList[1]) == '': + EdkLogger.error('Parser', FORMAT_INVALID, "Invalid GUID value format", + ExtraData=self._CurrentLine + \ + " (<CName> = <GuidValueInCFormat:{8,4,4,{2,2,2,2,2,2,2,2}}>)", + File=self.MetaFile, Line=self._LineIndex+1) + self._ValueList[0] = TokenList[0] + #Parse the Guid value format + GuidValueList = TokenList[1].strip(' {}').split(',') + Index = 0 + HexList = [] + if len(GuidValueList) == 11: + for GuidValue in GuidValueList: + GuidValue = GuidValue.strip() + if GuidValue.startswith('0x') or GuidValue.startswith('0X'): + HexList.append('0x' + str(GuidValue[2:])) + Index += 1 + continue + else: + if GuidValue.startswith('{'): + HexList.append('0x' + str(GuidValue[3:])) + Index += 1 + self._ValueList[1] = "{ %s, %s, %s, { %s, %s, %s, %s, %s, %s, %s, %s }}" % (HexList[0], HexList[1], HexList[2],HexList[3],HexList[4],HexList[5],HexList[6],HexList[7],HexList[8],HexList[9],HexList[10]) + else: + EdkLogger.error('Parser', FORMAT_INVALID, "Invalid GUID value format", + ExtraData=self._CurrentLine + \ + " (<CName> = <GuidValueInCFormat:{8,4,4,{2,2,2,2,2,2,2,2}}>)", + File=self.MetaFile, Line=self._LineIndex+1) + self._ValueList[0] = '' + + ## PCD sections parser + # + # [PcdsFixedAtBuild] + # [PcdsPatchableInModule] + # [PcdsFeatureFlag] + # [PcdsDynamicEx + # [PcdsDynamic] + # + @ParseMacro + def _PcdParser(self): + TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT, 1) + self._ValueList[0:1] = GetSplitValueList(TokenList[0], TAB_SPLIT) + # check PCD information + if self._ValueList[0] == '' or self._ValueList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No token space GUID or PCD name specified", + ExtraData=self._CurrentLine + \ + " (<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>)", + File=self.MetaFile, Line=self._LineIndex+1) + # check PCD datum information + if len(TokenList) < 2 or TokenList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "No PCD Datum information given", + ExtraData=self._CurrentLine + \ + " (<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>)", + File=self.MetaFile, Line=self._LineIndex+1) + + + ValueRe = re.compile(r'^\s*L?\".*\|.*\"') + PtrValue = ValueRe.findall(TokenList[1]) + + # Has VOID* type string, may contain "|" character in the string. + if len(PtrValue) != 0: + ptrValueList = re.sub(ValueRe, '', TokenList[1]) + ValueList = GetSplitValueList(ptrValueList) + ValueList[0] = PtrValue[0] + else: + ValueList = GetSplitValueList(TokenList[1]) + + + # check if there's enough datum information given + if len(ValueList) != 3: + EdkLogger.error('Parser', FORMAT_INVALID, "Invalid PCD Datum information given", + ExtraData=self._CurrentLine + \ + " (<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>)", + File=self.MetaFile, Line=self._LineIndex+1) + # check default value + if ValueList[0] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "Missing DefaultValue in PCD Datum information", + ExtraData=self._CurrentLine + \ + " (<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>)", + File=self.MetaFile, Line=self._LineIndex+1) + # check datum type + if ValueList[1] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "Missing DatumType in PCD Datum information", + ExtraData=self._CurrentLine + \ + " (<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>)", + File=self.MetaFile, Line=self._LineIndex+1) + # check token of the PCD + if ValueList[2] == '': + EdkLogger.error('Parser', FORMAT_INVALID, "Missing Token in PCD Datum information", + ExtraData=self._CurrentLine + \ + " (<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>)", + File=self.MetaFile, Line=self._LineIndex+1) + # check format of default value against the datum type + IsValid, Cause = CheckPcdDatum(ValueList[1], ValueList[0]) + if not IsValid: + EdkLogger.error('Parser', FORMAT_INVALID, Cause, ExtraData=self._CurrentLine, + File=self.MetaFile, Line=self._LineIndex+1) + + if ValueList[0] in ['True', 'true', 'TRUE']: + ValueList[0] = '1' + elif ValueList[0] in ['False', 'false', 'FALSE']: + ValueList[0] = '0' + + self._ValueList[2] = ValueList[0].strip() + '|' + ValueList[1].strip() + '|' + ValueList[2].strip() + + _SectionParser = { + MODEL_META_DATA_HEADER : MetaFileParser._DefineParser, + MODEL_EFI_INCLUDE : MetaFileParser._PathParser, + MODEL_EFI_LIBRARY_CLASS : MetaFileParser._PathParser, + MODEL_EFI_GUID : _GuidParser, + MODEL_EFI_PPI : _GuidParser, + MODEL_EFI_PROTOCOL : _GuidParser, + MODEL_PCD_FIXED_AT_BUILD : _PcdParser, + MODEL_PCD_PATCHABLE_IN_MODULE : _PcdParser, + MODEL_PCD_FEATURE_FLAG : _PcdParser, + MODEL_PCD_DYNAMIC : _PcdParser, + MODEL_PCD_DYNAMIC_EX : _PcdParser, + MODEL_UNKNOWN : MetaFileParser._Skip, + MODEL_META_DATA_USER_EXTENSION : MetaFileParser._Skip, + } + + +## FdfObject +# +# This class defined basic Fdf object which is used by inheriting +# +# @param object: Inherited from object class +# +class FdfObject(object): + def __init__(self): + object.__init__() + +## Fdf +# +# This class defined the structure used in Fdf object +# +# @param FdfObject: Inherited from FdfObject class +# @param Filename: Input value for Ffilename of Fdf file, default is None +# @param WorkspaceDir: Input value for current workspace directory, default is None +# +class Fdf(FdfObject): + def __init__(self, Filename = None, IsToDatabase = False, WorkspaceDir = None, Database = None): + self.WorkspaceDir = WorkspaceDir + self.IsToDatabase = IsToDatabase + + self.Cur = Database.Cur + self.TblFile = Database.TblFile + self.TblFdf = Database.TblFdf + self.FileID = -1 + self.FileList = {} + + # + # Load Fdf file if filename is not None + # + if Filename != None: + self.LoadFdfFile(Filename) + + # + # Insert a FDF file record into database + # + def InsertFile(self, Filename): + FileID = -1 + Filename = NormPath(Filename) + if Filename not in self.FileList: + FileID = self.TblFile.InsertFile(Filename, MODEL_FILE_FDF) + self.FileList[Filename] = FileID + + return self.FileList[Filename] + + + ## Load Fdf file + # + # Load the file if it exists + # + # @param Filename: Input value for filename of Fdf file + # + def LoadFdfFile(self, Filename): + FileList = [] + # + # Parse Fdf file + # + Filename = NormPath(Filename) + Fdf = FdfParser(Filename) + Fdf.ParseFile() + + # + # Insert inf file and pcd information + # + if self.IsToDatabase: + (Model, Value1, Value2, Value3, Scope1, Scope2, BelongsToItem, BelongsToFile, StartLine, StartColumn, EndLine, EndColumn, Enabled) = \ + (0, '', '', '', 'COMMON', 'COMMON', -1, -1, -1, -1, -1, -1, 0) + for Index in range(0, len(Fdf.Profile.PcdDict)): + pass + for Key in Fdf.Profile.PcdDict.keys(): + Model = MODEL_PCD + Value1 = Key[1] + Value2 = Key[0] + FileName = Fdf.Profile.PcdFileLineDict[Key][0] + StartLine = Fdf.Profile.PcdFileLineDict[Key][1] + BelongsToFile = self.InsertFile(FileName) + self.TblFdf.Insert(Model, Value1, Value2, Value3, Scope1, Scope2, BelongsToItem, BelongsToFile, StartLine, StartColumn, EndLine, EndColumn, Enabled) + for Index in range(0, len(Fdf.Profile.InfList)): + Model = MODEL_META_DATA_COMPONENT + Value1 = Fdf.Profile.InfList[Index] + Value2 = '' + FileName = Fdf.Profile.InfFileLineList[Index][0] + StartLine = Fdf.Profile.InfFileLineList[Index][1] + BelongsToFile = self.InsertFile(FileName) + self.TblFdf.Insert(Model, Value1, Value2, Value3, Scope1, Scope2, BelongsToItem, BelongsToFile, StartLine, StartColumn, EndLine, EndColumn, Enabled) + +## +# +# This acts like the main() function for the script, unless it is 'import'ed into another +# script. +# +if __name__ == '__main__': + pass + diff --git a/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileTable.py b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileTable.py new file mode 100644 index 0000000000..89bc7f262f --- /dev/null +++ b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/MetaFileTable.py @@ -0,0 +1,332 @@ +## @file
+# This file is used to create/update/query/erase a meta file table
+#
+# Copyright (c) 2008, 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.
+#
+
+##
+# Import Modules
+#
+import uuid
+
+import Common.EdkLogger as EdkLogger
+import EccGlobalData
+
+from MetaDataTable import Table
+from MetaDataTable import ConvertToSqlString
+from CommonDataClass.DataClass import MODEL_FILE_DSC, MODEL_FILE_DEC, MODEL_FILE_INF, \
+ MODEL_FILE_OTHERS
+
+class MetaFileTable(Table):
+ ## Constructor
+ def __init__(self, Cursor, MetaFile, FileType, TableName, Temporary = False):
+ self.MetaFile = MetaFile
+ self.TblFile = EccGlobalData.gDb.TblFile
+ if (FileType == MODEL_FILE_INF):
+ TableName = "Inf"
+ if (FileType == MODEL_FILE_DSC):
+ if Temporary:
+ TableName = "_%s_%s" % ("Dsc", uuid.uuid4().hex)
+ else:
+ TableName = "Dsc"
+ if (FileType == MODEL_FILE_DEC):
+ TableName = "Dec"
+
+ Table.__init__(self, Cursor, TableName, 0, Temporary)
+ self.Create(False)
+
+
+## Python class representation of table storing module data
+class ModuleTable(MetaFileTable):
+ _COLUMN_ = '''
+ ID REAL PRIMARY KEY,
+ Model INTEGER NOT NULL,
+ Value1 TEXT NOT NULL,
+ Value2 TEXT,
+ Value3 TEXT,
+ Scope1 TEXT,
+ Scope2 TEXT,
+ BelongsToItem REAL NOT NULL,
+ BelongsToFile SINGLE NOT NULL,
+ StartLine INTEGER NOT NULL,
+ StartColumn INTEGER NOT NULL,
+ EndLine INTEGER NOT NULL,
+ EndColumn INTEGER NOT NULL,
+ Enabled INTEGER DEFAULT 0
+ '''
+ # used as table end flag, in case the changes to database is not committed to db file
+ _DUMMY_ = "-1, -1, '====', '====', '====', '====', '====', -1, -1, -1, -1, -1, -1, -1"
+
+ ## Constructor
+ def __init__(self, Cursor):
+ MetaFileTable.__init__(self, Cursor, '', MODEL_FILE_INF, "Inf", False)
+
+ ## Insert a record into table Inf
+ #
+ # @param Model: Model of a Inf item
+ # @param Value1: Value1 of a Inf item
+ # @param Value2: Value2 of a Inf item
+ # @param Value3: Value3 of a Inf item
+ # @param Scope1: Arch of a Inf item
+ # @param Scope2 Platform os a Inf item
+ # @param BelongsToItem: The item belongs to which another item
+ # @param StartLine: StartLine of a Inf item
+ # @param StartColumn: StartColumn of a Inf item
+ # @param EndLine: EndLine of a Inf item
+ # @param EndColumn: EndColumn of a Inf item
+ # @param Enabled: If this item enabled
+ #
+ def Insert(self, Model, Value1, Value2, Value3, Scope1='COMMON', Scope2='COMMON',
+ BelongsToItem=-1, BelongsToFile = -1, StartLine=-1, StartColumn=-1, EndLine=-1, EndColumn=-1, Enabled=0):
+ (Value1, Value2, Value3, Scope1, Scope2) = ConvertToSqlString((Value1, Value2, Value3, Scope1, Scope2))
+ return Table.Insert(
+ self,
+ Model,
+ Value1,
+ Value2,
+ Value3,
+ Scope1,
+ Scope2,
+ BelongsToItem,
+ BelongsToFile,
+ StartLine,
+ StartColumn,
+ EndLine,
+ EndColumn,
+ Enabled
+ )
+
+ ## Query table
+ #
+ # @param Model: The Model of Record
+ # @param Arch: The Arch attribute of Record
+ # @param Platform The Platform attribute of Record
+ #
+ # @retval: A recordSet of all found records
+ #
+ def Query(self, Model, Arch=None, Platform=None):
+ ConditionString = "Model=%s AND Enabled>=0" % Model
+ ValueString = "Value1,Value2,Value3,Scope1,Scope2,ID,StartLine"
+
+ if Arch != None and Arch != 'COMMON':
+ ConditionString += " AND (Scope1='%s' OR Scope1='COMMON')" % Arch
+ if Platform != None and Platform != 'COMMON':
+ ConditionString += " AND (Scope2='%s' OR Scope2='COMMON' OR Scope2='DEFAULT')" % Platform
+
+ SqlCommand = "SELECT %s FROM %s WHERE %s" % (ValueString, self.Table, ConditionString)
+ return self.Exec(SqlCommand)
+
+## Python class representation of table storing package data
+class PackageTable(MetaFileTable):
+ _COLUMN_ = '''
+ ID REAL PRIMARY KEY,
+ Model INTEGER NOT NULL,
+ Value1 TEXT NOT NULL,
+ Value2 TEXT,
+ Value3 TEXT,
+ Scope1 TEXT,
+ Scope2 TEXT,
+ BelongsToItem REAL NOT NULL,
+ BelongsToFile SINGLE NOT NULL,
+ StartLine INTEGER NOT NULL,
+ StartColumn INTEGER NOT NULL,
+ EndLine INTEGER NOT NULL,
+ EndColumn INTEGER NOT NULL,
+ Enabled INTEGER DEFAULT 0
+ '''
+ # used as table end flag, in case the changes to database is not committed to db file
+ _DUMMY_ = "-1, -1, '====', '====', '====', '====', '====', -1, -1, -1, -1, -1, -1, -1"
+
+ ## Constructor
+ def __init__(self, Cursor):
+ MetaFileTable.__init__(self, Cursor, '', MODEL_FILE_DEC, "Dec", False)
+
+ ## Insert table
+ #
+ # Insert a record into table Dec
+ #
+ # @param Model: Model of a Dec item
+ # @param Value1: Value1 of a Dec item
+ # @param Value2: Value2 of a Dec item
+ # @param Value3: Value3 of a Dec item
+ # @param Scope1: Arch of a Dec item
+ # @param Scope2: Module type of a Dec item
+ # @param BelongsToItem: The item belongs to which another item
+ # @param StartLine: StartLine of a Dec item
+ # @param StartColumn: StartColumn of a Dec item
+ # @param EndLine: EndLine of a Dec item
+ # @param EndColumn: EndColumn of a Dec item
+ # @param Enabled: If this item enabled
+ #
+ def Insert(self, Model, Value1, Value2, Value3, Scope1='COMMON', Scope2='COMMON',
+ BelongsToItem=-1, BelongsToFile = -1, StartLine=-1, StartColumn=-1, EndLine=-1, EndColumn=-1, Enabled=0):
+ (Value1, Value2, Value3, Scope1, Scope2) = ConvertToSqlString((Value1, Value2, Value3, Scope1, Scope2))
+ return Table.Insert(
+ self,
+ Model,
+ Value1,
+ Value2,
+ Value3,
+ Scope1,
+ Scope2,
+ BelongsToItem,
+ BelongsToFile,
+ StartLine,
+ StartColumn,
+ EndLine,
+ EndColumn,
+ Enabled
+ )
+
+ ## Query table
+ #
+ # @param Model: The Model of Record
+ # @param Arch: The Arch attribute of Record
+ #
+ # @retval: A recordSet of all found records
+ #
+ def Query(self, Model, Arch=None):
+ ConditionString = "Model=%s AND Enabled>=0" % Model
+ ValueString = "Value1,Value2,Value3,Scope1,ID,StartLine"
+
+ if Arch != None and Arch != 'COMMON':
+ ConditionString += " AND (Scope1='%s' OR Scope1='COMMON')" % Arch
+
+ SqlCommand = "SELECT %s FROM %s WHERE %s" % (ValueString, self.Table, ConditionString)
+ return self.Exec(SqlCommand)
+
+## Python class representation of table storing platform data
+class PlatformTable(MetaFileTable):
+ _COLUMN_ = '''
+ ID REAL PRIMARY KEY,
+ Model INTEGER NOT NULL,
+ Value1 TEXT NOT NULL,
+ Value2 TEXT,
+ Value3 TEXT,
+ Scope1 TEXT,
+ Scope2 TEXT,
+ BelongsToItem REAL NOT NULL,
+ BelongsToFile SINGLE NOT NULL,
+ FromItem REAL NOT NULL,
+ StartLine INTEGER NOT NULL,
+ StartColumn INTEGER NOT NULL,
+ EndLine INTEGER NOT NULL,
+ EndColumn INTEGER NOT NULL,
+ Enabled INTEGER DEFAULT 0
+ '''
+ # used as table end flag, in case the changes to database is not committed to db file
+ _DUMMY_ = "-1, -1, '====', '====', '====', '====', '====', -1, -1, -1, -1, -1, -1, -1, -1"
+
+ ## Constructor
+ def __init__(self, Cursor, MetaFile = '', FileType = MODEL_FILE_DSC, Temporary = False):
+ MetaFileTable.__init__(self, Cursor, MetaFile, FileType, "Dsc", Temporary)
+
+ ## Insert table
+ #
+ # Insert a record into table Dsc
+ #
+ # @param Model: Model of a Dsc item
+ # @param Value1: Value1 of a Dsc item
+ # @param Value2: Value2 of a Dsc item
+ # @param Value3: Value3 of a Dsc item
+ # @param Scope1: Arch of a Dsc item
+ # @param Scope2: Module type of a Dsc item
+ # @param BelongsToItem: The item belongs to which another item
+ # @param FromItem: The item belongs to which dsc file
+ # @param StartLine: StartLine of a Dsc item
+ # @param StartColumn: StartColumn of a Dsc item
+ # @param EndLine: EndLine of a Dsc item
+ # @param EndColumn: EndColumn of a Dsc item
+ # @param Enabled: If this item enabled
+ #
+ def Insert(self, Model, Value1, Value2, Value3, Scope1='COMMON', Scope2='COMMON', BelongsToItem=-1, BelongsToFile = -1,
+ FromItem=-1, StartLine=-1, StartColumn=-1, EndLine=-1, EndColumn=-1, Enabled=1):
+ (Value1, Value2, Value3, Scope1, Scope2) = ConvertToSqlString((Value1, Value2, Value3, Scope1, Scope2))
+ return Table.Insert(
+ self,
+ Model,
+ Value1,
+ Value2,
+ Value3,
+ Scope1,
+ Scope2,
+ BelongsToItem,
+ BelongsToFile,
+ FromItem,
+ StartLine,
+ StartColumn,
+ EndLine,
+ EndColumn,
+ Enabled
+ )
+
+ ## Query table
+ #
+ # @param Model: The Model of Record
+ # @param Scope1: Arch of a Dsc item
+ # @param Scope2: Module type of a Dsc item
+ # @param BelongsToItem: The item belongs to which another item
+ # @param FromItem: The item belongs to which dsc file
+ #
+ # @retval: A recordSet of all found records
+ #
+ def Query(self, Model, Scope1=None, Scope2=None, BelongsToItem=None, FromItem=None):
+ ConditionString = "Model=%s AND Enabled>0" % Model
+ ValueString = "Value1,Value2,Value3,Scope1,Scope2,ID,StartLine"
+
+ if Scope1 != None and Scope1 != 'COMMON':
+ ConditionString += " AND (Scope1='%s' OR Scope1='COMMON')" % Scope1
+ if Scope2 != None and Scope2 != 'COMMON':
+ ConditionString += " AND (Scope2='%s' OR Scope2='COMMON' OR Scope2='DEFAULT')" % Scope2
+
+ if BelongsToItem != None:
+ ConditionString += " AND BelongsToItem=%s" % BelongsToItem
+ else:
+ ConditionString += " AND BelongsToItem<0"
+
+ if FromItem != None:
+ ConditionString += " AND FromItem=%s" % FromItem
+
+ SqlCommand = "SELECT %s FROM %s WHERE %s" % (ValueString, self.Table, ConditionString)
+ return self.Exec(SqlCommand)
+
+## Factory class to produce different storage for different type of meta-file
+class MetaFileStorage(object):
+ _FILE_TABLE_ = {
+ MODEL_FILE_INF : ModuleTable,
+ MODEL_FILE_DEC : PackageTable,
+ MODEL_FILE_DSC : PlatformTable,
+ MODEL_FILE_OTHERS : MetaFileTable,
+ }
+
+ _FILE_TYPE_ = {
+ ".inf" : MODEL_FILE_INF,
+ ".dec" : MODEL_FILE_DEC,
+ ".dsc" : MODEL_FILE_DSC,
+ }
+
+ ## Constructor
+ def __new__(Class, Cursor, MetaFile, FileType=None, Temporary=False):
+ # no type given, try to find one
+ if not FileType:
+ if MetaFile.Type in self._FILE_TYPE_:
+ FileType = Class._FILE_TYPE_[MetaFile.Type]
+ else:
+ FileType = MODEL_FILE_OTHERS
+
+ # don't pass the type around if it's well known
+ if FileType == MODEL_FILE_OTHERS:
+ Args = (Cursor, MetaFile, FileType, Temporary)
+ else:
+ Args = (Cursor, MetaFile, FileType, Temporary)
+
+ # create the storage object and return it to caller
+ return Class._FILE_TABLE_[FileType](*Args)
+
diff --git a/BaseTools/Source/Python/Ecc/MetaFileWorkspace/__init__.py b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/__init__.py new file mode 100644 index 0000000000..05cd34bad5 --- /dev/null +++ b/BaseTools/Source/Python/Ecc/MetaFileWorkspace/__init__.py @@ -0,0 +1,15 @@ +## @file
+# Python 'Workspace' package initialization file.
+#
+# This file is required to make Python interpreter treat the directory
+# as containing package.
+#
+# Copyright (c) 2008 - 2010, 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.
+#
diff --git a/BaseTools/Source/Python/Ecc/c.py b/BaseTools/Source/Python/Ecc/c.py index 532f4a0918..ea7d99fecd 100644 --- a/BaseTools/Source/Python/Ecc/c.py +++ b/BaseTools/Source/Python/Ecc/c.py @@ -2305,28 +2305,90 @@ def CheckFileHeaderDoxygenComments(FullFileName): """ % (FileTable, DataClass.MODEL_IDENTIFIER_COMMENT)
ResultSet = Db.TblFile.Exec(SqlStatement)
if len(ResultSet) == 0:
- PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'No Comment appear at the very beginning of file.', 'File', FileID)
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'No File License header appear at the very beginning of file.', 'File', FileID)
return ErrorMsgList
- IsFoundError1 = True
- IsFoundError2 = True
- IsFoundError3 = True
+ NoHeaderCommentStartFlag = True
+ NoHeaderCommentEndFlag = True
+ NoHeaderCommentPeriodFlag = True
+ NoCopyrightFlag = True
+ NoLicenseFlag = True
+ NoRevReferFlag = True
+ NextLineIndex = 0
for Result in ResultSet:
+ FileStartFlag = False
+ CommentStrList = []
CommentStr = Result[0].strip()
+ CommentStrListTemp = CommentStr.split('\n')
+ if (len(CommentStrListTemp) <= 1):
+ # For Mac
+ CommentStrListTemp = CommentStr.split('\r')
+ # Skip the content before the file header
+ for CommentLine in CommentStrListTemp:
+ if CommentLine.strip().startswith('/** @file'):
+ FileStartFlag = True
+ if FileStartFlag == True:
+ CommentStrList.append(CommentLine)
+
ID = Result[1]
- if CommentStr.startswith('/** @file'):
- IsFoundError1 = False
- if CommentStr.endswith('**/'):
- IsFoundError2 = False
- if CommentStr.find('.') != -1:
- IsFoundError3 = False
-
- if IsFoundError1:
+ Index = 0
+ if CommentStrList and CommentStrList[0].strip().startswith('/** @file'):
+ NoHeaderCommentStartFlag = False
+ else:
+ continue
+ if CommentStrList and CommentStrList[-1].strip().endswith('**/'):
+ NoHeaderCommentEndFlag = False
+ else:
+ continue
+
+ for CommentLine in CommentStrList:
+ Index = Index + 1
+ NextLineIndex = Index
+ if CommentLine.startswith('/** @file'):
+ continue
+ if CommentLine.startswith('**/'):
+ break
+ # Check whether C File header Comment content start with two spaces.
+ if EccGlobalData.gConfig.HeaderCheckCFileCommentStartSpacesNum == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
+ if CommentLine.startswith('/** @file') == False and CommentLine.startswith('**/') == False and CommentLine.strip() and CommentLine.startswith(' ') == False:
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment content should start with two spaces at each line', FileTable, ID)
+
+ CommentLine = CommentLine.strip()
+ if CommentLine.startswith('Copyright'):
+ NoCopyrightFlag = False
+ if CommentLine.find('All rights reserved') == -1:
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, '""All rights reserved"" announcement should be following the ""Copyright"" at the same line', FileTable, ID)
+ if CommentLine.endswith('<BR>') == -1:
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'The ""<BR>"" at the end of the Copyright line is required', FileTable, ID)
+ if NextLineIndex < len(CommentStrList) and CommentStrList[NextLineIndex].strip().startswith('Copyright') == False and CommentStrList[NextLineIndex].strip():
+ NoLicenseFlag = False
+ if CommentLine.startswith('@par Revision Reference:'):
+ NoRevReferFlag = False
+ RefListFlag = False
+ for RefLine in CommentStrList[NextLineIndex:]:
+ if RefLine.strip() and (NextLineIndex + 1) < len(CommentStrList) and CommentStrList[NextLineIndex+1].strip() and CommentStrList[NextLineIndex+1].strip().startswith('**/') == False:
+ RefListFlag = True
+ if RefLine.strip() == False or RefLine.strip().startswith('**/'):
+ RefListFlag = False
+ break
+ # Check whether C File header Comment's each reference at list should begin with a bullet character.
+ if EccGlobalData.gConfig.HeaderCheckCFileCommentReferenceFormat == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
+ if RefListFlag == True:
+ if RefLine.strip() and RefLine.strip().startswith('**/') == False and RefLine.startswith(' -') == False:
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'Each reference on a separate line should begin with a bullet character ""-"" ', FileTable, ID)
+
+ if NoHeaderCommentStartFlag:
PrintErrorMsg(ERROR_DOXYGEN_CHECK_FILE_HEADER, 'File header comment should begin with ""/** @file""', FileTable, ID)
- if IsFoundError2:
+ return
+ if NoHeaderCommentEndFlag:
PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment should end with ""**/""', FileTable, ID)
- if IsFoundError3:
- PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMENT_DESCRIPTION, 'Comment description should end with period "".""', FileTable, ID)
+ return
+ if NoCopyrightFlag:
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment missing the ""Copyright""', FileTable, ID)
+ #Check whether C File header Comment have the License immediately after the ""Copyright"" line.
+ if EccGlobalData.gConfig.HeaderCheckCFileCommentLicenseFormat == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
+ if NoLicenseFlag:
+ PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment should have the License immediately after the ""Copyright"" line', FileTable, ID)
def CheckFuncHeaderDoxygenComments(FullFileName):
ErrorMsgList = []
diff --git a/BaseTools/Source/Python/Ecc/config.ini b/BaseTools/Source/Python/Ecc/config.ini index 357c52ced0..c55276fce7 100644 --- a/BaseTools/Source/Python/Ecc/config.ini +++ b/BaseTools/Source/Python/Ecc/config.ini @@ -97,6 +97,14 @@ HeaderCheckAll = 0 HeaderCheckFile = 1
# Check whether Function header exists
HeaderCheckFunction = 1
+# Check whether Meta data File header Comment End with '##'
+HeaderCheckFileCommentEnd = 0
+# Check whether C File header Comment content start with two spaces
+HeaderCheckCFileCommentStartSpacesNum = 0
+# Check whether C File header Comment's each reference at list should begin with a bullet character '-'
+HeaderCheckCFileCommentReferenceFormat = 0
+# Check whether C File header Comment have the License immediately after the ""Copyright"" line
+HeaderCheckCFileCommentLicenseFormat = 0
#
# C Function Layout Checking
|