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: 
kinsamanka@3750: 
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.
kinsamanka@3750:         return reduce(operator.concat, list(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