Răsfoiți Sursa

Add SConsUI tool.

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2494 bbd45198-f89e-11dd-88c7-29a3b14d5316
bernard.xiong@gmail.com 12 ani în urmă
părinte
comite
be73747fc4
2 a modificat fișierele cu 563 adăugiri și 0 ștergeri
  1. 409 0
      tools/sconsui.py
  2. 154 0
      tools/win32spawn.py

+ 409 - 0
tools/sconsui.py

@@ -0,0 +1,409 @@
+#! /usr/bin/env python
+#coding=utf-8
+
+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
+        
+        if platform.system() == 'Windows':
+            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()
+
+        executor = None
+
+def ExecCmd(cmd):
+    global executor
+    if executor:
+        print 'cmd not exit, return'
+        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)
+        
+        # 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)
+        
+        # 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)
+        
+        padding = ttk.Frame(master)
+        padding.pack(fill=X)
+        quit = ttk.Button(padding, text='Quit', command = self.quit)
+        quit.pack(side=RIGHT)
+
+        # read setting 
+        self.read_setting()
+        
+    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 = file(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 os.environ.has_key('RTT_ROOT'):
+            self.RTTRoot.set_path(os.environ['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 = file(os.path.join(home, '.rtt_scons'), 'wb+')
+        # 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 do_build(self):
+        self.prepare_build()
+        command = 'scons'
+
+        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'
+
+        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
+        
+        bsp = self.BSPRoot.get_path()
+        os.chdir(bsp)
+
+        self.output.delete(1.0, END)
+        self.output.insert(END, 'make project ...\n')
+        ExecCmd(command)
+
+    def quit(self):
+        exit(0)
+
+def StartSConsUI(path=None):
+    global val, root
+    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__':
+    global val, root
+    root = Tk()
+    root.title('scons_builder')
+    root.geometry('590x510+50+50')
+    lock = threading.RLock()
+    scons_ui = SconsUI(root)
+    builder = scons_ui
+    root.mainloop()

+ 154 - 0
tools/win32spawn.py

@@ -0,0 +1,154 @@
+import os
+import threading
+import Queue
+
+# Windows import 
+import win32file
+import win32pipe
+import win32api
+import win32con
+import win32security
+import win32process
+import win32event
+
+class Win32Spawn(object):
+    def __init__(self, cmd, shell=False):
+        self.queue = Queue.Queue()
+        self.is_terminated = False
+        self.wake_up_event = win32event.CreateEvent(None, 0, 0, None)
+        
+        exec_dir = os.getcwd()
+        comspec = os.environ.get("COMSPEC", "cmd.exe")
+        cmd = comspec + ' /c ' + cmd
+        
+        win32event.ResetEvent(self.wake_up_event)
+
+        currproc = win32api.GetCurrentProcess()
+
+        sa = win32security.SECURITY_ATTRIBUTES()
+        sa.bInheritHandle = 1
+
+        child_stdout_rd, child_stdout_wr = win32pipe.CreatePipe(sa, 0)
+        child_stdout_rd_dup = win32api.DuplicateHandle(currproc, child_stdout_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
+        win32file.CloseHandle(child_stdout_rd)
+
+        child_stderr_rd, child_stderr_wr = win32pipe.CreatePipe(sa, 0)
+        child_stderr_rd_dup = win32api.DuplicateHandle(currproc, child_stderr_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
+        win32file.CloseHandle(child_stderr_rd)
+
+        child_stdin_rd, child_stdin_wr = win32pipe.CreatePipe(sa, 0)
+        child_stdin_wr_dup = win32api.DuplicateHandle(currproc, child_stdin_wr, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
+        win32file.CloseHandle(child_stdin_wr)
+
+        startup_info = win32process.STARTUPINFO()
+        startup_info.hStdInput = child_stdin_rd
+        startup_info.hStdOutput = child_stdout_wr
+        startup_info.hStdError = child_stderr_wr
+        startup_info.dwFlags = win32process.STARTF_USESTDHANDLES
+
+        cr_flags = 0
+        cr_flags = win32process.CREATE_NEW_PROCESS_GROUP
+
+        env = os.environ.copy()
+        self.h_process, h_thread, dw_pid, dw_tid = win32process.CreateProcess(None, cmd, None, None, 1,
+                                                                              cr_flags, env, os.path.abspath(exec_dir),
+                                                                              startup_info)
+ 
+        win32api.CloseHandle(h_thread)
+
+        win32file.CloseHandle(child_stdin_rd)
+        win32file.CloseHandle(child_stdout_wr)
+        win32file.CloseHandle(child_stderr_wr)
+
+        self.__child_stdout = child_stdout_rd_dup
+        self.__child_stderr = child_stderr_rd_dup
+        self.__child_stdin = child_stdin_wr_dup
+
+        self.exit_code = -1
+
+    def close(self):
+        win32file.CloseHandle(self.__child_stdout)
+        win32file.CloseHandle(self.__child_stderr)
+        win32file.CloseHandle(self.__child_stdin)
+        win32api.CloseHandle(self.h_process)
+        win32api.CloseHandle(self.wake_up_event)
+
+    def kill_subprocess():
+        win32event.SetEvent(self.wake_up_event)
+
+    def sleep(secs):
+        win32event.ResetEvent(self.wake_up_event)
+        timeout = int(1000 * secs)
+        val = win32event.WaitForSingleObject(self.wake_up_event, timeout)
+        if val == win32event.WAIT_TIMEOUT:
+            return True
+        else:
+            # The wake_up_event must have been signalled
+            return False
+    
+    def get(self, block=True, timeout=None):
+        return self.queue.get(block=block, timeout=timeout)
+
+    def qsize(self):
+        return self.queue.qsize()
+
+    def __wait_for_child(self):
+        # kick off threads to read from stdout and stderr of the child process
+        threading.Thread(target=self.__do_read, args=(self.__child_stdout, )).start()
+        threading.Thread(target=self.__do_read, args=(self.__child_stderr, )).start()
+
+        while True:
+            # block waiting for the process to finish or the interrupt to happen
+            handles = (self.wake_up_event, self.h_process)
+            val = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
+
+            if val >= win32event.WAIT_OBJECT_0 and val < win32event.WAIT_OBJECT_0 + len(handles):
+                handle = handles[val - win32event.WAIT_OBJECT_0]
+                if handle == self.wake_up_event:
+                    win32api.TerminateProcess(self.h_process, 1)
+                    win32event.ResetEvent(self.wake_up_event)
+                    return False
+                elif handle == self.h_process:
+                    # the process has ended naturally
+                    return True
+                else:
+                    assert False, "Unknown handle fired"
+            else:
+                assert False, "Unexpected return from WaitForMultipleObjects"
+
+    # Wait for job to finish. Since this method blocks, it can to be called from another thread.
+    # If the application wants to kill the process, it should call kill_subprocess().
+    def wait(self):
+        if not self.__wait_for_child():
+            # it's been killed
+            result = False
+        else:
+            # normal termination
+            self.exit_code = win32process.GetExitCodeProcess(self.h_process)
+            result = self.exit_code == 0
+        self.close()
+        self.is_terminated = True
+        
+        return result
+
+    # This method gets called on a worker thread to read from either a stderr
+    # or stdout thread from the child process.
+    def __do_read(self, handle):
+        bytesToRead = 1024
+        while 1:
+            try:
+                finished = 0
+                hr, data = win32file.ReadFile(handle, bytesToRead, None)
+                self.queue.put_nowait(data)
+            except win32api.error:
+                finished = 1
+
+            if finished:
+                return
+
+    def start_pipe(self):
+        def worker(pipe):
+            return pipe.wait()
+        
+        thrd = threading.Thread(target=worker, args=(self, ))
+        thrd.start()