Bug 1269517 - Implement try_compile for Python configure. r=glandium

MozReview-Commit-ID: AE7uRVneGXJ
This commit is contained in:
Chris Manchester 2016-07-26 15:27:19 -07:00
parent 46a1df138f
commit 414823b805
4 changed files with 212 additions and 0 deletions

View File

@ -0,0 +1,55 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
# 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.
# - `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)
flags = flags or []
flags.append('-c')
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
@depends(cxx_compiler, c_compiler)
@checking_fn
def check(cxx_info, c_info):
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

View File

@ -676,6 +676,8 @@ host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
other_compiler=cxx_compiler,
other_c_compiler=c_compiler)
include('compilechecks.configure')
@depends(c_compiler)
def default_debug_flags(compiler_info):
# Debug info is ON by default.

View File

@ -39,6 +39,7 @@ PYTHON_UNIT_TESTS += [
'mozbuild/mozbuild/test/compilation/test_warnings.py',
'mozbuild/mozbuild/test/configure/test_checks_configure.py',
'mozbuild/mozbuild/test/configure/test_configure.py',
'mozbuild/mozbuild/test/configure/test_header_checks.py',
'mozbuild/mozbuild/test/configure/test_moz_configure.py',
'mozbuild/mozbuild/test/configure/test_options.py',
'mozbuild/mozbuild/test/configure/test_toolchain_configure.py',

View File

@ -0,0 +1,154 @@
# 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/.
from __future__ import absolute_import, print_function, unicode_literals
import os
import textwrap
import unittest
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
class TestHeaderChecks(unittest.TestCase):
def get_mock_compiler(self, expected_test_content=None, expected_flags=None):
expected_flags = expected_flags or []
def mock_compiler(stdin, args):
args, test_file = args[:-1], args[-1]
self.assertIn('-c', args)
for flag in expected_flags:
self.assertIn(flag, args)
if expected_test_content:
with open(test_file) as fh:
test_content = fh.read()
self.assertEqual(test_content, expected_test_content)
return FakeCompiler()(None, args)
return mock_compiler
def do_compile_test(self, command, expected_test_content=None,
expected_flags=None):
paths = {
os.path.abspath('/usr/bin/mockcc'): self.get_mock_compiler(
expected_test_content=expected_test_content,
expected_flags=expected_flags),
}
mock_compiler_defs = textwrap.dedent('''\
@depends('--help')
def c_compiler(_):
return namespace(
flags=[],
compiler=os.path.abspath('/usr/bin/mockcc'),
wrapper=[],
)
@depends('--help')
def cxx_compiler(_):
return namespace(
flags=[],
compiler=os.path.abspath('/usr/bin/mockcc'),
wrapper=[],
)
''')
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)
sandbox.run()
except SystemExit as e:
status = e.code
return config, out.getvalue(), status
def test_try_compile_include(self):
expected_test_content = textwrap.dedent('''\
#include <foo.h>
#include <bar.h>
int
main(void)
{
;
return 0;
}
''')
cmd = textwrap.dedent('''\
try_compile(['foo.h', 'bar.h'], language='C')
''')
config, out, status = self.do_compile_test(cmd, expected_test_content)
self.assertEqual(status, 0)
self.assertEqual(config, {})
def test_try_compile_flags(self):
expected_flags = ['--extra', '--flags']
cmd = textwrap.dedent('''\
try_compile(language='C++', flags=['--flags', '--extra'])
''')
config, out, status = self.do_compile_test(cmd, expected_flags=expected_flags)
self.assertEqual(status, 0)
self.assertEqual(config, {})
def test_try_compile_failure(self):
cmd = textwrap.dedent('''\
@depends(try_compile(body='somefn();', flags=['-funknown-flag']))
def have_fn(value):
if value is not None:
return True
set_config('HAVE_SOMEFN', have_fn)
@depends(try_compile(body='anotherfn();', language='C'))
def have_another(value):
if value is not None:
return True
set_config('HAVE_ANOTHERFN', have_another)
''')
config, out, status = self.do_compile_test(cmd)
self.assertEqual(status, 0)
self.assertEqual(config, {
'HAVE_ANOTHERFN': True,
})
def test_try_compile_msg(self):
cmd = textwrap.dedent('''\
@depends(try_compile(language='C++', flags=['-fknown-flag'],
check_msg='whether -fknown-flag works'))
def known_flag(result):
if result is not None:
return True
set_config('HAVE_KNOWN_FLAG', known_flag)
''')
config, out, status = self.do_compile_test(cmd)
self.assertEqual(status, 0)
self.assertEqual(config, {'HAVE_KNOWN_FLAG': True})
self.assertEqual(out, textwrap.dedent('''\
checking whether -fknown-flag works... yes
'''))
if __name__ == '__main__':
main()