Bug 1412983 - part 6 - compile headers for cross-compilers during configure; r=ted.mielczarek
authorNathan Froyd <froydnj@mozilla.com>
Wed, 15 Nov 2017 10:12:22 -0500
changeset 391925 b0eedb4c140bcafafc82475b352355a7b5affd3b
parent 391924 0e6f349c815e0424ede297c7269914330ad986a5
child 391926 ff6af466f196b774e339f3814653d8eac07c9107
push id97367
push usernfroyd@mozilla.com
push dateWed, 15 Nov 2017 15:12:44 +0000
treeherdermozilla-inbound@b0eedb4c140b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1412983 - part 6 - compile headers for cross-compilers during configure; r=ted.mielczarek Our toolchain detection logic checks whether we can reuse the target C (resp. C++) compiler for the host compiler. This is generally only applicable in the not-cross-compiling case, but we had special logic to check for clang in the cross-compiling case and accept it, as clang is able to generate code for multiple architectures from a single compiler binary. Our recent switch to clang on Android has exposed a problem in this logic: we would never check whether the target clang, compiling for the host, could actually find the host's headers. This was especially problematic on OS X hosts, where the host clang contains special logic to grovel inside the XCode installation to find C++ headers. The clang from the NDK, however, was ignorant of the XCode installation. Therefore, the NDK clang would happily compile code for the host, even including C headers for the host, but would be hopelessly lost when it came to compiling C++ headers during the actual build. In hopes of mitigating this, we now include a check for a representative header for C and C++ when checking compilers for each of those languages. This check will detect such problems as the above, and will also alert people to potentially misconfigured compilers in other situations. We need to modify our test framework to cope with headers being included, since our mock environment isn't actually equipped with a full set of compilers and headers.
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -409,16 +409,27 @@ def get_compiler_info(compiler, language
     check += dedent('''\
         #if _MSC_VER || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
         %ENDIANNESS "little"
         #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
         %ENDIANNESS "big"
+    # Try including a header to ensure that cross-compilers are set up
+    # correctly.
+    header = {
+        'C': 'stddef.h',
+        'C++': 'cstddef',
+    }[language]
+    check += dedent('''\
+        #include <%(header)s>
+    ''' % {'header': header})
     result = try_preprocess(compiler, language, check)
     if not result:
         raise FatalCheckError(
             'Unknown compiler or compiler not supported.')
     # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
     # have non-ASCII characters. Treat the output as bytearray.
@@ -800,33 +811,37 @@ 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,
-    @depends(compiler, provided_compiler, compiler_wrapper, host_or_target)
+    @depends(compiler, provided_compiler, compiler_wrapper, host_or_target,
+             extra_toolchain_flags)
     @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,
-                       host_or_target):
+                       host_or_target, extra_flags):
         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.
             if provided_wrapper[:len(wrapper)] == wrapper:
                 provided_wrapper = provided_wrapper[len(wrapper):]
             flags = provided_compiler.flags
             flags = []
+        toolchain_flags = []
+        if host_or_target_str == 'target' and extra_flags:
+            toolchain_flags = extra_flags
         # Ideally, we'd always use the absolute path, but unfortunately, on
         # Windows, the compiler is very often in a directory containing spaces.
         # Unfortunately, due to the way autoconf does its compiler tests with
         # eval, that doesn't work out. So in that case, check that the
         # compiler can still be found in $PATH, and use the file name instead
         # of the full path.
         if quote(compiler) != compiler:
@@ -838,29 +853,30 @@ def compiler(language, host_or_target, c
                     % quote(os.path.dirname(full_path)))
             if os.path.normcase(find_program(compiler)) != os.path.normcase(
                 die('Found `%s` before `%s` in your $PATH. '
                     'Please reorder your $PATH.',
-        info = check_compiler(wrapper + [compiler] + flags, language,
-                              host_or_target)
+        info = check_compiler(wrapper + [compiler] + flags + toolchain_flags,
+                              language, host_or_target)
         # Check that the additional flags we got are enough to not require any
         # more flags. If we get an exception, just ignore it; it's liable to be
         # invalid command-line flags, which means the compiler we're checking
         # doesn't support those command-line flags and will fail one or more of
         # the checks below.
             if info.flags:
                 flags += info.flags
-                info = check_compiler(wrapper + [compiler] + flags, language,
-                                      host_or_target)
+                info = check_compiler(wrapper + [compiler] + flags +
+                                      toolchain_flags,
+                                      language, host_or_target)
         except FatalCheckError:
         if not info.target_cpu or info.target_cpu != host_or_target.cpu:
             raise FatalCheckError(
                 '%s %s compiler target CPU (%s) does not match --%s CPU (%s)'
                 % (host_or_target_str.capitalize(), language,
                    info.target_cpu or 'unknown', host_or_target_str,
--- a/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py
+++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py
@@ -56,16 +56,30 @@ class CompilerPreprocessor(Preprocessor)
             for k, v in context.iteritems()
             return Preprocessor.do_if(self, normalize_has_feature(expression),
             self.context = context
+    def do_include(self, args, filters=True):
+        # We need to check headers during normal configure to make sure the
+        # user doesn't have a misconfigured environment.  But for the purposes
+        # of tests, we want to ignore headers.
+        # stddef.h and cstddef are the headers compiler configure checks use.
+        if type(args) in (str, unicode) and '<' in args:
+            if args in ('<stddef.h>', '<cstddef>'):
+                return
+            raise Preprocessor.Error(self, 'INVALID_INCLUDE_FILE', args)
+        # Pass everything else through.
+        Preprocessor.do_include(self, args, filters)
     class Context(dict):
         def __missing__(self, key):
             return None
     def filter_c_substitution(self, line):
         def repl(matchobj):
             varname = matchobj.group('VAR')
             if varname in self.context: