Fix android build compatibility problems
authorCaiwen Zhang <caiwen.zhang@intel.com>
Thu, 25 Dec 2014 10:37:00 +0000 (18:37 +0800)
committerCaiwen Zhang <caiwen.zhang@intel.com>
Fri, 26 Dec 2014 02:03:41 +0000 (10:03 +0800)
1. Some C++11 features are used in IoTivity. Old Android NDK may doesn't
support a part of these features directly. Implement and include it if
it is required.

2. Fix libc function missing problem. Some functions, e.g. rand, srand.
strtof ... are static inline prior to android-L(it is defined and
implemented in the header file). So before android-L libc.so doesn't
include them. If build on android-L and run on an old platform(earlier
than android-L), there will be 'can't locate xxx' problem.

e.g.:
rand, it is defined in stdlib.h as "static __inline__ int rand() { ....} in
the ndk < 10. it's defined as 'extern int rand();' from ndk-10x. With old
ndk, if a file (x.c) used 'rand' function, when build x.c, 'rand' is compiled
into x.o. 'rand' won't exit in neither libc.so nor libc.a. If build an
application with ndk-10x and run it on emulator or device(developed with
NDK < 10), it requires implement 'rand' function by owerself or there
will be be 'can't locate rand' problem.

For convenience, add these functions in a file, if necessary, build it as
library and auto link into binary.

Change-Id: If47da94db7d06fde9887eba335bc0c67762514a9
Signed-off-by: Caiwen Zhang<caiwen.zhang@intel.com>
17 files changed:
build_common/android/SConscript
build_common/android/compatibility/android_cpp11_compat.cpp [new file with mode: 0644]
build_common/android/compatibility/android_cpp11_compat.h [new file with mode: 0644]
build_common/android/compatibility/c_compat.c [new file with mode: 0644]
build_common/android/compatibility/c_compat.scons [new file with mode: 0644]
build_common/android/compatibility/cpp11_compat.scons [new file with mode: 0644]
build_common/android/jni/Android.mk
resource/examples/SConscript
resource/examples/ocicuc/SConscript
resource/include/OCRepresentation.h
resource/oc_logger/SConscript
resource/oc_logger/examples/SConscript
resource/src/SConscript
service/SConscript
service/notification-manager/SConscript
service/protocol-plugin/plugins/SConscript
service/things-manager/SConscript

index 6e81213..be49aee 100644 (file)
@@ -45,8 +45,7 @@ elif platform.system().lower() == 'darwin':
 
 # Android build system default cofig
 env.AppendUnique(CPPDEFINES = ['ANDROID'])
-env.AppendUnique(CFLAGS = ['-Wa,--noexecstack'])
-env.AppendUnique(CXXFLAGS = ['-Wa,--noexecstack', '-fstack-protector'])
+env.AppendUnique(SHCFLAGS = ['-Wa,--noexecstack'])
 env.AppendUnique(LINKFLAGS = ['-Wl,--gc-sections', '-Wl,-z,nocopyreloc'])
 
 ######################################################################
@@ -80,20 +79,12 @@ if ANDROID_HOME is None or not os.path.exists(ANDROID_HOME):
 
 target_arch = env.get('TARGET_ARCH')
 
-# Android ndk early version doesn't support C++11. Detect the toolchain
-# and platform version to make sure the newest version is used.
-
-# Detect toolchain version
-for tc_ver in ['4.9', '4.8', '4.7', '']:
-       if os.path.exists(android_ndk + '/sources/cxx-stl/gnu-libstdc++/' + tc_ver):
+# 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.7', '4.8', '4.9', '']:
+       if os.path.exists(android_ndk + '/toolchains/x86-' + tc_ver):
                break
 
-# Detect platform version.
-platform_ver = ''
-for pf_ver in range(0, 100): # 100 should be big enough :)
-       if os.path.exists(android_ndk + '/platforms/android-%d' % pf_ver):
-               platform_ver = "%d" % pf_ver
-
 cmd = [ndk_build_cmd]
 cmd.append('APP_ABI=' + target_arch)
 cmd.append('APP_STL=gnustl_static')
