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
|
# -*- coding: utf-8 -*-
# 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
import json
from util import generate_metric_ids, generate_ping_ids, is_implemented_metric_type
from glean_parser import util
def cpp_datatypes_filter(value):
"""
A Jinja2 filter that renders Rust literals.
Based on Python's JSONEncoder, but overrides:
- lists to array literals {}
- strings to "value"
"""
class CppEncoder(json.JSONEncoder):
def iterencode(self, value):
if isinstance(value, list):
yield "{"
first = True
for subvalue in list(value):
if not first:
yield ", "
yield from self.iterencode(subvalue)
first = False
yield "}"
elif isinstance(value, str):
yield '"' + value + '"'
else:
yield from super().iterencode(value)
return "".join(CppEncoder().iterencode(value))
def type_name(obj):
"""
Returns the C++ type to use for a given metric object.
"""
generate_enums = getattr(obj, "_generate_enums", []) # Extra Keys? Reasons?
if len(generate_enums):
for name, suffix in generate_enums:
if not len(getattr(obj, name)) and suffix == "Keys":
return util.Camelize(obj.type) + "Metric<uint32_t>"
else:
return "{}Metric<{}>".format(
util.Camelize(obj.type), util.Camelize(obj.name) + suffix
)
return util.Camelize(obj.type) + "Metric"
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 a util.snake_case function for the templates to use
util.snake_case = lambda value: value.replace(".", "_").replace("-", "_")
# 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
get_metric_id = generate_metric_ids(objs)
get_ping_id = generate_ping_ids(objs)
if len(objs) == 1 and "pings" in objs:
template_filename = "cpp_pings.jinja2"
else:
template_filename = "cpp.jinja2"
template = util.get_jinja2_template(
template_filename,
filters=(
("cpp", cpp_datatypes_filter),
("snake_case", util.snake_case),
("type_name", type_name),
("metric_id", get_metric_id),
("ping_id", get_ping_id),
("is_implemented_type", is_implemented_metric_type),
("Camelize", util.Camelize),
),
)
output_fd.write(template.render(all_objs=objs))
output_fd.write("\n")
|