Bug 1253203 - Move parts of configure.py into sandboxed moz.configure. r=nalexander,r=chmanchester
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 04 Mar 2016 17:31:10 +0900
changeset 312360 e654e71b61b5f11198c6732254f1aa41c01bdc45
parent 312359 671a3f345959ef5aacdedd184e35c2e4223aef6a
child 312361 2439b4a424353eb0b3f97ca56717ac317790f24f
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-esr52@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander, chmanchester
bugs1253203
milestone47.0a1
Bug 1253203 - Move parts of configure.py into sandboxed moz.configure. r=nalexander,r=chmanchester This moves all the reading mozconfig, finding autoconf, refreshing the old configure, and running the old configure into sandboxed moz.configure. This effectively bootstraps the sandboxed python configure.
build/autoconf/altoptions.m4
build/autoconf/config.status.m4
build/moz.configure/init.configure
build/moz.configure/old.configure
build/moz.configure/util.configure
configure.py
js/src/old-configure.in
moz.configure
old-configure.in
python/mozbuild/mozbuild/mach_commands.py
--- a/build/autoconf/altoptions.m4
+++ b/build/autoconf/altoptions.m4
@@ -15,109 +15,81 @@ dnl MOZ_ARG_DISABLE_BOOL(          NAME,
 dnl MOZ_ARG_ENABLE_STRING(         NAME, HELP, IF-SET [, ELSE])
 dnl MOZ_ARG_ENABLE_BOOL_OR_STRING( NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]])
 dnl MOZ_ARG_WITH_BOOL(             NAME, HELP, IF-YES [, IF-NO [, ELSE])
 dnl MOZ_ARG_WITHOUT_BOOL(          NAME, HELP, IF-NO [, IF-YES [, ELSE])
 dnl MOZ_ARG_WITH_STRING(           NAME, HELP, IF-SET [, ELSE])
 dnl MOZ_ARG_HEADER(Comment)
 dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file
 
+define([MOZ_DIVERSION_ARGS], 12)
+
+AC_DEFUN([MOZ_ARG],[dnl
+AC_DIVERT_PUSH(MOZ_DIVERSION_ARGS)dnl
+    '$1',
+AC_DIVERT_POP()dnl
+])
+AC_DEFUN([MOZ_AC_ARG_ENABLE],[MOZ_ARG([--enable-]translit([$1],[_],[-]))AC_ARG_ENABLE([$1], [$2], [$3], [$4])])
+AC_DEFUN([MOZ_AC_ARG_WITH],[MOZ_ARG([--with-]translit([$1],[_],[-]))AC_ARG_WITH([$1], [$2], [$3], [$4])])
 
 dnl MOZ_TWO_STRING_TEST(NAME, VAL, STR1, IF-STR1, STR2, IF-STR2 [, ELSE])
 AC_DEFUN([MOZ_TWO_STRING_TEST],
 [if test "[$2]" = "[$3]"; then
     ifelse([$4], , :, [$4])
   elif test "[$2]" = "[$5]"; then
     ifelse([$6], , :, [$6])
   else
     ifelse([$7], ,
       [AC_MSG_ERROR([Option, [$1], does not take an argument ([$2]).])],
       [$7])
   fi])
 
 dnl MOZ_ARG_ENABLE_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]])
 AC_DEFUN([MOZ_ARG_ENABLE_BOOL],
-[AC_ARG_ENABLE([$1], [$2], 
+[MOZ_AC_ARG_ENABLE([$1], [$2],
  [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4])],
  [$5])])
 
 dnl MOZ_ARG_DISABLE_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]])
 AC_DEFUN([MOZ_ARG_DISABLE_BOOL],
-[AC_ARG_ENABLE([$1], [$2],
+[MOZ_AC_ARG_ENABLE([$1], [$2],
  [MOZ_TWO_STRING_TEST([$1], [$enableval], no, [$3], yes, [$4])],
  [$5])])
 
 dnl MOZ_ARG_ENABLE_STRING(NAME, HELP, IF-SET [, ELSE])
 AC_DEFUN([MOZ_ARG_ENABLE_STRING],
-[AC_ARG_ENABLE([$1], [$2], [$3], [$4])])
+[MOZ_AC_ARG_ENABLE([$1], [$2], [$3], [$4])])
 
 dnl MOZ_ARG_ENABLE_BOOL_OR_STRING(NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]])
 AC_DEFUN([MOZ_ARG_ENABLE_BOOL_OR_STRING],
 [ifelse([$5], , 
  [errprint([Option, $1, needs an "IF-SET" argument.
 ])
   m4exit(1)],
- [AC_ARG_ENABLE([$1], [$2],
+ [MOZ_AC_ARG_ENABLE([$1], [$2],
   [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4], [$5])],
   [$6])])])
 
 dnl MOZ_ARG_WITH_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE])
 AC_DEFUN([MOZ_ARG_WITH_BOOL],
-[AC_ARG_WITH([$1], [$2],
+[MOZ_AC_ARG_WITH([$1], [$2],
  [MOZ_TWO_STRING_TEST([$1], [$withval], yes, [$3], no, [$4])],
  [$5])])
 
 dnl MOZ_ARG_WITHOUT_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE])
 AC_DEFUN([MOZ_ARG_WITHOUT_BOOL],
-[AC_ARG_WITH([$1], [$2],
+[MOZ_AC_ARG_WITH([$1], [$2],
  [MOZ_TWO_STRING_TEST([$1], [$withval], no, [$3], yes, [$4])],
  [$5])])
 
 dnl MOZ_ARG_WITH_STRING(NAME, HELP, IF-SET [, ELSE])
 AC_DEFUN([MOZ_ARG_WITH_STRING],
-[AC_ARG_WITH([$1], [$2], [$3], [$4])])
+[MOZ_AC_ARG_WITH([$1], [$2], [$3], [$4])])
 
 dnl MOZ_ARG_HEADER(Comment)
 dnl This is used by webconfig to group options
 define(MOZ_ARG_HEADER, [# $1])
 
 dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file
 AC_DEFUN([MOZ_READ_MOZCONFIG],
 [AC_REQUIRE([AC_INIT_BINSH])dnl
-inserted=
-dnl Shell is hard, so here is what the following does:
-dnl - Reset $@ (command line arguments)
-dnl - Add the configure options from mozconfig to $@ one by one
-dnl - Add the original command line arguments after that, one by one
-dnl
-dnl There are several tricks involved:
-dnl - It is not possible to preserve the whitespaces in $@ by assigning to
-dnl   another variable, so the two first steps above need to happen in the first
-dnl   iteration of the third step.
-dnl - We always want the configure options to be added, so the loop must be
-dnl   iterated at least once, so we add a dummy argument first, and discard it.
-dnl - something | while read line ... makes the while run in a subshell, meaning
-dnl   that anything it does is not propagated to the main shell, so we can't do
-dnl   set -- foo there. As a consequence, what the while loop reading mach
-dnl   environment output does is output a set of shell commands for the main shell
-dnl   to eval.
-dnl - Extra care is due when lines from mach environment output contain special
-dnl   shell characters, so we use ' for quoting and ensure no ' end up in between
-dnl   the quoting mark unescaped.
-dnl Some of the above is directly done in mach environment --format=configure.
-failed_eval() {
-  echo "Failed eval'ing the following:"
-  $(dirname [$]0)/[$1]/mach environment --format=configure
-  exit 1
-}
-
-set -- dummy "[$]@"
-for ac_option
-do
-  if test -z "$inserted"; then
-    set --
-    eval "$($(dirname [$]0)/[$1]/mach environment --format=configure)" || failed_eval
-    inserted=1
-  else
-    set -- "[$]@" "$ac_option"
-  fi
-done
+. ./old-configure.vars
 ])
--- a/build/autoconf/config.status.m4
+++ b/build/autoconf/config.status.m4
@@ -152,16 +152,20 @@ EOF
 if test -n "$_NON_GLOBAL_ACDEFINES"; then
   for var in $_NON_GLOBAL_ACDEFINES; do
     echo "    '$var'," >> $CONFIG_STATUS
   done
 fi
 
 cat >> $CONFIG_STATUS <<EOF
 ]
+
+flags = [
+undivert(MOZ_DIVERSION_ARGS)dnl
+]
 EOF
 
 changequote([, ])
 ])
 
 define([m4_fatal],[
 errprint([$1
 ])
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/init.configure
@@ -0,0 +1,122 @@
+# -*- 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/.
+
+include('util.configure')
+
+option(env='DIST', nargs=1, help='DIST directory')
+
+# Do not allow objdir == srcdir builds.
+# ==============================================================
+@depends('--help', 'DIST')
+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__), '..', '..')))
+
+    set_config('TOPSRCDIR', topsrcdir)
+    set_config('TOPOBJDIR', topobjdir)
+    set_config('MOZ_BUILD_ROOT', topobjdir)
+    if dist:
+        set_config('DIST', normsep(dist[0]))
+    else:
+        set_config('DIST', os.path.join(topobjdir, 'dist'))
+
+    if help:
+        return
+
+    if topsrcdir == topobjdir:
+        error(
+            '  ***\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 os.path.exists(os.path.join(topsrcdir, f))
+    ]
+    if conflict_files:
+        error(
+            '  ***\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)
+        )
+
+
+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', check_build_environment, '--help')
+@advanced
+def mozconfig(current_project, mozconfig,  build_env, help):
+    from mozbuild.mozconfig import MozconfigLoader
+
+    # Don't read the mozconfig for the js configure (yay backwards
+    # compatibility)
+    if build_env['TOPOBJDIR'].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
+
+
+@template
+@advanced
+def command_line_helper():
+    # This escapes the sandbox. Don't copy this. This is only here because
+    # it is a one off and because the required functionality doesn't need
+    # to be exposed for other usecases.
+    return depends.__self__._helper
+
+
+@depends(mozconfig)
+def mozconfig_options(mozconfig):
+    if mozconfig['path']:
+        helper = command_line_helper()
+        warn('Adding configure options from %s' % mozconfig['path'])
+        for arg in mozconfig['configure_args']:
+            warn('  %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)
+
+        # Ideally we'd handle mozconfig['env'] and mozconfig['vars'] here,
+        # but at the moment, moz.configure has no knowledge of the options
+        # that may appear there. We'll opt-in when we move things from
+        # old-configure.in, which will be tedious but necessary until we
+        # can discriminate what old-configure.in supports.
+
+del command_line_helper
+
+
+option(env='MOZILLABUILD', nargs=1,
+       help='Path to Mozilla Build (Windows-only)')
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/old.configure
@@ -0,0 +1,399 @@
+# -*- 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/.
+
+# It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py,
+# but the end goal being that the configure script would go away...
+@depends('MOZILLABUILD')
+@advanced
+def shell(mozillabuild):
+    import sys
+
+    shell = 'sh'
+    if mozillabuild:
+        shell = mozillabuild[0] + '/msys/bin/sh'
+    if sys.platform == 'win32':
+        shell = shell + '.exe'
+    return shell
+
+
+option(env='AUTOCONF', nargs=1, help='Path to autoconf 2.13')
+
+@depends(mozconfig, 'AUTOCONF')
+@advanced
+def autoconf(mozconfig, autoconf):
+    import re
+
+    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 find:
+            autoconf = os.path.normpath(os.path.join(
+                fink, '..', '..', 'lib', 'autoconf2.13', 'bin', 'autoconf'))
+
+    if not autoconf:
+        error('Could not find autoconf 2.13')
+
+    set_config('AUTOCONF', autoconf)
+    return autoconf
+
+
+option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script')
+
+@depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell)
+@advanced
+def prepare_configure(old_configure, mozconfig, autoconf, build_env, shell):
+    import glob
+    import itertools
+    import subprocess
+    import sys
+    # Import getmtime without overwriting the sandbox os.path.
+    from os.path import getmtime
+
+    from mozbuild.shellutil import quote
+
+    if not old_configure:
+        error('The OLD_CONFIGURE environment variable must be set')
+
+    # 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])
+
+    refresh = True
+    if os.path.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:
+        warn('Refreshing %s with %s' % (old_configure, autoconf))
+        with open(old_configure, 'wb') as fh:
+            subprocess.check_call([
+                shell, autoconf,
+                '--localdir=%s' % os.path.dirname(old_configure),
+                old_configure + '.in'], stdout=fh)
+
+    cmd = [shell, old_configure] + sys.argv[1:]
+    with open('old-configure.vars', 'w') as out:
+        if mozconfig['path']:
+            if mozconfig['configure_args']:
+                cmd += mozconfig['configure_args']
+
+            for key, value in mozconfig['env']['added'].items():
+                print("export %s=%s" % (key, quote(value)), file=out)
+            for key, (old, value) in mozconfig['env']['modified'].items():
+                print("export %s=%s" % (key, quote(value)), file=out)
+            for key, value in mozconfig['vars']['added'].items():
+                print("%s=%s" % (key, quote(value)), file=out)
+            for key, (old, value) in mozconfig['vars']['modified'].items():
+                print("%s=%s" % (key, quote(value)), file=out)
+            for t in ('env', 'vars'):
+                for key in mozconfig[t]['removed'].keys():
+                    print("unset %s" % key, file=out)
+
+    return cmd
+
+
+@template
+def old_configure_options(*options):
+    for opt in options:
+        option(opt, nargs='*', help='Help missing for old configure options')
+
+    @depends('--help')
+    def all_options(help):
+        return set(options)
+
+    return depends(prepare_configure, all_options, *options)
+
+
+@old_configure_options(
+    '--cache-file',
+    '--enable-accessibility',
+    '--enable-address-sanitizer',
+    '--enable-alsa',
+    '--enable-android-apz',
+    '--enable-android-omx',
+    '--enable-android-resource-constrained',
+    '--enable-application',
+    '--enable-approximate-location',
+    '--enable-artifact-builds',
+    '--enable-b2g-bt',
+    '--enable-b2g-camera',
+    '--enable-b2g-ril',
+    '--enable-build-backend',
+    '--enable-bundled-fonts',
+    '--enable-callgrind',
+    '--enable-chrome-format',
+    '--enable-clang-plugin',
+    '--enable-compile-environment',
+    '--enable-content-sandbox',
+    '--enable-cookies',
+    '--enable-cpp-rtti',
+    '--enable-crashreporter',
+    '--enable-ctypes',
+    '--enable-dbm',
+    '--enable-dbus',
+    '--enable-debug',
+    '--enable-debug-js-modules',
+    '--enable-debug-symbols',
+    '--enable-default-toolkit',
+    '--enable-directshow',
+    '--enable-dmd',
+    '--enable-dtrace',
+    '--enable-dump-painting',
+    '--enable-elf-hack',
+    '--enable-eme',
+    '--enable-export-js',
+    '--enable-extensions',
+    '--enable-faststripe',
+    '--enable-feeds',
+    '--enable-ffmpeg',
+    '--enable-fmp4',
+    '--enable-gamepad',
+    '--enable-gc-trace',
+    '--enable-gconf',
+    '--enable-gczeal',
+    '--enable-gio',
+    '--enable-gnomeui',
+    '--enable-gold',
+    '--enable-gps-debug',
+    '--enable-hardware-aec-ns',
+    '--enable-icf',
+    '--enable-install-strip',
+    '--enable-instruments',
+    '--enable-ion',
+    '--enable-ios-target',
+    '--enable-ipdl-tests',
+    '--enable-jemalloc',
+    '--enable-jitspew',
+    '--enable-jprof',
+    '--enable-libjpeg-turbo',
+    '--enable-libproxy',
+    '--enable-llvm-hacks',
+    '--enable-logrefcnt',
+    '--enable-macos-target',
+    '--enable-maintenance-service',
+    '--enable-media-navigator',
+    '--enable-memory-sanitizer',
+    '--enable-mobile-optimize',
+    '--enable-more-deterministic',
+    '--enable-mozril-geoloc',
+    '--enable-necko-protocols',
+    '--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-perf',
+    '--enable-permissions',
+    '--enable-pie',
+    '--enable-png-arm-neon-support',
+    '--enable-posix-nspr-emulation',
+    '--enable-pref-extensions',
+    '--enable-printing',
+    '--enable-profilelocking',
+    '--enable-profiling',
+    '--enable-pulseaudio',
+    '--enable-raw',
+    '--enable-readline',
+    '--enable-reflow-perf',
+    '--enable-release',
+    '--enable-replace-malloc',
+    '--enable-require-all-d3dc-versions',
+    '--enable-rust',
+    '--enable-safe-browsing',
+    '--enable-sandbox',
+    '--enable-shared-js',
+    '--enable-signmar',
+    '--enable-simulator',
+    '--enable-skia',
+    '--enable-skia-gpu',
+    '--enable-small-chunk-size',
+    '--enable-startup-notification',
+    '--enable-startupcache',
+    '--enable-stdcxx-compat',
+    '--enable-strip',
+    '--enable-synth-pico',
+    '--enable-synth-speechd',
+    '--enable-system-cairo',
+    '--enable-system-extension-dirs',
+    '--enable-system-ffi',
+    '--enable-system-hunspell',
+    '--enable-system-pixman',
+    '--enable-system-sqlite',
+    '--enable-systrace',
+    '--enable-tasktracer',
+    '--enable-tests',
+    '--enable-thread-sanitizer',
+    '--enable-trace-logging',
+    '--enable-tree-freetype',
+    '--enable-ui-locale',
+    '--enable-universalchardet',
+    '--enable-update-channel',
+    '--enable-update-packaging',
+    '--enable-updater',
+    '--enable-url-classifier',
+    '--enable-valgrind',
+    '--enable-verify-mar',
+    '--enable-vtune',
+    '--enable-warnings-as-errors',
+    '--enable-webapp-runtime',
+    '--enable-webrtc',
+    '--enable-websms-backend',
+    '--enable-webspeech',
+    '--enable-webspeechtestbackend',
+    '--enable-wmf',
+    '--enable-xterm-updates',
+    '--enable-xul',
+    '--enable-zipwriter',
+    '--host',
+    '--no-create',
+    '--prefix',
+    '--target',
+    '--with-adjust-sdk-keyfile',
+    '--with-android-cxx-stl',
+    '--with-android-distribution-directory',
+    '--with-android-gnu-compiler-version',
+    '--with-android-max-sdk',
+    '--with-android-min-sdk',
+    '--with-android-ndk',
+    '--with-android-sdk',
+    '--with-android-toolchain',
+    '--with-android-version',
+    '--with-app-basename',
+    '--with-app-name',
+    '--with-arch',
+    '--with-arm-kuser',
+    '--with-bing-api-keyfile',
+    '--with-branding',
+    '--with-ccache',
+    '--with-compiler-wrapper',
+    '--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-external-source-dir',
+    '--with-float-abi',
+    '--with-fpu',
+    '--with-gl-provider',
+    '--with-gonk',
+    '--with-gonk-toolchain-prefix',
+    '--with-google-api-keyfile',
+    '--with-google-oauth-api-keyfile',
+    '--with-gradle',
+    '--with-intl-api',
+    '--with-ios-sdk',
+    '--with-java-bin-path',
+    '--with-jitreport-granularity',
+    '--with-l10n-base',
+    '--with-libxul-sdk',
+    '--with-linux-headers',
+    '--with-macbundlename-prefix',
+    '--with-macos-private-frameworks',
+    '--with-macos-sdk',
+    '--with-mozilla-api-keyfile',
+    '--with-nspr-cflags',
+    '--with-nspr-libs',
+    '--with-pthreads',
+    '--with-qemu-exe',
+    '--with-qtdir',
+    '--with-servo',
+    '--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',
+    '--with-windows-version',
+    '--with-x',
+    '--with-xulrunner-stub-name',
+    '--x-includes',
+    '--x-libraries',
+)
+@advanced
+def old_configure(prepare_configure, all_options, *options):
+    import codecs
+    import os
+    import subprocess
+    import sys
+    import types
+
+    ret = subprocess.call(prepare_configure)
+    if ret:
+        sys.exit(ret)
+
+    raw_config = {}
+    encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
+    with codecs.open('config.data', 'r', encoding) 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.
+    for flag in raw_config['flags']:
+        if flag not in all_options:
+            error('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')
+
+    config = {}
+    for k, v in raw_config['substs']:
+        set_config(k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v)
+
+    for k, v in dict(raw_config['defines']).iteritems():
+        set_define(k[1:-1], v[1:-1])
+
+    set_config('non_global_defines', raw_config['non_global_defines'])
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/util.configure
@@ -0,0 +1,69 @@
+# -*- 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/.
+
+@template
+@advanced
+def warn(*args):
+    import sys
+    print(*args, file=sys.stderr)
+    sys.stderr.flush()
+
+
+@template
+@advanced
+def error(*args):
+    import sys
+    print(*args, file=sys.stderr)
+    sys.stderr.flush()
+    sys.exit(1)
+
+
+@template
+@advanced
+def is_absolute_or_relative(path):
+    import os
+    if os.altsep and os.altsep in path:
+        return True
+    return os.sep in path
+
+
+@template
+@advanced
+def normsep(path):
+    import mozpack.path as mozpath
+    return mozpath.normsep(path)
+
+
+@template
+@advanced
+def find_program(file):
+    if is_absolute_or_relative(file):
+        return os.path.abspath(file) if os.path.isfile(file) else None
+    from which import which, WhichError
+    try:
+        return normsep(which(file))
+    except WhichError:
+        return None
+
+
+@depends('--help')
+def _defines(help):
+    ret = {}
+    set_config('DEFINES', ret)
+    return ret
+
+
+@template
+def set_define(name, value):
+    @depends(_defines)
+    @advanced
+    def _add_define(defines):
+        from mozbuild.configure import ConfigureError
+        if name in defines:
+            raise ConfigureError("'%s' is already defined" % name)
+        defines[name] = value
+
+del _defines
--- a/configure.py
+++ b/configure.py
@@ -1,189 +1,74 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import print_function, unicode_literals
 
 import codecs
