# 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 https://mozilla.org/MPL/2.0/.
import functools
import hashlib
from os.path
import dirname
from pathlib
import Path
import jinja2
from buildconfig
import config, topsrcdir
# type: ignore
from schema_parser
import parse_and_validate
THIS_DIR = Path(dirname(__file__))
TEMPLATES = THIS_DIR /
"templates"
@functools.cache
def get_deps():
# Any imported python module is added as a dependency automatically,
# so we only need the templates.
return {
*[str(p)
for p
in TEMPLATES.iterdir()],
str(THIS_DIR /
"gecko-trace.schema.yaml"),
}
def generate_cpp_events(output_fd, *inputs):
"""Generate C++ events header"""
events = parse_and_validate(inputs
if inputs
else load_schema_index())
template = _jinja2_env().get_template(
"GeckoTraceEvents.h.jinja2")
output_fd.write(
template.render(
events=events,
enabled=config.substs.get(
"GECKO_TRACE_ENABLE",
False),
# Generate a unique hash to prevent include guard conflicts when
# multiple event files are generated and included together (e.g., in gtests).
# This ensures each generated header has a distinct include guard.
input_hash=hashlib
.sha256(
"".join(inputs).encode())
.hexdigest()
.upper()[:
15],
)
)
return get_deps().union(load_schema_index()
if not inputs
else {})
def generate_glean_metrics(output_fd, *inputs):
events = parse_and_validate(inputs
if inputs
else load_schema_index())
template = _jinja2_env().get_template(
"generated-metrics.yaml.jinja2")
output_fd.write(
template.render(
events=events,
)
)
def generate_glean_adapter(output_fd, *inputs):
events = parse_and_validate(inputs
if inputs
else load_schema_index())
template = _jinja2_env().get_template(
"glean_adapter.rs.jinja2")
output_fd.write(
template.render(
events=events,
)
)
return get_deps().union(load_schema_index()
if not inputs
else {})
@functools.cache
def load_schema_index():
index = THIS_DIR.parent /
"index.py"
with open(index)
as f:
index_src = f.read()
namespace = {}
exec(index_src, namespace)
return [str(Path(topsrcdir) / x)
for x
in namespace[
"gecko_trace_files"]]
@functools.cache
def _jinja2_env():
from jinja2.exceptions
import TemplateRuntimeError
from jinja2.ext
import Extension
class RaiseExtension(Extension):
tags = set([
"raise"])
def parse(self, parser):
lineno = next(parser.stream).lineno
message_node = parser.parse_expression()
return jinja2.nodes.CallBlock(
self.call_method(
"_raise", [message_node], lineno=lineno),
[],
[],
[],
lineno=lineno,
)
def _
raise(self, msg, caller):
raise TemplateRuntimeError(msg)
def camelize(name):
"""Convert snake_case to PascalCase"""
return "".join(
word.capitalize()
for word
in name.replace(
"-",
"_").replace(
".",
"_").split(
"_")
)
def snake_case(name):
"""Convert name to snake_case"""
return name.replace(
".",
"_").replace(
"-",
"_").lower()
def debug(value):
print(value)
return value
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(TEMPLATES),
trim_blocks=
True,
lstrip_blocks=
True,
extensions=[RaiseExtension],
)
env.filters[
"camelize"] = camelize
env.filters[
"snake_case"] = snake_case
env.filters[
"debug"] = debug
return env
def main(*args):
# mach requires this
pass