]> git.tdb.fi Git - gldbg.git/blob - generate.py
Add dependency generation to generate.py
[gldbg.git] / generate.py
1 #!/usr/bin/python
2 # $Id$
3
4 import sys
5 import os
6 import optparse
7
8 class InputFile:
9         def __init__(self, fn):
10                 self.file = open(fn)
11
12         def __iter__(self):
13                 for l in self.file:
14                         h = l.find("#")
15                         if h==0 or (h>0 and l[h-1].isspace()):
16                                 l = l[:h]
17
18                         l = l.rstrip()
19                         if not l:
20                                 continue
21
22                         yield l
23                 
24
25 def strip_name(name):
26         """Strips any vendor suffix and GL prefix from a name (but not GLX prefix)"""
27
28         suffix = ""
29         if name.endswith(" *"):
30                 suffix = " *"
31                 name = name[:-2]
32         elif name.endswith("Pointer"):
33                 suffix = "Pointer"
34                 name = name[:-7]
35
36         prefix = ""
37         if name.startswith("const "):
38                 prefix = "const "
39                 name = name[6:]
40
41         if name.startswith("GL") and not name.startswith("GLX"):
42                 name = name[2:]
43         if name.endswith(("EXT", "ARB", "SGI", "IBM", "ATI")):
44                 return prefix+name[:-3]+suffix
45         elif name.endswith("SGIX"):
46                 return prefix+name[:-4]+suffix
47         elif name.endswith(("NV", "HP")):
48                 return prefix+name[:-2]+suffix
49         else:
50                 return prefix+name+suffix
51
52
53 class Typemap:
54         def __init__(self, fn):
55                 self.map = {}
56                 for line in InputFile(fn):
57                         parts = [p.strip() for p in line.split(',')]
58                         if parts[3]=="*":
59                                 parts[3] = parts[0]
60                         elif parts[3][-1]=='*' and parts[3][-2]!=' ':
61                                 parts[3] = parts[3][:-1]+" *"
62                         self.map[tuple(parts[0:3])] = tuple(parts[3:6])
63
64         def wildcard_match(self, a, b):
65                 if a=="*" or b=="*":
66                         return True
67                 return a==b
68
69         def __getitem__(self, key):
70                 try:
71                         return self.map[(key[0], "*", "*")]
72                 except KeyError:
73                         for k, v in self.map.iteritems():
74                                 if strip_name(k[0])==strip_name(key[0]) and self.wildcard_match(k[1], key[1]) and self.wildcard_match(k[2], key[2]):
75                                         return v
76                         raise KeyError, key
77
78         def update(self, other):
79                 self.map.update(other.map)
80
81
82 class IOmap:
83         def __init__(self, fn):
84                 self.map = {}
85                 self.map["void"] = None
86                 for line in InputFile(fn):
87                         parts = [p.strip() for p in line.split(',')]
88                         self.map[parts[0]] = tuple(parts[1:])
89
90         def __getitem__(self, key):
91                 return self.map[strip_name(key)]
92
93
94 class Function:
95         class Parameter:
96                 def __init__(self, func, name):
97                         self.func = func
98                         self.name = name
99                         self.type = None
100                         self.direction = "in"
101                         self.kind = "value"
102                         self.size = None
103                         self.ctype = None
104                         self.csize = None
105                         self.io = None
106
107                 def set_type(self, type, dir, kind):
108                         self.type = type
109                         self.direction = dir
110                         self.kind = kind
111
112                 def set_size(self, size):
113                         if type(size)==str and size.isdigit():
114                                 self.size = int(size)
115                         else:
116                                 self.size = size
117
118                 def derive_ctype(self, typemap, iomap):
119                         m = typemap[(self.type, self.direction, self.kind)]
120                         self.ctype = m[0]
121                         if m[1]!="*":
122                                 self.direction = m[1]
123                         if m[2]!="*":
124                                 self.kind = m[2]
125                         self.base_ctype = self.ctype
126                         if self.kind=="value":
127                                 if self.base_ctype.startswith("const "):
128                                         self.base_ctype = self.base_ctype[6:]
129                         else:
130                                 if self.direction=="in":
131                                         self.ctype = "const "+self.ctype
132                                 self.ctype = self.ctype+" *"
133                         self.io = iomap[self.base_ctype]
134
135                 def derive_csize(self):
136                         if self.kind=="array" and self.size is not None:
137                                 self.csize = None
138                                 if type(self.size)==int:
139                                         self.csize = "%d*sizeof(%s)"%(self.size, self.base_ctype)
140                                 elif self.size.startswith("COMPSIZE("):
141                                         self.csize = self.func.compsize(self.size[9:-1], self.base_ctype)
142                                 elif self.size=="pname":
143                                         self.csize = "paramsize(pname)*sizeof(%s)"%self.base_ctype
144                                 else:
145                                         s = self.func.get_param(self.size.split('*')[0])
146                                         if (s.type=="SizeI" or s.type.endswith("Int32") or s.type.startswith("BufferSize")):
147                                                 if s.kind=="value":
148                                                         self.csize = "%s"%self.size
149                                                         if self.func.name.startswith("glUniformMatrix") and self.func.name[16]!='x':
150                                                                 self.csize += "*%d"%(int(self.func.name[15])**2)
151                                                         elif self.func.name.startswith("glUniform") and self.func.name[9].isdigit():
152                                                                 self.csize += "*%s"%self.func.name[9]
153                                                         if strip_name(self.base_ctype)!="void":
154                                                                 self.csize += "*sizeof(%s)"%self.base_ctype
155                                                 elif s.kind=="array" and s.size==1:
156                                                         self.csize = "*%s"%s.name
157                                         if not self.csize:
158                                                 sys.stderr.write("Could not determine size for array parameter '%s[%s]' of function '%s'\n"%(self.name, self.size, self.func.name))
159                         elif self.kind=="reference":
160                                 self.csize = "sizeof(%s)"%self.base_ctype
161
162         def __init__(self, name, pnames):
163                 self.name = name
164                 self.ret = Function.Parameter(self, "ret")
165                 self.params = [Function.Parameter(self, n) for n in pnames]
166                 self.category = None
167
168         def get_param(self, pname):
169                 for p in self.params:
170                         if p.name==pname:
171                                 return p
172                 raise KeyError, pname
173
174         def set_category(self, cat):
175                 self.category = cat
176
177         def compsize(self, size, btype):
178                 if not size:
179                         return
180
181                 res = ""
182                 have_type = False
183                 for c in size.replace(',', '/').split('/'):
184                         param = self.get_param(c)
185                         if not param:
186                                 sys.stderr.write("Compsize '%s' for function '%s' failed: No parameter '%s'\n"%(size, self.name, c))
187                                 return
188
189                         if res:
190                                 res += "*"
191
192                         cn = strip_name(param.type)
193                         if cn.endswith("Type"):
194                                 res += "typesize(%s)"%param.name
195                                 have_type = True
196                         elif cn.endswith("Format"):
197                                 res += "formatsize(%s)"%param.name
198                         elif param.name=="pname" or cn.endswith("Parameter") or (param.name=="target" and cn=="enum"):
199                                 res += "paramsize(%s)"%param.name
200                         elif param.name=="buffer" and cn=="enum":
201                                 res += "buffersize(%s)"%param.name
202                         elif (cn=="SizeI" or cn.endswith("Int32")) and not param.size:
203                                 res += param.name
204                         else:
205                                 sys.stderr.write("Compsize '%s' for function '%s' failed: Parameter '%s' has unknown type '%s'\n"%(size, self.name, param.name, param.type))
206                                 return
207                 if not have_type:
208                         res += "*sizeof(%s)"%btype
209                 return res
210
211         def finalize(self, typemap, iomap):
212                 self.ret.derive_ctype(typemap, iomap)
213                 for p in self.params:
214                         p.derive_ctype(typemap, iomap)
215                 for p in self.params:
216                         p.derive_csize()
217
218
219 class Enum:
220         def __init__(self, name, category, value):
221                 self.name = name
222                 self.category = category
223                 self.value = value
224
225
226 class Template:
227         def __init__(self, fn):
228                 self.mode = "functions"
229                 self.sections = []
230                 self.handcode = []
231
232                 literal = True
233                 text = ""
234                 for line in InputFile(fn):
235                         if line[0]==':':
236                                 if not literal and text:
237                                         self.add_section(text, literal)
238                                         text = ""
239                                 text += line[1:]+"\n"
240                                 literal = True
241                         elif line[0]=='!':
242                                 parts = line[1:].split()
243                                 if parts[0]=="handcode":
244                                         self.handcode.append(parts[1])
245                                 elif parts[0]=="mode":
246                                         self.mode = parts[1]
247                                 else:
248                                         sys.stderr.write("Unknown keyword '%s'\n"%parts[0])
249                         else:
250                                 if literal and text:
251                                         self.add_section(text, literal)
252                                         text = ""
253                                 text += line+"\n"
254                                 literal = False
255                 if text:
256                         self.add_section(text, literal)
257
258         def add_section(self, text, literal):
259                 if literal:
260                         self.sections.append(text)
261                 else:
262                         self.sections.append(compile(text, "-", "exec"))
263
264         def write(self, str, *args):
265                 sys.stdout.write(str%args)
266
267         def writeln(self, str, *args):
268                 sys.stdout.write(str%args+"\n")
269
270         def process_function(self, code, func):
271                 if func.name in self.handcode:
272                         return
273
274                 globals = {
275                         "w": self.write,
276                         "wl": self.writeln,
277                         "func": func,
278                         "ret": func.ret,
279                         "params": func.params
280                 }
281                 eval(code, globals)
282
283         def process_enum(self, code, enum):
284                 if enum.value is None:
285                         return
286
287                 globals = {
288                         "w": self.write,
289                         "wl": self.writeln,
290                         "enum": enum
291                 }
292                 eval(code, globals)
293
294         def process_sections(self, objects, handler):
295                 for sect in self.sections:
296                         if type(sect)==str:
297                                 print sect
298                         else:
299                                 for obj in objects:
300                                         handler(sect, obj)
301
302         def process(self, apis):
303                 if self.mode=="functions":
304                         functions = []
305                         for api in apis:
306                                 typemap = Typemap(api.typemap)
307                                 iomap = IOmap(api.iomap)
308                                 for spec in api.specs:
309                                         funcs = read_spec(spec, api.prefix)
310                                         funcs = [f for f in funcs if f.name not in api.ignore_funcs and f.category not in api.ignore_categs]
311                                         for func in funcs:
312                                                 func.finalize(typemap, iomap)
313                                         names = [f.name for f in funcs]
314                                         functions = [f for f in functions if f.name not in names]+funcs
315                         self.process_sections(functions, self.process_function)
316                 elif self.mode=="enums":
317                         enums = []
318                         for api in apis:
319                                 for spec in api.enumspecs:
320                                         ens = read_enums(spec, api.enumprefix)
321                                         enums += ens
322                         enums.sort(lambda x, y: cmp(x.value, y.value)*2+cmp(x.category, y.category))
323                         self.process_sections(enums, self.process_enum)
324
325
326 class Api:
327         def __init__(self, fn):
328                 self.typemap = None
329                 self.iomap = None
330                 self.specs = []
331                 self.prefix = None
332                 self.enumspecs = []
333                 self.enumprefix = None
334                 self.ignore_categs = []
335                 self.ignore_funcs = []
336
337                 for line in InputFile(fn):
338                         parts = line.split()
339                         if parts[0]=="typemap":
340                                 self.typemap = parts[1]
341                         elif parts[0]=="iomap":
342                                 self.iomap = parts[1]
343                         elif parts[0]=="spec":
344                                 self.specs.append(parts[1])
345                         elif parts[0]=="prefix":
346                                 self.prefix = parts[1]
347                         elif parts[0]=="enumspec":
348                                 self.enumspecs.append(parts[1])
349                         elif parts[0]=="enumprefix":
350                                 self.enumprefix = parts[1]
351                         elif parts[0]=="ignore":
352                                 if parts[1]=="category":
353                                         self.ignore_categs.append(parts[2])
354                                 elif parts[1]=="function":
355                                         self.ignore_funcs.append(parts[2])
356                         else:
357                                 sys.stderr.write("Unknown keyword '%s'\n", parts[0])
358
359
360 def read_spec(fn, prefix):
361         funcs = []
362         cur_func = None
363
364         for line in InputFile(fn):
365                 if line.find(':')>=0:
366                         continue
367                 elif line[0]=='\t' and cur_func:
368                         parts = line.split()
369                         if parts[0]=="return":
370                                 cur_func.ret.set_type(parts[1], "out", "value")
371                         elif parts[0]=="param":
372                                 bracket = parts[4].find('[')
373                                 if bracket>=0:
374                                         parts.insert(5, parts[4][bracket:])
375                                         parts[4] = parts[4][:bracket]
376
377                                 param = cur_func.get_param(parts[1])
378                                 param.set_type(parts[2], parts[3], parts[4])
379                                 if len(parts)==6 or (len(parts)>6 and parts[6]!="retained"):
380                                         param.set_size(parts[5][1:-1])
381                         elif parts[0]=="category":
382                                 cur_func.set_category(parts[1])
383                         elif parts[0]=="glxvendorglx" and cur_func.category=="glx":
384                                 cur_func.set_category("glxext")
385                 else:
386                         paren = line.find('(')
387                         if paren>0:
388                                 cparen = line.rfind(')')
389                                 if cparen>paren+1:
390                                         pnames = [n.strip() for n in line[paren+1:cparen].split(",")]
391                                 else:
392                                         pnames = []
393                                 cur_func = Function(prefix+line[:paren], pnames)
394                                 funcs.append(cur_func)
395
396         return funcs
397
398 def read_enums(fn, prefix):
399         enums = []
400         cur_categ = []
401
402         for line in InputFile(fn):
403                 if ':' in line:
404                         parts = line[:line.find(':')].split()
405                         if len(parts)==2 and parts[1]=="enum":
406                                 cur_categ = parts[0]
407                 elif cur_categ:
408                         parts = line.split()
409                         if parts[0]=="use":
410                                 enums.append(Enum(prefix+parts[2], cur_categ, None))
411                         elif parts[1]=="=":
412                                 try:
413                                         enums.append(Enum(prefix+parts[0], cur_categ, int(parts[2], 0)))
414                                 except ValueError, e:
415                                         sys.stderr.write("Syntax error in %s: %s\n"%(fn, e))
416
417         for e in enums:
418                 if e.value is None:
419                         for n in enums:
420                                 if n.name==e.name and n.value is not None:
421                                         e.value = n.value
422                         if e.value is None:
423                                 sys.stderr.write("Could not find value for enum reference %s in category %s\n"%(e.name, e.category))
424
425         return enums
426
427 parser = optparse.OptionParser()
428 parser.add_option("--depends", dest="depends", default=False)
429 (options, args) = parser.parse_args()
430
431 template = Template(args[0])
432 apis = []
433 for i in args[1:]:
434         apis.append(Api(i))
435
436 if options.depends:
437         deps = args[:]
438         if template.mode=="functions":
439                 for api in apis:
440                         deps += api.specs
441                         deps.append(api.typemap)
442                         deps.append(api.iomap)
443         elif template.mode=="enums":
444                 for api in apis:
445                         deps += api.enumspecs
446         sys.stdout.write("%s: %s\n"%(options.depends, " ".join(deps)))
447 else:
448         template.process(apis)