Automatically detect C99 flags for supported compilers.
authorOssama Othman <ossama.othman@intel.com>
Tue, 17 Mar 2015 21:43:18 +0000 (14:43 -0700)
committerErich Keane <erich.keane@intel.com>
Mon, 23 Mar 2015 19:54:05 +0000 (19:54 +0000)
This patch set allows the required C99 command line flags to be
automatically set earlier in the build process.  Currently only gcc is
supported, but adding support for other compilers is straightforward.

Hard-coded '-std=c99' and '-std=gnu99' CFLAGS found throughout
iotivity SConscript files have been removed.  Feature test macros have
been added where needed to enable extensions that were disabled by
automatic addition of '-std=c99' to the CFLAGS build variable.

Change-Id: I6d05313aefff1911d1aaa2097954a1878874e73a
Signed-off-by: Ossama Othman <ossama.othman@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/495
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Jon A. Cruz <jonc@osg.samsung.com>
Reviewed-by: Erich Keane <erich.keane@intel.com>
12 files changed:
build_common/SConscript
build_common/iotivityconfig/__init__.py
build_common/iotivityconfig/compiler/configuration.py
build_common/iotivityconfig/compiler/default_configuration.py
build_common/iotivityconfig/compiler/factory.py
build_common/iotivityconfig/compiler/gcc_configuration.py
resource/csdk/SConscript
resource/csdk/libcoap-4.1.1/SConscript
resource/oc_logger/SConscript
resource/oc_logger/examples/SConscript
service/protocol-plugin/plugins/mqtt-fan/lib/SConscript
service/protocol-plugin/plugins/mqtt-fan/lib/mosquitto.c

index 56a872b..22501f2 100644 (file)
@@ -243,9 +243,15 @@ from iotivityconfig import *
 
 conf = env.Configure(
         custom_tests = {
+                'CheckC99Flags' : iotivityconfig.check_c99_flags,
                 'CheckCXX11Flags' : iotivityconfig.check_cxx11_flags
         } )
 
+# IoTivity requires support for C99 for the C SDK.
+if not conf.CheckC99Flags():
+        print('C99 support is required!')
+        Exit(1)
+
 # IoTivity requires support for C++11 for the C++ SDK.
 #
 # However, some platforms, such as Arduino, only support the C SDK.
index 3f9be7c..ea18ea1 100644 (file)
@@ -59,6 +59,22 @@ def _inform_user_of_broken_gcc_headers(context, flag):
             # handle the issue in that case.
             _check_for_broken_gcc_headers(context, flag)
 
+def check_c99_flags(context):
+    """
+    Check if command line flag is required to enable C99 support.
+
+    Returns 1 if no flag is required, 0 if no flag was found, or the
+    actual flag if one was found.
+    """
+
+    cc = context.env['CC']
+    context.Message('Checking for C99 flag for ' + cc + '... ')
+    config = factory.make_c_compiler_config(context)
+    ret = config.check_c99_flags()
+    context.Result(ret)
+
+    return ret
+
 def check_cxx11_flags(context):
     """
     Check if command line flag is required to enable C++11 support.
@@ -69,7 +85,7 @@ def check_cxx11_flags(context):
 
     cxx = context.env['CXX']
     context.Message('Checking for C++11 flag for ' + cxx + '... ')
-    config = factory.make_compiler_config(context)
+    config = factory.make_cxx_compiler_config(context)
     ret = config.check_cxx11_flags()
     context.Result(ret)
 
index e96215e..e328e28 100644 (file)
@@ -31,6 +31,23 @@ class Configuration:
         self._context = context      # scons configure context
         self._env     = context.env  # scons environment
 
+    def check_c99_flags(self):
+        """
+        Check if command line flag is required to enable C99
+        support.
+
+        Returns 1 if no flag is required, 0 if no flag was
+        found, and the actual flag if one was found.
+
+        CFLAGS will be updated with appropriate C99 flag,
+        accordingly.
+        """
+
+        return self._check_flags(self._c99_flags(),
+                                 self._c99_test_program(),
+                                 '.c',
+                                 'CFLAGS')
+
     def check_cxx11_flags(self):
         """
         Check if command line flag is required to enable C++11
@@ -93,12 +110,32 @@ class Configuration:
         return ret
 
     # ------------------------------------------------------------
+    # Return test program to be used when checking for basic C99
+    # support.
+    #
+    # Subclasses should implement this template method or use the
+    # default test program found in the DefaultConfiguration class
+    # through composition.
+    # ------------------------------------------------------------
+    def _c99_test_program(self):
+        raise NotImplementedError('unimplemented method')
+
+    # --------------------------------------------------------------
+    # Get list of flags that could potentially enable C99 support.
+    #
+    # Subclasses should implement this template method if flags are
+    # needed to enable C99 support.
+    # --------------------------------------------------------------
+    def _c99_flags(self):
+        raise NotImplementedError('unimplemented method')
+
+    # ------------------------------------------------------------
     # Return test program to be used when checking for basic C++11
     # support.
     #
     # Subclasses should implement this template method or use the
     # default test program found in the DefaultConfiguration class
