Bug 921003 - For a given tier, skip directories without a Makefile.in and without variables in moz.build that are relevant to that tier. r=gps
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 02 Oct 2013 09:02:41 +0900
changeset 158213 c5906eed61fcc7a83f8ed23cdbbc868d6c45aaaf
parent 158212 6be8c784235c92a7c72c1cf9cc797cf564cc8335
child 158214 b13ff613f1cf7633eb9bedf6e002f9edd963fcc0
push idunknown
push userunknown
push dateunknown
reviewersgps
bugs921003
milestone27.0a1
Bug 921003 - For a given tier, skip directories without a Makefile.in and without variables in moz.build that are relevant to that tier. r=gps
build/docs/environment-variables.rst
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/frontend/mach_commands.py
python/mozbuild/mozbuild/frontend/sandbox.py
python/mozbuild/mozbuild/frontend/sandbox_symbols.py
python/mozbuild/mozbuild/test/frontend/test_sandbox_symbols.py
--- a/build/docs/environment-variables.rst
+++ b/build/docs/environment-variables.rst
@@ -37,8 +37,13 @@ MOZ_PSEUDO_DERECURSE
    this build mode to be the default. At which time, this variable will
    likely go away.
 
    A value of ``1`` activates the mode with full optimizations.
 
    A value of ``no-parallel-export`` activates the mode without
    optimizations to the *export* tier, which are known to be slightly
    buggy.
+
+   A value of ``no-skip`` activates the mode without optimizations to skip
+   some directories during traversal.
+
+   Values may be combined with a comma.
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -205,17 +205,17 @@ class RecursiveMakeTraversal(object):
                 if start_node != '':
                     deps[start_node] = prev_nodes
                 prev_nodes = (start_node,)
             if not start_node in self._traversal:
                 return prev_nodes
             parallel_nodes = []
             for node in parallel:
                 nodes = recurse(node, prev_nodes)
-                if nodes != ('',):
+                if nodes and nodes != ('',):
                     parallel_nodes.extend(nodes)
             if parallel_nodes:
                 prev_nodes = tuple(parallel_nodes)
             for dir in sequential:
                 prev_nodes = recurse(dir, prev_nodes)
             return prev_nodes
 
         return recurse(''), deps
@@ -296,21 +296,30 @@ class RecursiveMakeBackend(CommonBackend
                 'dist_public',
                 'dist_private',
                 'dist_sdk',
                 'tests',
                 'xpidl',
             ]}
 
         self._traversal = RecursiveMakeTraversal()
+        self._may_skip = {
+            'export': set(),
+            'compile': set(),
+            'binaries': set(),
+            'libs': set(),
+            'tools': set(),
+        }
 
         derecurse = self.environment.substs.get('MOZ_PSEUDO_DERECURSE', '').split(',')
         self._parallel_export = False
-        if derecurse != [''] and not 'no-parallel-export' in derecurse:
-            self._parallel_export = True
+        self._no_skip = False
+        if derecurse != ['']:
+            self._parallel_export = 'no-parallel-export' not in derecurse
+            self._no_skip = 'no-skip' in derecurse
 
     def _update_from_avoid_write(self, result):
         existed, updated = result
 
         if not existed:
             self.summary.created_count += 1
         elif updated:
             self.summary.updated_count += 1
@@ -403,37 +412,47 @@ class RecursiveMakeBackend(CommonBackend
         self._backend_files[obj.srcdir] = backend_file
 
     def _fill_root_mk(self):
         """
         Create two files, root.mk and root-deps.mk, the first containing
         convenience variables, and the other dependency definitions for a
         hopefully proper directory traversal.
         """
+        if not self._no_skip:
+            for tier, skip in self._may_skip.items():
+                self.log(logging.DEBUG, 'fill_root_mk', {
+                    'number': len(skip), 'tier': tier
+                    }, 'Ignoring {number} directories during {tier}')
+
         # Traverse directories in parallel, and skip static dirs
         def parallel_filter(current, subdirs):
             all_subdirs = subdirs.parallel + subdirs.dirs + \
                           subdirs.tests + subdirs.tools
+            if current in self._may_skip[tier]:
+                current = None
             # subtiers/*_start and subtiers/*_finish, under subtiers/*, are
             # kept sequential. Others are all forced parallel.
-            if current.startswith('subtiers/') and all_subdirs and \
+            if current and current.startswith('subtiers/') and all_subdirs and \
                     all_subdirs[0].startswith('subtiers/'):
                 return current, [], all_subdirs
             return current, all_subdirs, []
 
         # Skip static dirs during export traversal, or build everything in
         # parallel when enabled.
         def export_filter(current, subdirs):
             if self._parallel_export:
                 return parallel_filter(current, subdirs)
             return current, subdirs.parallel, \
                 subdirs.dirs + subdirs.tests + subdirs.tools
 
         # Skip tools dirs during libs traversal
         def libs_filter(current, subdirs):
+            if current in self._may_skip[tier]:
+                current = None
             return current, subdirs.parallel, \
                 subdirs.static + subdirs.dirs + subdirs.tests
 
         # compile, binaries and tools tiers use the same traversal as export
         filters = {
             'export': export_filter,
             'compile': parallel_filter,
             'binaries': parallel_filter,
@@ -446,18 +465,19 @@ class RecursiveMakeBackend(CommonBackend
         # Fill the dependencies for traversal of each tier.
         for tier, filter in filters.items():
             main, all_deps = \
                 self._traversal.compute_dependencies(filter)
             for dir, deps in all_deps.items():
                 rule = root_deps_mk.create_rule(['%s/%s' % (dir, tier)])
                 if deps is not None:
                     rule.add_dependencies('%s/%s' % (d, tier) for d in deps if d)
-            root_deps_mk.create_rule(['recurse_%s' % tier]) \
-                        .add_dependencies('%s/%s' % (d, tier) for d in main)
+            rule = root_deps_mk.create_rule(['recurse_%s' % tier])
+            if main:
+                rule.add_dependencies('%s/%s' % (d, tier) for d in main)
 
         root_mk = Makefile()
 
         # Fill root.mk with the convenience variables.
         for tier, filter in filters.items() + [('all', self._traversal.default_filter)]:
             # Gather filtered subtiers for the given tier
             all_direct_subdirs = reduce(lambda x, y: x + y,
                                         self._traversal.get_subdirs(''), [])
@@ -565,18 +585,16 @@ class RecursiveMakeBackend(CommonBackend
                                    '  $(addprefix $(CURDIR)/,$(%s))'
                                    % unified_files_makefile_variable)
             rule = makefile.create_rule(['$(all_absolute_unified_files)'])
             rule.add_dependencies(['$(CURDIR)/%: %'])
 
     def consume_finished(self):
         CommonBackend.consume_finished(self)
 
-        self._fill_root_mk()
-
         for srcdir in sorted(self._backend_files.keys()):
             bf = self._backend_files[srcdir]
 
             if not os.path.exists(bf.objdir):
                 try:
                     os.makedirs(bf.objdir)
                 except OSError as error:
                     if error.errno != errno.EEXIST:
@@ -593,27 +611,37 @@ class RecursiveMakeBackend(CommonBackend
                     {'path': makefile}, 'Substituting makefile: {path}')
 
                 # Adding the Makefile.in here has the desired side-effect that
                 # if the Makefile.in disappears, this will force moz.build
                 # traversal. This means that when we remove empty Makefile.in
                 # files, the old file will get replaced with the autogenerated
                 # one automatically.
                 self.backend_input_files.add(makefile_in)
+
+                for skiplist in self._may_skip.values():
+                    if bf.relobjdir in skiplist:
+                        skiplist.remove(bf.relobjdir)
             else:
                 self.log(logging.DEBUG, 'stub_makefile',
                     {'path': makefile}, 'Creating stub Makefile: {path}')
 
+            # Can't skip directories with a jar.mn for the 'libs' tier.
+            if bf.relobjdir in self._may_skip['libs'] and \
+                    os.path.exists(os.path.join(srcdir, 'jar.mn')):
+                self._may_skip['libs'].remove(bf.relobjdir)
+
             self._update_from_avoid_write(
                 bf.environment.create_makefile(makefile, stub=stub))
             self.summary.managed_count += 1
 
             self._update_from_avoid_write(bf.close())
             self.summary.managed_count += 1
 
+        self._fill_root_mk()
 
         # Write out a master list of all IPDL source files.
         ipdl_dir = os.path.join(self.environment.topobjdir, 'ipc', 'ipdl')
         ipdls = FileAvoidWrite(os.path.join(ipdl_dir, 'ipdlsrcs.mk'))
         mk = mozmakeutil.Makefile()
 
         sorted_ipdl_sources = list(sorted(self._ipdl_sources))
         mk.add_statement('ALL_IPDLSRCS := %s\n' % ' '.join(sorted_ipdl_sources))
@@ -794,16 +822,29 @@ class RecursiveMakeBackend(CommonBackend
 
         # The directory needs to be registered whether subdirectories have been
         # registered or not.
         self._traversal.add(backend_file.relobjdir)
 
         if obj.is_tool_dir:
             fh.write('IS_TOOL_DIR := 1\n')
 
+        if self._no_skip:
+            return
+
+        affected_tiers = set(obj.affected_tiers)
+        if 'compile' in affected_tiers or 'binaries' in affected_tiers:
+            affected_tiers.add('libs')
+        if obj.is_tool_dir and 'libs' in affected_tiers:
+            affected_tiers.remove('libs')
+            affected_tiers.add('tools')
+
+        for tier in set(self._may_skip.keys()) - affected_tiers:
+            self._may_skip[tier].add(backend_file.relobjdir)
+
     def _process_exports(self, obj, exports, backend_file, namespace=""):
         # This may not be needed, but is present for backwards compatibility
         # with the old make rules, just in case.
         if not obj.dist_install:
             return
 
         strings = exports.get_strings()
         if namespace:
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -330,16 +330,17 @@ class TreeMetadataEmitter(LoggingMixin):
         o.dirs = sandbox.get('DIRS', [])
         o.parallel_dirs = sandbox.get('PARALLEL_DIRS', [])
         o.tool_dirs = sandbox.get('TOOL_DIRS', [])
         o.test_dirs = sandbox.get('TEST_DIRS', [])
         o.test_tool_dirs = sandbox.get('TEST_TOOL_DIRS', [])
         o.external_make_dirs = sandbox.get('EXTERNAL_MAKE_DIRS', [])
         o.parallel_external_make_dirs = sandbox.get('PARALLEL_EXTERNAL_MAKE_DIRS', [])
         o.is_tool_dir = sandbox.get('IS_TOOL_DIR', False)
+        o.affected_tiers = sandbox.get_affected_tiers()
 
         if 'TIERS' in sandbox:
             for tier in sandbox['TIERS']:
                 o.tier_dirs[tier] = sandbox['TIERS'][tier]['regular'] + \
                     sandbox['TIERS'][tier]['external']
                 o.tier_static_dirs[tier] = sandbox['TIERS'][tier]['static']
 
         yield o
--- a/python/mozbuild/mozbuild/frontend/mach_commands.py
+++ b/python/mozbuild/mozbuild/frontend/mach_commands.py
@@ -107,17 +107,17 @@ class MozbuildFileCommands(object):
         print('')
 
         for v in sorted(SPECIAL_VARIABLES.keys()):
             self.special_reference(v)
 
         return 0
 
     def variable_reference(self, v):
-        st_typ, in_type, default, doc = VARIABLES[v]
+        st_typ, in_type, default, doc, tier = VARIABLES[v]
 
         print(v)
         print('=' * len(v))
         print('')
 
         summary, extra = get_doc(doc)
 
         print(summary)
--- a/python/mozbuild/mozbuild/frontend/sandbox.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox.py
@@ -119,18 +119,18 @@ class GlobalNamespace(dict):
 
     def __setitem__(self, name, value):
         if self._allow_all_writes:
             dict.__setitem__(self, name, value)
             return
 
         # We don't need to check for name.isupper() here because LocalNamespace
         # only sends variables our way if isupper() is True.
-        stored_type, input_type, default, docs = \
-            self._allowed_variables.get(name, (None, None, None, None))
+        stored_type, input_type, default, docs, tier = \
+            self._allowed_variables.get(name, (None, None, None, None, None))
 
         # Variable is unknown.
         if stored_type is None:
             self.last_name_error = KeyError('global_ns', 'set_unknown', name,
                 value)
             raise self.last_name_error
 
         # If the incoming value is not the type we store, we try to convert
@@ -262,16 +262,17 @@ class Sandbox(object):
     """
     def __init__(self, allowed_variables=None, builtins=None):
         """Initialize a Sandbox ready for execution.
 
         The arguments are proxied to GlobalNamespace.__init__.
         """
         self._globals = GlobalNamespace(allowed_variables=allowed_variables,
             builtins=builtins)
+        self._allowed_variables = allowed_variables
         self._locals = LocalNamespace(self._globals)
         self._execution_stack = []
         self.main_path = None
         self.all_paths = set()
 
     def exec_file(self, path):
         """Execute code at a path in the sandbox.
 
@@ -358,8 +359,13 @@ class Sandbox(object):
     def iterkeys(self):
         return self.__iter__()
 
     def __contains__(self, key):
         return key in self._globals
 
     def get(self, key, default=None):
         return self._globals.get(key, default)
+
+    def get_affected_tiers(self):
+        tiers = (self._allowed_variables[key][4] for key in self
+                 if key in self._allowed_variables)
+        return set(tier for tier in tiers if tier)
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -51,37 +51,48 @@ def doc_to_paragraphs(doc):
 
     return paragraphs
 
 
 # This defines the set of mutable global variables.
 #
 # Each variable is a tuple of:
 #
-#   (storage_type, input_types, default_value, docs)
+#   (storage_type, input_types, default_value, docs, tier)
 #
+# Tier says for which specific tier the variable has an effect.
+# Valid tiers are:
+# - 'export'
+# - 'compile': everything in relation with compiling objects.
+# - 'binaries': everything in relation with linking objects, producing
+#      programs and libraries.
+# - 'libs': everything that is not compile or binaries and that has
+#      traditionally been in the libs tier.
+# - 'tools'
+# A value of None means the variable has no direct effect on any tier.
+
 VARIABLES = {
     # Variables controlling reading of other frontend files.
     'ASFILES': (StrictOrderingOnAppendList, list, [],
         """Assembly file sources.
 
         This variable contains a list of files to invoke the assembler on.
-        """),
+        """, 'compile'),
 
     'CMMSRCS': (StrictOrderingOnAppendList, list, [],
         """Sources to compile with the Objective C/C++ compiler.
 
         This variable contains a list of objective-C++ sources to compile.
-        """),
+        """, 'compile'),
 
     'CSRCS': (StrictOrderingOnAppendList, list, [],
         """C code source files.
 
         This variable contains a list of C source files to compile.
-        """),
+        """, 'compile'),
 
     'DEFINES': (OrderedDict, dict, OrderedDict(),
         """Dictionary of compiler defines to declare.
 
         These are passed in to the compiler as -Dkey='value' for string values,
         -Dkey=value for numeric values, or -Dkey if the value is True. Note
         that for string values, the outer-level of single-quotes will be
         consumed by the shell. If you want to have a string-literal in the
@@ -97,223 +108,223 @@ VARIABLES = {
         respectively. These could also be combined into a single
         update:
 
         DEFINES.update({
             'NS_NO_XPCOM': True,
             'MOZ_EXTENSIONS_DB_SCHEMA': 15,
             'DLL_SUFFIX': '".so"',
         })
-        """),
+        """, None),
 
     'DIRS': (list, list, [],
         """Child directories to descend into looking for build frontend files.
 
         This works similarly to the DIRS variable in make files. Each str value
         in the list is the name of a child directory. When this file is done
         parsing, the build reader will descend into each listed directory and
         read the frontend file there. If there is no frontend file, an error
         is raised.
 
         Values are relative paths. They can be multiple directory levels
         above or below. Use ".." for parent directories and "/" for path
         delimiters.
-        """),
+        """, None),
 
     'EXPORT_LIBRARY': (bool, bool, False,
         """Install the library to the static libraries folder.
-        """),
+        """, None),
 
     'EXTRA_COMPONENTS': (StrictOrderingOnAppendList, list, [],
         """Additional component files to distribute.
 
        This variable contains a list of files to copy into $(FINAL_TARGET)/components/.
-        """),
+        """, 'libs'),
 
     'EXTRA_JS_MODULES': (StrictOrderingOnAppendList, list, [],
         """Additional JavaScript files to distribute.
 
         This variable contains a list of files to copy into
         $(FINAL_TARGET)/$(JS_MODULES_PATH). JS_MODULES_PATH defaults to
         "modules" if left undefined.
-        """),
+        """, 'libs'),
 
     'EXTRA_PP_JS_MODULES': (StrictOrderingOnAppendList, list, [],
         """Additional JavaScript files to distribute.
 
         This variable contains a list of files to copy into
         $(FINAL_TARGET)/$(JS_MODULES_PATH), after preprocessing.
         JS_MODULES_PATH defaults to "modules" if left undefined.
-        """),
+        """, 'libs'),
 
     'EXTRA_PP_COMPONENTS': (StrictOrderingOnAppendList, list, [],
         """Javascript XPCOM files.
 
        This variable contains a list of files to preprocess.  Generated
        files will be installed in the /components directory of the distribution.
-        """),
+        """, 'libs'),
 
     'CPP_UNIT_TESTS': (StrictOrderingOnAppendList, list, [],
         """C++ source files for unit tests.
 
         This is a list of C++ unit test sources. Entries must be files that
         exist. These generally have .cpp extensions.
-        """),
+        """, 'binaries'),
 
     'FAIL_ON_WARNINGS': (bool, bool, False,
         """Whether to treat warnings as errors.
-        """),
+        """, None),
 
     'FORCE_SHARED_LIB': (bool, bool, False,
         """Whether the library in this directory is a shared library.
-        """),
+        """, None),
 
     'FORCE_STATIC_LIB': (bool, bool, False,
         """Whether the library in this directory is a static library.
