etisserant@285: import os, re, operator
Edouard@726: from util.ProcessLogger import ProcessLogger
etisserant@203: import hashlib
etisserant@203: 
etisserant@285: includes_re =  re.compile('\s*#include\s*["<]([^">]*)[">].*')
etisserant@285: 
etisserant@203: class toolchain_gcc():
etisserant@203:     """
etisserant@203:     This abstract class contains GCC specific code.
etisserant@203:     It cannot be used as this and should be inherited in a target specific
etisserant@203:     class such as target_linux or target_win32
etisserant@203:     """
Edouard@725:     def __init__(self, CTRInstance):
Edouard@725:         self.CTRInstance = CTRInstance
greg@427:         self.buildpath = None
Edouard@725:         self.SetBuildPath(self.CTRInstance._getBuildPath())
laurent@510:     
etisserant@297:     def getBuilderCFLAGS(self):
etisserant@290:         """
etisserant@290:         Returns list of builder specific CFLAGS
etisserant@290:         """
Laurent@1315:         return [self.CTRInstance.GetTarget().getcontent().getCFLAGS()]
etisserant@290: 
etisserant@297:     def getBuilderLDFLAGS(self):
etisserant@290:         """
etisserant@290:         Returns list of builder specific LDFLAGS
etisserant@290:         """
Edouard@725:         return self.CTRInstance.LDFLAGS + \
Laurent@1315:                [self.CTRInstance.GetTarget().getcontent().getLDFLAGS()]
etisserant@290: 
etisserant@203:     def GetBinaryCode(self):
etisserant@203:         try:
etisserant@203:             return open(self.exe_path, "rb").read()
etisserant@203:         except Exception, e:
etisserant@203:             return None
etisserant@203:         
etisserant@203:     def _GetMD5FileName(self):
etisserant@203:         return os.path.join(self.buildpath, "lastbuildPLC.md5")
etisserant@203: 
laurent@677:     def ResetBinaryCodeMD5(self):
laurent@677:         self.md5key = None
laurent@677:         try:
laurent@677:             os.remove(self._GetMD5FileName())
laurent@677:         except Exception, e:
laurent@677:             pass
laurent@677:     
etisserant@203:     def GetBinaryCodeMD5(self):
etisserant@203:         if self.md5key is not None:
etisserant@203:             return self.md5key
etisserant@203:         else:
etisserant@203:             try:
etisserant@203:                 return open(self._GetMD5FileName(), "r").read()
etisserant@203:             except Exception, e:
etisserant@203:                 return None
greg@427:     
greg@427:     def SetBuildPath(self, buildpath):
greg@427:         if self.buildpath != buildpath:
greg@427:             self.buildpath = buildpath
Edouard@725:             self.exe = self.CTRInstance.GetProjectName() + self.extension
greg@427:             self.exe_path = os.path.join(self.buildpath, self.exe)
greg@427:             self.md5key = None
greg@427:             self.srcmd5 = {}
greg@427:     
etisserant@285:     def check_and_update_hash_and_deps(self, bn):
etisserant@285:         # Get latest computed hash and deps
etisserant@285:         oldhash, deps = self.srcmd5.get(bn,(None,[]))
etisserant@285:         # read source
etisserant@285:         src = open(os.path.join(self.buildpath, bn)).read()
etisserant@285:         # compute new hash
etisserant@285:         newhash = hashlib.md5(src).hexdigest()
etisserant@285:         # compare
etisserant@285:         match = (oldhash == newhash)
etisserant@285:         if not match:
etisserant@285:             # file have changed
etisserant@285:             # update direct dependencies
etisserant@285:             deps = []
etisserant@285:             for l in src.splitlines():
etisserant@285:                 res = includes_re.match(l)
etisserant@285:                 if res is not None:
etisserant@285:                     depfn = res.groups()[0]
etisserant@285:                     if os.path.exists(os.path.join(self.buildpath, depfn)):
etisserant@285:                         #print bn + " depends on "+depfn
etisserant@285:                         deps.append(depfn)
etisserant@285:             # store that hashand deps
etisserant@285:             self.srcmd5[bn] = (newhash, deps)
etisserant@285:         # recurse through deps
etisserant@285:         # TODO detect cicular deps.
etisserant@285:         return reduce(operator.and_, map(self.check_and_update_hash_and_deps, deps), match)
etisserant@203:                 
etisserant@203:     def build(self):
etisserant@203:         # Retrieve toolchain user parameters
Laurent@1315:         toolchain_params = self.CTRInstance.GetTarget().getcontent()
etisserant@203:         self.compiler = toolchain_params.getCompiler()
etisserant@203:         self.linker = toolchain_params.getLinker()
etisserant@290: 
etisserant@290:         Builder_CFLAGS = ' '.join(self.getBuilderCFLAGS())
etisserant@203: 
etisserant@203:         ######### GENERATE OBJECT FILES ########################################
etisserant@203:         obns = []
etisserant@203:         objs = []
Laurent@1023:         relink = self.GetBinaryCode() is None
Edouard@725:         for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS:
Edouard@728:             if CFilesAndCFLAGS:
Edouard@728:                 if Location :
Edouard@728:                     self.CTRInstance.logger.write(".".join(map(str,Location))+" :\n")
Edouard@728:                 else:
Edouard@728:                     self.CTRInstance.logger.write(_("PLC :\n"))
etisserant@203:                 
etisserant@203:             for CFile, CFLAGS in CFilesAndCFLAGS:
Edouard@693:                 if CFile.endswith(".c"):
Edouard@693:                     bn = os.path.basename(CFile)
Edouard@693:                     obn = os.path.splitext(bn)[0]+".o"
Edouard@693:                     objectfilename = os.path.splitext(CFile)[0]+".o"
etisserant@285: 
Edouard@693:                     match = self.check_and_update_hash_and_deps(bn)
Edouard@693:                     
Edouard@693:                     if match:
Edouard@725:                         self.CTRInstance.logger.write("   [pass]  "+bn+" -> "+obn+"\n")
Edouard@693:                     else:
Edouard@693:                         relink = True
etisserant@285: 
Edouard@725:                         self.CTRInstance.logger.write("   [CC]  "+bn+" -> "+obn+"\n")
Edouard@693:                         
Edouard@693:                         status, result, err_result = ProcessLogger(
Edouard@725:                                self.CTRInstance.logger,
Edouard@693:                                "\"%s\" -c \"%s\" -o \"%s\" %s %s"%
Edouard@693:                                    (self.compiler, CFile, objectfilename, Builder_CFLAGS, CFLAGS)
Edouard@693:                                ).spin()
etisserant@285: 
Edouard@693:                         if status :
Edouard@693:                             self.srcmd5.pop(bn)
Edouard@725:                             self.CTRInstance.logger.write_error(_("C compilation of %s failed.\n")%bn)
Edouard@693:                             return False
Edouard@693:                     obns.append(obn)
Edouard@693:                     objs.append(objectfilename)
Edouard@693:                 elif CFile.endswith(".o"):
Edouard@693:                     obns.append(os.path.basename(CFile))
Edouard@693:                     objs.append(CFile)
etisserant@203: 
etisserant@203:         ######### GENERATE library FILE ########################################
etisserant@203:         # Link all the object files into one binary file
Edouard@725:         self.CTRInstance.logger.write(_("Linking :\n"))
etisserant@285:         if relink:
etisserant@285:             objstring = []
etisserant@285:     
etisserant@285:             # Generate list .o files
etisserant@285:             listobjstring = '"' + '"  "'.join(objs) + '"'
etisserant@285:     
etisserant@290:             ALLldflags = ' '.join(self.getBuilderLDFLAGS())
etisserant@285:     
Edouard@725:             self.CTRInstance.logger.write("   [CC]  " + ' '.join(obns)+" -> " + self.exe + "\n")
etisserant@285:     
etisserant@285:             status, result, err_result = ProcessLogger(
Edouard@725:                    self.CTRInstance.logger,
etisserant@285:                    "\"%s\" %s -o \"%s\" %s"%
etisserant@285:                        (self.linker,
etisserant@285:                         listobjstring,
etisserant@285:                         self.exe_path,
etisserant@285:                         ALLldflags)
etisserant@285:                    ).spin()
etisserant@285:             
etisserant@285:             if status :
etisserant@285:                 return False
laurent@677:                 
etisserant@285:         else:
Edouard@725:             self.CTRInstance.logger.write("   [pass]  " + ' '.join(obns)+" -> " + self.exe + "\n")
laurent@677:         
laurent@677:         # Calculate md5 key and get data for the new created PLC
laurent@677:         data=self.GetBinaryCode()
laurent@677:         self.md5key = hashlib.md5(data).hexdigest()
laurent@677: 
laurent@677:         # Store new PLC filename based on md5 key
laurent@677:         f = open(self._GetMD5FileName(), "w")
laurent@677:         f.write(self.md5key)
laurent@677:         f.close()
etisserant@203:         
etisserant@203:         return True
etisserant@203: