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

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

Issue 330053003: First crack at adding a GYP_GENERATOR for determining various things (Closed) Base URL: http://gyp.googlecode.com/svn/trunk
Patch Set: address review comments Created 6 years, 6 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 | test/analyzer/gyptest-analyzer.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 # found in the LICENSE file.
4
5 """
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
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.
10 """
11
12 import gyp.common
13 import gyp.ninja_syntax as ninja_syntax
14 import os
15 import posixpath
16
17 generator_supports_multiple_toolsets = True
18
19 generator_wants_static_library_dependencies_adjusted = False
20
21 generator_default_variables = {
22 }
23 for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR',
24 'LIB_DIR', 'SHARED_LIB_DIR']:
25 generator_default_variables[dirname] = '!!!'
26
27 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
28 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
29 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
30 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
31 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
32 'CONFIGURATION_NAME']:
33 generator_default_variables[unused] = ''
34
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 # Gyp paths are always posix style.
44 path = path.replace('\\', '/')
45 if path.endswith('#target'):
46 path = path[0:len(path) - len('#target')]
47 return path
48
49 def __ExtractBasePath(target):
50 """Extracts the path components of the specified gyp target path."""
51 last_index = target.rfind('/')
52 if last_index == -1:
53 return ''
54 return target[0:(last_index + 1)]
55
56 def __AddSources(sources, base_path, base_path_components, result):
57 """Extracts valid sources from |sources| and adds them to |result|. Each
58 source file is relative to |base_path|, but may contain '..'. To make
59 resolving '..' easier |base_path_components| contains each of the
60 directories in |base_path|. Additionally each source may contain variables.
61 Such sources are ignored as it is assumed dependencies on them are expressed
62 and tracked in some other means."""
63 # NOTE: gyp paths are always posix style.
64 for source in sources:
65 if source.startswith('!!!'):
66 continue;
67 if source.startswith('$'):
68 continue
69 # variable expansion may lead to //.
70 source = source.replace('//', '/')
Mark Mentovai 2014/06/12 18:36:32 Technically this is only correct for double-slashe
71 if source.startswith('../'):
72 path_components = base_path_components[:]
73 # Resolve relative paths.
74 while source.startswith('../'):
75 path_components.pop(len(path_components) - 1)
76 source = source[3:]
77 result.append('/'.join(path_components) + source)
78 continue
79 result.append(base_path + source)
80
81 def __ExtractSourcesFromAction(action, base_path, base_path_components,
82 results):
83 if 'inputs' in action:
84 __AddSources(action['inputs'], base_path, base_path_components, results)
85
86 def __ExtractSources(target, target_dict):
87 base_path = posixpath.dirname(target)
88 base_path_components = base_path.split('/')
89 # Add a trailing '/' so that __AddSources() can easily build paths.
90 if len(base_path):
91 base_path += '/'
92 results = []
93 if 'sources' in target_dict:
94 __AddSources(target_dict['sources'], base_path, base_path_components,
95 results)
96 # Include the inputs from any actions. Any changes to these effect the
97 # resulting output.
98 if 'actions' in target_dict:
99 for action in target_dict['actions']:
100 __ExtractSourcesFromAction(action, base_path, base_path_components,
101 results)
102 if 'rules' in target_dict:
103 for rule in target_dict['rules']:
104 __ExtractSourcesFromAction(rule, base_path, base_path_components, results)
105
106 return results
107
108 class Target(object):
109 """Holds information about a particular target:
110 sources: set of source files defined by this target. This includes inputs to
111 actions and rules.
112 deps: list of direct dependencies."""
113 def __init__(self):
114 self.sources = []
115 self.deps = []
116
117 def __GenerateTargets(target_list, target_dicts):
118 """Generates a dictionary with the key the name of a target and the value a
119 Target."""
120 targets = {}
121
122 # Queue of targets to visit.
123 targets_to_visit = target_list[:]
124
125 while len(targets_to_visit) > 0:
126 absolute_target_name = targets_to_visit.pop()
127 # |absolute_target| may be an absolute path and may include #target.
128 # References to targets are relative, so we need to clean the name.
129 relative_target_name = __MakeRelativeTargetName(absolute_target_name)
130 if relative_target_name in targets:
131 continue
132
133 target = Target()
134 targets[relative_target_name] = target
135 target.sources.extend(__ExtractSources(relative_target_name,
136 target_dicts[absolute_target_name]))
137
138 for dep in target_dicts[absolute_target_name].get('dependencies', []):
139 targets[relative_target_name].deps.append(__MakeRelativeTargetName(dep))
140 targets_to_visit.append(dep)
141
142 return targets
143
144 def __GetFiles(params):
145 """Returns the list of files to analyze, or None if none specified."""
146 generator_flags = params.get('generator_flags', {})
147 file_path = generator_flags.get('file_path', None)
148 if not file_path:
149 return None
150 try:
151 f = open(file_path, 'r')
152 result = []
153 for file_name in f:
154 if file_name.endswith('\n'):
155 file_name = file_name[0:len(file_name) - 1]
156 if len(file_name):
157 result.append(file_name)
158 f.close()
159 return result
160 except IOError:
161 print 'Unable to open file', file_path
162 return None
163
164 def CalculateVariables(default_variables, params):
165 """Calculate additional variables for use in the build (called by gyp)."""
166 flavor = gyp.common.GetFlavor(params)
167 if flavor == 'mac':
168 default_variables.setdefault('OS', 'mac')
169 elif flavor == 'win':
170 default_variables.setdefault('OS', 'win')
171 else:
172 operating_system = flavor
173 if flavor == 'android':
174 operating_system = 'linux' # Keep this legacy behavior for now.
175 default_variables.setdefault('OS', operating_system)
176
177 def GenerateOutput(target_list, target_dicts, data, params):
178 """Called by gyp as the final stage. Outputs results."""
179 files = __GetFiles(params)
180 if not files:
181 print 'Must specify files to analyze via file_path generator flag'
182 return
183
184 targets = __GenerateTargets(target_list, target_dicts)
185
186 files_set = frozenset(files)
187 found_in_all_sources = 0
188 for target_name, target in targets.iteritems():
189 sources = files_set.intersection(target.sources)
190 if len(sources):
191 print 'Found dependency'
192 return
193
194 print 'No dependencies'
OLDNEW
« no previous file with comments | « no previous file | test/analyzer/gyptest-analyzer.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698