-        """),
+        """, None),
 
     'GTEST_C_SOURCES': (StrictOrderingOnAppendList, list, [],
         """C code source files for GTest unit tests.
 
         This variable contains a list of C GTEST unit test source files to
         compile.
-        """),
+        """, 'compile'),
 
     'GTEST_CMM_SOURCES': (StrictOrderingOnAppendList, list, [],
         """Sources for GTest unit tests to compile with the Objective C/C++ compiler.
 
         This variable contains a list of objective-C++ GTest unit test sources
         to compile.
-        """),
+        """, 'compile'),
 
     'GTEST_CPP_SOURCES': (list, list, [],
         """C++ source files for GTest unit tests.
 
         This is a list of C++ GTest unit test sources. Entries must be files
         that exist. These generally have .cpp, .cc, or .cxx extensions.
-        """),
+        """, 'compile'),
 
     'HOST_CPPSRCS': (StrictOrderingOnAppendList, list, [],
         """C++ source files to compile with the host compiler.
 
         This variable contains a list of C++ source files to compile.
-        """),
+        """, 'compile'),
 
     'HOST_CSRCS': (StrictOrderingOnAppendList, list, [],
         """C source files to compile with the host compiler.
 
         This variable contains a list of C source files to compile.
-        """),
+        """, 'compile'),
 
     'IS_COMPONENT': (bool, bool, False,
         """Whether the library contains a binary XPCOM component manifest.
