summaryrefslogtreecommitdiffstats
path: root/build/moz.configure
diff options
context:
space:
mode:
Diffstat (limited to 'build/moz.configure')
-rw-r--r--build/moz.configure/android-ndk.configure150
-rw-r--r--build/moz.configure/checks.configure144
-rw-r--r--build/moz.configure/compile-checks.configure152
-rw-r--r--build/moz.configure/compilers-util.configure62
-rw-r--r--build/moz.configure/headers.configure93
-rw-r--r--build/moz.configure/init.configure780
-rw-r--r--build/moz.configure/java.configure62
-rw-r--r--build/moz.configure/keyfiles.configure68
-rw-r--r--build/moz.configure/memory.configure98
-rw-r--r--build/moz.configure/old.configure425
-rw-r--r--build/moz.configure/pkg.configure97
-rw-r--r--build/moz.configure/rust.configure166
-rw-r--r--build/moz.configure/toolchain.configure910
-rw-r--r--build/moz.configure/util.configure440
-rw-r--r--build/moz.configure/warnings.configure111
-rw-r--r--build/moz.configure/windows.configure431
16 files changed, 4189 insertions, 0 deletions
diff --git a/build/moz.configure/android-ndk.configure b/build/moz.configure/android-ndk.configure
new file mode 100644
index 000000000..6a0b30529
--- /dev/null
+++ b/build/moz.configure/android-ndk.configure
@@ -0,0 +1,150 @@
+# -*- 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/.
+
+
+js_option('--with-android-ndk', nargs=1,
+ help='location where the Android NDK can be found')
+
+js_option('--with-android-toolchain', nargs=1,
+ help='location of the Android toolchain')
+
+js_option('--with-android-gnu-compiler-version', nargs=1,
+ help='GNU compiler version to use')
+
+min_android_version = dependable(lambda: '9')
+
+js_option('--with-android-version',
+ nargs=1,
+ help='android platform version',
+ default=min_android_version)
+
+@depends('--with-android-version', min_android_version)
+@imports(_from='__builtin__', _import='ValueError')
+def android_version(value, min_version):
+ if not value:
+ # Someone has passed --without-android-version.
+ die('--with-android-version cannot be disabled.')
+
+ try:
+ version = int(value[0])
+ except ValueError:
+ die('--with-android-version expects an integer value')
+
+ if version < int(min_version):
+ die('--with-android-version must be at least %s (got %s)',
+ min_version, value[0])
+
+ return version
+
+add_old_configure_assignment('android_version', android_version)
+
+@depends('--with-android-ndk', build_project)
+def ndk(value, build_project):
+ if build_project == 'mobile/android' and not value:
+ die('You must specify --with-android-ndk=/path/to/ndk when '
+ 'building mobile/android')
+ if value:
+ return value[0]
+
+set_config('ANDROID_NDK', ndk)
+add_old_configure_assignment('android_ndk', ndk)
+
+@depends(target, android_version, ndk)
+@checking('for android platform directory')
+@imports(_from='os.path', _import='isdir')
+def android_platform(target, android_version, ndk):
+ if target.os != 'Android':
+ return
+
+ if 'mips' in target.cpu:
+ target_dir_name = 'mips'
+ elif 'aarch64' == target.cpu:
+ target_dir_name = 'arm64'
+ else:
+ target_dir_name = target.cpu
+
+ # Not all Android releases have their own platform release. We use
+ # the next lower platform version in these cases.
+ if android_version in (11, 10):
+ platform_version = 9
+ elif android_version in (20, 22):
+ platform_version = android_version - 1
+ else:
+ platform_version = android_version
+
+ platform_dir = os.path.join(ndk,
+ 'platforms',
+ 'android-%s' % platform_version,
+ 'arch-%s' % target_dir_name)
+
+ if not isdir(platform_dir):
+ die("Android platform directory not found. With the current "
+ "configuration, it should be in %s" % platform_dir)
+
+ return platform_dir
+
+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')
+@imports(_from='os.path', _import='isdir')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def android_toolchain(target, host, ndk, toolchain, gnu_compiler_version):
+ if not ndk:
+ return
+ if toolchain:
+ return toolchain[0]
+ else:
+ if target.cpu == 'arm' and target.endianness == 'little':
+ target_base = 'arm-linux-androideabi'
+ elif target.cpu == 'x86':
+ target_base = 'x86'
+ elif target.cpu == 'mips32' and target.endianness == 'little':
+ target_base = 'mipsel-linux-android'
+ elif target.cpu == 'aarch64' and target.endianness == 'little':
+ target_base = 'aarch64-linux-android'
+ else:
+ die('Target cpu is not supported.')
+
+ toolchain_format = '%s/toolchains/%s-%s/prebuilt/%s-%s'
+
+ for version in gnu_compiler_version or ['4.9', '4.8', '4.7']:
+ toolchain = toolchain_format % (ndk, target_base, version,
+ host.kernel.lower(), host.cpu)
+ log.debug('Trying %s' % quote(toolchain))
+ if not isdir(toolchain) and host.cpu == 'x86_64':
+ toolchain = toolchain_format % (ndk, target_base, version,
+ host.kernel.lower(), 'x86')
+ log.debug('Trying %s' % quote(toolchain))
+ if isdir(toolchain):
+ return toolchain
+ else:
+ if gnu_compiler_version:
+ die('Your --with-android-gnu-compiler-version may be wrong')
+ die('You have to specify --with-android-toolchain='
+ '/path/to/ndk/toolchain.')
+
+set_config('ANDROID_TOOLCHAIN', android_toolchain)
+
+@depends(target, android_toolchain)
+def android_toolchain_prefix(target, toolchain):
+ if toolchain:
+ if target.cpu == 'x86':
+ # Ideally, the --target should just have the right x86 variant
+ # in the first place.
+ return '%s/bin/i686-linux-android-' % toolchain
+ return '%s/bin/%s-' % (toolchain, target.toolchain)
+
+imply_option('--with-toolchain-prefix', android_toolchain_prefix,
+ reason='--with-android-ndk')
diff --git a/build/moz.configure/checks.configure b/build/moz.configure/checks.configure
new file mode 100644
index 000000000..8c2dbc0cc
--- /dev/null
+++ b/build/moz.configure/checks.configure
@@ -0,0 +1,144 @@
+# -*- 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/.
+
+# Templates implementing some generic checks.
+# ==============================================================
+
+# Declare some exceptions. This is cumbersome, but since we shouldn't need a
+# lot of them, let's stack them all here. When adding a new one, put it in the
+# _declare_exceptions template, and add it to the return statement. Then
+# destructure in the assignment below the function declaration.
+@template
+@imports(_from='__builtin__', _import='Exception')
+def _declare_exceptions():
+ class FatalCheckError(Exception):
+ '''An exception to throw from a function decorated with @checking.
+ It will result in calling die() with the given message.
+ Debugging messages emitted from the decorated function will also be
+ printed out.'''
+ return (FatalCheckError,)
+
+(FatalCheckError,) = _declare_exceptions()
+
+del _declare_exceptions
+
+# Helper to display "checking" messages
+# @checking('for foo')
+# def foo():
+# return 'foo'
+# is equivalent to:
+# def foo():
+# log.info('checking for foo... ')
+# ret = foo
+# log.info(ret)
+# return ret
+# This can be combined with e.g. @depends:
+# @depends(some_option)
+# @checking('for something')
+# def check(value):
+# ...
+# An optional callback can be given, that will be used to format the returned
+# value when displaying it.
+@template
+def checking(what, callback=None):
+ def decorator(func):
+ def wrapped(*args, **kwargs):
+ log.info('checking %s... ', what)
+ with log.queue_debug():
+ error, ret = None, None
+ try:
+ ret = func(*args, **kwargs)
+ except FatalCheckError as e:
+ 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 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
+# the program is found.
+# - `progs` is a list (or tuple) of program names that will be searched for.
+# It can also be a reference to a @depends function that returns such a
+# list. If the list is empty and there is no input, the check is skipped.
+# - `what` is a human readable description of what is being looked for. It
+# defaults to the lowercase version of `var`.
+# - `input` is a string reference to an existing option or a reference to a
+# @depends function resolving to explicit input for the program check.
+# The default is to create an option for the environment variable `var`.
+# This argument allows to use a different kind of option (possibly using a
+# configure flag), or doing some pre-processing with a @depends function.
+# - `allow_missing` indicates whether not finding the program is an error.
+# - `paths` is a list of paths or @depends function returning a list of paths
+# that will cause the given path(s) to be searched rather than $PATH. Input
+# paths may either be individual paths or delimited by os.pathsep, to allow
+# passing $PATH (for example) as an element.
+#
+# The simplest form is:
+# check_prog('PROG', ('a', 'b'))
+# This will look for 'a' or 'b' in $PATH, and set_config PROG to the one
+# it can find. If PROG is already set from the environment or command line,
+# use that value instead.
+@template
+@imports(_from='mozbuild.shellutil', _import='quote')
+def check_prog(var, progs, what=None, input=None, allow_missing=False,
+ paths=None):
+ if input:
+ # Wrap input with type checking and normalization.
+ @depends(input)
+ def input(value):
+ if not value:
+ return
+ if isinstance(value, str):
+ return (value,)
+ if isinstance(value, (tuple, list)) and len(value) == 1:
+ return value
+ configure_error('input must resolve to a tuple or a list with a '
+ 'single element, or a string')
+ else:
+ option(env=var, nargs=1,
+ help='Path to %s' % (what or 'the %s program' % var.lower()))
+ input = var
+ what = what or var.lower()
+
+ # Trick to make a @depends function out of an immediate value.
+ progs = dependable(progs)
+ paths = dependable(paths)
+
+ @depends_if(input, progs, paths)
+ @checking('for %s' % what, lambda x: quote(x) if x else 'not found')
+ def check(value, progs, paths):
+ if progs is None:
+ progs = ()
+
+ if not isinstance(progs, (tuple, list)):
+ configure_error('progs must resolve to a list or tuple!')
+
+ for prog in value or progs:
+ log.debug('%s: Trying %s', var.lower(), quote(prog))
+ result = find_program(prog, paths)
+ if result:
+ return result
+
+ if not allow_missing or value:
+ raise FatalCheckError('Cannot find %s' % what)
+
+ @depends_if(check, progs)
+ def normalized_for_config(value, progs):
+ return ':' if value is None else value
+
+ set_config(var, normalized_for_config)
+
+ return check
diff --git a/build/moz.configure/compile-checks.configure b/build/moz.configure/compile-checks.configure
new file mode 100644
index 000000000..9f0ebcd61
--- /dev/null
+++ b/build/moz.configure/compile-checks.configure
@@ -0,0 +1,152 @@
+# -*- 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
+def try_compile(includes=None, body='', language='C++', flags=None, check_msg=None,
+ when=None):
+ compiler = {
+ 'C': c_compiler,
+ 'C++': cxx_compiler,
+ }[language]
+
+ return compiler.try_compile(includes, body, flags, check_msg, when=when)
+
+
+# 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 always
+
+ if includes:
+ includes = includes[:]
+ else:
+ includes = []
+ includes.append(header)
+
+ have_header = try_compile(includes=includes, language=language, flags=flags,
+ check_msg='for %s' % header, when=when)
+ 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
+
+
+@depends(c_compiler)
+def warnings_cflags(c_compiler):
+ return []
+
+@depends(cxx_compiler)
+def warnings_cxxflags(cxx_compiler):
+ return []
+
+
+# Tests whether GCC or clang support the given warning flag, and if it is,
+# add it to the list of warning flags for the build.
+# - `warning` is the warning flag (e.g. -Wfoo)
+# - `compiler` (optional) is the compiler to test against (c_compiler or
+# cxx_compiler, from toolchain.configure). When omitted, both compilers
+# are tested.
+# - `when` (optional) is a @depends function or option name conditioning
+# when the warning flag is wanted.
+# - `check`, when not set, skips checking whether the flag is supported and
+# adds it to the list of warning flags unconditionally. This is only meant
+# for add_gcc_warning().
+@template
+def check_and_add_gcc_warning(warning, compiler=None, when=None, check=True):
+ if compiler:
+ compilers = (compiler,)
+ else:
+ compilers = (c_compiler, cxx_compiler)
+
+ when = when or always
+
+ for c in compilers:
+ assert c in (c_compiler, cxx_compiler)
+ lang, warnings_flags = {
+ c_compiler: ('C', warnings_cflags),
+ cxx_compiler: ('C++', warnings_cxxflags),
+ }[c]
+
+ # GCC and clang will fail if given an unknown warning option like
+ # -Wfoobar. But later versions won't fail if given an unknown negated
+ # warning option like -Wno-foobar. So when we are checking for support
+ # of a negated warning option, we actually test the positive form, but
+ # add the negated form to the flags variable.
+ if (warning.startswith('-Wno-') and
+ not warning.startswith('-Wno-error=')):
+ flags = ['-Werror', '-W' + warning[5:]]
+ elif warning.startswith('-Werror='):
+ flags = [warning]
+ else:
+ flags = ['-Werror', warning]
+
+ @depends(c, when)
+ def result(c, when):
+ if when and c.type in ('clang', 'gcc'):
+ return True
+
+ if check:
+ result = c.try_compile(
+ flags=flags, when=result,
+ check_msg='whether the %s compiler supports %s' % (lang,
+ warning))
+
+ @depends(result, warnings_flags)
+ def maybe_add_flag(result, warnings_flags):
+ if result:
+ warnings_flags.append(warning)
+
+# Add the given warning to the list of warning flags for the build.
+# - `warning` is the warning flag (e.g. -Wfoo)
+# - `compiler` (optional) is the compiler to add the flag for (c_compiler or
+# cxx_compiler, from toolchain.configure). When omitted, the warning flag
+# is added for both compilers.
+# - `when` (optional) is a @depends function or option name conditioning
+# when the warning flag is wanted.
+@template
+def add_gcc_warning(warning, compiler=None, when=None):
+ check_and_add_gcc_warning(warning, compiler, when, check=False)
diff --git a/build/moz.configure/compilers-util.configure b/build/moz.configure/compilers-util.configure
new file mode 100644
index 000000000..32271a010
--- /dev/null
+++ b/build/moz.configure/compilers-util.configure
@@ -0,0 +1,62 @@
+# -*- 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='SandboxDependsFunction')
+def compiler_class(compiler):
+ class Compiler(SandboxDependsFunction):
+ # 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, when=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)(fn)
+ else:
+ def checking_fn(fn):
+ return fn
+
+ @depends_when(self, dependable(flags), extra_toolchain_flags, when=when)
+ @checking_fn
+ def func(compiler, flags, extra_flags):
+ flags = flags or []
+ flags += extra_flags or []
+ flags.append('-c')
+
+ if try_invoke_compiler(
+ compiler.wrapper + [compiler.compiler] + compiler.flags,
+ compiler.language, source, flags,
+ onerror=onerror) is not None:
+ return True
+
+ return func
+
+ compiler.__class__ = Compiler
+ return compiler
diff --git a/build/moz.configure/headers.configure b/build/moz.configure/headers.configure
new file mode 100644
index 000000000..52ffa2f89
--- /dev/null
+++ b/build/moz.configure/headers.configure
@@ -0,0 +1,93 @@
+# -*- 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/.
+
+# Check for headers defining standard int types.
+check_header('stdint.h')
+have_inttypes = check_header('inttypes.h')
+
+# Assume we have ansi C header files available.
+set_define('STDC_HEADERS', True)
+
+set_config('HAVE_INTTYPES_H', have_inttypes)
+
+building_linux = depends(target)(lambda target: target.kernel == 'Linux')
+
+have_malloc = check_header('malloc.h')
+
+check_header('alloca.h')
+
+add_old_configure_assignment('HAVE_MALLOC_H', have_malloc)
+
+check_headers(
+ 'sys/byteorder.h',
+ 'getopt.h',
+ 'unistd.h',
+ 'nl_types.h',
+ 'cpuid.h',
+ when=non_msvc_compiler,
+)
+
+# These are all the places some variant of statfs can be hiding.
+check_headers(
+ 'sys/statvfs.h',
+ 'sys/statfs.h',
+ 'sys/vfs.h',
+ 'sys/mount.h',
+ when=non_msvc_compiler,
+)
+
+# Quota support
+check_header('sys/quota.h',
+ when=non_msvc_compiler)
+check_header('linux/quota.h',
+ includes=['sys/socket.h'],
+ when=building_linux)
+
+# SCTP support - needs various network include headers
+check_headers(
+ 'linux/if_addr.h',
+ 'linux/rtnetlink.h',
+ includes=['sys/socket.h'],
+ when=building_linux,
+)
+
+check_header('sys/queue.h',
+ when=non_msvc_compiler)
+
+check_headers(
+ 'sys/types.h',
+ 'netinet/in.h',
+ 'byteswap.h',
+ when=non_msvc_compiler,
+)
+
+# TODO: Move these checks to file specific to --enable-project=js.
+have_perf_event_h = check_header('linux/perf_event.h',
+ when=building_linux)
+
+js_option('--with-linux-headers',
+ help='location where the Linux kernel headers can be found',
+ nargs=1)
+
+passed_linux_header_flags = depends_if('--with-linux-headers')(lambda v: ['-I%s' % v[0]])
+
+@depends_when(try_compile(includes=['asm/unistd.h'],
+ body='return sizeof(__NR_perf_event_open);',
+ flags=passed_linux_header_flags,
+ check_msg='for perf_event_open system call'),
+ when=have_perf_event_h)
+def have_perf_event_open(have_perf_event_open):
+ if have_perf_event_open:
+ return True
+
+set_config('HAVE_LINUX_PERF_EVENT_H', have_perf_event_open)
+
+@depends(passed_linux_header_flags, have_perf_event_open)
+def linux_headers_includes(passed_linux_header_flags, have_perf_event_open):
+ if have_perf_event_open and passed_linux_header_flags:
+ return passed_linux_header_flags[0]
+
+set_config('LINUX_HEADERS_INCLUDES', linux_headers_includes)
diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure
new file mode 100644
index 000000000..2123bebc9
--- /dev/null
+++ b/build/moz.configure/init.configure
@@ -0,0 +1,780 @@
+# -*- 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/.
+
+include('util.configure')
+include('checks.configure')
+
+option(env='DIST', nargs=1, help='DIST directory')
+
+# Do not allow objdir == srcdir builds.
+# ==============================================================
+@depends('--help', 'DIST')
+@imports(_from='os.path', _import='exists')
+def check_build_environment(help, dist):
+ topobjdir = os.path.realpath(os.path.abspath('.'))
+ topsrcdir = os.path.realpath(os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..')))
+
+ if dist:
+ dist = normsep(dist[0])
+ else:
+ dist = os.path.join(topobjdir, 'dist')
+
+ result = namespace(
+ topsrcdir=topsrcdir,
+ topobjdir=topobjdir,
+ dist=dist,
+ )
+
+ if help:
+ return result
+
+ if topsrcdir == topobjdir:
+ die(' ***\n'
+ ' * Building directly in the main source directory is not allowed.\n'
+ ' *\n'
+ ' * To build, you must run configure from a separate directory\n'
+ ' * (referred to as an object directory).\n'
+ ' *\n'
+ ' * If you are building with a mozconfig, you will need to change your\n'
+ ' * mozconfig to point to a different object directory.\n'
+ ' ***'
+ )
+
+ # Check for a couple representative files in the source tree
+ conflict_files = [
+ '* %s' % f for f in ('Makefile', 'config/autoconf.mk')
+ if exists(os.path.join(topsrcdir, f))
+ ]
+ if conflict_files:
+ die(' ***\n'
+ ' * Your source tree contains these files:\n'
+ ' %s\n'
+ ' * This indicates that you previously built in the source tree.\n'
+ ' * A source tree build can confuse the separate objdir build.\n'
+ ' *\n'
+ ' * To clean up the source tree:\n'
+ ' * 1. cd %s\n'
+ ' * 2. gmake distclean\n'
+ ' ***'
+ % ('\n '.join(conflict_files), topsrcdir)
+ )
+
+ return result
+
+set_config('TOPSRCDIR', delayed_getattr(check_build_environment, 'topsrcdir'))
+set_config('TOPOBJDIR', delayed_getattr(check_build_environment, 'topobjdir'))
+set_config('MOZ_BUILD_ROOT', delayed_getattr(check_build_environment,
+ 'topobjdir'))
+set_config('DIST', delayed_getattr(check_build_environment, 'dist'))
+
+
+option(env='MOZ_AUTOMATION', help='Enable options for automated builds')
+set_config('MOZ_AUTOMATION', depends_if('MOZ_AUTOMATION')(lambda x: True))
+
+
+option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script')
+
+option(env='MOZ_CURRENT_PROJECT', nargs=1, help='Current build project')
+option(env='MOZCONFIG', nargs=1, help='Mozconfig location')
+
+# Read user mozconfig
+# ==============================================================
+# Note: the dependency on --help is only there to always read the mozconfig,
+# even when --help is passed. Without this dependency, the function wouldn't
+# be called when --help is passed, and the mozconfig wouldn't be read.
+@depends('MOZ_CURRENT_PROJECT', 'MOZCONFIG', 'OLD_CONFIGURE',
+ check_build_environment, '--help')
+@imports(_from='mozbuild.mozconfig', _import='MozconfigLoader')
+def mozconfig(current_project, mozconfig, old_configure, build_env, help):
+ if not old_configure:
+ die('The OLD_CONFIGURE environment variable must be set')
+
+ # Don't read the mozconfig for the js configure (yay backwards
+ # compatibility)
+ # While the long term goal is that js and top-level use the same configure
+ # and the same overall setup, including the possibility to use mozconfigs,
+ # figuring out what we want to do wrt mozconfig vs. command line and
+ # environment variable is not a clear-cut case, and it's more important to
+ # fix the immediate problem mozconfig causes to js developers by
+ # "temporarily" returning to the previous behavior of not loading the
+ # mozconfig for the js configure.
+ # Separately to the immediate problem for js developers, there is also the
+ # need to not load a mozconfig when running js configure as a subconfigure.
+ # Unfortunately, there is no direct way to tell whether the running
+ # configure is the js configure. The indirect way is to look at the
+ # OLD_CONFIGURE path, which points to js/src/old-configure.
+ # I expect we'll have figured things out for mozconfigs well before
+ # old-configure dies.
+ if os.path.dirname(os.path.abspath(old_configure[0])).endswith('/js/src'):
+ return {'path': None}
+
+ loader = MozconfigLoader(build_env.topsrcdir)
+ current_project = current_project[0] if current_project else None
+ mozconfig = mozconfig[0] if mozconfig else None
+ mozconfig = loader.find_mozconfig(env={'MOZCONFIG': mozconfig})
+ mozconfig = loader.read_mozconfig(mozconfig, moz_build_app=current_project)
+
+ return mozconfig
+
+set_config('MOZCONFIG', depends(mozconfig)(lambda m: m['path']))
+
+
+option(env='PYTHON', nargs=1, help='Python interpreter')
+
+# Setup python virtualenv
+# ==============================================================
+@depends('PYTHON', check_build_environment, mozconfig, '--help')
+@imports('os')
+@imports('sys')
+@imports('subprocess')
+@imports(_from='mozbuild.configure.util', _import='LineIO')
+@imports(_from='mozbuild.virtualenv', _import='VirtualenvManager')
+@imports(_from='mozbuild.virtualenv', _import='verify_python_version')
+@imports('distutils.sysconfig')
+def virtualenv_python(env_python, build_env, mozconfig, help):
+ if help:
+ return
+
+ python = env_python[0] if env_python else None
+
+ # Ideally we'd rely on the mozconfig injection from mozconfig_options,
+ # but we'd rather avoid the verbosity when we need to reexecute with
+ # a different python.
+ if mozconfig['path']:
+ if 'PYTHON' in mozconfig['env']['added']:
+ python = mozconfig['env']['added']['PYTHON']
+ elif 'PYTHON' in mozconfig['env']['modified']:
+ python = mozconfig['env']['modified']['PYTHON'][1]
+ elif 'PYTHON' in mozconfig['vars']['added']:
+ python = mozconfig['vars']['added']['PYTHON']
+ elif 'PYTHON' in mozconfig['vars']['modified']:
+ python = mozconfig['vars']['modified']['PYTHON'][1]
+
+ with LineIO(lambda l: log.error(l)) as out:
+ verify_python_version(out)
+ topsrcdir, topobjdir = build_env.topsrcdir, build_env.topobjdir
+ if topobjdir.endswith('/js/src'):
+ topobjdir = topobjdir[:-7]
+
+ with LineIO(lambda l: log.info(l)) as out:
+ manager = VirtualenvManager(
+ topsrcdir, topobjdir,
+ os.path.join(topobjdir, '_virtualenv'), out,
+ os.path.join(topsrcdir, 'build', 'virtualenv_packages.txt'))
+
+ if python:
+ # If we're not in the virtualenv, we need the which module for
+ # find_program.
+ if normsep(sys.executable) != normsep(manager.python_path):
+ sys.path.append(os.path.join(topsrcdir, 'python', 'which'))
+ found_python = find_program(python)
+ if not found_python:
+ die('The PYTHON environment variable does not contain '
+ 'a valid path. Cannot find %s', python)
+ python = found_python
+ else:
+ python = sys.executable
+
+ if not manager.up_to_date(python):
+ log.info('Creating Python environment')
+ manager.build(python)
+
+ python = normsep(manager.python_path)
+
+ if python != normsep(sys.executable):
+ log.info('Reexecuting in the virtualenv')
+ if env_python:
+ del os.environ['PYTHON']
+ # One would prefer to use os.execl, but that's completely borked on
+ # Windows.
+ sys.exit(subprocess.call([python] + sys.argv))
+
+ # We are now in the virtualenv
+ if not distutils.sysconfig.get_python_lib():
+ die('Could not determine python site packages directory')
+
+ return python
+
+set_config('PYTHON', virtualenv_python)
+add_old_configure_assignment('PYTHON', virtualenv_python)
+
+# Inject mozconfig options
+# ==============================================================
+# All options defined above this point can't be injected in mozconfig_options
+# below, so collect them.
+@template
+def early_options():
+ @dependable
+ @imports('__sandbox__')
+ def early_options():
+ return set(
+ option.env
+ for option in __sandbox__._options.itervalues()
+ if option.env
+ )
+ return early_options
+
+early_options = early_options()
+
+@depends(mozconfig, '--help')
+# This gives access to the sandbox. Don't copy this blindly.
+@imports('__sandbox__')
+@imports('os')
+def mozconfig_options(mozconfig, help):
+ if mozconfig['path']:
+ helper = __sandbox__._helper
+ log.info('Adding configure options from %s' % mozconfig['path'])
+ for arg in mozconfig['configure_args']:
+ log.info(' %s' % arg)
+ # We could be using imply_option() here, but it has other
+ # contraints that don't really apply to the command-line
+ # emulation that mozconfig provides.
+ helper.add(arg, origin='mozconfig', args=helper._args)
+
+ def add(key, value):
+ if key.isupper():
+ arg = '%s=%s' % (key, value)
+ log.info(' %s' % arg)
+ helper.add(arg, origin='mozconfig', args=helper._args)
+
+ for key, value in mozconfig['env']['added'].iteritems():
+ add(key, value)
+ os.environ[key] = value
+ for key, (_, value) in mozconfig['env']['modified'].iteritems():
+ add(key, value)
+ os.environ[key] = value
+ for key, value in mozconfig['vars']['added'].iteritems():
+ # mozconfig_loader adds _IS_SET variables that are irrelevant
+ if not key.endswith('_IS_SET'):
+ add(key, value)
+ for key, (_, value) in mozconfig['vars']['modified'].iteritems():
+ add(key, value)
+
+
+# Mozilla-Build
+# ==============================================================
+option(env='MOZILLABUILD', nargs=1,
+ help='Path to Mozilla Build (Windows-only)')
+
+option(env='CONFIG_SHELL', nargs=1, help='Path to a POSIX shell')
+
+# It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py,
+# but the end goal being that the configure script would go away...
+@depends('CONFIG_SHELL', 'MOZILLABUILD')
+@checking('for a shell')
+@imports('sys')
+def shell(value, mozillabuild):
+ if value:
+ return find_program(value[0])
+ shell = 'sh'
+ if mozillabuild:
+ shell = mozillabuild[0] + '/msys/bin/sh'
+ if sys.platform == 'win32':
+ shell = shell + '.exe'
+ return find_program(shell)
+
+
+# Host and target systems
+# ==============================================================
+option('--host', nargs=1, help='Define the system type performing the build')
+
+option('--target', nargs=1,
+ help='Define the system type where the resulting executables will be '
+ 'used')
+
+@imports(_from='mozbuild.configure.constants', _import='CPU')
+@imports(_from='mozbuild.configure.constants', _import='CPU_bitness')
+@imports(_from='mozbuild.configure.constants', _import='Endianness')
+@imports(_from='mozbuild.configure.constants', _import='Kernel')
+@imports(_from='mozbuild.configure.constants', _import='OS')
+def split_triplet(triplet):
+ # The standard triplet is defined as
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+ # There is also a quartet form:
+ # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+ # But we can consider the "KERNEL-OPERATING_SYSTEM" as one.
+ cpu, manufacturer, os = triplet.split('-', 2)
+
+ # Autoconf uses config.sub to validate and canonicalize those triplets,
+ # but the granularity of its results has never been satisfying to our
+ # use, so we've had our own, different, canonicalization. We've also
+ # historically not been very consistent with how we use the canonicalized
+ # values. Hopefully, this will help us make things better.
+ # The tests are inherited from our decades-old autoconf-based configure,
+ # which can probably be improved/cleaned up because they are based on a
+ # mix of uname and config.guess output, while we now only use the latter,
+ # which presumably has a cleaner and leaner output. Let's refine later.
+ os = os.replace('/', '_')
+ if 'android' in os:
+ canonical_os = 'Android'
+ canonical_kernel = 'Linux'
+ elif os.startswith('linux'):
+ canonical_os = 'GNU'
+ canonical_kernel = 'Linux'
+ elif os.startswith('kfreebsd') and os.endswith('-gnu'):
+ canonical_os = 'GNU'
+ canonical_kernel = 'kFreeBSD'
+ elif os.startswith('gnu'):
+ canonical_os = canonical_kernel = 'GNU'
+ elif os.startswith('mingw'):
+ canonical_os = canonical_kernel = 'WINNT'
+ elif os.startswith('darwin'):
+ canonical_kernel = 'Darwin'
+ canonical_os = 'OSX'
+ elif os.startswith('ios'):
+ canonical_kernel = 'Darwin'
+ canonical_os = 'iOS'
+ elif os.startswith('dragonfly'):
+ canonical_os = canonical_kernel = 'DragonFly'
+ elif os.startswith('freebsd'):
+ canonical_os = canonical_kernel = 'FreeBSD'
+ elif os.startswith('netbsd'):
+ canonical_os = canonical_kernel = 'NetBSD'
+ elif os.startswith('openbsd'):
+ canonical_os = canonical_kernel = 'OpenBSD'
+ else:
+ die('Unknown OS: %s' % os)
+
+ # The CPU granularity is probably not enough. Moving more things from
+ # old-configure will tell us if we need more
+ if cpu.endswith('86') or (cpu.startswith('i') and '86' in cpu):
+ canonical_cpu = 'x86'
+ endianness = 'little'
+ elif cpu in ('x86_64', 'ia64'):
+ canonical_cpu = cpu
+ endianness = 'little'
+ elif cpu in ('s390', 's390x'):
+ canonical_cpu = cpu
+ endianness = 'big'
+ elif cpu in ('powerpc64', 'ppc64', 'powerpc64le', 'ppc64le'):
+ canonical_cpu = 'ppc64'
+ endianness = 'little' if 'le' in cpu else 'big'
+ elif cpu in ('powerpc', 'ppc', 'rs6000') or cpu.startswith('powerpc'):
+ canonical_cpu = 'ppc'
+ endianness = 'big'
+ elif cpu in ('Alpha', 'alpha', 'ALPHA'):
+ canonical_cpu = 'Alpha'
+ endianness = 'little'
+ elif cpu.startswith('hppa') or cpu == 'parisc':
+ canonical_cpu = 'hppa'
+ endianness = 'big'
+ elif cpu.startswith('sparc64'):
+ canonical_cpu = 'sparc64'
+ endianness = 'big'
+ elif cpu.startswith('sparc') or cpu == 'sun4u':
+ canonical_cpu = 'sparc'
+ endianness = 'big'
+ elif cpu.startswith('arm'):
+ canonical_cpu = 'arm'
+ endianness = 'big' if cpu.startswith(('armeb', 'armbe')) else 'little'
+ elif cpu in ('mips', 'mipsel'):
+ canonical_cpu = 'mips32'
+ endianness = 'little' if 'el' in cpu else 'big'
+ elif cpu in ('mips64', 'mips64el'):
+ canonical_cpu = 'mips64'
+ endianness = 'little' if 'el' in cpu else 'big'
+ elif cpu.startswith('aarch64'):
+ canonical_cpu = 'aarch64'
+ endianness = 'little'
+ else:
+ die('Unknown CPU type: %s' % cpu)
+
+ return namespace(
+ alias=triplet,
+ cpu=CPU(canonical_cpu),
+ bitness=CPU_bitness[canonical_cpu],
+ kernel=Kernel(canonical_kernel),
+ os=OS(canonical_os),
+ endianness=Endianness(endianness),
+ raw_cpu=cpu,
+ raw_os=os,
+ # Toolchains, most notably for cross compilation may use cpu-os
+ # prefixes.
+ toolchain='%s-%s' % (cpu, os),
+ )
+
+
+@imports('subprocess')
+def config_sub(shell, triplet):
+ config_sub = os.path.join(os.path.dirname(__file__), '..',
+ 'autoconf', 'config.sub')
+ return subprocess.check_output([shell, config_sub, triplet]).strip()
+
+
+@depends('--host', shell)
+@checking('for host system type', lambda h: h.alias)
+@imports('subprocess')
+def host(value, shell):
+ if not value:
+ config_guess = os.path.join(os.path.dirname(__file__), '..',
+ 'autoconf', 'config.guess')
+ host = subprocess.check_output([shell, config_guess]).strip()
+ else:
+ host = value[0]
+
+ return split_triplet(config_sub(shell, host))
+
+
+@depends('--target', host, shell)
+@checking('for target system type', lambda t: t.alias)
+def target(value, host, shell):
+ if not value:
+ return host
+ return split_triplet(config_sub(shell, value[0]))
+
+
+@depends(host, target)
+@checking('whether cross compiling')
+def cross_compiling(host, target):
+ return host != target
+
+set_config('CROSS_COMPILE', cross_compiling)
+set_define('CROSS_COMPILE', cross_compiling)
+add_old_configure_assignment('CROSS_COMPILE', cross_compiling)
+
+
+@depends(target)
+def have_64_bit(target):
+ if target.bitness == 64:
+ return True
+
+set_config('HAVE_64BIT_BUILD', have_64_bit)
+set_define('HAVE_64BIT_BUILD', have_64_bit)
+add_old_configure_assignment('HAVE_64BIT_BUILD', have_64_bit)
+
+
+# Autoconf needs these set
+@depends(host)
+def host_for_old_configure(host):
+ return '--host=%s' % host.alias
+
+add_old_configure_arg(host_for_old_configure)
+
+@depends(host, target)
+def target_for_old_configure(host, target):
+ target_alias = target.alias
+ # old-configure does plenty of tests against $target and $target_os
+ # and expects darwin for iOS, so make it happy.
+ if target.os == 'iOS':
+ target_alias = target_alias.replace('-ios', '-darwin')
+ return '--target=%s' % target_alias
+
+add_old_configure_arg(target_for_old_configure)
+
+
+# These variables are for compatibility with the current moz.builds and
+# old-configure. Eventually, we'll want to canonicalize better.
+@depends(target)
+def target_variables(target):
+ if target.kernel == 'kFreeBSD':
+ os_target = 'GNU/kFreeBSD'
+ os_arch = 'GNU_kFreeBSD'
+ elif target.kernel == 'Darwin' or (target.kernel == 'Linux' and
+ target.os == 'GNU'):
+ os_target = target.kernel
+ os_arch = target.kernel
+ else:
+ os_target = target.os
+ os_arch = target.kernel
+
+ if target.kernel == 'Darwin' and target.cpu == 'x86':
+ os_test = 'i386'
+ else:
+ os_test = target.raw_cpu
+
+ return namespace(
+ OS_TARGET=os_target,
+ OS_ARCH=os_arch,
+ OS_TEST=os_test,
+ INTEL_ARCHITECTURE=target.cpu in ('x86', 'x86_64') or None,
+ )
+
+set_config('OS_TARGET', delayed_getattr(target_variables, 'OS_TARGET'))
+add_old_configure_assignment('OS_TARGET',
+ delayed_getattr(target_variables, 'OS_TARGET'))
+set_config('OS_ARCH', delayed_getattr(target_variables, 'OS_ARCH'))
+add_old_configure_assignment('OS_ARCH',
+ delayed_getattr(target_variables, 'OS_ARCH'))
+set_config('OS_TEST', delayed_getattr(target_variables, 'OS_TEST'))
+add_old_configure_assignment('OS_TEST',
+ delayed_getattr(target_variables, 'OS_TEST'))
+set_config('CPU_ARCH', delayed_getattr(target, 'cpu'))
+add_old_configure_assignment('CPU_ARCH', delayed_getattr(target, 'cpu'))
+set_config('INTEL_ARCHITECTURE', delayed_getattr(target_variables,
+ 'INTEL_ARCHITECTURE'))
+set_config('TARGET_CPU', delayed_getattr(target, 'raw_cpu'))
+set_config('TARGET_OS', delayed_getattr(target, 'raw_os'))
+
+
+@depends(host)
+def host_variables(host):
+ if host.kernel == 'kFreeBSD':
+ os_arch = 'GNU_kFreeBSD'
+ else:
+ os_arch = host.kernel
+ return namespace(
+ HOST_OS_ARCH=os_arch,
+ )
+
+set_config('HOST_OS_ARCH', delayed_getattr(host_variables, 'HOST_OS_ARCH'))
+add_old_configure_assignment('HOST_OS_ARCH',
+ delayed_getattr(host_variables, 'HOST_OS_ARCH'))
+
+@depends(target)
+def target_is_windows(target):
+ if target.kernel == 'WINNT':
+ return True
+
+set_define('_WINDOWS', target_is_windows)
+set_define('WIN32', target_is_windows)
+set_define('XP_WIN', target_is_windows)
+set_define('XP_WIN32', target_is_windows)
+
+@depends(target)
+def target_is_unix(target):
+ if target.kernel != 'WINNT':
+ return True
+
+set_define('XP_UNIX', target_is_unix)
+
+@depends(target)
+def target_is_darwin(target):
+ if target.kernel == 'Darwin':
+ return True
+
+set_define('XP_DARWIN', target_is_darwin)
+
+@depends(target)
+def target_is_ios(target):
+ if target.kernel == 'Darwin' and target.os == 'iOS':
+ return True
+
+set_define('XP_IOS', target_is_ios)
+
+@depends(target)
+def target_is_osx(target):
+ if target.kernel == 'Darwin' and target.os == 'OSX':
+ return True
+
+set_define('XP_MACOSX', target_is_osx)
+
+@depends(target)
+def target_is_linux(target):
+ if target.kernel == 'Linux':
+ return True
+
+set_define('XP_LINUX', target_is_linux)
+
+# The application/project to build
+# ==============================================================
+option('--enable-application', nargs=1, env='MOZ_BUILD_APP',
+ help='Application to build. Same as --enable-project.')
+
+@depends('--enable-application', '--help')
+def application(app, help):
+ if app:
+ return app
+
+imply_option('--enable-project', application)
+
+
+@depends(check_build_environment, '--help')
+def default_project(build_env, help):
+ if build_env.topobjdir.endswith('/js/src'):
+ return 'js'
+ return 'browser'
+
+option('--enable-project', nargs=1, default=default_project,
+ help='Project to build')
+
+option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1,
+ help='External directory containing additional build files')
+
+@depends('--enable-project', '--with-external-source-dir',
+ check_build_environment, '--help')
+@imports(_from='os.path', _import='exists')
+def include_project_configure(project, external_source_dir, build_env, help):
+ if not project:
+ die('--enable-project is required.')
+
+ base_dir = build_env.topsrcdir
+ if external_source_dir:
+ base_dir = os.path.join(base_dir, external_source_dir[0])
+
+ path = os.path.join(base_dir, project[0], 'moz.configure')
+ if not exists(path):
+ die('Cannot find project %s', project[0])
+ return path
+
+@depends('--with-external-source-dir')
+def external_source_dir(value):
+ if value:
+ return value[0]
+
+set_config('EXTERNAL_SOURCE_DIR', external_source_dir)
+add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir)
+
+
+@depends(include_project_configure, check_build_environment, '--help')
+def build_project(include_project_configure, build_env, help):
+ ret = os.path.dirname(os.path.relpath(include_project_configure,
+ build_env.topsrcdir))
+ return ret
+
+set_config('MOZ_BUILD_APP', build_project)
+set_define('MOZ_BUILD_APP', build_project)
+add_old_configure_assignment('MOZ_BUILD_APP', build_project)
+
+
+# set RELEASE_OR_BETA and NIGHTLY_BUILD variables depending on the cycle we're in
+# The logic works like this:
+# - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD)
+# - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora
+# - otherwise, we're building Release/Beta (define RELEASE_OR_BETA)
+@depends(check_build_environment, '--help')
+@imports(_from='__builtin__', _import='open')
+def milestone(build_env, _):
+ milestone_path = os.path.join(build_env.topsrcdir,
+ 'config',
+ 'milestone.txt')
+ with open(milestone_path, 'r') as fh:
+ milestone = fh.read().splitlines()[-1]
+
+ is_nightly = is_release_or_beta = None
+
+ if 'a1' in milestone:
+ is_nightly = True
+ elif 'a' not in milestone:
+ is_release_or_beta = True
+
+ return namespace(version=milestone,
+ is_nightly=is_nightly,
+ is_release_or_beta=is_release_or_beta)
+
+set_config('GRE_MILESTONE', delayed_getattr(milestone, 'version'))
+set_config('NIGHTLY_BUILD', delayed_getattr(milestone, 'is_nightly'))
+set_define('NIGHTLY_BUILD', delayed_getattr(milestone, 'is_nightly'))
+add_old_configure_assignment('NIGHTLY_BUILD',
+ delayed_getattr(milestone, 'is_nightly'))
+set_config('RELEASE_OR_BETA', delayed_getattr(milestone, 'is_release_or_beta'))
+set_define('RELEASE_OR_BETA', delayed_getattr(milestone, 'is_release_or_beta'))
+add_old_configure_assignment('RELEASE_OR_BETA',
+ delayed_getattr(milestone, 'is_release_or_beta'))
+
+# The app update channel is 'default' when not supplied. The value is used in
+# the application's confvars.sh (and is made available to a project specific
+# moz.configure).
+option('--enable-update-channel',
+ nargs=1,
+ help='Select application update channel',
+ default='default')
+
+@depends('--enable-update-channel')
+def update_channel(channel):
+ if channel[0] == '':
+ return 'default'
+ return channel[0].lower()
+
+set_config('MOZ_UPDATE_CHANNEL', update_channel)
+set_define('MOZ_UPDATE_CHANNEL', update_channel)
+add_old_configure_assignment('MOZ_UPDATE_CHANNEL', update_channel)
+
+
+# A template providing a shorthand for setting a variable. The created
+# option will only be settable with imply_option.
+# It is expected that a project-specific moz.configure will call imply_option
+# to set a value other than the default.
+# If required, the set_as_define and set_for_old_configure arguments
+# will additionally cause the variable to be set using set_define and
+# add_old_configure_assignment. util.configure would be an appropriate place for
+# this, but it uses add_old_configure_assignment, which is defined in this file.
+@template
+def project_flag(env=None, set_for_old_configure=False,
+ set_as_define=False, **kwargs):
+
+ if not env:
+ configure_error("A project_flag must be passed a variable name to set.")
+
+ opt = option(env=env, possible_origins=('implied',), **kwargs)
+
+ @depends(opt.option)
+ def option_implementation(value):
+ if value:
+ if len(value):
+ return value
+ return bool(value)
+
+ set_config(env, option_implementation)
+ if set_as_define:
+ set_define(env, option_implementation)
+ if set_for_old_configure:
+ add_old_configure_assignment(env, option_implementation)
+
+# milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set.
+@depends(milestone, '--help')
+def enabled_in_nightly(milestone, _):
+ return milestone.is_nightly
+
+# Set the MOZ_CONFIGURE_OPTIONS variable with all the options that
+# were passed somehow (environment, command line, mozconfig)
+@depends(mozconfig_options)
+@imports(_from='mozbuild.shellutil', _import='quote')
+@imports('__sandbox__')
+def all_configure_options(_):
+ result = []
+ previous = None
+ for option in __sandbox__._options.itervalues():
+ # __sandbox__._options contains items for both option.name and
+ # option.env. But it's also an OrderedDict, meaning both are
+ # consecutive.
+ # Also ignore OLD_CONFIGURE and MOZCONFIG because they're not
+ # interesting.
+ if option == previous or option.env in ('OLD_CONFIGURE', 'MOZCONFIG'):
+ continue
+ previous = option
+ value = __sandbox__._value_for(option)
+ # We only want options that were explicitly given on the command
+ # line, the environment, or mozconfig, and that differ from the
+ # defaults.
+ if (value is not None and value.origin not in ('default', 'implied') and
+ value != option.default):
+ result.append(__sandbox__._raw_options[option])
+ # We however always include options that are sent to old configure
+ # because we don't know their actual defaults. (Keep the conditions
+ # separate for ease of understanding and ease of removal)
+ elif (option.help == 'Help missing for old configure options' and
+ option in __sandbox__._raw_options):
+ result.append(__sandbox__._raw_options[option])
+
+ return quote(*result)
+
+set_config('MOZ_CONFIGURE_OPTIONS', all_configure_options)
+
+
+# This is temporary until js/src/configure and configure are merged.
+# Use instead of option() in js/moz.configure and more generally, for
+# options that are shared between configure and js/src/configure.
+@template
+def js_option(*args, **kwargs):
+ opt = option(*args, **kwargs)
+
+ @depends(opt.option, build_project)
+ def js_option(value, build_project):
+ if build_project != 'js':
+ return value.format(opt.option)
+
+ add_old_configure_arg(js_option)
+
+
+# Bug 1278542: This function is a workaround to resolve
+# |android_ndk_include|'s dependency on 'gonkdir.' The
+# actual implementation is located in b2g/moz.configure.
+# Remove this function as soon as 'android_ndk_include'
+# depends on 'target.'
+@depends('--help')
+def gonkdir(_):
+ return None
diff --git a/build/moz.configure/java.configure b/build/moz.configure/java.configure
new file mode 100644
index 000000000..459bd44cf
--- /dev/null
+++ b/build/moz.configure/java.configure
@@ -0,0 +1,62 @@
+# -*- 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/.
+
+
+# Java detection
+# ========================================================
+option('--with-java-bin-path', nargs=1,
+ help='Location of Java binaries (java, javac, jar)')
+
+@depends('--with-java-bin-path')
+@imports(_from='os', _import='environ')
+def java_search_paths(path):
+ if path:
+ # Look for javac and jar in the specified path.
+ return path
+ # With no path specified, look for javac and jar in $JAVA_HOME (if set)
+ # and $PATH.
+ if 'JAVA_HOME' in environ:
+ return [os.path.join(environ['JAVA_HOME'], 'bin'),
+ environ.get('PATH', '')]
+ return [environ.get('PATH')]
+
+# Finds the given java tool, failing with a custom error message if we can't
+# find it.
+@template
+def check_java_tool(tool):
+ check = check_prog(tool.upper(), (tool,), paths=java_search_paths,
+ allow_missing=True)
+
+ @depends(check)
+ def require_tool(result):
+ if result is None:
+ die("The program %s was not found. Set $JAVA_HOME to your Java "
+ "SDK directory or use '--with-java-bin-path={java-bin-dir}'"
+ % tool)
+ return result
+
+ return require_tool
+
+check_java_tool('java')
+check_java_tool('javah')
+check_java_tool('jar')
+check_java_tool('jarsigner')
+check_java_tool('keytool')
+javac = check_java_tool('javac')
+
+@depends(javac)
+@checking('for javac version')
+@imports('subprocess')
+def javac_version(javac):
+ try:
+ output = subprocess.check_output([javac, '-version'],
+ stderr=subprocess.STDOUT).rstrip()
+ version = Version(output.split(' ')[-1])
+ if version < '1.7':
+ die('javac 1.7 or higher is required (found %s)' % version)
+ return version
+ except subprocess.CalledProcessError as e:
+ die('Failed to get javac version: %s', e.output)
diff --git a/build/moz.configure/keyfiles.configure b/build/moz.configure/keyfiles.configure
new file mode 100644
index 000000000..5a71fd4b2
--- /dev/null
+++ b/build/moz.configure/keyfiles.configure
@@ -0,0 +1,68 @@
+# -*- 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
+def keyfile(desc, help=None, callback=lambda x: x):
+ help = help or ('Use the secret key contained in the given keyfile '
+ 'for %s requests' % desc)
+ name = desc.lower().replace(' ', '-')
+ no_key = callback('no-%s-key' % name)
+
+ option('--with-%s-keyfile' % name, nargs=1, help=help)
+
+ @depends('--with-%s-keyfile' % name)
+ @checking('for the %s key' % desc, lambda x: x and x is not no_key)
+ @imports(_from='__builtin__', _import='open')
+ @imports(_from='__builtin__', _import='IOError')
+ def keyfile(value):
+ if value:
+ try:
+ with open(value[0]) as fh:
+ result = fh.read().strip()
+ if result:
+ return callback(result)
+ raise FatalCheckError("'%s' is empty." % value[0])
+ except IOError as e:
+ raise FatalCheckError("'%s': %s." % (value[0], e.strerror))
+ return no_key
+
+ return keyfile
+
+
+@template
+def simple_keyfile(desc):
+ value = keyfile(desc)
+ set_config('MOZ_%s_KEY' % desc.upper().replace(' ', '_'), value)
+ # Only really required for MOZ_ADJUST_SDK_KEY currently still used in
+ # old-configure.
+ add_old_configure_assignment('MOZ_%s_KEY' % desc.upper().replace(' ', '_'),
+ value)
+
+
+@template
+def id_and_secret_keyfile(desc):
+ def id_and_secret(value):
+ if value.startswith('no-') and value.endswith('-key'):
+ id = value[:-3] + 'clientid'
+ secret = value
+ elif ' ' in value:
+ id, secret = value.split(' ', 1)
+ else:
+ raise FatalCheckError('%s key file has an invalid format.' % desc)
+ return namespace(
+ id=id,
+ secret=secret,
+ )
+
+ content = keyfile(desc, help='Use the client id and secret key contained '
+ 'in the given keyfile for %s requests' % desc,
+ callback=id_and_secret)
+
+
+ name = desc.upper().replace(' ', '_')
+ set_config('MOZ_%s_CLIENTID' % name, delayed_getattr(content, 'id'))
+ set_config('MOZ_%s_KEY' % name, delayed_getattr(content, 'secret'))
diff --git a/build/moz.configure/memory.configure b/build/moz.configure/memory.configure
new file mode 100644
index 000000000..3beed2fb2
--- /dev/null
+++ b/build/moz.configure/memory.configure
@@ -0,0 +1,98 @@
+# -*- 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/.
+
+
+option(env='MOZ_JEMALLOC4', help='Enable jemalloc 4')
+imply_option('--enable-jemalloc', depends_if('MOZ_JEMALLOC4')(lambda x: '4'))
+
+
+option('--enable-jemalloc', nargs='?', choices=('4', 'moz'), env='MOZ_MEMORY',
+ help='Replace memory allocator with jemalloc')
+
+@depends('--enable-jemalloc', target, build_project, c_compiler)
+def jemalloc(value, target, build_project, c_compiler):
+ if value.origin != 'default':
+ return bool(value) or None
+
+ if build_project == 'js':
+ return True
+
+ if target.kernel == 'Darwin' and target.cpu == 'x86_64':
+ # Don't enable by default on 32-bits OSX. See bug 702250.
+ return True
+
+ if target.kernel == 'WINNT' and c_compiler.type in ('msvc', 'clang-cl'):
+ return True
+
+ if target.kernel == 'Linux':
+ return True
+
+@depends('--enable-jemalloc')
+def jemalloc4(jemalloc):
+ if len(jemalloc) and jemalloc[0] == '4':
+ return True
+
+
+set_config('MOZ_MEMORY', jemalloc)
+set_define('MOZ_MEMORY', jemalloc)
+add_old_configure_assignment('MOZ_MEMORY', jemalloc)
+
+set_config('MOZ_JEMALLOC4', jemalloc4)
+set_define('MOZ_JEMALLOC4', jemalloc4)
+add_old_configure_assignment('MOZ_JEMALLOC4', jemalloc4)
+
+
+# Because --enable-jemalloc doesn't use a default because of the dependency
+# on the target, we can't use a js_option for it to propagate to js/src
+# through the old-configure.
+@depends(jemalloc, jemalloc4)
+def jemalloc_for_old_configure(jemalloc, jemalloc4):
+ if jemalloc:
+ return '--enable-jemalloc=4' if jemalloc4 else '--enable-jemalloc'
+ return '--disable-jemalloc'
+
+add_old_configure_arg(jemalloc_for_old_configure)
+
+
+@depends(jemalloc, jemalloc4, target)
+def jemalloc_os_define(jemalloc, jemalloc4, target):
+ if jemalloc and not jemalloc4:
+ if target.kernel == 'WINNT':
+ return 'MOZ_MEMORY_WINDOWS'
+ if target.kernel == 'Linux':
+ return 'MOZ_MEMORY_LINUX'
+ if target.kernel == 'Darwin':
+ return 'MOZ_MEMORY_DARWIN'
+ if target.kernel in ('kFreeBSD', 'FreeBSD', 'NetBSD'):
+ return 'MOZ_MEMORY_BSD'
+ die('--enable-jemalloc is not supported on %s', target.kernel)
+
+set_define(jemalloc_os_define, '1')
+
+@depends(jemalloc, jemalloc4, target)
+def jemalloc_os_define_android(jemalloc, jemalloc4, target):
+ if jemalloc and not jemalloc4 and target.os == 'Android':
+ return 'MOZ_MEMORY_ANDROID'
+
+set_define(jemalloc_os_define_android, '1')
+
+
+option('--enable-replace-malloc',
+ help='Enable ability to dynamically replace the malloc implementation')
+
+@depends('--enable-replace-malloc', jemalloc, milestone, build_project)
+def replace_malloc(value, jemalloc, milestone, build_project):
+ # Enable on central for the debugging opportunities it adds.
+ if value and not jemalloc:
+ die('--enable-replace-malloc requires --enable-jemalloc')
+ if value.origin != 'default':
+ return bool(value) or None
+ if milestone.is_nightly and jemalloc and build_project != 'js':
+ return True
+
+set_config('MOZ_REPLACE_MALLOC', replace_malloc)
+set_define('MOZ_REPLACE_MALLOC', replace_malloc)
+add_old_configure_assignment('MOZ_REPLACE_MALLOC', replace_malloc)
diff --git a/build/moz.configure/old.configure b/build/moz.configure/old.configure
new file mode 100644
index 000000000..b32c3f7b7
--- /dev/null
+++ b/build/moz.configure/old.configure
@@ -0,0 +1,425 @@
+# -*- 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/.
+
+@imports('codecs')
+@imports('sys')
+def encoded_open(path, mode):
+ encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
+ return codecs.open(path, mode, encoding)
+
+
+option(env='AUTOCONF', nargs=1, help='Path to autoconf 2.13')
+
+@depends(mozconfig, 'AUTOCONF')
+@checking('for autoconf')
+@imports(_from='os.path', _import='exists')
+@imports('re')
+def autoconf(mozconfig, autoconf):
+ mozconfig_autoconf = None
+ if mozconfig['path']:
+ make_extra = mozconfig['make_extra']
+ if make_extra:
+ for assignment in make_extra:
+ m = re.match('(?:export\s+)?AUTOCONF\s*:?=\s*(.+)$',
+ assignment)
+ if m:
+ mozconfig_autoconf = m.group(1)
+
+ autoconf = autoconf[0] if autoconf else None
+
+ for ac in (mozconfig_autoconf, autoconf, 'autoconf-2.13', 'autoconf2.13',
+ 'autoconf213'):
+ if ac:
+ autoconf = find_program(ac)
+ if autoconf:
+ break
+ else:
+ fink = find_program('fink')
+ if fink:
+ autoconf = os.path.normpath(os.path.join(
+ fink, '..', '..', 'lib', 'autoconf2.13', 'bin', 'autoconf'))
+ else:
+ brew = find_program('brew')
+ if brew:
+ autoconf = os.path.normpath(os.path.join(
+ brew, '..', '..', 'Cellar', 'autoconf213', '2.13', 'bin',
+ 'autoconf213'))
+
+ if not autoconf:
+ die('Could not find autoconf 2.13')
+
+ if not exists(autoconf):
+ die('Could not find autoconf 2.13 at %s', autoconf)
+
+ return autoconf
+
+set_config('AUTOCONF', autoconf)
+
+
+@depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell,
+ old_configure_assignments, build_project)
+@imports(_from='__builtin__', _import='open')
+@imports(_from='__builtin__', _import='print')
+@imports('glob')
+@imports('itertools')
+@imports('subprocess')
+# Import getmtime without overwriting the sandbox os.path.
+@imports(_from='os.path', _import='getmtime')
+@imports(_from='os.path', _import='exists')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def prepare_configure(old_configure, mozconfig, autoconf, build_env, shell,
+ old_configure_assignments, build_project):
+ # os.path.abspath in the sandbox will ensure forward slashes on Windows,
+ # which is actually necessary because this path actually ends up literally
+ # as $0, and backslashes there breaks autoconf's detection of the source
+ # directory.
+ old_configure = os.path.abspath(old_configure[0])
+ if build_project == 'js':
+ old_configure_dir = os.path.dirname(old_configure)
+ if not old_configure_dir.endswith('/js/src'):
+ old_configure = os.path.join(old_configure_dir, 'js', 'src',
+ os.path.basename(old_configure))
+
+ refresh = True
+ if exists(old_configure):
+ mtime = getmtime(old_configure)
+ aclocal = os.path.join(build_env.topsrcdir, 'build', 'autoconf',
+ '*.m4')
+ for input in itertools.chain(
+ (old_configure + '.in',
+ os.path.join(os.path.dirname(old_configure), 'aclocal.m4')),
+ glob.iglob(aclocal),
+ ):
+ if getmtime(input) > mtime:
+ break
+ else:
+ refresh = False
+
+ if refresh:
+ log.info('Refreshing %s with %s', old_configure, autoconf)
+ script = subprocess.check_output([
+ shell, autoconf,
+ '--localdir=%s' % os.path.dirname(old_configure),
+ old_configure + '.in'])
+
+ # Make old-configure append to config.log, where we put our own log.
+ # This could be done with a m4 macro, but it's way easier this way
+ script = script.replace('>./config.log', '>>./config.log')
+
+ with open(old_configure, 'wb') as fh:
+ fh.write(script)
+
+ cmd = [shell, old_configure]
+ with encoded_open('old-configure.vars', 'w') as out:
+ log.debug('Injecting the following to old-configure:')
+ def inject(command):
+ print(command, file=out)
+ log.debug('| %s', command)
+
+ if mozconfig['path']:
+ for key, value in mozconfig['vars']['added'].items():
+ inject("%s=%s" % (key, quote(value)))
+ for key, (old, value) in mozconfig['vars']['modified'].items():
+ inject("%s=%s" % (key, quote(value)))
+ for t in ('env', 'vars'):
+ for key in mozconfig[t]['removed'].keys():
+ inject("unset %s" % key)
+
+ # Autoconf is special, because it might be passed from
+ # mozconfig['make_extra'], which we don't pass automatically above.
+ inject('export AUTOCONF=%s' % quote(autoconf))
+
+ for assignment in old_configure_assignments:
+ inject(assignment)
+
+ return cmd
+
+
+@template
+def old_configure_options(*options):
+ for opt in options:
+ option(opt, nargs='*', help='Help missing for old configure options')
+
+ @dependable
+ def all_options():
+ return list(options)
+
+ return depends(prepare_configure, extra_old_configure_args, all_options,
+ *options)
+
+
+@old_configure_options(
+ '--cache-file',
+ '--datadir',
+ '--enable-accessibility',
+ '--enable-address-sanitizer',
+ '--enable-alsa',
+ '--enable-android-omx',
+ '--enable-b2g-bt',
+ '--enable-b2g-camera',
+ '--enable-b2g-ril',
+ '--enable-bundled-fonts',
+ '--enable-clang-plugin',
+ '--enable-content-sandbox',
+ '--enable-cookies',
+ '--enable-cpp-rtti',
+ '--enable-crashreporter',
+ '--enable-dbus',
+ '--enable-debug-js-modules',
+ '--enable-directshow',
+ '--enable-dtrace',
+ '--enable-dump-painting',
+ '--enable-elf-hack',
+ '--enable-extensions',
+ '--enable-faststripe',
+ '--enable-feeds',
+ '--enable-gamepad',
+ '--enable-gconf',
+ '--enable-gczeal',
+ '--enable-gio',
+ '--enable-gnomeui',
+ '--enable-gold',
+ '--enable-hardware-aec-ns',
+ '--enable-icf',
+ '--enable-install-strip',
+ '--enable-ion',
+ '--enable-ios-target',
+ '--enable-jitspew',
+ '--enable-libjpeg-turbo',
+ '--enable-libproxy',
+ '--enable-llvm-hacks',
+ '--enable-logrefcnt',
+ '--enable-maintenance-service',
+ '--enable-memory-sanitizer',
+ '--enable-mobile-optimize',
+ '--enable-mozril-geoloc',
+ '--enable-necko-wifi',
+ '--enable-negotiateauth',
+ '--enable-nfc',
+ '--enable-nspr-build',
+ '--enable-official-branding',
+ '--enable-omx-plugin',
+ '--enable-oom-breakpoint',
+ '--enable-optimize',
+ '--enable-parental-controls',
+ '--enable-pie',
+ '--enable-png-arm-neon-support',
+ '--enable-posix-nspr-emulation',
+ '--enable-pref-extensions',
+ '--enable-pulseaudio',
+ '--enable-raw',
+ '--enable-readline',
+ '--enable-reflow-perf',
+ '--enable-release',
+ '--enable-require-all-d3dc-versions',
+ '--enable-safe-browsing',
+ '--enable-sandbox',
+ '--enable-signmar',
+ '--enable-simulator',
+ '--enable-small-chunk-size',
+ '--enable-startup-notification',
+ '--enable-startupcache',
+ '--enable-stdcxx-compat',
+ '--enable-strip',
+ '--enable-synth-pico',
+ '--enable-system-cairo',
+ '--enable-system-extension-dirs',
+ '--enable-system-pixman',
+ '--enable-system-sqlite',
+ '--enable-tasktracer',
+ '--enable-thread-sanitizer',
+ '--enable-trace-logging',
+ '--enable-ui-locale',
+ '--enable-universalchardet',
+ '--enable-updater',
+ '--enable-url-classifier',
+ '--enable-valgrind',
+ '--enable-verify-mar',
+ '--enable-webrtc',
+ '--enable-xul',
+ '--enable-zipwriter',
+ '--includedir',
+ '--libdir',
+ '--no-create',
+ '--prefix',
+ '--with-android-cxx-stl',
+ '--with-android-distribution-directory',
+ '--with-android-max-sdk',
+ '--with-android-min-sdk',
+ '--with-android-sdk',
+ '--with-app-basename',
+ '--with-app-name',
+ '--with-arch',
+ '--with-branding',
+ '--with-crashreporter-enable-percent',
+ '--with-cross-lib',
+ '--with-debug-label',
+ '--with-default-mozilla-five-home',
+ '--with-distribution-id',
+ '--with-doc-include-dirs',
+ '--with-doc-input-dirs',
+ '--with-doc-output-dir',
+ '--with-float-abi',
+ '--with-fpu',
+ '--with-intl-api',
+ '--with-ios-sdk',
+ '--with-jitreport-granularity',
+ '--with-macbundlename-prefix',
+ '--with-macos-private-frameworks',
+ '--with-macos-sdk',
+ '--with-nspr-cflags',
+ '--with-nspr-exec-prefix',
+ '--with-nspr-libs',
+ '--with-nspr-prefix',
+ '--with-nss-exec-prefix',
+ '--with-nss-prefix',
+ '--with-pthreads',
+ '--with-qemu-exe',
+ '--with-sixgill',
+ '--with-soft-float',
+ '--with-system-bz2',
+ '--with-system-icu',
+ '--with-system-jpeg',
+ '--with-system-libevent',
+ '--with-system-libvpx',
+ '--with-system-nspr',
+ '--with-system-nss',
+ '--with-system-png',
+ '--with-system-zlib',
+ '--with-thumb',
+ '--with-thumb-interwork',
+ '--with-unify-dist',
+ '--with-user-appdir',
+ '--x-includes',
+ '--x-libraries',
+
+ # Below are the configure flags used by comm-central.
+ '--enable-ldap',
+ '--enable-mapi',
+ '--enable-calendar',
+ '--enable-incomplete-external-linkage',
+)
+@imports(_from='__builtin__', _import='compile')
+@imports(_from='__builtin__', _import='open')
+@imports('logging')
+@imports('os')
+@imports('subprocess')
+@imports('sys')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def old_configure(prepare_configure, extra_old_configure_args, all_options,
+ *options):
+ cmd = prepare_configure
+
+ # old-configure only supports the options listed in @old_configure_options
+ # so we don't need to pass it every single option we've been passed. Only
+ # the ones that are not supported by python configure need to.
+ cmd += [
+ value.format(name)
+ for name, value in zip(all_options, options)
+ if value.origin != 'default'
+ ]
+
+ # We also pass it the options from js/moz.configure so that it can pass
+ # them down to js/src/configure. Note this list is empty when running
+ # js/src/configure, in which case we don't need to pass those options
+ # to old-configure since old-configure doesn't handle them anyways.
+ if extra_old_configure_args:
+ cmd += extra_old_configure_args
+
+ # For debugging purpose, in case it's not what we'd expect.
+ log.debug('Running %s', quote(*cmd))
+
+ # Our logging goes to config.log, the same file old.configure uses.
+ # We can't share the handle on the file, so close it. We assume nothing
+ # beyond this point is going to be interesting to log to config.log from
+ # our end, so we don't make the effort to recreate a logging.FileHandler.
+ logger = logging.getLogger('moz.configure')
+ for handler in logger.handlers:
+ if isinstance(handler, logging.FileHandler):
+ handler.close()
+ logger.removeHandler(handler)
+
+ log_size = os.path.getsize('config.log')
+
+ ret = subprocess.call(cmd)
+ if ret:
+ with log.queue_debug():
+ with encoded_open('config.log', 'r') as fh:
+ fh.seek(log_size)
+ for line in fh:
+ log.debug(line.rstrip())
+ log.error('old-configure failed')
+ sys.exit(ret)
+
+ raw_config = {}
+ with encoded_open('config.data', 'r') as fh:
+ code = compile(fh.read(), 'config.data', 'exec')
+ # Every variation of the exec() function I tried led to:
+ # SyntaxError: unqualified exec is not allowed in function 'main' it
+ # contains a nested function with free variables
+ exec code in raw_config
+
+ # Ensure all the flags known to old-configure appear in the
+ # @old_configure_options above.
+ all_options = set(all_options)
+ for flag in raw_config['flags']:
+ if flag not in all_options:
+ die('Missing option in `@old_configure_options` in %s: %s',
+ __file__, flag)
+
+ # If the code execution above fails, we want to keep the file around for
+ # debugging.
+ os.remove('config.data')
+ return raw_config
+
+
+# set_config is only available in the global namespace, not directly in
+# @depends functions, but we do need to enumerate the result of
+# old_configure, so we cheat.
+@imports('__sandbox__')
+def set_old_configure_config(name, value):
+ __sandbox__.set_config_impl(name, value)
+
+# Same as set_old_configure_config, but for set_define.
+@imports('__sandbox__')
+def set_old_configure_define(name, value):
+ __sandbox__.set_define_impl(name, value)
+
+
+@depends(old_configure)
+@imports('types')
+def post_old_configure(raw_config):
+ for k, v in raw_config['substs']:
+ set_old_configure_config(
+ k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v)
+
+ for k, v in dict(raw_config['defines']).iteritems():
+ set_old_configure_define(k[1:-1], v[1:-1])
+
+ set_old_configure_config('non_global_defines',
+ raw_config['non_global_defines'])
+
+
+# Assuming no other option is declared after this function, handle the
+# env options that were injected by mozconfig_options by creating dummy
+# Option instances and having the sandbox's CommandLineHelper handle
+# them. We only do so for options that haven't been declared so far,
+# which should be a proxy for the options that old-configure handles
+# and that we don't know anything about.
+@depends('--help')
+@imports('__sandbox__')
+@imports(_from='mozbuild.configure.options', _import='Option')
+def remaining_mozconfig_options(_):
+ helper = __sandbox__._helper
+ for arg in helper:
+ if helper._origins[arg] != 'mozconfig':
+ continue
+ name = arg.split('=', 1)[0]
+ if name.isupper() and name not in __sandbox__._options:
+ option = Option(env=name, nargs='*', help=name)
+ helper.handle(option)
+
+# Please do not add anything after remaining_mozconfig_options()
diff --git a/build/moz.configure/pkg.configure b/build/moz.configure/pkg.configure
new file mode 100644
index 000000000..991f401ae
--- /dev/null
+++ b/build/moz.configure/pkg.configure
@@ -0,0 +1,97 @@
+# -*- 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/.
+
+@depends('--enable-compile-environment')
+def pkg_config(compile_env):
+ if compile_env:
+ return ('pkg-config',)
+
+pkg_config = check_prog('PKG_CONFIG', pkg_config, allow_missing=True)
+
+@depends_if(pkg_config)
+@checking('for pkg-config version')
+@imports('subprocess')
+def pkg_config_version(pkg_config):
+ return Version(check_cmd_output(pkg_config, '--version').rstrip())
+
+# Locates the given module using pkg-config.
+# - `var` determines the name of variables to set when the package is found.
+# <var>_CFLAGS and <var>_LIBS are set with corresponding values.
+# - `package_desc` package name and version requirement string, list of
+# strings describing packages to locate, or depends function that will
+# resolve to such a string or list of strings.
+# - `when` a depends function that will determine whether to perform
+# any checks (default is to always perform checks).
+# - `allow_missing` If set, failure to fulfill the package description
+# will not result in an error or logged message, and any error message
+# will be returned to the caller.
+# Returns `True` when the package description is fulfilled.
+@template
+def pkg_check_modules(var, package_desc, when=always,
+ allow_missing=False):
+ if isinstance(package_desc, (tuple, list)):
+ package_desc = ' '.join(package_desc)
+ package_desc = dependable(package_desc)
+
+ @depends(when, '--enable-compile-environment')
+ def when_and_compile_environment(when, compile_environment):
+ return when and compile_environment
+
+ @depends_when(pkg_config, pkg_config_version,
+ when=when_and_compile_environment)
+ def check_pkg_config(pkg_config, version):
+ min_version = '0.9.0'
+ if pkg_config is None:
+ die("*** The pkg-config script could not be found. Make sure it is\n"
+ "*** in your path, or set the PKG_CONFIG environment variable\n"
+ "*** to the full path to pkg-config.")
+ if version < min_version:
+ die("*** Your version of pkg-config is too old. You need version %s or newer.",
+ min_version)
+
+ @depends_when(pkg_config, package_desc, when=when_and_compile_environment)
+ @imports('subprocess')
+ @imports('sys')
+ @imports(_from='mozbuild.configure.util', _import='LineIO')
+ def package(pkg_config, package_desc):
+ # package_desc may start as a depends function, so we can't use
+ # @checking here.
+ log.info("checking for %s... " % package_desc)
+ with log.queue_debug():
+ try:
+ subprocess.check_output([pkg_config, '--errors-to-stdout',
+ '--print-errors', package_desc])
+ log.info("yes")
+ return True
+ except subprocess.CalledProcessError as e:
+ log.info("no")
+ log_writer = log.warning if allow_missing else log.error
+ with LineIO(lambda l: log_writer(l)) as o:
+ o.write(e.output)
+ if not allow_missing:
+ sys.exit(1)
+
+ @depends_when(pkg_config, package_desc, when=package)
+ @checking('%s_CFLAGS' % var, callback=lambda t: ' '.join(t))
+ def pkg_cflags(pkg_config, package_desc):
+ flags = check_cmd_output(pkg_config, '--cflags', package_desc)
+ return tuple(flags.split())
+
+ @depends_when(pkg_config, package_desc, when=package)
+ @checking('%s_LIBS' % var, callback=lambda t: ' '.join(t))
+ def pkg_libs(pkg_config, package_desc):
+ libs = check_cmd_output(pkg_config, '--libs', package_desc)
+ # Remove evil flags like -Wl,--export-dynamic
+ return tuple(libs.replace('-Wl,--export-dynamic', '').split())
+
+ @depends_when(pkg_cflags, pkg_libs, when=package)
+ def pkg_info(cflags, libs):
+ return namespace(cflags=cflags, libs=libs)
+
+ set_config('%s_CFLAGS' % var, pkg_cflags)
+ set_config('%s_LIBS' % var, pkg_libs)
+
+ return pkg_info
diff --git a/build/moz.configure/rust.configure b/build/moz.configure/rust.configure
new file mode 100644
index 000000000..261768f64
--- /dev/null
+++ b/build/moz.configure/rust.configure
@@ -0,0 +1,166 @@
+# -*- 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/.
+
+option('--enable-rust', help='Include Rust language sources')
+
+@depends('--enable-rust')
+def rust_compiler_names(value):
+ if value:
+ return ['rustc']
+
+@depends('--enable-rust')
+def cargo_binary_names(value):
+ if value:
+ return ['cargo']
+
+rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True)
+cargo = check_prog('CARGO', cargo_binary_names, allow_missing=True)
+
+@depends_if(rustc)
+@checking('rustc version', lambda info: info.version)
+def rustc_info(rustc):
+ out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
+ info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
+ return namespace(
+ version=Version(info.get('release', '0')),
+ commit=info.get('commit-hash', 'unknown'),
+ )
+
+@depends_if(cargo)
+@checking('cargo support for --frozen')
+@imports('subprocess')
+@imports('os')
+def cargo_supports_frozen(cargo):
+ try:
+ lines = subprocess.check_output(
+ [cargo, 'help', 'build']
+ ).splitlines()
+ supported = any(' --frozen' in l for l in lines)
+ if 'MOZ_AUTOMATION' in os.environ and not supported:
+ die('cargo in automation must support --frozen')
+ return supported
+ except subprocess.CalledProcessError as e:
+ die('Failed to call cargo: %s', e.message)
+
+set_config('MOZ_CARGO_SUPPORTS_FROZEN', cargo_supports_frozen)
+
+@depends('--enable-rust', rustc, rustc_info)
+@imports(_from='textwrap', _import='dedent')
+def rust_compiler(value, rustc, rustc_info):
+ if value:
+ if not rustc:
+ die(dedent('''\
+ Rust compiler not found.
+ To compile rust language sources, you must have 'rustc' in your path.
+ See https//www.rust-lang.org/ for more information.
+ '''))
+ version = rustc_info.version
+ min_version = Version('1.10')
+ if version < min_version:
+ die(dedent('''\
+ Rust compiler {} is too old.
+ To compile Rust language sources please install at least
+ version {} of the 'rustc' toolchain and make sure it is
+ first in your path.
+ You can verify this by typing 'rustc --version'.
+ '''.format(version, min_version)))
+ return True
+
+set_config('MOZ_RUST', rust_compiler)
+
+@depends(rust_compiler, rustc, target, cross_compiling)
+@imports('os')
+@imports('subprocess')
+@imports(_from='mozbuild.configure.util', _import='LineIO')
+@imports(_from='mozbuild.shellutil', _import='quote')
+@imports(_from='tempfile', _import='mkstemp')
+def rust_target(rust_compiler, rustc, target, cross_compiling):
+ if rust_compiler:
+ # Rust's --target options are similar to, but not exactly the same
+ # as, the autoconf-derived targets we use. An example would be that
+ # Rust uses distinct target triples for targetting the GNU C++ ABI
+ # and the MSVC C++ ABI on Win32, whereas autoconf has a single
+ # triple and relies on the user to ensure that everything is
+ # compiled for the appropriate ABI. We need to perform appropriate
+ # munging to get the correct option to rustc.
+ #
+ # The canonical list of targets supported can be derived from:
+ #
+ # https://github.com/rust-lang/rust/tree/master/mk/cfg
+
+ # Avoid having to write out os+kernel for all the platforms where
+ # they don't differ.
+ os_or_kernel = target.kernel if target.kernel == 'Linux' and target.os != 'Android' else target.os
+ rustc_target = {
+ # DragonFly
+ ('x86_64', 'DragonFly'): 'x86_64-unknown-dragonfly',
+ # FreeBSD
+ ('x86', 'FreeBSD'): 'i686-unknown-freebsd',
+ ('x86_64', 'FreeBSD'): 'x86_64-unknown-freebsd',
+ # NetBSD
+ ('x86_64', 'NetBSD'): 'x86_64-unknown-netbsd',
+ # OpenBSD
+ ('x86_64', 'OpenBSD'): 'x86_64-unknown-openbsd',
+ # Linux
+ ('x86', 'Linux'): 'i586-unknown-linux-gnu',
+ # Linux
+ ('x86_64', 'Linux'): 'x86_64-unknown-linux-gnu',
+ # OS X and iOS
+ ('x86', 'OSX'): 'i686-apple-darwin',
+ ('x86', 'iOS'): 'i386-apple-ios',
+ ('x86_64', 'OSX'): 'x86_64-apple-darwin',
+ # Android
+ ('x86', 'Android'): 'i686-linux-android',
+ ('arm', 'Android'): 'armv7-linux-androideabi',
+ # Windows
+ # XXX better detection of CXX needed here, to figure out whether
+ # we need i686-pc-windows-gnu instead, since mingw32 builds work.
+ ('x86', 'WINNT'): 'i686-pc-windows-msvc',
+ ('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc',
+ }.get((target.cpu, os_or_kernel), None)
+
+ if rustc_target is None:
+ die("Don't know how to translate {} for rustc".format(target.alias))
+
+ # Check to see whether our rustc has a reasonably functional stdlib
+ # for our chosen target.
+ target_arg = '--target=' + rustc_target
+ in_fd, in_path = mkstemp(prefix='conftest', suffix='.rs')
+ out_fd, out_path = mkstemp(prefix='conftest', suffix='.rlib')
+ os.close(out_fd)
+ try:
+ source = 'pub extern fn hello() { println!("Hello world"); }'
+ log.debug('Creating `%s` with content:', in_path)
+ with LineIO(lambda l: log.debug('| %s', l)) as out:
+ out.write(source)
+
+ os.write(in_fd, source)
+ os.close(in_fd)
+
+ cmd = [
+ rustc,
+ '--crate-type', 'staticlib',
+ target_arg,
+ '-o', out_path,
+ in_path,
+ ]
+ def failed():
+ die('Cannot compile for {} with {}'.format(target.alias, rustc))
+ check_cmd_output(*cmd, onerror=failed)
+ if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
+ failed()
+ finally:
+ os.remove(in_path)
+ os.remove(out_path)
+ # This target is usable.
+ return rustc_target
+
+set_config('RUST_TARGET', rust_target)
+
+# Until we remove all the other Rust checks in old-configure.
+add_old_configure_assignment('MOZ_RUST', rust_compiler)
+add_old_configure_assignment('RUSTC', rustc)
+add_old_configure_assignment('RUST_TARGET', rust_target)
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')
diff --git a/build/moz.configure/util.configure b/build/moz.configure/util.configure
new file mode 100644
index 000000000..33ed17de6
--- /dev/null
+++ b/build/moz.configure/util.configure
@@ -0,0 +1,440 @@
+# -*- 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/.
+
+@imports('sys')
+def die(*args):
+ 'Print an error and terminate configure.'
+ log.error(*args)
+ sys.exit(1)
+
+
+@imports(_from='mozbuild.configure', _import='ConfigureError')
+def configure_error(message):
+ '''Raise a programming error and terminate configure.
+ Primarily for use in moz.configure templates to sanity check
+ their inputs from moz.configure usage.'''
+ raise ConfigureError(message)
+
+# A wrapper to obtain a process' output that returns the output generated
+# by running the given command if it exits normally, and streams that
+# output to log.debug and calls die or the given error callback if it
+# does not.
+@imports('subprocess')
+@imports('sys')
+@imports(_from='mozbuild.configure.util', _import='LineIO')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def check_cmd_output(*args, **kwargs):
+ onerror = kwargs.pop('onerror', None)
+
+ with log.queue_debug():
+ log.debug('Executing: `%s`', quote(*args))
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = proc.communicate()
+ retcode = proc.wait()
+ if retcode == 0:
+ return stdout
+
+ log.debug('The command returned non-zero exit status %d.',
+ retcode)
+ for out, desc in ((stdout, 'output'), (stderr, 'error output')):
+ if out:
+ log.debug('Its %s was:', desc)
+ with LineIO(lambda l: log.debug('| %s', l)) as o:
+ o.write(out)
+ if onerror:
+ return onerror()
+ die('Command `%s` failed with exit status %d.' %
+ (quote(*args), retcode))
+
+@imports('os')
+def is_absolute_or_relative(path):
+ if os.altsep and os.altsep in path:
+ return True
+ return os.sep in path
+
+
+@imports(_import='mozpack.path', _as='mozpath')
+def normsep(path):
+ return mozpath.normsep(path)
+
+
+@imports('ctypes')
+@imports(_from='ctypes', _import='wintypes')
+@imports(_from='mozbuild.configure.constants', _import='WindowsBinaryType')
+def windows_binary_type(path):
+ """Obtain the type of a binary on Windows.
+
+ Returns WindowsBinaryType constant.
+ """
+ GetBinaryTypeW = ctypes.windll.kernel32.GetBinaryTypeW
+ GetBinaryTypeW.argtypes = [wintypes.LPWSTR, wintypes.POINTER(wintypes.DWORD)]
+ GetBinaryTypeW.restype = wintypes.BOOL
+
+ bin_type = wintypes.DWORD()
+ res = GetBinaryTypeW(path, ctypes.byref(bin_type))
+ if not res:
+ die('could not obtain binary type of %s' % path)
+
+ if bin_type.value == 0:
+ return WindowsBinaryType('win32')
+ elif bin_type.value == 6:
+ return WindowsBinaryType('win64')
+ # If we see another binary type, something is likely horribly wrong.
+ else:
+ die('unsupported binary type on %s: %s' % (path, bin_type))
+
+
+@imports('ctypes')
+@imports(_from='ctypes', _import='wintypes')
+def get_GetShortPathNameW():
+ GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW
+ GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR,
+ wintypes.DWORD]
+ GetShortPathNameW.restype = wintypes.DWORD
+ return GetShortPathNameW
+
+
+@template
+@imports('ctypes')
+@imports('platform')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def normalize_path():
+ # Until the build system can properly handle programs that need quoting,
+ # transform those paths into their short version on Windows (e.g.
+ # c:\PROGRA~1...).
+ if platform.system() == 'Windows':
+ GetShortPathNameW = get_GetShortPathNameW()
+
+ def normalize_path(path):
+ path = normsep(path)
+ if quote(path) == path:
+ return path
+ size = 0
+ while True:
+ out = ctypes.create_unicode_buffer(size)
+ needed = GetShortPathNameW(path, out, size)
+ if size >= needed:
+ if ' ' in out.value:
+ die("GetShortPathName returned a long path name. "
+ "Are 8dot3 filenames disabled?")
+ return normsep(out.value)
+ size = needed
+
+ else:
+ def normalize_path(path):
+ return normsep(path)
+
+ return normalize_path
+
+normalize_path = normalize_path()
+
+
+# Locates the given program using which, or returns the given path if it
+# exists.
+# The `paths` parameter may be passed to search the given paths instead of
+# $PATH.
+@imports(_from='which', _import='which')
+@imports(_from='which', _import='WhichError')
+@imports('itertools')
+@imports(_from='os', _import='pathsep')
+def find_program(file, paths=None):
+ try:
+ if is_absolute_or_relative(file):
+ return normalize_path(which(os.path.basename(file),
+ [os.path.dirname(file)]))
+ if paths:
+ if not isinstance(paths, (list, tuple)):
+ die("Paths provided to find_program must be a list of strings, "
+ "not %r", paths)
+ paths = list(itertools.chain(
+ *(p.split(pathsep) for p in paths if p)))
+ return normalize_path(which(file, path=paths))
+ except WhichError:
+ return None
+
+
+@imports('os')
+@imports('subprocess')
+@imports(_from='mozbuild.configure.util', _import='LineIO')
+@imports(_from='tempfile', _import='mkstemp')
+def try_invoke_compiler(compiler, language, source, flags=None, onerror=None):
+ flags = flags or []
+
+ if not isinstance(flags, (list, tuple)):
+ die("Flags provided to try_compile must be a list of strings, "
+ "not %r", paths)
+
+ suffix = {
+ 'C': '.c',
+ 'C++': '.cpp',
+ }[language]
+
+ fd, path = mkstemp(prefix='conftest.', suffix=suffix)
+ try:
+ source = source.encode('ascii', 'replace')
+
+ log.debug('Creating `%s` with content:', path)
+ with LineIO(lambda l: log.debug('| %s', l)) as out:
+ out.write(source)
+
+ os.write(fd, source)
+ os.close(fd)
+ cmd = compiler + list(flags) + [path]
+ kwargs = {'onerror': onerror}
+ return check_cmd_output(*cmd, **kwargs)
+ finally:
+ os.remove(path)
+
+
+def unique_list(l):
+ result = []
+ for i in l:
+ if l not in result:
+ result.append(i)
+ return result
+
+
+# Get values out of the Windows registry. This function can only be called on
+# Windows.
+# The `pattern` argument is a string starting with HKEY_ and giving the full
+# "path" of the registry key to get the value for, with backslash separators.
+# The string can contains wildcards ('*').
+# The result of this functions is an enumerator yielding tuples for each
+# match. Each of these tuples contains the key name matching wildcards
+# followed by the value.
+#
+# Examples:
+# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
+# r'Windows Kits\Installed Roots\KitsRoot*')
+# yields e.g.:
+# ('KitsRoot81', r'C:\Program Files (x86)\Windows Kits\8.1\')
+# ('KitsRoot10', r'C:\Program Files (x86)\Windows Kits\10\')
+#
+# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
+# r'Windows Kits\Installed Roots\KitsRoot8.1')
+# yields e.g.:
+# (r'C:\Program Files (x86)\Windows Kits\8.1\',)
+#
+# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
+# r'Windows Kits\*\KitsRoot*')
+# yields e.g.:
+# ('Installed Roots', 'KitsRoot81',
+# r'C:\Program Files (x86)\Windows Kits\8.1\')
+# ('Installed Roots', 'KitsRoot10',
+# r'C:\Program Files (x86)\Windows Kits\10\')
+#
+# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
+# r'VisualStudio\VC\*\x86\*\Compiler')
+# yields e.g.:
+# ('19.0', 'arm', r'C:\...\amd64_arm\cl.exe')
+# ('19.0', 'x64', r'C:\...\amd64\cl.exe')
+# ('19.0', 'x86', r'C:\...\amd64_x86\cl.exe')
+@imports(_import='_winreg', _as='winreg')
+@imports(_from='__builtin__', _import='WindowsError')
+@imports(_from='fnmatch', _import='fnmatch')
+def get_registry_values(pattern):
+ def enum_helper(func, key):
+ i = 0
+ while True:
+ try:
+ yield func(key, i)
+ except WindowsError:
+ break
+ i += 1
+
+ def get_keys(key, pattern):
+ try:
+ s = winreg.OpenKey(key, '\\'.join(pattern[:-1]))
+ except WindowsError:
+ return
+ for k in enum_helper(winreg.EnumKey, s):
+ if fnmatch(k, pattern[-1]):
+ try:
+ yield k, winreg.OpenKey(s, k)
+ except WindowsError:
+ pass
+
+ def get_values(key, pattern):
+ try:
+ s = winreg.OpenKey(key, '\\'.join(pattern[:-1]))
+ except WindowsError:
+ return
+ for k, v, t in enum_helper(winreg.EnumValue, s):
+ if fnmatch(k, pattern[-1]):
+ yield k, v
+
+ def split_pattern(pattern):
+ subpattern = []
+ for p in pattern:
+ subpattern.append(p)
+ if '*' in p:
+ yield subpattern
+ subpattern = []
+ if subpattern:
+ yield subpattern
+
+ pattern = pattern.split('\\')
+ assert pattern[0].startswith('HKEY_')
+ keys = [(getattr(winreg, pattern[0]),)]
+ pattern = list(split_pattern(pattern[1:]))
+ for i, p in enumerate(pattern):
+ next_keys = []
+ for base_key in keys:
+ matches = base_key[:-1]
+ base_key = base_key[-1]
+ if i == len(pattern) - 1:
+ want_name = '*' in p[-1]
+ for name, value in get_values(base_key, p):
+ yield matches + ((name, value) if want_name else (value,))
+ else:
+ for name, k in get_keys(base_key, p):
+ next_keys.append(matches + (name, k))
+ keys = next_keys
+
+
+@imports(_from='mozbuild.configure.util', _import='Version', _as='_Version')
+def Version(v):
+ 'A version number that can be compared usefully.'
+ return _Version(v)
+
+# Denotes a deprecated option. Combines option() and @depends:
+# @deprecated_option('--option')
+# def option(value):
+# ...
+# @deprecated_option() takes the same arguments as option(), except `help`.
+# The function may handle the option like a typical @depends function would,
+# but it is recommended it emits a deprecation error message suggesting an
+# alternative option to use if there is one.
+@template
+def deprecated_option(*args, **kwargs):
+ assert 'help' not in kwargs
+ kwargs['help'] = 'Deprecated'
+ opt = option(*args, **kwargs)
+
+ def decorator(func):
+ @depends(opt.option)
+ def deprecated(value):
+ if value.origin != 'default':
+ return func(value)
+ return deprecated
+
+ return decorator
+
+
+# from mozbuild.util import ReadOnlyNamespace as namespace
+@imports(_from='mozbuild.util', _import='ReadOnlyNamespace')
+def namespace(**kwargs):
+ return ReadOnlyNamespace(**kwargs)
+
+
+# Turn an object into an object that can be used as an argument to @depends.
+# The given object can be a literal value, a function that takes no argument,
+# or, for convenience, a @depends function.
+@template
+@imports(_from='inspect', _import='isfunction')
+@imports(_from='mozbuild.configure', _import='SandboxDependsFunction')
+def dependable(obj):
+ if isinstance(obj, SandboxDependsFunction):
+ return obj
+ if isfunction(obj):
+ return depends(when=True)(obj)
+ return depends(when=True)(lambda: obj)
+
+
+always = dependable(True)
+never = dependable(False)
+
+
+# Some @depends function return namespaces, and one could want to use one
+# specific attribute from such a namespace as a "value" given to functions
+# such as `set_config`. But those functions do not take immediate values.
+# The `delayed_getattr` function allows access to attributes from the result
+# of a @depends function in a non-immediate manner.
+# @depends('--option')
+# def option(value)
+# return namespace(foo=value)
+# set_config('FOO', delayed_getattr(option, 'foo')
+@template
+def delayed_getattr(func, key):
+ @depends(func)
+ def result(value):
+ # The @depends function we're being passed may have returned
+ # None, or an object that simply doesn't have the wanted key.
+ # In that case, just return None.
+ return getattr(value, key, None)
+
+ return result
+
+
+# Like @depends, but the decorated function is only called if one of the
+# arguments it would be called with has a positive value (bool(value) is True)
+@template
+def depends_if(*args):
+ def decorator(func):
+ @depends(*args)
+ def wrapper(*args):
+ if any(arg for arg in args):
+ return func(*args)
+ return wrapper
+ return decorator
+
+# Like @depends_if, but a distinguished value passed as a keyword argument
+# "when" is truth tested instead of every argument. This value is not passed
+# to the function if it is called.
+@template
+def depends_when(*args, **kwargs):
+ if not len(kwargs) == 1 and kwargs.get('when'):
+ die('depends_when requires a single keyword argument, "when"')
+ when = kwargs['when']
+ if not when:
+ return depends(*args)
+
+ def decorator(fn):
+ @depends(when, *args)
+ def wrapper(val, *args):
+ if val:
+ return fn(*args)
+ return wrapper
+ return decorator
+
+
+# Hacks related to old-configure
+# ==============================
+
+@dependable
+def old_configure_assignments():
+ return []
+
+@dependable
+def extra_old_configure_args():
+ return []
+
+@template
+def add_old_configure_assignment(var, value):
+ var = dependable(var)
+ value = dependable(value)
+
+ @depends(old_configure_assignments, var, value)
+ @imports(_from='mozbuild.shellutil', _import='quote')
+ def add_assignment(assignments, var, value):
+ if var is None or value is None:
+ return
+ if value is True:
+ assignments.append('%s=1' % var)
+ elif value is False:
+ assignments.append('%s=' % var)
+ else:
+ if isinstance(value, (list, tuple)):
+ value = quote(*value)
+ assignments.append('%s=%s' % (var, quote(str(value))))
+
+@template
+def add_old_configure_arg(arg):
+ @depends(extra_old_configure_args, arg)
+ def add_arg(args, arg):
+ if arg:
+ args.append(arg)
diff --git a/build/moz.configure/warnings.configure b/build/moz.configure/warnings.configure
new file mode 100644
index 000000000..273a41bd9
--- /dev/null
+++ b/build/moz.configure/warnings.configure
@@ -0,0 +1,111 @@
+# -*- 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/.
+
+js_option('--enable-warnings-as-errors', env='MOZ_ENABLE_WARNINGS_AS_ERRORS',
+ default=depends('MOZ_AUTOMATION', '--help')(lambda x, _: bool(x)),
+ help='Enable treating warnings as errors')
+
+add_old_configure_assignment(
+ 'MOZ_ENABLE_WARNINGS_AS_ERRORS',
+ depends('--enable-warnings-as-errors')(lambda x: bool(x)))
+
+
+# GCC/Clang warnings:
+# https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Warning-Options.html
+
+# lots of useful warnings
+add_gcc_warning('-Wall')
+
+# catches C++ version forward-compat issues
+add_gcc_warning('-Wc++11-compat', cxx_compiler)
+
+# catches bugs, e.g. "if (c); foo();", few false positives
+add_gcc_warning('-Wempty-body')
+
+# catches return types with qualifiers like const
+add_gcc_warning('-Wignored-qualifiers')
+
+# function declaration hides virtual function from base class
+add_gcc_warning('-Woverloaded-virtual', cxx_compiler)
+
+# catches pointer arithmetic using NULL or sizeof(void)
+add_gcc_warning('-Wpointer-arith')
+
+# catches comparing signed/unsigned ints
+add_gcc_warning('-Wsign-compare')
+
+# catches overflow bugs, few false positives
+add_gcc_warning('-Wtype-limits')
+
+# catches some dead code
+add_gcc_warning('-Wunreachable-code')
+
+# catches treating string literals as non-const
+add_gcc_warning('-Wwrite-strings', cxx_compiler)
+
+# turned on by -Wall, but we use offsetof on non-POD types frequently
+add_gcc_warning('-Wno-invalid-offsetof', cxx_compiler)
+
+# catches objects passed by value to variadic functions.
+check_and_add_gcc_warning('-Wclass-varargs')
+
+# catches issues around loops
+check_and_add_gcc_warning('-Wloop-analysis')
+
+# catches C++ version forward-compat issues
+check_and_add_gcc_warning('-Wc++11-compat-pedantic', cxx_compiler)
+check_and_add_gcc_warning('-Wc++14-compat', cxx_compiler)
+check_and_add_gcc_warning('-Wc++14-compat-pedantic', cxx_compiler)
+check_and_add_gcc_warning('-Wc++1z-compat', cxx_compiler)
+
+# catches unintentional switch case fallthroughs
+check_and_add_gcc_warning('-Wimplicit-fallthrough', cxx_compiler)
+
+# catches expressions used as a null pointer constant
+# XXX: at the time of writing, the version of clang used on the OS X test
+# machines has a bug that causes it to reject some valid files if both
+# -Wnon-literal-null-conversion and -Wsometimes-uninitialized are
+# specified. We work around this by instead using
+# -Werror=non-literal-null-conversion, but we only do that when
+# --enable-warnings-as-errors is specified so that no unexpected fatal
+# warnings are produced.
+check_and_add_gcc_warning('-Werror=non-literal-null-conversion',
+ when='--enable-warnings-as-errors')
+
+# catches string literals used in boolean expressions
+check_and_add_gcc_warning('-Wstring-conversion')
+
+# catches inconsistent use of mutexes
+check_and_add_gcc_warning('-Wthread-safety')
+
+# we inline 'new' and 'delete' in mozalloc
+check_and_add_gcc_warning('-Wno-inline-new-delete', cxx_compiler)
+
+# Prevent the following GCC warnings from being treated as errors:
+# too many false positives
+check_and_add_gcc_warning('-Wno-error=maybe-uninitialized')
+
+# we don't want our builds held hostage when a platform-specific API
+# becomes deprecated.
+check_and_add_gcc_warning('-Wno-error=deprecated-declarations')
+
+# false positives depending on optimization
+check_and_add_gcc_warning('-Wno-error=array-bounds')
+
+# can't get rid of those PGO warnings
+check_and_add_gcc_warning('-Wno-error=coverage-mismatch', when='MOZ_PGO')
+
+# false positives during PGO
+check_and_add_gcc_warning('-Wno-error=free-nonheap-object', when='MOZ_PGO')
+
+# We use mix of both POSIX and Win32 printf format across the tree, so format
+# warnings are useless on mingw.
+check_and_add_gcc_warning('-Wno-format',
+ when=depends(target)(lambda t: t.kernel == 'WINNT'))
+
+# Please keep these last in this file
+add_old_configure_assignment('_WARNINGS_CFLAGS', warnings_cflags)
+add_old_configure_assignment('_WARNINGS_CXXFLAGS', warnings_cxxflags)
diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure
new file mode 100644
index 000000000..b9a3898a1
--- /dev/null
+++ b/build/moz.configure/windows.configure
@@ -0,0 +1,431 @@
+# -*- 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/.
+
+option('--with-windows-version', nargs=1, default='603',
+ help='Windows SDK version to target. Win 8.1 (603) is currently'
+ 'the minimum supported version.')
+
+@depends(target)
+def is_windows(target):
+ return target.kernel == 'WINNT'
+
+
+@template
+def depends_win(*args):
+ return depends_when(*args, when=is_windows)
+
+
+@depends_win('--with-windows-version')
+@imports(_from='__builtin__', _import='ValueError')
+def valid_windows_version(value):
+ if not value:
+ die('Cannot build with --without-windows-version')
+ try:
+ version = int(value[0], 16)
+ if version in (0x603,):
+ return version
+ except ValueError:
+ pass
+
+ die('Invalid value for --with-windows-version (%s)', value[0])
+
+
+option(env='WINDOWSSDKDIR', nargs=1,
+ help='Directory containing the Windows SDK')
+
+@depends_win('WINDOWSSDKDIR', host)
+def windows_sdk_dir(value, host):
+ if value:
+ return value
+ if host.kernel != 'WINNT':
+ return ()
+
+ return tuple(x[1] for x in get_registry_values(
+ r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
+ r'\KitsRoot*'))
+
+# The Windows SDK 8.1 and 10 have different layouts. The former has
+# $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir.
+# The vcvars* scripts don't actually care about the version, they just take
+# the last alphanumerically.
+# The $SDK/lib directories always have version subdirectories, but while the
+# versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK
+# 8.1.
+@imports('os')
+@imports('re')
+@imports(_from='__builtin__', _import='sorted')
+@imports(_from='__builtin__', _import='WindowsError')
+def get_sdk_dirs(sdk, subdir):
+ def get_dirs_containing(sdk, stem, subdir):
+ base = os.path.join(sdk, stem)
+ try:
+ subdirs = [d for d in os.listdir(base)
+ if os.path.isdir(os.path.join(base, d))]
+ except WindowsError:
+ subdirs = []
+ if not subdirs:
+ return ()
+ if subdir in subdirs:
+ return (base,)
+ # At this point, either we have an incomplete or invalid SDK directory,
+ # or we exclusively have version numbers in subdirs.
+ return tuple(os.path.join(base, s) for s in subdirs
+ if os.path.isdir(os.path.join(base, s, subdir)))
+
+ def categorize(dirs):
+ return {os.path.basename(d): d for d in dirs}
+
+ include_dirs = categorize(get_dirs_containing(sdk, 'include', subdir))
+ lib_dirs = categorize(get_dirs_containing(sdk, 'lib', subdir))
+
+ if 'include' in include_dirs:
+ include_dirs['winv6.3'] = include_dirs['include']
+ del include_dirs['include']
+
+ valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True)
+ if valid_versions:
+ return namespace(
+ path=sdk,
+ lib=lib_dirs[valid_versions[0]],
+ include=include_dirs[valid_versions[0]],
+ )
+
+
+@imports(_from='mozbuild.shellutil', _import='quote')
+def valid_windows_sdk_dir_result(value):
+ if value:
+ return '0x%04x in %s' % (value.version, quote(value.path))
+
+@depends_win(c_compiler, windows_sdk_dir, valid_windows_version,
+ 'WINDOWSSDKDIR')
+@checking('for Windows SDK', valid_windows_sdk_dir_result)
+@imports(_from='__builtin__', _import='sorted')
+@imports(_from='textwrap', _import='dedent')
+def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
+ windows_sdk_dir_env):
+ if windows_sdk_dir_env:
+ windows_sdk_dir_env = windows_sdk_dir_env[0]
+ sdks = {}
+ for d in windows_sdk_dir:
+ sdk = get_sdk_dirs(d, 'um')
+ if sdk:
+ check = dedent('''\
+ #include <winsdkver.h>
+ WINVER_MAXVER
+ ''')
+ um_dir = os.path.join(sdk.include, 'um')
+ shared_dir = os.path.join(sdk.include, 'shared')
+ result = try_preprocess(compiler.wrapper + [compiler.compiler] +
+ compiler.flags +
+ ['-I', um_dir, '-I', shared_dir], 'C',
+ check)
+ if result:
+ maxver = result.splitlines()[-1]
+ try:
+ maxver = int(maxver, 0)
+ except:
+ pass
+ else:
+ sdks[d] = maxver, sdk
+ continue
+ if d == windows_sdk_dir_env:
+ raise FatalCheckError(
+ 'Error while checking the version of the SDK in '
+ 'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
+ 'complete SDK installation.' % windows_sdk_dir_env)
+
+ valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
+ if valid_sdks:
+ biggest_version, sdk = sdks[valid_sdks[0]]
+ if not valid_sdks or biggest_version < target_version:
+ if windows_sdk_dir_env:
+ raise FatalCheckError(
+ 'You are targeting Windows version 0x%04x, but your SDK only '
+ 'supports up to version 0x%04x. Install and use an updated SDK, '
+ 'or target a lower version using --with-windows-version. '
+ 'Alternatively, try running the Windows SDK Configuration Tool '
+ 'and selecting a newer SDK. See '
+ 'https://developer.mozilla.org/En/Windows_SDK_versions for '
+ 'details on fixing this.' % (target_version, biggest_version))
+
+ raise FatalCheckError(
+ 'Cannot find a Windows SDK for version >= 0x%04x.' % target_version)
+
+ return namespace(
+ path=sdk.path,
+ include=sdk.include,
+ lib=sdk.lib,
+ version=biggest_version,
+ )
+
+
+add_old_configure_assignment(
+ 'WINDOWSSDKDIR',
+ delayed_getattr(valid_windows_sdk_dir, 'path'))
+add_old_configure_assignment(
+ 'MOZ_WINSDK_MAXVER',
+ depends(valid_windows_sdk_dir)(
+ lambda x: '0x%04X0000' % x.version if x else None))
+
+
+@imports(_from='mozbuild.shellutil', _import='quote')
+def valid_ucrt_sdk_dir_result(value):
+ if value:
+ return '%s in %s' % (value.version, quote(value.path))
+
+@depends_win(windows_sdk_dir, 'WINDOWSSDKDIR')
+@checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
+@imports('os')
+@imports(_from='__builtin__', _import='sorted')
+@imports(_import='mozpack.path', _as='mozpath')
+def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
+ if windows_sdk_dir_env:
+ windows_sdk_dir_env = windows_sdk_dir_env[0]
+ sdks = {}
+ for d in windows_sdk_dir:
+ sdk = get_sdk_dirs(d, 'ucrt')
+ if sdk:
+ version = os.path.basename(sdk.include)
+ # We're supposed to always find a version in the directory, because
+ # the 8.1 SDK, which doesn't have a version in the directory, doesn't
+ # contain the Universal CRT SDK. When the main SDK is 8.1, there
+ # is, however, supposed to be a reduced install of the SDK 10
+ # with the UCRT.
+ if version != 'include':
+ sdks[d] = Version(version), sdk
+ continue
+ if d == windows_sdk_dir_env:
+ # When WINDOWSSDKDIR is set in the environment and we can't find the
+ # Universal CRT SDK, chances are this is a start-shell-msvc*.bat
+ # setup, where INCLUDE and LIB already contain the UCRT paths.
+ ucrt_includes = [
+ p for p in os.environ.get('INCLUDE', '').split(os.pathsep)
+ if os.path.basename(p).lower() == 'ucrt'
+ ]
+ ucrt_libs = [
+ p for p in os.environ.get('LIB', '').split(os.pathsep)
+ if os.path.basename(os.path.dirname(p)).lower() == 'ucrt'
+ ]
+ if ucrt_includes and ucrt_libs:
+ # Pick the first of each, since they are the ones that the
+ # compiler would look first. Assume they contain the SDK files.
+ include = os.path.dirname(ucrt_includes[0])
+ lib = os.path.dirname(os.path.dirname(ucrt_libs[0]))
+ path = os.path.dirname(os.path.dirname(include))
+ version = os.path.basename(include)
+ if version != 'include' and mozpath.basedir(lib, [path]):
+ sdks[d] = Version(version), namespace(
+ path=path,
+ include=include,
+ lib=lib,
+ )
+ continue
+ raise FatalCheckError(
+ 'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
+ 'CRT.' % windows_sdk_dir_env)
+
+ valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
+ if not valid_sdks:
+ raise FatalCheckError('Cannot find the Universal CRT SDK. '
+ 'Please install it.')
+
+ version, sdk = sdks[valid_sdks[0]]
+
+ return namespace(
+ path=sdk.path,
+ include=sdk.include,
+ lib=sdk.lib,
+ version=version,
+ )
+
+
+@depends_win(c_compiler)
+@imports('os')
+def vc_path(c_compiler):
+ if c_compiler.type != 'msvc':
+ return
+ # Normally, we'd start from c_compiler.compiler, but for now, it's not the
+ # ideal full path to the compiler. At least, we're guaranteed find_program
+ # will get us the one we found in toolchain.configure.
+ cl = find_program(c_compiler.compiler)
+ result = os.path.dirname(cl)
+ while True:
+ next, p = os.path.split(result)
+ if next == result:
+ die('Cannot determine the Visual C++ directory the compiler (%s) '
+ 'is in' % cl)
+ result = next
+ if p.lower() == 'bin':
+ break
+ return result
+
+
+@depends_win(vc_path)
+@checking('for the Debug Interface Access SDK', lambda x: x or 'not found')
+@imports(_from='os.path', _import='isdir')
+def dia_sdk_dir(vc_path):
+ if vc_path:
+ path = os.path.join(os.path.dirname(vc_path), 'DIA SDK')
+ if isdir(path):
+ return path
+
+
+@depends_win(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
+@imports('os')
+def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
+ if not vc_path:
+ return
+ atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'include')
+ if not os.path.isdir(atlmfc_dir):
+ die('Cannot find the ATL/MFC headers in the Visual C++ directory (%s). '
+ 'Please install them.' % vc_path)
+
+ winrt_dir = os.path.join(windows_sdk_dir.include, 'winrt')
+ if not os.path.isdir(winrt_dir):
+ die('Cannot find the WinRT headers in the Windows SDK directory (%s). '
+ 'Please install them.' % windows_sdk_dir.path)
+
+ includes = []
+ include_env = os.environ.get('INCLUDE')
+ if include_env:
+ includes.append(include_env)
+ includes.extend((
+ os.path.join(vc_path, 'include'),
+ atlmfc_dir,
+ os.path.join(windows_sdk_dir.include, 'shared'),
+ os.path.join(windows_sdk_dir.include, 'um'),
+ winrt_dir,
+ os.path.join(ucrt_sdk_dir.include, 'ucrt'),
+ ))
+ if dia_sdk_dir:
+ includes.append(os.path.join(dia_sdk_dir, 'include'))
+ # Set in the environment for old-configure
+ includes = os.pathsep.join(includes)
+ os.environ['INCLUDE'] = includes
+ return includes
+
+set_config('INCLUDE', include_path)
+
+
+@depends_win(target, vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
+@imports('os')
+def lib_path(target, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
+ if not vc_path:
+ return
+ vc_target = {
+ 'x86': '',
+ 'x86_64': 'amd64',
+ 'arm': 'arm',
+ }.get(target.cpu)
+ if vc_target is None:
+ return
+ # As vc_target can be '', and os.path.join will happily use the empty
+ # string, leading to a string ending with a backslash, that Make will
+ # interpret as a "string continues on next line" indicator, use variable
+ # args.
+ vc_target = (vc_target,) if vc_target else ()
+ sdk_target = {
+ 'x86': 'x86',
+ 'x86_64': 'x64',
+ 'arm': 'arm',
+ }.get(target.cpu)
+
+ atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_target)
+ if not os.path.isdir(atlmfc_dir):
+ die('Cannot find the ATL/MFC libraries in the Visual C++ directory (%s). '
+ 'Please install them.' % vc_path)
+
+
+ libs = []
+ lib_env = os.environ.get('LIB')
+ if lib_env:
+ libs.append(lib_env)
+ libs.extend((
+ os.path.join(vc_path, 'lib', *vc_target),
+ atlmfc_dir,
+ os.path.join(windows_sdk_dir.lib, 'um', sdk_target),
+ os.path.join(ucrt_sdk_dir.lib, 'ucrt', sdk_target),
+ ))
+ if dia_sdk_dir:
+ libs.append(os.path.join(dia_sdk_dir, 'lib', *vc_target))
+ # Set in the environment for old-configure
+ libs = os.pathsep.join(libs)
+ os.environ['LIB'] = libs
+ return libs
+
+set_config('LIB', lib_path)
+
+
+option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool')
+
+@depends_win(valid_windows_sdk_dir)
+@imports(_from='os', _import='environ')
+@imports('platform')
+def sdk_bin_path(valid_windows_sdk_dir):
+ if not valid_windows_sdk_dir:
+ return
+
+ vc_host = {
+ 'x86': 'x86',
+ 'AMD64': 'x64',
+ }.get(platform.machine())
+
+ result = [
+ environ['PATH'],
+ os.path.join(valid_windows_sdk_dir.path, 'bin', vc_host)
+ ]
+ if vc_host == 'x64':
+ result.append(
+ os.path.join(valid_windows_sdk_dir.path, 'bin', 'x86'))
+ return result
+
+
+mt = check_prog('MT', depends_win()(lambda: ('mt.exe',)), input='MT',
+ paths=sdk_bin_path)
+
+
+# Check that MT is not something unexpected like "magnetic tape manipulation
+# utility".
+@depends_win(mt)
+@checking('whether MT is really Microsoft Manifest Tool', lambda x: bool(x))
+@imports('subprocess')
+def valid_mt(path):
+ try:
+ out = subprocess.check_output([path]).splitlines()
+ out = '\n'.join(l for l in out
+ if 'Microsoft (R) Manifest Tool' in l)
+ if out:
+ return path
+ except subprocess.CalledProcessError:
+ pass
+ raise FatalCheckError('%s is not Microsoft Manifest Tool')
+
+
+set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x)))
+
+
+# Ultimately, this will move to toolchain.configure and be turned into a
+# cross-platform check.
+option(env='LD', nargs=1, help='Path to the linker')
+
+link = check_prog('LINK', depends_win()(lambda: ('link.exe',)), input='LD',
+ paths=vc_compiler_path)
+
+add_old_configure_assignment('LD', depends_win(link)(lambda x: x))
+
+
+# Normally, we'd just have CC, etc. set to absolute paths, but the build system
+# doesn't currently handle properly the case where the paths contain spaces.
+# Additionally, there's the issue described in toolchain.configure, in
+# valid_compiler().
+@depends_win(sdk_bin_path)
+@imports('os')
+def alter_path(sdk_bin_path):
+ path = os.pathsep.join(sdk_bin_path)
+ os.environ['PATH'] = path
+ return path
+
+set_config('PATH', alter_path)