OLD | NEW |
---|---|
(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' | |
OLD | NEW |