-        """),
+        """, None),
 
     'PARALLEL_DIRS': (list, list, [],
         """A parallel version of DIRS.
 
         Ideally this variable does not exist. It is provided so a transition
         from recursive makefiles can be made. Once the build system has been
         converted to not use Makefile's for the build frontend, this will
         likely go away.
-        """),
+        """, None),
 
     'HOST_LIBRARY_NAME': (unicode, unicode, "",
         """Name of target library generated when cross compiling.
-        """),
+        """, 'binaries'),
 
     'JS_MODULES_PATH': (unicode, unicode, "",
         """Sub-directory of $(FINAL_TARGET) to install EXTRA_JS_MODULES.
 
         EXTRA_JS_MODULES files are copied to
         $(FINAL_TARGET)/$(JS_MODULES_PATH). This variable does not
         need to be defined if the desired destination directory is
         $(FINAL_TARGET)/modules.
-        """),
+        """, None),
 
     'LIBRARY_NAME': (unicode, unicode, "",
         """The name of the library generated for a directory.
 
         Example:
         In example/components/moz.build,
         LIBRARY_NAME = 'xpcomsample'
         would generate example/components/libxpcomsample.so on Linux, or
         example/components/xpcomsample.lib on Windows.
-        """),
+        """, 'binaries'),
 
     'LIBS': (StrictOrderingOnAppendList, list, [],
         """Linker libraries and flags.
 
         A list of libraries and flags to include when linking.
-        """),
+        """, None),
 
     'LIBXUL_LIBRARY': (bool, bool, False,
         """Whether the library in this directory is linked into libxul.
 
         Implies MOZILLA_INTERNAL_API and FORCE_STATIC_LIB.