-    # through inheritance or composition.
+    # through composition.
     # ------------------------------------------------------------
     def _cxx11_test_program(self):
         raise NotImplementedError('unimplemented method')
index 8735f3d..ed90f40 100644 (file)
@@ -25,6 +25,42 @@ class DefaultConfiguration(Configuration):
     # Return test program to be used when checking for basic C++11
     # support.
     # ------------------------------------------------------------
+    def _c99_test_program(self):
+        return """
+// Some headers found in C99.
+#include <stdbool.h>
+#include <stdint.h>
+
+int main()
+{
+    struct foo
+    {
+        bool b;      // C99 type
+        int i;
+        uint64_t q;  // C99 type
+    };
+
+    // Designated initializer.
+    struct foo bar = { .b = false, .q = UINT64_MAX };
+
+    // Implicitly initialized field.
+    return bar.i != 0;
+}
+"""
+
+    # --------------------------------------------------------------
+    # Get list of flags that could potentially enable C99 support.
+    #
+    # The default configuration assumes that no flag is needed to
+    # enable C99 support.
+    # --------------------------------------------------------------
+    def _c99_flags(self):
+        return []
+
+    # ------------------------------------------------------------
+    # Return test program to be used when checking for basic C++11
+    # support.
+    # ------------------------------------------------------------
     def _cxx11_test_program(self):
         return """
 int main()
index fcde402..7b0bee7 100644 (file)
 from default_configuration import *
 from gcc_configuration import *
 
-# Canonicalize the C++ compiler name to "g++" if gcc is being used to
-# simplify mapping to the appropriate C++11 flags since gcc may be
-# installed under a different name.  This will be used when mapping
-# compiler name to configuration in the factory submodule.
-_GCC = 'g++'
+# Canonicalize the C or C++ compiler name to "gcc" if gcc is being
+# used to simplify mapping to the GCC compiler configuration since GCC
+# may be installed under a different name.  This will be used when
+# mapping compiler name to configuration in the factory submodule.
+_GCC = 'gcc'
 
 # Update this dictionary with new compiler configurations as needed.
 _CONFIG_MAP = { _GCC : GccConfiguration }
 
-_compiler_config = None
+_c_compiler_config = None
+_cxx_compiler_config = None
+
+def check_for_gcc_c(context):
+    """
+    Check if the C compiler is GCC
+
+    Returns 1 if gcc, 0 otherwise
+    """
+
+    test_program = """
+#if !defined(__GNUC__)
+#  error "Not the GCC C compiler."
+#endif
+
+int foo(void)
+{
+    return 0;
+}
+"""
+
+    return context.TryCompile(test_program, '.c')
 
 def check_for_gcc_cxx(context):
     """
@@ -52,9 +73,35 @@ private:
 
     return context.TryCompile(test_program, '.cpp')
 
-def make_compiler_config(context):
+def make_c_compiler_config(context):
+    """
+    Create C compiler-specific configuration object.
+
+    Arguments:
+    context -- the scons configure context
+
+    The 'CC' key in the SCons environment will be mapped to the
+    appropriate supported compiler configuration.  If no match is
+    found compiler configuration operations will simply be no-ops.
+    """
+
+    global _c_compiler_config
+
+    if _c_compiler_config is None:
+        cc = context.env['CC']
+
+        if check_for_gcc_c(context):
+            cc = _GCC
+
+        config = _CONFIG_MAP.get(cc, DefaultConfiguration)
+
+        _c_compiler_config = config(context)
+
+    return _c_compiler_config
+
+def make_cxx_compiler_config(context):
     """
-    Create compiler-specific configuration object.
+    Create C++ compiler-specific configuration object.
 
     Arguments:
     context -- the scons configure context
@@ -64,9 +111,9 @@ def make_compiler_config(context):
     found compiler configuration operations will simply be no-ops.
     """
 
-    global _compiler_config
+    global _cxx_compiler_config
 
-    if _compiler_config is None:
+    if _cxx_compiler_config is None:
         cxx = context.env['CXX']
 
         if check_for_gcc_cxx(context):
@@ -74,6 +121,6 @@ def make_compiler_config(context):
 
         config = _CONFIG_MAP.get(cxx, DefaultConfiguration)
 
-        _compiler_config = config(context)
+        _cxx_compiler_config = config(context)
 
