7bd056b8b72bab3d22f262291b2a361ead350572
[iotivity.git] / build_common / SConscript
1 # -*- mode: python; python-indent-offset: 4; indent-tabs-mode: nil -*-
2 ##
3 # This script includes generic build options:
4 #    release/debug, target os, target arch, cross toolchain, build environment etc
5 ##
6 import os
7 import sys
8 import platform
9 import re
10
11 project_version = '2.0.0'
12
13 # Map of build host to possible target os
14 host_target_map = {
15     'linux': ['linux', 'android', 'yocto', 'tizen'],
16     'windows': ['windows', 'android'],
17     'darwin': ['darwin', 'ios', 'android'],
18     'msys_nt': ['msys_nt'],
19 }
20
21 # Map of target os to possible target architecture
22 os_arch_map = {
23     'linux': [
24         'x86', 'x86_64', 'arm', 'arm-v7a', 'armeabi-v7a', 'arm64', 'mips',
25         'mipsel', 'mips64', 'mips64el', 'i386', 'powerpc', 'sparc', 'aarch64',
26         'armv6l', 'armv7l'
27     ],
28     'tizen': ['x86', 'x86_64', 'arm', 'arm-v7a', 'armeabi-v7a', 'arm64'],
29     'android': [
30         'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'armeabi-v7a-hard',
31         'arm64-v8a'
32     ],
33     'windows': ['x86', 'amd64', 'arm'],
34     'msys_nt': ['x86', 'x86_64'],
35     'darwin': ['i386', 'x86_64'],
36     'ios': ['i386', 'x86_64', 'armv7', 'armv7s', 'arm64'],
37     'yocto': [
38         'i586', 'i686', 'x86_64', 'arm', 'aarch64', 'powerpc', 'powerpc64',
39         'mips', 'mipsel'
40     ],
41 }
42
43 # Fetch host from Python platform information and smash case
44 host = platform.system().lower()
45
46 # In case of msys_nt, the host string contains an internal Windows version
47 # which is not interesting for lookups in the maps.
48 # Canonicalize the msys_nt-XX.X system name to msys-nt
49 if 'msys_nt' in host:
50     host = 'msys_nt'
51
52 if host not in host_target_map:
53     msg = "\nError: building on host os '%s' is not currently supported.\n" % host
54     Exit(msg)
55
56 ######################################################################
57 # Get build options from command line
58 ######################################################################
59 target_os = ARGUMENTS.get('TARGET_OS', host).lower()
60
61 if target_os not in host_target_map[host]:
62     msg = "\nError: host '%s' cannot currently build target '%s'" % (host, target_os)
63     msg += "\n\tchoices: %s\n" % host_target_map[host]
64     Exit(msg)
65
66 # work out a reasonable default for target_arch if not specified by user
67 if target_os == 'android':
68     default_arch = 'x86'
69 else:
70     default_arch = platform.machine()
71     if target_os == 'windows':
72         default_arch = default_arch.lower()
73     if target_os == 'linux' and default_arch in ('i586', 'i686'):
74         default_arch = 'x86'
75
76 target_arch = ARGUMENTS.get('TARGET_ARCH', default_arch)  # target arch
77
78 default_with_upstream_libcoap = 0
79
80 if ARGUMENTS.get('TEST'):
81     logging_default = False
82 else:
83     release_mode = False
84     if ARGUMENTS.get('RELEASE', True) in [
85             'y', 'yes', 'true', 't', '1', 'on', 'all', True
86     ]:
87         release_mode = True
88     logging_default = (release_mode is False)
89
90 # generate a list of unique targets: convert to set() for uniqueness,
91 # then convert back to a list
92 targetlist = list(set(x for l in list(host_target_map.values()) for x in l))
93
94 ######################################################################
95 # Common build options
96 ######################################################################
97
98 help_vars = Variables()
99 help_vars.AddVariables(
100     ('PROJECT_VERSION',
101                  'The version of IoTivity',
102                  project_version),
103     BoolVariable('VERBOSE',
104                  'Show compilation',
105                  default=False),
106     BoolVariable('RELEASE',
107                  'Build for release?',
108                  default=True),
109     EnumVariable('TARGET_OS',
110                  'Target platform',
111                  default=host,
112                  allowed_values=targetlist),
113     EnumVariable('SECURED',
114                  'Build with DTLS',
115                  default='1',
116                  allowed_values=('0', '1')),
117     ListVariable('TARGET_TRANSPORT',
118                  'Target transport',
119                  default='ALL',
120                  names=('ALL', 'BT', 'BLE', 'IP', 'NFC')),
121     BoolVariable('WITH_TCP',
122                  'Build with TCP adapter',
123                  default=False),
124     BoolVariable('WITH_PROXY',
125                  'Build with CoAP-HTTP Proxy',
126                  default=True),
127     ListVariable('WITH_MQ',
128                  'Build with MQ publisher/broker',
129                  default='OFF',
130                  names=('OFF', 'SUB', 'PUB', 'BROKER')),
131     BoolVariable('WITH_CLOUD',
132                  'Build including AccountManager class and Cloud Client sample',
133                  default=False),
134     BoolVariable('WITH_TEST',
135                  'Build unit tests',
136                  default=True),
137     ListVariable('RD_MODE',
138                  'Resource Directory build mode',
139                  default='CLIENT',
140                  names=('CLIENT', 'SERVER')),
141     BoolVariable('SIMULATOR',
142                  'Build with simulator module',
143                  default=False),
144     EnumVariable('TARGET_ARCH',
145                  'Target architecture',
146                  default=default_arch,
147                  allowed_values=os_arch_map[target_os]),
148     EnumVariable('MULTIPLE_OWNER',
149                  'Enable multiple owner',
150                  default='1',
151                  allowed_values=('0', '1')),
152     EnumVariable('EXC_PROV_SUPPORT',
153                  'Except OCPMAPI library(libocpmapi.so)',
154                  default='0',
155                  allowed_values=('0', '1')),
156     EnumVariable('TEST',
157                  'Run unit tests',
158                  default='0',
159                  allowed_values=('0', '1')),
160     BoolVariable('LOGGING',
161                  'Enable stack logging',
162                  default=logging_default),
163     EnumVariable('LOG_LEVEL',
164                  'Enable stack logging level',
165                  default='DEBUG',
166                  allowed_values=('DEBUG', 'INFO', 'ERROR', 'WARNING', 'FATAL')),
167     EnumVariable('ROUTING',
168                  'Enable routing',
169                  default='EP',
170                  allowed_values=('GW', 'EP')),
171     EnumVariable('WITH_UPSTREAM_LIBCOAP',
172                  'Use latest stable version of LibCoAP downloaded from github',
173                  default=default_with_upstream_libcoap,
174                  allowed_values=('0', '1')),
175     EnumVariable('BUILD_SAMPLE',
176                  'Build with sample',
177                  default='ON',
178                  allowed_values=('ON', 'OFF')),
179 #    ANDROID_NDK set at end after default computed
180 #    ANDROID_HOME set at end after default computed
181 #    ANDROID_GRADLE set at end after default computed
182     BoolVariable('WITH_ENV',
183                  'Use compiler options from environment',
184                  default=False),
185     BoolVariable('AUTOMATIC_UPDATE',
186                  'Makes libcoap update automatically to the required versions if needed.',
187                  default=False),
188     BoolVariable('BUILD_JAVA',
189                  'Build Java bindings',
190                  default=False),
191     PathVariable('JAVA_HOME',
192                  'JDK directory',
193                  default=os.environ.get('JAVA_HOME'),
194                  validator=PathVariable.PathAccept),
195     EnumVariable('OIC_SUPPORT_TIZEN_TRACE',
196                  'Tizen Trace(T-trace) api availability',
197                  default=False,
198                  allowed_values=('True', 'False')),
199     BoolVariable('MANDATORY',
200                  'Enable/disable(default) mandatory',
201                  default=False)
202 )
203
204 ######################################################################
205 # Platform (build target) specific options
206 ######################################################################
207 if target_os in ['linux']:
208     # Build option to enable failing build if warnings encountered.
209     # May need to be off for developing with newer compilers which
210     # are stricter about emitting warnings. Defaults to true so
211     # developer builds and gerrit builds have all warnings examined.
212     help_vars.Add(
213         BoolVariable('ERROR_ON_WARN',
214                      'Make all compiler warnings into errors.',
215                       default=False))
216
217 targets_support_valgrind = ['linux', 'darwin']
218 if target_os in targets_support_valgrind:
219     # Build option to enable unit tests to be run under valgrind.
220     # May need to disable on small-memory platforms (e.g. Raspberry Pi)
221     # Checking for memory leak and usage errors is part of the
222     # acceptance criteria for project code, so this should default to on.
223     help_vars.Add(
224         BoolVariable('VALGRIND_CHECKS',
225                      'Build support for running code coverage checks',
226                       default=True))
227
228 targets_support_gcov = ['linux', 'darwin']
229 if target_os in targets_support_gcov:
230     # Build option to enable coverage checking using gcov.
231     # Requires gcc or clang family compilers.
232     # Actual compiler flags need to be set in target-specific script.
233     help_vars.Add(
234         BoolVariable('COVERAGE_CHECKS',
235                      'Build support for running code coverage checks',
236                       default=False))
237
238 if target_os == 'windows':
239     # Builds differ based on Visual Studio version
240     #   For VS2013, MSVC_VERSION is '12.0'.
241     #   For VS2015, MSVC_VERSION is '14.0'.
242     #   For VS2017, MSVC_VERSION is '14.1'.
243     # Default value is None, which means SCons will pick
244     help_vars.Add(
245         EnumVariable('MSVC_VERSION',
246                      'MSVC compiler version - Windows',
247                      default=None,
248                      allowed_values=('12.0', '14.0', '14.1')))
249     help_vars.Add(
250         EnumVariable('MSVC_UWP_APP',
251                      'Build a Universal Windows Platform (UWP) Application',
252                      default='0',
253                      allowed_values=('0', '1')))
254
255 AddOption(
256     '--prefix',
257     dest='prefix',
258     type='string',
259     nargs=1,
260     action='store',
261     metavar='DIR',
262     help='installation prefix')
263
264 ######################################################################
265 # Platform (build target) specific options: SDK/NDK & toolchain
266 ######################################################################
267 targets_support_cc = ['linux', 'tizen']
268
269 if target_os in targets_support_cc:
270     # Set cross compile toolchain
271     help_vars.Add('TC_PREFIX',
272                   "Toolchain prefix (Generally only required for cross-compiling)",
273                   default=None)
274     help_vars.Add(
275         PathVariable('TC_PATH',
276                      'Toolchain path (Generally only required for cross-compiling)',
277                      default=None,
278                      validator=PathVariable.PathAccept))
279
280 ######################################################################
281 # this is where the setup of the construction envionment begins
282 ######################################################################
283 if target_os in ['android']:
284     # Android always uses GNU compiler regardless of the host
285     env = Environment(
286         variables=help_vars,
287         tools=['gnulink', 'gcc', 'g++', 'ar', 'as', 'textfile'])
288 else:
289     env = Environment(
290         variables=help_vars,
291         tools=['default', 'textfile'],
292         TARGET_ARCH=target_arch,
293         TARGET_OS=target_os,
294         PREFIX=GetOption('prefix'),
295         LIB_INSTALL_DIR=ARGUMENTS.get('LIB_INSTALL_DIR')  #for 64bit build
296     )
297
298 if env.get('WITH_ENV'):
299     env['ENV'] = os.environ
300     if 'CC' in os.environ:
301         env['CC'] = Split(os.environ['CC'])
302         print("using CC from environment: %s" % env['CC'])
303     if 'CXX' in os.environ:
304         env['CXX'] = Split(os.environ['CXX'])
305         print("using CXX from environment: %s" % env['CXX'])
306     if 'CFLAGS' in os.environ:
307         env['CFLAGS'] = Split(os.environ['CFLAGS'])
308         print("using CFLAGS from environment: %s" % env['CFLAGS'])
309     if 'CXXFLAGS' in os.environ:
310         env['CXXFLAGS'] = Split(os.environ['CXXFLAGS'])
311         print("using CXXFLAGS from environment: %s" % env['CXXFLAGS'])
312     if 'CCFLAGS' in os.environ:
313         env['CCFLAGS'] = Split(os.environ['CCFLAGS'])
314         print("using CCFLAGS from environment: %s" % env['CCFLAGS'])
315     if 'CPPFLAGS' in os.environ:
316         env['CPPFLAGS'] = Split(os.environ['CPPFLAGS'])
317         print("using CPPFLAGS from environment: %s" % env['CPPFLAGS'])
318     if 'LDFLAGS' in os.environ:
319         env['LINKFLAGS'] = Split(os.environ['LDFLAGS'])
320         print("using LDFLAGS/LINKFLAGS from environment: %s" % env['LINKFLAGS'])
321
322 # set quieter build messages unless verbose mode was requested
323 if not env.get('VERBOSE'):
324     env['CCCOMSTR'] = "Compiling $TARGET"
325     env['SHCCCOMSTR'] = "Compiling $TARGET"
326     env['CXXCOMSTR'] = "Compiling $TARGET"
327     env['SHCXXCOMSTR'] = "Compiling $TARGET"
328     env['LINKCOMSTR'] = "Linking $TARGET"
329     env['SHLINKCOMSTR'] = "Linking shared object $TARGET"
330     env['ARCOMSTR'] = "Archiving $TARGET"
331     env['RANLIBCOMSTR'] = "Indexing Archive $TARGET"
332
333 if env.get('MANDATORY'):
334     env.AppendUnique(CPPDEFINES=['__MANDATORY__'])
335
336 tc_set_msg = '''
337 ************************************ Warning **********************************
338 * Warning: TC_PREFIX and/or TC_PATH is set in the environment.
339 * This means a non-default compilation toolchain will be used.
340 * If this is not what you expected you should unset, or it
341 * may lead to unexpected results.
342 *******************************************************************************
343 '''
344 if target_os in targets_support_cc:
345     prefix = env.get('TC_PREFIX')
346     tc_path = env.get('TC_PATH')
347     if prefix:
348         env.Replace(CC=prefix + env.get('CC', 'gcc'))
349         env.Replace(CXX=prefix + env.get('CXX', 'g++'))
350         env.Replace(AR=prefix + env.get('AR', 'ar'))
351         env.Replace(AS=prefix + env.get('AS', 'as'))
352         env.Replace(RANLIB=prefix + env.get('RANLIB', 'ranlib'))
353
354     if tc_path:
355         env.PrependENVPath('PATH', tc_path)
356         sys_root = os.path.abspath(tc_path + '/../')
357         env.AppendUnique(CCFLAGS=['--sysroot=' + sys_root])
358         env.AppendUnique(LINKFLAGS=['--sysroot=' + sys_root])
359
360     if prefix or tc_path:
361         print(tc_set_msg)
362
363 # Import env variables only if reproductibility is ensured
364 if target_os in ['yocto']:
365     env['CONFIG_ENVIRONMENT_IMPORT'] = True
366 else:
367     env['CONFIG_ENVIRONMENT_IMPORT'] = False
368
369 if env['CONFIG_ENVIRONMENT_IMPORT']:
370     print("warning: importing some environment variables for OS: %s" % target_os)
371     for ev in [
372         'PATH',
373         'PKG_CONFIG',
374         'PKG_CONFIG_PATH',
375         'PKG_CONFIG_SYSROOT_DIR'
376     ]:
377         if os.environ.get(ev) is not None:
378             env['ENV'][ev] = os.environ.get(ev)
379     if os.environ['LDFLAGS'] != None:
380         env.AppendUnique(LINKFLAGS=Split(os.environ['LDFLAGS']))
381
382 if target_os in ['windows']:
383     # UnpackAll.py needs access to system PATH components that SCons
384     # does not include by default - e.g., the path to 7z.exe.
385     env.AppendUnique(PATH=os.environ['PATH'])
386
387 # Ensure scons is able to change its working directory
388 env.SConscriptChdir(1)
389
390 ######################################################################
391 # Convenience functions to "extend" SCons
392 ######################################################################
393
394
395 def __set_dir(env, dir):
396     #
397     # Set the source and build directories
398     #   Source directory: 'dir'
399     #   Build directory: 'dir'/out/<target_os>/<target_arch>/<release or debug>/
400     #   On windows, the build directory will be:
401     #     'dir'/out/windows/<win32 or uwp>/<target_arch>/<release or debug>/
402     #
403     # You can get the directory as following:
404     #   env.get('SRC_DIR')
405     #   env.get('BUILD_DIR')
406     #
407     if not os.path.exists(dir + '/SConstruct'):
408         msg = '''
409 *************************************** Error *********************************
410 * The directory (%s) seems not to be a buildable directory,
411 * no SConstruct file found.
412 *******************************************************************************
413 ''' % dir
414         Exit(msg)
415
416     build_dir = dir + '/out/' + target_os + '/'
417
418     if target_os == 'windows':
419         if env.get('MSVC_UWP_APP') == '1':
420             build_dir = build_dir + 'uwp/'
421         else:
422             build_dir = build_dir + 'win32/'
423
424     build_dir = build_dir + target_arch
425
426     if env.get('RELEASE'):
427         build_dir = build_dir + '/release/'
428     else:
429         build_dir = build_dir + '/debug/'
430
431     env.VariantDir(build_dir, dir, duplicate=0)
432
433     env.Replace(BUILD_DIR=build_dir)
434     env.Replace(SRC_DIR=dir)
435
436
437 def __src_to_obj(env, src, home=''):
438     '''
439     Make sure builds happen in BUILD_DIR (by default they
440     would happen in the directory of the source file)
441     Note this does not seem to be used, VariantDir is used instead
442     '''
443     obj = env.get('BUILD_DIR') + src.replace(home, '')
444     if env.get('OBJSUFFIX'):
445         obj += env.get('OBJSUFFIX')
446     return env.Object(obj, src)
447
448
449 def __install(ienv, targets, name=''):
450     '''
451     Copy files to internal place (not for install to system)
452     only use UserInstall() for copying files to system using "scons install"
453     '''
454     for filename in ienv.GetBuildPath(targets):
455         basename = os.path.basename(filename)
456         dst = env.get('BUILD_DIR') + basename
457         i_n = Command(dst, filename, Copy("$TARGET", "$SOURCE"))
458         if '' == name:
459             name = basename
460         ienv.Alias(name, i_n)
461         env.AppendUnique(TS=[name])
462
463
464 def __installlib(ienv, targets, name=''):
465     '''
466     Install files to system, using "scons install" and remove rpath info if present
467     If prefix or lib install dir is not specified, for developer convenience
468     files are copied in relative "deploy" folder along executables (rpath is kept)
469     to avoid overlap with "internal place" above
470     '''
471     user_prefix = env.get('PREFIX')
472     if user_prefix:
473         user_lib = env.get('LIB_INSTALL_DIR')
474         if user_lib:
475             dst_dir  = user_lib
476         else:
477             dst_dir  = user_prefix + '/lib'
478     else:
479         dst_dir  = env.get('BUILD_DIR') + '/deploy'
480     action = ienv.Install(dst_dir, targets)
481     ienv.Alias("install", action)
482
483
484
485 def __installbin(ienv, targets, name=''):
486     '''
487     ' Install files to system, using "scons install"
488     ' If prefix is not specified, for developer convenience
489     ' files are copied in relative "deploy" folder along libraries
490     '''
491     user_prefix = env.get('PREFIX')
492     if user_prefix:
493         dst_dir  = user_prefix + '/bin'
494     else:
495         dst_dir  = env.get('BUILD_DIR') + '/deploy'
496     ienv.Alias("install", ienv.Install(dst_dir , targets))
497
498
499 def __installheader(ienv, targets, dir, name):
500     user_prefix = env.get('PREFIX')
501     if user_prefix:
502         i_n = ienv.Install(user_prefix + '/include/iotivity/' + dir, targets)
503     else:
504         i_n = ienv.Install(env.get('BUILD_DIR') + 'deploy/include/' + dir, targets)
505     ienv.Alias("install", i_n)
506
507
508 def __installpcfile(ienv, targets, name):
509     user_prefix = env.get('PREFIX')
510     if user_prefix:
511         user_lib = env.get('LIB_INSTALL_DIR')
512         if user_lib:
513             i_n = ienv.Install(user_lib + '/pkgconfig', targets)
514         else:
515             i_n = ienv.Install(user_prefix + '/lib/pkgconfig', targets)
516     else:
517         i_n = ienv.Install(env.get('BUILD_DIR') + 'deploy/pkgconfig', targets)
518     ienv.Alias("install", i_n)
519
520
521 def __installextra(ienv, targets, subdir="."):
522     '''
523     Install extra files, by default use file relative location as subdir
524     or use any other prefix of your choice, or in explicit "deploy" folder
525     '''
526     user_lib = env.get('LIB_INSTALL_DIR')
527     user_prefix = env.get('PREFIX')
528     for target in targets:
529         if "." == subdir:
530             dst = Dir('.').srcnode().path
531         else:
532             dst = subdir
533         if user_lib:
534             dst = user_lib + '/iotivity/' + dst
535         elif user_prefix:
536             dst = user_prefix + '/lib/iotivity/' + dst
537         else:
538             dst = env.get('BUILD_DIR') + '/deploy/extra/' + dst
539         i_n = ienv.Install(dst, target)
540         ienv.Alias('install', i_n)
541
542
543 def __append_target(ienv, name, targets=None):
544     if targets:
545         env.Alias(name, targets)
546     env.AppendUnique(TS=[name])
547
548
549 def __add_pthread_if_needed(ienv):
550     if 'gcc' == ienv.get('CC') and target_os not in ['android']:
551         ienv.AppendUnique(LINKFLAGS="-pthread")
552
553
554 def __print_targets(env):
555     Help('''
556 ===============================================================================
557 Targets:\n    ''')
558     for t in env.get('TS'):
559         Help(t + ' ')
560     Help('''
561
562 Default: all targets will be built. You can specify the target to build:
563
564     $ scons [options] [target]
565 ===============================================================================
566 ''')
567
568
569 env.AddMethod(__set_dir, 'SetDir')
570 env.AddMethod(__print_targets, 'PrintTargets')
571 env.AddMethod(__src_to_obj, 'SrcToObj')
572 env.AddMethod(__append_target, 'AppendTarget')
573 env.AddMethod(__add_pthread_if_needed, 'AddPthreadIfNeeded')
574 env.AddMethod(__install, 'InstallTarget')
575 env.AddMethod(__installlib, 'UserInstallTargetLib')
576 env.AddMethod(__installbin, 'UserInstallTargetBin')
577 env.AddMethod(__installheader, 'UserInstallTargetHeader')
578 env.AddMethod(__installpcfile, 'UserInstallTargetPCFile')
579 env.AddMethod(__installextra, 'UserInstallTargetExtra')
580 env.SetDir(env.GetLaunchDir())
581 env['ROOT_DIR'] = env.GetLaunchDir() + '/..'
582
583 # make 'env' available to all other build scripts to Import()
584 Export('env')
585
586 ####################################################################i#
587 # Generate the iotivity.pc file from iotivity.pc.in file
588 ######################################################################
589 pc_file = env.get('SRC_DIR') + '/iotivity.pc.in'
590
591 user_prefix = env.get('PREFIX')
592 user_lib = env.get('LIB_INSTALL_DIR')
593
594 if not user_prefix:
595     try:
596         user_prefix = env.get('BUILD_DIR').encode('string_escape')
597     except LookupError:
598         user_prefix = env.get('BUILD_DIR').encode('unicode_escape')
599
600 if not user_lib:
601     user_lib = '$${prefix}/lib'
602
603 defines = []
604 if env.get('LOGGING'):
605     defines.append('-DTB_LOG=1')
606
607 if env.get('ROUTING') == 'GW':
608     defines.append('-DROUTING_GATEWAY=1')
609 elif env.get('ROUTING') == 'EP':
610     defines.append('-DROUTING_EP=1')
611
612 libs = []
613 if env.get('WITH_TCP'):
614     defines.append('-DTCP_ADAPTER=1')
615     if env.get('SECURED') == '1':
616         defines.append('-D__WITH_TLS__=1')
617
618 if env.get('SECURED') == '1':
619     defines.append('-D__WITH_DTLS__=1')
620     if env.get('EXC_PROV_SUPPORT') == '0':
621         libs.append('-locpmapi')
622
623 pc_vars = {
624     '\@VERSION\@': project_version,
625     '\@PREFIX\@': user_prefix,
626     '\@EXEC_PREFIX\@': user_prefix,
627     '\@LIB_INSTALL_DIR\@': user_lib,
628     '\@DEFINES\@': " ".join(defines),
629     '\@LIBS\@': " ".join(libs)
630 }
631
632 env.Substfile(pc_file, SUBST_DICT=pc_vars)
633
634 ######################################################################
635 # Setting global compiler flags
636 ######################################################################
637 target_transport = env.get('TARGET_TRANSPORT')
638 with_mq = env.get('WITH_MQ')
639 with_ra = env.get('WITH_RA')
640 with_tcp = env.get('WITH_TCP')
641 rd_mode = env.get('RD_MODE')
642 with_ra_ibb = env.get('WITH_RA_IBB')
643
644 env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
645
646 if not env.get('PREFIX') and not env.get('LIB_INSTALL_DIR'):
647    env.AppendUnique(LIBPATH = [env.get('BUILD_DIR') + '/deploy'])
648
649 if (env.get('WITH_UPSTREAM_LIBCOAP') == '1'):
650     env.AppendUnique(CPPDEFINES=['WITH_UPSTREAM_LIBCOAP'])
651
652 if (target_os not in ['windows']):
653     env.AppendUnique(CPPDEFINES=['WITH_POSIX'])
654
655 if (target_os in ['darwin', 'ios']):
656     env.AppendUnique(CPPDEFINES=['_DARWIN_C_SOURCE'])
657
658 if (env.get('SECURED') == '1'):
659     env.AppendUnique(CPPDEFINES=['SECURED'])
660     env.AppendUnique(CPPDEFINES=['__WITH_DTLS__'])
661
662 if ((env.get('SECURED') == '1') and with_tcp):
663     env.AppendUnique(CPPDEFINES=['__WITH_TLS__'])
664
665 if (env.get('MULTIPLE_OWNER') == '1'):
666     env.AppendUnique(CPPDEFINES=['MULTIPLE_OWNER'])
667
668 if (env.get('ROUTING') == 'GW'):
669     env.AppendUnique(CPPDEFINES=['ROUTING_GATEWAY'])
670 elif (env.get('ROUTING') == 'EP'):
671     env.AppendUnique(CPPDEFINES=['ROUTING_EP'])
672
673 if (('IP' in target_transport) or ('ALL' in target_transport)):
674     env.AppendUnique(CPPDEFINES=['WITH_BWT'])
675
676 if (target_os in ['linux', 'tizen', 'android', 'yocto'] and with_tcp):
677     env.AppendUnique(CPPDEFINES=['WITH_TCP'])
678
679 if (target_os in ['linux', 'tizen', 'android', 'ios', 'yocto']):
680     if (('BLE' in target_transport) or ('BT' in target_transport) or
681         ('ALL' in target_transport)):
682         env.AppendUnique(CPPDEFINES=['WITH_TCP'])
683
684 if 'ALL' in target_transport:
685     if with_ra:
686         env.AppendUnique(CPPDEFINES=['RA_ADAPTER'])
687     if with_tcp:
688         env.AppendUnique(CPPDEFINES=['TCP_ADAPTER'])
689     if (target_os in ['linux', 'yocto']):
690         env.AppendUnique(
691             CPPDEFINES=['IP_ADAPTER', 'NO_EDR_ADAPTER', 'LE_ADAPTER'])
692     elif (target_os == 'android'):
693         env.AppendUnique(CPPDEFINES=[
694             'IP_ADAPTER', 'EDR_ADAPTER', 'LE_ADAPTER', 'NFC_ADAPTER'
695         ])
696     elif (target_os in ['darwin', 'ios', 'msys_nt', 'windows']):
697         env.AppendUnique(
698             CPPDEFINES=['IP_ADAPTER', 'NO_EDR_ADAPTER', 'NO_LE_ADAPTER'])
699     else:
700         env.AppendUnique(
701             CPPDEFINES=['IP_ADAPTER', 'EDR_ADAPTER', 'LE_ADAPTER'])
702 else:
703     if ('BT' in target_transport):
704         if target_os in ('linux', 'yocto'):
705             msg = "CA Transport BT is not supported "
706             Exit(msg)
707         else:
708             env.AppendUnique(CPPDEFINES=['EDR_ADAPTER'])
709     else:
710         env.AppendUnique(CPPDEFINES=['NO_EDR_ADAPTER'])
711
712     if ('BLE' in target_transport):
713         env.AppendUnique(CPPDEFINES=['LE_ADAPTER'])
714     else:
715         env.AppendUnique(CPPDEFINES=['NO_LE_ADAPTER'])
716
717     if ('IP' in target_transport):
718         env.AppendUnique(CPPDEFINES=['IP_ADAPTER'])
719     else:
720         env.AppendUnique(CPPDEFINES=['NO_IP_ADAPTER'])
721
722     if with_tcp:
723         if (target_os in [
724                 'linux', 'tizen', 'android', 'ios', 'windows', 'yocto'
725         ]):
726             env.AppendUnique(CPPDEFINES=['TCP_ADAPTER', 'WITH_TCP'])
727         else:
728             msg = "CA Transport TCP is not supported "
729             Exit(msg)
730     else:
731         env.AppendUnique(CPPDEFINES=['NO_TCP_ADAPTER'])
732
733     if ('NFC' in target_transport):
734         if (target_os == 'android'):
735             env.AppendUnique(CPPDEFINES=['NFC_ADAPTER'])
736         else:
737             msg = "CA Transport NFC is not supported "
738             Exit(msg)
739     else:
740         env.AppendUnique(CPPDEFINES=['NO_NFC_ADAPTER'])
741
742 if ('SUB' in with_mq):
743     env.AppendUnique(CPPDEFINES=['MQ_SUBSCRIBER', 'WITH_MQ'])
744
745 if ('PUB' in with_mq):
746     env.AppendUnique(CPPDEFINES=['MQ_PUBLISHER', 'WITH_MQ'])
747
748 if ('BROKER' in with_mq):
749     env.AppendUnique(CPPDEFINES=['MQ_BROKER', 'WITH_MQ'])
750
751 env.AppendUnique(CPPDEFINES={'OC_LOG_LEVEL': env.get('LOG_LEVEL')})
752
753 if env.get('LOGGING'):
754     env.AppendUnique(CPPDEFINES=['TB_LOG'])
755
756 if env.get('WITH_CLOUD') and with_tcp:
757     env.AppendUnique(CPPDEFINES=['WITH_CLOUD'])
758
759 if 'CLIENT' in rd_mode:
760     env.AppendUnique(CPPDEFINES=['RD_CLIENT'])
761
762 if 'SERVER' in rd_mode:
763     env.AppendUnique(CPPDEFINES=['RD_SERVER'])
764
765 if with_ra_ibb:
766     env.AppendUnique(CPPDEFINES=['RA_ADAPTER_IBB'])
767
768 if env.get('RELEASE'):
769     env.AppendUnique(CPPDEFINES=['NDEBUG'])
770
771 env.SConscript('external_builders.scons')
772 ######################################################################
773 # Link scons to Yocto cross-toolchain ONLY when target_os is yocto
774 ######################################################################
775 if target_os == "yocto":
776     '''
777     This code injects Yocto cross-compilation tools+flags into the scons
778     construction environment in order to invoke the relevant tools while
779     performing a build.
780     '''
781     import os.path
782     try:
783         CC = os.environ['CC']
784         target_prefix = CC.split()[0]
785         target_prefix = target_prefix[:-3]
786         tools = {
787             "CC": target_prefix + "gcc",
788             "CXX": target_prefix + "g++",
789             "AS": target_prefix + "as",
790             "LD": target_prefix + "ld",
791             "GDB": target_prefix + "gdb",
792             "STRIP": target_prefix + "strip",
793             "RANLIB": target_prefix + "ranlib",
794             "OBJCOPY": target_prefix + "objcopy",
795             "OBJDUMP": target_prefix + "objdump",
796             "AR": target_prefix + "ar",
797             "NM": target_prefix + "nm",
798             "M4": "m4",
799             "STRINGS": target_prefix + "strings"
800         }
801         PATH = os.environ['PATH'].split(os.pathsep)
802         for tool in tools:
803             if tool in os.environ:
804                 for path in PATH:
805                     if os.path.isfile(os.path.join(path, tools[tool])):
806                         env[tool] = os.path.join(path, os.environ[tool])
807                         break
808         env['CROSS_COMPILE'] = target_prefix[:-1]
809     except:
810         msg = "ERROR in Yocto cross-toolchain environment"
811         Exit(msg)
812     '''
813     Now reset TARGET_OS to linux so that all linux specific build configurations
814     hereupon apply for the entirety of the build process.
815     '''
816     env['TARGET_OS'] = 'linux'
817     '''
818     We want to preserve debug symbols to allow BitBake to generate both DEBUG and
819     RELEASE packages for OIC.
820     '''
821     env.AppendUnique(CCFLAGS=['-g'])
822     '''
823     Additional flags to pass to the Yocto toolchain.
824     '''
825     env.AppendUnique(CPPDEFINES=['__linux__', '_GNU_SOURCE'])
826     env.AppendUnique(CFLAGS=['-std=gnu99'])
827     env.AppendUnique(CCFLAGS=['-Wall', '-Wextra', '-fPIC'])
828     env.AppendUnique(LIBS=['dl', 'pthread', 'uuid'])
829 else:
830     '''
831     If target_os is not Yocto, continue with the regular build process
832     '''
833     # Load config of target os
834     env.SConscript(target_os + '/SConscript')
835
836 if env.get('CROSS_COMPILE'):
837     env.Append(RPATH=env.Literal('\\$$ORIGIN'))
838 else:
839     env.Append(RPATH=env.get('BUILD_DIR'))
840
841 # Delete the temp files of configuration
842 if env.GetOption('clean'):
843     dir = env.get('SRC_DIR')
844
845     if os.path.exists(dir + '/config.log'):
846         Execute(Delete(dir + '/config.log'))
847     if os.path.exists(dir + '/.sconsign.dblite'):
848         Execute(Delete(dir + '/.sconsign.dblite'))
849     if os.path.exists(dir + '/.sconf_temp'):
850         Execute(Delete(dir + '/.sconf_temp'))
851
852 ######################################################################
853 # Check for PThreads support
854 ######################################################################
855 import iotivityconfig
856 from iotivityconfig import *
857
858 conf = Configure(
859     env, custom_tests={'CheckPThreadsSupport': iotivityconfig.check_pthreads})
860
861 # Identify whether we have pthreads support, which is necessary for
862 # threading and mutexes.  This will set the environment variable
863 # POSIX_SUPPORTED, 1 if it is supported, 0 otherwise
864 conf.CheckPThreadsSupport()
865 env = conf.Finish()
866
867 ######################################################################
868 # Generate Cbor from json files
869 ######################################################################
870 json2cbor = env.get('BUILD_DIR') + 'resource/csdk/security/tool/json2cbor' + env.get('PROGSUFFIX')
871
872 def generate_actions(source, target, env, for_signature):
873     Depends(target, json2cbor)
874     return " %s %s %s" % (json2cbor, source[0], target[0])
875
876 builder = Builder(generator = generate_actions,
877                   suffix = '.dat',
878                   src_suffix = '.json')
879
880 env.Append(BUILDERS = {'Cbor' : builder})
881
882
883 def ScanJSON(env, directory=None):
884     targets = []
885     if not directory:
886         directory = Dir('.').srcnode().path
887     if env.GetOption('clean') or env.get('SECURED') != '1':
888         return targets
889     dst_dir = env.get('BUILD_DIR') + '/' + directory + '/'
890     src_dir = env.get('SRC_DIR') + '/' + directory + '/'
891     for json_file in Glob('*.json'):
892         filename = str(json_file.name)
893         src = src_dir + filename
894         dst = dst_dir + filename
895         targets += Command(dst, src, Copy("$TARGET", "$SOURCE"))
896         if env.get('CROSS_COMPILE') == None:
897             # Copy back compiled files to sources (to be used when compilation is not possible)
898             cbor_file = env.Cbor(json_file)
899             targets.append(cbor_file)
900             cbor_file = Flatten(cbor_file)[0].name
901             src = dst_dir + cbor_file
902             dst = src_dir + cbor_file
903             Command(dst, src, Copy("$TARGET", "$SOURCE"))
904         else:
905             # Can compile files at build time, so rely on previous generated files
906             cbor_file = re.sub('\.json$', '.dat', filename)
907             src = src_dir + cbor_file
908             dst = dst_dir + cbor_file
909             targets += Command(dst, src, Copy("$TARGET", "$SOURCE"))
910     return targets
911
912 AddMethod(env, ScanJSON)
913
914 ######################################################################
915 env.SConscript('external_libs.scons')
916
917 # these variables depend on a subsidiary script having set a path to
918 # use as the default. Such paths may include embedded version strings,
919 # for example, and we want to not embed those all over, so defer setting
920 # the help until we're back from those scripts. Then we can finally
921 # build the help message. However, it's also possible that we never
922 # needed to call those subsidary scripts, and then we come back with
923 # values unset, so deal with that as well.
924 if env.get('ANDROID_NDK'):
925     android_ndk = env['ANDROID_NDK']
926 else:
927     android_ndk = None
928 if env.get('ANDROID_GRADLE'):
929     android_gradle = env['ANDROID_GRADLE']
930 else:
931     android_gradle = None
932 if env.get('ANDROID_HOME'):
933     android_sdk = env['ANDROID_HOME']
934 else:
935     android_sdk = None
936 help_vars.AddVariables(
937     PathVariable('ANDROID_NDK',
938                  'Android NDK path',
939                  default=android_ndk,
940                  validator=PathVariable.PathAccept),
941     PathVariable('ANDROID_GRADLE',
942                  'Gradle executable location',
943                  default=android_gradle,
944                  validator=PathVariable.PathIsFile),
945     PathVariable('ANDROID_HOME',
946                  'Android SDK path',
947                  default=android_sdk,
948                  validator=PathVariable.PathAccept),
949 )
950 help_vars.Update(env)
951 Help(help_vars.GenerateHelpText(env, sort=True))
952 # Replicate change that occured after help_var initialisation from env
953 if target_os == "yocto":
954     env['TARGET_OS'] = 'linux'
955
956 Return('env')