-        """),
+        """, None),
 
     'LOCAL_INCLUDES': (StrictOrderingOnAppendList, list, [],
         """Additional directories to be searched for include files by the compiler.
-        """),
+        """, None),
 
     'MSVC_ENABLE_PGO': (bool, bool, False,
         """Whether profile-guided optimization is enabled in this directory.
-        """),
+        """, None),
 
     'OS_LIBS': (list, list, [],
         """System link libraries.
 
         This variable contains a list of system libaries to link against.
-        """),
+        """, None),
 
     'SDK_LIBRARY': (StrictOrderingOnAppendList, list, [],
         """Elements of the distributed SDK.
 
         Files on this list will be copied into SDK_LIB_DIR ($DIST/sdk/lib).
-        """),
+        """, None),
 
     'SHARED_LIBRARY_LIBS': (StrictOrderingOnAppendList, list, [],
         """Libraries linked into a shared library.
 
         A list of static library paths which should be linked into the current shared library.
-        """),
+        """, None),
 
     'SIMPLE_PROGRAMS': (StrictOrderingOnAppendList, list, [],
         """Generate a list of binaries from source.
 
         A list of sources, one per program, to compile & link with libs into standalone programs.
-        """),
+        """, 'binaries'),
 
     'SSRCS': (StrictOrderingOnAppendList, list, [],
         """Assembly source files.
 
         This variable contains a list of files to invoke the assembler on.
-        """),
+        """, 'compile'),
 
     'TOOL_DIRS': (list, list, [],
         """Like DIRS but for tools.
 
         Tools are for pieces of the build system that aren't required to
         produce a working binary (in theory). They provide things like test
         code and utilities.
