summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@78cadc50-ecff-11dd-a971-7dbc132099af>2014-07-30 20:29:26 +0000
committersky@chromium.org <sky@chromium.org@78cadc50-ecff-11dd-a971-7dbc132099af>2014-07-30 20:29:26 +0000
commitac2684beed6e8ca04ac5f837c9db869eefec7bb5 (patch)
treef3bd9df0b8e3d4bf997d6bee0c3d266652ea329b
parent39bb8956231c997babf0f25befdfb531f4d0b43c (diff)
downloadgyp-ac2684beed6e8ca04ac5f837c9db869eefec7bb5.tar.gz
Updates analyzer to output to a file
File is specified by way of analyzer_output_path. I'm also changing from a hard error if a target can't be found to a warning. I'm also including a new set of tests. I forgot to svn add this last time around. BUG=109173 TEST=covered by tests R=mark@chromium.org Review URL: https://codereview.chromium.org/429243003 git-svn-id: http://gyp.googlecode.com/svn/trunk@1959 78cadc50-ecff-11dd-a971-7dbc132099af
-rw-r--r--pylib/gyp/generator/analyzer.py39
-rw-r--r--test/analyzer/gyptest-analyzer.new.py199
2 files changed, 231 insertions, 7 deletions
diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py
index 6f3b610c..dc55da67 100644
--- a/pylib/gyp/generator/analyzer.py
+++ b/pylib/gyp/generator/analyzer.py
@@ -9,11 +9,15 @@ and targets to search for. The following keys are supported:
files: list of paths (relative) of the files to search for.
targets: list of targets to search for. The target names are unqualified.
-The following (as JSON) is output:
+The following is output:
error: only supplied if there is an error.
+warning: only supplied if there is a warning.
targets: the set of targets passed in via targets that either directly or
indirectly depend upon the set of paths supplied in files.
status: indicates if any of the supplied files matched at least one target.
+
+If the generator flag analyzer_output_path is specified, output is written
+there. Otherwise output is written to stdout.
"""
import gyp.common
@@ -293,6 +297,20 @@ def _GetTargetsDependingOn(all_targets, possible_targets):
found.append(gyp.common.ParseQualifiedTarget(target)[1])
return found
+def _WriteOutput(params, **values):
+ """Writes the output, either to stdout or a file is specified."""
+ output_path = params.get('generator_flags', {}).get(
+ 'analyzer_output_path', None)
+ if not output_path:
+ print json.dumps(values)
+ return
+ try:
+ f = open(output_path, 'w')
+ f.write(json.dumps(values) + '\n')
+ f.close()
+ except IOError as e:
+ print 'Error writing to output file', output_path, str(e)
+
def CalculateVariables(default_variables, params):
"""Calculate additional variables for use in the build (called by gyp)."""
flavor = gyp.common.GetFlavor(params)
@@ -342,6 +360,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
print found_dependency_string if matched else no_dependency_string
return
+ warning = None
if matched:
unqualified_mapping = _GetUnqualifiedToQualifiedMapping(
all_targets, config.targets)
@@ -350,15 +369,21 @@ def GenerateOutput(target_list, target_dicts, data, params):
for target in config.targets:
if not target in unqualified_mapping:
not_found.append(target)
- raise Exception('Unable to find all targets: ' + str(not_found))
- qualified_targets = [unqualified_mapping[x] for x in config.targets]
+ warning = 'Unable to find all targets: ' + str(not_found)
+ qualified_targets = []
+ for target in config.targets:
+ if target in unqualified_mapping:
+ qualified_targets.append(unqualified_mapping[target])
output_targets = _GetTargetsDependingOn(all_targets, qualified_targets)
else:
output_targets = []
- print json.dumps(
- {'targets': output_targets,
- 'status': found_dependency_string if matched else no_dependency_string })
+ result_dict = { 'targets': output_targets,
+ 'status': found_dependency_string if matched else
+ no_dependency_string }
+ if warning:
+ result_dict['warning'] = warning
+ _WriteOutput(params, **result_dict)
except Exception as e:
- print json.dumps({'error': str(e)})
+ _WriteOutput(params, error=str(e))
diff --git a/test/analyzer/gyptest-analyzer.new.py b/test/analyzer/gyptest-analyzer.new.py
new file mode 100644
index 00000000..db7e1251
--- /dev/null
+++ b/test/analyzer/gyptest-analyzer.new.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for analyzer
+"""
+
+import json
+import TestGyp
+
+# TODO(sky): when done migrating recipes rename to gyptest-analyzer and nuke
+# existing gyptest-analyzer.
+
+found = 'Found dependency'
+not_found = 'No dependencies'
+
+def _CreateTestFile(files, targets):
+ f = open('test_file', 'w')
+ to_write = {'files': files, 'targets': targets }
+ json.dump(to_write, f)
+ f.close()
+
+def _CreateBogusTestFile():
+ f = open('test_file','w')
+ f.write('bogus')
+ f.close()
+
+def _ReadOutputFileContents():
+ f = open('analyzer_output', 'r')
+ result = json.load(f)
+ f.close()
+ return result
+
+# NOTE: this would be clearer if it subclassed TestGypCustom, but that trips
+# over a bug in pylint (E1002).
+test = TestGyp.TestGypCustom(format='analyzer')
+
+def run_analyzer(*args, **kw):
+ """Runs the test specifying a particular config and output path."""
+ args += ('-Gconfig_path=test_file',
+ '-Ganalyzer_output_path=analyzer_output')
+ test.run_gyp('test.gyp', *args, **kw)
+
+def EnsureContains(targets=set(), matched=False):
+ """Verifies output contains |targets| and |direct_targets|."""
+ result = _ReadOutputFileContents()
+ if result.get('error', None):
+ print 'unexpected error', result.get('error')
+ test.fail_test()
+
+ if result.get('warning', None):
+ print 'unexpected warning', result.get('warning')
+ test.fail_test()
+
+ actual_targets = set(result['targets'])
+ if actual_targets != targets:
+ print 'actual targets:', actual_targets, '\nexpected targets:', targets
+ test.fail_test()
+
+ if matched and result['status'] != found:
+ print 'expected', found, 'got', result['status']
+ test.fail_test()
+ elif not matched and result['status'] != not_found:
+ print 'expected', not_found, 'got', result['status']
+ test.fail_test()
+
+def EnsureError(expected_error_string):
+ """Verifies output contains the error string."""
+ result = _ReadOutputFileContents()
+ if result.get('error', '').find(expected_error_string) == -1:
+ print 'actual error:', result.get('error', ''), '\nexpected error:', \
+ expected_error_string
+ test.fail_test()
+
+def EnsureWarning(expected_warning_string):
+ """Verifies output contains the warning string."""
+ result = _ReadOutputFileContents()
+ if result.get('warning', '').find(expected_warning_string) == -1:
+ print 'actual warning:', result.get('warning', ''), \
+ '\nexpected warning:', expected_warning_string
+ test.fail_test()
+
+# Verifies file_path must be specified.
+test.run_gyp('test.gyp',
+ stdout='Must specify files to analyze via file_path generator '
+ 'flag\n')
+
+# Verifies config_path must point to a valid file.
+test.run_gyp('test.gyp', '-Gconfig_path=bogus_file',
+ '-Ganalyzer_output_path=analyzer_output')
+EnsureError('Unable to open file bogus_file')
+
+# Verify get error when bad target is specified.
+_CreateTestFile(['exe2.c'], ['bad_target'])
+run_analyzer()
+EnsureWarning('Unable to find all targets')
+
+# Verifies config_path must point to a valid json file.
+_CreateBogusTestFile()
+run_analyzer()
+EnsureError('Unable to parse config file test_file')
+
+# Trivial test of a source.
+_CreateTestFile(['foo.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Conditional source that is excluded.
+_CreateTestFile(['conditional_source.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Conditional source that is included by way of argument.
+_CreateTestFile(['conditional_source.c'], [])
+run_analyzer('-Dtest_variable=1')
+EnsureContains(matched=True)
+
+# Two unknown files.
+_CreateTestFile(['unknown1.c', 'unoknow2.cc'], [])
+run_analyzer()
+EnsureContains()
+
+# Two unknown files.
+_CreateTestFile(['unknown1.c', 'subdir/subdir_sourcex.c'], [])
+run_analyzer()
+EnsureContains()
+
+# Included dependency
+_CreateTestFile(['unknown1.c', 'subdir/subdir_source.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Included inputs to actions.
+_CreateTestFile(['action_input.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Don't consider outputs.
+_CreateTestFile(['action_output.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Rule inputs.
+_CreateTestFile(['rule_input.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Ignore path specified with PRODUCT_DIR.
+_CreateTestFile(['product_dir_input.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Path specified via a variable.
+_CreateTestFile(['subdir/subdir_source2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Verifies paths with // are fixed up correctly.
+_CreateTestFile(['parent_source.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Verifies relative paths are resolved correctly.
+_CreateTestFile(['subdir/subdir_source.h'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Various permutations when passing in targets.
+_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe3'])
+run_analyzer()
+EnsureContains(matched=True, targets={'exe3'})
+
+_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe'])
+run_analyzer()
+EnsureContains(matched=True)
+
+# Verifies duplicates are ignored.
+_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe'])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['exe2.c'], ['exe'])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['subdir/subdir2b_source.c', 'exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+_CreateTestFile(['exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True)
+
+test.pass_test()