chromium/components/exo/wayland/fuzzer/wayland_utils.py

# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utilities for working with wayland protocols.

A collection of all the generically useful wayland utilities.
"""

from __future__ import absolute_import
from __future__ import print_function

import argparse
from xml.etree import ElementTree


def ReadProtocols(protocol_paths):
  return [ElementTree.parse(path).getroot() for path in protocol_paths]


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 IsDestructor(message):
  """Check if a message is a destructor.

  Args:
    message: the message which you want to check.

  Returns:
    True if the message has the type='destructor' attribute, false otherwise.
  """
  return message.get('type') == 'destructor'


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.get('interface')


def NeedsListener(interface):
  return interface.find('event') is not None


def GetDocumentation(element):
  """Return this element's documentation as a list of strings.

  Args:
    element: the xml.etree.ElementTree node you want the documentation for.

  Returns:
    A list of strings, containing the data from this node's "summary" attribute,
    or its "description" subelement.
  """
  doc = element.find('description')
  if doc is None:
    summary = element.get('summary')
    return [summary] if summary is not None else []
  ret = [doc.get('summary')]
  if doc.text:
    ret += [l.strip() for l in doc.text.split('\n')]
    # Remove blank lines from the tail only.
    while not ret[-1]:
      ret.pop()
  return ret


def ParseOpts(argv):
  """Parses the given command line arguments for the templater.

  Args:
    argv: the arguments to be parsed.

  Returns:
    An argparse.ArgumentParser which provides the user's chosen configuration
    for this templater run.
  """
  parser = argparse.ArgumentParser()
  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)
  return parser.parse_args(argv[1:])