Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: pylib/gyp/generator/analyzer.py

Issue 395483002: Fixes bug in path handling of analyzer (Closed) Base URL: http://gyp.googlecode.com/svn/trunk/
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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.startswith(toplevel_dir):
Mark Mentovai 2014/07/16 15:48:42 I still think this is wrong. Now toplevel_dir will
94 base_path = target[len(toplevel_dir):]
95 else:
96 base_path = target
97 base_path = base_path.replace('\\', '/')
Mark Mentovai 2014/07/16 15:48:42 This is invalid on non-Windows.
98 base_path = posixpath.dirname(base_path)
102 base_path_components = base_path.split('/') 99 base_path_components = base_path.split('/')
100
103 # Add a trailing '/' so that __AddSources() can easily build paths. 101 # Add a trailing '/' so that __AddSources() can easily build paths.
104 if len(base_path): 102 if len(base_path):
105 base_path += '/' 103 base_path += '/'
104
105 if debug:
106 print 'ExtractSources', target, base_path
107
106 results = [] 108 results = []
107 if 'sources' in target_dict: 109 if 'sources' in target_dict:
108 __AddSources(target_dict['sources'], base_path, base_path_components, 110 __AddSources(target_dict['sources'], base_path, base_path_components,
109 results) 111 results)
110 # Include the inputs from any actions. Any changes to these effect the 112 # Include the inputs from any actions. Any changes to these effect the
111 # resulting output. 113 # resulting output.
112 if 'actions' in target_dict: 114 if 'actions' in target_dict:
113 for action in target_dict['actions']: 115 for action in target_dict['actions']:
114 __ExtractSourcesFromAction(action, base_path, base_path_components, 116 __ExtractSourcesFromAction(action, base_path, base_path_components,
115 results) 117 results)
116 if 'rules' in target_dict: 118 if 'rules' in target_dict:
117 for rule in target_dict['rules']: 119 for rule in target_dict['rules']:
118 __ExtractSourcesFromAction(rule, base_path, base_path_components, results) 120 __ExtractSourcesFromAction(rule, base_path, base_path_components, results)
119 121
120 return results 122 return results
121 123
122 class Target(object): 124 class Target(object):
123 """Holds information about a particular target: 125 """Holds information about a particular target:
124 sources: set of source files defined by this target. This includes inputs to 126 sources: set of source files defined by this target. This includes inputs to
125 actions and rules. 127 actions and rules.
126 deps: list of direct dependencies.""" 128 deps: list of direct dependencies."""
127 def __init__(self): 129 def __init__(self):
128 self.sources = [] 130 self.sources = []
129 self.deps = [] 131 self.deps = []
130 132
131 def __GenerateTargets(target_list, target_dicts): 133 def __GenerateTargets(target_list, target_dicts, toplevel_dir):
132 """Generates a dictionary with the key the name of a target and the value a 134 """Generates a dictionary with the key the name of a target and the value a
133 Target.""" 135 Target. |toplevel_dir| is the root of the source tree."""
134 targets = {} 136 targets = {}
135 137
136 # Queue of targets to visit. 138 # Queue of targets to visit.
137 targets_to_visit = target_list[:] 139 targets_to_visit = target_list[:]
138 140
139 while len(targets_to_visit) > 0: 141 while len(targets_to_visit) > 0:
140 absolute_target_name = targets_to_visit.pop() 142 target_name = targets_to_visit.pop()
141 # |absolute_target| may be an absolute path and may include #target. 143 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 144 continue
146 145
147 target = Target() 146 target = Target()
148 targets[relative_target_name] = target 147 targets[target_name] = target
149 target.sources.extend(__ExtractSources(relative_target_name, 148 target.sources.extend(__ExtractSources(target_name,
150 target_dicts[absolute_target_name])) 149 target_dicts[target_name],
150 toplevel_dir))
151 151
152 for dep in target_dicts[absolute_target_name].get('dependencies', []): 152 for dep in target_dicts[target_name].get('dependencies', []):
153 targets[relative_target_name].deps.append(__MakeRelativeTargetName(dep)) 153 targets[target_name].deps.append(dep)
154 targets_to_visit.append(dep) 154 targets_to_visit.append(dep)
155 155
156 return targets 156 return targets
157 157
158 def __GetFiles(params): 158 def __GetFiles(params):
159 """Returns the list of files to analyze, or None if none specified.""" 159 """Returns the list of files to analyze, or None if none specified."""
160 generator_flags = params.get('generator_flags', {}) 160 generator_flags = params.get('generator_flags', {})
161 file_path = generator_flags.get('file_path', None) 161 file_path = generator_flags.get('file_path', None)
162 if not file_path: 162 if not file_path:
163 return None 163 return None
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 operating_system = 'linux' # Keep this legacy behavior for now. 197 operating_system = 'linux' # Keep this legacy behavior for now.
198 default_variables.setdefault('OS', operating_system) 198 default_variables.setdefault('OS', operating_system)
199 199
200 def GenerateOutput(target_list, target_dicts, data, params): 200 def GenerateOutput(target_list, target_dicts, data, params):
201 """Called by gyp as the final stage. Outputs results.""" 201 """Called by gyp as the final stage. Outputs results."""
202 files = __GetFiles(params) 202 files = __GetFiles(params)
203 if not files: 203 if not files:
204 print 'Must specify files to analyze via file_path generator flag' 204 print 'Must specify files to analyze via file_path generator flag'
205 return 205 return
206 206
207 targets = __GenerateTargets(target_list, target_dicts) 207 toplevel_dir = os.path.abspath(params['options'].toplevel_dir)
208 if len(toplevel_dir) >= len(os.sep) and toplevel_dir[-len(os.sep)] != os.sep:
209 toplevel_dir += os.sep
210
211 targets = __GenerateTargets(target_list, target_dicts, 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'
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698