Bug 1257823 - Move set_config() from @depends to global scope draft
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 22 Mar 2016 17:51:05 +0900
changeset 343863 767341c1e29822fbfd63c7a54e3b38abd452e1ba
parent 343862 14f8bd79e14906469b3aee92c1b94cd069796607
child 343864 df663e31087d9a06c9bd823c40545e1bd8bad957
push id13691
push userbmo:mh+mozilla@glandium.org
push dateWed, 23 Mar 2016 10:00:34 +0000
bugs1257823
milestone48.0a1
Bug 1257823 - Move set_config() from @depends to global scope
b2g/common.configure
build/moz.configure/checks.configure
build/moz.configure/init.configure
build/moz.configure/old.configure
build/moz.configure/util.configure
js/moz.configure
mobile/android/gradle.configure
moz.configure
python/mozbuild/mozbuild/test/configure/data/extra.configure
python/mozbuild/mozbuild/test/configure/data/included.configure
python/mozbuild/mozbuild/test/configure/data/moz.configure
toolkit/moz.configure
--- a/b2g/common.configure
+++ b/b2g/common.configure
@@ -9,13 +9,15 @@
 option(env='MOZTTDIR', nargs=1, help='Path to truetype fonts for B2G')
 
 @depends('MOZTTDIR')
 def mozttdir(value):
     if value:
         path = value[0]
         if not os.path.isdir(path):
             error('MOZTTDIR "%s" is not a valid directory' % path)
-        set_config('MOZTTDIR', path)
         set_define('PACKAGE_MOZTT', True)
+        return path
+
+set_config('MOZTTDIR', mozttdir)
 
 
 include('../toolkit/moz.configure')
