1#!/usr/bin/env python3 2"""apis_generator.py - Generate a C++ interface that automates loading OpenCL. 3 4Usage: apis_generator.py <headerPaths...> 5 6The generated code looks roughly like this: 7------------------------------------------------------------------------ 8 9// apis.h 10 11CL_MACRO ( returnType, funcname, (fargs...), (callArgs...) ) 12 13""" 14 15import os.path 16import re 17import sys 18 19GENERATED_FILE_WARNING = """ 20/* 21 * This file is generated by {} 22 * Do not edit this file directly. 23 */""".format(os.path.basename(__file__)) 24 25MACRO_GUARD = """ 26#ifndef CL_MACRO 27#error You need to define CL_MACRO before including apis 28#endif""" 29 30 31def include_for_header(header): 32 return '#include <CL/{}>'.format(header) 33 34 35def extract_license_lines(lines): 36 license_lines = [] 37 for line in lines: 38 license_lines.append(line) 39 if line.find('*/') != -1: 40 return license_lines 41 sys.exit("License text didn't terminate") 42 43 44assert (extract_license_lines(['/* LICENSE */', 45 'something']) == ['/* LICENSE */']) 46assert (extract_license_lines(['/* LICENSE', ' * TEXT */', 47 'something']) == ['/* LICENSE', ' * TEXT */']) 48assert (extract_license_lines(['/* LICENSE', ' * TEXT', ' */', 'something' 49 ]) == ['/* LICENSE', ' * TEXT', ' */']) 50 51 52def parse_arg_strs(str): 53 paren_depth = 0 54 current_arg = '' 55 ret = [] 56 for c in str: 57 if c == '(': 58 paren_depth += 1 59 elif c == ')': 60 paren_depth -= 1 61 if c == ',' and paren_depth == 0: 62 ret.append(current_arg) 63 current_arg = '' 64 else: 65 current_arg += c 66 if current_arg != '': 67 ret.append(current_arg) 68 return ret 69 70 71def process_type(raw): 72 # strip redundant [] (where one is before the name) 73 raw = re.sub(r'(\[[0-9]*\])\s*(\w+)\s*\[[0-9]*\]', r'\2\1', raw) 74 # strip cases where the name comment hinted at the number of elements in an array 75 raw = re.sub(r'\*\s*(\w+)\s*\[[0-9]+\]', r'*\1', raw) 76 raw = ' '.join(raw.split()) 77 return raw 78 79 80def parse_api(api_signature): 81 m = None 82 83 api_signature = re.sub(r'\bextern\b', '', api_signature) 84 api_signature = re.sub('CL_\w+', '', api_signature) 85 86 m = re.match(r'\s*(.*)\s+(\w+)\((.*)\)\s*;', api_signature) 87 if m == None: 88 print(api_signature) 89 90 assert (m is not None) 91 assert (len(m.groups()) == 3) 92 arg_strs = None 93 if re.match('\s*void\s*', m.group(3)): 94 arg_strs = [] 95 else: 96 arg_strs = parse_arg_strs(m.group(3)) 97 args = [] 98 for arg_str in arg_strs: 99 nm = re.search(r'(\w+)\s*(\)|\[|$)', arg_str) 100 assert (nm is not None) 101 args.append({'type': process_type(arg_str), 'name': nm.group(1)}) 102 return {'return': m.group(1).strip(), 'name': m.group(2), 'args': args} 103 104 105def extract_apis(lines): 106 state = 'scanning' 107 apis = [] 108 api_signature = '' 109 for line in lines: 110 if state == 'scanning': 111 if line.find('CL_API_ENTRY') != -1 and line.find('typedef') == -1: 112 api_signature = line 113 if line.find(';') != -1: 114 apis.append( 115 parse_api( 116 api_signature.replace('/*', '').replace('*/', '').replace( 117 'CL_CALLBACK ', ''))) 118 api_signature = '' 119 else: 120 state = 'expectAPILine' 121 elif state == 'expectAPILine': 122 api_signature += ' ' + line 123 if line.find(';') != -1: 124 apis.append( 125 parse_api( 126 api_signature.replace('/*', '').replace('*/', '').replace( 127 'CL_CALLBACK ', ''))) 128 api_signature = '' 129 state = 'scanning' 130 return apis 131 132 133def generate_apis(apis): 134 print(GENERATED_FILE_WARNING) 135 print() 136 print(MACRO_GUARD) 137 print() 138 139 for api in apis: 140 fargs = (arg['type'] for arg in api['args']) 141 cargs = (arg['name'] for arg in api['args']) 142 print('CL_MACRO( {}, {}, ({}), ({}) )\n'.format(api['return'], api['name'], 143 ', '.join(fargs), 144 ', '.join(cargs))) 145 146 147def main(): 148 headers = sys.argv[1:] 149 apis = [] 150 151 with open(headers[0]) as header: 152 lines = [line.strip() for line in header.readlines()] 153 license_lines = extract_license_lines(lines) 154 155 for header_name in headers: 156 with open(header_name) as header: 157 lines = [line.strip() for line in header.readlines()] 158 apis = apis + extract_apis(lines) 159 160 generate_apis(apis) 161 162 163if __name__ == '__main__': 164 main() 165