diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-05-09 20:29:21 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-07-03 16:08:53 +0200 |
commit | 4c17c33edd7e4dc8aac4e84371d8a6e8e19a4ce1 (patch) | |
tree | 6e452d0a344b2c561b94b5af3cd4762fa193fabe | |
parent | c04af51eb260708cd36ed2cc7a97084da7d51e90 (diff) | |
download | external_libqmi-4c17c33edd7e4dc8aac4e84371d8a6e8e19a4ce1.zip external_libqmi-4c17c33edd7e4dc8aac4e84371d8a6e8e19a4ce1.tar.gz external_libqmi-4c17c33edd7e4dc8aac4e84371d8a6e8e19a4ce1.tar.bz2 |
codegen: new `qmi-codegen' build utility
It will try to generate message creator/parsers from a manually maintained
JSON-encoded database.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | build-aux/Makefile.am | 3 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Client.py | 213 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Container.py | 249 | ||||
-rw-r--r-- | build-aux/qmi-codegen/ContainerInput.py | 38 | ||||
-rw-r--r-- | build-aux/qmi-codegen/ContainerOutput.py | 38 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Field.py | 225 | ||||
-rw-r--r-- | build-aux/qmi-codegen/FieldArray.py | 111 | ||||
-rw-r--r-- | build-aux/qmi-codegen/FieldBasic.py | 99 | ||||
-rw-r--r-- | build-aux/qmi-codegen/FieldStruct.py | 121 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Makefile.am | 14 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Message.py | 180 | ||||
-rw-r--r-- | build-aux/qmi-codegen/MessageList.py | 69 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Struct.py | 50 | ||||
-rwxr-xr-x | build-aux/qmi-codegen/qmi-codegen | 65 | ||||
-rw-r--r-- | build-aux/qmi-codegen/utils.py | 148 | ||||
-rw-r--r-- | configure.ac | 1 |
17 files changed, 1624 insertions, 2 deletions
@@ -42,3 +42,5 @@ src/qmi-enum-types.c cli/.deps cli/.libs cli/qmicli + +build-aux/qmi-codegen/*.pyc diff --git a/build-aux/Makefile.am b/build-aux/Makefile.am index 37dc9ed..b369687 100644 --- a/build-aux/Makefile.am +++ b/build-aux/Makefile.am @@ -1,3 +1,2 @@ -SUBDIRS = templates - +SUBDIRS = templates qmi-codegen diff --git a/build-aux/qmi-codegen/Client.py b/build-aux/qmi-codegen/Client.py new file mode 100644 index 0000000..fa1bb16 --- /dev/null +++ b/build-aux/qmi-codegen/Client.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +from MessageList import MessageList +import utils + +class Client: + def __init__(self, objects_dictionary): + self.name = None + + # Loop items in the list, looking for the special 'Client' type + for object_dictionary in objects_dictionary: + if object_dictionary['type'] == 'Client': + self.name = object_dictionary['name'] + + # We NEED the Client field + if self.name is None: + raise ValueError('Missing Client field') + + + def __emit_class(self, hfile, cfile): + translations = { 'underscore' : utils.build_underscore_name(self.name), + 'no_prefix_underscore_upper' : string.upper(utils.build_underscore_name(self.name[4:])), + 'camelcase' : utils.build_camelcase_name (self.name) } + + # Emit class header + template = ( + '#define QMI_TYPE_${no_prefix_underscore_upper} (${underscore}_get_type ())\n' + '#define QMI_${no_prefix_underscore_upper}(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QMI_TYPE_${no_prefix_underscore_upper}, ${camelcase}))\n' + '#define QMI_${no_prefix_underscore_upper}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QMI_TYPE_${no_prefix_underscore_upper}, ${camelcase}Class))\n' + '#define QMI_IS_${no_prefix_underscore_upper}(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QMI_TYPE_${no_prefix_underscore_upper}))\n' + '#define QMI_IS_${no_prefix_underscore_upper}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QMI_TYPE_${no_prefix_underscore_upper}))\n' + '#define QMI_${no_prefix_underscore_upper}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QMI_TYPE_${no_prefix_underscore_upper}, ${camelcase}Class))\n' + '\n' + 'typedef struct _${camelcase} ${camelcase};\n' + 'typedef struct _${camelcase}Class ${camelcase}Class;\n' + '\n' + 'struct _${camelcase} {\n' + ' QmiClient parent;\n' + ' gpointer priv_unused;\n' + '};\n' + '\n' + 'struct _${camelcase}Class {\n' + ' QmiClientClass parent;\n' + '};\n' + '\n' + 'GType ${underscore}_get_type (void);\n' + '\n') + hfile.write(string.Template(template).substitute(translations)) + + # Emit class source + template = ( + '\n' + 'G_DEFINE_TYPE (${camelcase}, ${underscore}, QMI_TYPE_CLIENT);\n' + '\n' + 'static void\n' + '${underscore}_init (${camelcase} *self)\n' + '{\n' + '}\n' + '\n' + 'static void\n' + '${underscore}_class_init (${camelcase}Class *klass)\n' + '{\n' + '}\n' + '\n') + cfile.write(string.Template(template).substitute(translations)) + + + def __emit_methods(self, hfile, cfile, message_list): + translations = { 'underscore' : utils.build_underscore_name(self.name), + 'camelcase' : utils.build_camelcase_name (self.name) } + + for message in message_list.list: + translations['message_underscore'] = utils.build_underscore_name(message.name) + translations['message_fullname_underscore'] = utils.build_underscore_name(message.fullname) + translations['input_camelcase'] = utils.build_camelcase_name(message.input.fullname) + translations['output_camelcase'] = utils.build_camelcase_name(message.output.fullname) + translations['input_underscore'] = utils.build_underscore_name(message.input.fullname) + translations['output_underscore'] = utils.build_underscore_name(message.output.fullname) + + if message.input.fields is None: + input_arg_template = 'gpointer unused' + translations['input_var'] = 'NULL' + else: + input_arg_template = '${input_camelcase} *input' + translations['input_var'] = 'input' + template = ( + '\n' + 'void ${underscore}_${message_underscore} (\n' + ' ${camelcase} *self,\n' + ' %s,\n' + ' guint timeout,\n' + ' GCancellable *cancellable,\n' + ' GAsyncReadyCallback callback,\n' + ' gpointer user_data);\n' + '${output_camelcase} *${underscore}_${message_underscore}_finish (\n' + ' ${camelcase} *self,\n' + ' GAsyncResult *res,\n' + ' GError **error);\n' % input_arg_template) + hfile.write(string.Template(template).substitute(translations)) + + template = ( + '\n' + '${output_camelcase} *\n' + '${underscore}_${message_underscore}_finish (\n' + ' ${camelcase} *self,\n' + ' GAsyncResult *res,\n' + ' GError **error)\n' + '{\n' + ' if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))\n' + ' return NULL;\n' + '\n' + ' return ${output_underscore}_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));\n' + '}\n' + '\n' + 'static void\n' + '${message_underscore}_ready (\n' + ' QmiDevice *device,\n' + ' GAsyncResult *res,\n' + ' GSimpleAsyncResult *simple)\n' + '{\n' + ' GError *error = NULL;\n' + ' QmiMessage *reply;\n' + ' ${output_camelcase} *output;\n' + '\n' + ' reply = qmi_device_command_finish (device, res, &error);\n' + ' if (!reply) {\n' + ' g_simple_async_result_take_error (simple, error);\n' + ' g_simple_async_result_complete (simple);\n' + ' g_object_unref (simple);\n' + ' return;\n' + ' }\n' + '\n' + ' /* Parse reply */\n' + ' output = ${message_fullname_underscore}_response_parse (reply, &error);\n' + ' if (!output)\n' + ' g_simple_async_result_take_error (simple, error);\n' + ' else\n' + ' g_simple_async_result_set_op_res_gpointer (simple,\n' + ' output,\n' + ' (GDestroyNotify)${output_underscore}_unref);\n' + ' g_simple_async_result_complete (simple);\n' + ' g_object_unref (simple);\n' + ' qmi_message_unref (reply);\n' + '}\n' + '\n' + 'void\n' + '${underscore}_${message_underscore} (\n' + ' ${camelcase} *self,\n' + ' %s,\n' + ' guint timeout,\n' + ' GCancellable *cancellable,\n' + ' GAsyncReadyCallback callback,\n' + ' gpointer user_data)\n' + '{\n' + ' GSimpleAsyncResult *result;\n' + ' QmiMessage *request;\n' + ' GError *error = NULL;\n' + '\n' + ' result = g_simple_async_result_new (G_OBJECT (self),\n' + ' callback,\n' + ' user_data,\n' + ' ${underscore}_${message_underscore});\n' + '\n' + ' request = ${message_fullname_underscore}_request_create (\n' + ' qmi_client_get_next_transaction_id (QMI_CLIENT (self)),\n' + ' qmi_client_get_cid (QMI_CLIENT (self)),\n' + ' ${input_var},\n' + ' &error);\n' + ' if (!request) {\n' + ' g_prefix_error (&error, "Couldn\'t create request message: ");\n' + ' g_simple_async_result_take_error (result, error);\n' + ' g_simple_async_result_complete_in_idle (result);\n' + ' g_object_unref (result);\n' + ' return;\n' + ' }\n' + '\n' + ' qmi_device_command (QMI_DEVICE (qmi_client_peek_device (QMI_CLIENT (self))),\n' + ' request,\n' + ' timeout,\n' + ' cancellable,\n' + ' (GAsyncReadyCallback)${message_underscore}_ready,\n' + ' result);\n' + ' qmi_message_unref (request);\n' + '}\n' + '\n' % input_arg_template) + cfile.write(string.Template(template).substitute(translations)) + + def emit(self, hfile, cfile, message_list): + # First, emit common class code + utils.add_separator(hfile, 'CLIENT', self.name); + utils.add_separator(cfile, 'CLIENT', self.name); + self.__emit_class(hfile, cfile) + self.__emit_methods(hfile, cfile, message_list) diff --git a/build-aux/qmi-codegen/Container.py b/build-aux/qmi-codegen/Container.py new file mode 100644 index 0000000..ac15691 --- /dev/null +++ b/build-aux/qmi-codegen/Container.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +import utils +from FieldStruct import FieldStruct +from FieldArray import FieldArray +from FieldBasic import FieldBasic + +class Container: + """ + The Container class takes care of handling collections of Input or + Output fields + """ + + def __init__(self, prefix, dictionary): + # The field container prefix usually contains the name of the Message, + # e.g. "Qmi Message Ctl Something" + self.prefix = prefix + + # self.name needs to be set by the subclass + if self.name != 'Input' and self.name != 'Output': + raise ValueError('Cannot handle container \'%s\'' % self.name) + + # Create the composed full name (prefix + name), + # e.g. "Qmi Message Ctl Something Output Result" + self.fullname = self.prefix + ' ' + self.name + + self.fields = None + if dictionary is not None: + self.fields = [] + for field_dictionary in dictionary: + if field_dictionary['format'] == 'array': + self.fields.append(FieldArray(self.fullname, field_dictionary)) + elif field_dictionary['format'] == 'struct': + self.fields.append(FieldStruct(self.fullname, field_dictionary)) + elif field_dictionary['format'] == 'guint8' or \ + field_dictionary['format'] == 'guint16' or \ + field_dictionary['format'] == 'guint32' or \ + field_dictionary['format'] == 'gint8' or \ + field_dictionary['format'] == 'gint16' or \ + field_dictionary['format'] == 'gint32': + self.fields.append(FieldBasic(self.fullname, field_dictionary)) + else: + raise ValueError('Cannot handle type \'%s\'' % field_dictionary['type']) + + + def __emit_tlv_ids_enum(self, f): + if self.fields is None: + return + + translations = { 'enum_type' : utils.build_camelcase_name (self.fullname + ' TLV') } + template = ( + '\n' + '/**\n' + ' * ${enum_type}:\n') + f.write(string.Template(template).substitute(translations)) + + for tlv in self.fields: + translations['enum_name'] = tlv.id_enum_name + translations['enum_value'] = tlv.id + template = ( + ' * @${enum_name}: TODO,\n') + f.write(string.Template(template).substitute(translations)) + + template = ( + ' * TODO: comment enum\n' + ' */\n' + 'typedef enum {\n') + f.write(string.Template(template).substitute(translations)) + + for tlv in self.fields: + translations['enum_name'] = tlv.id_enum_name + translations['enum_value'] = tlv.id + template = ( + ' ${enum_name} = ${enum_value},\n') + f.write(string.Template(template).substitute(translations)) + + template = ( + '} ${enum_type};\n') + f.write(string.Template(template).substitute(translations)) + + + def __emit_types(self, hfile, cfile, translations): + # Emit types header + template = ( + '\n' + 'typedef struct _${camelcase} ${camelcase};\n') + hfile.write(string.Template(template).substitute(translations)) + + # Emit types source + template = ( + '\n' + 'struct _${camelcase} {\n' + ' volatile gint ref_count;\n' + '\n') + cfile.write(string.Template(template).substitute(translations)) + + if self.fields is not None: + for field in self.fields: + translations['field_type'] = field.field_type + translations['field_variable_name'] = field.variable_name + translations['field_name'] = field.name + template = ( + '\n' + ' /* ${field_name} */\n' + ' gboolean ${field_variable_name}_set;\n' + ' ${field_type} ${field_variable_name};\n') + cfile.write(string.Template(template).substitute(translations)) + + cfile.write( + '};\n') + + + def __emit_core(self, hfile, cfile, translations): + # Emit container core header + template = ( + '\n' + '${camelcase} *${underscore}_ref (${camelcase} *self);\n' + 'void ${underscore}_unref (${camelcase} *self);\n') + if self.readonly == False: + template += ( + '${camelcase} *${underscore}_new (void);\n') + hfile.write(string.Template(template).substitute(translations)) + + # Emit container core source + template = ( + '\n' + '/**\n' + ' * ${underscore}_ref:\n' + ' * @self: a #${camelcase}.\n' + ' *\n' + ' * Atomically increments the reference count of @self by one.\n' + ' *\n' + ' * Returns: the new reference to @self.\n' + ' */\n' + '${camelcase} *\n' + '${underscore}_ref (${camelcase} *self)\n' + '{\n' + ' g_return_val_if_fail (self != NULL, NULL);\n' + '\n' + ' g_atomic_int_inc (&self->ref_count);\n' + ' return self;\n' + '}\n' + '\n' + '/**\n' + ' * ${underscore}_unref:\n' + ' * @self: a #${camelcase}.\n' + ' *\n' + ' * Atomically decrements the reference count of @self by one.\n' + ' * If the reference count drops to 0, @self is completely disposed.\n' + ' */\n' + 'void\n' + '${underscore}_unref (${camelcase} *self)\n' + '{\n' + ' g_return_if_fail (self != NULL);\n' + '\n' + ' if (g_atomic_int_dec_and_test (&self->ref_count)) {\n') + cfile.write(string.Template(template).substitute(translations)) + + if self.fields is not None: + for field in self.fields: + if field.dispose is not None: + translations['field_dispose'] = field.dispose + translations['field_variable_name'] = field.variable_name + template = ( + ' ${field_dispose} (self->${field_variable_name});\n') + cfile.write(string.Template(template).substitute(translations)) + + template = ( + ' g_slice_free (${camelcase}, self);\n' + ' }\n' + '}\n') + cfile.write(string.Template(template).substitute(translations)) + + # _new() is only generated if the container is not readonly + if self.readonly == True: + return + + template = ( + '\n' + '/**\n' + ' * ${underscore}_new:\n' + ' *\n' + ' * Allocates a new ${camelcase}.\n' + ' *\n' + ' * Returns: the newly created ${camelcase}.\n' + ' */\n' + '${camelcase} *\n' + '${underscore}_new (void)\n' + '{\n' + ' return g_slice_new0 (${camelcase});\n' + '}\n') + cfile.write(string.Template(template).substitute(translations)) + + + def __emit_fields (self, hfile, cfile): + # Emit field getter/setter + if self.fields is None: + return + + for field in self.fields: + field.emit_types(hfile) + field.emit_getter(hfile, cfile) + if self.readonly == False: + field.emit_setter(hfile, cfile) + + + def emit(self, hfile, cfile): + translations = { 'name' : self.name, + 'camelcase' : utils.build_camelcase_name (self.fullname), + 'underscore' : utils.build_underscore_name (self.fullname) } + + if self.fields is None: + template = ('\n' + '/* Note: no fields in the ${name} container */\n') + hfile.write(string.Template(template).substitute(translations)) + cfile.write(string.Template(template).substitute(translations)) + return + + # Emit the container types + self.__emit_types(hfile, cfile, translations) + + # Emit TLV enums + self.__emit_tlv_ids_enum(cfile) + + # Emit fields + self.__emit_fields(hfile, cfile) + + # Emit the container core + self.__emit_core(hfile, cfile, translations) diff --git a/build-aux/qmi-codegen/ContainerInput.py b/build-aux/qmi-codegen/ContainerInput.py new file mode 100644 index 0000000..2d5c34f --- /dev/null +++ b/build-aux/qmi-codegen/ContainerInput.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +from Field import Field +from Container import Container + +class ContainerInput(Container): + """ + The ContainerInput class takes care of handling collections of Input + fields + """ + + def __init__(self, prefix, dictionary): + + self.name = 'Input' + self.readonly = False + + # Call the parent constructor + Container.__init__(self, prefix, dictionary) diff --git a/build-aux/qmi-codegen/ContainerOutput.py b/build-aux/qmi-codegen/ContainerOutput.py new file mode 100644 index 0000000..ccfc33c --- /dev/null +++ b/build-aux/qmi-codegen/ContainerOutput.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +from Field import Field +from Container import Container + +class ContainerOutput(Container): + """ + The ContainerOutput class takes care of handling collections of Output + fields + """ + + def __init__(self, prefix, dictionary): + + self.name = 'Output' + self.readonly = True + + # Call the parent constructor + Container.__init__(self, prefix, dictionary) diff --git a/build-aux/qmi-codegen/Field.py b/build-aux/qmi-codegen/Field.py new file mode 100644 index 0000000..de26ae1 --- /dev/null +++ b/build-aux/qmi-codegen/Field.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +from Struct import Struct +import utils + +class Field: + """ + The Field class takes care of handling Input and Output TLVs + """ + + def __init__(self, prefix, dictionary): + # The field prefix, usually the name of the Container, + # e.g. "Qmi Message Ctl Something Output" + self.prefix = prefix + # The name of the specific field, e.g. "Result" + self.name = dictionary['name'] + # The specific TLV ID + self.id = dictionary['id'] + # Whether the field is to be considered mandatory in the message + self.mandatory = dictionary['mandatory'] + # Specific format of the field + self.format = dictionary['format'] + # The type, which must always be "TLV" + self.type = dictionary['type'] + # Output Fields may have prerequisites + self.prerequisite = dictionary['prerequisite'] if 'prerequisite' in dictionary else None + + # Strings containing how the given type is to be copied and disposed + self.copy = None + self.dispose = None + # The field type to be used in the generated code + self.field_type = None + + # Create the composed full name (prefix + name), + # e.g. "Qmi Message Ctl Something Output Result" + self.fullname = self.prefix + ' ' + self.name + + # Create the variable name within the Container + self.variable_name = 'arg_' + string.lower(utils.build_underscore_name(self.name)) + + # Create the ID enumeration name + self.id_enum_name = string.upper(utils.build_underscore_name(self.prefix + ' TLV ' + self.name)) + + + def emit_types(self, hfile): + ''' + Subclasses can implement the method to emit the required type + information + ''' + pass + + + def emit_getter(self, hfile, cfile): + translations = { 'name' : self.name, + 'variable_name' : self.variable_name, + 'field_type' : self.field_type, + 'field_dispose_msg' : ' Dispose @value with ' + self.dispose + '() when no longer needed.' if self.dispose is not None else '', + 'field_copy' : self.copy if self.copy is not None else '', + 'underscore' : utils.build_underscore_name(self.name), + 'prefix_camelcase' : utils.build_camelcase_name(self.prefix), + 'prefix_underscore' : utils.build_underscore_name(self.prefix) } + + # Emit the getter header + template = ( + '\n' + 'gboolean ${prefix_underscore}_get_${underscore} (\n' + ' ${prefix_camelcase} *self,\n' + ' ${field_type} *value,\n' + ' GError **error);\n') + hfile.write(string.Template(template).substitute(translations)) + + # Emit the getter source + template = ( + '\n' + '/**\n' + ' * ${prefix_underscore}_get_${underscore}:\n' + ' * @self: a ${prefix_camelcase}.\n' + ' * @value: a placeholder for the output value, or #NULL.\n' + ' * @error: a #GError.\n' + ' *\n' + ' * Get the \'${name}\' field from @self.\n' + ' *\n' + ' * Returns: #TRUE if the field is found and @value is set, #FALSE otherwise.${field_dispose_msg}\n' + ' */\n' + 'gboolean\n' + '${prefix_underscore}_get_${underscore} (\n' + ' ${prefix_camelcase} *self,\n' + ' ${field_type} *value,\n' + ' GError **error)\n' + '{\n' + ' g_return_val_if_fail (self != NULL, FALSE);\n' + '\n' + ' if (!self->${variable_name}_set) {\n' + ' g_set_error (error,\n' + ' QMI_CORE_ERROR,\n' + ' QMI_CORE_ERROR_TLV_NOT_FOUND,\n' + ' "Field \'${name}\' was not found in the message");\n' + ' return FALSE;\n' + ' }\n' + '\n' + ' if (value)\n' + ' *value = ${field_copy}(self->${variable_name});\n' + ' return TRUE;\n' + '}\n') + cfile.write(string.Template(template).substitute(translations)) + + + def emit_setter(self, hfile, cfile): + translations = { 'name' : self.name, + 'variable_name' : self.variable_name, + 'field_type' : self.field_type, + 'field_dispose' : self.dispose + '(self->' + self_variable_name + ');\n' if self.dispose is not None else '', + 'field_copy' : self.copy if self.copy is not None else '', + 'underscore' : utils.build_underscore_name(self.name), + 'prefix_camelcase' : utils.build_camelcase_name(self.prefix), + 'prefix_underscore' : utils.build_underscore_name(self.prefix) } + + # Emit the setter header + template = ( + '\n' + 'gboolean ${prefix_underscore}_set_${underscore} (\n' + ' ${prefix_camelcase} *self,\n' + ' ${field_type} value,\n' + ' GError **error);\n') + hfile.write(string.Template(template).substitute(translations)) + + # Emit the setter source + template = ( + '\n' + '/**\n' + ' * ${prefix_underscore}_set_${underscore}:\n' + ' * @self: a ${prefix_camelcase}.\n' + ' * @value: the value to set.\n' + ' * @error: a #GError.\n' + ' *\n' + ' * Set the \'${name}\' field in the message.\n' + ' *\n' + ' * Returns: #TRUE if @value was successfully set, #FALSE otherwise.\n' + ' */\n' + 'gboolean\n' + '${prefix_underscore}_set_${underscore} (\n' + ' ${prefix_camelcase} *self,\n' + ' ${field_type} value,\n' + ' GError **error)\n' + '{\n' + ' g_return_val_if_fail (self != NULL, FALSE);\n' + '\n' + ' ${field_dispose}' + ' self->${variable_name}_set = TRUE;\n' + ' self->${variable_name} = value;\n' + '\n' + ' return TRUE;\n' + '}\n' + '\n') + cfile.write(string.Template(template).substitute(translations)) + + + def emit_input_mandatory_check(self, f, line_prefix): + if self.mandatory == 'yes': + translations = { 'name' : self.name, + 'variable_name' : self.variable_name, + 'lp' : line_prefix } + + template = ( + '\n' + '${lp}if (!input || !input->${variable_name}_set) {\n' + '${lp} g_set_error (error,\n' + '${lp} QMI_CORE_ERROR,\n' + '${lp} QMI_CORE_ERROR_INVALID_ARGS,\n' + '${lp} "Missing mandatory TLV \'${name}\'");\n' + '${lp} qmi_message_unref (self);\n' + '${lp} return NULL;\n' + '${lp}}\n') + + f.write(string.Template(template).substitute(translations)) + + def emit_input_tlv_add(self, f, line_prefix): + ''' + Subclasses can implement the method to emit the required TLV adding + ''' + pass + + + def emit_output_prerequisite_check(self, f, line_prefix): + translations = { 'lp' : line_prefix } + template = None + if self.prerequisite is not None: + translations['prerequisite_field'] = utils.build_underscore_name(self.prerequisite['field']) + translations['prerequisite_operation'] = self.prerequisite['operation'] + translations['prerequisite_value'] = self.prerequisite['value'] + template = ( + '${lp}/* Prerequisite.... */\n' + '${lp}if (!(self->arg_${prerequisite_field} ${prerequisite_operation} ${prerequisite_value}))\n' + '${lp} break;\n') + else: + template = ( + '${lp}/* No Prerequisites for field */\n') + f.write(string.Template(template).substitute(translations)) + + + def emit_output_tlv_get(self, f, line_prefix): + ''' + Subclasses can implement the method to emit the required TLV retrieval + ''' + pass diff --git a/build-aux/qmi-codegen/FieldArray.py b/build-aux/qmi-codegen/FieldArray.py new file mode 100644 index 0000000..f013943 --- /dev/null +++ b/build-aux/qmi-codegen/FieldArray.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +import utils +from Struct import Struct +from Field import Field + +class FieldArray(Field): + """ + The FieldArray class takes care of handling 'array' format based + Input and Output TLVs + """ + + def __init__(self, prefix, dictionary): + # Call the parent constructor + Field.__init__(self, prefix, dictionary) + + if dictionary['array-element']['format'] != 'struct': + raise ValueError('Cannot handle arrays of format \'%s\'' % dictionary['array-element']['format']) + + # Set a struct as content + self.array_element = Struct(utils.build_camelcase_name (self.fullname + + ' ' + + dictionary['array-element']['name']), + dictionary['array-element']['contents']) + + # We'll use standard GArrays + self.field_type = 'GArray *'; + # The array needs to get disposed + self.dispose = 'g_array_unref' + + + def emit_types(self, f): + ''' + Emit the type for the struct used as array element + ''' + self.array_element.emit(f) + + + def emit_input_tlv_add(self, cfile, line_prefix): + # TODO + raise ValueError('Array as input not implemented yet') + + def emit_output_tlv_get(self, f, line_prefix): + translations = { 'name' : self.name, + 'container_underscore' : utils.build_underscore_name (self.prefix), + 'array_element_type' : self.array_element.name, + 'tlv_id' : self.id_enum_name, + 'variable_name' : self.variable_name, + 'lp' : line_prefix, + 'error' : 'error' if self.mandatory == 'yes' else 'NULL'} + + template = ( + '\n' + '${lp}guint i;\n' + '${lp}guint8 buffer[1024];\n' + '${lp}guint16 buffer_len = 1024;\n' + '${lp}${array_element_type} *item;\n' + '\n' + '${lp}if (qmi_message_tlv_get_varlen (message,\n' + '${lp} ${tlv_id},\n' + '${lp} &buffer_len,\n' + '${lp} buffer,\n' + '${lp} ${error})) {\n' + '${lp} guint8 nitems = buffer[0];\n' + '\n' + '${lp} self->${variable_name}_set = TRUE;\n' + '${lp} self->${variable_name} = g_array_sized_new (FALSE, FALSE, sizeof (${array_element_type}), nitems);\n' + '${lp} for (i = 0, item = (${array_element_type} *)&buffer[1]; i < nitems; i++, item++) {\n') + f.write(string.Template(template).substitute(translations)) + + for struct_field in self.array_element.members: + f.write('%s %s;\n' % (line_prefix, + utils.he_from_le ('item->' + utils.build_underscore_name(struct_field['name']), + 'item->' + utils.build_underscore_name(struct_field['name']), + struct_field['type']))) + + template = ( + '${lp} g_array_insert_val (self->${variable_name}, i, *item);\n' + '${lp} }\n') + + if self.mandatory == 'yes': + template += ( + '${lp}} else {\n' + '${lp} g_prefix_error (error, \"Couldn\'t get the ${name} TLV: \");\n' + '${lp} ${container_underscore}_unref (self);\n' + '${lp} return NULL;\n' + '${lp}}\n') + else: + template += ( + '${lp}}\n') + f.write(string.Template(template).substitute(translations)) diff --git a/build-aux/qmi-codegen/FieldBasic.py b/build-aux/qmi-codegen/FieldBasic.py new file mode 100644 index 0000000..9905c71 --- /dev/null +++ b/build-aux/qmi-codegen/FieldBasic.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +import utils +from Field import Field + +class FieldBasic(Field): + """ + The FieldBasic class takes care of handling Input and Output TLVs based + on basic types (e.g. integers) + """ + + def __init__(self, prefix, dictionary): + + # Call the parent constructor + Field.__init__(self, prefix, dictionary) + + # The field type to be used in the generated code is the same as the one + # given in the database + self.field_type = self.format; + + def emit_input_tlv_add(self, cfile, line_prefix): + translations = { 'name' : self.name, + 'tlv_id' : self.id_enum_name, + 'field_type' : self.field_type, + 'set_variable' : utils.le_from_he ('input->' + self.variable_name, 'tmp', self.field_type), + 'lp' : line_prefix } + + template = ( + '${lp}/* Try to add the \'${name}\' TLV */\n' + '${lp}{\n' + '${lp} ${field_type} tmp;\n' + '\n' + '${lp} ${set_variable};\n' + '\n' + '${lp} if (!qmi_message_tlv_add (self,\n' + '${lp} (guint8)${tlv_id},\n' + '${lp} sizeof (tmp),\n' + '${lp} &tmp,\n' + '${lp} error)) {\n' + '${lp} g_prefix_error (error, \"Couldn\'t set the ${name} TLV: \");\n' + '${lp} qmi_message_unref (self);\n' + '${lp} return NULL;\n' + '${lp} }\n' + '${lp}}\n') + cfile.write(string.Template(template).substitute(translations)) + + + def emit_output_tlv_get(self, f, line_prefix): + translations = { 'name' : self.name, + 'container_underscore' : utils.build_underscore_name (self.prefix), + 'tlv_id' : self.id_enum_name, + 'variable_name' : self.variable_name, + 'lp' : line_prefix, + 'error' : 'error' if self.mandatory == 'yes' else 'NULL'} + + template = ( + '${lp}if (qmi_message_tlv_get (message,\n' + '${lp} ${tlv_id},\n' + '${lp} sizeof (self->${variable_name}),\n' + '${lp} &self->${variable_name},\n' + '${lp} ${error})) {\n' + '${lp} self->${variable_name}_set = TRUE;\n') + cfile.write(string.Template(template).substitute(translations)) + + cfile.write('%s %s;\n' % (line_prefix, + utils.le_from_he ('self->' + self.variable_name, + 'self->' + self.variable_name, + self.field_type))) + if self.mandatory is 'yes': + template = ( + '${lp}} else {\n' + '${lp} g_prefix_error (error, \"Couldn\'t get the ${name} TLV: \");\n' + '${lp} ${container_underscore}_unref (self);\n' + '${lp} return NULL;\n' + '${lp}}\n') + else: + template = ( + '${lp}}\n') + cfile.write(string.Template(template).substitute(translations)) diff --git a/build-aux/qmi-codegen/FieldStruct.py b/build-aux/qmi-codegen/FieldStruct.py new file mode 100644 index 0000000..fc18fd3 --- /dev/null +++ b/build-aux/qmi-codegen/FieldStruct.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +import utils +from Struct import Struct +from Field import Field + +class FieldStruct(Field): + """ + The FieldStruct class takes care of handling 'struct' format based + Input and Output TLVs + """ + + def __init__(self, prefix, dictionary): + # Call the parent constructor + Field.__init__(self, prefix, dictionary) + + # The field type will be the given struct name + self.field_type = utils.build_camelcase_name(self.fullname) + + # Set a struct as content + self.contents = Struct(self.field_type, + dictionary['contents']) + + + def emit_types(self, f): + ''' + Emit the Struct type info + ''' + self.contents.emit(f) + + + def emit_input_tlv_add(self, cfile, line_prefix): + translations = { 'name' : self.name, + 'tlv_id' : self.id_enum_name, + 'field_type' : self.field_type, + 'variable_name' : self.variable_name, + 'lp' : line_prefix } + + template = ( + '${lp}/* Try to add the \'${name}\' TLV */\n' + '${lp}{\n' + '${lp} ${field_type} tmp;\n' + '\n') + cfile.write(string.Template(template).substitute(translations)) + + # uint32 and uint16 fields need to be converted to host-endianness + for struct_field in self.contents.members: + cfile.write('%s %s;\n' % (line_prefix, + utils.le_from_he ('input->' + self.variable_name + '.' + utils.build_underscore_name(struct_field['name']), + 'tmp.' + utils.build_underscore_name(struct_field['name']), + struct_field['type']))) + + template = ( + '\n' + '${lp} if (!qmi_message_tlv_add (self,\n' + '${lp} (guint8)${tlv_id},\n' + '${lp} sizeof (tmp),\n' + '${lp} &tmp,\n' + '${lp} error)) {\n' + '${lp} g_prefix_error (error, \"Couldn\'t set the ${name} TLV: \");\n' + '${lp} qmi_message_unref (self);\n' + '${lp} return NULL;\n' + '${lp} }\n' + '${lp}}\n') + cfile.write(string.Template(template).substitute(translations)) + + + def emit_output_tlv_get(self, f, line_prefix): + translations = { 'name' : self.name, + 'container_underscore' : utils.build_underscore_name (self.prefix), + 'field_type' : self.field_type, + 'tlv_id' : self.id_enum_name, + 'variable_name' : self.variable_name, + 'lp' : line_prefix, + 'error' : 'error' if self.mandatory == 'yes' else 'NULL'} + + template = ( + '${lp}if (qmi_message_tlv_get (message,\n' + '${lp} ${tlv_id},\n' + '${lp} sizeof (self->${variable_name}),\n' + '${lp} &self->${variable_name},\n' + '${lp} ${error})) {\n' + '${lp} self->${variable_name}_set = TRUE;\n') + f.write(string.Template(template).substitute(translations)) + + for struct_field in self.contents.members: + f.write('%s %s;\n' % (line_prefix, + utils.he_from_le ('self->' + self.variable_name + '.' + utils.build_underscore_name(struct_field['name']), + 'self->' + self.variable_name + '.' + utils.build_underscore_name(struct_field['name']), + struct_field['type']))) + if self.mandatory is 'yes': + template = ( + '${lp}} else {\n' + '${lp} g_prefix_error (error, \"Couldn\'t get the ${name} TLV: \");\n' + '${lp} ${container_underscore}_unref (self);\n' + '${lp} return NULL;\n' + '${lp}}\n') + else: + template = ( + '${lp}}\n') + f.write(string.Template(template).substitute(translations)) diff --git a/build-aux/qmi-codegen/Makefile.am b/build-aux/qmi-codegen/Makefile.am new file mode 100644 index 0000000..f6df6c8 --- /dev/null +++ b/build-aux/qmi-codegen/Makefile.am @@ -0,0 +1,14 @@ + +EXTRA_DIST = \ + Client.py \ + MessageList.py \ + Message.py \ + Container.py \ + ContainerInput.py \ + ContainerOutput.py \ + Field.py \ + FieldArray.py \ + FieldStruct.py \ + FieldBasic.py \ + utils.py \ + qmi-codegen diff --git a/build-aux/qmi-codegen/Message.py b/build-aux/qmi-codegen/Message.py new file mode 100644 index 0000000..0415704 --- /dev/null +++ b/build-aux/qmi-codegen/Message.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +import utils +from ContainerOutput import ContainerOutput +from ContainerInput import ContainerInput + +class Message: + def __init__(self, dictionary): + # The message prefix + self.prefix = 'Qmi Message' + # The message service, e.g. "Ctl" + self.service = dictionary['service'] + # The name of the specific message, e.g. "Something" + self.name = dictionary['name'] + # The specific message ID + self.id = dictionary['id'] + # The type, which must always be 'Message' + self.type = dictionary['type'] + + # Create the composed full name (prefix + service + name), + # e.g. "Qmi Message Ctl Something Output Result" + self.fullname = self.prefix + ' ' + self.service + ' ' + self.name + + # Create the ID enumeration name + self.id_enum_name = string.upper(utils.build_underscore_name(self.fullname)) + + # Build output container. + # Every defined message will have its own output container, which + # will generate a new Output type and public getters for each output + # field + self.output = ContainerOutput(self.fullname, + dictionary['output']) + + # Build input container. + # Every defined message will have its own input container, which + # will generate a new Input type and public getters for each input + # field + self.input = ContainerInput(self.fullname, + dictionary['input'] if 'input' in dictionary else None) + + + def __emit_request_creator(self, hfile, cfile): + translations = { 'service' : self.service, + 'container' : utils.build_camelcase_name (self.input.fullname), + 'underscore' : utils.build_underscore_name (self.fullname), + 'message_id' : self.id_enum_name } + + input_arg_template = 'gpointer unused' if self.input.fields is None else '${container} *input' + template = ( + '\n' + 'QmiMessage *${underscore}_request_create (\n' + ' guint8 transaction_id,\n' + ' guint8 cid,\n' + ' %s,\n' + ' GError **error);\n' % input_arg_template) + hfile.write(string.Template(template).substitute(translations)) + + # Emit message creator + template = ( + '\n' + '/**\n' + ' * ${underscore}_request_create:\n' + ' */\n' + 'QmiMessage *\n' + '${underscore}_request_create (\n' + ' guint8 transaction_id,\n' + ' guint8 cid,\n' + ' %s,\n' + ' GError **error)\n' + '{\n' + ' QmiMessage *self;\n' + '\n' + ' self = qmi_message_new (QMI_SERVICE_${service},\n' + ' cid,\n' + ' transaction_id,\n' + ' ${message_id});\n' + '\n' % input_arg_template) + cfile.write(string.Template(template).substitute(translations)) + + if self.input.fields is not None: + for field in self.input.fields: + field.emit_input_mandatory_check(cfile, ' ') + field.emit_input_tlv_add(cfile, ' ') + + cfile.write( + ' return self;\n' + '}\n') + + + def __emit_response_parser(self, hfile, cfile): + translations = { 'name' : self.name, + 'container' : utils.build_camelcase_name (self.output.fullname), + 'container_underscore' : utils.build_underscore_name (self.output.fullname), + 'underscore' : utils.build_underscore_name (self.fullname), + 'message_id' : self.id_enum_name } + + template = ( + '\n' + '${container} *${underscore}_response_parse (\n' + ' QmiMessage *message,\n' + ' GError **error);\n') + hfile.write(string.Template(template).substitute(translations)) + + template = ( + '\n' + '/**\n' + ' * ${underscore}_response_parse:\n' + ' * @message: a #QmiMessage response.\n' + ' * @error: a #GError.\n' + ' *\n' + ' * Parse the \'${name}\' response.\n' + ' *\n' + ' * Returns: a #${container} which should be disposed with ${container_underscore}_unref(), or #NULL if @error is set.\n' + ' */\n' + '${container} *\n' + '${underscore}_response_parse (\n' + ' QmiMessage *message,\n' + ' GError **error)\n' + '{\n' + ' ${container} *self;\n' + '\n' + ' g_return_val_if_fail (qmi_message_get_message_id (message) == ${message_id}, NULL);\n' + '\n' + ' self = g_slice_new0 (${container});\n' + ' self->ref_count = 1;\n') + cfile.write(string.Template(template).substitute(translations)) + + for field in self.output.fields: + cfile.write( + '\n' + ' do {\n') + field.emit_output_prerequisite_check(cfile, ' ') + cfile.write( + '\n' + ' {\n') + field.emit_output_tlv_get(cfile, ' ') + cfile.write( + '\n' + ' }\n') + cfile.write( + ' } while (0);\n') + cfile.write( + '\n' + ' return self;\n' + '}\n') + + + def emit(self, hfile, cfile): + utils.add_separator(hfile, 'REQUEST/RESPONSE', self.fullname); + utils.add_separator(cfile, 'REQUEST/RESPONSE', self.fullname); + + hfile.write('\n/* --- Input -- */\n'); + cfile.write('\n/* --- Input -- */\n'); + self.input.emit(hfile, cfile) + self.__emit_request_creator (hfile, cfile) + + hfile.write('\n/* --- Output -- */\n'); + cfile.write('\n/* --- Output -- */\n'); + self.output.emit(hfile, cfile) + self.__emit_response_parser (hfile, cfile) diff --git a/build-aux/qmi-codegen/MessageList.py b/build-aux/qmi-codegen/MessageList.py new file mode 100644 index 0000000..784100b --- /dev/null +++ b/build-aux/qmi-codegen/MessageList.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +from Message import Message +import utils + +class MessageList: + def __init__(self, objects_dictionary): + self.list = [] + self.message_id_enum_name = None + + # Loop items in the list, creating Message objects for the messages + # and looking for the special 'Message-ID-Enum' type + for object_dictionary in objects_dictionary: + if object_dictionary['type'] == 'Message': + message = Message(object_dictionary) + self.list.append(message) + elif object_dictionary['type'] == 'Message-ID-Enum': + self.message_id_enum_name = object_dictionary['name'] + + # We NEED the Message-ID-Enum field + if self.message_id_enum_name is None: + raise ValueError('Missing Message-ID-Enum field') + + + def emit_message_ids_enum(self, f): + translations = { 'enum_type' : utils.build_camelcase_name (self.message_id_enum_name) } + template = ( + '\n' + 'typedef enum {\n') + for message in self.list: + translations['enum_name'] = message.id_enum_name + translations['enum_value'] = message.id + enum_template = ( + ' ${enum_name} = ${enum_value},\n') + template += string.Template(enum_template).substitute(translations) + + template += ( + '} ${enum_type};\n' + '\n') + f.write(string.Template(template).substitute(translations)) + + + def emit(self, hfile, cfile): + # First, emit the message IDs enum + self.emit_message_ids_enum(cfile) + + # Then, emit all message handlers + for message in self.list: + message.emit(hfile, cfile) diff --git a/build-aux/qmi-codegen/Struct.py b/build-aux/qmi-codegen/Struct.py new file mode 100644 index 0000000..fd3dad9 --- /dev/null +++ b/build-aux/qmi-codegen/Struct.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +import utils + +class Struct: + + def __init__(self, name, members): + # The struct type name, e.g. QmiTheStruct + self.name = name + # The struct members, a dictionary of 'type'+'name' pairs + self.members = members + + + # Emits the definition of the struct + def emit(self, f): + translations = { 'name' : self.name } + template = ( + '\n' + 'typedef struct _${name} {\n') + f.write(string.Template(template).substitute(translations)) + + for var in self.members: + translations['variable_type'] = var['type'] + translations['variable_name'] = utils.build_underscore_name(var['name']) + template = ( + ' ${variable_type} ${variable_name};\n') + f.write(string.Template(template).substitute(translations)) + + template = ('} __attribute__((__packed__)) ${name};\n\n') + f.write(string.Template(template).substitute(translations)) diff --git a/build-aux/qmi-codegen/qmi-codegen b/build-aux/qmi-codegen/qmi-codegen new file mode 100755 index 0000000..aa113a8 --- /dev/null +++ b/build-aux/qmi-codegen/qmi-codegen @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import os +import sys +import json + +from Client import Client +from MessageList import MessageList +import utils + +# Setup inputs +if len(sys.argv) != 3: + print "Usage: qmi-codegen <path to database.json> <path to output>" + sys.exit(1) + +database_file_path = sys.argv[1] +base_output_path = sys.argv[2] + +output_file_c = open(base_output_path + ".c", 'w') +output_file_h = open(base_output_path + ".h", 'w') + +# Load database file contents +database_file = open(sys.argv[1]) +database_file_contents = database_file.read() +database_file.close() + +# Add common stuff to the output files +utils.add_copyright(output_file_c); +utils.add_copyright(output_file_h); +utils.add_header_start(output_file_h, os.path.basename(base_output_path)) +utils.add_source_start(output_file_c, os.path.basename(base_output_path)) + +# Get our message collection +object_list_json = json.loads(database_file_contents) +message_list = MessageList(object_list_json) + +# Emit the message creation/parsing code +message_list.emit(output_file_h, output_file_c) + +# Build our own client +client = Client(object_list_json) +client.emit(output_file_h, output_file_c, message_list) + +utils.add_header_stop(output_file_h, os.path.basename(base_output_path)) + +output_file_c.close() +output_file_h.close() diff --git a/build-aux/qmi-codegen/utils.py b/build-aux/qmi-codegen/utils.py new file mode 100644 index 0000000..5c14f5b --- /dev/null +++ b/build-aux/qmi-codegen/utils.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program 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 of the License, or (at your option) any +# later version. +# +# This 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 program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2012 Lanedo GmbH +# + +import string + +def add_copyright(f): + f.write("\n" + "/* GENERATED CODE... DO NOT EDIT */\n" + "\n" + "/*\n" + " * This library is free software; you can redistribute it and/or\n" + " * modify it under the terms of the GNU Lesser General Public\n" + " * License as published by the Free Software Foundation; either\n" + " * version 2 of the License, or (at your option) any later version.\n" + " *\n" + " * This library is distributed in the hope that it will be useful,\n" + " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + " * Lesser General Public License for more details.\n" + " *\n" + " * You should have received a copy of the GNU Lesser General Public\n" + " * License along with this library; if not, write to the\n" + " * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n" + " * Boston, MA 02110-1301 USA.\n" + " *\n" + " * Copyright (C) 2012 Lanedo GmbH\n" + " */\n" + "\n"); + +def build_header_guard(output_name): + return "__LIBQMI_GLIB_" + string.upper(string.replace (output_name, '-', '_')) + "__" + +def add_header_start(f, output_name): + template = string.Template ( + "\n" + "#ifndef ${guard}\n" + "#define ${guard}\n" + "\n" + "#include <glib.h>\n" + "#include <glib-object.h>\n" + "#include <gio/gio.h>\n" + "\n" + "#include \"qmi-enum-types.h\"\n" + "#include \"qmi-message.h\"\n" + "#include \"qmi-client.h\"\n" + "\n" + "G_BEGIN_DECLS\n" + "\n") + f.write(template.substitute(guard = build_header_guard(output_name))) + +def add_header_stop(f, output_name): + template = string.Template ( + "\n" + "G_END_DECLS\n" + "\n" + "#endif /* ${guard} */\n") + f.write(template.substitute(guard = build_header_guard(output_name))) + +#def add_global_include(f, header): +# template = string.Template ( +# "\n" +# "#include <${header}>\n") +# f.write(template.substitute(header = header)) +# +#def add_local_include(f, header): +# template = string.Template ( +# "\n" +# "#include \"${header}\"\n") +# f.write(template.substitute(header = header)) + + +def add_source_start(f, output_name): + template = string.Template ( + "\n" + "#include \"${name}.h\"\n" + "#include \"qmi-error-types.h\"\n" + "#include \"qmi-device.h\"\n" + '\n' + '#define QMI_STATUS_SUCCESS 0x0000\n' + '#define QMI_STATUS_FAILURE 0x0001\n' + "\n") + f.write(template.substitute(name = output_name)) + + +def add_separator(f, separator_type, separator_name): + template = string.Template ( + "\n" + "/*****************************************************************************/\n" + "/* ${type}: ${name} */\n" + "\n") + f.write(template.substitute(type = separator_type, + name = separator_name)) + +def build_underscore_name(name): + return string.lower(string.replace (name, ' ', '_')) + +def build_camelcase_name(name): + return string.replace(string.capwords(name), ' ', '') + + +#def emit_struct_type(f, struct_type, dictionary): +# translations = { 'struct_type' : struct_type } +# template = ( +# '\n' +# 'typedef struct _${struct_type} {\n') +# f.write(string.Template(template).substitute(translations)) +# for var in dictionary['contents']: +# translations['variable_type'] = var['type'] +# translations['variable_name'] = build_underscore_name(var['name']) +# template = ( +# ' ${variable_type} ${variable_name};\n') +# f.write(string.Template(template).substitute(translations)) +# template = ('} __attribute__((__packed__)) ${struct_type};\n\n') +# f.write(string.Template(template).substitute(translations)) + + +def he_from_le(input_variable, output_variable, variable_type): + if variable_type == 'guint16': + return '%s = GUINT16_FROM_LE (%s)' % (output_variable, input_variable) + elif variable_type == 'guint32': + return '%s = GUINT32_FROM_LE (%s)' % (output_variable, input_variable) + if variable_type == 'gint16': + return '%s = GINT16_FROM_LE (%s)' % (output_variable, input_variable) + elif variable_type == 'gint32': + return '%s = GINT32_FROM_LE (%s)' % (output_variable, input_variable) + else: + return '%s = %s' % (output_variable, input_variable) + + +def le_from_he(input_variable, output_variable, variable_type): + return he_from_le(input_variable, output_variable, variable_type) diff --git a/configure.ac b/configure.ac index 4196026..f1c1f78 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,7 @@ AC_SUBST(GLIB_MKENUMS) AC_CONFIG_FILES([Makefile build-aux/Makefile build-aux/templates/Makefile + build-aux/qmi-codegen/Makefile src/Makefile cli/Makefile utils/Makefile]) |