477 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			477 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#! /usr/bin/env python
 | 
						|
#coding=utf-8
 | 
						|
 | 
						|
#
 | 
						|
# File      : sconsui.py
 | 
						|
# This file is part of RT-Thread RTOS
 | 
						|
# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
 | 
						|
#
 | 
						|
#  This program is free software; you can redistribute it and/or modify
 | 
						|
#  it under the terms of the GNU General Public License as published by
 | 
						|
#  the Free Software Foundation; either version 2 of the License, or
 | 
						|
#  (at your option) any later version.
 | 
						|
#
 | 
						|
#  This program is distributed in the hope that it will be useful,
 | 
						|
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
#  GNU General Public License for more details.
 | 
						|
#
 | 
						|
#  You should have received a copy of the GNU General Public License along
 | 
						|
#  with this program; if not, write to the Free Software Foundation, Inc.,
 | 
						|
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
						|
#
 | 
						|
# Change Logs:
 | 
						|
# Date           Author       Notes
 | 
						|
# 2015-01-20     Bernard      Add copyright information
 | 
						|
#
 | 
						|
 | 
						|
import sys
 | 
						|
 | 
						|
py2 = py30 = py31 = False
 | 
						|
version = sys.hexversion
 | 
						|
if version >= 0x020600F0 and version < 0x03000000 :
 | 
						|
    py2 = True    # Python 2.6 or 2.7
 | 
						|
    from Tkinter import *
 | 
						|
    import ttk
 | 
						|
elif version >= 0x03000000 and version < 0x03010000 :
 | 
						|
    py30 = True
 | 
						|
    from tkinter import *
 | 
						|
    import ttk
 | 
						|
elif version >= 0x03010000:
 | 
						|
    py31 = True
 | 
						|
    from tkinter import *
 | 
						|
    import tkinter.ttk as ttk
 | 
						|
else:
 | 
						|
    print ("""
 | 
						|
    You do not have a version of python supporting ttk widgets..
 | 
						|
    You need a version >= 2.6 to execute PAGE modules.
 | 
						|
    """)
 | 
						|
    sys.exit()
 | 
						|
 | 
						|
import ScrolledText
 | 
						|
import tkFileDialog
 | 
						|
import tkMessageBox
 | 
						|
 | 
						|
import os
 | 
						|
import threading
 | 
						|
import platform
 | 
						|
 | 
						|
builder = None
 | 
						|
executor = None
 | 
						|
lock = None
 | 
						|
 | 
						|
class CmdExecutor(threading.Thread):
 | 
						|
    def __init__(self, cmd, output):
 | 
						|
        threading.Thread.__init__(self) 
 | 
						|
        self.cmd = cmd
 | 
						|
        self.child = None
 | 
						|
 | 
						|
    def run(self):
 | 
						|
        global executor, builder, lock
 | 
						|
        
 | 
						|
        if platform.system() == 'Windows':
 | 
						|
            try:
 | 
						|
                from win32spawn import Win32Spawn
 | 
						|
                subprocess = Win32Spawn(self.cmd)
 | 
						|
                subprocess.start_pipe()
 | 
						|
                
 | 
						|
                builder.progressbar.start()
 | 
						|
                while not subprocess.is_terminated or subprocess.qsize() > 0:
 | 
						|
                    try:
 | 
						|
                        line = subprocess.get(timeout=1)
 | 
						|
                        line = line.replace('\r', '')
 | 
						|
                        if line:
 | 
						|
                            lock.acquire()
 | 
						|
                            builder.output.see(END)
 | 
						|
                            builder.output.insert(END, line)
 | 
						|
                            lock.release()
 | 
						|
                    except:
 | 
						|
                        pass
 | 
						|
                
 | 
						|
                builder.progressbar.stop()
 | 
						|
            except:
 | 
						|
                pass
 | 
						|
 | 
						|
        executor = None
 | 
						|
        if builder.is_makeing_project:
 | 
						|
            builder.output.insert(END, 'Done')
 | 
						|
            builder.is_makeing_project = False
 | 
						|
 | 
						|
def ExecCmd(cmd):
 | 
						|
    global executor
 | 
						|
    if executor:
 | 
						|
        print 'last task does not exit'
 | 
						|
        return
 | 
						|
 | 
						|
    executor = CmdExecutor(cmd, builder)
 | 
						|
    executor.start()
 | 
						|
 | 
						|
