Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2014 Google Inc. All rights reserved. | 1 # Copyright (c) 2014 Google Inc. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """ | 5 """ |
| 6 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of | 6 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of |
| 7 the generator flag file_path) the list of relative file paths to consider. If | 7 the generator flag file_path) the list of relative file paths to consider. If |
| 8 any target has at least one of the paths as a source (or input to an action or | 8 any target has at least one of the paths as a source (or input to an action or |
| 9 rule) then 'Found dependency' is output, otherwise 'No dependencies' is output. | 9 rule) then 'Found dependency' is output, otherwise 'No dependencies' is output. |
| 10 """ | 10 """ |
| 11 | 11 |
| 12 import gyp.common | 12 import gyp.common |
| 13 import gyp.ninja_syntax as ninja_syntax | 13 import gyp.ninja_syntax as ninja_syntax |
| 14 import os | 14 import os |
| 15 import posixpath | 15 import posixpath |
| 16 | 16 |
| 17 debug = False | |
| 18 | |
| 17 generator_supports_multiple_toolsets = True | 19 generator_supports_multiple_toolsets = True |
| 18 | 20 |
| 19 generator_wants_static_library_dependencies_adjusted = False | 21 generator_wants_static_library_dependencies_adjusted = False |
| 20 | 22 |
| 21 generator_default_variables = { | 23 generator_default_variables = { |
| 22 } | 24 } |
| 23 for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR', | 25 for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR', |
| 24 'LIB_DIR', 'SHARED_LIB_DIR']: | 26 'LIB_DIR', 'SHARED_LIB_DIR']: |
| 25 generator_default_variables[dirname] = '!!!' | 27 generator_default_variables[dirname] = '!!!' |
| 26 | 28 |
| 27 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', | 29 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', |
| 28 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', | 30 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', |
| 29 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', | 31 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', |
| 30 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', | 32 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', |
| 31 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', | 33 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', |
| 32 'CONFIGURATION_NAME']: | 34 'CONFIGURATION_NAME']: |
| 33 generator_default_variables[unused] = '' | 35 generator_default_variables[unused] = '' |
| 34 | 36 |
| 35 def __MakeRelativeTargetName(path): | |
| 36 """Converts a gyp target name into a relative name. For example, the path to a | |
| 37 gyp file may be something like c:\foo\bar.gyp:target, this converts it to | |
| 38 bar.gyp. | |
| 39 """ | |
| 40 prune_path = os.getcwd() | |
| 41 if path.startswith(prune_path): | |
| 42 path = path[len(prune_path):] | |
| 43 if len(path) and path.startswith(os.sep): | |
| 44 path = path[len(os.sep):] | |
| 45 # Gyp paths are always posix style. | |
| 46 path = path.replace('\\', '/') | |
| 47 if path.endswith('#target'): | |
| 48 path = path[0:len(path) - len('#target')] | |
| 49 return path | |
| 50 | |
| 51 def __ExtractBasePath(target): | 37 def __ExtractBasePath(target): |
| 52 """Extracts the path components of the specified gyp target path.""" | 38 """Extracts the path components of the specified gyp target path.""" |
| 53 last_index = target.rfind('/') | 39 last_index = target.rfind('/') |
| 54 if last_index == -1: | 40 if last_index == -1: |
| 55 return '' | 41 return '' |
| 56 return target[0:(last_index + 1)] | 42 return target[0:(last_index + 1)] |
| 57 | 43 |
| 58 def __ResolveParent(path, base_path_components): | 44 def __ResolveParent(path, base_path_components): |
| 59 """Resolves |path|, which starts with at least one '../'. Returns an empty | 45 """Resolves |path|, which starts with at least one '../'. Returns an empty |
| 60 string if the path shouldn't be considered. See __AddSources() for a | 46 string if the path shouldn't be considered. See __AddSources() for a |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 77 source file is relative to |base_path|, but may contain '..'. To make | 63 source file is relative to |base_path|, but may contain '..'. To make |
| 78 resolving '..' easier |base_path_components| contains each of the | 64 resolving '..' easier |base_path_components| contains each of the |
| 79 directories in |base_path|. Additionally each source may contain variables. | 65 directories in |base_path|. Additionally each source may contain variables. |
| 80 Such sources are ignored as it is assumed dependencies on them are expressed | 66 Such sources are ignored as it is assumed dependencies on them are expressed |
| 81 and tracked in some other means.""" | 67 and tracked in some other means.""" |
| 82 # NOTE: gyp paths are always posix style. | 68 # NOTE: gyp paths are always posix style. |
| 83 for source in sources: | 69 for source in sources: |
| 84 if not len(source) or source.startswith('!!!') or source.startswith('$'): | 70 if not len(source) or source.startswith('!!!') or source.startswith('$'): |
| 85 continue | 71 continue |
| 86 # variable expansion may lead to //. | 72 # variable expansion may lead to //. |
| 73 org_source = source | |
| 87 source = source[0] + source[1:].replace('//', '/') | 74 source = source[0] + source[1:].replace('//', '/') |
| 88 if source.startswith('../'): | 75 if source.startswith('../'): |
| 89 source = __ResolveParent(source, base_path_components) | 76 source = __ResolveParent(source, base_path_components) |
| 90 if len(source): | 77 if len(source): |
| 91 result.append(source) | 78 result.append(source) |
| 92 continue | 79 continue |
| 93 result.append(base_path + source) | 80 result.append(base_path + source) |
| 81 if debug: | |
| 82 print 'AddSource', org_source, result[len(result) - 1] | |
| 94 | 83 |
| 95 def __ExtractSourcesFromAction(action, base_path, base_path_components, | 84 def __ExtractSourcesFromAction(action, base_path, base_path_components, |
| 96 results): | 85 results): |
| 97 if 'inputs' in action: | 86 if 'inputs' in action: |
| 98 __AddSources(action['inputs'], base_path, base_path_components, results) | 87 __AddSources(action['inputs'], base_path, base_path_components, results) |
| 99 | 88 |
| 100 def __ExtractSources(target, target_dict): | 89 def __ExtractSources(target, target_dict, toplevel_dir): |
| 101 base_path = posixpath.dirname(target) | 90 # |target| is either absolute or relative and in the format of the OS. Gyp |
| 91 # source paths are always posix. Convert |target| to a posix path relative to | |
| 92 # |toplevel_dir_|. This is done to make it easy to build source paths. | |
| 93 if target == toplevel_dir: | |
| 94 base_path = '' | |
| 95 elif target.startswith(toplevel_dir + os.sep): | |
| 96 base_path = target[len(toplevel_dir) + len(os.sep):] | |
| 97 else: | |
| 98 base_path = target | |
| 99 if os.sep == '\\' and os.altsep == '/': | |
|
Mark Mentovai
2014/07/16 20:28:17
I think that you should do this part first. Window
sky
2014/07/16 21:14:55
Done.
| |
| 100 base_path = base_path.replace('\\', '/') | |
| 101 base_path = posixpath.dirname(base_path) | |
| 102 base_path_components = base_path.split('/') | 102 base_path_components = base_path.split('/') |
| 103 | |
| 103 # Add a trailing '/' so that __AddSources() can easily build paths. | 104 # Add a trailing '/' so that __AddSources() can easily build paths. |
| 104 if len(base_path): | 105 if len(base_path): |
| 105 base_path += '/' | 106 base_path += '/' |
| 107 | |
| 108 if debug: | |
| 109 print 'ExtractSources', target, base_path | |
| 110 | |
| 106 results = [] | 111 results = [] |
| 107 if 'sources' in target_dict: | 112 if 'sources' in target_dict: |
| 108 __AddSources(target_dict['sources'], base_path, base_path_components, | 113 __AddSources(target_dict['sources'], base_path, base_path_components, |
| 109 results) | 114 results) |
| 110 # Include the inputs from any actions. Any changes to these effect the | 115 # Include the inputs from any actions. Any changes to these effect the |
| 111 # resulting output. | 116 # resulting output. |
| 112 if 'actions' in target_dict: | 117 if 'actions' in target_dict: |
| 113 for action in target_dict['actions']: | 118 for action in target_dict['actions']: |
| 114 __ExtractSourcesFromAction(action, base_path, base_path_components, | 119 __ExtractSourcesFromAction(action, base_path, base_path_components, |
| 115 results) | 120 results) |
| 116 if 'rules' in target_dict: | 121 if 'rules' in target_dict: |
| 117 for rule in target_dict['rules']: | 122 for rule in target_dict['rules']: |
| 118 __ExtractSourcesFromAction(rule, base_path, base_path_components, results) | 123 __ExtractSourcesFromAction(rule, base_path, base_path_components, results) |
| 119 | 124 |
| 120 return results | 125 return results |
| 121 | 126 |
| 122 class Target(object): | 127 class Target(object): |
| 123 """Holds information about a particular target: | 128 """Holds information about a particular target: |
| 124 sources: set of source files defined by this target. This includes inputs to | 129 sources: set of source files defined by this target. This includes inputs to |
| 125 actions and rules. | 130 actions and rules. |
| 126 deps: list of direct dependencies.""" | 131 deps: list of direct dependencies.""" |
| 127 def __init__(self): | 132 def __init__(self): |
| 128 self.sources = [] | 133 self.sources = [] |
| 129 self.deps = [] | 134 self.deps = [] |
| 130 | 135 |
| 131 def __GenerateTargets(target_list, target_dicts): | 136 def __GenerateTargets(target_list, target_dicts, toplevel_dir): |
| 132 """Generates a dictionary with the key the name of a target and the value a | 137 """Generates a dictionary with the key the name of a target and the value a |
| 133 Target.""" | 138 Target. |toplevel_dir| is the root of the source tree.""" |
| 134 targets = {} | 139 targets = {} |
| 135 | 140 |
| 136 # Queue of targets to visit. | 141 # Queue of targets to visit. |
| 137 targets_to_visit = target_list[:] | 142 targets_to_visit = target_list[:] |
| 138 | 143 |
| 139 while len(targets_to_visit) > 0: | 144 while len(targets_to_visit) > 0: |
| 140 absolute_target_name = targets_to_visit.pop() | 145 target_name = targets_to_visit.pop() |
| 141 # |absolute_target| may be an absolute path and may include #target. | 146 if target_name in targets: |
| 142 # References to targets are relative, so we need to clean the name. | |
| 143 relative_target_name = __MakeRelativeTargetName(absolute_target_name) | |
| 144 if relative_target_name in targets: | |
| 145 continue | 147 continue |
| 146 | 148 |
| 147 target = Target() | 149 target = Target() |
| 148 targets[relative_target_name] = target | 150 targets[target_name] = target |
| 149 target.sources.extend(__ExtractSources(relative_target_name, | 151 target.sources.extend(__ExtractSources(target_name, |
| 150 target_dicts[absolute_target_name])) | 152 target_dicts[target_name], |
| 153 toplevel_dir)) | |
| 151 | 154 |
| 152 for dep in target_dicts[absolute_target_name].get('dependencies', []): | 155 for dep in target_dicts[target_name].get('dependencies', []): |
| 153 targets[relative_target_name].deps.append(__MakeRelativeTargetName(dep)) | 156 targets[target_name].deps.append(dep) |
| 154 targets_to_visit.append(dep) | 157 targets_to_visit.append(dep) |
| 155 | 158 |
| 156 return targets | 159 return targets |
| 157 | 160 |
| 158 def __GetFiles(params): | 161 def __GetFiles(params): |
| 159 """Returns the list of files to analyze, or None if none specified.""" | 162 """Returns the list of files to analyze, or None if none specified.""" |
| 160 generator_flags = params.get('generator_flags', {}) | 163 generator_flags = params.get('generator_flags', {}) |
| 161 file_path = generator_flags.get('file_path', None) | 164 file_path = generator_flags.get('file_path', None) |
| 162 if not file_path: | 165 if not file_path: |
| 163 return None | 166 return None |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 197 operating_system = 'linux' # Keep this legacy behavior for now. | 200 operating_system = 'linux' # Keep this legacy behavior for now. |
| 198 default_variables.setdefault('OS', operating_system) | 201 default_variables.setdefault('OS', operating_system) |
| 199 | 202 |
| 200 def GenerateOutput(target_list, target_dicts, data, params): | 203 def GenerateOutput(target_list, target_dicts, data, params): |
| 201 """Called by gyp as the final stage. Outputs results.""" | 204 """Called by gyp as the final stage. Outputs results.""" |
| 202 files = __GetFiles(params) | 205 files = __GetFiles(params) |
| 203 if not files: | 206 if not files: |
| 204 print 'Must specify files to analyze via file_path generator flag' | 207 print 'Must specify files to analyze via file_path generator flag' |
| 205 return | 208 return |
| 206 | 209 |
| 207 targets = __GenerateTargets(target_list, target_dicts) | 210 targets = __GenerateTargets(target_list, target_dicts, |
| 211 os.path.abspath(params['options'].toplevel_dir)) | |
| 208 | 212 |
| 209 files_set = frozenset(files) | 213 files_set = frozenset(files) |
| 210 found_in_all_sources = 0 | 214 found_in_all_sources = 0 |
| 211 for target_name, target in targets.iteritems(): | 215 for target_name, target in targets.iteritems(): |
| 212 sources = files_set.intersection(target.sources) | 216 sources = files_set.intersection(target.sources) |
| 213 if len(sources): | 217 if len(sources): |
| 214 print 'Found dependency' | 218 print 'Found dependency' |
| 219 if debug: | |
| 220 print 'Found dependency in', target_name, target.sources | |
| 215 return | 221 return |
| 216 | 222 |
| 217 print 'No dependencies' | 223 print 'No dependencies' |
| OLD | NEW |