bug 1295937 - Improvements to gyp_reader to handle NSS gyp files. r?glandium draft
authorTed Mielczarek <ted@mielczarek.org>
Tue, 15 Nov 2016 12:37:09 -0500
changeset 439320 3e77b0917ffc1155918cd7ed15d48974be6b312b
parent 439319 e57a658848da1b1c633e9b6562e93969e9b7b457
child 439321 89cd31997c13892c03c7a58d203040e9077f153f
push id35972
push userbmo:ted@mielczarek.org
push dateTue, 15 Nov 2016 21:18:17 +0000
reviewersglandium
bugs1295937
milestone52.0a1
bug 1295937 - Improvements to gyp_reader to handle NSS gyp files. r?glandium This patch contains a number of changes to the gyp_reader code: * Add three new flags to GYP_DIRS: ** no_chromium, to skip forcing the includes/etc needed for chromium gyp files ** no_unified, to force building all sources without unification ** action_overrides, to pass scripts used when mapping gyp actions to moz.build GENERATED_FILES * Handle the flags mentioned above in read_from_gyp * Handle actions in gyp targets by mapping them to GENERATED_FILES, using scripts specified in the action_overrides flag. We don't try to handle the generic action case, we require special-casing for each action. * Handle a subset of copies in gyp targets by mapping them to EXPORTS, just enough to handle the use of them for NSS exports. * Handle shared_library and executable gyp targets * Handle gyp target dependencies/libraries as USE_LIBS/OS_LIBS * Handle generated source files * Handle .def files in sources by mapping them to SYMBOLS_FILE * Special-case some include_dirs: ** Map `<(PRODUCT_DIR)/dist/` to $DIST/include (to handle include paths for NSS exports) ** Map include_dirs starting with topobjdir to objdir-relative paths, to handle passing the NSPR include path to NSS * split /build/gyp.mozbuild into two parts, with gyp_base.mozbuild containing generic bits, and gyp.mozbuild containing chromium-specific bits MozReview-Commit-ID: FbDmlqDjRp4
build/gyp.mozbuild
build/gyp_base.mozbuild
build/moz.configure/init.configure
python/mozbuild/mozbuild/frontend/context.py
python/mozbuild/mozbuild/frontend/gyp_reader.py
python/mozbuild/mozbuild/frontend/reader.py
--- a/build/gyp.mozbuild
+++ b/build/gyp.mozbuild
@@ -1,15 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-gyp_vars = {
+include('gyp_base.mozbuild')
+
+gyp_vars.update({
     'lsan': 0,
     'asan': 0,
     'build_with_mozilla': 1,
     'build_with_chromium': 0,
     'use_official_google_api_keys': 0,
     'have_clock_monotonic': 1 if CONFIG['HAVE_CLOCK_MONOTONIC'] else 0,
     'have_ethtool_cmd_speed_hi': 1 if CONFIG['MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI'] else 0,
     'include_alsa_audio': 1 if CONFIG['MOZ_ALSA'] else 0,
@@ -64,61 +66,32 @@ gyp_vars = {
     'include_g711': 1,
     'include_opus': 1,
     'include_g722': 1,
     'include_ilbc': 0,
     # We turn on ISAC because the AGC uses parts of it, and depend on the
     # linker to throw away uneeded bits.
     'include_isac': 1,
     'include_pcm16b': 1,
-}
-
-os = CONFIG['OS_TARGET']
+})
 
-if os == 'WINNT':
-    gyp_vars.update(
-        MSVS_VERSION=CONFIG['_MSVS_VERSION'],
-        MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32,
-    )
-elif os == 'Android':
+if os == 'Android':
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         gyp_vars['build_with_gonk'] = 1
         gyp_vars['moz_widget_toolkit_gonk'] = 1
         gyp_vars['opus_complexity'] = 1
         if int(CONFIG['ANDROID_VERSION']) >= 18:
           gyp_vars['moz_webrtc_omx'] = 1
     else:
         gyp_vars.update(
             gtest_target_type='executable',
             moz_webrtc_mediacodec=1,
             android_toolchain=CONFIG.get('ANDROID_TOOLCHAIN', ''),
         )
 
-flavors = {
-    'WINNT': 'win',
-    'Android': 'linux' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' else 'android',
-    'Linux': 'linux',
-    'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
-    'SunOS': 'solaris',
-    'GNU/kFreeBSD': 'freebsd',
-    'DragonFly': 'dragonfly',
-    'FreeBSD': 'freebsd',
-    'NetBSD': 'netbsd',
-    'OpenBSD': 'openbsd',
-}
-gyp_vars['OS'] = flavors.get(os)
-
-arches = {
-    'x86_64': 'x64',
-    'x86': 'ia32',
-    'aarch64': 'arm64',
-}
-
-gyp_vars['target_arch'] = arches.get(CONFIG['CPU_ARCH'], CONFIG['CPU_ARCH'])
-
 if CONFIG['ARM_ARCH']:
     if int(CONFIG['ARM_ARCH']) < 7:
         gyp_vars['armv7'] = 0
         gyp_vars['arm_neon_optional'] = 0
     elif os == 'Android':
         gyp_vars['armv7'] = 1
     else:
         # CPU detection for ARM works on Android only.  armv7 always uses CPU
new file mode 100644
--- /dev/null
+++ b/build/gyp_base.mozbuild
@@ -0,0 +1,38 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gyp_vars = {}
+
+os = CONFIG['OS_TARGET']
+
+if os == 'WINNT':
+    gyp_vars.update(
+        MSVS_VERSION=CONFIG['_MSVS_VERSION'],
+        MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32,
+    )
+
+flavors = {
+    'WINNT': 'win',
+    'Android': 'linux' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' else 'android',
+    'Linux': 'linux',
+    'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
+    'SunOS': 'solaris',
+    'GNU/kFreeBSD': 'freebsd',
+    'DragonFly': 'dragonfly',
+    'FreeBSD': 'freebsd',
+    'NetBSD': 'netbsd',
+    'OpenBSD': 'openbsd',
+}
+gyp_vars['OS'] = flavors.get(os)
+
+arches = {
+    'x86_64': 'x64',
+    'x86': 'ia32',
+    'aarch64': 'arm64',
+}
+
+gyp_vars['host_arch'] = arches.get(CONFIG['HOST_CPU_ARCH'], CONFIG['HOST_CPU_ARCH'])
+gyp_vars['target_arch'] = arches.get(CONFIG['CPU_ARCH'], CONFIG['CPU_ARCH'])
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -553,16 +553,17 @@ def host_variables(host):
     if host.kernel == 'kFreeBSD':
         os_arch = 'GNU_kFreeBSD'
     else:
         os_arch = host.kernel
     return namespace(
         HOST_OS_ARCH=os_arch,
     )
 
+set_config('HOST_CPU_ARCH', delayed_getattr(host, 'cpu'))
 set_config('HOST_OS_ARCH', delayed_getattr(host_variables, 'HOST_OS_ARCH'))
 add_old_configure_assignment('HOST_OS_ARCH',
                              delayed_getattr(host_variables, 'HOST_OS_ARCH'))
 
 @depends(target)
 def target_is_windows(target):
     if target.kernel == 'WINNT':
         return True
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -1623,34 +1623,43 @@ VARIABLES = {
         files are elsehwere (jar.mn, for instance) and this state of affairs
         is OK.
         """),
 
     'GYP_DIRS': (StrictOrderingOnAppendListWithFlagsFactory({
             'variables': dict,
             'input': unicode,
             'sandbox_vars': dict,
+            'no_chromium': bool,
+            'no_unified': bool,
             'non_unified_sources': StrictOrderingOnAppendList,
+            'action_overrides': dict,
         }), list,
         """Defines a list of object directories handled by gyp configurations.
 
         Elements of this list give the relative object directory. For each
         element of the list, GYP_DIRS may be accessed as a dictionary
         (GYP_DIRS[foo]). The object this returns has attributes that need to be
         set to further specify gyp processing:
             - input, gives the path to the root gyp configuration file for that
               object directory.
             - variables, a dictionary containing variables and values to pass
               to the gyp processor.
             - sandbox_vars, a dictionary containing variables and values to
               pass to the mozbuild processor on top of those derived from gyp
               configuration.
+            - no_chromium, a boolean which if set to True disables some
+              special handling that emulates gyp_chromium.
+            - no_unified, a boolean which if set to True disables source
+              file unification entirely.
             - non_unified_sources, a list containing sources files, relative to
               the current moz.build, that should be excluded from source file
               unification.
+            - action_overrides, a dict of action_name to values of the `script`
+              attribute to use for GENERATED_FILES for the specified action.
 
         Typical use looks like:
             GYP_DIRS += ['foo', 'bar']
             GYP_DIRS['foo'].input = 'foo/foo.gyp'
             GYP_DIRS['foo'].variables = {
                 'foo': 'bar',
                 (...)
             }
--- a/python/mozbuild/mozbuild/frontend/gyp_reader.py
+++ b/python/mozbuild/mozbuild/frontend/gyp_reader.py
@@ -1,22 +1,24 @@
 # 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 absolute_import, unicode_literals
 
 import gyp
+import gyp.msvs_emulation
 import sys
 import os
 import types
 import mozpack.path as mozpath
 from mozpack.files import FileFinder
 from .sandbox import alphabetical_sorted
 from .context import (
+    ObjDirPath,
     SourcePath,
     TemplateContext,
     VARIABLES,
 )
 from mozbuild.util import (
     expand_variables,
     List,
     memoize,
@@ -33,23 +35,30 @@ sys.modules['gyp.generator.mozbuild'] = 
 #   sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
 # We're not importing gyp_chromium, but we want both script_dir and
 # chrome_src for the default includes, so go backwards from the pylib
 # directory, which is the parent directory of gyp module.
 chrome_src = mozpath.abspath(mozpath.join(mozpath.dirname(gyp.__file__),
     '../../../..'))
 script_dir = mozpath.join(chrome_src, 'build')
 
+
+def encode(value):
+    if isinstance(value, unicode):
+        return value.encode('utf-8')
+    return value
+
+
 # Default variables gyp uses when evaluating gyp files.
 generator_default_variables = {
 }
-for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR',
-                'LIB_DIR', 'SHARED_LIB_DIR']:
+for dirname in [b'INTERMEDIATE_DIR', b'SHARED_INTERMEDIATE_DIR', b'PRODUCT_DIR',
+                b'LIB_DIR', b'SHARED_LIB_DIR']:
   # Some gyp steps fail if these are empty(!).
-  generator_default_variables[dirname] = b'dir'
+  generator_default_variables[dirname] = b'$' + dirname
 
 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
                'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
                'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
                'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
                'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
                'LINKER_SUPPORTS_ICF']:
   generator_default_variables[unused] = b''
@@ -63,56 +72,92 @@ class GypContext(TemplateContext):
     relative to the topobjdir defined in the ConfigEnvironment.
     """
     def __init__(self, config, relobjdir):
         self._relobjdir = relobjdir
         TemplateContext.__init__(self, template='Gyp',
             allowed_variables=VARIABLES, config=config)
 
 
-def encode(value):
-    if isinstance(value, unicode):
-        return value.encode('utf-8')
-    return value
+def handle_actions(actions, context, action_overrides):
+  idir = '$INTERMEDIATE_DIR/'
+  for action in actions:
+    name = action['action_name']
+    if name not in action_overrides:
+      raise RuntimeError('GYP action %s not listed in action_overrides' % name)
+    outputs = action['outputs']
+    if len(outputs) > 1:
+      raise NotImplementedError('GYP actions with more than one output not supported: %s' % name)
+    output = outputs[0]
+    if not output.startswith(idir):
+      raise NotImplementedError('GYP actions outputting to somewhere other than <(INTERMEDIATE_DIR) not supported: %s' % output)
+    output = output[len(idir):]
+    context['GENERATED_FILES'] += [output]
+    g = context['GENERATED_FILES'][output]
+    g.script = action_overrides[name]
+    g.inputs = action['inputs']
 
+def handle_copies(copies, context):
+  dist = '$PRODUCT_DIR/dist/'
+  for copy in copies:
+    dest = copy['destination']
+    if not dest.startswith(dist):
+      raise NotImplementedError('GYP copies to somewhere other than <(PRODUCT_DIR)/dist not supported: %s' % dest)
+    dest_paths = dest[len(dist):].split('/')
+    exports = context['EXPORTS']
+    while dest_paths:
+      exports = getattr(exports, dest_paths.pop(0))
+    exports += sorted(copy['files'], key=lambda x: x.lower())
 
-def read_from_gyp(config, path, output, vars, non_unified_sources = set()):
+def read_from_gyp(config, path, output, vars, no_chromium, no_unified, action_overrides, non_unified_sources = set()):
     """Read a gyp configuration and emits GypContexts for the backend to
     process.
 
     config is a ConfigEnvironment, path is the path to a root gyp configuration
     file, output is the base path under which the objdir for the various gyp
     dependencies will be, and vars a dict of variables to pass to the gyp
     processor.
     """
 
+    is_win = config.substs['OS_TARGET'] == 'WINNT'
+    is_msvc = bool(config.substs['_MSC_VER'])
     # gyp expects plain str instead of unicode. The frontend code gives us
     # unicode strings, so convert them.
     path = encode(path)
     str_vars = dict((name, encode(value)) for name, value in vars.items())
+    if is_msvc:
+        # This isn't actually used anywhere in this generator, but it's needed
+        # to override the registry detection of VC++ in gyp.
+        os.environ['GYP_MSVS_OVERRIDE_PATH'] = 'fake_path'
+        os.environ['GYP_MSVS_VERSION'] = config.substs['MSVS_VERSION']
 
     params = {
         b'parallel': False,
         b'generator_flags': {},
         b'build_files': [path],
         b'root_targets': None,
     }
 
-    # Files that gyp_chromium always includes
-    includes = [encode(mozpath.join(script_dir, 'common.gypi'))]
-    finder = FileFinder(chrome_src, find_executables=False)
-    includes.extend(encode(mozpath.join(chrome_src, name))
-        for name, _ in finder.find('*/supplement.gypi'))
+    if no_chromium:
+      includes = []
+      depth = mozpath.dirname(path)
+    else:
+      depth = chrome_src
+      # Files that gyp_chromium always includes
+      includes = [encode(mozpath.join(script_dir, 'common.gypi'))]
+      finder = FileFinder(chrome_src, find_executables=False)
+      includes.extend(encode(mozpath.join(chrome_src, name))
+          for name, _ in finder.find('*/supplement.gypi'))
 
     # Read the given gyp file and its dependencies.
     generator, flat_list, targets, data = \
         gyp.Load([path], format=b'mozbuild',
             default_variables=str_vars,
             includes=includes,
-            depth=encode(chrome_src),
+            depth=encode(depth),
             params=params)
 
     # Process all targets from the given gyp files and its dependencies.
     # The path given to AllTargets needs to use os.sep, while the frontend code
     # gives us paths normalized with forward slash separator.
     for target in gyp.common.AllTargets(flat_list, targets, path.replace(b'/', os.sep)):
         build_file, target_name, toolset = gyp.common.ParseQualifiedTarget(target)
 
@@ -141,70 +186,144 @@ def read_from_gyp(config, path, output, 
 
         # Derive which gyp configuration to use based on MOZ_DEBUG.
         c = 'Debug' if config.substs['MOZ_DEBUG'] else 'Release'
         if c not in spec['configurations']:
             raise RuntimeError('Missing %s gyp configuration for target %s '
                                'in %s' % (c, target_name, build_file))
         target_conf = spec['configurations'][c]
 
+        if 'actions' in spec:
+          handle_actions(spec['actions'], context, action_overrides)
+        if 'copies' in spec:
+          handle_copies(spec['copies'], context)
+
+        use_libs = []
+        libs = []
+        def add_deps(s):
+            for t in s.get('dependencies', []) + s.get('dependencies_original', []):
+                ty = targets[t]['type']
+                if ty in ('static_library', 'shared_library'):
+                    use_libs.append(targets[t]['target_name'])
+                # Manually expand out transitive dependencies--
+                # gyp won't do this for static libs or none targets.
+                if ty in ('static_library', 'none'):
+                    add_deps(targets[t])
+            libs.extend(spec.get('libraries', []))
+        #XXX: this sucks, but webrtc breaks with this right now because
+        # it builds a library called 'gtest' and we just get lucky
+        # that it isn't in USE_LIBS by that name anywhere.
+        if no_chromium:
+            add_deps(spec)
+
+        os_libs = []
+        for l in libs:
+          if l.startswith('-'):
+              os_libs.append(l)
+          elif l.endswith('.lib'):
+              os_libs.append(l[:-4])
+          elif l:
+            # For library names passed in from moz.build.
+            use_libs.append(os.path.basename(l))
+
         if spec['type'] == 'none':
+          if not ('actions' in spec or 'copies' in spec):
             continue
-        elif spec['type'] == 'static_library':
+        elif spec['type'] in ('static_library', 'shared_library', 'executable'):
             # Remove leading 'lib' from the target_name if any, and use as
             # library name.
             name = spec['target_name']
-            if name.startswith('lib'):
-                name = name[3:]
-            # The context expects an unicode string.
-            context['LIBRARY_NAME'] = name.decode('utf-8')
+            if spec['type'] in ('static_library', 'shared_library'):
+                if name.startswith('lib'):
+                    name = name[3:]
+                # The context expects an unicode string.
+                context['LIBRARY_NAME'] = name.decode('utf-8')
+            else:
+                context['PROGRAM'] = name.decode('utf-8')
+            if spec['type'] == 'shared_library':
+                context['FORCE_SHARED_LIB'] = True
+            elif spec['type'] == 'static_library' and spec.get('variables', {}).get('no_expand_libs', '0') == '1':
+                # PSM links a NSS static library, but our folded libnss
+                # doesn't actually export everything that all of the
+                # objects within would need, so that one library
+                # should be built as a real static library.
+                context['NO_EXPAND_LIBS'] = True
+            if use_libs:
+                context['USE_LIBS'] = sorted(use_libs, key=lambda s: s.lower())
+            if os_libs:
+                context['OS_LIBS'] = os_libs
             # gyp files contain headers and asm sources in sources lists.
             sources = []
             unified_sources = []
             extensions = set()
+            use_defines_in_asflags = False
             for f in spec.get('sources', []):
                 ext = mozpath.splitext(f)[-1]
                 extensions.add(ext)
-                s = SourcePath(context, f)
+                if f.startswith('$INTERMEDIATE_DIR/'):
+                  s = ObjDirPath(context, f.replace('$INTERMEDIATE_DIR/', '!'))
+                else:
+                  s = SourcePath(context, f)
                 if ext == '.h':
                     continue
-                if ext != '.S' and s not in non_unified_sources:
+                if ext == '.def':
+                    context['SYMBOLS_FILE'] = s
+                elif ext != '.S' and not no_unified and s not in non_unified_sources:
                     unified_sources.append(s)
                 else:
                     sources.append(s)
+                # The Mozilla build system doesn't use DEFINES for building
+                # ASFILES.
+                if ext == '.s':
+                    use_defines_in_asflags = True
 
             # The context expects alphabetical order when adding sources
             context['SOURCES'] = alphabetical_sorted(sources)
             context['UNIFIED_SOURCES'] = alphabetical_sorted(unified_sources)
 
-            for define in target_conf.get('defines', []):
+            defines = target_conf.get('defines', [])
+            if is_msvc and no_chromium:
+                msvs_settings = gyp.msvs_emulation.MsvsSettings(spec, {})
+                defines.extend(msvs_settings.GetComputedDefines(c))
+            for define in defines:
                 if '=' in define:
                     name, value = define.split('=', 1)
                     context['DEFINES'][name] = value
                 else:
                     context['DEFINES'][define] = True
 
+            product_dir_dist = '$PRODUCT_DIR/dist/'
             for include in target_conf.get('include_dirs', []):
-                # moz.build expects all LOCAL_INCLUDES to exist, so ensure they do.
-                #
-                # NB: gyp files sometimes have actual absolute paths (e.g.
-                # /usr/include32) and sometimes paths that moz.build considers
-                # absolute, i.e. starting from topsrcdir. There's no good way
-                # to tell them apart here, and the actual absolute paths are
-                # likely bogus. In any event, actual absolute paths will be
-                # filtered out by trying to find them in topsrcdir.
-                if include.startswith('/'):
-                    resolved = mozpath.abspath(mozpath.join(config.topsrcdir, include[1:]))
+                if include.startswith(product_dir_dist):
+                    # special-case includes of <(PRODUCT_DIR)/dist/ to match
+                    # handle_copies above. This is used for NSS' exports.
+                    include = '!/dist/include/' + include[len(product_dir_dist):]
+                elif include.startswith(config.topobjdir):
+                    # NSPR_INCLUDE_DIR gets passed into the NSS build this way.
+                    include = '!/' + mozpath.relpath(include, config.topobjdir)
                 else:
-                    resolved = mozpath.abspath(mozpath.join(mozpath.dirname(build_file), include))
-                if not os.path.exists(resolved):
-                    continue
+                  # moz.build expects all LOCAL_INCLUDES to exist, so ensure they do.
+                  #
+                  # NB: gyp files sometimes have actual absolute paths (e.g.
+                  # /usr/include32) and sometimes paths that moz.build considers
+                  # absolute, i.e. starting from topsrcdir. There's no good way
+                  # to tell them apart here, and the actual absolute paths are
+                  # likely bogus. In any event, actual absolute paths will be
+                  # filtered out by trying to find them in topsrcdir.
+                  if include.startswith('/'):
+                      resolved = mozpath.abspath(mozpath.join(config.topsrcdir, include[1:]))
+                  else:
+                      resolved = mozpath.abspath(mozpath.join(mozpath.dirname(build_file), include))
+                  if not os.path.exists(resolved):
+                      continue
                 context['LOCAL_INCLUDES'] += [include]
 
             context['ASFLAGS'] = target_conf.get('asflags_mozilla', [])
+            if use_defines_in_asflags and defines:
+                context['ASFLAGS'] += ['-D' + d for d in defines]
             flags = target_conf.get('cflags_mozilla', [])
             if flags:
                 suffix_map = {
                     '.c': 'CFLAGS',
                     '.cpp': 'CXXFLAGS',
                     '.cc': 'CXXFLAGS',
                     '.m': 'CMFLAGS',
                     '.mm': 'CMMFLAGS',
@@ -222,27 +341,28 @@ def read_from_gyp(config, path, output, 
                         if not f:
                             continue
                         # the result may be a string or a list.
                         if isinstance(f, types.StringTypes):
                             context[var].append(f)
                         else:
                             context[var].extend(f)
         else:
-            # Ignore other types than static_library because we don't have
+            # Ignore other types because we don't have
             # anything using them, and we're not testing them. They can be
             # added when that becomes necessary.
             raise NotImplementedError('Unsupported gyp target type: %s' % spec['type'])
 
-        # Add some features to all contexts. Put here in case LOCAL_INCLUDES
-        # order matters.
-        context['LOCAL_INCLUDES'] += [
-            '!/ipc/ipdl/_ipdlheaders',
-            '/ipc/chromium/src',
-            '/ipc/glue',
-        ]
-        # These get set via VC project file settings for normal GYP builds.
-        if config.substs['OS_TARGET'] == 'WINNT':
-            context['DEFINES']['UNICODE'] = True
-            context['DEFINES']['_UNICODE'] = True
+        if not no_chromium:
+          # Add some features to all contexts. Put here in case LOCAL_INCLUDES
+          # order matters.
+          context['LOCAL_INCLUDES'] += [
+              '!/ipc/ipdl/_ipdlheaders',
+              '/ipc/chromium/src',
+              '/ipc/glue',
+          ]
+          # These get set via VC project file settings for normal GYP builds.
+          if is_win:
+              context['DEFINES']['UNICODE'] = True
+              context['DEFINES']['_UNICODE'] = True
         context['DISABLE_STL_WRAPPING'] = True
 
         yield context
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -1152,22 +1152,28 @@ class BuildReader(object):
             from .gyp_reader import read_from_gyp
             non_unified_sources = set()
             for s in gyp_dir.non_unified_sources:
                 source = SourcePath(context, s)
                 if not self._finder.get(source.full_path):
                     raise SandboxValidationError('Cannot find %s.' % source,
                         context)
                 non_unified_sources.add(source)
+            action_overrides = {}
+            for action, script in gyp_dir.action_overrides.iteritems():
+                action_overrides[action] = SourcePath(context, script)
             time_start = time.time()
             for gyp_context in read_from_gyp(context.config,
                                              mozpath.join(curdir, gyp_dir.input),
                                              mozpath.join(context.objdir,
                                                           target_dir),
                                              gyp_dir.variables,
+                                             gyp_dir.no_chromium,
+                                             gyp_dir.no_unified,
+                                             action_overrides,
                                              non_unified_sources = non_unified_sources):
                 gyp_context.update(gyp_dir.sandbox_vars)
                 gyp_contexts.append(gyp_context)
                 self._file_count += len(gyp_context.all_paths)
             self._execution_time += time.time() - time_start
 
         for gyp_context in gyp_contexts:
             context['DIRS'].append(mozpath.relpath(gyp_context.objdir, context.objdir))