]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/Python/Common/Misc.py
Sync BaseTool trunk (version r2599) into EDKII BaseTools.
[mirror_edk2.git] / BaseTools / Source / Python / Common / Misc.py
index 2883169f3c6888997e4c028c9a5e5c69d4121d30..afdfb93ad62c108f37a345b00774555f43774674 100644 (file)
@@ -1,8 +1,8 @@
 ## @file
 # Common routines used by all tools
 #
-# Copyright (c) 2007, Intel Corporation
-# All rights reserved. This program and the accompanying materials
+# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
 # are licensed and made available under the terms and conditions of the BSD License
 # which accompanies this distribution.  The full text of the license may be found at
 # http://opensource.org/licenses/bsd-license.php
@@ -22,13 +22,15 @@ import threading
 import time
 import re
 import cPickle
+import array
 from UserDict import IterableUserDict
 from UserList import UserList
 
 from Common import EdkLogger as EdkLogger
 from Common import GlobalData as GlobalData
-
+from DataType import *
 from BuildToolError import *
+from CommonDataClass.DataClass import *
 
 ## Regular expression used to find out place holders in string template
 gPlaceholderPattern = re.compile("\$\{([^$()\s]+)\}", re.MULTILINE|re.UNICODE)
@@ -155,7 +157,7 @@ def GuidStructureStringToGuidValueName(GuidValue):
     guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")
     guidValueList = guidValueString.split(",")
     if len(guidValueList) != 11:
-        EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
+        EdkLogger.error(None, FORMAT_INVALID, "Invalid GUID value string [%s]" % GuidValue)
     return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
             int(guidValueList[0], 16),
             int(guidValueList[1], 16),
@@ -251,7 +253,15 @@ def SaveFileOnChange(File, Content, IsBinaryFile=True):
         except:
             EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)
 
-    CreateDirectory(os.path.dirname(File))
+    DirName = os.path.dirname(File)
+    if not CreateDirectory(DirName):
+        EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)
+    else:
+        if DirName == '':
+            DirName = os.getcwd()
+        if not os.access(DirName, os.W_OK):
+            EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)
+
     try:
         if GlobalData.gIsWindows:
             try:
@@ -266,8 +276,8 @@ def SaveFileOnChange(File, Content, IsBinaryFile=True):
             Fd = open(File, "wb")
             Fd.write(Content)
             Fd.close()
-    except:
-        EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData=File)
+    except IOError, X:
+        EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s'%X)
 
     return True
 
@@ -436,8 +446,10 @@ def RealPath2(File, Dir='', OverrideDir=''):
                 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
             else:
                 return NewFile[len(OverrideDir)+1:], NewFile[0:len(OverrideDir)]
-
-    NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
+    if GlobalData.gAllFiles:
+        NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
+    else:
+        NewFile = os.path.normpath(os.path.join(Dir, File))
     if NewFile:
         if Dir:
             if Dir[-1] == os.path.sep:
