Bug 1264482 - Move adding -std=gnu99 and -std=gnu++11 to moz.configure. r?ted draft
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 06 Apr 2016 17:18:07 +0900
changeset 350676 4c4bebbaa2345f92ceb2f156eda137d162553224
parent 350675 3469be0504dbcefa776784b49926c55ae12cd5a9
child 518375 54183f06bf85972ba8c16999a758a472e16bc282
push id15385
push userbmo:mh+mozilla@glandium.org
push dateThu, 14 Apr 2016 04:41:28 +0000
reviewersted
bugs1264482
milestone48.0a1
Bug 1264482 - Move adding -std=gnu99 and -std=gnu++11 to moz.configure. r?ted We were unconditionally adding them, now actually check what the compilers default to and add the flags if they are necessary. This will, in the future, allow finer grained policy changes, where we can decide that C++11 and C++14 are fine, downgrading compilers that do C++17, etc.
build/autoconf/toolchain.m4
build/moz.configure/toolchain.configure
js/src/old-configure.in
old-configure.in
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -135,19 +135,16 @@ PATH=$_SAVE_PATH
 ])
 
 AC_DEFUN([MOZ_CXX11],
 [
 dnl Updates to the test below should be duplicated further below for the
 dnl cross-compiling case.
 AC_LANG_CPLUSPLUS
 if test "$GNU_CXX"; then
-    CXXFLAGS="$CXXFLAGS -std=gnu++11"
-    _ADDED_CXXFLAGS="-std=gnu++11"
-
     if test -n "$CLANG_CC"; then
         dnl We'd normally just check for the version from CC_VERSION (fed
         dnl from __clang_major__ and __clang_minor__), but the clang that
         dnl comes with Xcode has a completely different version scheme
         dnl despite exposing the version with the same defines.
         dnl So instead of a version check, do a feature check. Normally,
         dnl we'd use __has_feature, but there are unfortunately no C++11
         dnl differences in clang 3.4. However, it supports the 2013-08-28
@@ -196,30 +193,26 @@ if test -n "$CROSS_COMPILE"; then
 	changequote([,])
 
 	if test "$HOST_GCC_MAJOR_VERSION" -eq 4 -a "$HOST_GCC_MINOR_VERSION" -lt 8 ||
 	   test "$HOST_GCC_MAJOR_VERSION" -lt 4; then
 	    AC_MSG_ERROR([Only GCC 4.8 or newer supported for host compiler])
 	fi
     fi
 
-    HOST_CXXFLAGS="$HOST_CXXFLAGS -std=gnu++11"
-
     _SAVE_CXXFLAGS="$CXXFLAGS"
     _SAVE_CPPFLAGS="$CPPFLAGS"
     _SAVE_CXX="$CXX"
     CXXFLAGS="$HOST_CXXFLAGS"
     CPPFLAGS="$HOST_CPPFLAGS"
     CXX="$HOST_CXX"
     if test "$HOST_CC_TYPE" = clang; then
 	AC_TRY_COMPILE([], [#if !__cpp_static_assert
 			    #error ISO WG21 SG10 feature test macros unsupported
 			    #endif],,AC_MSG_ERROR([Only clang/llvm 3.4 or newer supported]))
     fi
 
     CXXFLAGS="$_SAVE_CXXFLAGS"
     CPPFLAGS="$_SAVE_CPPFLAGS"
     CXX="$_SAVE_CXX"
-elif test "$GNU_CXX"; then
-    HOST_CXXFLAGS="$HOST_CXXFLAGS $_ADDED_CXXFLAGS"
 fi
 AC_LANG_C
 ])
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -186,42 +186,121 @@ def try_preprocess(compiler, language, s
                 with LineIO(lambda l: log.debug('| %s', l)) as o:
                     o.write(out)
     finally:
         os.remove(path)
 
 
 @imports(_from='mozbuild.util', _import='LimitedString')
 @imports(_from='textwrap', _import='dedent')
-def check_compiler(compiler, language):
+def get_compiler_info(compiler, language):
+    '''Returns information about the given `compiler` (command line in the
+    form of a list or tuple), in the given `language`.
+
+    The returned information includes:
+    - the compiler type (msvc, clang-cl, clang or gcc)
+    - the compiler version
+    - the compiler supported language
+    - the compiler supported language version
+    '''
     class CompilerType(LimitedString):
         POSSIBLE_VALUES = ('msvc', 'clang-cl', 'clang', 'gcc')
 
+    # Note: MSVC doesn't expose __STDC_VERSION__. It does expose __STDC__,
+    # but only when given the -Za option, which disables compiler
+    # extensions.
     check = dedent('''\
         #if defined(_MSC_VER)
         #if defined(__clang__)
-        COMPILER clang-cl _MSC_VER
+        %COMPILER clang-cl
+        %VERSION _MSC_VER
         #else
-        COMPILER msvc _MSC_FULL_VER
+        %COMPILER msvc
+        %VERSION _MSC_FULL_VER
         #endif
         #elif defined(__clang__)
-        COMPILER clang __clang_major__.__clang_minor__.__clang_patchlevel__
+        %COMPILER clang
+        %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
         #elif defined(__GNUC__)
-        COMPILER gcc __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
+        %COMPILER gcc
+        %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
+        #endif
+
+        #if __cplusplus
+        %cplusplus __cplusplus
+        #elif __STDC_VERSION__
+        %STDC_VERSION __STDC_VERSION__
+        #elif __STDC__
+        %STDC_VERSION 198900L
         #endif
     ''')
 
     result = try_preprocess(compiler, language, check)
 
-    if result:
-        for line in result.splitlines():
-            if line.startswith('COMPILER '):
-                _, type, version = line.split(None, 2)
-                version = version.replace(' ', '')
-                return CompilerType(type), version
+    if not result:
+        return
+
+    data = {}
+    for line in result.splitlines():
+        if line.startswith('%') and ' ' in line:
+            k, v = line.split(' ', 1)
+            k = k.lstrip('%')
+            data[k] = v.replace(' ', '')
+            log.debug('%s = %s', k, data[k])
+
+    type = data.get('COMPILER')
+    if not type:
+        raise FatalCheckError(
+            'Unknown compiler or compiler not supported.')
+
+    cplusplus = int(data.get('cplusplus', '0L').rstrip('L'))
+    stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L'))
+
+    return namespace(
+        type=CompilerType(type),
+        version=data['VERSION'],
+        language='C++' if cplusplus else 'C',
+        language_version=cplusplus if cplusplus else stdc_version,
+    )
+
+
+@imports(_from='mozbuild.shellutil', _import='quote')
+def check_compiler(compiler, language):
+    info = get_compiler_info(compiler, language)
+
+    flags = []
+
+    def append_flag(flag):
+        if flag not in flags:
+            if info.type == 'clang-cl':
+                flags.append('-Xclang')
+            flags.append(flag)
+
+    # Check language standards
+    # --------------------------------------------------------------------
+    if language != info.language:
+        raise FatalCheckError(
+            '`%s` is not a %s compiler.' % (quote(*compiler), language))
+
+    # Note: We do a strict version check because there sometimes are backwards
+    # incompatible changes in the standard, and not all code that compiles as
+    # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
+    # example)
+    if info.language == 'C' and info.language_version != 199901:
+        if info.type in ('clang-cl', 'clang', 'gcc'):
+            append_flag('-std=gnu99')
+
+    # Note: MSVC, while supporting C++11, still reports 199711L for __cplusplus.
+    # Note: this is a strict version check because we used to always add
+    # -std=gnu++11.
+    if info.language == 'C++' and info.language_version != 201103:
+        if info.type in ('clang-cl', 'clang', 'gcc'):
+            append_flag('-std=gnu++11')
+
+    return info.type, info.version, flags
 
 
 @template
 def default_c_compilers(host_or_target):
     '''Template defining the set of default C compilers for the host and
     target platforms.
     `host_or_target` is either `host` or `target` (the @depends functions
     from init.configure.
@@ -340,17 +419,17 @@ def compiler(language, host_or_target, c
     # Normally, we'd use `var` instead of `_var`, but the interaction with
     # old-configure complicates things, and for now, we a) can't take the plain
     # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
     # old-configure AC_SUBST it (because it's autoconf doing it, not us)
     compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
                           input=delayed_getattr(provided_compiler, 'compiler'))
 
     @depends(compiler, provided_compiler, compiler_wrapper)
-    @checking('%s version' % what, lambda x: x.version if x else 'not found')
+    @checking('whether %s can be used' % what, lambda x: bool(x))
     @imports(_from='mozbuild.shellutil', _import='quote')
     def valid_compiler(compiler, provided_compiler, compiler_wrapper):
         wrapper = list(compiler_wrapper or ())
         if provided_compiler:
             provided_wrapper = list(provided_compiler.wrapper)
             # When doing a subconfigure, the compiler is set by old-configure
             # and it contains the wrappers from --with-compiler-wrapper and
             # --with-ccache.
@@ -378,26 +457,31 @@ def compiler(language, host_or_target, c
                     full_path):
                 die('Found `%s` before `%s` in your $PATH. '
                     'Please reorder your $PATH.',
                     quote(os.path.dirname(found_compiler)),
                     quote(os.path.dirname(full_path)))
 
         result = check_compiler(wrapper + [compiler] + flags, language)
         if result:
-            type, version = result
+            type, version, more_flags = result
             return namespace(
                 wrapper=wrapper,
                 compiler=compiler,
-                flags=flags,
+                flags=flags + more_flags,
                 type=type,
                 version=version,
             )
         die('Failed to get the compiler version')
 
+    @depends(valid_compiler)
+    @checking('%s version' % what)
+    def compiler_version(compiler):
+        return compiler.version
+
     if language == 'C++':
         @depends(valid_compiler, c_compiler)
         def compiler_suite_consistency(compiler, c_compiler):
             if compiler.type != c_compiler.type:
                 die('The %s C compiler is %s, while the %s C++ compiler is '
                     '%s. Need to use the same compiler suite.',
                     host_or_target_str, c_compiler.type,
                     host_or_target_str, compiler.type)
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -710,18 +710,16 @@ MOZ_ARG_ENABLE_BOOL(warnings-as-errors,
                           Enable treating warnings as errors],
     MOZ_ENABLE_WARNINGS_AS_ERRORS=1,
     MOZ_ENABLE_WARNINGS_AS_ERRORS=)
 
 dnl ========================================================
 dnl GNU specific defaults
 dnl ========================================================
 if test "$GNU_CC"; then
-    # We use C99.
-    CFLAGS="$CFLAGS -std=gnu99"
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(DSO_SONAME) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(DSO_SONAME) -o $@'
     DSO_LDOPTS='-shared'
     if test "$GCC_USE_GNU_LD"; then
         # Some tools like ASan use a runtime library that is only
         # linked against executables, so we must allow undefined
         # symbols for shared objects in some cases.
         if test -z "$MOZ_NO_WLZDEFS"; then
--- a/old-configure.in
+++ b/old-configure.in
@@ -897,18 +897,16 @@ MOZ_ARG_ENABLE_BOOL(warnings-as-errors,
 
 dnl ========================================================
 dnl GNU specific defaults
 dnl ========================================================
 if test "$GNU_CC"; then
     MMX_FLAGS="-mmmx"
     SSE_FLAGS="-msse"
     SSE2_FLAGS="-msse2"
-    # We use C99.
-    CFLAGS="$CFLAGS -std=gnu99"
     # FIXME: Let us build with strict aliasing. bug 414641.
     CFLAGS="$CFLAGS -fno-strict-aliasing"
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(DSO_SONAME) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(DSO_SONAME) -o $@'
     WARNINGS_AS_ERRORS='-Werror'
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-fPIC'
     ASFLAGS="$ASFLAGS -fPIC"