-import glob
-import itertools
 import json
 import os
 import subprocess
 import sys
-import re
-import types
 
 
 base_dir = os.path.abspath(os.path.dirname(__file__))
 sys.path.append(os.path.join(base_dir, 'python', 'which'))
 sys.path.append(os.path.join(base_dir, 'python', 'mozbuild'))
-from which import which, WhichError
-from mozbuild.mozconfig import MozconfigLoader
-
-
-# If feel dirty replicating this from python/mozbuild/mozbuild/mozconfig.py,
-# but the end goal being that the configure script would go away...
-shell = 'sh'
-if 'MOZILLABUILD' in os.environ:
-    shell = os.environ['MOZILLABUILD'] + '/msys/bin/sh'
-if sys.platform == 'win32':
-    shell = shell + '.exe'
-
-
-def is_absolute_or_relative(path):
-    if os.altsep and os.altsep in path:
-        return True
-    return os.sep in path
-
-
-def find_program(file):
-    if is_absolute_or_relative(file):
-        return os.path.abspath(file) if os.path.isfile(file) else None
-    try:
-        return which(file)
-    except WhichError:
-        return None
+from mozbuild.configure import ConfigureSandbox
 
 
-def autoconf_refresh(configure):
-    if os.path.exists(configure):
-        mtime = os.path.getmtime(configure)
-        aclocal = os.path.join(base_dir, 'build', 'autoconf', '*.m4')
-        for input in itertools.chain(
-            (configure + '.in',
-             os.path.join(os.path.dirname(configure), 'aclocal.m4')),
-            glob.iglob(aclocal),
-        ):
-            if os.path.getmtime(input) > mtime:
-                break
-        else:
-            return
+def main(argv):
+    config = {}
+    sandbox = ConfigureSandbox(config, os.environ, argv)
+    sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure'))
 
