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