Build extlibs libs straight to destination
[iotivity.git] / extlibs / libcoap / SConscript
1 ################################################################################
2 #
3 # Copyright 2016 Intel Corporation
4 #
5 #
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 #      http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19 ################################################################################
20
21 ##
22 # 'libcoap' script to make sure LibCoAP library is "installed"
23 ##
24
25 import os
26 import subprocess
27 import SCons.Util
28
29 Import('env')
30
31 target_os = env.get('TARGET_OS')
32 src_dir = env.get('SRC_DIR')
33 build_dir = env.get('BUILD_DIR')
34 ca_transport = env.get('TARGET_TRANSPORT')
35 with_tcp = env.get('WITH_TCP')
36 with_upstream_libcoap = env.get('WITH_UPSTREAM_LIBCOAP')
37
38 # Temporary LibCoAP URL is a fork of the original.
39 # Once a pull request is merged, change this back to the obgm original below.
40 libcoap_repo_url = 'https://github.com/dthaler/libcoap'
41 #libcoap_repo_url    = 'https://github.com/obgm/libcoap'
42
43 ######################################################################
44 # Check for, and possibly update libcoap from git
45 ######################################################################
46 # The libcoap tag here must match the one in extlibs/libcoap/prep.sh.
47 libcoap_version = 'IoTivity-1.4'
48 libcoap_dir = os.path.join(src_dir, 'extlibs', 'libcoap', 'libcoap')
49
50 # git commands
51 libcoap_checkout_cmd = 'git clone ' + libcoap_repo_url + '.git extlibs/libcoap/libcoap -b ' + libcoap_version
52 libcoap_update_cmd = 'git fetch --tags && git checkout -f ' + libcoap_version
53 libcoap_tag_cmd = 'git tag -l ' + libcoap_version
54 libcoap_sync_cmd = 'git checkout -f ' + libcoap_version
55 libcoap_chdir_cmd = 'cd ' + libcoap_dir
56
57 # define msg strings here so code sequence is readable
58 err_nocoap = '''
59 *********************************** Error: ************************************
60 * Please download libcoap using the following command:
61 *     $ %s
62 *******************************************************************************
63 ''' % libcoap_checkout_cmd
64
65 info_oldcoap = '''
66 ******************************* Info: *****************************************
67 * Your libCoAP repo is not up to date with the latest version we require (%s).
68 *******************************************************************************
69 ''' % libcoap_version
70
71 info_updatingcoap = '''
72 ******************************* Info: *****************************************
73 * Automatically updating libcoap to version %s.
74 *******************************************************************************
75 ''' % libcoap_version
76
77 info_updatecoap = '''
78 ******************************* Info: *****************************************
79 * Please update using the following commands:
80 *   %s
81 *   %s
82 *******************************************************************************
83 ''' % (libcoap_chdir_cmd, libcoap_update_cmd)
84
85 info_forkedcoap = '''
86 ******************************* Info: *****************************************
87 * Using FORKED copy of libCoap located in:
88 * resource/csdk/connectivity/lib/libcoap-4.1.1
89 *******************************************************************************
90 '''
91
92 libcoap_env = env.Clone()
93 if with_upstream_libcoap == '0':
94     print(info_forkedcoap)
95 else:
96     # If using the github libcoap, check for correct version
97     # Right now this script assumes the revision is a tag, not a branch or
98     # an arbitrary commit. If this changes, update the check below, or else
99     # the script will always conclude the repo is not up to date because a tag
100     # with that name does not exist.
101     print('*** Checking for installation of libCoAP %s ***' % libcoap_version)
102     if not os.path.exists(libcoap_dir):
103         Exit(err_nocoap)
104
105     # Tizen uses its own process to prepare the libcoap repo in gbsbuild.sh
106     # and cannot use git during the build. That process removes .git.
107     # Do the git checks only if .git is present.
108     if os.path.exists(libcoap_dir + '/.git/HEAD'):
109         start_dir = os.getcwd()
110         os.chdir(libcoap_dir)
111         out = subprocess.check_output(libcoap_tag_cmd, shell=True).rstrip()
112         if libcoap_version not in SCons.Util.to_String(out):
113             print(info_oldcoap)
114             if libcoap_env.get('AUTOMATIC_UPDATE'):
115                 print(info_updatingcoap)
116                 try:
117                     retcode = subprocess.call(libcoap_update_cmd, shell=True)
118                     if retcode:
119                         Exit("libcoap: update failed: " + str(retcode))
120                 except OSError as e:
121                     Exit("libcoap: execution failed: " + e)
122             else:
123                 Exit(info_updatecoap)
124         # to be pedantic, always checkout to the tag
125         retcode = subprocess.call(libcoap_sync_cmd, shell=True)
126         if retcode:
127             Exit("libcoap: checkout failed: " + str(retcode))
128         os.chdir(start_dir)
129
130 ######################################################################
131 # Build libCoAP
132 ######################################################################
133 # Build flags
134 if target_os not in ['windows', 'winrt', 'msys_nt']:
135     # libcoap uses ipv6 features from <netinet/in.h> which only
136     # turn on if __GNU_SOURCE is defined
137     libcoap_env.AppendUnique(CPPDEFINES=['WITH_POSIX', '_GNU_SOURCE'])
138     libcoap_env.AppendUnique(CFLAGS=['-std=gnu99','-fPIC'])
139
140 if target_os not in ['windows', 'winrt']:
141     libcoap_env.AppendUnique(CFLAGS=['-Wall', '-ffunction-sections',
142             '-fdata-sections', '-fno-exceptions'])
143
144 if target_os == 'msys_nt':
145     libcoap_env.AppendUnique(CPPDEFINES=['_DEFAULT_SOURCE'])
146     libcoap_env.AppendUnique(CFLAGS=['-std=c99'])
147
148 if target_os in ['linux', 'tizen', 'android', 'ios', 'windows']:
149     if with_tcp == True:
150         libcoap_env.AppendUnique(CPPDEFINES=['WITH_TCP', 'WITH_WS'])
151
152 if target_os in ['linux', 'tizen', 'android']:
153     libcoap_env.AppendUnique(LIBS=['log'])
154     if 'BLE' in ca_transport or 'BT' in ca_transport or 'ALL' in ca_transport:
155         libcoap_env.AppendUnique(CPPDEFINES=['WITH_TCP'])
156
157 # Remove -Werror build flag when building the 'coap' library  it
158 # is external code. see IOT-2539
159 while '-Werror' in libcoap_env['CCFLAGS']: libcoap_env['CCFLAGS'].remove('-Werror')
160 if env['CC'] == 'cl':
161     # In external code, don't fail on warnings:
162     #  - warning C4267: conversion from size_t to unsigned short, possible loss of data
163     #  - warning C4244: '=' : conversion from 'size_t' to 'unsigned short', possible loss of data
164     # TODO: fix libcoap fork, we introduced this error, it isn't from upstream
165     libcoap_env.AppendUnique(CCFLAGS=['/wd4267', '/wd4244'])
166
167 ######################################################################
168 # Source files and Target(s)
169 ######################################################################
170 if with_upstream_libcoap == '0':
171     # For bring up purposes only, the forked version will live here.
172     libcoap_src_root = '#/resource/csdk/connectivity/lib/libcoap-4.1.1/'
173     libcoap_src = Glob(libcoap_src_root + '*.c')
174 else:
175     # We need to generate a pair of headers to describe our config
176     coap_h_pc_file = os.path.join(libcoap_dir, 'include', 'coap', 'coap.h.in')
177     config_h_file = os.path.join(libcoap_dir, 'include', 'coap', 'coap_config.h')
178     libcoap_env.PrependUnique(CPPPATH=['libcoap/include/coap'])
179     libcoap_env.AppendUnique(CPPDEFINES=['WITH_UPSTREAM_LIBCOAP'])
180
181     # Generate coap_config.h
182     target_arch = env.get('TARGET_ARCH')
183     lib_prefix = '' + str(libcoap_env.get('LIBPREFIX'))
184
185     if not os.path.isfile(config_h_file) and env.GetOption not in ('clean', 'help'):
186         if ((target_os == 'windows') and (libcoap_env.get('MSVC_UWP_APP') == '1')):
187             conf = Configure(libcoap_env.Clone(LIBS=libcoap_env.get('UWP_LIBS')))
188         else:
189             conf = Configure(libcoap_env.Clone(LIBS=[]))
190
191         config_h_header = '''
192 /* ****************************************************************************
193  * coap_config.h - libcoap platform-specific configuration header.
194  *
195  * Auto-generated code for the %s %s platform. Do not edit.
196  *
197  *************************************************************************** */
198
199 #ifndef _COAP_CONFIG_H_
200 #define _COAP_CONFIG_H_
201
202 ''' % (str(target_os), str(target_arch))
203
204         config_h_footer = '''
205
206 /* Define to the full name of this package. */
207 #define PACKAGE_NAME "%s"
208
209 /* Define to the full name and version of this package. */
210 #define PACKAGE_STRING "%s"
211
212 #ifndef COAP_STATIC_INLINE
213 #  if defined(__cplusplus)
214 #    define COAP_STATIC_INLINE inline
215 #  else
216 #    ifdef _MSC_VER
217 #      define COAP_STATIC_INLINE static __inline
218 #    else
219 #      define COAP_STATIC_INLINE static inline
220 #    endif
221 #  endif
222 #endif
223
224 #endif // _COAP_CONFIG_H_
225
226 ''' % (str(lib_prefix + 'coap'), str(lib_prefix + 'coap ' + libcoap_version))
227
228         config_h_body = ''
229
230         cxx_headers = [
231             'arpa/inet.h',
232             'assert.h',
233             'limits.h',
234             'netinet/in.h',
235             'stdio.h',
236             'strings.h',
237             'sys/select.h',
238             'sys/socket.h',
239             'sys/time.h',
240             'sys/types.h',
241             'sys/uio.h',
242             'sys/unistd.h',
243             'syslog.h',
244             'time.h',
245             'unistd.h',
246             'winsock2.h',
247             'ws2tcpip.h'
248         ]
249
250         cxx_functions = [
251             'malloc',
252             'snprintf',
253             'strnlen',
254             'vprintf',
255             'fls',
256             'flsll',
257         ]
258
259         def get_define_from_string(string_):
260             string_converted = string_.replace("/", "_").replace(".", "_").upper()
261             return "HAVE_" + string_converted
262
263         for header_file_name in cxx_headers:
264             if conf.CheckCXXHeader(header_file_name):
265                 config_h_body += "#define %s 1\n\n" % get_define_from_string(
266                     header_file_name)
267
268         for function_name in cxx_functions:
269             if conf.CheckFunc(function_name):
270                 config_h_body += "#define %s 1\n\n" % get_define_from_string(
271                     function_name)
272
273         if conf.CheckCXXHeader('windows.h'):
274             config_h_body += "#define ssize_t SSIZE_T\n\n"
275             config_h_body += "#define in_port_t uint16_t\n\n"
276
277         conf.Finish()
278         with open(config_h_file, "w") as configfile:
279             configfile.write(config_h_header + config_h_body + config_h_footer)
280
281     # Sanity check to ensure that the above block created the file.
282     if not os.path.exists(config_h_file) and not env.GetOption('clean'):
283         msg = "Error: coap_config.h file not created!"
284         Exit(msg)
285
286     # Generate coap.h from coap.h.in by substitution
287     pc_vars = {
288         '@PACKAGE_NAME@': lib_prefix + 'coap',
289         '@PACKAGE_STRING@': lib_prefix + 'coap-' + libcoap_version,
290         '@PACKAGE_URL@': libcoap_repo_url,
291         '@PACKAGE_BUGREPORT@': libcoap_repo_url + '/issues',
292         '@PACKAGE_VERSION@': libcoap_version
293     }
294     libcoap_env.Substfile(coap_h_pc_file, SUBST_DICT=pc_vars)
295
296     libcoap_src_root = 'libcoap/src/'
297     try:
298         libcoap_src = Glob(libcoap_src_root + '*.c',
299                            exclude=libcoap_src_root + 'coap_io_lwip.c')
300     except TypeError:   # very old SCons doesn't have 'exclude' in Glob
301         libcoap_src = Glob(libcoap_src_root + '*.c')
302         libcoap_src = [src for src in libcoap_src if 'coap_io_lwip.c' not in src.path]
303
304 # finally ready to build:
305 libcoap = libcoap_env.StaticLibrary(build_dir + 'coap', libcoap_src, OBJPREFIX='libcoap_')
306 libcoap_env.UserInstallTargetLib(libcoap)
307
308 # set for use by other scripts:
309 if with_upstream_libcoap == '1':
310     env.AppendUnique(LIBCOAP_INC='#/extlibs/libcoap/libcoap/include')
311 else:
312     env.AppendUnique(LIBCOAP_INC='#/resource/csdk/connectivity/lib/libcoap-4.1.1/include')