Esempio n. 1
0
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent

        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True

        self.TCHAIN = self.Configs.getCompiler()
        self.make = self.Configs.getMakeCmd()

        self.UserCode = None
        self.CleanBuild = True
        self.CompilerProcess = None  # todo: use QtCore.QProcess class instead

        self.LogList = QtCore.QStringList()
Esempio n. 2
0
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent
        
        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True
            
        self.TCHAIN = self.Configs.getCompiler()
        self.make = self.Configs.getMakeCmd()

        self.UserCode = None
        self.CleanBuild = True
        self.CompilerProcess = None # todo: use QtCore.QProcess class instead
        
        self.LogList = QtCore.QStringList()
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent
        
        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True
            
        self.CC = self.Configs.getCompiler()
        self.cflags = self.Configs.getCflags()
        self.lflags = self.Configs.getLflags()
            
        self.CompilerCommands = None            
        self.CompilerProcess = None # todo: use QtCore.QProcess class instead
        
        self.LogList = QtCore.QStringList()
Esempio n. 4
0
class GccCompilerThread(QtCore.QThread):
    '''
    classdocs
    '''
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent
        
        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True
            
        self.TCHAIN = self.Configs.getCompiler()
        self.make = self.Configs.getMakeCmd()

        self.UserCode = None
        self.CleanBuild = True
        self.CompilerProcess = None # todo: use QtCore.QProcess class instead
        
        self.LogList = QtCore.QStringList()
            
    def run(self):
        if not self.TCHAIN:
            print 'no supported compiler!'
            return
        
        self.LogList.clear()
        if not self.UserCode: # just get version info
            command = [ self.TCHAIN + 'gcc', '--version' ]
        else:
            projectName = os.path.splitext(os.path.basename(self.UserCode))[0]
            # output folder - same location with user code
            outpath = os.path.join( os.path.dirname(self.UserCode) , OUT_DIR )
            
            Result, Includes, Sources = parseUserCode( self.UserCode, outpath )
            #print Includes, Sources
            if not Result or not self.generateMakefile(outpath, projectName, Includes, Sources, self.CleanBuild):
                self.BuildProcess = None
                self.LogList.append( "<font color=red>file write error</font>" )
                return

            command = [ self.make , '-f'+os.path.join(outpath, MAKEFILE)]
            if self.CleanBuild:
                command.append('clean')
            command.append('all')
            
        try:
            self.CompilerProcess = subprocess.Popen( command,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT,
                         shell=self.isWin32Platform )
            error_count = 0
            while True:
                self.usleep(50000)
                if not self.CompilerProcess:
                    break;
                # read single lines    
                buff = self.CompilerProcess.stdout.readline()
                if buff == '': # got nothing
                    if self.CompilerProcess.poll() != None: # process exited
                        self.CompilerProcess = None
                        # print 'compiler process finished.'
                        break
                else:
                    msg = str(buff)
                    msg_lowered = msg.lower()
                    # string to QString
                    if msg_lowered.find("warning:") >= 0:
                        self.LogList.append( "<font color=orange>%s</font>" % msg )
                    # todo: other error messages
                    elif msg_lowered.find("error:") >= 0 \
                            or msg_lowered.find("make: ***") >= 0 \
                            or msg_lowered.find(": multiple definition") >= 0 \
                            or msg_lowered.find("undefined reference to") >= 0:                    
                        self.LogList.append( "<font color=red>%s</font>" % msg )
                        error_count += 1
                    else:
                        self.LogList.append( "<font color=green>%s</font>" % msg )
        except:
            print 'got errors in compiler thread!'
            self.LogList.append( "<font color=red>ERROR: build failed!</font>")
            self.LogList.append( "<font color=red>%s</font>" % self.TCHAIN)
            self.CompilerProcess = None
        
        if not error_count:
            self.LogList.append( "<font size=4 color=cyan>done.</font>" )
        else:
            self.LogList.append( "<font size=4 color=red>done with error(s) !</font>"  )
        
        print 'compiler thread done.'
            
    def getCompilerInfo(self):
        if self.isRunning():
            return None

        self.UserCode = None
        self.start()
        while True:
            self.usleep(1000)
            if not self.isRunning():
                break;            
        if not self.LogList.count():
            return None
        else:
            self.LogList.takeLast()
            info = ''
            for msg in self.LogList:
                info += msg
            return info                        

    def buildProject(self, userCode=None, cleanBuild=False):
        if self.isRunning():
            return False, "busy"
        if not os.path.isfile(userCode):
            return False, "file not found"
        
        self.UserCode = str(userCode)
        self.CleanBuild = cleanBuild
        self.start()
        return True, "Build process running. Please wait..."

    def pollBuildProcess(self, stopProcess=False):
        if self.isRunning() or self.LogList.count()>0:
            if stopProcess:
                self.LogList.clear()
                try:
                    self.CompilerProcess.kill() # needs Admin privilege on Windows!
                    self.CompilerProcess = None
                    self.exit()
                    return True, "killed"
                except:
                    print "n0 u can't kill me! :-p"
                    self.CompilerProcess.wait() # just wait for the process to finish
                    self.CompilerProcess = None
                    self.exit()
                    return False, "waited"             
            if self.LogList.count():
                return True, str(self.LogList.takeFirst())
            else:
                return True, ''
        else:
            return False, "process not running"
            
    def generateMakefile(self, outPath='.', projectName='a', includePaths='', sourceFiles='', verbose=False):
        objects = []
        try:
            fout = open( os.path.join(outPath, MAKEFILE), 'w' )
            fout.write( '#\n# Automatically generated Makefile\n#\n\n' )
            fout.write( 'PROJECT = ' + projectName + '\n\n' )
            fout.write( 'OUTPUT_DIR = ' + outPath + '\n' )
            fout.write( 'ELF_FILE = $(OUTPUT_DIR)/$(PROJECT).elf\n' )
            fout.write( 'BIN_FILE = $(OUTPUT_DIR)/$(PROJECT).bin\n' )
            fout.write( 'MAP_FILE = $(OUTPUT_DIR)/$(PROJECT).map\n' )
            fout.write( 'LKR_SCRIPT = ' + getLinkerScript() + '\n\n')
            fout.write( 'TCHAIN = ' + self.TCHAIN.replace('\\','/') + '\n\n' )
            fout.write( 'INCLUDES =  \\\n' )
            for path in includePaths:
                fout.write( '\t' + path + ' \\\n' )
            fout.write( '\n\n' )
            fout.write( 'DEFINES = ' + getCompilerDefines() + '\n')
            fout.write( 'CFLAGS = ' + self.Configs.getCflags() + ' $(DEFINES)\n')
            fout.write( 'CXXFLAGS = ' + self.Configs.getCxxflags() + ' $(DEFINES)\n')
            fout.write( 'AFLAGS = ' + self.Configs.getAflags() + '\n' )
            fout.write( 'LFLAGS = ' + self.Configs.getLflags() + '\n\n' )
            fout.write( 'LIBGCC = ${shell ${TCHAIN}gcc ${CFLAGS} -print-libgcc-file-name}\n' )
            fout.write( 'LIBC = ${shell ${TCHAIN}gcc ${CFLAGS} -print-file-name=libc.a}\n' )
            fout.write( 'LIBM = ${shell ${TCHAIN}gcc ${CFLAGS} -print-file-name=libm.a}\n' )
            fout.write( 'LIBNOSYS = ${shell ${TCHAIN}gcc ${CXXFLAGS} -print-file-name=libnosys.a}\n' )
            fout.write( 'LIBCPP = ${shell ${TCHAIN}g++ ${CXXFLAGS} -print-file-name=libstdc++.a}\n\n\n' )
            fout.write( 'RM = ' + self.Configs.getRmCmd() + '\n\n\n' )
            fout.write( 'OBJECTS =  \\\n' )
            for src in sourceFiles:
                src = str(src)
                folder, objname = os.path.split( src[:src.rfind('.')] + '.o' )
                if folder.find('libraries')!=0 and folder.find('hardware')!=0:
                    folder = 'user'
                objdir = os.path.join(outPath, 'obj', folder)
                if not os.path.exists(objdir): os.makedirs( objdir )
                obj = '$(OUTPUT_DIR)/obj/' + folder + '/' + objname
                fout.write( '\t' + obj + ' \\\n' )
                objects.append(obj)
            fout.write( '\n\n' )
            
            fout.write( 'all : $(BIN_FILE)\n' )
            fout.write( '\t@$(TCHAIN)size $(ELF_FILE)\n\n' )
            
            fout.write( 'clean:\n' )
            fout.write( '\t@$(RM) $(OBJECTS)\n' )
            fout.write( '\t@$(RM) $(OUTPUT_DIR)/$(PROJECT).*\n\n\n' )
            fout.write( '$(ELF_FILE): $(OBJECTS)\n' )
            
            fout.write( '\t@echo [LINKER] $(@F)\n\t')
            if not verbose: fout.write( '@' )
            fout.write( '$(TCHAIN)ld $(LFLAGS) $^ -o $@ $(LIBGCC) $(LIBC) $(LIBM) $(LIBNOSYS) $(LIBCPP)\n\n' )
            
            fout.write( '$(BIN_FILE): $(ELF_FILE)\n' )
            fout.write( '\t@echo [BIN Copy] $(@F)\n')
            fout.write( '\t@$(TCHAIN)objcopy -Obinary $< $@\n\n\n' )
            
            i = 0
            for src in sourceFiles:
                src = str(src)
                fout.write( objects[i] + ' : ' + src + '\n')
                src_ext = os.path.splitext(src)[1].lower()
                if src_ext == USER_CODE_EXT:
                    fout.write( '\t@echo [CXX] $< \n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)gcc $(INCLUDES) $(CXXFLAGS) -x c++ $< -o $@\n\n')                        
                elif src_ext == '.s':
                    fout.write( '\t@echo [AS] $(<F)\n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)as $(AFLAGS) $< -o $@\n\n')
                elif src_ext == '.c':
                    fout.write( '\t@echo [CC] $(<F)\n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)gcc $(INCLUDES) $(CFLAGS) $< -o $@\n\n')
                elif src_ext == '.cpp':
                    fout.write( '\t@echo [CPP] $(<F)\n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)g++ $(INCLUDES) $(CXXFLAGS) $< -o $@\n\n')
                    
                i += 1
            fout.close()
            return True
        except:
            return False
            
    def getExpectedBinFileName(self, userCode=None):
        if not userCode:
            return None
        outpath = os.path.dirname( str(userCode) ) + '/' + OUT_DIR
        fname = os.path.basename( str(userCode) )
        dotpos = fname.rfind('.')
        if dotpos > 0:
            binfile = outpath + '/' + fname[:dotpos] + '.bin'
        else:
            binfile = outpath + '/' + fname + '.bin'
        return binfile
