diff options
author | sky@chromium.org <sky@chromium.org@78cadc50-ecff-11dd-a971-7dbc132099af> | 2014-07-30 20:29:26 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@78cadc50-ecff-11dd-a971-7dbc132099af> | 2014-07-30 20:29:26 +0000 |
commit | ac2684beed6e8ca04ac5f837c9db869eefec7bb5 (patch) | |
tree | f3bd9df0b8e3d4bf997d6bee0c3d266652ea329b | |
parent | 39bb8956231c997babf0f25befdfb531f4d0b43c (diff) | |
download | gyp-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.py | 39 | ||||
-rw-r--r-- | test/analyzer/gyptest-analyzer.new.py | 199 |
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() |