X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=BaseTools%2FSource%2FPython%2FCommon%2FString.py;h=5dc5b85dc5a459fb03358fd9ff07571cdc1de3b1;hp=5da0cacfb01638efd77c61cd80e1a0f20cbe2800;hb=066c71544ed1c0e1a703b26982f9da60d21bcc5a;hpb=30fdf1140b8d1ce93f3821d986fa165552023440 diff --git a/BaseTools/Source/Python/Common/String.py b/BaseTools/Source/Python/Common/String.py index 5da0cacfb0..5dc5b85dc5 100644 --- a/BaseTools/Source/Python/Common/String.py +++ b/BaseTools/Source/Python/Common/String.py @@ -1,8 +1,8 @@ ## @file # This file is used to define common string related functions used in parsing process # -# Copyright (c) 2007 ~ 2008, Intel Corporation -# All rights reserved. This program and the accompanying materials +# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+# 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 @@ -16,12 +16,18 @@ # import re import DataType -import os.path +import Common.LongFilePathOs as os import string import EdkLogger as EdkLogger -from GlobalData import * +import GlobalData from BuildToolError import * +from CommonDataClass.Exceptions import * +from Common.LongFilePathSupport import OpenLongFilePath as open +from Common.MultipleWorkspace import MultipleWorkspace as mws + +gHexVerPatt = re.compile('0x[a-f0-9]{4}[a-f0-9]{4}$', re.IGNORECASE) +gHumanReadableVerPatt = re.compile(r'([1-9][0-9]*|0)\.[0-9]{1,2}$') ## GetSplitValueList # @@ -35,8 +41,64 @@ from BuildToolError import * # # @retval list() A list for splitted string # -def GetSplitValueList(String, SplitTag = DataType.TAB_VALUE_SPLIT, MaxSplit = -1): - return map(lambda l: l.strip(), String.split(SplitTag, MaxSplit)) +def GetSplitValueList(String, SplitTag=DataType.TAB_VALUE_SPLIT, MaxSplit= -1): + ValueList = [] + Last = 0 + Escaped = False + InSingleQuoteString = False + InDoubleQuoteString = False + InParenthesis = 0 + for Index in range(0, len(String)): + Char = String[Index] + + if not Escaped: + # Found a splitter not in a string, split it + if (not InSingleQuoteString or not InDoubleQuoteString) and InParenthesis == 0 and Char == SplitTag: + ValueList.append(String[Last:Index].strip()) + Last = Index + 1 + if MaxSplit > 0 and len(ValueList) >= MaxSplit: + break + + if Char == '\\' and (InSingleQuoteString or InDoubleQuoteString): + Escaped = True + elif Char == '"' and not InSingleQuoteString: + if not InDoubleQuoteString: + InDoubleQuoteString = True + else: + InDoubleQuoteString = False + elif Char == "'" and not InDoubleQuoteString: + if not InSingleQuoteString: + InSingleQuoteString = True + else: + InSingleQuoteString = False + elif Char == '(': + InParenthesis = InParenthesis + 1 + elif Char == ')': + InParenthesis = InParenthesis - 1 + else: + Escaped = False + + if Last < len(String): + ValueList.append(String[Last:].strip()) + elif Last == len(String): + ValueList.append('') + + return ValueList + +## GetSplitList +# +# Get a value list from a string with multiple values splited with SplitString +# The default SplitTag is DataType.TAB_VALUE_SPLIT +# 'AAA|BBB|CCC' -> ['AAA', 'BBB', 'CCC'] +# +# @param String: The input string to be splitted +# @param SplitStr: The split key, default is DataType.TAB_VALUE_SPLIT +# @param MaxSplit: The max number of split values, default is -1 +# +# @retval list() A list for splitted string +# +def GetSplitList(String, SplitStr=DataType.TAB_VALUE_SPLIT, MaxSplit= -1): + return map(lambda l: l.strip(), String.split(SplitStr, MaxSplit)) ## MergeArches # @@ -186,7 +248,7 @@ def SplitModuleType(Key): # # @retval NewList A new string list whose macros are replaced # -def ReplaceMacros(StringList, MacroDefinitions={}, SelfReplacement = False): +def ReplaceMacros(StringList, MacroDefinitions={}, SelfReplacement=False): NewList = [] for String in StringList: if type(String) == type(''): @@ -207,20 +269,23 @@ def ReplaceMacros(StringList, MacroDefinitions={}, SelfReplacement = False): # # @retval string The string whose macros are replaced # -def ReplaceMacro(String, MacroDefinitions={}, SelfReplacement = False): +def ReplaceMacro(String, MacroDefinitions={}, SelfReplacement=False, RaiseError=False): LastString = String - while MacroDefinitions: - MacroUsed = gMacroPattern.findall(String) + while String and MacroDefinitions: + MacroUsed = GlobalData.gMacroRefPattern.findall(String) # no macro found in String, stop replacing if len(MacroUsed) == 0: break for Macro in MacroUsed: if Macro not in MacroDefinitions: + if RaiseError: + raise SymbolNotFound("%s not defined" % Macro) if SelfReplacement: String = String.replace("$(%s)" % Macro, '') continue - String = String.replace("$(%s)" % Macro, MacroDefinitions[Macro]) + if "$(%s)" % Macro not in MacroDefinitions[Macro]: + String = String.replace("$(%s)" % Macro, MacroDefinitions[Macro]) # in case there's macro not defined if String == LastString: break @@ -238,7 +303,7 @@ def ReplaceMacro(String, MacroDefinitions={}, SelfReplacement = False): # # @retval Path Formatted path # -def NormPath(Path, Defines = {}): +def NormPath(Path, Defines={}): IsRelativePath = False if Path: if Path[0] == '.': @@ -252,6 +317,11 @@ def NormPath(Path, Defines = {}): # To local path format # Path = os.path.normpath(Path) + if Path.startswith(GlobalData.gWorkspace) and not Path.startswith(GlobalData.gBuildDirectory) and not os.path.exists(Path): + Path = Path[len (GlobalData.gWorkspace):] + if Path[0] == os.path.sep: + Path = Path[1:] + Path = mws.join(GlobalData.gWorkspace, Path) if IsRelativePath and Path[0] != '.': Path = os.path.join('.', Path) @@ -268,20 +338,49 @@ def NormPath(Path, Defines = {}): # # @retval Path Formatted path # -def CleanString(Line, CommentCharacter = DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False): +def CleanString(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False, BuildOption=False): # # remove whitespace # Line = Line.strip(); # - # Replace R8's comment character + # Replace Edk's comment character # if AllowCppStyleComment: - Line = Line.replace(DataType.TAB_COMMENT_R8_SPLIT, CommentCharacter) + Line = Line.replace(DataType.TAB_COMMENT_EDK_SPLIT, CommentCharacter) # - # remove comments + # remove comments, but we should escape comment character in string # - Line = Line.split(CommentCharacter, 1)[0]; + InDoubleQuoteString = False + InSingleQuoteString = False + CommentInString = False + for Index in range(0, len(Line)): + if Line[Index] == '"' and not InSingleQuoteString: + InDoubleQuoteString = not InDoubleQuoteString + elif Line[Index] == "'" and not InDoubleQuoteString: + InSingleQuoteString = not InSingleQuoteString + elif Line[Index] == CommentCharacter and (InSingleQuoteString or InDoubleQuoteString): + CommentInString = True + elif Line[Index] == CommentCharacter and not (InSingleQuoteString or InDoubleQuoteString): + Line = Line[0: Index] + break + + if CommentInString and BuildOption: + Line = Line.replace('"', '') + ChIndex = Line.find('#') + while ChIndex >= 0: + if GlobalData.gIsWindows: + if ChIndex == 0 or Line[ChIndex - 1] != '^': + Line = Line[0:ChIndex] + '^' + Line[ChIndex:] + ChIndex = Line.find('#', ChIndex + 2) + else: + ChIndex = Line.find('#', ChIndex + 1) + else: + if ChIndex == 0 or Line[ChIndex - 1] != '\\': + Line = Line[0:ChIndex] + '\\' + Line[ChIndex:] + ChIndex = Line.find('#', ChIndex + 2) + else: + ChIndex = Line.find('#', ChIndex + 1) # # remove whitespace again # @@ -289,6 +388,47 @@ def CleanString(Line, CommentCharacter = DataType.TAB_COMMENT_SPLIT, AllowCppSty return Line +## CleanString2 +# +# Split statement with 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 +# +# @retval Path Formatted path +# +def CleanString2(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False): + # + # remove whitespace + # + Line = Line.strip(); + # + # Replace Edk's comment character + # + if AllowCppStyleComment: + Line = Line.replace(DataType.TAB_COMMENT_EDK_SPLIT, CommentCharacter) + # + # separate comments and statements, but we should escape comment character in string + # + InDoubleQuoteString = False + InSingleQuoteString = False + CommentInString = False + Comment = '' + for Index in range(0, len(Line)): + if Line[Index] == '"' and not InSingleQuoteString: + InDoubleQuoteString = not InDoubleQuoteString + elif Line[Index] == "'" and not InDoubleQuoteString: + InSingleQuoteString = not InSingleQuoteString + elif Line[Index] == CommentCharacter and (InDoubleQuoteString or InSingleQuoteString): + CommentInString = True + elif Line[Index] == CommentCharacter and not (InDoubleQuoteString or InSingleQuoteString): + Comment = Line[Index:].strip() + Line = Line[0:Index].strip() + break + + return Line, Comment + ## GetMultipleValuesOfKeyFromLines # # Parse multiple strings to clean comment and spaces @@ -326,6 +466,34 @@ def GetDefineValue(String, Key, CommentCharacter): String = CleanString(String) return String[String.find(Key + ' ') + len(Key + ' ') : ] +## GetHexVerValue +# +# Get a Hex Version Value +# +# @param VerString: The version string to be parsed +# +# +# @retval: If VerString is incorrectly formatted, return "None" which will break the build. +# If VerString is correctly formatted, return a Hex value of the Version Number (0xmmmmnnnn) +# where mmmm is the major number and nnnn is the adjusted minor number. +# +def GetHexVerValue(VerString): + VerString = CleanString(VerString) + + if gHumanReadableVerPatt.match(VerString): + ValueList = VerString.split('.') + Major = ValueList[0] + Minor = ValueList[1] + if len(Minor) == 1: + Minor += '0' + DeciValue = (int(Major) << 16) + int(Minor); + return "0x%08x" % DeciValue + elif gHexVerPatt.match(VerString): + return VerString + else: + return None + + ## GetSingleValueOfKeyFromLines # # Parse multiple strings as below to get value of each definition line @@ -429,7 +597,7 @@ def PreCheck(FileName, FileContent, SupSectionTag): # if Line.find('$') > -1: if Line.find('$(') < 0 or Line.find(')') < 0: - EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError = EdkLogger.IsRaiseError) + EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=EdkLogger.IsRaiseError) # # Check [] @@ -439,7 +607,7 @@ def PreCheck(FileName, FileContent, SupSectionTag): # Only get one '[' or one ']' # if not (Line.find('[') > -1 and Line.find(']') > -1): - EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError = EdkLogger.IsRaiseError) + EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=EdkLogger.IsRaiseError) # # Regenerate FileContent @@ -447,7 +615,7 @@ def PreCheck(FileName, FileContent, SupSectionTag): NewFileContent = NewFileContent + Line + '\r\n' if IsFailed: - EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError = EdkLogger.IsRaiseError) + EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=EdkLogger.IsRaiseError) return NewFileContent @@ -465,8 +633,8 @@ def PreCheck(FileName, FileContent, SupSectionTag): # # @retval True The file type is correct # -def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo = -1): - if CheckFilename != '' and CheckFilename != None: +def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo= -1): + if CheckFilename != '' and CheckFilename is not None: (Root, Ext) = os.path.splitext(CheckFilename) if Ext.upper() != ExtName.upper(): ContainerFile = open(ContainerFilename, 'r').read() @@ -474,7 +642,7 @@ def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo = GetLineNo(ContainerFile, Line) ErrorMsg = "Invalid %s. '%s' is found, but '%s' file is needed" % (SectionName, CheckFilename, ExtName) EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, Line=LineNo, - File=ContainerFilename, RaiseError = EdkLogger.IsRaiseError) + File=ContainerFilename, RaiseError=EdkLogger.IsRaiseError) return True @@ -492,9 +660,9 @@ def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, # # @retval The file full path if the file exists # -def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line, LineNo = -1): +def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line, LineNo= -1): CheckFile = '' - if CheckFilename != '' and CheckFilename != None: + if CheckFilename != '' and CheckFilename is not None: CheckFile = WorkspaceFile(WorkspaceDir, CheckFilename) if not os.path.isfile(CheckFile): ContainerFile = open(ContainerFilename, 'r').read() @@ -502,7 +670,7 @@ def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, LineNo = GetLineNo(ContainerFile, Line) ErrorMsg = "Can't find file '%s' defined in section '%s'" % (CheckFile, SectionName) EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, - File=ContainerFilename, Line = LineNo, RaiseError = EdkLogger.IsRaiseError) + File=ContainerFilename, Line=LineNo, RaiseError=EdkLogger.IsRaiseError) return CheckFile @@ -516,7 +684,7 @@ def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, # @retval int Index of the line # @retval -1 The line is not found # -def GetLineNo(FileContent, Line, IsIgnoreComment = True): +def GetLineNo(FileContent, Line, IsIgnoreComment=True): LineList = FileContent.splitlines() for Index in range(len(LineList)): if LineList[Index].find(Line) > -1: @@ -539,13 +707,13 @@ def GetLineNo(FileContent, Line, IsIgnoreComment = True): # @param File: File which has the string # @param Format: Correct format # -def RaiseParserError(Line, Section, File, Format = '', LineNo = -1): +def RaiseParserError(Line, Section, File, Format='', LineNo= -1): if LineNo == -1: LineNo = GetLineNo(open(os.path.normpath(File), 'r').read(), Line) ErrorMsg = "Invalid statement '%s' is found in section '%s'" % (Line, Section) if Format != '': Format = "Correct format is " + Format - EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, File=File, Line=LineNo, ExtraData=Format, RaiseError = EdkLogger.IsRaiseError) + EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, File=File, Line=LineNo, ExtraData=Format, RaiseError=EdkLogger.IsRaiseError) ## WorkspaceFile # @@ -557,7 +725,7 @@ def RaiseParserError(Line, Section, File, Format = '', LineNo = -1): # @retval string A full path # def WorkspaceFile(WorkspaceDir, Filename): - return os.path.join(NormPath(WorkspaceDir), NormPath(Filename)) + return mws.join(NormPath(WorkspaceDir), NormPath(Filename)) ## Split string # @@ -607,11 +775,11 @@ def RemoveBlockComment(Lines): # # Remove comment block # - if Line.find(DataType.TAB_COMMENT_R8_START) > -1: - ReservedLine = GetSplitValueList(Line, DataType.TAB_COMMENT_R8_START, 1)[0] + if Line.find(DataType.TAB_COMMENT_EDK_START) > -1: + ReservedLine = GetSplitList(Line, DataType.TAB_COMMENT_EDK_START, 1)[0] IsFindBlockComment = True - if Line.find(DataType.TAB_COMMENT_R8_END) > -1: - Line = ReservedLine + GetSplitValueList(Line, DataType.TAB_COMMENT_R8_END, 1)[1] + if Line.find(DataType.TAB_COMMENT_EDK_END) > -1: + Line = ReservedLine + GetSplitList(Line, DataType.TAB_COMMENT_EDK_END, 1)[1] ReservedLine = '' IsFindBlockComment = False if IsFindBlockComment: @@ -624,7 +792,7 @@ def RemoveBlockComment(Lines): # # Get String of a List # -def GetStringOfList(List, Split = ' '): +def GetStringOfList(List, Split=' '): if type(List) != type([]): return List Str = '' @@ -648,21 +816,35 @@ def GetHelpTextList(HelpTextClassList): def StringToArray(String): if isinstance(String, unicode): - if len(unicode) ==0: - return "{0x00, 0x00}" - return "{%s, 0x00, 0x00}" % ", ".join(["0x%02x, 0x00" % ord(C) for C in String]) + if len(unicode) == 0: + return "{0x00,0x00}" + return "{%s,0x00,0x00}" % ",".join(["0x%02x,0x00" % ord(C) for C in String]) elif String.startswith('L"'): if String == "L\"\"": - return "{0x00, 0x00}" + return "{0x00,0x00}" else: - return "{%s, 0x00, 0x00}" % ", ".join(["0x%02x, 0x00" % ord(C) for C in String[2:-1]]) + return "{%s,0x00,0x00}" % ",".join(["0x%02x,0x00" % ord(C) for C in String[2:-1]]) elif String.startswith('"'): if String == "\"\"": - return "{0x00}"; + return "{0x00,0x00}" else: - return "{%s, 0x00}" % ", ".join(["0x%02x" % ord(C) for C in String[1:-1]]) + StringLen = len(String[1:-1]) + if StringLen % 2: + return "{%s,0x00}" % ",".join(["0x%02x" % ord(C) for C in String[1:-1]]) + else: + return "{%s,0x00,0x00}" % ",".join(["0x%02x" % ord(C) for C in String[1:-1]]) + elif String.startswith('{'): + StringLen = len(String.split(",")) + if StringLen % 2: + return "{%s,0x00}" % ",".join([ C.strip() for C in String[1:-1].split(',')]) + else: + return "{%s}" % ",".join([ C.strip() for C in String[1:-1].split(',')]) + else: - return '{%s, 0}' % ', '.join(String.split()) + if len(String.split()) % 2: + return '{%s,0}' % ','.join(String.split()) + else: + return '{%s,0,0}' % ','.join(String.split()) def StringArrayLength(String): if isinstance(String, unicode): @@ -673,7 +855,7 @@ def StringArrayLength(String): return (len(String) - 2 + 1) else: return len(String.split()) + 1 - + def RemoveDupOption(OptionString, Which="/I", Against=None): OptionList = OptionString.split() ValueList = []