1# Copyright (c) 2021 The Khronos Group Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from collections import OrderedDict 16from collections import namedtuple 17 18import argparse 19import sys 20import urllib 21import xml.etree.ElementTree as etree 22import urllib.request 23 24# parse_xml - Helper function to parse the XML file from a URL or local file. 25def parse_xml(path): 26 file = urllib.request.urlopen(path) if path.startswith("http") else open(path, 'r') 27 with file: 28 tree = etree.parse(file) 29 return tree 30 31# noneStr - returns string argument, or "" if argument is None. 32def noneStr(s): 33 if s: 34 return s 35 return "" 36 37def parse_args(): 38 parser = argparse.ArgumentParser() 39 40 # To pull the latest registry file from GitHub, pass: 41 # -registry "https://raw.githubusercontent.com/KhronosGroup/OpenCL-Registry/master/xml/cl.xml" 42 43 parser.add_argument('-registry', action='store', 44 default='cl.xml', 45 help='Use specified registry file instead of cl.xml') 46 parser.add_argument('-o', action='store', dest='directory', 47 default='.', 48 help='Create target and related files in specified directory') 49 50 args = parser.parse_args() 51 return args 52 53def load_spec(args): 54 specpath = args.registry 55 56 print('Parsing XML file from: ' + specpath) 57 spec = parse_xml(specpath) 58 return spec 59 60def get_apitypedefs(spec): 61 typedefs = OrderedDict() 62 Typedef = namedtuple('Typedef', 'Typedef Name') 63 print('Generating API typedef dictionary...') 64 for type in spec.findall('types/type'): 65 if type.get('category') == 'define': 66 if noneStr(type.text).startswith("typedef"): 67 typedef = noneStr(type.text) 68 name = "" 69 for elem in type: 70 if elem.tag == 'name': 71 name = noneStr(elem.text) 72 else: 73 typedef = typedef + noneStr(elem.text) + noneStr(elem.tail) 74 typedef = typedef.strip() 75 name = name.strip() 76 typedefs[name] = Typedef(typedef, name) 77 return typedefs 78 79def get_apimacros(spec): 80 macros = OrderedDict() 81 Macro = namedtuple('Macro', 'Define Name Macro') 82 print('Generating API macro dictionary...') 83 for type in spec.findall('types/type'): 84 if type.get('category') == 'define': 85 if noneStr(type.text).startswith("#define"): 86 define = noneStr(type.text) 87 name = "" 88 macro = "" 89 for elem in type: 90 if elem.tag == 'name': 91 name = noneStr(elem.text) 92 macro = macro + noneStr(elem.tail) 93 define = define.strip() 94 name = name.strip() 95 macro = macro.rstrip() # keep spaces on the left! 96 macros[name] = Macro(define, name, macro) 97 return macros 98 99def get_apistructs(spec): 100 structs = OrderedDict() 101 Struct = namedtuple('Struct', 'Name Members') 102 StructMember = namedtuple('StructMember', 'Type TypeEnd Name') 103 print('Generating API struct dictionary...') 104 for type in spec.findall('types/type'): 105 if type.get('category') == 'struct': 106 name = type.get('name') 107 mlist = [] 108 for member in type.findall('member'): 109 mtype = noneStr(member.text) 110 mtypeend = "" 111 mname = "" 112 for elem in member: 113 if elem.tag == 'name': 114 mname = noneStr(elem.text) 115 mtypeend = noneStr(elem.tail) 116 elif elem.tag == 'enum': 117 # Assumes any additional enums are for array limits, e.g.: 118 # <member><type>char</type><name>name</name>[<enum>CL_NAME_VERSION_MAX_NAME_SIZE</enum>]</member> 119 mtypeend = mtypeend + noneStr(elem.text) + noneStr(elem.tail) 120 else: 121 mtype = mtype + noneStr(elem.text) + noneStr(elem.tail) 122 mtype = mtype.strip() 123 mtypeend = mtypeend.strip() 124 mname = mname.strip() 125 mlist.append(StructMember(mtype, mtypeend, mname)) 126 structs[name] = Struct(name, mlist) 127 return structs 128 129def get_apienums(spec): 130 enums = OrderedDict() 131 Enum = namedtuple('Enum', 'Value Bitpos Name') 132 print('Generating API enum dictionary...') 133 for enum in spec.findall('enums/enum'): 134 value = enum.get('value') 135 bitpos = enum.get('bitpos') 136 name = enum.get('name') 137 enums[name] = Enum(value, bitpos, name) 138 return enums 139 140def get_apisigs(spec): 141 apisigs = OrderedDict() 142 ApiSignature = namedtuple('ApiSignature', 'Name RetType Params Suffix') 143 ApiParam = namedtuple('ApiParam', 'Type TypeEnd Name') 144 print('Generating API signatures dictionary...') 145 for command in spec.findall('commands/command'): 146 suffix = noneStr(command.get('suffix')) 147 proto = command.find('proto') 148 ret = noneStr(proto.text) 149 name = "" 150 params = "" 151 for elem in proto: 152 if elem.tag == 'name': 153 name = noneStr(elem.text) + noneStr(elem.tail) 154 else: 155 ret = ret + noneStr(elem.text) + noneStr(elem.tail) 156 ret = ret.strip() 157 name = name.strip() 158 159 plist = [] 160 for param in command.findall('param'): 161 ptype = noneStr(param.text) 162 ptypeend = "" 163 pname = "" 164 for elem in param: 165 if elem.tag == 'name': 166 pname = noneStr(elem.text) 167 ptypeend = noneStr(elem.tail) 168 else: 169 ptype = ptype + noneStr(elem.text) + noneStr(elem.tail) 170 ptype = ptype.strip() 171 ptypeend = ptypeend.strip() 172 pname = pname.strip() 173 plist.append(ApiParam(ptype, ptypeend, pname)) 174 175 # For an empty parameter list (for e.g. clUnloadCompiler), add a single 176 # unnamed void parameter to make generation easier. 177 if len(plist) == 0: 178 plist.append(ApiParam("void", "", "")) 179 180 apisigs[name] = ApiSignature(name, ret, plist, suffix) 181 return apisigs 182 183def get_coreapis(spec, apisigs): 184 coreapis = OrderedDict() 185 print('Generating core API dictionary...') 186 for feature in spec.findall('feature'): 187 version = noneStr(feature.get('name')) 188 189 alist = [] 190 for function in feature.findall('require/command'): 191 name = function.get('name') 192 alist.append(apisigs[name]) 193 coreapis[version] = alist 194 return coreapis 195 196def get_extapis(spec, apisigs): 197 extapis = OrderedDict() 198 print('Generating API extensions dictionary...') 199 for feature in spec.findall('extensions/extension'): 200 extension = noneStr(feature.get('name')) 201 202 alist = [] 203 for function in feature.findall('require/command'): 204 name = function.get('name') 205 alist.append(apisigs[name]) 206 extapis[extension] = alist 207 return extapis 208 209def get_apis(spec, apisigs): 210 return (get_coreapis(spec, apisigs), get_extapis(spec, apisigs)) 211