-    mozconfig_autoconf = None
-    configure_dir = os.path.dirname(configure)
-    # Don't read the mozconfig for the js configure (yay backwards
-    # compatibility)
-    if not configure_dir.replace(os.sep, '/').endswith('/js/src'):
-        loader = MozconfigLoader(os.path.dirname(configure))
-        project = os.environ.get('MOZ_CURRENT_PROJECT')
-        mozconfig = loader.find_mozconfig(env=os.environ)
-        mozconfig = loader.read_mozconfig(mozconfig, moz_build_app=project)
-        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)
-
-    for ac in (mozconfig_autoconf, os.environ.get('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'))
-
-    if not autoconf:
-        raise RuntimeError('Could not find autoconf 2.13')
-
-    # Add or adjust AUTOCONF for subprocesses, especially the js/src configure
-    os.environ['AUTOCONF'] = autoconf
-
-    print('Refreshing %s with %s' % (configure, autoconf), file=sys.stderr)
+    if sandbox._help:
+        return 0
 
-    with open(configure, 'wb') as fh:
-        subprocess.check_call([
-            shell, autoconf, '--localdir=%s' % os.path.dirname(configure),
-            configure + '.in'], stdout=fh)
-
-
-def main(args):
-    old_configure = os.environ.get('OLD_CONFIGURE')
-
-    if not old_configure:
-        raise Exception('The OLD_CONFIGURE environment variable must be set')
-
-    # We need to replace backslashes with forward slashes on Windows because
-    # this path actually ends up literally as $0, which breaks autoconf's
-    # detection of the source directory.
-    old_configure = os.path.abspath(old_configure).replace(os.sep, '/')
-
-    try:
-        autoconf_refresh(old_configure)
-    except RuntimeError as e:
-        print(e.message, file=sys.stderr)
-        return 1
-
-    ret = subprocess.call([shell, old_configure] + args)
-    # We don't want to create and run config.status if --help was one of the
-    # command line arguments.
-    if ret or '--help' in args:
-        return ret
-
-    raw_config = {}
-    encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
-    with codecs.open('config.data', 'r', encoding) 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
-    # If the code execution above fails, we want to keep the file around for
-    # debugging.
-    os.remove('config.data')
-
-    # Sanitize config data
-    config = {}
-    substs = config['substs'] = {
-        k[1:-1]: v[1:-1] if isinstance(v, types.StringTypes) else v
-        for k, v in raw_config['substs']
+    # Sanitize config data to feed config.status
+    sanitized_config = {}
+    sanitized_config['substs'] = {
+        k: v for k, v in config.iteritems()
+        if k not in ('DEFINES', 'non_global_defines', 'TOPSRCDIR', 'TOPOBJDIR')
     }
-    config['defines'] = {
-        k[1:-1]: v[1:-1]
-        for k, v in raw_config['defines']
-    }
-    config['non_global_defines'] = raw_config['non_global_defines']
-    config['topsrcdir'] = base_dir
-    config['topobjdir'] = os.path.abspath(os.getcwd())
+    sanitized_config['defines'] = config['DEFINES']
+    sanitized_config['non_global_defines'] = config['non_global_defines']
+    sanitized_config['topsrcdir'] = config['TOPSRCDIR']
+    sanitized_config['topobjdir'] = config['TOPOBJDIR']
 
     # Create config.status. Eventually, we'll want to just do the work it does
     # here, when we're able to skip configure tests/use cached results/not rely
     # on autoconf.
     print("Creating config.status", file=sys.stderr)
+    encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
     with codecs.open('config.status', 'w', encoding) as fh:
-        fh.write('#!%s\n' % config['substs']['PYTHON'])
+        fh.write('#!%s\n' % config['PYTHON'])
         fh.write('# coding=%s\n' % encoding)
-        for k, v in config.iteritems():
+        for k, v in sanitized_config.iteritems():
             fh.write('%s = ' % k)
             json.dump(v, fh, sort_keys=True, indent=4, ensure_ascii=False)
             fh.write('\n')
         fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', "
                  "'non_global_defines', 'substs']")
 
