| ## @file | |
| # This file is used to parse DEC file. It will consumed by DecParser | |
| # | |
| # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> | |
| # | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ''' | |
| DecParser | |
| ''' | |
| ## Import modules | |
| # | |
| import Logger.Log as Logger | |
| from Logger.ToolError import FILE_PARSE_FAILURE | |
| from Logger.ToolError import FILE_OPEN_FAILURE | |
| from Logger import StringTable as ST | |
| from Logger.ToolError import FORMAT_INVALID | |
| import Library.DataType as DT | |
| from Library.ParserValidate import IsValidToken | |
| from Library.ParserValidate import IsValidPath | |
| from Library.ParserValidate import IsValidCFormatGuid | |
| from Library.ParserValidate import IsValidIdString | |
| from Library.ParserValidate import IsValidUserId | |
| from Library.ParserValidate import IsValidArch | |
| from Library.ParserValidate import IsValidWord | |
| from Library.ParserValidate import IsValidDecVersionVal | |
| from Parser.DecParserMisc import TOOL_NAME | |
| from Parser.DecParserMisc import CleanString | |
| from Parser.DecParserMisc import IsValidPcdDatum | |
| from Parser.DecParserMisc import ParserHelper | |
| from Parser.DecParserMisc import StripRoot | |
| from Parser.DecParserMisc import VERSION_PATTERN | |
| from Parser.DecParserMisc import CVAR_PATTERN | |
| from Parser.DecParserMisc import PCD_TOKEN_PATTERN | |
| from Parser.DecParserMisc import MACRO_PATTERN | |
| from Parser.DecParserMisc import FileContent | |
| from Object.Parser.DecObject import _DecComments | |
| from Object.Parser.DecObject import DecDefineObject | |
| from Object.Parser.DecObject import DecDefineItemObject | |
| from Object.Parser.DecObject import DecIncludeObject | |
| from Object.Parser.DecObject import DecIncludeItemObject | |
| from Object.Parser.DecObject import DecLibraryclassObject | |
| from Object.Parser.DecObject import DecLibraryclassItemObject | |
| from Object.Parser.DecObject import DecGuidObject | |
| from Object.Parser.DecObject import DecPpiObject | |
| from Object.Parser.DecObject import DecProtocolObject | |
| from Object.Parser.DecObject import DecGuidItemObject | |
| from Object.Parser.DecObject import DecUserExtensionObject | |
| from Object.Parser.DecObject import DecUserExtensionItemObject | |
| from Object.Parser.DecObject import DecPcdObject | |
| from Object.Parser.DecObject import DecPcdItemObject | |
| from Library.Misc import GuidStructureStringToGuidString | |
| from Library.Misc import CheckGuidRegFormat | |
| from Library.StringUtils import ReplaceMacro | |
| from Library.StringUtils import GetSplitValueList | |
| from Library.StringUtils import gMACRO_PATTERN | |
| from Library.StringUtils import ConvertSpecialChar | |
| from Library.CommentParsing import ParsePcdErrorCode | |
| ## | |
| # _DecBase class for parsing | |
| # | |
| class _DecBase: | |
| def __init__(self, RawData): | |
| self._RawData = RawData | |
| self._ItemDict = {} | |
| self._LocalMacro = {} | |
| # | |
| # Data parsed by 'self' are saved to this object | |
| # | |
| self.ItemObject = None | |
| def GetDataObject(self): | |
| return self.ItemObject | |
| def GetLocalMacro(self): | |
| return self._LocalMacro | |
| ## BlockStart | |
| # | |
| # Called if a new section starts | |
| # | |
| def BlockStart(self): | |
| self._LocalMacro = {} | |
| ## _CheckReDefine | |
| # | |
| # @param Key: to be checked if multi-defined | |
| # @param Scope: Format: [[SectionName, Arch], ...]. | |
| # If scope is none, use global scope | |
| # | |
| def _CheckReDefine(self, Key, Scope = None): | |
| if not Scope: | |
| Scope = self._RawData.CurrentScope | |
| return | |
| SecArch = [] | |
| # | |
| # Copy scope to SecArch, avoid Scope be changed outside | |
| # | |
| SecArch[0:1] = Scope[:] | |
| if Key not in self._ItemDict: | |
| self._ItemDict[Key] = [[SecArch, self._RawData.LineIndex]] | |
| return | |
| for Value in self._ItemDict[Key]: | |
| for SubValue in Scope: | |
| # | |
| # If current is common section | |
| # | |
| if SubValue[-1] == 'COMMON': | |
| for Other in Value[0]: | |
| # Key in common cannot be redefined in other arches | |
| # [:-1] means stripping arch info | |
| if Other[:-1] == SubValue[:-1]: | |
| self._LoggerError(ST.ERR_DECPARSE_REDEFINE % (Key, Value[1])) | |
| return | |
| continue | |
| CommonScope = [] | |
| CommonScope[0:1] = SubValue | |
| CommonScope[-1] = 'COMMON' | |
| # | |
| # Cannot be redefined if this key already defined in COMMON Or defined in same arch | |
| # | |
| if SubValue in Value[0] or CommonScope in Value[0]: | |
| self._LoggerError(ST.ERR_DECPARSE_REDEFINE % (Key, Value[1])) | |
| return | |
| self._ItemDict[Key].append([SecArch, self._RawData.LineIndex]) | |
| ## CheckRequiredFields | |
| # Some sections need to check if some fields exist, define section for example | |
| # Derived class can re-implement, top parser will call this function after all parsing done | |
| # | |
| def CheckRequiredFields(self): | |
| if self._RawData: | |
| pass | |
| return True | |
| ## IsItemRequired | |
| # In DEC spec, sections must have at least one statement except user | |
| # extension. | |
| # For example: "[guids" [<attribs>] "]" <EOL> <statements>+ | |
| # sub class can override this method to indicate if statement is a must. | |
| # | |
| def _IsStatementRequired(self): | |
| if self._RawData: | |
| pass | |
| return False | |
| def _LoggerError(self, ErrorString): | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._RawData.Filename, | |
| Line = self._RawData.LineIndex, | |
| ExtraData=ErrorString + ST.ERR_DECPARSE_LINE % self._RawData.CurrentLine) | |
| def _ReplaceMacro(self, String): | |
| if gMACRO_PATTERN.findall(String): | |
| String = ReplaceMacro(String, self._LocalMacro, False, | |
| FileName = self._RawData.Filename, | |
| Line = ['', self._RawData.LineIndex]) | |
| String = ReplaceMacro(String, self._RawData.Macros, False, | |
| FileName = self._RawData.Filename, | |
| Line = ['', self._RawData.LineIndex]) | |
| MacroUsed = gMACRO_PATTERN.findall(String) | |
| if MacroUsed: | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, | |
| File=self._RawData.Filename, | |
| Line = self._RawData.LineIndex, | |
| ExtraData = ST.ERR_DECPARSE_MACRO_RESOLVE % (str(MacroUsed), String)) | |
| return String | |
| def _MacroParser(self, String): | |
| TokenList = GetSplitValueList(String, ' ', 1) | |
| if len(TokenList) < 2 or TokenList[1] == '': | |
| self._LoggerError(ST.ERR_DECPARSE_MACRO_PAIR) | |
| TokenList = GetSplitValueList(TokenList[1], DT.TAB_EQUAL_SPLIT, 1) | |
| if TokenList[0] == '': | |
| self._LoggerError(ST.ERR_DECPARSE_MACRO_NAME) | |
| elif not IsValidToken(MACRO_PATTERN, TokenList[0]): | |
| self._LoggerError(ST.ERR_DECPARSE_MACRO_NAME_UPPER % TokenList[0]) | |
| if len(TokenList) == 1: | |
| self._LocalMacro[TokenList[0]] = '' | |
| else: | |
| self._LocalMacro[TokenList[0]] = self._ReplaceMacro(TokenList[1]) | |
| ## _ParseItem | |
| # | |
| # Parse specified item, this function must be derived by subclass | |
| # | |
| def _ParseItem(self): | |
| if self._RawData: | |
| pass | |
| # | |
| # Should never be called | |
| # | |
| return None | |
| ## _TailCommentStrategy | |
| # | |
| # This function can be derived to parse tail comment | |
| # default is it will not consume any lines | |
| # | |
| # @param Comment: Comment of current line | |
| # | |
| def _TailCommentStrategy(self, Comment): | |
| if Comment: | |
| pass | |
| if self._RawData: | |
| pass | |
| return False | |
| ## _StopCurrentParsing | |
| # | |
| # Called in Parse if current parsing should be stopped when encounter some | |
| # keyword | |
| # Default is section start and end | |
| # | |
| # @param Line: Current line | |
| # | |
| def _StopCurrentParsing(self, Line): | |
| if self._RawData: | |
| pass | |
| return Line[0] == DT.TAB_SECTION_START and Line[-1] == DT.TAB_SECTION_END | |
| ## _TryBackSlash | |
| # | |
| # Split comment and DEC content, concatenate lines if end of char is '\' | |
| # | |
| # @param ProcessedLine: ProcessedLine line | |
| # @param ProcessedComments: ProcessedComments line | |
| # | |
| def _TryBackSlash(self, ProcessedLine, ProcessedComments): | |
| CatLine = '' | |
| Comment = '' | |
| Line = ProcessedLine | |
| CommentList = ProcessedComments | |
| while not self._RawData.IsEndOfFile(): | |
| if Line == '': | |
| self._LoggerError(ST.ERR_DECPARSE_BACKSLASH_EMPTY) | |
| break | |
| if Comment: | |
| CommentList.append((Comment, self._RawData.LineIndex)) | |
| if Line[-1] != DT.TAB_SLASH: | |
| CatLine += Line | |
| break | |
| elif len(Line) < 2 or Line[-2] != ' ': | |
| self._LoggerError(ST.ERR_DECPARSE_BACKSLASH) | |
| else: | |
| CatLine += Line[:-1] | |
| Line, Comment = CleanString(self._RawData.GetNextLine()) | |
| # | |
| # Reach end of content | |
| # | |
| if self._RawData.IsEndOfFile(): | |
| if not CatLine: | |
| if ProcessedLine[-1] == DT.TAB_SLASH: | |
| self._LoggerError(ST.ERR_DECPARSE_BACKSLASH_EMPTY) | |
| CatLine = ProcessedLine | |
| else: | |
| if not Line or Line[-1] == DT.TAB_SLASH: | |
| self._LoggerError(ST.ERR_DECPARSE_BACKSLASH_EMPTY) | |
| CatLine += Line | |
| # | |
| # All MACRO values defined by the DEFINE statements in any section | |
| # (except [Userextensions] sections for Intel) of the INF or DEC file | |
| # must be expanded before processing of the file. | |
| # | |
| __IsReplaceMacro = True | |
| Header = self._RawData.CurrentScope[0] if self._RawData.CurrentScope else None | |
| if Header and len(Header) > 2: | |
| if Header[0].upper() == 'USEREXTENSIONS' and not (Header[1] == 'TianoCore' and Header[2] == '"ExtraFiles"'): | |
| __IsReplaceMacro = False | |
| if __IsReplaceMacro: | |
| self._RawData.CurrentLine = self._ReplaceMacro(CatLine) | |
| else: | |
| self._RawData.CurrentLine = CatLine | |
| return CatLine, CommentList | |
| ## Parse | |
| # This is a template method in which other member functions which might | |
| # override by sub class are called. It is responsible for reading file | |
| # line by line, and call other member functions to parse. This function | |
| # should not be re-implement by sub class. | |
| # | |
| def Parse(self): | |
| HeadComments = [] | |
| TailComments = [] | |
| #====================================================================== | |
| # CurComments may pointer to HeadComments or TailComments | |
| #====================================================================== | |
| CurComments = HeadComments | |
| CurObj = None | |
| ItemNum = 0 | |
| FromBuf = False | |
| #====================================================================== | |
| # Used to report error information if empty section found | |
| #====================================================================== | |
| Index = self._RawData.LineIndex | |
| LineStr = self._RawData.CurrentLine | |
| while not self._RawData.IsEndOfFile() or self._RawData.NextLine: | |
| if self._RawData.NextLine: | |
| #============================================================== | |
| # Have processed line in buffer | |
| #============================================================== | |
| Line = self._RawData.NextLine | |
| HeadComments.extend(self._RawData.HeadComment) | |
| TailComments.extend(self._RawData.TailComment) | |
| self._RawData.ResetNext() | |
| Comment = '' | |
| FromBuf = True | |
| else: | |
| #============================================================== | |
| # No line in buffer, read next line | |
| #============================================================== | |
| Line, Comment = CleanString(self._RawData.GetNextLine()) | |
| FromBuf = False | |
| if Line: | |
| if not FromBuf and CurObj and TailComments: | |
| #========================================================== | |
| # Set tail comments to previous statement if not empty. | |
| #========================================================== | |
| CurObj.SetTailComment(CurObj.GetTailComment()+TailComments) | |
| if not FromBuf: | |
| del TailComments[:] | |
| CurComments = TailComments | |
| Comments = [] | |
| if Comment: | |
| Comments = [(Comment, self._RawData.LineIndex)] | |
| #============================================================== | |
| # Try if last char of line has backslash | |
| #============================================================== | |
| Line, Comments = self._TryBackSlash(Line, Comments) | |
| CurComments.extend(Comments) | |
| #============================================================== | |
| # Macro found | |
| #============================================================== | |
| if Line.startswith('DEFINE '): | |
| self._MacroParser(Line) | |
| del HeadComments[:] | |
| del TailComments[:] | |
| CurComments = HeadComments | |
| continue | |
| if self._StopCurrentParsing(Line): | |
| #========================================================== | |
| # This line does not belong to this parse, | |
| # Save it, can be used by next parse | |
| #========================================================== | |
| self._RawData.SetNext(Line, HeadComments, TailComments) | |
| break | |
| Obj = self._ParseItem() | |
| ItemNum += 1 | |
| if Obj: | |
| Obj.SetHeadComment(Obj.GetHeadComment()+HeadComments) | |
| Obj.SetTailComment(Obj.GetTailComment()+TailComments) | |
| del HeadComments[:] | |
| del TailComments[:] | |
| CurObj = Obj | |
| else: | |
| CurObj = None | |
| else: | |
| if id(CurComments) == id(TailComments): | |
| #========================================================== | |
| # Check if this comment belongs to tail comment | |
| #========================================================== | |
| if not self._TailCommentStrategy(Comment): | |
| CurComments = HeadComments | |
| if Comment: | |
| CurComments.append(((Comment, self._RawData.LineIndex))) | |
| else: | |
| del CurComments[:] | |
| if self._IsStatementRequired() and ItemNum == 0: | |
| Logger.Error( | |
| TOOL_NAME, FILE_PARSE_FAILURE, | |
| File=self._RawData.Filename, | |
| Line=Index, | |
| ExtraData=ST.ERR_DECPARSE_STATEMENT_EMPTY % LineStr | |
| ) | |
| ## _DecDefine | |
| # Parse define section | |
| # | |
| class _DecDefine(_DecBase): | |
| def __init__(self, RawData): | |
| _DecBase.__init__(self, RawData) | |
| self.ItemObject = DecDefineObject(RawData.Filename) | |
| self._LocalMacro = self._RawData.Macros | |
| self._DefSecNum = 0 | |
| # | |
| # Each field has a function to validate | |
| # | |
| self.DefineValidation = { | |
| DT.TAB_DEC_DEFINES_DEC_SPECIFICATION : self._SetDecSpecification, | |
| DT.TAB_DEC_DEFINES_PACKAGE_NAME : self._SetPackageName, | |
| DT.TAB_DEC_DEFINES_PACKAGE_GUID : self._SetPackageGuid, | |
| DT.TAB_DEC_DEFINES_PACKAGE_VERSION : self._SetPackageVersion, | |
| DT.TAB_DEC_DEFINES_PKG_UNI_FILE : self._SetPackageUni, | |
| } | |
| def BlockStart(self): | |
| self._DefSecNum += 1 | |
| if self._DefSecNum > 1: | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_MULTISEC) | |
| ## CheckRequiredFields | |
| # | |
| # Check required fields: DEC_SPECIFICATION, PACKAGE_NAME | |
| # PACKAGE_GUID, PACKAGE_VERSION | |
| # | |
| def CheckRequiredFields(self): | |
| Ret = False | |
| if self.ItemObject.GetPackageSpecification() == '': | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._RawData.Filename, | |
| ExtraData=ST.ERR_DECPARSE_DEFINE_REQUIRED % DT.TAB_DEC_DEFINES_DEC_SPECIFICATION) | |
| elif self.ItemObject.GetPackageName() == '': | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._RawData.Filename, | |
| ExtraData=ST.ERR_DECPARSE_DEFINE_REQUIRED % DT.TAB_DEC_DEFINES_PACKAGE_NAME) | |
| elif self.ItemObject.GetPackageGuid() == '': | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._RawData.Filename, | |
| ExtraData=ST.ERR_DECPARSE_DEFINE_REQUIRED % DT.TAB_DEC_DEFINES_PACKAGE_GUID) | |
| elif self.ItemObject.GetPackageVersion() == '': | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._RawData.Filename, | |
| ExtraData=ST.ERR_DECPARSE_DEFINE_REQUIRED % DT.TAB_DEC_DEFINES_PACKAGE_VERSION) | |
| else: | |
| Ret = True | |
| return Ret | |
| def _ParseItem(self): | |
| Line = self._RawData.CurrentLine | |
| TokenList = GetSplitValueList(Line, DT.TAB_EQUAL_SPLIT, 1) | |
| if TokenList[0] == DT.TAB_DEC_DEFINES_PKG_UNI_FILE: | |
| self.DefineValidation[TokenList[0]](TokenList[1]) | |
| elif len(TokenList) < 2: | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_FORMAT) | |
| elif TokenList[0] not in self.DefineValidation: | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_UNKNOWKEY % TokenList[0]) | |
| else: | |
| self.DefineValidation[TokenList[0]](TokenList[1]) | |
| DefineItem = DecDefineItemObject() | |
| DefineItem.Key = TokenList[0] | |
| DefineItem.Value = TokenList[1] | |
| self.ItemObject.AddItem(DefineItem, self._RawData.CurrentScope) | |
| return DefineItem | |
| def _SetDecSpecification(self, Token): | |
| if self.ItemObject.GetPackageSpecification(): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_DEFINED % DT.TAB_DEC_DEFINES_DEC_SPECIFICATION) | |
| if not IsValidToken('0[xX][0-9a-fA-F]{8}', Token): | |
| if not IsValidDecVersionVal(Token): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_SPEC) | |
| self.ItemObject.SetPackageSpecification(Token) | |
| def _SetPackageName(self, Token): | |
| if self.ItemObject.GetPackageName(): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_DEFINED % DT.TAB_DEC_DEFINES_PACKAGE_NAME) | |
| if not IsValidWord(Token): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_PKGNAME) | |
| self.ItemObject.SetPackageName(Token) | |
| def _SetPackageGuid(self, Token): | |
| if self.ItemObject.GetPackageGuid(): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_DEFINED % DT.TAB_DEC_DEFINES_PACKAGE_GUID) | |
| if not CheckGuidRegFormat(Token): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_PKGGUID) | |
| self.ItemObject.SetPackageGuid(Token) | |
| def _SetPackageVersion(self, Token): | |
| if self.ItemObject.GetPackageVersion(): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_DEFINED % DT.TAB_DEC_DEFINES_PACKAGE_VERSION) | |
| if not IsValidToken(VERSION_PATTERN, Token): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_PKGVERSION) | |
| else: | |
| if not DT.TAB_SPLIT in Token: | |
| Token = Token + '.0' | |
| self.ItemObject.SetPackageVersion(Token) | |
| def _SetPackageUni(self, Token): | |
| if self.ItemObject.GetPackageUniFile(): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_DEFINED % DT.TAB_DEC_DEFINES_PKG_UNI_FILE) | |
| self.ItemObject.SetPackageUniFile(Token) | |
| ## _DecInclude | |
| # | |
| # Parse include section | |
| # | |
| class _DecInclude(_DecBase): | |
| def __init__(self, RawData): | |
| _DecBase.__init__(self, RawData) | |
| self.ItemObject = DecIncludeObject(RawData.Filename) | |
| def _ParseItem(self): | |
| Line = self._RawData.CurrentLine | |
| if not IsValidPath(Line, self._RawData.PackagePath): | |
| self._LoggerError(ST.ERR_DECPARSE_INCLUDE % Line) | |
| Item = DecIncludeItemObject(StripRoot(self._RawData.PackagePath, Line), self._RawData.PackagePath) | |
| self.ItemObject.AddItem(Item, self._RawData.CurrentScope) | |
| return Item | |
| ## _DecLibraryclass | |
| # | |
| # Parse library class section | |
| # | |
| class _DecLibraryclass(_DecBase): | |
| def __init__(self, RawData): | |
| _DecBase.__init__(self, RawData) | |
| self.ItemObject = DecLibraryclassObject(RawData.Filename) | |
| def _ParseItem(self): | |
| Line = self._RawData.CurrentLine | |
| TokenList = GetSplitValueList(Line, DT.TAB_VALUE_SPLIT) | |
| if len(TokenList) != 2: | |
| self._LoggerError(ST.ERR_DECPARSE_LIBCLASS_SPLIT) | |
| if TokenList[0] == '' or TokenList[1] == '': | |
| self._LoggerError(ST.ERR_DECPARSE_LIBCLASS_EMPTY) | |
| if not IsValidToken('[A-Z][0-9A-Za-z]*', TokenList[0]): | |
| self._LoggerError(ST.ERR_DECPARSE_LIBCLASS_LIB) | |
| self._CheckReDefine(TokenList[0]) | |
| Value = TokenList[1] | |
| # | |
| # Must end with .h | |
| # | |
| if not Value.endswith('.h'): | |
| self._LoggerError(ST.ERR_DECPARSE_LIBCLASS_PATH_EXT) | |
| # | |
| # Path must be existed | |
| # | |
| if not IsValidPath(Value, self._RawData.PackagePath): | |
| self._LoggerError(ST.ERR_DECPARSE_INCLUDE % Value) | |
| Item = DecLibraryclassItemObject(TokenList[0], StripRoot(self._RawData.PackagePath, Value), | |
| self._RawData.PackagePath) | |
| self.ItemObject.AddItem(Item, self._RawData.CurrentScope) | |
| return Item | |
| ## _DecPcd | |
| # | |
| # Parse PCD section | |
| # | |
| class _DecPcd(_DecBase): | |
| def __init__(self, RawData): | |
| _DecBase.__init__(self, RawData) | |
| self.ItemObject = DecPcdObject(RawData.Filename) | |
| # | |
| # Used to check duplicate token | |
| # Key is token space and token number (integer), value is C name | |
| # | |
| self.TokenMap = {} | |
| def _ParseItem(self): | |
| Line = self._RawData.CurrentLine | |
| TokenList = Line.split(DT.TAB_VALUE_SPLIT) | |
| if len(TokenList) < 4: | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_SPLIT) | |
| # | |
| # Token space guid C name | |
| # | |
| PcdName = GetSplitValueList(TokenList[0], DT.TAB_SPLIT) | |
| if len(PcdName) != 2 or PcdName[0] == '' or PcdName[1] == '': | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_NAME) | |
| Guid = PcdName[0] | |
| if not IsValidToken(CVAR_PATTERN, Guid): | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_CVAR_GUID) | |
| # | |
| # PCD C name | |
| # | |
| CName = PcdName[1] | |
| if not IsValidToken(CVAR_PATTERN, CName): | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_CVAR_PCDCNAME) | |
| self._CheckReDefine(Guid + DT.TAB_SPLIT + CName) | |
| # | |
| # Default value, may be C array, string or number | |
| # | |
| Data = DT.TAB_VALUE_SPLIT.join(TokenList[1:-2]).strip() | |
| # | |
| # PCD data type | |
| # | |
| DataType = TokenList[-2].strip() | |
| Valid, Cause = IsValidPcdDatum(DataType, Data) | |
| if not Valid: | |
| self._LoggerError(Cause) | |
| PcdType = self._RawData.CurrentScope[0][0] | |
| if PcdType == DT.TAB_PCDS_FEATURE_FLAG_NULL.upper() and DataType != 'BOOLEAN': | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_FEATUREFLAG) | |
| # | |
| # Token value is the last element in list. | |
| # | |
| Token = TokenList[-1].strip() | |
| if not IsValidToken(PCD_TOKEN_PATTERN, Token): | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_TOKEN % Token) | |
| elif not Token.startswith('0x') and not Token.startswith('0X'): | |
| if int(Token) > 4294967295: | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_TOKEN_INT % Token) | |
| Token = '0x%x' % int(Token) | |
| IntToken = int(Token, 0) | |
| if (Guid, IntToken) in self.TokenMap: | |
| if self.TokenMap[Guid, IntToken] != CName: | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_TOKEN_UNIQUE%(Token)) | |
| else: | |
| self.TokenMap[Guid, IntToken] = CName | |
| Item = DecPcdItemObject(Guid, CName, Data, DataType, Token) | |
| self.ItemObject.AddItem(Item, self._RawData.CurrentScope) | |
| return Item | |
| ## _DecGuid | |
| # | |
| # Parse GUID, PPI, Protocol section | |
| # | |
| class _DecGuid(_DecBase): | |
| def __init__(self, RawData): | |
| _DecBase.__init__(self, RawData) | |
| self.GuidObj = DecGuidObject(RawData.Filename) | |
| self.PpiObj = DecPpiObject(RawData.Filename) | |
| self.ProtocolObj = DecProtocolObject(RawData.Filename) | |
| self.ObjectDict = \ | |
| { | |
| DT.TAB_GUIDS.upper() : self.GuidObj, | |
| DT.TAB_PPIS.upper() : self.PpiObj, | |
| DT.TAB_PROTOCOLS.upper() : self.ProtocolObj | |
| } | |
| def GetDataObject(self): | |
| if self._RawData.CurrentScope: | |
| return self.ObjectDict[self._RawData.CurrentScope[0][0]] | |
| return None | |
| def GetGuidObject(self): | |
| return self.GuidObj | |
| def GetPpiObject(self): | |
| return self.PpiObj | |
| def GetProtocolObject(self): | |
| return self.ProtocolObj | |
| def _ParseItem(self): | |
| Line = self._RawData.CurrentLine | |
| TokenList = GetSplitValueList(Line, DT.TAB_EQUAL_SPLIT, 1) | |
| if len(TokenList) < 2: | |
| self._LoggerError(ST.ERR_DECPARSE_CGUID) | |
| if TokenList[0] == '': | |
| self._LoggerError(ST.ERR_DECPARSE_CGUID_NAME) | |
| if TokenList[1] == '': | |
| self._LoggerError(ST.ERR_DECPARSE_CGUID_GUID) | |
| if not IsValidToken(CVAR_PATTERN, TokenList[0]): | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_CVAR_GUID) | |
| self._CheckReDefine(TokenList[0]) | |
| if TokenList[1][0] != '{': | |
| if not CheckGuidRegFormat(TokenList[1]): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_PKGGUID) | |
| GuidString = TokenList[1] | |
| else: | |
| # | |
| # Convert C format GUID to GUID string and Simple error check | |
| # | |
| GuidString = GuidStructureStringToGuidString(TokenList[1]) | |
| if TokenList[1][0] != '{' or TokenList[1][-1] != '}' or GuidString == '': | |
| self._LoggerError(ST.ERR_DECPARSE_CGUID_GUIDFORMAT) | |
| # | |
| # Check C format GUID | |
| # | |
| if not IsValidCFormatGuid(TokenList[1]): | |
| self._LoggerError(ST.ERR_DECPARSE_CGUID_GUIDFORMAT) | |
| Item = DecGuidItemObject(TokenList[0], TokenList[1], GuidString) | |
| ItemObject = self.ObjectDict[self._RawData.CurrentScope[0][0]] | |
| ItemObject.AddItem(Item, self._RawData.CurrentScope) | |
| return Item | |
| ## _DecUserExtension | |
| # | |
| # Parse user extension section | |
| # | |
| class _DecUserExtension(_DecBase): | |
| def __init__(self, RawData): | |
| _DecBase.__init__(self, RawData) | |
| self.ItemObject = DecUserExtensionObject(RawData.Filename) | |
| self._Headers = [] | |
| self._CurItems = [] | |
| def BlockStart(self): | |
| self._CurItems = [] | |
| for Header in self._RawData.CurrentScope: | |
| if Header in self._Headers: | |
| self._LoggerError(ST.ERR_DECPARSE_UE_DUPLICATE) | |
| else: | |
| self._Headers.append(Header) | |
| for Item in self._CurItems: | |
| if Item.UserId == Header[1] and Item.IdString == Header[2]: | |
| Item.ArchAndModuleType.append(Header[3]) | |
| break | |
| else: | |
| Item = DecUserExtensionItemObject() | |
| Item.UserId = Header[1] | |
| Item.IdString = Header[2] | |
| Item.ArchAndModuleType.append(Header[3]) | |
| self._CurItems.append(Item) | |
| self.ItemObject.AddItem(Item, None) | |
| self._LocalMacro = {} | |
| def _ParseItem(self): | |
| Line = self._RawData.CurrentLine | |
| Item = None | |
| for Item in self._CurItems: | |
| if Item.UserString: | |
| Item.UserString = '\n'.join([Item.UserString, Line]) | |
| else: | |
| Item.UserString = Line | |
| return Item | |
| ## Dec | |
| # | |
| # Top dec parser | |
| # | |
| class Dec(_DecBase, _DecComments): | |
| def __init__(self, DecFile, Parse = True): | |
| try: | |
| Content = ConvertSpecialChar(open(DecFile, 'r').readlines()) | |
| except BaseException: | |
| Logger.Error(TOOL_NAME, FILE_OPEN_FAILURE, File=DecFile, | |
| ExtraData=ST.ERR_DECPARSE_FILEOPEN % DecFile) | |
| # | |
| # Pre-parser for Private section | |
| # | |
| self._Private = '' | |
| __IsFoundPrivate = False | |
| NewContent = [] | |
| for Line in Content: | |
| Line = Line.strip() | |
| if Line.startswith(DT.TAB_SECTION_START) and Line.endswith(DT.TAB_PRIVATE + DT.TAB_SECTION_END): | |
| __IsFoundPrivate = True | |
| if Line.startswith(DT.TAB_SECTION_START) and Line.endswith(DT.TAB_SECTION_END)\ | |
| and not Line.endswith(DT.TAB_PRIVATE + DT.TAB_SECTION_END): | |
| __IsFoundPrivate = False | |
| if __IsFoundPrivate: | |
| self._Private += Line + '\r' | |
| if not __IsFoundPrivate: | |
| NewContent.append(Line + '\r') | |
| RawData = FileContent(DecFile, NewContent) | |
| _DecComments.__init__(self) | |
| _DecBase.__init__(self, RawData) | |
| self.BinaryHeadComment = [] | |
| self.PcdErrorCommentDict = {} | |
| self._Define = _DecDefine(RawData) | |
| self._Include = _DecInclude(RawData) | |
| self._Guid = _DecGuid(RawData) | |
| self._LibClass = _DecLibraryclass(RawData) | |
| self._Pcd = _DecPcd(RawData) | |
| self._UserEx = _DecUserExtension(RawData) | |
| # | |
| # DEC file supported data types (one type per section) | |
| # | |
| self._SectionParser = { | |
| DT.TAB_DEC_DEFINES.upper() : self._Define, | |
| DT.TAB_INCLUDES.upper() : self._Include, | |
| DT.TAB_LIBRARY_CLASSES.upper() : self._LibClass, | |
| DT.TAB_GUIDS.upper() : self._Guid, | |
| DT.TAB_PPIS.upper() : self._Guid, | |
| DT.TAB_PROTOCOLS.upper() : self._Guid, | |
| DT.TAB_PCDS_FIXED_AT_BUILD_NULL.upper() : self._Pcd, | |
| DT.TAB_PCDS_PATCHABLE_IN_MODULE_NULL.upper() : self._Pcd, | |
| DT.TAB_PCDS_FEATURE_FLAG_NULL.upper() : self._Pcd, | |
| DT.TAB_PCDS_DYNAMIC_NULL.upper() : self._Pcd, | |
| DT.TAB_PCDS_DYNAMIC_EX_NULL.upper() : self._Pcd, | |
| DT.TAB_USER_EXTENSIONS.upper() : self._UserEx | |
| } | |
| if Parse: | |
| self.ParseDecComment() | |
| self.Parse() | |
| # | |
| # Parsing done, check required fields | |
| # | |
| self.CheckRequiredFields() | |
| def CheckRequiredFields(self): | |
| for SectionParser in self._SectionParser.values(): | |
| if not SectionParser.CheckRequiredFields(): | |
| return False | |
| return True | |
| ## | |
| # Parse DEC file | |
| # | |
| def ParseDecComment(self): | |
| IsFileHeader = False | |
| IsBinaryHeader = False | |
| FileHeaderLineIndex = -1 | |
| BinaryHeaderLineIndex = -1 | |
| TokenSpaceGuidCName = '' | |
| # | |
| # Parse PCD error comment section | |
| # | |
| while not self._RawData.IsEndOfFile(): | |
| self._RawData.CurrentLine = self._RawData.GetNextLine() | |
| if self._RawData.CurrentLine.startswith(DT.TAB_COMMENT_SPLIT) and \ | |
| DT.TAB_SECTION_START in self._RawData.CurrentLine and \ | |
| DT.TAB_SECTION_END in self._RawData.CurrentLine: | |
| self._RawData.CurrentLine = self._RawData.CurrentLine.replace(DT.TAB_COMMENT_SPLIT, '').strip() | |
| if self._RawData.CurrentLine[0] == DT.TAB_SECTION_START and \ | |
| self._RawData.CurrentLine[-1] == DT.TAB_SECTION_END: | |
| RawSection = self._RawData.CurrentLine[1:-1].strip() | |
| if RawSection.upper().startswith(DT.TAB_PCD_ERROR.upper()+'.'): | |
| TokenSpaceGuidCName = RawSection.split(DT.TAB_PCD_ERROR+'.')[1].strip() | |
| continue | |
| if TokenSpaceGuidCName and self._RawData.CurrentLine.startswith(DT.TAB_COMMENT_SPLIT): | |
| self._RawData.CurrentLine = self._RawData.CurrentLine.replace(DT.TAB_COMMENT_SPLIT, '').strip() | |
| if self._RawData.CurrentLine != '': | |
| if DT.TAB_VALUE_SPLIT not in self._RawData.CurrentLine: | |
| self._LoggerError(ST.ERR_DECPARSE_PCDERRORMSG_MISS_VALUE_SPLIT) | |
| PcdErrorNumber, PcdErrorMsg = GetSplitValueList(self._RawData.CurrentLine, DT.TAB_VALUE_SPLIT, 1) | |
| PcdErrorNumber = ParsePcdErrorCode(PcdErrorNumber, self._RawData.Filename, self._RawData.LineIndex) | |
| if not PcdErrorMsg.strip(): | |
| self._LoggerError(ST.ERR_DECPARSE_PCD_MISS_ERRORMSG) | |
| self.PcdErrorCommentDict[(TokenSpaceGuidCName, PcdErrorNumber)] = PcdErrorMsg.strip() | |
| else: | |
| TokenSpaceGuidCName = '' | |
| self._RawData.LineIndex = 0 | |
| self._RawData.CurrentLine = '' | |
| self._RawData.NextLine = '' | |
| while not self._RawData.IsEndOfFile(): | |
| Line, Comment = CleanString(self._RawData.GetNextLine()) | |
| # | |
| # Header must be pure comment | |
| # | |
| if Line != '': | |
| self._RawData.UndoNextLine() | |
| break | |
| if Comment and Comment.startswith(DT.TAB_SPECIAL_COMMENT) and Comment.find(DT.TAB_HEADER_COMMENT) > 0 \ | |
| and not Comment[2:Comment.find(DT.TAB_HEADER_COMMENT)].strip(): | |
| IsFileHeader = True | |
| IsBinaryHeader = False | |
| FileHeaderLineIndex = self._RawData.LineIndex | |
| # | |
| # Get license information before '@file' | |
| # | |
| if not IsFileHeader and not IsBinaryHeader and Comment and Comment.startswith(DT.TAB_COMMENT_SPLIT) and \ | |
| DT.TAB_BINARY_HEADER_COMMENT not in Comment: | |
| self._HeadComment.append((Comment, self._RawData.LineIndex)) | |
| if Comment and IsFileHeader and \ | |
| not(Comment.startswith(DT.TAB_SPECIAL_COMMENT) \ | |
| and Comment.find(DT.TAB_BINARY_HEADER_COMMENT) > 0): | |
| self._HeadComment.append((Comment, self._RawData.LineIndex)) | |
| # | |
| # Double '#' indicates end of header comments | |
| # | |
| if (not Comment or Comment == DT.TAB_SPECIAL_COMMENT) and IsFileHeader: | |
| IsFileHeader = False | |
| continue | |
| if Comment and Comment.startswith(DT.TAB_SPECIAL_COMMENT) \ | |
| and Comment.find(DT.TAB_BINARY_HEADER_COMMENT) > 0: | |
| IsBinaryHeader = True | |
| IsFileHeader = False | |
| BinaryHeaderLineIndex = self._RawData.LineIndex | |
| if Comment and IsBinaryHeader: | |
| self.BinaryHeadComment.append((Comment, self._RawData.LineIndex)) | |
| # | |
| # Double '#' indicates end of header comments | |
| # | |
| if (not Comment or Comment == DT.TAB_SPECIAL_COMMENT) and IsBinaryHeader: | |
| IsBinaryHeader = False | |
| break | |
| if FileHeaderLineIndex > -1 and not IsFileHeader and not IsBinaryHeader: | |
| break | |
| if FileHeaderLineIndex > BinaryHeaderLineIndex and FileHeaderLineIndex > -1 and BinaryHeaderLineIndex > -1: | |
| self._LoggerError(ST.ERR_BINARY_HEADER_ORDER) | |
| if FileHeaderLineIndex == -1: | |
| # self._LoggerError(ST.ERR_NO_SOURCE_HEADER) | |
| Logger.Error(TOOL_NAME, FORMAT_INVALID, | |
| ST.ERR_NO_SOURCE_HEADER, | |
| File=self._RawData.Filename) | |
| return | |
| def _StopCurrentParsing(self, Line): | |
| return False | |
| def _ParseItem(self): | |
| self._SectionHeaderParser() | |
| if len(self._RawData.CurrentScope) == 0: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_EMPTY) | |
| SectionObj = self._SectionParser[self._RawData.CurrentScope[0][0]] | |
| SectionObj.BlockStart() | |
| SectionObj.Parse() | |
| return SectionObj.GetDataObject() | |
| def _UserExtentionSectionParser(self): | |
| self._RawData.CurrentScope = [] | |
| ArchList = set() | |
| Section = self._RawData.CurrentLine[1:-1] | |
| Par = ParserHelper(Section, self._RawData.Filename) | |
| while not Par.End(): | |
| # | |
| # User extention | |
| # | |
| Token = Par.GetToken() | |
| if Token.upper() != DT.TAB_USER_EXTENSIONS.upper(): | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_UE) | |
| UserExtension = Token.upper() | |
| Par.AssertChar(DT.TAB_SPLIT, ST.ERR_DECPARSE_SECTION_UE, self._RawData.LineIndex) | |
| # | |
| # UserID | |
| # | |
| Token = Par.GetToken() | |
| if not IsValidUserId(Token): | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_UE_USERID) | |
| UserId = Token | |
| Par.AssertChar(DT.TAB_SPLIT, ST.ERR_DECPARSE_SECTION_UE, self._RawData.LineIndex) | |
| # | |
| # IdString | |
| # | |
| Token = Par.GetToken() | |
| if not IsValidIdString(Token): | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_UE_IDSTRING) | |
| IdString = Token | |
| Arch = 'COMMON' | |
| if Par.Expect(DT.TAB_SPLIT): | |
| Token = Par.GetToken() | |
| Arch = Token.upper() | |
| if not IsValidArch(Arch): | |
| self._LoggerError(ST.ERR_DECPARSE_ARCH) | |
| ArchList.add(Arch) | |
| if [UserExtension, UserId, IdString, Arch] not in \ | |
| self._RawData.CurrentScope: | |
| self._RawData.CurrentScope.append( | |
| [UserExtension, UserId, IdString, Arch] | |
| ) | |
| if not Par.Expect(DT.TAB_COMMA_SPLIT): | |
| break | |
| elif Par.End(): | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_COMMA) | |
| Par.AssertEnd(ST.ERR_DECPARSE_SECTION_UE, self._RawData.LineIndex) | |
| if 'COMMON' in ArchList and len(ArchList) > 1: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_COMMON) | |
| ## Section header parser | |
| # | |
| # The section header is always in following format: | |
| # | |
| # [section_name.arch<.platform|module_type>] | |
| # | |
| def _SectionHeaderParser(self): | |
| if self._RawData.CurrentLine[0] != DT.TAB_SECTION_START or self._RawData.CurrentLine[-1] != DT.TAB_SECTION_END: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_IDENTIFY) | |
| RawSection = self._RawData.CurrentLine[1:-1].strip().upper() | |
| # | |
| # Check defines section which is only allowed to occur once and | |
| # no arch can be followed | |
| # | |
| if RawSection.startswith(DT.TAB_DEC_DEFINES.upper()): | |
| if RawSection != DT.TAB_DEC_DEFINES.upper(): | |
| self._LoggerError(ST.ERR_DECPARSE_DEFINE_SECNAME) | |
| # | |
| # Check user extension section | |
| # | |
| if RawSection.startswith(DT.TAB_USER_EXTENSIONS.upper()): | |
| return self._UserExtentionSectionParser() | |
| self._RawData.CurrentScope = [] | |
| SectionNames = [] | |
| ArchList = set() | |
| for Item in GetSplitValueList(RawSection, DT.TAB_COMMA_SPLIT): | |
| if Item == '': | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_SUBEMPTY % self._RawData.CurrentLine) | |
| ItemList = GetSplitValueList(Item, DT.TAB_SPLIT) | |
| # | |
| # different types of PCD are permissible in one section | |
| # | |
| SectionName = ItemList[0] | |
| if SectionName not in self._SectionParser: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_UNKNOW % SectionName) | |
| if SectionName not in SectionNames: | |
| SectionNames.append(SectionName) | |
| # | |
| # In DEC specification, all section headers have at most two part: | |
| # SectionName.Arch except UserExtension | |
| # | |
| if len(ItemList) > 2: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_SUBTOOMANY % Item) | |
| if DT.TAB_PCDS_FEATURE_FLAG_NULL.upper() in SectionNames and len(SectionNames) > 1: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_FEATUREFLAG % DT.TAB_PCDS_FEATURE_FLAG_NULL) | |
| # | |
| # S1 is always Arch | |
| # | |
| if len(ItemList) > 1: | |
| Str1 = ItemList[1] | |
| if not IsValidArch(Str1): | |
| self._LoggerError(ST.ERR_DECPARSE_ARCH) | |
| else: | |
| Str1 = 'COMMON' | |
| ArchList.add(Str1) | |
| if [SectionName, Str1] not in self._RawData.CurrentScope: | |
| self._RawData.CurrentScope.append([SectionName, Str1]) | |
| # | |
| # 'COMMON' must not be used with specific ARCHs at the same section | |
| # | |
| if 'COMMON' in ArchList and len(ArchList) > 1: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_COMMON) | |
| if len(SectionNames) == 0: | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_SUBEMPTY % self._RawData.CurrentLine) | |
| if len(SectionNames) != 1: | |
| for Sec in SectionNames: | |
| if not Sec.startswith(DT.TAB_PCDS.upper()): | |
| self._LoggerError(ST.ERR_DECPARSE_SECTION_NAME % str(SectionNames)) | |
| def GetDefineSectionMacro(self): | |
| return self._Define.GetLocalMacro() | |
| def GetDefineSectionObject(self): | |
| return self._Define.GetDataObject() | |
| def GetIncludeSectionObject(self): | |
| return self._Include.GetDataObject() | |
| def GetGuidSectionObject(self): | |
| return self._Guid.GetGuidObject() | |
| def GetProtocolSectionObject(self): | |
| return self._Guid.GetProtocolObject() | |
| def GetPpiSectionObject(self): | |
| return self._Guid.GetPpiObject() | |
| def GetLibraryClassSectionObject(self): | |
| return self._LibClass.GetDataObject() | |
| def GetPcdSectionObject(self): | |
| return self._Pcd.GetDataObject() | |
| def GetUserExtensionSectionObject(self): | |
| return self._UserEx.GetDataObject() | |
| def GetPackageSpecification(self): | |
| return self._Define.GetDataObject().GetPackageSpecification() | |
| def GetPackageName(self): | |
| return self._Define.GetDataObject().GetPackageName() | |
| def GetPackageGuid(self): | |
| return self._Define.GetDataObject().GetPackageGuid() | |
| def GetPackageVersion(self): | |
| return self._Define.GetDataObject().GetPackageVersion() | |
| def GetPackageUniFile(self): | |
| return self._Define.GetDataObject().GetPackageUniFile() | |
| def GetPrivateSections(self): | |
| return self._Private |