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