mapi: Add the ability to parse GLAPI XML.
[mesa.git] / src / mapi / mapi / mapi_abi.py
1 #!/usr/bin/env python
2
3 # Mesa 3-D graphics library
4 # Version: 7.9
5 #
6 # Copyright (C) 2010 LunarG Inc.
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a
9 # copy of this software and associated documentation files (the "Software"),
10 # to deal in the Software without restriction, including without limitation
11 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 # and/or sell copies of the Software, and to permit persons to whom the
13 # Software is furnished to do so, subject to the following conditions:
14 #
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 # DEALINGS IN THE SOFTWARE.
25 #
26 # Authors:
27 # Chia-I Wu <olv@lunarg.com>
28
29 import sys
30 import re
31 from optparse import OptionParser
32
33 # number of dynamic entries
34 ABI_NUM_DYNAMIC_ENTRIES = 256
35
36 class ABIEntry(object):
37 """Represent an ABI entry."""
38
39 _match_c_param = re.compile(
40 '^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
41
42 def __init__(self, cols, attrs):
43 self._parse(cols)
44
45 self.slot = attrs['slot']
46 self.hidden = attrs['hidden']
47 self.alias = attrs['alias']
48 self.handcode = attrs['handcode']
49
50 def c_prototype(self):
51 return '%s %s(%s)' % (self.c_return(), self.name, self.c_params())
52
53 def c_return(self):
54 ret = self.ret
55 if not ret:
56 ret = 'void'
57
58 return ret
59
60 def c_params(self):
61 """Return the parameter list used in the entry prototype."""
62 c_params = []
63 for t, n, a in self.params:
64 sep = '' if t.endswith('*') else ' '
65 arr = '[%d]' % a if a else ''
66 c_params.append(t + sep + n + arr)
67 if not c_params:
68 c_params.append('void')
69
70 return ", ".join(c_params)
71
72 def c_args(self):
73 """Return the argument list used in the entry invocation."""
74 c_args = []
75 for t, n, a in self.params:
76 c_args.append(n)
77
78 return ", ".join(c_args)
79
80 def _parse(self, cols):
81 ret = cols.pop(0)
82 if ret == 'void':
83 ret = None
84
85 name = cols.pop(0)
86
87 params = []
88 if not cols:
89 raise Exception(cols)
90 elif len(cols) == 1 and cols[0] == 'void':
91 pass
92 else:
93 for val in cols:
94 params.append(self._parse_param(val))
95
96 self.ret = ret
97 self.name = name
98 self.params = params
99
100 def _parse_param(self, c_param):
101 m = self._match_c_param.match(c_param)
102 if not m:
103 raise Exception('unrecognized param ' + c_param)
104
105 c_type = m.group('type').strip()
106 c_name = m.group('name')
107 c_array = m.group('array')
108 c_array = int(c_array) if c_array else 0
109
110 return (c_type, c_name, c_array)
111
112 def __str__(self):
113 return self.c_prototype()
114
115 def __cmp__(self, other):
116 # compare slot, alias, and then name
117 res = cmp(self.slot, other.slot)
118 if not res:
119 if not self.alias:
120 res = -1
121 elif not other.alias:
122 res = 1
123
124 if not res:
125 res = cmp(self.name, other.name)
126
127 return res
128
129 def abi_parse_xml(xml):
130 """Parse a GLAPI XML file for ABI entries."""
131 import os
132 GLAPI = "./%s/../glapi/gen" % (os.path.dirname(sys.argv[0]))
133 sys.path.append(GLAPI)
134 import gl_XML, glX_XML
135
136 api = gl_XML.parse_GL_API(xml, glX_XML.glx_item_factory())
137
138 entry_dict = {}
139 for func in api.functionIterateByOffset():
140 # make sure func.name appear first
141 entry_points = func.entry_points[:]
142 entry_points.remove(func.name)
143 entry_points.insert(0, func.name)
144
145 for name in entry_points:
146 attrs = {
147 'slot': func.offset,
148 'hidden': not func.is_static_entry_point(name),
149 'alias': None if name == func.name else func.name,
150 'handcode': bool(func.has_different_protocol(name)),
151 }
152
153 # post-process attrs
154 if attrs['alias']:
155 try:
156 alias = entry_dict[attrs['alias']]
157 except KeyError:
158 raise Exception('failed to alias %s' % attrs['alias'])
159 if alias.alias:
160 raise Exception('recursive alias %s' % ent.name)
161 attrs['alias'] = alias
162 if attrs['handcode']:
163 attrs['handcode'] = func.static_glx_name(name)
164 else:
165 attrs['handcode'] = None
166
167 if entry_dict.has_key(name):
168 raise Exception('%s is duplicated' % (name))
169
170 cols = []
171 cols.append(func.return_type)
172 cols.append(name)
173 params = func.get_parameter_string(name)
174 cols.extend([p.strip() for p in params.split(',')])
175
176 ent = ABIEntry(cols, attrs)
177 entry_dict[ent.name] = ent
178
179 entries = entry_dict.values()
180 entries.sort()
181
182 return entries
183
184 def abi_parse_line(line):
185 cols = [col.strip() for col in line.split(',')]
186
187 attrs = {
188 'slot': -1,
189 'hidden': False,
190 'alias': None,
191 'handcode': None,
192 }
193
194 # extract attributes from the first column
195 vals = cols[0].split(':')
196 while len(vals) > 1:
197 val = vals.pop(0)
198 if val.startswith('slot='):
199 attrs['slot'] = int(val[5:])
200 elif val == 'hidden':
201 attrs['hidden'] = True
202 elif val.startswith('alias='):
203 attrs['alias'] = val[6:]
204 elif val.startswith('handcode='):
205 attrs['handcode'] = val[9:]
206 elif not val:
207 pass
208 else:
209 raise Exception('unknown attribute %s' % val)
210 cols[0] = vals[0]
211
212 return (attrs, cols)
213
214 def abi_parse(filename):
215 """Parse a CSV file for ABI entries."""
216 fp = open(filename) if filename != '-' else sys.stdin
217 lines = [line.strip() for line in fp.readlines()
218 if not line.startswith('#') and line.strip()]
219
220 entry_dict = {}
221 next_slot = 0
222 for line in lines:
223 attrs, cols = abi_parse_line(line)
224
225 # post-process attributes
226 if attrs['alias']:
227 try:
228 alias = entry_dict[attrs['alias']]
229 except KeyError:
230 raise Exception('failed to alias %s' % attrs['alias'])
231 if alias.alias:
232 raise Exception('recursive alias %s' % ent.name)
233 slot = alias.slot
234 attrs['alias'] = alias
235 else:
236 slot = next_slot
237 next_slot += 1
238
239 if attrs['slot'] < 0:
240 attrs['slot'] = slot
241 elif attrs['slot'] != slot:
242 raise Exception('invalid slot in %s' % (line))
243
244 ent = ABIEntry(cols, attrs)
245 if entry_dict.has_key(ent.name):
246 raise Exception('%s is duplicated' % (ent.name))
247 entry_dict[ent.name] = ent
248
249 entries = entry_dict.values()
250 entries.sort()
251
252 return entries
253
254 def abi_sanity_check(entries):
255 if not entries:
256 return
257
258 all_names = []
259 last_slot = entries[-1].slot
260 i = 0
261 for slot in xrange(last_slot + 1):
262 if entries[i].slot != slot:
263 raise Exception('entries are not ordered by slots')
264 if entries[i].alias:
265 raise Exception('first entry of slot %d aliases %s'
266 % (slot, entries[i].alias.name))
267 handcode = None
268 while i < len(entries) and entries[i].slot == slot:
269 ent = entries[i]
270 if not handcode and ent.handcode:
271 handcode = ent.handcode
272 elif ent.handcode != handcode:
273 raise Exception('two aliases with handcode %s != %s',
274 ent.handcode, handcode)
275
276 if ent.name in all_names:
277 raise Exception('%s is duplicated' % (ent.name))
278 if ent.alias and ent.alias.name not in all_names:
279 raise Exception('failed to alias %s' % (ent.alias.name))
280 all_names.append(ent.name)
281 i += 1
282 if i < len(entries):
283 raise Exception('there are %d invalid entries' % (len(entries) - 1))
284
285 class ABIPrinter(object):
286 """MAPI Printer"""
287
288 def __init__(self, entries):
289 self.entries = entries
290
291 # sort entries by their names
292 self.entries_sorted_by_names = self.entries[:]
293 self.entries_sorted_by_names.sort(lambda x, y: cmp(x.name, y.name))
294
295 self.indent = ' ' * 3
296 self.noop_warn = 'noop_warn'
297 self.noop_generic = 'noop_generic'
298
299 self.api_defines = []
300 self.api_headers = ['"KHR/khrplatform.h"']
301 self.api_call = 'KHRONOS_APICALL'
302 self.api_entry = 'KHRONOS_APIENTRY'
303 self.api_attrs = 'KHRONOS_APIATTRIBUTES'
304
305 self.lib_need_table_size = True
306 self.lib_need_noop_array = True
307 self.lib_need_stubs = True
308 self.lib_need_entries = True
309
310 def c_notice(self):
311 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
312
313 def c_public_includes(self):
314 """Return includes of the client API headers."""
315 defines = ['#define ' + d for d in self.api_defines]
316 includes = ['#include ' + h for h in self.api_headers]
317 return "\n".join(defines + includes)
318
319 def need_entry_point(self, ent):
320 """Return True if an entry point is needed for the entry."""
321 # non-handcode hidden aliases may share the entry they alias
322 use_alias = (ent.hidden and ent.alias and not ent.handcode)
323 return not use_alias
324
325 def c_public_declarations(self, prefix):
326 """Return the declarations of public entry points."""
327 decls = []
328 for ent in self.entries:
329 if not self.need_entry_point(ent):
330 continue
331 export = self.api_call if not ent.hidden else ''
332 decls.append(self._c_decl(ent, prefix, True, export) + ';')
333
334 return "\n".join(decls)
335
336 def c_mapi_table(self):
337 """Return defines of the dispatch table size."""
338 num_static_entries = 0
339 for ent in self.entries:
340 if not ent.alias:
341 num_static_entries += 1
342
343 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
344 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
345 num_static_entries, ABI_NUM_DYNAMIC_ENTRIES)
346
347 def c_mapi_table_initializer(self, prefix):
348 """Return the array initializer for mapi_table_fill."""
349 entries = [self._c_function(ent, prefix)
350 for ent in self.entries if not ent.alias]
351 pre = self.indent + '(mapi_proc) '
352 return pre + (',\n' + pre).join(entries)
353
354 def c_mapi_table_spec(self):
355 """Return the spec for mapi_init."""
356 specv1 = []
357 line = '"1'
358 for ent in self.entries:
359 if not ent.alias:
360 line += '\\0"\n'
361 specv1.append(line)
362 line = '"'
363 line += '%s\\0' % ent.name
364 line += '";'
365 specv1.append(line)
366
367 return self.indent + self.indent.join(specv1)
368
369 def _c_function(self, ent, prefix, mangle=False, stringify=False):
370 """Return the function name of an entry."""
371 formats = { True: '"%s%s"', False: '%s%s' }
372 fmt = formats[stringify]
373 name = ent.name
374 if mangle and ent.hidden:
375 name = '_dispatch_stub_' + str(ent.slot)
376 return fmt % (prefix, name)
377
378 def _c_function_call(self, ent, prefix):
379 """Return the function name used for calling."""
380 if ent.handcode:
381 # _c_function does not handle this case
382 fmt = '%s%s'
383 name = fmt % (prefix, ent.handcode)
384 elif self.need_entry_point(ent):
385 name = self._c_function(ent, prefix, True)
386 else:
387 name = self._c_function(ent.alias, prefix, True)
388 return name
389
390 def _c_decl(self, ent, prefix, mangle=False, export=''):
391 """Return the C declaration for the entry."""
392 decl = '%s %s %s(%s)' % (ent.c_return(), self.api_entry,
393 self._c_function(ent, prefix, mangle), ent.c_params())
394 if export:
395 decl = export + ' ' + decl
396 if self.api_attrs:
397 decl += ' ' + self.api_attrs
398
399 return decl
400
401 def _c_cast(self, ent):
402 """Return the C cast for the entry."""
403 cast = '%s (%s *)(%s)' % (
404 ent.c_return(), self.api_entry, ent.c_params())
405
406 return cast
407
408 def c_private_declarations(self, prefix):
409 """Return the declarations of private functions."""
410 decls = [self._c_decl(ent, prefix) + ';'
411 for ent in self.entries if not ent.alias]
412
413 return "\n".join(decls)
414
415 def c_public_dispatches(self, prefix):
416 """Return the public dispatch functions."""
417 dispatches = []
418 for ent in self.entries:
419 if not self.need_entry_point(ent):
420 continue
421
422 export = self.api_call if not ent.hidden else ''
423
424 proto = self._c_decl(ent, prefix, True, export)
425 cast = self._c_cast(ent)
426
427 ret = ''
428 if ent.ret:
429 ret = 'return '
430 stmt1 = self.indent
431 stmt1 += 'const struct mapi_table *tbl = u_current_get();'
432 stmt2 = self.indent
433 stmt2 += 'mapi_func func = ((const mapi_func *) tbl)[%d];' % (
434 ent.slot)
435 stmt3 = self.indent
436 stmt3 += '%s((%s) func)(%s);' % (ret, cast, ent.c_args())
437
438 disp = '%s\n{\n%s\n%s\n%s\n}' % (proto, stmt1, stmt2, stmt3)
439
440 if ent.handcode:
441 disp = '#if 0\n' + disp + '\n#endif'
442
443 dispatches.append(disp)
444
445 return '\n\n'.join(dispatches)
446
447 def c_public_initializer(self, prefix):
448 """Return the initializer for public dispatch functions."""
449 names = []
450 for ent in self.entries:
451 if ent.alias:
452 continue
453
454 name = '%s(mapi_func) %s' % (self.indent,
455 self._c_function_call(ent, prefix))
456 names.append(name)
457
458 return ',\n'.join(names)
459
460 def c_stub_string_pool(self):
461 """Return the string pool for use by stubs."""
462 # sort entries by their names
463 sorted_entries = self.entries[:]
464 sorted_entries.sort(lambda x, y: cmp(x.name, y.name))
465
466 pool = []
467 offsets = {}
468 count = 0
469 for ent in sorted_entries:
470 offsets[ent] = count
471 pool.append('%s' % (ent.name))
472 count += len(ent.name) + 1
473
474 pool_str = self.indent + '"' + \
475 ('\\0"\n' + self.indent + '"').join(pool) + '";'
476 return (pool_str, offsets)
477
478 def c_stub_initializer(self, prefix, pool_offsets):
479 """Return the initializer for struct mapi_stub array."""
480 stubs = []
481 for ent in self.entries_sorted_by_names:
482 stubs.append('%s{ (void *) %d, %d, NULL }' % (
483 self.indent, pool_offsets[ent], ent.slot))
484
485 return ',\n'.join(stubs)
486
487 def c_noop_functions(self, prefix, warn_prefix):
488 """Return the noop functions."""
489 noops = []
490 for ent in self.entries:
491 if ent.alias:
492 continue
493
494 proto = self._c_decl(ent, prefix, False, 'static')
495
496 stmt1 = self.indent + '%s(%s);' % (self.noop_warn,
497 self._c_function(ent, warn_prefix, False, True))
498
499 if ent.ret:
500 stmt2 = self.indent + 'return (%s) 0;' % (ent.ret)
501 noop = '%s\n{\n%s\n%s\n}' % (proto, stmt1, stmt2)
502 else:
503 noop = '%s\n{\n%s\n}' % (proto, stmt1)
504
505 noops.append(noop)
506
507 return '\n\n'.join(noops)
508
509 def c_noop_initializer(self, prefix, use_generic):
510 """Return an initializer for the noop dispatch table."""
511 entries = [self._c_function(ent, prefix)
512 for ent in self.entries if not ent.alias]
513 if use_generic:
514 entries = [self.noop_generic] * len(entries)
515
516 entries.extend([self.noop_generic] * ABI_NUM_DYNAMIC_ENTRIES)
517
518 pre = self.indent + '(mapi_func) '
519 return pre + (',\n' + pre).join(entries)
520
521 def c_asm_gcc(self, prefix):
522 asm = []
523
524 asm.append('__asm__(')
525 for ent in self.entries:
526 if not self.need_entry_point(ent):
527 continue
528
529 name = self._c_function(ent, prefix, True, True)
530
531 if ent.handcode:
532 asm.append('#if 0')
533
534 if ent.hidden:
535 asm.append('".hidden "%s"\\n"' % (name))
536
537 if ent.alias:
538 asm.append('".globl "%s"\\n"' % (name))
539 asm.append('".set "%s", "%s"\\n"' % (name,
540 self._c_function(ent.alias, prefix, True, True)))
541 else:
542 asm.append('STUB_ASM_ENTRY(%s)"\\n"' % (name))
543 asm.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent.slot))
544
545 if ent.handcode:
546 asm.append('#endif')
547 asm.append('')
548 asm.append(');')
549
550 return "\n".join(asm)
551
552 def output_for_lib(self):
553 print self.c_notice()
554 print
555 print '#ifdef MAPI_TMP_DEFINES'
556 print self.c_public_includes()
557 print
558 print self.c_public_declarations(self.prefix_lib)
559 print '#undef MAPI_TMP_DEFINES'
560 print '#endif /* MAPI_TMP_DEFINES */'
561
562 if self.lib_need_table_size:
563 print
564 print '#ifdef MAPI_TMP_TABLE'
565 print self.c_mapi_table()
566 print '#undef MAPI_TMP_TABLE'
567 print '#endif /* MAPI_TMP_TABLE */'
568
569 if self.lib_need_noop_array:
570 print
571 print '#ifdef MAPI_TMP_NOOP_ARRAY'
572 print '#ifdef DEBUG'
573 print
574 print self.c_noop_functions(self.prefix_noop, self.prefix_lib)
575 print
576 print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
577 print self.c_noop_initializer(self.prefix_noop, False)
578 print '};'
579 print
580 print '#else /* DEBUG */'
581 print
582 print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
583 print self.c_noop_initializer(self.prefix_noop, True)
584 print '};'
585 print
586 print '#endif /* DEBUG */'
587 print '#undef MAPI_TMP_NOOP_ARRAY'
588 print '#endif /* MAPI_TMP_NOOP_ARRAY */'
589
590 if self.lib_need_stubs:
591 pool, pool_offsets = self.c_stub_string_pool()
592 print
593 print '#ifdef MAPI_TMP_PUBLIC_STUBS'
594 print 'static const char public_string_pool[] ='
595 print pool
596 print
597 print 'static const struct mapi_stub public_stubs[] = {'
598 print self.c_stub_initializer(self.prefix_lib, pool_offsets)
599 print '};'
600 print '#undef MAPI_TMP_PUBLIC_STUBS'
601 print '#endif /* MAPI_TMP_PUBLIC_STUBS */'
602
603 if self.lib_need_entries:
604 print
605 print '#ifdef MAPI_TMP_PUBLIC_ENTRIES'
606 print self.c_public_dispatches(self.prefix_lib)
607 print
608 print 'static const mapi_func public_entries[] = {'
609 print self.c_public_initializer(self.prefix_lib)
610 print '};'
611 print '#undef MAPI_TMP_PUBLIC_ENTRIES'
612 print '#endif /* MAPI_TMP_PUBLIC_ENTRIES */'
613
614 print
615 print '#ifdef MAPI_TMP_STUB_ASM_GCC'
616 print self.c_asm_gcc(self.prefix_lib)
617 print '#undef MAPI_TMP_STUB_ASM_GCC'
618 print '#endif /* MAPI_TMP_STUB_ASM_GCC */'
619
620 def output_for_app(self):
621 print self.c_notice()
622 print
623 print self.c_private_declarations(self.prefix_app)
624 print
625 print '#ifdef API_TMP_DEFINE_SPEC'
626 print
627 print 'static const char %s_spec[] =' % (self.prefix_app)
628 print self.c_mapi_table_spec()
629 print
630 print 'static const mapi_proc %s_procs[] = {' % (self.prefix_app)
631 print self.c_mapi_table_initializer(self.prefix_app)
632 print '};'
633 print
634 print '#endif /* API_TMP_DEFINE_SPEC */'
635
636 class GLAPIPrinter(ABIPrinter):
637 """OpenGL API Printer"""
638
639 def __init__(self, entries):
640 super(GLAPIPrinter, self).__init__(entries)
641
642 self.api_defines = ['GL_GLEXT_PROTOTYPES']
643 self.api_headers = ['"GL/gl.h"', '"GL/glext.h"']
644 self.api_call = 'GLAPI'
645 self.api_entry = 'APIENTRY'
646 self.api_attrs = ''
647
648 self.prefix_lib = 'gl'
649 self.prefix_app = '_mesa_'
650 self.prefix_noop = 'noop'
651
652 def output_for_app(self):
653 # not used
654 pass
655
656 class ES1APIPrinter(GLAPIPrinter):
657 """OpenGL ES 1.x API Printer"""
658
659 def __init__(self, entries):
660 super(ES1APIPrinter, self).__init__(entries)
661
662 self.api_headers = ['"GLES/gl.h"', '"GLES/glext.h"']
663 self.api_call = 'GL_API'
664 self.api_entry = 'GL_APIENTRY'
665
666 class ES2APIPrinter(GLAPIPrinter):
667 """OpenGL ES 2.x API Printer"""
668
669 def __init__(self, entries):
670 super(ES2APIPrinter, self).__init__(entries)
671
672 self.api_headers = ['"GLES2/gl2.h"', '"GLES2/gl2ext.h"']
673 self.api_call = 'GL_APICALL'
674 self.api_entry = 'GL_APIENTRY'
675
676 class VGAPIPrinter(ABIPrinter):
677 """OpenVG API Printer"""
678
679 def __init__(self, entries):
680 super(VGAPIPrinter, self).__init__(entries)
681
682 self.api_defines = ['VG_VGEXT_PROTOTYPES']
683 self.api_headers = ['"VG/openvg.h"', '"VG/vgext.h"']
684 self.api_call = 'VG_API_CALL'
685 self.api_entry = 'VG_API_ENTRY'
686 self.api_attrs = 'VG_API_EXIT'
687
688 self.prefix_lib = 'vg'
689 self.prefix_app = 'vega'
690 self.prefix_noop = 'noop'
691
692 def parse_args():
693 printers = ['glapi', 'es1api', 'es2api', 'vgapi']
694 modes = ['lib', 'app']
695
696 parser = OptionParser(usage='usage: %prog [options] <filename>')
697 parser.add_option('-p', '--printer', dest='printer',
698 help='printer to use: %s' % (", ".join(printers)))
699 parser.add_option('-m', '--mode', dest='mode',
700 help='target user: %s' % (", ".join(modes)))
701
702 options, args = parser.parse_args()
703 if not args or options.printer not in printers or \
704 options.mode not in modes:
705 parser.print_help()
706 sys.exit(1)
707
708 return (args[0], options)
709
710 def main():
711 printers = {
712 'vgapi': VGAPIPrinter,
713 'glapi': GLAPIPrinter,
714 'es1api': ES1APIPrinter,
715 'es2api': ES2APIPrinter
716 }
717
718 filename, options = parse_args()
719
720 if filename.endswith('.xml'):
721 entries = abi_parse_xml(filename)
722 else:
723 entries = abi_parse(filename)
724 abi_sanity_check(entries)
725
726 printer = printers[options.printer](entries)
727 if options.mode == 'lib':
728 printer.output_for_lib()
729 else:
730 printer.output_for_app()
731
732 if __name__ == '__main__':
733 main()