Merge branch '1.3-rel' (1a2c9ee2)
[iotivity.git] / build_common / arduino / SConscript
1 ##
2 # This script includes arduino specific config
3 ##
4 import os
5 import platform
6
7 Import('env')
8
9
10 def __parse_config(f):
11     dict = {}
12
13     if not os.path.isfile(f):
14         return dict
15
16     file = open(f, 'r')
17     strs = file.readlines()
18     for str in strs:
19         str = str.strip()
20         if len(str) > 0 and str[0] == '#':
21             continue
22
23         idx = str.find('=')
24         if idx > 0:
25             dict.setdefault(str[0:idx], str[idx + 1:])
26
27     return dict
28
29
30 def __get_boards(dict):
31     boards = []
32     keys = dict.keys()
33     for key in keys:
34         idx = key.find('.name')
35         if idx > 0:
36             if key.endswith('.name'):
37                 boards.append(key[0:idx])
38     return boards
39
40
41 def __get_cpu(dict, board):
42     cpus = []
43     keys = dict.keys()
44     for key in keys:
45         idx = key.find(board + '.menu.cpu.')
46         start = len(board + '.menu.cpu.')
47         if idx >= 0:
48             end = key.find('.', start)
49             if end > 0:
50                 cpu = key[start:end]
51                 exist = False
52                 for c in cpus:
53                     if c == cpu:
54                         exist = True
55                         break
56
57                 if not exist:
58                     cpus.append(cpu)
59     return cpus
60
61
62 def __get_board_info(board, key):
63     if cpu:
64         v = boards_info.get(board + '.menu.cpu.' + cpu + key)
65         if not v:
66             v = boards_info.get(board + key)
67     else:
68         v = boards_info.get(board + key)
69     return v
70
71
72 def __search_files(path,
73                    pattern,
74                    ondisk=True,
75                    source=True,
76                    strings=False,
77                    recursive=True):
78     if not recursive:
79         return Glob(pattern, ondisk, source, strings)
80
81     matches = []
82     for root, dirnames, filenames in os.walk(path):
83         # This is a helper function to build Arduino libraries. Scripts are using this function
84         # to add all the files in a folder as compilation targets rather than specifying each
85         # file to compile from a Arduino library folder.
86
87         # Since the function is recursive, it adds even "/library/<library-name>/examples" to the
88         # compilation list. This is an extra overhead as stack is never going to use ".o" generated
89         # for these examples.
90         if 'examples' not in root:
91             matches.extend(
92                 Glob(os.path.join(root, pattern), ondisk, source, strings))
93     return matches
94
95
96 # To make sure the src is built in 'BUILD_DIR' (by default it will be built at
97 # the same directory as the .c .cpp .S)
98 def __src_to_obj(env, srcs):
99     objs = []
100     prefix = env.get('BOARD') + '_'
101     if env.get('CPU'):
102         prefix += env.get('CPU') + '_'
103
104     build_dir = os.path.join(env.get('BUILD_DIR'), 'arduino')
105     for src in srcs:
106         if (os.path.isabs(src.path)):
107             obj = src.path.replace(arduino_home, build_dir)
108         else:
109             obj = os.path.join(build_dir, src.path)
110         i = obj.rfind('.')
111         obj = obj[0:i]
112         if env.get('OBJSUFFIX'):
113             obj += env.get('OBJSUFFIX')
114         objs.extend(env.Object(obj, src, OBJPREFIX=prefix))
115     return objs
116
117
118 def __import_lib(env, lib):
119     lib_path = os.path.join(arduino_home, 'libraries', lib)
120     if not os.path.exists(lib_path):
121         if target_arch == 'avr':
122             lib_path = os.path.join(arduino_home, 'hardware', 'arduino', 'avr',
123                                     'libraries', lib)
124         else:
125             lib_path = os.path.join(arduino_home, 'hardware', 'arduino', 'sam',
126                                     'libraries', lib)
127
128     if os.path.exists(os.path.join(lib_path, 'src')):
129         lib_path = os.path.join(lib_path, 'src')
130
131     env.AppendUnique(CPPPATH=[lib_path])
132
133     if os.path.exists(os.path.join(lib_path, 'utility')):
134         env.AppendUnique(CPPPATH=[os.path.join(lib_path, 'utility')])
135
136     lib_src = []
137     lib_src.extend(__search_files(lib_path, '*.S'))
138     lib_src.extend(__search_files(lib_path, '*.c'))
139     lib_src.extend(__search_files(lib_path, '*.cpp'))
140
141     lib_obj = __src_to_obj(env, lib_src)
142     build_dir = env.get('BUILD_DIR')
143     if build_dir:
144         lib_a = env.StaticLibrary(build_dir + lib, lib_obj)
145     else:
146         lib_a = env.StaticLibrary(lib, lib_obj)
147
148     # If we link libSPI.a, the final binary is not getting launched
149     # on the board.  Which is not the case if we directly use SPI.o.
150
151     if env.get('TARGET_ARCH') == 'arm':
152         if lib == 'SPI':
153             for obj in lib_obj:
154                 if obj.name.endswith('SPI.o'):
155                     env.PrependUnique(LIBS=[File(obj)])
156         else:
157
158             env.AppendUnique(LIBS=[File(lib_a[0])])
159     else:
160         env.PrependUnique(LIBS=[File(lib_a[0])])
161
162     #env.AppendUnique(LIBS = [File(lib_a[0])])
163
164
165 def __build_core(env):
166     core_src = __search_files(core_folder, '*.S')
167     core_src.extend(__search_files(core_folder, '*.c'))
168     core_src.extend(__search_files(core_folder, '*.cpp'))
169
170     core_src.extend(__search_files(variant_folder, '*.S'))
171     core_src.extend(__search_files(variant_folder, '*.c'))
172     core_src.extend(__search_files(variant_folder, '*.cpp'))
173
174     core_obj = __src_to_obj(env, core_src)
175
176     prefix = env.get('BOARD') + '_'
177     if env.get('CPU'):
178         prefix += env.get('CPU') + '_'
179
180     core = os.path.join(env.get('BUILD_DIR', '.'), 'arduino', prefix + 'core')
181     s_core = env.StaticLibrary(core, core_obj)
182
183     env.AppendUnique(LIBS=[File(s_core[0])])
184
185     # To avoid compiler issue. Otherewise there may be warnings:
186     # undefined reference to '_exit' '_close', '_getpid' ...
187     # Above functions are used in libc.a and implemented in syscalls_sam3.c
188     if env.get('TARGET_ARCH') == 'arm':
189         for obj in core_obj:
190             if obj.name.endswith('syscalls_sam3.o'):
191                 env.AppendUnique(LIBS=[File(obj)])
192
193
194 def __create_bin(env, source):
195     name = source
196     if env.get('TARGET_ARCH') == 'avr':
197         eep = env.Command(
198             name + '.eep', source,
199             'avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 $SOURCE $TARGET'
200         )
201         hex = env.Command(name + '.hex', source,
202                           'avr-objcopy -O ihex -R .eeprom $SOURCE $TARGET')
203     else:
204         hex = env.Command(name + '.hex', source,
205                           'arm-none-eabi-objcopy -O binary $SOURCE $TARGET')
206
207
208 #Currently supporting only mega (ie. Arduino ATMega2560) and arduino_due_x/arduino_due_x_dbg (i.e. Arduino Due) builds
209 def __upload(env, binary):
210     if target_arch == 'avr':
211         protocol = __get_board_info(board, '.upload.protocol')
212         speed = __get_board_info(board, '.upload.speed')
213         port = '/dev/ttyACM0'
214         upload_cmd = arduino_home + '/hardware/tools/avr/bin/avrdude -C' + arduino_home +'/hardware/tools/avr/etc/avrdude.conf -p' \
215         + mcu + ' -c' + protocol + ' -P' + port + ' -b' + speed + ' -D -Uflash:w:' + binary + ':i'
216         print "Upload command: %s" % upload_cmd
217         install_cmd = env.Command('install_cmd', None, upload_cmd)
218         env.Default('install_cmd')
219     elif target_arch == 'arm':
220         port = 'ttyACM0'
221         upload_cmd = 'stty -F /dev/' + port + ' speed 1200 cs8 -cstopb -parenb \n' + arduino_home + '/hardware/tools/bossac -i --port=' + port + ' -U false -e -w -b ' + binary + ' -R'
222         print "Upload command: %s" % upload_cmd
223         install_cmd = env.Command('install_cmd', None, upload_cmd)
224         env.Default('install_cmd')
225
226
227 # Print the command line that to upload binary to the board
228 def __upload_help(env):
229     if target_arch == 'avr':
230         protocol = __get_board_info(board, '.upload.protocol')
231         speed = __get_board_info(board, '.upload.speed')
232
233         upload_cmd = arduino_home + '/hardware/tools/avr/bin/avrdude -C' + arduino_home +'/hardware/tools/avr/etc/avrdude.conf -v -v -v -v -p' \
234        + mcu + ' -c' + protocol + ' -P<serial_port>' + ' -b' + speed + ' -D -Uflash:w:<hex_file>:i'
235     else:
236         uu = __get_board_info(board, '.upload.native_usb')
237         upload_cmd = arduino_home + '/hardware/tools/bossac -i -d --port=<serial_port> -U ' + uu + ' -e -w -v -b <bin file> -R'
238
239     Help('''
240 ===============================================================================
241 You can upload the bin file with following command line:
242 ''')
243     Help('\n   $ ' + upload_cmd)
244     Help('''
245 \nPlease replace <xxx> according to the actual situation.
246 ===============================================================================
247 ''')
248
249
250 # ARDUINO_HOME build option
251 help_vars = Variables()
252 help_vars.Add(
253     PathVariable('ARDUINO_HOME',
254                  'ARDUINO root directory',
255                  os.environ.get('ARDUINO_HOME')))
256 help_vars.Update(env)
257 Help(help_vars.GenerateHelpText(env))
258
259 target_arch = env.get('TARGET_ARCH')
260
261 # Verify that the arduino, time, red bear, and nordic libraries are
262 # installed.  If not, get them and install them.
263 SConscript('#extlibs/arduino/SConscript')
264 arduino_home = env.get('ARDUINO_HOME')
265 print 'ARDUINO_HOME = ' + env.get('ARDUINO_HOME')
266
267 # Overwrite suffixes and prefixes
268 if env['HOST_OS'] == 'win32':
269     env['OBJSUFFIX'] = '.o'
270     env['SHOBJSUFFIX'] = '.os'
271     env['LIBPREFIX'] = 'lib'
272     env['LIBSUFFIX'] = '.a'
273     env['SHLIBPREFIX'] = 'lib'
274     env['SHLIBSUFFIX'] = '.so'
275     env['LIBPREFIXES'] = ['lib']
276     env['LIBSUFFIXES'] = ['.a', '.so']
277     env['PROGSUFFIX'] = ''
278 elif platform.system().lower() == 'darwin':
279     env['SHLIBSUFFIX'] = '.so'
280     env['LIBSUFFIXES'] = ['.a', '.so']
281     env['PROGSUFFIX'] = ''
282
283 # Debug/release relative flags
284 if env.get('RELEASE'):
285     env.AppendUnique(CCFLAGS=['-Os'])
286 else:
287     env.AppendUnique(CCFLAGS=['-g'])
288
289 # Force header presence defines
290 env.AppendUnique(CPPDEFINES=['HAVE_ARDUINO_TIME_H'])
291
292 # BOARD / CPU option
293
294 # Get IDE version
295 if os.path.exists(os.path.join(arduino_home, 'lib', 'version.txt')):
296     vf = open(os.path.join(arduino_home, 'lib', 'version.txt'), 'r')
297     version = vf.readline().replace('.', '').strip()
298 else:
299     print '''
300 ************************************* Error ***********************************
301 * Can't find version file (lib/version.txt), please check if (%s)
302 * is arduino root directory.                                                  *
303 *******************************************************************************
304 ''' % arduino_home
305     Exit(1)
306
307 if version[0:2] == '10':
308     is_1_0_x = True
309     boards_info = __parse_config(
310         os.path.join(arduino_home, 'hardware', 'arduino', 'boards.txt'))
311     env.PrependENVPath('PATH',
312                        os.path.join(arduino_home, 'hardware', 'tools', 'avr',
313                                     'bin'))
314     env.Replace(CC='avr-gcc')
315     env.Replace(CXX='avr-g++')
316     env.Replace(AR='avr-ar')
317     env.Replace(AS='avr-as')
318     env.Replace(LINK='avr-gcc')
319     env.Replace(RANLIB='avr-ranlib')
320     if target_arch != 'avr':
321         print '''
322 ************************************* Error ***********************************
323 * Arduino 1.0.x IDE only support 'avr', to support other arch at least 1.5.x  *
324 * is required.
325 *******************************************************************************
326 '''
327         Exit(1)
328 else:
329     is_1_0_x = False
330     if target_arch == 'avr':
331         boards_info = __parse_config(
332             os.path.join(arduino_home, 'hardware', 'arduino', 'avr',
333                          'boards.txt'))
334         platform_info = __parse_config(
335             os.path.join(arduino_home, 'hardware', 'arduino', 'avr',
336                          'platform.txt'))
337     elif target_arch == 'arm':
338         boards_info = __parse_config(
339             os.path.join(arduino_home, 'hardware', 'arduino', 'sam',
340                          'boards.txt'))
341         platform_info = __parse_config(
342             os.path.join(arduino_home, 'hardware', 'arduino', 'sam',
343                          'platform.txt'))
344     else:
345         print '''
346 ************************************* Error ***********************************
347 * CPU arch %s isn't supported currently.
348 *******************************************************************************
349 ''' % target_arch
350
351 #Board option, let user to select the board
352 boards = __get_boards(boards_info)
353 help_vars = Variables()
354 help_vars.Add(EnumVariable('BOARD', 'arduino board', boards[0], boards))
355 help_vars.Update(env)
356 Help(help_vars.GenerateHelpText(env))
357
358 #CPU option
359 board = env.get('BOARD')
360 cpus = __get_cpu(boards_info, board)
361 if len(cpus) > 0:
362     help_vars = Variables()
363     help_vars.Add(EnumVariable('CPU', 'arduino board cpu', cpus[0], cpus))
364     help_vars.Update(env)
365     Help(help_vars.GenerateHelpText(env))
366
367 # Arduino commom flags
368 cpu = env.get('CPU')
369 board = env.get('BOARD')
370 mcu = __get_board_info(board, '.build.mcu')
371 f_cpu = __get_board_info(board, '.build.f_cpu')
372 usb_vid = __get_board_info(board, '.build.vid')
373 usb_pid = __get_board_info(board, '.build.pid')
374 variant = __get_board_info(board, '.build.variant')
375
376 if not usb_vid:
377     usb_vid = __get_board_info(board, '.vid.0')
378 if not usb_pid:
379     usb_pid = __get_board_info(board, '.pid.0')
380
381 if is_1_0_x:
382     core_base = os.path.join(arduino_home, 'hardware', 'arduino')
383 else:
384     if target_arch == 'avr':
385         core_base = os.path.join(arduino_home, 'hardware', 'arduino', 'avr')
386     else:
387         core_base = os.path.join(arduino_home, 'hardware', 'arduino', 'sam')
388
389 variant_folder = os.path.join(core_base, 'variants', variant)
390 env.AppendUnique(CPPPATH=[variant_folder])
391
392 core = __get_board_info(board, '.build.core')
393 core_folder = os.path.join(core_base, 'cores', core)
394 env.AppendUnique(CPPPATH=[core_folder])
395
396 if is_1_0_x:
397     comm_flags = ['-std=c99']
398     if mcu:
399         comm_flags.extend(['-mmcu=' + mcu])
400     if f_cpu:
401         comm_flags.extend(['-DF_CPU=' + f_cpu])
402     comm_flags.extend(['-DARDUINO=' + version])
403     if usb_vid:
404         comm_flags.extend(['-DUSB_VID=' + usb_vid])
405     if usb_pid:
406         comm_flags.extend(['-DUSB_PID=' + usb_pid])
407
408     env.AppendUnique(ASFLAGS=['-x', 'assembler-with-cpp'])
409     env.AppendUnique(ASFLAGS=comm_flags)
410
411     env.AppendUnique(
412         CFLAGS=['-Os', '-ffunction-sections', '-fdata-sections', '-MMD'])
413     env.AppendUnique(CFLAGS=comm_flags)
414
415     env.AppendUnique(CXXFLAGS=[
416         '-Os', '-fno-exceptions', '-ffunction-sections', '-fdata-sections',
417         '-MMD'
418     ])
419     env.AppendUnique(CXXFLAGS=comm_flags)
420
421     env.AppendUnique(LINKFLAGS=['-Os'])
422     if mcu == 'atmega2560':
423         env.AppendUnique(LINKFLAGS=['-Wl,--gc-sections,--relax'])
424     else:
425         env.AppendUnique(LINKFLAGS=['-Wl,--gc-sections'])
426     env.AppendUnique(LINKFLAGS=['-mmcu=' + mcu])
427 else:
428     if target_arch == 'avr':
429         cpu_flag = '-mmcu=' + mcu
430     else:
431         cpu_flag = '-mcpu=' + mcu
432
433     comm_flag = [
434         cpu_flag, '-DF_CPU=' + f_cpu, '-DARDUINO=' + version,
435         '-DARDUINO_' + __get_board_info(board, '.build.board')
436     ]
437     if target_arch == 'arm':
438         # As of 1.5.8 the arduino headers for arm had _asm_ bugs with ARM and
439         # require gnu99 to be used
440         comm_flag.extend(['-std=gnu99', '-DARDUINO_ARCH_SAM'])
441     else:
442         comm_flag.extend(['-std=c99', '-DARDUINO_ARCH_AVR'])
443
444     compiler_path = platform_info.get('compiler.path')
445     compiler_path = compiler_path.replace('{runtime.ide.path}', arduino_home)
446     env.PrependENVPath('PATH', compiler_path)
447     env.Replace(CC=platform_info.get('compiler.c.cmd'))
448     env.Replace(CXX=platform_info.get('compiler.cpp.cmd'))
449     env.Replace(AR=platform_info.get('compiler.ar.cmd'))
450     if target_arch == 'arm':
451         env.AppendUnique(CPPPATH=[
452             os.path.join(arduino_home, 'hardware', 'arduino', 'sam', 'system',
453                          'libsam'),
454             os.path.join(arduino_home, 'hardware', 'arduino', 'sam', 'system',
455                          'CMSIS', 'CMSIS', 'Include'),
456             os.path.join(arduino_home, 'hardware', 'arduino', 'sam', 'system',
457                          '', 'CMSIS', 'Device', 'ATMEL')
458         ])
459     env.AppendUnique(ASFLAGS=['-x', 'assembler-with-cpp'])
460     env.AppendUnique(ASFLAGS=comm_flag)
461     env.AppendUnique(CFLAGS=Split(platform_info.get('compiler.c.flags')))
462     env.AppendUnique(CXXFLAGS=Split(platform_info.get('compiler.cpp.flags')))
463     env.AppendUnique(ARFLAGS=Split(platform_info.get('compiler.ar.flags')))
464     env.AppendUnique(CCFLAGS=comm_flag)
465
466     extra_flags = __get_board_info(board, '.build.extra_flags')
467     if extra_flags:
468         extra_flags = extra_flags.replace('{build.usb_flags}', '')
469         env.AppendUnique(CCFLAGS=Split(extra_flags))
470         usb_flags = [
471             '-DUSB_VID=' + usb_vid, '-DUSB_PID=' + usb_pid, '-DUSBCON',
472             '-DUSB_MANUFACTURER="Unknown"'
473         ]
474         env.AppendUnique(CCFLAGS=usb_flags)
475
476     if target_arch == 'arm':
477         env.AppendUnique(LINKFLAGS=[
478             '-Os', '-Wl,--gc-sections', cpu_flag, '-T' + os.path.join(
479                 variant_folder, __get_board_info(board, '.build.ldscript'))
480         ])
481         env.AppendUnique(LINKFLAGS=Split(
482             '-lm -lgcc -mthumb -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group'
483         ))
484
485         variant_system_lib = __get_board_info(board,
486                                               '.build.variant_system_lib')
487         if variant_system_lib:
488             if variant_folder.find(' ') >= 0:
489                 variant_folder = '"' + variant_folder + '"'
490             env.Replace(
491                 LINKCOM=
492                 '$LINK -o $TARGET $_LIBDIRFLAGS $LINKFLAGS $SOURCES $_LIBFLAGS '
493                 + os.path.join(variant_folder,
494                                variant_system_lib) + ' -Wl,--end-group')
495         else:
496             env.Replace(
497                 LINKCOM=
498                 '$LINK -o $TARGET $_LIBDIRFLAGS $LINKFLAGS $SOURCES $_LIBFLAGS -Wl,--end-group'
499             )
500     else:
501         env.AppendUnique(
502             LINKFLAGS=Split(platform_info.get('compiler.c.elf.flags')))
503         env.AppendUnique(LINKFLAGS=[cpu_flag])
504         env.AppendUnique(LIBS='m')
505     env.Replace(ARCOM='$AR ' + platform_info.get('compiler.ar.flags') +
506                 ' $TARGET $SOURCES')
507
508 # Make sure the .d files are removed when clean the build
509 if env.GetOption('clean'):
510     dfs = __search_files(env.get('BUILD_DIR'), '*.d')
511     for df in dfs:
512         Execute(Delete(df))
513 __build_core(env)
514
515 env.AddMethod(__import_lib, "ImportLib")  #import arduino library
516 env.AddMethod(__build_core, "BuildCore")  #build arduino core
517 env.AddMethod(__create_bin, "CreateBin")  #create binary files(.eep and .hex)
518 env.AddMethod(__upload, "Upload")  #Upload binary to board
519 env.AddMethod(
520     __upload_help,
521     "UploadHelp")  #print the command line that to upload binary to the boardf