summaryrefslogtreecommitdiffstats
path: root/build/moz.configure/toolchain.configure
diff options
context:
space:
mode:
Diffstat (limited to 'build/moz.configure/toolchain.configure')
-rw-r--r--build/moz.configure/toolchain.configure910
1 files changed, 910 insertions, 0 deletions
diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure
new file mode 100644
index 000000000..8b2416152
--- /dev/null
+++ b/build/moz.configure/toolchain.configure
@@ -0,0 +1,910 @@
+# -*- 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/.
+
+# PGO
+# ==============================================================
+option(env='MOZ_PGO', help='Build with profile guided optimizations')
+
+set_config('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
+add_old_configure_assignment('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
+
+# yasm detection
+# ==============================================================
+yasm = check_prog('YASM', ['yasm'], allow_missing=True)
+
+@depends_if(yasm)
+@checking('yasm version')
+def yasm_version(yasm):
+ version = check_cmd_output(
+ yasm, '--version',
+ onerror=lambda: die('Failed to get yasm version.')
+ ).splitlines()[0].split()[1]
+ return Version(version)
+
+# Until we move all the yasm consumers out of old-configure.
+# bug 1257904
+add_old_configure_assignment('_YASM_MAJOR_VERSION',
+ delayed_getattr(yasm_version, 'major'))
+add_old_configure_assignment('_YASM_MINOR_VERSION',
+ delayed_getattr(yasm_version, 'minor'))
+
+@depends(yasm, target)
+def yasm_asflags(yasm, target):
+ if yasm:
+ asflags = {
+ ('OSX', 'x86'): '-f macho32',
+ ('OSX', 'x86_64'): '-f macho64',
+ ('WINNT', 'x86'): '-f win32',
+ ('WINNT', 'x86_64'): '-f x64',
+ }.get((target.os, target.cpu), None)
+ if asflags is None:
+ # We're assuming every x86 platform we support that's
+ # not Windows or Mac is ELF.
+ if target.cpu == 'x86':
+ asflags = '-f elf32'
+ elif target.cpu == 'x86_64':
+ asflags = '-f elf64'
+ if asflags:
+ asflags += ' -rnasm -pnasm'
+ return asflags
+
+set_config('YASM_ASFLAGS', yasm_asflags)
+
+@depends(yasm_asflags)
+def have_yasm(value):
+ if value:
+ return True
+
+set_config('HAVE_YASM', have_yasm)
+# Until the YASM variable is not necessary in old-configure.
+add_old_configure_assignment('YASM', have_yasm)
+
+# Android NDK
+# ==============================================================
+
+@depends('--disable-compile-environment', build_project, gonkdir, '--help')
+def compiling_android(compile_env, build_project, gonkdir, _):
+ return compile_env and (gonkdir or build_project in ('mobile/android', 'js'))
+
+include('android-ndk.configure', when=compiling_android)
+
+# MacOS deployment target version
+# ==============================================================
+# This needs to happen before any compilation test is done.
+
+option('--enable-macos-target', env='MACOSX_DEPLOYMENT_TARGET', nargs=1,
+ default='10.7', help='Set the minimum MacOS version needed at runtime')
+
+@depends('--enable-macos-target', target)
+@imports(_from='os', _import='environ')
+def macos_target(value, target):
+ if value and target.os == 'OSX':
+ # Ensure every compiler process we spawn uses this value.
+ environ['MACOSX_DEPLOYMENT_TARGET'] = value[0]
+ return value[0]
+ if value and value.origin != 'default':
+ die('--enable-macos-target cannot be used when targeting %s',
+ target.os)
+
+
+set_config('MACOSX_DEPLOYMENT_TARGET', macos_target)
+add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target)
+
+
+# Compiler wrappers
+# ==============================================================
+# Normally, we'd use js_option and automatically have those variables
+# propagated to js/src, but things are complicated by possible additional
+# wrappers in CC/CXX, and by other subconfigures that do not handle those
+# options and do need CC/CXX altered.
+option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
+ help='Enable compiling with wrappers such as distcc and ccache')
+
+option('--with-ccache', env='CCACHE', nargs='?',
+ help='Enable compiling with ccache')
+
+@depends_if('--with-ccache')
+def ccache(value):
+ if len(value):
+ return value
+ # If --with-ccache was given without an explicit value, we default to
+ # 'ccache'.
+ return 'ccache'
+
+ccache = check_prog('CCACHE', progs=(), input=ccache)
+
+@depends_if(ccache)
+def using_ccache(ccache):
+ return True
+
+set_config('MOZ_USING_CCACHE', using_ccache)
+
+@depends('--with-compiler-wrapper', ccache)
+@imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
+def compiler_wrapper(wrapper, ccache):
+ if wrapper:
+ raw_wrapper = wrapper[0]
+ wrapper = shell_split(raw_wrapper)
+ wrapper_program = find_program(wrapper[0])
+ if not wrapper_program:
+ die('Cannot find `%s` from the given compiler wrapper `%s`',
+ wrapper[0], raw_wrapper)
+ wrapper[0] = wrapper_program
+
+ if ccache:
+ if wrapper:
+ return tuple([ccache] + wrapper)
+ else:
+ return (ccache,)
+ elif wrapper:
+ return tuple(wrapper)
+
+add_old_configure_assignment('COMPILER_WRAPPER', compiler_wrapper)
+
+@depends_if(compiler_wrapper)
+def using_compiler_wrapper(compiler_wrapper):
+ return True
+
+set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
+
+
+# Cross-compilation related things.
+# ==============================================================
+js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
+ help='Prefix for the target toolchain')
+
+@depends('--with-toolchain-prefix', target, host, cross_compiling)
+def toolchain_prefix(value, target, host, cross_compiling):
+ if value:
+ return tuple(value)
+ if cross_compiling:
+ return ('%s-' % target.toolchain, '%s-' % target.alias)
+
+@depends(toolchain_prefix, target)
+def first_toolchain_prefix(toolchain_prefix, target):
+ # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
+ # command line/environment (in which case there's only one value in the tuple),
+ # or when cross-compiling for Android.
+ if toolchain_prefix and (target.os == 'Android' or len(toolchain_prefix) == 1):
+ return toolchain_prefix[0]
+
+set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix)
+add_old_configure_assignment('TOOLCHAIN_PREFIX', first_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')
+@imports(_from='textwrap', _import='dedent')
+def get_compiler_info(compiler, language):
+ '''Returns information about the given `compiler` (command line in the
+ form of a list or tuple), in the given `language`.
+
+ The returned information includes:
+ - the compiler type (msvc, clang-cl, clang or gcc)
+ - the compiler version
+ - the compiler supported language
+ - the compiler supported language version
+ '''
+ # Note: MSVC doesn't expose __STDC_VERSION__. It does expose __STDC__,
+ # but only when given the -Za option, which disables compiler
+ # extensions.
+ # Note: We'd normally do a version check for clang, but versions of clang
+ # in Xcode have a completely different versioning scheme despite exposing
+ # the version with the same defines.
+ # So instead, we make things such that the version is missing when the
+ # clang used is below the minimum supported version (currently clang 3.6).
+ # We then only include the version information when the C++ compiler
+ # matches the feature check, so that an unsupported version of clang would
+ # have no version number.
+ check = dedent('''\
+ #if defined(_MSC_VER)
+ #if defined(__clang__)
+ %COMPILER "clang-cl"
+ %VERSION _MSC_FULL_VER
+ #else
+ %COMPILER "msvc"
+ %VERSION _MSC_FULL_VER
+ #endif
+ #elif defined(__clang__)
+ %COMPILER "clang"
+ # if !__cplusplus || __has_feature(cxx_alignof)
+ %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
+ # endif
+ #elif defined(__GNUC__)
+ %COMPILER "gcc"
+ %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
+ #endif
+
+ #if __cplusplus
+ %cplusplus __cplusplus
+ #elif __STDC_VERSION__
+ %STDC_VERSION __STDC_VERSION__
+ #elif __STDC__
+ %STDC_VERSION 198900L
+ #endif
+ ''')
+
+ # While we're doing some preprocessing, we might as well do some more
+ # preprocessor-based tests at the same time, to check the toolchain
+ # matches what we want.
+ for name, preprocessor_checks in (
+ ('CPU', CPU_preprocessor_checks),
+ ('KERNEL', kernel_preprocessor_checks),
+ ):
+ for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
+ check += dedent('''\
+ #%(if)s %(condition)s
+ %%%(name)s "%(value)s"
+ ''' % {
+ 'if': 'elif' if n else 'if',
+ 'condition': condition,
+ 'name': name,
+ 'value': value,
+ })
+ check += '#endif\n'
+
+ # Also check for endianness. The advantage of living in modern times is
+ # that all the modern compilers we support now have __BYTE_ORDER__ defined
+ # by the preprocessor, except MSVC, which only supports little endian.
+ check += dedent('''\
+ #if _MSC_VER || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ %ENDIANNESS "little"
+ #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ %ENDIANNESS "big"
+ #endif
+ ''')
+
+ 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.
+ data = {}
+ for line in result.splitlines():
+ if line.startswith(b'%'):
+ k, _, v = line.partition(' ')
+ k = k.lstrip('%')
+ data[k] = v.replace(' ', '').lstrip('"').rstrip('"')
+ log.debug('%s = %s', k, data[k])
+
+ try:
+ type = CompilerType(data['COMPILER'])
+ except:
+ raise FatalCheckError(
+ 'Unknown compiler or compiler not supported.')
+
+ cplusplus = int(data.get('cplusplus', '0L').rstrip('L'))
+ stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L'))
+
+ version = data.get('VERSION')
+ if version and type in ('msvc', 'clang-cl'):
+ msc_ver = version
+ version = msc_ver[0:2]
+ if len(msc_ver) > 2:
+ version += '.' + msc_ver[2:4]
+ if len(msc_ver) > 4:
+ version += '.' + msc_ver[4:]
+
+ if version:
+ version = Version(version)
+
+ return namespace(
+ type=type,
+ version=version,
+ cpu=data.get('CPU'),
+ kernel=data.get('KERNEL'),
+ endianness=data.get('ENDIANNESS'),
+ language='C++' if cplusplus else 'C',
+ language_version=cplusplus if cplusplus else stdc_version,
+ )
+
+
+@imports(_from='mozbuild.shellutil', _import='quote')
+def check_compiler(compiler, language, target):
+ info = get_compiler_info(compiler, language)
+
+ flags = []
+
+ def append_flag(flag):
+ if flag not in flags:
+ if info.type == 'clang-cl':
+ flags.append('-Xclang')
+ flags.append(flag)
+
+ # Check language standards
+ # --------------------------------------------------------------------
+ if language != info.language:
+ raise FatalCheckError(
+ '`%s` is not a %s compiler.' % (quote(*compiler), language))
+
+ # Note: We do a strict version check because there sometimes are backwards
+ # incompatible changes in the standard, and not all code that compiles as
+ # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
+ # example)
+ if info.language == 'C' and info.language_version != 199901:
+ if info.type in ('clang-cl', 'clang', 'gcc'):
+ append_flag('-std=gnu99')
+
+ # Note: MSVC, while supporting C++11, still reports 199711L for __cplusplus.
+ # Note: this is a strict version check because we used to always add
+ # -std=gnu++11.
+ if info.language == 'C++':
+ if info.type in ('clang', 'gcc') and info.language_version != 201103:
+ append_flag('-std=gnu++11')
+ # MSVC 2015 headers include C++14 features, but don't guard them
+ # with appropriate checks.
+ if info.type == 'clang-cl' and info.language_version != 201402:
+ append_flag('-std=c++14')
+
+ # We force clang-cl to emulate Visual C++ 2015 Update 3 with fallback to
+ # cl.exe.
+ if info.type == 'clang-cl' and info.version != '19.00.24213':
+ # Those flags are direct clang-cl flags that don't need -Xclang, add
+ # them directly.
+ flags.append('-fms-compatibility-version=19.00.24213')
+ flags.append('-fallback')
+
+ # Check compiler target
+ # --------------------------------------------------------------------
+ if not info.cpu or info.cpu != target.cpu:
+ if info.type == 'clang':
+ append_flag('--target=%s' % target.toolchain)
+ elif info.type == 'gcc':
+ same_arch_different_bits = (
+ ('x86', 'x86_64'),
+ ('ppc', 'ppc64'),
+ ('sparc', 'sparc64'),
+ )
+ if (target.cpu, info.cpu) in same_arch_different_bits:
+ append_flag('-m32')
+ elif (info.cpu, target.cpu) in same_arch_different_bits:
+ append_flag('-m64')
+
+ if not info.kernel or info.kernel != target.kernel:
+ if info.type == 'clang':
+ append_flag('--target=%s' % target.toolchain)
+
+ if not info.endianness or info.endianness != target.endianness:
+ if info.type == 'clang':
+ append_flag('--target=%s' % target.toolchain)
+
+ return namespace(
+ type=info.type,
+ version=info.version,
+ target_cpu=info.cpu,
+ target_kernel=info.kernel,
+ target_endianness=info.endianness,
+ flags=flags,
+ )
+
+
+@imports(_from='collections', _import='defaultdict')
+@imports(_from='__builtin__', _import='sorted')
+def get_vc_paths(base):
+ vc = defaultdict(lambda: defaultdict(dict))
+ subkey = r'Microsoft\VisualStudio\VC\*\*\*\Compiler'
+ for v, h, t, p in get_registry_values(base + '\\' + subkey):
+ vc[v][h][t] = p
+ if not vc:
+ return
+ version, data = sorted(vc.iteritems(), key=lambda x: Version(x[0]))[-1]
+ return data
+
+
+@depends(host)
+@imports('platform')
+def vc_compiler_path(host):
+ if host.kernel != 'WINNT':
+ return
+ vc_host = {
+ 'x86': 'x86',
+ 'AMD64': 'x64',
+ }.get(platform.machine())
+ if vc_host is None:
+ return
+ vc_target = {
+ 'x86': 'x86',
+ 'x86_64': 'x64',
+ 'arm': 'arm',
+ }.get(host.cpu)
+ if vc_target is None:
+ return
+
+ base_key = r'HKEY_LOCAL_MACHINE\SOFTWARE'
+ data = get_vc_paths(base_key)
+ if not data:
+ data = get_vc_paths(base_key + r'\Wow6432Node')
+ if not data:
+ return
+
+ path = data.get(vc_host, {}).get(vc_target)
+ if not path and vc_host == 'x64':
+ vc_host = 'x86'
+ path = data.get(vc_host, {}).get(vc_target)
+ if not path:
+ return
+ path = os.path.dirname(path)
+ if vc_host != vc_target:
+ other_path = data.get(vc_host, {}).get(vc_host)
+ if other_path:
+ return (path, os.path.dirname(other_path))
+ return (path,)
+
+
+@depends(vc_compiler_path)
+@imports('os')
+def toolchain_search_path(vc_compiler_path):
+ if vc_compiler_path:
+ result = [os.environ.get('PATH')]
+ result.extend(vc_compiler_path)
+ # We're going to alter PATH for good in windows.configure, but we also
+ # need to do it for the valid_compiler() check below.
+ os.environ['PATH'] = os.pathsep.join(result)
+ return result
+
+
+@template
+def default_c_compilers(host_or_target):
+ '''Template defining the set of default C compilers for the host and
+ target platforms.
+ `host_or_target` is either `host` or `target` (the @depends functions
+ from init.configure.
+ '''
+ assert host_or_target in (host, target)
+
+ @depends(host_or_target, target, toolchain_prefix)
+ def default_c_compilers(host_or_target, target, toolchain_prefix):
+ gcc = ('gcc',)
+ if toolchain_prefix and host_or_target is target:
+ gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc
+
+ if host_or_target.kernel == 'WINNT':
+ return ('cl', 'clang-cl') + gcc + ('clang',)
+ if host_or_target.kernel == 'Darwin':
+ return ('clang',)
+ return gcc + ('clang',)
+
+ return default_c_compilers
+
+
+@template
+def default_cxx_compilers(c_compiler):
+ '''Template defining the set of default C++ compilers for the host and
+ target platforms.
+ `c_compiler` is the @depends function returning a Compiler instance for
+ the desired platform.
+
+ Because the build system expects the C and C++ compilers to be from the
+ same compiler suite, we derive the default C++ compilers from the C
+ compiler that was found if none was provided.
+ '''
+
+ @depends(c_compiler)
+ def default_cxx_compilers(c_compiler):
+ dir = os.path.dirname(c_compiler.compiler)
+ file = os.path.basename(c_compiler.compiler)
+
+ if c_compiler.type == 'gcc':
+ return (os.path.join(dir, file.replace('gcc', 'g++')),)
+
+ if c_compiler.type == 'clang':
+ return (os.path.join(dir, file.replace('clang', 'clang++')),)
+
+ return (c_compiler.compiler,)
+
+ return default_cxx_compilers
+
+
+@template
+def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
+ other_c_compiler=None):
+ '''Template handling the generic base checks for the compiler for the
+ given `language` on the given platform (`host_or_target`).
+ `host_or_target` is either `host` or `target` (the @depends functions
+ from init.configure.
+ When the language is 'C++', `c_compiler` is the result of the `compiler`
+ template for the language 'C' for the same `host_or_target`.
+ When `host_or_target` is `host`, `other_compiler` is the result of the
+ `compiler` template for the same `language` for `target`.
+ When `host_or_target` is `host` and the language is 'C++',
+ `other_c_compiler` is the result of the `compiler` template for the
+ language 'C' for `target`.
+ '''
+ assert host_or_target in (host, target)
+ assert language in ('C', 'C++')
+ assert language == 'C' or c_compiler
+ assert host_or_target == target or other_compiler
+ assert language == 'C' or host_or_target == target or other_c_compiler
+
+ host_or_target_str = {
+ host: 'host',
+ target: 'target',
+ }[host_or_target]
+
+ var = {
+ ('C', target): 'CC',
+ ('C++', target): 'CXX',
+ ('C', host): 'HOST_CC',
+ ('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)
+
+ 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')
+ def provided_compiler(cmd):
+ # Historically, the compiler variables have contained more than the
+ # path to the compiler itself. So for backwards compatibility, try to
+ # find what is what in there, assuming the first dash-prefixed item is
+ # a compiler option, the item before that is the compiler, and anything
+ # before that is a compiler wrapper.
+ cmd = shell_split(cmd[0])
+
+ without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd))
+
+ return namespace(
+ wrapper=without_flags[:-1],
+ compiler=without_flags[-1],
+ flags=cmd[len(without_flags):],
+ )
+
+ # Derive the host compiler from the corresponding target compiler when no
+ # explicit compiler was given and we're not cross compiling. For the C++
+ # compiler, though, prefer to derive from the host C compiler when it
+ # doesn't match the target C compiler.
+ # As a special case, since clang supports all kinds of targets in the same
+ # executable, when cross compiling with clang, default to the same compiler
+ # as the target compiler, resetting flags.
+ if host_or_target == host:
+ args = (c_compiler, other_c_compiler) if other_c_compiler else ()
+
+ @depends(provided_compiler, other_compiler, cross_compiling, *args)
+ def provided_compiler(value, other_compiler, cross_compiling, *args):
+ if value:
+ return value
+ c_compiler, other_c_compiler = args if args else (None, None)
+ if not cross_compiling and c_compiler == other_c_compiler:
+ return other_compiler
+ if cross_compiling and other_compiler.type == 'clang':
+ return namespace(**{
+ k: [] if k == 'flags' else v
+ for k, v in other_compiler.__dict__.iteritems()
+ })
+
+ # 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,
+ input=delayed_getattr(provided_compiler, 'compiler'),
+ paths=toolchain_search_path)
+
+ @depends(compiler, provided_compiler, compiler_wrapper, host_or_target)
+ @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):
+ 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):]
+ wrapper.extend(provided_wrapper)
+ flags = provided_compiler.flags
+ else:
+ 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:
+ full_path = os.path.abspath(compiler)
+ compiler = os.path.basename(compiler)
+ found_compiler = find_program(compiler)
+ if not found_compiler:
+ die('%s is not in your $PATH'
+ % quote(os.path.dirname(full_path)))
+ if os.path.normcase(find_program(compiler)) != os.path.normcase(
+ full_path):
+ die('Found `%s` before `%s` in your $PATH. '
+ 'Please reorder your $PATH.',
+ quote(os.path.dirname(found_compiler)),
+ quote(os.path.dirname(full_path)))
+
+ info = check_compiler(wrapper + [compiler] + flags, language,
+ host_or_target)
+
+ # Check that the additional flags we got are enough to not require any
+ # more flags.
+ if info.flags:
+ flags += info.flags
+ info = check_compiler(wrapper + [compiler] + flags, language,
+ host_or_target)
+
+ 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,
+ host_or_target.raw_cpu))
+
+ if not info.target_kernel or (info.target_kernel !=
+ host_or_target.kernel):
+ raise FatalCheckError(
+ '%s %s compiler target kernel (%s) does not match --%s kernel (%s)'
+ % (host_or_target_str.capitalize(), language,
+ info.target_kernel or 'unknown', host_or_target_str,
+ host_or_target.kernel))
+
+ if not info.target_endianness or (info.target_endianness !=
+ host_or_target.endianness):
+ raise FatalCheckError(
+ '%s %s compiler target endianness (%s) does not match --%s '
+ 'endianness (%s)'
+ % (host_or_target_str.capitalize(), language,
+ info.target_endianness or 'unknown', host_or_target_str,
+ host_or_target.endianness))
+
+ if info.flags:
+ raise FatalCheckError(
+ 'Unknown compiler or compiler not supported.')
+
+ # Compiler version checks
+ # ===================================================
+ # Check the compiler version here instead of in `compiler_version` so
+ # that the `checking` message doesn't pretend the compiler can be used
+ # to then bail out one line later.
+ if info.type == 'gcc' and info.version < '4.8.0':
+ raise FatalCheckError(
+ 'Only GCC 4.8 or newer is supported (found version %s).'
+ % info.version)
+
+ # If you want to bump the version check here search for
+ # __cpp_static_assert above, and see the associated comment.
+ if info.type == 'clang' and not info.version:
+ raise FatalCheckError(
+ 'Only clang/llvm 3.6 or newer is supported.')
+
+ if info.type == 'msvc':
+ if info.version < '19.00.24213':
+ raise FatalCheckError(
+ 'This version (%s) of the MSVC compiler is not '
+ 'supported.\n'
+ 'You must install Visual C++ 2015 Update 3 or newer in '
+ 'order to build.\n'
+ 'See https://developer.mozilla.org/en/'
+ 'Windows_Build_Prerequisites' % info.version)
+
+ return namespace(
+ wrapper=wrapper,
+ compiler=compiler,
+ flags=flags,
+ type=info.type,
+ version=info.version,
+ language=language,
+ )
+
+ @depends(valid_compiler)
+ @checking('%s version' % what)
+ def compiler_version(compiler):
+ return compiler.version
+
+ if language == 'C++':
+ @depends(valid_compiler, c_compiler)
+ def valid_compiler(compiler, c_compiler):
+ if compiler.type != c_compiler.type:
+ die('The %s C compiler is %s, while the %s C++ compiler is '
+ '%s. Need to use the same compiler suite.',
+ host_or_target_str, c_compiler.type,
+ host_or_target_str, compiler.type)
+
+ if compiler.version != c_compiler.version:
+ die('The %s C compiler is version %s, while the %s C++ '
+ 'compiler is version %s. Need to use the same compiler '
+ 'version.',
+ host_or_target_str, c_compiler.version,
+ host_or_target_str, compiler.version)
+ return compiler
+
+ # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
+ # and the flags that were part of the user input for those variables to
+ # be provided.
+ add_old_configure_assignment(var, depends_if(valid_compiler)(
+ lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
+
+ # 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':
+ set_config(
+ '%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
+ 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)
+
+
+ # Set CPP/CXXCPP for both the build system and old-configure. We don't
+ # need to check this works for preprocessing, because we already relied
+ # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
+ # in the first place.
+ if host_or_target == target:
+ pp_var = {
+ 'C': 'CPP',
+ 'C++': 'CXXCPP',
+ }[language]
+
+ preprocessor = depends_if(valid_compiler)(
+ lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
+
+ set_config(pp_var, preprocessor)
+ add_old_configure_assignment(pp_var, preprocessor)
+
+ 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,
+ other_c_compiler=c_compiler)
+
+# Generic compiler-based conditions.
+non_msvc_compiler = depends(c_compiler)(lambda info: info.type != 'msvc')
+building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
+
+include('compile-checks.configure')
+
+@depends(have_64_bit,
+ try_compile(body='static_assert(sizeof(void *) == 8, "")',
+ check_msg='for 64-bit OS'))
+def check_have_64_bit(have_64_bit, compiler_have_64_bit):
+ if have_64_bit != compiler_have_64_bit:
+ configure_error('The target compiler does not agree with configure '
+ 'about the target bitness.')
+
+
+@depends(c_compiler)
+def default_debug_flags(compiler_info):
+ # Debug info is ON by default.
+ if compiler_info.type in ('msvc', 'clang-cl'):
+ return '-Zi'
+ return '-g'
+
+option(env='MOZ_DEBUG_FLAGS',
+ nargs=1,
+ help='Debug compiler flags')
+
+imply_option('--enable-debug-symbols',
+ depends_if('--enable-debug')(lambda v: v))
+
+js_option('--enable-debug-symbols',
+ nargs='?',
+ default=True,
+ help='Enable debug symbols using the given compiler flags')
+
+set_config('MOZ_DEBUG_SYMBOLS',
+ depends_if('--enable-debug-symbols')(lambda _: True))
+
+@depends('MOZ_DEBUG_FLAGS', '--enable-debug-symbols', default_debug_flags)
+def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
+ # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
+ # --enable-debug-symbols takes precedence. Note, the value of
+ # --enable-debug-symbols may be implied by --enable-debug.
+ if len(enable_debug_flags):
+ return enable_debug_flags[0]
+ if env_debug_flags:
+ return env_debug_flags[0]
+ return default_debug_flags
+
+set_config('MOZ_DEBUG_FLAGS', debug_flags)
+add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
+
+# Some standard library headers (notably bionic on Android) declare standard
+# functions (e.g. getchar()) and also #define macros for those standard
+# functions. libc++ deals with this by doing something like the following
+# (explanatory comments added):
+#
+# #ifdef FUNC
+# // Capture the definition of FUNC.
+# inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
+# #undef FUNC
+# // Use a real inline definition.
+# inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
+# #endif
+#
+# _LIBCPP_INLINE_VISIBILITY is typically defined as:
+#
+# __attribute__((__visibility__("hidden"), __always_inline__))
+#
+# Unfortunately, this interacts badly with our system header wrappers, as the:
+#
+# #pragma GCC visibility push(default)
+#
+# that they do prior to including the actual system header is treated by the
+# compiler as an explicit declaration of visibility on every function declared
+# in the header. Therefore, when the libc++ code above is encountered, it is
+# as though the compiler has effectively seen:
+#
+# int FUNC(...) __attribute__((__visibility__("default")));
+# int FUNC(...) __attribute__((__visibility__("hidden")));
+#
+# and the compiler complains about the mismatched visibility declarations.
+#
+# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
+# existing definition. We can therefore define it to the empty string (since
+# we are properly managing visibility ourselves) and avoid this whole mess.
+# Note that we don't need to do this with gcc, as libc++ detects gcc and
+# effectively does the same thing we are doing here.
+@depends(c_compiler, target)
+def libcxx_inline_visibility(c_compiler, target):
+ if c_compiler.type == 'clang' and target.os == 'Android':
+ return ''
+
+set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_inline_visibility)
+set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49', libcxx_inline_visibility)
+
+@depends(c_compiler, target, check_build_environment)
+def visibility_flags(c_compiler, target, env):
+ if target.os != 'WINNT':
+ if target.kernel == 'Darwin':
+ return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
+ return ('-I%s/system_wrappers' % os.path.join(env.dist),
+ '-include',
+ '%s/config/gcc_hidden.h' % env.topsrcdir)
+
+@depends(target, visibility_flags)
+def wrap_system_includes(target, visibility_flags):
+ if visibility_flags and target.kernel != 'Darwin':
+ return True
+
+set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
+ depends(visibility_flags)(lambda v: bool(v) or None))
+set_define('HAVE_VISIBILITY_ATTRIBUTE',
+ depends(visibility_flags)(lambda v: bool(v) or None))
+set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
+set_config('VISIBILITY_FLAGS', visibility_flags)
+
+include('windows.configure')
+include('rust.configure')