Commit 6cc7b352 authored by Nicholas Hollingum's avatar Nicholas Hollingum Committed by Commit Bot

Templated version of the wayland fuzzer

This cl adds the templating engine which is used to generate the proto
and the harness used by the fuzzer. The templater uses Jinja2 and has a
.gni wrapper for ease of use in our build system.

I have confirmed that the fuzzer has more coverage than the prototype
in crrev.com/c/1640526, however it requires further work to improve
the coverage to the desired levels.

Bug: 961564
Change-Id: Ie27d7ff0b97cf3ed88dadfebf4d977682054c699
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1645134Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarTimothy Loh <timloh@chromium.org>
Reviewed-by: default avatarOliver Chang <ochang@chromium.org>
Commit-Queue: Nic Hollingum <hollingum@google.com>
Cr-Commit-Position: refs/heads/master@{#669918}
parent cbbf3559
......@@ -2,10 +2,39 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//components/exo/wayland/fuzzer/wayland_templater.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni")
import("//third_party/protobuf/proto_library.gni")
# This is the canonical list of protocols which the generators should use
# (though they don't have to)
kDefaultWaylandProtocols = [
"//third_party/wayland/src/protocol/wayland.xml",
"//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml",
]
wayland_templater("actions_tmpl") {
sources = [
"actions.proto.tmpl",
]
protocols = kDefaultWaylandProtocols
}
wayland_templater("harness_h_tmpl") {
sources = [
"harness.h.tmpl",
]
protocols = kDefaultWaylandProtocols
}
wayland_templater("harness_cc_tmpl") {
sources = [
"harness.cc.tmpl",
]
protocols = kDefaultWaylandProtocols
}
if (use_libfuzzer) {
fuzzer_test("wayland_fuzzer") {
sources = [
......@@ -64,20 +93,27 @@ source_set("server_environment") {
}
source_set("harness") {
sources = [
"harness.cc",
"harness.h",
]
sources = get_target_outputs(":harness_h_tmpl") +
get_target_outputs(":harness_cc_tmpl")
deps = [
":actions",
":harness_cc_tmpl",
":harness_h_tmpl",
"//base",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:xdg_shell_protocol",
]
}
fuzzable_proto_library("actions") {
sources = [
"actions.proto",
sources = get_target_outputs(":actions_tmpl")
# Since the .proto file is under gen/ we need to manually tell the
# output directory to rebase under the source root.
proto_out_dir = rebase_path(".", "//")
deps = [
":actions_tmpl",
]
}
hollingum@chromium.org
hollingum@google.com
......@@ -4,22 +4,32 @@
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
package exo.wayland_fuzzer;
package exo.wayland_fuzzer.actions;
enum small_value {
ZERO = 0;
ONE = 1;
TWO = 2;
THREE = 3;
}
// Enumerates all the expected globals which the fuzzer might target. It is
// reasonable for the fuzzer to target the "unspecified" global, in which case
// we will attempt to bind an index that does not exist.
enum global {
GLOBAL_UNSPECIFIED = 0;
wayland_wl_compositor = 1;
wayland_wl_shm = 2;
{% for interface in interfaces if interface.is_global %}
{{interface.name}} = {{interface.idx + 1}};
{% endfor %}
}
message action {
oneof act {
wayland_wl_display_get_registry act_wayland_wl_display_get_registry = 3;
wayland_wl_display_sync act_wayland_wl_display_sync = 4;
wayland_wl_registry_bind act_wayland_wl_registry_bind = 5;
{% for interface in interfaces %}
{% for request in interface.requests %}
{{interface.name}}_{{request.name}} act_{{interface.name}}_{{request.name}} = {{request.idx + 1}};
{% endfor %}
{% endfor %}
}
}
......@@ -27,20 +37,23 @@ message actions {
repeated action acts = 1;
}
message wayland_wl_display_get_registry {
uint32 receiver = 1;
}
message wayland_wl_display_sync {
uint32 receiver = 1;
}
{% for interface in interfaces %}
{% for request in interface.requests %}
{% if request.name != "bind" or interface.name != "wl_registry" %}
message {{interface.name}}_{{request.name}} {
small_value receiver = 1;
{% for arg in request.args if arg.proto_type %}
{{arg.proto_type}} {{arg.name}} = {{loop.index + 1}};
{% endfor %}
}
{% endif %}
{% endfor %}
{% endfor %}
// Bind is a special case where we want the fuzzer to grab global interfaces we
// know about (so we use an enum) but the other fields are free game.
//
// Next available id: 4
message wayland_wl_registry_bind {
uint32 receiver = 1;
message wl_registry_bind {
small_value receiver = 1;
global global = 2;
}
......@@ -18,7 +18,7 @@ class FuzzerEnvironment {
exo::wayland_fuzzer::ServerEnvironment server_environment_;
};
DEFINE_TEXT_PROTO_FUZZER(const exo::wayland_fuzzer::actions& acts) {
DEFINE_TEXT_PROTO_FUZZER(const exo::wayland_fuzzer::actions::actions& acts) {
static base::AtExitManager exit_manager;
static FuzzerEnvironment environment;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/exo/wayland/fuzzer/harness.h"
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <wayland-util.h>
#include <cstring>
#include <functional>
#include "base/logging.h"
#include "components/exo/wayland/fuzzer/actions.pb.h"
namespace exo {
namespace wayland_fuzzer {
namespace wayland {
namespace wl_display {
void sync(Harness* harness,
const wayland_fuzzer::wayland_wl_display_sync& action);
void get_registry(Harness* harness,
const wayland_wl_display_get_registry& action);
} // namespace wl_display
namespace wl_registry {
void bind(Harness* harness, const wayland_wl_registry_bind& action);
void global(void* data,
struct wl_registry* registry,
uint32_t name,
const char* interface,
uint32_t version);
void global_remove(void* data, struct wl_registry* registry, uint32_t name);
static const struct wl_registry_listener kListener = {global, global_remove};
} // namespace wl_registry
namespace wl_callback {
void done(void* data, struct wl_callback* receiver, uint32_t callback_data);
static const struct wl_callback_listener kListener = {done};
} // namespace wl_callback
namespace wl_compositor {}
namespace wl_shm {
void format(void* data, struct wl_shm* receiver, uint32_t format);
static const struct wl_shm_listener kListener = {format};
} // namespace wl_shm
} // namespace wayland
void wayland::wl_display::sync(
Harness* harness,
const wayland_fuzzer::wayland_wl_display_sync& action) {
struct wl_display* receiver = harness->get_wl_display(action.receiver());
if (!receiver)
return;
struct wl_callback* callback = wl_display_sync(receiver);
wl_callback_add_listener(callback, &wayland::wl_callback::kListener, harness);
harness->add_wl_callback(callback);
}
void wayland::wl_display::get_registry(
Harness* harness,
const wayland_fuzzer::wayland_wl_display_get_registry& action) {
struct wl_display* receiver = harness->get_wl_display(action.receiver());
if (!receiver)
return;
struct wl_registry* new_object = wl_display_get_registry(receiver);
wl_registry_add_listener(new_object, &wayland::wl_registry::kListener,
harness);
harness->add_wl_registry(new_object);
}
void wayland::wl_registry::bind(Harness* harness,
const wayland_wl_registry_bind& action) {
struct wl_registry* receiver = harness->get_wl_registry(action.receiver());
if (!receiver)
return;
void* new_object = nullptr;
switch (action.global()) {
case global::wayland_wl_compositor:
if (harness->wl_compositor_globals_.empty())
return;
new_object = wl_registry_bind(
receiver, harness->wl_compositor_globals_[0].first,
&wl_compositor_interface, harness->wl_compositor_globals_[0].second);
harness->add_wl_compositor(
static_cast<struct wl_compositor*>(new_object));
break;
case global::wayland_wl_shm:
if (harness->wl_shm_globals_.empty())
return;
new_object = wl_registry_bind(receiver, harness->wl_shm_globals_[0].first,
&wl_shm_interface,
harness->wl_shm_globals_[0].second);
wl_shm_add_listener(static_cast<struct wl_shm*>(new_object),
&wayland::wl_shm::kListener, harness);
harness->add_wl_shm(static_cast<struct wl_shm*>(new_object));
break;
default:
return;
}
}
void wayland::wl_registry::global(void* data,
struct wl_registry* registry,
uint32_t name,
const char* interface,
uint32_t version) {
#define GLOBL(WLT) \
if (strcmp(interface, #WLT) == 0) { \
harness->WLT##_globals_.emplace_back(name, version); \
}
Harness* harness = static_cast<Harness*>(data);
GLOBL(wl_display);
GLOBL(wl_registry);
GLOBL(wl_callback);
GLOBL(wl_compositor);
GLOBL(wl_shm);
#undef GLOBL
}
void wayland::wl_registry::global_remove(void* data,
struct wl_registry* registry,
uint32_t name) {}
void wayland::wl_callback::done(void* data,
struct wl_callback* receiver,
uint32_t callback_data) {}
void wayland::wl_shm::format(void* data,
struct wl_shm* receiver,
uint32_t format) {}
Harness::Harness() {
wl_display_list_.emplace_back(wl_display_connect(nullptr));
DCHECK(wl_display_list_.front());
}
Harness::~Harness() {
wl_display_disconnect(wl_display_list_.front());
}
void Harness::Run(const wayland_fuzzer::actions& actions) {
for (const wayland_fuzzer::action& act : actions.acts())
Run(act);
}
void Harness::Run(const wayland_fuzzer::action& action) {
switch (action.act_case()) {
case action::ActCase::ACT_NOT_SET:
wl_display_roundtrip(wl_display_list_.front());
break;
case action::ActCase::kActWaylandWlDisplayGetRegistry:
wayland::wl_display::get_registry(
this, action.act_wayland_wl_display_get_registry());
break;
case action::ActCase::kActWaylandWlDisplaySync:
wayland::wl_display::sync(this, action.act_wayland_wl_display_sync());
break;
case action::ActCase::kActWaylandWlRegistryBind:
wayland::wl_registry::bind(this, action.act_wayland_wl_registry_bind());
break;
}
}
} // namespace wayland_fuzzer
} // namespace exo
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/exo/wayland/fuzzer/harness.h"
#include <wayland-util.h>
{% for protocol in protocol_names %}
{% if protocol == 'wayland' %}
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
{% else %}
#include <{{protocol | replace('_','-')}}-client-protocol.h>
{% endif %}
{% endfor %}
#include <cstring>
#include <functional>
#include "base/logging.h"
#include "components/exo/wayland/fuzzer/actions.pb.h"
namespace exo {
namespace wayland_fuzzer {
{% for interface in interfaces if interface.has_listener and interface.name != 'wl_display' %}
namespace {{interface.name}} {
{% for event in interface.events %}
void {{event.name}}(
void* data,
::{{interface.name}}* receiver
{% for arg in event.args %}
, {{arg.cpp_type}} {{arg.name}}
{% endfor %}
);
{% endfor %}
static const ::{{interface.name}}_listener kListener = {
{% for event in interface.events %}
{{event.name}},
{% endfor %}
};
} // namespace {{interface.name}}
{% endfor %}
{% for interface in interfaces if interface.name != 'wl_registry' %}
namespace {{interface.name}} {
{% for event in interface.events %}
void {{event.name}}(
void* data,
::{{interface.name}}* receiver
{% for arg in event.args %}
, {{arg.cpp_type}} {{arg.name}}
{% endfor %}
){
{% if event.is_constructor %}
Harness* harness = static_cast<Harness*>(data);
{% for arg in event.args if arg.type == 'new_id' %}
{{arg.cpp_type}} new_object = {{arg.name}};
{% endfor %}
{% if event.constructed_has_listener %}
{{event.constructed}}_add_listener(new_object, &{{event.constructed}}::kListener, harness);
{% endif %}
harness->add_{{event.constructed}}(new_object);
{% endif %}
}
{% endfor %}
{% for request in interface.requests %}
void {{request.name}}(Harness* harness,
const actions::{{interface.name}}_{{request.name}}& action) {
::{{interface.name}}* receiver = harness->get_{{interface.name}}(action.receiver());
if (!receiver)
return;
{% if request.is_constructor %}
::{{request.constructed}}* new_object =
{% endif %}
{{interface.name}}_{{request.name}}(receiver
{% for arg in request.args %}
{% if arg.type == 'object' %}
, harness->get_{{arg.interface}}(action.{{arg.name}}())
{% elif arg.type != 'new_id' %}
, action.{{arg.name}}(){% if arg.type == 'string' %}.c_str(){% endif %}
{% endif %}
{% endfor %}
);
{% if request.is_constructor %}
{% if request.constructed_has_listener %}
{{request.constructed}}_add_listener(new_object, &{{request.constructed}}::kListener, harness);
{% endif %}
harness->add_{{request.constructed}}(new_object);
{% endif %}
}
{% endfor %}
} // namespace {{interface.name}}
{% endfor %}
namespace wl_registry {
void bind(Harness* harness, const actions::wl_registry_bind& action) {
::wl_registry* receiver = harness->get_wl_registry(action.receiver());
if (!receiver)
return;
switch (action.global()) {
{% for interface in interfaces if interface.is_global %}
case actions::global::{{interface.name}}: {
if (harness->{{interface.name}}_globals_.empty())
return;
void* new_object = wl_registry_bind(
receiver, harness->{{interface.name}}_globals_[0].first,
&{{interface.name}}_interface, harness->{{interface.name}}_globals_[0].second);
{% if interface.has_listener and interface.name != 'wl_display' %}
{{interface.name}}_add_listener(static_cast<::{{interface.name}}*>(new_object), &{{interface.name}}::kListener, harness);
{% endif %}
harness->add_{{interface.name}}(
static_cast<::{{interface.name}}*>(new_object));
break;
}
{% endfor %}
case actions::global::global_INT_MIN_SENTINEL_DO_NOT_USE_:
case actions::global::global_INT_MAX_SENTINEL_DO_NOT_USE_:
case actions::global::GLOBAL_UNSPECIFIED:
break;
}
}
void global(void* data,
::wl_registry* registry,
uint32_t name,
const char* interface,
uint32_t version) {
Harness* harness = static_cast<Harness*>(data);
{% for interface in interfaces if interface.is_global %}
if (strcmp(interface, "{{interface.name}}") == 0) {
harness->{{interface.name}}_globals_.emplace_back(name, version);
return;
}
{% endfor %}
}
} // namespace wl_registry
void wl_registry::global_remove(void* data, ::wl_registry* registry, uint32_t name) {}
Harness::Harness() {
wl_display_list_.emplace_back(wl_display_connect(nullptr));
DCHECK(wl_display_list_.front());
}
Harness::~Harness() {
wl_display_disconnect(wl_display_list_.front());
}
void Harness::Run(const actions::actions& all_steps) {
for (const actions::action& current : all_steps.acts())
Run(current);
}
void Harness::Run(const actions::action& current_step) {
switch (current_step.act_case()) {
case actions::action::ActCase::ACT_NOT_SET:
wl_display_roundtrip(wl_display_list_.front());
break;
{% for interface in interfaces %}
{% for request in interface.requests %}
case actions::action::ActCase::kAct{{(interface.name + "_" + request.name) | replace('_', ' ') | title | replace(' ', '')}}:
{{interface.name}}::{{request.name}}(this, current_step.act_{{interface.name}}_{{request.name}}());
break;
{% endfor %}
{% endfor %}
}
}
} // namespace wayland_fuzzer
} // namespace exo
......@@ -10,16 +10,15 @@
#include "base/macros.h"
#include "components/exo/wayland/fuzzer/actions.pb.h"
struct wl_display;
struct wl_registry;
struct wl_callback;
struct wl_compositor;
struct wl_shm;
// Forwards declarations for the wayland-defined structs.
{% for interface in interfaces %}
struct {{interface.name}};
{% endfor %}
namespace exo {
namespace wayland_fuzzer {
// When using LPM to fuzz wayland, the wauland_fuzzer::actions proto defines the
// When using LPM to fuzz wayland, the wayland_fuzzer::actions proto defines the
// sequence of events that the fuzzer wants to perform. It then falls to this
// harness to actually convert that sequence into the relevant calls, as if it
// were a normal wayland client.
......@@ -29,26 +28,26 @@ class Harness {
~Harness();
void Run(const wayland_fuzzer::action& actions);
void Run(const wayland_fuzzer::actions& actions);
// TODO(hollingum): This is a macro for now but when we move to autogenerated
// code this will be generated directly.
#define GET_AND_SET(WLT) \
std::vector<struct WLT*> WLT##_list_; \
void add_##WLT(struct WLT* new_obj) { WLT##_list_.push_back(new_obj); } \
struct WLT* get_##WLT(uint32_t idx) const { \
return idx < WLT##_list_.size() ? WLT##_list_[idx] : nullptr; \
} \
std::vector<std::pair<uint32_t, uint32_t>> WLT##_globals_
GET_AND_SET(wl_display);
GET_AND_SET(wl_registry);
GET_AND_SET(wl_callback);
GET_AND_SET(wl_compositor);
GET_AND_SET(wl_shm);
#undef GET_AND_SET
void Run(const actions::action& current_step);
void Run(const actions::actions& all_steps);
{% for interface in interfaces %}
std::vector<::{{interface.name}}*> {{interface.name}}_list_;
void add_{{interface.name}}(::{{interface.name}}* new_obj) {
{{interface.name}}_list_.push_back(new_obj);
}
::{{interface.name}}* get_{{interface.name}}(uint32_t idx) const {
return idx < {{interface.name}}_list_.size()
? {{interface.name}}_list_[idx]
: nullptr;
}
{% if interface.is_global %}
std::vector<std::pair<uint32_t, uint32_t>> {{interface.name}}_globals_;
{% endif %}
{% endfor %}
private:
DISALLOW_COPY_AND_ASSIGN(Harness);
......
......@@ -46,9 +46,9 @@ class WaylandFuzzerTest : public test::ExoTestBaseViews {
};
void RunHarness(Harness* harness, base::WaitableEvent* event) {
wayland_fuzzer::actions acts;
acts.add_acts()->mutable_act_wayland_wl_display_get_registry()->set_receiver(
0);
actions::actions acts;
acts.add_acts()->mutable_act_wl_display_get_registry()->set_receiver(
actions::small_value::ZERO);
acts.add_acts();
harness->Run(acts);
event->Signal();
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# A wayland templater takes one or more |protocols| and processes its
# |sources| so as to instantiate a template for fuzzing wayland.
# The output of the wayland source will be the input's path, rebased
# under the gen/ directory and with the .tmpl suffix removed.
#
# This templater is used for all kinds of files (i.e. more than just
# c++), so we do not provide a convenient source_set target to depend
# on. Instead you must manually depend on the outputs like:
#
# wayland_templater("foo_tmpl") {
# ...
# }
#
# source_set("foo") {
# sources = get_target_outputs(":foo_tmpl")
# deps = [
# ":foo_tmpl",
# ]
# }
template("wayland_templater") {
assert(defined(invoker.sources), "Need sources for wayland_templater")
assert(defined(invoker.protocols), "Need protocols for wayland_templater")
# Process the inputs to determine the output:
# "//a/b/c.xyz.tmpl" -> "gen/a/b/c.xyz"
template_outputs = []
foreach(src, invoker.sources) {
dir = get_path_info(src, "dir")
name = get_path_info(src, "name")
template_outputs += [ "${target_gen_dir}/${dir}/${name}" ]
}
# Jinja2 doesnt like having ".." in the target path, so we give it the
# source-tree's path rather than the build-directory's relative path (which
# usually contains "..").
build_to_src_path = rebase_path("//", root_build_dir)
src_path_to_inputs = rebase_path(invoker.sources, "//")
build_path_to_outputs = rebase_path(template_outputs, root_build_dir)
build_path_to_protocols = rebase_path(invoker.protocols, root_build_dir)
action(target_name) {
script = "//third_party/blink/renderer/build/scripts/run_with_pythonpath.py"
sources = [
script,
"wayland_templater.py",
] + invoker.sources + invoker.protocols
outputs = template_outputs
args = [
"-I",
rebase_path("//third_party", root_build_dir),
rebase_path("wayland_templater.py", root_build_dir),
"--directory",
build_to_src_path,
"--input",
] + src_path_to_inputs + [ "--output" ] + build_path_to_outputs +
[ "--spec" ] + build_path_to_protocols
}
}
# Copyright (c) 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Templatize a file based on wayland specifications.
The templating engine takes an input template and one or more wayland
specifications (see third_party/wayland/src/protocol/wayland.dtd), and
instantiates the template based on the wayland content.
"""
from __future__ import absolute_import
from __future__ import print_function
import argparse
import sys
from xml.etree import ElementTree
import jinja2
proto_type_conversions = {
'object': 'small_value',
'int': 'int32',
'uint': 'uint32',
'string': 'string',
'fd': 'uint32',
}
cpp_type_conversions = {
'int': 'int32_t',
'uint': 'uint32_t',
'fixed': 'wl_fixed_t',
'string': 'const char*',
'array': 'struct wl_array*',
'fd': 'int32_t',
}
def AllInterfaces(protocols):
"""Get the interfaces in these protocols.
Args:
protocols: the list of protocols you want the interfaces of.
Yields:
Tuples (p, i) of (p)rotocol (i)nterface.
"""
for p in protocols:
for i in p.findall('interface'):
yield (p, i)
def AllMessages(protocols):
"""Get the messages in these protocols.
Args:
protocols: the list of protocols you want the messages of.
Yields:
Tuples (p, i, m) of (p)rotocol, (i)nterface, and (m)essage.
"""
for (p, i) in AllInterfaces(protocols):
for r in i.findall('request'):
yield (p, i, r)
for e in i.findall('event'):
yield (p, i, e)
def GetConstructorArg(message):
"""Get the argument that this message constructs, or None.
Args:
message: the message which you want to find the constructor arg of.
Returns:
The argument (as an ElementTree node) that constructs a new interface, or
None.
"""
return message.find('arg[@type="new_id"]')
def IsConstructor(message):
"""Check if a message is a constructor.
Args:
message: the message which you want to check.
Returns:
True if the message constructs an object (via new_id), False otherwise.
"""
return GetConstructorArg(message) is not None
def GetConstructedInterface(message):
"""Gets the interface constructed by a message.
Note that even if IsConstructor(message) returns true, get_constructed can
still return None when the message constructs an unknown interface (e.g.
wl_registry.bind()).
Args:
message: the event/request which may be a constructor.
Returns:
The name of the constructed interface (if there is one), or None.
"""
cons_arg = GetConstructorArg(message)
if cons_arg is None:
return None
return cons_arg.attrib.get('interface')
def NeedsListener(interface):
return interface.find('event') is not None
def GetCppType(arg):
ty = arg.attrib['type']
if ty in ['object', 'new_id']:
return ('::' if 'interface' in arg.attrib else '') + arg.attrib.get(
'interface', 'void') + '*'
return cpp_type_conversions[ty]
class TemplaterContext(object):
"""The context object used for recording stateful/expensive things.
An instance of this class is used when generating the template data, we use
it to cache pre-computed information, as well as to side-effect stateful
queries (such as counters) while generating the template data.
"""
def __init__(self, protocols):
self.non_global_names = {
GetConstructedInterface(m) for _, _, m in AllMessages(protocols)
} - {None}
self.interfaces_with_listeners = {
i.attrib['name']
for p, i in AllInterfaces(protocols)
if NeedsListener(i)
}
self.counts = {}
def GetAndIncrementCount(self, counter):
"""Return the number of times the given key has been counted.
Args:
counter: the key used to identify this count value.
Returns:
An int which is the number of times this method has been called before
with this counter's key.
"""
self.counts[counter] = self.counts.get(counter, 0) + 1
return self.counts[counter] - 1
def GetArg(arg):
ty = arg.attrib['type']
return {
'name': arg.attrib['name'],
'type': ty,
'proto_type': proto_type_conversions.get(ty),
'cpp_type': GetCppType(arg),
'interface': arg.attrib.get('interface'),
}
def GetMessage(message, context):
name = message.attrib['name']
constructed = GetConstructedInterface(message)
return {
'name':
name,
'idx':
context.GetAndIncrementCount('message_index'),
'args': [GetArg(a) for a in message.findall('arg')],
'is_constructor':
IsConstructor(message),
'constructed':
constructed,
'constructed_has_listener':
constructed in context.interfaces_with_listeners,
}
def GetInterface(interface, context):
name = interface.attrib['name']
return {
'name':
name,
'idx':
context.GetAndIncrementCount('interface_index'),
'is_global':
name not in context.non_global_names,
'events': [GetMessage(m, context) for m in interface.findall('event')],
'requests': [
GetMessage(m, context) for m in interface.findall('request')
],
'has_listener':
NeedsListener(interface)
}
def GetTemplateData(protocol_paths):
protocols = [ElementTree.parse(path).getroot() for path in protocol_paths]
context = TemplaterContext(protocols)
interfaces = []
for p in protocols:
for i in p.findall('interface'):
interfaces.append(GetInterface(i, context))
return {
'protocol_names': [p.attrib['name'] for p in protocols],
'interfaces': interfaces,
}
def main(argv):
"""Execute the templater, based on the user provided args.
Args:
argv: the command line arguments (including the script name)
"""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'-d',
'--directory',
help='treat input paths as relative to this directory',
default='.')
parser.add_argument(
'-i',
'--input',
help='path to the input template file (relative to -d)',
required=True)
parser.add_argument(
'-o',
'--output',
help='path to write the generated file to',
required=True)
parser.add_argument(
'-s',
'--spec',
help='path(s) to the wayland specification(s)',
nargs='+',
required=True)
parsed_args = parser.parse_args(argv[1:])
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(parsed_args.directory),
keep_trailing_newline=True, # newline-terminate generated files
lstrip_blocks=True,
trim_blocks=True) # so don't need {%- -%} everywhere
template = env.get_template(parsed_args.input)
with open(parsed_args.output, 'w') as out_fi:
out_fi.write(template.render(GetTemplateData(parsed_args.spec)))
if __name__ == '__main__':
main(sys.argv)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment