Home | History | Annotate | Download | only in Library
      1 ## @file

      2 # Common routines used by all tools

      3 #

      4 # Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>

      5 #

      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

     10 #

     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.

     13 #

     14 
     15 '''
     16 Misc
     17 '''
     18 
     19 ##

     20 # Import Modules

     21 #

     22 import os.path
     23 from os import access
     24 from os import F_OK
     25 from os import makedirs
     26 from os import getcwd
     27 from os import chdir
     28 from os import listdir
     29 from os import remove
     30 from os import rmdir
     31 from os import linesep
     32 from os import walk
     33 from os import environ
     34 import re
     35 from UserDict import IterableUserDict
     36 
     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.String 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
     54 
     55 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C 

     56 # structure style

     57 #

     58 # @param      Guid:    The GUID string

     59 #

     60 def GuidStringToGuidStructureString(Guid):
     61     GuidList = Guid.split('-')
     62     Result = '{'
     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]
     68     Result += '}}'
     69     return Result
     70 
     71 ## Check whether GUID string is of format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

     72 #

     73 # @param      GuidValue:   The GUID value

     74 #

     75 def CheckGuidRegFormat(GuidValue):
     76     ## Regular expression used to find out register format of GUID

     77     #

     78     RegFormatGuidPattern = re.compile("^\s*([0-9a-fA-F]){8}-"
     79                                        "([0-9a-fA-F]){4}-"
     80                                        "([0-9a-fA-F]){4}-"
     81                                        "([0-9a-fA-F]){4}-"
     82                                        "([0-9a-fA-F]){12}\s*$")
     83 
     84     if RegFormatGuidPattern.match(GuidValue):
     85         return True
     86     else:
     87         return False
     88 
     89 
     90 ## Convert GUID string in C structure style to 

     91 # xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

     92 #

     93 # @param      GuidValue:   The GUID value in C structure format

     94 #

     95 def GuidStructureStringToGuidString(GuidValue):
     96     GuidValueString = GuidValue.lower().replace("{", "").replace("}", "").\
     97     replace(" ", "").replace(";", "")
     98     GuidValueList = GuidValueString.split(",")
     99     if len(GuidValueList) != 11:
    100         return ''
    101     try:
    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)
    114                 )
    115     except BaseException:
    116         return ''
    117 
    118 ## Create directories

    119 #

    120 # @param      Directory:   The directory name

    121 #

    122 def CreateDirectory(Directory):
    123     if Directory == None or Directory.strip() == "":
    124         return True
    125     try:
    126         if not access(Directory, F_OK):
    127             makedirs(Directory)
    128     except BaseException:
    129         return False
    130     return True
    131 
    132 ## Remove directories, including files and sub-directories in it

    133 #

    134 # @param      Directory:   The directory name

    135 #

    136 def RemoveDirectory(Directory, Recursively=False):
    137     if Directory == None or Directory.strip() == "" or not \
    138     os.path.exists(Directory):
    139         return
    140     if Recursively:
    141         CurrentDirectory = getcwd()
    142         chdir(Directory)
    143         for File in listdir("."):
    144             if os.path.isdir(File):
    145                 RemoveDirectory(File, Recursively)
    146             else:
    147                 remove(File)
    148         chdir(CurrentDirectory)
    149     rmdir(Directory)
    150 
    151 ## Store content in file

    152 #

    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 

    155 # won't.

    156 #

    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 

    160 #                              or not

    161 #

    162 def SaveFileOnChange(File, Content, IsBinaryFile=True):
    163     if not IsBinaryFile:
    164         Content = Content.replace("\n", linesep)
    165 
    166     if os.path.exists(File):
    167         try:
    168             if Content == __FileHookOpen__(File, "rb").read():
    169                 return False
    170         except BaseException:
    171             Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File)
    172 
    173     CreateDirectory(os.path.dirname(File))
    174     try:
    175         FileFd = __FileHookOpen__(File, "wb")
    176         FileFd.write(Content)
    177         FileFd.close()
    178     except BaseException:
    179         Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File)
    180 
    181     return True
    182 
    183 ## Get all files of a directory

    184 #

    185 # @param Root:       Root dir

    186 # @param SkipList :  The files need be skipped

    187 #

    188 def GetFiles(Root, SkipList=None, FullPath=True):
    189     OriPath = os.path.normpath(Root)
    190     FileList = []
    191     for Root, Dirs, Files in walk(Root):
    192         if SkipList:
    193             for Item in SkipList:
    194                 if Item in Dirs:
    195                     Dirs.remove(Item)
    196                 if Item in Files:
    197                     Files.remove(Item)
    198         for Dir in Dirs:
    199             if Dir.startswith('.'):
    200                 Dirs.remove(Dir)
    201 
    202         for File in Files:
    203             if File.startswith('.'):
    204                 continue
    205             File = os.path.normpath(os.path.join(Root, File))
    206             if not FullPath:
    207                 File = File[len(OriPath) + 1:]
    208             FileList.append(File)
    209 
    210     return FileList
    211 
    212 ## Get all non-metadata files of a directory

    213 #

    214 # @param Root:       Root Dir

    215 # @param SkipList :  List of path need be skipped

    216 # @param FullPath:  True if the returned file should be full path

    217 # @param PrefixPath: the path that need to be added to the files found

    218 # @return: the list of files found

    219 #  

    220 def GetNonMetaDataFiles(Root, SkipList, FullPath, PrefixPath):
    221     FileList = GetFiles(Root, SkipList, FullPath)
    222     NewFileList = []
    223     for File in FileList:
    224         ExtName = os.path.splitext(File)[1]
    225         #

    226         # skip '.dec', '.inf', '.dsc', '.fdf' files

    227         #

    228         if ExtName.lower() not in ['.dec', '.inf', '.dsc', '.fdf']:
    229             NewFileList.append(os.path.normpath(os.path.join(PrefixPath, File)))
    230 
    231     return NewFileList
    232 
    233 ## Check if given file exists or not

    234 #

    235 # @param      File:    File name or path to be checked

    236 # @param      Dir:     The directory the file is relative to

    237 #

    238 def ValidFile(File, Ext=None):
    239     File = File.replace('\\', '/')
    240     if Ext != None:
    241         FileExt = os.path.splitext(File)[1]
    242         if FileExt.lower() != Ext.lower():
    243             return False
    244     if not os.path.exists(File):
    245         return False
    246     return True
    247 
    248 ## RealPath
    249 #
    250 # @param      File:    File name or path to be checked
    251 # @param      Dir:     The directory the file is relative to
    252 # @param      OverrideDir:     The override directory
    253 #
    254 def RealPath(File, Dir='', OverrideDir=''):
    255     NewFile = os.path.normpath(os.path.join(Dir, File))
    256     NewFile = GlobalData.gALL_FILES[NewFile]
    257     if not NewFile and OverrideDir:
    258         NewFile = os.path.normpath(os.path.join(OverrideDir, File))
    259         NewFile = GlobalData.gALL_FILES[NewFile]
    260     return NewFile
    261 
    262 ## RealPath2
    263 #
    264 # @param      File:    File name or path to be checked
    265 # @param      Dir:     The directory the file is relative to
    266 # @param      OverrideDir:     The override directory
    267 #
    268 def RealPath2(File, Dir='', OverrideDir=''):
    269     if OverrideDir:
    270         NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join\
    271                                                         (OverrideDir, File))]
    272         if NewFile:
    273             if OverrideDir[-1] == os.path.sep:
    274                 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
    275             else:
    276                 return NewFile[len(OverrideDir) + 1:], \
    277             NewFile[0:len(OverrideDir)]
    278 
    279     NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join(Dir, File))]
    280     if NewFile:
    281         if Dir:
    282             if Dir[-1] == os.path.sep:
    283                 return NewFile[len(Dir):], NewFile[0:len(Dir)]
    284             else:
    285                 return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]
    286         else:
    287             return NewFile, ''
    288 
    289     return None, None
    290 
    291 ## A dict which can access its keys and/or values orderly
    292 #
    293 #  The class implements a new kind of dict which its keys or values can be
    294 #  accessed in the order they are added into the dict. It guarantees the order
    295 #  by making use of an internal list to keep a copy of keys.
    296 #
    297 class Sdict(IterableUserDict):
    298     ## Constructor
    299     #
    300     def __init__(self):
    301         IterableUserDict.__init__(self)
    302         self._key_list = []
    303 
    304     ## [] operator
    305     #
    306     def __setitem__(self, Key, Value):
    307         if Key not in self._key_list:
    308             self._key_list.append(Key)
    309         IterableUserDict.__setitem__(self, Key, Value)
    310 
    311     ## del operator
    312     #
    313     def __delitem__(self, Key):
    314         self._key_list.remove(Key)
    315         IterableUserDict.__delitem__(self, Key)
    316 
    317     ## used in "for k in dict" loop to ensure the correct order
    318     #
    319     def __iter__(self):
    320         return self.iterkeys()
    321 
    322     ## len() support
    323     #
    324     def __len__(self):
    325         return len(self._key_list)
    326 
    327     ## "in" test support
    328     #
    329     def __contains__(self, Key):
    330         return Key in self._key_list
    331 
    332     ## indexof support
    333     #
    334     def index(self, Key):
    335         return self._key_list.index(Key)
    336 
    337     ## insert support
    338     #
    339     def insert(self, Key, Newkey, Newvalue, Order):
    340         Index = self._key_list.index(Key)
    341         if Order == 'BEFORE':
    342             self._key_list.insert(Index, Newkey)
    343             IterableUserDict.__setitem__(self, Newkey, Newvalue)
    344         elif Order == 'AFTER':
    345             self._key_list.insert(Index + 1, Newkey)
    346             IterableUserDict.__setitem__(self, Newkey, Newvalue)
    347 
    348     ## append support
    349     #
    350     def append(self, Sdict2):
    351         for Key in Sdict2:
    352             if Key not in self._key_list:
    353                 self._key_list.append(Key)
    354             IterableUserDict.__setitem__(self, Key, Sdict2[Key])
    355     ## hash key
    356     #
    357     def has_key(self, Key):
    358         return Key in self._key_list
    359 
    360     ## Empty the dict
    361     #
    362     def clear(self):
    363         self._key_list = []
    364         IterableUserDict.clear(self)
    365 
    366     ## Return a copy of keys
    367     #
    368     def keys(self):
    369         Keys = []
    370         for Key in self._key_list:
    371             Keys.append(Key)
    372         return Keys
    373 
    374     ## Return a copy of values
    375     #
    376     def values(self):
    377         Values = []
    378         for Key in self._key_list:
    379             Values.append(self[Key])
    380         return Values
    381 
    382     ## Return a copy of (key, value) list
    383     #
    384     def items(self):
    385         Items = []
    386         for Key in self._key_list:
    387             Items.append((Key, self[Key]))
    388         return Items
    389 
    390     ## Iteration support
    391     #
    392     def iteritems(self):
    393         return iter(self.items())
    394 
    395     ## Keys interation support
    396     #
    397     def iterkeys(self):
    398         return iter(self.keys())
    399 
    400     ## Values interation support
    401     #
    402     def itervalues(self):
    403         return iter(self.values())
    404 
    405     ## Return value related to a key, and remove the (key, value) from the dict
    406     #
    407     def pop(self, Key, *Dv):
    408         Value = None
    409         if Key in self._key_list:
    410             Value = self[Key]
    411             self.__delitem__(Key)
    412         elif len(Dv) != 0 :
    413             Value = Dv[0]
    414         return Value
    415 
    416     ## Return (key, value) pair, and remove the (key, value) from the dict
    417     #
    418     def popitem(self):
    419         Key = self._key_list[-1]
    420         Value = self[Key]
    421         self.__delitem__(Key)
    422         return Key, Value
    423     ## update method
    424     #
    425     def update(self, Dict=None, **Kwargs):
    426         if Dict != None:
    427             for Key1, Val1 in Dict.items():
    428                 self[Key1] = Val1
    429         if len(Kwargs):
    430             for Key1, Val1 in Kwargs.items():
    431                 self[Key1] = Val1
    432 
    433 ## CommonPath
    434 #
    435 # @param PathList: PathList
    436 #
    437 def CommonPath(PathList):
    438     Path1 = min(PathList).split(os.path.sep)
    439     Path2 = max(PathList).split(os.path.sep)
    440     for Index in xrange(min(len(Path1), len(Path2))):
    441         if Path1[Index] != Path2[Index]:
    442             return os.path.sep.join(Path1[:Index])
    443     return os.path.sep.join(Path1)
    444 
    445 ## PathClass
    446 #
    447 class PathClass(object):
    448     def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
    449                  Arch='COMMON', ToolChainFamily='', Target='', TagName='', \
    450                  ToolCode=''):
    451         self.Arch = Arch
    452         self.File = str(File)
    453         if os.path.isabs(self.File):
    454             self.Root = ''
    455             self.AlterRoot = ''
    456         else:
    457             self.Root = str(Root)
    458             self.AlterRoot = str(AlterRoot)
    459 
    460         #
    461         # Remove any '.' and '..' in path
    462         #
    463         if self.Root:
    464             self.Path = os.path.normpath(os.path.join(self.Root, self.File))
    465             self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
    466             #
    467             # eliminate the side-effect of 'C:'
    468             #
    469             if self.Root[-1] == ':':
    470                 self.Root += os.path.sep
    471             #
    472             # file path should not start with path separator
    473             #
    474             if self.Root[-1] == os.path.sep:
    475                 self.File = self.Path[len(self.Root):]
    476             else:
    477                 self.File = self.Path[len(self.Root) + 1:]
    478         else:
    479             self.Path = os.path.normpath(self.File)
    480 
    481         self.SubDir, self.Name = os.path.split(self.File)
    482         self.BaseName, self.Ext = os.path.splitext(self.Name)
    483 
    484         if self.Root:
    485             if self.SubDir:
    486                 self.Dir = os.path.join(self.Root, self.SubDir)
    487             else:
    488                 self.Dir = self.Root
    489         else:
    490             self.Dir = self.SubDir
    491 
    492         if IsBinary:
    493             self.Type = Type
    494         else:
    495             self.Type = self.Ext.lower()
    496 
    497         self.IsBinary = IsBinary
    498         self.Target = Target
    499         self.TagName = TagName
    500         self.ToolCode = ToolCode
    501         self.ToolChainFamily = ToolChainFamily
    502 
    503         self._Key = None
    504 
    505     ## Convert the object of this class to a string
    506     #
    507     #  Convert member Path of the class to a string
    508     #
    509     def __str__(self):
    510         return self.Path
    511 
    512     ## Override __eq__ function
    513     #
    514     # Check whether PathClass are the same
    515     #
    516     def __eq__(self, Other):
    517         if type(Other) == type(self):
    518             return self.Path == Other.Path
    519         else:
    520             return self.Path == str(Other)
    521 
    522     ## Override __hash__ function
    523     #
    524     # Use Path as key in hash table
    525     #
    526     def __hash__(self):
    527         return hash(self.Path)
    528 
    529     ## _GetFileKey
    530     #
    531     def _GetFileKey(self):
    532         if self._Key == None:
    533             self._Key = self.Path.upper()
    534         return self._Key
    535     ## Validate
    536     #
    537     def Validate(self, Type='', CaseSensitive=True):
    538         if GlobalData.gCASE_INSENSITIVE:
    539             CaseSensitive = False
    540         if Type and Type.lower() != self.Type:
    541             return ToolError.FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % \
    542         (self.File, Type, self.Type)
    543 
    544         RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
    545         if not RealRoot and not RealFile:
    546             RealFile = self.File
    547             if self.AlterRoot:
    548                 RealFile = os.path.join(self.AlterRoot, self.File)
    549             elif self.Root:
    550                 RealFile = os.path.join(self.Root, self.File)
    551             return ToolError.FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)
    552 
    553         ErrorCode = 0
    554         ErrorInfo = ''
    555         if RealRoot != self.Root or RealFile != self.File:
    556             if CaseSensitive and (RealFile != self.File or \
    557                                   (RealRoot != self.Root and RealRoot != \
    558                                    self.AlterRoot)):
    559                 ErrorCode = ToolError.FILE_CASE_MISMATCH
    560                 ErrorInfo = self.File + '\n\t' + RealFile + \
    561                  " [in file system]"
    562 
    563             self.SubDir, self.Name = os.path.split(RealFile)
    564             self.BaseName, self.Ext = os.path.splitext(self.Name)
    565             if self.SubDir:
    566                 self.Dir = os.path.join(RealRoot, self.SubDir)
    567             else:
    568                 self.Dir = RealRoot
    569             self.File = RealFile
    570             self.Root = RealRoot
    571             self.Path = os.path.join(RealRoot, RealFile)
    572         return ErrorCode, ErrorInfo
    573 
    574     Key = property(_GetFileKey)
    575 
    576 ## Get current workspace
    577 #
    578 #  get WORKSPACE from environment variable if present,if not use current working directory as WORKSPACE
    579 #
    580 def GetWorkspace():
    581     #
    582     # check WORKSPACE
    583     #
    584     if "WORKSPACE" in environ:
    585         WorkspaceDir = os.path.normpath(environ["WORKSPACE"])
    586         if not os.path.exists(WorkspaceDir):
    587             Logger.Error("UPT",
    588                          ToolError.UPT_ENVIRON_MISSING_ERROR,
    589                          ST.ERR_WORKSPACE_NOTEXIST,
    590                          ExtraData="%s" % WorkspaceDir)
    591     else:
    592         WorkspaceDir = os.getcwd()
    593 
    594     if WorkspaceDir[-1] == ':':
    595         WorkspaceDir += os.sep
    596 
    597     PackagesPath = os.environ.get("PACKAGES_PATH")
    598     mws.setWs(WorkspaceDir, PackagesPath)
    599 
    600     return WorkspaceDir, mws.PACKAGES_PATH
    601 
    602 ## Get relative path
    603 #
    604 #  use full path and workspace to get relative path
    605 #  the destination of this function is mainly to resolve the root path issue(like c: or c:\)  
    606 #
    607 #  @param Fullpath: a string of fullpath
    608 #  @param Workspace: a string of workspace
    609 #
    610 def GetRelativePath(Fullpath, Workspace):
    611     
    612     RelativePath = ''
    613     if Workspace.endswith(os.sep):
    614         RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace):]
    615     else:
    616         RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace)+1:]
    617         
    618     return RelativePath
    619     
    620 ## Check whether all module types are in list
    621 #
    622 # check whether all module types (SUP_MODULE_LIST) are in list
    623 #  
    624 # @param ModuleList:  a list of ModuleType
    625 #
    626 def IsAllModuleList(ModuleList):
    627     NewModuleList = [Module.upper() for Module in ModuleList]
    628     for Module in SUP_MODULE_LIST:
    629         if Module not in NewModuleList:
    630             return False
    631     else:
    632         return True
    633 
    634 ## Dictionary that use comment(GenericComment, TailComment) as value,
    635 # if a new comment which key already in the dic is inserted, then the 
    636 # comment will be merged.
    637 # Key is (Statement, SupArch), when TailComment is added, it will ident 
    638 # according to Statement
    639 #
    640 class MergeCommentDict(dict):
    641     ## []= operator
    642     #
    643     def __setitem__(self, Key, CommentVal):
    644         GenericComment, TailComment = CommentVal
    645         if Key in self:
    646             OrigVal1, OrigVal2 = dict.__getitem__(self, Key)
    647             Statement = Key[0]
    648             dict.__setitem__(self, Key, (OrigVal1 + GenericComment, OrigVal2 \
    649                                          + len(Statement) * ' ' + TailComment))
    650         else:
    651             dict.__setitem__(self, Key, (GenericComment, TailComment))
    652 
    653     ## =[] operator
    654     #
    655     def __getitem__(self, Key):
    656         return dict.__getitem__(self, Key)
    657 
    658 
    659 ## GenDummyHelpTextObj
    660 #
    661 # @retval HelpTxt:   Generated dummy help text object
    662 #
    663 def GenDummyHelpTextObj():
    664     HelpTxt = TextObject()
    665     HelpTxt.SetLang(TAB_LANGUAGE_EN_US)
    666     HelpTxt.SetString(' ')
    667     return HelpTxt
    668 
    669 ## ConvertVersionToDecimal, the minor version should be within 0 - 99
    670 # <HexVersion>          ::=  "0x" <Major> <Minor>
    671 # <Major>               ::=  (a-fA-F0-9){4}
    672 # <Minor>               ::=  (a-fA-F0-9){4}
    673 # <DecVersion>          ::=  (0-65535) ["." (0-99)]
    674 # 
    675 # @param StringIn:  The string contains version defined in INF file.
    676 #                   It can be Decimal or Hex
    677 #
    678 def ConvertVersionToDecimal(StringIn):
    679     if IsValidHexVersion(StringIn):
    680         Value = int(StringIn, 16)
    681         Major = Value >> 16
    682         Minor = Value & 0xFFFF
    683         MinorStr = str(Minor)
    684         if len(MinorStr) == 1:
    685             MinorStr = '0' + MinorStr
    686         return str(Major) + '.' + MinorStr
    687     else:
    688         if StringIn.find(TAB_SPLIT) != -1:
    689             return StringIn
    690         elif StringIn:
    691             return StringIn + '.0'
    692         else:
    693             #
    694             # when StringIn is '', return it directly
    695             #
    696             return StringIn
    697 
    698 ## GetHelpStringByRemoveHashKey
    699 #
    700 # Remove hash key at the header of string and return the remain.
    701 #
    702 # @param String:  The string need to be processed.
    703 #
    704 def GetHelpStringByRemoveHashKey(String):
    705     ReturnString = ''
    706     PattenRemoveHashKey = re.compile(r"^[#+\s]+", re.DOTALL)
    707     String = String.strip()
    708     if String == '':
    709         return String
    710 
    711     LineList = GetSplitValueList(String, END_OF_LINE)
    712     for Line in LineList:
    713         ValueList = PattenRemoveHashKey.split(Line)
    714         if len(ValueList) == 1:
    715             ReturnString += ValueList[0] + END_OF_LINE
    716         else:
    717             ReturnString += ValueList[1] + END_OF_LINE
    718 
    719     if ReturnString.endswith('\n') and not ReturnString.endswith('\n\n') and ReturnString != '\n':
    720         ReturnString = ReturnString[:-1]
    721 
    722     return ReturnString
    723 
    724 ## ConvPathFromAbsToRel
    725 #
    726 # Get relative file path from absolute path.
    727 #
    728 # @param Path:  The string contain file absolute path.
    729 # @param Root:  The string contain the parent path of Path in.
    730 #
    731 #
    732 def ConvPathFromAbsToRel(Path, Root):
    733     Path = os.path.normpath(Path)
    734     Root = os.path.normpath(Root)
    735     FullPath = os.path.normpath(os.path.join(Root, Path))
    736 
    737     #
    738     # If Path is absolute path.
    739     # It should be in Root.
    740     #
    741     if os.path.isabs(Path):
    742         return FullPath[FullPath.find(Root) + len(Root) + 1:]
    743 
    744     else:
    745         return Path
    746 
    747 ## ConvertPath
    748 #
    749 # Convert special characters to '_', '\' to '/'
    750 # return converted path: Test!1.inf -> Test_1.inf
    751 #
    752 # @param Path: Path to be converted
    753 #
    754 def ConvertPath(Path):
    755     RetPath = ''
    756     for Char in Path.strip():
    757         if Char.isalnum() or Char in '.-_/':
    758             RetPath = RetPath + Char
    759         elif Char == '\\':
    760             RetPath = RetPath + '/'
    761         else:
    762             RetPath = RetPath + '_'
    763     return RetPath
    764 
    765 ## ConvertSpec
    766 #
    767 # during install, convert the Spec string extract from UPD into INF allowable definition, 
    768 # the difference is period is allowed in the former (not the first letter) but not in the latter.
    769 # return converted Spec string
    770 #
    771 # @param SpecStr: SpecStr to be converted
    772 #
    773 def ConvertSpec(SpecStr):
    774     RetStr = ''
    775     for Char in SpecStr:
    776         if Char.isalnum() or Char == '_':
    777             RetStr = RetStr + Char
    778         else:
    779             RetStr = RetStr + '_'
    780 
    781     return RetStr
    782 
    783 
    784 ## IsEqualList
    785 #
    786 # Judge two lists are identical(contain same item).
    787 # The rule is elements in List A are in List B and elements in List B are in List A.
    788 #
    789 # @param ListA, ListB  Lists need to be judged.
    790 # 
    791 # @return True  ListA and ListB are identical
    792 # @return False ListA and ListB are different with each other
    793 #
    794 def IsEqualList(ListA, ListB):
    795     if ListA == ListB:
    796         return True
    797 
    798     for ItemA in ListA:
    799         if not ItemA in ListB:
    800             return False
    801 
    802     for ItemB in ListB:
    803         if not ItemB in ListA:
    804             return False
    805 
    806     return True
    807 
    808 ## ConvertArchList
    809 #
    810 # Convert item in ArchList if the start character is lower case.
    811 # In UDP spec, Arch is only allowed as: [A-Z]([a-zA-Z0-9])* 
    812 #
    813 # @param ArchList The ArchList need to be converted.
    814 # 
    815 # @return NewList  The ArchList been converted.
    816 #
    817 def ConvertArchList(ArchList):
    818     NewArchList = []
    819     if not ArchList:
    820         return NewArchList
    821 
    822     if type(ArchList) == list:
    823         for Arch in ArchList:
    824             Arch = Arch.upper()
    825             NewArchList.append(Arch)
    826     elif type(ArchList) == str:
    827         ArchList = ArchList.upper()
    828         NewArchList.append(ArchList)
    829 
    830     return NewArchList
    831 
    832 ## ProcessLineExtender
    833 #
    834 # Process the LineExtender of Line in LineList.
    835 # If one line ends with a line extender, then it will be combined together with next line.
    836 #
    837 # @param LineList The LineList need to be processed.
    838 # 
    839 # @return NewList  The ArchList been processed.
    840 #
    841 def ProcessLineExtender(LineList):
    842     NewList = []
    843     Count = 0
    844     while Count < len(LineList):
    845         if LineList[Count].strip().endswith("\\") and Count + 1 < len(LineList):
    846             NewList.append(LineList[Count].strip()[:-2] + LineList[Count + 1])
    847             Count = Count + 1
    848         else:
    849             NewList.append(LineList[Count])
    850 
    851         Count = Count + 1
    852 
    853     return NewList
    854 
    855 ## ProcessEdkComment
    856 #
    857 # Process EDK style comment in LineList: c style /* */ comment or cpp style // comment 
    858 #
    859 #
    860 # @param LineList The LineList need to be processed.
    861 # 
    862 # @return LineList  The LineList been processed.
    863 # @return FirstPos  Where Edk comment is first found, -1 if not found
    864 #
    865 def ProcessEdkComment(LineList):
    866     FindEdkBlockComment = False
    867     Count = 0
    868     StartPos = -1
    869     EndPos = -1
    870     FirstPos = -1
    871     
    872     while(Count < len(LineList)):
    873         Line = LineList[Count].strip()
    874         if Line.startswith("/*"):
    875             #
    876             # handling c style comment
    877             #
    878             StartPos = Count
    879             while Count < len(LineList):
    880                 Line = LineList[Count].strip()
    881                 if Line.endswith("*/"):
    882                     if (Count == StartPos) and Line.strip() == '/*/':
    883                         Count = Count + 1
    884                         continue
    885                     EndPos = Count
    886                     FindEdkBlockComment = True
    887                     break
    888                 Count = Count + 1
    889             
    890             if FindEdkBlockComment:
    891                 if FirstPos == -1:
    892                     FirstPos = StartPos
    893                 for Index in xrange(StartPos, EndPos+1):
    894                     LineList[Index] = ''
    895                 FindEdkBlockComment = False
    896         elif Line.find("//") != -1 and not Line.startswith("#"):
    897             #
    898             # handling cpp style comment
    899             #
    900             LineList[Count] = Line.replace("//", '#')

    901             if FirstPos == -1:
    902                 FirstPos = Count
    903         
    904         Count = Count + 1
    905     
    906     return LineList, FirstPos
    907 
    908 ## GetLibInstanceInfo

    909 #

    910 # Get the information from Library Instance INF file.

    911 #

    912 # @param string.  A string start with # and followed by INF file path

    913 # @param WorkSpace. The WorkSpace directory used to combined with INF file path.

    914 #

    915 # @return GUID, Version

    916 def GetLibInstanceInfo(String, WorkSpace, LineNo):
    917 
    918     FileGuidString = ""
    919     VerString = ""
    920 
    921     OrignalString = String
    922     String = String.strip()
    923     if not String:
    924         return None, None
    925     #

    926     # Remove "#" characters at the beginning

    927     #

    928     String = GetHelpStringByRemoveHashKey(String)
    929     String = String.strip()
    930 
    931     #

    932     # Validate file name exist.

    933     #

    934     FullFileName = os.path.normpath(os.path.realpath(os.path.join(WorkSpace, String)))
    935     if not (ValidFile(FullFileName)):
    936         Logger.Error("InfParser",
    937                      ToolError.FORMAT_INVALID,
    938                      ST.ERR_FILELIST_EXIST % (String),
    939                      File=GlobalData.gINF_MODULE_NAME,
    940                      Line=LineNo,
    941                      ExtraData=OrignalString)
    942 
    943     #

    944     # Validate file exist/format.

    945     #

    946     if IsValidPath(String, WorkSpace):
    947         IsValidFileFlag = True
    948     else:
    949         Logger.Error("InfParser",
    950                      ToolError.FORMAT_INVALID,
    951                      ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID % (String),
    952                      File=GlobalData.gINF_MODULE_NAME,
    953                      Line=LineNo,
    954                      ExtraData=OrignalString)
    955         return False
    956     if IsValidFileFlag:
    957         FileLinesList = []
    958 
    959         try:
    960             FInputfile = open(FullFileName, "rb", 0)
    961             try:
    962                 FileLinesList = FInputfile.readlines()
    963             except BaseException:
    964                 Logger.Error("InfParser",
    965                              ToolError.FILE_READ_FAILURE,
    966                              ST.ERR_FILE_OPEN_FAILURE,
    967                              File=FullFileName)
    968             finally:
    969                 FInputfile.close()
    970         except BaseException:
    971             Logger.Error("InfParser",
    972                          ToolError.FILE_READ_FAILURE,
    973                          ST.ERR_FILE_OPEN_FAILURE,
    974                          File=FullFileName)
    975 
    976         ReFileGuidPattern = re.compile("^\s*FILE_GUID\s*=.*$")
    977         ReVerStringPattern = re.compile("^\s*VERSION_STRING\s*=.*$")
    978 
    979         FileLinesList = ProcessLineExtender(FileLinesList)
    980 
    981         for Line in FileLinesList:
    982             if ReFileGuidPattern.match(Line):
    983                 FileGuidString = Line
    984             if ReVerStringPattern.match(Line):
    985                 VerString = Line
    986 
    987         if FileGuidString:
    988             FileGuidString = GetSplitValueList(FileGuidString, '=', 1)[1]
    989         if VerString:
    990             VerString = GetSplitValueList(VerString, '=', 1)[1]
    991 
    992         return FileGuidString, VerString
    993 
    994 ## GetLocalValue

    995 #

    996 # Generate the local value for INF and DEC file. If Lang attribute not present, then use this value.

    997 # If present, and there is no element without the Lang attribute, and one of the elements has the rfc1766 code is 

    998 # "en-x-tianocore", or "en-US" if "en-x-tianocore" was not found, or "en" if "en-US" was not found, or startswith 'en' 

    999 # if 'en' was not found, then use this value.

   1000 # If multiple entries of a tag exist which have the same language code, use the last entry.

   1001 #

   1002 # @param ValueList  A list need to be processed.

   1003 # @param UseFirstValue: True to use the first value, False to use the last value 

   1004 #

   1005 # @return LocalValue

   1006 def GetLocalValue(ValueList, UseFirstValue=False):
   1007     Value1 = ''
   1008     Value2 = ''
   1009     Value3 = ''
   1010     Value4 = ''
   1011     Value5 = ''
   1012     for (Key, Value) in ValueList:
   1013         if Key == TAB_LANGUAGE_EN_X:
   1014             if UseFirstValue:
   1015                 if not Value1:
   1016                     Value1 = Value
   1017             else:
   1018                 Value1 = Value
   1019         if Key == TAB_LANGUAGE_EN_US:
   1020             if UseFirstValue:
   1021                 if not Value2:
   1022                     Value2 = Value
   1023             else:
   1024                 Value2 = Value
   1025         if Key == TAB_LANGUAGE_EN:
   1026             if UseFirstValue:
   1027                 if not Value3:
   1028                     Value3 = Value
   1029             else:
   1030                 Value3 = Value
   1031         if Key.startswith(TAB_LANGUAGE_EN):
   1032             if UseFirstValue:
   1033                 if not Value4:
   1034                     Value4 = Value
   1035             else:
   1036                 Value4 = Value
   1037         if Key == '':
   1038             if UseFirstValue:
   1039                 if not Value5:
   1040                     Value5 = Value
   1041             else:
   1042                 Value5 = Value
   1043             
   1044     if Value1:
   1045         return Value1
   1046     if Value2:
   1047         return Value2
   1048     if Value3:
   1049         return Value3
   1050     if Value4:
   1051         return Value4
   1052     if Value5:
   1053         return Value5
   1054     
   1055     return ''
   1056 
   1057 
   1058 ## GetCharIndexOutStr

   1059 #

   1060 # Get comment character index outside a string

   1061 #

   1062 # @param Line:              The string to be checked

   1063 # @param CommentCharacter:  Comment char, used to ignore comment content

   1064 #

   1065 # @retval Index

   1066 #

   1067 def GetCharIndexOutStr(CommentCharacter, Line):
   1068     #

   1069     # remove whitespace

   1070     #

   1071     Line = Line.strip()
   1072 
   1073     #

   1074     # Check whether comment character is in a string

   1075     #

   1076     InString = False
   1077     for Index in range(0, len(Line)):
   1078         if Line[Index] == '"':
   1079             InString = not InString
   1080         elif Line[Index] == CommentCharacter and InString :
   1081             pass
   1082         elif Line[Index] == CommentCharacter and (Index +1) < len(Line) and Line[Index+1] == CommentCharacter \
   1083             and not InString :
   1084             return Index
   1085     return -1
   1086 
   1087 ## ValidateUNIFilePath

   1088 #

   1089 # Check the UNI file path

   1090 #

   1091 # @param FilePath: The UNI file path 

   1092 #

   1093 def ValidateUNIFilePath(Path):
   1094     Suffix = Path[Path.rfind(TAB_SPLIT):]
   1095     
   1096     #

   1097     # Check if the suffix is one of the '.uni', '.UNI', '.Uni' 

   1098     #

   1099     if Suffix not in TAB_UNI_FILE_SUFFIXS:
   1100         Logger.Error("Unicode File Parser", 
   1101                         ToolError.FORMAT_INVALID, 
   1102                         Message=ST.ERR_UNI_FILE_SUFFIX_WRONG, 
   1103                         ExtraData=Path)    
   1104     
   1105     #

   1106     # Check if '..' in the file name(without suffixe)

   1107     #

   1108     if (TAB_SPLIT + TAB_SPLIT) in Path:
   1109         Logger.Error("Unicode File Parser", 
   1110                         ToolError.FORMAT_INVALID, 
   1111                         Message=ST.ERR_UNI_FILE_NAME_INVALID, 
   1112                         ExtraData=Path)    
   1113     
   1114     #

   1115     # Check if the file name is valid according to the DEC and INF specification

   1116     #

   1117     Pattern = '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*'
   1118     FileName = Path.replace(Suffix, '')
   1119     InvalidCh = re.sub(Pattern, '', FileName)
   1120     if InvalidCh:
   1121         Logger.Error("Unicode File Parser", 
   1122                         ToolError.FORMAT_INVALID, 
   1123                         Message=ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID, 
   1124                         ExtraData=Path)    
   1125 
   1126