1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
# 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/.
"""
Outputter to generate C++ code for metrics.
"""
import jinja2
from glean_parser import metrics, util
from mozbuild.util import memoize
from util import generate_metric_ids, generate_ping_ids, get_metrics
def type_name(obj):
"""
Returns the C++ type to use for a given metric object.
"""
if getattr(obj, "labeled", False):
class_name = util.Camelize(obj.type[8:]) # strips "labeled_" off the front.
label_enum = "DynamicLabel"
if obj.labels and len(obj.labels):
label_enum = f"{util.Camelize(obj.name)}Label"
if class_name == "Counter":
return f"Labeled<impl::{class_name}Metric<impl::CounterType::eBaseOrLabeled>, {label_enum}>"
return f"Labeled<impl::{class_name}Metric, {label_enum}>"
generate_enums = getattr(obj, "_generate_enums", []) # Extra Keys? Reasons?
if len(generate_enums):
for name, _ in generate_enums:
if not len(getattr(obj, name)) and isinstance(obj, metrics.Event):
return util.Camelize(obj.type) + "Metric<NoExtraKeys>"
else:
# we always use the `extra` suffix,
# because we only expose the new event API
suffix = "Extra"
return f"{util.Camelize(obj.type)}Metric<{util.Camelize(obj.name) + suffix}>"
generate_structure = getattr(obj, "_generate_structure", []) # Object metric?
if len(generate_structure):
generic = util.Camelize(obj.name) + "Object"
tag = generic + "Tag"
return f"ObjectMetric<{generic}, struct {tag}>"
if obj.type == "counter":
return "CounterMetric<impl::CounterType::eBaseOrLabeled>"
return util.Camelize(obj.type) + "Metric"
def extra_type_name(typ: str) -> str:
"""
Returns the corresponding C++ type for event's extra key types.
"""
if typ == "boolean":
return "bool"
elif typ == "string":
return "nsCString"
elif typ == "quantity":
return "uint32_t"
else:
return "UNSUPPORTED"
def structure_type_name(typ: str) -> str:
"""
Returns the corresponding C++ type for objects' structure types.
"""
if typ == "boolean":
return "bool"
elif typ == "string":
return "nsCString"
elif typ == "number":
return "int64_t"
else:
return "UNSUPPORTED"
def jsonwriter_prefix(typ: str) -> str:
"""
Returns the JSONWriter function prefix for a given structure type.
"""
if typ == "boolean":
return "Bool"
elif typ == "string":
return "String"
elif typ == "number":
return "Int"
else:
return "UNSUPPORTED"
def has_structure(all_objs) -> bool:
"""
Returns true if there's a metric in objs that needs a generated structure.
"""
for _, objs in all_objs.items():
for metric in objs.values():
if hasattr(metric, "_generate_structure"):
return True
return False
@memoize
def get_metrics_template(get_metric_id):
return util.get_jinja2_template(
"cpp.jinja2",
filters=(
("snake_case", lambda value: value.replace(".", "_").replace("-", "_")),
("type_name", type_name),
("extra_type_name", extra_type_name),
("structure_type_name", structure_type_name),
("jsonwriter_prefix", jsonwriter_prefix),
("has_structure", has_structure),
("metric_id", get_metric_id),
),
)
def output_cpp(objs, output_fd, options={}):
"""
Given a tree of objects, output C++ code to the file-like object `output_fd`.
:param objs: A tree of objects (metrics and pings) as returned from
`parser.parse_objects`.
:param output_fd: Writeable file to write the output to.
:param options: options dictionary.
"""
# Monkeypatch util.get_jinja2_template to find templates nearby
def get_local_template(template_name, filters=()):
env = jinja2.Environment(
loader=jinja2.PackageLoader("cpp", "templates"),
trim_blocks=True,
lstrip_blocks=True,
)
env.filters["camelize"] = util.camelize
env.filters["Camelize"] = util.Camelize
for filter_name, filter_func in filters:
env.filters[filter_name] = filter_func
return env.get_template(template_name)
util.get_jinja2_template = get_local_template
if "pings" in objs:
template = util.get_jinja2_template(
"cpp_pings.jinja2",
filters=(("ping_id", generate_ping_ids(objs)),),
)
if objs.get("tags"):
del objs["tags"]
else:
template = get_metrics_template(
options["get_metric_id"]
if "get_metric_id" in options
else generate_metric_ids(objs, options)
)
objs = get_metrics(objs)
output_fd.write(template.render(all_objs=objs, options=options))
output_fd.write("\n")
|