summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@78cadc50-ecff-11dd-a971-7dbc132099af>2014-08-06 16:41:05 +0000
committersky@chromium.org <sky@chromium.org@78cadc50-ecff-11dd-a971-7dbc132099af>2014-08-06 16:41:05 +0000
commit93303ec2a58c910b7b2f26230002e8c736115844 (patch)
treee3a5d6ddc74fedacf9bf905c05e285c5120eef15
parent6bd80e4bba1e6076b262fbfa67517ec683a144c8 (diff)
downloadgyp-93303ec2a58c910b7b2f26230002e8c736115844.tar.gz
Currently I've special cased gyp* file modifications higher in the
stack. By that I mean if a gyp* file has been modified I don't run analyze and assume everything has changed. This change adds support for modification to gyp* files. If a gyp* file has changed it assumes all targets in the file are modified. Similarly if an included file has been modified all targets in the file that did the include are considered modified. Lastly, if one of the modified files is specified on the command line via -I the I early out and assume everything needs to be recompiled. BUG=109173 R=scottmg@chromium.org Review URL: https://codereview.chromium.org/442083004 git-svn-id: http://gyp.googlecode.com/svn/trunk@1962 78cadc50-ecff-11dd-a971-7dbc132099af
-rw-r--r--pylib/gyp/generator/analyzer.py95
-rw-r--r--test/analyzer/common.gypi6
-rw-r--r--test/analyzer/gyptest-analyzer.new.py32
-rw-r--r--test/analyzer/subdir2/subdir.gyp18
-rw-r--r--test/analyzer/subdir2/subdir.includes.gypi9
-rw-r--r--test/analyzer/test2.gyp25
-rw-r--r--test/analyzer/test2.includes.gypi13
-rw-r--r--test/analyzer/test2.includes.includes.gypi9
-rw-r--r--test/analyzer/test2.toplevel_includes.gypi15
9 files changed, 202 insertions, 20 deletions
diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py
index dc55da67..2648e7d5 100644
--- a/pylib/gyp/generator/analyzer.py
+++ b/pylib/gyp/generator/analyzer.py
@@ -64,6 +64,12 @@ for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
'CONFIGURATION_NAME']:
generator_default_variables[unused] = ''
+def _ToGypPath(path):
+ """Converts a path to the format used by gyp."""
+ if os.sep == '\\' and os.altsep == '/':
+ return path.replace('\\', '/')
+ return path
+
def __ExtractBasePath(target):
"""Extracts the path components of the specified gyp target path."""
last_index = target.rfind('/')
@@ -120,10 +126,7 @@ def __ExtractSources(target, target_dict, toplevel_dir):
# |target| is either absolute or relative and in the format of the OS. Gyp
# source paths are always posix. Convert |target| to a posix path relative to
# |toplevel_dir_|. This is done to make it easy to build source paths.
- if os.sep == '\\' and os.altsep == '/':
- base_path = target.replace('\\', '/')
- else:
- base_path = target
+ base_path = _ToGypPath(target)
if base_path == toplevel_dir:
base_path = ''
elif base_path.startswith(toplevel_dir + '/'):
@@ -216,7 +219,30 @@ class Config(object):
except IOError:
raise Exception('Unable to open file', file_path)
-def __GenerateTargets(target_list, target_dicts, toplevel_dir, files):
+def _WasBuildFileModified(build_file, data, files):
+ """Returns true if the build file |build_file| is either in |files| or
+ one of the files included by |build_file| is in |files|."""
+ if _ToGypPath(build_file) in files:
+ if debug:
+ print 'gyp file modified', build_file
+ return True
+
+ # First element of included_files is the file itself.
+ if len(data[build_file]['included_files']) <= 1:
+ return False
+
+ for include_file in data[build_file]['included_files'][1:]:
+ # |included_files| are relative to the directory of the |build_file|.
+ rel_include_file = \
+ _ToGypPath(gyp.common.UnrelativePath(include_file, build_file))
+ if rel_include_file in files:
+ if debug:
+ print 'included gyp file modified, gyp_file=', build_file, \
+ 'included file=', rel_include_file
+ return True
+ return False
+
+def __GenerateTargets(data, target_list, target_dicts, toplevel_dir, files):
"""Generates a dictionary with the key the name of a target and the value a
Target. |toplevel_dir| is the root of the source tree. If the sources of
a target match that of |files|, then |target.matched| is set to True.
@@ -229,6 +255,10 @@ def __GenerateTargets(target_list, target_dicts, toplevel_dir, files):
matched = False
+ # Maps from build file to a boolean indicating whether the build file is in
+ # |files|.
+ build_file_in_files = {}
+
while len(targets_to_visit) > 0:
target_name = targets_to_visit.pop()
if target_name in targets:
@@ -236,13 +266,25 @@ def __GenerateTargets(target_list, target_dicts, toplevel_dir, files):
target = Target()
targets[target_name] = target
- sources = __ExtractSources(target_name, target_dicts[target_name],
- toplevel_dir)
- for source in sources:
- if source in files:
- target.match_status = MATCH_STATUS_MATCHES
- matched = True
- break
+
+ build_file = gyp.common.ParseQualifiedTarget(target_name)[0]
+ if not build_file in build_file_in_files:
+ build_file_in_files[build_file] = \
+ _WasBuildFileModified(build_file, data, files)
+
+ # If a build file (or any of its included files) is modified we assume all
+ # targets in the file are modified.
+ if build_file_in_files[build_file]:
+ target.match_status = MATCH_STATUS_MATCHES
+ matched = True
+ else:
+ sources = __ExtractSources(target_name, target_dicts[target_name],
+ toplevel_dir)
+ for source in sources:
+ if source in files:
+ target.match_status = MATCH_STATUS_MATCHES
+ matched = True
+ break
for dep in target_dicts[target_name].get('dependencies', []):
targets[target_name].deps.add(dep)
@@ -345,15 +387,28 @@ def GenerateOutput(target_list, target_dicts, data, params):
raise Exception('Must specify files to analyze via config_path generator '
'flag')
- toplevel_dir = os.path.abspath(params['options'].toplevel_dir)
- if os.sep == '\\' and os.altsep == '/':
- toplevel_dir = toplevel_dir.replace('\\', '/')
+ toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir))
if debug:
print 'toplevel_dir', toplevel_dir
- all_targets, matched = __GenerateTargets(target_list, target_dicts,
- toplevel_dir,
- frozenset(config.files))
+ matched = False
+ matched_include = False
+
+ # If one of the modified files is an include file then everything is
+ # affected.
+ if params['options'].includes:
+ for include in params['options'].includes:
+ if _ToGypPath(include) in config.files:
+ if debug:
+ print 'include path modified', include
+ matched_include = True
+ matched = True
+ break
+
+ if not matched:
+ all_targets, matched = __GenerateTargets(data, target_list, target_dicts,
+ toplevel_dir,
+ frozenset(config.files))
# Set of targets that refer to one of the files.
if config.look_for_dependency_only:
@@ -361,7 +416,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
return
warning = None
- if matched:
+ if matched_include:
+ output_targets = config.targets
+ elif matched:
unqualified_mapping = _GetUnqualifiedToQualifiedMapping(
all_targets, config.targets)
if len(unqualified_mapping) != len(config.targets):
diff --git a/test/analyzer/common.gypi b/test/analyzer/common.gypi
new file mode 100644
index 00000000..7c664e40
--- /dev/null
+++ b/test/analyzer/common.gypi
@@ -0,0 +1,6 @@
+# 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.
+
+{
+}
diff --git a/test/analyzer/gyptest-analyzer.new.py b/test/analyzer/gyptest-analyzer.new.py
index db7e1251..b7368671 100644
--- a/test/analyzer/gyptest-analyzer.new.py
+++ b/test/analyzer/gyptest-analyzer.new.py
@@ -42,8 +42,14 @@ def run_analyzer(*args, **kw):
'-Ganalyzer_output_path=analyzer_output')
test.run_gyp('test.gyp', *args, **kw)
+def run_analyzer2(*args, **kw):
+ """Runs the test specifying a particular config and output path."""
+ args += ('-Gconfig_path=test_file',
+ '-Ganalyzer_output_path=analyzer_output')
+ test.run_gyp('test2.gyp', *args, **kw)
+
def EnsureContains(targets=set(), matched=False):
- """Verifies output contains |targets| and |direct_targets|."""
+ """Verifies output contains |targets|."""
result = _ReadOutputFileContents()
if result.get('error', None):
print 'unexpected error', result.get('error')
@@ -196,4 +202,28 @@ _CreateTestFile(['exe2.c'], [])
run_analyzer()
EnsureContains(matched=True)
+# Assertions when modifying build (gyp/gypi) files, especially when said files
+# are included.
+_CreateTestFile(['subdir2/d.cc'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'foo'})
+
+_CreateTestFile(['subdir2/subdir.includes.gypi'],
+ ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'foo'})
+
+_CreateTestFile(['subdir2/subdir.gyp'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'foo'})
+
+_CreateTestFile(['test2.includes.gypi'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'exe2', 'exe3'})
+
+# Verify modifying a file included makes all targets dirty.
+_CreateTestFile(['common.gypi'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2('-Icommon.gypi')
+EnsureContains(matched=True, targets={'exe', 'foo', 'exe2', 'exe3'})
+
test.pass_test()
diff --git a/test/analyzer/subdir2/subdir.gyp b/test/analyzer/subdir2/subdir.gyp
new file mode 100644
index 00000000..d6c709c9
--- /dev/null
+++ b/test/analyzer/subdir2/subdir.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'foo',
+ 'type': 'static_library',
+ 'sources': [
+ 'subdir_source.c',
+ ],
+ 'includes': [
+ 'subdir.includes.gypi',
+ ],
+ },
+ ],
+}
diff --git a/test/analyzer/subdir2/subdir.includes.gypi b/test/analyzer/subdir2/subdir.includes.gypi
new file mode 100644
index 00000000..324e92bc
--- /dev/null
+++ b/test/analyzer/subdir2/subdir.includes.gypi
@@ -0,0 +1,9 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'sources': [
+ 'd.cc'
+ ],
+}
diff --git a/test/analyzer/test2.gyp b/test/analyzer/test2.gyp
new file mode 100644
index 00000000..782b6e64
--- /dev/null
+++ b/test/analyzer/test2.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'exe',
+ 'type': 'executable',
+ 'dependencies': [
+ 'subdir2/subdir.gyp:foo',
+ ],
+ },
+ {
+ 'target_name': 'exe2',
+ 'type': 'executable',
+ 'includes': [
+ 'test2.includes.gypi',
+ ],
+ },
+ ],
+ 'includes': [
+ 'test2.toplevel_includes.gypi',
+ ],
+}
diff --git a/test/analyzer/test2.includes.gypi b/test/analyzer/test2.includes.gypi
new file mode 100644
index 00000000..3e21de23
--- /dev/null
+++ b/test/analyzer/test2.includes.gypi
@@ -0,0 +1,13 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'sources': [
+ 'a.cc',
+ 'b.cc'
+ ],
+ 'includes': [
+ 'test2.includes.includes.gypi',
+ ],
+}
diff --git a/test/analyzer/test2.includes.includes.gypi b/test/analyzer/test2.includes.includes.gypi
new file mode 100644
index 00000000..de3a025d
--- /dev/null
+++ b/test/analyzer/test2.includes.includes.gypi
@@ -0,0 +1,9 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'sources': [
+ 'c.cc'
+ ],
+}
diff --git a/test/analyzer/test2.toplevel_includes.gypi b/test/analyzer/test2.toplevel_includes.gypi
new file mode 100644
index 00000000..54fa453b
--- /dev/null
+++ b/test/analyzer/test2.toplevel_includes.gypi
@@ -0,0 +1,15 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'exe3',
+ 'type': 'executable',
+ 'sources': [
+ 'e.cc',
+ ],
+ },
+ ],
+}