build/buildutils/armsize.py
changeset 87 1627c337e51e
equal deleted inserted replaced
80:d6dafc5d983f 87:1627c337e51e
       
     1 #!/usr/bin/python
       
     2 #
       
     3 # Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 # All rights reserved.
       
     5 # This component and the accompanying materials are made available
       
     6 # under the terms of "Eclipse Public License v1.0"
       
     7 # which accompanies this distribution, and is available
       
     8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 #
       
    10 # Initial Contributors:
       
    11 # Nokia Corporation - initial contribution.
       
    12 #
       
    13 # Contributors:
       
    14 #
       
    15 # Description: 
       
    16 #  This utility is used to find out the size in disk of files 
       
    17 #  written to ROM images (or compiled). Script finds out Symbian 
       
    18 #  build enviornment binary file paths based on iby-file(s) or 
       
    19 #  mmp-files, and prints out file sizes.
       
    20 #
       
    21 #  Run the script with option --help to see the parameters
       
    22 
       
    23 import os.path, fnmatch, re, subprocess, glob
       
    24 from optparse import OptionParser
       
    25 from itertools import chain
       
    26 
       
    27 RE_MMP_TARGET = re.compile(r"\s*TARGET\s+(\S+)", re.IGNORECASE)
       
    28 RE_DEFINE = re.compile(r"^define\s*(\S+)\s+(\S+)\s*$", re.IGNORECASE)
       
    29 RE_IBYTARGET = re.compile(r"^(?:file|data)=(\S+)\s+\S+\s*$", re.IGNORECASE)
       
    30 
       
    31 # Base defines used in IBY files
       
    32 BASE_DEFINES = [("ABI_DIR", r"\epoc32\release\armv5"),
       
    33                 ("BUILD_DIR", r"urel"),
       
    34                 ("DATAZ_", r"\epoc32\data\z"),
       
    35                 ("RESOURCE_FILES_DIR", r"resource"),
       
    36                 ("ZRESOURCE", r"DATAZ_\resource"),
       
    37                 ("ZSYSTEM", r"DATAZ_\system"),
       
    38                 ("ZPRIVATE", r"DATAZ_\private")]
       
    39 
       
    40 def flatten(listOfLists):
       
    41     return list(chain(*listOfLists))
       
    42 
       
    43 def cppFile(source, cppDefines = None, include = None):
       
    44     ''' Preprocess source with epoc32/include, and return preprocessed lines '''
       
    45     includePaths = [r"/epoc32/include"]
       
    46 
       
    47     if cppDefines:
       
    48         cppDefines = " ".join(["-D%s" % d for d in cppDefines])
       
    49     else:
       
    50         cppDefines = ""
       
    51     if include:
       
    52         path, filename = os.path.split(include)
       
    53         includePaths.append(path)
       
    54 
       
    55         include = "-include " + include
       
    56         
       
    57     else:
       
    58         include = ""
       
    59 
       
    60     drive, tail = os.path.splitdrive(source)
       
    61     command = r'cpp %s %s %s "%s"' % (" ".join(["-I %s" % i for i in includePaths]),
       
    62                                       cppDefines, include, source)
       
    63     process = subprocess.Popen(command, 
       
    64                                shell = True,
       
    65                                cwd = "%s\\" % drive,
       
    66                                stdout = subprocess.PIPE)
       
    67     for line in process.stdout.readlines():
       
    68         yield line
       
    69 
       
    70 def replaceDefines(line, defines):
       
    71     ''' Replace the macro definitions recursively '''
       
    72     oldLine = None
       
    73     while oldLine != line:
       
    74         oldLine = line
       
    75         for search, replace in defines:
       
    76             line = line.replace(search, replace)
       
    77     return line
       
    78 
       
    79 def parseIby(lines):
       
    80     ''' Parse IBY file lines, using defines '''
       
    81     defines = BASE_DEFINES[:]
       
    82     for l in lines:
       
    83         defineMatch = RE_DEFINE.match(l)
       
    84         if defineMatch:
       
    85             search, replace = defineMatch.groups()
       
    86             defines.append((search, replace))
       
    87         else:
       
    88             yield replaceDefines(l, defines)
       
    89             
       
    90 def getIbySources(ibyfile, cppDefines = None, include = None):
       
    91     ''' Get IBY file source files'''
       
    92     drive, tail = os.path.splitdrive(ibyfile)
       
    93     for l in parseIby(cppFile(ibyfile, cppDefines, include)):
       
    94         match = RE_IBYTARGET.match(l)
       
    95         if match:
       
    96             yield drive + match.group(1)
       
    97 
       
    98 def findFiles(root, pattern):
       
    99     ''' Find files recursively '''
       
   100     for path, dirs, files in os.walk(root):
       
   101         for f in fnmatch.filter(files, pattern):
       
   102             yield os.path.join(path, f)
       
   103 
       
   104 def getMmpTarget(mmpfile):
       
   105     ''' Get the target binary name from mmp-file '''
       
   106     for l in open(mmpfile, "r").readlines():
       
   107         match = RE_MMP_TARGET.search(l)
       
   108         if match:
       
   109             return match.group(1)
       
   110     return None
       
   111 
       
   112 def getMmpTargets(mmpRoot):
       
   113     ''' Get the target binary paths of all mmp-files found under root '''
       
   114     drive, tail = os.path.splitdrive(mmpRoot)
       
   115 
       
   116     totalSize = 0
       
   117     for mmp in findFiles(mmpRoot, "*.mmp"):
       
   118         target = getMmpTarget(mmp)
       
   119         if not target:
       
   120             continue
       
   121         binPath = drive + "/epoc32/release/armv5/urel/" + target
       
   122         if os.path.exists(binPath):
       
   123             yield binPath
       
   124 
       
   125 def getSizes(binaries):
       
   126     ''' Get the sizes of given files, return list of tuples (size|None, file) '''
       
   127     for binPath in binaries:
       
   128         if not os.path.exists(binPath):
       
   129             yield None, binPath
       
   130         else:
       
   131             yield os.path.getsize(binPath), binPath
       
   132 
       
   133 def printSizes(sizes):
       
   134     ''' Print file sizes and a total '''
       
   135     totalSize = 0
       
   136     for size, binPath in sizes:
       
   137         if size:
       
   138             totalSize = totalSize + size
       
   139 
       
   140         print "%s\t%s" % (size, binPath)
       
   141 
       
   142     print "%d\tTotal" % totalSize
       
   143 
       
   144 def main():
       
   145     parser = OptionParser()
       
   146     parser.add_option(
       
   147         "--mmproot", dest = "mmproot",
       
   148         help = "root for searching mmp-files (whose target is used for filenames)")
       
   149     parser.add_option(
       
   150         "--ibyfile", dest = "ibyfile",
       
   151         help = "ibyfile(s) which are processed to find out target file paths")
       
   152     parser.add_option(
       
   153         "--sortname", dest = "sortname", action = "store_true", default = False,
       
   154         help = "sort files by name")
       
   155     parser.add_option(
       
   156         "--sortsize", dest = "sortsize", action = "store_true", default = False,
       
   157         help = "sort files by size")
       
   158     parser.add_option(
       
   159         "--cppdefines", dest = "cppdefines",
       
   160         help = "cpp defines for iby, e.g. --cppdefines __JAVA,__JAVA_MIDP20")
       
   161     parser.add_option(
       
   162         "--include", dest = "include",
       
   163         help = "Include file for iby cpp, e.g. --include /epoc32/include/config/ncp52/bldpublic.hrh")
       
   164     (options, args) = parser.parse_args()
       
   165 
       
   166     if options.mmproot:
       
   167         binaries = getMmpTargets(options.mmproot)
       
   168 
       
   169     elif options.ibyfile:
       
   170         cppdefines = options.cppdefines and options.cppdefines.split(",")
       
   171 
       
   172         ibys = glob.glob(options.ibyfile)
       
   173         binaries = flatten([getIbySources(iby, cppdefines, options.include) 
       
   174                             for iby in ibys])
       
   175         
       
   176     sizes = getSizes(binaries)
       
   177 
       
   178     if options.sortsize:
       
   179         sizes = sorted(sizes)
       
   180     if options.sortname:
       
   181         sizes = sorted(sizes, cmp=lambda x,y: cmp(x[1], y[1]))
       
   182 
       
   183     printSizes(sizes)
       
   184 
       
   185 if __name__ == "__main__":
       
   186     main()