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: Edouard@1387: import os, re, operator Edouard@1387: from util.ProcessLogger import ProcessLogger Edouard@1387: import hashlib Edouard@1387: Edouard@1387: import time Edouard@1387: Edouard@1387: includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') Edouard@1387: Edouard@1402: Edouard@1402: if os.name == 'nt': Edouard@1402: # on windows, desktop shortcut launches Beremiz.py Edouard@1402: # with working dir set to mingw/bin. Edouard@1402: # then we prefix CWD to PATH in order to ensure that Edouard@1402: # commands invoked from Makefiles will first resolve Edouard@1402: # to here. Edouard@1402: os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] Edouard@1402: Edouard@1387: class toolchain_makefile(): Edouard@1387: def __init__(self, CTRInstance): Edouard@1387: self.CTRInstance = CTRInstance Edouard@1387: self.md5key = None Edouard@1387: self.buildpath = None Edouard@1387: self.SetBuildPath(self.CTRInstance._getBuildPath()) Edouard@1387: Edouard@1387: def SetBuildPath(self, buildpath): Edouard@1387: if self.buildpath != buildpath: Edouard@1387: self.buildpath = buildpath Edouard@1387: self.md5key = None Edouard@1387: Edouard@1387: def GetBinaryCode(self): Edouard@1387: return None Edouard@1387: Edouard@1387: def _GetMD5FileName(self): Edouard@1387: return os.path.join(self.buildpath, "lastbuildPLC.md5") Edouard@1387: Edouard@1387: def ResetBinaryCodeMD5(self): Edouard@1387: self.md5key = None Edouard@1387: try: Edouard@1387: os.remove(self._GetMD5FileName()) Edouard@1387: except Exception, e: Edouard@1387: pass Edouard@1387: Edouard@1387: def GetBinaryCodeMD5(self): Edouard@1387: if self.md5key is not None: Edouard@1387: return self.md5key Edouard@1387: else: Edouard@1387: try: Edouard@1387: return open(self._GetMD5FileName(), "r").read() Edouard@1387: except IOError, e: Edouard@1387: return None Edouard@1387: Edouard@1387: def concat_deps(self, bn): Edouard@1387: # read source Edouard@1387: src = open(os.path.join(self.buildpath, bn),"r").read() Edouard@1387: # update direct dependencies Edouard@1387: deps = [] Edouard@1387: for l in src.splitlines(): Edouard@1387: res = includes_re.match(l) Edouard@1387: if res is not None: Edouard@1387: depfn = res.groups()[0] Edouard@1387: if os.path.exists(os.path.join(self.buildpath, depfn)): Edouard@1387: #print bn + " depends on "+depfn Edouard@1387: deps.append(depfn) Edouard@1387: # recurse through deps Edouard@1387: # TODO detect cicular deps. Edouard@1387: return reduce(operator.concat, map(self.concat_deps, deps), src) Edouard@1387: Edouard@1387: def build(self): Edouard@1387: srcfiles= [] Edouard@1387: cflags = [] Edouard@1387: wholesrcdata = "" Edouard@1387: for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: Edouard@1387: # Get CFiles list to give it to makefile Edouard@1387: for CFile, CFLAGS in CFilesAndCFLAGS: Edouard@1387: CFileName = os.path.basename(CFile) Edouard@1387: wholesrcdata += self.concat_deps(CFileName) Edouard@1387: srcfiles.append(CFileName) Edouard@1387: if CFLAGS not in cflags: Edouard@1387: cflags.append(CFLAGS) Edouard@1387: Edouard@1387: oldmd5 = self.md5key Edouard@1387: self.md5key = hashlib.md5(wholesrcdata).hexdigest() Edouard@1387: props = self.CTRInstance.GetProjectProperties() Edouard@1387: self.md5key += '#'.join([props[key] for key in ['companyName', Edouard@1387: 'projectName', Edouard@1387: 'productName']]) Edouard@1387: self.md5key += '#' #+','.join(map(str,time.localtime())) Edouard@1387: # Store new PLC filename based on md5 key Edouard@1387: f = open(self._GetMD5FileName(), "w") Edouard@1387: f.write(self.md5key) Edouard@1387: f.close() Edouard@1387: Edouard@1387: if oldmd5 != self.md5key : Edouard@1387: target = self.CTRInstance.GetTarget().getcontent() Edouard@1387: beremizcommand = {"src": ' '.join(srcfiles), Edouard@1387: "cflags": ' '.join(cflags), Edouard@1387: "md5": self.md5key, Edouard@1387: "buildpath": self.buildpath Edouard@1387: } Edouard@1387: andrej@1543: # clean sequence of multiple whitespaces andrej@1543: cmd = re.sub(r"[ ]+", " ", target.getCommand()) andrej@1543: andrej@1543: command = [ token % beremizcommand for token in cmd.split(' ')] Edouard@1387: Edouard@1387: # Call Makefile to build PLC code and link it with target specific code Edouard@1387: status, result, err_result = ProcessLogger(self.CTRInstance.logger, Edouard@1387: command).spin() Edouard@1387: if status : Edouard@1387: self.md5key = None Edouard@1387: self.CTRInstance.logger.write_error(_("C compilation failed.\n")) Edouard@1387: return False Edouard@1387: return True Edouard@1387: else : Edouard@1387: self.CTRInstance.logger.write(_("Source didn't change, no build.\n")) Edouard@1387: return True Edouard@1387: