mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
15adf94f4d
This introduces the machinery needed to generate crash annotations from a YAML file. The relevant C++ functions are updated to take a typed enum. JavaScript calls are unaffected but they will throw if the string argument does not correspond to one of the known entries in the C++ enum. The existing whitelists and blacklists of annotations are also generated from the YAML file and all duplicate code related to them has been consolidated. Once written out to the .extra file the annotations are converted in string form and are no different than the existing ones. All existing annotations have been included in the list (and some obsolete ones have been removed) and all call sites have been updated including tests where appropriate. --HG-- extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
201 lines
6.5 KiB
Python
201 lines
6.5 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
import string
|
|
import sys
|
|
import textwrap
|
|
import yaml
|
|
|
|
###############################################################################
|
|
# Language-agnostic functionality #
|
|
###############################################################################
|
|
|
|
template_header = "/* This file was autogenerated by " \
|
|
"toolkit/crashreporter/generate_crash_reporter_sources.py. DO NOT EDIT */\n\n"
|
|
|
|
|
|
def validate_annotations(annotations):
|
|
""" Ensure that the annotations have all the required fields """
|
|
|
|
for (name, data) in sorted(annotations.items()):
|
|
if "description" not in data:
|
|
print("Annotation " + name + " does not have a description\n")
|
|
sys.exit(1)
|
|
if "type" not in data:
|
|
print("Annotation " + name + " does not have a type\n")
|
|
sys.exit(1)
|
|
else:
|
|
annotation_type = data.get("type")
|
|
valid_types = ["boolean", "integer", "string"]
|
|
if not any(annotation_type == t for t in valid_types):
|
|
print("Annotation " + name + " has an unknown type: " + annotation_type + "\n")
|
|
sys.exit(1)
|
|
|
|
|
|
def read_annotations(annotations_filename):
|
|
"""Read the annotations from a YAML file.
|
|
If an error is encountered quit the program."""
|
|
|
|
try:
|
|
with open(annotations_filename, "r") as annotations_file:
|
|
annotations = yaml.safe_load(annotations_file)
|
|
except (IOError, ValueError) as e:
|
|
print("Error parsing " + annotations_filename + ":\n" + str(e) + "\n")
|
|
sys.exit(1)
|
|
|
|
validate_annotations(annotations)
|
|
|
|
return annotations
|
|
|
|
|
|
def read_template(template_filename):
|
|
"""Read the contents of the template.
|
|
If an error is encountered quit the program."""
|
|
|
|
try:
|
|
with open(template_filename, "r") as template_file:
|
|
template = template_file.read()
|
|
except IOError as ex:
|
|
print("Error when reading " + template_filename + ":\n" + str(ex) + "\n")
|
|
sys.exit(1)
|
|
|
|
return template
|
|
|
|
|
|
def extract_crash_ping_whitelist(annotations):
|
|
"""Extract an array holding the names of the annotations whitelisted for
|
|
inclusion in the crash ping."""
|
|
|
|
return [name
|
|
for (name, data)
|
|
in sorted(annotations.items())
|
|
if data.get("ping", False)]
|
|
|
|
|
|
def extract_content_process_blacklist(annotations):
|
|
"""Extract an array holding the names of the annotations blacklisted when
|
|
read from a content process."""
|
|
|
|
return [name
|
|
for (name, data)
|
|
in sorted(annotations.items())
|
|
if not data.get("content", True)]
|
|
|
|
###############################################################################
|
|
# C++ code generation #
|
|
###############################################################################
|
|
|
|
|
|
def generate_strings(annotations):
|
|
"""Generate strings corresponding to every annotation."""
|
|
|
|
names = [" \"" + data.get("altname", name) + "\""
|
|
for (name, data)
|
|
in sorted(annotations.items())]
|
|
|
|
return ",\n".join(names)
|
|
|
|
|
|
def generate_enum(annotations):
|
|
"""Generate the C++ typed enum holding all the annotations and return it
|
|
as a string."""
|
|
|
|
enum = ""
|
|
|
|
for i, (name, _) in enumerate(sorted(annotations.items())):
|
|
enum += " " + name + " = " + str(i) + ",\n"
|
|
|
|
enum += " Count = " + str(len(annotations))
|
|
|
|
return enum
|
|
|
|
|
|
def generate_array_initializer(contents):
|
|
"""Generates the initializer for a C++ array of annotations."""
|
|
|
|
initializer = [" Annotation::" + name for name in contents]
|
|
|
|
return ",\n".join(initializer)
|
|
|
|
|
|
def generate_header(template, annotations):
|
|
"""Generate a header by filling the template with the the list of
|
|
annotations and return it as a string."""
|
|
|
|
whitelist = extract_crash_ping_whitelist(annotations)
|
|
blacklist = extract_content_process_blacklist(annotations)
|
|
|
|
return template_header + string.Template(template).substitute({
|
|
"enum": generate_enum(annotations),
|
|
"strings": generate_strings(annotations),
|
|
"whitelist": generate_array_initializer(whitelist),
|
|
"blacklist": generate_array_initializer(blacklist),
|
|
})
|
|
|
|
|
|
def emit_header(output, template_filename, annotations_filename):
|
|
"""Generate the C++ header from the template and write it out."""
|
|
|
|
annotations = read_annotations(annotations_filename)
|
|
template = read_template(template_filename)
|
|
generated_header = generate_header(template, annotations)
|
|
|
|
try:
|
|
output.write(generated_header)
|
|
except IOError as ex:
|
|
print("Error while writing out the generated file:\n" + str(ex) + "\n")
|
|
sys.exit(1)
|
|
|
|
###############################################################################
|
|
# Java code generation #
|
|
###############################################################################
|
|
|
|
|
|
def generate_java_array_initializer(contents):
|
|
"""Generates the initializer for an array of strings.
|
|
Effectively turns `["a", "b"]` into ' \"a\",\n \"b\"\n'."""
|
|
|
|
initializer = ""
|
|
|
|
for name in contents:
|
|
initializer += " \"" + name + "\",\n"
|
|
|
|
return initializer.strip(",\n")
|
|
|
|
|
|
def generate_class(template, annotations):
|
|
"""Fill the class template from the list of annotations."""
|
|
|
|
whitelist = extract_crash_ping_whitelist(annotations)
|
|
|
|
return template_header + string.Template(template).substitute({
|
|
"whitelist": generate_java_array_initializer(whitelist),
|
|
})
|
|
|
|
|
|
def emit_class(output, annotations_filename):
|
|
"""Generate the CrashReporterConstants.java file."""
|
|
|
|
template = textwrap.dedent("""\
|
|
package org.mozilla.gecko;
|
|
|
|
/**
|
|
* Constants used by the crash reporter. These are generated so that they
|
|
* are kept in sync with the other C++ and JS users.
|
|
*/
|
|
public class CrashReporterConstants {
|
|
public static final String[] ANNOTATION_WHITELIST = {
|
|
${whitelist}
|
|
};
|
|
}""")
|
|
|
|
annotations = read_annotations(annotations_filename)
|
|
generated_class = generate_class(template, annotations)
|
|
|
|
try:
|
|
output.write(generated_class)
|
|
except IOError as ex:
|
|
print("Error while writing out the generated file:\n" + str(ex) + "\n")
|
|
sys.exit(1)
|