-        """),
+        """, None),
 
     'TEST_DIRS': (list, list, [],
         """Like DIRS but only for directories that contain test-only code.
 
         If tests are not enabled, this variable will be ignored.
 
         This variable may go away once the transition away from Makefiles is
         complete.
-        """),
+        """, None),
 
     'TEST_TOOL_DIRS': (list, list, [],
         """TOOL_DIRS that is only executed if tests are enabled.
-        """),
+        """, None),
 
 
     'TIERS': (OrderedDict, dict, OrderedDict(),
         """Defines directories constituting the tier traversal mechanism.
 
         The recursive make backend iteration is organized into tiers. There are
         major tiers (keys in this dict) that correspond roughly to applications
         or libraries being built. e.g. base, nspr, js, platform, app. Within
@@ -322,170 +333,170 @@ VARIABLES = {
         next tier until all tiers are exhausted.
 
         Tiers are a way of working around deficiencies in recursive make. These
         will probably disappear once we no longer rely on recursive make for
         the build backend. They will likely be replaced by DIRS.
 
         This variable is typically not populated directly. Instead, it is
         populated by calling add_tier_dir().
-        """),
+        """, None),
 
     'EXTERNAL_MAKE_DIRS': (list, list, [],
         """Directories that build with make but don't use moz.build files.
 
         This is like DIRS except it implies that |make| is used to build the
         directory and that the directory does not define itself with moz.build
         files.
-        """),
+        """, None),
 
     'PARALLEL_EXTERNAL_MAKE_DIRS': (list, list, [],
         """Parallel version of EXTERNAL_MAKE_DIRS.
-        """),
+        """, None),
 
     'CONFIGURE_SUBST_FILES': (StrictOrderingOnAppendList, list, [],
         """Output files that will be generated using configure-like substitution.
 
         This is a substitute for AC_OUTPUT in autoconf. For each path in this
         list, we will search for a file in the srcdir having the name
         {path}.in. The contents of this file will be read and variable patterns
         like @foo@ will be substituted with the values of the AC_SUBST
         variables declared during configure.
-        """),
+        """, None),
 
     'MODULE': (unicode, unicode, "",
         """Module name.
 
         Historically, this variable was used to describe where to install header
         files, but that feature is now handled by EXPORTS_NAMESPACES. MODULE
         will likely be removed in the future.
-        """),
+        """, None),
 
     'EXPORTS': (HierarchicalStringList, list, HierarchicalStringList(),
         """List of files to be exported, and in which subdirectories.
 
         EXPORTS is generally used to list the include files to be exported to
         dist/include, but it can be used for other files as well. This variable
         behaves as a list when appending filenames for export in the top-level
         directory. Files can also be appended to a field to indicate which
         subdirectory they should be exported to. For example, to export 'foo.h'
         to the top-level directory, and 'bar.h' to mozilla/dom/, append to
         EXPORTS like so:
 
         EXPORTS += ['foo.h']
         EXPORTS.mozilla.dom += ['bar.h']