-    return _compiler_config
+    return _cxx_compiler_config
index d9ceb17..ded43a7 100644 (file)
@@ -22,6 +22,38 @@ class GccConfiguration(Configuration):
         Configuration.__init__(self, context)
 
     # ------------------------------------------------------------
+    # Return test program to be used when checking for basic C99
+    # support in GCC.
+    # ------------------------------------------------------------
+    def _c99_test_program(self):
+        # Use the default C99 test program but enable pedantic
+        # diagnostics specific to GCC to force errors to occur if a
+        # flag is required to compile C99 code without warning or
+        # error.
+
+        from default_configuration import DefaultConfiguration
+        def_config = DefaultConfiguration(self._context)
+
+        return """
+#pragma GCC diagnostic error "-Wall"
+#pragma GCC diagnostic error "-Werror"
+#pragma GCC diagnostic error "-pedantic"
+""" + def_config._c99_test_program()
+
+    # -------------------------------------------------------------
+    # Get flags known to enable C99 support for GCC C compiler.
+    # -------------------------------------------------------------
+    def _c99_flags(self):
+        # Favor flags that do not enable GNU extensions by default,
+        # e.g. '-std=c99'.
+        return [ '-std=c99',
+                 '-std=iso9899:1999',
+                 '-std=gnu99',
+                 '-std=c9x',
+                 '-std=iso9899:199x',
+                 '-std=gnu9x' ]
+
+    # ------------------------------------------------------------
     # Return test program to be used when checking for basic C++11
     # support in GCC.
     # ------------------------------------------------------------
index 83e9bfa..84f440f 100755 (executable)
@@ -31,7 +31,6 @@ liboctbstack_env.PrependUnique(CPPPATH = [
 
 if target_os not in ['arduino', 'windows', 'winrt']:
        liboctbstack_env.AppendUnique(CPPDEFINES  = ['WITH_POSIX'])
-       liboctbstack_env.AppendUnique(CFLAGS = ['-std=c99'])
 
 if target_os not in ['windows', 'winrt']:
        liboctbstack_env.AppendUnique(CFLAGS = ['-Wall'])
index c7bba15..1d13158 100644 (file)
@@ -26,8 +26,7 @@ libcoap_env.PrependUnique(CPPPATH = [
                ])
 
 if target_os not in ['arduino', 'windows', 'winrt']:
-       libcoap_env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
-       libcoap_env.AppendUnique(CFLAGS = ['-std=gnu99'])
+       libcoap_env.AppendUnique(CPPDEFINES = ['WITH_POSIX', '_BSD_SOURCE'])
 
 if target_os not in ['windows', 'winrt']:
        libcoap_env.AppendUnique(CFLAGS = ['-Wall', '-ffunction-sections',
index c5e3a15..17f7883 100644 (file)
@@ -16,7 +16,6 @@ if target_os == 'android':
        liboc_logger_env.AppendUnique(LIBS = ['gnustl_shared', 'log'])
 
 if target_os not in ['arduino', 'windows', 'winrt']:
-       liboc_logger_env.AppendUnique(CFLAGS = ['-std=c99'])
        liboc_logger_env.AppendUnique(CCFLAGS = ['-Wall'])
 
 ######################################################################
index 73cce37..46e1b08 100644 (file)
@@ -18,7 +18,7 @@ if target_os == 'android':
        examples_env.AppendUnique(LIBS = ['gnustl_shared'])
 
 if target_os not in ['arduino', 'windows', 'winrt']:
-       examples_env.AppendUnique(CFLAGS = Split('-Wall -std=c99 -Werror'))
+       examples_env.AppendUnique(CFLAGS = Split('-Wall -Werror'))
        examples_env.AppendUnique(CXXFLAGS = '-Wall')
 
 ######################################################################
index a5695c2..fc2add8 100644 (file)
@@ -14,6 +14,8 @@ target_os = env.get('TARGET_OS')
 ######################################################################
 mosquitto_env.AppendUnique(CPPPATH = ['./'])
 if target_os not in ['windows', 'winrt']:
+       # strdup() and pselect() require specific extensions to be enabled.
+       mosquitto_env.AppendUnique(CPPDEFINES = [('_XOPEN_SOURCE', 600)])
        mosquitto_env.AppendUnique(CFLAGS = ['-Wall', '-ggdb', '-fPIC',
                        '-DWITH_TLS', '-DWITH_TLS_PSK', '-DWITH_THREADING'])
 ######################################################################
index 7f94dea..a6dd2a7 100644 (file)
@@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE.
 #include <stdio.h>
 #include <string.h>
 #ifndef WIN32
+#include <strings.h>  /* for strcasecmp() */
 #include <sys/select.h>
 #include <sys/time.h>
 #include <unistd.h>