bug 1295937 - Improvements to gyp_reader to handle NSS gyp files. r=glandium
authorTed Mielczarek <ted@mielczarek.org>
Tue, 15 Nov 2016 12:37:09 -0500
changeset 323299 165a160a0f689ab1e0143376d7ee8ce07c300fb6
parent 323298 95e32667262020fd3da24ad0450e7cda74cc6559
child 323300 ee70776759bf296c951a9805d5b79169291be4d3
push id30973
push userryanvm@gmail.com
push dateFri, 18 Nov 2016 20:47:14 +0000
treeherdermozilla-central@f09e137ead39 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1295937
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -515,16 +515,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
@@ -1619,34 +1619,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))