diff --git a/build/moz.configure/android-ndk.configure b/build/moz.configure/android-ndk.configure index ddbc46e6d616..d88cf56dbb9e 100644 --- a/build/moz.configure/android-ndk.configure +++ b/build/moz.configure/android-ndk.configure @@ -85,6 +85,13 @@ def android_platform(target, android_version, ndk): add_old_configure_assignment('android_platform', android_platform) +@depends(android_platform) +def extra_toolchain_flags(platform_dir): + if not platform_dir: + return [] + return ['-idirafter', + os.path.join(platform_dir, 'usr', 'include')] + @depends(target, host, ndk, '--with-android-toolchain', '--with-android-gnu-compiler-version') @checking('for the Android toolchain directory', lambda x: x or 'not found') diff --git a/build/moz.configure/compilechecks.configure b/build/moz.configure/compilechecks.configure index 2daa1f302bc2..4d128666942d 100644 --- a/build/moz.configure/compilechecks.configure +++ b/build/moz.configure/compilechecks.configure @@ -32,8 +32,6 @@ def try_compile(includes=None, body='', language='C++', flags=None, check_msg=No return 0; } ''' % body) - flags = flags or [] - flags.append('-c') if check_msg: def checking_fn(fn): @@ -42,9 +40,17 @@ def try_compile(includes=None, body='', language='C++', flags=None, check_msg=No def checking_fn(fn): return fn - @depends(cxx_compiler, c_compiler) + def get_flags(): + if flags: + return flags[:] + + @depends(cxx_compiler, c_compiler, extra_toolchain_flags) @checking_fn - def check(cxx_info, c_info): + 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, @@ -53,3 +59,52 @@ def try_compile(includes=None, body='', language='C++', flags=None, check_msg=No 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. +# - `header` is the header, as a file name, to check for. +# - `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`. +# - `includes` are additional includes, as file names, to appear before the +# header checked for. +# - `when` is a depends function that if present will make performing the check +# conditional on the value of that function. +@template +def check_header(header, language='C++', flags=None, includes=None, when=None): + when = when or depends('--help')(lambda _: True) + + if includes: + includes = includes[:] + else: + includes = [] + includes.append(header) + + @depends_when(try_compile(includes=includes, language=language, flags=flags, + check_msg='for %s' % header), when=when) + def have_header(value): + if value is not None: + return True + header_var = 'HAVE_%s' % (header.upper() + .replace('-', '_') + .replace('/', '_') + .replace('.', '_')) + set_define(header_var, have_header) + return have_header + +# A convenience wrapper for check_header for checking multiple headers. +# returns an array of the resulting checks in order corresponding to the +# provided headers. +# - `headers` are the headers to be checked. +# - `kwargs` are keyword arguments passed verbatim to check_header. +@template +def check_headers(*headers, **kwargs): + checks = [] + for header in headers: + checks.append(check_header(header, **kwargs)) + return checks diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure index 113fd5df22d3..564a81e1b610 100644 --- a/build/moz.configure/toolchain.configure +++ b/build/moz.configure/toolchain.configure @@ -55,6 +55,11 @@ set_config('HAVE_YASM', have_yasm) # Until the YASM variable is not necessary in old-configure. add_old_configure_assignment('YASM', have_yasm) +@depends('--help') +def extra_toolchain_flags(_): + # This value will be overriden for android builds, where + # extra flags are required to do basic checks. + return [] # Android NDK # ============================================================== diff --git a/python/mozbuild/mozbuild/test/configure/test_header_checks.py b/python/mozbuild/mozbuild/test/configure/test_header_checks.py index 502c4fba7360..19f005b9b8ef 100644 --- a/python/mozbuild/mozbuild/test/configure/test_header_checks.py +++ b/python/mozbuild/mozbuild/test/configure/test_header_checks.py @@ -60,6 +60,9 @@ class TestHeaderChecks(unittest.TestCase): compiler=os.path.abspath('/usr/bin/mockcc'), wrapper=[], ) + @depends('--help') + def extra_toolchain_flags(_): + return [] ''') config = {} @@ -150,5 +153,102 @@ class TestHeaderChecks(unittest.TestCase): checking whether -fknown-flag works... yes ''')) + def test_check_header(self): + expected_test_content = textwrap.dedent('''\ + #include + int + main(void) + { + + ; + return 0; + } + ''') + + cmd = textwrap.dedent('''\ + check_header('foo.h') + ''') + + config, out, status = self.do_compile_test(cmd, + expected_test_content=expected_test_content) + self.assertEqual(status, 0) + self.assertEqual(config, {'DEFINES': {'HAVE_FOO_H': True}}) + self.assertEqual(out, textwrap.dedent('''\ + checking for foo.h... yes + ''')) + + def test_check_header_include(self): + expected_test_content = textwrap.dedent('''\ + #include + #include + #include + int + main(void) + { + + ; + return 0; + } + ''') + + cmd = textwrap.dedent('''\ + have_foo = check_header('foo.h', includes=['std.h', 'bar.h']) + set_config('HAVE_FOO_H', have_foo) + ''') + + config, out, status = self.do_compile_test(cmd, + expected_test_content=expected_test_content) + + self.assertEqual(status, 0) + self.assertEqual(config, { + 'HAVE_FOO_H': True, + 'DEFINES': { + 'HAVE_FOO_H': True, + } + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for foo.h... yes + ''')) + + def test_check_headers_multiple(self): + cmd = textwrap.dedent('''\ + baz_bar, quux_bar = check_headers('baz/foo-bar.h', 'baz-quux/foo-bar.h') + set_config('HAVE_BAZ_BAR', baz_bar) + set_config('HAVE_QUUX_BAR', quux_bar) + ''') + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'HAVE_BAZ_BAR': True, + 'HAVE_QUUX_BAR': True, + 'DEFINES': { + 'HAVE_BAZ_FOO_BAR_H': True, + 'HAVE_BAZ_QUUX_FOO_BAR_H': True, + } + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for baz/foo-bar.h... yes + checking for baz-quux/foo-bar.h... yes + ''')) + + def test_check_headers_not_found(self): + + cmd = textwrap.dedent('''\ + baz_bar, quux_bar = check_headers('baz/foo-bar.h', 'baz-quux/foo-bar.h', + flags=['-funknown-flag']) + set_config('HAVE_BAZ_BAR', baz_bar) + set_config('HAVE_QUUX_BAR', quux_bar) + ''') + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, {'DEFINES': {}}) + self.assertEqual(out, textwrap.dedent('''\ + checking for baz/foo-bar.h... no + checking for baz-quux/foo-bar.h... no + ''')) + + if __name__ == '__main__': main()