#!/usr/bin/env python # # Copyright 2016, Data61 # Commonwealth Scientific and Industrial Research Organisation (CSIRO) # ABN 41 687 119 230. # # This software may be distributed and modified according to the terms of # the BSD 2-Clause license. Note that NO WARRANTY is provided. # See "LICENSE_BSD2.txt" for details. # # @TAG(D61_BSD) # import sys, tempita, re, argparse from lxml import etree # ------------------------------------------- Configuration ---------------------------------------- DESCRIPTION = """\ CIDL - Simple C IDL Compiler is made to save the trouble of manually implementing RPC interface stubs to marshal / unmarshal C style arguments and return value. CIDL uses a simple XML-based IDL language. """ TITLE_MESSAGE = """\ /* DO NOT EDIT MANUALLY!!! This file was generated by CIDL. Copyright 2016, Data61 Commonwealth Scientific and Industrial Research Organisation (CSIRO) ABN 41 687 119 230. This software may be distributed and modified according to the terms of the BSD 2-Clause license. Note that NO WARRANTY is provided. See "LICENSE_BSD2.txt" for details. @TAG(D61_BSD) */ """ IMPL_WEAK_ATTRIB = '__attribute__((weak))' HEADER_PFX = 'refos-rpc/' CPTR_TYPE = 'seL4_CPtr' CSLOT_TYPE = 'seL4_CSlot' TEMPLATE_ROOT = open('cidl_templates/root.py', 'r').read() TEMPLATE_CLIENT_HEADER = open('cidl_templates/client_header.py', 'r').read() TEMPLATE_CLIENT = open('cidl_templates/client.py', 'r').read() TEMPLATE_SERVER_HEADER = open('cidl_templates/server_header.py', 'r').read() TEMPLATE_SERVER = open('cidl_templates/server.py', 'r').read() TEMPLATE_DISPATCHER = open('cidl_templates/dispatcher.py', 'r').read() TEMPLATE_DISPATCHER_CONTAINER = open('cidl_templates/dispatcher_container.py', 'r').read() TEMPLATE_ENUM = open('cidl_templates/enum.py', 'r').read() # ------------------------------------------ Helper Functions -------------------------------------- def get_if_name(if_name): if_name = re.sub(r'.xml$', '', if_name.strip()) if_name = re.search('([^\/]*)$', if_name).group(0) if_name = re.sub(r'_interface$', '', if_name) return if_name def get_headerfile_name(if_name, client_mode): return (HEADER_PFX + get_if_name(if_name) + '_%s.h')%('client' if client_mode else 'server') def preprocess(filestr, pre_process = True): if not pre_process: return filestr filestr_ = "" for fileline in filestr.splitlines(): if fileline.startswith("#") == False: filestr_ += fileline return filestr_.replace("\n",'').replace(r' ','').replace(r'____', ' ').\ replace("\\n", "\n").replace("\\\\", "\\") # ---------------------------------------- Arguments Processing ------------------------------------ CONNECT_EP = '' def process_arg(name_idl, type_idl, dr = '', mode_idl = None, lenvar_idl = ''): dir_idl = 'in'; if dr == 'out': dir_idl = 'out' apfx = '' aref = '' apsfx = '' if type_idl in ['int', 'int32_t', 'uint32_t', 'char', 'uintptr_t', 'size_t', 'void*', 'cslot', CSLOT_TYPE]: type_internal = 'uint' elif type_idl in ['cptr', CPTR_TYPE]: type_internal = 'cptr' elif type_idl == 'char*': type_internal = 'str' elif type_idl.endswith('*'): type_internal = 'buf' if mode_idl == 'array': apfx = '_array'; apsfx = ', ' + lenvar_idl else: type_internal = 'buf' aref = '&' if mode_idl is None: mode_idl = 'normal' if mode_idl == 'length': dir_idl = 'length' if mode_idl == 'connect_ep': global CONNECT_EP; CONNECT_EP = name_idl dir_idl = 'neither' return (dir_idl, (type_idl, type_internal, name_idl, mode_idl, dir_idl, apfx, aref, apsfx)) def process_arg_idl(arg_idl): return process_arg(arg_idl.get("name"), arg_idl.get("type"), arg_idl.get("dir"),\ arg_idl.get("mode"), arg_idl.get("lenvar")) def process_arg_last(alist, return_type = '', ralist = []): if return_type != '' and return_type != 'void': (_, arg_obj) = process_arg('__ret__', return_type, 'out') alist.append(arg_obj) ralist.append(arg_obj) if len(alist) <= 0: return # ---------------------------------------- Generator functions ------------------------------------- def process_function(func_idl, template_str, dct_func = {}, dbg = True): global CONNECT_EP alist = [] oalist = [] calist = [] ralist = [] for arg_idl in func_idl: (dr, arg_obj) = process_arg_idl(arg_idl) if arg_obj is None: continue calist.append(arg_obj) if dr == 'in': alist.append(arg_obj) elif dr == 'out': oalist.append(arg_obj) process_arg_last(alist) process_arg_last(oalist, func_idl.get('return'), ralist) process_arg_last(calist) dct_func['alist'] = alist dct_func['oalist'] = oalist dct_func['calist'] = calist dct_func['ralist'] = ralist dct_func['fname'] = func_idl.get('name') dct_func['return_type'] = func_idl.get('return') dct_func['comment_text'] = re.sub(r'^ +', ' ', func_idl.text.strip(), flags = re.MULTILINE) dct_func['connect_ep'] = str(CONNECT_EP) dct_func['weak_attrib'] = IMPL_WEAK_ATTRIB if dbg else '' return tempita.Template(template_str).substitute(dct_func) def process(filename, client, header, disp, pre_process, dbg): xml_idl = etree.parse(filename).getroot() dct_root = {} dct_root['includes'] = [] dct_root['includes'].append('#include <%srpc.h>' % HEADER_PFX) dct_root['ifname'] = get_if_name(filename) dct_root['label_min'] = xml_idl.get('label_min'); dct_root['default_connect_ep'] = xml_idl.get('connect_ep') dct_root['header_mode'] = header dct_root['client_mode'] = client if not header: dct_root['includes'].append('#include <%s>' % get_headerfile_name(filename, client)) if disp: template_str = TEMPLATE_DISPATCHER elif header: template_str = TEMPLATE_CLIENT_HEADER if client else TEMPLATE_SERVER_HEADER else: template_str = TEMPLATE_CLIENT if client else TEMPLATE_SERVER template_str = preprocess(template_str, pre_process) template_enum = preprocess(TEMPLATE_ENUM, pre_process) template_root = preprocess(TEMPLATE_ROOT, pre_process) if disp: template_root = preprocess(TEMPLATE_DISPATCHER_CONTAINER, pre_process) dct_root['func_list'] = []; dct_root['enum_list'] = [] for x in xml_idl: if x.tag == 'include': dct_root['includes'].append(re.sub(r'^\s*', '#include <', x.text.strip(),\ flags = re.MULTILINE) + '>\n') continue dct_root['func_list'].append(process_function(x, template_str, dct_root, dbg)) dct_root['enum_list'].append(process_function(x, template_enum, dct_root, dbg)) print(TITLE_MESSAGE) print(tempita.Template(template_root).substitute(dct_root)) parser = argparse.ArgumentParser(description = DESCRIPTION) parser.add_argument('-r', '--header', action='store_true',\ help='generate header declarations.') parser.add_argument('-c', '--client', action='store_true',\ help='generate header/src files for the RPC client.') parser.add_argument('-s', '--server', action='store_true',\ help='generate header/src files for the RPC server.') parser.add_argument('-d', '--dispatcher', action='store_true',\ help='generate dispatcher source file for RPC server.') parser.add_argument('-n', '--no_preprocess', action='store_false',\ help='skip pre-process template files.') parser.add_argument('-g', '--debug', action='store_true',\ help='debug mode, adds weak symbols to handler implementation functions.') parser.add_argument('-v', '--version', action='version',\ version='%(prog)s 1.2 Wed 14 Aug 2013 13:57:15 EST ') parser.add_argument('filename', metavar='IDL_FILE', action='store', \ help='the XML-based IDL file to compile.') args = parser.parse_args() process(args.filename, args.client, args.header, args.dispatcher, args.no_preprocess, args.debug)