From 8432ec2cc86f37f48e15dd18d2c85c10cd70b6e2 Mon Sep 17 00:00:00 2001 From: "sky@chromium.org" Date: Wed, 13 Aug 2014 16:40:36 +0000 Subject: Removes old way to specify files to look for This was from the first version and is no longer used. Also cleaned up tests as well. BUG=none TEST=none R=scottmg@chromium.org Review URL: https://codereview.chromium.org/473483002 git-svn-id: http://gyp.googlecode.com/svn/trunk@1965 78cadc50-ecff-11dd-a971-7dbc132099af --- pylib/gyp/generator/analyzer.py | 37 +----- test/analyzer/gyptest-analyzer.new.py | 229 --------------------------------- test/analyzer/gyptest-analyzer.py | 236 ++++++++++++++++++++++++++++------ 3 files changed, 201 insertions(+), 301 deletions(-) delete mode 100644 test/analyzer/gyptest-analyzer.new.py diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py index 8a8ac70c..b5a8155b 100644 --- a/pylib/gyp/generator/analyzer.py +++ b/pylib/gyp/generator/analyzer.py @@ -167,32 +167,19 @@ class Target(object): class Config(object): """Details what we're looking for - look_for_dependency_only: if true only search for a target listing any of - the files in files. files: set of files to search for targets: see file description for details""" def __init__(self): - self.look_for_dependency_only = True self.files = [] self.targets = [] def Init(self, params): - """Initializes Config. This is a separate method as it may raise an - exception if there is a parse error.""" + """Initializes Config. This is a separate method as it raises an exception + if there is a parse error.""" generator_flags = params.get('generator_flags', {}) - # TODO(sky): nuke file_path and look_for_dependency_only once migrate - # recipes. - file_path = generator_flags.get('file_path', None) - if file_path: - self._InitFromFilePath(file_path) - return - - # If |file_path| wasn't specified then we look for config_path. - # TODO(sky): always look for config_path once migrated recipes. config_path = generator_flags.get('config_path', None) if not config_path: return - self.look_for_dependency_only = False try: f = open(config_path, 'r') config = json.load(f) @@ -207,18 +194,6 @@ class Config(object): # Coalesce duplicates self.targets = list(set(config.get('targets', []))) - def _InitFromFilePath(self, file_path): - try: - f = open(file_path, 'r') - for file_name in f: - if file_name.endswith('\n'): - file_name = file_name[0:len(file_name) - 1] - if len(file_name): - self.files.append(file_name) - f.close() - except IOError: - raise Exception('Unable to open file', file_path) - def _WasBuildFileModified(build_file, data, files): """Returns true if the build file |build_file| is either in |files| or @@ -389,9 +364,6 @@ def GenerateOutput(target_list, target_dicts, data, params): try: config.Init(params) if not config.files: - if config.look_for_dependency_only: - print 'Must specify files to analyze via file_path generator flag' - return raise Exception('Must specify files to analyze via config_path generator ' 'flag') @@ -418,11 +390,6 @@ def GenerateOutput(target_list, target_dicts, data, params): toplevel_dir, frozenset(config.files)) - # Set of targets that refer to one of the files. - if config.look_for_dependency_only: - print found_dependency_string if matched else no_dependency_string - return - warning = None if matched_include: output_targets = config.targets diff --git a/test/analyzer/gyptest-analyzer.new.py b/test/analyzer/gyptest-analyzer.new.py deleted file mode 100644 index b7368671..00000000 --- a/test/analyzer/gyptest-analyzer.new.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/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 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|.""" - 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) - -# 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/gyptest-analyzer.py b/test/analyzer/gyptest-analyzer.py index e3746279..df249a14 100644 --- a/test/analyzer/gyptest-analyzer.py +++ b/test/analyzer/gyptest-analyzer.py @@ -6,75 +6,237 @@ """Tests for analyzer """ +import json import TestGyp -found = 'Found dependency\n' -not_found = 'No dependencies\n' +found = 'Found dependency' +not_found = 'No dependencies' -def __CreateTestFile(files): + +def _CreateTestFile(files, targets): f = open('test_file', 'w') - for file in files: - f.write(file + '\n') + 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') -# Verifies file_path must be specified. -test.run_gyp('test.gyp', - stdout='Must specify files to analyze via file_path generator ' - 'flag\n') + +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 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|.""" + 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 EnsureStdoutContains(expected_error_string): + if test.stdout().find(expected_error_string) == -1: + print 'actual stdout:', test.stdout(), '\nexpected stdout:', \ + 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 config_path must be specified. +test.run_gyp('test.gyp') +EnsureStdoutContains('Must specify files to analyze via config_path') + +# 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']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateTestFile(['foo.c'], []) +run_analyzer() +EnsureContains(matched=True) # Conditional source that is excluded. -__CreateTestFile(['conditional_source.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateTestFile(['conditional_source.c'], []) +run_analyzer() +EnsureContains(matched=False) # Conditional source that is included by way of argument. -__CreateTestFile(['conditional_source.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', '-Dtest_variable=1', - stdout=found) +_CreateTestFile(['conditional_source.c'], []) +run_analyzer('-Dtest_variable=1') +EnsureContains(matched=True) # Two unknown files. -__CreateTestFile(['unknown1.c', 'unoknow2.cc']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateTestFile(['unknown1.c', 'unoknow2.cc'], []) +run_analyzer() +EnsureContains() # Two unknown files. -__CreateTestFile(['unknown1.c', 'subdir/subdir_sourcex.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateTestFile(['unknown1.c', 'subdir/subdir_sourcex.c'], []) +run_analyzer() +EnsureContains() # Included dependency -__CreateTestFile(['unknown1.c', 'subdir/subdir_source.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateTestFile(['unknown1.c', 'subdir/subdir_source.c'], []) +run_analyzer() +EnsureContains(matched=True) # Included inputs to actions. -__CreateTestFile(['action_input.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateTestFile(['action_input.c'], []) +run_analyzer() +EnsureContains(matched=True) # Don't consider outputs. -__CreateTestFile(['action_output.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateTestFile(['action_output.c'], []) +run_analyzer() +EnsureContains(matched=False) # Rule inputs. -__CreateTestFile(['rule_input.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateTestFile(['rule_input.c'], []) +run_analyzer() +EnsureContains(matched=True) -# Ignore patch specified with PRODUCT_DIR. -__CreateTestFile(['product_dir_input.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +# 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']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateTestFile(['subdir/subdir_source2.c'], []) +run_analyzer() +EnsureContains(matched=True) # Verifies paths with // are fixed up correctly. -__CreateTestFile(['parent_source.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateTestFile(['parent_source.c'], []) +run_analyzer() +EnsureContains(matched=True) # Verifies relative paths are resolved correctly. -__CreateTestFile(['subdir/subdir_source.h']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_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) + +# 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() -- cgit v1.2.3 From ce177a087448f95dffe7a66bf7d789ff1bfc858b Mon Sep 17 00:00:00 2001 From: "sky@chromium.org" Date: Mon, 18 Aug 2014 20:24:09 +0000 Subject: Makes the analyzer output the set of targets needing a build The set of build targets is the minimal set of targets reachable from the all target that contains one of the specified files (matched target), or depeneds on the set of matched targets. BUG=109173 TEST=none R=scottmg@chromium.org Review URL: https://codereview.chromium.org/481433003 git-svn-id: http://gyp.googlecode.com/svn/trunk@1966 78cadc50-ecff-11dd-a971-7dbc132099af --- pylib/gyp/generator/analyzer.py | 293 +++++++++++++++++++++++++++----------- test/analyzer/gyptest-analyzer.py | 227 +++++++++++++++++++++-------- test/analyzer/test.gyp | 32 ++++- test/analyzer/test3.gyp | 77 ++++++++++ test/analyzer/test4.gyp | 80 +++++++++++ test/analyzer/test5.gyp | 25 ++++ 6 files changed, 595 insertions(+), 139 deletions(-) create mode 100644 test/analyzer/test3.gyp create mode 100644 test/analyzer/test4.gyp create mode 100644 test/analyzer/test5.gyp diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py index b5a8155b..ba53e493 100644 --- a/pylib/gyp/generator/analyzer.py +++ b/pylib/gyp/generator/analyzer.py @@ -14,7 +14,13 @@ 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. +build_targets: minimal set of targets that directly depend on the changed + files and need to be built. The expectation is this set of targets is passed + into a build step. +status: outputs one of three values: none of the supplied files were found, + one of the include files changed so that it should be assumed everything + changed (in this case targets and build_targets are not output) or at + least one file was found. If the generator flag analyzer_output_path is specified, output is written there. Otherwise output is written to stdout. @@ -31,6 +37,8 @@ debug = False found_dependency_string = 'Found dependency' no_dependency_string = 'No dependencies' +# Status when it should be assumed that everything has changed. +all_changed_string = 'Found dependency (all)' # MatchStatus is used indicate if and how a target depends upon the supplied # sources. @@ -143,7 +151,7 @@ def _ExtractSources(target, target_dict, toplevel_dir): if 'sources' in target_dict: _AddSources(target_dict['sources'], base_path, base_path_components, results) - # Include the inputs from any actions. Any changes to these effect the + # Include the inputs from any actions. Any changes to these affect the # resulting output. if 'actions' in target_dict: for action in target_dict['actions']: @@ -158,20 +166,39 @@ def _ExtractSources(target, target_dict, toplevel_dir): class Target(object): """Holds information about a particular target: - deps: set of the names of direct dependent targets. - match_staus: one of the MatchStatus values""" - def __init__(self): + deps: set of Targets this Target depends upon. This is not recursive, only the + direct dependent Targets. + match_status: one of the MatchStatus values. + back_deps: set of Targets that have a dependency on this Target. + visited: used during iteration to indicate whether we've visited this target. + This is used for two iterations, once in building the set of Targets and + again in _GetBuildTargets(). + name: fully qualified name of the target. + requires_build: True if the target type is such that it needs to be built. + See _DoesTargetTypeRequireBuild for details. + added_to_compile_targets: used when determining if the target was added to the + set of targets that needs to be built. + in_roots: true if this target is a descendant of one of the root nodes.""" + def __init__(self, name): self.deps = set() self.match_status = MATCH_STATUS_TBD + self.back_deps = set() + self.name = name + # TODO(sky): I don't like hanging this off Target. This state is specific + # to certain functions and should be isolated there. + self.visited = False + self.requires_build = False + self.added_to_compile_targets = False + self.in_roots = False class Config(object): """Details what we're looking for files: set of files to search for - targets: see file description for details""" + targets: see file description for details.""" def __init__(self): self.files = [] - self.targets = [] + self.targets = set() def Init(self, params): """Initializes Config. This is a separate method as it raises an exception @@ -191,8 +218,7 @@ class Config(object): if not isinstance(config, dict): raise Exception('config_path must be a JSON file containing a dictionary') self.files = config.get('files', []) - # Coalesce duplicates - self.targets = list(set(config.get('targets', []))) + self.targets = set(config.get('targets', [])) def _WasBuildFileModified(build_file, data, files): @@ -219,60 +245,105 @@ def _WasBuildFileModified(build_file, data, files): 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. - This returns a tuple of the dictionary and whether at least one target's - sources listed one of the paths in |files|.""" +def _GetOrCreateTargetByName(targets, target_name): + """Creates or returns the Target at targets[target_name]. If there is no + Target for |target_name| one is created. Returns a tuple of whether a new + Target was created and the Target.""" + if target_name in targets: + return False, targets[target_name] + target = Target(target_name) + targets[target_name] = target + return True, target + + +def _DoesTargetTypeRequireBuild(target_dict): + """Returns true if the target type is such that it needs to be built.""" + # If a 'none' target has rules or actions we assume it requires a build. + return target_dict['type'] != 'none' or \ + target_dict.get('actions') or target_dict.get('rules') + + +def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, + build_files): + """Returns a tuple of the following: + . A dictionary mapping from fully qualified name to Target. + . A list of the targets that have a source file in |files|. + . Set of root Targets reachable from the the files |build_files|. + This sets the |match_status| of the targets that contain any of the source + files in |files| to MATCH_STATUS_MATCHES. + |toplevel_dir| is the root of the source tree.""" + # Maps from target name to Target. targets = {} + # Targets that matched. + matching_targets = [] + # Queue of targets to visit. targets_to_visit = target_list[:] - matched = False - # Maps from build file to a boolean indicating whether the build file is in # |files|. build_file_in_files = {} + # Root targets across all files. + roots = set() + + # Set of Targets in |build_files|. + build_file_targets = set() + while len(targets_to_visit) > 0: target_name = targets_to_visit.pop() - if target_name in targets: + created_target, target = _GetOrCreateTargetByName(targets, target_name) + if created_target: + roots.add(target) + elif target.visited: continue - target = Target() - targets[target_name] = target + target.visited = True + target.requires_build = _DoesTargetTypeRequireBuild( + target_dicts[target_name]) 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 build_file in build_files: + build_file_targets.add(target) + # 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]: + print 'matching target from modified build file', target_name target.match_status = MATCH_STATUS_MATCHES - matched = True + matching_targets.append(target) else: sources = _ExtractSources(target_name, target_dicts[target_name], toplevel_dir) for source in sources: if source in files: + print 'target', target_name, 'matches', source target.match_status = MATCH_STATUS_MATCHES - matched = True + matching_targets.append(target) break + # Add dependencies to visit as well as updating back pointers for deps. for dep in target_dicts[target_name].get('dependencies', []): - targets[target_name].deps.add(dep) targets_to_visit.append(dep) - return targets, matched + created_dep_target, dep_target = _GetOrCreateTargetByName(targets, dep) + if not created_dep_target: + roots.discard(dep_target) + + target.deps.add(dep_target) + dep_target.back_deps.add(target) + return targets, matching_targets, roots & build_file_targets -def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): - """Returns a mapping (dictionary) from unqualified name to qualified name for - all the targets in |to_find|.""" + +def _GetUnqualifiedToTargetMapping(all_targets, to_find): + """Returns a mapping (dictionary) from unqualified name to Target for all the + Targets in |to_find|.""" result = {} if not to_find: return result @@ -281,47 +352,91 @@ def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): extracted = gyp.common.ParseQualifiedTarget(target_name) if len(extracted) > 1 and extracted[1] in to_find: to_find.remove(extracted[1]) - result[extracted[1]] = target_name + result[extracted[1]] = all_targets[target_name] if not to_find: return result return result -def _DoesTargetDependOn(target, all_targets): +def _DoesTargetDependOn(target): """Returns true if |target| or any of its dependencies matches the supplied set of paths. This updates |matches| of the Targets as it recurses. - target: the Target to look for. - all_targets: mapping from target name to Target. - matching_targets: set of targets looking for.""" + target: the Target to look for.""" if target.match_status == MATCH_STATUS_DOESNT_MATCH: return False if target.match_status == MATCH_STATUS_MATCHES or \ target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY: return True - for dep_name in target.deps: - dep_target = all_targets[dep_name] - if _DoesTargetDependOn(dep_target, all_targets): - dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY + for dep in target.deps: + if _DoesTargetDependOn(dep): + target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY return True - dep_target.match_status = MATCH_STATUS_DOESNT_MATCH + target.match_status = MATCH_STATUS_DOESNT_MATCH return False -def _GetTargetsDependingOn(all_targets, possible_targets): - """Returns the list of targets in |possible_targets| that depend (either - directly on indirectly) on the matched files. - all_targets: mapping from target name to Target. +def _GetTargetsDependingOn(possible_targets): + """Returns the list of Targets in |possible_targets| that depend (either + directly on indirectly) on the matched targets. possible_targets: targets to search from.""" found = [] for target in possible_targets: - if _DoesTargetDependOn(all_targets[target], all_targets): - # possible_targets was initially unqualified, keep it unqualified. - found.append(gyp.common.ParseQualifiedTarget(target)[1]) + if _DoesTargetDependOn(target): + found.append(target) return found +def _AddBuildTargets(target, roots, add_if_no_ancestor, result): + """Recurses through all targets that depend on |target|, adding all targets + that need to be built (and are in |roots|) to |result|. + roots: set of root targets. + add_if_no_ancestor: If true and there are no ancestors of |target| then add + |target| to |result|. |target| must still be in |roots|. + result: targets that need to be built are added here.""" + if target.visited: + return + + target.visited = True + target.in_roots = not target.back_deps and target in roots + + for back_dep_target in target.back_deps: + _AddBuildTargets(back_dep_target, roots, False, result) + target.added_to_compile_targets |= back_dep_target.added_to_compile_targets + target.in_roots |= back_dep_target.in_roots + + if not target.added_to_compile_targets and target.in_roots and \ + (add_if_no_ancestor or target.requires_build): + result.add(target) + target.added_to_compile_targets = True + + +def _GetBuildTargets(matching_targets, roots): + """Returns the set of Targets that require a build. + matching_targets: targets that changed and need to be built. + roots: set of root targets in the build files to search from.""" + result = set() + for target in matching_targets: + _AddBuildTargets(target, roots, True, result) + return result + + def _WriteOutput(params, **values): """Writes the output, either to stdout or a file is specified.""" + if 'error' in values: + print 'Error:', values['error'] + if 'status' in values: + print values['status'] + if 'targets' in values: + values['targets'].sort() + print 'Supplied targets that depend on changed files:' + for target in values['targets']: + print '\t', target + if 'build_targets' in values: + values['build_targets'].sort() + print 'Targets that require a build:' + for target in values['build_targets']: + print '\t', target + output_path = params.get('generator_flags', {}).get( 'analyzer_output_path', None) if not output_path: @@ -335,6 +450,28 @@ def _WriteOutput(params, **values): print 'Error writing to output file', output_path, str(e) +def _WasGypIncludeFileModified(params, files): + """Returns true if one of the files in |files| is in the set of included + files.""" + if params['options'].includes: + for include in params['options'].includes: + if _ToGypPath(include) in files: + print 'Include file modified, assuming all changed', include + return True + return False + + +def _NamesNotIn(names, mapping): + """Returns a list of the values in |names| that are not in |mapping|.""" + return [name for name in names if name not in mapping] + + +def _LookupTargets(names, mapping): + """Returns a list of the mapping[name] for each value in |names| that is in + |mapping|.""" + return [mapping[name] for name in names if name in mapping] + + def CalculateVariables(default_variables, params): """Calculate additional variables for use in the build (called by gyp).""" flavor = gyp.common.GetFlavor(params) @@ -371,48 +508,42 @@ def GenerateOutput(target_list, target_dicts, data, params): if debug: print 'toplevel_dir', toplevel_dir - 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 _WasGypIncludeFileModified(params, config.files): + result_dict = { 'status': all_changed_string, + 'targets': list(config.targets) } + _WriteOutput(params, **result_dict) + return - if not matched: - all_targets, matched = _GenerateTargets(data, target_list, target_dicts, - toplevel_dir, - frozenset(config.files)) + all_targets, matching_targets, roots = _GenerateTargets( + data, target_list, target_dicts, toplevel_dir, frozenset(config.files), + params['build_files']) warning = None - if matched_include: - output_targets = config.targets - elif matched: - unqualified_mapping = _GetUnqualifiedToQualifiedMapping( - all_targets, config.targets) - if len(unqualified_mapping) != len(config.targets): - not_found = [] - for target in config.targets: - if not target in unqualified_mapping: - not_found.append(target) - 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) + unqualified_mapping = _GetUnqualifiedToTargetMapping(all_targets, + config.targets) + if len(unqualified_mapping) != len(config.targets): + not_found = _NamesNotIn(config.targets, unqualified_mapping) + warning = 'Unable to find all targets: ' + str(not_found) + + if matching_targets: + search_targets = _LookupTargets(config.targets, unqualified_mapping) + matched_search_targets = _GetTargetsDependingOn(search_targets) + # Reset the visited status for _GetBuildTargets. + for target in all_targets.itervalues(): + target.visited = False + build_targets = _GetBuildTargets(matching_targets, roots) + matched_search_targets = [gyp.common.ParseQualifiedTarget(target.name)[1] + for target in matched_search_targets] + build_targets = [gyp.common.ParseQualifiedTarget(target.name)[1] + for target in build_targets] else: - output_targets = [] + matched_search_targets = [] + build_targets = [] - result_dict = { 'targets': output_targets, - 'status': found_dependency_string if matched else - no_dependency_string } + result_dict = { 'targets': matched_search_targets, + 'status': found_dependency_string if matching_targets else + no_dependency_string, + 'build_targets': build_targets} if warning: result_dict['warning'] = warning _WriteOutput(params, **result_dict) diff --git a/test/analyzer/gyptest-analyzer.py b/test/analyzer/gyptest-analyzer.py index df249a14..89d62522 100644 --- a/test/analyzer/gyptest-analyzer.py +++ b/test/analyzer/gyptest-analyzer.py @@ -10,17 +10,20 @@ import json import TestGyp found = 'Found dependency' +found_all = 'Found dependency (all)' not_found = 'No dependencies' -def _CreateTestFile(files, targets): +def _CreateConfigFile(files, targets): + """Creates the analyzer conflig file, which is used as the input to analyzer. + See description of analyzer.py for description of the arguments.""" f = open('test_file', 'w') to_write = {'files': files, 'targets': targets } json.dump(to_write, f) f.close() -def _CreateBogusTestFile(): +def _CreateBogusConfigFile(): f = open('test_file','w') f.write('bogus') f.close() @@ -37,22 +40,36 @@ def _ReadOutputFileContents(): # over a bug in pylint (E1002). test = TestGyp.TestGypCustom(format='analyzer') +def CommonArgs(): + return ('-Gconfig_path=test_file', + '-Ganalyzer_output_path=analyzer_output') + 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') + args += CommonArgs() 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') + """Same as run_analyzer(), but passes in test2.gyp instead of test.gyp.""" + args += CommonArgs() test.run_gyp('test2.gyp', *args, **kw) -def EnsureContains(targets=set(), matched=False): +def run_analyzer3(*args, **kw): + """Same as run_analyzer(), but passes in test3.gyp instead of test.gyp.""" + args += CommonArgs() + test.run_gyp('test3.gyp', *args, **kw) + + +def run_analyzer4(*args, **kw): + """Same as run_analyzer(), but passes in test3.gyp instead of test.gyp.""" + args += CommonArgs() + test.run_gyp('test4.gyp', *args, **kw) + + +def EnsureContains(targets=set(), matched=False, build_targets=set()): """Verifies output contains |targets|.""" result = _ReadOutputFileContents() if result.get('error', None): @@ -68,6 +85,12 @@ def EnsureContains(targets=set(), matched=False): print 'actual targets:', actual_targets, '\nexpected targets:', targets test.fail_test() + actual_build_targets = set(result['build_targets']) + if actual_build_targets != build_targets: + print 'actual build_targets:', actual_build_targets, \ + '\nexpected build_targets:', build_targets + test.fail_test() + if matched and result['status'] != found: print 'expected', found, 'got', result['status'] test.fail_test() @@ -76,6 +99,26 @@ def EnsureContains(targets=set(), matched=False): test.fail_test() +def EnsureMatchedAll(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() + + if result['status'] != found_all: + print 'expected', found_all, 'got', result['status'] + test.fail_test() + + actual_targets = set(result['targets']) + if actual_targets != targets: + print 'actual targets:', actual_targets, '\nexpected targets:', targets + test.fail_test() + + def EnsureError(expected_error_string): """Verifies output contains the error string.""" result = _ReadOutputFileContents() @@ -100,7 +143,6 @@ def EnsureWarning(expected_warning_string): '\nexpected warning:', expected_warning_string test.fail_test() - # Verifies config_path must be specified. test.run_gyp('test.gyp') EnsureStdoutContains('Must specify files to analyze via config_path') @@ -110,133 +152,204 @@ 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']) +# Verify get warning when bad target is specified. +_CreateConfigFile(['exe2.c'], ['bad_target']) run_analyzer() EnsureWarning('Unable to find all targets') # Verifies config_path must point to a valid json file. -_CreateBogusTestFile() +_CreateBogusConfigFile() run_analyzer() EnsureError('Unable to parse config file test_file') # Trivial test of a source. -_CreateTestFile(['foo.c'], []) +_CreateConfigFile(['foo.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Conditional source that is excluded. -_CreateTestFile(['conditional_source.c'], []) +_CreateConfigFile(['conditional_source.c'], []) run_analyzer() EnsureContains(matched=False) # Conditional source that is included by way of argument. -_CreateTestFile(['conditional_source.c'], []) +_CreateConfigFile(['conditional_source.c'], []) run_analyzer('-Dtest_variable=1') -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Two unknown files. -_CreateTestFile(['unknown1.c', 'unoknow2.cc'], []) +_CreateConfigFile(['unknown1.c', 'unoknow2.cc'], []) run_analyzer() EnsureContains() # Two unknown files. -_CreateTestFile(['unknown1.c', 'subdir/subdir_sourcex.c'], []) +_CreateConfigFile(['unknown1.c', 'subdir/subdir_sourcex.c'], []) run_analyzer() EnsureContains() # Included dependency -_CreateTestFile(['unknown1.c', 'subdir/subdir_source.c'], []) +_CreateConfigFile(['unknown1.c', 'subdir/subdir_source.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe', 'exe3'}) # Included inputs to actions. -_CreateTestFile(['action_input.c'], []) +_CreateConfigFile(['action_input.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Don't consider outputs. -_CreateTestFile(['action_output.c'], []) +_CreateConfigFile(['action_output.c'], []) run_analyzer() EnsureContains(matched=False) # Rule inputs. -_CreateTestFile(['rule_input.c'], []) +_CreateConfigFile(['rule_input.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Ignore path specified with PRODUCT_DIR. -_CreateTestFile(['product_dir_input.c'], []) +_CreateConfigFile(['product_dir_input.c'], []) run_analyzer() EnsureContains(matched=False) # Path specified via a variable. -_CreateTestFile(['subdir/subdir_source2.c'], []) +_CreateConfigFile(['subdir/subdir_source2.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Verifies paths with // are fixed up correctly. -_CreateTestFile(['parent_source.c'], []) +_CreateConfigFile(['parent_source.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe', 'exe3'}) # Verifies relative paths are resolved correctly. -_CreateTestFile(['subdir/subdir_source.h'], []) +_CreateConfigFile(['subdir/subdir_source.h'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Various permutations when passing in targets. -_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe3']) +_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe3']) run_analyzer() -EnsureContains(matched=True, targets={'exe3'}) +EnsureContains(matched=True, targets={'exe3'}, build_targets={'exe2', 'exe3'}) -_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe']) +_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe']) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe2', 'exe3'}) # Verifies duplicates are ignored. -_CreateTestFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe']) +_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe']) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe2', 'exe3'}) + +_CreateConfigFile(['exe2.c'], ['exe']) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe2'}) + +_CreateConfigFile(['exe2.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe2'}) -_CreateTestFile(['exe2.c'], ['exe']) +_CreateConfigFile(['subdir/subdir2b_source.c', 'exe2.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe2', 'exe3'}) -_CreateTestFile(['exe2.c'], []) +_CreateConfigFile(['subdir/subdir2b_source.c'], ['exe3']) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, targets={'exe3'}, build_targets={'exe3'}) -_CreateTestFile(['subdir/subdir2b_source.c', 'exe2.c'], []) +_CreateConfigFile(['exe2.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe2'}) -_CreateTestFile(['exe2.c'], []) +_CreateConfigFile(['foo.c'], []) run_analyzer() -EnsureContains(matched=True) +EnsureContains(matched=True, build_targets={'exe'}) # Assertions when modifying build (gyp/gypi) files, especially when said files # are included. -_CreateTestFile(['subdir2/d.cc'], ['exe', 'exe2', 'foo', 'exe3']) +_CreateConfigFile(['subdir2/d.cc'], ['exe', 'exe2', 'foo', 'exe3']) run_analyzer2() -EnsureContains(matched=True, targets={'exe', 'foo'}) +EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'}) -_CreateTestFile(['subdir2/subdir.includes.gypi'], +_CreateConfigFile(['subdir2/subdir.includes.gypi'], ['exe', 'exe2', 'foo', 'exe3']) run_analyzer2() -EnsureContains(matched=True, targets={'exe', 'foo'}) +EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'}) -_CreateTestFile(['subdir2/subdir.gyp'], ['exe', 'exe2', 'foo', 'exe3']) +_CreateConfigFile(['subdir2/subdir.gyp'], ['exe', 'exe2', 'foo', 'exe3']) run_analyzer2() -EnsureContains(matched=True, targets={'exe', 'foo'}) +EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'}) -_CreateTestFile(['test2.includes.gypi'], ['exe', 'exe2', 'foo', 'exe3']) +_CreateConfigFile(['test2.includes.gypi'], ['exe', 'exe2', 'foo', 'exe3']) run_analyzer2() -EnsureContains(matched=True, targets={'exe', 'exe2', 'exe3'}) +EnsureContains(matched=True, targets={'exe', 'exe2', 'exe3'}, + build_targets={'exe', 'exe2', 'exe3'}) # Verify modifying a file included makes all targets dirty. -_CreateTestFile(['common.gypi'], ['exe', 'exe2', 'foo', 'exe3']) +_CreateConfigFile(['common.gypi'], ['exe', 'exe2', 'foo', 'exe3']) run_analyzer2('-Icommon.gypi') -EnsureContains(matched=True, targets={'exe', 'foo', 'exe2', 'exe3'}) +EnsureMatchedAll({'exe', 'exe2', 'foo', 'exe3'}) + +# Assertions from test3.gyp. +_CreateConfigFile(['d.c', 'f.c'], ['a']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'}) + +_CreateConfigFile(['f.c'], ['a']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'}) + +_CreateConfigFile(['f.c'], []) +run_analyzer3() +EnsureContains(matched=True, build_targets={'a', 'b'}) + +_CreateConfigFile(['c.c', 'e.c'], []) +run_analyzer3() +EnsureContains(matched=True, build_targets={'a', 'b'}) + +_CreateConfigFile(['d.c'], ['a']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'}) + +_CreateConfigFile(['a.c'], ['a', 'b']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a'}) + +_CreateConfigFile(['a.c'], ['a', 'b']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a'}) + +_CreateConfigFile(['d.c'], ['a', 'b']) +run_analyzer3() +EnsureContains(matched=True, targets={'a', 'b'}, build_targets={'a', 'b'}) + +_CreateConfigFile(['f.c'], ['a']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'}) + +_CreateConfigFile(['a.c'], ['a']) +run_analyzer3() +EnsureContains(matched=True, targets={'a'}, build_targets={'a'}) + +_CreateConfigFile(['a.c'], []) +run_analyzer3() +EnsureContains(matched=True, build_targets={'a'}) + +_CreateConfigFile(['d.c'], []) +run_analyzer3() +EnsureContains(matched=True, build_targets={'a', 'b'}) + +# Assertions around test4.gyp. +_CreateConfigFile(['f.c'], []) +run_analyzer4() +EnsureContains(matched=True, build_targets={'e'}) + +_CreateConfigFile(['d.c'], []) +run_analyzer4() +EnsureContains(matched=True, build_targets={'a'}) + +_CreateConfigFile(['i.c'], []) +run_analyzer4() +EnsureContains(matched=True, build_targets={'h'}) test.pass_test() diff --git a/test/analyzer/test.gyp b/test/analyzer/test.gyp index afc312b2..a5201cba 100644 --- a/test/analyzer/test.gyp +++ b/test/analyzer/test.gyp @@ -2,6 +2,36 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# These gyp files create the following dependencies: +# +# test.gyp: +# #exe -> subdir/subdir.gyp#foo, subdir/subdir2/subdir2.gyp#subdir2 +# foo.c +# subdir/subdir_source2.c +# conditional_source.c (if test_variable==1) +# action_input.c +# action_output.c +# rule_input.c +# rule_output.pdf +# #exe2 +# exe2.c +# #exe3 -> subdir/subdir.gyp#foo, subdir/subdir.gyp#subdir2a +# exe3.c +# #all (type none) -> exe, exe3 +# +# subdir/subdir.gyp +# #foo +# subdir/subdir_source.c +# parent_source.c +# #subdir2a -> subdir2b +# subdir/subdir2_source.c +# #subdir2b +# subdir/subdir2b_source.c +# +# subdir/subdir2/subdir2.gyp +# #subdir2 +# subdir/subdir_source.h + { 'variables': { 'test_variable%': 0, @@ -73,7 +103,7 @@ }, { 'target_name': 'all', - 'type': 'executable', + 'type': 'none', 'dependencies': [ 'exe', 'exe3', diff --git a/test/analyzer/test3.gyp b/test/analyzer/test3.gyp new file mode 100644 index 00000000..a1fdfdde --- /dev/null +++ b/test/analyzer/test3.gyp @@ -0,0 +1,77 @@ +# 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': 'all', + 'type': 'none', + 'dependencies': [ + 'a', + 'b', + ], + }, + { + 'target_name': 'a', + 'type': 'executable', + 'sources': [ + 'a.c', + ], + 'dependencies': [ + 'c', + 'd', + ], + }, + { + 'target_name': 'b', + 'type': 'executable', + 'sources': [ + 'b.c', + ], + 'dependencies': [ + 'd', + 'e', + ], + }, + { + 'target_name': 'c', + 'type': 'executable', + 'sources': [ + 'c.c', + ], + }, + { + 'target_name': 'd', + 'type': 'none', + 'sources': [ + 'd.c', + ], + 'dependencies': [ + 'f', + 'g', + ], + }, + { + 'target_name': 'e', + 'type': 'executable', + 'sources': [ + 'e.c', + ], + }, + { + 'target_name': 'f', + 'type': 'executable', + 'sources': [ + 'f.c', + ], + }, + { + 'target_name': 'g', + 'type': 'executable', + 'sources': [ + 'g.c', + ], + }, + ], +} diff --git a/test/analyzer/test4.gyp b/test/analyzer/test4.gyp new file mode 100644 index 00000000..91cea56c --- /dev/null +++ b/test/analyzer/test4.gyp @@ -0,0 +1,80 @@ +# 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': 'a', + 'type': 'executable', + 'sources': [ + 'a.c', + ], + 'dependencies': [ + 'b', + 'c', + ], + }, + { + 'target_name': 'b', + 'type': 'executable', + 'sources': [ + 'b.c', + ], + 'dependencies': [ + 'd', + ], + }, + { + 'target_name': 'c', + 'type': 'executable', + 'sources': [ + 'c.c', + ], + 'dependencies': [ + 'b', + 'd', + ], + }, + { + 'target_name': 'd', + 'type': 'executable', + 'sources': [ + 'd.c', + ], + }, + { + 'target_name': 'e', + 'type': 'executable', + 'dependencies': [ + 'test5.gyp:f', + ], + }, + { + 'target_name': 'h', + 'type': 'none', + 'dependencies': [ + 'i', + ], + 'rules': [ + { + 'rule_name': 'rule', + 'extension': 'pdf', + 'inputs': [ + 'rule_input.c', + ], + 'outputs': [ + 'rule_output.pdf', + ], + }, + ], + }, + { + 'target_name': 'i', + 'type': 'static_library', + 'sources': [ + 'i.c', + ], + }, + ], +} diff --git a/test/analyzer/test5.gyp b/test/analyzer/test5.gyp new file mode 100644 index 00000000..f3ea5b00 --- /dev/null +++ b/test/analyzer/test5.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': 'f', + 'type': 'executable', + 'sources': [ + 'f.c', + ], + }, + { + 'target_name': 'g', + 'type': 'executable', + 'sources': [ + 'g.c', + ], + 'dependencies': [ + 'f', + ], + }, + ], +} -- cgit v1.2.3 From b38e5f2f93fa5b31a6001bd21b9d8dcbf73e44e7 Mon Sep 17 00:00:00 2001 From: "scottmg@chromium.org" Date: Tue, 19 Aug 2014 00:32:02 +0000 Subject: ninja win: don't expect pdb to be generated when GenerateDebugInformation: false if comparison was incorrect for truthy values when added at https://codereview.chromium.org/126443004/ (such as 'false'). Test addition fails before this CL. R=thakis@chromium.org, sky@chromium.org BUG=chromium:404872 Review URL: https://codereview.chromium.org/483133002 git-svn-id: http://gyp.googlecode.com/svn/trunk@1967 78cadc50-ecff-11dd-a971-7dbc132099af --- pylib/gyp/msvs_emulation.py | 2 +- test/win/gyptest-link-pdb-no-output.py | 25 +++++++++++++++++++++++++ test/win/gyptest-link-pdb-output.py | 1 - test/win/linker-flags/pdb-output.gyp | 13 +++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 test/win/gyptest-link-pdb-no-output.py diff --git a/pylib/gyp/msvs_emulation.py b/pylib/gyp/msvs_emulation.py index 5f71e9e1..5384df1c 100644 --- a/pylib/gyp/msvs_emulation.py +++ b/pylib/gyp/msvs_emulation.py @@ -370,7 +370,7 @@ class MsvsSettings(object): output_file = self._Setting(('VCLinkerTool', 'ProgramDatabaseFile'), config) generate_debug_info = self._Setting( ('VCLinkerTool', 'GenerateDebugInformation'), config) - if generate_debug_info: + if generate_debug_info == 'true': if output_file: return expand_special(self.ConvertVSMacros(output_file, config=config)) else: diff --git a/test/win/gyptest-link-pdb-no-output.py b/test/win/gyptest-link-pdb-no-output.py new file mode 100644 index 00000000..6da0aeae --- /dev/null +++ b/test/win/gyptest-link-pdb-no-output.py @@ -0,0 +1,25 @@ +#!/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. + +""" +Ensure that when debug information is not output, a pdb is not expected. +""" + +import TestGyp + +import os +import sys + +if sys.platform == 'win32': + test = TestGyp.TestGyp() + CHDIR = 'linker-flags' + test.run_gyp('pdb-output.gyp', chdir=CHDIR) + test.build('pdb-output.gyp', 'test_pdb_output_disabled', chdir=CHDIR) + # Make sure that the build doesn't expect a PDB to be generated when there + # will be none. + test.up_to_date('pdb-output.gyp', 'test_pdb_output_disabled', chdir=CHDIR) + + test.pass_test() diff --git a/test/win/gyptest-link-pdb-output.py b/test/win/gyptest-link-pdb-output.py index 8080410b..27245f7e 100644 --- a/test/win/gyptest-link-pdb-output.py +++ b/test/win/gyptest-link-pdb-output.py @@ -31,4 +31,3 @@ if sys.platform == 'win32': test.fail_test() test.pass_test() - diff --git a/test/win/linker-flags/pdb-output.gyp b/test/win/linker-flags/pdb-output.gyp index 21d3cd76..1a03c67c 100644 --- a/test/win/linker-flags/pdb-output.gyp +++ b/test/win/linker-flags/pdb-output.gyp @@ -32,5 +32,18 @@ }, }, }, + { + 'target_name': 'test_pdb_output_disabled', + 'type': 'executable', + 'sources': ['hello.cc'], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'DebugInformationFormat': '0' + }, + 'VCLinkerTool': { + 'GenerateDebugInformation': 'false', + }, + }, + }, ] } -- cgit v1.2.3