@@ -459,7 +471,7 @@ def ValidFile2(AllFiles, File, Ext=None, Workspace='', EfiSource='', EdkSource='
         if FileExt.lower() != Ext.lower():
             return False, File
 
-    # Replace the R8 macros
+    # Replace the Edk macros
     if OverrideDir != '' and OverrideDir != None:
         if OverrideDir.find('$(EFI_SOURCE)') > -1:
             OverrideDir = OverrideDir.replace('$(EFI_SOURCE)', EfiSource)
@@ -471,7 +483,7 @@ def ValidFile2(AllFiles, File, Ext=None, Workspace='', EfiSource='', EdkSource='
         Dir = os.getcwd()
         Dir = Dir[len(Workspace)+1:]
 
-    # First check if File has R8 definition itself
+    # First check if File has Edk definition itself
     if File.find('$(EFI_SOURCE)') > -1 or File.find('$(EDK_SOURCE)') > -1:
         NewFile = File.replace('$(EFI_SOURCE)', EfiSource)
         NewFile = NewFile.replace('$(EDK_SOURCE)', EdkSource)
@@ -497,7 +509,7 @@ def ValidFile2(AllFiles, File, Ext=None, Workspace='', EfiSource='', EdkSource='
 #
 #
 def ValidFile3(AllFiles, File, Workspace='', EfiSource='', EdkSource='', Dir='.', OverrideDir=''):
-    # Replace the R8 macros
+    # Replace the Edk macros
     if OverrideDir != '' and OverrideDir != None:
         if OverrideDir.find('$(EFI_SOURCE)') > -1:
             OverrideDir = OverrideDir.replace('$(EFI_SOURCE)', EfiSource)
@@ -515,7 +527,7 @@ def ValidFile3(AllFiles, File, Workspace='', EfiSource='', EdkSource='', Dir='.'
     NewRelaPath = RelaPath
 
     while(True):
-        # First check if File has R8 definition itself
+        # First check if File has Edk definition itself
         if File.find('$(EFI_SOURCE)') > -1 or File.find('$(EDK_SOURCE)') > -1:
             File = File.replace('$(EFI_SOURCE)', EfiSource)
             File = File.replace('$(EDK_SOURCE)', EdkSource)
@@ -718,7 +730,7 @@ class TemplateString(object):
         while Template:
             MatchObj = gPlaceholderPattern.search(Template, SearchFrom)
             if not MatchObj:
-                if MatchEnd < len(Template):
+                if MatchEnd <= len(Template):
                     TemplateSection = TemplateString.Section(Template[SectionStart:], PlaceHolderList)
                     TemplateSectionList.append(TemplateSection)
                 break
@@ -1165,27 +1177,238 @@ def ParseConsoleLog(Filename):
     Opr.close()
     Opw.close()
 
+## AnalyzeDscPcd
+#
+#  Analyze DSC PCD value, since there is no data type info in DSC
+#  This fuction is used to match functions (AnalyzePcdData, AnalyzeHiiPcdData, AnalyzeVpdPcdData) used for retrieving PCD value from database
+#  1. Feature flag: TokenSpace.PcdCName|PcdValue
+#  2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|MaxSize]
+#  3. Dynamic default:
+#     TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
+#     TokenSpace.PcdCName|PcdValue
+#  4. Dynamic VPD:
+#     TokenSpace.PcdCName|VpdOffset[|VpdValue]
+#     TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
+#  5. Dynamic HII:
+#     TokenSpace.PcdCName|HiiString|VaiableGuid|VariableOffset[|HiiValue]
+#  PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
+#    there might have "|" operator, also in string value.
+#
+#  @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
+#  @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
+#  @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
+#  @retval:
+#    ValueList: A List contain fields described above
+#    IsValid:   True if conforming EBNF, otherwise False
+#    Index:     The index where PcdValue is in ValueList
+#
+def AnalyzeDscPcd(Setting, PcdType, DataType=''):
+    Setting = Setting.strip()
+    # There might be escaped quote in a string: \", \\\"
+    Data = Setting.replace('\\\\', '//').replace('\\\"', '\\\'')
+    # There might be '|' in string and in ( ... | ... ), replace it with '-'
+    NewStr = ''
+    InStr = False
+    Pair = 0
+    for ch in Data:
+        if ch == '"':
+            InStr = not InStr
+        elif ch == '(' and not InStr:
+            Pair += 1
+        elif ch == ')' and not InStr:
+            Pair -= 1
+        
+        if (Pair > 0 or InStr) and ch == TAB_VALUE_SPLIT:
+            NewStr += '-'
+        else:
+            NewStr += ch
+    FieldList = []
+    StartPos = 0
+    while True:
+        Pos = NewStr.find(TAB_VALUE_SPLIT, StartPos)
+        if Pos < 0:
+            FieldList.append(Setting[StartPos:].strip())
+            break
+        FieldList.append(Setting[StartPos:Pos].strip())
+        StartPos = Pos + 1
+
+    IsValid = True
+    if PcdType in (MODEL_PCD_FIXED_AT_BUILD, MODEL_PCD_PATCHABLE_IN_MODULE, MODEL_PCD_FEATURE_FLAG):
+        Value = FieldList[0]
+        Size = ''
+        if len(FieldList) > 1:
+            Size = FieldList[1]
+        if DataType == 'VOID*':
+            IsValid = (len(FieldList) <= 2)
+        else:
+            IsValid = (len(FieldList) <= 1)
+        return [Value, '', Size], IsValid, 0
+    elif PcdType in (MODEL_PCD_DYNAMIC_DEFAULT, MODEL_PCD_DYNAMIC_EX_DEFAULT):
+        Value = FieldList[0]
+        Size = Type = ''
+        if len(FieldList) > 1:
+            Type = FieldList[1]
+        if len(FieldList) > 2:
+            Size = FieldList[2]
+        if DataType == 'VOID*':
+            IsValid = (len(FieldList) <= 3)
+        else:
+            IsValid = (len(FieldList) <= 1)
+        return [Value, Type, Size], IsValid, 0 
+    elif PcdType in (MODEL_PCD_DYNAMIC_VPD, MODEL_PCD_DYNAMIC_EX_VPD):
+        VpdOffset = FieldList[0]
+        Value = Size = ''
+        if not DataType == 'VOID*':
+            if len(FieldList) > 1:
+                Value = FieldList[1]
+        else:
+            if len(FieldList) > 1:
+                Size = FieldList[1]
+            if len(FieldList) > 2:
+                Value = FieldList[2]
+        if DataType == 'VOID*':
+            IsValid = (len(FieldList) <= 3)
+        else:
+            IsValid = (len(FieldList) <= 2)
+        return [VpdOffset, Size, Value], IsValid, 2
+    elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):
+        HiiString = FieldList[0]
+        Guid = Offset = Value = ''
+        if len(FieldList) > 1:
+            Guid = FieldList[1]
+        if len(FieldList) > 2:
+            Offset = FieldList[2]
+        if len(FieldList) > 3:
+            Value = FieldList[3]
+        IsValid = (3 <= len(FieldList) <= 4)
+        return [HiiString, Guid, Offset, Value], IsValid, 3
+    return [], False, 0
+
+## AnalyzePcdData
+#
+#  Analyze the pcd Value, Datum type and TokenNumber.
+#  Used to avoid split issue while the value string contain "|" character
+#
+#  @param[in] Setting:  A String contain value/datum type/token number information;
+#  
+#  @retval   ValueList: A List contain value, datum type and toke number. 
+#
+def AnalyzePcdData(Setting):   
+    ValueList = ['', '', '']    
+    
+    ValueRe  = re.compile(r'^\s*L?\".*\|.*\"')
+    PtrValue = ValueRe.findall(Setting)
+    
+    ValueUpdateFlag = False
+    
+    if len(PtrValue) >= 1:
+        Setting = re.sub(ValueRe, '', Setting)
+        ValueUpdateFlag = True   
+
+    TokenList = Setting.split(TAB_VALUE_SPLIT)
+    ValueList[0:len(TokenList)] = TokenList
+    
+    if ValueUpdateFlag:
+        ValueList[0] = PtrValue[0]
+        
+    return ValueList   
+## AnalyzeHiiPcdData
+#
+#  Analyze the pcd Value, variable name, variable Guid and variable offset.
+#  Used to avoid split issue while the value string contain "|" character
+#
+#  @param[in] Setting:  A String contain VariableName, VariableGuid, VariableOffset, DefaultValue information;
+#  
+#  @retval   ValueList: A List contaian VariableName, VariableGuid, VariableOffset, DefaultValue. 
+#
+def AnalyzeHiiPcdData(Setting):   
+    ValueList = ['', '', '', '']    
+    
+    ValueRe  = re.compile(r'^\s*L?\".*\|.*\"')
+    PtrValue = ValueRe.findall(Setting)
+    
+    ValueUpdateFlag = False
+    
+    if len(PtrValue) >= 1:
+        Setting = re.sub(ValueRe, '', Setting)
+        ValueUpdateFlag = True   
+
+    TokenList = Setting.split(TAB_VALUE_SPLIT)
+    ValueList[0:len(TokenList)] = TokenList
+    
+    if ValueUpdateFlag:
+        ValueList[0] = PtrValue[0]
+        
+    return ValueList     
+
+## AnalyzeVpdPcdData
+#
+#  Analyze the vpd pcd VpdOffset, MaxDatumSize and InitialValue.
+#  Used to avoid split issue while the value string contain "|" character
+#
+#  @param[in] Setting:  A String contain VpdOffset/MaxDatumSize/InitialValue information;
+#  
+#  @retval   ValueList: A List contain VpdOffset, MaxDatumSize and InitialValue. 
+#
+def AnalyzeVpdPcdData(Setting):   
+    ValueList = ['', '', '']    
+    
+    ValueRe  = re.compile(r'\s*L?\".*\|.*\"\s*$')
+    PtrValue = ValueRe.findall(Setting)
+    
+    ValueUpdateFlag = False
+    
+    if len(PtrValue) >= 1:
+        Setting = re.sub(ValueRe, '', Setting)
+        ValueUpdateFlag = True   
+
+    TokenList = Setting.split(TAB_VALUE_SPLIT)
+    ValueList[0:len(TokenList)] = TokenList
+    
+    if ValueUpdateFlag:
+        ValueList[2] = PtrValue[0]
+        
+    return ValueList     
+
 ## check format of PCD value against its the datum type
 #
 # For PCD value setting
 #
 def CheckPcdDatum(Type, Value):
     if Type == "VOID*":
-        if not ((Value.startswith('L"') or Value.startswith('"') and Value.endswith('"'))
+        ValueRe  = re.compile(r'\s*L?\".*\"\s*$')
+        if not (((Value.startswith('L"') or Value.startswith('"')) and Value.endswith('"'))
                 or (Value.startswith('{') and Value.endswith('}'))
                ):
             return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
-                          ", or \"...\" for string, or L\"...\" for unicode string" % (Value, Type)
+                          ", or \"...\" for string, or L\"...\" for unicode string" % (Value, Type)        
+        elif ValueRe.match(Value):
+            # Check the chars in UnicodeString or CString is printable
+            if Value.startswith("L"):
+                Value = Value[2:-1]
+            else:
+                Value = Value[1:-1]
+            Printset = set(string.printable)
+            Printset.remove(TAB_PRINTCHAR_VT)
+            Printset.add(TAB_PRINTCHAR_BS)
+            Printset.add(TAB_PRINTCHAR_NUL)
+            if not set(Value).issubset(Printset):
+                PrintList = list(Printset)
+                PrintList.sort()
+                return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type, PrintList)
     elif Type == 'BOOLEAN':
-        if Value not in ['TRUE', 'FALSE']:
-            return False, "Invalid value [%s] of type [%s]; must be TRUE or FALSE" % (Value, Type)
-    elif type(Value) == type(""):
+        if Value not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
+            return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
+                          ", FALSE, False, false, 0x0, 0x00, 0" % (Value, Type)
+    elif Type in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64]:
         try:
             Value = long(Value, 0)
         except:
             return False, "Invalid value [%s] of type [%s];"\
-                          " must be a hexadecimal, decimal or octal in C language format."\
-                            % (Value, Type)
+                          " must be a hexadecimal, decimal or octal in C language format." % (Value, Type)
+    else:
+        return False, "Invalid type [%s]; must be one of VOID*, BOOLEAN, UINT8, UINT16, UINT32, UINT64." % (Type)
 
     return True, ""
 
@@ -1299,6 +1522,27 @@ class PathClass(object):
         else:
             return self.Path == str(Other)
 
+    ## Override __cmp__ function
+    #
+    # Customize the comparsion operation of two PathClass
+    #
+    # @retval 0     The two PathClass are different
+    # @retval -1    The first PathClass is less than the second PathClass
+    # @retval 1     The first PathClass is Bigger than the second PathClass
+    def __cmp__(self, Other):
+        if type(Other) == type(self):
+            OtherKey = Other.Path
+        else:
+            OtherKey = str(Other)
+            
+        SelfKey = self.Path
+        if SelfKey == OtherKey:
+            return 0
+        elif SelfKey > OtherKey:
+            return 1
+        else:
+            return -1
+
     ## Override __hash__ function
     #
     # Use Path as key in hash table
@@ -1313,6 +1557,9 @@ class PathClass(object):
             self._Key = self.Path.upper()   # + self.ToolChainFamily + self.TagName + self.ToolCode + self.Target
         return self._Key
 
+    def _GetTimeStamp(self):
+        return os.stat(self.Path)[8]
+
     def Validate(self, Type='', CaseSensitive=True):
         if GlobalData.gCaseInsensitive:
             CaseSensitive = False
@@ -1321,7 +1568,12 @@ class PathClass(object):
 
         RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
         if not RealRoot and not RealFile:
-            return FILE_NOT_FOUND, self.File
+            RealFile = self.File
+            if self.AlterRoot:
+                RealFile = os.path.join(self.AlterRoot, self.File)
+            elif self.Root:
+                RealFile = os.path.join(self.Root, self.File)
+            return FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)
 
         ErrorCode = 0
         ErrorInfo = ''
@@ -1342,7 +1594,92 @@ class PathClass(object):
         return ErrorCode, ErrorInfo
 
     Key = property(_GetFileKey)
+    TimeStamp = property(_GetTimeStamp)
 
+## Parse PE image to get the required PE informaion.
+#
+class PeImageClass():
+    ## Constructor
+    #
+    #   @param  File FilePath of PeImage
+    #
+    def __init__(self, PeFile):
+        self.FileName   = PeFile
+        self.IsValid    = False
+        self.Size       = 0
+        self.EntryPoint = 0
+        self.SectionAlignment  = 0
+        self.SectionHeaderList = []
+        self.ErrorInfo = ''
+        try:
+            PeObject = open(PeFile, 'rb')
+        except:
+            self.ErrorInfo = self.FileName + ' can not be found\n'
+            return
+        # Read DOS header
+        ByteArray = array.array('B')
+        ByteArray.fromfile(PeObject, 0x3E)
+        ByteList = ByteArray.tolist()
+        # DOS signature should be 'MZ'
+        if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':
+            self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'
+            return
+
+        # Read 4 byte PE Signature
+        PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])
+        PeObject.seek(PeOffset)
+        ByteArray = array.array('B')
+        ByteArray.fromfile(PeObject, 4)
+        # PE signature should be 'PE\0\0'
+        if ByteArray.tostring() != 'PE\0\0':
+            self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'
+            return
+
+        # Read PE file header
+        ByteArray = array.array('B')
+        ByteArray.fromfile(PeObject, 0x14)
+        ByteList = ByteArray.tolist()
+        SecNumber = self._ByteListToInt(ByteList[0x2:0x4])
+        if SecNumber == 0:
+            self.ErrorInfo = self.FileName + ' has no section header'
+            return
+
+        # Read PE optional header
+        OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])
+        ByteArray = array.array('B')
+        ByteArray.fromfile(PeObject, OptionalHeaderSize)
+        ByteList = ByteArray.tolist()
+        self.EntryPoint       = self._ByteListToInt(ByteList[0x10:0x14])
+        self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])
+        self.Size             = self._ByteListToInt(ByteList[0x38:0x3C])
+
+        # Read each Section Header
+        for Index in range(SecNumber):
+            ByteArray = array.array('B')
+            ByteArray.fromfile(PeObject, 0x28)
+            ByteList = ByteArray.tolist()
+            SecName  = self._ByteListToStr(ByteList[0:8])
+            SecVirtualSize = self._ByteListToInt(ByteList[8:12])
+            SecRawAddress  = self._ByteListToInt(ByteList[20:24])
+            SecVirtualAddress = self._ByteListToInt(ByteList[12:16])
+            self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))
+        self.IsValid = True
+        PeObject.close()
+
+    def _ByteListToStr(self, ByteList):
+        String = ''
+        for index in range(len(ByteList)):
+            if ByteList[index] == 0: 
+                break
+            String += chr(ByteList[index])
+        return String
+
+    def _ByteListToInt(self, ByteList):
+        Value = 0
+        for index in range(len(ByteList) - 1, -1, -1):
+            Value = (Value << 8) | int(ByteList[index])
+        return Value
+        
 ##
 #
 # This acts like the main() function for the script, unless it is 'import'ed into another