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: andrej@1881: andrej@1881: from __future__ import absolute_import andrej@1732: import os andrej@1732: import re andrej@1732: import operator andrej@1832: import hashlib andrej@2456: from functools import reduce Edouard@1387: from util.ProcessLogger import ProcessLogger Edouard@1387: Edouard@1387: andrej@2439: includes_re = re.compile(r'\s*#include\s*["<]([^">]*)[">].*') Edouard@1387: andrej@1736: andrej@1831: class toolchain_makefile(object): Edouard@1387: def __init__(self, CTRInstance): Edouard@1387: self.CTRInstance = CTRInstance andrej@1730: 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@2463: def GetBinaryPath(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@2463: def ResetBinaryMD5(self): Edouard@1387: self.md5key = None Edouard@1387: try: Edouard@1387: os.remove(self._GetMD5FileName()) andrej@1846: except Exception: Edouard@1387: pass Edouard@1387: Edouard@2463: def GetBinaryMD5(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() andrej@1846: except IOError: Edouard@1387: return None Edouard@1387: Edouard@1387: def concat_deps(self, bn): Edouard@1387: # read source andrej@1740: 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)): andrej@1782: # 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): andrej@1742: srcfiles = [] Edouard@1387: cflags = [] andrej@1730: wholesrcdata = "" andrej@1847: 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) andrej@1730: Edouard@1387: oldmd5 = self.md5key Edouard@1387: self.md5key = hashlib.md5(wholesrcdata).hexdigest() andrej@1575: 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: andrej@1739: 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, andrej@1773: "buildpath": self.buildpath} andrej@1730: andrej@1730: # clean sequence of multiple whitespaces andrej@1709: cmd = re.sub(r"[ ]+", " ", target.getCommand().strip()) andrej@1543: andrej@1747: 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 andrej@1847: status, _result, _err_result = ProcessLogger(self.CTRInstance.logger, andrej@1847: command).spin() andrej@1739: 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 andrej@1739: else: Edouard@1387: self.CTRInstance.logger.write(_("Source didn't change, no build.\n")) Edouard@1387: return True