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 | |
16 generator_supports_multiple_toolsets = True | |
17 | |
18 generator_wants_static_library_dependencies_adjusted = False | |
19 | |
20 generator_default_variables = { | |
21 } | |
22 for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR', | |
23 'LIB_DIR', 'SHARED_LIB_DIR']: | |
24 generator_default_variables[dirname] = '!!!' | |
25 | |
26 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', | |
27 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', | |
28 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', | |
29 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', | |
30 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', | |
31 'CONFIGURATION_NAME']: | |
32 generator_default_variables[unused] = '' | |
33 | |
34 def __MakeRelativeTargetName(path): | |
35 """Converts a gyp target name into a relative name. For example, the path to a | |
36 gyp file may be something like c:\foo\bar.gyp:target, this converts it to | |
37 bar.gyp. | |
38 """ | |
39 prune_path = os.getcwd() | |
40 if path.startswith(prune_path): | |
41 path = path[len(prune_path):] | |
42 # windows | |
43 path = path.replace('\\', '/') | |
sky
2014/06/12 16:41:55
This feels a bit run, but seemed to be necessary o
| |
44 if path.endswith('#target'): | |
45 path = path[0:len(path) - len('#target')] | |
46 return path | |
47 | |
48 def __ExtractBasePath(target): | |
49 """Extracts the path components of the specified gyp target path.""" | |
Mark Mentovai
2014/06/12 17:40:05
Is this just doing what posixpath.dirname does?
sky
2014/06/12 18:30:54
Indeed it is. Updated.
| |
50 last_index = target.rfind('/') | |
51 if last_index == -1: | |
52 return '' | |
53 return target[0:(last_index + 1)] | |
54 | |
55 def AddSources(sources, base_path, base_path_components, result): | |
56 """Adds the sources to result. base_path_components come from | |
Mark Mentovai
2014/06/12 17:40:05
It’d be helpful to have a description of the proce
sky
2014/06/12 18:30:54
Done.
| |
57 __ExtractBasePath().""" | |
58 for source in sources: | |
59 # Ignore sources which refer to built in variables, eg PRODUCT_DIR. Assume | |
sky
2014/06/12 16:41:55
I'm not entirely confident I'm handling variables
Mark Mentovai
2014/06/12 17:40:05
sky wrote:
| |
60 # dependencies on them are expressed and tracked in some other means. | |
61 if source.startswith('!!!'): | |
62 continue; | |
63 if source.startswith('$'): | |
64 continue | |
65 if source.startswith('../'): | |
66 path_components = base_path_components[:] | |
67 while source.startswith('../'): | |
Mark Mentovai
2014/06/12 17:40:05
Should this deal with ..//../.. ?
sky
2014/06/12 18:30:54
Done. I also added test coverage.
| |
68 path_components.pop(len(path_components) - 1) | |
69 source = source[3:] | |
70 result.append('/'.join(path_components) + source) | |
71 continue | |
72 result.append(base_path + source) | |
73 | |
74 def ExtractSourcesFromAction(action, base_path, base_path_components, results): | |
75 if 'inputs' in action: | |
76 AddSources(action['inputs'], base_path, base_path_components, results) | |
77 | |
78 def ExtractSources(target, target_dict): | |
79 base_path = __ExtractBasePath(target) | |
80 base_path_components = base_path[:-1].split('/') | |
81 results = [] | |
82 if 'sources' in target_dict: | |
83 AddSources(target_dict['sources'], base_path, base_path_components, | |
84 results) | |
85 # Include the inputs from any actions. Any changes to these effect the | |
86 # resulting output. | |
87 if 'actions' in target_dict: | |
88 for action in target_dict['actions']: | |
89 ExtractSourcesFromAction(action, base_path, base_path_components, results) | |
90 if 'rules' in target_dict: | |
91 for rule in target_dict['rules']: | |
92 ExtractSourcesFromAction(rule, base_path, base_path_components, results) | |
93 | |
94 return results | |
95 | |
96 class Target(object): | |
97 """Holds information about a particular target: | |
98 sources: set of source files defined by this target. This includes inputs to | |
99 actions and rules. | |
100 deps: list of direct dependencies.""" | |
101 def __init__(self): | |
102 self.sources = [] | |
103 self.deps = [] | |
104 | |
105 def __GenerateTargets(target_list, target_dicts): | |
106 """Generates a dictionary with the key the name of a target and the value a | |
107 Target.""" | |
108 targets = {} | |
109 | |
110 # Queue of targets to visit. | |
111 targets_to_visit = target_list[:] | |
112 | |
113 while len(targets_to_visit) > 0: | |
114 absolute_target_name = targets_to_visit.pop() | |
115 # |absolute_target| may be an absolute path and may include #target. | |
116 # References to targets are relative, so we need to clean the name. | |
117 relative_target_name = __MakeRelativeTargetName(absolute_target_name) | |
118 if relative_target_name in targets: | |
119 continue | |
120 | |
121 target = Target() | |
122 targets[relative_target_name] = target | |
123 target.sources.extend(ExtractSources(relative_target_name, | |
124 target_dicts[absolute_target_name])) | |
125 | |
126 for dep in target_dicts[absolute_target_name].get('dependencies', []): | |
127 targets[relative_target_name].deps.append(__MakeRelativeTargetName(dep)) | |
128 targets_to_visit.append(dep) | |
129 | |
130 return targets | |
131 | |
132 def __GetFiles(params): | |
133 """Returns the list of files to analyze, or None if none specified.""" | |
134 generator_flags = params.get('generator_flags', {}) | |
135 file_path = generator_flags.get('file_path', None) | |
136 if not file_path: | |
137 return None | |
138 try: | |
139 f = open(file_path, 'r') | |
140 result = [] | |
141 for file_name in f: | |
142 if file_name.endswith('\n'): | |
143 file_name = file_name[0:len(file_name) - 1] | |
144 if len(file_name): | |
145 result.append(file_name) | |
146 f.close() | |
147 return result | |
148 except IOError: | |
149 print 'Unable to open file', file_path | |
150 return None | |
151 | |
152 def CalculateVariables(default_variables, params): | |
153 """Calculate additional variables for use in the build (called by gyp).""" | |
154 flavor = gyp.common.GetFlavor(params) | |
155 if flavor == 'mac': | |
156 default_variables.setdefault('OS', 'mac') | |
157 elif flavor == 'win': | |
158 default_variables.setdefault('OS', 'win') | |
159 else: | |
160 operating_system = flavor | |
161 if flavor == 'android': | |
162 operating_system = 'linux' # Keep this legacy behavior for now. | |
163 default_variables.setdefault('OS', operating_system) | |
164 | |
165 def GenerateOutput(target_list, target_dicts, data, params): | |
166 """Called by gyp as the final stage. Outputs results.""" | |
167 files = __GetFiles(params) | |
168 if not files: | |
169 print 'Must specify files to analyze via file_path generator flag' | |
170 return | |
171 | |
172 targets = __GenerateTargets(target_list, target_dicts) | |
173 | |
174 files_set = frozenset(files) | |
175 found_in_all_sources = 0 | |
176 for target_name, target in targets.iteritems(): | |
177 sources = files_set.intersection(target.sources) | |
178 if len(sources): | |
179 print 'Found dependency' | |
180 return | |
181 | |
182 print 'No dependencies' | |
OLD | NEW |