Esempio n. 5
0
class GccCompilerThread(QtCore.QThread):
    '''
    classdocs
    '''
    # enum tasks
    GET_INFO = 0
    BUILD_PROJECT = 1
    PROGRAM_HEX = 2
    _task = GET_INFO

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent

        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True

        self.TCHAIN = self.Configs.getCompiler()
        self.make = self.Configs.getMakeCmd()

        self.McuPart = None
        self.UserCode = None
        self.CleanBuild = True
        self.serialPortName = None
        self.CompilerProcess = None  # todo: use QtCore.QProcess class instead

        self.LogList = QtCore.QStringList()

    def run(self):
        if not self.TCHAIN:
            print 'no supported compiler!'
            return

        self.LogList.clear()
        if self._task == self.GET_INFO:
            command = [self.TCHAIN + 'gcc', '--version']
        elif self._task == self.PROGRAM_HEX:
            # output folder - same location with user code
            outpath = os.path.join(os.path.dirname(self.UserCode), OUT_DIR)
            command = [self.make, '-f' + os.path.join(outpath, MAKEFILE)]
            command.append('COMPORT=%s' % self.serialPortName)
            command.append('program')
            #print command
        else:  # self._task == self.BUILD_PROJECT
            projectName = os.path.splitext(os.path.basename(self.UserCode))[0]
            # output folder - same location with user code
            outpath = os.path.join(os.path.dirname(self.UserCode), OUT_DIR)

            Result, Includes, Sources = parseUserCode(self.UserCode, outpath,
                                                      self.McuPart)
            #print Includes, Sources
            if not Result or not self.generateMakefile(
                    outpath, projectName, Includes, Sources, self.CleanBuild):
                self.BuildProcess = None
                self.LogList.append("<font color=red>file write error</font>")
                return

            command = [self.make, '-f' + os.path.join(outpath, MAKEFILE)]
            if self.CleanBuild:
                command.append('clean')
            command.append('all')

        try:
            self.CompilerProcess = subprocess.Popen(command,
                                                    stdin=subprocess.PIPE,
                                                    stdout=subprocess.PIPE,
                                                    stderr=subprocess.STDOUT,
                                                    shell=self.isWin32Platform)
            error_count = 0
            while True:
                self.usleep(50000)
                if not self.CompilerProcess:
                    break
                # read single lines
                buff = self.CompilerProcess.stdout.readline()
                if buff == '':  # got nothing
                    if self.CompilerProcess.poll() != None:  # process exited
                        self.CompilerProcess = None
                        # print 'compiler process finished.'
                        break
                else:
                    msg = str(buff)
                    msg_lowered = msg.lower()
                    # string to QString
                    if msg_lowered.find("warning:") >= 0:
                        self.LogList.append("<font color=orange>%s</font>" %
                                            msg)
                    # todo: other error messages
                    elif msg_lowered.find("error:") >= 0 \
                            or msg_lowered.find("make: ***") >= 0 \
                            or msg_lowered.find(": multiple definition") >= 0 \
                            or msg_lowered.find("undefined reference to") >= 0:
                        self.LogList.append("<font color=red>%s</font>" % msg)
                        error_count += 1
                    else:
                        self.LogList.append("<font color=green>%s</font>" %
                                            msg)
        except:
            print 'got errors in compiler thread!'
            self.LogList.append("<font color=red>ERROR: build failed!</font>")
            self.LogList.append("<font color=red>%s</font>" % self.TCHAIN)
            self.CompilerProcess = None

        if not error_count:
            self.LogList.append("<font size=4 color=cyan>done.</font>")
        else:
            self.LogList.append(
                "<font size=4 color=red>done with error(s) !</font>")

        print 'compiler thread done.'

    def getCompilerInfo(self):
        if self.isRunning():
            return None

        self._task = self.GET_INFO
        self.start()
        while True:
            self.usleep(1000)
            if not self.isRunning():
                break
        if not self.LogList.count():
            return None
        else:
            self.LogList.takeLast()
            info = ''
            for msg in self.LogList:
                info += msg
            return info

    def buildProject(self, mcuPart, userCode, cleanBuild=False):
        if self.isRunning():
            return False, "busy"
        if not os.path.isfile(userCode):
            return False, "file not found"

        self._task = self.BUILD_PROJECT
        self.McuPart = str(mcuPart)
        self.UserCode = str(userCode)
        self.CleanBuild = cleanBuild
        self.start()
        return True, "Build process running. Please wait..."

    def programHex(self, mcuPart, userCode, serialPort=None):
        if self.isRunning():
            return False, "busy"
        if not serialPort:
            return False, "no port selected"

        self.McuPart = str(mcuPart)
        self.serialPortName = str(serialPort)
        self.UserCode = str(userCode)

        outpath = os.path.join(os.path.dirname(self.UserCode), OUT_DIR)
        makefile = os.path.join(outpath, MAKEFILE)
        hexfile = self.getExpectedHexFileName(userCode)

        if not os.path.isfile(makefile) or not os.path.isfile(hexfile):
            return False, "No *.hex file found! (re)build first the project."

        self._task = self.PROGRAM_HEX
        self.start()
        return True, "Flash Loader running. Please wait..."

    def pollBuildProcess(self, stopProcess=False):
        if self.isRunning() or self.LogList.count() > 0:
            if stopProcess:
                self.LogList.clear()
                try:
                    self.CompilerProcess.kill(
                    )  # needs Admin privilege on Windows!
                    self.CompilerProcess = None
                    self.exit()
                    return True, "killed"
                except:
                    print "n0 u can't kill me! :-p"
                    self.CompilerProcess.wait(
                    )  # just wait for the process to finish
                    self.CompilerProcess = None
                    self.exit()
                    return False, "waited"
            if self.LogList.count():
                return True, str(self.LogList.takeFirst())
            else:
                return True, ''
        else:
            return False, "process not running"

    def generateMakefile(self,
                         outPath='.',
                         projectName='a',
                         includePaths='',
                         sourceFiles='',
                         verbose=False):
        objects = []
        try:
            fout = open(os.path.join(outPath, MAKEFILE), 'w')
            fout.write('#\n# Automatically generated Makefile\n#\n\n')
            fout.write('PROJECT = ' + projectName + '\n')
            fout.write('MCUARCH = ' + getMcuArchitecture(self.McuPart) + '\n')
            fout.write('MCUPART = ' + self.McuPart + '\n\n')
            fout.write('OUTPUT_DIR = ' + outPath + '\n')
            fout.write('ELF_FILE = $(OUTPUT_DIR)/$(PROJECT).elf\n')
            fout.write('HEX_FILE = $(OUTPUT_DIR)/$(PROJECT).hex\n')
            fout.write('MAP_FILE = $(OUTPUT_DIR)/$(PROJECT).map\n')
            fout.write('LKR_SCRIPT = ' + getLinkerScript(self.McuPart) +
                       '\n\n')
            fout.write('TCHAIN = ' + self.TCHAIN.replace('\\', '/') + '\n\n')
            fout.write('INCLUDES =  \\\n')
            for path in includePaths:
                fout.write('\t' + path + ' \\\n')
            fout.write('\n\n')
            fout.write('DEFINES = ' + getCompilerDefines() + '\n')
            fout.write('CFLAGS = ' + self.Configs.getCflags() +
                       ' $(DEFINES)\n')
            fout.write('CXXFLAGS = ' + self.Configs.getCxxflags() +
                       ' $(DEFINES)\n')
            fout.write('AFLAGS = ' + self.Configs.getAflags() + '\n')
            fout.write('LFLAGS = ' + self.Configs.getLflags() + '\n\n\n')
            fout.write('RM = ' + self.Configs.getRmCmd() + '\n\n\n')
            fout.write('OBJECTS =  \\\n')
            for src in sourceFiles:
                src = str(src)
                folder, objname = os.path.split(src[:src.rfind('.')] + '.o')
                if folder.find('libraries') != 0 and folder.find(
                        'hardware') != 0:
                    folder = 'user'
                objdir = os.path.join(outPath, 'obj', folder)
                if not os.path.exists(objdir): os.makedirs(objdir)
                obj = '$(OUTPUT_DIR)/obj/' + folder + '/' + objname
                fout.write('\t' + obj + ' \\\n')
                objects.append(obj)
            fout.write('\n\n')
            fout.write('all : $(OBJECTS)\n')
            fout.write('\t@echo [LINK] $(notdir $(ELF_FILE))\n\t')
            if not verbose: fout.write('@')
            fout.write('$(TCHAIN)g++ $(LFLAGS) $^ -o $(ELF_FILE)\n')
            fout.write('\t@echo [HEX] $(notdir $(HEX_FILE))\n')
            fout.write(
                '\t@$(TCHAIN)objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $(ELF_FILE) $(HEX_FILE)\n'
            )
            fout.write('\t@$(TCHAIN)size $(ELF_FILE)\n\n')
            fout.write('clean:\n')
            fout.write('\t@$(RM) $(OBJECTS)\n')
            fout.write('\t@$(RM) $(ELF_FILE) $(HEX_FILE) $(MAP_FILE)\n\n\n')
            fout.write('program: $(HEX_FILE)\n')
            fout.write(
                '\tbatchisp -device at32$(MCUPART) -hardware RS232 -port $(COMPORT) '
            )
            fout.write(
                '-operation erase f memory flash blankcheck loadbuffer $(HEX_FILE) '
            )
            fout.write('program verify start reset 0\n\n\n')
            i = 0
            for src in sourceFiles:
                src = str(src)
                fout.write(objects[i] + ' : ' + src + '\n')
                src_ext = os.path.splitext(src)[1].lower()
                if src_ext == USER_CODE_EXT:
                    fout.write('\t@echo [CXX] $< \n\t')
                    if not verbose: fout.write('@')
                    fout.write(
                        '$(TCHAIN)g++ $(INCLUDES) $(CXXFLAGS) -x c++ $< -o $@\n\n'
                    )
                elif src_ext == '.s':
                    fout.write('\t@echo [AS] $(<F)\n\t')
                    if not verbose: fout.write('@')
                    fout.write(
                        '$(TCHAIN)gcc -x assembler-with-cpp $(AFLAGS) $< -o $@\n\n'
                    )
                elif src_ext == '.c':
                    fout.write('\t@echo [CC] $(<F)\n\t')
                    if not verbose: fout.write('@')
                    fout.write(
                        '$(TCHAIN)gcc $(INCLUDES) $(CFLAGS) $< -o $@\n\n')
                elif src_ext == '.cpp':
                    fout.write('\t@echo [CPP] $(<F)\n\t')
                    if not verbose: fout.write('@')
                    fout.write(
                        '$(TCHAIN)g++ $(INCLUDES) $(CXXFLAGS) $< -o $@\n\n')

                i += 1
            fout.close()
            return True
        except:
            return False

    def getExpectedHexFileName(self, userCode=None):
        if not userCode:
            return None
        outpath = os.path.dirname(str(userCode)) + '/' + OUT_DIR
        fname = os.path.basename(str(userCode))
        dotpos = fname.rfind('.')
        if dotpos > 0:
            hexfile = outpath + '/' + fname[:dotpos] + '.hex'
        else:
            hexfile = outpath + '/' + fname + '.hex'
        return hexfile
