# Load common build config
SConscript('build_common/SConscript')
-# Load extra options
-SConscript('extra_options.scons')
Import('env')
target_os = env.get('TARGET_OS')
if target_os == 'arduino':
SConscript('arduino.scons')
+else:
+ # Prepare libraries
+ env.PrepareLib('cereal')
+ env.PrepareLib('expat')
+ env.PrepareLib('boost', 'boost_thread', env.get('SRC_DIR') + '/extlibs/boost/')
+ env.PrepareLib('boost', 'boost_system', env.get('SRC_DIR') + '/extlibs/boost/')
+
# By default, src_dir is current dir, the build_dir is:
# ./out/<target_os>/<target_arch>/<release or debug>/
#
##
Import('env')
+# Add 'NET' build option, let user select board network connection type
+vars = Variables()
+vars.Add(EnumVariable('NET', 'Network connection type', 'Ethernet', ['Ethernet', 'Wifi']))
+vars.Update(env)
+Help(vars.GenerateHelpText(env))
+
env.ImportLib('Time')
if env.get('NET') == 'Ethernet':
env.ImportLib('Ethernet')
else:
env.ImportLib('WiFi')
-env.ImportLib('SPI')
\ No newline at end of file
+env.ImportLib('SPI')
+
+env.BuildCore()
\ No newline at end of file
Alias(name, i_n)
env.AppendUnique(TS = [name])
-def __append_target(ienv, target):
- env.AppendUnique(TS = [target])
+def __append_target(ienv, name, targets = None):
+ if targets:
+ env.Alias(name, targets)
+ env.AppendUnique(TS = [name])
def __print_targets(env):
Help('''
else:
env.SConscript(target_os + '/SConscript')
+env.SConscript('external_libs.scons')
+
# Delete the temp files of configuration
if env.GetOption('clean'):
dir = env.get('SRC_DIR')
# Android ndk early version doesn't support C++11. Detect the toolchain version
# to make sure proper toolchain is used
-for tc_ver in ['4.9', '4.8', '4.7', '']:
+for tc_ver in ['4.7', '4.8', '4.9', '']:
if os.path.exists(android_ndk + '/toolchains/x86-' + tc_ver):
break
if not platform_ver.isdigit():
platform_ver = ''
-# Add external libraries including boost
-env.AppendUnique(CPPPATH = [env.get('SRC_DIR') + '/out/android/include' ])
-if target_arch == 'x86':
- env.AppendUnique(LIBPATH = [env.get('SRC_DIR') + '/out/android/lib/x86'])
-else:
- env.AppendUnique(LIBPATH = [env.get('SRC_DIR') + '/out/android/lib/arm-linux-androideabi'])
######################################################################
# Set release/debug flags
env.AppendUnique(LIBS = 'm')
env.Replace(ARCOM = '$AR ' + platform_info.get('compiler.ar.flags') + ' $TARGET $SOURCES')
-__build_core(env)
-
env.AddMethod(__import_lib, "ImportLib") #import arduino library
-#env.AddMethod(__build_core, "BuildCore") #build arduino core
+env.AddMethod(__build_core, "BuildCore") #build arduino core
env.AddMethod(__create_bin, "CreateBin") #create binary files(.eep and .hex)
env.AddMethod(__upload_help, "UploadHelp") #print the command line that to upload binary to the boardf
--- /dev/null
+##
+# This script manages external libraries
+#
+# Some methods are added to manage external packages:
+# 'PrepareLib': Checks the existence of an external library, if it
+# doesn't exist, calls the script user provided to download(if required)
+# and build the source code of the external library or notify user to
+# install the library.
+# 'Download': Download package from specify URL
+# 'UnpackAll': Unpack the package
+# 'Configure': Execute specify script(configure, bootstrap etc)
+# 'InstallHeadFile': Install head files
+# 'InstallLib': Install library binaries(.so, .a etc)
+#
+# By default, assume the script for an exteranl library is:
+# <src_dir>/extlibs/<libname>/SConscript
+#
+# Note: After the external library is built:
+# Head files should be installed to <src_dir>/deps/<target_os>/include
+# lib(e.g .so, .a) should be installed to <src_dir>/deps/<target_os>/lib/<arch>
+#
+##
+import os, subprocess
+import urllib2, urlparse
+import SCons.Errors
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+target_arch = env.get('TARGET_ARCH')
+
+# for android, doesn't distinguish 'armeabi-v7a-hard' and 'armeabi-v7a' library
+if target_os == 'android':
+ if target_arch == 'armeabi-v7a-hard':
+ target_arch = 'armeabi-v7a'
+
+if target_os == 'darwin':
+ env.AppendUnique(CPPPATH = ['/usr/local/include'])
+ env.AppendUnique(LIBPATH = ['/usr/local/lib'])
+
+# External library include files are in <src_dir>/deps/<target_os>/include
+# the library binaries are in <src_dir>/deps/<target_os>/lib/<arch>
+env.AppendUnique(CPPPATH = [env.get('SRC_DIR') + '/deps/' + target_os + '/include'])
+env.AppendUnique(LIBPATH = [env.get('SRC_DIR') + '/deps/' + target_os + '/lib/' + target_arch])
+
+# Check whether a library exists, if not, notify user to install it or try to
+# download the source code and build it
+# @param libname - the name of the library try to prepare
+# @param lib - the lib(.so, .a etc) to check (a library may include more then
+# one lib, e.g. boost, includes boost_thread, boost_system ...
+# @param path - the directory of the library building script, if it's not set,
+# by default, it's <src_dir>/extlibs/<libname>/
+# @param script - the building script, by default, it's 'SConscript'
+#
+def __prepare_lib(ienv, libname, lib = None, path = None, script = None):
+ p_env = ienv.Clone()
+ if p_env.GetOption('clean') or p_env.GetOption('help'):
+ return
+
+ conf = Configure(p_env)
+
+ if not lib:
+ lib = libname
+ if not conf.CheckLib(lib):
+ if path:
+ dir = path
+ else:
+ dir = env.get('SRC_DIR') + '/extlibs/' + libname
+
+ # Execute the script to download(if required) and build source code
+ if script:
+ st = dir + '/' + script
+ else:
+ st = dir + '/SConscript'
+
+ if os.path.exists(st):
+ SConscript(st)
+ else:
+ if target_os in ['linux', 'darwin', 'tizen']:
+ print 'Don\'t find library(%s), please intall it, exit ...' % libname
+ else:
+ print 'Don\'t find library(%s) and don\'t find the process script (%s), exit ...' % (libname, st)
+ Exit(1)
+
+ conf.Finish()
+
+# Run configure command (usually it's done before build a library)
+def __configure(env, cwd, cmd) :
+ print "Configuring using [%s/%s] ..." % (cwd, cmd)
+ # build it now (we need the shell, because some programs need it)
+ devnull = open(os.devnull, "wb")
+ handle = subprocess.Popen(cmd, shell=True, cwd=cwd, stdout=devnull)
+
+ if handle.wait() <> 0 :
+ raise SCons.Errors.BuildError( "Run configuring script [%s]" % (cmd) )
+
+# Download from URL 'url', will save as 'target'
+def __download(ienv, target, url) :
+ if os.path.exists(target) :
+ return target
+
+ try :
+ print "Download %s from %s" % (target, url)
+ print "Downloading ..."
+ stream = urllib2.urlopen(url)
+ file = open(target, 'wb')
+ file.write(stream.read())
+ file.close()
+ print "Download %s from %s complete" % (target, url)
+ return target
+ except Exception, e :
+ raise SCons.Errors.StopError( '%s [%s]' % (e, url) )
+
+# Install header file(s) to <src_dir>/deps/<target_os>/include
+def __install_head_file(ienv, file):
+ return ienv.Install(env.get('SRC_DIR') + '/deps/' + target_os + '/include', file)
+
+# Install library binaries to <src_dir>/deps/<target_os>/lib/<arch>
+def __install_lib(ienv, lib):
+ return ienv.Install(env.get('SRC_DIR') + '/deps/' + target_os + '/lib/' + target_arch, lib)
+
+SConscript('tools/UnpackAll.py')
+
+env.AddMethod(__prepare_lib, "PrepareLib")
+env.AddMethod(__configure, "Configure")
+env.AddMethod(__download, "Download")
+env.AddMethod(__install_head_file, "InstallHeadFile")
+env.AddMethod(__install_lib, "InstallLib")
--- /dev/null
+# -*- coding: utf-8 -*-
+
+############################################################################
+# GPL License #
+# #
+# This file is a SCons (http://www.scons.org/) builder #
+# Copyright (c) 2012-14, Philipp Kraus, <philipp.kraus@flashpixx.de> #
+# Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved. #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as #
+# published by the Free Software Foundation, either version 3 of the #
+# License, or (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+############################################################################
+
+# This builder originated from work by Philipp Kraus and flashpixx project
+# (see https://github.com/flashpixx). Based on the Unpack.py, it only
+# contains changes to allow a complete unpacking of the archive.
+# It is assumed that the target represents a file in the archive after it
+# is unpacked.
+
+# The Unpack Builder can be used for unpacking archives (eg Zip, TGZ, BZ, ... ).
+# The emitter of the Builder reads the archive data and creates a returning
+# file list the builder extract the archive. The environment variable
+# stores a dictionary "UNPACK" for set different extractions (subdict "EXTRACTOR"):
+# {
+# PRIORITY => a value for setting the extractor order (lower numbers = extractor is used earlier)
+# SUFFIX => defines a list with file suffixes, which should be handled with this extractor
+# EXTRACTSUFFIX => suffix of the extract command
+# EXTRACTFLAGS => a string parameter for the RUN command for extracting the data
+# EXTRACTCMD => full extract command of the builder
+# RUN => the main program which will be started (if the parameter is empty, the extractor will be ignored)
+# LISTCMD => the listing command for the emitter
+# LISTFLAGS => the string options for the RUN command for showing a list of files
+# LISTSUFFIX => suffix of the list command
+# LISTEXTRACTOR => a optional Python function, that is called on each output line of the
+# LISTCMD for extracting file & dir names, the function need two parameters (first line number,
+# second line content) and must return a string with the file / dir path (other value types
+# will be ignored)
+# }
+# Other options in the UNPACK dictionary are:
+# STOPONEMPTYFILE => bool variable for stoping if the file has empty size (default True)
+# VIWEXTRACTOUTPUT => shows the output messages of the extraction command (default False)
+# EXTRACTDIR => path in that the data will be extracted (default #)
+#
+# The file which is handled by the first suffix match of the extractor, the extractor list can be append for other files.
+# The order of the extractor dictionary creates the listing & extractor command eg file extension .tar.gz should be
+# before .gz, because the tar.gz is extract in one shoot.
+#
+# Under *nix system these tools are supported: tar, bzip2, gzip, unzip
+# Under Windows only 7-Zip (http://www.7-zip.org/) is supported
+
+
+import subprocess, os
+import SCons.Errors, SCons.Warnings, SCons.Util
+
+# enables Scons warning for this builder
+class UnpackWarning(SCons.Warnings.Warning) :
+ pass
+
+SCons.Warnings.enableWarningClass(UnpackWarning)
+
+# extractor function for Tar output
+# @param env environment object
+# @param count number of returning lines
+# @param no number of the output line
+# @param i line content
+def __fileextractor_nix_tar( env, count, no, i ) :
+ return i.split()[-1]
+
+# extractor function for GZip output,
+# ignore the first line
+# @param env environment object
+# @param count number of returning lines
+# @param no number of the output line
+# @param i line content
+def __fileextractor_nix_gzip( env, count, no, i ) :
+ if no == 0 :
+ return None
+ return i.split()[-1]
+
+# extractor function for Unzip output,
+# ignore the first & last two lines
+# @param env environment object
+# @param count number of returning lines
+# @param no number of the output line
+# @param i line content
+def __fileextractor_nix_unzip( env, count, no, i ) :
+ if no < 3 or no >= count - 2 :
+ return None
+ return i.split()[-1]
+
+# extractor function for 7-Zip
+# @param env environment object
+# @param count number of returning lines
+# @param no number of the output line
+# @param i line content
+def __fileextractor_win_7zip( env, count, no, i ) :
+ item = i.split()
+ if no > 8 and no < count - 2 :
+ return item[-1]
+ return None
+
+
+
+# returns the extractor item for handling the source file
+# @param source input source file
+# @param env environment object
+# @return extractor entry or None on non existing
+def __getExtractor( source, env ) :
+ # we check each unpacker and get the correct list command first, run the command and
+ # replace the target filelist with the list values, we sorte the extractors by their priority
+ for unpackername, extractor in sorted(env["UNPACK"]["EXTRACTOR"].iteritems(), key = lambda (k,v) : (v["PRIORITY"],k)):
+
+ if not SCons.Util.is_String(extractor["RUN"]) :
+ raise SCons.Errors.StopError("list command of the unpack builder for [%s] archives is not a string" % (unpackername))
+ if not len(extractor["RUN"]) :
+ raise SCons.Errors.StopError("run command of the unpack builder for [%s] archives is not set - can not extract files" % (unpackername))
+
+
+ if not SCons.Util.is_String(extractor["LISTFLAGS"]) :
+ raise SCons.Errors.StopError("list flags of the unpack builder for [%s] archives is not a string" % (unpackername))
+ if not SCons.Util.is_String(extractor["LISTCMD"]) :
+ raise SCons.Errors.StopError("list command of the unpack builder for [%s] archives is not a string" % (unpackername))
+
+ if not SCons.Util.is_String(extractor["EXTRACTFLAGS"]) :
+ raise SCons.Errors.StopError("extract flags of the unpack builder for [%s] archives is not a string" % (unpackername))
+ if not SCons.Util.is_String(extractor["EXTRACTCMD"]) :
+ raise SCons.Errors.StopError("extract command of the unpack builder for [%s] archives is not a string" % (unpackername))
+
+
+ # check the source file suffix and if the first is found, run the list command
+ if not SCons.Util.is_List(extractor["SUFFIX"]) :
+ raise SCons.Errors.StopError("suffix list of the unpack builder for [%s] archives is not a list" % (unpackername))
+
+ for suffix in extractor["SUFFIX"] :
+ if str(source[0]).lower()[-len(suffix):] == suffix.lower() :
+ return extractor
+
+ return None
+
+
+# creates the extracter output message
+# @param s original message
+# @param target target name
+# @param source source name
+# @param env environment object
+def __message( s, target, source, env ) :
+ print "extract [%s] ..." % (source[0])
+
+
+# action function for extracting of the data
+# @param target target packed file
+# @param source extracted files
+# @param env environment object
+def __action( target, source, env ) :
+ extractor = __getExtractor([File(source)], env)
+ if not extractor :
+ raise SCons.Errors.StopError( "can not find any extractor value for the source file [%s]" % (source) )
+
+ extractor_cmd = extractor["EXTRACTCMD"]
+
+ # if the extract command is empty, we create an error
+ if len(extractor_cmd) == 0 :
+ raise SCons.Errors.StopError( "the extractor command for the source file [%s] is empty" % (source) )
+
+ # build it now (we need the shell, because some programs need it)
+ handle = None
+
+ cmd = env.subst(extractor_cmd, source=source, target=target)
+ cwd = os.path.realpath('.')
+
+ if env["UNPACK"]["VIWEXTRACTOUTPUT"] :
+ handle = subprocess.Popen( cmd, shell=True )
+ else :
+ devnull = open(os.devnull, "wb")
+ handle = subprocess.Popen(cmd, shell=True, stdout=devnull, cwd = cwd)
+
+ if handle.wait() <> 0 :
+ print '''******************************* Error *****************************************
+*
+* Fail to unpack (%s). It should be due to it isn't downloaded completely.
+* Please download it manually or delete it and let the script auto
+* download again.
+*
+*******************************************************************************
+'''
+ raise SCons.Errors.BuildError( "error running extractor [%s] on the source [%s]" % (cmd, source) )
+
+# emitter function for getting the files
+# within the archive
+# @param target target packed file
+# @param source extracted files
+# @param env environment object
+def __emitter( target, source, env ) :
+ return target, source
+
+def __unpack_all(env, target, source) :
+ if os.path.exists(target):
+ return
+
+ print "Unpacking %s ..." % source
+ __action(target, source, env)
+
+# generate function, that adds the builder to the environment
+# @param env environment object
+def generate( env ) :
+ # setup environment variable
+ toolset = {
+ "STOPONEMPTYFILE" : True,
+ "VIWEXTRACTOUTPUT" : False,
+ "EXTRACTDIR" : os.curdir,
+ "EXTRACTOR" : {
+ "TARGZ" : {
+ "PRIORITY" : 0,
+ "SUFFIX" : [".tar.gz", ".tgz", ".tar.gzip"],
+ "EXTRACTSUFFIX" : "",
+ "EXTRACTFLAGS" : "",
+ "EXTRACTCMD" : "${UNPACK['EXTRACTOR']['TARGZ']['RUN']} ${UNPACK['EXTRACTOR']['TARGZ']['EXTRACTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['TARGZ']['EXTRACTSUFFIX']}",
+ "RUN" : "",
+ "LISTCMD" : "${UNPACK['EXTRACTOR']['TARGZ']['RUN']} ${UNPACK['EXTRACTOR']['TARGZ']['LISTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['TARGZ']['LISTSUFFIX']}",
+ "LISTSUFFIX" : "",
+ "LISTFLAGS" : "",
+ "LISTEXTRACTOR" : None
+ },
+
+ "TARBZ" : {
+ "PRIORITY" : 0,
+ "SUFFIX" : [".tar.bz", ".tbz", ".tar.bz2", ".tar.bzip2", ".tar.bzip"],
+ "EXTRACTSUFFIX" : "",
+ "EXTRACTFLAGS" : "",
+ "EXTRACTCMD" : "${UNPACK['EXTRACTOR']['TARBZ']['RUN']} ${UNPACK['EXTRACTOR']['TARBZ']['EXTRACTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['TARBZ']['EXTRACTSUFFIX']}",
+ "RUN" : "",
+ "LISTCMD" : "${UNPACK['EXTRACTOR']['TARBZ']['RUN']} ${UNPACK['EXTRACTOR']['TARBZ']['LISTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['TARBZ']['LISTSUFFIX']}",
+ "LISTSUFFIX" : "",
+ "LISTFLAGS" : "",
+ "LISTEXTRACTOR" : None
+ },
+
+ "BZIP" : {
+ "PRIORITY" : 1,
+ "SUFFIX" : [".bz", "bzip", ".bz2", ".bzip2"],
+ "EXTRACTSUFFIX" : "",
+ "EXTRACTFLAGS" : "",
+ "EXTRACTCMD" : "${UNPACK['EXTRACTOR']['BZIP']['RUN']} ${UNPACK['EXTRACTOR']['BZIP']['EXTRACTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['BZIP']['EXTRACTSUFFIX']}",
+ "RUN" : "",
+ "LISTCMD" : "${UNPACK['EXTRACTOR']['BZIP']['RUN']} ${UNPACK['EXTRACTOR']['BZIP']['LISTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['BZIP']['LISTSUFFIX']}",
+ "LISTSUFFIX" : "",
+ "LISTFLAGS" : "",
+ "LISTEXTRACTOR" : None
+ },
+
+ "GZIP" : {
+ "PRIORITY" : 1,
+ "SUFFIX" : [".gz", ".gzip"],
+ "EXTRACTSUFFIX" : "",
+ "EXTRACTFLAGS" : "",
+ "EXTRACTCMD" : "${UNPACK['EXTRACTOR']['GZIP']['RUN']} ${UNPACK['EXTRACTOR']['GZIP']['EXTRACTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['GZIP']['EXTRACTSUFFIX']}",
+ "RUN" : "",
+ "LISTCMD" : "${UNPACK['EXTRACTOR']['GZIP']['RUN']} ${UNPACK['EXTRACTOR']['GZIP']['LISTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['GZIP']['LISTSUFFIX']}",
+ "LISTSUFFIX" : "",
+ "LISTFLAGS" : "",
+ "LISTEXTRACTOR" : None
+ },
+
+ "TAR" : {
+ "PRIORITY" : 1,
+ "SUFFIX" : [".tar"],
+ "EXTRACTSUFFIX" : "",
+ "EXTRACTFLAGS" : "",
+ "EXTRACTCMD" : "${UNPACK['EXTRACTOR']['TAR']['RUN']} ${UNPACK['EXTRACTOR']['TAR']['EXTRACTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['TAR']['EXTRACTSUFFIX']}",
+ "RUN" : "",
+ "LISTCMD" : "${UNPACK['EXTRACTOR']['TAR']['RUN']} ${UNPACK['EXTRACTOR']['TAR']['LISTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['TAR']['LISTSUFFIX']}",
+ "LISTSUFFIX" : "",
+ "LISTFLAGS" : "",
+ "LISTEXTRACTOR" : None
+ },
+
+ "ZIP" : {
+ "PRIORITY" : 1,
+ "SUFFIX" : [".zip"],
+ "EXTRACTSUFFIX" : "",
+ "EXTRACTFLAGS" : "",
+ "EXTRACTCMD" : "${UNPACK['EXTRACTOR']['ZIP']['RUN']} ${UNPACK['EXTRACTOR']['ZIP']['EXTRACTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['ZIP']['EXTRACTSUFFIX']}",
+ "RUN" : "",
+ "LISTCMD" : "${UNPACK['EXTRACTOR']['ZIP']['RUN']} ${UNPACK['EXTRACTOR']['ZIP']['LISTFLAGS']} $SOURCE ${UNPACK['EXTRACTOR']['ZIP']['LISTSUFFIX']}",
+ "LISTSUFFIX" : "",
+ "LISTFLAGS" : "",
+ "LISTEXTRACTOR" : None
+ }
+ }
+ }
+
+ # read tools for Windows system
+ if env["PLATFORM"] <> "darwin" and "win" in env["PLATFORM"] :
+
+ if env.WhereIs("7z") :
+ toolset["EXTRACTOR"]["TARGZ"]["RUN"] = "7z"
+ toolset["EXTRACTOR"]["TARGZ"]["LISTEXTRACTOR"] = __fileextractor_win_7zip
+ toolset["EXTRACTOR"]["TARGZ"]["LISTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["TARGZ"]["LISTSUFFIX"] = "-so -y | ${UNPACK['EXTRACTOR']['TARGZ']['RUN']} l -sii -ttar -y -so"
+ toolset["EXTRACTOR"]["TARGZ"]["EXTRACTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["TARGZ"]["EXTRACTSUFFIX"] = "-so -y | ${UNPACK['EXTRACTOR']['TARGZ']['RUN']} x -sii -ttar -y -oc:${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["TARBZ"]["RUN"] = "7z"
+ toolset["EXTRACTOR"]["TARBZ"]["LISTEXTRACTOR"] = __fileextractor_win_7zip
+ toolset["EXTRACTOR"]["TARBZ"]["LISTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["TARBZ"]["LISTSUFFIX"] = "-so -y | ${UNPACK['EXTRACTOR']['TARGZ']['RUN']} l -sii -ttar -y -so"
+ toolset["EXTRACTOR"]["TARBZ"]["EXTRACTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["TARBZ"]["EXTRACTSUFFIX"] = "-so -y | ${UNPACK['EXTRACTOR']['TARGZ']['RUN']} x -sii -ttar -y -oc:${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["BZIP"]["RUN"] = "7z"
+ toolset["EXTRACTOR"]["BZIP"]["LISTEXTRACTOR"] = __fileextractor_win_7zip
+ toolset["EXTRACTOR"]["BZIP"]["LISTFLAGS"] = "l"
+ toolset["EXTRACTOR"]["BZIP"]["LISTSUFFIX"] = "-y -so"
+ toolset["EXTRACTOR"]["BZIP"]["EXTRACTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["BZIP"]["EXTRACTSUFFIX"] = "-y -oc:${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["GZIP"]["RUN"] = "7z"
+ toolset["EXTRACTOR"]["GZIP"]["LISTEXTRACTOR"] = __fileextractor_win_7zip
+ toolset["EXTRACTOR"]["GZIP"]["LISTFLAGS"] = "l"
+ toolset["EXTRACTOR"]["GZIP"]["LISTSUFFIX"] = "-y -so"
+ toolset["EXTRACTOR"]["GZIP"]["EXTRACTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["GZIP"]["EXTRACTSUFFIX"] = "-y -oc:${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["ZIP"]["RUN"] = "7z"
+ toolset["EXTRACTOR"]["ZIP"]["LISTEXTRACTOR"] = __fileextractor_win_7zip
+ toolset["EXTRACTOR"]["ZIP"]["LISTFLAGS"] = "l"
+ toolset["EXTRACTOR"]["ZIP"]["LISTSUFFIX"] = "-y -so"
+ toolset["EXTRACTOR"]["ZIP"]["EXTRACTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["ZIP"]["EXTRACTSUFFIX"] = "-y -oc:${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["TAR"]["RUN"] = "7z"
+ toolset["EXTRACTOR"]["TAR"]["LISTEXTRACTOR"] = __fileextractor_win_7zip
+ toolset["EXTRACTOR"]["TAR"]["LISTFLAGS"] = "l"
+ toolset["EXTRACTOR"]["TAR"]["LISTSUFFIX"] = "-y -ttar -so"
+ toolset["EXTRACTOR"]["TAR"]["EXTRACTFLAGS"] = "x"
+ toolset["EXTRACTOR"]["TAR"]["EXTRACTSUFFIX"] = "-y -ttar -oc:${UNPACK['EXTRACTDIR']}"
+
+ # here can add some other Windows tools, that can handle the archive files
+ # but I don't know which ones can handle all file types
+
+
+
+ # read the tools on *nix systems and sets the default parameters
+ elif env["PLATFORM"] in ["darwin", "linux", "posix"] :
+
+ if env.WhereIs("unzip") :
+ toolset["EXTRACTOR"]["ZIP"]["RUN"] = "unzip"
+ toolset["EXTRACTOR"]["ZIP"]["LISTEXTRACTOR"] = __fileextractor_nix_unzip
+ toolset["EXTRACTOR"]["ZIP"]["LISTFLAGS"] = "-l"
+ toolset["EXTRACTOR"]["ZIP"]["EXTRACTFLAGS"] = "-oqq"
+ toolset["EXTRACTOR"]["ZIP"]["EXTRACTSUFFIX"] = "-d ${UNPACK['EXTRACTDIR']}"
+
+ if env.WhereIs("tar") :
+ toolset["EXTRACTOR"]["TAR"]["RUN"] = "tar"
+ toolset["EXTRACTOR"]["TAR"]["LISTEXTRACTOR"] = __fileextractor_nix_tar
+ toolset["EXTRACTOR"]["TAR"]["LISTFLAGS"] = "tvf"
+ toolset["EXTRACTOR"]["TAR"]["EXTRACTFLAGS"] = "xf"
+ toolset["EXTRACTOR"]["TAR"]["EXTRACTSUFFIX"] = "-C ${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["TARGZ"]["RUN"] = "tar"
+ toolset["EXTRACTOR"]["TARGZ"]["LISTEXTRACTOR"] = __fileextractor_nix_tar
+ toolset["EXTRACTOR"]["TARGZ"]["EXTRACTFLAGS"] = "xfz"
+ toolset["EXTRACTOR"]["TARGZ"]["LISTFLAGS"] = "tvfz"
+ toolset["EXTRACTOR"]["TARGZ"]["EXTRACTSUFFIX"] = "-C ${UNPACK['EXTRACTDIR']}"
+
+ toolset["EXTRACTOR"]["TARBZ"]["RUN"] = "tar"
+ toolset["EXTRACTOR"]["TARBZ"]["LISTEXTRACTOR"] = __fileextractor_nix_tar
+ toolset["EXTRACTOR"]["TARBZ"]["EXTRACTFLAGS"] = "xfj"
+ toolset["EXTRACTOR"]["TARBZ"]["LISTFLAGS"] = "tvfj"
+ toolset["EXTRACTOR"]["TARBZ"]["EXTRACTSUFFIX"] = "-C ${UNPACK['EXTRACTDIR']}"
+
+ if env.WhereIs("bzip2") :
+ toolset["EXTRACTOR"]["BZIP"]["RUN"] = "bzip2"
+ toolset["EXTRACTOR"]["BZIP"]["EXTRACTFLAGS"] = "-df"
+
+ if env.WhereIs("gzip") :
+ toolset["EXTRACTOR"]["GZIP"]["RUN"] = "gzip"
+ toolset["EXTRACTOR"]["GZIP"]["LISTEXTRACTOR"] = __fileextractor_nix_gzip
+ toolset["EXTRACTOR"]["GZIP"]["LISTFLAGS"] = "-l"
+ toolset["EXTRACTOR"]["GZIP"]["EXTRACTFLAGS"] = "-df"
+
+ else :
+ raise SCons.Errors.StopError("Unpack tool detection on this platform [%s] unkown" % (env["PLATFORM"]))
+
+ # the target_factory must be a "Entry", because the target list can be files and dirs, so we can not specified the targetfactory explicite
+ env.Replace(UNPACK = toolset)
+ env.AddMethod(__unpack_all, 'UnpackAll')
+
+# env["BUILDERS"]["UnpackAll"] = SCons.Builder.Builder( action = __action, emitter = __emitter, target_factory = SCons.Node.FS.Entry, source_factory = SCons.Node.FS.File, single_source = True, PRINT_CMD_LINE_FUNC = __message )
+
+
+# existing function of the builder
+# @param env environment object
+# @return true
+def exists(env) :
+ return 1
+
+Import('env')
+generate(env)
--- /dev/null
+import os, string, sys, subprocess
+
+Import('env')
+
+boost_env = env.Clone()
+
+modules = ['thread','program_options']
+
+target_os = env.get('TARGET_OS')
+
+boost_version = '1.57.0'
+boost_base_name = 'boost_'+string.replace(boost_version,'.','_')
+boost_arch_name = boost_base_name+'.zip'
+boost_url = 'http://downloads.sourceforge.net/project/boost/boost/'+boost_version+'/'+boost_arch_name+'?r=&ts=1421801329&use_mirror=iweb'
+
+host_os = sys.platform
+
+if host_os == 'linux2' :
+ boost_bootstrap = boost_base_name+os.sep+'bootstrap.sh'
+ boost_b2_name = boost_base_name+os.sep+'b2'
+else :
+ msg="Host platform (%s) is currently not supported for boost builds" % host_os
+ raise SCons.Errors.EnvironmentError(msg)
+
+# Download source code
+boost_zip = boost_env.Download(boost_arch_name, boost_url)
+
+# Unpack source code
+if not os.path.exists(boost_bootstrap):
+ boost_env.UnpackAll(boost_bootstrap, boost_zip)
+
+# Run bootstrap.sh
+if not os.path.exists(boost_b2_name):
+ boost_env.Configure(boost_base_name, './bootstrap.sh')
+
+cmd = None
+
+# Windows...
+if boost_env["PLATFORM"] in ["win32"] :
+ if boost_env.WhereIs("cmd") :
+ # TODO: Add Windows Support
+ cmd = None
+
+# read the tools on *nix systems and sets the default parameters
+elif boost_env["PLATFORM"] in ["darwin", "linux", "posix"] :
+ if boost_env.WhereIs("sh") :
+ cmd = ['./b2']
+
+if not cmd :
+ raise SCons.Errors.StopError("Boost build system not supported on this platform [%s]" % (boost_env["PLATFORM"]))
+
+# We need to be in the target's directory
+cwd = boost_base_name
+
+# Gather all of the path, bin and flags
+version = boost_env.get('VERSION','')
+target_os = boost_env['TARGET_OS']
+target_arch = boost_env['TARGET_ARCH']
+tool_path = os.path.dirname(boost_env['CXX'])
+cxx_bin = os.path.basename(boost_env['CXX'])
+ar_bin = os.path.basename(boost_env['AR'])
+ranlib_bin = os.path.basename(boost_env['RANLIB'])
+ccflags = list(boost_env['CFLAGS'])
+cxxflags = list(boost_env['CXXFLAGS'])
+
+try:
+ cxxflags.remove('-fno-rtti')
+except ValueError:
+ pass
+try:
+ cxxflags.remove('-fno-exceptions')
+except ValueError:
+ pass
+
+# Write a user-config for this variant
+user_config_name = cwd+os.sep+'tools'+os.sep+'build'+os.sep+'src'+os.sep+'user-config.jam'
+user_config_file = open(user_config_name, 'w')
+user_config_file.write('import os ;\n')
+user_config_file.write('using gcc :')
+user_config_file.write(' '+version+' :')
+#user_config_file.write(' :')
+#user_config_file.write(' '+os.path.basename(toolchain['CXX']['BIN'])+' :\n')
+user_config_file.write(' '+cxx_bin+' :\n')
+user_config_file.write(' <archiver>'+ar_bin+'\n')
+user_config_file.write(' <ranlib>'+ranlib_bin+'\n')
+for value in boost_env['CPPDEFINES'] :
+ if len(value) > 1 :
+ user_config_file.write(' <compileflags>-D'+value[0]+'='+value[1]+'\n')
+ else :
+ user_config_file.write(' <compileflags>-D'+value[0]+'\n')
+for value in boost_env['CPPPATH'] :
+ user_config_file.write(' <compileflags>-I'+value+'\n')
+for flag in ccflags :
+ user_config_file.write(' <compileflags>'+flag+'\n')
+for flag in cxxflags :
+ user_config_file.write(' <cxxflags>'+flag+'\n')
+user_config_file.write(' ;\n')
+user_config_file.close();
+
+# Ensure that the toolchain is in the PATH
+penv = os.environ.copy()
+penv["PATH"] = tool_path+":" + penv["PATH"]
+
+build_path = 'build' + os.sep + target_os + os.sep + target_arch
+
+cmd.append('-q')
+cmd.append('target-os=linux')
+cmd.append('link=static')
+cmd.append('threading=multi')
+cmd.append('--layout=system')
+cmd.append('--build-type=minimal')
+cmd.append('--prefix='+ build_path + os.sep + 'install_tmp')
+cmd.append('--build-dir='+build_path)
+for module in modules :
+ cmd.append('--with-'+module)
+cmd.append('headers')
+cmd.append('install')
+
+# build it now (we need the shell, because some programs need it)
+devnull = open(os.devnull, "wb")
+print "Building boost [%s] on the source [%s]" % (cmd, boost_b2_name)
+handle = subprocess.Popen(cmd, env=penv, cwd=cwd)#, stdout=devnull )
+
+if handle.wait() <> 0 :
+ raise SCons.Errors.BuildError( "Building boost [%s] on the source [%s]" % (cmd, boost_b2_name) )
+
+# Use Copy instead of InstallXXX to make sure boost is installed immediately
+Execute(Copy(env.get('SRC_DIR') + '/deps/' + target_os + '/include',
+ boost_base_name + os.sep + build_path + os.sep + 'install_tmp' + os.sep + 'include'))
+
+Execute(Copy(env.get('SRC_DIR') + '/deps/' + target_os + '/lib/' + target_arch,
+ boost_base_name + os.sep + build_path + os.sep + 'install_tmp' + os.sep + 'lib'))
--- /dev/null
+######################################################################
+# Cereal library build script
+#
+# Only 'hpp' is used by Iotivity, it's unnecessary to build it
+######################################################################
+import os
+
+Import('env')
+
+src_dir = env.get('SRC_DIR')
+
+# In the pass, cereal library is in extlibs/cereal, according to external
+# library management rule, cereal should be put in extlibs/cereal/cereal.
+# jenkins of gerrit server, still follow the old, to avoid jenkins fail
+# both places are handled.
+old = src_dir + '/extlibs/cereal/include'
+cur = src_dir + '/extlibs/cereal/cereal/include'
+
+# check 'cereal' library, if it doesn't exits, ask user to download it
+if not os.path.exists(old) and not os.path.exists(cur):
+ print '''
+*********************************** Error: ************************************
+* 'Cereal' library doesn't exist. please download cereal to extlibs directory*
+* add apply the patch as following: *
+* $ git clone https://github.com/USCiLab/cereal.git extlibs/cereal/cereal *
+* $ cd extlibs/cereal/cereal *
+* $ git reset --hard 7121e91e6ab8c3e6a6516d9d9c3e6804e6f65245 *
+* $ git apply ../../../resource/patches/cereal_gcc46.patch *
+*******************************************************************************
+'''
+ Exit(1)
+else:
+ env.AppendUnique(CPPPATH = [old, cur])
--- /dev/null
+##
+# build script for expat library
+#
+# Note:
+# 1) Only a part of source code are required to be built.
+# 2) Download code from https://android.googlesource.com/platform/external/expat.git
+# otherwise, after code is downloaded, it's required to execute './configure' to
+# generate expat_config.h
+#
+##
+
+import os
+
+Import('env')
+
+expat_env = env.Clone()
+
+######################################################################
+# Build flags
+######################################################################
+src_dir = 'expat-2.1.0/'
+
+if not os.path.exists(src_dir):
+ # Prepare source code (download / unpack / run configure)
+ env.Download('expat.tar.gz', 'http://sourceforge.net/projects/expat/files/expat/2.1.0/expat-2.1.0.tar.gz/download')
+ expat_src = env.UnpackAll(src_dir + '/Makefile', 'expat.tar.gz')
+ env.Configure(src_dir, './configure')
+
+expat_env.AppendUnique(CPPPATH = [src_dir, src_dir + 'lib'])
+
+target_os = env.get('TARGET_OS')
+if target_os not in ['windows', 'winrt']:
+ expat_env.AppendUnique(CCFLAGS = ['-g', '-O2', '-Wall',
+ '-Wmissing-prototypes', '-Wstrict-prototypes',
+ '-fexceptions', '-fno-common'])
+ expat_env.AppendUnique(CPPDEFINES = ['HAVE_EXPAT_CONFIG_H'])
+
+######################################################################
+# Source files and Targets
+######################################################################
+expat_src = [
+ src_dir + 'lib/xmlparse.c',
+ src_dir + 'lib/xmltok.c',
+ src_dir + 'lib/xmlrole.c',
+ ]
+
+libexpat = expat_env.SharedLibrary('expat', expat_src)
+
+######################################################################
+# Install header files and library binary
+######################################################################
+h = expat_env.InstallHeadFile([src_dir + 'lib/expat.h', src_dir + 'lib/expat_external.h'])
+lib = expat_env.InstallLib(libexpat)
+
+expat_env.AppendTarget('libexpat', [h, lib])
+++ /dev/null
-######################################################################
-# This script manages extra build options
-#
-######################################################################
-import os
-import platform
-
-Import('env')
-
-target_os = env.get('TARGET_OS')
-target_arch = env.get('TARGET_ARCH')
-src_dir = env.get('SRC_DIR')
-
-if target_os == 'arduino':
- # Add 'NET' build option, let user select board network connection type
- vars = Variables()
- vars.Add(EnumVariable('NET', 'Network connection type', 'Ethernet', ['Ethernet', 'Wifi']))
- vars.Update(env)
- Help(vars.GenerateHelpText(env))
-
-# check 'cereal' library, temporarily put it here
-if not os.path.exists(src_dir + '/extlibs/cereal'):
- print '''
-*********************************** Error: **************************************
-* 'Cereal' library doesn't exist. please download cereal to extlibs directory *
-* add apply the patch as following: *
-* $ git clone https://github.com/USCiLab/cereal.git <src_dir>/extlibs/cereal*
-* $ cd <src_dir>/extlibs/cereal *
-* $ git reset --hard 7121e91e6ab8c3e6a6516d9d9c3e6804e6f65245 *
-* $ git apply ../../resource/patches/cereal_gcc46.patch *
-*********************************************************************************
-'''
- Exit(1)
-else:
- env.AppendUnique(CPPPATH = [src_dir + '/extlibs/cereal/include'])
##
Import('env')
-# Add third party libraries
-lib_env = env.Clone()
-#SConscript(env.get('SRC_DIR') + '/resource/third_party_libs.scons', 'lib_env')
-
-compatibilitylib_env = lib_env.Clone()
+compatibilitylib_env = env.Clone()
######################################################################
# Build flags
######################################################################
if target_os == 'android':
compatibilitylib_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
- #oclib_env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
compatibilitylib_env.AppendUnique(LIBS = ['gnustl_static'])
######################################################################
##
# liboctbstack (share library) build script
##
-
Import('env')
-lib_env = env.Clone()
-SConscript(env.get('SRC_DIR') + '/resource/third_party_libs.scons', 'lib_env')
-
-liboctbstack_env = lib_env.Clone()
+liboctbstack_env = env.Clone()
target_os = env.get('TARGET_OS')
# As in the source code, it includes arduino Time library (C++)
arduino_simplecs = arduino_simplecs_env.Program('SimpleClientServer', 'ocserver.cpp')
env.CreateBin('SimpleClientServer')
-i_arduino_simplecs = arduino_simplecs_env.Install(env.get('BUILD_DIR'), arduino_simplecs)
-
-Alias('arduino_simplecs', i_arduino_simplecs)
-env.AppendTarget('arduino_simplecs')
\ No newline at end of file
+Alias('arduino_simplecs', arduino_simplecs)
+env.AppendTarget('arduino_simplecs')
# Examples build script
##
Import('env')
-# Add third party libraries
-lib_env = env.Clone()
-SConscript(env.get('SRC_DIR') + '/resource/third_party_libs.scons', 'lib_env')
-
-examples_env = lib_env.Clone()
+examples_env = env.Clone()
######################################################################
# Build flags
######################################################################
Import('env')
-lib_env = env.Clone()
-SConscript(env.get('SRC_DIR') + '/resource/third_party_libs.scons', 'lib_env')
-
-liboc_logger_env = lib_env.Clone()
+liboc_logger_env = env.Clone()
######################################################################
# Build flags
######################################################################
Import('env')
-lib_env = env.Clone()
-SConscript(env.get('SRC_DIR') + '/resource/third_party_libs.scons', 'lib_env')
-
-examples_env = lib_env.Clone()
+examples_env = env.Clone()
######################################################################
# Build flags
######################################################################
##
Import('env')
-# Add third party libraries
-lib_env = env.Clone()
-SConscript(env.get('SRC_DIR') + '/resource/third_party_libs.scons', 'lib_env')
-
-oclib_env = lib_env.Clone()
+oclib_env = env.Clone()
######################################################################
# Build flags
######################################################################
+++ /dev/null
-######################################################################
-# This script manages third party libraries
-#
-#Note: The paths must keep consistent with oic-utilities
-######################################################################
-import os
-import platform
-
-Import('env', 'lib_env')
-
-target_os = env.get('TARGET_OS')
-target_arch = env.get('TARGET_ARCH')
-src_dir = env.get('SRC_DIR')
-
-
-######################################################################
-# Check dependent packages (Linux only)
-######################################################################
-if target_os in ['linux', 'tizen']:
- if not env.GetOption('help'):
- if not target_arch == platform.machine():
- print '''
-*********************************** Warning ***********************************
-* You are trying cross build, please make sure (%s) version libraries are
-* installed! *
-*******************************************************************************
-''' % target_arch
-
- conf = Configure(lib_env)
-
- if not conf.CheckLib('boost_program_options'):
- print 'Did not find boost_program_options, exiting!'
- Exit(1)
-
- conf.Finish()
-
-
-######################################################################
-# The path of third party libraries binary
-######################################################################
-if target_os == 'android':
- if target_arch == 'armeabi-v7a-hard':
- target_arch = 'armeabi-v7a'
-
- if target_arch not in ['x86', 'armeabi', 'armeabi-v7a']:
- if not env.GetOption('help') and not env.GetOption('clean'):
- print '''
-*********************************** Warning ***********************************
-* current only x86, armeabi, armeabi-v7a libraries are provided! *
-*******************************************************************************
-'''
- else:
- # Too much boost warning, suppress the warning
- lib_env.AppendUnique(CCFLAGS = ['-w'])
-
-elif target_os == 'ios':
- lib_env.AppendUnique(FRAMEWORKS = ['boost'])
-elif target_os == 'darwin':
- lib_env.AppendUnique(CPPPATH = ['/usr/local/include'])
- lib_env.AppendUnique(LIBPATH = ['/usr/local/lib'])
sample_env.AppendUnique(CXXFLAGS = ['-Wall', '-pthread', '-std=c++0x'])
sample_env.AppendUnique(LIBS = ['pthread'])
-sample_env.AppendUnique(LIBS = ['oc', 'oc_logger', 'octbstack', 'coap', 'ppm', 'pmimpl', 'dl'])
+sample_env.AppendUnique(LIBS = ['oc', 'oc_logger', 'octbstack', 'coap', 'ppm', 'pmimpl', 'boost_system', 'boost_thread', 'dl'])
######################################################################
# Source files and Targets
######################################################################
mqttclient = sample_env.Program('mqtt/mqttclient', 'mqtt/mqttclient.cpp')
-Alias('mqttclient', mqttclient)
-env.AppendTarget('mqttclient')
\ No newline at end of file
+env.AppendTarget('mqttclient', mqttclient)
Import('env', 'lib_env')
-target_os = env.get('TARGET_OS')
-target_arch = env.get('TARGET_ARCH')
-src_dir = env.get('SRC_DIR')
-
-resource_path = src_dir + '/resource'
-
-######################################################################
-# Check dependent packages (Linux only)
-######################################################################
-if target_os in ['linux', 'tizen']:
- if not env.GetOption('help'):
- if not target_arch == platform.machine():
- print '''
-*********************************** Warning ***********************************
-* You are trying cross build, please make sure (%s) version libraries are
-* installed! *
-*******************************************************************************
-''' % target_arch
-
- conf = Configure(lib_env)
-
- if not conf.CheckLib('boost_thread'):
- print 'Did not find boost_thread, exiting!'
- Exit(1)
-
- if not conf.CheckLib('boost_system'):
- print 'Did not find boost_system, exiting!'
- Exit(1)
-
- conf.Finish()
-
-######################################################################
-# The 'include' path of external libraries
-######################################################################
+resource_path = env.get('SRC_DIR') + '/resource'
lib_env.AppendUnique(CPPPATH = [
resource_path ,
resource_path + '/include' ,
resource_path + '/csdk/logger/include'
])
-######################################################################
-# The path of third party libraries binary
-######################################################################
-if target_os == 'android':
- if target_arch == 'armeabi-v7a-hard':
- target_arch = 'armeabi-v7a'
-
- if target_arch not in ['x86', 'armeabi', 'armeabi-v7a']:
- if not env.GetOption('help') and not env.GetOption('clean'):
- print '''
-*********************************** Warning ***********************************
-* current only x86, armeabi, armeabi-v7a libraries are provided! *
-*******************************************************************************
-'''
- else:
- # Too much boost warning, suppress the warning
- lib_env.AppendUnique(CCFLAGS = ['-w'])
-
-elif target_os == 'ios':
- lib_env.AppendUnique(FRAMEWORKS = ['boost', 'expat', 'openssl'])
-elif target_os == 'darwin':
- lib_env.AppendUnique(CPPPATH = ['/usr/local/include'])
- lib_env.AppendUnique(LIBPATH = ['/usr/local/lib'])
-
-Import('env')
-lib_env.AppendUnique(LIBPATH = env.get('BUILD_DIR'))
+lib_env.AppendUnique(LIBPATH = env.get('BUILD_DIR'))
\ No newline at end of file