[IOT-3096] Revert Fix for TCP IPv6 socket
[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', 'armv7l', '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', 'armv7l'],
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 __chrpath(target, source, env):
465     '''
466     Remove RPATH (if installed elsewhere)
467     '''
468     if target_os in ['linux', 'tizen']:
469         env.Command(None, target, 'chrpath -d $SOURCE')
470
471
472 def __installlib(ienv, targets, name=''):
473     '''
474     Install files to system, using "scons install" and remove rpath info if present
475     If prefix or lib install dir is not specified, for developer convenience
476     files are copied in relative "deploy" folder along executables (rpath is kept)
477     to avoid overlap with "internal place" above
478     '''
479     user_prefix = env.get('PREFIX')
480     if user_prefix:
481         user_lib = env.get('LIB_INSTALL_DIR')
482         if user_lib:
483             dst_dir  = user_lib
484         else:
485             dst_dir  = user_prefix + '/lib'
486     else:
487         dst_dir  = env.get('BUILD_DIR') + '/deploy'
488     action = ienv.Install(dst_dir, targets)
489     if not user_prefix and str(targets[0]).endswith(env['SHLIBSUFFIX']):
490         ienv.AddPostAction(action, __chrpath)
491     ienv.Alias("install", action)
492
493
494
495 def __installbin(ienv, targets, name=''):
496     '''
497     ' Install files to system, using "scons install"
498     ' If prefix is not specified, for developer convenience
499     ' files are copied in relative "deploy" folder along libraries
500     '''
501     user_prefix = env.get('PREFIX')
502     if user_prefix:
503         dst_dir  = user_prefix + '/bin'
504     else:
505         dst_dir  = env.get('BUILD_DIR') + '/deploy'
506     ienv.Alias("install", ienv.Install(dst_dir , targets))
507
508
509 def __installheader(ienv, targets, dir, name):
510     user_prefix = env.get('PREFIX')
511     if user_prefix:
512         i_n = ienv.Install(user_prefix + '/include/iotivity/' + dir, targets)
513     else:
514         i_n = ienv.Install(env.get('BUILD_DIR') + 'deploy/include/' + dir, targets)
515     ienv.Alias("install", i_n)
516
517
518 def __installpcfile(ienv, targets, name):
519     user_prefix = env.get('PREFIX')
520     if user_prefix:
521         user_lib = env.get('LIB_INSTALL_DIR')
522         if user_lib:
523             i_n = ienv.Install(user_lib + '/pkgconfig', targets)
524         else:
525             i_n = ienv.Install(user_prefix + '/lib/pkgconfig', targets)
526     else:
527         i_n = ienv.Install(env.get('BUILD_DIR') + 'deploy/pkgconfig', targets)
528     ienv.Alias("install", i_n)
529
530
531 def __installextra(ienv, targets, subdir="."):
532     '''
533     Install extra files, by default use file relative location as subdir
534     or use any other prefix of your choice, or in explicit "deploy" folder
535     '''
536     user_lib = env.get('LIB_INSTALL_DIR')
537     user_prefix = env.get('PREFIX')
538     for target in targets:
539         if "." == subdir:
540             dst = Dir('.').srcnode().path
541         else:
542             dst = subdir
543         if user_lib:
544             dst = user_lib + '/iotivity/' + dst
545         elif user_prefix:
546             dst = user_prefix + '/lib/iotivity/' + dst
547         else:
548             dst = env.get('BUILD_DIR') + '/deploy/extra/' + dst
549         i_n = ienv.Install(dst, target)
550         ienv.Alias('install', i_n)
551
552
553 def __append_target(ienv, name, targets=None):
554     if targets:
555         env.Alias(name, targets)
556     env.AppendUnique(TS=[name])
557
558
559 def __add_pthread_if_needed(ienv):
560     if 'gcc' == ienv.get('CC') and target_os not in ['android']:
561         ienv.AppendUnique(LINKFLAGS="-pthread")
562
563
564 def __print_targets(env):
565     Help('''
566 ===============================================================================
567 Targets:\n    ''')
568     for t in env.get('TS'):
569         Help(t + ' ')
570     Help('''
571
572 Default: all targets will be built. You can specify the target to build:
573
574     $ scons [options] [target]
575 ===============================================================================
576 ''')
577
578
579 env.AddMethod(__set_dir, 'SetDir')
580 env.AddMethod(__print_targets, 'PrintTargets')
581 env.AddMethod(__src_to_obj, 'SrcToObj')
582 env.AddMethod(__append_target, 'AppendTarget')
583 env.AddMethod(__add_pthread_if_needed, 'AddPthreadIfNeeded')
584 env.AddMethod(__install, 'InstallTarget')
585 env.AddMethod(__installlib, 'UserInstallTargetLib')
586 env.AddMethod(__installbin, 'UserInstallTargetBin')
587 env.AddMethod(__installheader, 'UserInstallTargetHeader')
588 env.AddMethod(__installpcfile, 'UserInstallTargetPCFile')
589 env.AddMethod(__installextra, 'UserInstallTargetExtra')
590 env.SetDir(env.GetLaunchDir())
591 env['ROOT_DIR'] = env.GetLaunchDir() + '/..'
592
593 # make 'env' available to all other build scripts to Import()
594 Export('env')
595
596 ####################################################################i#
597 # Generate the iotivity.pc file from iotivity.pc.in file
598 ######################################################################
599 pc_file = env.get('SRC_DIR') + '/iotivity.pc.in'
600
601 user_prefix = env.get('PREFIX')
602 user_lib = env.get('LIB_INSTALL_DIR')
603
604 if not user_prefix:
605     try:
606         user_prefix = env.get('BUILD_DIR').encode('string_escape')
607     except LookupError:
608         user_prefix = env.get('BUILD_DIR').encode('unicode_escape')
609
610 if not user_lib:
611     user_lib = '$${prefix}/lib'
612
613 defines = []
614 if env.get('LOGGING'):
615     defines.append('-DTB_LOG=1')
616
617 if env.get('ROUTING') == 'GW':
618     defines.append('-DROUTING_GATEWAY=1')
619 elif env.get('ROUTING') == 'EP':
620     defines.append('-DROUTING_EP=1')
621
622 libs = []
623 if env.get('WITH_TCP'):
624     defines.append('-DTCP_ADAPTER=1')
625     if env.get('SECURED') == '1':
626         defines.append('-D__WITH_TLS__=1')
627
628 if env.get('SECURED') == '1':
629     defines.append('-D__WITH_DTLS__=1')
630     if env.get('EXC_PROV_SUPPORT') == '0':
631         libs.append('-locpmapi')
632
633 pc_vars = {
634     '\@VERSION\@': project_version,
635     '\@PREFIX\@': user_prefix,
636     '\@EXEC_PREFIX\@': user_prefix,
637     '\@LIB_INSTALL_DIR\@': user_lib,
638     '\@DEFINES\@': " ".join(defines),
639     '\@LIBS\@': " ".join(libs)
640 }
641
642 env.Substfile(pc_file, SUBST_DICT=pc_vars)
643
644 ######################################################################
645 # Setting global compiler flags
646 ######################################################################
647 target_transport = env.get('TARGET_TRANSPORT')
648 with_mq = env.get('WITH_MQ')
649 with_ra = env.get('WITH_RA')
650 with_tcp = env.get('WITH_TCP')
651 rd_mode = env.get('RD_MODE')
652 with_ra_ibb = env.get('WITH_RA_IBB')
653
654 env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
655
656 if not env.get('PREFIX') and not env.get('LIB_INSTALL_DIR'):
657    env.AppendUnique(LIBPATH = [env.get('BUILD_DIR') + '/deploy'])
658
659 if (env.get('WITH_UPSTREAM_LIBCOAP') == '1'):
660     env.AppendUnique(CPPDEFINES=['WITH_UPSTREAM_LIBCOAP'])
661
662 if (target_os not in ['windows']):
663     env.AppendUnique(CPPDEFINES=['WITH_POSIX'])
664
665 if (target_os in ['darwin', 'ios']):
666     env.AppendUnique(CPPDEFINES=['_DARWIN_C_SOURCE'])
667
668 if (env.get('SECURED') == '1'):
669     env.AppendUnique(CPPDEFINES=['SECURED'])
670     env.AppendUnique(CPPDEFINES=['__WITH_DTLS__'])
671
672 if ((env.get('SECURED') == '1') and with_tcp):
673     env.AppendUnique(CPPDEFINES=['__WITH_TLS__'])
674
675 if (env.get('MULTIPLE_OWNER') == '1'):
676     env.AppendUnique(CPPDEFINES=['MULTIPLE_OWNER'])
677
678 if (env.get('ROUTING') == 'GW'):
679     env.AppendUnique(CPPDEFINES=['ROUTING_GATEWAY'])
680 elif (env.get('ROUTING') == 'EP'):
681     env.AppendUnique(CPPDEFINES=['ROUTING_EP'])
682
683 if (('IP' in target_transport) or ('ALL' in target_transport)):
684     env.AppendUnique(CPPDEFINES=['WITH_BWT'])
685
686 if (target_os in ['linux', 'tizen', 'android', 'yocto'] and with_tcp):
687     env.AppendUnique(CPPDEFINES=['WITH_TCP'])
688
689 if (target_os in ['linux', 'tizen', 'android', 'ios', 'yocto']):
690     if (('BLE' in target_transport) or ('BT' in target_transport) or
691         ('ALL' in target_transport)):
692         env.AppendUnique(CPPDEFINES=['WITH_TCP'])
693
694 if 'ALL' in target_transport:
695     if with_ra:
696         env.AppendUnique(CPPDEFINES=['RA_ADAPTER'])
697     if with_tcp:
698         env.AppendUnique(CPPDEFINES=['TCP_ADAPTER'])
699     if (target_os in ['linux', 'yocto']):
700         env.AppendUnique(
701             CPPDEFINES=['IP_ADAPTER', 'NO_EDR_ADAPTER', 'LE_ADAPTER'])
702     elif (target_os == 'android'):
703         env.AppendUnique(CPPDEFINES=[
704             'IP_ADAPTER', 'EDR_ADAPTER', 'LE_ADAPTER', 'NFC_ADAPTER'
705         ])
706     elif (target_os in ['darwin', 'ios', 'msys_nt', 'windows']):
707         env.AppendUnique(
708             CPPDEFINES=['IP_ADAPTER', 'NO_EDR_ADAPTER', 'NO_LE_ADAPTER'])
709     else:
710         env.AppendUnique(
711             CPPDEFINES=['IP_ADAPTER', 'EDR_ADAPTER', 'LE_ADAPTER'])
712 else:
713     if ('BT' in target_transport):
714         if target_os in ('linux', 'yocto'):
715             msg = "CA Transport BT is not supported "
716             Exit(msg)
717         else:
718             env.AppendUnique(CPPDEFINES=['EDR_ADAPTER'])
719     else:
720         env.AppendUnique(CPPDEFINES=['NO_EDR_ADAPTER'])
721
722     if ('BLE' in target_transport):
723         env.AppendUnique(CPPDEFINES=['LE_ADAPTER'])
724     else:
725         env.AppendUnique(CPPDEFINES=['NO_LE_ADAPTER'])
726
727     if ('IP' in target_transport):
728         env.AppendUnique(CPPDEFINES=['IP_ADAPTER'])
729     else:
730         env.AppendUnique(CPPDEFINES=['NO_IP_ADAPTER'])
731
732     if with_tcp:
733         if (target_os in [
734                 'linux', 'tizen', 'android', 'ios', 'windows', 'yocto'
735         ]):
736             env.AppendUnique(CPPDEFINES=['TCP_ADAPTER', 'WITH_TCP'])
737         else:
738             msg = "CA Transport TCP is not supported "
739             Exit(msg)
740     else:
741         env.AppendUnique(CPPDEFINES=['NO_TCP_ADAPTER'])
742
743     if ('NFC' in target_transport):
744         if (target_os == 'android'):
745             env.AppendUnique(CPPDEFINES=['NFC_ADAPTER'])
746         else:
747             msg = "CA Transport NFC is not supported "
748             Exit(msg)
749     else:
750         env.AppendUnique(CPPDEFINES=['NO_NFC_ADAPTER'])
751
752 if ('SUB' in with_mq):
753     env.AppendUnique(CPPDEFINES=['MQ_SUBSCRIBER', 'WITH_MQ'])
754
755 if ('PUB' in with_mq):
756     env.AppendUnique(CPPDEFINES=['MQ_PUBLISHER', 'WITH_MQ'])
757
758 if ('BROKER' in with_mq):
759     env.AppendUnique(CPPDEFINES=['MQ_BROKER', 'WITH_MQ'])
760
761 env.AppendUnique(CPPDEFINES={'OC_LOG_LEVEL': env.get('LOG_LEVEL')})
762
763 if env.get('LOGGING'):
764     env.AppendUnique(CPPDEFINES=['TB_LOG'])
765
766 if env.get('WITH_CLOUD') and with_tcp:
767     env.AppendUnique(CPPDEFINES=['WITH_CLOUD'])
768
769 if 'CLIENT' in rd_mode:
770     env.AppendUnique(CPPDEFINES=['RD_CLIENT'])
771
772 if 'SERVER' in rd_mode:
773     env.AppendUnique(CPPDEFINES=['RD_SERVER'])
774
775 if with_ra_ibb:
776     env.AppendUnique(CPPDEFINES=['RA_ADAPTER_IBB'])
777
778 if env.get('RELEASE'):
779     env.AppendUnique(CPPDEFINES=['NDEBUG'])
780
781 env.SConscript('external_builders.scons')
782 ######################################################################
783 # Link scons to Yocto cross-toolchain ONLY when target_os is yocto
784 ######################################################################
785 if target_os == "yocto":
786     '''
787     This code injects Yocto cross-compilation tools+flags into the scons
788     construction environment in order to invoke the relevant tools while
789     performing a build.
790     '''
791     import os.path
792     try:
793         CC = os.environ['CC']
794         target_prefix = CC.split()[0]
795         target_prefix = target_prefix[:-3]
796         tools = {
797             "CC": target_prefix + "gcc",
798             "CXX": target_prefix + "g++",
799             "AS": target_prefix + "as",
800             "LD": target_prefix + "ld",
801             "GDB": target_prefix + "gdb",
802             "STRIP": target_prefix + "strip",
803             "RANLIB": target_prefix + "ranlib",
804             "OBJCOPY": target_prefix + "objcopy",
805             "OBJDUMP": target_prefix + "objdump",
806             "AR": target_prefix + "ar",
807             "NM": target_prefix + "nm",
808             "M4": "m4",
809             "STRINGS": target_prefix + "strings"
810         }
811         PATH = os.environ['PATH'].split(os.pathsep)
812         for tool in tools:
813             if tool in os.environ:
814                 for path in PATH:
815                     if os.path.isfile(os.path.join(path, tools[tool])):
816                         env[tool] = os.path.join(path, os.environ[tool])
817                         break
818         env['CROSS_COMPILE'] = target_prefix[:-1]
819     except:
820         msg = "ERROR in Yocto cross-toolchain environment"
821         Exit(msg)
822     '''
823     Now reset TARGET_OS to linux so that all linux specific build configurations
824     hereupon apply for the entirety of the build process.
825     '''
826     env['TARGET_OS'] = 'linux'
827     '''
828     We want to preserve debug symbols to allow BitBake to generate both DEBUG and
829     RELEASE packages for OIC.
830     '''
831     env.AppendUnique(CCFLAGS=['-g'])
832     '''
833     Additional flags to pass to the Yocto toolchain.
834     '''
835     env.AppendUnique(CPPDEFINES=['__linux__', '_GNU_SOURCE'])
836     env.AppendUnique(CFLAGS=['-std=gnu99'])
837     env.AppendUnique(CCFLAGS=['-Wall', '-Wextra', '-fPIC'])
838     env.AppendUnique(LIBS=['dl', 'pthread', 'uuid'])
839 else:
840     '''
841     If target_os is not Yocto, continue with the regular build process
842     '''
843     # Load config of target os
844     env.SConscript(target_os + '/SConscript')
845
846 if env.get('CROSS_COMPILE'):
847     env.Append(RPATH=env.Literal('\\$$ORIGIN'))
848 else:
849     env.Append(RPATH=env.get('BUILD_DIR'))
850
851 # Delete the temp files of configuration
852 if env.GetOption('clean'):
853     dir = env.get('SRC_DIR')
854
855     if os.path.exists(dir + '/config.log'):
856         Execute(Delete(dir + '/config.log'))
857     if os.path.exists(dir + '/.sconsign.dblite'):
858         Execute(Delete(dir + '/.sconsign.dblite'))
859     if os.path.exists(dir + '/.sconf_temp'):
860         Execute(Delete(dir + '/.sconf_temp'))
861
862 ######################################################################
863 # Check for PThreads support
864 ######################################################################
865 import iotivityconfig
866 from iotivityconfig import *
867
868 conf = Configure(
869     env, custom_tests={'CheckPThreadsSupport': iotivityconfig.check_pthreads})
870
871 # Identify whether we have pthreads support, which is necessary for
872 # threading and mutexes.  This will set the environment variable
873 # POSIX_SUPPORTED, 1 if it is supported, 0 otherwise
874 conf.CheckPThreadsSupport()
875 env = conf.Finish()
876
877 ######################################################################
878 # Generate Cbor from json files
879 ######################################################################
880 json2cbor = env.get('BUILD_DIR') + 'resource/csdk/security/tool/json2cbor' + env.get('PROGSUFFIX')
881
882 def generate_actions(source, target, env, for_signature):
883     Depends(target, json2cbor)
884     return " %s %s %s" % (json2cbor, source[0], target[0])
885
886 builder = Builder(generator = generate_actions,
887                   suffix = '.dat',
888                   src_suffix = '.json')
889
890 env.Append(BUILDERS = {'Cbor' : builder})
891
892
893 def ScanJSON(env, directory=None):
894     targets = []
895     if not directory:
896         directory = Dir('.').srcnode().path
897     if env.GetOption('clean') or env.get('SECURED') != '1':
898         return targets
899     dst_dir = env.get('BUILD_DIR') + '/' + directory + '/'
900     src_dir = env.get('SRC_DIR') + '/' + directory + '/'
901     for json_file in Glob('*.json'):
902         filename = str(json_file.name)
903         src = src_dir + filename
904         dst = dst_dir + filename
905         targets += Command(dst, src, Copy("$TARGET", "$SOURCE"))
906         if env.get('CROSS_COMPILE') == None:
907             # Copy back compiled files to sources (to be used when compilation is not possible)
908             cbor_file = env.Cbor(json_file)
909             targets.append(cbor_file)
910             cbor_file = Flatten(cbor_file)[0].name
911             src = dst_dir + cbor_file
912             dst = src_dir + cbor_file
913             Command(dst, src, Copy("$TARGET", "$SOURCE"))
914         else:
915             # Can compile files at build time, so rely on previous generated files
916             cbor_file = re.sub('\.json$', '.dat', filename)
917             src = src_dir + cbor_file
918             dst = dst_dir + cbor_file
919             targets += Command(dst, src, Copy("$TARGET", "$SOURCE"))
920     return targets
921
922 AddMethod(env, ScanJSON)
923
924 ######################################################################
925 env.SConscript('external_libs.scons')
926
927 # these variables depend on a subsidiary script having set a path to
928 # use as the default. Such paths may include embedded version strings,
929 # for example, and we want to not embed those all over, so defer setting
930 # the help until we're back from those scripts. Then we can finally
931 # build the help message. However, it's also possible that we never
932 # needed to call those subsidary scripts, and then we come back with
933 # values unset, so deal with that as well.
934 if env.get('ANDROID_NDK'):
935     android_ndk = env['ANDROID_NDK']
936 else:
937     android_ndk = None
938 if env.get('ANDROID_GRADLE'):
939     android_gradle = env['ANDROID_GRADLE']
940 else:
941     android_gradle = None
942 if env.get('ANDROID_HOME'):
943     android_sdk = env['ANDROID_HOME']
944 else:
945     android_sdk = None
946 help_vars.AddVariables(
947     PathVariable('ANDROID_NDK',
948                  'Android NDK path',
949                  default=android_ndk,
950                  validator=PathVariable.PathAccept),
951     PathVariable('ANDROID_GRADLE',
952                  'Gradle executable location',
953                  default=android_gradle,
954                  validator=PathVariable.PathIsFile),
955     PathVariable('ANDROID_HOME',
956                  'Android SDK path',
957                  default=android_sdk,
958                  validator=PathVariable.PathAccept),
959 )
960 help_vars.Update(env)
961 Help(help_vars.GenerateHelpText(env, sort=True))
962 # Replicate change that occured after help_var initialisation from env
963 if target_os == "yocto":
964     env['TARGET_OS'] = 'linux'
965
966 Return('env')