Bug 1292046 - Add a check that the compiler works with -c out of the box. r=chmanchester

The base compiler check in python configure does some preprocessing,
which ensures the compiler works to some extent. Autoconf used to have
a more complete test, doing a compile/link. We do have plenty of tests
afterwards that do that anyways, but it's better if we fail early if
the toolchain fails somehow.

This refactors try_compile such that the *_compiler variable themselves
can be used to trigger compiler tests. Eventually, we'll want something
similar for preprocessing and possibly other invocations.

This also removes similar tests from build/autoconf/toolchain.m4.

--HG--
extra : rebase_source : c60d1d6e39b6bd2a377516687affd9b8932ebc12
This commit is contained in:
Mike Hommey 2016-08-04 15:51:47 +09:00
parent 6bdf5756ec
commit afa6c4d5f3
6 changed files with 97 additions and 68 deletions

View File

@ -49,34 +49,11 @@ AC_DEFUN([MOZ_CROSS_COMPILER],
[ [
echo "cross compiling from $host to $target" echo "cross compiling from $host to $target"
_SAVE_CC="$CC"
_SAVE_CFLAGS="$CFLAGS"
_SAVE_LDFLAGS="$LDFLAGS"
if test -z "$HOST_AR_FLAGS"; then if test -z "$HOST_AR_FLAGS"; then
HOST_AR_FLAGS="$AR_FLAGS" HOST_AR_FLAGS="$AR_FLAGS"
fi fi
AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :) AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :)
AC_CHECK_PROGS(HOST_AR, $HOST_AR ar, ar, :) AC_CHECK_PROGS(HOST_AR, $HOST_AR ar, ar, :)
CC="$HOST_CC"
CFLAGS="$HOST_CFLAGS"
LDFLAGS="$HOST_LDFLAGS"
AC_MSG_CHECKING([whether the host c compiler ($HOST_CC $HOST_CFLAGS $HOST_LDFLAGS) works])
AC_TRY_COMPILE([], [return(0);],
[ac_cv_prog_hostcc_works=1 AC_MSG_RESULT([yes])],
AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CC cannot create executables.]) )
CC="$HOST_CXX"
CFLAGS="$HOST_CXXFLAGS"
AC_MSG_CHECKING([whether the host c++ compiler ($HOST_CXX $HOST_CXXFLAGS $HOST_LDFLAGS) works])
AC_TRY_COMPILE([], [return(0);],
[ac_cv_prog_hostcxx_works=1 AC_MSG_RESULT([yes])],
AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CXX cannot create executables.]) )
CC=$_SAVE_CC
CFLAGS=$_SAVE_CFLAGS
LDFLAGS=$_SAVE_LDFLAGS
dnl AC_CHECK_PROGS manually goes through $PATH, and as such fails to handle dnl AC_CHECK_PROGS manually goes through $PATH, and as such fails to handle
dnl absolute or relative paths. Relative paths wouldn't work anyways, but dnl absolute or relative paths. Relative paths wouldn't work anyways, but

View File

@ -60,7 +60,7 @@ def checking(what, callback=None):
log.info('no') log.info('no')
else: else:
log.info(display_ret) log.info(display_ret)
if error: if error is not None:
die(error) die(error)
return ret return ret
return wrapped return wrapped

View File