class DirSelectBox(ttk.Frame):
 | 
						|
    def __init__(self, master=None, **kw):
 | 
						|
        ttk.Frame.__init__(self, master, **kw)
 | 
						|
        self.dir_var = StringVar()
 | 
						|
        self.entry = ttk.Entry(self, textvariable = self.dir_var)
 | 
						|
        self.entry.pack(fill=BOTH, expand=1,side=LEFT)
 | 
						|
        self.entry.configure(width = 50)
 | 
						|
        
 | 
						|
        self.browser_button = ttk.Button(self, text="Browser", command=self.browser)
 | 
						|
        self.browser_button.pack(side=RIGHT)
 | 
						|
 | 
						|
    def browser(self):
 | 
						|
        dir = tkFileDialog.askdirectory(parent=self, title='Open directory', initialdir=self.dir_var.get())
 | 
						|
        if dir != '':
 | 
						|
            self.dir_var.set(dir)
 | 
						|
 | 
						|
    def set_path(self, path):
 | 
						|
        path = path.replace('\\', '/')
 | 
						|
        self.dir_var.set(path)
 | 
						|
    
 | 
						|
    def get_path(self):
 | 
						|
        return self.dir_var.get()
 | 
						|
 | 
						|
COMPILER = [
 | 
						|
        ("GNU GCC", "GCC"),
 | 
						|
        ("Keil ARMCC", "ARMCC"),
 | 
						|
        ("IAR Compiler", "IAR"),
 | 
						|
    ]
 | 
						|
 | 
						|
IDE = [
 | 
						|
    ('Keil MDK4', 'mdk4'),
 | 
						|
    ('Keil MDK', 'mdk'),
 | 
						|
    ('IAR Compiler', 'iar')
 | 
						|
]
 | 
						|
 | 
						|
