OLD | NEW |
1 # Copyright 2017 The Chromium Authors. All rights reserved. | 1 # Copyright 2017 The Chromium Authors. 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 import ast | 5 import ast |
6 import optparse | 6 import optparse |
7 import os.path | 7 import os.path |
8 import re | 8 import re |
9 import sys | 9 import sys |
10 | 10 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 model = [] | 96 model = [] |
97 while len(source): | 97 while len(source): |
98 match, source = match_and_consume(r"interface\s(\w*)\s?\{([^\{]*)\}", so
urce) | 98 match, source = match_and_consume(r"interface\s(\w*)\s?\{([^\{]*)\}", so
urce) |
99 if not match: | 99 if not match: |
100 sys.stderr.write("Cannot parse %s\n" % source[:100]) | 100 sys.stderr.write("Cannot parse %s\n" % source[:100]) |
101 sys.exit(1) | 101 sys.exit(1) |
102 model.append(File(match.group(1), match.group(2))) | 102 model.append(File(match.group(1), match.group(2))) |
103 return model | 103 return model |
104 | 104 |
105 | 105 |
| 106 def include_probes_header(): |
| 107 return "#include \"%s\"" % config["settings"]["probes_header"] |
| 108 |
| 109 |
106 class File(object): | 110 class File(object): |
107 def __init__(self, name, source): | 111 def __init__(self, name, source): |
108 self.name = name | 112 self.name = name |
109 self.header_name = self.name + "Inl" | 113 self.header_name = self.name + "Inl" |
110 self.includes = [include_inspector_header(base_name)] | 114 self.includes = [include_probes_header()] |
111 self.forward_declarations = [] | 115 self.forward_declarations = [] |
112 self.declarations = [] | 116 self.declarations = [] |
113 for line in map(str.strip, source.split("\n")): | 117 for line in map(str.strip, source.split("\n")): |
114 line = re.sub(r"\s{2,}", " ", line).strip() # Collapse whitespace | 118 line = re.sub(r"\s{2,}", " ", line).strip() # Collapse whitespace |
115 if len(line) == 0: | 119 if len(line) == 0: |
116 continue | 120 continue |
117 elif line.startswith("#include"): | 121 elif line.startswith("#include"): |
118 self.includes.append(line) | 122 self.includes.append(line) |
119 elif line.startswith("class ") or line.startswith("struct "): | 123 elif line.startswith("class ") or line.startswith("struct "): |
120 self.forward_declarations.append(line) | 124 self.forward_declarations.append(line) |
121 else: | 125 else: |
122 self.declarations.append(Method(line)) | 126 self.declarations.append(Method(line)) |
123 self.includes.sort() | 127 self.includes.sort() |
124 self.forward_declarations.sort() | 128 self.forward_declarations.sort() |
125 | 129 |
126 | 130 |
127 def include_header(name): | |
128 return "#include \"%s.h\"" % name | |
129 | |
130 | |
131 def include_inspector_header(name): | |
132 if name == "PerformanceMonitor": | |
133 return include_header("core/frame/" + name) | |
134 if name == "PlatformProbes": | |
135 return include_header("platform/probe/" + name) | |
136 if name == "CoreProbes": | |
137 return include_header("core/probe/" + name) | |
138 return include_header("core/inspector/" + name) | |
139 | |
140 | |
141 class Method(object): | 131 class Method(object): |
142 def __init__(self, source): | 132 def __init__(self, source): |
143 match = re.match(r"(\[[\w|,|=|\s]*\])?\s?(\w*\*?) (\w*)\((.*)\)\s?;", so
urce) | 133 match = re.match(r"(?:(\w+\*?)\s+)?(\w+)\s*\((.*)\)\s*;", source) |
144 if not match: | 134 if not match: |
145 sys.stderr.write("Cannot parse %s\n" % source) | 135 sys.stderr.write("Cannot parse %s\n" % source) |
146 sys.exit(1) | 136 sys.exit(1) |
147 | 137 |
148 self.options = [] | 138 self.return_type = match.group(1) or "" |
149 if match.group(1): | 139 self.name = match.group(2) |
150 options_str = re.sub(r"\s", "", match.group(1)[1:-1]) | |
151 if len(options_str) != 0: | |
152 self.options = options_str.split(",") | |
153 | |
154 self.return_type = match.group(2) | |
155 self.name = match.group(3) | |
156 self.is_scoped = self.return_type == "" | 140 self.is_scoped = self.return_type == "" |
157 | 141 |
158 # Splitting parameters by a comma, assuming that attribute lists contain
no more than one attribute. | 142 # Splitting parameters by a comma, assuming that attribute lists contain
no more than one attribute. |
159 self.params = map(Parameter, map(str.strip, match.group(4).split(","))) | 143 self.params = map(Parameter, map(str.strip, match.group(3).split(","))) |
160 | 144 |
161 self.returns_value = self.return_type != "" and self.return_type != "voi
d" | 145 self.returns_value = self.return_type != "" and self.return_type != "voi
d" |
162 if self.return_type == "bool": | 146 if self.return_type == "bool": |
163 self.default_return_value = "false" | 147 self.default_return_value = "false" |
164 elif self.returns_value: | 148 elif self.returns_value: |
165 sys.stderr.write("Can only return bool: %s\n" % self.name) | 149 sys.stderr.write("Can only return bool: %s\n" % self.name) |
166 sys.exit(1) | 150 sys.exit(1) |
167 | 151 |
168 self.agents = [option for option in self.options if "=" not in option] | |
169 | |
170 if self.returns_value and len(self.agents) > 1: | |
171 sys.stderr.write("Can only return value from a single agent: %s\n" %
self.name) | |
172 sys.exit(1) | |
173 | |
174 | 152 |
175 class Parameter(object): | 153 class Parameter(object): |
176 def __init__(self, source): | 154 def __init__(self, source): |
177 self.options = [] | 155 self.options = [] |
178 match, source = match_and_consume(r"\[(\w*)\]", source) | 156 match, source = match_and_consume(r"\[(\w*)\]", source) |
179 if match: | 157 if match: |
180 self.options.append(match.group(1)) | 158 self.options.append(match.group(1)) |
181 | 159 |
182 parts = map(str.strip, source.split("=")) | 160 parts = map(str.strip, source.split("=")) |
183 self.default_value = parts[1] if len(parts) != 1 else None | 161 self.default_value = parts[1] if len(parts) != 1 else None |
(...skipping 30 matching lines...) Expand all Loading... |
214 default_config = { | 192 default_config = { |
215 "settings": {}, | 193 "settings": {}, |
216 "observers": {} | 194 "observers": {} |
217 } | 195 } |
218 if not file_name: | 196 if not file_name: |
219 return default_config | 197 return default_config |
220 with open(file_name) as config_file: | 198 with open(file_name) as config_file: |
221 return _json5_loads(config_file.read()) or default_config | 199 return _json5_loads(config_file.read()) or default_config |
222 | 200 |
223 | 201 |
| 202 def build_observers(): |
| 203 all_pidl_probes = set() |
| 204 for f in files: |
| 205 probes = set([probe.name for probe in f.declarations]) |
| 206 if all_pidl_probes & probes: |
| 207 raise Exception("Multiple probe declarations: %s" % all_pidl_probes
& probes) |
| 208 all_pidl_probes |= probes |
| 209 |
| 210 all_observers = set() |
| 211 observers_by_probe = {} |
| 212 unused_probes = set(all_pidl_probes) |
| 213 for observer_name in config["observers"]: |
| 214 all_observers.add(observer_name) |
| 215 observer = config["observers"][observer_name] |
| 216 for probe in observer["probes"]: |
| 217 unused_probes.discard(probe) |
| 218 if probe not in all_pidl_probes: |
| 219 raise Exception('Probe %s is not declared in PIDL file' % probe) |
| 220 if probe not in observers_by_probe: |
| 221 observers_by_probe[probe] = set() |
| 222 observers_by_probe[probe].add(observer_name) |
| 223 if unused_probes: |
| 224 raise Exception("Unused probes: %s" % unused_probes) |
| 225 |
| 226 for f in files: |
| 227 for probe in f.declarations: |
| 228 probe.agents = observers_by_probe[probe.name] |
| 229 if probe.returns_value and len(probe.agents) > 1: |
| 230 raise Exception("Can only return value from a single observer: %
s\n" % probe.name) |
| 231 return all_observers |
| 232 |
| 233 |
224 cmdline_parser = optparse.OptionParser() | 234 cmdline_parser = optparse.OptionParser() |
225 cmdline_parser.add_option("--output_dir") | 235 cmdline_parser.add_option("--output_dir") |
226 cmdline_parser.add_option("--config") | 236 cmdline_parser.add_option("--config") |
227 | 237 |
228 try: | 238 try: |
229 arg_options, arg_values = cmdline_parser.parse_args() | 239 arg_options, arg_values = cmdline_parser.parse_args() |
230 if len(arg_values) != 1: | 240 if len(arg_values) != 1: |
231 raise Exception("Exactly one plain argument expected (found %s)" % len(a
rg_values)) | 241 raise Exception("Exactly one plain argument expected (found %s)" % len(a
rg_values)) |
232 input_path = arg_values[0] | 242 input_path = arg_values[0] |
233 output_dirpath = arg_options.output_dir | 243 output_dirpath = arg_options.output_dir |
234 if not output_dirpath: | 244 if not output_dirpath: |
235 raise Exception("Output directory must be specified") | 245 raise Exception("Output directory must be specified") |
236 config_file_name = arg_options.config | 246 config_file_name = arg_options.config |
237 except Exception: | 247 except Exception: |
238 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html | 248 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html |
239 exc = sys.exc_info()[1] | 249 exc = sys.exc_info()[1] |
240 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) | 250 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) |
241 sys.stderr.write("Usage: <script> [options] <probes.pidl>\n") | 251 sys.stderr.write("Usage: <script> [options] <probes.pidl>\n") |
242 sys.stderr.write("Options:\n") | 252 sys.stderr.write("Options:\n") |
243 sys.stderr.write("\t--config <config_file.json5>\n") | 253 sys.stderr.write("\t--config <config_file.json5>\n") |
244 sys.stderr.write("\t--output_dir <output_dir>\n") | 254 sys.stderr.write("\t--output_dir <output_dir>\n") |
245 exit(1) | 255 exit(1) |
246 | 256 |
247 config = load_config(config_file_name) | 257 config = load_config(config_file_name) |
248 jinja_env = initialize_jinja_env(output_dirpath) | 258 jinja_env = initialize_jinja_env(output_dirpath) |
249 all_agents = set() | |
250 base_name = os.path.splitext(os.path.basename(input_path))[0] | 259 base_name = os.path.splitext(os.path.basename(input_path))[0] |
251 | 260 |
252 fin = open(input_path, "r") | 261 fin = open(input_path, "r") |
253 files = load_model_from_idl(fin.read()) | 262 files = load_model_from_idl(fin.read()) |
254 fin.close() | 263 fin.close() |
255 | 264 |
256 for f in files: | |
257 for declaration in f.declarations: | |
258 for agent in declaration.agents: | |
259 all_agents.add(agent) | |
260 | |
261 template_context = { | 265 template_context = { |
262 "files": files, | 266 "files": files, |
263 "agents": all_agents, | 267 "agents": build_observers(), |
264 "config": config, | 268 "config": config, |
265 "name": base_name, | 269 "name": base_name, |
266 "input_file": os.path.basename(input_path) | 270 "input_file": os.path.basename(input_path) |
267 } | 271 } |
268 cpp_template = jinja_env.get_template("/InstrumentingProbesImpl.cpp.tmpl") | 272 cpp_template = jinja_env.get_template("/InstrumentingProbesImpl.cpp.tmpl") |
269 cpp_file = open(output_dirpath + "/" + base_name + "Impl.cpp", "w") | 273 cpp_file = open(output_dirpath + "/" + base_name + "Impl.cpp", "w") |
270 cpp_file.write(cpp_template.render(template_context)) | 274 cpp_file.write(cpp_template.render(template_context)) |
271 cpp_file.close() | 275 cpp_file.close() |
272 | 276 |
273 sink_h_template = jinja_env.get_template("/ProbeSink.h.tmpl") | 277 sink_h_template = jinja_env.get_template("/ProbeSink.h.tmpl") |
274 sink_h_file = open(output_dirpath + "/" + to_singular(base_name) + "Sink.h", "w"
) | 278 sink_h_file = open(output_dirpath + "/" + to_singular(base_name) + "Sink.h", "w"
) |
275 sink_h_file.write(sink_h_template.render(template_context)) | 279 sink_h_file.write(sink_h_template.render(template_context)) |
276 sink_h_file.close() | 280 sink_h_file.close() |
277 | 281 |
278 for f in files: | 282 for f in files: |
279 template_context["file"] = f | 283 template_context["file"] = f |
280 h_template = jinja_env.get_template("/InstrumentingProbesInl.h.tmpl") | 284 h_template = jinja_env.get_template("/InstrumentingProbesInl.h.tmpl") |
281 h_file = open(output_dirpath + "/" + f.header_name + ".h", "w") | 285 h_file = open(output_dirpath + "/" + f.header_name + ".h", "w") |
282 h_file.write(h_template.render(template_context)) | 286 h_file.write(h_template.render(template_context)) |
283 h_file.close() | 287 h_file.close() |
OLD | NEW |