-        if not substs.get('BUILDING_JS') or substs.get('JS_STANDALONE'):
+        if not config.get('BUILDING_JS') or config.get('JS_STANDALONE'):
             fh.write('''
 if __name__ == '__main__':
     args = dict([(name, globals()[name]) for name in __all__])
     from mozbuild.config_status import config_status
     config_status(**args)
 ''')
 
     # Other things than us are going to run this file, so we need to give it
     # executable permissions.
     os.chmod('config.status', 0755)
-    if not substs.get('BUILDING_JS') or substs.get('JS_STANDALONE'):
-        if not substs.get('JS_STANDALONE'):
+    if not config.get('BUILDING_JS') or config.get('JS_STANDALONE'):
+        if not config.get('JS_STANDALONE'):
             os.environ['WRITE_MOZINFO'] = '1'
         # Until we have access to the virtualenv from this script, execute
         # config.status externally, with the virtualenv python.
-        return subprocess.call([config['substs']['PYTHON'], 'config.status'])
+        return subprocess.call([config['PYTHON'], 'config.status'])
     return 0
 
 if __name__ == '__main__':
-    sys.exit(main(sys.argv[1:]))
+    sys.exit(main(sys.argv))
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -71,52 +71,16 @@ dnl ====================================
 USE_PTHREADS=
 _PTHREAD_LDFLAGS=""
 
 dnl Do not allow objdir == srcdir builds
 dnl ==============================================================
 _topsrcdir=`cd $srcdir; pwd -W 2>/dev/null || pwd -P`
 _objdir=`pwd -P`
 