Esempio n. 6
0
class GccCompilerThread(QtCore.QThread):
    '''
    classdocs
    '''
    # enum tasks
    GET_INFO = 0
    BUILD_PROJECT = 1
    PROGRAM_HEX = 2
    _task = GET_INFO
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent

        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True

        self.TCHAIN = self.Configs.getCompiler()
        self.make = self.Configs.getMakeCmd()

        self.McuPart = None
        self.UserCode = None
        self.CleanBuild = True
        self.serialPortName = None
        self.CompilerProcess = None # todo: use QtCore.QProcess class instead

        self.LogList = list()

    def run(self):
        if not self.TCHAIN:
            print('no supported compiler!')
            return

        self.LogList.clear()
        if self._task == self.GET_INFO:
            command = [ self.TCHAIN + 'gcc', '--version' ]
        elif self._task == self.PROGRAM_HEX:
            # output folder - same location with user code
            outpath = os.path.join( os.path.dirname(self.UserCode) , OUT_DIR )
            command = [ self.make , '-f'+os.path.join(outpath, MAKEFILE)]
            command.append( 'COMPORT=%s' % self.serialPortName )
            command.append( 'program' )
            #print command
        else: # self._task == self.BUILD_PROJECT
            projectName = os.path.splitext(os.path.basename(self.UserCode))[0]
            # output folder - same location with user code
            outpath = os.path.join( os.path.dirname(self.UserCode) , OUT_DIR )

            Result, Includes, Sources = parseUserCode( self.UserCode, outpath, self.McuPart )
            #print Includes, Sources
            if not Result or not self.generateMakefile(outpath, projectName, Includes, Sources, self.CleanBuild):
                self.BuildProcess = None
                self.LogList.append( "<font color=red>file write error</font>" )
                return

            command = [ self.make , '-f'+os.path.join(outpath, MAKEFILE)]
            if self.CleanBuild:
                command.append('clean')
            command.append('all')

        try:
            self.CompilerProcess = subprocess.Popen( command,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT,
                         shell=self.isWin32Platform )
            error_count = 0
            while True:
                self.usleep(50000)
                if not self.CompilerProcess:
                    break;
                # read single lines
                buff = self.CompilerProcess.stdout.readline().strip().decode('utf-8')
                if not len(buff): # got nothing
                    if self.CompilerProcess.poll() != None: # process exited
                        self.CompilerProcess = None
                        # print 'compiler process finished.'
                        break
                else:
                    msg = str(buff)
                    msg_lowered = msg.lower()
                    # string to QString
                    if msg_lowered.find("warning:") >= 0:
                        self.LogList.append( "<font color=orange>%s</font>" % msg )
                    # todo: other error messages
                    elif msg_lowered.find("error:") >= 0 \
                            or msg_lowered.find("make: ***") >= 0 \
                            or msg_lowered.find(": multiple definition") >= 0 \
                            or msg_lowered.find("undefined reference to") >= 0:
                        self.LogList.append( "<font color=red>%s</font>" % msg )
                        error_count += 1
                    else:
                        self.LogList.append( "<font color=green>%s</font>" % msg )

        except:
            print('got errors in compiler thread!')
            self.LogList.append( "<font color=red>ERROR: build failed!</font>")
            self.LogList.append( "<font color=red>%s</font>" % self.TCHAIN)
            self.CompilerProcess = None

        if not error_count:
            self.LogList.append( "<font size=4 color=cyan>done.</font>" )
        else:
            self.LogList.append( "<font size=4 color=red>done with error(s) !</font>"  )

        print('compiler thread done.')

    def getCompilerInfo(self):
        if self.isRunning():
            return None

        self._task = self.GET_INFO
        self.start()
        while True:
            self.usleep(1000)
            if not self.isRunning():
                break;
        if not len(self.LogList):
            return None
        else:
            self.LogList.pop() # takeLast
            info = ''
            for msg in self.LogList:
                info += msg
            return info

    def buildProject(self, mcuPart, userCode, cleanBuild=False):
        if self.isRunning():
            return False, "busy"
        if not os.path.isfile(userCode):
            return False, "file not found"

        self._task = self.BUILD_PROJECT
        self.McuPart = str(mcuPart)
        self.UserCode = str(userCode)
        self.CleanBuild = cleanBuild
        self.start()
        return True, "Build process running. Please wait..."

    def programHex(self, mcuPart, userCode, serialPort=None):
        if self.isRunning():
            return False, "busy"
        if not serialPort:
            return False, "no port selected"

        self.McuPart = str(mcuPart)
        self.serialPortName = str(serialPort)
        self.UserCode = str(userCode)

        outpath = os.path.join( os.path.dirname(self.UserCode) , OUT_DIR )
        makefile = os.path.join(outpath, MAKEFILE)
        hexfile = self.getExpectedHexFileName(userCode)

        if not os.path.isfile(makefile) or not os.path.isfile(hexfile):
            return False, "No *.hex file found! (re)build first the project."

        self._task = self.PROGRAM_HEX
        self.start()
        return True, "Flash Loader running. Please wait..."

    def pollBuildProcess(self, stopProcess=False):
        if self.isRunning() or len(self.LogList):
            if stopProcess:
                self.LogList.clear()
                try:
                    self.CompilerProcess.kill() # needs Admin privilege on Windows!
                    self.CompilerProcess = None
                    self.exit()
                    return True, "killed"
                except:
                    #print("n0 u can't kill me! :-p")
                    self.CompilerProcess.wait() # just wait for the process to finish
                    self.CompilerProcess = None
                    self.exit()
                    return False, "waited"
            if len(self.LogList):
                return True, str(self.LogList.pop(0)) # takeFirst
            else:
                return True, ''
        else:
            return False, "process not running"

    def generateMakefile(self, outPath='.', projectName='a', includePaths='', sourceFiles='', verbose=False):
        objects = []
        try:
            fout = open( os.path.join(outPath, MAKEFILE), 'w' )
            fout.write( '#\n# Automatically generated Makefile\n#\n\n' )
            fout.write( 'PROJECT = ' + projectName + '\n' )
            fout.write( 'MCUARCH = ' + getMcuArchitecture(self.McuPart) + '\n' )
            fout.write( 'MCUPART = ' + self.McuPart + '\n\n' )
            fout.write( 'OUTPUT_DIR = ' + outPath + '\n' )
            fout.write( 'ELF_FILE = $(OUTPUT_DIR)/$(PROJECT).elf\n' )
            fout.write( 'HEX_FILE = $(OUTPUT_DIR)/$(PROJECT).hex\n' )
            fout.write( 'MAP_FILE = $(OUTPUT_DIR)/$(PROJECT).map\n' )
            fout.write( 'LKR_SCRIPT = ' + getLinkerScript(self.McuPart) + '\n\n')
            fout.write( 'TCHAIN = ' + self.TCHAIN.replace('\\','/') + '\n\n' )
            fout.write( 'INCLUDES =  \\\n' )
            for path in includePaths:
                fout.write( '\t' + path + ' \\\n' )
            fout.write( '\n\n' )
            fout.write( 'DEFINES = ' + getCompilerDefines() + '\n')
            fout.write( 'CFLAGS = ' + self.Configs.getCflags() + ' $(DEFINES)\n')
            fout.write( 'CXXFLAGS = ' + self.Configs.getCxxflags() + ' $(DEFINES)\n')
            fout.write( 'AFLAGS = ' + self.Configs.getAflags() + '\n' )
            fout.write( 'LFLAGS = ' + self.Configs.getLflags() + '\n\n\n' )
            fout.write( 'RM = ' + self.Configs.getRmCmd() + '\n\n\n' )
            fout.write( 'OBJECTS =  \\\n' )
            for src in sourceFiles:
                src = str(src)
                folder, objname = os.path.split( src[:src.rfind('.')] + '.o' )
                if folder.find('libraries')!=0 and folder.find('hardware')!=0:
                    folder = 'user'
                objdir = os.path.join(outPath, 'obj', folder)
                if not os.path.exists(objdir): os.makedirs( objdir )
                obj = '$(OUTPUT_DIR)/obj/' + folder + '/' + objname
                fout.write( '\t' + obj + ' \\\n' )
                objects.append(obj)
            fout.write( '\n\n' )
            fout.write( 'all : $(OBJECTS)\n' )
            fout.write( '\t@echo [LINK] $(notdir $(ELF_FILE))\n\t')
            if not verbose: fout.write( '@' )
            fout.write( '$(TCHAIN)g++ $(LFLAGS) $^ -o $(ELF_FILE)\n' )
            fout.write( '\t@echo [HEX] $(notdir $(HEX_FILE))\n')
            fout.write( '\t@$(TCHAIN)objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $(ELF_FILE) $(HEX_FILE)\n' )
            fout.write( '\t@$(TCHAIN)size $(ELF_FILE)\n\n' )
            fout.write( 'clean:\n' )
            fout.write( '\t@$(RM) $(OBJECTS)\n' )
            fout.write( '\t@$(RM) $(ELF_FILE) $(HEX_FILE) $(MAP_FILE)\n\n\n' )
            fout.write( 'program: $(HEX_FILE)\n' )
            fout.write( '\tbatchisp -device at32$(MCUPART) -hardware RS232 -port $(COMPORT) ' )
            fout.write( '-operation erase f memory flash blankcheck loadbuffer $(HEX_FILE) ' )
            fout.write( 'program verify start reset 0\n\n\n' )
            i = 0
            for src in sourceFiles:
                src = str(src)
                fout.write( objects[i] + ' : ' + src + '\n')
                src_ext = os.path.splitext(src)[1].lower()
                if src_ext == USER_CODE_EXT:
                    fout.write( '\t@echo [CXX] $< \n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)g++ $(INCLUDES) $(CXXFLAGS) -x c++ $< -o $@\n\n')
                elif src_ext == '.s':
                    fout.write( '\t@echo [AS] $(<F)\n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)gcc -x assembler-with-cpp $(AFLAGS) $< -o $@\n\n')
                elif src_ext == '.c':
                    fout.write( '\t@echo [CC] $(<F)\n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)gcc $(INCLUDES) $(CFLAGS) $< -o $@\n\n')
                elif src_ext == '.cpp':
                    fout.write( '\t@echo [CPP] $(<F)\n\t' )
                    if not verbose: fout.write( '@' )
                    fout.write( '$(TCHAIN)g++ $(INCLUDES) $(CXXFLAGS) $< -o $@\n\n')

                i += 1
            fout.close()
            return True
        except:
            return False

    def getExpectedHexFileName(self, userCode=None):
        if not userCode:
            return None
        outpath = os.path.dirname( str(userCode) ) + '/' + OUT_DIR
        fname = os.path.basename( str(userCode) )
        dotpos = fname.rfind('.')
        if dotpos > 0:
            hexfile = outpath + '/' + fname[:dotpos] + '.hex'
        else:
            hexfile = outpath + '/' + fname + '.hex'
        return hexfile