@ -18,47 +18,14 @@
# - `check_msg` is the message to be printed to accompany compiling the test # - `check_msg` is the message to be printed to accompany compiling the test
# program. # program.
@template @template
@imports('textwrap')
def try_compile(includes=None, body='', language='C++', flags=None, check_msg=None): def try_compile(includes=None, body='', language='C++', flags=None, check_msg=None):
includes = includes or [] compiler = {
source_lines = ['#include <%s>' % f for f in includes] 'C': c_compiler,
source = '\n'.join(source_lines) + '\n' 'C++': cxx_compiler,
source += textwrap.dedent('''\ }[language]
int
main(void)
{
%s
;
return 0;
}
''' % body)
if check_msg: return compiler.try_compile(includes, body, flags, check_msg)
def checking_fn(fn):
return checking(check_msg, callback=lambda r: r is not None)(fn)
else:
def checking_fn(fn):
return fn
def get_flags():
if flags:
return flags[:]
@depends(cxx_compiler, c_compiler, extra_toolchain_flags)
@checking_fn
def check(cxx_info, c_info, extra_flags):
flags = get_flags() or []
flags += extra_flags
flags.append('-c')
info = {
'C': c_info,
'C++': cxx_info,
}[language]
return try_invoke_compiler(info.wrapper + [info.compiler] + info.flags,
language, source, flags,
onerror=lambda: None)
return check
# Checks for the presence of the given header on the target system by compiling # Checks for the presence of the given header on the target system by compiling
# a test program including that header. The return value of the template is a # a test program including that header. The return value of the template is a

View File

@ -0,0 +1,65 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
@template
@imports('textwrap')
@imports(_from='mozbuild.configure', _import='DependsFunction')
def compiler_class(compiler):
class Compiler(DependsFunction):
# Generates a test program and attempts to compile it. In case of
# failure, the resulting check will return None. If the test program
# succeeds, it will return the output of the test program.
# - `includes` are the includes (as file names) that will appear at the
# top of the generated test program.
# - `body` is the code that will appear in the main function of the
# generated test program. `return 0;` is appended to the function
# body automatically.
# - `flags` are the flags to be passed to the compiler, in addition to
# `-c`.
# - `check_msg` is the message to be printed to accompany compiling the
# test program.
def try_compile(self, includes=None, body='', flags=None,
check_msg=None, onerror=lambda: None):
includes = includes or []
source_lines = ['#include <%s>' % f for f in includes]
source = '\n'.join(source_lines) + '\n'
source += textwrap.dedent('''\
int
main(void)
{
%s
;
return 0;
}
''' % body)
if check_msg:
def checking_fn(fn):
return checking(check_msg,
callback=lambda r: r is not None)(fn)
else:
def checking_fn(fn):
return fn
def get_flags():
if flags:
return flags[:]
@depends(self, extra_toolchain_flags)
@checking_fn
def func(compiler, extra_flags):
flags = get_flags() or []
flags += extra_flags
flags.append('-c')
return try_invoke_compiler(
compiler.wrapper + [compiler.compiler] + compiler.flags,
compiler.language, source, flags, onerror=onerror)
return func
compiler.__class__ = Compiler
return compiler

View File

@ -171,6 +171,8 @@ add_old_configure_assignment('TOOLCHAIN_PREFIX', toolchain_prefix)
# Compilers # Compilers
# ============================================================== # ==============================================================
include('compilers-util.configure')
def try_preprocess(compiler, language, source): def try_preprocess(compiler, language, source):
return try_invoke_compiler(compiler, language, source, ['-E']) return try_invoke_compiler(compiler, language, source, ['-E'])
@ -533,7 +535,7 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
'C++': lambda: default_cxx_compilers(c_compiler), 'C++': lambda: default_cxx_compilers(c_compiler),
}[language]() }[language]()
what='the %s %s compiler' % (host_or_target_str, language), what='the %s %s compiler' % (host_or_target_str, language)
option(env=var, nargs=1, help='Path to %s' % what) option(env=var, nargs=1, help='Path to %s' % what)
@ -736,6 +738,15 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
add_old_configure_assignment( add_old_configure_assignment(
'%s_VERSION' % var, delayed_getattr(valid_compiler, 'version')) '%s_VERSION' % var, delayed_getattr(valid_compiler, 'version'))
valid_compiler = compiler_class(valid_compiler)
def compiler_error():
raise FatalCheckError('Failed compiling a simple %s source with %s'
% (language, what))
valid_compiler.try_compile(check_msg='%s works' % what,
onerror=compiler_error)
return valid_compiler return valid_compiler

View File

@ -7,6 +7,7 @@ from __future__ import absolute_import, print_function, unicode_literals
import os import os
import textwrap import textwrap
import unittest import unittest
import mozpack.path as mozpath
from StringIO import StringIO from StringIO import StringIO
@ -44,32 +45,40 @@ class TestHeaderChecks(unittest.TestCase):
expected_flags=expected_flags), expected_flags=expected_flags),
} }
base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
mock_compiler_defs = textwrap.dedent('''\ mock_compiler_defs = textwrap.dedent('''\
@depends('--help')
def extra_toolchain_flags(_):
return []
include('%s/compilers-util.configure')
@compiler_class
@depends('--help') @depends('--help')
def c_compiler(_): def c_compiler(_):
return namespace( return namespace(
flags=[], flags=[],
compiler=os.path.abspath('/usr/bin/mockcc'), compiler=os.path.abspath('/usr/bin/mockcc'),
wrapper=[], wrapper=[],
language='C',
) )
@compiler_class
@depends('--help') @depends('--help')
def cxx_compiler(_): def cxx_compiler(_):
return namespace( return namespace(
flags=[], flags=[],
compiler=os.path.abspath('/usr/bin/mockcc'), compiler=os.path.abspath('/usr/bin/mockcc'),
wrapper=[], wrapper=[],
language='C++',
) )
@depends('--help') ''' % mozpath.normsep(base_dir))
def extra_toolchain_flags(_):
return []
''')
config = {} config = {}
out = StringIO() out = StringIO()
sandbox = ConfigureTestSandbox(paths, config, {}, ['/bin/configure'], sandbox = ConfigureTestSandbox(paths, config, {}, ['/bin/configure'],
out, out) out, out)
base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
sandbox.include_file(os.path.join(base_dir, 'util.configure')) sandbox.include_file(os.path.join(base_dir, 'util.configure'))
sandbox.include_file(os.path.join(base_dir, 'checks.configure')) sandbox.include_file(os.path.join(base_dir, 'checks.configure'))
exec_(mock_compiler_defs, sandbox) exec_(mock_compiler_defs, sandbox)