-if test "$_topsrcdir" = "$_objdir"
-then
-  echo "  ***"
-  echo "  * Building directly in the main source directory is not allowed."
-  echo "  *"
-  echo "  * To build, you must run configure from a separate directory"
-  echo "  * (referred to as an object directory)."
-  echo "  ***"
-  exit 1
-fi
-
-# Check for a couple representative files in the source tree
-_conflict_files=
-for file in $_topsrcdir/Makefile $_topsrcdir/config/autoconf.mk; do
-  if test -f $file; then
-    _conflict_files="$_conflict_files $file"
-  fi
-done
-if test "$_conflict_files"; then
-  echo "***"
-  echo "*   Your source tree contains these files:"
-  for file in $_conflict_files; do
-    echo "*         $file"
-  done
-  cat 1>&2 <<-EOF
-	*   This indicates that you previously built in the source tree.
-	*   A source tree build can confuse the separate objdir build.
-	*
-	*   To clean up the source tree:
-	*     1. cd $_topsrcdir
-	*     2. gmake distclean
-	***
-	EOF
-  exit 1
-  break
-fi
 MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd -P`
 
 MOZ_BUILD_BACKEND(../..)
 
 MOZ_DEFAULT_COMPILER
 
 COMPILE_ENVIRONMENT=1
 MOZ_ARG_DISABLE_BOOL(compile-environment,
@@ -3430,18 +3394,16 @@ AC_SUBST(ENABLE_STRIP)
 AC_SUBST(PKG_SKIP_STRIP)
 AC_SUBST(INCREMENTAL_LINKER)
 AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS)
 
 AC_SUBST(MOZ_FIX_LINK_PATHS)
 
 AC_SUBST(USE_DEPENDENT_LIBS)
 
-AC_SUBST(MOZ_BUILD_ROOT)
-
 AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
 AC_SUBST(MOZ_APP_VERSION)
 
 AC_SUBST(MOZ_PKG_SPECIAL)
 
new file mode 100644
--- /dev/null
+++ b/moz.configure
@@ -0,0 +1,10 @@
+# -*- 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/.
+
+include('build/moz.configure/init.configure')
+
+# Fallthrough to autoconf-based configure
+include('build/moz.configure/old.configure')
--- a/old-configure.in
+++ b/old-configure.in
@@ -91,54 +91,16 @@ dnl ====================================
 MOZ_USE_PTHREADS=
 _PTHREAD_LDFLAGS=""
 
 dnl Do not allow objdir == srcdir builds.
 dnl ==============================================================
 _topsrcdir=`cd \`dirname $0\`; pwd -W 2>/dev/null || pwd -P`
 _objdir=`pwd -P`
 