--- a/build/moz.configure/checks.configure
+++ b/build/moz.configure/checks.configure
@@ -65,16 +65,21 @@ def check_prog(var, progs, allow_missing
             result = find_program(prog)
             if result:
                 return result
         return not_found
 
     @depends(check)
     @advanced
     def postcheck(value):
-        set_config(var, ':' if value is not_found else value)
         if value is not_found and not allow_missing:
             from mozbuild.shellutil import quote
             error('Cannot find %s (tried: %s)'
                   % (var.lower(), ', '.join(quote(p) for p in progs)))
         return None if value is not_found else value
 
+    @depends(postcheck)
+    def normalized_for_config(value):
+        return ':' if value is None else value
+
+    set_config(var, normalized_for_config)
+
     return postcheck
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -21,20 +21,16 @@ def check_build_environment(help, dist):
     else:
         dist = os.path.join(topobjdir, 'dist')
 
     result = namespace(
         topsrcdir=topsrcdir,
         topobjdir=topobjdir,
         dist=dist,
     )
-    set_config('TOPSRCDIR', topsrcdir)
-    set_config('TOPOBJDIR', topobjdir)
-    set_config('MOZ_BUILD_ROOT', topobjdir)
-    set_config('DIST', dist)
 
     if help:
         return result
 
     if topsrcdir == topobjdir:
         error(
             '  ***\n'
             '  * Building directly in the main source directory is not allowed.\n'
@@ -64,16 +60,22 @@ def check_build_environment(help, dist):
             '  *     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='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
 # ==============================================================
@@ -215,20 +217,20 @@ def virtualenv_python(env_python, build_
         # Windows.
         sys.exit(subprocess.call([python] + sys.argv))
 
     # We are now in the virtualenv
     import distutils.sysconfig
     if not distutils.sysconfig.get_python_lib():
         error('Could not determine python site packages directory')
 
-    set_config('PYTHON', python)
     add_old_configure_assignment('PYTHON', python)
     return python
 
+set_config('PYTHON', virtualenv_python)
 
 # Inject mozconfig options
 # ==============================================================
 @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
@@ -499,45 +501,55 @@ def target_variables(target):
     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
     add_old_configure_assignment('OS_TARGET', os_target)
-    set_config('OS_TARGET', os_target)
     add_old_configure_assignment('OS_ARCH', os_arch)
-    set_config('OS_ARCH', os_arch)
 
     if target.os == 'Darwin' and target.cpu == 'x86':
         os_test = 'i386'
     else:
         os_test = target.raw_cpu
     add_old_configure_assignment('OS_TEST', os_test)
-    set_config('OS_TEST', os_test)
 
     add_old_configure_assignment('CPU_ARCH', target.cpu)
-    set_config('CPU_ARCH', target.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,
+    )
 
-    if target.cpu in ('x86', 'x86_64'):
-        set_config('INTEL_ARCHITECTURE', True)
+set_config('OS_TARGET', delayed_getattr(target_variables, 'OS_TARGET'))
+set_config('OS_ARCH', delayed_getattr(target_variables, 'OS_ARCH'))
+set_config('OS_TEST', delayed_getattr(target_variables, 'OS_TEST'))
+set_config('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'))
 
-    set_config('TARGET_CPU', target.raw_cpu)
-    set_config('TARGET_OS', target.raw_os)
 
 @depends(host)
 def host_variables(host):
     if host.kernel == 'kFreeBSD':
         os_arch = 'GNU_kFreeBSD'
     else:
         os_arch = host.kernel
     add_old_configure_assignment('HOST_OS_ARCH', os_arch)
-    set_config('HOST_OS_ARCH', os_arch)
+    return namespace(
+        HOST_OS_ARCH=os_arch,
+    )
 
+set_config('HOST_OS_ARCH', delayed_getattr(host_variables, 'HOST_OS_ARCH'))
 
 @depends(target)
 def target_platform_defines(target):
     if target.kernel == 'WINNT':
         set_define('_WINDOWS', True)
         set_define('WIN32', True)
         set_define('XP_WIN', True)
         set_define('XP_WIN32', True)
@@ -580,69 +592,78 @@ option('--with-external-source-dir', env
 @depends('--enable-project', '--with-external-source-dir',
          check_build_environment, '--help')
 def include_project_configure(project, external_source_dir, build_env, help):
     if not project:
         error('--enable-project is required.')
 
     base_dir = build_env.topsrcdir
     if external_source_dir:
-        set_config('EXTERNAL_SOURCE_DIR', external_source_dir[0])
         add_old_configure_assignment('EXTERNAL_SOURCE_DIR',
                                      external_source_dir[0])
         base_dir = os.path.join(base_dir, external_source_dir[0])
 
     path = os.path.join(base_dir, project[0], 'moz.configure')
     if not os.path.exists(path):
         error('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)
+
+
 @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))
-    set_config('MOZ_BUILD_APP', ret)
     set_define('MOZ_BUILD_APP', ret)
     add_old_configure_assignment('MOZ_BUILD_APP', ret)
     return ret
 
+set_config('MOZ_BUILD_APP', build_project)
+
 
 # set RELEASE_BUILD 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_BUILD)
 @depends(check_build_environment)
 @advanced
 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]
 
-    set_config('GRE_MILESTONE', milestone)
-
-    is_nightly = is_release = False
+    is_nightly = is_release = None
 
     if 'a1' in milestone:
-        set_config('NIGHTLY_BUILD', True)
         set_define('NIGHTLY_BUILD', True)
         add_old_configure_assignment('NIGHTLY_BUILD', True)
         is_nightly = True
     elif 'a' not in milestone:
-        set_config('RELEASE_BUILD', True)
         set_define('RELEASE_BUILD', True)
         add_old_configure_assignment('RELEASE_BUILD', True)
         is_release = True
 
     return namespace(version=milestone,
                      is_nightly=is_nightly,
                      is_release=is_release)
 
+set_config('GRE_MILESTONE', delayed_getattr(milestone, 'version'))
+set_config('NIGHTLY_BUILD', delayed_getattr(milestone, 'is_nightly'))
+set_config('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
+
+
 # This is temporary until js/src/configure and configure are merged.
 # Use instead of option() in js/moz.configure
 @template
 def js_option(*args, **kwargs):
     opt = option(*args, **kwargs)
 
     @depends(opt.option, build_project)
     def js_option(value, build_project):
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -51,19 +51,20 @@ def autoconf(mozconfig, autoconf):
                     'autoconf213'))
 
     if not autoconf:
         error('Could not find autoconf 2.13')
 
     if not os.path.exists(autoconf):
         error('Could not find autoconf 2.13 at %s' % (autoconf,))
 
-    set_config('AUTOCONF', autoconf)
     return autoconf
 
+set_config('AUTOCONF', autoconf)
+
 
 # See comment in mozconfig_options() from build/moz.configure/init.configure
 @template
 @advanced
 def check_mozconfig_variables():
     # This escapes the sandbox. Don't copy this. This is only here because it
     # is a one off until old-configure is gone.
     all_options = depends.__self__._options.itervalues()
@@ -366,17 +367,16 @@ def old_configure_options(*options):
     '--enable-incomplete-external-linkage',
 )
 @advanced
 def old_configure(prepare_configure, extra_old_configure_args, all_options,
                   *options):
     import os
     import subprocess
     import sys
