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.
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -44,44 +44,21 @@ fi
AC_SUBST(CLANG_CXX)
AC_SUBST(CLANG_CL)
])
AC_DEFUN([MOZ_CROSS_COMPILER],
[
echo "cross compiling from $host to $target"
-_SAVE_CC="$CC"
-_SAVE_CFLAGS="$CFLAGS"
-_SAVE_LDFLAGS="$LDFLAGS"
-
if test -z "$HOST_AR_FLAGS"; then
HOST_AR_FLAGS="$AR_FLAGS"
fi
AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :)
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 absolute or relative paths. Relative paths wouldn't work anyways, but
dnl absolute paths would. Trick AC_CHECK_PROGS into working in that case by
dnl adding / to PATH. This is temporary until this moves to moz.configure
dnl (soon).
_SAVE_PATH=$PATH
case "${TOOLCHAIN_PREFIX}" in
--- a/build/moz.configure/checks.configure
+++ b/build/moz.configure/checks.configure
@@ -55,17 +55,17 @@ def checking(what, callback=None):
error = e.message
display_ret = callback(ret) if callback else ret
if display_ret is True:
log.info('yes')
elif display_ret is False or display_ret is None:
log.info('no')
else:
log.info(display_ret)
- if error:
+ if error is not None:
die(error)
return ret
return wrapped
return decorator
# Template to check for programs in $PATH.
# - `var` is the name of the variable that will be set with `set_config` when
--- a/build/moz.configure/compilechecks.configure
+++ b/build/moz.configure/compilechecks.configure
@@ -13,57 +13,24 @@
# - `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.
# - `language` is the language selection, so that the appropriate compiler is
# used.
# - `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.
@template
-@imports('textwrap')
def try_compile(includes=None, body='', language='C++', flags=None, check_msg=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)
+ compiler = {
+ 'C': c_compiler,
+ 'C++': cxx_compiler,
+ }[language]
- 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[:]
+ return compiler.try_compile(includes, body, flags, check_msg)
- @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
# a test program including that header. The return value of the template is a
# check function returning True if the header is present, and None if it is not.
# The value of this check function is also used to set a variable (with set_define)
# corresponding to the checked header. For instance, HAVE_MALLOC_H will be set in
# defines if check_header if called with 'malloc.h' as input and malloc.h is
# present on the target.
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/compilers-util.configure
@@ -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
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -166,16 +166,18 @@ def toolchain_prefix(value, target, host
return '%s-' % target.toolchain
set_config('TOOLCHAIN_PREFIX', toolchain_prefix)
add_old_configure_assignment('TOOLCHAIN_PREFIX', toolchain_prefix)
# Compilers
# ==============================================================
+include('compilers-util.configure')
+
def try_preprocess(compiler, language, source):
return try_invoke_compiler(compiler, language, source, ['-E'])
@imports(_from='mozbuild.configure.constants', _import='CompilerType')
@imports(_from='mozbuild.configure.constants',
_import='CPU_preprocessor_checks')
@imports(_from='mozbuild.configure.constants',
_import='kernel_preprocessor_checks')
@@ -528,17 +530,17 @@ def compiler(language, host_or_target, c
('C++', host): 'HOST_CXX',
}[language, host_or_target]
default_compilers = {
'C': lambda: default_c_compilers(host_or_target),
'C++': lambda: default_cxx_compilers(c_compiler),
}[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)
# Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
# HOST_CXX variables.
@depends_if(var)
@imports(_from='itertools', _import='takewhile')
@imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
@@ -731,16 +733,25 @@ def compiler(language, host_or_target, c
# Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
# old-configure to do some of its still existing checks.
if language == 'C':
add_old_configure_assignment(
'%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
add_old_configure_assignment(
'%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
c_compiler = compiler('C', target)
cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
host_c_compiler = compiler('C', host, other_compiler=c_compiler)
host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
other_compiler=cxx_compiler,
--- a/python/mozbuild/mozbuild/test/configure/test_header_checks.py
+++ b/python/mozbuild/mozbuild/test/configure/test_header_checks.py
@@ -2,16 +2,17 @@
# 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/.
from __future__ import absolute_import, print_function, unicode_literals
import os
import textwrap
import unittest
+import mozpack.path as mozpath
from StringIO import StringIO
from buildconfig import topsrcdir
from common import ConfigureTestSandbox
from mozbuild.util import exec_
from mozunit import main
from test_toolchain_helpers import FakeCompiler
@@ -39,42 +40,50 @@ class TestHeaderChecks(unittest.TestCase
expected_flags=None):
paths = {
os.path.abspath('/usr/bin/mockcc'): self.get_mock_compiler(
expected_test_content=expected_test_content,
expected_flags=expected_flags),
}
+ base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
+
mock_compiler_defs = textwrap.dedent('''\
@depends('--help')
+ def extra_toolchain_flags(_):
+ return []
+
+ include('%s/compilers-util.configure')
+
+ @compiler_class
+ @depends('--help')
def c_compiler(_):
return namespace(
flags=[],
compiler=os.path.abspath('/usr/bin/mockcc'),
wrapper=[],
+ language='C',
)
+ @compiler_class
@depends('--help')
def cxx_compiler(_):
return namespace(
flags=[],
compiler=os.path.abspath('/usr/bin/mockcc'),
wrapper=[],
+ language='C++',
)
- @depends('--help')
- def extra_toolchain_flags(_):
- return []
- ''')
+ ''' % mozpath.normsep(base_dir))
config = {}
out = StringIO()
sandbox = ConfigureTestSandbox(paths, config, {}, ['/bin/configure'],
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, 'checks.configure'))
exec_(mock_compiler_defs, sandbox)
sandbox.include_file(os.path.join(base_dir, 'compilechecks.configure'))
status = 0
try:
exec_(command, sandbox)