diff options
author | Android Chromium Automerger <chromium-automerger@android> | 2014-08-19 16:00:04 +0000 |
---|---|---|
committer | Android Chromium Automerger <chromium-automerger@android> | 2014-08-19 16:00:04 +0000 |
commit | ac39e3a7fd8efa1ee3596507d7d6c59f38ebe9cf (patch) | |
tree | 75a724b27a45d1cbdb048a6225d43827cc95d295 | |
parent | 31a32b697f297638d203d82f2b7b1b7fc82dfc98 (diff) | |
parent | b38e5f2f93fa5b31a6001bd21b9d8dcbf73e44e7 (diff) | |
download | gyp-ac39e3a7fd8efa1ee3596507d7d6c59f38ebe9cf.tar.gz |
Merge tools/gyp from https://chromium.googlesource.com/external/gyp.git at b38e5f2f93fa5b31a6001bd21b9d8dcbf73e44e7
This commit was generated by merge_from_chromium.py.
Change-Id: Ide8ea95cde4d50b827c99556bef533b85232e6b9
-rw-r--r-- | pylib/gyp/generator/analyzer.py | 330 | ||||
-rw-r--r-- | pylib/gyp/msvs_emulation.py | 2 | ||||
-rw-r--r-- | test/analyzer/gyptest-analyzer.new.py | 229 | ||||
-rw-r--r-- | test/analyzer/gyptest-analyzer.py | 349 | ||||
-rw-r--r-- | test/analyzer/test.gyp | 32 | ||||
-rw-r--r-- | test/analyzer/test3.gyp | 77 | ||||
-rw-r--r-- | test/analyzer/test4.gyp | 80 | ||||
-rw-r--r-- | test/analyzer/test5.gyp | 25 | ||||
-rw-r--r-- | test/win/gyptest-link-pdb-no-output.py | 25 | ||||
-rw-r--r-- | test/win/gyptest-link-pdb-output.py | 1 | ||||
-rw-r--r-- | test/win/linker-flags/pdb-output.gyp | 13 |
11 files changed, 778 insertions, 385 deletions
diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py index 8a8ac70c..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,41 +166,47 @@ 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 - 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""" + targets: see file description for details.""" def __init__(self): - self.look_for_dependency_only = True self.files = [] - self.targets = [] + self.targets = set() 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) @@ -204,20 +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', []))) - - 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) + self.targets = set(config.get('targets', [])) def _WasBuildFileModified(build_file, data, files): @@ -244,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 @@ -306,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: @@ -360,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) @@ -389,9 +501,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') @@ -399,53 +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 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: - print found_dependency_string if matched else no_dependency_string + if _WasGypIncludeFileModified(params, config.files): + result_dict = { 'status': all_changed_string, + 'targets': list(config.targets) } + _WriteOutput(params, **result_dict) return + 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/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/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..89d62522 100644 --- a/test/analyzer/gyptest-analyzer.py +++ b/test/analyzer/gyptest-analyzer.py @@ -6,75 +6,350 @@ """Tests for analyzer """ +import json import TestGyp -found = 'Found dependency\n' -not_found = 'No dependencies\n' +found = 'Found dependency' +found_all = 'Found dependency (all)' +not_found = 'No dependencies' -def __CreateTestFile(files): + +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') - for file in files: - f.write(file + '\n') + to_write = {'files': files, 'targets': targets } + json.dump(to_write, f) f.close() + +def _CreateBogusConfigFile(): + 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 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 += CommonArgs() + test.run_gyp('test.gyp', *args, **kw) + + +def run_analyzer2(*args, **kw): + """Same as run_analyzer(), but passes in test2.gyp instead of test.gyp.""" + args += CommonArgs() + test.run_gyp('test2.gyp', *args, **kw) + + +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): + 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() + + 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() + elif not matched and result['status'] != not_found: + print 'expected', not_found, 'got', result['status'] + 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() + 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 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. +_CreateBogusConfigFile() +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) +_CreateConfigFile(['foo.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe'}) # Conditional source that is excluded. -__CreateTestFile(['conditional_source.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateConfigFile(['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) +_CreateConfigFile(['conditional_source.c'], []) +run_analyzer('-Dtest_variable=1') +EnsureContains(matched=True, build_targets={'exe'}) # Two unknown files. -__CreateTestFile(['unknown1.c', 'unoknow2.cc']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateConfigFile(['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) +_CreateConfigFile(['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) +_CreateConfigFile(['unknown1.c', 'subdir/subdir_source.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe', 'exe3'}) # Included inputs to actions. -__CreateTestFile(['action_input.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateConfigFile(['action_input.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe'}) # Don't consider outputs. -__CreateTestFile(['action_output.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=not_found) +_CreateConfigFile(['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) +_CreateConfigFile(['rule_input.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe'}) -# 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. +_CreateConfigFile(['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) +_CreateConfigFile(['subdir/subdir_source2.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe'}) # Verifies paths with // are fixed up correctly. -__CreateTestFile(['parent_source.c']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateConfigFile(['parent_source.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe', 'exe3'}) # Verifies relative paths are resolved correctly. -__CreateTestFile(['subdir/subdir_source.h']) -test.run_gyp('test.gyp', '-Gfile_path=test_file', stdout=found) +_CreateConfigFile(['subdir/subdir_source.h'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe'}) + +# Various permutations when passing in targets. +_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe3']) +run_analyzer() +EnsureContains(matched=True, targets={'exe3'}, build_targets={'exe2', 'exe3'}) + +_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe']) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe2', 'exe3'}) + +# Verifies duplicates are ignored. +_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, build_targets={'exe2'}) + +_CreateConfigFile(['subdir/subdir2b_source.c', 'exe2.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe2', 'exe3'}) + +_CreateConfigFile(['subdir/subdir2b_source.c'], ['exe3']) +run_analyzer() +EnsureContains(matched=True, targets={'exe3'}, build_targets={'exe3'}) + +_CreateConfigFile(['exe2.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe2'}) + +_CreateConfigFile(['foo.c'], []) +run_analyzer() +EnsureContains(matched=True, build_targets={'exe'}) + +# Assertions when modifying build (gyp/gypi) files, especially when said files +# are included. +_CreateConfigFile(['subdir2/d.cc'], ['exe', 'exe2', 'foo', 'exe3']) +run_analyzer2() +EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'}) + +_CreateConfigFile(['subdir2/subdir.includes.gypi'], + ['exe', 'exe2', 'foo', 'exe3']) +run_analyzer2() +EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'}) + +_CreateConfigFile(['subdir2/subdir.gyp'], ['exe', 'exe2', 'foo', 'exe3']) +run_analyzer2() +EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'}) + +_CreateConfigFile(['test2.includes.gypi'], ['exe', 'exe2', 'foo', 'exe3']) +run_analyzer2() +EnsureContains(matched=True, targets={'exe', 'exe2', 'exe3'}, + build_targets={'exe', 'exe2', 'exe3'}) + +# Verify modifying a file included makes all targets dirty. +_CreateConfigFile(['common.gypi'], ['exe', 'exe2', 'foo', 'exe3']) +run_analyzer2('-Icommon.gypi') +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', + ], + }, + ], +} 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', + }, + }, + }, ] } |