Merge from Chromium at DEPS revision 288042

This commit was generated by merge_to_master.py.

Change-Id: I579101318ff77c5e0666a08e4acd86ffb7277d4c
diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py
index 8b83f80..dc55da6 100644
--- a/pylib/gyp/generator/analyzer.py
+++ b/pylib/gyp/generator/analyzer.py
@@ -4,18 +4,48 @@
 
 """
 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of
-the generator flag file_path) the list of relative file paths to consider. If
-any target has at least one of the paths as a source (or input to an action or
-rule) then 'Found dependency' is output, otherwise 'No dependencies' is output.
+the generator flag config_path) the path of a json file that dictates the files
+and targets to search for. The following keys are supported:
+files: list of paths (relative) of the files to search for.
+targets: list of targets to search for. The target names are unqualified.
+
+The following is output:
+error: only supplied if there is an error.
+warning: only supplied if there is a warning.
+targets: the set of targets passed in via targets that either directly or
+  indirectly depend upon the set of paths supplied in files.
+status: indicates if any of the supplied files matched at least one target.
+
+If the generator flag analyzer_output_path is specified, output is written
+there. Otherwise output is written to stdout.
 """
 
 import gyp.common
 import gyp.ninja_syntax as ninja_syntax
+import json
 import os
 import posixpath
+import sys
 
 debug = False
 
+found_dependency_string = 'Found dependency'
+no_dependency_string = 'No dependencies'
+
+# MatchStatus is used indicate if and how a target depends upon the supplied
+# sources.
+# The target's sources contain one of the supplied paths.
+MATCH_STATUS_MATCHES = 1
+# The target has a dependency on another target that contains one of the
+# supplied paths.
+MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2
+# The target's sources weren't in the supplied paths and none of the target's
+# dependencies depend upon a target that matched.
+MATCH_STATUS_DOESNT_MATCH = 3
+# The target doesn't contain the source, but the dependent targets have not yet
+# been visited to determine a more specific status yet.
+MATCH_STATUS_TBD = 4
+
 generator_supports_multiple_toolsets = True
 
 generator_wants_static_library_dependencies_adjusted = False
@@ -126,21 +156,79 @@
 
 class Target(object):
   """Holds information about a particular target:
-  sources: set of source files defined by this target. This includes inputs to
-           actions and rules.
-  deps: list of direct dependencies."""
+  deps: set of the names of direct dependent targets.
+  match_staus: one of the MatchStatus values"""
   def __init__(self):
-    self.sources = []
-    self.deps = []
+    self.deps = set()
+    self.match_status = MATCH_STATUS_TBD
 
-def __GenerateTargets(target_list, target_dicts, toplevel_dir):
+class Config(object):
+  """Details what we're looking for
+  look_for_dependency_only: if true only search for a target listing any of
+                            the files in files.
+  files: set of files to search for
+  targets: see file description for details"""
+  def __init__(self):
+    self.look_for_dependency_only = True
+    self.files = []
+    self.targets = []
+
+  def Init(self, params):
+    """Initializes Config. This is a separate method as it may raise an
+    exception if there is a parse error."""
+    generator_flags = params.get('generator_flags', {})
+    # TODO(sky): nuke file_path and look_for_dependency_only once migrate
+    # recipes.
+    file_path = generator_flags.get('file_path', None)
+    if file_path:
+      self._InitFromFilePath(file_path)
+      return
+
+    # If |file_path| wasn't specified then we look for config_path.
+    # TODO(sky): always look for config_path once migrated recipes.
+    config_path = generator_flags.get('config_path', None)
+    if not config_path:
+      return
+    self.look_for_dependency_only = False
+    try:
+      f = open(config_path, 'r')
+      config = json.load(f)
+      f.close()
+    except IOError:
+      raise Exception('Unable to open file ' + config_path)
+    except ValueError as e:
+      raise Exception('Unable to parse config file ' + config_path + str(e))
+    if not isinstance(config, dict):
+      raise Exception('config_path must be a JSON file containing a dictionary')
+    self.files = config.get('files', [])
+    # Coalesce duplicates
+    self.targets = list(set(config.get('targets', [])))
+
+  def _InitFromFilePath(self, file_path):
+    try:
+      f = open(file_path, 'r')
+      for file_name in f:
+        if file_name.endswith('\n'):
+          file_name = file_name[0:len(file_name) - 1]
+          if len(file_name):
+            self.files.append(file_name)
+      f.close()
+    except IOError:
+      raise Exception('Unable to open file', file_path)
+
+def __GenerateTargets(target_list, target_dicts, toplevel_dir, files):
   """Generates a dictionary with the key the name of a target and the value a
-  Target. |toplevel_dir| is the root of the source tree."""
+  Target. |toplevel_dir| is the root of the source tree. If the sources of
+  a target match that of |files|, then |target.matched| is set to True.
+  This returns a tuple of the dictionary and whether at least one target's
+  sources listed one of the paths in |files|."""
   targets = {}
 
   # Queue of targets to visit.
   targets_to_visit = target_list[:]
 
+  matched = False
+
   while len(targets_to_visit) > 0:
     target_name = targets_to_visit.pop()
     if target_name in targets:
@@ -148,35 +236,80 @@
 
     target = Target()
     targets[target_name] = target
-    target.sources.extend(__ExtractSources(target_name,
-                                           target_dicts[target_name],
-                                           toplevel_dir))
+    sources = __ExtractSources(target_name, target_dicts[target_name],
+                               toplevel_dir)
+    for source in sources:
+      if source in files:
+        target.match_status = MATCH_STATUS_MATCHES
+        matched = True
+        break
 
     for dep in target_dicts[target_name].get('dependencies', []):
-      targets[target_name].deps.append(dep)
+      targets[target_name].deps.add(dep)
       targets_to_visit.append(dep)
 
-  return targets
+  return targets, matched
 
-def __GetFiles(params):
-  """Returns the list of files to analyze, or None if none specified."""
-  generator_flags = params.get('generator_flags', {})
-  file_path = generator_flags.get('file_path', None)
-  if not file_path:
-    return None
-  try:
-    f = open(file_path, 'r')
-    result = []
-    for file_name in f:
-      if file_name.endswith('\n'):
-        file_name = file_name[0:len(file_name) - 1]
-      if len(file_name):
-        result.append(file_name)
-    f.close()
+def _GetUnqualifiedToQualifiedMapping(all_targets, to_find):
+  """Returns a mapping (dictionary) from unqualified name to qualified name for
+  all the targets in |to_find|."""
+  result = {}
+  if not to_find:
     return result
