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