sbus_codegen revision b699c4d7f85a5404be1d1ee9450331aea869b886
0b062f4990db5cc6db2fe3398926f71b92a67407Brian Wellington#!/usr/bin/python
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Authors:
0b062f4990db5cc6db2fe3398926f71b92a67407Brian Wellington# Stef Walter <stefw@redhat.com>
0b062f4990db5cc6db2fe3398926f71b92a67407Brian Wellington#
0b062f4990db5cc6db2fe3398926f71b92a67407Brian Wellington# Copyright (C) 2014 Red Hat
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
9016767f4e15191b7c763b8a4ad36a57dc2705a2Mark Andrews# This program is free software; you can redistribute it and/or modify
9016767f4e15191b7c763b8a4ad36a57dc2705a2Mark Andrews# it under the terms of the GNU General Public License as published by
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# the Free Software Foundation; either version 3 of the License, or
9016767f4e15191b7c763b8a4ad36a57dc2705a2Mark Andrews# (at your option) any later version.
9016767f4e15191b7c763b8a4ad36a57dc2705a2Mark Andrews#
9016767f4e15191b7c763b8a4ad36a57dc2705a2Mark Andrews# This program is distributed in the hope that it will be useful,
9016767f4e15191b7c763b8a4ad36a57dc2705a2Mark Andrews# but WITHOUT ANY WARRANTY; without even the implied warranty of
0b062f4990db5cc6db2fe3398926f71b92a67407Brian Wellington# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews# GNU General Public License for more details.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# You should have received a copy of the GNU General Public License
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# along with this program. If not, see <http://www.gnu.org/licenses/>.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Some parser code from GLib
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Copyright (C) 2008-2011 Red Hat, Inc.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# This library is free software; you can redistribute it and/or
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# modify it under the terms of the GNU Lesser General Public
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# License as published by the Free Software Foundation; either
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# version 2 of the License, or (at your option) any later version.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# This library is distributed in the hope that it will be useful,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# but WITHOUT ANY WARRANTY; without even the implied warranty of
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Lesser General Public License for more details.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# You should have received a copy of the GNU Lesser General
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Public License along with this library; if not, write to the
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Boston, MA 02111-1307, USA.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews# Portions by: David Zeuthen <davidz@redhat.com>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# DBus interfaces are defined here:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# The introspection data format has become the standard way to represent a
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# DBus interface. For many examples see /usr/share/dbus-1/interfaces/ on a
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# typical linux machine.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# A word about annotations. These are extra flags or values that can be
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# assigned to anything. So far, the codegen supports this annotation:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# org.freedesktop.DBus.GLib.CSymbol
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# - An annotation specified in the specification that tells us what C symbol
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# to generate for a given interface or method. By default the codegen will
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# build up a symbol name from the DBus name.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinimport optparse
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinimport os
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinimport re
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinimport StringIO
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinimport sys
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinimport xml.parsers.expat
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# -----------------------------------------------------------------------------
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Objects
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass DBusXmlException(Exception):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein line = 0
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein file = None
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein # Lets us print problems like a compiler would
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __str__(self):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein message = Exception.__str__(self)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if self.file and self.line:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return "%s:%d: %s" % (self.file, self.line, message)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein elif self.file:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return "%s: %s" % (self.file, message)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein else:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return message
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass Base:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __init__(self, name):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if not name:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein raise DBusXmlException('No name on element')
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.name = name
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.annotations = { }
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def c_name(self):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return self.annotations.get("org.freedesktop.DBus.GLib.CSymbol", self.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass Arg(Base):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __init__(self, method, name, signature):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Base.__init__(self, name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.method = method
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.signature = signature
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass Method(Base):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __init__(self, iface, name):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Base.__init__(self, name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.iface = iface
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.in_args = []
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.out_args = []
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def fq_c_name(self):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return "%s_%s" % (self.iface.c_name(), self.c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass Signal(Base):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __init__(self, iface, name):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Base.__init__(self, name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.iface = iface
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.args = []
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def fq_c_name(self):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return "%s_%s" % (self.iface.c_name(), self.c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass Property(Base):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __init__(self, iface, name, signature, access):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Base.__init__(self, name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.iface = iface
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.signature = signature
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.readable = False
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.writable = False
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if access == 'readwrite':
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.readable = True
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.writable = True
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein elif access == 'read':
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.readable = True
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein elif access == 'write':
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.writable = True
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein else:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein raise DBusXmlException('Invalid access type %s'%self.access)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def fq_c_name(self):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return "%s_%s" % (self.iface.c_name(), self.c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinclass Interface(Base):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def __init__(self, name):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Base.__init__(self, name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.methods = []
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.signals = []
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.properties = []
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein def c_name(self):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return self.annotations.get("org.freedesktop.DBus.GLib.CSymbol",
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein self.name.replace(".", "_"))
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# -----------------------------------------------------------------------------
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein# Code Generation
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeindef out(format, *args):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein str = format % args
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein sys.stdout.write(str)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein sys.stdout.write("\n")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeindef source_args(parent, args, suffix):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("/* arguments for %s.%s */", parent.iface.name, parent.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("const struct sbus_arg_meta %s%s[] = {", parent.fq_c_name(), suffix)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for arg in args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" { \"%s\", \"%s\" },", arg.name, arg.signature)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" { NULL, }")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("};")
8ffa8320abcc17ae593af566cb946a58fe293860Brian Wellington
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeindef source_methods(iface, methods):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for meth in methods:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if meth.in_args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein source_args(meth, meth.in_args, "__in")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if meth.out_args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein source_args(meth, meth.out_args, "__out")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("/* methods for %s */", iface.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("const struct sbus_method_meta %s__methods[] = {", iface.c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for meth in methods:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" {")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" \"%s\", /* name */", meth.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if meth.in_args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" %s__in,", meth.fq_c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein else:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" NULL, /* no in_args */")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if meth.out_args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" %s__out,", meth.fq_c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein else:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" NULL, /* no out_args */")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" },")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" { NULL, }")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("};")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeindef source_signals(iface, signals):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for sig in iface.signals:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if sig.args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein source_args(sig, sig.args, "__args")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("/* signals for %s */", iface.name)
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews out("const struct sbus_signal_meta %s__signals[] = {", iface.c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for sig in signals:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" {")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" \"%s\", /* name */", sig.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if sig.args:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" %s__args", sig.fq_c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein else:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" NULL, /* no args */")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" },")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" { NULL, }")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("};")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeindef source_properties(iface, properties):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("/* property info for %s */", iface.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("const struct sbus_property_meta %s__properties[] = {", iface.c_name())
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for prop in properties:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" {")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" \"%s\", /* name */", prop.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" \"%s\", /* signature */", prop.signature)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if prop.readable and prop.writable:
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews out(" SBUS_PROPERTY_READABLE | SBUS_PROPERTY_WRITABLE,")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein elif prop.readable:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" SBUS_PROPERTY_READABLE,")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein elif prop.writable:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" SBUS_PROPERTY_WRITABLE,")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein else:
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein assert False, "should not be reached"
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" },")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out(" { NULL, }")
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews out("};")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeindef header_interface(iface):
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("")
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("/* interface info for %s */", iface.name)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein out("extern const struct sbus_interface_meta %s_meta;", iface.c_name())
def source_interface(iface):
out("")
out("/* interface info for %s */", iface.name)
out("const struct sbus_interface_meta %s_meta = {", iface.c_name())
out(" \"%s\", /* name */", iface.name)
if iface.methods:
out(" %s__methods,", iface.c_name())
else:
out(" NULL, /* no methods */")
if iface.signals:
out(" %s__signals,", iface.c_name())
else:
out(" NULL, /* no signals */")
if iface.properties:
out(" %s__properties", iface.c_name())
else:
out(" NULL, /* no propetries */")
out("};")
def generate_source(ifaces, filename):
basename = os.path.basename(filename)
out("/* The following definitions are auto-generated from %s */", basename)
out("")
out("#include \"util/util.h\"")
out("#include \"sbus/sssd_dbus.h\"")
out("#include \"sbus/sssd_dbus_meta.h\"")
for iface in ifaces:
# The methods
if iface.methods:
source_methods(iface, iface.methods)
# The signals array
if iface.signals:
source_signals(iface, iface.signals)
# The properties array
if iface.properties:
source_properties(iface, iface.properties)
# The sbus_interface structure
source_interface(iface)
def generate_header(ifaces, filename):
basename = os.path.basename(filename)
guard = "__%s__" % re.sub(r'([^_A-Z0-9])', "_", basename.upper())
out("/* The following declarations are auto-generated from %s */", basename)
out("")
out("#ifndef %s", guard)
out("#define %s", guard)
out("")
out("#include \"sbus/sssd_dbus.h\"")
out("")
out("/* ------------------------------------------------------------------------")
out(" * DBus Interface Metadata")
out(" *")
out(" * These structure definitions are filled in with the information about")
out(" * the interfaces, methods, properties and so on.")
out(" *")
out(" * The actual definitions are found in the accompanying C file next")
out(" * to this header.")
out(" */")
for iface in ifaces:
header_interface(iface)
out("")
out("#endif /* %s */", guard)
# -----------------------------------------------------------------------------
# XML Interface Parsing
STATE_TOP = 'top'
STATE_NODE = 'node'
STATE_INTERFACE = 'interface'
STATE_METHOD = 'method'
STATE_SIGNAL = 'signal'
STATE_PROPERTY = 'property'
STATE_ARG = 'arg'
STATE_ANNOTATION = 'annotation'
STATE_IGNORED = 'ignored'
def expect_attr(attrs, name):
if name not in attrs:
raise DBusXmlException("Missing attribute '%s'" % name)
if attrs[name] == "":
raise DBusXmlException("Empty attribute '%s'" % name)
return attrs[name]
class DBusXMLParser:
def __init__(self, filename):
parser = xml.parsers.expat.ParserCreate()
parser.CommentHandler = self.handle_comment
parser.CharacterDataHandler = self.handle_char_data
parser.StartElementHandler = self.handle_start_element
parser.EndElementHandler = self.handle_end_element
self.parsed_interfaces = []
self.cur_object = None
self.state = STATE_TOP
self.state_stack = []
self.cur_object = None
self.cur_object_stack = []
self.arg_count = 0
try:
with open(filename, "r") as f:
parser.ParseFile(f)
except DBusXmlException, ex:
ex.line = parser.CurrentLineNumber
ex.file = filename
raise
except xml.parsers.expat.ExpatError, ex:
exc = DBusXmlException(str(ex))
exc.line = ex.lineno
exc.file = filename
raise exc
def handle_comment(self, data):
pass
def handle_char_data(self, data):
pass
def handle_start_element(self, name, attrs):
old_state = self.state
old_cur_object = self.cur_object
if self.state == STATE_IGNORED:
self.state = STATE_IGNORED
elif self.cur_object and name == STATE_ANNOTATION:
val = attrs.get('value', '')
self.cur_object.annotations[expect_attr(attrs, 'name')] = val
self.state = STATE_IGNORED
elif self.state == STATE_TOP:
if name == STATE_NODE:
self.state = STATE_NODE
else:
self.state = STATE_IGNORED
elif self.state == STATE_NODE:
if name == STATE_INTERFACE:
self.state = STATE_INTERFACE
iface = Interface(expect_attr(attrs, 'name'))
self.cur_object = iface
self.parsed_interfaces.append(iface)
else:
self.state = STATE_IGNORED
elif self.state == STATE_INTERFACE:
if name == STATE_METHOD:
self.state = STATE_METHOD
method = Method(self.cur_object, expect_attr(attrs, 'name'))
self.cur_object.methods.append(method)
self.cur_object = method
self.arg_count = 0
elif name == STATE_SIGNAL:
self.state = STATE_SIGNAL
signal = Signal(self.cur_object, expect_attr(attrs, 'name'))
self.cur_object.signals.append(signal)
self.cur_object = signal
self.arg_count = 0
elif name == STATE_PROPERTY:
self.state = STATE_PROPERTY
prop = Property(self.cur_object,
expect_attr(attrs, 'name'),
expect_attr(attrs, 'type'),
expect_attr(attrs, 'access'))
self.cur_object.properties.append(prop)
self.cur_object = prop
else:
self.state = STATE_IGNORED
elif self.state == STATE_METHOD:
if name == STATE_ARG:
self.state = STATE_ARG
arg = Arg(self.cur_object,
expect_attr(attrs, 'name'),
expect_attr(attrs, 'type'))
direction = attrs.get('direction', 'in')
if direction == 'in':
self.cur_object.in_args.append(arg)
elif direction == 'out':
self.cur_object.out_args.append(arg)
else:
raise DBusXmlException('Invalid direction "%s"' % direction)
self.cur_object = arg
else:
self.state = STATE_IGNORED
elif self.state == STATE_SIGNAL:
if name == STATE_ARG:
self.state = STATE_ARG
arg = Arg(self.cur_object,
expect_attr(attrs, 'name'),
expect_attr(attrs, 'type'))
self.cur_object.args.append(arg)
self.cur_object = arg
else:
self.state = STATE_IGNORED
elif self.state == STATE_PROPERTY:
self.state = STATE_IGNORED
elif self.state == STATE_ARG:
self.state = STATE_IGNORED
else:
assert False, 'Unhandled state "%s" while entering element with name "%s"' % (self.state, name)
self.state_stack.append(old_state)
self.cur_object_stack.append(old_cur_object)
def handle_end_element(self, name):
self.state = self.state_stack.pop()
self.cur_object = self.cur_object_stack.pop()
def parse_options():
parser = optparse.OptionParser("usage: %prog [options] introspect.xml ...")
parser.set_description("sbus_codegen generates sbus interface structures \
from standard XML Introspect data.")
parser.add_option("--mode",
dest="mode", default="header",
help="'header' or 'source' (default: header)",
metavar="MODE")
parser.add_option("--output",
dest="output", default=None,
help="Set output file name (default: stdout)",
metavar="FILE")
(options, args) = parser.parse_args()
if not args:
print >> sys.stderr, "sbus_codegen: no input file specified"
sys.exit(2)
if options.mode not in ["header", "source"]:
print >> sys.stderr, "sbus_codegen: specify --mode=header or --mode=source"
return options, args
def main():
options, args = parse_options()
if options.output:
sys.stdout = buf = StringIO.StringIO()
for filename in args:
parser = DBusXMLParser(filename)
if options.mode == "header":
generate_header(parser.parsed_interfaces, filename)
elif options.mode == "source":
generate_source(parser.parsed_interfaces, filename)
else:
assert False, "should not be reached"
# Write output at end to be nice to 'make'
if options.output:
output = open(options.output, "w")
output.write(buf.getvalue())
output.close()
if __name__ == "__main__":
try:
main()
except DBusXmlException, ex:
print >> sys.stderr, str(ex)
sys.exit(1)