-  except IOError:
-    print 'Unable to open file', file_path
-  return None
+  to_find = set(to_find)
+  for target_name in all_targets.keys():
+    extracted = gyp.common.ParseQualifiedTarget(target_name)
+    if len(extracted) > 1 and extracted[1] in to_find:
+      to_find.remove(extracted[1])
+      result[extracted[1]] = target_name
+      if not to_find:
+        return result
+  return result
+
+def _DoesTargetDependOn(target, all_targets):
+  """Returns true if |target| or any of its dependencies matches the supplied
+  set of paths. This updates |matches| of the Targets as it recurses.
+  target: the Target to look for.
+  all_targets: mapping from target name to Target.
+  matching_targets: set of targets looking for."""
+  if target.match_status == MATCH_STATUS_DOESNT_MATCH:
+    return False
+  if target.match_status == MATCH_STATUS_MATCHES or \
+      target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY:
+    return True
+  for dep_name in target.deps:
+    dep_target = all_targets[dep_name]
+    if _DoesTargetDependOn(dep_target, all_targets):
+      dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
+      return True
+    dep_target.match_status = MATCH_STATUS_DOESNT_MATCH
+  return False
+
+def _GetTargetsDependingOn(all_targets, possible_targets):
+  """Returns the list of targets in |possible_targets| that depend (either
+  directly on indirectly) on the matched files.
+  all_targets: mapping from target name to Target.
+  possible_targets: targets to search from."""
+  found = []
+  for target in possible_targets:
+    if _DoesTargetDependOn(all_targets[target], all_targets):
+      # possible_targets was initially unqualified, keep it unqualified.
+      found.append(gyp.common.ParseQualifiedTarget(target)[1])
+  return found
+
+def _WriteOutput(params, **values):
+  """Writes the output, either to stdout or a file is specified."""
+  output_path = params.get('generator_flags', {}).get(
+      'analyzer_output_path', None)
+  if not output_path:
+    print json.dumps(values)
+    return
+  try:
+    f = open(output_path, 'w')
+    f.write(json.dumps(values) + '\n')
+    f.close()
+  except IOError as e:
+    print 'Error writing to output file', output_path, str(e)
 
 def CalculateVariables(default_variables, params):
   """Calculate additional variables for use in the build (called by gyp)."""
@@ -202,26 +335,55 @@
 
 def GenerateOutput(target_list, target_dicts, data, params):
   """Called by gyp as the final stage. Outputs results."""
-  files = __GetFiles(params)
-  if not files:
-    print 'Must specify files to analyze via file_path generator flag'
-    return
+  config = Config()
+  try:
+    config.Init(params)
+    if not config.files:
+      if config.look_for_dependency_only:
+        print 'Must specify files to analyze via file_path generator flag'
+        return
+      raise Exception('Must specify files to analyze via config_path generator '
+                      'flag')
 
-  toplevel_dir = os.path.abspath(params['options'].toplevel_dir)
-  if os.sep == '\\' and os.altsep == '/':
-    toplevel_dir = toplevel_dir.replace('\\', '/')
-  if debug:
-    print 'toplevel_dir', toplevel_dir
-  targets = __GenerateTargets(target_list, target_dicts, toplevel_dir)
+    toplevel_dir = os.path.abspath(params['options'].toplevel_dir)
+    if os.sep == '\\' and os.altsep == '/':
+      toplevel_dir = toplevel_dir.replace('\\', '/')
+    if debug:
+      print 'toplevel_dir', toplevel_dir
 
-  files_set = frozenset(files)
-  found_in_all_sources = 0
-  for target_name, target in targets.iteritems():
-    sources = files_set.intersection(target.sources)
-    if len(sources):
-      print 'Found dependency'
-      if debug:
-        print 'Found dependency in', target_name, target.sources
+    all_targets, matched = __GenerateTargets(target_list, target_dicts,
+                                             toplevel_dir,
+                                             frozenset(config.files))
+
+    # Set of targets that refer to one of the files.
+    if config.look_for_dependency_only:
+      print found_dependency_string if matched else no_dependency_string
       return
 
-  print 'No dependencies'
+    warning = None
+    if matched:
+      unqualified_mapping = _GetUnqualifiedToQualifiedMapping(
+          all_targets, config.targets)
+      if len(unqualified_mapping) != len(config.targets):
+        not_found = []
+        for target in config.targets:
+          if not target in unqualified_mapping:
+            not_found.append(target)
+        warning = 'Unable to find all targets: ' + str(not_found)
+      qualified_targets = []
+      for target in config.targets:
+        if target in unqualified_mapping:
+          qualified_targets.append(unqualified_mapping[target])
+      output_targets = _GetTargetsDependingOn(all_targets, qualified_targets)
+    else:
+      output_targets = []
+
+    result_dict = { 'targets': output_targets,
+                    'status': found_dependency_string if matched else
+                              no_dependency_string }
+    if warning:
+      result_dict['warning'] = warning
+    _WriteOutput(params, **result_dict)
+
+  except Exception as e:
+    _WriteOutput(params, error=str(e))
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py
index f31068a..3d33d0a 100644
--- a/pylib/gyp/generator/ninja.py
+++ b/pylib/gyp/generator/ninja.py
@@ -14,6 +14,7 @@
 import sys
 import gyp
 import gyp.common
+from gyp.common import OrderedSet
 import gyp.msvs_emulation
 import gyp.MSVSUtil as MSVSUtil
 import gyp.xcode_emulation
@@ -598,8 +599,9 @@
       is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action)
                    if self.flavor == 'win' else False)
       args = action['action']
+      pool = 'console' if int(action.get('ninja_use_console', 0)) else None
       rule_name, _ = self.WriteNewNinjaRule(name, args, description,
-                                            is_cygwin, env=env)
+                                            is_cygwin, env, pool)
 
       inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
       if int(action.get('process_outputs_as_sources', False)):
@@ -637,8 +639,9 @@
           ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
       is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule)
                    if self.flavor == 'win' else False)
+      pool = 'console' if int(rule.get('ninja_use_console', 0)) else None
       rule_name, args = self.WriteNewNinjaRule(
-          name, args, description, is_cygwin, env=env)
+          name, args, description, is_cygwin, env, pool)
 
       # TODO: if the command references the outputs directly, we should
       # simplify it to just use $out.
@@ -1426,7 +1429,7 @@
       values = []
     ninja_file.variable(var, ' '.join(values))
 
-  def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
+  def WriteNewNinjaRule(self, name, args, description, is_cygwin, env, pool):
     """Write out a new ninja "rule" statement for a given command.
 
     Returns the name of the new rule, and a copy of |args| with variables
@@ -1484,7 +1487,7 @@
     # GYP rules/actions express being no-ops by not touching their outputs.
     # Avoid executing downstream dependencies in this case by specifying
     # restat=1 to ninja.
-    self.ninja.rule(rule_name, command, description, restat=True,
+    self.ninja.rule(rule_name, command, description, restat=True, pool=pool,
                     rspfile=rspfile, rspfile_content=rspfile_content)
     self.ninja.newline()
 
@@ -1777,8 +1780,15 @@
       wrappers[key_prefix] = os.path.join(build_to_root, value)
 
   if flavor == 'win':
+    configs = [target_dicts[qualified_target]['configurations'][config_name]
+               for qualified_target in target_list]
+    shared_system_includes = None
+    if not generator_flags.get('ninja_use_custom_environment_files', 0):
+      shared_system_includes = \
+          gyp.msvs_emulation.ExtractSharedMSVSSystemIncludes(
+              configs, generator_flags)
     cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
-        toplevel_build, generator_flags, OpenOutput)
+        toplevel_build, generator_flags, shared_system_includes, OpenOutput)
     for arch, path in cl_paths.iteritems():
       if clang_cl:
         # If we have selected clang-cl, use that instead.
diff --git a/pylib/gyp/generator/xcode.py b/pylib/gyp/generator/xcode.py
index 7972459..b4d1e19 100644
--- a/pylib/gyp/generator/xcode.py
+++ b/pylib/gyp/generator/xcode.py
@@ -69,6 +69,7 @@
 # The Xcode-specific keys that exist on targets and aren't moved down to
 # configurations.
 generator_additional_non_configuration_keys = [
+  'ios_app_extension',
   'mac_bundle',
   'mac_bundle_resources',
   'mac_framework_headers',
@@ -644,14 +645,15 @@
     # com.googlecode.gyp.xcode.bundle, a pseudo-type that xcode.py interprets
     # to create a single-file mh_bundle.
     _types = {
-      'executable':             'com.apple.product-type.tool',
-      'loadable_module':        'com.googlecode.gyp.xcode.bundle',
-      'shared_library':         'com.apple.product-type.library.dynamic',
-      'static_library':         'com.apple.product-type.library.static',
-      'executable+bundle':      'com.apple.product-type.application',
-      'loadable_module+bundle': 'com.apple.product-type.bundle',
-      'loadable_module+xctest': 'com.apple.product-type.bundle.unit-test',
-      'shared_library+bundle':  'com.apple.product-type.framework',
+      'executable':                  'com.apple.product-type.tool',
+      'loadable_module':             'com.googlecode.gyp.xcode.bundle',
+      'shared_library':              'com.apple.product-type.library.dynamic',
+      'static_library':              'com.apple.product-type.library.static',
+      'executable+bundle':           'com.apple.product-type.application',
+      'loadable_module+bundle':      'com.apple.product-type.bundle',
+      'loadable_module+xctest':      'com.apple.product-type.bundle.unit-test',
+      'shared_library+bundle':       'com.apple.product-type.framework',
+      'executable+extension+bundle': 'com.apple.product-type.app-extension',
     }
 
     target_properties = {
@@ -662,6 +664,7 @@
     type = spec['type']
     is_xctest = int(spec.get('mac_xctest_bundle', 0))
     is_bundle = int(spec.get('mac_bundle', 0)) or is_xctest
+    is_extension = int(spec.get('ios_app_extension', 0))
     if type != 'none':
       type_bundle_key = type
       if is_xctest:
@@ -669,6 +672,10 @@
         assert type == 'loadable_module', (
             'mac_xctest_bundle targets must have type loadable_module '
             '(target %s)' % target_name)
+      elif is_extension:
+        assert is_bundle, ('ios_app_extension flag requires mac_bundle '
+            '(target %s)' % target_name)
+        type_bundle_key += '+extension+bundle'
       elif is_bundle:
         type_bundle_key += '+bundle'
 
diff --git a/pylib/gyp/input.py b/pylib/gyp/input.py
index e0813f3..bb853a5 100644
--- a/pylib/gyp/input.py
+++ b/pylib/gyp/input.py
@@ -994,23 +994,29 @@
     # Prepare for the next match iteration.
     input_str = output
 
-  # Look for more matches now that we've replaced some, to deal with
-  # expanding local variables (variables defined in the same
-  # variables block as this one).
-  gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
-  if type(output) is list:
-    if output and type(output[0]) is list:
-      # Leave output alone if it's a list of lists.
-      # We don't want such lists to be stringified.
-      pass
-    else:
-      new_output = []
-      for item in output:
-        new_output.append(
-            ExpandVariables(item, phase, variables, build_file))
-      output = new_output
+  if output == input:
+    gyp.DebugOutput(gyp.DEBUG_VARIABLES,
+                    "Found only identity matches on %r, avoiding infinite "
+                    "recursion.",
+                    output)
   else:
-    output = ExpandVariables(output, phase, variables, build_file)
+    # Look for more matches now that we've replaced some, to deal with
+    # expanding local variables (variables defined in the same
+    # variables block as this one).
+    gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
+    if type(output) is list:
+      if output and type(output[0]) is list:
+        # Leave output alone if it's a list of lists.
+        # We don't want such lists to be stringified.
+        pass
+      else:
+        new_output = []
+        for item in output:
+          new_output.append(
+              ExpandVariables(item, phase, variables, build_file))
+        output = new_output
+    else:
+      output = ExpandVariables(output, phase, variables, build_file)
 
   # Convert all strings that are canonically-represented integers into integers.
   if type(output) is list:
diff --git a/pylib/gyp/msvs_emulation.py b/pylib/gyp/msvs_emulation.py
index 63593a4..1b82adb 100644
--- a/pylib/gyp/msvs_emulation.py
+++ b/pylib/gyp/msvs_emulation.py
@@ -12,6 +12,7 @@
 import subprocess
 import sys
 
+from gyp.common import OrderedSet
 import gyp.MSVSVersion
 
 windows_quoter_regex = re.compile(r'(\\*)"')
@@ -131,6 +132,54 @@
   return dxsdk_dir
 
 
+def GetGlobalVSMacroEnv(vs_version):
+  """Get a dict of variables mapping internal VS macro names to their gyp
+  equivalents. Returns all variables that are independent of the target."""
+  env = {}
+  # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
+  # Visual Studio is actually installed.
+  if vs_version.Path():
+    env['$(VSInstallDir)'] = vs_version.Path()
+    env['$(VCInstallDir)'] = os.path.join(vs_version.Path(), 'VC') + '\\'
+  # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
+  # set. This happens when the SDK is sync'd via src-internal, rather than
+  # by typical end-user installation of the SDK. If it's not set, we don't
+  # want to leave the unexpanded variable in the path, so simply strip it.
+  dxsdk_dir = _FindDirectXInstallation()
+  env['$(DXSDK_DIR)'] = dxsdk_dir if dxsdk_dir else ''
+  # Try to find an installation location for the Windows DDK by checking
+  # the WDK_DIR environment variable, may be None.
+  env['$(WDK_DIR)'] = os.environ.get('WDK_DIR', '')
+  return env
+
+def ExtractSharedMSVSSystemIncludes(configs, generator_flags):
+  """Finds msvs_system_include_dirs that are common to all targets, removes
+  them from all targets, and returns an OrderedSet containing them."""
+  all_system_includes = OrderedSet(
+      configs[0].get('msvs_system_include_dirs', []))
+  for config in configs[1:]:
+    system_includes = config.get('msvs_system_include_dirs', [])
+    all_system_includes = all_system_includes & OrderedSet(system_includes)
+  if not all_system_includes:
+    return None
+  # Expand macros in all_system_includes.
+  env = GetGlobalVSMacroEnv(GetVSVersion(generator_flags))
+  expanded_system_includes = OrderedSet([ExpandMacros(include, env)
+                                         for include in all_system_includes])
+  if any(['$' in include for include in expanded_system_includes]):
+    # Some path relies on target-specific variables, bail.
+    return None
+
+  # Remove system includes shared by all targets from the targets.
+  for config in configs:
+    includes = config.get('msvs_system_include_dirs', [])
+    if includes:  # Don't insert a msvs_system_include_dirs key if not needed.
+      # This must check the unexpanded includes list:
+      new_includes = [i for i in includes if i not in all_system_includes]
+      config['msvs_system_include_dirs'] = new_includes
+  return expanded_system_includes
+
+
 class MsvsSettings(object):
   """A class that understands the gyp 'msvs_...' values (especially the
   msvs_settings field). They largely correpond to the VS2008 IDE DOM. This
@@ -139,11 +188,6 @@
   def __init__(self, spec, generator_flags):
     self.spec = spec
     self.vs_version = GetVSVersion(generator_flags)
-    self.dxsdk_dir = _FindDirectXInstallation()
-
-    # Try to find an installation location for the Windows DDK by checking
-    # the WDK_DIR environment variable, may be None.
-    self.wdk_dir = os.environ.get('WDK_DIR')
 
     supported_fields = [
         ('msvs_configuration_attributes', dict),
@@ -194,18 +238,7 @@
         '$(PlatformName)': target_platform,
         '$(ProjectDir)\\': '',
     }
-    # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
-    # Visual Studio is actually installed.
-    if self.vs_version.Path():
-      replacements['$(VSInstallDir)'] = self.vs_version.Path()
-      replacements['$(VCInstallDir)'] = os.path.join(self.vs_version.Path(),
-                                                     'VC') + '\\'
-    # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
-    # set. This happens when the SDK is sync'd via src-internal, rather than
-    # by typical end-user installation of the SDK. If it's not set, we don't
-    # want to leave the unexpanded variable in the path, so simply strip it.
-    replacements['$(DXSDK_DIR)'] = self.dxsdk_dir if self.dxsdk_dir else ''
-    replacements['$(WDK_DIR)'] = self.wdk_dir if self.wdk_dir else ''
+    replacements.update(GetGlobalVSMacroEnv(self.vs_version))
     return replacements
 
   def ConvertVSMacros(self, s, base_to_build=None, config=None):
@@ -897,7 +930,8 @@
     if line.startswith('LOC:'):
       return line[len('LOC:'):].strip()
 
-def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
+def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags,
+                             system_includes, open_out):
   """It's not sufficient to have the absolute path to the compiler, linker,
   etc. on Windows, as those tools rely on .dlls being in the PATH. We also
   need to support both x86 and x64 compilers within the same build (to support
@@ -928,6 +962,13 @@
         args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     variables, _ = popen.communicate()
     env = _ExtractImportantEnvironment(variables)
+
+    # Inject system includes from gyp files into INCLUDE.
+    if system_includes:
+      system_includes = system_includes | OrderedSet(
+                                              env.get('INCLUDE', '').split(';'))
+      env['INCLUDE'] = ';'.join(system_includes)
+
     env_block = _FormatAsEnvironmentBlock(env)
     f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
     f.write(env_block)
diff --git a/pylib/gyp/xcode_emulation.py b/pylib/gyp/xcode_emulation.py
index 859cd5a..2f34bc6 100644
--- a/pylib/gyp/xcode_emulation.py
+++ b/pylib/gyp/xcode_emulation.py
@@ -218,6 +218,9 @@
   def _IsBundle(self):
     return int(self.spec.get('mac_bundle', 0)) != 0
 
+  def _IsIosAppExtension(self):
+    return int(self.spec.get('ios_app_extension', 0)) != 0
+
   def GetFrameworkVersion(self):
     """Returns the framework version of the current target. Only valid for
     bundles."""
@@ -237,7 +240,10 @@
           'WRAPPER_EXTENSION', default=default_wrapper_extension)
       return '.' + self.spec.get('product_extension', wrapper_extension)
     elif self.spec['type'] == 'executable':
-      return '.' + self.spec.get('product_extension', 'app')
+      if self._IsIosAppExtension():
+        return '.' + self.spec.get('product_extension', 'appex')
+      else:
+        return '.' + self.spec.get('product_extension', 'app')
     else:
       assert False, "Don't know extension for '%s', target '%s'" % (
           self.spec['type'], self.spec['target_name'])
@@ -292,6 +298,10 @@
 
   def GetProductType(self):
     """Returns the PRODUCT_TYPE of this target."""
+    if self._IsIosAppExtension():
+      assert self._IsBundle(), ('ios_app_extension flag requires mac_bundle '
+          '(target %s)' % self.spec['target_name'])
+      return 'com.apple.product-type.app-extension'
     if self._IsBundle():
       return {
         'executable': 'com.apple.product-type.application',
@@ -794,6 +804,18 @@
     for directory in framework_dirs:
       ldflags.append('-F' + directory.replace('$(SDKROOT)', sdk_root))
 
+    if sdk_root and self._IsIosAppExtension():
+      # Adds the link flags for extensions. These flags are common for all
+      # extensions and provide loader and main function.
+      # These flags reflect the compilation options used by xcode to compile
+      # extensions.
+      ldflags.append('-lpkstart')
+      ldflags.append(sdk_root +
+          '/System/Library/PrivateFrameworks/PlugInKit.framework/PlugInKit')
+      ldflags.append('-fapplication-extension')
+      ldflags.append('-Xlinker -rpath '
+          '-Xlinker @executable_path/../../Frameworks')
+
     self._Appendf(ldflags, 'CLANG_CXX_LIBRARY', '-stdlib=%s')
 
     self.configname = None
@@ -921,7 +943,7 @@
     """Return a shell command to codesign the iOS output binary so it can
     be deployed to a device.  This should be run as the very last step of the
     build."""
-    if not (self.isIOS and self.spec['type'] == "executable"):
+    if not (self.isIOS and self.spec['type'] == 'executable'):
       return []
 
     settings = self.xcode_settings[configname]
diff --git a/pylib/gyp/xcode_ninja.py b/pylib/gyp/xcode_ninja.py
index 0e5a70c..2a89fa9 100644
--- a/pylib/gyp/xcode_ninja.py
+++ b/pylib/gyp/xcode_ninja.py
@@ -90,6 +90,7 @@
           new_xcode_settings
 
   ninja_target['mac_bundle'] = old_spec.get('mac_bundle', 0)
+  ninja_target['ios_app_extension'] = old_spec.get('ios_app_extension', 0)
   ninja_target['type'] = old_spec['type']
   if ninja_toplevel:
     ninja_target['actions'] = [
diff --git a/pylib/gyp/xcodeproj_file.py b/pylib/gyp/xcodeproj_file.py
index 13eaf3a..7c7f1fb 100644
--- a/pylib/gyp/xcodeproj_file.py
+++ b/pylib/gyp/xcodeproj_file.py
@@ -2238,10 +2238,12 @@
   # Mapping from Xcode product-types to settings.  The settings are:
   #  filetype : used for explicitFileType in the project file
   #  prefix : the prefix for the file name
-  #  suffix : the suffix for the filen ame
+  #  suffix : the suffix for the file name
   _product_filetypes = {
     'com.apple.product-type.application':       ['wrapper.application',
                                                  '', '.app'],
+    'com.apple.product-type.app-extension':     ['wrapper.app-extension',
+                                                 '', '.appex'],
     'com.apple.product-type.bundle':            ['wrapper.cfbundle',
                                                  '', '.bundle'],
     'com.apple.product-type.framework':         ['wrapper.framework',
@@ -2314,11 +2316,11 @@
 
         if force_extension is not None:
           # If it's a wrapper (bundle), set WRAPPER_EXTENSION.
+          # Extension override.
+          suffix = '.' + force_extension
           if filetype.startswith('wrapper.'):
             self.SetBuildSetting('WRAPPER_EXTENSION', force_extension)
           else:
-            # Extension override.
-            suffix = '.' + force_extension
             self.SetBuildSetting('EXECUTABLE_EXTENSION', force_extension)
 
           if filetype.startswith('compiled.mach-o.executable'):
diff --git a/test/analyzer/gyptest-analyzer.new.py b/test/analyzer/gyptest-analyzer.new.py
new file mode 100644
index 0000000..db7e125
--- /dev/null
+++ b/test/analyzer/gyptest-analyzer.new.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for analyzer
+"""
+
+import json
+import TestGyp
+
+# TODO(sky): when done migrating recipes rename to gyptest-analyzer and nuke
+# existing gyptest-analyzer.
+
+found = 'Found dependency'
+not_found = 'No dependencies'
+
+def _CreateTestFile(files, targets):
+  f = open('test_file', 'w')
+  to_write = {'files': files, 'targets': targets }
+  json.dump(to_write, f)
+  f.close()
+
+def _CreateBogusTestFile():
+  f = open('test_file','w')
+  f.write('bogus')
+  f.close()
+
+def _ReadOutputFileContents():
+  f = open('analyzer_output', 'r')
+  result = json.load(f)
+  f.close()
+  return result
+
+# NOTE: this would be clearer if it subclassed TestGypCustom, but that trips
+# over a bug in pylint (E1002).
+test = TestGyp.TestGypCustom(format='analyzer')
+
+def run_analyzer(*args, **kw):
+  """Runs the test specifying a particular config and output path."""
+  args += ('-Gconfig_path=test_file',
+           '-Ganalyzer_output_path=analyzer_output')
+  test.run_gyp('test.gyp', *args, **kw)
+
+def EnsureContains(targets=set(), matched=False):
+  """Verifies output contains |targets| and |direct_targets|."""
+  result = _ReadOutputFileContents()
+  if result.get('error', None):
+    print 'unexpected error', result.get('error')
+    test.fail_test()
+
+  if result.get('warning', None):
+    print 'unexpected warning', result.get('warning')
+    test.fail_test()
+
+  actual_targets = set(result['targets'])
+  if actual_targets != targets:
+    print 'actual targets:', actual_targets, '\nexpected targets:', targets
+    test.fail_test()
+
+  if matched and result['status'] != found:
+    print 'expected', found, 'got', result['status']
+    test.fail_test()
+  elif not matched and result['status'] != not_found:
+    print 'expected', not_found, 'got', result['status']
+    test.fail_test()
+
+def EnsureError(expected_error_string):
+  """Verifies output contains the error string."""
+  result = _ReadOutputFileContents()
+  if result.get('error', '').find(expected_error_string) == -1:
+    print 'actual error:', result.get('error', ''), '\nexpected error:', \
+        expected_error_string
+    test.fail_test()
+
+def EnsureWarning(expected_warning_string):
+  """Verifies output contains the warning string."""
+  result = _ReadOutputFileContents()
+  if result.get('warning', '').find(expected_warning_string) == -1:
+    print 'actual warning:', result.get('warning', ''), \
+        '\nexpected warning:', expected_warning_string
+    test.fail_test()
+
+# Verifies file_path must be specified.
+test.run_gyp('test.gyp',
+             stdout='Must specify files to analyze via file_path generator '
+             'flag\n')
+
+# Verifies config_path must point to a valid file.
+test.run_gyp('test.gyp', '-Gconfig_path=bogus_file',
+             '-Ganalyzer_output_path=analyzer_output')
+EnsureError('Unable to open file bogus_file')
+
+# Verify get error when bad target is specified.
+_CreateTestFile(['exe2.c'], ['bad_target'])
+run_analyzer()
+EnsureWarning('Unable to find all targets')
+
+# Verifies config_path must point to a valid json file.
+_CreateBogusTestFile()
+run_analyzer()
+EnsureError('Unable to parse config file test_file')
+
+# Trivial test of a source.
+_CreateTestFile(['foo.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Conditional source that is excluded.
+_CreateTestFile(['conditional_source.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Conditional source that is included by way of argument.
+_CreateTestFile(['conditional_source.c'], [])
+run_analyzer('-Dtest_variable=1')
+EnsureContains(matched=True)
+
+# Two unknown files.
+_CreateTestFile(['unknown1.c', 'unoknow2.cc'], [])
+run_analyzer()
+EnsureContains()
+
+# Two unknown files.
+_CreateTestFile(['unknown1.c', 'subdir/subdir_sourcex.c'], [])
+run_analyzer()
+EnsureContains()
+
+# Included dependency
+_CreateTestFile(['unknown1.c', 'subdir/subdir_source.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Included inputs to actions.
+_CreateTestFile(['action_input.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Don't consider outputs.
+_CreateTestFile(['action_output.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Rule inputs.
+_CreateTestFile(['rule_input.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Ignore path specified with PRODUCT_DIR.
+_CreateTestFile(['product_dir_input.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Path specified via a variable.
+_CreateTestFile(['subdir/subdir_source2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Verifies paths with // are fixed up correctly.
+_CreateTestFile(['parent_source.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Verifies relative paths are resolved correctly.
+_CreateTestFile(['subdir/subdir_source.h'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Various permutations when passing in targets.
+_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe3'])
+run_analyzer()
+EnsureContains(matched=True, targets={'exe3'})
+
+_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe'])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Verifies duplicates are ignored.
+_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe'])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['exe2.c'], ['exe'])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['subdir/subdir2b_source.c', 'exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+test.pass_test()
diff --git a/test/analyzer/subdir/subdir.gyp b/test/analyzer/subdir/subdir.gyp
index cd0f013..bfa2df4 100644
--- a/test/analyzer/subdir/subdir.gyp
+++ b/test/analyzer/subdir/subdir.gyp
@@ -15,5 +15,22 @@
         '<(trailing_dir_path)/parent_source.c',
       ],
     },
+    {
+      'target_name': 'subdir2a',
+      'type': 'static_library',
+      'sources': [
+        'subdir2_source.c',
+      ],
+      'dependencies': [
+        'subdir2b',
+      ],
+    },
+    {
+      'target_name': 'subdir2b',
+      'type': 'static_library',
+      'sources': [
+        'subdir2b_source.c',
+      ],
+    },
   ],
 }
diff --git a/test/analyzer/test.gyp b/test/analyzer/test.gyp
index 482ede3..afc312b 100644
--- a/test/analyzer/test.gyp
+++ b/test/analyzer/test.gyp
@@ -53,5 +53,31 @@
         },
       ],
     },
+    {
+      'target_name': 'exe2',
+      'type': 'executable',
+      'sources': [
+        'exe2.c',
+      ],
+    },
+    {
+      'target_name': 'exe3',
+      'type': 'executable',
+      'dependencies': [
+        'subdir/subdir.gyp:foo',
+        'subdir/subdir.gyp:subdir2a',
+      ],
+      'sources': [
+        'exe3.c',
+      ],
+    },
+    {
+      'target_name': 'all',
+      'type': 'executable',
+      'dependencies': [
+        'exe',
+        'exe3',
+      ],
+    },
   ],
 }
diff --git a/test/ios/extension/ActionExtension/ActionViewController.h b/test/ios/extension/ActionExtension/ActionViewController.h
new file mode 100644
index 0000000..1c92509
--- /dev/null
+++ b/test/ios/extension/ActionExtension/ActionViewController.h
@@ -0,0 +1,9 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+@interface ActionViewController : UIViewController
+
+@end
diff --git a/test/ios/extension/ActionExtension/ActionViewController.m b/test/ios/extension/ActionExtension/ActionViewController.m
new file mode 100644
index 0000000..d37bacd
--- /dev/null
+++ b/test/ios/extension/ActionExtension/ActionViewController.m
@@ -0,0 +1,31 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ActionViewController.h"
+#import <MobileCoreServices/MobileCoreServices.h>
+
+@interface ActionViewController ()
+
+@end
+
+@implementation ActionViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+}
+
+- (void)didReceiveMemoryWarning {
+  [super didReceiveMemoryWarning];
+  // Dispose of any resources that can be recreated.
+}
+
+- (IBAction)done {
+  // Return any edited content to the host app.
+  // This template doesn't do anything, so we just echo the passed in items.
+  [self.extensionContext
+      completeRequestReturningItems:self.extensionContext.inputItems
+      completionHandler:nil];
+}
+
+@end
diff --git a/test/ios/extension/ActionExtension/Info.plist b/test/ios/extension/ActionExtension/Info.plist
new file mode 100644
index 0000000..f89cd79
--- /dev/null
+++ b/test/ios/extension/ActionExtension/Info.plist
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleDisplayName</key>
+	<string>ActionExtension</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.google.gyptest.extension.ActionExtension</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>XPC!</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>NSExtension</key>
+	<dict>
+		<key>NSExtensionAttributes</key>
+		<dict>
+			<key>NSExtensionActivationRule</key>
+			<string>TRUEPREDICATE</string>
+			<key>NSExtensionPointName</key>
+			<string>com.apple.ui-services</string>
+			<key>NSExtensionPointVersion</key>
+			<string>1.0</string>
+		</dict>
+		<key>NSExtensionMainStoryboard</key>
+		<string>MainInterface</string>
+		<key>NSExtensionPointIdentifier</key>
+		<string>com.apple.ui-services</string>
+	</dict>
+</dict>
+</plist>
diff --git a/test/ios/extension/ActionExtension/MainInterface.storyboard b/test/ios/extension/ActionExtension/MainInterface.storyboard
new file mode 100644
index 0000000..5aa5818
--- /dev/null
+++ b/test/ios/extension/ActionExtension/MainInterface.storyboard
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6148" systemVersion="14A229a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="ObA-dk-sSI">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6147"/>
+    </dependencies>
+    <scenes>
+        <!--Action View Controller - Image-->
+        <scene sceneID="7MM-of-jgj">
+            <objects>
+                <viewController title="Image" id="ObA-dk-sSI" customClass="ActionViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="qkL-Od-lgU"/>
+                        <viewControllerLayoutGuide type="bottom" id="n38-gi-rB5"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="zMn-AG-sqS">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="528"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="9ga-4F-77Z">
+                                <rect key="frame" x="0.0" y="64" width="320" height="464"/>
+                            </imageView>
+                            <navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NOA-Dm-cuz">
+                                <rect key="frame" x="0.0" y="20" width="320" height="44"/>
+                                <items>
+                                    <navigationItem id="3HJ-uW-3hn">
+                                        <barButtonItem key="leftBarButtonItem" title="Done" style="done" id="WYi-yp-eM6">
+                                            <connections>
+                                                <action selector="done" destination="ObA-dk-sSI" id="Qdu-qn-U6V"/>
+                                            </connections>
+                                        </barButtonItem>
+                                    </navigationItem>
+                                </items>
+                            </navigationBar>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="NOA-Dm-cuz" secondAttribute="trailing" id="A05-Pj-hrr"/>
+                            <constraint firstItem="9ga-4F-77Z" firstAttribute="top" secondItem="NOA-Dm-cuz" secondAttribute="bottom" id="Fps-3D-QQW"/>
+                            <constraint firstItem="NOA-Dm-cuz" firstAttribute="leading" secondItem="zMn-AG-sqS" secondAttribute="leading" id="HxO-8t-aoh"/>
+                            <constraint firstAttribute="trailing" secondItem="9ga-4F-77Z" secondAttribute="trailing" id="Ozw-Hg-0yh"/>
+                            <constraint firstItem="9ga-4F-77Z" firstAttribute="leading" secondItem="zMn-AG-sqS" secondAttribute="leading" id="XH5-ld-ONA"/>
+                            <constraint firstItem="n38-gi-rB5" firstAttribute="top" secondItem="9ga-4F-77Z" secondAttribute="bottom" id="eQg-nn-Zy4"/>
+                            <constraint firstItem="NOA-Dm-cuz" firstAttribute="top" secondItem="qkL-Od-lgU" secondAttribute="bottom" id="we0-1t-bgp"/>
+                        </constraints>
+                    </view>
+                    <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+                    <size key="freeformSize" width="320" height="528"/>
+                    <connections>
+                        <outlet property="imageView" destination="9ga-4F-77Z" id="5y6-5w-9QO"/>
+                        <outlet property="view" destination="zMn-AG-sqS" id="Qma-de-2ek"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="X47-rx-isc" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="252" y="-124"/>
+        </scene>
+    </scenes>
+    <simulatedMetricsContainer key="defaultSimulatedMetrics">
+        <simulatedStatusBarMetrics key="statusBar"/>
+        <simulatedOrientationMetrics key="orientation"/>
+        <simulatedScreenMetrics key="destination" type="retina4"/>
+    </simulatedMetricsContainer>
+</document>
diff --git a/test/ios/extension/ExtensionContainer/AppDelegate.h b/test/ios/extension/ExtensionContainer/AppDelegate.h
new file mode 100644
index 0000000..510e230
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/AppDelegate.h
@@ -0,0 +1,12 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
+
diff --git a/test/ios/extension/ExtensionContainer/AppDelegate.m b/test/ios/extension/ExtensionContainer/AppDelegate.m
new file mode 100644
index 0000000..1197bc1
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/AppDelegate.m
@@ -0,0 +1,19 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication*)application
+    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+  // Override point for customization after application launch.
+  return YES;
+}
+
+@end
diff --git a/test/ios/extension/ExtensionContainer/Base.lproj/Main.storyboard b/test/ios/extension/ExtensionContainer/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..e8f3cfb
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6162" systemVersion="14A238h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6160"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/test/ios/extension/ExtensionContainer/Images.xcassets/AppIcon.appiconset/Contents.json b/test/ios/extension/ExtensionContainer/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..f697f61
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,53 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/test/ios/extension/ExtensionContainer/Images.xcassets/LaunchImage.launchimage/Contents.json b/test/ios/extension/ExtensionContainer/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 0000000..4458b40
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,51 @@
+{
+  "images" : [
+    {
+      "orientation" : "portrait",
+      "idiom" : "iphone",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    },
+    {
+      "orientation" : "portrait",
+      "idiom" : "iphone",
+      "subtype" : "retina4",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    },
+    {
+      "orientation" : "portrait",
+      "idiom" : "ipad",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "1x"
+    },
+    {
+      "orientation" : "landscape",
+      "idiom" : "ipad",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "1x"
+    },
+    {
+      "orientation" : "portrait",
+      "idiom" : "ipad",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    },
+    {
+      "orientation" : "landscape",
+      "idiom" : "ipad",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/test/ios/extension/ExtensionContainer/Info.plist b/test/ios/extension/ExtensionContainer/Info.plist
new file mode 100644
index 0000000..31ccf4c
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>CFBundleDevelopmentRegion</key>
+    <string>en</string>
+    <key>CFBundleExecutable</key>
+    <string>ExtensionContainer</string>
+    <key>CFBundleIdentifier</key>
+    <string>com.google.gyptest.extension</string>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundleName</key>
+    <string>${PRODUCT_NAME}</string>
+    <key>CFBundlePackageType</key>
+    <string>APPL</string>
+    <key>CFBundleShortVersionString</key>
+    <string>1.0</string>
+    <key>CFBundleSignature</key>
+    <string>????</string>
+    <key>CFBundleVersion</key>
+    <string>1</string>
+    <key>LSRequiresIPhoneOS</key>
+    <true/>
+    <key>UIMainStoryboardFile</key>
+    <string>Main</string>
+    <key>UIRequiredDeviceCapabilities</key>
+    <array>
+        <string>armv7</string>
+    </array>
+</dict>
+</plist>
diff --git a/test/ios/extension/ExtensionContainer/ViewController.h b/test/ios/extension/ExtensionContainer/ViewController.h
new file mode 100644
index 0000000..fad7754
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/ViewController.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+
+
+@end
+
diff --git a/test/ios/extension/ExtensionContainer/ViewController.m b/test/ios/extension/ExtensionContainer/ViewController.m
new file mode 100644
index 0000000..3810fa9
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/ViewController.m
@@ -0,0 +1,24 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ViewController.h"
+
+@interface ViewController ()
+
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+  [super didReceiveMemoryWarning];
+  // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/test/ios/extension/ExtensionContainer/main.m b/test/ios/extension/ExtensionContainer/main.m
new file mode 100644
index 0000000..47aecb5
--- /dev/null
+++ b/test/ios/extension/ExtensionContainer/main.m
@@ -0,0 +1,13 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char* argv[]) {
+  @autoreleasepool {
+    return UIApplicationMain(argc, argv, nil,
+        NSStringFromClass([AppDelegate class]));
+  }
+}
diff --git a/test/ios/extension/extension.gyp b/test/ios/extension/extension.gyp
new file mode 100644
index 0000000..6fa468c
--- /dev/null
+++ b/test/ios/extension/extension.gyp
@@ -0,0 +1,83 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'make_global_settings': [
+    ['CC', '/usr/bin/clang'],
+    ['CXX', '/usr/bin/clang++'],
+  ],
+  'targets': [
+    {
+      'target_name': 'ExtensionContainer',
+      'product_name': 'ExtensionContainer',
+      'type': 'executable',
+      'mac_bundle': 1,
+      'mac_bundle_resources': [
+        'ExtensionContainer/Base.lproj/Main.storyboard',
+      ],
+      'sources': [
+        'ExtensionContainer/AppDelegate.h',
+        'ExtensionContainer/AppDelegate.m',
+        'ExtensionContainer/ViewController.h',
+        'ExtensionContainer/ViewController.m',
+        'ExtensionContainer/main.m',
+      ],
+      'copies': [
+        {
+          'destination': '<(PRODUCT_DIR)/ExtensionContainer.app/PlugIns',
+          'files': [
+            '<(PRODUCT_DIR)/ActionExtension.appex',
+      ]}],
+      'dependencies': [
+        'ActionExtension'
+      ],
+
+      'link_settings': {
+        'libraries': [
+          '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+          '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+        ],
+      },
+      'xcode_settings': {
+        'OTHER_CFLAGS': [
+          '-fobjc-abi-version=2',
+        ],
+        'INFOPLIST_FILE': 'ExtensionContainer/Info.plist',
+        'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+        'ARCHS': [ 'armv7' ],
+        'SDKROOT': 'iphoneos',
+        'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+      },
+    },
+    {
+      'target_name': 'ActionExtension',
+      'product_name': 'ActionExtension',
+      'type': 'executable',
+      'mac_bundle': 1,
+      'ios_app_extension': 1,
+      'sources': [
+        'ActionExtension/ActionViewController.h',
+        'ActionExtension/ActionViewController.m',
+      ],
+      'link_settings': {
+        'libraries': [
+          '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+          '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+          '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+        ],
+      },
+      'xcode_settings': {
+        'OTHER_CFLAGS': [
+          '-fobjc-abi-version=2',
+        ],
+        'INFOPLIST_FILE': 'ActionExtension/Info.plist',
+        'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+        'ARCHS': [ 'armv7' ],
+        'SDKROOT': 'iphoneos',
+        'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+      },
+    },
+  ],
+}
+
diff --git a/test/ios/gyptest-extension.py b/test/ios/gyptest-extension.py
new file mode 100755
index 0000000..103eb3f
--- /dev/null
+++ b/test/ios/gyptest-extension.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that ios app extensions are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import sys
+if sys.platform == 'darwin' and TestMac.Xcode.Version()>="0600":
+  test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+  test.run_gyp('extension.gyp', chdir='extension')
+
+  test.build('extension.gyp', 'ExtensionContainer', chdir='extension')
+
+  # Test that the extension is .appex
+  test.built_file_must_exist(
+      'ExtensionContainer.app/PlugIns/ActionExtension.appex',
+      chdir='extension')
+
+  test.pass_test()
+
diff --git a/test/mac/gyptest-loadable-module-bundle-product-extension.py b/test/mac/gyptest-loadable-module-bundle-product-extension.py
new file mode 100644
index 0000000..90c2083
--- /dev/null
+++ b/test/mac/gyptest-loadable-module-bundle-product-extension.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Tests that loadable_modules don't collide when using the same name with
+different file extensions.
+"""
+
+import TestGyp
+
+import os
+import struct
+import sys
+
+if sys.platform == 'darwin':
+  test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+  CHDIR = 'loadable-module-bundle-product-extension'
+  test.run_gyp('test.gyp', chdir=CHDIR)
+  test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+  test.must_exist(test.built_file_path('Collide.foo', chdir=CHDIR))
+  test.must_exist(test.built_file_path('Collide.bar', chdir=CHDIR))
+
+  test.pass_test()
diff --git a/test/mac/loadable-module-bundle-product-extension/src.cc b/test/mac/loadable-module-bundle-product-extension/src.cc
new file mode 100644
index 0000000..3d878e9
--- /dev/null
+++ b/test/mac/loadable-module-bundle-product-extension/src.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+int test() {
+  return 1337;
+}
diff --git a/test/mac/loadable-module-bundle-product-extension/test.gyp b/test/mac/loadable-module-bundle-product-extension/test.gyp
new file mode 100644
index 0000000..684a2c0
--- /dev/null
+++ b/test/mac/loadable-module-bundle-product-extension/test.gyp
@@ -0,0 +1,24 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'targets': [{
+    'target_name': 'test',
+    'type': 'none',
+    'dependencies': ['child_one', 'child_two'],
+  }, {
+    'target_name': 'child_one',
+    'product_name': 'Collide',
+    'product_extension': 'bar',
+    'sources': ['src.cc'],
+    'type': 'loadable_module',
+    'mac_bundle': 1,
+  }, {
+    'target_name': 'child_two',
+    'product_name': 'Collide',
+    'product_extension': 'foo',
+    'sources': ['src.cc'],
+    'type': 'loadable_module',
+    'mac_bundle': 1,
+  }],
+}
diff --git a/test/ninja/use-console/foo.bar b/test/ninja/use-console/foo.bar
new file mode 100644
index 0000000..07c476a
--- /dev/null
+++ b/test/ninja/use-console/foo.bar
@@ -0,0 +1,5 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+This is a dummy file for rule/action input.
diff --git a/test/ninja/use-console/gyptest-use-console.py b/test/ninja/use-console/gyptest-use-console.py
new file mode 100644
index 0000000..f76fcd9
--- /dev/null
+++ b/test/ninja/use-console/gyptest-use-console.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Make sure 'ninja_use_console' is supported in actions and rules.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['ninja'])
+
+test.run_gyp('use-console.gyp')
+
+no_pool = open(test.built_file_path('obj/no_pool.ninja')).read()
+if 'pool =' in no_pool:
+  test.fail_test()
+
+action_pool = open(test.built_file_path('obj/action_pool.ninja')).read()
+if 'pool = console' not in action_pool:
+  test.fail_test()
+
+rule_pool = open(test.built_file_path('obj/rule_pool.ninja')).read()
+if 'pool = console' not in rule_pool:
+  test.fail_test()
+
+test.pass_test()
diff --git a/test/ninja/use-console/use-console.gyp b/test/ninja/use-console/use-console.gyp
new file mode 100644
index 0000000..84e6318
--- /dev/null
+++ b/test/ninja/use-console/use-console.gyp
@@ -0,0 +1,60 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'no_pool',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'some_action',
+          'action': ['echo', 'hello'],
+          'inputs': ['foo.bar'],
+          'outputs': ['dummy'],
+        },
+      ],
+      'rules': [
+        {
+          'rule_name': 'some_rule',
+          'extension': 'bar',
+          'action': ['echo', 'hello'],
+          'outputs': ['dummy'],
+        },
+      ],
+      'sources': [
+        'foo.bar',
+      ],
+    },
+    {
+      'target_name': 'action_pool',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'some_action',
+          'action': ['echo', 'hello'],
+          'inputs': ['foo.bar'],
+          'outputs': ['dummy'],
+          'ninja_use_console': 1,
+        },
+      ],
+    },
+    {
+      'target_name': 'rule_pool',
+      'type': 'none',
+      'rules': [
+        {
+          'rule_name': 'some_rule',
+          'extension': 'bar',
+          'action': ['echo', 'hello'],
+          'outputs': ['dummy'],
+          'ninja_use_console': 1,
+        },
+      ],
+      'sources': [
+        'foo.bar',
+      ],
+    },
+  ],
+}
diff --git a/test/win/gyptest-system-include.py b/test/win/gyptest-system-include.py
new file mode 100644
index 0000000..9a47d98
--- /dev/null
+++ b/test/win/gyptest-system-include.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Checks that msvs_system_include_dirs works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'win32':
+  test = TestGyp.TestGyp(formats=['msvs', 'ninja'])
+
+  CHDIR = 'system-include'
+  test.run_gyp('test.gyp', chdir=CHDIR)
+  test.build('test.gyp', test.ALL, chdir=CHDIR)
+  test.pass_test()
diff --git a/test/win/system-include/bar/header.h b/test/win/system-include/bar/header.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/win/system-include/bar/header.h
diff --git a/test/win/system-include/common/commonheader.h b/test/win/system-include/common/commonheader.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/win/system-include/common/commonheader.h
diff --git a/test/win/system-include/foo/header.h b/test/win/system-include/foo/header.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/win/system-include/foo/header.h
diff --git a/test/win/system-include/main.cc b/test/win/system-include/main.cc
new file mode 100644
index 0000000..b04ea8a
--- /dev/null
+++ b/test/win/system-include/main.cc
@@ -0,0 +1,4 @@
+#include <commonheader.h>
+#include <header.h>
+
+int main() {}
diff --git a/test/win/system-include/test.gyp b/test/win/system-include/test.gyp
new file mode 100644
index 0000000..07f2636
--- /dev/null
+++ b/test/win/system-include/test.gyp
@@ -0,0 +1,26 @@
+{
+  'target_defaults': {
+    'msvs_settings': {
+      'VCCLCompilerTool': {
+        'WarningLevel': '4',
+        'WarnAsError': 'true',
+      },
+    },
+    'msvs_system_include_dirs': [
+      '$(ProjectName)',  # Different for each target
+      'common',  # Same for all targets
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'foo',
+      'type': 'executable',
+      'sources': [ 'main.cc', ],
+    },
+    {
+      'target_name': 'bar',
+      'type': 'executable',
+      'sources': [ 'main.cc', ],
+    },
+  ],
+}