-        """),
+        """, None),
 
     'PROGRAM' : (unicode, unicode, "",
         """Compiled executable name.
 
         If the configuration token 'BIN_SUFFIX' is set, its value will be
         automatically appended to PROGRAM. If PROGRAM already ends with
         BIN_SUFFIX, PROGRAM will remain unchanged.
-        """),
+        """, 'binaries'),
 
     'CPP_SOURCES': (list, list, [],
         """C++ source file list.
 
         This is a list of C++ files to be compiled. Entries must be files that
         exist. These generally have .cpp, .cc, or .cxx extensions.
-        """),
+        """, 'compile'),
 
     'NO_DIST_INSTALL': (bool, bool, False,
         """Disable installing certain files into the distribution directory.
 
         If present, some files defined by other variables won't be
         distributed/shipped with the produced build.
-        """),
+        """, None),
 
     # IDL Generation.
     'XPIDL_SOURCES': (StrictOrderingOnAppendList, list, [],
         """XPCOM Interface Definition Files (xpidl).
 
         This is a list of files that define XPCOM interface definitions.
         Entries must be files that exist. Entries are almost certainly .idl
         files.
-        """),
+        """, None),
 
     'XPIDL_MODULE': (unicode, unicode, "",
         """XPCOM Interface Definition Module Name.
 
         This is the name of the .xpt file that is created by linking
         XPIDL_SOURCES together. If unspecified, it defaults to be the same as
         MODULE.
-        """),
+        """, None),
 
     'IPDL_SOURCES': (StrictOrderingOnAppendList, list, [],
         """IPDL source files.
 
         These are .ipdl files that will be parsed and converted to .cpp files.
-        """),
+        """, 'export'),
 
     'WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
         """WebIDL source files.
 
         These will be parsed and converted to .cpp and .h files.
-        """),
+        """, 'export'),
 
     'GENERATED_EVENTS_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
         """WebIDL source files for generated events.
 
         These will be parsed and converted to .cpp and .h files.
-        """),
+        """, 'export'),
 
     'TEST_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
          """Test WebIDL source files.
 
          These will be parsed and converted to .cpp and .h files if tests are
          enabled.
-         """),
+         """, 'export'),
 
     'GENERATED_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
          """Generated WebIDL source files.
 
          These will be generated from some other files.
-         """),
+         """, 'export'),
 
     'PREPROCESSED_TEST_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
          """Preprocessed test WebIDL source files.
 
          These will be preprocessed, then parsed and converted to .cpp
          and .h files if tests are enabled.
-         """),
+         """, 'export'),
 
     'PREPROCESSED_WEBIDL_FILES': (StrictOrderingOnAppendList, list, [],
          """Preprocessed WebIDL source files.
 
          These will be preprocessed before being parsed and converted.
-         """),
+         """, 'export'),
 
     # Test declaration.
     'A11Y_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining a11y tests.
-        """),
+        """, None),
 
     'BROWSER_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining browser chrome tests.
-        """),
+        """, None),
 
     'MOCHITEST_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining mochitest tests.
-        """),
+        """, None),
 
     'MOCHITEST_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining mochitest chrome tests.
-        """),
+        """, None),
 
     'WEBRTC_SIGNALLING_TEST_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining WebRTC signalling tests.
-        """),
+        """, None),
 
     'XPCSHELL_TESTS_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining xpcshell tests.
-        """),
+        """, None),
 }
 
 # The set of functions exposed to the sandbox.
 #
 # Each entry is a tuple of:
 #
 #  (method attribute, (argument types), docs)
 #
--- a/python/mozbuild/mozbuild/test/frontend/test_sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_sandbox_symbols.py
@@ -31,17 +31,17 @@ class TestSymbols(unittest.TestCase):
 
         self.assertGreater(len(lines), 0)
         self.assertGreater(len(lines[0].strip()), 0)
 
         # Last line should be empty.
         self.assertEqual(lines[-1].strip(), '')
 
     def test_documentation_formatting(self):
-        for typ, inp, default, doc in VARIABLES.values():
+        for typ, inp, default, doc, tier in VARIABLES.values():
             self._verify_doc(doc)
 
         for attr, args, doc in FUNCTIONS.values():
             self._verify_doc(doc)
 
         for typ, doc in SPECIAL_VARIABLES.values():
             self._verify_doc(doc)