tools: update gyp to r1601
authorBen Noordhuis <info@bnoordhuis.nl>
Sun, 24 Mar 2013 13:29:17 +0000 (14:29 +0100)
committerBen Noordhuis <info@bnoordhuis.nl>
Sun, 24 Mar 2013 14:03:09 +0000 (15:03 +0100)
Among other things, this should make it easier for people to build
node.js on openbsd.

23 files changed:
tools/gyp/AUTHORS
tools/gyp/PRESUBMIT.py
tools/gyp/buildbot/buildbot_run.py [deleted file]
tools/gyp/data/win/large-pdb-shim.cc [new file with mode: 0644]
tools/gyp/gyptest.py
tools/gyp/pylib/gyp/MSVSUtil.py [new file with mode: 0644]
tools/gyp/pylib/gyp/MSVSVersion.py
tools/gyp/pylib/gyp/__init__.py
tools/gyp/pylib/gyp/common.py
tools/gyp/pylib/gyp/common_test.py
tools/gyp/pylib/gyp/generator/android.py
tools/gyp/pylib/gyp/generator/eclipse.py
tools/gyp/pylib/gyp/generator/make.py
tools/gyp/pylib/gyp/generator/msvs.py
tools/gyp/pylib/gyp/generator/ninja.py
tools/gyp/pylib/gyp/generator/xcode.py
tools/gyp/pylib/gyp/input.py
tools/gyp/pylib/gyp/mac_tool.py
tools/gyp/pylib/gyp/msvs_emulation.py
tools/gyp/pylib/gyp/xcode_emulation.py
tools/gyp/pylib/gyp/xcodeproj_file.py
tools/gyp/tools/emacs/gyp-tests.el
tools/gyp/tools/emacs/gyp.el

index 6db82b9..8977761 100644 (file)
@@ -2,5 +2,7 @@
 # Name or Organization <email address>
 
 Google Inc.
+Bloomberg Finance L.P.
+
 Steven Knight <knight@baldmt.com>
 Ryan Norton <rnorton10@gmail.com>
index 0338fb4..6523566 100644 (file)
@@ -75,13 +75,20 @@ def CheckChangeOnUpload(input_api, output_api):
 
 def CheckChangeOnCommit(input_api, output_api):
   report = []
+
+  # Accept any year number from 2009 to the current year.
+  current_year = int(input_api.time.strftime('%Y'))
+  allowed_years = (str(s) for s in reversed(xrange(2009, current_year + 1)))
+  years_re = '(' + '|'.join(allowed_years) + ')'
+
+  # The (c) is deprecated, but tolerate it until it's removed from all files.
   license = (
-      r'.*? Copyright \(c\) %(year)s Google Inc\. All rights reserved\.\n'
+      r'.*? Copyright (\(c\) )?%(year)s Google Inc\. All rights reserved\.\n'
       r'.*? Use of this source code is governed by a BSD-style license that '
         r'can be\n'
       r'.*? found in the LICENSE file\.\n'
   ) % {
-      'year': input_api.time.strftime('%Y'),
+      'year': years_re,
   }
 
   report.extend(input_api.canned_checks.PanProjectChecks(
@@ -106,4 +113,4 @@ def CheckChangeOnCommit(input_api, output_api):
 
 
 def GetPreferredTrySlaves():
-  return ['gyp-win32', 'gyp-win64', 'gyp-linux', 'gyp-mac']
+  return ['gyp-win32', 'gyp-win64', 'gyp-linux', 'gyp-mac', 'gyp-android']
diff --git a/tools/gyp/buildbot/buildbot_run.py b/tools/gyp/buildbot/buildbot_run.py
deleted file mode 100755 (executable)
index 57fdb65..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-
-"""Argument-less script to select what to run on the buildbots."""
-
-
-import os
-import shutil
-import subprocess
-import sys
-
-
-if sys.platform in ['win32', 'cygwin']:
-  EXE_SUFFIX = '.exe'
-else:
-  EXE_SUFFIX = ''
-
-
-BUILDBOT_DIR = os.path.dirname(os.path.abspath(__file__))
-TRUNK_DIR = os.path.dirname(BUILDBOT_DIR)
-ROOT_DIR = os.path.dirname(TRUNK_DIR)
-OUT_DIR = os.path.join(TRUNK_DIR, 'out')
-
-
-def GypTestFormat(title, format=None, msvs_version=None):
-  """Run the gyp tests for a given format, emitting annotator tags.
-
-  See annotator docs at:
-    https://sites.google.com/a/chromium.org/dev/developers/testing/chromium-build-infrastructure/buildbot-annotations
-  Args:
-    format: gyp format to test.
-  Returns:
-    0 for sucesss, 1 for failure.
-  """
-  if not format:
-    format = title
-
-  print '@@@BUILD_STEP ' + title + '@@@'
-  sys.stdout.flush()
-  env = os.environ.copy()
-  # TODO(bradnelson): remove this when this issue is resolved:
-  #     http://code.google.com/p/chromium/issues/detail?id=108251
-  if format == 'ninja':
-    env['NOGOLD'] = '1'
-  if msvs_version:
-    env['GYP_MSVS_VERSION'] = msvs_version
-  retcode = subprocess.call(' '.join(
-      [sys.executable, 'trunk/gyptest.py',
-       '--all',
-       '--passed',
-       '--format', format,
-       '--chdir', 'trunk',
-       '--path', '../scons']),
-      cwd=ROOT_DIR, env=env, shell=True)
-  if retcode:
-    # Emit failure tag, and keep going.
-    print '@@@STEP_FAILURE@@@'
-    return 1
-  return 0
-
-
-def GypBuild():
-  # Dump out/ directory.
-  print '@@@BUILD_STEP cleanup@@@'
-  print 'Removing %s...' % OUT_DIR
-  shutil.rmtree(OUT_DIR, ignore_errors=True)
-  print 'Done.'
-
-  retcode = 0
-  if sys.platform.startswith('linux'):
-    retcode += GypTestFormat('ninja')
-    retcode += GypTestFormat('scons')
-    retcode += GypTestFormat('make')
-  elif sys.platform == 'darwin':
-    retcode += GypTestFormat('ninja')
-    retcode += GypTestFormat('xcode')
-    retcode += GypTestFormat('make')
-  elif sys.platform == 'win32':
-    retcode += GypTestFormat('ninja')
-    retcode += GypTestFormat('msvs-2008', format='msvs', msvs_version='2008')
-    if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64':
-      retcode += GypTestFormat('msvs-2010', format='msvs', msvs_version='2010')
-  else:
-    raise Exception('Unknown platform')
-  if retcode:
-    # TODO(bradnelson): once the annotator supports a postscript (section for
-    #     after the build proper that could be used for cumulative failures),
-    #     use that instead of this. This isolates the final return value so
-    #     that it isn't misattributed to the last stage.
-    print '@@@BUILD_STEP failures@@@'
-    sys.exit(retcode)
-
-
-if __name__ == '__main__':
-  GypBuild()
diff --git a/tools/gyp/data/win/large-pdb-shim.cc b/tools/gyp/data/win/large-pdb-shim.cc
new file mode 100644 (file)
index 0000000..8bca510
--- /dev/null
@@ -0,0 +1,12 @@
+// 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.
+
+// This file is used to generate an empty .pdb -- with a 4KB pagesize -- that is
+// then used during the final link for modules that have large PDBs. Otherwise,
+// the linker will generate a pdb with a page size of 1KB, which imposes a limit
+// of 1GB on the .pdb. By generating an initial empty .pdb with the compiler
+// (rather than the linker), this limit is avoided. With this in place PDBs may
+// grow to 2GB.
+//
+// This file is referenced by the msvs_large_pdb mechanism in MSVSUtil.py.
index 6c6b009..efa75a7 100755 (executable)
@@ -212,6 +212,7 @@ def main(argv=None):
     format_list = {
       'freebsd7': ['make'],
       'freebsd8': ['make'],
+      'openbsd5': ['make'],
       'cygwin':   ['msvs'],
       'win32':    ['msvs', 'ninja'],
       'linux2':   ['make', 'ninja'],
diff --git a/tools/gyp/pylib/gyp/MSVSUtil.py b/tools/gyp/pylib/gyp/MSVSUtil.py
new file mode 100644 (file)
index 0000000..5afcd1f
--- /dev/null
@@ -0,0 +1,212 @@
+# 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.
+
+"""Utility functions shared amongst the Windows generators."""
+
+import copy
+import os
+
+
+_TARGET_TYPE_EXT = {
+  'executable': '.exe',
+  'shared_library': '.dll'
+}
+
+
+def _GetLargePdbShimCcPath():
+  """Returns the path of the large_pdb_shim.cc file."""
+  this_dir = os.path.abspath(os.path.dirname(__file__))
+  src_dir = os.path.abspath(os.path.join(this_dir, '..', '..'))
+  win_data_dir = os.path.join(src_dir, 'data', 'win')
+  large_pdb_shim_cc = os.path.join(win_data_dir, 'large-pdb-shim.cc')
+  return large_pdb_shim_cc
+
+
+def _DeepCopySomeKeys(in_dict, keys):
+  """Performs a partial deep-copy on |in_dict|, only copying the keys in |keys|.
+
+  Arguments:
+    in_dict: The dictionary to copy.
+    keys: The keys to be copied. If a key is in this list and doesn't exist in
+        |in_dict| this is not an error.
+  Returns:
+    The partially deep-copied dictionary.
+  """
+  d = {}
+  for key in keys:
+    if key not in in_dict:
+      continue
+    d[key] = copy.deepcopy(in_dict[key])
+  return d
+
+
+def _SuffixName(name, suffix):
+  """Add a suffix to the end of a target.
+
+  Arguments:
+    name: name of the target (foo#target)
+    suffix: the suffix to be added
+  Returns:
+    Target name with suffix added (foo_suffix#target)
+  """
+  parts = name.rsplit('#', 1)
+  parts[0] = '%s_%s' % (parts[0], suffix)
+  return '#'.join(parts)
+
+
+def _ShardName(name, number):
+  """Add a shard number to the end of a target.
+
+  Arguments:
+    name: name of the target (foo#target)
+    number: shard number
+  Returns:
+    Target name with shard added (foo_1#target)
+  """
+  return _SuffixName(name, str(number))
+
+
+def ShardTargets(target_list, target_dicts):
+  """Shard some targets apart to work around the linkers limits.
+
+  Arguments:
+    target_list: List of target pairs: 'base/base.gyp:base'.
+    target_dicts: Dict of target properties keyed on target pair.
+  Returns:
+    Tuple of the new sharded versions of the inputs.
+  """
+  # Gather the targets to shard, and how many pieces.
+  targets_to_shard = {}
+  for t in target_dicts:
+    shards = int(target_dicts[t].get('msvs_shard', 0))
+    if shards:
+      targets_to_shard[t] = shards
+  # Shard target_list.
+  new_target_list = []
+  for t in target_list:
+    if t in targets_to_shard:
+      for i in range(targets_to_shard[t]):
+        new_target_list.append(_ShardName(t, i))
+    else:
+      new_target_list.append(t)
+  # Shard target_dict.
+  new_target_dicts = {}
+  for t in target_dicts:
+    if t in targets_to_shard:
+      for i in range(targets_to_shard[t]):
+        name = _ShardName(t, i)
+        new_target_dicts[name] = copy.copy(target_dicts[t])
+        new_target_dicts[name]['target_name'] = _ShardName(
+             new_target_dicts[name]['target_name'], i)
+        sources = new_target_dicts[name].get('sources', [])
+        new_sources = []
+        for pos in range(i, len(sources), targets_to_shard[t]):
+          new_sources.append(sources[pos])
+        new_target_dicts[name]['sources'] = new_sources
+    else:
+      new_target_dicts[t] = target_dicts[t]
+  # Shard dependencies.
+  for t in new_target_dicts:
+    dependencies = copy.copy(new_target_dicts[t].get('dependencies', []))
+    new_dependencies = []
+    for d in dependencies:
+      if d in targets_to_shard:
+        for i in range(targets_to_shard[d]):
+          new_dependencies.append(_ShardName(d, i))
+      else:
+        new_dependencies.append(d)
+    new_target_dicts[t]['dependencies'] = new_dependencies
+
+  return (new_target_list, new_target_dicts)
+
+
+def InsertLargePdbShims(target_list, target_dicts, vars):
+  """Insert a shim target that forces the linker to use 4KB pagesize PDBs.
+
+  This is a workaround for targets with PDBs greater than 1GB in size, the
+  limit for the 1KB pagesize PDBs created by the linker by default.
+
+  Arguments:
+    target_list: List of target pairs: 'base/base.gyp:base'.
+    target_dicts: Dict of target properties keyed on target pair.
+    vars: A dictionary of common GYP variables with generator-specific values.
+  Returns:
+    Tuple of the shimmed version of the inputs.
+  """
+  # Determine which targets need shimming.
+  targets_to_shim = []
+  for t in target_dicts:
+    target_dict = target_dicts[t]
+    # We only want to shim targets that have msvs_large_pdb enabled.
+    if not int(target_dict.get('msvs_large_pdb', 0)):
+      continue
+    # This is intended for executable, shared_library and loadable_module
+    # targets where every configuration is set up to produce a PDB output.
+    # If any of these conditions is not true then the shim logic will fail
+    # below.
+    targets_to_shim.append(t)
+
+  large_pdb_shim_cc = _GetLargePdbShimCcPath()
+
+  for t in targets_to_shim:
+    target_dict = target_dicts[t]
+    target_name = target_dict.get('target_name')
+
+    base_dict = _DeepCopySomeKeys(target_dict,
+          ['configurations', 'default_configuration', 'toolset'])
+
+    # This is the dict for copying the source file (part of the GYP tree)
+    # to the intermediate directory of the project. This is necessary because
+    # we can't always build a relative path to the shim source file (on Windows
+    # GYP and the project may be on different drives), and Ninja hates absolute
+    # paths (it ends up generating the .obj and .obj.d alongside the source
+    # file, polluting GYPs tree).
+    copy_suffix = '_large_pdb_copy'
+    copy_target_name = target_name + '_' + copy_suffix
+    full_copy_target_name = _SuffixName(t, copy_suffix)
+    shim_cc_basename = os.path.basename(large_pdb_shim_cc)
+    shim_cc_dir = vars['SHARED_INTERMEDIATE_DIR'] + '/' + copy_target_name
+    shim_cc_path = shim_cc_dir + '/' + shim_cc_basename
+    copy_dict = copy.deepcopy(base_dict)
+    copy_dict['target_name'] = copy_target_name
+    copy_dict['type'] = 'none'
+    copy_dict['sources'] = [ large_pdb_shim_cc ]
+    copy_dict['copies'] = [{
+      'destination': shim_cc_dir,
+      'files': [ large_pdb_shim_cc ]
+    }]
+
+    # This is the dict for the PDB generating shim target. It depends on the
+    # copy target.
+    shim_suffix = '_large_pdb_shim'
+    shim_target_name = target_name + '_' + shim_suffix
+    full_shim_target_name = _SuffixName(t, shim_suffix)
+    shim_dict = copy.deepcopy(base_dict)
+    shim_dict['target_name'] = shim_target_name
+    shim_dict['type'] = 'static_library'
+    shim_dict['sources'] = [ shim_cc_path ]
+    shim_dict['dependencies'] = [ full_copy_target_name ]
+
+    # Set up the shim to output its PDB to the same location as the final linker
+    # target.
+    for config in shim_dict.get('configurations').itervalues():
+      msvs = config.setdefault('msvs_settings')
+
+      linker = msvs.pop('VCLinkerTool')  # We want to clear this dict.
+      pdb_path = linker.get('ProgramDatabaseFile')
+
+      compiler = msvs.setdefault('VCCLCompilerTool', {})
+      compiler.setdefault('DebugInformationFormat', '3')
+      compiler.setdefault('ProgramDataBaseFileName', pdb_path)
+
+    # Add the new targets.
+    target_list.append(full_copy_target_name)
+    target_list.append(full_shim_target_name)
+    target_dicts[full_copy_target_name] = copy_dict
+    target_dicts[full_shim_target_name] = shim_dict
+
+    # Update the original target to depend on the shim target.
+    target_dict.setdefault('dependencies', []).append(full_shim_target_name)
+
+  return (target_list, target_dicts)
\ No newline at end of file
index 97caf66..2d95cd0 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 Google Inc. All rights reserved.
+# 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.
 
@@ -355,6 +355,13 @@ def SelectVisualStudioVersion(version='auto'):
     '2012': ('11.0',),
     '2012e': ('11.0',),
   }
+  override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
+  if override_path:
+    msvs_version = os.environ.get('GYP_MSVS_VERSION')
+    if not msvs_version or 'e' not in msvs_version:
+      raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
+                       'set to an "e" version (e.g. 2010e)')
+    return _CreateVersion(msvs_version, override_path, sdk_based=True)
   version = str(version)
   versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
   if not versions:
index ac300a9..3769c52 100755 (executable)
@@ -23,8 +23,8 @@ DEBUG_VARIABLES = 'variables'
 DEBUG_INCLUDES = 'includes'
 
 
-def DebugOutput(mode, message):
-  if 'all' in gyp.debug.keys() or mode in gyp.debug.keys():
+def DebugOutput(mode, message, *args):
+  if 'all' in gyp.debug or mode in gyp.debug:
     ctx = ('unknown', 0, 'unknown')
     try:
       f = traceback.extract_stack(limit=2)
@@ -32,6 +32,8 @@ def DebugOutput(mode, message):
         ctx = f[0][:3]
     except:
       pass
+    if args:
+      message %= args
     print '%s:%s:%d:%s %s' % (mode.upper(), os.path.basename(ctx[0]),
                               ctx[1], ctx[2], message)
 
@@ -376,21 +378,22 @@ def gyp_main(args):
       options.generator_output = g_o
 
   if not options.parallel and options.use_environment:
-    options.parallel = bool(os.environ.get('GYP_PARALLEL'))
+    p = os.environ.get('GYP_PARALLEL')
+    options.parallel = bool(p and p != '0')
 
   for mode in options.debug:
     gyp.debug[mode] = 1
 
   # Do an extra check to avoid work when we're not debugging.
-  if DEBUG_GENERAL in gyp.debug.keys():
+  if DEBUG_GENERAL in gyp.debug:
     DebugOutput(DEBUG_GENERAL, 'running with these options:')
     for option, value in sorted(options.__dict__.items()):
       if option[0] == '_':
         continue
       if isinstance(value, basestring):
-        DebugOutput(DEBUG_GENERAL, "  %s: '%s'" % (option, value))
+        DebugOutput(DEBUG_GENERAL, "  %s: '%s'", option, value)
       else:
-        DebugOutput(DEBUG_GENERAL, "  %s: %s" % (option, str(value)))
+        DebugOutput(DEBUG_GENERAL, "  %s: %s", option, value)
 
   if not build_files:
     build_files = FindBuildFiles()
@@ -440,9 +443,9 @@ def gyp_main(args):
   if options.defines:
     defines += options.defines
   cmdline_default_variables = NameValueListToDict(defines)
-  if DEBUG_GENERAL in gyp.debug.keys():
+  if DEBUG_GENERAL in gyp.debug:
     DebugOutput(DEBUG_GENERAL,
-                "cmdline_default_variables: %s" % cmdline_default_variables)
+                "cmdline_default_variables: %s", cmdline_default_variables)
 
   # Set up includes.
   includes = []