class SconsUI():
 | 
						|
    def __init__(self, master=None):
 | 
						|
        style = ttk.Style()
 | 
						|
        theme = style.theme_use()
 | 
						|
        default = style.lookup(theme, 'background')
 | 
						|
        master.configure(background=default)
 | 
						|
        
 | 
						|
        notebook = ttk.Notebook(master)
 | 
						|
        notebook.pack(fill=BOTH, padx=5, pady=5)
 | 
						|
        
 | 
						|
        # building page 
 | 
						|
        page_building = ttk.Frame(notebook)
 | 
						|
        notebook.add(page_building, padding=3)
 | 
						|
        notebook.tab(0, text='Build', underline="-1")
 | 
						|
        self.setup_building_ui(page_building)
 | 
						|
        self.building_page = page_building
 | 
						|
        
 | 
						|
        # make project page
 | 
						|
        page_project = ttk.Frame(notebook)
 | 
						|
        notebook.add(page_project, padding = 3)
 | 
						|
        notebook.tab(1, text = 'Project', underline = '-1')
 | 
						|
        self.setup_project_ui(page_project)
 | 
						|
        self.project_page = page_project
 | 
						|
        
 | 
						|
        # setting page 
 | 
						|
        page_setting = ttk.Frame(notebook)
 | 
						|
        notebook.add(page_setting, padding = 3)
 | 
						|
        notebook.tab(2, text = 'Setting', underline = '-1')
 | 
						|
        self.setup_setting_ui(page_setting)
 | 
						|
        self.setting_page = page_setting
 | 
						|
        
 | 
						|
        padding = ttk.Frame(master)
 | 
						|
        padding.pack(fill=X)
 | 
						|
        quit = ttk.Button(padding, text='Quit', command = self.quit)
 | 
						|
        quit.pack(side=RIGHT)
 | 
						|
 | 
						|
        # set notebook to self
 | 
						|
        self.notebook = notebook
 | 
						|
        
 | 
						|
        # read setting 
 | 
						|
        self.read_setting()
 | 
						|
        self.is_makeing_project = False
 | 
						|
        
 | 
						|
    def read_setting(self):
 | 
						|
        import platform
 | 
						|
        import os
 | 
						|
        
 | 
						|
        home = ''
 | 
						|
        if platform.system() == 'Windows':
 | 
						|
            driver = os.environ['HOMEDRIVE']
 | 
						|
            home = os.environ['HOMEPATH']
 | 
						|
            home = os.path.join(driver, home)
 | 
						|
        else:
 | 
						|
            home = os.environ['HOME']
 | 
						|
        
 | 
						|
        setting_path = os.path.join(home, '.rtt_scons')
 | 
						|
        if os.path.exists(setting_path):
 | 
						|
            setting = open(os.path.join(home, '.rtt_scons'))
 | 
						|
            for line in setting:
 | 
						|
                line = line.replace('\n', '')
 | 
						|
                line = line.replace('\r', '')
 | 
						|
                if line.find('=') != -1:
 | 
						|
                    items = line.split('=')
 | 
						|
                    if items[0] == 'RTTRoot':
 | 
						|
                        self.RTTRoot.set_path(items[1])
 | 
						|
                    elif items[0] == 'BSPRoot':
 | 
						|
                        self.BSPRoot.set_path(items[1])
 | 
						|
                    elif items[0] == 'compiler':
 | 
						|
                        compiler = items[1]
 | 
						|
                    else:
 | 
						|
                        self.CompilersPath[items[0]].set_path(items[1])
 | 
						|
            setting.close()
 | 
						|
 | 
						|
        # set  RT-Thread Root Directory according environ
 | 
						|
        if 'RTT_ROOT' in os.environ:
 | 
						|
            self.RTTRoot.set_path(os.environ['RTT_ROOT'])
 | 
						|
        
 | 
						|
        if self.RTTRoot.get_path() == '':
 | 
						|
            rtt_root = ''
 | 
						|
            # detect RT-Thread directory 
 | 
						|
            if os.path.exists(os.path.join('..', 'include', 'rtthread.h')):
 | 
						|
                rtt_root = os.path.join('..')
 | 
						|
            elif os.path.exists(os.path.join('..', '..', 'include', 'rtthread.h')):
 | 
						|
                rtt_root = os.path.join('..', '..')
 | 
						|
            if rtt_root:
 | 
						|
                self.RTTRoot.set_path(os.path.abspath(rtt_root))
 | 
						|
 | 
						|
        # detect compiler path
 | 
						|
        if platform.system() == 'Windows':
 | 
						|
            # Keil MDK
 | 
						|
            if not self.CompilersPath['ARMCC'].get_path():
 | 
						|
                if os.path.exists('C:\\Keil'):
 | 
						|
                    self.CompilersPath['ARMCC'].set_path('C:\\Keil')
 | 
						|
                elif os.path.exists('D:\\Keil'):
 | 
						|
                    self.CompilersPath['ARMCC'].set_path('D:\\Keil')
 | 
						|
                elif os.path.exists('E:\\Keil'):
 | 
						|
                    self.CompilersPath['ARMCC'].set_path('E:\\Keil')
 | 
						|
                elif os.path.exists('F:\\Keil'):
 | 
						|
                    self.CompilersPath['ARMCC'].set_path('F:\\Keil')
 | 
						|
                elif os.path.exists('G:\\Keil'):
 | 
						|
                    self.CompilersPath['ARMCC'].set_path('G:\\Keil')
 | 
						|
 | 
						|
            # GNU GCC
 | 
						|
            if not self.CompilersPath['GCC'].get_path():
 | 
						|
                paths = os.environ['PATH']
 | 
						|
                paths = paths.split(';')
 | 
						|
                
 | 
						|
                for path in paths:
 | 
						|
                    if path.find('CodeSourcery') != -1:
 | 
						|
                        self.CompilersPath['GCC'].set_path(path)
 | 
						|
                        break
 | 
						|
                    elif path.find('GNU Tools ARM Embedded') != -1:
 | 
						|
                        self.CompilersPath['GCC'].set_path(path)
 | 
						|
                        break
 | 
						|
 | 
						|
    def save_setting(self):
 | 
						|
        import platform
 | 
						|
        import os
 | 
						|
 | 
						|
        home = ''
 | 
						|
        if platform.system() == 'Windows':
 | 
						|
            driver = os.environ['HOMEDRIVE']
 | 
						|
            home = os.environ['HOMEPATH']
 | 
						|
            home = os.path.join(driver, home)
 | 
						|
        else:
 | 
						|
            home = os.environ['HOME']
 | 
						|
 | 
						|
        setting = open(os.path.join(home, '.rtt_scons'), 'w+')
 | 
						|
        # current comiler 
 | 
						|
        # line = '%s=%s\n' % ('compiler', self.compilers.get()))
 | 
						|
        line = '%s=%s\n' % ('compiler', 'iar')
 | 
						|
        setting.write(line)
 | 
						|
 | 
						|
        # RTT Root Folder
 | 
						|
        if self.RTTRoot.get_path():
 | 
						|
            line = '%s=%s\n' % ('RTTRoot', self.RTTRoot.get_path())
 | 
						|
            setting.write(line)
 | 
						|
 | 
						|
        # BSP Root Folder
 | 
						|
        if self.BSPRoot.get_path():
 | 
						|
            line = '%s=%s\n' % ('BSPRoot', self.BSPRoot.get_path())
 | 
						|
            setting.write(line)
 | 
						|
 | 
						|
        for (compiler, path) in self.CompilersPath.iteritems():
 | 
						|
            if path.get_path():
 | 
						|
                line = '%s=%s\n' % (compiler, path.get_path())
 | 
						|
                setting.write(line)
 | 
						|
 | 
						|
        setting.close()
 | 
						|
        tkMessageBox.showinfo("RT-Thread SCons UI",
 | 
						|
                    "Save setting sucessfully")
 | 
						|
 | 
						|
    def setup_building_ui(self, frame):
 | 
						|
        padding = ttk.Frame(frame)
 | 
						|
        padding.pack(fill=X)
 | 
						|
        
 | 
						|
        button = ttk.Button(padding, text='Clean', command=self.do_clean)
 | 
						|
        button.pack(side=RIGHT)
 | 
						|
        button = ttk.Button(padding, text='Build', command=self.do_build)
 | 
						|
        button.pack(side=RIGHT)
 | 
						|
        label = ttk.Label(padding, relief = 'flat', text = 'Click Build or Clean to build or clean system -->')
 | 
						|
        label.pack(side=RIGHT, ipady = 5)
 | 
						|
        
 | 
						|
        self.progressbar = ttk.Progressbar(frame)
 | 
						|
        self.progressbar.pack(fill=X)
 | 
						|
        
 | 
						|
        separator = ttk.Separator(frame)
 | 
						|
        separator.pack(fill=X)
 | 
						|
 | 
						|
        self.output = ScrolledText.ScrolledText(frame)
 | 
						|
        self.output.pack(fill=X)
 | 
						|
 | 
						|
    def setup_project_ui(self, frame):
 | 
						|
        label = ttk.Label(frame, relief = 'flat', text = 'Choose Integrated Development Environment:')
 | 
						|
        label.pack(fill=X, pady = 5)
 | 
						|
        
 | 
						|
        separator = ttk.Separator(frame)
 | 
						|
        separator.pack(fill=X)
 | 
						|
    
 | 
						|
        self.ide = StringVar()
 | 
						|
        self.ide.set("mdk4") # initialize
 | 
						|
        
 | 
						|
        for text,mode in IDE:
 | 
						|
            radiobutton = ttk.Radiobutton(frame, text=text, variable = self.ide, value = mode)
 | 
						|
            radiobutton.pack(fill=X, padx=10)
 | 
						|
    
 | 
						|
        bottom = ttk.Frame(frame)
 | 
						|
        bottom.pack(side=BOTTOM, fill=X)
 | 
						|
        button = ttk.Button(bottom, text="Make Project", command = self.do_make_project)
 | 
						|
        button.pack(side=RIGHT, padx = 10, pady = 10)
 | 
						|
 | 
						|
    def setup_setting_ui(self, frame):
 | 
						|
        row = 0
 | 
						|
        label = ttk.Label (frame, relief = 'flat', text='RT-Thread Root Folder:')
 | 
						|
        label.grid(row=row, column=0,ipadx=5, ipady=5, padx = 5)
 | 
						|
    
 | 
						|
        self.RTTRoot = DirSelectBox(frame)
 | 
						|
        self.RTTRoot.grid(row=row, column=1, sticky=E+W)
 | 
						|
        row = row + 1
 | 
						|
    
 | 
						|
        label = ttk.Label (frame, relief = 'flat', text='Board Support Folder:')
 | 
						|
        label.grid(row=row, column=0,ipadx=5, ipady=5, padx = 5)
 | 
						|
    
 | 
						|
        self.BSPRoot = DirSelectBox(frame)
 | 
						|
        self.BSPRoot.grid(row=row, column=1, sticky=E+W)
 | 
						|
        row = row + 1
 | 
						|
    
 | 
						|
        label = ttk.Label (frame, relief='flat', text='Toolchain:')
 | 
						|
        label.grid(row=row, column=0,ipadx=5, ipady=5, sticky=E+W)
 | 
						|
        row = row + 1
 | 
						|
        
 | 
						|
        separator = ttk.Separator(frame)
 | 
						|
        separator.grid(row = row, column = 0, columnspan = 2, sticky = E+W)
 | 
						|
        row = row + 1
 | 
						|
        
 | 
						|
        self.compilers = StringVar()
 | 
						|
        self.compilers.set("GCC") # initialize
 | 
						|
 | 
						|
        self.CompilersPath = {}
 | 
						|
 | 
						|
        for text,compiler in COMPILER:
 | 
						|
            radiobutton = ttk.Radiobutton(frame, text=text, variable = self.compilers, value = compiler)
 | 
						|
            radiobutton.grid(row=row, column = 0, sticky = W, ipadx = 5, ipady = 5, padx = 20)
 | 
						|
 | 
						|
            self.CompilersPath[compiler] = DirSelectBox(frame)
 | 
						|
            self.CompilersPath[compiler].grid(row=row, column=1, sticky=E+W)
 | 
						|
            row = row + 1
 | 
						|
    
 | 
						|
        button = ttk.Button(frame, text='Save Setting', command = self.save_setting)
 | 
						|
        button.grid(row = row, column = 1, sticky = E)
 | 
						|
        row = row + 1
 | 
						|
 | 
						|
    def prepare_build(self):
 | 
						|
        # get compiler 
 | 
						|
        compiler = self.compilers.get()
 | 
						|
        if compiler == 'GCC':
 | 
						|
            compiler = 'gcc'
 | 
						|
        elif compiler == 'ARMCC':
 | 
						|
            compiler = 'keil'
 | 
						|
        elif compiler == 'IAR':
 | 
						|
            compiler = 'iar'
 | 
						|
 | 
						|
        # get RTT Root
 | 
						|
        rtt_root = self.RTTRoot.get_path()
 | 
						|
        # get Compiler path 
 | 
						|
        exec_path = self.CompilersPath[self.compilers.get()].get_path()
 | 
						|
        
 | 
						|
        command = ''
 | 
						|
 | 
						|
        os.environ['RTT_ROOT'] = rtt_root
 | 
						|
        os.environ['RTT_CC'] = compiler
 | 
						|
        os.environ['RTT_EXEC_PATH'] = exec_path
 | 
						|
        
 | 
						|
        return command 
 | 
						|
    
 | 
						|
    def check_path(self):
 | 
						|
        result = True
 | 
						|
        
 | 
						|
        if self.BSPRoot.get_path() == '':
 | 
						|
            result = False
 | 
						|
        
 | 
						|
        if self.RTTRoot.get_path() == '':
 | 
						|
            result = False
 | 
						|
        
 | 
						|
        if not result:
 | 
						|
            tkMessageBox.showinfo("RT-Thread SCons UI",
 | 
						|
                                    "Folder is empty, please choose correct directory.")
 | 
						|
 | 
						|
        return result
 | 
						|
 | 
						|
    def do_build(self):
 | 
						|
        self.prepare_build()
 | 
						|
        command = 'scons'
 | 
						|
 | 
						|
        if not self.check_path():
 | 
						|
            return
 | 
						|
 | 
						|
        bsp = self.BSPRoot.get_path()
 | 
						|
        os.chdir(bsp)
 | 
						|
 | 
						|
        self.output.delete(1.0, END)
 | 
						|
        self.output.insert(END, 'building project...\n')
 | 
						|
        ExecCmd(command)        
 | 
						|
    
 | 
						|
    def do_clean(self):
 | 
						|
        self.prepare_build()
 | 
						|
        command = 'scons -c'
 | 
						|
 | 
						|
        if not self.check_path():
 | 
						|
            return
 | 
						|
 | 
						|
        bsp = self.BSPRoot.get_path()
 | 
						|
        os.chdir(bsp)
 | 
						|
 | 
						|
        self.output.delete(1.0, END)
 | 
						|
        self.output.insert(END, 'clean project...\n')
 | 
						|
        ExecCmd(command)
 | 
						|
    
 | 
						|
    def do_make_project(self):
 | 
						|
        ide = self.ide.get()
 | 
						|
        self.prepare_build()
 | 
						|
        command = 'scons --target=%s -s' % ide
 | 
						|
        
 | 
						|
        if not self.check_path():
 | 
						|
            return
 | 
						|
        
 | 
						|
        # select build page 
 | 
						|
        self.notebook.select(self.building_page)
 | 
						|
        
 | 
						|
        bsp = self.BSPRoot.get_path()
 | 
						|
        os.chdir(bsp)
 | 
						|
 | 
						|
        self.output.delete(1.0, END)
 | 
						|
        self.output.insert(END, 'Generate project ...\n')
 | 
						|
        self.is_makeing_project = True
 | 
						|
        ExecCmd(command)
 | 
						|
 | 
						|
    def quit(self):
 | 
						|
        exit(0)
 | 
						|
 | 
						|
def StartSConsUI(path=None):
 | 
						|
    global val, root, builder, lock
 | 
						|
    root = Tk()
 | 
						|
    root.title('RT-Thread SCons UI')
 | 
						|
    #root.geometrygeometry('590x510+50+50')
 | 
						|
    lock = threading.RLock()
 | 
						|
    builder = SconsUI(root)
 | 
						|
    if path:
 | 
						|
        builder.BSPRoot.set_path(path)
 | 
						|
    root.mainloop()
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    StartSConsUI()
 |