-    import types
     from mozbuild.shellutil import quote
 
     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 += [
@@ -412,17 +412,33 @@ def old_configure(prepare_configure, ext
     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')
+    return raw_config
 
-    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.
+@template
+def set_old_configure_config(name, value):
+    set_config(name, value)
+
+
+@depends(old_configure)
+@advanced
+def post_old_configure(raw_config):
+    import types
+
     for k, v in raw_config['substs']:
-        set_config(k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v)
+        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_define(k[1:-1], v[1:-1])
 
-    set_config('non_global_defines', raw_config['non_global_defines'])
+    set_old_configure_config('non_global_defines',
+                             raw_config['non_global_defines'])
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -101,8 +101,26 @@ def deprecated_option(*args, **kwargs):
 
 
 # from mozbuild.util import ReadOnlyNamespace as namespace
 @template
 @advanced
 def namespace(**kwargs):
     from mozbuild.util import ReadOnlyNamespace
     return ReadOnlyNamespace(**kwargs)
+
+
+# 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)
+    @advanced
+    def result(value):
+        return getattr(value, key)
+    return result
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -16,62 +16,71 @@ def building_js(build_project, help):
 # top-level configure, it can go away, although the JS_STANDALONE config
 # will still need to be set depending on building_js above.
 option(env='JS_STANDALONE', default=building_js,
        help='Reserved for internal use')
 
 @depends('JS_STANDALONE')
 def js_standalone(value):
     if value:
-        set_config('JS_STANDALONE', True)
         add_old_configure_assignment('JS_STANDALONE', True)
+        return True
 
+set_config('JS_STANDALONE', js_standalone)
 
 js_option('--disable-js-shell', default=building_js,
        help='Do not build the JS shell')
 
 @depends('--disable-js-shell')
-def js_shell(value):
+def js_disable_shell(value):
     if not value:
-        set_config('JS_DISABLE_SHELL', True)
+        return True
+
+set_config('JS_DISABLE_SHELL', js_disable_shell)
 
 
 # Use SpiderMonkey Promise implementation if it's enabled
 # =======================================================
 js_option('--enable-sm-promise', help='Enable SpiderMonkey promises')
 
 @depends('--enable-sm-promise')
 def sm_promise(value):
     if value:
-        set_config('SPIDERMONKEY_PROMISE', True)
         set_define('SPIDERMONKEY_PROMISE', True)
+        return True
 
+set_config('SPIDERMONKEY_PROMISE', sm_promise)
 
 # SpiderMonkey as a shared library, and how its symbols are exported
 # ==================================================================
 js_option('--disable-shared-js', default=building_js,
           help='Do not create a shared library')
 
 js_option('--disable-export-js', default=building_js,
           help='Do not mark JS symbols as DLL exported/visible')
 
 @depends('--disable-shared-js', '--disable-export-js')
 def static_js(shared_js, export_js):
     if shared_js:
         if not export_js:
             error('Must export JS symbols when building a shared library.')
-        set_config('JS_SHARED_LIBRARY', True)
         add_old_configure_assignment('JS_SHARED_LIBRARY', True)
     else:
         if export_js:
             set_define('STATIC_EXPORTABLE_JS_API', True)
         else:
             set_define('STATIC_JS_API', True)
         set_define('MOZ_STATIC_JS', True)
 
+@depends('--disable-shared-js')
+def shared_js(value):
+    if value:
+        return True
+
+set_config('JS_SHARED_LIBRARY', shared_js)
 
 @deprecated_option(env='DISABLE_SHARED_JS', nargs='?')
 def disable_shared_js(value):
     # DISABLE_SHARED_JS=1 gets us an empty PositiveOptionValue
     if value and not len(value):
         suggestion = '--disable-shared-js'
     else:
         suggestion = '--enable-shared-js'
@@ -97,21 +106,22 @@ js_option('--enable-instruments', env='M
           help='Enable instruments remote profiling')
 
 @depends('--enable-instruments', target)
 def instruments(value, target):
     if value and target.os != 'OSX':
         error('--enable-instruments cannot be used when targeting %s'
               % target.os)
     if value:
-        set_config('MOZ_INSTRUMENTS', True)
         set_define('MOZ_INSTRUMENTS', True)
         add_old_configure_assignment('MOZ_INSTRUMENTS', True)
         imply_option('--enable-profiling', reason='--enable-instruments')
+        return True
 
+set_config('MOZ_INSTRUMENTS', instruments)
 
 js_option('--enable-callgrind', env='MOZ_CALLGRIND',
           help='Enable callgrind profiling')
 
 @depends('--enable-callgrind')
 def callgrind(value):
     if value:
         set_define('MOZ_CALLGRIND', True)
@@ -120,24 +130,28 @@ def callgrind(value):
 
 js_option('--enable-profiling', env='MOZ_PROFILING',
           help='Set compile flags necessary for using sampling profilers '
                '(e.g. shark, perf)')
 
 @depends('--enable-profiling', target)
 def profiling(value, target):
     if value:
-        set_config('MOZ_PROFILING', True)
         set_define('MOZ_PROFILING', True)
         add_old_configure_assignment('MOZ_PROFILING', True)
 
         if target.kernel == 'WINNT' or (target.kernel == 'Linux' and
                                         target.os == 'GNU'):
             imply_option('--enable-vtune', reason='--enable-profiling')
+        return True
+
+set_config('MOZ_PROFILING', profiling)
 
 
 js_option('--enable-vtune', env='MOZ_VTUNE', help='Enable vtune profiling')
 
 @depends('--enable-vtune')
 def vtune(value):
     if value:
-        set_config('MOZ_VTUNE', True)
         set_define('MOZ_VTUNE', True)
+        return True
+
+set_config('MOZ_VTUNE', vtune)
--- a/mobile/android/gradle.configure
+++ b/mobile/android/gradle.configure
@@ -7,43 +7,52 @@
 # If --with-gradle is specified, build mobile/android with Gradle.  If no
 # Gradle binary is specified, or if --without-gradle is specified, use the in
 # tree Gradle wrapper.  The wrapper downloads and installs Gradle, which is
 # good for local developers but not good in automation.
 option('--with-gradle', nargs='?',
        help='Enable building mobile/android with Gradle '
             '(argument: location of binary or wrapper (gradle/gradlew))')
 
+@depends('--with-gradle')
+def with_gradle(value):
+    if value:
+        return True
+
+set_config('MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE', with_gradle)
+
+
 @depends('--with-gradle', check_build_environment)
 def gradle(value, build_env):
-    if value:
-        set_config('MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE', True)
-
     gradle = value[0] if len(value) else \
         os.path.join(build_env.topsrcdir, 'gradlew')
 
     # TODO: verify that $GRADLE is executable.
     if not os.path.isfile(gradle):
         error('GRADLE must be executable: %s' % gradle)
 
-    set_config('GRADLE', gradle)
+    return gradle
 
-    return gradle
+set_config('GRADLE', gradle)
 
 
 # Automation uses this to change log levels, not use the daemon, and use
 # offline mode.
 option(env='GRADLE_FLAGS', default='', help='Flags to pass to Gradle.')
 
 @depends('GRADLE_FLAGS')
 def gradle_flags(value):
-    set_config('GRADLE_FLAGS', value[0] if value else '')
+    return value[0] if value else ''
+
+set_config('GRADLE_FLAGS', gradle_flags)
 
 
 # Automation will set this to file:///path/to/local via the mozconfig.
 # Local developer default is jcenter.
 option(env='GRADLE_MAVEN_REPOSITORY', default='https://jcenter.bintray.com/',
        help='Path to Maven repository containing Gradle dependencies.')
 
 @depends('GRADLE_MAVEN_REPOSITORY')
 def gradle_maven_repository(value):
     if value:
-        set_config('GRADLE_MAVEN_REPOSITORY', value[0])
+        return value[0]
+
+set_config('GRADLE_MAVEN_REPOSITORY', gradle_maven_repository)
--- a/moz.configure
+++ b/moz.configure
@@ -15,40 +15,44 @@ include('build/moz.configure/checks.conf
 # - Spidermonkey-specific options and rules should go in js/moz.configure.
 # - etc.
 
 # Multiprocess Firefox Testing UI - Nightly and Aurora
 # To be removed in Bug 1003313
 @depends(milestone)
 def e10s_testing_only(milestone):
     if not milestone.is_release:
-        set_config('E10S_TESTING_ONLY', True)
         set_define('E10S_TESTING_ONLY', True)
+        return True
+
+set_config('E10S_TESTING_ONLY', e10s_testing_only)
 
 
 option('--enable-artifact-builds', env='MOZ_ARTIFACT_BUILDS',
        help='Download and use prebuilt binary artifacts.')
 
 @depends('--enable-artifact-builds')
 def artifact_builds(value):
     if value:
         imply_option('--disable-compile-environment')
-        set_config('MOZ_ARTIFACT_BUILDS', True)
-    return bool(value)
+        return True
+
+set_config('MOZ_ARTIFACT_BUILDS', artifact_builds)
 
 
 option('--disable-compile-environment',
        help='Disable compiler/library checks')
 
 @depends('--disable-compile-environment')
 def compile_environment(value):
     if value:
-        set_config('COMPILE_ENVIRONMENT', True)
         add_old_configure_assignment('COMPILE_ENVIRONMENT', True)
-    return bool(value)
+        return True
+
+set_config('COMPILE_ENVIRONMENT', compile_environment)
 
 
 @depends('--help')
 @advanced
 def build_backends_choices(help):
     from mozbuild.backend import backends
     return tuple(backends)
 
@@ -58,17 +62,19 @@ option('--enable-build-backend', nargs='
 
 @depends('--enable-build-backend', '--enable-artifact-builds')
 def build_backend(backends, artifact_builds):
     if artifact_builds:
         all_backends = ['FasterMake+RecursiveMake']
     else:
         all_backends = ['RecursiveMake', 'FasterMake']
     all_backends.extend(backends)
-    set_config('BUILD_BACKENDS', unique_list(all_backends))
+    return unique_list(all_backends)
+
+set_config('BUILD_BACKENDS', build_backend)
 
 
 # Awk detection
 # ==============================================================
 awk = check_prog('AWK', ('gawk', 'mawk', 'nawk', 'awk'))
 
 # Until the AWK variable is not necessary in old-configure
 @depends(awk)
@@ -155,22 +161,30 @@ def yasm_asflags(yasm, target):
             # 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'
-            set_config('YASM_ASFLAGS', asflags)
-            set_config('HAVE_YASM', True)
             # Until the YASM variable is not necessary in old-configure.
             add_old_configure_assignment('YASM', True)
         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)
+
+
 # Miscellaneous programs
 # ==============================================================
 check_prog('DOXYGEN', ('doxygen',), allow_missing=True)
 check_prog('TAR', ('gnutar', 'gtar', 'tar'))
 check_prog('UNZIP', ('unzip',))
 check_prog('XARGS', ('xargs',))
 check_prog('ZIP', ('zip',))
 
--- a/python/mozbuild/mozbuild/test/configure/data/extra.configure
+++ b/python/mozbuild/mozbuild/test/configure/data/extra.configure
@@ -3,9 +3,11 @@
 # 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('--extra', help='Extra')
 
 @depends('--extra')
 def extra(extra):
-    set_config('EXTRA', extra)
+    return extra
+
+set_config('EXTRA', extra)
--- a/python/mozbuild/mozbuild/test/configure/data/included.configure
+++ b/python/mozbuild/mozbuild/test/configure/data/included.configure
@@ -5,43 +5,54 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # For more complex and repetitive things, we can create templates
 @template
 def check_compiler_flag(flag):
     @depends(is_gcc)
     def check(value):
         if value:
-            set_config('CFLAGS', [flag])
+            return [flag]
+    set_config('CFLAGS', check)
+    return check
 
-    return check
 
 check_compiler_flag('-Werror=foobar')
 
 # A template that doesn't return functions can be used in @depends functions.
 @template
 def fortytwo():
     return 42
 
 @template
 def twentyone():
     yield 21
 
 @depends(is_gcc)
 def check(value):
     if value:
-        set_config('TEMPLATE_VALUE', fortytwo())
+        return fortytwo()
+
+set_config('TEMPLATE_VALUE', check)
+
+@depends(is_gcc)
+def check(value):
+    if value:
         for val in twentyone():
-            set_config('TEMPLATE_VALUE_2', val)
+            return val
+
+set_config('TEMPLATE_VALUE_2', check)
 
 # Templates can use @advanced too to import modules and get the full set of
 # builtins.
 @template
 @advanced
 def platform():
     import sys
     return sys.platform
 
 option('--enable-advanced-template', help='Advanced template')
 @depends('--enable-advanced-template')
 def check(value):
     if value:
-        set_config('PLATFORM', platform())
+        return platform()
+
+set_config('PLATFORM', check)
--- a/python/mozbuild/mozbuild/test/configure/data/moz.configure
+++ b/python/mozbuild/mozbuild/test/configure/data/moz.configure
@@ -35,70 +35,80 @@ option('--option', env='MOZ_OPTION', hel
 # It is also possible to pass options through the environment only.
 option(env='CC', nargs=1, help='C Compiler')
 
 # Call the function when the --enable-simple option is processed, with its
 # OptionValue as argument.
 @depends('--enable-simple')
 def simple(simple):
     if simple:
-        set_config('ENABLED_SIMPLE', simple)
+        return simple
+
+set_config('ENABLED_SIMPLE', simple)
 
 # There can be multiple functions depending on the same option.
 @depends('--enable-simple')
 def simple(simple):
-    set_config('SIMPLE', simple)
+    return simple
+
+set_config('SIMPLE', simple)
 
 @depends('--enable-with-env')
 def with_env(with_env):
-    set_config('WITH_ENV', with_env)
+    return with_env
+
+set_config('WITH_ENV', with_env)
 
 # It doesn't matter if the dependency is on --enable or --disable
 @depends('--disable-values')
 def with_env2(values):
-    set_config('VALUES', values)
     return values
 
+set_config('VALUES', with_env2)
+
 # It is possible to @depends on environment-only options.
 @depends('CC')
 def is_gcc(cc):
     return cc and 'gcc' in cc[0]
 
-@depends(is_gcc)
-def is_gcc_check(is_gcc):
-    set_config('IS_GCC', is_gcc)
+set_config('IS_GCC', is_gcc)
 
 # It is possible to depend on the result from another function.
 @depends(with_env2)
 def with_env3(values):
-    set_config('VALUES2', values)
     return values
 
+set_config('VALUES2', with_env3)
+
 # @depends functions can also return results for use as input to another
 # @depends.
 @depends(with_env3)
 def with_env4(values):
     return values
 
 @depends(with_env4)
 def with_env5(values):
-    set_config('VALUES3', values)
+    return values
+
+set_config('VALUES3', with_env5)
 
 # The result from @depends functions can also be used as input to options.
 # The result must be returned, not implied. The function must also depend
 # on --help.
 @depends('--enable-simple', '--help')
 def simple(simple, help):
     return 'simple' if simple else 'not-simple'
 
 option('--with-returned-default', default=simple, help='Returned default')
 
 @depends('--with-returned-default')
 def default(value):
-    set_config('DEFAULTED', value)
+    return value
+
+set_config('DEFAULTED', default)
 
 # @depends functions can also declare that some extra options are implied.
 # Those options need to be defined _after_ the function, and they mustn't
 # appear on the command line or the environment with conflicting values.
 @depends('--enable-values')
 def values(values):
     if values:
         imply_option('--enable-implied')
@@ -108,45 +118,55 @@ def values(values):
 option('--enable-implied', help='Implied')
 
 option('--with-implied-values', nargs='*', help='Implied values')
 
 option(env='WITH_IMPLIED_ENV', nargs='*', help='Implied env')
 
 @depends('--enable-implied')
 def implied(value):
-    set_config('IMPLIED', value)
+    return value
+
+set_config('IMPLIED', implied)
 
 @depends('--with-implied-values')
 def implied(values):
-    set_config('IMPLIED_VALUES', values)
+    return values
+
+set_config('IMPLIED_VALUES', implied)
 
 @depends('WITH_IMPLIED_ENV')
 def implied(values):
-    set_config('IMPLIED_ENV', values)
+    return values
+
+set_config('IMPLIED_ENV', implied)
 
 @depends('--enable-values', '--help')
 def choices(values, help):
     if len(values):
         return {
             'alpha': ('a', 'b', 'c'),
             'numeric': ('0', '1', '2'),
         }.get(values[0])
 
 option('--returned-choices', choices=choices, help='Choices')
 
 @depends('--returned-choices')
 def returned_choices(values):
-    set_config('CHOICES', values)
+    return values
+
+set_config('CHOICES', returned_choices)
 
 # All options must be referenced by some @depends function.
 # It is possible to depend on multiple options/functions
 @depends('--without-thing', '--with-stuff', with_env4, '--option')
 def remainder(*args):
-    set_config('REMAINDER', args)
+    return args
+
+set_config('REMAINDER', remainder)
 
 # It is possible to include other files to extend the configuration script.
 include('included.configure')
 
 # It is also possible for the include file path to come from the result of a
 # @depends function. That function needs to depend on '--help' like for option
 # defaults and choices.
 option('--enable-include', nargs=1, help='Include')
@@ -160,37 +180,47 @@ include(include_path)
 # the standard builtins instead of restricted ones. The order of the decorators
 # matter: @advanced needs to appear last.
 option('--with-advanced', nargs='?', help='Advanced')
 @depends('--with-advanced')
 @advanced
 def with_advanced(value):
     if value:
         from mozbuild.configure.options import OptionValue
-        set_config('ADVANCED', isinstance(value, OptionValue))
+        return isinstance(value, OptionValue)
+
+set_config('ADVANCED', with_advanced)
 
 # Trying to import without @advanced will fail at runtime.
 @depends('--with-advanced')
 def with_advanced(value):
     if len(value) and value[0] == 'break':
         from mozbuild.configure.options import OptionValue
-        set_config('ADVANCED', isinstance(value, OptionValue))
+        return isinstance(value, OptionValue)
+
+set_config('ADVANCED2', with_advanced)
 
 # A limited set of functions from os.path are exposed to non @advanced
 # functions.
 @depends('--with-advanced')
 def with_advanced(value):
     if len(value):
-        set_config('IS_FILE', os.path.isfile(value[0]))
+        return os.path.isfile(value[0])
+
+set_config('IS_FILE', with_advanced)
 
 # An @advanced function can still import the full set.
 @depends('--with-advanced')
 @advanced
 def with_advanced(value):
     if len(value):
         import os.path
-        set_config('HAS_GETATIME', hasattr(os.path, 'getatime'))
+        return hasattr(os.path, 'getatime')
+
+set_config('HAS_GETATIME', with_advanced)
 
 @depends('--with-advanced')
 @advanced
 def with_advanced(value):
     if len(value):
-        set_config('HAS_GETATIME2', hasattr(os.path, 'getatime'))
+        return hasattr(os.path, 'getatime')
+
+set_config('HAS_GETATIME2', with_advanced)
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -22,48 +22,52 @@ def systrace(value, target):
 
 
 option('--enable-jprof', env='MOZ_JPROF',
        help='Enable jprof profiling tool (needs mozilla/tools/jprof)')
 
 @depends('--enable-jprof')
 def jprof(value):
     if value:
-        set_config('MOZ_JPROF', True)
         set_define('MOZ_JPROF', True)
         imply_option('--enable-profiling')
+        return True
 
+set_config('MOZ_JPROF', jprof)
 
 @depends(target)
 def sps_profiler(target):
     if target.os == 'Android':
         return target.cpu in ('arm', 'x86')
     elif target.kernel == 'Linux':
         return target.cpu in ('x86', 'x86_64')
     return target.os in ('OSX', 'WINNT')
 
 @depends(sps_profiler)
 def sps_profiler_define(value):
     if value:
-        set_config('MOZ_ENABLE_PROFILER_SPS', True)
         set_define('MOZ_ENABLE_PROFILER_SPS', True)
+        return True
+
+set_config('MOZ_ENABLE_PROFILER_SPS', sps_profiler_define)
 
 
 option('--enable-dmd', env='MOZ_DMD',
        help='Enable Dark Matter Detector (heap profiler). '
             'Also enables jemalloc, replace-malloc and profiling')
 
 @depends('--enable-dmd')
 def dmd(value):
     if value:
-        set_config('MOZ_DMD', True)
         set_define('MOZ_DMD', True)
         add_old_configure_assignment('MOZ_DMD', True)
         imply_option('--enable-profiling')
+        return True
 
+set_config('MOZ_DMD', dmd)
 
 # Javascript engine
 # ==============================================================
 include('../js/moz.configure')
 
 
 # L10N
 # ==============================================================
@@ -71,18 +75,19 @@ option('--with-l10n-base', nargs=1, env=
        help='Path to l10n repositories')
 
 @depends('--with-l10n-base')
 def l10n_base(value):
     if value:
         path = value[0]
         if not os.path.isdir(path):
             error("Invalid value --with-l10n-base, %s doesn't exist" % path)
+        return os.path.realpath(os.path.abspath(path))
 
-        set_config('L10NBASEDIR', os.path.realpath(os.path.abspath(path)))
+set_config('L10NBASEDIR', l10n_base)
 
 
 # Default toolkit
 # ==============================================================
 # Normally, we'd want to use the `default` field on the option, but that
 # requires --target to be resolved at --help time, which requires to run
 # config.guess, which we want to avoid. Even better, we could actually set
 # `choices` depending on the target, but that doesn't pan out for the same
@@ -126,91 +131,107 @@ def toolkit(value, target, gonkdir):
 
 
 @depends(toolkit)
 def toolkit(toolkit):
     if toolkit == 'cairo-gtk2-x11':
         widget_toolkit = 'gtk2'
     else:
         widget_toolkit = toolkit.replace('cairo-', '')
-    set_config('MOZ_WIDGET_TOOLKIT', widget_toolkit)
     add_old_configure_assignment('MOZ_WIDGET_TOOLKIT', widget_toolkit)
 
     if widget_toolkit == 'gtk2':
         set_define('MOZ_WIDGET_GTK', '2')
     elif widget_toolkit == 'gtk3':
         set_define('MOZ_WIDGET_GTK', '3')
     elif widget_toolkit != 'windows':
         set_define('MOZ_WIDGET_%s' % widget_toolkit.upper(), True)
 
     return widget_toolkit
 
+set_config('MOZ_WIDGET_TOOLKIT', toolkit)
+
 
 option('--without-x', env='WITHOUT_X', help='Disable X11 support')
 
 @depends('--without-x', toolkit)
 def x11(value, toolkit):
     if not value and toolkit != 'qt':
         error('--without-x is only valid with --enable-default-toolkit=qt')
 
     x11_toolkits = ('gtk2', 'gtk3', 'qt')
     if value and value.origin != 'default' and toolkit not in x11_toolkits:
         error('--with-x is only valid with --enable-default-toolkit={%s}'
               % ','.join(x11_toolkits))
 
     if value and toolkit in x11_toolkits:
-        set_config('MOZ_ENABLE_XREMOTE', True)
         set_define('MOZ_ENABLE_XREMOTE', True)
-        set_config('MOZ_X11', True)
         set_define('MOZ_X11', True)
         add_old_configure_assignment('MOZ_X11', True)
 
-    return value and toolkit in x11_toolkits
+    return True if value and toolkit in x11_toolkits else None
 
+set_config('MOZ_ENABLE_XREMOTE', x11)
+set_config('MOZ_X11', x11)
 
 # GL Provider
 # ==============================================================
 option('--with-gl-provider', nargs=1, help='Set GL provider backend type')
 
-@depends('--with-gl-provider', x11)
-def gl_provider(value, x11):
+@depends('--with-gl-provider')
+def gl_provider(value):
     if value:
         provider = value[0]
-        set_config('MOZ_GL_PROVIDER', provider)
         set_define('MOZ_GL_PROVIDER', 'GLContextProvider%s' % provider)
-        set_config('MOZ_GL_DEFAULT_PROVIDER', provider)
         set_define('GL_PROVIDER_%s' % provider, True)
+        return provider
+
+@depends(gl_provider, x11)
+def gl_default_provider(value, x11):
+    if value:
+        return value
     elif x11:
-        set_config('MOZ_GL_DEFAULT_PROVIDER', 'GLX')
         set_define('GL_PROVIDER_GLX', True)
+        return 'GLX'
 
+set_config('MOZ_GL_PROVIDER', gl_provider)
+set_config('MOZ_GL_DEFAULT_PROVIDER', gl_default_provider)
 
 # PDF printing
 # ==============================================================
 @depends(toolkit)
 def pdf_printing(toolkit):
     if toolkit in ('windows', 'gtk2', 'gtk3', 'qt', 'android', 'gonk'):
-        set_config('MOZ_PDF_PRINTING', True)
-        set_config('PDF_SURFACE_FEATURE', '#define CAIRO_HAS_PDF_SURFACE 1')
+        return True
+
+@depends(pdf_printing)
+def pdf_surface_feature(pdf_printing):
+    if pdf_printing:
+        return '#define CAIRO_HAS_PDF_SURFACE 1'
     else:
         # CONFIGURE_SUBST_FILES need explicit empty values.
-        set_config('PDF_SURFACE_FEATURE', '')
+        return ''
+
+set_config('MOZ_PDF_PRINTING', pdf_printing)
+set_config('PDF_SURFACE_FEATURE', pdf_surface_feature)
 
 
 # Event loop instrumentation
 # ==============================================================
 option(env='MOZ_INSTRUMENT_EVENT_LOOP',
        help='Force-enable event loop instrumentation')
 
 @depends('MOZ_INSTRUMENT_EVENT_LOOP', toolkit)
 def instrument_event_loop(value, toolkit):
     if value or (toolkit in ('windows', 'gtk2', 'gtk3', 'cocoa', 'android',
                              'gonk') and value.origin == 'default'):
-        set_config('MOZ_INSTRUMENT_EVENT_LOOP', True)
         set_define('MOZ_INSTRUMENT_EVENT_LOOP', True)
+        return True
+
+set_config('MOZ_INSTRUMENT_EVENT_LOOP', instrument_event_loop)
 
 
 # Fontconfig Freetype
 # ==============================================================
 option(env='USE_FC_FREETYPE',
        help='Force-enable the use of fontconfig freetype')
 
 @depends('USE_FC_FREETYPE', toolkit)
@@ -220,22 +241,21 @@ def fc_freetype(value, toolkit):
         add_old_configure_assignment('USE_FC_FREETYPE', True)
 
 
 # Apple platform decoder support
 # ==============================================================
 @depends(toolkit)
 def applemedia(toolkit):
     if toolkit in ('cocoa', 'uikit'):
-        set_config('MOZ_APPLEMEDIA', True)
         set_define('MOZ_APPLEMEDIA', True)
         add_old_configure_assignment('MOZ_APPLEMEDIA', True)
         return True
-    return False
 
+set_config('MOZ_APPLEMEDIA', applemedia)
 
 # Windows Media Foundation support
 # ==============================================================
 option('--disable-wmf',
        help='Disable support for Windows Media Foundation')
 
 @depends('--disable-wmf', target)
 def wmf(value, target):
@@ -244,70 +264,75 @@ def wmf(value, target):
         # Enable Windows Media Foundation support by default.
         # Note our minimum SDK version is Windows 7 SDK, so we are (currently)
         # guaranteed to have a recent-enough SDK to build WMF.
         enabled = target.os == 'WINNT'
     if enabled and target.os != 'WINNT':
         error('Cannot enable Windows Media Foundation support on %s'
               % target.os)
     if enabled:
-        set_config('MOZ_WMF', True)
         set_define('MOZ_WMF', True)
-    return enabled
+        return True
 
+set_config('MOZ_WMF', wmf)
 
 # FFmpeg H264/AAC Decoding Support
 # ==============================================================
 option('--disable-ffmpeg',
        help='Disable FFmpeg for fragmented H264/AAC decoding')
 
 @depends('--disable-ffmpeg', target)
 def ffmpeg(value, target):
     enabled = bool(value)
     if value.origin == 'default':
         enabled = target.os not in ('Android', 'WINNT')
     if enabled:
         set_define('MOZ_FFMPEG', True)
-        set_config('MOZ_FFMPEG', True)
         imply_option('--enable-fmp4', '--enable-ffmpeg')
-    return enabled
+        return True
 
+set_config('MOZ_FFMPEG', ffmpeg)
 
 # Built-in fragmented MP4 support.
 # ==============================================================
 option('--disable-fmp4', env='MOZ_FMP4',
        help='Disable support for in built Fragmented MP4 parsing')
 
 @depends('--disable-fmp4', target, wmf, applemedia)
 def fmp4(value, target, wmf, applemedia):
     enabled = bool(value)
     if value.origin == 'default':
         # target.os == 'Android' includes all B2G versions
         enabled = wmf or applemedia or target.os == 'Android'
     if enabled:
-        set_config('MOZ_FMP4', True)
         set_define('MOZ_FMP4', True)
         add_old_configure_assignment('MOZ_FMP4', True)
-    return enabled
+        return True
 
+set_config('MOZ_FMP4', fmp4)
 
 # EME Support
 # ==============================================================
 option('--enable-eme', nargs='*', choices=('adobe',),
        help='Enable support for Encrypted Media Extensions')
 
 @depends('--enable-eme', fmp4)
 def eme(value, fmp4):
     enabled = bool(value)
     if value.origin == 'default':
         enabled = enabled or fmp4
     if enabled and not fmp4:
         error('Encrypted Media Extension support requires '
               'Fragmented MP4 support')
     if enabled:
-        set_config('MOZ_EME', True)
         set_define('MOZ_EME', True)
+        return True
+
+@depends('--enable-eme')
+def eme_modules(value):
     # Theoretically, we could pass `value` directly when it is a
     # PositiveOptionValue, but somehow, the JSON serialization in configure.py
     # outputs inconsistent data in some cases when we do (a closing bracket
     # without an opening one).
-    set_config('MOZ_EME_MODULES', list(value) if value else [])
-    return enabled
+    return list(value) if value else []
+
+set_config('MOZ_EME', eme)
+set_config('MOZ_EME_MODULES', eme_modules)