1#!/usr/bin/env python 2 3# 4# Copyright 2016, Data61 5# Commonwealth Scientific and Industrial Research Organisation (CSIRO) 6# ABN 41 687 119 230. 7# 8# This software may be distributed and modified according to the terms of 9# the BSD 2-Clause license. Note that NO WARRANTY is provided. 10# See "LICENSE_BSD2.txt" for details. 11# 12# @TAG(D61_BSD) 13# 14 15import sys, tempita, re, argparse 16from lxml import etree 17 18# ------------------------------------------- Configuration ---------------------------------------- 19DESCRIPTION = """\ 20CIDL - Simple C IDL Compiler is made to save the trouble of manually implementing 21RPC interface stubs to marshal / unmarshal C style arguments and return value. 22CIDL uses a simple XML-based IDL language. 23""" 24TITLE_MESSAGE = """\ 25/* DO NOT EDIT MANUALLY!!! 26 This file was generated by CIDL. 27 28 Copyright 2016, Data61 29 Commonwealth Scientific and Industrial Research Organisation (CSIRO) 30 ABN 41 687 119 230. 31 32 This software may be distributed and modified according to the terms of 33 the BSD 2-Clause license. Note that NO WARRANTY is provided. 34 See "LICENSE_BSD2.txt" for details. 35 36 @TAG(D61_BSD) 37*/ 38""" 39IMPL_WEAK_ATTRIB = '__attribute__((weak))' 40HEADER_PFX = 'refos-rpc/' 41CPTR_TYPE = 'seL4_CPtr' 42CSLOT_TYPE = 'seL4_CSlot' 43TEMPLATE_ROOT = open('cidl_templates/root.py', 'r').read() 44TEMPLATE_CLIENT_HEADER = open('cidl_templates/client_header.py', 'r').read() 45TEMPLATE_CLIENT = open('cidl_templates/client.py', 'r').read() 46TEMPLATE_SERVER_HEADER = open('cidl_templates/server_header.py', 'r').read() 47TEMPLATE_SERVER = open('cidl_templates/server.py', 'r').read() 48TEMPLATE_DISPATCHER = open('cidl_templates/dispatcher.py', 'r').read() 49TEMPLATE_DISPATCHER_CONTAINER = open('cidl_templates/dispatcher_container.py', 'r').read() 50TEMPLATE_ENUM = open('cidl_templates/enum.py', 'r').read() 51 52# ------------------------------------------ Helper Functions -------------------------------------- 53 54def get_if_name(if_name): 55 if_name = re.sub(r'.xml$', '', if_name.strip()) 56 if_name = re.search('([^\/]*)$', if_name).group(0) 57 if_name = re.sub(r'_interface$', '', if_name) 58 return if_name 59 60def get_headerfile_name(if_name, client_mode): 61 return (HEADER_PFX + get_if_name(if_name) + '_%s.h')%('client' if client_mode else 'server') 62 63def preprocess(filestr, pre_process = True): 64 if not pre_process: 65 return filestr 66 filestr_ = "" 67 for fileline in filestr.splitlines(): 68 if fileline.startswith("#") == False: 69 filestr_ += fileline 70 return filestr_.replace("\n",'').replace(r' ','').replace(r'____', ' ').\ 71 replace("\\n", "\n").replace("\\\\", "\\") 72 73# ---------------------------------------- Arguments Processing ------------------------------------ 74 75CONNECT_EP = '' 76def process_arg(name_idl, type_idl, dr = '', mode_idl = None, lenvar_idl = ''): 77 dir_idl = 'in'; 78 if dr == 'out': 79 dir_idl = 'out' 80 81 apfx = '' 82 aref = '' 83 apsfx = '' 84 85 if type_idl in ['int', 'int32_t', 'uint32_t', 'char', 'uintptr_t', 86 'size_t', 'void*', 'cslot', CSLOT_TYPE]: 87 type_internal = 'uint' 88 elif type_idl in ['cptr', CPTR_TYPE]: 89 type_internal = 'cptr' 90 elif type_idl == 'char*': 91 type_internal = 'str' 92 elif type_idl.endswith('*'): 93 type_internal = 'buf' 94 if mode_idl == 'array': apfx = '_array'; apsfx = ', ' + lenvar_idl 95 else: 96 type_internal = 'buf' 97 aref = '&' 98 99 if mode_idl is None: mode_idl = 'normal' 100 if mode_idl == 'length': dir_idl = 'length' 101 if mode_idl == 'connect_ep': 102 global CONNECT_EP; 103 CONNECT_EP = name_idl 104 dir_idl = 'neither' 105 106 return (dir_idl, (type_idl, type_internal, name_idl, mode_idl, dir_idl, apfx, aref, apsfx)) 107 108def process_arg_idl(arg_idl): 109 return process_arg(arg_idl.get("name"), arg_idl.get("type"), arg_idl.get("dir"),\ 110 arg_idl.get("mode"), arg_idl.get("lenvar")) 111 112def process_arg_last(alist, return_type = '', ralist = []): 113 if return_type != '' and return_type != 'void': 114 (_, arg_obj) = process_arg('__ret__', return_type, 'out') 115 alist.append(arg_obj) 116 ralist.append(arg_obj) 117 if len(alist) <= 0: 118 return 119# ---------------------------------------- Generator functions ------------------------------------- 120def process_function(func_idl, template_str, dct_func = {}, dbg = True): 121 global CONNECT_EP 122 123 alist = [] 124 oalist = [] 125 calist = [] 126 ralist = [] 127 128 for arg_idl in func_idl: 129 (dr, arg_obj) = process_arg_idl(arg_idl) 130 if arg_obj is None: 131 continue 132 calist.append(arg_obj) 133 if dr == 'in': 134 alist.append(arg_obj) 135 elif dr == 'out': 136 oalist.append(arg_obj) 137 138 process_arg_last(alist) 139 process_arg_last(oalist, func_idl.get('return'), ralist) 140 process_arg_last(calist) 141 142 dct_func['alist'] = alist 143 dct_func['oalist'] = oalist 144 dct_func['calist'] = calist 145 dct_func['ralist'] = ralist 146 dct_func['fname'] = func_idl.get('name') 147 dct_func['return_type'] = func_idl.get('return') 148 dct_func['comment_text'] = re.sub(r'^ +', ' ', func_idl.text.strip(), flags = re.MULTILINE) 149 dct_func['connect_ep'] = str(CONNECT_EP) 150 dct_func['weak_attrib'] = IMPL_WEAK_ATTRIB if dbg else '' 151 152 return tempita.Template(template_str).substitute(dct_func) 153 154def process(filename, client, header, disp, pre_process, dbg): 155 xml_idl = etree.parse(filename).getroot() 156 157 dct_root = {} 158 dct_root['includes'] = [] 159 dct_root['includes'].append('#include <%srpc.h>' % HEADER_PFX) 160 dct_root['ifname'] = get_if_name(filename) 161 dct_root['label_min'] = xml_idl.get('label_min'); 162 dct_root['default_connect_ep'] = xml_idl.get('connect_ep') 163 dct_root['header_mode'] = header 164 dct_root['client_mode'] = client 165 if not header: 166 dct_root['includes'].append('#include <%s>' % get_headerfile_name(filename, client)) 167 168 if disp: 169 template_str = TEMPLATE_DISPATCHER 170 elif header: 171 template_str = TEMPLATE_CLIENT_HEADER if client else TEMPLATE_SERVER_HEADER 172 else: 173 template_str = TEMPLATE_CLIENT if client else TEMPLATE_SERVER 174 175 template_str = preprocess(template_str, pre_process) 176 template_enum = preprocess(TEMPLATE_ENUM, pre_process) 177 template_root = preprocess(TEMPLATE_ROOT, pre_process) 178 if disp: 179 template_root = preprocess(TEMPLATE_DISPATCHER_CONTAINER, pre_process) 180 181 dct_root['func_list'] = []; dct_root['enum_list'] = [] 182 for x in xml_idl: 183 if x.tag == 'include': 184 dct_root['includes'].append(re.sub(r'^\s*', '#include <', x.text.strip(),\ 185 flags = re.MULTILINE) + '>\n') 186 continue 187 dct_root['func_list'].append(process_function(x, template_str, dct_root, dbg)) 188 dct_root['enum_list'].append(process_function(x, template_enum, dct_root, dbg)) 189 print(TITLE_MESSAGE) 190 print(tempita.Template(template_root).substitute(dct_root)) 191 192parser = argparse.ArgumentParser(description = DESCRIPTION) 193parser.add_argument('-r', '--header', action='store_true',\ 194 help='generate header declarations.') 195parser.add_argument('-c', '--client', action='store_true',\ 196 help='generate header/src files for the RPC client.') 197parser.add_argument('-s', '--server', action='store_true',\ 198 help='generate header/src files for the RPC server.') 199parser.add_argument('-d', '--dispatcher', action='store_true',\ 200 help='generate dispatcher source file for RPC server.') 201parser.add_argument('-n', '--no_preprocess', action='store_false',\ 202 help='skip pre-process template files.') 203parser.add_argument('-g', '--debug', action='store_true',\ 204 help='debug mode, adds weak symbols to handler implementation functions.') 205parser.add_argument('-v', '--version', action='version',\ 206 version='%(prog)s 1.2 Wed 14 Aug 2013 13:57:15 EST ') 207parser.add_argument('filename', metavar='IDL_FILE', action='store', \ 208 help='the XML-based IDL file to compile.') 209args = parser.parse_args() 210 211process(args.filename, args.client, args.header, args.dispatcher, args.no_preprocess, args.debug) 212