2 # Common routines used by all tools
4 # Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
6 # This program and the accompanying materials are licensed and made available
7 # under the terms and conditions of the BSD License which accompanies this
8 # distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25 from os
import makedirs
28 from os
import listdir
31 from os
import linesep
33 from os
import environ
35 from collections
import OrderedDict
as Sdict
37 import Logger
.Log
as Logger
38 from Logger
import StringTable
as ST
39 from Logger
import ToolError
40 from Library
import GlobalData
41 from Library
.DataType
import SUP_MODULE_LIST
42 from Library
.DataType
import END_OF_LINE
43 from Library
.DataType
import TAB_SPLIT
44 from Library
.DataType
import TAB_LANGUAGE_EN_US
45 from Library
.DataType
import TAB_LANGUAGE_EN
46 from Library
.DataType
import TAB_LANGUAGE_EN_X
47 from Library
.DataType
import TAB_UNI_FILE_SUFFIXS
48 from Library
.StringUtils
import GetSplitValueList
49 from Library
.ParserValidate
import IsValidHexVersion
50 from Library
.ParserValidate
import IsValidPath
51 from Object
.POM
.CommonObject
import TextObject
52 from Core
.FileHook
import __FileHookOpen__
53 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
55 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C
58 # @param Guid: The GUID string
60 def GuidStringToGuidStructureString(Guid
):
61 GuidList
= Guid
.split('-')
63 for Index
in range(0, 3, 1):
64 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
65 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
66 for Index
in range(0, 12, 2):
67 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
71 ## Check whether GUID string is of format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
73 # @param GuidValue: The GUID value
75 def CheckGuidRegFormat(GuidValue
):
76 ## Regular expression used to find out register format of GUID
78 RegFormatGuidPattern
= re
.compile("^\s*([0-9a-fA-F]){8}-"
82 "([0-9a-fA-F]){12}\s*$")
84 if RegFormatGuidPattern
.match(GuidValue
):
90 ## Convert GUID string in C structure style to
91 # xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
93 # @param GuidValue: The GUID value in C structure format
95 def GuidStructureStringToGuidString(GuidValue
):
96 GuidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").\
97 replace(" ", "").replace(";", "")
98 GuidValueList
= GuidValueString
.split(",")
99 if len(GuidValueList
) != 11:
102 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
103 int(GuidValueList
[0], 16),
104 int(GuidValueList
[1], 16),
105 int(GuidValueList
[2], 16),
106 int(GuidValueList
[3], 16),
107 int(GuidValueList
[4], 16),
108 int(GuidValueList
[5], 16),
109 int(GuidValueList
[6], 16),
110 int(GuidValueList
[7], 16),
111 int(GuidValueList
[8], 16),
112 int(GuidValueList
[9], 16),
113 int(GuidValueList
[10], 16)
115 except BaseException
:
118 ## Create directories
120 # @param Directory: The directory name
122 def CreateDirectory(Directory
):
123 if Directory
is None or Directory
.strip() == "":
126 if not access(Directory
, F_OK
):
128 except BaseException
:
132 ## Remove directories, including files and sub-directories in it
134 # @param Directory: The directory name
136 def RemoveDirectory(Directory
, Recursively
=False):
137 if Directory
is None or Directory
.strip() == "" or not \
138 os
.path
.exists(Directory
):
141 CurrentDirectory
= getcwd()
143 for File
in listdir("."):
144 if os
.path
.isdir(File
):
145 RemoveDirectory(File
, Recursively
)
148 chdir(CurrentDirectory
)
151 ## Store content in file
153 # This method is used to save file only when its content is changed. This is
154 # quite useful for "make" system to decide what will be re-built and what
157 # @param File: The path of file
158 # @param Content: The new content of the file
159 # @param IsBinaryFile: The flag indicating if the file is binary file
162 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
163 if os
.path
.exists(File
):
166 if Content
== __FileHookOpen__(File
, "rb").read():
168 except BaseException
:
169 Logger
.Error(None, ToolError
.FILE_OPEN_FAILURE
, ExtraData
=File
)
172 if Content
== __FileHookOpen__(File
, "r").read():
174 except BaseException
:
175 Logger
.Error(None, ToolError
.FILE_OPEN_FAILURE
, ExtraData
=File
)
177 CreateDirectory(os
.path
.dirname(File
))
180 FileFd
= __FileHookOpen__(File
, "wb")
181 FileFd
.write(Content
)
183 except BaseException
:
184 Logger
.Error(None, ToolError
.FILE_CREATE_FAILURE
, ExtraData
=File
)
187 FileFd
= __FileHookOpen__(File
, "w")
188 FileFd
.write(Content
)
190 except BaseException
:
191 Logger
.Error(None, ToolError
.FILE_CREATE_FAILURE
, ExtraData
=File
)
195 ## Get all files of a directory
197 # @param Root: Root dir
198 # @param SkipList : The files need be skipped
200 def GetFiles(Root
, SkipList
=None, FullPath
=True):
201 OriPath
= os
.path
.normpath(Root
)
203 for Root
, Dirs
, Files
in walk(Root
):
205 for Item
in SkipList
:
211 if Dir
.startswith('.'):
215 if File
.startswith('.'):
217 File
= os
.path
.normpath(os
.path
.join(Root
, File
))
219 File
= File
[len(OriPath
) + 1:]
220 FileList
.append(File
)
224 ## Get all non-metadata files of a directory
226 # @param Root: Root Dir
227 # @param SkipList : List of path need be skipped
228 # @param FullPath: True if the returned file should be full path
229 # @param PrefixPath: the path that need to be added to the files found
230 # @return: the list of files found
232 def GetNonMetaDataFiles(Root
, SkipList
, FullPath
, PrefixPath
):
233 FileList
= GetFiles(Root
, SkipList
, FullPath
)
235 for File
in FileList
:
236 ExtName
= os
.path
.splitext(File
)[1]
238 # skip '.dec', '.inf', '.dsc', '.fdf' files
240 if ExtName
.lower() not in ['.dec', '.inf', '.dsc', '.fdf']:
241 NewFileList
.append(os
.path
.normpath(os
.path
.join(PrefixPath
, File
)))
245 ## Check if given file exists or not
247 # @param File: File name or path to be checked
248 # @param Dir: The directory the file is relative to
250 def ValidFile(File
, Ext
=None):
251 File
= File
.replace('\\', '/')
253 FileExt
= os
.path
.splitext(File
)[1]
254 if FileExt
.lower() != Ext
.lower():
256 if not os
.path
.exists(File
):
262 # @param File: File name or path to be checked
263 # @param Dir: The directory the file is relative to
264 # @param OverrideDir: The override directory
266 def RealPath(File
, Dir
='', OverrideDir
=''):
267 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
268 NewFile
= GlobalData
.gALL_FILES
[NewFile
]
269 if not NewFile
and OverrideDir
:
270 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
271 NewFile
= GlobalData
.gALL_FILES
[NewFile
]
276 # @param File: File name or path to be checked
277 # @param Dir: The directory the file is relative to
278 # @param OverrideDir: The override directory
280 def RealPath2(File
, Dir
='', OverrideDir
=''):
282 NewFile
= GlobalData
.gALL_FILES
[os
.path
.normpath(os
.path
.join\
283 (OverrideDir
, File
))]
285 if OverrideDir
[-1] == os
.path
.sep
:
286 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
288 return NewFile
[len(OverrideDir
) + 1:], \
289 NewFile
[0:len(OverrideDir
)]
291 NewFile
= GlobalData
.gALL_FILES
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
294 if Dir
[-1] == os
.path
.sep
:
295 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
297 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
305 # @param PathList: PathList
307 def CommonPath(PathList
):
308 Path1
= min(PathList
).split(os
.path
.sep
)
309 Path2
= max(PathList
).split(os
.path
.sep
)
310 for Index
in range(min(len(Path1
), len(Path2
))):
311 if Path1
[Index
] != Path2
[Index
]:
312 return os
.path
.sep
.join(Path1
[:Index
])
313 return os
.path
.sep
.join(Path1
)
317 class PathClass(object):
318 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
319 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', \
322 self
.File
= str(File
)
323 if os
.path
.isabs(self
.File
):
327 self
.Root
= str(Root
)
328 self
.AlterRoot
= str(AlterRoot
)
331 # Remove any '.' and '..' in path
334 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
335 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
337 # eliminate the side-effect of 'C:'
339 if self
.Root
[-1] == ':':
340 self
.Root
+= os
.path
.sep
342 # file path should not start with path separator
344 if self
.Root
[-1] == os
.path
.sep
:
345 self
.File
= self
.Path
[len(self
.Root
):]
347 self
.File
= self
.Path
[len(self
.Root
) + 1:]
349 self
.Path
= os
.path
.normpath(self
.File
)
351 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
352 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
356 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
360 self
.Dir
= self
.SubDir
365 self
.Type
= self
.Ext
.lower()
367 self
.IsBinary
= IsBinary
369 self
.TagName
= TagName
370 self
.ToolCode
= ToolCode
371 self
.ToolChainFamily
= ToolChainFamily
375 ## Convert the object of this class to a string
377 # Convert member Path of the class to a string
382 ## Override __eq__ function
384 # Check whether PathClass are the same
386 def __eq__(self
, Other
):
387 if isinstance(Other
, type(self
)):
388 return self
.Path
== Other
.Path
390 return self
.Path
== str(Other
)
392 ## Override __hash__ function
394 # Use Path as key in hash table
397 return hash(self
.Path
)
401 def _GetFileKey(self
):
402 if self
._Key
is None:
403 self
._Key
= self
.Path
.upper()
407 def Validate(self
, Type
='', CaseSensitive
=True):
408 if GlobalData
.gCASE_INSENSITIVE
:
409 CaseSensitive
= False
410 if Type
and Type
.lower() != self
.Type
:
411 return ToolError
.FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % \
412 (self
.File
, Type
, self
.Type
)
414 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
415 if not RealRoot
and not RealFile
:
418 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
420 RealFile
= os
.path
.join(self
.Root
, self
.File
)
421 return ToolError
.FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
425 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
426 if CaseSensitive
and (RealFile
!= self
.File
or \
427 (RealRoot
!= self
.Root
and RealRoot
!= \
429 ErrorCode
= ToolError
.FILE_CASE_MISMATCH
430 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ \
433 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
434 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
436 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
441 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
442 return ErrorCode
, ErrorInfo
444 Key
= property(_GetFileKey
)
446 ## Get current workspace
448 # get WORKSPACE from environment variable if present,if not use current working directory as WORKSPACE
454 if "WORKSPACE" in environ
:
455 WorkspaceDir
= os
.path
.normpath(environ
["WORKSPACE"])
456 if not os
.path
.exists(WorkspaceDir
):
458 ToolError
.UPT_ENVIRON_MISSING_ERROR
,
459 ST
.ERR_WORKSPACE_NOTEXIST
,
460 ExtraData
="%s" % WorkspaceDir
)
462 WorkspaceDir
= os
.getcwd()
464 if WorkspaceDir
[-1] == ':':
465 WorkspaceDir
+= os
.sep
467 PackagesPath
= os
.environ
.get("PACKAGES_PATH")
468 mws
.setWs(WorkspaceDir
, PackagesPath
)
470 return WorkspaceDir
, mws
.PACKAGES_PATH
474 # use full path and workspace to get relative path
475 # the destination of this function is mainly to resolve the root path issue(like c: or c:\)
477 # @param Fullpath: a string of fullpath
478 # @param Workspace: a string of workspace
480 def GetRelativePath(Fullpath
, Workspace
):
483 if Workspace
.endswith(os
.sep
):
484 RelativePath
= Fullpath
[Fullpath
.upper().find(Workspace
.upper())+len(Workspace
):]
486 RelativePath
= Fullpath
[Fullpath
.upper().find(Workspace
.upper())+len(Workspace
)+1:]
490 ## Check whether all module types are in list
492 # check whether all module types (SUP_MODULE_LIST) are in list
494 # @param ModuleList: a list of ModuleType
496 def IsAllModuleList(ModuleList
):
497 NewModuleList
= [Module
.upper() for Module
in ModuleList
]
498 for Module
in SUP_MODULE_LIST
:
499 if Module
not in NewModuleList
:
504 ## Dictionary that use comment(GenericComment, TailComment) as value,
505 # if a new comment which key already in the dic is inserted, then the
506 # comment will be merged.
507 # Key is (Statement, SupArch), when TailComment is added, it will ident
508 # according to Statement
510 class MergeCommentDict(dict):
513 def __setitem__(self
, Key
, CommentVal
):
514 GenericComment
, TailComment
= CommentVal
516 OrigVal1
, OrigVal2
= dict.__getitem
__(self
, Key
)
518 dict.__setitem
__(self
, Key
, (OrigVal1
+ GenericComment
, OrigVal2 \
519 + len(Statement
) * ' ' + TailComment
))
521 dict.__setitem
__(self
, Key
, (GenericComment
, TailComment
))
525 def __getitem__(self
, Key
):
526 return dict.__getitem
__(self
, Key
)
529 ## GenDummyHelpTextObj
531 # @retval HelpTxt: Generated dummy help text object
533 def GenDummyHelpTextObj():
534 HelpTxt
= TextObject()
535 HelpTxt
.SetLang(TAB_LANGUAGE_EN_US
)
536 HelpTxt
.SetString(' ')
539 ## ConvertVersionToDecimal, the minor version should be within 0 - 99
540 # <HexVersion> ::= "0x" <Major> <Minor>
541 # <Major> ::= (a-fA-F0-9){4}
542 # <Minor> ::= (a-fA-F0-9){4}
543 # <DecVersion> ::= (0-65535) ["." (0-99)]
545 # @param StringIn: The string contains version defined in INF file.
546 # It can be Decimal or Hex
548 def ConvertVersionToDecimal(StringIn
):
549 if IsValidHexVersion(StringIn
):
550 Value
= int(StringIn
, 16)
552 Minor
= Value
& 0xFFFF
553 MinorStr
= str(Minor
)
554 if len(MinorStr
) == 1:
555 MinorStr
= '0' + MinorStr
556 return str(Major
) + '.' + MinorStr
558 if StringIn
.find(TAB_SPLIT
) != -1:
561 return StringIn
+ '.0'
564 # when StringIn is '', return it directly
568 ## GetHelpStringByRemoveHashKey
570 # Remove hash key at the header of string and return the remain.
572 # @param String: The string need to be processed.
574 def GetHelpStringByRemoveHashKey(String
):
576 PattenRemoveHashKey
= re
.compile(r
"^[#+\s]+", re
.DOTALL
)
577 String
= String
.strip()
581 LineList
= GetSplitValueList(String
, END_OF_LINE
)
582 for Line
in LineList
:
583 ValueList
= PattenRemoveHashKey
.split(Line
)
584 if len(ValueList
) == 1:
585 ReturnString
+= ValueList
[0] + END_OF_LINE
587 ReturnString
+= ValueList
[1] + END_OF_LINE
589 if ReturnString
.endswith('\n') and not ReturnString
.endswith('\n\n') and ReturnString
!= '\n':
590 ReturnString
= ReturnString
[:-1]
594 ## ConvPathFromAbsToRel
596 # Get relative file path from absolute path.
598 # @param Path: The string contain file absolute path.
599 # @param Root: The string contain the parent path of Path in.
602 def ConvPathFromAbsToRel(Path
, Root
):
603 Path
= os
.path
.normpath(Path
)
604 Root
= os
.path
.normpath(Root
)
605 FullPath
= os
.path
.normpath(os
.path
.join(Root
, Path
))
608 # If Path is absolute path.
609 # It should be in Root.
611 if os
.path
.isabs(Path
):
612 return FullPath
[FullPath
.find(Root
) + len(Root
) + 1:]
619 # Convert special characters to '_', '\' to '/'
620 # return converted path: Test!1.inf -> Test_1.inf
622 # @param Path: Path to be converted
624 def ConvertPath(Path
):
626 for Char
in Path
.strip():
627 if Char
.isalnum() or Char
in '.-_/':
628 RetPath
= RetPath
+ Char
630 RetPath
= RetPath
+ '/'
632 RetPath
= RetPath
+ '_'
637 # during install, convert the Spec string extract from UPD into INF allowable definition,
638 # the difference is period is allowed in the former (not the first letter) but not in the latter.
639 # return converted Spec string
641 # @param SpecStr: SpecStr to be converted
643 def ConvertSpec(SpecStr
):
646 if Char
.isalnum() or Char
== '_':
647 RetStr
= RetStr
+ Char
649 RetStr
= RetStr
+ '_'
656 # Judge two lists are identical(contain same item).
657 # The rule is elements in List A are in List B and elements in List B are in List A.
659 # @param ListA, ListB Lists need to be judged.
661 # @return True ListA and ListB are identical
662 # @return False ListA and ListB are different with each other
664 def IsEqualList(ListA
, ListB
):
669 if not ItemA
in ListB
:
673 if not ItemB
in ListA
:
680 # Convert item in ArchList if the start character is lower case.
681 # In UDP spec, Arch is only allowed as: [A-Z]([a-zA-Z0-9])*
683 # @param ArchList The ArchList need to be converted.
685 # @return NewList The ArchList been converted.
687 def ConvertArchList(ArchList
):
692 if isinstance(ArchList
, list):
693 for Arch
in ArchList
:
695 NewArchList
.append(Arch
)
696 elif isinstance(ArchList
, str):
697 ArchList
= ArchList
.upper()
698 NewArchList
.append(ArchList
)
702 ## ProcessLineExtender
704 # Process the LineExtender of Line in LineList.
705 # If one line ends with a line extender, then it will be combined together with next line.
707 # @param LineList The LineList need to be processed.
709 # @return NewList The ArchList been processed.
711 def ProcessLineExtender(LineList
):
714 while Count
< len(LineList
):
715 if LineList
[Count
].strip().endswith("\\") and Count
+ 1 < len(LineList
):
716 NewList
.append(LineList
[Count
].strip()[:-2] + LineList
[Count
+ 1])
719 NewList
.append(LineList
[Count
])
727 # Process EDK style comment in LineList: c style /* */ comment or cpp style // comment
730 # @param LineList The LineList need to be processed.
732 # @return LineList The LineList been processed.
733 # @return FirstPos Where Edk comment is first found, -1 if not found
735 def ProcessEdkComment(LineList
):
736 FindEdkBlockComment
= False
742 while(Count
< len(LineList
)):
743 Line
= LineList
[Count
].strip()
744 if Line
.startswith("/*"):
746 # handling c style comment
749 while Count
< len(LineList
):
750 Line
= LineList
[Count
].strip()
751 if Line
.endswith("*/"):
752 if (Count
== StartPos
) and Line
.strip() == '/*/':
756 FindEdkBlockComment
= True
760 if FindEdkBlockComment
:
763 for Index
in range(StartPos
, EndPos
+1):
765 FindEdkBlockComment
= False
766 elif Line
.find("//") != -1 and not Line
.startswith("#"):
768 # handling cpp style comment
770 LineList
[Count
] = Line
.replace("//", '#')
776 return LineList
, FirstPos
778 ## GetLibInstanceInfo
780 # Get the information from Library Instance INF file.
782 # @param string. A string start with # and followed by INF file path
783 # @param WorkSpace. The WorkSpace directory used to combined with INF file path.
785 # @return GUID, Version
786 def GetLibInstanceInfo(String
, WorkSpace
, LineNo
):
791 OriginalString
= String
792 String
= String
.strip()
796 # Remove "#" characters at the beginning
798 String
= GetHelpStringByRemoveHashKey(String
)
799 String
= String
.strip()
802 # Validate file name exist.
804 FullFileName
= os
.path
.normpath(os
.path
.realpath(os
.path
.join(WorkSpace
, String
)))
805 if not (ValidFile(FullFileName
)):
806 Logger
.Error("InfParser",
807 ToolError
.FORMAT_INVALID
,
808 ST
.ERR_FILELIST_EXIST
% (String
),
809 File
=GlobalData
.gINF_MODULE_NAME
,
811 ExtraData
=OriginalString
)
814 # Validate file exist/format.
816 if IsValidPath(String
, WorkSpace
):
817 IsValidFileFlag
= True
819 Logger
.Error("InfParser",
820 ToolError
.FORMAT_INVALID
,
821 ST
.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID
% (String
),
822 File
=GlobalData
.gINF_MODULE_NAME
,
824 ExtraData
=OriginalString
)
830 FInputfile
= open(FullFileName
, "r")
832 FileLinesList
= FInputfile
.readlines()
833 except BaseException
:
834 Logger
.Error("InfParser",
835 ToolError
.FILE_READ_FAILURE
,
836 ST
.ERR_FILE_OPEN_FAILURE
,
840 except BaseException
:
841 Logger
.Error("InfParser",
842 ToolError
.FILE_READ_FAILURE
,
843 ST
.ERR_FILE_OPEN_FAILURE
,
846 ReFileGuidPattern
= re
.compile("^\s*FILE_GUID\s*=.*$")
847 ReVerStringPattern
= re
.compile("^\s*VERSION_STRING\s*=.*$")
849 FileLinesList
= ProcessLineExtender(FileLinesList
)
851 for Line
in FileLinesList
:
852 if ReFileGuidPattern
.match(Line
):
853 FileGuidString
= Line
854 if ReVerStringPattern
.match(Line
):
858 FileGuidString
= GetSplitValueList(FileGuidString
, '=', 1)[1]
860 VerString
= GetSplitValueList(VerString
, '=', 1)[1]
862 return FileGuidString
, VerString
866 # Generate the local value for INF and DEC file. If Lang attribute not present, then use this value.
867 # If present, and there is no element without the Lang attribute, and one of the elements has the rfc1766 code is
868 # "en-x-tianocore", or "en-US" if "en-x-tianocore" was not found, or "en" if "en-US" was not found, or startswith 'en'
869 # if 'en' was not found, then use this value.
870 # If multiple entries of a tag exist which have the same language code, use the last entry.
872 # @param ValueList A list need to be processed.
873 # @param UseFirstValue: True to use the first value, False to use the last value
876 def GetLocalValue(ValueList
, UseFirstValue
=False):
882 for (Key
, Value
) in ValueList
:
883 if Key
== TAB_LANGUAGE_EN_X
:
889 if Key
== TAB_LANGUAGE_EN_US
:
895 if Key
== TAB_LANGUAGE_EN
:
901 if Key
.startswith(TAB_LANGUAGE_EN
):
928 ## GetCharIndexOutStr
930 # Get comment character index outside a string
932 # @param Line: The string to be checked
933 # @param CommentCharacter: Comment char, used to ignore comment content
937 def GetCharIndexOutStr(CommentCharacter
, Line
):
944 # Check whether comment character is in a string
947 for Index
in range(0, len(Line
)):
948 if Line
[Index
] == '"':
949 InString
= not InString
950 elif Line
[Index
] == CommentCharacter
and InString
:
952 elif Line
[Index
] == CommentCharacter
and (Index
+1) < len(Line
) and Line
[Index
+1] == CommentCharacter \
957 ## ValidateUNIFilePath
959 # Check the UNI file path
961 # @param FilePath: The UNI file path
963 def ValidateUNIFilePath(Path
):
964 Suffix
= Path
[Path
.rfind(TAB_SPLIT
):]
967 # Check if the suffix is one of the '.uni', '.UNI', '.Uni'
969 if Suffix
not in TAB_UNI_FILE_SUFFIXS
:
970 Logger
.Error("Unicode File Parser",
971 ToolError
.FORMAT_INVALID
,
972 Message
=ST
.ERR_UNI_FILE_SUFFIX_WRONG
,
976 # Check if '..' in the file name(without suffix)
978 if (TAB_SPLIT
+ TAB_SPLIT
) in Path
:
979 Logger
.Error("Unicode File Parser",
980 ToolError
.FORMAT_INVALID
,
981 Message
=ST
.ERR_UNI_FILE_NAME_INVALID
,
985 # Check if the file name is valid according to the DEC and INF specification
987 Pattern
= '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*'
988 FileName
= Path
.replace(Suffix
, '')
989 InvalidCh
= re
.sub(Pattern
, '', FileName
)
991 Logger
.Error("Unicode File Parser",
992 ToolError
.FORMAT_INVALID
,
993 Message
=ST
.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID
,