1## @file 2# This file is used to define common string related functions used in parsing 3# process 4# 5# Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR> 6# 7# This program and the accompanying materials are licensed and made available 8# under the terms and conditions of the BSD License which accompanies this 9# distribution. The full text of the license may be found at 10# http://opensource.org/licenses/bsd-license.php 11# 12# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14# 15''' 16String 17''' 18## 19# Import Modules 20# 21import re 22import os.path 23from string import strip 24import Logger.Log as Logger 25import Library.DataType as DataType 26from Logger.ToolError import FORMAT_INVALID 27from Logger.ToolError import PARSER_ERROR 28from Logger import StringTable as ST 29 30# 31# Regular expression for matching macro used in DSC/DEC/INF file inclusion 32# 33gMACRO_PATTERN = re.compile("\$\(([_A-Z][_A-Z0-9]*)\)", re.UNICODE) 34 35## GetSplitValueList 36# 37# Get a value list from a string with multiple values splited with SplitTag 38# The default SplitTag is DataType.TAB_VALUE_SPLIT 39# 'AAA|BBB|CCC' -> ['AAA', 'BBB', 'CCC'] 40# 41# @param String: The input string to be splitted 42# @param SplitTag: The split key, default is DataType.TAB_VALUE_SPLIT 43# @param MaxSplit: The max number of split values, default is -1 44# 45# 46def GetSplitValueList(String, SplitTag=DataType.TAB_VALUE_SPLIT, MaxSplit= -1): 47 return map(lambda l: l.strip(), String.split(SplitTag, MaxSplit)) 48 49## MergeArches 50# 51# Find a key's all arches in dict, add the new arch to the list 52# If not exist any arch, set the arch directly 53# 54# @param Dict: The input value for Dict 55# @param Key: The input value for Key 56# @param Arch: The Arch to be added or merged 57# 58def MergeArches(Dict, Key, Arch): 59 if Key in Dict.keys(): 60 Dict[Key].append(Arch) 61 else: 62 Dict[Key] = Arch.split() 63 64## GenDefines 65# 66# Parse a string with format "DEFINE <VarName> = <PATH>" 67# Generate a map Defines[VarName] = PATH 68# Return False if invalid format 69# 70# @param String: String with DEFINE statement 71# @param Arch: Supportted Arch 72# @param Defines: DEFINE statement to be parsed 73# 74def GenDefines(String, Arch, Defines): 75 if String.find(DataType.TAB_DEFINE + ' ') > -1: 76 List = String.replace(DataType.TAB_DEFINE + ' ', '').\ 77 split(DataType.TAB_EQUAL_SPLIT) 78 if len(List) == 2: 79 Defines[(CleanString(List[0]), Arch)] = CleanString(List[1]) 80 return 0 81 else: 82 return -1 83 return 1 84 85## GetLibraryClassesWithModuleType 86# 87# Get Library Class definition when no module type defined 88# 89# @param Lines: The content to be parsed 90# @param Key: Reserved 91# @param KeyValues: To store data after parsing 92# @param CommentCharacter: Comment char, used to ignore comment content 93# 94def GetLibraryClassesWithModuleType(Lines, Key, KeyValues, CommentCharacter): 95 NewKey = SplitModuleType(Key) 96 Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1] 97 LineList = Lines.splitlines() 98 for Line in LineList: 99 Line = CleanString(Line, CommentCharacter) 100 if Line != '' and Line[0] != CommentCharacter: 101 KeyValues.append([CleanString(Line, CommentCharacter), NewKey[1]]) 102 103 return True 104 105## GetDynamics 106# 107# Get Dynamic Pcds 108# 109# @param Lines: The content to be parsed 110# @param Key: Reserved 111# @param KeyValues: To store data after parsing 112# @param CommentCharacter: Comment char, used to ignore comment content 113# 114def GetDynamics(Lines, Key, KeyValues, CommentCharacter): 115 # 116 # Get SkuId Name List 117 # 118 SkuIdNameList = SplitModuleType(Key) 119 120 Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1] 121 LineList = Lines.splitlines() 122 for Line in LineList: 123 Line = CleanString(Line, CommentCharacter) 124 if Line != '' and Line[0] != CommentCharacter: 125 KeyValues.append([CleanString(Line, CommentCharacter), SkuIdNameList[1]]) 126 127 return True 128 129## SplitModuleType 130# 131# Split ModuleType out of section defien to get key 132# [LibraryClass.Arch.ModuleType|ModuleType|ModuleType] -> [ 133# 'LibraryClass.Arch', ['ModuleType', 'ModuleType', 'ModuleType'] ] 134# 135# @param Key: String to be parsed 136# 137def SplitModuleType(Key): 138 KeyList = Key.split(DataType.TAB_SPLIT) 139 # 140 # Fill in for arch 141 # 142 KeyList.append('') 143 # 144 # Fill in for moduletype 145 # 146 KeyList.append('') 147 ReturnValue = [] 148 KeyValue = KeyList[0] 149 if KeyList[1] != '': 150 KeyValue = KeyValue + DataType.TAB_SPLIT + KeyList[1] 151 ReturnValue.append(KeyValue) 152 ReturnValue.append(GetSplitValueList(KeyList[2])) 153 154 return ReturnValue 155 156## Replace macro in string 157# 158# This method replace macros used in given string. The macros are given in a 159# dictionary. 160# 161# @param String String to be processed 162# @param MacroDefinitions The macro definitions in the form of dictionary 163# @param SelfReplacement To decide whether replace un-defined macro to '' 164# @param Line: The content contain line string and line number 165# @param FileName: The meta-file file name 166# 167def ReplaceMacro(String, MacroDefinitions=None, SelfReplacement=False, Line=None, FileName=None, Flag=False): 168 LastString = String 169 if MacroDefinitions == None: 170 MacroDefinitions = {} 171 while MacroDefinitions: 172 QuotedStringList = [] 173 HaveQuotedMacroFlag = False 174 if not Flag: 175 MacroUsed = gMACRO_PATTERN.findall(String) 176 else: 177 ReQuotedString = re.compile('\"') 178 QuotedStringList = ReQuotedString.split(String) 179 if len(QuotedStringList) >= 3: 180 HaveQuotedMacroFlag = True 181 Count = 0 182 MacroString = "" 183 for QuotedStringItem in QuotedStringList: 184 Count += 1 185 if Count % 2 != 0: 186 MacroString += QuotedStringItem 187 188 if Count == len(QuotedStringList) and Count % 2 == 0: 189 MacroString += QuotedStringItem 190 191 MacroUsed = gMACRO_PATTERN.findall(MacroString) 192 # 193 # no macro found in String, stop replacing 194 # 195 if len(MacroUsed) == 0: 196 break 197 for Macro in MacroUsed: 198 if Macro not in MacroDefinitions: 199 if SelfReplacement: 200 String = String.replace("$(%s)" % Macro, '') 201 Logger.Debug(5, "Delete undefined MACROs in file %s line %d: %s!" % (FileName, Line[1], Line[0])) 202 continue 203 if not HaveQuotedMacroFlag: 204 String = String.replace("$(%s)" % Macro, MacroDefinitions[Macro]) 205 else: 206 Count = 0 207 for QuotedStringItem in QuotedStringList: 208 Count += 1 209 if Count % 2 != 0: 210 QuotedStringList[Count - 1] = QuotedStringList[Count - 1].replace("$(%s)" % Macro, 211 MacroDefinitions[Macro]) 212 elif Count == len(QuotedStringList) and Count % 2 == 0: 213 QuotedStringList[Count - 1] = QuotedStringList[Count - 1].replace("$(%s)" % Macro, 214 MacroDefinitions[Macro]) 215 216 RetString = '' 217 if HaveQuotedMacroFlag: 218 Count = 0 219 for QuotedStringItem in QuotedStringList: 220 Count += 1 221 if Count != len(QuotedStringList): 222 RetString += QuotedStringList[Count - 1] + "\"" 223 else: 224 RetString += QuotedStringList[Count - 1] 225 226 String = RetString 227 228 # 229 # in case there's macro not defined 230 # 231 if String == LastString: 232 break 233 LastString = String 234 235 return String 236 237## NormPath 238# 239# Create a normal path 240# And replace DFEINE in the path 241# 242# @param Path: The input value for Path to be converted 243# @param Defines: A set for DEFINE statement 244# 245def NormPath(Path, Defines=None): 246 IsRelativePath = False 247 if Defines == None: 248 Defines = {} 249 if Path: 250 if Path[0] == '.': 251 IsRelativePath = True 252 # 253 # Replace with Define 254 # 255 if Defines: 256 Path = ReplaceMacro(Path, Defines) 257 # 258 # To local path format 259 # 260 Path = os.path.normpath(Path) 261 262 if IsRelativePath and Path[0] != '.': 263 Path = os.path.join('.', Path) 264 return Path 265 266## CleanString 267# 268# Remove comments in a string 269# Remove spaces 270# 271# @param Line: The string to be cleaned 272# @param CommentCharacter: Comment char, used to ignore comment content, 273# default is DataType.TAB_COMMENT_SPLIT 274# 275def CleanString(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False): 276 # 277 # remove whitespace 278 # 279 Line = Line.strip() 280 # 281 # Replace EDK1's comment character 282 # 283 if AllowCppStyleComment: 284 Line = Line.replace(DataType.TAB_COMMENT_EDK1_SPLIT, CommentCharacter) 285 # 286 # remove comments, but we should escape comment character in string 287 # 288 InString = False 289 for Index in range(0, len(Line)): 290 if Line[Index] == '"': 291 InString = not InString 292 elif Line[Index] == CommentCharacter and not InString: 293 Line = Line[0: Index] 294 break 295 # 296 # remove whitespace again 297 # 298 Line = Line.strip() 299 300 return Line 301 302## CleanString2 303# 304# Split comments in a string 305# Remove spaces 306# 307# @param Line: The string to be cleaned 308# @param CommentCharacter: Comment char, used to ignore comment content, 309# default is DataType.TAB_COMMENT_SPLIT 310# 311def CleanString2(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False): 312 # 313 # remove whitespace 314 # 315 Line = Line.strip() 316 # 317 # Replace EDK1's comment character 318 # 319 if AllowCppStyleComment: 320 Line = Line.replace(DataType.TAB_COMMENT_EDK1_SPLIT, CommentCharacter) 321 # 322 # separate comments and statements 323 # 324 LineParts = Line.split(CommentCharacter, 1) 325 # 326 # remove whitespace again 327 # 328 Line = LineParts[0].strip() 329 if len(LineParts) > 1: 330 Comment = LineParts[1].strip() 331 # 332 # Remove prefixed and trailing comment characters 333 # 334 Start = 0 335 End = len(Comment) 336 while Start < End and Comment.startswith(CommentCharacter, Start, End): 337 Start += 1 338 while End >= 0 and Comment.endswith(CommentCharacter, Start, End): 339 End -= 1 340 Comment = Comment[Start:End] 341 Comment = Comment.strip() 342 else: 343 Comment = '' 344 345 return Line, Comment 346 347## GetMultipleValuesOfKeyFromLines 348# 349# Parse multiple strings to clean comment and spaces 350# The result is saved to KeyValues 351# 352# @param Lines: The content to be parsed 353# @param Key: Reserved 354# @param KeyValues: To store data after parsing 355# @param CommentCharacter: Comment char, used to ignore comment content 356# 357def GetMultipleValuesOfKeyFromLines(Lines, Key, KeyValues, CommentCharacter): 358 if Key: 359 pass 360 if KeyValues: 361 pass 362 Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1] 363 LineList = Lines.split('\n') 364 for Line in LineList: 365 Line = CleanString(Line, CommentCharacter) 366 if Line != '' and Line[0] != CommentCharacter: 367 KeyValues += [Line] 368 return True 369 370## GetDefineValue 371# 372# Parse a DEFINE statement to get defined value 373# DEFINE Key Value 374# 375# @param String: The content to be parsed 376# @param Key: The key of DEFINE statement 377# @param CommentCharacter: Comment char, used to ignore comment content 378# 379def GetDefineValue(String, Key, CommentCharacter): 380 if CommentCharacter: 381 pass 382 String = CleanString(String) 383 return String[String.find(Key + ' ') + len(Key + ' ') : ] 384 385## GetSingleValueOfKeyFromLines 386# 387# Parse multiple strings as below to get value of each definition line 388# Key1 = Value1 389# Key2 = Value2 390# The result is saved to Dictionary 391# 392# @param Lines: The content to be parsed 393# @param Dictionary: To store data after parsing 394# @param CommentCharacter: Comment char, be used to ignore comment content 395# @param KeySplitCharacter: Key split char, between key name and key value. 396# Key1 = Value1, '=' is the key split char 397# @param ValueSplitFlag: Value split flag, be used to decide if has 398# multiple values 399# @param ValueSplitCharacter: Value split char, be used to split multiple 400# values. Key1 = Value1|Value2, '|' is the value 401# split char 402# 403def GetSingleValueOfKeyFromLines(Lines, Dictionary, CommentCharacter, KeySplitCharacter, \ 404 ValueSplitFlag, ValueSplitCharacter): 405 Lines = Lines.split('\n') 406 Keys = [] 407 Value = '' 408 DefineValues = [''] 409 SpecValues = [''] 410 411 for Line in Lines: 412 # 413 # Handle DEFINE and SPEC 414 # 415 if Line.find(DataType.TAB_INF_DEFINES_DEFINE + ' ') > -1: 416 if '' in DefineValues: 417 DefineValues.remove('') 418 DefineValues.append(GetDefineValue(Line, DataType.TAB_INF_DEFINES_DEFINE, CommentCharacter)) 419 continue 420 if Line.find(DataType.TAB_INF_DEFINES_SPEC + ' ') > -1: 421 if '' in SpecValues: 422 SpecValues.remove('') 423 SpecValues.append(GetDefineValue(Line, DataType.TAB_INF_DEFINES_SPEC, CommentCharacter)) 424 continue 425 426 # 427 # Handle Others 428 # 429 LineList = Line.split(KeySplitCharacter, 1) 430 if len(LineList) >= 2: 431 Key = LineList[0].split() 432 if len(Key) == 1 and Key[0][0] != CommentCharacter: 433 # 434 # Remove comments and white spaces 435 # 436 LineList[1] = CleanString(LineList[1], CommentCharacter) 437 if ValueSplitFlag: 438 Value = map(strip, LineList[1].split(ValueSplitCharacter)) 439 else: 440 Value = CleanString(LineList[1], CommentCharacter).splitlines() 441 442 if Key[0] in Dictionary: 443 if Key[0] not in Keys: 444 Dictionary[Key[0]] = Value 445 Keys.append(Key[0]) 446 else: 447 Dictionary[Key[0]].extend(Value) 448 else: 449 Dictionary[DataType.TAB_INF_DEFINES_MACRO][Key[0]] = Value[0] 450 451 if DefineValues == []: 452 DefineValues = [''] 453 if SpecValues == []: 454 SpecValues = [''] 455 Dictionary[DataType.TAB_INF_DEFINES_DEFINE] = DefineValues 456 Dictionary[DataType.TAB_INF_DEFINES_SPEC] = SpecValues 457 458 return True 459 460## The content to be parsed 461# 462# Do pre-check for a file before it is parsed 463# Check $() 464# Check [] 465# 466# @param FileName: Used for error report 467# @param FileContent: File content to be parsed 468# @param SupSectionTag: Used for error report 469# 470def PreCheck(FileName, FileContent, SupSectionTag): 471 if SupSectionTag: 472 pass 473 LineNo = 0 474 IsFailed = False 475 NewFileContent = '' 476 for Line in FileContent.splitlines(): 477 LineNo = LineNo + 1 478 # 479 # Clean current line 480 # 481 Line = CleanString(Line) 482 # 483 # Remove commented line 484 # 485 if Line.find(DataType.TAB_COMMA_SPLIT) == 0: 486 Line = '' 487 # 488 # Check $() 489 # 490 if Line.find('$') > -1: 491 if Line.find('$(') < 0 or Line.find(')') < 0: 492 Logger.Error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=Logger.IS_RAISE_ERROR) 493 # 494 # Check [] 495 # 496 if Line.find('[') > -1 or Line.find(']') > -1: 497 # 498 # Only get one '[' or one ']' 499 # 500 if not (Line.find('[') > -1 and Line.find(']') > -1): 501 Logger.Error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=Logger.IS_RAISE_ERROR) 502 # 503 # Regenerate FileContent 504 # 505 NewFileContent = NewFileContent + Line + '\r\n' 506 507 if IsFailed: 508 Logger.Error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=Logger.IS_RAISE_ERROR) 509 510 return NewFileContent 511 512## CheckFileType 513# 514# Check if the Filename is including ExtName 515# Return True if it exists 516# Raise a error message if it not exists 517# 518# @param CheckFilename: Name of the file to be checked 519# @param ExtName: Ext name of the file to be checked 520# @param ContainerFilename: The container file which describes the file to be 521# checked, used for error report 522# @param SectionName: Used for error report 523# @param Line: The line in container file which defines the file 524# to be checked 525# 526def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo= -1): 527 if CheckFilename != '' and CheckFilename != None: 528 (Root, Ext) = os.path.splitext(CheckFilename) 529 if Ext.upper() != ExtName.upper() and Root: 530 ContainerFile = open(ContainerFilename, 'r').read() 531 if LineNo == -1: 532 LineNo = GetLineNo(ContainerFile, Line) 533 ErrorMsg = ST.ERR_SECTIONNAME_INVALID % (SectionName, CheckFilename, ExtName) 534 Logger.Error("Parser", PARSER_ERROR, ErrorMsg, Line=LineNo, \ 535 File=ContainerFilename, RaiseError=Logger.IS_RAISE_ERROR) 536 537 return True 538 539## CheckFileExist 540# 541# Check if the file exists 542# Return True if it exists 543# Raise a error message if it not exists 544# 545# @param CheckFilename: Name of the file to be checked 546# @param WorkspaceDir: Current workspace dir 547# @param ContainerFilename: The container file which describes the file to 548# be checked, used for error report 549# @param SectionName: Used for error report 550# @param Line: The line in container file which defines the 551# file to be checked 552# 553def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line, LineNo= -1): 554 CheckFile = '' 555 if CheckFilename != '' and CheckFilename != None: 556 CheckFile = WorkspaceFile(WorkspaceDir, CheckFilename) 557 if not os.path.isfile(CheckFile): 558 ContainerFile = open(ContainerFilename, 'r').read() 559 if LineNo == -1: 560 LineNo = GetLineNo(ContainerFile, Line) 561 ErrorMsg = ST.ERR_CHECKFILE_NOTFOUND % (CheckFile, SectionName) 562 Logger.Error("Parser", PARSER_ERROR, ErrorMsg, 563 File=ContainerFilename, Line=LineNo, RaiseError=Logger.IS_RAISE_ERROR) 564 return CheckFile 565 566## GetLineNo 567# 568# Find the index of a line in a file 569# 570# @param FileContent: Search scope 571# @param Line: Search key 572# 573def GetLineNo(FileContent, Line, IsIgnoreComment=True): 574 LineList = FileContent.splitlines() 575 for Index in range(len(LineList)): 576 if LineList[Index].find(Line) > -1: 577 # 578 # Ignore statement in comment 579 # 580 if IsIgnoreComment: 581 if LineList[Index].strip()[0] == DataType.TAB_COMMENT_SPLIT: 582 continue 583 return Index + 1 584 585 return -1 586 587## RaiseParserError 588# 589# Raise a parser error 590# 591# @param Line: String which has error 592# @param Section: Used for error report 593# @param File: File which has the string 594# @param Format: Correct format 595# 596def RaiseParserError(Line, Section, File, Format='', LineNo= -1): 597 if LineNo == -1: 598 LineNo = GetLineNo(open(os.path.normpath(File), 'r').read(), Line) 599 ErrorMsg = ST.ERR_INVALID_NOTFOUND % (Line, Section) 600 if Format != '': 601 Format = "Correct format is " + Format 602 Logger.Error("Parser", PARSER_ERROR, ErrorMsg, File=File, Line=LineNo, \ 603 ExtraData=Format, RaiseError=Logger.IS_RAISE_ERROR) 604 605## WorkspaceFile 606# 607# Return a full path with workspace dir 608# 609# @param WorkspaceDir: Workspace dir 610# @param Filename: Relative file name 611# 612def WorkspaceFile(WorkspaceDir, Filename): 613 return os.path.join(NormPath(WorkspaceDir), NormPath(Filename)) 614 615## Split string 616# 617# Revmove '"' which startswith and endswith string 618# 619# @param String: The string need to be splited 620# 621def SplitString(String): 622 if String.startswith('\"'): 623 String = String[1:] 624 if String.endswith('\"'): 625 String = String[:-1] 626 return String 627 628## Convert To Sql String 629# 630# Replace "'" with "''" in each item of StringList 631# 632# @param StringList: A list for strings to be converted 633# 634def ConvertToSqlString(StringList): 635 return map(lambda s: s.replace("'", "''") , StringList) 636 637## Convert To Sql String 638# 639# Replace "'" with "''" in the String 640# 641# @param String: A String to be converted 642# 643def ConvertToSqlString2(String): 644 return String.replace("'", "''") 645 646## GetStringOfList 647# 648# Get String of a List 649# 650# @param Lines: string list 651# @param Split: split character 652# 653def GetStringOfList(List, Split=' '): 654 if type(List) != type([]): 655 return List 656 Str = '' 657 for Item in List: 658 Str = Str + Item + Split 659 return Str.strip() 660 661## Get HelpTextList 662# 663# Get HelpTextList from HelpTextClassList 664# 665# @param HelpTextClassList: Help Text Class List 666# 667def GetHelpTextList(HelpTextClassList): 668 List = [] 669 if HelpTextClassList: 670 for HelpText in HelpTextClassList: 671 if HelpText.String.endswith('\n'): 672 HelpText.String = HelpText.String[0: len(HelpText.String) - len('\n')] 673 List.extend(HelpText.String.split('\n')) 674 return List 675 676## Get String Array Length 677# 678# Get String Array Length 679# 680# @param String: the source string 681# 682def StringArrayLength(String): 683 if isinstance(String, unicode): 684 return (len(String) + 1) * 2 + 1 685 elif String.startswith('L"'): 686 return (len(String) - 3 + 1) * 2 687 elif String.startswith('"'): 688 return (len(String) - 2 + 1) 689 else: 690 return len(String.split()) + 1 691 692## RemoveDupOption 693# 694# Remove Dup Option 695# 696# @param OptionString: the option string 697# @param Which: Which flag 698# @param Against: Against flag 699# 700def RemoveDupOption(OptionString, Which="/I", Against=None): 701 OptionList = OptionString.split() 702 ValueList = [] 703 if Against: 704 ValueList += Against 705 for Index in range(len(OptionList)): 706 Opt = OptionList[Index] 707 if not Opt.startswith(Which): 708 continue 709 if len(Opt) > len(Which): 710 Val = Opt[len(Which):] 711 else: 712 Val = "" 713 if Val in ValueList: 714 OptionList[Index] = "" 715 else: 716 ValueList.append(Val) 717 return " ".join(OptionList) 718 719## Check if the string is HexDgit 720# 721# Return true if all characters in the string are digits and there is at 722# least one character 723# or valid Hexs (started with 0x, following by hexdigit letters) 724# , false otherwise. 725# @param string: input string 726# 727def IsHexDigit(Str): 728 try: 729 int(Str, 10) 730 return True 731 except ValueError: 732 if len(Str) > 2 and Str.upper().startswith('0X'): 733 try: 734 int(Str, 16) 735 return True 736 except ValueError: 737 return False 738 return False 739 740## Check if the string is HexDgit and its interger value within limit of UINT32 741# 742# Return true if all characters in the string are digits and there is at 743# least one character 744# or valid Hexs (started with 0x, following by hexdigit letters) 745# , false otherwise. 746# @param string: input string 747# 748def IsHexDigitUINT32(Str): 749 try: 750 Value = int(Str, 10) 751 if (Value <= 0xFFFFFFFF) and (Value >= 0): 752 return True 753 except ValueError: 754 if len(Str) > 2 and Str.upper().startswith('0X'): 755 try: 756 Value = int(Str, 16) 757 if (Value <= 0xFFFFFFFF) and (Value >= 0): 758 return True 759 except ValueError: 760 return False 761 return False 762 763## CleanSpecialChar 764# 765# The ASCII text files of type INF, DEC, INI are edited by developers, 766# and may contain characters that cannot be directly translated to strings that 767# are conformant with the UDP XML Schema. Any characters in this category 768# (0x00-0x08, TAB [0x09], 0x0B, 0x0C, 0x0E-0x1F, 0x80-0xFF) 769# must be converted to a space character[0x20] as part of the parsing process. 770# 771def ConvertSpecialChar(Lines): 772 RetLines = [] 773 for line in Lines: 774 ReMatchSpecialChar = re.compile(r"[\x00-\x08]|\x09|\x0b|\x0c|[\x0e-\x1f]|[\x7f-\xff]") 775 RetLines.append(ReMatchSpecialChar.sub(' ', line)) 776 777 return RetLines 778 779## __GetTokenList 780# 781# Assume Str is a valid feature flag expression. 782# Return a list which contains tokens: alpha numeric token and other token 783# Whitespace are not stripped 784# 785def __GetTokenList(Str): 786 InQuote = False 787 Token = '' 788 TokenOP = '' 789 PreChar = '' 790 List = [] 791 for Char in Str: 792 if InQuote: 793 Token += Char 794 if Char == '"' and PreChar != '\\': 795 InQuote = not InQuote 796 List.append(Token) 797 Token = '' 798 continue 799 if Char == '"': 800 if Token and Token != 'L': 801 List.append(Token) 802 Token = '' 803 if TokenOP: 804 List.append(TokenOP) 805 TokenOP = '' 806 InQuote = not InQuote 807 Token += Char 808 continue 809 810 if not (Char.isalnum() or Char in '_'): 811 TokenOP += Char 812 if Token: 813 List.append(Token) 814 Token = '' 815 else: 816 Token += Char 817 if TokenOP: 818 List.append(TokenOP) 819 TokenOP = '' 820 821 if PreChar == '\\' and Char == '\\': 822 PreChar = '' 823 else: 824 PreChar = Char 825 if Token: 826 List.append(Token) 827 if TokenOP: 828 List.append(TokenOP) 829 return List 830 831## ConvertNEToNOTEQ 832# 833# Convert NE operator to NOT EQ 834# For example: 1 NE 2 -> 1 NOT EQ 2 835# 836# @param Expr: Feature flag expression to be converted 837# 838def ConvertNEToNOTEQ(Expr): 839 List = __GetTokenList(Expr) 840 for Index in range(len(List)): 841 if List[Index] == 'NE': 842 List[Index] = 'NOT EQ' 843 return ''.join(List) 844 845## ConvertNOTEQToNE 846# 847# Convert NOT EQ operator to NE 848# For example: 1 NOT NE 2 -> 1 NE 2 849# 850# @param Expr: Feature flag expression to be converted 851# 852def ConvertNOTEQToNE(Expr): 853 List = __GetTokenList(Expr) 854 HasNOT = False 855 RetList = [] 856 for Token in List: 857 if HasNOT and Token == 'EQ': 858 # At least, 'NOT' is in the list 859 while not RetList[-1].strip(): 860 RetList.pop() 861 RetList[-1] = 'NE' 862 HasNOT = False 863 continue 864 if Token == 'NOT': 865 HasNOT = True 866 elif Token.strip(): 867 HasNOT = False 868 RetList.append(Token) 869 870 return ''.join(RetList) 871 872## SplitPcdEntry 873# 874# Split an PCD entry string to Token.CName and PCD value and FFE. 875# NOTE: PCD Value and FFE can contain "|" in it's expression. And in INF specification, have below rule. 876# When using the characters "|" or "||" in an expression, the expression must be encapsulated in 877# open "(" and close ")" parenthesis. 878# 879# @param String An PCD entry string need to be split. 880# 881# @return List [PcdTokenCName, Value, FFE] 882# 883def SplitPcdEntry(String): 884 if not String: 885 return ['', '', ''], False 886 887 PcdTokenCName = '' 888 PcdValue = '' 889 PcdFeatureFlagExp = '' 890 891 ValueList = GetSplitValueList(String, "|", 1) 892 893 # 894 # Only contain TokenCName 895 # 896 if len(ValueList) == 1: 897 return [ValueList[0]], True 898 899 NewValueList = [] 900 901 if len(ValueList) == 2: 902 PcdTokenCName = ValueList[0] 903 904 InQuote = False 905 InParenthesis = False 906 StrItem = '' 907 for StrCh in ValueList[1]: 908 if StrCh == '"': 909 InQuote = not InQuote 910 elif StrCh == '(' or StrCh == ')': 911 InParenthesis = not InParenthesis 912 913 if StrCh == '|': 914 if not InQuote or not InParenthesis: 915 NewValueList.append(StrItem.strip()) 916 StrItem = ' ' 917 continue 918 919 StrItem += StrCh 920 921 NewValueList.append(StrItem.strip()) 922 923 if len(NewValueList) == 1: 924 PcdValue = NewValueList[0] 925 return [PcdTokenCName, PcdValue], True 926 elif len(NewValueList) == 2: 927 PcdValue = NewValueList[0] 928 PcdFeatureFlagExp = NewValueList[1] 929 return [PcdTokenCName, PcdValue, PcdFeatureFlagExp], True 930 else: 931 return ['', '', ''], False 932 933 return ['', '', ''], False 934 935## Check if two arches matched? 936# 937# @param Arch1 938# @param Arch2 939# 940def IsMatchArch(Arch1, Arch2): 941 if 'COMMON' in Arch1 or 'COMMON' in Arch2: 942 return True 943 if isinstance(Arch1, basestring) and isinstance(Arch2, basestring): 944 if Arch1 == Arch2: 945 return True 946 947 if isinstance(Arch1, basestring) and isinstance(Arch2, list): 948 return Arch1 in Arch2 949 950 if isinstance(Arch2, basestring) and isinstance(Arch1, list): 951 return Arch2 in Arch1 952 953 if isinstance(Arch1, list) and isinstance(Arch2, list): 954 for Item1 in Arch1: 955 for Item2 in Arch2: 956 if Item1 == Item2: 957 return True 958 959 return False 960 961# Search all files in FilePath to find the FileName with the largest index 962# Return the FileName with index +1 under the FilePath 963# 964def GetUniFileName(FilePath, FileName): 965 Files = [] 966 try: 967 Files = os.listdir(FilePath) 968 except: 969 pass 970 971 LargestIndex = -1 972 for File in Files: 973 if File.upper().startswith(FileName.upper()) and File.upper().endswith('.UNI'): 974 Index = File.upper().replace(FileName.upper(), '').replace('.UNI', '') 975 if Index: 976 try: 977 Index = int(Index) 978 except Exception: 979 Index = -1 980 else: 981 Index = 0 982 if Index > LargestIndex: 983 LargestIndex = Index + 1 984 985 if LargestIndex > -1: 986 return os.path.normpath(os.path.join(FilePath, FileName + str(LargestIndex) + '.uni')) 987 else: 988 return os.path.normpath(os.path.join(FilePath, FileName + '.uni')) 989