releasing/blocks/framework/src/SymbianUtils/Evalid.py
changeset 632 934f9131337b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/releasing/blocks/framework/src/SymbianUtils/Evalid.py	Thu Sep 02 15:02:14 2010 +0800
@@ -0,0 +1,293 @@
+#
+# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+# Generate checksums for different types of files
+#
+
+import sys
+import os
+import re
+import struct
+import subprocess
+import platform
+try:
+    from hashlib import md5
+except ImportError:
+    import md5
+
+import SymbianUtils
+
+class Evalid(object):
+    '''
+    Provide some of the functionality found in epoc32/tools/EvalidCompare.pm
+
+    generateSignature() - use appropriate method to calculate checksum for a file
+    getUid() - extract Uid from file
+    '''
+
+    class ExternalError(SymbianUtils.SymbianUtilsError):
+        """ An external utility exited with an error """
+
+    ext = ".exe" if platform.system() == "Windows" else ""
+    binPath = os.path.normpath(os.path.join(os.path.dirname(__file__), "bin"))
+    NM = os.path.join(binPath, "nm" + ext)
+    ELFDUMP = os.path.join(binPath, "elfdump" + ext)
+    ELF2E32 = os.path.join(binPath, "elf2e32" + ext)
+    PE_DUMP = os.path.join(binPath, "pe_dump" + ext)
+
+    ELF_DLL_NAME = re.compile(r"#<DLL>(\S+\.\S+)#<\\DLL>")
+    ELF_DEBUG = re.compile(r"^\.(rel\.)?debug_")
+    ELF_P_HEAD = re.compile(r"^\tProgram header offset.*$")
+    ELF_S_HEAD = re.compile(r"^\tSection header offset.*$")
+    PRECOMP_IGNORE = re.compile(r'^# \d+ ".*"( \d)?$')
+    E32_EMPTY = re.compile("Time Stamp:|E32ImageFile|Header CRC:")
+    E32_LOWER = re.compile("imports from")
+    INTEL_OBJECTPATH_WIN  = re.compile(r"\.\.\\[^(]*\\")
+    INTEL_OBJECTPATH_NIX  = re.compile("\.\.\/[^(]*\/")
+    INTEL_DLLTOOL = re.compile("^(.+ (_head|_))\w+_(EPOC32_\w+(_LIB|_iname))$", re.I)
+
+    @classmethod
+    def typeLookup(cls, type):
+        '''
+        Return the internally used identifier string for the type
+        @todo: Warning
+        @param type: The type
+        @type type: String
+        @return: Internally used type identifier
+        @rtype: String
+        '''
+        if type in ("e32", "default", "elf", "preprocessed_text", "intel", "intel_pe"):
+            return type
+        elif type in ("file", "symbol"):
+            return "default"
+        elif type in ("staticlib", "dso"):
+            return "elf"
+        elif type in ("exe", "plugin", "dll"):
+            return "e32"
+        else:
+            #sys.stderr.write("warning - unknown hashtype %s.\n"%type)
+            return "default"
+
+    @classmethod
+    def generateSignature(cls, path, fileType):
+        '''
+        Generic dispatcher method for file types. Use the appropriate method for
+        I{type} to generate the signature for file at I{path}.
+
+        @param path: The path where the file is located
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        if not isinstance(path, basestring):
+            raise TypeError, "path must be a string"
+        if not path:
+            raise ValueError, "path must not be zero length"
+        path = os.path.normpath(path)
+        fileType = cls.typeLookup(fileType)
+        methodName = "sig_" + fileType
+        if hasattr(cls, methodName):
+            method = getattr(cls, methodName)
+            return method(path)
+        else:
+            raise NotImplementedError("No signature generator for type %s" % fileType)
+
+    @staticmethod
+    def getUid(index, file):
+        '''Get UID of file
+
+        @param index: Which UID
+        @param file: Absolute path
+        @return: UID
+        @rtype: String
+        '''
+        if index not in (1, 2, 3):
+            raise ValueError("Index can only be one of 1, 2 or 3")
+        if os.path.getsize(file) < 12:
+            return None
+        start = (index-1) * 4
+        finish = start + 4
+        f = open(file, "rb")
+        head = f.read(12)
+        f.close()
+        return struct.unpack("<l", head[start:finish])[0]
+
+    @staticmethod
+    def getMd5():
+        '''A convenicence method to use appropriate library regardless of Python
+        version. Maintain compatibility while using hashlib whenever possible.
+
+        @return: md5 object
+        @rtype: md5
+        '''
+        if hasattr(md5, "new"):
+            return md5.new()
+        else:
+            return md5()
+
+    # Signatures for various formats
+
+    @classmethod
+    def sig_e32(cls, path):
+        '''
+        Return the checksum of significant parts using elf2e32
+
+        @param path: The absolute path
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        bin = cls.ELF2E32 + " --dump --e32input="
+        m = cls.getMd5()
+        fo = os.popen(bin+path, "r", -1)
+        for line in fo:
+            if cls.E32_EMPTY.search(line):
+                line = ""
+            if cls.E32_LOWER.search(line):
+                line = line.lower()
+            m.update(line)
+        if fo.close():
+            raise cls.ExternalError("elf2e32 failed at %s" % path)
+        return m.hexdigest()
+
+    @classmethod
+    def sig_default(cls, path):
+        '''
+        Calculate the checksum of the file without filtering.
+
+        @param path: The absolute path
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        m = cls.getMd5()
+        f = open(path, "rb")
+        while True:
+            buf = f.read(32*1024)
+            if not buf:
+                break
+            m.update(buf)
+        f.close()
+        return m.hexdigest()
+
+    @classmethod
+    def sig_elf(cls, path):
+        '''
+        Return the checksum of significant parts using elfdump
+
+        @param path: The absolute path
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        bin = cls.ELFDUMP + " -i "
+        def firstGroupToLc(match):
+            return ("#<DLL>" + match.group(1).lower() + "#<\\DLL>")
+        m = cls.getMd5()
+        fo = os.popen(bin+path, "r", -1)
+        for line in fo:
+            if cls.ELF_P_HEAD.match(line):
+                line = "Program header offset\n"
+            if cls.ELF_S_HEAD.match(line):
+                line = "Section header offset\n"
+            line = cls.ELF_DLL_NAME.sub(firstGroupToLc, line)
+            if cls.ELF_DEBUG.match(line):
+                line = ""
+            #sys.stderr.write(line)
+            m.update(line)
+        if fo.close():
+            raise cls.ExternalError("elfdump failed at %s" % path)
+        return m.hexdigest()
+
+    @classmethod
+    def sig_preprocessed_text(cls, path):
+        '''
+        Return the checksum of significant parts of preprocessed text
+
+        @param path: The absolute path
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        m = cls.getMd5()
+        f = open(path, "rb")
+        for line in f:
+            line = line.replace("\r\n", "\n")
+            if cls.PRECOMP_IGNORE.search(line):
+                line = "\n"
+            m.update(line)
+        f.close()
+        return m.hexdigest()
+
+    @classmethod
+    def sig_intel_pe(cls, path):
+        '''
+        Return the checksum of significant parts of pe_dump output
+
+        @param path: The absolute path
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        m = cls.getMd5()
+        fo = os.popen("%s %s" % (cls.PE_DUMP, path), "r", -1)
+        for line in fo:
+            m.update(line)
+        if fo.close():
+            raise cls.ExternalError("pe_dump failed at %s" % path)
+        return m.hexdigest()
+
+
+    @classmethod
+    def sig_intel(cls, path):
+        '''
+        Return the checksum of significant parts using nm
+
+        @param path: The absolute path
+        @type path: String
+        @return: checksum
+        @rtype: String
+        '''
+        m = cls.getMd5()
+        try:
+            s = subprocess.Popen([cls.NM, "--no-sort", path], env=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            out, err = s.communicate()
+        except OSError, e:
+            raise cls.ExternalError, "nm failed at %s: %s" % (path, str(e))
+        if s.returncode != 0:
+            raise cls.ExternalError, "nm failed at %s: %s" % (path, err)
+        for line in out.splitlines():
+            # no need for regexps here
+            if line.endswith(":\n") \
+                or line.startswith("BFD: ") \
+                or cls.INTEL_OBJECTPATH_WIN.search(line) \
+                or cls.INTEL_OBJECTPATH_NIX.search(line):
+                line = "\n"
+            match = cls.INTEL_DLLTOOL.search(line)
+            if match:
+                line = "%s_..._%s" % (match.groups()[0], match.groups()[2])
+                line = line.upper()
+            m.update(line)
+
+        if s.returncode != 0:
+            raise cls.ExternalError("nm failed at %s" % path)
+        return m.hexdigest()
+
+def main():
+    path = sys.argv[1]
+    ftype = sys.argv[2]
+    print Evalid.generateSignature(path, ftype)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file