@@ -111,8 +102,6 @@ else:
 *******************************************************************************
 '''
 
-if platform_ver != '':
-       cmd.append('APP_PLATFORM=android-' + platform_ver)
 cmd.append('-n')
 
 p = subprocess.Popen(cmd, stdout = subprocess.PIPE)
@@ -138,12 +127,6 @@ for flags in p.stdout.readlines():
                sysroot = flags[8:].strip()
                env.AppendUnique(LINKFLAGS = ['--sysroot=' + sysroot])
                env.AppendUnique(LIBPATH = [sysroot + '/usr/lib'])
-               # To fix android NDK issue
-               # Some functions, e.g. rand, srand. strtof ... are static in static inline
-               # prior to android-L. so libc.so before android-L doesn't have them. If build
-               # build on android-L and the function(s) is used, should link libc.a
-               if platform_ver == '' or int(platform_ver) > 20:
-                       env.AppendUnique(LIBS = File(sysroot + '/usr/lib/libc.a'))
 
        elif cmp(flags[0:8], 'LDFLAGS=') == 0:
                env.AppendUnique(LINKFLAGS = Split(flags[8:]))
@@ -153,6 +136,11 @@ for flags in p.stdout.readlines():
                env.AppendUnique(LIBPATH = [android_ndk + '/sources/cxx-stl/gnu-libstdc++/'
                                + ver + '/libs/' + target_arch])
 
+       elif cmp(flags[0:9], 'PLATFORM=') == 0:  # get target platform: android-x
+               platform_ver = flags[9+8:].strip()
+               if not platform_ver.isdigit():
+                       platform_ver = ''
+
 ######################################################################
 # Set release/debug flags
 ######################################################################
@@ -162,3 +150,16 @@ if env.get('RELEASE'):
        env.AppendUnique(LINKFLAGS = ['-s'])
 else:
        env.AppendUnique(CCFLAGS = ['-g'])
+
+# From android-5 (API > 20), all application must be built with flags '-fPIE' '-pie'.
+# Due to the limitation of Scons, it's required to added it into the command line
+# directly (otherwise, it will also be added when build share library)
+env.Replace(CCCOM = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM -fPIE $SOURCES')
+env.Replace(CXXCOM = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM -fPIE $SOURCES')
+env.Replace(LINKCOM = '$LINK -o $TARGET -pie $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS')
+
+# Fix android-ndk compatibility issue, make applications build on new NDK can run on old platform
+if platform_ver == '' or int(platform_ver) > 20:
+       SConscript('compatibility/c_compat.scons')
+
+SConscript('compatibility/cpp11_compat.scons')
diff --git a/build_common/android/compatibility/android_cpp11_compat.cpp b/build_common/android/compatibility/android_cpp11_compat.cpp
new file mode 100644 (file)
index 0000000..dedc927
--- /dev/null
@@ -0,0 +1,73 @@
+#include <sstream>
+#include "android_cpp11_compat.h"
+
+namespace OC {
+    template <typename T>
+    void from_string(const std::string& s, T& result) {
+        std::stringstream ss(s);
+        ss >> result;    // TODO handle errors
+    }
+}
+
+namespace std {
+
+    int stoi(const string& s)
+    {
+        int ret;
+        int &ref = ret;
+        OC::from_string(s, ref);
+        return ret;
+    }
+
+    double stod(const std::string& s)
+    {
+        double ret;
+        double &ref = ret;
+        OC::from_string(s, ref);
+        return ret;
+    }
+
+    long long stoll(const std::string& s)
+    {
+        long long ret;
+        long long int &ref = ret;
+        OC::from_string(s, ref);
+        return ret;
+    }
+
+    unsigned long long stoull(const std::string& s)
+    {
+        unsigned long long ret;
+        unsigned long long  &ref = ret;
+        OC::from_string(s, ref);
+        return ret;
+    }
+
+    long double stold(const string& s)
+    {
+        long double ret;
+        long double &ref = ret;
+        OC::from_string(s, ref);
+        return ret;
+    }
+
+    std::string to_string(int t) {
+        std::ostringstream os;
+            os << t;
+        return os.str();
+    }
+
+    std::string to_string(double t) {
+        std::ostringstream os;
+            os << t;
+        return os.str();
+    }
+
+    std::string to_string(uint32_t t)
+    {
+        std::ostringstream os;
+            os << t;
+        return os.str();
+    }
+
+} // std
diff --git a/build_common/android/compatibility/android_cpp11_compat.h b/build_common/android/compatibility/android_cpp11_compat.h
new file mode 100644 (file)
index 0000000..78af522
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _ANDRDIO_STRING_H_
+#define _ANDRDIO_STRING_H_
+
+#ifdef __ANDROID__
+#include <string>
+
+#ifndef ANDROID_C11_COMPAT
+using std::to_string;
+#else
+namespace std {
+    int stoi(const std::string& s);
+    double stod(const std::string& s);
+    long long stoll(const std::string& s);
+    unsigned long long stoull(const std::string& s);
+    long double stold(const string& s);
+
+    std::string to_string(int i);
+    std::string to_string(uint32_t i);
+    std::string to_string(double d);
+}
+#endif
+
+#endif
+
+#endif
\ No newline at end of file
diff --git a/build_common/android/compatibility/c_compat.c b/build_common/android/compatibility/c_compat.c
new file mode 100644 (file)
index 0000000..000428a
--- /dev/null
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+
+/* from stdlib.h */
+float strtof(const char *nptr, char **endptr)
+{
+    return (float)strtod(nptr, endptr);
+}
+
+double atof(const char *nptr)
+{
+    return strtod(nptr, NULL);
+}
+
+int abs(int __n)
+{
+    return (__n < 0) ? -__n : __n;
+}
+
+long labs(long __n)
+{
+    return (__n < 0L) ? -__n : __n;
+}
+
+long long llabs(long long __n)
+{
+    return (__n < 0LL) ? -__n : __n;
+}
+
+int rand(void)
+{
+    return (int)lrand48();
+}
+
+void srand(unsigned int __s)
+{
+    srand48(__s);
+}
+
+long random(void)
+{
+    return lrand48();
+}
+
+void srandom(unsigned int __s)
+{
+    srand48(__s);
+}
+
+/* from unistd.h */
+int getpagesize(void)
+{
+  extern unsigned int __page_size;
+  return __page_size;
+}
+
+int __getpageshift(void)
+{
+  extern unsigned int __page_shift;
+  return __page_shift;
+}
diff --git a/build_common/android/compatibility/c_compat.scons b/build_common/android/compatibility/c_compat.scons
new file mode 100644 (file)
index 0000000..8aa4456
--- /dev/null
@@ -0,0 +1,20 @@
+##
+# This script is for fixing android platform compatibility problem
+##
+
+# To fix android NDK compatibility problem
+# Some functions, e.g. rand, srand. strtof ... are static inline prior to
+# android-L. So before android-L libc.so doesn't include them. If build
+# on android-L and run on an old platform(earlier than android-L), there will
+# be 'can't locate xxx' problem.
+import os
+
+Import('env')
+
+sif_env = env.Clone()
+
+sif_lib = sif_env.StaticLibrary(env.get('BUILD_DIR') + '/c_compat',
+       env.SrcToObj(os.path.abspath('./c_compat.c'), env.get('SRC_DIR')))
+
+env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
+env.AppendUnique(LIBS = ['c_compat'])
\ No newline at end of file
diff --git a/build_common/android/compatibility/cpp11_compat.scons b/build_common/android/compatibility/cpp11_compat.scons
new file mode 100644 (file)
index 0000000..a06455f
--- /dev/null
@@ -0,0 +1,17 @@
+##
+# This script is for fixing android platform compatibility problem
+##
+import os
+
+Import('env')
+
+env.AppendUnique(CPPDEFINES = ['ANDROID_C11_COMPAT'])
+
+cc_env = env.Clone()
+cc_env.AppendUnique(CPPPATH = ['.'])
+cc_lib = cc_env.StaticLibrary(env.get('BUILD_DIR') + '/android_cpp11_compat',
+       env.SrcToObj(os.path.abspath('./android_cpp11_compat.cpp'), env.get('SRC_DIR')))
+
+env.AppendUnique(CPPPATH = [os.path.abspath('.')])
+env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
+env.AppendUnique(LIBS = ['android_cpp11_compat'])
index 9bc6284..282900a 100644 (file)
@@ -9,3 +9,4 @@ $(info CPPPATH=$(TARGET_C_INCLUDES) $(__ndk_modules.$(APP_STL).EXPORT_C_INCLUDES
 $(info SYSROOT=$(SYSROOT_LINK))
 $(info LDFLAGS=$(TARGET_LDFLAGS) $(TARGET_NO_EXECUTE_LDFLAGS) $(TARGET_NO_UNDEFINED_LDFLAGS) $(TARGET_RELRO_LDFLAGS))
 $(info TC_VER=$(TOOLCHAIN_VERSION))
+$(info PLATFORM=$(APP_PLATFORM))
\ No newline at end of file
index 4a12d01..f3a1e20 100644 (file)
@@ -36,7 +36,6 @@ examples_env.PrependUnique(LIBS = ['oc', 'octbstack', 'coap', 'oc_logger'])
 if target_os == 'android':
        examples_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
        examples_env.AppendUnique(LIBS = ['gnustl_static'])
-       examples_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
 
        if not env.get('RELEASE'):
                examples_env.AppendUnique(LIBS = ['log'])
index 0b52b29..f4d8bb9 100644 (file)
@@ -28,7 +28,6 @@ if target_os not in ['windows', 'winrt']:
 
 if target_os == 'android':
        ocicuc_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
-       ocicuc_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
        ocicuc_env.AppendUnique(LIBS = ['boost_program_options-gcc-mt-1_49', 'boost_thread-gcc-mt-1_49', 'gnustl_static'])
 
        if not env.get('RELEASE'):
@@ -46,4 +45,4 @@ monoprocess = ocicuc_env.Program('monoprocess', ['monoprocess.cpp', 'driver.cpp'
 small_example = ocicuc_env.Program('small_example', ['small_example.cpp', 'driver.cpp', 'utility.cpp'])
 
 Alias("examples_ocicuc", [client, server, monoprocess, small_example])
-env.AppendTarget('examples_ocicuc')
\ No newline at end of file
+env.AppendTarget('examples_ocicuc')
index fbfb7c5..3a1746b 100644 (file)
@@ -37,6 +37,9 @@
 
 #include <OCException.h>
 
+#ifdef __ANDROID__
+#include "android_cpp11_compat.h"
+#endif
 
 namespace cereal
 {
index 0b18e4d..52e665e 100644 (file)
@@ -16,7 +16,6 @@ liboc_logger_env.PrependUnique(CPPPATH = ['include'])
 target_os = env.get('TARGET_OS')
 if target_os == 'android':
        liboc_logger_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
-       liboc_logger_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
        liboc_logger_env.AppendUnique(LIBS = ['gnustl_static', 'log'])
 
 if target_os not in ['arduino', 'windows', 'winrt']:
index d3205ba..de1ab4a 100644 (file)
@@ -18,7 +18,6 @@ examples_env.AppendUnique(LIBS = ['oc_logger', 'stdc++'])
 target_os = env.get('TARGET_OS')
 if target_os == 'android':
        examples_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
-       examples_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
        examples_env.AppendUnique(LIBS = ['gnustl_static'])
 
 if target_os not in ['arduino', 'windows', 'winrt']:
@@ -32,4 +31,4 @@ examples_c = examples_env.Program('examples_c', 'test_logging.c', OBJPREFIX='c_'
 examples_cpp = examples_env.Program('examples_cpp', 'test_logging.cpp')
 
 Alias('liboc_logger_examples', [examples_c, examples_cpp])
-examples_env.AppendTarget('liboc_logger_examples')
\ No newline at end of file
+examples_env.AppendTarget('liboc_logger_examples')
index 7ec0c8e..4dc2570 100644 (file)
@@ -27,7 +27,6 @@ if target_os not in ['windows', 'winrt']:
 
 if target_os == 'android':
        oclib_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
-       oclib_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
        oclib_env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
        oclib_env.AppendUnique(LIBS = ['octbstack', 'coap', 'oc_logger', 'boost_thread-gcc-mt-1_49', 'gnustl_static', 'log'])
 
index acd51bc..1ea7f78 100644 (file)
@@ -14,7 +14,9 @@ if target_os != 'arduino':
        SConscript('soft-sensor-manager/SConscript')
 
        # Build protocol plugin project
-       SConscript('protocol-plugin/SConscript')
+       # protocol-plugin use 'inotify', this feature isn't support by MAC OSX
+       if target_os not in ['darwin', 'ios']:
+               SConscript('protocol-plugin/SConscript')
 
        # Build notification manager project
 #      SConscript('notification-manager/SConscript')
index a5e52dd..4c5dd05 100644 (file)
@@ -24,7 +24,6 @@ if target_os == 'linux':
 if target_os == 'android':
        notimgr_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
        notimgr_env.AppendUnique(LIBS = ['gnustl_static'])
-       notimgr_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
 
        if not env.get('RELEASE'):
                notimgr_env.AppendUnique(LIBS = ['log'])
index e00a35e..5e5d919 100644 (file)
@@ -27,7 +27,6 @@ if target_os not in ['windows', 'winrt']:
 if target_os == 'android':
        plugins_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
        plugins_env.AppendUnique(LIBS = ['gnustl_static'])
-       plugins_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
 
 plugins_env.AppendUnique(LIBS = [File(env.get('BUILD_DIR') + '/libmosquitto.a'),
                'mosquitto', 'ssl', 'rt'])
index 470b15f..412907c 100644 (file)
@@ -22,7 +22,6 @@ if target_os not in ['windows', 'winrt']:
 
 if target_os == 'android':
        things_manager_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
-       things_manager_env.AppendUnique(CPPDEFINES = ['_GLIBCXX_USE_C99=1', '_GLIBCXX_HAVE_WCSTOF=1'])
 
 ######################################################################
 # Source files and Targets