build/buildutils/export.py
branchRCL_3
changeset 19 04becd199f91
equal deleted inserted replaced
16:f5050f1da672 19:04becd199f91
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description: 
       
    15 #
       
    16 #! /usr/bin/python
       
    17 
       
    18 import sys
       
    19 import traceback
       
    20 import os
       
    21 import os.path
       
    22 import sys
       
    23 #import subprocess
       
    24 import shutil
       
    25 import re
       
    26 import types
       
    27 import zipfile
       
    28 
       
    29 from os.path import abspath
       
    30 
       
    31 class BuildError(Exception):
       
    32     pass
       
    33 
       
    34 def main():
       
    35     tmpExtension = ".cpp"
       
    36     try:
       
    37         args = sys.argv[1:]
       
    38 
       
    39         # Get macro definitios
       
    40         macros = [a[2:] for a in args if a.startswith("-D")]
       
    41         args = [a for a in args if not a.startswith("-D")]
       
    42 
       
    43         opcode = args[0]
       
    44         fileName = args[1]
       
    45 
       
    46         exports = readExports(fileName, fileName+tmpExtension, macros)
       
    47         for src, dst in exports:
       
    48             isZipFile = False
       
    49             if src.startswith("___zip___"):
       
    50                 isZipFile = True
       
    51                 src = src[9:]
       
    52 
       
    53             if not os.path.exists(src):
       
    54                 print "Exported file "+src+" doesn't exist"
       
    55             else:    
       
    56                 if opcode.lower() == "clean":
       
    57                     if isZipFile:
       
    58                         handleZipCommand(src, dst, doClean = True)
       
    59                     else:
       
    60                         delFile(dst)
       
    61                 else:
       
    62                     if isZipFile:
       
    63                         handleZipCommand(src, dst, doClean = False)
       
    64                     else:
       
    65                         updateFile(dst, src)
       
    66     except:
       
    67         print "Error during export "
       
    68         traceback.print_exc()
       
    69         sys.exit(-1)
       
    70 
       
    71     delFile(fileName+tmpExtension, False)
       
    72 
       
    73 def readExports(fileName, cppedFile, macros):
       
    74 
       
    75     if not os.path.exists(fileName):
       
    76         print "No %s, nothing to export" % fileName
       
    77         return ()
       
    78 
       
    79     # Preprocess bld.inf        
       
    80 
       
    81     args = [
       
    82         '-undef',
       
    83         '-nostdinc']
       
    84 
       
    85     if os.name=="nt":
       
    86         args = [
       
    87             '-undef',
       
    88             '-traditional', 
       
    89             '-+', 
       
    90             '-nostdinc']
       
    91 
       
    92     cppFile(
       
    93         fileName,
       
    94         cppedFile, 
       
    95         args,
       
    96         macros)
       
    97 
       
    98     lines = readFile(cppedFile)
       
    99 
       
   100     EXPORTS = {}
       
   101 
       
   102     bldInf = {}
       
   103 
       
   104     # Keeps track of which inf-file's contents inside the preprocessed
       
   105     # bld.inf-file are being processed
       
   106     infFile = None
       
   107 
       
   108     section = None
       
   109 
       
   110     # Read in bld.inf contents
       
   111     for line in [l.strip() for l in lines if len(l.strip()) > 0]:
       
   112         if line.startswith("#"):
       
   113             # Comment line indicates which inf-file any subsequent
       
   114             # lines were included from
       
   115 
       
   116             # Parse inf-file name
       
   117             infFile = re.search(r'^# \d+ "(.+)"', line).group(1)
       
   118 
       
   119             # Translate backslashes to slashes, remove dupes
       
   120             infFile = abspath(re.sub(r"\\+", "/", infFile))
       
   121         elif line.lower().startswith("prj_"):
       
   122             section = line.lower()[4:]
       
   123         else:
       
   124             if not section:
       
   125                 raise BuildError, \
       
   126                     "Data outside of data section in bld.inf: %s" % line
       
   127 
       
   128             # Collect lines by section (prj_exports etc), each entry is a
       
   129             # tuple of the actual data line and the name of the inf-file
       
   130             # where it originated from
       
   131             bldInf.setdefault(
       
   132                 section, []).append((line.strip(), infFile))
       
   133 
       
   134     # Collect exports to a separate list
       
   135     for export, infFile in bldInf.get("exports", []):
       
   136         isZipFile = False
       
   137         if export.strip().lower().startswith(":zip"):
       
   138             isZipFile = True
       
   139             export = export[5:]
       
   140         try:
       
   141             src, dst = export.split()
       
   142         except:
       
   143             raise BuildError, \
       
   144                 'Malformed line "%s" in %s' % (export, infFile)
       
   145 
       
   146         # Map source path through current inf file path
       
   147         if not src.startswith("\\") and not src.startswith("/"):
       
   148             src = os.path.join(os.path.split(infFile)[0], src)
       
   149 
       
   150         if dst.startswith("\\") or dst.startswith("/"):
       
   151             # Destination path is absolute, use it as is
       
   152             pass
       
   153         elif dst.startswith("|"):
       
   154             # Destination path is relative to the inf-file it was specified
       
   155             # in 
       
   156             dst = os.path.join(os.path.dirname(infFile), dst[1:])
       
   157 
       
   158         src, dst = map(abspath, [src, dst])    
       
   159         if isZipFile:
       
   160             src = "___zip___"+src
       
   161         EXPORTS[(src, dst)] = None
       
   162 
       
   163     return EXPORTS.keys()
       
   164 
       
   165 def handleZipCommand(zipfl, dirToUnzip, doClean = False):
       
   166     zfobj = zipfile.ZipFile(zipfl)
       
   167     if doClean:
       
   168         print("Deleting files extracted from %s:" % (zipfl))
       
   169     else:
       
   170         print("Extracting files from %s:" % (zipfl))
       
   171     for name in zfobj.namelist():
       
   172         #Not handling cases where zip file contains empty dirs
       
   173         if not name.endswith('/'):
       
   174             print("    " + os.path.join(dirToUnzip,name))
       
   175             destFile = os.path.join(dirToUnzip, name)
       
   176             if doClean:
       
   177                 delFile(destFile, doPrint = False)
       
   178             else:
       
   179                 makeDirs(destFile, isFile = True)
       
   180                 if uptodate(destFile, zipfl):
       
   181                     print("        Already extracted...")
       
   182                     continue
       
   183                 outfile = open(destFile, 'wb')
       
   184                 outfile.write(zfobj.read(name))
       
   185                 outfile.close()
       
   186 
       
   187 
       
   188 
       
   189 def updateFile(dst, src):
       
   190     if not uptodate(dst, src):
       
   191         print("Copying %s to %s" % (src, dst))
       
   192         shutil.copy(src, dst)
       
   193 
       
   194 def oldestTime(files):
       
   195     oldestTime = None
       
   196     oldestFile = None
       
   197     try:
       
   198         for f in files:
       
   199             fileTime = os.path.getmtime(f)
       
   200             if oldestTime == None or fileTime < oldestTime:
       
   201                 oldestTime = fileTime
       
   202                 oldestFile = f
       
   203     except:
       
   204         # One or more files don't exist
       
   205         return 0
       
   206 
       
   207     #print "oldest file is %s with %d" % (oldestFile, oldestTime)
       
   208     return oldestTime    
       
   209 
       
   210 def newestTime(files):
       
   211     newest = 0
       
   212     newestFile = None
       
   213 
       
   214     for f in files:
       
   215         try:
       
   216             fileTime = os.path.getmtime(f)
       
   217         except:
       
   218             print("Expected to find file " + f)
       
   219         else:    
       
   220             if fileTime > newest:
       
   221                 newestFile = f
       
   222                 newest = fileTime
       
   223 
       
   224     #print "newest file is %s with %d" % (newestFile, newest)
       
   225     return newest        
       
   226 
       
   227 def uptodate(targets, sources):
       
   228 
       
   229     # Make non-list arguments lists
       
   230     if not isinstance(targets, list):
       
   231         targets = [targets]
       
   232     if not isinstance(sources, list):
       
   233         sources = [sources]
       
   234 
       
   235     if oldestTime(targets) < newestTime(sources):
       
   236         # One or more target files don't exist or are older than one or
       
   237         # more source files, rebuild is needed
       
   238         result = False
       
   239     else:
       
   240         # Rebuild is not needed
       
   241         result = True
       
   242     
       
   243 
       
   244     # Create missing directories
       
   245     if result == False:
       
   246         for f in targets:
       
   247             dirName = os.path.dirname(f)
       
   248             if not os.path.exists(dirName):
       
   249                 os.makedirs(dirName)
       
   250 
       
   251     return result
       
   252 
       
   253 
       
   254 def cppFile(
       
   255         srcFile, 
       
   256         dstFile, 
       
   257         args, 
       
   258         macros = []):
       
   259 
       
   260     """ Preprocesses a file with CPP using given arguments. 
       
   261 
       
   262     By default dstFile is altered only if running CPP would change the file's
       
   263     contents. 
       
   264 
       
   265     Returns true if dstFile was altered. 
       
   266     """
       
   267 
       
   268     # Target file doesn't exist or preprocessing was forced
       
   269 
       
   270     # Create any missing directories for destination file
       
   271     makeDirs(dstFile, isFile = True)
       
   272 
       
   273     for m in macros:
       
   274         if len(m) == 0:
       
   275             raise BuildError, "empty macro definition"
       
   276         if m == "NOKIA_SYMBIAN_BLD":
       
   277             args.extend([
       
   278                 "-D__GNUC__=4",
       
   279                 "-I " + abspath("\\epoc32\\include\\"),
       
   280                 "-imacros " + abspath("\\epoc32\\include\\platform_paths.hrh")])
       
   281 
       
   282     cmd = ['cpp'] + \
       
   283         args + \
       
   284         ["-D" + m for m in macros] +\
       
   285         [srcFile, \
       
   286         dstFile]
       
   287 
       
   288     print " ".join(cmd)
       
   289 
       
   290     execute(cmd, False)
       
   291     return True
       
   292 
       
   293 
       
   294 def makeDirs(directory, isFile = False):
       
   295     """ Creates a directory if it doesn't exist. 
       
   296 
       
   297     Doesn't do anything if the directory already exists. 
       
   298 
       
   299     If isFile is True, the last element of the directory path is assumed to be
       
   300     a file and is discarded from the path to be created. """
       
   301 
       
   302     if isFile:
       
   303         directory = os.path.dirname(directory)
       
   304 
       
   305     try:
       
   306         os.makedirs(directory)
       
   307     except:
       
   308         pass
       
   309 
       
   310 def readFile(f):
       
   311     """ Reads contents of a file to a list and returns it. """
       
   312 
       
   313     f = open(f, 'r')
       
   314     contents = f.readlines()
       
   315     f.close()
       
   316 
       
   317     return contents
       
   318 
       
   319 def delFile(file, doPrint = True):
       
   320     """ Deletes a file.
       
   321 
       
   322     Fails if the file appears to be a directory. """
       
   323 
       
   324     if os.path.exists(file):
       
   325         if os.path.isfile(file):
       
   326             if doPrint:
       
   327                 print("Deleting file " + file)
       
   328             os.remove(file)
       
   329         else:    
       
   330             print('Can not delete file "%s", is a directory' % (file))
       
   331     
       
   332 def execute(
       
   333         cmd, 
       
   334         printMsg = True, 
       
   335         haltOnError = True, 
       
   336         input = None, 
       
   337         grabOutput = False,
       
   338         *args, 
       
   339         **kwd):
       
   340     """ Executes external command, keeps track of time spent outside. 
       
   341 
       
   342     Any arguments are passed directly to subprocess.call(). """
       
   343 
       
   344 
       
   345     if printMsg:
       
   346         # Construct string to be displayed on screen
       
   347         msg = ""
       
   348         if kwd.has_key('cwd'):
       
   349             msg = "[" + kwd['cwd'] + "] "
       
   350         if type(cmd) == types.StringType:
       
   351             msg += cmd
       
   352         elif type(cmd) == types.ListType:    
       
   353             msg += " ".join(cmd)   
       
   354         else:
       
   355             raise Error, "Invalid type %s for cmd argument" % type(cmd)
       
   356         print msg
       
   357 
       
   358     kwd['args'] = cmd    
       
   359 
       
   360 
       
   361     """
       
   362     if input:
       
   363         # Input data for command was specified, pipe it
       
   364         kwd['stdin'] = subprocess.PIPE
       
   365     if grabOutput:
       
   366         # Command output should be grabbed
       
   367         kwd['stdout'] = subprocess.PIPE
       
   368         kwd['stderr'] = subprocess.PIPE
       
   369         
       
   370     process = subprocess.Popen(*args, **kwd)
       
   371 
       
   372     if input or grabOutput:
       
   373         stdout, stderr = process.communicate(input)
       
   374     else:
       
   375         process.wait()
       
   376         
       
   377     result = process.returncode
       
   378     """
       
   379     cmd2 = " ".join(cmd)
       
   380     result = os.system(" ".join(cmd))
       
   381     if not result == 0 and haltOnError == True:
       
   382         print 'Error running external command "' + \
       
   383             (type(cmd) == types.StringType and cmd or ' '.join(cmd)) + \
       
   384             '", return code ' + str(result)
       
   385 
       
   386     return result
       
   387 
       
   388         
       
   389 if __name__ == "__main__":
       
   390     main()