| 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 |