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

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: sim 90 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 not len(source) or source.startswith('!!!') or source.startswith('$'):
66 continue
67 # variable expansion may lead to //.
68 source = source[0] + source[1:].replace('//', '/')
69 if source.startswith('../'):
70 path_components = base_path_components[:]
71 # Resolve relative paths.
72 while source.startswith('../'):
73 path_components.pop(len(path_components) - 1)
74 source = source[3:]
75 result.append('/'.join(path_components) + source)
76 continue
77 result.append(base_path + source)
78
79 def __ExtractSourcesFromAction(action, base_path, base_path_components,
80 results):
81 if 'inputs' in action:
82 __AddSources(action['inputs'], base_path, base_path_components, results)
83
84 def __ExtractSources(target, target_dict):
85 base_path = posixpath.dirname(target)
86 base_path_components = base_path.split('/')
87 # Add a trailing '/' so that __AddSources() can easily build paths.
88 if len(base_path):
89 base_path += '/'
90 results = []
91 if 'sources' in target_dict:
92 __AddSources(target_dict['sources'], base_path, base_path_components,
93 results)
94 # Include the inputs from any actions. Any changes to these effect the
95 # resulting output.
96 if 'actions' in target_dict:
97 for action in target_dict['actions']:
98 __ExtractSourcesFromAction(action, base_path, base_path_components,
99 results)
100 if 'rules' in target_dict:
101 for rule in target_dict['rules']:
102 __ExtractSourcesFromAction(rule, base_path, base_path_components, results)
103
104 return results
105
106 class Target(object):
107 """Holds information about a particular target:
108 sources: set of source files defined by this target. This includes inputs to
109 actions and rules.
110 deps: list of direct dependencies."""
111 def __init__(self):
112 self.sources = []
113 self.deps = []
114
115 def __GenerateTargets(target_list, target_dicts):
116 """Generates a dictionary with the key the name of a target and the value a
117 Target."""
118 targets = {}
119
120 # Queue of targets to visit.
121 targets_to_visit = target_list[:]
122
123 while len(targets_to_visit) > 0:
124 absolute_target_name = targets_to_visit.pop()
125 # |absolute_target| may be an absolute path and may include #target.
126 # References to targets are relative, so we need to clean the name.
127 relative_target_name = __MakeRelativeTargetName(absolute_target_name)
128 if relative_target_name in targets:
129 continue
130
131 target = Target()
132 targets[relative_target_name] = target
133 target.sources.extend(__ExtractSources(relative_target_name,
134 target_dicts[absolute_target_name]))
135
136 for dep in target_dicts[absolute_target_name].get('dependencies', []):
137 targets[relative_target_name].deps.append(__MakeRelativeTargetName(dep))
138 targets_to_visit.append(dep)
139
140 return targets
141
142 def __GetFiles(params):
143 """Returns the list of files to analyze, or None if none specified."""
144 generator_flags = params.get('generator_flags', {})
145 file_path = generator_flags.get('file_path', None)
146 if not file_path:
147 return None
148 try:
149 f = open(file_path, 'r')
150 result = []
151 for file_name in f:
152 if file_name.endswith('\n'):
153 file_name = file_name[0:len(file_name) - 1]
154 if len(file_name):
155 result.append(file_name)
156 f.close()
157 return result
158 except IOError:
159 print 'Unable to open file', file_path
160 return None
161
162 def CalculateVariables(default_variables, params):
163 """Calculate additional variables for use in the build (called by gyp)."""
164 flavor = gyp.common.GetFlavor(params)
165 if flavor == 'mac':
166 default_variables.setdefault('OS', 'mac')
167 elif flavor == 'win':
168 default_variables.setdefault('OS', 'win')
169 else:
170 operating_system = flavor
171 if flavor == 'android':
172 operating_system = 'linux' # Keep this legacy behavior for now.
173 default_variables.setdefault('OS', operating_system)
174
175 def GenerateOutput(target_list, target_dicts, data, params):
176 """Called by gyp as the final stage. Outputs results."""
177 files = __GetFiles(params)
178 if not files:
179 print 'Must specify files to analyze via file_path generator flag'
180 return
181
182 targets = __GenerateTargets(target_list, target_dicts)
183
184 files_set = frozenset(files)
185 found_in_all_sources = 0
186 for target_name, target in targets.iteritems():
187 sources = files_set.intersection(target.sources)
188 if len(sources):
189 print 'Found dependency'
190 return
191
192 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