@@ -468,7 +471,7 @@ def gyp_main(args):
     gen_flags += options.generator_flags
   generator_flags = NameValueListToDict(gen_flags)
   if DEBUG_GENERAL in gyp.debug.keys():
-    DebugOutput(DEBUG_GENERAL, "generator_flags: %s" % generator_flags)
+    DebugOutput(DEBUG_GENERAL, "generator_flags: %s", generator_flags)
 
   # TODO: Remove this and the option after we've gotten folks to move to the
   # generator flag.
index e917a59..e50f51c 100644 (file)
@@ -127,9 +127,9 @@ def RelativePath(path, relative_to):
   # directory, returns a relative path that identifies path relative to
   # relative_to.
 
-  # Convert to absolute (and therefore normalized paths).
-  path = os.path.abspath(path)
-  relative_to = os.path.abspath(relative_to)
+  # Convert to normalized (and therefore absolute paths).
+  path = os.path.realpath(path)
+  relative_to = os.path.realpath(relative_to)
 
   # Split the paths into components.
   path_split = path.split(os.path.sep)
@@ -151,6 +151,20 @@ def RelativePath(path, relative_to):
   return os.path.join(*relative_split)
 
 
+@memoize
+def InvertRelativePath(path, toplevel_dir=None):
+  """Given a path like foo/bar that is relative to toplevel_dir, return
+  the inverse relative path back to the toplevel_dir.
+
+  E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
+  should always produce the empty string, unless the path contains symlinks.
+  """
+  if not path:
+    return path
+  toplevel_dir = '.' if toplevel_dir is None else toplevel_dir
+  return RelativePath(toplevel_dir, os.path.join(toplevel_dir, path))
+
+
 def FixIfRelativePath(path, relative_to):
   # Like RelativePath but returns |path| unchanged if it is absolute.
   if os.path.isabs(path):
@@ -378,8 +392,10 @@ def GetFlavor(params):
     return 'solaris'
   if sys.platform.startswith('freebsd'):
     return 'freebsd'
-  if sys.platform.startswith('dragonfly'):
-    return 'dragonflybsd'
+  if sys.platform.startswith('openbsd'):
+    return 'openbsd'
+  if sys.platform.startswith('aix'):
+    return 'aix'
 
   return 'linux'
 
index 7fbac09..ad6f9a1 100755 (executable)
@@ -56,13 +56,13 @@ class TestGetFlavor(unittest.TestCase):
     self.assertEqual(expected, gyp.common.GetFlavor(param))
 
   def test_platform_default(self):
-    self.assertFlavor('dragonflybsd', 'dragonfly3', {})
-    self.assertFlavor('freebsd'     , 'freebsd9'  , {})
-    self.assertFlavor('freebsd'     , 'freebsd10' , {})
-    self.assertFlavor('solaris'     , 'sunos5'    , {});
-    self.assertFlavor('solaris'     , 'sunos'     , {});
-    self.assertFlavor('linux'       , 'linux2'    , {});
-    self.assertFlavor('linux'       , 'linux3'    , {});
+    self.assertFlavor('freebsd', 'freebsd9' , {})
+    self.assertFlavor('freebsd', 'freebsd10', {})
+    self.assertFlavor('openbsd', 'openbsd5' , {})
+    self.assertFlavor('solaris', 'sunos5'   , {});
+    self.assertFlavor('solaris', 'sunos'    , {});
+    self.assertFlavor('linux'  , 'linux2'   , {});
+    self.assertFlavor('linux'  , 'linux3'   , {});
 
   def test_param(self):
     self.assertFlavor('foobar', 'linux2' , {'flavor': 'foobar'})
index 872ec84..a01ead0 100644 (file)
@@ -19,6 +19,7 @@ import gyp.common
 import gyp.generator.make as make  # Reuse global functions from make backend.
 import os
 import re
+import subprocess
 
 generator_default_variables = {
   'OS': 'android',
@@ -38,7 +39,7 @@ generator_default_variables = {
   'RULE_INPUT_PATH': '$(RULE_SOURCES)',
   'RULE_INPUT_EXT': '$(suffix $<)',
   'RULE_INPUT_NAME': '$(notdir $<)',
-  'CONFIGURATION_NAME': 'NOT_USED_ON_ANDROID',
+  'CONFIGURATION_NAME': '$(GYP_DEFAULT_CONFIGURATION)',
 }
 
 # Make supports multiple toolsets
@@ -131,12 +132,13 @@ class AndroidMkWriter(object):
   def __init__(self, android_top_dir):
     self.android_top_dir = android_top_dir
 
-  def Write(self, qualified_target, base_path, output_filename, spec, configs,
-            part_of_all):
+  def Write(self, qualified_target, relative_target, base_path, output_filename,
+            spec, configs, part_of_all):
     """The main entry point: writes a .mk file for a single target.
 
     Arguments:
       qualified_target: target we're generating
+      relative_target: qualified target name relative to the root
       base_path: path relative to source root we're building in, used to resolve
                  target-relative paths
       output_filename: output .mk file name to write
@@ -150,6 +152,7 @@ class AndroidMkWriter(object):
     self.fp.write(header)
 
     self.qualified_target = qualified_target
+    self.relative_target = relative_target
     self.path = base_path
     self.target = spec['target_name']
     self.type = spec['type']
@@ -248,7 +251,7 @@ class AndroidMkWriter(object):
                    actions)
     """
     for action in actions:
-      name = make.StringToMakefileVariable('%s_%s' % (self.qualified_target,
+      name = make.StringToMakefileVariable('%s_%s' % (self.relative_target,
                                                       action['action_name']))
       self.WriteLn('### Rules for action "%s":' % action['action_name'])
       inputs = action['inputs']
@@ -295,6 +298,15 @@ class AndroidMkWriter(object):
                    '$(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)' %
                    main_output)
 
+      # Android's envsetup.sh adds a number of directories to the path including
+      # the built host binary directory. This causes actions/rules invoked by
+      # gyp to sometimes use these instead of system versions, e.g. bison.
+      # The built host binaries may not be suitable, and can cause errors.
+      # So, we remove them from the PATH using the ANDROID_BUILD_PATHS variable
+      # set by envsetup.
+      self.WriteLn('%s: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))'
+                   % main_output)
+
       for input in inputs:
         assert ' ' not in input, (
             "Spaces in action input filenames not supported (%s)"  % input)
@@ -334,7 +346,7 @@ class AndroidMkWriter(object):
       if len(rule.get('rule_sources', [])) == 0:
         continue
       did_write_rule = True
-      name = make.StringToMakefileVariable('%s_%s' % (self.qualified_target,
+      name = make.StringToMakefileVariable('%s_%s' % (self.relative_target,
                                                       rule['rule_name']))
       self.WriteLn('\n### Generated for rule "%s":' % name)
       self.WriteLn('# "%s":' % rule)
@@ -388,6 +400,10 @@ class AndroidMkWriter(object):
                      '$(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)'
                      % main_output)
 
+        # See explanation in WriteActions.
+        self.WriteLn('%s: export PATH := '
+                     '$(subst $(ANDROID_BUILD_PATHS),,$(PATH))' % main_output)
+
         main_output_deps = self.LocalPathify(rule_source)
         if inputs:
           main_output_deps += ' '
@@ -415,7 +431,7 @@ class AndroidMkWriter(object):
     """
     self.WriteLn('### Generated for copy rule.')
 
-    variable = make.StringToMakefileVariable(self.qualified_target + '_copies')
+    variable = make.StringToMakefileVariable(self.relative_target + '_copies')
     outputs = []
     for copy in copies:
       for path in copy['files']:
@@ -940,30 +956,16 @@ class AndroidMkWriter(object):
     return path
 
 
-def WriteAutoRegenerationRule(params, root_makefile, makefile_name,
-                              build_files):
-  """Write the target to regenerate the Makefile."""
+def PerformBuild(data, configurations, params):
+  # The android backend only supports the default configuration.
   options = params['options']
-  # Sort to avoid non-functional changes to makefile.
-  build_files = sorted([os.path.join('$(LOCAL_PATH)', f) for f in build_files])
-  build_files_args = [gyp.common.RelativePath(filename, options.toplevel_dir)
-                      for filename in params['build_files_arg']]
-  build_files_args = [os.path.join('$(PRIVATE_LOCAL_PATH)', f)
-                      for f in build_files_args]
-  gyp_binary = gyp.common.FixIfRelativePath(params['gyp_binary'],
-                                            options.toplevel_dir)
-  makefile_path = os.path.join('$(LOCAL_PATH)', makefile_name)
-  if not gyp_binary.startswith(os.sep):
-    gyp_binary = os.path.join('.', gyp_binary)
-  root_makefile.write('GYP_FILES := \\\n  %s\n\n' %
-                      '\\\n  '.join(map(Sourceify, build_files)))
-  root_makefile.write('%s: PRIVATE_LOCAL_PATH := $(LOCAL_PATH)\n' %
-                      makefile_path)
-  root_makefile.write('%s: $(GYP_FILES)\n' % makefile_path)
-  root_makefile.write('\techo ACTION Regenerating $@\n\t%s\n\n' %
-      gyp.common.EncodePOSIXShellList([gyp_binary, '-fandroid'] +
-                                      gyp.RegenerateFlags(options) +
-                                      build_files_args))
+  makefile = os.path.abspath(os.path.join(options.toplevel_dir,
+                                          'GypAndroid.mk'))
+  env = dict(os.environ)
+  env['ONE_SHOT_MAKEFILE'] = makefile
+  arguments = ['make', '-C', os.environ['ANDROID_BUILD_TOP'], 'gyp_all_modules']
+  print 'Building: %s' % arguments
+  subprocess.check_call(arguments, env=env)
 
 
 def GenerateOutput(target_list, target_dicts, data, params):
@@ -1030,7 +1032,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
   for qualified_target in target_list:
     build_file, target, toolset = gyp.common.ParseQualifiedTarget(
         qualified_target)
-    build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir))
+    relative_build_file = gyp.common.RelativePath(build_file,
+                                                  options.toplevel_dir)
+    build_files.add(relative_build_file)
     included_files = data[build_file]['included_files']
     for included_file in included_files:
       # The included_files entries are relative to the dir of the build file
@@ -1058,9 +1062,13 @@ def GenerateOutput(target_list, target_dicts, data, params):
                    not int(spec.get('suppress_wildcard', False)))
     if limit_to_target_all and not part_of_all:
       continue
+
+    relative_target = gyp.common.QualifiedTarget(relative_build_file, target,
+                                                 toolset)
     writer = AndroidMkWriter(android_top_dir)
-    android_module = writer.Write(qualified_target, base_path, output_file,
-                                  spec, configs, part_of_all=part_of_all)
+    android_module = writer.Write(qualified_target, relative_target, base_path,
+                                  output_file, spec, configs,
+                                  part_of_all=part_of_all)
     if android_module in android_modules:
       print ('ERROR: Android module names must be unique. The following '
              'targets both generate Android module name %s.\n  %s\n  %s' %
@@ -1077,6 +1085,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
 
   # Some tools need to know the absolute path of the top directory.
   root_makefile.write('GYP_ABS_ANDROID_TOP_DIR := $(shell pwd)\n')
+  root_makefile.write('GYP_DEFAULT_CONFIGURATION := %s\n' %
+                      default_configuration)
 
   # Write out the sorted list of includes.
   root_makefile.write('\n')
@@ -1084,9 +1094,6 @@ def GenerateOutput(target_list, target_dicts, data, params):
     root_makefile.write('include $(LOCAL_PATH)/' + include_file + '\n')
   root_makefile.write('\n')
 
-  if generator_flags.get('auto_regeneration', True):
-    WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)
-
   root_makefile.write(SHARED_FOOTER)
 
   root_makefile.close()
index 0f90b5e..08425da 100644 (file)
@@ -41,11 +41,11 @@ for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
                'CONFIGURATION_NAME']:
   generator_default_variables[unused] = ''
 
-# Include dirs will occasionaly use the SHARED_INTERMEDIATE_DIR variable as
+# Include dirs will occasionally use the SHARED_INTERMEDIATE_DIR variable as
 # part of the path when dealing with generated headers.  This value will be
 # replaced dynamically for each configuration.
 generator_default_variables['SHARED_INTERMEDIATE_DIR'] = \
-    '$SHARED_INTERMEDIATES_DIR'
+    '$SHARED_INTERMEDIATE_DIR'
 
 
 def CalculateVariables(default_variables, params):
@@ -65,7 +65,7 @@ def CalculateGeneratorInputInfo(params):
 
 
 def GetAllIncludeDirectories(target_list, target_dicts,
-                             shared_intermediates_dir, config_name):
+                             shared_intermediate_dirs, config_name):
   """Calculate the set of include directories to be used.
 
   Returns:
@@ -96,17 +96,18 @@ def GetAllIncludeDirectories(target_list, target_dicts,
       # Find standard gyp include dirs.
       if config.has_key('include_dirs'):
         include_dirs = config['include_dirs']
-        for include_dir in include_dirs:
-          include_dir = include_dir.replace('$SHARED_INTERMEDIATES_DIR',
-                                            shared_intermediates_dir)
-          if not os.path.isabs(include_dir):
-            base_dir = os.path.dirname(target_name)
+        for shared_intermediate_dir in shared_intermediate_dirs:
+          for include_dir in include_dirs:
+            include_dir = include_dir.replace('$SHARED_INTERMEDIATE_DIR',
+                                              shared_intermediate_dir)
+            if not os.path.isabs(include_dir):
+              base_dir = os.path.dirname(target_name)
 
-            include_dir = base_dir + '/' + include_dir
-            include_dir = os.path.abspath(include_dir)
+              include_dir = base_dir + '/' + include_dir
+              include_dir = os.path.abspath(include_dir)
 
-          if not include_dir in gyp_includes_set:
-            gyp_includes_set.add(include_dir)
+            if not include_dir in gyp_includes_set:
+              gyp_includes_set.add(include_dir)
 
 
   # Generate a list that has all the include dirs.
@@ -234,7 +235,10 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
                            config_name)
 
   toplevel_build = os.path.join(options.toplevel_dir, build_dir)
-  shared_intermediate_dir = os.path.join(toplevel_build, 'obj', 'gen')
+  # Ninja uses out/Debug/gen while make uses out/Debug/obj/gen as the
+  # SHARED_INTERMEDIATE_DIR. Include both possible locations.
+  shared_intermediate_dirs = [os.path.join(toplevel_build, 'obj', 'gen'),
+                              os.path.join(toplevel_build, 'gen')]
 
   if not os.path.exists(toplevel_build):
     os.makedirs(toplevel_build)
@@ -246,7 +250,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
   eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File',
                    'GNU C++', 'GNU C', 'Assembly']
   include_dirs = GetAllIncludeDirectories(target_list, target_dicts,
-                                          shared_intermediate_dir, config_name)
+                                          shared_intermediate_dirs, config_name)
   WriteIncludePaths(out, eclipse_langs, include_dirs)
   defines = GetAllDefines(target_list, target_dicts, data, config_name)
   WriteMacros(out, eclipse_langs, defines)
index bcc2cc6..9806c64 100644 (file)
@@ -259,7 +259,7 @@ all_deps :=
 #   export LINK=g++
 #
 # This will allow make to invoke N linker processes as specified in -jN.
-LINK ?= %(flock)s $(builddir)/linker.lock $(CXX)
+LINK ?= %(flock)s $(builddir)/linker.lock $(CXX.target)
 
 CC.target ?= %(CC.target)s
 CFLAGS.target ?= $(CFLAGS)
@@ -395,15 +395,14 @@ command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\\
 #   $| -- order-only dependencies
 prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))
 
-# Helper that executes all postbuilds, and deletes the output file when done
-# if any of the postbuilds failed.
+# Helper that executes all postbuilds until one fails.
 define do_postbuilds
   @E=0;\\
   for p in $(POSTBUILDS); do\\
     eval $$p;\\
-    F=$$?;\\
-    if [ $$F -ne 0 ]; then\\
-      E=$$F;\\
+    E=$$?;\\
+    if [ $$E -ne 0 ]; then\\
+      break;\\
     fi;\\
   done;\\
   if [ $$E -ne 0 ]; then\\
@@ -619,21 +618,6 @@ def QuoteSpaces(s, quote=r'\ '):
   return s.replace(' ', quote)
 
 
-def InvertRelativePath(path):
-  """Given a relative path like foo/bar, return the inverse relative path:
-  the path from the relative path back to the origin dir.
-
-  E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
-  should always produce the empty string."""
-
-  if not path:
-    return path
-  # Only need to handle relative paths into subdirectories for now.
-  assert '..' not in path, path
-  depth = len(path.split(os.path.sep))
-  return os.path.sep.join(['..'] * depth)
-
-
 # Map from qualified target to path to output.
 target_outputs = {}
 # Map from qualified target to any linkable output.  A subset
@@ -1417,7 +1401,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
               lambda p: Sourceify(self.Absolutify(p)))
 
           # TARGET_POSTBUILDS_$(BUILDTYPE) is added to postbuilds later on.
-          gyp_to_build = InvertRelativePath(self.path)
+          gyp_to_build = gyp.common.InvertRelativePath(self.path)
           target_postbuild = self.xcode_settings.GetTargetPostbuilds(
               configname,
               QuoteSpaces(os.path.normpath(os.path.join(gyp_to_build,
@@ -1541,7 +1525,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
       for link_dep in link_deps:
         assert ' ' not in link_dep, (
             "Spaces in alink input filenames not supported (%s)"  % link_dep)
-      if (self.flavor not in ('mac', 'win') and not
+      if (self.flavor not in ('mac', 'openbsd', 'win') and not
           self.is_standalone_static_library):
         self.WriteDoCmd([self.output_binary], link_deps, 'alink_thin',
                         part_of_all, postbuilds=postbuilds)
@@ -2000,7 +1984,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
         'flock_index': 2,
         'extra_commands': SHARED_HEADER_SUN_COMMANDS,
     })
-  elif flavor == 'freebsd' or flavor == 'dragonflybsd':
+  elif flavor == 'freebsd':
+    # Note: OpenBSD has sysutils/flock. lockf seems to be FreeBSD specific.
     header_params.update({
         'flock': 'lockf',
     })
@@ -2018,14 +2003,22 @@ def GenerateOutput(target_list, target_dicts, data, params):
 
   build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
   make_global_settings_array = data[build_file].get('make_global_settings', [])
+  wrappers = {}
+  wrappers['LINK'] = '%s $(builddir)/linker.lock' % flock_command
+  for key, value in make_global_settings_array:
+    if key.endswith('_wrapper'):
+      wrappers[key[:-len('_wrapper')]] = '$(abspath %s)' % value
   make_global_settings = ''
   for key, value in make_global_settings_array:
+    if re.match('.*_wrapper', key):
+      continue
     if value[0] != '$':
       value = '$(abspath %s)' % value
-    if key == 'LINK':
-      make_global_settings += ('%s ?= %s $(builddir)/linker.lock %s\n' %
-                               (key, flock_command, value))
-    elif key in ('CC', 'CC.host', 'CXX', 'CXX.host'):
+    wrapper = wrappers.get(key)
+    if wrapper:
+      value = '%s %s' % (wrapper, value)
+      del wrappers[key]
+    if key in ('CC', 'CC.host', 'CXX', 'CXX.host'):
       make_global_settings += (
           'ifneq (,$(filter $(origin %s), undefined default))\n' % key)
       # Let gyp-time envvars win over global settings.
@@ -2035,6 +2028,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
       make_global_settings += 'endif\n'
     else:
       make_global_settings += '%s ?= %s\n' % (key, value)
+  # TODO(ukai): define cmd when only wrapper is specified in
+  # make_global_settings.
+
   header_params['make_global_settings'] = make_global_settings
 
   ensure_directory_exists(makefile_path)
index 47cbd36..51acf2e 100644 (file)
@@ -17,6 +17,7 @@ import gyp.MSVSProject as MSVSProject
 import gyp.MSVSSettings as MSVSSettings
 import gyp.MSVSToolFile as MSVSToolFile
 import gyp.MSVSUserFile as MSVSUserFile
+import gyp.MSVSUtil as MSVSUtil
 import gyp.MSVSVersion as MSVSVersion
 from gyp.common import GypError
 
@@ -63,6 +64,7 @@ generator_additional_path_sections = [
 generator_additional_non_configuration_keys = [
     'msvs_cygwin_dirs',
     'msvs_cygwin_shell',
+    'msvs_large_pdb',
     'msvs_shard',
 ]
 
@@ -204,6 +206,10 @@ def _ConvertSourcesToFilterHierarchy(sources, prefix=None, excluded=None,
 
 def _ToolAppend(tools, tool_name, setting, value, only_if_unset=False):
   if not value: return
+  _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset)
+
+
+def _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset=False):
   # TODO(bradnelson): ugly hack, fix this more generally!!!
   if 'Directories' in setting or 'Dependencies' in setting:
     if type(value) == str:
@@ -232,7 +238,7 @@ def _ConfigPlatform(config_data):
 
 def _ConfigBaseName(config_name, platform_name):
   if config_name.endswith('_' + platform_name):
-    return config_name[0:-len(platform_name)-1]
+    return config_name[0:-len(platform_name) - 1]
   else:
     return config_name
 
@@ -270,7 +276,7 @@ def _BuildCommandLineForRuleRaw(spec, cmd, cygwin_shell, has_input_path,
                               '`cygpath -m "${INPUTPATH}"`')
                     for i in direct_cmd]
     direct_cmd = ['\\"%s\\"' % i.replace('"', '\\\\\\"') for i in direct_cmd]
-    #direct_cmd = gyp.common.EncodePOSIXShellList(direct_cmd)
+    # direct_cmd = gyp.common.EncodePOSIXShellList(direct_cmd)
     direct_cmd = ' '.join(direct_cmd)
     # TODO(quote):  regularize quoting path names throughout the module
     cmd = ''
@@ -306,7 +312,7 @@ def _BuildCommandLineForRuleRaw(spec, cmd, cygwin_shell, has_input_path,
     # If the argument starts with a slash or dash, it's probably a command line
     # switch
     arguments = [i if (i[:1] in "/-") else _FixPath(i) for i in cmd[1:]]
-    arguments = [i.replace('$(InputDir)','%INPUTDIR%') for i in arguments]
+    arguments = [i.replace('$(InputDir)', '%INPUTDIR%') for i in arguments]
     arguments = [MSVSSettings.FixVCMacroSlashes(i) for i in arguments]
     if quote_cmd:
       # Support a mode for using cmd directly.
@@ -720,7 +726,7 @@ def _EscapeCommandLineArgumentForMSBuild(s):
   """Escapes a Windows command-line argument for use by MSBuild."""
 
   def _Replace(match):
-    return (len(match.group(1))/2*4)*'\\' + '\\"'
+    return (len(match.group(1)) / 2 * 4) * '\\' + '\\"'
 
   # Escape all quotes so that they are interpreted literally.
   s = quote_replacer_regex2.sub(_Replace, s)
@@ -1001,12 +1007,12 @@ def _GetMSVSConfigurationType(spec, build_file):
         }[spec['type']]
   except KeyError:
     if spec.get('type'):
-      raise Exception('Target type %s is not a valid target type for '
-                      'target %s in %s.' %
-                      (spec['type'], spec['target_name'], build_file))
+      raise GypError('Target type %s is not a valid target type for '
+                     'target %s in %s.' %
+                     (spec['type'], spec['target_name'], build_file))
     else:
-      raise Exception('Missing type field for target %s in %s.' %
-                      (spec['target_name'], build_file))
+      raise GypError('Missing type field for target %s in %s.' %
+                     (spec['target_name'], build_file))
   return config_type
 
 
@@ -1041,6 +1047,10 @@ def _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config):
   # Add in user specified msvs_settings.
   msvs_settings = config.get('msvs_settings', {})
   MSVSSettings.ValidateMSVSSettings(msvs_settings)
+
+  # Prevent default library inheritance from the environment.
+  _ToolAppend(tools, 'VCLinkerTool', 'AdditionalDependencies', ['$(NOINHERIT)'])
+
   for tool in msvs_settings:
     settings = config['msvs_settings'][tool]
     for setting in settings:
@@ -1663,7 +1673,7 @@ def _CreateProjectObjects(target_list, target_dicts, options, msvs_version):
   for qualified_target in target_list:
     spec = target_dicts[qualified_target]
     if spec['toolset'] != 'target':
-      raise Exception(
+      raise GypError(
           'Multiple toolsets not supported in msvs build (target %s)' %
           qualified_target)
     proj_path, fixpath_prefix = _GetPathOfProject(qualified_target, spec,
@@ -1718,74 +1728,6 @@ def CalculateVariables(default_variables, params):
     default_variables['MSVS_OS_BITS'] = 32
 
 
-def _ShardName(name, number):
-  """Add a shard number to the end of a target.
-
-  Arguments:
-    name: name of the target (foo#target)
-    number: shard number
-  Returns:
-    Target name with shard added (foo_1#target)
-  """
-  parts = name.rsplit('#', 1)
-  parts[0] = '%s_%d' % (parts[0], number)
-  return '#'.join(parts)
-
-
-def _ShardTargets(target_list, target_dicts):
-  """Shard some targets apart to work around the linkers limits.
-
-  Arguments:
-    target_list: List of target pairs: 'base/base.gyp:base'.
-    target_dicts: Dict of target properties keyed on target pair.
-  Returns:
-    Tuple of the new sharded versions of the inputs.
-  """
-  # Gather the targets to shard, and how many pieces.
-  targets_to_shard = {}
-  for t in target_dicts:
-    shards = int(target_dicts[t].get('msvs_shard', 0))
-    if shards:
-      targets_to_shard[t] = shards
-  # Shard target_list.
-  new_target_list = []
-  for t in target_list:
-    if t in targets_to_shard:
-      for i in range(targets_to_shard[t]):
-        new_target_list.append(_ShardName(t, i))
-    else:
-      new_target_list.append(t)
-  # Shard target_dict.
-  new_target_dicts = {}
-  for t in target_dicts:
-    if t in targets_to_shard:
-      for i in range(targets_to_shard[t]):
-        name = _ShardName(t, i)
-        new_target_dicts[name] = copy.copy(target_dicts[t])
-        new_target_dicts[name]['target_name'] = _ShardName(
-             new_target_dicts[name]['target_name'], i)
-        sources = new_target_dicts[name].get('sources', [])
-        new_sources = []
-        for pos in range(i, len(sources), targets_to_shard[t]):
-          new_sources.append(sources[pos])
-        new_target_dicts[name]['sources'] = new_sources
-    else:
-      new_target_dicts[t] = target_dicts[t]
-  # Shard dependencies.
-  for t in new_target_dicts:
-    dependencies = copy.copy(new_target_dicts[t].get('dependencies', []))
-    new_dependencies = []
-    for d in dependencies:
-      if d in targets_to_shard:
-        for i in range(targets_to_shard[d]):
-          new_dependencies.append(_ShardName(d, i))
-      else:
-        new_dependencies.append(d)
-    new_target_dicts[t]['dependencies'] = new_dependencies
-
-  return (new_target_list, new_target_dicts)
-
-
 def PerformBuild(data, configurations, params):
   options = params['options']
   msvs_version = params['msvs_version']
@@ -1825,7 +1767,12 @@ def GenerateOutput(target_list, target_dicts, data, params):
   generator_flags = params.get('generator_flags', {})
 
   # Optionally shard targets marked with 'msvs_shard': SHARD_COUNT.
-  (target_list, target_dicts) = _ShardTargets(target_list, target_dicts)
+  (target_list, target_dicts) = MSVSUtil.ShardTargets(target_list, target_dicts)
+
+  # Optionally use the large PDB workaround for targets marked with
+  # 'msvs_large_pdb': 1.
+  (target_list, target_dicts) = MSVSUtil.InsertLargePdbShims(
+        target_list, target_dicts, generator_default_variables)
 
   # Prepare the set of configurations.
   configs = set()
@@ -1872,9 +1819,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
     error_message = "Missing input files:\n" + \
                     '\n'.join(set(missing_sources))
     if generator_flags.get('msvs_error_on_missing_sources', False):
-      raise Exception(error_message)
+      raise GypError(error_message)
     else:
-      print >>sys.stdout, "Warning: " + error_message
+      print >> sys.stdout, "Warning: " + error_message
 
 
 def _GenerateMSBuildFiltersFile(filters_path, source_files,
@@ -2809,8 +2756,10 @@ def _FinalizeMSBuildSettings(spec, configuration):
               'AdditionalIncludeDirectories', include_dirs)
   _ToolAppend(msbuild_settings, 'ResourceCompile',
               'AdditionalIncludeDirectories', resource_include_dirs)
-  # Add in libraries.
-  _ToolAppend(msbuild_settings, 'Link', 'AdditionalDependencies', libraries)
+  # Add in libraries, note that even for empty libraries, we want this
+  # set, to prevent inheriting default libraries from the enviroment.
+  _ToolSetOrAppend(msbuild_settings, 'Link', 'AdditionalDependencies',
+                  libraries)
   if out_file:
     _ToolAppend(msbuild_settings, msbuild_tool, 'OutputFile', out_file,
                 only_if_unset=True)
@@ -2844,8 +2793,7 @@ def _GetValueFormattedForMSBuild(tool_name, name, value):
   if type(value) == list:
     # For some settings, VS2010 does not automatically extends the settings
     # TODO(jeanluc) Is this what we want?
-    if name in ['AdditionalDependencies',
-                'AdditionalIncludeDirectories',
+    if name in ['AdditionalIncludeDirectories',
                 'AdditionalLibraryDirectories',
                 'AdditionalOptions',
                 'DelayLoadDLLs',
index fa6bd86..c6bceaf 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 Google Inc. All rights reserved.
+# 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.
 
@@ -13,7 +13,7 @@ import sys
 import gyp
 import gyp.common
 import gyp.msvs_emulation
-import gyp.MSVSVersion
+import gyp.MSVSUtil as MSVSUtil
 import gyp.xcode_emulation
 
 from gyp.common import GetEnvironFallback
@@ -97,21 +97,6 @@ def Define(d, flavor):
   return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
 
 
-def InvertRelativePath(path):
-  """Given a relative path like foo/bar, return the inverse relative path:
-  the path from the relative path back to the origin dir.
-
-  E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
-  should always produce the empty string."""
-
-  if not path:
-    return path
-  # Only need to handle relative paths into subdirectories for now.
-  assert '..' not in path, path
-  depth = len(path.split(os.path.sep))
-  return os.path.sep.join(['..'] * depth)
-
-
 class Target:
   """Target represents the paths used within a single gyp target.
 
@@ -218,12 +203,12 @@ class Target:
 
 class NinjaWriter:
   def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
-               output_file, flavor, abs_build_dir=None):
+               output_file, 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
     build_dir: path from source root to build output
-    abs_build_dir: absolute path to the build directory
+    toplevel_dir: path to the toplevel directory
     """
 
     self.qualified_target = qualified_target
@@ -232,7 +217,10 @@ class NinjaWriter:
     self.build_dir = build_dir
     self.ninja = ninja_syntax.Writer(output_file)
     self.flavor = flavor
-    self.abs_build_dir = abs_build_dir
+    self.abs_build_dir = None
+    if toplevel_dir is not None:
+      self.abs_build_dir = os.path.abspath(os.path.join(toplevel_dir,
+                                                        build_dir))
     self.obj_ext = '.obj' if flavor == 'win' else '.o'
     if flavor == 'win':
       # See docstring of msvs_emulation.GenerateEnvironmentFiles().
@@ -241,9 +229,11 @@ class NinjaWriter:
         self.win_env[arch] = 'environment.' + arch
 
     # Relative path from build output dir to base dir.
-    self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir)
+    build_to_top = gyp.common.InvertRelativePath(build_dir, toplevel_dir)
+    self.build_to_base = os.path.join(build_to_top, base_dir)
     # Relative path from base dir to build dir.
-    self.base_to_build = os.path.join(InvertRelativePath(base_dir), build_dir)
+    base_to_top = gyp.common.InvertRelativePath(base_dir, toplevel_dir)
+    self.base_to_build = os.path.join(base_to_top, build_dir)
 
   def ExpandSpecial(self, path, product_dir=None):
     """Expand specials like $!PRODUCT_DIR in |path|.
@@ -428,7 +418,8 @@ class NinjaWriter:
         gyp.msvs_emulation.VerifyMissingSources(
             sources, self.abs_build_dir, generator_flags, self.GypPathToNinja)
         pch = gyp.msvs_emulation.PrecompiledHeader(
-            self.msvs_settings, config_name, self.GypPathToNinja)
+            self.msvs_settings, config_name, self.GypPathToNinja,
+            self.GypPathToUniqueOutput, self.obj_ext)
       else:
         pch = gyp.xcode_emulation.MacPrefixHeader(
             self.xcode_settings, self.GypPathToNinja,
@@ -743,7 +734,15 @@ class NinjaWriter:
       cflags_c = self.msvs_settings.GetCflagsC(config_name)
       cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
       extra_defines = self.msvs_settings.GetComputedDefines(config_name)
-      self.WriteVariableList('pdbname', [self.name + '.pdb'])
+      pdbpath = self.msvs_settings.GetCompilerPdbName(
+          config_name, self.ExpandSpecial)
+      if not pdbpath:
+        obj = 'obj'
+        if self.toolset != 'target':
+          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])
     else:
       cflags = config.get('cflags', [])
@@ -824,9 +823,14 @@ class NinjaWriter:
       if not case_sensitive_filesystem:
         output = output.lower()
       implicit = precompiled_header.GetObjDependencies([input], [output])
+      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,
                        implicit=[gch for _, _, gch in implicit],
-                       order_only=predepends)
+                       order_only=predepends, variables=variables)
       outputs.append(output)
 
     self.WritePchTargets(pch_commands)
@@ -848,8 +852,6 @@ class NinjaWriter:
       }[lang]
 
       map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
-      if self.flavor == 'win':
-        map.update({'c': 'cc_pch', 'cc': 'cxx_pch'})
       cmd = map.get(lang)
       self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)])
 
@@ -903,16 +905,12 @@ class NinjaWriter:
       extra_bindings.append(('postbuilds',
                              self.GetPostbuildCommand(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)
     elif self.flavor == 'win':
-      libflags = self.msvs_settings.GetLibFlags(config_name,
-                                                self.GypPathToNinja)
-      self.WriteVariableList(
-          'libflags', gyp.common.uniquer(map(self.ExpandSpecial, libflags)))
-      is_executable = spec['type'] == 'executable'
       manifest_name = self.GypPathToUniqueOutput(
           self.ComputeOutputFileName(spec))
       ldflags, manifest_files = self.msvs_settings.GetLdflags(config_name,
@@ -920,6 +918,9 @@ class NinjaWriter:
       self.WriteVariableList('manifests', manifest_files)
     else:
       ldflags = config.get('ldflags', [])
+      if is_executable and len(solibs):
+        ldflags.append('-Wl,-rpath=\$$ORIGIN/lib/')
+        ldflags.append('-Wl,-rpath-link=lib/')
     self.WriteVariableList('ldflags',
                            gyp.common.uniquer(map(self.ExpandSpecial,
                                                   ldflags)))
@@ -975,6 +976,10 @@ class NinjaWriter:
         self.ninja.build(self.target.binary, 'alink_thin', link_deps,
                          order_only=compile_deps, variables=variables)
       else:
+        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)
     else:
@@ -1046,10 +1051,9 @@ class NinjaWriter:
     env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
     # G will be non-null if any postbuild fails. Run all postbuilds in a
     # subshell.
-    commands = env + ' (F=0; ' + \
-        ' '.join([ninja_syntax.escape(command) + ' || F=$$?;'
-                                 for command in postbuilds])
-    command_string = (commands + ' exit $$F); G=$$?; '
+    commands = env + ' (' + \
+        ' && '.join([ninja_syntax.escape(command) for command in postbuilds])
+    command_string = (commands + '); G=$$?; '
                       # Remove the final output if any postbuild failed.
                       '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
     if is_command_start:
@@ -1315,6 +1319,13 @@ def OpenOutput(path, mode='w'):
   return open(path, mode)
 
 
+def CommandWithWrapper(cmd, wrappers, prog):
+  wrapper = wrappers.get(cmd, '')
+  if wrapper:
+    return wrapper + ' ' + prog
+  return prog
+
+
 def GenerateOutputForConfig(target_list, target_dicts, data, params,
                             config_name):
   options = params['options']
@@ -1372,7 +1383,14 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
 
   build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
   make_global_settings = data[build_file].get('make_global_settings', [])
-  build_to_root = InvertRelativePath(build_dir)
+  build_to_root = gyp.common.InvertRelativePath(build_dir,
+                                                options.toplevel_dir)
+  flock = 'flock'
+  if flavor == 'mac':
+    flock = './gyp-mac-tool flock'
+  wrappers = {}
+  if flavor != 'win':
+    wrappers['LINK'] = flock + ' linker.lock'
   for key, value in make_global_settings:
     if key == 'CC':
       cc = os.path.join(build_to_root, value)
@@ -1388,14 +1406,13 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
       cxx_host_global_setting = value
     if key == 'LD.host':
       ld_host = os.path.join(build_to_root, value)
+    if key.endswith('_wrapper'):
+      wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
 
-  flock = 'flock'
-  if flavor == 'mac':
-    flock = './gyp-mac-tool flock'
   cc = GetEnvironFallback(['CC_target', 'CC'], cc)
-  master_ninja.variable('cc', cc)
+  master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
   cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
-  master_ninja.variable('cxx', cxx)
+  master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx))
   ld = GetEnvironFallback(['LD_target', 'LD'], ld)
 
   if not cc_host:
@@ -1412,7 +1429,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
     master_ninja.variable('mt', 'mt.exe')
     master_ninja.variable('use_dep_database', '1')
   else:
-    master_ninja.variable('ld', flock + ' linker.lock ' + ld)
+    master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
     master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
 
   master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
@@ -1426,12 +1443,15 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
     cc_host = cc_host_global_setting.replace('$(CC)', cc)
   if '$(CXX)' in cxx_host and cxx_host_global_setting:
     cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
-  master_ninja.variable('cc_host', cc_host)
-  master_ninja.variable('cxx_host', cxx_host)
+  master_ninja.variable('cc_host',
+                        CommandWithWrapper('CC.host', wrappers, cc_host))
+  master_ninja.variable('cxx_host',
+                        CommandWithWrapper('CXX.host', wrappers, cxx_host))
   if flavor == 'win':
     master_ninja.variable('ld_host', ld_host)
   else:
-    master_ninja.variable('ld_host', flock + ' linker.lock ' + ld_host)
+    master_ninja.variable('ld_host', CommandWithWrapper(
+        'LINK', wrappers, ld_host))
 
   master_ninja.newline()
 
@@ -1454,45 +1474,25 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
               '$cflags_pch_cc -c $in -o $out'),
       depfile='$out.d')
   else:
-    # Template for compile commands mostly shared between compiling files
-    # and generating PCH. In the case of PCH, the "output" is specified by /Fp
-    # rather than /Fo (for object files), but we still need to specify an /Fo
-    # when compiling PCH.
-    cc_template = ('ninja -t msvc -r . -o $out -e $arch '
+    cc_command = ('ninja -t msvc -o $out -e $arch '
+                  '-- '
+                  '$cc /nologo /showIncludes /FC '
+                  '@$out.rsp /c $in /Fo$out /Fd$pdbname ')
+    cxx_command = ('ninja -t msvc -o $out -e $arch '
                    '-- '
-                   '$cc /nologo /showIncludes /FC '
-                   '@$out.rsp '
-                   '$cflags_pch_c /c $in %(outspec)s /Fd$pdbname ')
-    cxx_template = ('ninja -t msvc -r . -o $out -e $arch '
-                    '-- '
-                    '$cxx /nologo /showIncludes /FC '
-                    '@$out.rsp '
-                    '$cflags_pch_cc /c $in %(outspec)s $pchobj /Fd$pdbname ')
+                   '$cxx /nologo /showIncludes /FC '
+                   '@$out.rsp /c $in /Fo$out /Fd$pdbname ')
     master_ninja.rule(
       'cc',
       description='CC $out',
-      command=cc_template % {'outspec': '/Fo$out'},
-      depfile='$out.d',
-      rspfile='$out.rsp',
-      rspfile_content='$defines $includes $cflags $cflags_c')
-    master_ninja.rule(
-      'cc_pch',
-      description='CC PCH $out',
-      command=cc_template % {'outspec': '/Fp$out /Fo$out.obj'},
+      command=cc_command,
       depfile='$out.d',
       rspfile='$out.rsp',
       rspfile_content='$defines $includes $cflags $cflags_c')
     master_ninja.rule(
       'cxx',
       description='CXX $out',
-      command=cxx_template % {'outspec': '/Fo$out'},
-      depfile='$out.d',
-      rspfile='$out.rsp',
-      rspfile_content='$defines $includes $cflags $cflags_cc')
-    master_ninja.rule(
-      'cxx_pch',
-      description='CXX PCH $out',
-      command=cxx_template % {'outspec': '/Fp$out /Fo$out.obj'},
+      command=cxx_command,
       depfile='$out.d',
       rspfile='$out.rsp',
       rspfile_content='$defines $includes $cflags $cflags_cc')
@@ -1559,7 +1559,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
     master_ninja.rule(
       'link',
       description='LINK $out',
-      command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib '
+      command=('$ld $ldflags -o $out '
                '-Wl,--start-group $in $solibs -Wl,--end-group $libs'))
   elif flavor == 'win':
     master_ninja.rule(
@@ -1575,6 +1575,9 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
               '$ld /nologo $implibflag /DLL /OUT:$dll '
               '/PDB:$dll.pdb @$dll.rsp' % sys.executable)
     dllcmd += (' && %s gyp-win-tool manifest-wrapper $arch '
+               'cmd /c if exist $dll.manifest del $dll.manifest' %
+               sys.executable)
+    dllcmd += (' && %s gyp-win-tool manifest-wrapper $arch '
                '$mt -nologo -manifest $manifests -out:$dll.manifest' %
                sys.executable)
     master_ninja.rule('solink', description=dlldesc, command=dllcmd,
@@ -1593,8 +1596,10 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
         command=('%s gyp-win-tool link-wrapper $arch '
                  '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp && '
                  '%s gyp-win-tool manifest-wrapper $arch '
+                 'cmd /c if exist $out.manifest del $out.manifest && '
+                 '%s gyp-win-tool manifest-wrapper $arch '
                  '$mt -nologo -manifest $manifests -out:$out.manifest' %
-                 (sys.executable, sys.executable)),
+                 (sys.executable, sys.executable, sys.executable)),
         rspfile='$out.rsp',
         rspfile_content='$in_newline $libs $ldflags')
   else:
@@ -1729,7 +1734,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
     abs_build_dir = os.path.abspath(toplevel_build)
     writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
                          OpenOutput(os.path.join(toplevel_build, output_file)),
-                         flavor, abs_build_dir=abs_build_dir)
+                         flavor, toplevel_dir=options.toplevel_dir)
     master_ninja.subninja(output_file)
 
     target = writer.WriteSpec(
@@ -1777,6 +1782,11 @@ def CallGenerateOutputForConfig(arglist):
 
 def GenerateOutput(target_list, target_dicts, data, params):
   user_config = params.get('generator_flags', {}).get('config', None)
+  if gyp.common.GetFlavor(params) == 'win':
+    target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
+    target_list, target_dicts = MSVSUtil.InsertLargePdbShims(
+        target_list, target_dicts, generator_default_variables)
+
   if user_config:
     GenerateOutputForConfig(target_list, target_dicts, data, params,
                             user_config)
index 7b21bae..ca3b01e 100644 (file)
@@ -1110,20 +1110,29 @@ exit 1
         AddHeaderToTarget(header, pbxp, xct, True)
 
     # Add "copies".
+    pbxcp_dict = {}
     for copy_group in spec.get('copies', []):
-      pbxcp = gyp.xcodeproj_file.PBXCopyFilesBuildPhase({
-            'name': 'Copy to ' + copy_group['destination']
-          },
-          parent=xct)
       dest = copy_group['destination']
       if dest[0] not in ('/', '$'):
         # Relative paths are relative to $(SRCROOT).
         dest = '$(SRCROOT)/' + dest
-      pbxcp.SetDestination(dest)
 
-      # TODO(mark): The usual comment about this knowing too much about
-      # gyp.xcodeproj_file internals applies.
-      xct._properties['buildPhases'].insert(prebuild_index, pbxcp)
+      # Coalesce multiple "copies" sections in the same target with the same
+      # "destination" property into the same PBXCopyFilesBuildPhase, otherwise
+      # they'll wind up with ID collisions.
+      pbxcp = pbxcp_dict.get(dest, None)
+      if pbxcp is None:
+        pbxcp = gyp.xcodeproj_file.PBXCopyFilesBuildPhase({
+              'name': 'Copy to ' + copy_group['destination']
+            },
+            parent=xct)
+        pbxcp.SetDestination(dest)
+
+        # TODO(mark): The usual comment about this knowing too much about
+        # gyp.xcodeproj_file internals applies.
+        xct._properties['buildPhases'].insert(prebuild_index, pbxcp)
+
+        pbxcp_dict[dest] = pbxcp
 
       for file in copy_group['files']:
         pbxcp.AddFile(file)
index 6523667..eca0eb9 100644 (file)
@@ -46,21 +46,16 @@ base_path_sections = [
 ]
 path_sections = []
 
+is_path_section_charset = set('=+?!')
+is_path_section_match_re = re.compile('_(dir|file|path)s?$')
 
 def IsPathSection(section):
   # If section ends in one of these characters, it's applied to a section
   # without the trailing characters.  '/' is notably absent from this list,
   # because there's no way for a regular expression to be treated as a path.
-  while section[-1:] in ('=', '+', '?', '!'):
-    section = section[0:-1]
-
-  if section in path_sections or \
-     section.endswith('_dir') or section.endswith('_dirs') or \
-     section.endswith('_file') or section.endswith('_files') or \
-     section.endswith('_path') or section.endswith('_paths'):
-    return True
-  return False
-
+  while section[-1:] in is_path_section_charset:
+    section = section[:-1]
+  return section in path_sections or is_path_section_match_re.search(section)
 
 # base_non_configuraiton_keys is a list of key names that belong in the target
 # itself and should not be propagated into its configurations.  It is merged
@@ -269,7 +264,7 @@ def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data,
       aux_data[subdict_path]['included'] = []
     aux_data[subdict_path]['included'].append(include)
 
-    gyp.DebugOutput(gyp.DEBUG_INCLUDES, "Loading Included File: '%s'" % include)
+    gyp.DebugOutput(gyp.DEBUG_INCLUDES, "Loading Included File: '%s'", include)
 
     MergeDicts(subdict,
                LoadOneBuildFile(include, data, aux_data, variables, None,
@@ -359,7 +354,7 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
   data['target_build_files'].add(build_file_path)
 
   gyp.DebugOutput(gyp.DEBUG_INCLUDES,
-                  "Loading Target Build File '%s'" % build_file_path)
+                  "Loading Target Build File '%s'", build_file_path)
 
   build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables,
                                      includes, True, check)
@@ -494,7 +489,7 @@ def CallLoadTargetBuildFile(global_flags,
             aux_data_out,
             dependencies)
   except Exception, e:
-    print "Exception: ", e
+    print >>sys.stderr, 'Exception: ', e
     return None
 
 
@@ -569,6 +564,12 @@ def LoadTargetBuildFileParallel(build_file_path, data, aux_data,
     parallel_state.condition.acquire()
     while parallel_state.dependencies or parallel_state.pending:
       if parallel_state.error:
+        print >>sys.stderr, (
+            '\n'
+            'Note: an error occurred while running gyp using multiprocessing.\n'
+            'For more verbose output, set GYP_PARALLEL=0 in your environment.\n'
+            'If the error only occurs when GYP_PARALLEL=1, '
+            'please report a bug!')
         break
       if not parallel_state.dependencies:
         parallel_state.condition.wait()
@@ -608,32 +609,27 @@ def LoadTargetBuildFileParallel(build_file_path, data, aux_data,
 # the input is something like "<(foo <(bar)) blah", then it would
 # return (1, 13), indicating the entire string except for the leading
 # "<" and trailing " blah".
-def FindEnclosingBracketGroup(input):
-  brackets = { '}': '{',
-               ']': '[',
-               ')': '(', }
+LBRACKETS= set('{[(')
+BRACKETS = {'}': '{', ']': '[', ')': '('}
+def FindEnclosingBracketGroup(input_str):
   stack = []
-  count = 0
   start = -1
-  for char in input:
-    if char in brackets.values():
+  for index, char in enumerate(input_str):
+    if char in LBRACKETS:
       stack.append(char)
       if start == -1:
-        start = count
-    if char in brackets.keys():
-      try:
-        last_bracket = stack.pop()
-      except IndexError:
+        start = index
+    elif char in BRACKETS:
+      if not stack:
         return (-1, -1)
-      if last_bracket != brackets[char]:
+      if stack.pop() != BRACKETS[char]:
         return (-1, -1)
-      if len(stack) == 0:
-        return (start, count + 1)
-    count = count + 1
+      if not stack:
+        return (start, index + 1)
   return (-1, -1)
 
 
-canonical_int_re = re.compile('^(0|-?[1-9][0-9]*)$')
+canonical_int_re = re.compile('(0|-?[1-9][0-9]*)$')
 
 
 def IsStrCanonicalInt(string):
@@ -641,10 +637,7 @@ def IsStrCanonicalInt(string):
 
   The canonical form is such that str(int(string)) == string.
   """
-  if not isinstance(string, str) or not canonical_int_re.match(string):
-    return False
-
-  return True
+  return isinstance(string, str) and canonical_int_re.match(string)
 
 
 # This matches things like "<(asdf)", "<!(cmd)", "<!@(cmd)", "<|(list)",
@@ -713,7 +706,7 @@ def ExpandVariables(input, phase, variables, build_file):
 
   # Get the entire list of matches as a list of MatchObject instances.
   # (using findall here would return strings instead of MatchObjects).
-  matches = [match for match in variable_re.finditer(input_str)]
+  matches = list(variable_re.finditer(input_str))
   if not matches:
     return input_str
 
@@ -725,8 +718,7 @@ def ExpandVariables(input, phase, variables, build_file):
   matches.reverse()
   for match_group in matches:
     match = match_group.groupdict()
-    gyp.DebugOutput(gyp.DEBUG_VARIABLES,
-                    "Matches: %s" % repr(match))
+    gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Matches: %r", match)
     # match['replace'] is the substring to look for, match['type']
     # is the character code for the replacement type (< > <! >! <| >| <@
     # >@ <!@ >!@), match['is_array'] contains a '[' for command
@@ -839,8 +831,8 @@ def ExpandVariables(input, phase, variables, build_file):
       cached_value = cached_command_results.get(cache_key, None)
       if cached_value is None:
         gyp.DebugOutput(gyp.DEBUG_VARIABLES,
-                        "Executing command '%s' in directory '%s'" %
-                        (contents,build_file_dir))
+                        "Executing command '%s' in directory '%s'",
+                        contents, build_file_dir)
 
         replacement = ''
 
@@ -852,12 +844,17 @@ def ExpandVariables(input, phase, variables, build_file):
           # <!(python modulename param eters). Do this in |build_file_dir|.
           oldwd = os.getcwd()  # Python doesn't like os.open('.'): no fchdir.
           os.chdir(build_file_dir)
-
-          parsed_contents = shlex.split(contents)
-          py_module = __import__(parsed_contents[0])
-          replacement = str(py_module.DoMain(parsed_contents[1:])).rstrip()
-
-          os.chdir(oldwd)
+          try:
+
+            parsed_contents = shlex.split(contents)
+            try:
+              py_module = __import__(parsed_contents[0])
+            except ImportError as e:
+              raise GypError("Error importing pymod_do_main"
+                             "module (%s): %s" % (parsed_contents[0], e))
+            replacement = str(py_module.DoMain(parsed_contents[1:])).rstrip()
+          finally:
+            os.chdir(oldwd)
           assert replacement != None
         elif command_string:
           raise GypError("Unknown command string '%s' in '%s'." %
@@ -884,8 +881,8 @@ def ExpandVariables(input, phase, variables, build_file):
         cached_command_results[cache_key] = replacement
       else:
         gyp.DebugOutput(gyp.DEBUG_VARIABLES,
-                        "Had cache value for command '%s' in directory '%s'" %
-                        (contents,build_file_dir))
+                        "Had cache value for command '%s' in directory '%s'",
+                        contents,build_file_dir)
         replacement = cached_value
 
     else:
@@ -960,8 +957,7 @@ def ExpandVariables(input, 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 %s, recursing." % repr(output))
+  gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
   if isinstance(output, list):
     if output and isinstance(output[0], list):
       # Leave output alone if it's a list of lists.
@@ -1062,7 +1058,7 @@ def ProcessConditionsInDict(the_dict, phase, variables, build_file):
     except NameError, e:
       gyp.common.ExceptionAppend(e, 'while evaluating condition \'%s\' in %s' %
                                  (cond_expr_expanded, build_file))
-      raise
+      raise GypError(e)
 
     if merge_dict != None:
       # Expand variables and nested conditinals in the merge_dict before
@@ -1407,6 +1403,25 @@ def RemoveDuplicateDependencies(targets):
         target_dict[dependency_key] = Unify(dependencies)
 
 
+def Filter(l, item):
+  """Removes item from l."""
+  res = {}
+  return [res.setdefault(e, e) for e in l if e != item]
+
+
+def RemoveSelfDependencies(targets):
+  """Remove self dependencies from targets that have the prune_self_dependency
+  variable set."""
+  for target_name, target_dict in targets.iteritems():
+    for dependency_key in dependency_sections:
+      dependencies = target_dict.get(dependency_key, [])
+      if dependencies:
+        for t in dependencies:
+          if t == target_name:
+            if targets[t].get('variables', {}).get('prune_self_dependency', 0):
+              target_dict[dependency_key] = Filter(dependencies, target_name)
+
+
 class DependencyGraphNode(object):
   """
 
@@ -1845,12 +1860,10 @@ def MakePathRelative(to_file, fro_file, item):
     return ret
 
 def MergeLists(to, fro, to_file, fro_file, is_paths=False, append=True):
-  def is_hashable(x):
-    try:
-      hash(x)
-    except TypeError:
-      return False
-    return True
+  # Python documentation recommends objects which do not support hash
+  # set this value to None. Python library objects follow this rule.
+  is_hashable = lambda val: val.__hash__
+
   # If x is hashable, returns whether x is in s. Else returns whether x is in l.
   def is_in_set_or_list(x, s, l):
     if is_hashable(x):
@@ -1861,8 +1874,7 @@ def MergeLists(to, fro, to_file, fro_file, is_paths=False, append=True):
 
   # Make membership testing of hashables in |to| (in particular, strings)
   # faster.
-  hashable_to_set = set([x for x in to if is_hashable(x)])
-
+  hashable_to_set = set(x for x in to if is_hashable(x))
   for item in fro:
     singleton = False
     if isinstance(item, str) or isinstance(item, int):
@@ -2056,7 +2068,7 @@ def SetUpConfigurations(target, target_dict):
   if not 'configurations' in target_dict:
     target_dict['configurations'] = {'Default': {}}
   if not 'default_configuration' in target_dict:
-    concrete = [i for i in target_dict['configurations'].keys()
+    concrete = [i for i in target_dict['configurations'].iterkeys()
                 if not target_dict['configurations'][i].get('abstract')]
     target_dict['default_configuration'] = sorted(concrete)[0]
 
@@ -2315,8 +2327,8 @@ def ValidateTargetType(target, target_dict):
 
 
 def ValidateSourcesInTarget(target, target_dict, build_file):
-  # TODO: Check if MSVC allows this for non-static_library targets.
-  if target_dict.get('type', None) != 'static_library':
+  # TODO: Check if MSVC allows this for loadable_module targets.
+  if target_dict.get('type', None) not in ('static_library', 'shared_library'):
     return
   sources = target_dict.get('sources', [])
   basenames = {}
@@ -2548,7 +2560,7 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
     build_file = os.path.normpath(build_file)
     try:
       if parallel:
-        print >>sys.stderr, 'Using parallel processing (experimental).'
+        print >>sys.stderr, 'Using parallel processing.'
         LoadTargetBuildFileParallel(build_file, data, aux_data,
                                     variables, includes, depth, check)
       else:
@@ -2564,6 +2576,10 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
   # Fully qualify all dependency links.
   QualifyDependencies(targets)
 
+  # Remove self-dependencies from targets that have 'prune_self_dependencies'
+  # set to 1.
+  RemoveSelfDependencies(targets)
+
   # Expand dependencies specified as build_file:*.
   ExpandWildcardDependencies(targets, data)
 
index 6926769..c06e3be 100755 (executable)
@@ -80,6 +80,19 @@ class MacTool(object):
   def _CopyStringsFile(self, source, dest):
     """Copies a .strings file using iconv to reconvert the input into UTF-16."""
     input_code = self._DetectInputEncoding(source) or "UTF-8"
+
+    # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call
+    # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints
+    #     CFPropertyListCreateFromXMLData(): Old-style plist parser: missing
+    #     semicolon in dictionary.
+    # on invalid files. Do the same kind of validation.
+    import CoreFoundation
+    s = open(source).read()
+    d = CoreFoundation.CFDataCreate(None, s, len(s))
+    _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None)
+    if error:
+      return
+
     fp = open(dest, 'w')
     args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code',
         'UTF-16', source]
index 840a79b..bc2afca 100644 (file)
@@ -168,8 +168,6 @@ class MsvsSettings(object):
     equivalents."""
     target_platform = 'Win32' if self.GetArch(config) == 'x86' else 'x64'
     replacements = {
-        '$(VSInstallDir)': self.vs_version.Path(),
-        '$(VCInstallDir)': os.path.join(self.vs_version.Path(), 'VC') + '\\',
         '$(OutDir)\\': base_to_build + '\\' if base_to_build else '',
         '$(IntDir)': '$!INTERMEDIATE_DIR',
         '$(InputPath)': '${source}',
@@ -178,6 +176,12 @@ class MsvsSettings(object):
         '$(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
@@ -275,6 +279,16 @@ class MsvsSettings(object):
         ('VCCLCompilerTool', 'PreprocessorDefinitions'), config, default=[]))
     return defines
 
+  def GetCompilerPdbName(self, config, expand_special):
+    """Get the pdb file name that should be used for compiler invocations, or
+    None if there's no explicit name specified."""
+    config = self._TargetConfig(config)
+    pdbname = self._Setting(
+        ('VCCLCompilerTool', 'ProgramDataBaseFileName'), config)
+    if pdbname:
+      pdbname = expand_special(self.ConvertVSMacros(pdbname))
+    return pdbname
+
   def GetOutputName(self, config, expand_special):
     """Gets the explicitly overridden output name for a target or returns None
     if it's not overridden."""
@@ -309,6 +323,7 @@ class MsvsSettings(object):
        map={'0': 'd', '1': '1', '2': '2', '3': 'x'}, prefix='/O')
     cl('InlineFunctionExpansion', prefix='/Ob')
     cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy')
+    cl('EnableIntrinsicFunctions', map={'false': '-', 'true': ''}, prefix='/Oi')
     cl('FavorSizeOrSpeed', map={'1': 't', '2': 's'}, prefix='/O')
     cl('WholeProgramOptimization', map={'true': '/GL'})
     cl('WarningLevel', prefix='/W')
@@ -323,8 +338,13 @@ class MsvsSettings(object):
     cl('RuntimeLibrary',
         map={'0': 'T', '1': 'Td', '2': 'D', '3': 'Dd'}, prefix='/M')
     cl('ExceptionHandling', map={'1': 'sc','2': 'a'}, prefix='/EH')
+    cl('DefaultCharIsUnsigned', map={'true': '/J'})
+    cl('TreatWChar_tAsBuiltInType',
+        map={'false': '-', 'true': ''}, prefix='/Zc:wchar_t')
     cl('EnablePREfast', map={'true': '/analyze'})
     cl('AdditionalOptions', prefix='')
+    cflags.extend(['/FI' + f for f in self._Setting(
+        ('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])])
     # ninja handles parallelism by itself, don't have the compiler do it too.
     cflags = filter(lambda x: not x.startswith('/MP'), cflags)
     return cflags
@@ -378,6 +398,7 @@ class MsvsSettings(object):
                           'VCLibrarianTool', append=libflags)
     libflags.extend(self._GetAdditionalLibraryDirectories(
         'VCLibrarianTool', config, gyp_to_build_path))
+    lib('LinkTimeCodeGeneration', map={'true': '/LTCG'})
     lib('AdditionalOptions')
     return libflags
 
@@ -414,6 +435,7 @@ class MsvsSettings(object):
       ldflags.append('/PDB:' + pdb)
     ld('AdditionalOptions', prefix='')
     ld('SubSystem', map={'1': 'CONSOLE', '2': 'WINDOWS'}, prefix='/SUBSYSTEM:')
+    ld('TerminalServerAware', map={'1': ':NO', '2': ''}, prefix='/TSAWARE')
     ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL')
     ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED')
     ld('RandomizedBaseAddress',
@@ -426,13 +448,11 @@ class MsvsSettings(object):
     ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
     ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
     ld('EntryPointSymbol', prefix='/ENTRY:')
-    ld('Profile', map={ 'true': '/PROFILE'})
+    ld('Profile', map={'true': '/PROFILE'})
+    ld('LargeAddressAware',
+        map={'1': ':NO', '2': ''}, prefix='/LARGEADDRESSAWARE')
     # TODO(scottmg): This should sort of be somewhere else (not really a flag).
     ld('AdditionalDependencies', prefix='')
-    # TODO(scottmg): These too.
-    ldflags.extend(('kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib',
-        'comdlg32.lib', 'advapi32.lib', 'shell32.lib', 'ole32.lib',
-        'oleaut32.lib', 'uuid.lib', 'odbc32.lib', 'DelayImp.lib'))
 
     # If the base address is not specifically controlled, DYNAMICBASE should
     # be on by default.
@@ -576,7 +596,8 @@ class MsvsSettings(object):
                  ('iid', iid),
                  ('proxy', proxy)]
     # TODO(scottmg): Are there configuration settings to set these flags?
-    flags = ['/char', 'signed', '/env', 'win32', '/Oicf']
+    target_platform = 'win32' if self.GetArch(config) == 'x86' else 'x64'
+    flags = ['/char', 'signed', '/env', target_platform, '/Oicf']
     return outdir, output, variables, flags
 
 
@@ -586,29 +607,25 @@ def _LanguageMatchesForPch(source_ext, pch_source_ext):
   return ((source_ext in c_exts and pch_source_ext in c_exts) or
           (source_ext in cc_exts and pch_source_ext in cc_exts))
 
+
 class PrecompiledHeader(object):
   """Helper to generate dependencies and build rules to handle generation of
   precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
   """
-  def __init__(self, settings, config, gyp_to_build_path):
+  def __init__(
+      self, settings, config, gyp_to_build_path, gyp_to_unique_output, obj_ext):
     self.settings = settings
     self.config = config
-    self.gyp_to_build_path = gyp_to_build_path
+    pch_source = self.settings.msvs_precompiled_source[self.config]
+    self.pch_source = gyp_to_build_path(pch_source)
+    filename, _ = os.path.splitext(pch_source)
+    self.output_obj = gyp_to_unique_output(filename + obj_ext).lower()
 
   def _PchHeader(self):
     """Get the header that will appear in an #include line for all source
     files."""
     return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
 
-  def _PchSource(self):
-    """Get the source file that is built once to compile the pch data."""
-    return self.gyp_to_build_path(
-        self.settings.msvs_precompiled_source[self.config])
-
-  def _PchOutput(self):
-    """Get the name of the output of the compiled pch data."""
-    return '${pchprefix}.' + self._PchHeader() + '.pch'
-
   def GetObjDependencies(self, sources, objs):
     """Given a list of sources files and the corresponding object files,
     returns a list of the pch files that should be depended upon. The
@@ -616,24 +633,30 @@ class PrecompiledHeader(object):
     with make.py on Mac, and xcode_emulation.py."""
     if not self._PchHeader():
       return []
-    source = self._PchSource()
-    assert source
-    pch_ext = os.path.splitext(self._PchSource())[1]
+    pch_ext = os.path.splitext(self.pch_source)[1]
     for source in sources:
       if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
-        return [(None, None, self._PchOutput())]
+        return [(None, None, self.output_obj)]
     return []
 
   def GetPchBuildCommands(self):
-    """Returns [(path_to_pch, language_flag, language, header)].
-    |path_to_gch| and |header| are relative to the build directory."""
-    header = self._PchHeader()
-    source = self._PchSource()
-    if not source or not header:
-      return []
-    ext = os.path.splitext(source)[1]
-    lang = 'c' if ext == '.c' else 'cc'
-    return [(self._PchOutput(), '/Yc' + header, lang, source)]
+    """Not used on Windows as there are no additional build steps required
+    (instead, existing steps are modified in GetFlagsModifications below)."""
+    return []
+
+  def GetFlagsModifications(self, input, output, implicit, command,
+                            cflags_c, cflags_cc, expand_special):
+    """Get the modified cflags and implicit dependencies that should be used
+    for the pch compilation step."""
+    if input == self.pch_source:
+      pch_output = ['/Yc' + self._PchHeader()]
+      if command == 'cxx':
+        return ([('cflags_cc', map(expand_special, cflags_cc + pch_output))],
+                self.output_obj, [])
+      elif command == 'cc':
+        return ([('cflags_c', map(expand_special, cflags_c + pch_output))],
+                self.output_obj, [])
+    return [], output, implicit
 
 
 vs_version = None
@@ -711,7 +734,13 @@ def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
   of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
   sets up the environment, and then we do not prefix the compiler with
   an absolute path, instead preferring something like "cl.exe" in the rule
-  which will then run whichever the environment setup has put in the path."""
+  which will then run whichever the environment setup has put in the path.
+  When the following procedure to generate environment files does not
+  meet your requirement (e.g. for custom toolchains), you can pass
+  "-G ninja_use_custom_environment_files" to the gyp to suppress file
+  generation and use custom environment files prepared by yourself."""
+  if generator_flags.get('ninja_use_custom_environment_files', 0):
+    return
   vs = GetVSVersion(generator_flags)
   for arch in ('x86', 'x64'):
     args = vs.SetupScript(arch)
index ef5b460..806f92b 100644 (file)
@@ -11,13 +11,16 @@ import gyp.common
 import os.path
 import re
 import shlex
+import subprocess
+import sys
+from gyp.common import GypError
 
 class XcodeSettings(object):
   """A class that understands the gyp 'xcode_settings' object."""
 
-  # Computed lazily by _GetSdkBaseDir(). Shared by all XcodeSettings, so cached
+  # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached
   # at class-level for efficiency.
-  _sdk_base_dir = None
+  _sdk_path_cache = {}
 
   def __init__(self, spec):
     self.spec = spec
@@ -219,34 +222,34 @@ class XcodeSettings(object):
     else:
       return self._GetStandaloneBinaryPath()
 
-  def _GetSdkBaseDir(self):
-    """Returns the root of the 'Developer' directory. On Xcode 4.2 and prior,
-    this is usually just /Developer. Xcode 4.3 moved that folder into the Xcode
-    bundle."""
-    if not XcodeSettings._sdk_base_dir:
-      import subprocess
-      job = subprocess.Popen(['xcode-select', '-print-path'],
-                             stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-      out, err = job.communicate()
-      if job.returncode != 0:
-        print out
-        raise Exception('Error %d running xcode-select' % job.returncode)
-      # The Developer folder moved in Xcode 4.3.
-      xcode43_sdk_path = os.path.join(
-          out.rstrip(), 'Platforms/MacOSX.platform/Developer/SDKs')
-      if os.path.isdir(xcode43_sdk_path):
-        XcodeSettings._sdk_base_dir = xcode43_sdk_path
-      else:
-        XcodeSettings._sdk_base_dir = os.path.join(out.rstrip(), 'SDKs')
-    return XcodeSettings._sdk_base_dir
+  def _GetSdkVersionInfoItem(self, sdk, infoitem):
+    job = subprocess.Popen(['xcodebuild', '-version', '-sdk', sdk, infoitem],
+                           stdout=subprocess.PIPE,
+                           stderr=subprocess.STDOUT)
+    out = job.communicate()[0]
+    if job.returncode != 0:
+      sys.stderr.write(out + '\n')
+      raise GypError('Error %d running xcodebuild' % job.returncode)
+    return out.rstrip('\n')
 
   def _SdkPath(self):
-    sdk_root = self.GetPerTargetSetting('SDKROOT', default='macosx10.5')
-    if sdk_root.startswith('macosx'):
-      return os.path.join(self._GetSdkBaseDir(),
-                          'MacOSX' + sdk_root[len('macosx'):] + '.sdk')
-    return sdk_root
+    sdk_root = self.GetPerTargetSetting('SDKROOT', default='macosx')
+    if sdk_root not in XcodeSettings._sdk_path_cache:
+      XcodeSettings._sdk_path_cache[sdk_root] = self._GetSdkVersionInfoItem(
+          sdk_root, 'Path')
+    return XcodeSettings._sdk_path_cache[sdk_root]
+
+  def _AppendPlatformVersionMinFlags(self, lst):
+    self._Appendf(lst, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
+    if 'IPHONEOS_DEPLOYMENT_TARGET' in self._Settings():
+      # TODO: Implement this better?
+      sdk_path_basename = os.path.basename(self._SdkPath())
+      if sdk_path_basename.lower().startswith('iphonesimulator'):
+        self._Appendf(lst, 'IPHONEOS_DEPLOYMENT_TARGET',
+                      '-mios-simulator-version-min=%s')
+      else:
+        self._Appendf(lst, 'IPHONEOS_DEPLOYMENT_TARGET',
+                      '-miphoneos-version-min=%s')
 
   def GetCflags(self, configname):
     """Returns flags that need to be added to .c, .cc, .m, and .mm
@@ -261,6 +264,9 @@ class XcodeSettings(object):
     if 'SDKROOT' in self._Settings():
       cflags.append('-isysroot %s' % sdk_root)
 
+    if self._Test('CLANG_WARN_CONSTANT_CONVERSION', 'YES', default='NO'):
+      cflags.append('-Wconstant-conversion')
+
     if self._Test('GCC_CHAR_IS_UNSIGNED_CHAR', 'YES', default='NO'):
       cflags.append('-funsigned-char')
 
@@ -301,7 +307,7 @@ class XcodeSettings(object):
     if self._Test('GCC_WARN_ABOUT_MISSING_NEWLINE', 'YES', default='NO'):
       cflags.append('-Wnewline-eof')
 
-    self._Appendf(cflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
+    self._AppendPlatformVersionMinFlags(cflags)
 
     # TODO:
     if self._Test('COPY_PHASE_STRIP', 'YES', default='NO'):
@@ -354,6 +360,16 @@ class XcodeSettings(object):
     """Returns flags that need to be added to .cc, and .mm compilations."""
     self.configname = configname
     cflags_cc = []
+
+    clang_cxx_language_standard = self._Settings().get(
+        'CLANG_CXX_LANGUAGE_STANDARD')
+    # Note: Don't make c++0x to c++11 so that c++0x can be used with older
+    # clangs that don't understand c++11 yet (like Xcode 4.2's).
+    if clang_cxx_language_standard:
+      cflags_cc.append('-std=%s' % clang_cxx_language_standard)
+
+    self._Appendf(cflags_cc, 'CLANG_CXX_LIBRARY', '-stdlib=%s')
+
     if self._Test('GCC_ENABLE_CPP_RTTI', 'NO', default='YES'):
       cflags_cc.append('-fno-rtti')
     if self._Test('GCC_ENABLE_CPP_EXCEPTIONS', 'NO', default='YES'):
@@ -362,6 +378,7 @@ class XcodeSettings(object):
       cflags_cc.append('-fvisibility-inlines-hidden')
     if self._Test('GCC_THREADSAFE_STATICS', 'NO', default='YES'):
       cflags_cc.append('-fno-threadsafe-statics')
+    # Note: This flag is a no-op for clang, it only has an effect for gcc.
     if self._Test('GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO', 'NO', default='YES'):
       cflags_cc.append('-Wno-invalid-offsetof')
 
@@ -524,8 +541,9 @@ class XcodeSettings(object):
         ldflags, 'DYLIB_COMPATIBILITY_VERSION', '-compatibility_version %s')
     self._Appendf(
         ldflags, 'DYLIB_CURRENT_VERSION', '-current_version %s')
-    self._Appendf(
-        ldflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
+
+    self._AppendPlatformVersionMinFlags(ldflags)
+
     if 'SDKROOT' in self._Settings():
       ldflags.append('-isysroot ' + self._SdkPath())
 
@@ -1042,7 +1060,7 @@ def _TopologicallySortedEnvVarKeys(env):
     order.reverse()
     return order
   except gyp.common.CycleError, e:
-    raise Exception(
+    raise GypError(
         'Xcode environment variables are cyclically dependent: ' + str(e.nodes))
 
 
index ec4cb96..47712a7 100644 (file)
@@ -1503,6 +1503,7 @@ class PBXFileReference(XCFileLikeElement, XCContainerPortal, XCRemoteObject):
         'r':           'sourcecode.rez',
         'rez':         'sourcecode.rez',
         's':           'sourcecode.asm',
+        'storyboard':  'file.storyboard',
         'strings':     'text.plist.strings',
         'ttf':         'file',
         'xcconfig':    'text.xcconfig',
index e988a35..11b8497 100644 (file)
     (insert-file-contents-literally (concat filename ".fontified"))
     (read (current-buffer))))
 
+(defun equivalent-face (face)
+  "For the purposes of face comparison, we're not interested in the
+   differences between certain faces. For example, the difference between
+   font-lock-comment-delimiter and font-lock-comment-face."
+  (case face
+    ((font-lock-comment-delimiter-face) font-lock-comment-face)
+    (t face)))
+
 (defun text-face-properties (s)
   "Extract the text properties from s"
   (let ((result (list t)))
     (dotimes (i (length s))
-      (setq result (cons (get-text-property i 'face s) result)))
+      (setq result (cons (equivalent-face (get-text-property i 'face s))
+                         result)))
     (nreverse result)))
 
 (ert-deftest test-golden-samples ()
index c20fc8d..f558b53 100644 (file)
             (setq sections (cdr sections)) ; pop out a level
           (cond ((looking-at-p "['\"]") ; a string
                  (setq string-start (point))
-                 (forward-sexp 1)
+                 (goto-char (scan-sexps (point) 1))
                  (if (gyp-inside-dictionary-p)
                      ;; Look for sections inside a dictionary
                      (let ((section (gyp-section-name