1 ## @file 2 # This file is used to define common string related functions used in parsing 3 # process 4 # 5 # Copyright (c) 2011 - 2014, 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 ''' 16 String 17 ''' 18 ## 19 # Import Modules 20 # 21 import re 22 import os.path 23 from string import strip 24 import Logger.Log as Logger 25 import Library.DataType as DataType 26 from Logger.ToolError import FORMAT_INVALID 27 from Logger.ToolError import PARSER_ERROR 28 from Logger import StringTable as ST 29 30 # 31 # Regular expression for matching macro used in DSC/DEC/INF file inclusion 32 # 33 gMACRO_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 # 46 def 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 # 58 def 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 # 74 def 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 # 94 def 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 # 114 def 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 # 137 def 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 # 167 def 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 # 245 def 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 # 275 def 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 # 311 def 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 # 357 def 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 # 379 def 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 # 403 def 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 # 470 def 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 # 526 def 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 # 553 def 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 # 573 def 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 # 596 def 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 # 612 def 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 # 621 def 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 # 634 def 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 # 643 def 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 # 653 def 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 # 667 def 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 # 682 def 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 # 700 def 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 # 727 def 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 # 748 def 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 # 771 def 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 # 785 def __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 # 838 def 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 # 852 def 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 # 883 def 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 # 940 def 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