ovsdb: Add ovsdb IDL compiler to build system.
[cascardo/ovs.git] / ovsdb / ovsdb-idlc.in
1 #! @PYTHON@
2
3 import getopt
4 import re
5 import sha
6 import sys
7
8 sys.path.insert(0, "@abs_top_srcdir@/ovsdb")
9 import simplejson as json
10
11 argv0 = sys.argv[0]
12
13 class Error(Exception):
14     def __init__(self, msg):
15         Exception.__init__(self)
16         self.msg = msg
17
18 def getMember(json, name, validTypes, description, default=None):
19     if name in json:
20         member = json[name]
21         if type(member) not in validTypes:
22             raise Error("%s: type mismatch for '%s' member"
23                         % (description, name))
24         return member
25     return default
26
27 def mustGetMember(json, name, expectedType, description):
28     member = getMember(json, name, expectedType, description)
29     if member == None:
30         raise Error("%s: missing '%s' member" % (description, name))
31     return member
32
33 class DbSchema:
34     def __init__(self, name, comment, tables):
35         self.name = name
36         self.comment = comment
37         self.tables = tables
38
39     @staticmethod
40     def fromJson(json):
41         name = mustGetMember(json, 'name', [unicode], 'database')
42         comment = getMember(json, 'comment', [unicode], 'database')
43         tablesJson = mustGetMember(json, 'tables', [dict], 'database')
44         tables = {}
45         for name, json in tablesJson.iteritems():
46             tables[name] = TableSchema.fromJson(json, "%s table" % name)
47         return DbSchema(name, comment, tables)
48
49     def toJson(self):
50         d = {"name": self.name,
51              "tables": {}}
52         for name, table in self.tables.iteritems():
53             d["tables"][name] = table.toJson()
54         if self.comment != None:
55             d["comment"] = self.comment
56         return d
57
58 class TableSchema:
59     def __init__(self, comment, columns):
60         self.comment = comment
61         self.columns = columns
62
63     @staticmethod
64     def fromJson(json, description):
65         comment = getMember(json, 'comment', [unicode], description)
66         columnsJson = mustGetMember(json, 'columns', [dict], description)
67         columns = {}
68         for name, json in columnsJson.iteritems():
69             columns[name] = ColumnSchema.fromJson(
70                 json, "column %s in %s" % (name, description))
71         return TableSchema(comment, columns)
72
73     def toJson(self):
74         d = {"columns": {}}
75         for name, column in self.columns.iteritems():
76             d["columns"][name] = column.toJson()
77         if self.comment != None:
78             d["comment"] = self.comment
79         return d
80
81 class ColumnSchema:
82     def __init__(self, comment, type, persistent):
83         self.comment = comment
84         self.type = type
85         self.persistent = persistent
86
87     @staticmethod
88     def fromJson(json, description):
89         comment = getMember(json, 'comment', [unicode], description)
90         type = Type.fromJson(mustGetMember(json, 'type', [dict, unicode],
91                                            description),
92                              'type of %s' % description)
93         ephemeral = getMember(json, 'ephemeral', [True,False], description)
94         persistent = ephemeral != True
95         return ColumnSchema(comment, type, persistent)
96
97     def toJson(self):
98         d = {"type": self.type.toJson()}
99         if self.persistent == False:
100             d["ephemeral"] = True
101         if self.comment != None:
102             d["comment"] = self.comment
103         return d
104
105 class Type:
106     def __init__(self, key, keyRefTable=None, value=None, valueRefTable=None,
107                  min=1, max=1):
108         self.key = key
109         self.keyRefTable = keyRefTable
110         self.value = value
111         self.valueRefTable = valueRefTable
112         self.min = min
113         self.max = max
114     
115     @staticmethod
116     def fromJson(json, description):
117         if type(json) == unicode:
118             return Type(json)
119         else:
120             key = mustGetMember(json, 'key', [unicode], description)
121             keyRefTable = getMember(json, 'keyRefTable', [unicode], description)
122             value = getMember(json, 'value', [unicode], description)
123             valueRefTable = getMember(json, 'valueRefTable', [unicode], description)
124             min = getMember(json, 'min', [int], description, 1)
125             max = getMember(json, 'max', [int, unicode], description, 1)
126             return Type(key, keyRefTable, value, valueRefTable, min, max)
127
128     def toJson(self):
129         if self.value == None and self.min == 1 and self.max == 1:
130             return self.key
131         else:
132             d = {"key": self.key}
133             if self.value != None:
134                 d["value"] = self.value
135             if self.min != 1:
136                 d["min"] = self.min
137             if self.max != 1:
138                 d["max"] = self.max
139             return d
140
141 def parseSchema(filename):
142     file = open(filename, "r")
143     s = ""
144     for line in file:
145         if not line.startswith('//'):
146             s += line
147     return DbSchema.fromJson(json.loads(s))
148
149 def cBaseType(prefix, type, refTable=None):
150     if type == 'uuid' and refTable:
151         return "struct %s%s *" % (prefix, refTable.lower())
152     else:
153         return {'integer': 'int64_t ',
154                 'real': 'double ',
155                 'uuid': 'struct uuid ',
156                 'boolean': 'bool ',
157                 'string': 'char *'}[type]
158
159 def printCIDLHeader(schema):
160     prefix = 'ovsrec_'
161     print '''\
162 /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
163
164 #ifndef %(prefix)sIDL_HEADER
165 #define %(prefix)sIDL_HEADER 1
166
167 #include <stdbool.h>
168 #include <stddef.h>
169 #include <stdint.h>
170 #include "uuid.h"''' % {'prefix': prefix.upper()}
171     for tableName, table in schema.tables.iteritems():
172         print
173         if table.comment != None:
174             print "/* %s table (%s). */" % (tableName, table.comment)
175         else:
176             print "/* %s table. */" % (tableName)
177         print "struct %s%s {" % (prefix, tableName.lower())
178         print "\t/* Columns automatically included in every table. */"
179         print "\tstruct uuid uuid_;"
180         print "\tstruct uuid version_;"
181         for columnName, column in table.columns.iteritems():
182             print "\n\t/* %s column. */" % columnName
183             type = column.type
184             if type.min == 1 and type.max == 1:
185                 singleton = True
186                 pointer = ''
187             else:
188                 singleton = False
189                 pointer = '*'
190             if type.value:
191                 print "\tkey_%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName)
192                 print "\tvalue_%s%s%s;" % (cBaseType(prefix, type.value, type.valueRefTable), pointer, columnName)
193             else:
194                 print "\t%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName)
195             if not singleton:
196                 print "\tsize_t n_%s;" % columnName
197         print "};"
198     print
199     print "#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
200
201 def ovsdb_escape(string):
202     def escape(match):
203         c = match.group(0)
204         if c == '\0':
205             raise Error("strings may not contain null bytes")
206         elif c == '\\':
207             return '\\\\'
208         elif c == '\n':
209             return '\\n'
210         elif c == '\r':
211             return '\\r'
212         elif c == '\t':
213             return '\\t'
214         elif c == '\b':
215             return '\\b'
216         elif c == '\a':
217             return '\\a'
218         else:
219             return '\\x%02x' % ord(c)
220     return re.sub(r'["\\\000-\037]', escape, string)
221
222 def printOVSDBSchema(schema):
223     json.dump(schema.toJson(), sys.stdout, sort_keys=True, indent=2)
224
225 def usage():
226     print """\
227 %(argv0)s: ovsdb schema compiler
228 usage: %(argv0)s [OPTIONS] ACTION SCHEMA
229 where SCHEMA is the ovsdb schema to read (in JSON format).
230
231 One of the following actions must specified:
232   validate                    validate schema without taking any other action
233   c-idl-header                print C header file for IDL
234   c-idl-source                print C source file for IDL implementation
235   ovsdb-schema                print ovsdb parseable schema
236
237 The following options are also available:
238   -h, --help                  display this help message
239   -V, --version               display version information\
240 """ % {'argv0': argv0}
241     sys.exit(0)
242
243 if __name__ == "__main__":
244     try:
245         try:
246             options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
247                                               ['help',
248                                                'version'])
249         except getopt.GetoptError, geo:
250             sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
251             sys.exit(1)
252             
253         optKeys = [key for key, value in options]
254         if '-h' in optKeys or '--help' in optKeys:
255             usage()
256         elif '-V' in optKeys or '--version' in optKeys:
257             print "ovsdb-idlc (Open vSwitch) @VERSION@"
258             sys.exit(0)
259
260         if len(args) != 2:
261             sys.stderr.write("%s: exactly two non-option arguments are "
262                              "required (use --help for help)\n" % argv0)
263             sys.exit(1)
264
265         action, inputFile = args
266         schema = parseSchema(inputFile)
267         if action == 'validate':
268             pass
269         elif action == 'ovsdb-schema':
270             printOVSDBSchema(schema)
271         elif action == 'c-idl-header':
272             printCIDLHeader(schema)
273         elif action == 'c-idl-source':
274             printCIDLSource(schema)
275         else:
276             sys.stderr.write(
277                 "%s: unknown action '%s' (use --help for help)\n" %
278                 (argv0, action))
279             sys.exit(1)
280     except Error, e:
281         sys.stderr.write("%s: %s\n" % (argv0, e.msg))
282         sys.exit(1)
283
284 # Local variables:
285 # mode: python
286 # End: