Merge from Chromium at DEPS revision 258528
This commit was generated by merge_to_master.py.
Change-Id: I7981e2b0356dfc87f80e08615dfb9144a31ff55a
diff --git a/pylib/gyp/generator/msvs.py b/pylib/gyp/generator/msvs.py
index e20cee1..d1e91c1 100644
--- a/pylib/gyp/generator/msvs.py
+++ b/pylib/gyp/generator/msvs.py
@@ -81,6 +81,7 @@
'msvs_external_builder_out_dir',
'msvs_external_builder_build_cmd',
'msvs_external_builder_clean_cmd',
+ 'msvs_external_builder_clcompile_cmd',
]
@@ -1862,6 +1863,14 @@
'clean',
'$(ProjectName)',
]
+ if not spec.get('msvs_external_builder_clcompile_cmd'):
+ spec['msvs_external_builder_clcompile_cmd'] = [
+ sys.executable,
+ '$(OutDir)/gyp-win-tool',
+ 'cl-compile',
+ '$(ProjectDir)',
+ '$(SelectedFiles)',
+ ]
def CalculateVariables(default_variables, params):
@@ -3233,7 +3242,9 @@
def _GetMSBuildExternalBuilderTargets(spec):
"""Return a list of MSBuild targets for external builders.
- Right now, only "Build" and "Clean" targets are generated.
+ The "Build" and "Clean" targets are always generated. If the spec contains
+ 'msvs_external_builder_clcompile_cmd', then the "ClCompile" target will also
+ be generated, to support building selected C/C++ files.
Arguments:
spec: The gyp target spec.
@@ -3252,7 +3263,17 @@
clean_target = ['Target', {'Name': 'Clean'}]
clean_target.append(['Exec', {'Command': clean_cmd}])
- return [build_target, clean_target]
+ targets = [build_target, clean_target]
+
+ if spec.get('msvs_external_builder_clcompile_cmd'):
+ clcompile_cmd = _BuildCommandLineForRuleRaw(
+ spec, spec['msvs_external_builder_clcompile_cmd'],
+ False, False, False, False)
+ clcompile_target = ['Target', {'Name': 'ClCompile'}]
+ clcompile_target.append(['Exec', {'Command': clcompile_cmd}])
+ targets.append(clcompile_target)
+
+ return targets
def _GetMSBuildExtensions(props_files_of_rules):
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py
index 053e5e9..efbe854 100644
--- a/pylib/gyp/generator/ninja.py
+++ b/pylib/gyp/generator/ninja.py
@@ -344,7 +344,7 @@
return os.path.normpath(os.path.join(obj, self.base_dir, path_dir,
path_basename))
- def WriteCollapsedDependencies(self, name, targets):
+ def WriteCollapsedDependencies(self, name, targets, order_only=None):
"""Given a list of targets, return a path for a single file
representing the result of building all the targets or None.
@@ -352,10 +352,11 @@
assert targets == filter(None, targets), targets
if len(targets) == 0:
+ assert not order_only
return None
- if len(targets) > 1:
+ if len(targets) > 1 or order_only:
stamp = self.GypPathToUniqueOutput(name + '.stamp')
- targets = self.ninja.build(stamp, 'stamp', targets)
+ targets = self.ninja.build(stamp, 'stamp', targets, order_only=order_only)
self.ninja.newline()
return targets[0]
@@ -619,12 +620,14 @@
env = self.GetToolchainEnv()
all_outputs = []
for rule in rules:
- # First write out a rule for the rule action.
- name = '%s_%s' % (rule['rule_name'],
- hashlib.md5(self.qualified_target).hexdigest())
# Skip a rule with no action and no inputs.
if 'action' not in rule and not rule.get('rule_sources', []):
continue
+
+ # First write out a rule for the rule action.
+ name = '%s_%s' % (rule['rule_name'],
+ hashlib.md5(self.qualified_target).hexdigest())
+
args = rule['action']
description = self.GenerateDescription(
'RULE',
@@ -653,8 +656,22 @@
return path.replace('\\', '/')
return path
+ inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])]
+
+ # If there are n source files matching the rule, and m additional rule
+ # inputs, then adding 'inputs' to each build edge written below will
+ # write m * n inputs. Collapsing reduces this to m + n.
+ sources = rule.get('rule_sources', [])
+ num_inputs = len(inputs)
+ if prebuild:
+ num_inputs += 1
+ if num_inputs > 2 and len(sources) > 2:
+ inputs = [
+ self.WriteCollapsedDependencies(name, inputs, order_only=prebuild)]
+ prebuild = []
+
# For each source file, write an edge that generates all the outputs.
- for source in rule.get('rule_sources', []):
+ for source in sources:
source = os.path.normpath(source)
dirname, basename = os.path.split(source)
root, ext = os.path.splitext(basename)
@@ -663,9 +680,6 @@
outputs = [self.ExpandRuleVariables(o, root, dirname,
source, ext, basename)
for o in rule['outputs']]
- inputs = [self.ExpandRuleVariables(i, root, dirname,
- source, ext, basename)
- for i in rule.get('inputs', [])]
if int(rule.get('process_outputs_as_sources', False)):
extra_sources += outputs
@@ -703,7 +717,6 @@
else:
assert var == None, repr(var)
- inputs = [self.GypPathToNinja(i, env) for i in inputs]
outputs = [self.GypPathToNinja(o, env) for o in outputs]
extra_bindings.append(('unique_name',
hashlib.md5(outputs[0]).hexdigest()))
diff --git a/pylib/gyp/msvs_emulation.py b/pylib/gyp/msvs_emulation.py
index 6de77d7..6b5dfc2 100644
--- a/pylib/gyp/msvs_emulation.py
+++ b/pylib/gyp/msvs_emulation.py
@@ -390,12 +390,6 @@
cflags = filter(lambda x: not x.startswith('/MP'), cflags)
return cflags
- def GetPrecompiledHeader(self, config, gyp_to_build_path):
- """Returns an object that handles the generation of precompiled header
- build steps."""
- config = self._TargetConfig(config)
- return _PchHelper(self, config, gyp_to_build_path)
-
def _GetPchFlags(self, config, extension):
"""Get the flags to be added to the cflags for precompiled header support.
"""
@@ -789,7 +783,7 @@
def GetObjDependencies(self, sources, objs, arch):
"""Given a list of sources files and the corresponding object files,
returns a list of the pch files that should be depended upon. The
- additional wrapping in the return value is for interface compatability
+ additional wrapping in the return value is for interface compatibility
with make.py on Mac, and xcode_emulation.py."""
assert arch is None
if not self._PchHeader():
diff --git a/pylib/gyp/win_tool.py b/pylib/gyp/win_tool.py
index 5872f07..7e2d968 100755
--- a/pylib/gyp/win_tool.py
+++ b/pylib/gyp/win_tool.py
@@ -13,6 +13,7 @@
import re
import shutil
import subprocess
+import stat
import string
import sys
@@ -89,9 +90,19 @@
"""Emulation of rm -rf out && cp -af in out."""
if os.path.exists(dest):
if os.path.isdir(dest):
- shutil.rmtree(dest)
+ def _on_error(fn, path, excinfo):
+ # The operation failed, possibly because the file is set to
+ # read-only. If that's why, make it writable and try the op again.
+ if not os.access(path, os.W_OK):
+ os.chmod(path, stat.S_IWRITE)
+ fn(path)
+ shutil.rmtree(dest, onerror=_on_error)
else:
+ if not os.access(dest, os.W_OK):
+ # Attempt to make the file writable before deleting it.
+ os.chmod(dest, stat.S_IWRITE)
os.unlink(dest)
+
if os.path.isdir(source):
shutil.copytree(source, dest)
else:
@@ -288,5 +299,16 @@
dir = dir[0] if dir else None
return subprocess.call(args, shell=True, env=env, cwd=dir)
+ def ExecClCompile(self, project_dir, selected_files):
+ """Executed by msvs-ninja projects when the 'ClCompile' target is used to
+ build selected C/C++ files."""
+ project_dir = os.path.relpath(project_dir, BASE_DIR)
+ selected_files = selected_files.split(';')
+ ninja_targets = [os.path.join(project_dir, filename) + '^^'
+ for filename in selected_files]
+ cmd = ['ninja.exe']
+ cmd.extend(ninja_targets)
+ return subprocess.call(cmd, shell=True, cwd=BASE_DIR)
+
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/test/rules/src/special-variables.gyp b/test/rules/src/special-variables.gyp
index fc55665..d1443af 100644
--- a/test/rules/src/special-variables.gyp
+++ b/test/rules/src/special-variables.gyp
@@ -13,7 +13,6 @@
'extension': 'S',
'inputs': [
'as.bat',
- '$(InputPath)'
],
'outputs': [
'$(IntDir)/$(InputName).obj',
diff --git a/test/win/command-quote/command-quote.gyp b/test/win/command-quote/command-quote.gyp
index 8489c50..faf7246 100644
--- a/test/win/command-quote/command-quote.gyp
+++ b/test/win/command-quote/command-quote.gyp
@@ -15,7 +15,6 @@
'rule_name': 'build_with_batch',
'msvs_cygwin_shell': 0,
'extension': 'S',
- 'inputs': ['<(RULE_INPUT_PATH)'],
'outputs': ['output.obj'],
'action': ['call go.bat', '<(RULE_INPUT_PATH)', 'output.obj'],
},],
@@ -29,7 +28,6 @@
'rule_name': 'build_with_batch2',
'msvs_cygwin_shell': 0,
'extension': 'S',
- 'inputs': ['<(RULE_INPUT_PATH)'],
'outputs': ['output2.obj'],
'action': ['call', 'go.bat', '<(RULE_INPUT_PATH)', 'output2.obj'],
},],
@@ -43,7 +41,6 @@
'rule_name': 'build_with_batch3',
'msvs_cygwin_shell': 0,
'extension': 'S',
- 'inputs': ['<(RULE_INPUT_PATH)'],
'outputs': ['output3.obj'],
'action': ['bat with spaces.bat', '<(RULE_INPUT_PATH)', 'output3.obj'],
},],
@@ -57,7 +54,6 @@
'rule_name': 'build_with_batch3',
'msvs_cygwin_shell': 1,
'extension': 'S',
- 'inputs': ['<(RULE_INPUT_PATH)'],
'outputs': ['output4.obj'],
'arguments': ['-v'],
'action': ['python', '-c', 'import shutil; '
@@ -73,7 +69,6 @@
'rule_name': 'build_with_batch3',
'msvs_cygwin_shell': 1,
'extension': 'S',
- 'inputs': ['<(RULE_INPUT_PATH)'],
'outputs': ['output5.obj'],
'action': ['python', '-c', "import shutil; "
"shutil.copy('<(RULE_INPUT_PATH)', 'output5.obj')"],
diff --git a/test/win/command-quote/subdir/and/another/in-subdir.gyp b/test/win/command-quote/subdir/and/another/in-subdir.gyp
index be363bb..3dff4c4 100644
--- a/test/win/command-quote/subdir/and/another/in-subdir.gyp
+++ b/test/win/command-quote/subdir/and/another/in-subdir.gyp
@@ -18,7 +18,6 @@
'rule_name': 'build_with_batch4',
'msvs_cygwin_shell': 0,
'extension': 'S',
- 'inputs': ['<(RULE_INPUT_PATH)'],
'outputs': ['output4.obj'],
'action': ['<@(filepath)', '<(RULE_INPUT_PATH)', 'output4.obj'],
},],
diff --git a/test/win/vs-macros/containing-gyp.gyp b/test/win/vs-macros/containing-gyp.gyp
index fa799a4..c07b639 100644
--- a/test/win/vs-macros/containing-gyp.gyp
+++ b/test/win/vs-macros/containing-gyp.gyp
@@ -16,7 +16,6 @@
'extension': 'S',
'inputs': [
'as.py',
- '$(InputPath)'
],
'outputs': [
'$(IntDir)/$(InputName).obj',
diff --git a/test/win/vs-macros/input-output-macros.gyp b/test/win/vs-macros/input-output-macros.gyp
index b7a3c1e..b4520f8 100644
--- a/test/win/vs-macros/input-output-macros.gyp
+++ b/test/win/vs-macros/input-output-macros.gyp
@@ -13,7 +13,6 @@
'rule_name': 'generate_file',
'extension': 'blah',
'inputs': [
- '<(RULE_INPUT_PATH)',
'do_stuff.py',
],
'outputs': [
diff --git a/test/win/win-tool/copies_readonly_files.gyp b/test/win/win-tool/copies_readonly_files.gyp
new file mode 100644
index 0000000..3cd7e69
--- /dev/null
+++ b/test/win/win-tool/copies_readonly_files.gyp
@@ -0,0 +1,29 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'foo',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/dest',
+ 'files': [
+ 'read-only-file',
+ ],
+ },
+ ],
+ }, # target: foo
+
+ {
+ 'target_name': 'bar',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/dest',
+ 'files': [
+ 'subdir/',
+ ],
+ },
+ ],
+ }, # target: bar
+ ],
+}
diff --git a/test/win/win-tool/gyptest-win-tool-handles-readonly-files.py b/test/win/win-tool/gyptest-win-tool-handles-readonly-files.py
new file mode 100644
index 0000000..951b952
--- /dev/null
+++ b/test/win/win-tool/gyptest-win-tool-handles-readonly-files.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Make sure overwriting read-only files works as expected (via win-tool).
+"""
+
+import TestGyp
+
+import filecmp
+import os
+import stat
+import sys
+
+if sys.platform == 'win32':
+ test = TestGyp.TestGyp(formats=['ninja'])
+
+ # First, create the source files.
+ os.makedirs('subdir')
+ read_only_files = ['read-only-file', 'subdir/A', 'subdir/B', 'subdir/C']
+ for f in read_only_files:
+ test.write(f, 'source_contents')
+ test.chmod(f, stat.S_IREAD)
+ if os.access(f, os.W_OK):
+ test.fail_test()
+
+ # Second, create the read-only destination files. Note that we are creating
+ # them where the ninja and win-tool will try to copy them to, in order to test
+ # that copies overwrite the files.
+ os.makedirs(test.built_file_path('dest/subdir'))
+ for f in read_only_files:
+ f = os.path.join('dest', f)
+ test.write(test.built_file_path(f), 'SHOULD BE OVERWRITTEN')
+ test.chmod(test.built_file_path(f), stat.S_IREAD)
+ # Ensure not writable.
+ if os.access(test.built_file_path(f), os.W_OK):
+ test.fail_test()
+
+ test.run_gyp('copies_readonly_files.gyp')
+ test.build('copies_readonly_files.gyp')
+
+ # Check the destination files were overwritten by ninja.
+ for f in read_only_files:
+ f = os.path.join('dest', f)
+ test.must_contain(test.built_file_path(f), 'source_contents')
+
+ # This will fail if the files are not the same mode or contents.
+ for f in read_only_files:
+ if not filecmp.cmp(f, test.built_file_path(os.path.join('dest', f))):
+ test.fail_test()
+
+ test.pass_test()