1
0
Fork 0
gedit/tools/generate-message.py
Daniel Baumann 61eec86fe3
Adding upstream version 48.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 19:48:29 +02:00

777 lines
25 KiB
Python
Executable file

#!/usr/bin/env python
from xml.etree import ElementTree
import sys
import re
import os
from optparse import OptionParser
import pwd
import datetime
class ParamSpec:
def __init__(self, name, nick, desc, flags, **kwargs):
self.name = name
self.nick = nick
self.desc = desc
self.flags = flags
self.args = []
def finalizer(self, container):
return None
def get_value(self, val, container):
return 'g_value_set_pointer (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_pointer (%s);' % (container, self.cname(), val)
def prop_enum(self):
return 'PROP_' + self.name.upper().replace('-', '_')
def cname(self):
return self.name.replace('-', '_')
def ctype(self):
return 'gpointer'
def spec_name(self):
name = self.__class__.__name__
if name.startswith('ParamSpec'):
name = name[len('ParamSpec'):]
return name.lower()
def format_str(self, s):
if s.startswith('"') or s.startswith('_("'):
return s
return '"%s"' % (s.replace('"', '\\"'),)
def __str__(self):
name = "g_param_spec_" + self.spec_name()
indent = " " * (len(name) + 2)
argstr = ''
if self.args:
ret = (",\n%s" % (indent,)).join(str(x) for x in self.args)
argstr = "\n%s%s," % (indent, ret)
flags = (' |\n%s' % (indent,)).join(x.strip() for x in self.flags.split('|'))
return "%s (%s,\n%s%s,\n%s%s,%s\n%s%s)" % (name,
self.format_str(self.name),
indent,
self.format_str(self.nick),
indent,
self.format_str(self.desc),
argstr,
indent,
flags)
def write(self):
delim = "\n" + (" " * 33)
spec = delim.join(str(self).splitlines())
return """
g_object_class_install_property (object_class,
%s,
%s);""" % (self.prop_enum(), spec)
class ParamSpecTyped(ParamSpec):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpec.__init__(self, name, nick, desc, flags, **kwargs)
if 'ctype' in kwargs:
self._ctype = kwargs['ctype']
else:
parts = kwargs['gtype'].split('_')
del parts[1]
self._ctype = '%s *' % (''.join(x.title() for x in parts),)
self.args.append(kwargs['gtype'])
def ctype(self):
return self._ctype
class ParamSpecBoolean(ParamSpec):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpec.__init__(self, name, nick, desc, flags, **kwargs)
if 'default' in kwargs:
if kwargs['default'].lower() in ['true', '1']:
self.args = ['TRUE']
else:
self.args = ['FALSE']
else:
self.args = ['FALSE']
def get_value(self, val, container):
return 'g_value_set_boolean (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_boolean (%s);' % (container, self.cname(), val)
def ctype(self):
return 'gboolean'
class ParamSpecBoxed(ParamSpecTyped):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecTyped.__init__(self, name, nick, desc, flags, **kwargs)
def finalizer(self, container):
return 'if (%s->%s != NULL)\n{\n\tg_boxed_free (%s->%s);\n}' % (container, self.cname(), container, self.cname())
def get_value(self, val, container):
return 'g_value_set_boxed (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_dup_boxed (%s);' % (container, self.cname(), val)
class ParamSpecNumeric(ParamSpec):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpec.__init__(self, name, nick, desc, flags, **kwargs)
if 'min' in kwargs:
minv = kwargs['min']
else:
minv = self.min_value()
if 'max' in kwargs:
maxv = kwargs['max']
else:
maxv = self.max_value()
if 'default' in kwargs:
default = kwargs['default']
else:
default = self.default_value()
self.args = [minv, maxv, default]
def min_value(self):
return '0'
def max_value(self):
return '0'
def default_value(self):
return '0'
class ParamSpecDouble(ParamSpecNumeric):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecNumeric.__init__(self, name, nick, desc, flags, **kwargs)
def min_value(self):
return 'G_MINDOUBLE'
def max_value(self):
return 'G_MAXDOUBLE'
def ctype(self):
return 'gdouble'
def get_value(self, val, container):
return 'g_value_set_double (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_double (%s);' % (container, self.cname(), val)
class ParamSpecEnum(ParamSpecTyped):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecTyped.__init__(self, name, nick, desc, flags, **kwargs)
if 'default' in kwargs:
default = kwargs['default']
else:
default = '0'
self.args.append(default)
def get_value(self, val, container):
return 'g_value_set_enum (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_enum (%s);' % (container, self.cname(), val)
class ParamSpecFlags(ParamSpecEnum):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecEnum.__init__(self, name, nick, desc, flags, **kwargs)
def get_value(self, val, container):
return 'g_value_set_flags (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_flags (%s);' % (container, self.cname(), val)
class ParamSpecFloat(ParamSpecNumeric):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecNumeric.__init__(self, name, nick, desc, flags, **kwargs)
def min_value(self):
return 'G_MINFLOAT'
def max_value(self):
return 'G_MAXFLOAT'
def ctype(self):
return 'gfloat'
def get_value(self, val, container):
return 'g_value_set_float (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_float (%s);' % (container, self.cname(), val)
class ParamSpecInt(ParamSpecNumeric):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecNumeric.__init__(self, name, nick, desc, flags, **kwargs)
def min_value(self):
return 'G_MININT'
def max_value(self):
return 'G_MAXINT'
def ctype(self):
return 'gint'
def get_value(self, val, container):
return 'g_value_set_int (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_int (%s);' % (container, self.cname(), val)
class ParamSpecUInt(ParamSpecNumeric):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecNumeric.__init__(self, name, nick, desc, flags, **kwargs)
def min_value(self):
return '0'
def max_value(self):
return 'G_MAXUINT'
def ctype(self):
return 'guint'
def get_value(self, val, container):
return 'g_value_set_uint (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_get_uint (%s);' % (container, self.cname(), val)
class ParamSpecObject(ParamSpecTyped):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpecTyped.__init__(self, name, nick, desc, flags, **kwargs)
def finalizer(self, container):
return 'if (%s->%s)\n{\n\tg_object_unref (%s->%s);\n}' % (container, self.cname(), container, self.cname())
def get_value(self, val, container):
return 'g_value_set_object (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_dup_object (%s);' % (container, self.cname(), val)
class ParamSpecPointer(ParamSpec):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpec.__init__(self, name, nick, desc, flags, **kwargs)
class ParamSpecString(ParamSpec):
def __init__(self, name, nick, desc, flags, **kwargs):
ParamSpec.__init__(self, name, nick, desc, flags, **kwargs)
if 'default' in kwargs:
default = kwargs['default']
else:
default = 'NULL'
if not default.startswith('"') and not default.startswith('_("') and default != 'NULL':
default = '"' + default.replace('"', '\\"') + '"'
self.args.append(default)
def ctype(self):
return 'gchar *'
def finalizer(self, container):
return 'g_free (%s->%s);' % (container, self.cname())
def get_value(self, val, container):
return 'g_value_set_string (%s, %s->%s);' % (val, container, self.cname())
def set_value(self, val, container):
return '%s->%s = g_value_dup_string (%s);' % (container, self.cname(), val)
_prop_types = {
'boolean': ParamSpecBoolean,
'boxed': ParamSpecBoxed,
'double': ParamSpecDouble,
'enum': ParamSpecEnum,
'flags': ParamSpecFlags,
'float': ParamSpecFloat,
'int': ParamSpecInt,
'object': ParamSpecObject,
'pointer': ParamSpecPointer,
'string': ParamSpecString,
'uint': ParamSpecUInt
}
GPL = """
/*
* #FILENAME
* This file is part of #PROGRAM
*
* Copyright (C) #YEAR - #AUTHOR
*
* #PROGRAM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* #PROGRAM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with #PROGRAM; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
"""
LGPL = """
/*
* #FILENAME
* This file is part of #PROGRAM
*
* Copyright (C) #YEAR - #AUTHOR
*
* ${2} is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* #PROGRAM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
"""
class Include:
def __init__(self, elem):
self.path = elem.text
self.is_system = elem.get('system') != None
def __str__(self):
if self.is_system:
return '#include <%s>' % (self.path,)
else:
return '#include "%s"' % (self.path,)
class Message:
def __init__(self, elem):
self.name = elem.get('name')
self.namespace = elem.get('namespace')
if not self.name:
sys.stderr.write("Name for message is required...\n")
return
if not self.namespace:
sys.stderr.write("Namespace for message is required (%s)...\n" % (self.name,))
self.properties = []
self.includes = []
for prop in elem.findall('property'):
self.add_prop(prop)
for inc in elem.findall('include'):
self.includes.append(Include(inc))
self.setup()
def setup(self):
parts = [x.group(0) for x in re.finditer('[A-Z]+[a-z0-9]*', self.name)]
self.ns_upper = self.namespace.upper()
self.ns_lower = self.namespace.lower()
self.name_upper = '_'.join(x.upper() for x in parts)
self.name_lower = self.name_upper.lower()
self.cname_upper = '%s_%s' % (self.ns_upper, self.name_upper)
self.cname_lower = self.cname_upper.lower()
self.ctype = '%s_%s_%s' % (self.ns_upper, 'TYPE', self.name_upper)
self.cclass = '%s%sClass' % (self.namespace, self.name)
self.cobj = '%s%s' % (self.namespace, self.name)
self.filename = '%s-%s' % (self.ns_lower, '-'.join(x.lower() for x in parts))
def normalize_name(self, name):
if not name:
return name
return name.replace('_', '-')
def parse_flags(self, flags):
return ' | '.join('G_PARAM_' + x.strip().replace('-', '_').upper() for x in flags.split('|'))
def add_prop(self, elem):
name = self.normalize_name(elem.get('name'))
typ = elem.get('type')
if not name:
sys.stderr.write("Name for property not specified...\n")
return False
if not typ:
sys.stderr.write("Type for property `%s' not specified...\n" % (name,))
return False
if not typ in _prop_types:
sys.stderr.write("Invalid property type `%s'...\n" % (typ,))
blurb = elem.get('blurb')
nick = elem.get('nick')
flags = elem.get('flags')
if not flags:
flags = 'G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS'
else:
flags = self.parse_flags(flags)
if not nick:
nick = ' '.join(map(lambda x: x.title(), name.split('-')))
if not blurb:
blurb = str(nick)
attr = elem.attrib
for k in ['blurb', 'nick', 'flags', 'name', 'type']:
if k in attr:
del attr[k]
self.properties.append(_prop_types[typ](name, nick, blurb, flags, **attr))
def _write(self, *args):
if len(args) > 1:
self.f.write(args[0] % args[1:])
elif len(args) > 0:
self.f.write(args[0])
self.f.write("\n")
def _write_license(self, text, options, filename):
text = text.replace('#PROGRAM', options.program)
text = text.replace('#YEAR', str(datetime.date.today().year))
text = text.replace('#AUTHOR', options.author)
text = text.replace('#FILENAME', filename)
self._write(text)
def _write_gpl(self, options, filename):
self._write_license(GPL, options, filename)
def _write_lgpl(self, options, filename):
self._write_license(LGPL, options, filename)
def write_header(self, outdir, options):
fname = '%s.h' % (self.filename,)
self.f = file(os.path.join(outdir, fname), 'w')
if options.gpl:
self._write_gpl(options, fname)
elif options.lgpl:
self._write_lgpl(options, fname)
self._write("#ifndef __%s_H__", self.cname_upper)
self._write("#define __%s_H__", self.cname_upper)
self._write()
self._write("#include <gedit/gedit-message.h>\n")
self._write("G_BEGIN_DECLS\n")
alignon = len('%s_IS_%s_CLASS(klass)' % (self.ns_upper, self.name_upper))
alignsec = ' ' * (alignon + len('#define '))
self._write("#define %s (%s_get_type ())", self.ctype.ljust(alignon), self.cname_lower)
self._write("#define %s (G_TYPE_CHECK_INSTANCE_CAST ((obj),\\\n%s%s,\\\n%s%s))",
('%s(obj)' % (self.cname_upper,)).ljust(alignon),
alignsec, self.ctype,
alignsec, self.cobj)
self._write("#define %s (G_TYPE_CHECK_INSTANCE_CAST ((obj),\\\n%s%s,\\\n%s%s))",
('%s_CONST(obj)' % (self.cname_upper,)).ljust(alignon),
alignsec, self.ctype,
alignsec, '%s const' % (self.cobj,))
self._write("#define %s (G_TYPE_CHECK_CLASS_CAST ((klass),\\\n%s%s,\\\n%s%s))",
('%s_CLASS(klass)' % (self.cname_upper,)).ljust(alignon),
alignsec, self.ctype,
alignsec, self.cclass)
self._write("#define %s (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\\\n%s%s))",
('%s_IS_%s(obj)' % (self.ns_upper, self.name_upper)).ljust(alignon),
alignsec, self.ctype)
self._write("#define %s (G_TYPE_CHECK_CLASS_TYPE ((klass),\\\n%s%s))",
('%s_IS_%s_CLASS(klass)' % (self.ns_upper, self.name_upper)).ljust(alignon),
alignsec, self.ctype)
self._write("#define %s (G_TYPE_INSTANCE_GET_CLASS ((obj),\\\n%s%s,\\\n%s%s))",
('%s_GET_CLASS(obj)' % (self.cname_upper,)).ljust(alignon),
alignsec, self.ctype,
alignsec, self.cclass)
thepriv = '%sPrivate' % (self.cobj,)
alignon = len(thepriv)
self._write()
self._write('typedef struct _%s %s;', self.cobj.ljust(alignon), self.cobj)
self._write('typedef struct _%s %s;', self.cclass.ljust(alignon), self.cclass)
self._write('typedef struct _%s %s;', thepriv.ljust(alignon), thepriv)
self._write()
self._write('struct _%s', self.cobj)
self._write('{')
self._write('\tGeditMessage parent;')
self._write()
self._write('\t%s *priv;', thepriv)
self._write('};')
self._write()
self._write('struct _%s', self.cclass)
self._write('{')
self._write('\tGeditMessageClass parent_class;')
self._write('};')
self._write()
self._write('GType %s_get_type (void) G_GNUC_CONST;', self.cname_lower)
self._write()
self._write('G_END_DECLS')
self._write()
self._write("#endif /* __%s_H__ */", self.cname_upper)
self.f.close()
def needs_finalize(self):
for prop in self.properties:
out = prop.finalizer('ignore')
if out:
return True
return False
def write_body(self, outdir, options):
fname = '%s.c' % (self.filename,)
self.f = file(os.path.join(outdir, fname), 'w')
if options.gpl:
self._write_gpl(options, fname)
elif options.lgpl:
self._write_lgpl(options, fname)
self._write('#include "config.h"')
self._write('#include "%s.h"', self.filename)
for inc in self.includes:
self._write('%s', inc)
self._write()
self._write('enum')
self._write('{')
self._write('\tPROP_0,')
self._write()
for prop in self.properties:
self._write('\t%s,', prop.prop_enum())
self._write('};')
self._write()
self._write('struct _%sPrivate', self.cobj)
self._write('{')
for prop in self.properties:
ct = prop.ctype()
if not ct.endswith('*'):
ct += ' '
self._write('\t%s%s;', ct, prop.cname())
self._write('};')
self._write('''
G_DEFINE_TYPE_EXTENDED ({0},
{1},
GEDIT_TYPE_MESSAGE,
0,
G_ADD_PRIVATE ({0}))
'''.format(self.cobj, self.cname_lower))
if self.needs_finalize():
self._write('static void')
self._write('%s_finalize (GObject *obj)', self.cname_lower)
self._write('{')
self._write('\t%s *msg = %s (obj);', self.cobj, self.cname_upper)
self._write()
haswritten = False
for prop in self.properties:
out = prop.finalizer('msg->priv')
if out:
haswritten = True
self._write('\t%s', '\n\t'.join(out.splitlines()))
if haswritten:
self._write()
self._write('\tG_OBJECT_CLASS (%s_parent_class)->finalize (obj);', self.cname_lower)
self._write('}')
self._write()
ll = ' ' * (len(self.cname_lower))
self._write('static void')
self._write('%s_get_property (GObject *obj,', self.cname_lower)
self._write('%s guint prop_id,', ll)
self._write('%s GValue *value,', ll)
self._write('%s GParamSpec *pspec)', ll)
self._write('{')
self._write('\t%s *msg;', self.cobj)
self._write()
self._write('\tmsg = %s (obj);', self.cname_upper)
self._write()
self._write('\tswitch (prop_id)')
self._write('\t{')
for prop in self.properties:
self._write('\t\tcase %s:', prop.prop_enum())
self._write('\t\t\t%s', prop.get_value('value', 'msg->priv'))
self._write('\t\t\tbreak;')
self._write('\t}')
self._write('}')
self._write()
self._write('static void')
self._write('%s_set_property (GObject *obj,', self.cname_lower)
self._write('%s guint prop_id,', ll)
self._write('%s GValue const *value,', ll)
self._write('%s GParamSpec *pspec)', ll)
self._write('{')
self._write('\t%s *msg;', self.cobj)
self._write()
self._write('\tmsg = %s (obj);', self.cname_upper)
self._write()
self._write('\tswitch (prop_id)')
self._write('\t{')
for prop in self.properties:
self._write('\t\tcase %s:', prop.prop_enum())
out = prop.finalizer('msg->priv')
if out:
out = '\n\t\t\t'.join(out.splitlines())
self._write('\t\t{')
self._write('\t\t\t%s' % (out,))
self._write('\t\t\t%s', prop.set_value('value', 'msg->priv'))
self._write('\t\t\tbreak;')
if out:
self._write('\t\t}')
self._write('\t}')
self._write('}')
self._write()
self._write('static void')
self._write('%s_class_init (%s *klass)', self.cname_lower, self.cclass)
self._write('{')
self._write('\tGObjectClass *object_class = G_OBJECT_CLASS(klass);')
self._write()
if self.needs_finalize():
self._write('\tobject_class->finalize = %s_finalize;', self.cname_lower)
self._write()
self._write('\tobject_class->get_property = %s_get_property;', self.cname_lower)
self._write('\tobject_class->set_property = %s_set_property;', self.cname_lower)
pp = 'g_object_class_install_property ('
prefix = '\t%s' % (' ' * len(pp),)
for prop in self.properties:
self._write()
out = str(prop)
out = ("\n%s" % (prefix,)).join(out.splitlines())
self._write('\tg_object_class_install_property (object_class,\n%s%s,', prefix, prop.prop_enum())
self._write('%s%s);', prefix, out)
if len(self.properties) == 0:
self._write()
self._write('}')
self._write()
self._write('static void')
self._write('%s_init (%s *message)', self.cname_lower, self.cobj)
self._write('{')
self._write('\tmessage->priv = %s_get_instance_private (message);', self.cname_lower)
self._write('}')
self.f.close()
class Generator:
def __init__(self, filename, options):
self.filename = filename
self.doc = ElementTree.parse(filename)
self.options = options
def write_message(self, outputdir, elem):
msg = Message(elem)
msg.write_header(outputdir, options)
msg.write_body(outputdir, options)
def write(self, outputdir = '.'):
for message in self.doc.findall('message'):
self.write_message(outputdir, message)
author = pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0]
parser = OptionParser()
parser.add_option("-o", "--output-directory", dest="output", help="Output directory", default=".")
parser.add_option("-g", "--gpl", dest="gpl", action="store_true", help="Add GPL stub to generated files", default=False)
parser.add_option("-l", "--lgpl", dest="lgpl", action="store_true", help="Add LGPL stub to generated files", default=False)
parser.add_option("-p", "--program", dest="program", help="Program name used in license snippet", default="program")
parser.add_option("-a", "--author", dest="author", help="Author used in license snippet", default=author)
(options, args) = parser.parse_args()
for arg in args:
generator = Generator(arg, options)
generator.write(options.output)
# vi:ex:ts=4:et