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() |