-if test "$_topsrcdir" = "$_objdir"; then
-  echo "  ***"
-  echo "  * Building directly in the main source directory is not allowed."
-  echo "  *"
-  echo "  * To build, you must run configure from a separate directory"
-  echo "  * (referred to as an object directory)."
-  echo "  *"
-  echo "  * If you are building with a mozconfig, you will need to change your"
-  echo "  * mozconfig to point to a different object directory."
-  echo "  ***"
-  exit 1
-fi
-
-# Check for a couple representative files in the source tree
-_conflict_files=
-for file in $_topsrcdir/Makefile $_topsrcdir/config/autoconf.mk; do
-  if test -f $file; then
-    _conflict_files="$_conflict_files $file"
-  fi
-done
-if test "$_conflict_files"; then
-  echo "***"
-  echo "*   Your source tree contains these files:"
-  for file in $_conflict_files; do
-    echo "*         $file"
-  done
-  cat 1>&2 <<-EOF
-  *   This indicates that you previously built in the source tree.
-  *   A source tree build can confuse the separate objdir build.
-  *
-  *   To clean up the source tree:
-  *     1. cd $_topsrcdir
-  *     2. gmake distclean
-  ***
-EOF
-  exit 1
-  break
-fi
 MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd -P`
 DIST="$MOZ_BUILD_ROOT/dist"
 
 MOZ_PYTHON
 
 MOZ_DEFAULT_COMPILER
 
 COMPILE_ENVIRONMENT=1
@@ -8469,18 +8431,16 @@ AC_SUBST(USE_ELF_HACK)
 AC_SUBST(INCREMENTAL_LINKER)
 AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS)
 AC_SUBST(MOZ_COMPONENT_NSPR_LIBS)
 
 AC_SUBST(MOZ_FIX_LINK_PATHS)
 
 AC_SUBST(USE_DEPENDENT_LIBS)
 
-AC_SUBST(MOZ_BUILD_ROOT)
-
 AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 AC_SUBST(MOZ_LINKER_EXTRACT)
 
 AC_SUBST(MOZ_ADDON_SIGNING)
 AC_SUBST(MOZ_REQUIRE_SIGNING)
 
 if test -n "$MOZ_BINARY_EXTENSIONS"; then
   AC_DEFINE(MOZ_BINARY_EXTENSIONS)
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1386,40 +1386,16 @@ class MachDebug(MachCommandBase):
         print('MOZ_OBJDIR=%s' % objdir, file=out)
         if 'MOZ_CURRENT_PROJECT' in os.environ:
             objdir = mozpath.join(objdir, os.environ['MOZ_CURRENT_PROJECT'])
         print('OBJDIR=%s' % objdir, file=out)
         if self.mozconfig['path']:
             print('FOUND_MOZCONFIG=%s' % mozpath.normsep(self.mozconfig['path']),
                 file=out)
 
-    def _environment_configure(self, out, verbose):
-        if self.mozconfig['path']:
-            # Replace ' with '"'"', so that shell quoting e.g.
-            # a'b becomes 'a'"'"'b'.
-            quote = lambda s: s.replace("'", """'"'"'""")
-            if self.mozconfig['configure_args']:
-                print('echo Adding configure options from %s' %
-                    mozpath.normsep(self.mozconfig['path']), file=out)
-                for arg in self.mozconfig['configure_args']:
-                    quoted_arg = quote(arg)
-                    print("echo '  %s'" % quoted_arg, file=out)
-                    print("""set -- "$@" '%s'""" % quoted_arg, file=out)
-            for key, value in self.mozconfig['env']['added'].items():
-                print("export %s='%s'" % (key, quote(value)), file=out)
-            for key, (old, value) in self.mozconfig['env']['modified'].items():
-                print("export %s='%s'" % (key, quote(value)), file=out)
-            for key, value in self.mozconfig['vars']['added'].items():
-                print("%s='%s'" % (key, quote(value)), file=out)
-            for key, (old, value) in self.mozconfig['vars']['modified'].items():
-                print("%s='%s'" % (key, quote(value)), file=out)
-            for key in self.mozconfig['env']['removed'].keys() + \
-                    self.mozconfig['vars']['removed'].keys():
-                print("unset %s" % key, file=out)
-
     def _environment_json(self, out, verbose):
         import json
         class EnvironmentEncoder(json.JSONEncoder):
             def default(self, obj):
                 if isinstance(obj, MozbuildObject):
                     result = {
                         'topsrcdir': obj.topsrcdir,
                         'topobjdir': obj.topobjdir,