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