andrej@1511: #!/usr/bin/env python andrej@1511: # -*- coding: utf-8 -*- andrej@1511: andrej@1511: # This file is part of Beremiz, a Integrated Development Environment for andrej@1511: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1511: # andrej@1511: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1511: # andrej@1511: # See COPYING file for copyrights details. andrej@1511: # andrej@1511: # This program is free software; you can redistribute it and/or andrej@1511: # modify it under the terms of the GNU General Public License andrej@1511: # as published by the Free Software Foundation; either version 2 andrej@1511: # of the License, or (at your option) any later version. andrej@1511: # andrej@1511: # This program is distributed in the hope that it will be useful, andrej@1511: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1511: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1511: # GNU General Public License for more details. andrej@1511: # andrej@1511: # You should have received a copy of the GNU General Public License andrej@1511: # along with this program; if not, write to the Free Software andrej@1511: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. andrej@1511: 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: