Merge from Chromium at DEPS revision r217092

This commit was generated by merge_to_master.py.

Change-Id: Id686c3257c13898b9bf2b05dc65eb0302739e3bb
diff --git a/pylib/gyp/common.py b/pylib/gyp/common.py
index e50f51c..19f1cf4 100644
--- a/pylib/gyp/common.py
+++ b/pylib/gyp/common.py
@@ -131,6 +131,13 @@
   path = os.path.realpath(path)
   relative_to = os.path.realpath(relative_to)
 
+  # On Windows, we can't create a relative path to a different drive, so just
+  # use the absolute path.
+  if sys.platform == 'win32':
+    if (os.path.splitdrive(path)[0].lower() !=
+        os.path.splitdrive(relative_to)[0].lower()):
+      return path
+
   # Split the paths into components.
   path_split = path.split(os.path.sep)
   relative_to_split = relative_to.split(os.path.sep)
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py
index ccb4871..19cecd2 100644
--- a/pylib/gyp/generator/ninja.py
+++ b/pylib/gyp/generator/ninja.py
@@ -97,6 +97,12 @@
   return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
 
 
+def AddArch(output, arch):
+  """Adds an arch string to an output path."""
+  output, extension = os.path.splitext(output)
+  return '%s.%s%s' % (output, arch, extension)
+
+
 class Target:
   """Target represents the paths used within a single gyp target.
 
@@ -203,7 +209,8 @@
 
 class NinjaWriter:
   def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
-               output_file, flavor, toplevel_dir=None):
+               output_file, toplevel_build, output_file_name, flavor,
+               toplevel_dir=None):
     """
     base_dir: path from source root to directory containing this gyp file,
               by gyp semantics, all input paths are relative to this
@@ -216,6 +223,9 @@
     self.base_dir = base_dir
     self.build_dir = build_dir
     self.ninja = ninja_syntax.Writer(output_file)
+    self.toplevel_build = toplevel_build
+    self.output_file_name = output_file_name
+
     self.flavor = flavor
     self.abs_build_dir = None
     if toplevel_dir is not None:
@@ -345,6 +355,10 @@
       self.ninja.newline()
     return targets[0]
 
+  def _SubninjaNameForArch(self, arch):
+    output_file_base = os.path.splitext(self.output_file_name)[0]
+    return '%s.%s.ninja' % (output_file_base, arch)
+
   def WriteSpec(self, spec, config_name, generator_flags,
       case_sensitive_filesystem):
     """The main entry point for NinjaWriter: write the build rules for a spec.
@@ -373,6 +387,16 @@
       self.ninja.variable('cc', '$cl_' + arch)
       self.ninja.variable('cxx', '$cl_' + arch)
 
+    if self.flavor == 'mac':
+      self.archs = self.xcode_settings.GetActiveArchs(config_name)
+      if len(self.archs) > 1:
+        self.arch_subninjas = dict(
+            (arch, ninja_syntax.Writer(
+                open(os.path.join(self.toplevel_build,
+                                  self._SubninjaNameForArch(arch)),
+                     'w')))
+            for arch in self.archs)
+
     # Compute predepends for all rules.
     # actions_depends is the dependencies this target depends on before running
     # any of its action/rule/copy steps.
@@ -415,6 +439,12 @@
     link_deps = []
     sources = spec.get('sources', []) + extra_sources
     if sources:
+      if self.flavor == 'mac' and len(self.archs) > 1:
+        # Write subninja file containing compile and link commands scoped to
+        # a single arch if a fat binary is being built.
+        for arch in self.archs:
+          self.ninja.subninja(self._SubninjaNameForArch(arch))
+
       pch = None
       if self.flavor == 'win':
         gyp.msvs_emulation.VerifyMissingSources(
@@ -427,11 +457,17 @@
             self.xcode_settings, self.GypPathToNinja,
             lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang))
       link_deps = self.WriteSources(
-          config_name, config, sources, compile_depends_stamp, pch,
+          self.ninja, config_name, config, sources, compile_depends_stamp, pch,
           case_sensitive_filesystem, spec)
       # Some actions/rules output 'sources' that are already object files.
-      link_deps += [self.GypPathToNinja(f)
-          for f in sources if f.endswith(self.obj_ext)]
+      obj_outputs = [f for f in sources if f.endswith(self.obj_ext)]
+      if obj_outputs:
+        if self.flavor != 'mac' or len(self.archs) == 1:
+          link_deps += [self.GypPathToNinja(o) for o in obj_outputs]
+        else:
+          print "Warning: Actions/rules writing object files don't work with " \
+                "multiarch targets, dropping. (target %s)" % spec['target_name']
+
 
     if self.flavor == 'win' and self.target.type == 'static_library':
       self.target.component_objs = link_deps
@@ -720,7 +756,7 @@
                                 ('env', env)])
     bundle_depends.append(out)
 
-  def WriteSources(self, config_name, config, sources, predepends,
+  def WriteSources(self, ninja_file, config_name, config, sources, predepends,
                    precompiled_header, case_sensitive_filesystem, spec):
     """Write build rules to compile all of |sources|."""
     if self.toolset == 'host':
@@ -729,9 +765,24 @@
       self.ninja.variable('cxx', '$cxx_host')
       self.ninja.variable('ld', '$ld_host')
 
+    if self.flavor != 'mac' or len(self.archs) == 1:
+      return self.WriteSourcesForArch(
+          self.ninja, config_name, config, sources, predepends,
+          precompiled_header, case_sensitive_filesystem, spec)
+    else:
+      return dict((arch, self.WriteSourcesForArch(
+            self.arch_subninjas[arch], config_name, config, sources, predepends,
+            precompiled_header, case_sensitive_filesystem, spec, arch=arch))
+          for arch in self.archs)
+
+  def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
+                          predepends, precompiled_header,
+                          case_sensitive_filesystem, spec, arch=None):
+    """Write build rules to compile all of |sources|."""
+
     extra_defines = []
     if self.flavor == 'mac':
-      cflags = self.xcode_settings.GetCflags(config_name)
+      cflags = self.xcode_settings.GetCflags(config_name, arch=arch)
       cflags_c = self.xcode_settings.GetCflagsC(config_name)
       cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
       cflags_objc = ['$cflags_c'] + \
@@ -751,17 +802,25 @@
           obj += '.' + self.toolset
         pdbpath = os.path.normpath(os.path.join(obj, self.base_dir,
                                                 self.name + '.pdb'))
-      self.WriteVariableList('pdbname', [pdbpath])
-      self.WriteVariableList('pchprefix', [self.name])
+      self.WriteVariableList(ninja_file, 'pdbname', [pdbpath])
+      self.WriteVariableList(ninja_file, 'pchprefix', [self.name])
     else:
       cflags = config.get('cflags', [])
-      cflags_c = config.get('cflags_c', [])
-      cflags_cc = config.get('cflags_cc', [])
+
+      # Respect environment variables related to build, but target-specific
+      # flags can still override them.
+      cflags_c = (os.environ.get('CPPFLAGS', '').split() +
+                  os.environ.get('CFLAGS', '').split() +
+                  config.get('cflags_c', []))
+      cflags_cc = (os.environ.get('CPPFLAGS', '').split() +
+                   os.environ.get('CXXFLAGS', '').split() +
+                   config.get('cflags_cc', []))
 
     defines = config.get('defines', []) + extra_defines
-    self.WriteVariableList('defines', [Define(d, self.flavor) for d in defines])
+    self.WriteVariableList(ninja_file, 'defines',
+                           [Define(d, self.flavor) for d in defines])
     if self.flavor == 'win':
-      self.WriteVariableList('rcflags',
+      self.WriteVariableList(ninja_file, 'rcflags',
           [QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
            for f in self.msvs_settings.GetRcflags(config_name,
                                                   self.GypPathToNinja)])
@@ -771,27 +830,30 @@
       include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
                                                           config_name)
     env = self.GetSortedXcodeEnv()
-    self.WriteVariableList('includes',
+    self.WriteVariableList(ninja_file, 'includes',
         [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
          for i in include_dirs])
 
-    pch_commands = precompiled_header.GetPchBuildCommands()
+    pch_commands = precompiled_header.GetPchBuildCommands(arch)
     if self.flavor == 'mac':
       # Most targets use no precompiled headers, so only write these if needed.
       for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'),
                        ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]:
-        include = precompiled_header.GetInclude(ext)
-        if include: self.ninja.variable(var, include)
+        include = precompiled_header.GetInclude(ext, arch)
+        if include: ninja_file.variable(var, include)
 
-    self.WriteVariableList('cflags', map(self.ExpandSpecial, cflags))
-    self.WriteVariableList('cflags_c', map(self.ExpandSpecial, cflags_c))
-    self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc))
+    self.WriteVariableList(ninja_file, 'cflags',
+                           map(self.ExpandSpecial, cflags))
+    self.WriteVariableList(ninja_file, 'cflags_c',
+                           map(self.ExpandSpecial, cflags_c))
+    self.WriteVariableList(ninja_file, 'cflags_cc',
+                           map(self.ExpandSpecial, cflags_cc))
     if self.flavor == 'mac':
-      self.WriteVariableList('cflags_objc', map(self.ExpandSpecial,
-                                                cflags_objc))
-      self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial,
-                                                 cflags_objcc))
-    self.ninja.newline()
+      self.WriteVariableList(ninja_file, 'cflags_objc',
+                             map(self.ExpandSpecial, cflags_objc))
+      self.WriteVariableList(ninja_file, 'cflags_objcc',
+                             map(self.ExpandSpecial, cflags_objcc))
+    ninja_file.newline()
     outputs = []
     for source in sources:
       filename, ext = os.path.splitext(source)
@@ -823,29 +885,31 @@
         continue
       input = self.GypPathToNinja(source)
       output = self.GypPathToUniqueOutput(filename + obj_ext)
+      if arch is not None:
+        output = AddArch(output, arch)
       # Ninja's depfile handling gets confused when the case of a filename
       # changes on a case-insensitive file system. To work around that, always
       # convert .o filenames to lowercase on such file systems. See
       # https://github.com/martine/ninja/issues/402 for details.
       if not case_sensitive_filesystem:
         output = output.lower()
-      implicit = precompiled_header.GetObjDependencies([input], [output])
+      implicit = precompiled_header.GetObjDependencies([input], [output], arch)
       variables = []
       if self.flavor == 'win':
         variables, output, implicit = precompiled_header.GetFlagsModifications(
             input, output, implicit, command, cflags_c, cflags_cc,
             self.ExpandSpecial)
-      self.ninja.build(output, command, input,
+      ninja_file.build(output, command, input,
                        implicit=[gch for _, _, gch in implicit],
                        order_only=predepends, variables=variables)
       outputs.append(output)
 
-    self.WritePchTargets(pch_commands)
+    self.WritePchTargets(ninja_file, pch_commands)
 
-    self.ninja.newline()
+    ninja_file.newline()
     return outputs
 
-  def WritePchTargets(self, pch_commands):
+  def WritePchTargets(self, ninja_file, pch_commands):
     """Writes ninja rules to compile prefix headers."""
     if not pch_commands:
       return
@@ -860,11 +924,28 @@
 
       map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
       cmd = map.get(lang)
-      self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)])
+      ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
 
   def WriteLink(self, spec, config_name, config, link_deps):
     """Write out a link step. Fills out target.binary. """
+    if self.flavor != 'mac' or len(self.archs) == 1:
+      return self.WriteLinkForArch(
+          self.ninja, spec, config_name, config, link_deps)
+    else:
+      output = self.ComputeOutput(spec)
+      inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec,
+                                      config_name, config, link_deps[arch],
+                                      arch=arch)
+                for arch in self.archs]
+      extra_bindings = []
+      if not self.is_mac_bundle:
+        self.AppendPostbuildVariable(extra_bindings, spec, output, output)
+      self.ninja.build(output, 'lipo', inputs, variables=extra_bindings)
+      return output
 
+  def WriteLinkForArch(self, ninja_file, spec, config_name, config,
+                       link_deps, arch=None):
+    """Write out a link step. Fills out target.binary. """
     command = {
       'executable':      'link',
       'loadable_module': 'solink_module',
@@ -906,24 +987,21 @@
       link_deps.extend(list(extra_link_deps))
 
     extra_bindings = []
-    if self.is_mac_bundle:
-      output = self.ComputeMacBundleBinaryOutput()
-    else:
-      output = self.ComputeOutput(spec)
-      extra_bindings.append(('postbuilds',
-                             self.GetPostbuildCommand(spec, output, output)))
+    output = self.ComputeOutput(spec, arch)
+    if arch is None and not self.is_mac_bundle:
+      self.AppendPostbuildVariable(extra_bindings, spec, output, output)
 
     is_executable = spec['type'] == 'executable'
     if self.flavor == 'mac':
       ldflags = self.xcode_settings.GetLdflags(config_name,
           self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
-          self.GypPathToNinja)
+          self.GypPathToNinja, arch)
     elif self.flavor == 'win':
       manifest_name = self.GypPathToUniqueOutput(
           self.ComputeOutputFileName(spec))
       ldflags, manifest_files = self.msvs_settings.GetLdflags(config_name,
           self.GypPathToNinja, self.ExpandSpecial, manifest_name, is_executable)
-      self.WriteVariableList('manifests', manifest_files)
+      self.WriteVariableList(ninja_file, 'manifests', manifest_files)
       command_suffix = _GetWinLinkRuleNameSuffix(
           self.msvs_settings.IsEmbedManifest(config_name),
           self.msvs_settings.IsLinkIncremental(config_name))
@@ -931,16 +1009,18 @@
       if def_file:
         implicit_deps.add(def_file)
     else:
-      ldflags = config.get('ldflags', [])
+      # Respect environment variables related to build, but target-specific
+      # flags can still override them.
+      ldflags = (os.environ.get('LDFLAGS', '').split() +
+                 config.get('ldflags', []))
       if is_executable and len(solibs):
         rpath = 'lib/'
         if self.toolset != 'target':
           rpath += self.toolset
         ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
         ldflags.append('-Wl,-rpath-link=%s' % rpath)
-    self.WriteVariableList('ldflags',
-                           gyp.common.uniquer(map(self.ExpandSpecial,
-                                                  ldflags)))
+    self.WriteVariableList(ninja_file, 'ldflags',
+                           gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
 
     library_dirs = config.get('library_dirs', [])
     if self.flavor == 'win':
@@ -961,9 +1041,9 @@
     elif self.flavor == 'win':
       libraries = self.msvs_settings.AdjustLibraries(libraries)
 
-    self.WriteVariableList('libs', library_dirs + libraries)
+    self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries)
 
-    self.target.binary = output
+    linked_binary = output
 
     if command in ('solink', 'solink_module'):
       extra_bindings.append(('soname', os.path.split(output)[1]))
@@ -982,9 +1062,10 @@
     if len(solibs):
       extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs)))
 
-    self.ninja.build(output, command + command_suffix, link_deps,
+    ninja_file.build(output, command + command_suffix, link_deps,
                      implicit=list(implicit_deps),
                      variables=extra_bindings)
+    return linked_binary
 
   def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
     if spec['type'] == 'none':
@@ -993,39 +1074,53 @@
       self.target.binary = compile_deps
     elif spec['type'] == 'static_library':
       self.target.binary = self.ComputeOutput(spec)
-      variables = []
-      postbuild = self.GetPostbuildCommand(
-          spec, self.target.binary, self.target.binary)
-      if postbuild:
-        variables.append(('postbuilds', postbuild))
-      if self.xcode_settings:
-        libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
-        if libtool_flags:
-          variables.append(('libtool_flags', libtool_flags))
       if (self.flavor not in ('mac', 'openbsd', 'win') and not
           self.is_standalone_static_library):
         self.ninja.build(self.target.binary, 'alink_thin', link_deps,
-                         order_only=compile_deps, variables=variables)
+                         order_only=compile_deps)
       else:
+        variables = []
+        if self.xcode_settings:
+          libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
+          if libtool_flags:
+            variables.append(('libtool_flags', libtool_flags))
         if self.msvs_settings:
           libflags = self.msvs_settings.GetLibFlags(config_name,
                                                     self.GypPathToNinja)
           variables.append(('libflags', libflags))
-        self.ninja.build(self.target.binary, 'alink', link_deps,
-                         order_only=compile_deps, variables=variables)
+
+        if self.flavor != 'mac' or len(self.archs) == 1:
+          self.AppendPostbuildVariable(variables, spec,
+                                       self.target.binary, self.target.binary)
+          self.ninja.build(self.target.binary, 'alink', link_deps,
+                           order_only=compile_deps, variables=variables)
+        else:
+          inputs = []
+          for arch in self.archs:
+            output = self.ComputeOutput(spec, arch)
+            self.arch_subninjas[arch].build(output, 'alink', link_deps[arch],
+                                            order_only=compile_deps,
+                                            variables=variables)
+            inputs.append(output)
+          # TODO: It's not clear if libtool_flags should be passed to the alink
+          # call that combines single-arch .a files into a fat .a file.
+          self.AppendPostbuildVariable(variables, spec,
+                                       self.target.binary, self.target.binary)
+          self.ninja.build(self.target.binary, 'alink', inputs,
+                           # FIXME: test proving order_only=compile_deps isn't
+                           # needed.
+                           variables=variables)
     else:
-      self.WriteLink(spec, config_name, config, link_deps)
+      self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
     return self.target.binary
 
   def WriteMacBundle(self, spec, mac_bundle_depends):
     assert self.is_mac_bundle
     package_framework = spec['type'] in ('shared_library', 'loadable_module')
     output = self.ComputeMacBundleOutput()
-    postbuild = self.GetPostbuildCommand(spec, output, self.target.binary,
-                                         is_command_start=not package_framework)
     variables = []
-    if postbuild:
-      variables.append(('postbuilds', postbuild))
+    self.AppendPostbuildVariable(variables, spec, output, self.target.binary,
+                                 is_command_start=not package_framework)
     if package_framework:
       variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
       self.ninja.build(output, 'package_framework', mac_bundle_depends,
@@ -1056,8 +1151,14 @@
       postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
     return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
 
-  def GetPostbuildCommand(self, spec, output, output_binary,
-                          is_command_start=False):
+  def AppendPostbuildVariable(self, variables, spec, output, binary,
+                              is_command_start=False):
+    """Adds a 'postbuild' variable if there is a postbuild for |output|."""
+    postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
+    if postbuild:
+      variables.append(('postbuilds', postbuild))
+
+  def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
     """Returns a shell command that runs all the postbuilds, and removes
     |output| if any of them fails. If |is_command_start| is False, then the
     returned string will start with ' && '."""
@@ -1111,13 +1212,6 @@
     return self.ExpandSpecial(
         os.path.join(path, self.xcode_settings.GetWrapperName()))
 
-  def ComputeMacBundleBinaryOutput(self):
-    """Return the 'output' (full output path) to the binary in a bundle."""
-    assert self.is_mac_bundle
-    path = generator_default_variables['PRODUCT_DIR']
-    return self.ExpandSpecial(
-        os.path.join(path, self.xcode_settings.GetExecutablePath()))
-
   def ComputeOutputFileName(self, spec, type=None):
     """Compute the filename of the final output for the current target."""
     if not type:
@@ -1168,12 +1262,9 @@
     else:
       raise Exception('Unhandled output type %s' % type)
 
-  def ComputeOutput(self, spec, type=None):
+  def ComputeOutput(self, spec, arch=None):
     """Compute the path for the final output of the spec."""
-    assert not self.is_mac_bundle or type
-
-    if not type:
-      type = spec['type']
+    type = spec['type']
 
     if self.flavor == 'win':
       override = self.msvs_settings.GetOutputName(self.config_name,
@@ -1181,13 +1272,13 @@
       if override:
         return override
 
-    if self.flavor == 'mac' and type in (
+    if arch is None and self.flavor == 'mac' and type in (
         'static_library', 'executable', 'shared_library', 'loadable_module'):
       filename = self.xcode_settings.GetExecutablePath()
     else:
       filename = self.ComputeOutputFileName(spec, type)
 
-    if 'product_dir' in spec:
+    if arch is None and 'product_dir' in spec:
       path = os.path.join(spec['product_dir'], filename)
       return self.ExpandSpecial(path)
 
@@ -1199,7 +1290,14 @@
     elif self.flavor == 'win' and self.toolset == 'target':
       type_in_output_root += ['shared_library']
 
-    if type in type_in_output_root or self.is_standalone_static_library:
+    if arch is not None:
+      # Make sure partial executables don't end up in a bundle or the regular
+      # output directory.
+      archdir = 'arch'
+      if self.toolset != 'target':
+        archdir = os.path.join('arch', '%s' % self.toolset)
+      return os.path.join(archdir, AddArch(filename, arch))
+    elif type in type_in_output_root or self.is_standalone_static_library:
       return filename
     elif type == 'shared_library':
       libdir = 'lib'
@@ -1209,11 +1307,11 @@
     else:
       return self.GypPathToUniqueOutput(filename, qualified=False)
 
-  def WriteVariableList(self, var, values):
+  def WriteVariableList(self, ninja_file, var, values):
     assert not isinstance(values, str)
     if values is None:
       values = []
-    self.ninja.variable(var, ' '.join(values))
+    ninja_file.variable(var, ' '.join(values))
 
   def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
     """Write out a new ninja "rule" statement for a given command.
@@ -1759,6 +1857,10 @@
               './gyp-mac-tool filter-libtool libtool $libtool_flags '
               '-static -o $out $in'
               '$postbuilds')
+    master_ninja.rule(
+      'lipo',
+      description='LIPO $out, POSTBUILDS',
+      command='rm -f $out && lipo -create $in -output $out$postbuilds')
 
     # Record the public interface of $lib in $lib.TOC. See the corresponding
     # comment in the posix section above for details.
@@ -1873,6 +1975,7 @@
 
     writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
                          OpenOutput(os.path.join(toplevel_build, output_file)),
+                         toplevel_build, output_file,
                          flavor, toplevel_dir=options.toplevel_dir)
     master_ninja.subninja(output_file)
 
diff --git a/pylib/gyp/generator/ninja_test.py b/pylib/gyp/generator/ninja_test.py
index 90dd153..52661bc 100644
--- a/pylib/gyp/generator/ninja_test.py
+++ b/pylib/gyp/generator/ninja_test.py
@@ -14,31 +14,31 @@
 
 
 class TestPrefixesAndSuffixes(unittest.TestCase):
-  if sys.platform in ('win32', 'cygwin'):
-    def test_BinaryNamesWindows(self):
-      writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'ninja.build', 'win')
-      spec = { 'target_name': 'wee' }
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'executable').
-          endswith('.exe'))
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
-          endswith('.dll'))
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
-          endswith('.lib'))
+  def test_BinaryNamesWindows(self):
+    writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.',
+        'build.ninja', 'win')
+    spec = { 'target_name': 'wee' }
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'executable').
+        endswith('.exe'))
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
+        endswith('.dll'))
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
+        endswith('.lib'))
 
-  if sys.platform == 'linux2':
-    def test_BinaryNamesLinux(self):
-      writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'ninja.build', 'linux')
-      spec = { 'target_name': 'wee' }
-      self.assertTrue('.' not in writer.ComputeOutputFileName(spec,
-                                                              'executable'))
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
-          startswith('lib'))
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
-          startswith('lib'))
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
-          endswith('.so'))
-      self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
-          endswith('.a'))
+  def test_BinaryNamesLinux(self):
+    writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.',
+        'build.ninja', 'linux')
+    spec = { 'target_name': 'wee' }
+    self.assertTrue('.' not in writer.ComputeOutputFileName(spec,
+                                                            'executable'))
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
+        startswith('lib'))
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
+        startswith('lib'))
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
+        endswith('.so'))
+    self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
+        endswith('.a'))
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/pylib/gyp/msvs_emulation.py b/pylib/gyp/msvs_emulation.py
index ae65e85..0e16ed6 100644
--- a/pylib/gyp/msvs_emulation.py
+++ b/pylib/gyp/msvs_emulation.py
@@ -677,11 +677,12 @@
     files."""
     return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
 
-  def GetObjDependencies(self, sources, objs):
+  def GetObjDependencies(self, sources, objs, arch):
     """Given a list of sources files and the corresponding object files,
     returns a list of the pch files that should be depended upon. The
     additional wrapping in the return value is for interface compatability
     with make.py on Mac, and xcode_emulation.py."""
+    assert arch is None
     if not self._PchHeader():
       return []
     pch_ext = os.path.splitext(self.pch_source)[1]
@@ -690,7 +691,7 @@
         return [(None, None, self.output_obj)]
     return []
 
-  def GetPchBuildCommands(self):
+  def GetPchBuildCommands(self, arch):
     """Not used on Windows as there are no additional build steps required
     (instead, existing steps are modified in GetFlagsModifications below)."""
     return []
diff --git a/pylib/gyp/xcode_emulation.py b/pylib/gyp/xcode_emulation.py
index 3ef1cc7..5e8f2b7 100644
--- a/pylib/gyp/xcode_emulation.py
+++ b/pylib/gyp/xcode_emulation.py
@@ -39,6 +39,15 @@
                                              None):
         self.isIOS = True
 
+      # If you need this, speak up at http://crbug.com/122592
+      conditional_keys = [key for key in self.xcode_settings[configname]
+                          if key.endswith(']')]
+      if conditional_keys:
+        print 'Warning: Conditional keys not implemented, ignoring:', \
+              ' '.join(conditional_keys)
+        for key in conditional_keys:
+          del self.xcode_settings[configname][key]
+
     # This is only non-None temporarily during the execution of some methods.
     self.configname = None
 
@@ -231,6 +240,12 @@
     else:
       return self._GetStandaloneBinaryPath()
 
+  def GetActiveArchs(self, configname):
+    """Returns the architectures this target should be built for."""
+    # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set
+    # CURRENT_ARCH / NATIVE_ARCH env vars?
+    return self.xcode_settings[configname].get('ARCHS', ['i386'])
+
   def _GetSdkVersionInfoItem(self, sdk, infoitem):
     job = subprocess.Popen(['xcodebuild', '-version', '-sdk', sdk, infoitem],
                            stdout=subprocess.PIPE)
@@ -261,7 +276,7 @@
         self._Appendf(lst, 'IPHONEOS_DEPLOYMENT_TARGET',
                       '-miphoneos-version-min=%s')
 
-  def GetCflags(self, configname):
+  def GetCflags(self, configname, arch=None):
     """Returns flags that need to be added to .c, .cc, .m, and .mm
     compilations."""
     # This functions (and the similar ones below) do not offer complete
@@ -334,7 +349,10 @@
     self._WarnUnimplemented('MACH_O_TYPE')
     self._WarnUnimplemented('PRODUCT_TYPE')
 
-    archs = self._Settings().get('ARCHS', ['i386'])
+    if arch is not None:
+      archs = [arch]
+    else:
+      archs = self._Settings().get('ARCHS', ['i386'])
     if len(archs) != 1:
       # TODO: Supporting fat binaries will be annoying.
       self._WarnUnimplemented('ARCHS')
@@ -366,7 +384,10 @@
     """Returns flags that need to be added to .c, and .m compilations."""
     self.configname = configname
     cflags_c = []
-    self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s')
+    if self._Settings().get('GCC_C_LANGUAGE_STANDARD', '') == 'ansi':
+      cflags_c.append('-ansi')
+    else:
+      self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s')
     cflags_c += self._Settings().get('OTHER_CFLAGS', [])
     self.configname = None
     return cflags_c
@@ -532,7 +553,7 @@
       ldflag = '-L' + gyp_to_build_path(ldflag[len('-L'):])
     return ldflag
 
-  def GetLdflags(self, configname, product_dir, gyp_to_build_path):
+  def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None):
     """Returns flags that need to be passed to the linker.
 
     Args:
@@ -574,7 +595,10 @@
                      '-Wl,' + gyp_to_build_path(
                                   self._Settings()['ORDER_FILE']))
 
-    archs = self._Settings().get('ARCHS', ['i386'])
+    if arch is not None:
+      archs = [arch]
+    else:
+      archs = self._Settings().get('ARCHS', ['i386'])
     if len(archs) != 1:
       # TODO: Supporting fat binaries will be annoying.
       self._WarnUnimplemented('ARCHS')
@@ -779,21 +803,28 @@
               self.header, lang)
       self.header = gyp_path_to_build_path(self.header)
 
-  def GetInclude(self, lang):
+  def _CompiledHeader(self, lang, arch):
+    assert self.compile_headers
+    h = self.compiled_headers[lang]
+    if arch:
+      h += '.' + arch
+    return h
+
+  def GetInclude(self, lang, arch=None):
     """Gets the cflags to include the prefix header for language |lang|."""
     if self.compile_headers and lang in self.compiled_headers:
-      return '-include %s' % self.compiled_headers[lang]
+      return '-include %s' % self._CompiledHeader(lang, arch)
     elif self.header:
       return '-include %s' % self.header
     else:
       return ''
 
-  def _Gch(self, lang):
+  def _Gch(self, lang, arch):
     """Returns the actual file name of the prefix header for language |lang|."""
     assert self.compile_headers
-    return self.compiled_headers[lang] + '.gch'
+    return self._CompiledHeader(lang, arch) + '.gch'
 
-  def GetObjDependencies(self, sources, objs):
+  def GetObjDependencies(self, sources, objs, arch=None):
     """Given a list of source files and the corresponding object files, returns
     a list of (source, object, gch) tuples, where |gch| is the build-directory
     relative path to the gch file each object file depends on.  |compilable[i]|
@@ -811,20 +842,20 @@
         '.mm': 'mm',
       }.get(ext, None)
       if lang:
-        result.append((source, obj, self._Gch(lang)))
+        result.append((source, obj, self._Gch(lang, arch)))
     return result
 
-  def GetPchBuildCommands(self):
+  def GetPchBuildCommands(self, arch=None):
     """Returns [(path_to_gch, language_flag, language, header)].
     |path_to_gch| and |header| are relative to the build directory.
     """
     if not self.header or not self.compile_headers:
       return []
     return [
-      (self._Gch('c'), '-x c-header', 'c', self.header),
-      (self._Gch('cc'), '-x c++-header', 'cc', self.header),
-      (self._Gch('m'), '-x objective-c-header', 'm', self.header),
-      (self._Gch('mm'), '-x objective-c++-header', 'mm', self.header),
+      (self._Gch('c', arch), '-x c-header', 'c', self.header),
+      (self._Gch('cc', arch), '-x c++-header', 'cc', self.header),
+      (self._Gch('m', arch), '-x objective-c-header', 'm', self.header),
+      (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header),
     ]
 
 
diff --git a/test/cflags/gyptest-cflags.py b/test/cflags/gyptest-cflags.py
index a007623..5de8c32 100755
--- a/test/cflags/gyptest-cflags.py
+++ b/test/cflags/gyptest-cflags.py
@@ -10,6 +10,7 @@
 """
 
 import os
+import sys
 import TestGyp
 
 env_stack = []
@@ -22,9 +23,12 @@
 def PopEnv():
   os.eniron=env_stack.pop()
 
-# Regenerating build files when a gyp file changes is currently only supported
-# by the make generator.
-test = TestGyp.TestGyp(formats=['make'])
+formats = ['make']
+if sys.platform.startswith('linux'):
+  # Only Linux ninja generator supports CFLAGS.
+  formats.append('ninja')
+
+test = TestGyp.TestGyp(formats=formats)
 
 try:
   PushEnv()
diff --git a/test/mac/archs/empty_main.cc b/test/mac/archs/empty_main.cc
new file mode 100644
index 0000000..237c8ce
--- /dev/null
+++ b/test/mac/archs/empty_main.cc
@@ -0,0 +1 @@
+int main() {}
diff --git a/test/mac/archs/file.mm b/test/mac/archs/file.mm
new file mode 100644
index 0000000..d0b39d1
--- /dev/null
+++ b/test/mac/archs/file.mm
@@ -0,0 +1 @@
+MyInt f() { return 0; }
diff --git a/test/mac/archs/header.h b/test/mac/archs/header.h
new file mode 100644
index 0000000..0716e50
--- /dev/null
+++ b/test/mac/archs/header.h
@@ -0,0 +1 @@
+typedef int MyInt;
diff --git a/test/mac/archs/test-archs-multiarch.gyp b/test/mac/archs/test-archs-multiarch.gyp
new file mode 100644
index 0000000..a187ca5
--- /dev/null
+++ b/test/mac/archs/test-archs-multiarch.gyp
@@ -0,0 +1,60 @@
+# Copyright (c) 2012 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': 'static_32_64',
+      'type': 'static_library',
+      'sources': [ 'my_file.cc' ],
+      'xcode_settings': {
+        'ARCHS': [ 'i386', 'x86_64' ],
+      },
+    },
+    {
+      'target_name': 'shared_32_64',
+      'type': 'shared_library',
+      'sources': [ 'my_file.cc' ],
+      'xcode_settings': {
+        'ARCHS': [ 'i386', 'x86_64' ],
+      },
+    },
+    {
+      'target_name': 'module_32_64',
+      'type': 'loadable_module',
+      'sources': [ 'my_file.cc' ],
+      'xcode_settings': {
+        'ARCHS': [ 'i386', 'x86_64' ],
+      },
+    },
+    {
+      'target_name': 'exe_32_64',
+      'type': 'executable',
+      'sources': [ 'empty_main.cc' ],
+      'xcode_settings': {
+        'ARCHS': [ 'i386', 'x86_64' ],
+      },
+    },
+    {
+      'target_name': 'exe_32_64_bundle',
+      'product_name': 'Test App',
+      'type': 'executable',
+      'mac_bundle': 1,
+      'sources': [ 'empty_main.cc' ],
+      'xcode_settings': {
+        'ARCHS': [ 'i386', 'x86_64' ],
+      },
+    },
+    # This only needs to compile.
+    {
+      'target_name': 'precompiled_prefix_header_mm_32_64',
+      'type': 'shared_library',
+      'sources': [ 'file.mm', ],
+      'xcode_settings': {
+        'GCC_PREFIX_HEADER': 'header.h',
+        'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
+      },
+    },
+  ]
+}
diff --git a/test/mac/cflags/test.gyp b/test/mac/cflags/test.gyp
index 6b04b5f..d330a54 100644
--- a/test/mac/cflags/test.gyp
+++ b/test/mac/cflags/test.gyp
@@ -115,5 +115,18 @@
         'GCC_C_LANGUAGE_STANDARD': 'c99',
       },
     },
+    {
+      'target_name': 'ansi_standard',
+      'type': 'shared_library',
+      'sources': [
+        'cfile.c',
+      ],
+      'xcode_settings': {
+        'OTHER_CFLAGS': [
+          '-DCFLAG',
+        ],
+        'GCC_C_LANGUAGE_STANDARD': 'ansi',
+      },
+    },
   ],
 }
diff --git a/test/mac/gyptest-archs.py b/test/mac/gyptest-archs.py
index 781e9ef..70bb6a3 100644
--- a/test/mac/gyptest-archs.py
+++ b/test/mac/gyptest-archs.py
@@ -10,6 +10,7 @@
 
 import TestGyp
 
+import re
 import subprocess
 import sys
 
@@ -20,7 +21,7 @@
     proc = subprocess.Popen(['file', '-b', file], stdout=subprocess.PIPE)
     o = proc.communicate()[0].strip()
     assert not proc.returncode
-    if o != expected:
+    if not re.match(expected, o, re.DOTALL):
       print 'File: Expected %s, got %s' % (expected, o)
       test.fail_test()
 
@@ -28,10 +29,39 @@
   test.build('test-no-archs.gyp', test.ALL, chdir='archs')
   result_file = test.built_file_path('Test', chdir='archs')
   test.must_exist(result_file)
-  CheckFileType(result_file, 'Mach-O executable i386')
+  # FIXME: The default setting changed from i386 to x86_64 in Xcode 5.
+  #CheckFileType(result_file, '^Mach-O executable i386')
 
   test.run_gyp('test-archs-x86_64.gyp', chdir='archs')
   test.build('test-archs-x86_64.gyp', test.ALL, chdir='archs')
   result_file = test.built_file_path('Test64', chdir='archs')
   test.must_exist(result_file)
-  CheckFileType(result_file, 'Mach-O 64-bit executable x86_64')
+  CheckFileType(result_file, '^Mach-O 64-bit executable x86_64$')
+
+  if test.format != 'make':
+    test.run_gyp('test-archs-multiarch.gyp', chdir='archs')
+    test.build('test-archs-multiarch.gyp', test.ALL, chdir='archs')
+
+    result_file = test.built_file_path(
+        'static_32_64', chdir='archs', type=test.STATIC_LIB)
+    test.must_exist(result_file)
+    CheckFileType(result_file, 'Mach-O universal binary with 2 architectures'
+                               '.*architecture i386.*architecture x86_64')
+
+    result_file = test.built_file_path(
+        'shared_32_64', chdir='archs', type=test.SHARED_LIB)
+    test.must_exist(result_file)
+    CheckFileType(result_file, 'Mach-O universal binary with 2 architectures'
+                               '.*architecture i386.*architecture x86_64')
+
+    result_file = test.built_file_path(
+        'exe_32_64', chdir='archs', type=test.EXECUTABLE)
+    test.must_exist(result_file)
+    CheckFileType(result_file, 'Mach-O universal binary with 2 architectures'
+                               '.*architecture i386.*architecture x86_64')
+
+    result_file = test.built_file_path('Test App.app/Contents/MacOS/Test App',
+                                       chdir='archs')
+    test.must_exist(result_file)
+    CheckFileType(result_file, 'Mach-O universal binary with 2 architectures'
+                               '.*architecture i386.*architecture x86_64')
diff --git a/test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py b/test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py
new file mode 100644
index 0000000..8c8c365
--- /dev/null
+++ b/test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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.
+
+"""
+Test that the generator output can be written to a different drive on Windows.
+"""
+
+import os
+import TestGyp
+import string
+import subprocess
+import sys
+
+
+if sys.platform == 'win32':
+  import win32api
+
+  test = TestGyp.TestGyp(formats=['msvs', 'ninja'])
+
+  def GetFirstFreeDriveLetter():
+    """ Returns the first unused Windows drive letter in [A, Z] """
+    all_letters = [c for c in string.uppercase]
+    in_use = win32api.GetLogicalDriveStrings()
+    free = list(set(all_letters) - set(in_use))
+    return free[0]
+
+  output_dir = os.path.join('different-drive', 'output')
+  if not os.path.isdir(os.path.abspath(output_dir)):
+    os.makedirs(os.path.abspath(output_dir))
+  output_drive = GetFirstFreeDriveLetter()
+  subprocess.call(['subst', '%c:' % output_drive, os.path.abspath(output_dir)])
+  try:
+    test.run_gyp('prog.gyp', '--generator-output=%s' % (
+        os.path.join(output_drive, 'output')))
+    test.build('prog.gyp', test.ALL, chdir=os.path.join(output_drive, 'output'))
+    test.built_file_must_exist('program', chdir=os.path.join(output_drive,
+                                                             'output'),
+                               type=test.EXECUTABLE)
+    test.pass_test()
+  finally:
+    subprocess.call(['subst', '%c:' % output_drive, '/D'])
diff --git a/test/win/generator-output-different-drive/prog.c b/test/win/generator-output-different-drive/prog.c
new file mode 100644
index 0000000..2f855c4
--- /dev/null
+++ b/test/win/generator-output-different-drive/prog.c
@@ -0,0 +1,10 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+  printf("Hello from prog.c\n");
+  return 0;
+}
diff --git a/test/win/generator-output-different-drive/prog.gyp b/test/win/generator-output-different-drive/prog.gyp
new file mode 100644
index 0000000..92f53e5
--- /dev/null
+++ b/test/win/generator-output-different-drive/prog.gyp
@@ -0,0 +1,15 @@
+# Copyright 2013 The Chromium Authors. 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': 'program',
+      'type': 'executable',
+      'sources': [
+        'prog.c',
+      ],
+    },
+  ],
+}