class PicCompilerThread(QtCore.QThread):
    '''
    classdocs
    '''
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent
        
        self.Configs = CompilerConfig(self)
        self.Configs.saveCompilerSettings()

        # platform dependent settings
        self.isWin32Platform = False
        if os.sys.platform == 'win32':
            self.isWin32Platform = True
            
        self.CC = self.Configs.getCompiler()
        self.cflags = self.Configs.getCflags()
        self.lflags = self.Configs.getLflags()
            
        self.CompilerCommands = None            
        self.CompilerProcess = None # todo: use QtCore.QProcess class instead
        
        self.LogList = QtCore.QStringList()
            
    def run(self):
        if not self.CC:
            print 'no supported compiler!'
            return
        #print self.CC
        self.LogList.clear()
        bStop = False
        for command in self.CompilerCommands:
            if bStop:
                break
            try:
                arguments = []
                for arg in command:
                    arguments.append(arg)
                self.CompilerProcess = subprocess.Popen(
                             arguments,
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT,
                             shell=self.isWin32Platform )
                while True:
                    self.usleep(50000)
                    if not self.CompilerProcess:
                        break;
                    # read single lines    
                    buff = self.CompilerProcess.stdout.readline()
                    if buff == '': # got nothing
                        if self.CompilerProcess.poll() != None: # process exited
                            self.CompilerProcess = None
                            # print 'compiler process finished.'
                            break
                    else:
                        msg = str(buff)
                        msg_lowered = msg.lower()
                        # string to QString
                        if msg_lowered.find("warning") >= 0:
                            self.LogList.append( "<font color=orange>%s</font>" % msg )
                        # todo: other error messages
                        elif msg_lowered.find("error") >= 0 \
                                or msg_lowered.find("exit status = 1") >= 0 \
                                or msg_lowered.find("^ (") >= 0 \
                                or msg_lowered.find("defined") >= 0 \
                                or msg_lowered.find("conflicts with") >= 0 \
                                or msg_lowered.find("cannot find the path specified") >= 0 :
                            self.LogList.append( "<font color=red>%s</font>" % msg )
                            bStop = True
                        else:
                            self.LogList.append( "<font color=green>%s</font>" % msg )
            except:
                print 'got errors in compiler thread!'
                self.LogList.append( "<font color=red>%s</font>" % self.CC)
                self.CompilerProcess = None
                bStop = True
                
        self.CompilerCommands = None
        if bStop:
            self.LogList.append( "<font size=4 color=red>ERROR: build failed!</font>")
        else:
            self.LogList.append( "<font size=4 color=cyan>Done building.</font>")
            
    def getCompilerInfo(self):
        if self.isRunning():
            return None
        self.CompilerCommands = [[ self.CC, '--ver' ]] # get version info
        self.start()
        while True:
            self.usleep(1000)
            if not self.isRunning():
                break;            
        if not self.LogList.count():
            return None
        else:
            info = ''
            self.LogList.takeLast()
            for msg in self.LogList:
                info += msg
            return info
                        

    def buildProject(self, userCode=None, boardName='', verbose=False):
        if self.isRunning():
            return False, "busy"
        if not os.path.isfile(userCode):
            return False, "file not found"
        
        if boardName == 'Anito-877A':
            self.chip = '16F877A'
        elif boardName == 'Anito-4520':
            self.chip = '18F4520'
        else:
            return False, "board not supported"
        
        # output folder - same location with user code
        outpath = os.path.dirname( str(userCode) ) + '/' + OUT_DIR
        
        Result, Includes, Sources, Defines = parseUserCode( userCode, outpath + '/' + LIB_OUT_DIR )
        # print Result, Includes, Sources, Defines
        if not Result:
            self.BuildProcess = None
            return False, "file write error"

        self.CompilerCommands = []
        pcodeFiles = []
        for i in range(len(Sources)):
            src = Sources[i]
            command = [ self.CC, '--CHIP=' + self.chip ]
            command += Includes
            command += Defines
            command += self.cflags.split(' ')
            if verbose:
                command += ['-V', '-V'] # 2 V's
            else:
                command += ['-Q']
            if i==0: # user code
                command += ['--OUTDIR=' + outpath, src]
                self.CompilerCommands.append( [ '@echo', '[CC]', os.path.basename(str(userCode)) ] )
                pcodeFiles.append( outpath + '/' + os.path.basename(src)[:-2] + '.p1' )
            else: # separate folder for library intermediates
                ext = os.path.splitext(src)[1]
                if ext == '.as':
                    command += ['--OUTDIR=' + outpath + '/' + LIB_OUT_DIR, src]
                    self.CompilerCommands.append( [ '@echo', '[AS]', os.path.basename(src) ] )
                    pcodeFiles.append( outpath + '/' + LIB_OUT_DIR + '/' + os.path.basename(src)[:-3] + '.obj' )
                else:
                    command += ['--OUTDIR=' + outpath + '/' + LIB_OUT_DIR, src]
                    self.CompilerCommands.append( [ '@echo', '[CC]', os.path.basename(src) ] )
                    pcodeFiles.append( outpath + '/' + LIB_OUT_DIR + '/' + os.path.basename(src)[:-2] + '.p1' )
            self.CompilerCommands.append(command)

        self.CompilerCommands.append( [ '@echo', '[LD]', os.path.basename(str(userCode)) + '.hex' ] )
        command = [ self.CC, '--CHIP=' + self.chip ]
        command += ['--OUTDIR=' + outpath]
        command += self.lflags.split(' ')
        if verbose:
            command += ['-V', '-V', '--TIME'] # 2 V's
        else:
            command += ['-Q']
        command += pcodeFiles
        self.CompilerCommands.append(command)

        self.start()
        return True, "Build process running. Please wait..."

    def pollBuildProcess(self, stopProcess=False):
        if self.isRunning() or self.LogList.count()>0:
            if stopProcess:
                self.LogList.clear()
                try:
                    self.CompilerProcess.kill() # needs Admin privilege on Windows!
                    self.CompilerProcess = None
                    self.exit()
                    return True, "killed"
                except:
                    print "n0 u can't kill me! :-p"
                    self.CompilerProcess.wait() # just wait for the process to finish
                    self.CompilerProcess = None
                    self.exit()
                    return False, "waited"             
            if self.LogList.count():
                return True, str(self.LogList.takeFirst())
            else:
                return True, ''
        else:
            return False, "process not running"
            
    def getExpectedHexFileName(self, userCode=None):
        if not userCode:
            return None
        outpath = os.path.dirname( str(userCode) ) + '/' + OUT_DIR
        fname = os.path.basename( str(userCode) )
        return outpath + '/' + fname + '.hex'