xref: /nrf52832-nimble/rt-thread/tools/sconsui.py (revision 104654410c56c573564690304ae786df310c91fc)
1#! /usr/bin/env python
2#coding=utf-8
3
4#
5# File      : sconsui.py
6# This file is part of RT-Thread RTOS
7# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
8#
9#  This program is free software; you can redistribute it and/or modify
10#  it under the terms of the GNU General Public License as published by
11#  the Free Software Foundation; either version 2 of the License, or
12#  (at your option) any later version.
13#
14#  This program is distributed in the hope that it will be useful,
15#  but WITHOUT ANY WARRANTY; without even the implied warranty of
16#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17#  GNU General Public License for more details.
18#
19#  You should have received a copy of the GNU General Public License along
20#  with this program; if not, write to the Free Software Foundation, Inc.,
21#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22#
23# Change Logs:
24# Date           Author       Notes
25# 2015-01-20     Bernard      Add copyright information
26#
27
28import sys
29
30py2 = py30 = py31 = False
31version = sys.hexversion
32if version >= 0x020600F0 and version < 0x03000000 :
33    py2 = True    # Python 2.6 or 2.7
34    from Tkinter import *
35    import ttk
36elif version >= 0x03000000 and version < 0x03010000 :
37    py30 = True
38    from tkinter import *
39    import ttk
40elif version >= 0x03010000:
41    py31 = True
42    from tkinter import *
43    import tkinter.ttk as ttk
44else:
45    print ("""
46    You do not have a version of python supporting ttk widgets..
47    You need a version >= 2.6 to execute PAGE modules.
48    """)
49    sys.exit()
50
51import ScrolledText
52import tkFileDialog
53import tkMessageBox
54
55import os
56import threading
57import platform
58
59builder = None
60executor = None
61lock = None
62
63class CmdExecutor(threading.Thread):
64    def __init__(self, cmd, output):
65        threading.Thread.__init__(self)
66        self.cmd = cmd
67        self.child = None
68
69    def run(self):
70        global executor, builder, lock
71
72        if platform.system() == 'Windows':
73            try:
74                from win32spawn import Win32Spawn
75                subprocess = Win32Spawn(self.cmd)
76                subprocess.start_pipe()
77
78                builder.progressbar.start()
79                while not subprocess.is_terminated or subprocess.qsize() > 0:
80                    try:
81                        line = subprocess.get(timeout=1)
82                        line = line.replace('\r', '')
83                        if line:
84                            lock.acquire()
85                            builder.output.see(END)
86                            builder.output.insert(END, line)
87                            lock.release()
88                    except:
89                        pass
90
91                builder.progressbar.stop()
92            except:
93                pass
94
95        executor = None
96        if builder.is_makeing_project:
97            builder.output.insert(END, 'Done')
98            builder.is_makeing_project = False
99
100def ExecCmd(cmd):
101    global executor
102    if executor:
103        print 'last task does not exit'
104        return
105
106    executor = CmdExecutor(cmd, builder)
107    executor.start()
108
109class DirSelectBox(ttk.Frame):
110    def __init__(self, master=None, **kw):
111        ttk.Frame.__init__(self, master, **kw)
112        self.dir_var = StringVar()
113        self.entry = ttk.Entry(self, textvariable = self.dir_var)
114        self.entry.pack(fill=BOTH, expand=1,side=LEFT)
115        self.entry.configure(width = 50)
116
117        self.browser_button = ttk.Button(self, text="Browser", command=self.browser)
118        self.browser_button.pack(side=RIGHT)
119
120    def browser(self):
121        dir = tkFileDialog.askdirectory(parent=self, title='Open directory', initialdir=self.dir_var.get())
122        if dir != '':
123            self.dir_var.set(dir)
124
125    def set_path(self, path):
126        path = path.replace('\\', '/')
127        self.dir_var.set(path)
128
129    def get_path(self):
130        return self.dir_var.get()
131
132COMPILER = [
133        ("GNU GCC", "GCC"),
134        ("Keil ARMCC", "ARMCC"),
135        ("IAR Compiler", "IAR"),
136    ]
137
138IDE = [
139    ('Keil MDK4', 'mdk4'),
140    ('Keil MDK', 'mdk'),
141    ('IAR Compiler', 'iar')
142]
143
144class SconsUI():
145    def __init__(self, master=None):
146        style = ttk.Style()
147        theme = style.theme_use()
148        default = style.lookup(theme, 'background')
149        master.configure(background=default)
150
151        notebook = ttk.Notebook(master)
152        notebook.pack(fill=BOTH, padx=5, pady=5)
153
154        # building page
155        page_building = ttk.Frame(notebook)
156        notebook.add(page_building, padding=3)
157        notebook.tab(0, text='Build', underline="-1")
158        self.setup_building_ui(page_building)
159        self.building_page = page_building
160
161        # make project page
162        page_project = ttk.Frame(notebook)
163        notebook.add(page_project, padding = 3)
164        notebook.tab(1, text = 'Project', underline = '-1')
165        self.setup_project_ui(page_project)
166        self.project_page = page_project
167
168        # setting page
169        page_setting = ttk.Frame(notebook)
170        notebook.add(page_setting, padding = 3)
171        notebook.tab(2, text = 'Setting', underline = '-1')
172        self.setup_setting_ui(page_setting)
173        self.setting_page = page_setting
174
175        padding = ttk.Frame(master)
176        padding.pack(fill=X)
177        quit = ttk.Button(padding, text='Quit', command = self.quit)
178        quit.pack(side=RIGHT)
179
180        # set notebook to self
181        self.notebook = notebook
182
183        # read setting
184        self.read_setting()
185        self.is_makeing_project = False
186
187    def read_setting(self):
188        import platform
189        import os
190
191        home = ''
192        if platform.system() == 'Windows':
193            driver = os.environ['HOMEDRIVE']
194            home = os.environ['HOMEPATH']
195            home = os.path.join(driver, home)
196        else:
197            home = os.environ['HOME']
198
199        setting_path = os.path.join(home, '.rtt_scons')
200        if os.path.exists(setting_path):
201            setting = open(os.path.join(home, '.rtt_scons'))
202            for line in setting:
203                line = line.replace('\n', '')
204                line = line.replace('\r', '')
205                if line.find('=') != -1:
206                    items = line.split('=')
207                    if items[0] == 'RTTRoot':
208                        self.RTTRoot.set_path(items[1])
209                    elif items[0] == 'BSPRoot':
210                        self.BSPRoot.set_path(items[1])
211                    elif items[0] == 'compiler':
212                        compiler = items[1]
213                    else:
214                        self.CompilersPath[items[0]].set_path(items[1])
215            setting.close()
216
217        # set  RT-Thread Root Directory according environ
218        if 'RTT_ROOT' in os.environ:
219            self.RTTRoot.set_path(os.environ['RTT_ROOT'])
220
221        if self.RTTRoot.get_path() == '':
222            rtt_root = ''
223            # detect RT-Thread directory
224            if os.path.exists(os.path.join('..', 'include', 'rtthread.h')):
225                rtt_root = os.path.join('..')
226            elif os.path.exists(os.path.join('..', '..', 'include', 'rtthread.h')):
227                rtt_root = os.path.join('..', '..')
228            if rtt_root:
229                self.RTTRoot.set_path(os.path.abspath(rtt_root))
230
231        # detect compiler path
232        if platform.system() == 'Windows':
233            # Keil MDK
234            if not self.CompilersPath['ARMCC'].get_path():
235                if os.path.exists('C:\\Keil'):
236                    self.CompilersPath['ARMCC'].set_path('C:\\Keil')
237                elif os.path.exists('D:\\Keil'):
238                    self.CompilersPath['ARMCC'].set_path('D:\\Keil')
239                elif os.path.exists('E:\\Keil'):
240                    self.CompilersPath['ARMCC'].set_path('E:\\Keil')
241                elif os.path.exists('F:\\Keil'):
242                    self.CompilersPath['ARMCC'].set_path('F:\\Keil')
243                elif os.path.exists('G:\\Keil'):
244                    self.CompilersPath['ARMCC'].set_path('G:\\Keil')
245
246            # GNU GCC
247            if not self.CompilersPath['GCC'].get_path():
248                paths = os.environ['PATH']
249                paths = paths.split(';')
250
251                for path in paths:
252                    if path.find('CodeSourcery') != -1:
253                        self.CompilersPath['GCC'].set_path(path)
254                        break
255                    elif path.find('GNU Tools ARM Embedded') != -1:
256                        self.CompilersPath['GCC'].set_path(path)
257                        break
258
259    def save_setting(self):
260        import platform
261        import os
262
263        home = ''
264        if platform.system() == 'Windows':
265            driver = os.environ['HOMEDRIVE']
266            home = os.environ['HOMEPATH']
267            home = os.path.join(driver, home)
268        else:
269            home = os.environ['HOME']
270
271        setting = open(os.path.join(home, '.rtt_scons'), 'w+')
272        # current comiler
273        # line = '%s=%s\n' % ('compiler', self.compilers.get()))
274        line = '%s=%s\n' % ('compiler', 'iar')
275        setting.write(line)
276
277        # RTT Root Folder
278        if self.RTTRoot.get_path():
279            line = '%s=%s\n' % ('RTTRoot', self.RTTRoot.get_path())
280            setting.write(line)
281
282        # BSP Root Folder
283        if self.BSPRoot.get_path():
284            line = '%s=%s\n' % ('BSPRoot', self.BSPRoot.get_path())
285            setting.write(line)
286
287        for (compiler, path) in self.CompilersPath.iteritems():
288            if path.get_path():
289                line = '%s=%s\n' % (compiler, path.get_path())
290                setting.write(line)
291
292        setting.close()
293        tkMessageBox.showinfo("RT-Thread SCons UI",
294                    "Save setting sucessfully")
295
296    def setup_building_ui(self, frame):
297        padding = ttk.Frame(frame)
298        padding.pack(fill=X)
299
300        button = ttk.Button(padding, text='Clean', command=self.do_clean)
301        button.pack(side=RIGHT)
302        button = ttk.Button(padding, text='Build', command=self.do_build)
303        button.pack(side=RIGHT)
304        label = ttk.Label(padding, relief = 'flat', text = 'Click Build or Clean to build or clean system -->')
305        label.pack(side=RIGHT, ipady = 5)
306
307        self.progressbar = ttk.Progressbar(frame)
308        self.progressbar.pack(fill=X)
309
310        separator = ttk.Separator(frame)
311        separator.pack(fill=X)
312
313        self.output = ScrolledText.ScrolledText(frame)
314        self.output.pack(fill=X)
315
316    def setup_project_ui(self, frame):
317        label = ttk.Label(frame, relief = 'flat', text = 'Choose Integrated Development Environment:')
318        label.pack(fill=X, pady = 5)
319
320        separator = ttk.Separator(frame)
321        separator.pack(fill=X)
322
323        self.ide = StringVar()
324        self.ide.set("mdk4") # initialize
325
326        for text,mode in IDE:
327            radiobutton = ttk.Radiobutton(frame, text=text, variable = self.ide, value = mode)
328            radiobutton.pack(fill=X, padx=10)
329
330        bottom = ttk.Frame(frame)
331        bottom.pack(side=BOTTOM, fill=X)
332        button = ttk.Button(bottom, text="Make Project", command = self.do_make_project)
333        button.pack(side=RIGHT, padx = 10, pady = 10)
334
335    def setup_setting_ui(self, frame):
336        row = 0
337        label = ttk.Label (frame, relief = 'flat', text='RT-Thread Root Folder:')
338        label.grid(row=row, column=0,ipadx=5, ipady=5, padx = 5)
339
340        self.RTTRoot = DirSelectBox(frame)
341        self.RTTRoot.grid(row=row, column=1, sticky=E+W)
342        row = row + 1
343
344        label = ttk.Label (frame, relief = 'flat', text='Board Support Folder:')
345        label.grid(row=row, column=0,ipadx=5, ipady=5, padx = 5)
346
347        self.BSPRoot = DirSelectBox(frame)
348        self.BSPRoot.grid(row=row, column=1, sticky=E+W)
349        row = row + 1
350
351        label = ttk.Label (frame, relief='flat', text='Toolchain:')
352        label.grid(row=row, column=0,ipadx=5, ipady=5, sticky=E+W)
353        row = row + 1
354
355        separator = ttk.Separator(frame)
356        separator.grid(row = row, column = 0, columnspan = 2, sticky = E+W)
357        row = row + 1
358
359        self.compilers = StringVar()
360        self.compilers.set("GCC") # initialize
361
362        self.CompilersPath = {}
363
364        for text,compiler in COMPILER:
365            radiobutton = ttk.Radiobutton(frame, text=text, variable = self.compilers, value = compiler)
366            radiobutton.grid(row=row, column = 0, sticky = W, ipadx = 5, ipady = 5, padx = 20)
367
368            self.CompilersPath[compiler] = DirSelectBox(frame)
369            self.CompilersPath[compiler].grid(row=row, column=1, sticky=E+W)
370            row = row + 1
371
372        button = ttk.Button(frame, text='Save Setting', command = self.save_setting)
373        button.grid(row = row, column = 1, sticky = E)
374        row = row + 1
375
376    def prepare_build(self):
377        # get compiler
378        compiler = self.compilers.get()
379        if compiler == 'GCC':
380            compiler = 'gcc'
381        elif compiler == 'ARMCC':
382            compiler = 'keil'
383        elif compiler == 'IAR':
384            compiler = 'iar'
385
386        # get RTT Root
387        rtt_root = self.RTTRoot.get_path()
388        # get Compiler path
389        exec_path = self.CompilersPath[self.compilers.get()].get_path()
390
391        command = ''
392
393        os.environ['RTT_ROOT'] = rtt_root
394        os.environ['RTT_CC'] = compiler
395        os.environ['RTT_EXEC_PATH'] = exec_path
396
397        return command
398
399    def check_path(self):
400        result = True
401
402        if self.BSPRoot.get_path() == '':
403            result = False
404
405        if self.RTTRoot.get_path() == '':
406            result = False
407
408        if not result:
409            tkMessageBox.showinfo("RT-Thread SCons UI",
410                                    "Folder is empty, please choose correct directory.")
411
412        return result
413
414    def do_build(self):
415        self.prepare_build()
416        command = 'scons'
417
418        if not self.check_path():
419            return
420
421        bsp = self.BSPRoot.get_path()
422        os.chdir(bsp)
423
424        self.output.delete(1.0, END)
425        self.output.insert(END, 'building project...\n')
426        ExecCmd(command)
427
428    def do_clean(self):
429        self.prepare_build()
430        command = 'scons -c'
431
432        if not self.check_path():
433            return
434
435        bsp = self.BSPRoot.get_path()
436        os.chdir(bsp)
437
438        self.output.delete(1.0, END)
439        self.output.insert(END, 'clean project...\n')
440        ExecCmd(command)
441
442    def do_make_project(self):
443        ide = self.ide.get()
444        self.prepare_build()
445        command = 'scons --target=%s -s' % ide
446
447        if not self.check_path():
448            return
449
450        # select build page
451        self.notebook.select(self.building_page)
452
453        bsp = self.BSPRoot.get_path()
454        os.chdir(bsp)
455
456        self.output.delete(1.0, END)
457        self.output.insert(END, 'Generate project ...\n')
458        self.is_makeing_project = True
459        ExecCmd(command)
460
461    def quit(self):
462        exit(0)
463
464def StartSConsUI(path=None):
465    global val, root, builder, lock
466    root = Tk()
467    root.title('RT-Thread SCons UI')
468    #root.geometrygeometry('590x510+50+50')
469    lock = threading.RLock()
470    builder = SconsUI(root)
471    if path:
472        builder.BSPRoot.set_path(path)
473    root.mainloop()
474
475if __name__ == '__main__':
476    StartSConsUI()
477