| #!/usr/bin/python3 |
| |
| # Copyright (c) 2019-2020 The Khronos Group Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from collections import OrderedDict |
| |
| import argparse |
| import sys |
| import urllib |
| import xml.etree.ElementTree as etree |
| import urllib.request |
| |
| def parse_xml(path): |
| file = urllib.request.urlopen(path) if path.startswith("http") else open(path, 'r') |
| with file: |
| tree = etree.parse(file) |
| return tree |
| |
| # File Header: |
| def GetHeader(): |
| return """// Copyright 2017-2020 The Khronos Group. This work is licensed under a |
| // Creative Commons Attribution 4.0 International License; see |
| // http://creativecommons.org/licenses/by/4.0/ |
| |
| """ |
| |
| # File Footer: |
| def GetFooter(): |
| return """ |
| """ |
| |
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument('-registry', action='store', |
| default='cl.xml', |
| help='Use specified registry file instead of cl.xml') |
| parser.add_argument('-o', action='store', dest='directory', |
| default='.', |
| help='Create target and related files in specified directory') |
| |
| args = parser.parse_args() |
| |
| linkFileName = args.directory + '/api-dictionary.asciidoc' |
| nolinkFileName = args.directory + '/api-dictionary-no-links.asciidoc' |
| |
| specpath = args.registry |
| #specpath = "https://raw.githubusercontent.com/KhronosGroup/OpenCL-Registry/master/xml/cl.xml" |
| |
| print('Generating dictionaries from: ' + specpath) |
| |
| spec = parse_xml(specpath) |
| |
| linkFile = open(linkFileName, 'w') |
| nolinkFile = open(nolinkFileName, 'w') |
| linkFile.write( GetHeader() ) |
| nolinkFile.write( GetHeader() ) |
| |
| # Generate the API functions dictionaries: |
| |
| numberOfFuncs = 0 |
| |
| # Add core API functions with and without links: |
| for feature in spec.findall('feature/require'): |
| for api in feature.findall('command'): |
| name = api.get('name') |
| #print('found api: ' + name) |
| |
| # Example with link: |
| # |
| # // clEnqueueNDRangeKernel |
| # :clEnqueueNDRangeKernel_label: pass:q[*clEnqueueNDRangeKernel*] |
| # :clEnqueueNDRangeKernel: <<clEnqueueNDRangeKernel,{clEnqueueNDRangeKernel_label}>> |
| linkFile.write('// ' + name + '\n') |
| linkFile.write(':' + name + '_label: pass:q[*' + name + '*]\n') |
| linkFile.write(':' + name + ': <<' + name + ',{' + name + '_label}>>\n') |
| linkFile.write('\n') |
| |
| # Example without link: |
| # |
| # // clEnqueueNDRangeKernel |
| # :clEnqueueNDRangeKernel: pass:q[*clEnqueueNDRangeKernel*] |
| nolinkFile.write('// ' + name + '\n') |
| nolinkFile.write(':' + name + ': pass:q[*' + name + '*]\n') |
| nolinkFile.write('\n') |
| |
| numberOfFuncs = numberOfFuncs + 1 |
| |
| # Add extension API functions without links: |
| for extension in spec.findall('extensions/extension/require'): |
| for api in extension.findall('command'): |
| name = api.get('name') |
| #print('found extension api: ' +name) |
| |
| # Example without link: |
| # |
| # // clGetGLObjectInfo |
| # :clGetGLObjectInfo: pass:q[*clGetGLObjectInfo*] |
| linkFile.write('// ' + name + '\n') |
| linkFile.write(':' + name + ': pass:q[*' + name + '*]\n') |
| linkFile.write('\n') |
| |
| nolinkFile.write('// ' + name + '\n') |
| nolinkFile.write(':' + name + ': pass:q[*' + name + '*]\n') |
| nolinkFile.write('\n') |
| |
| numberOfFuncs = numberOfFuncs + 1 |
| |
| print('Found ' + str(numberOfFuncs) + ' API functions.') |
| |
| # Generate the API enums dictionaries: |
| |
| numberOfEnums = 0 |
| |
| for enums in spec.findall('enums'): |
| # Skip Vendor Extension Enums |
| vendor = enums.get('vendor') |
| name = enums.get('name') # special-case: enum block with KHR enums assigned to vendor |
| include_anyway = name == 'enums.4010' |
| if not vendor or vendor == 'Khronos' or vendor == 'Multiple' or include_anyway: |
| for enum in enums.findall('enum'): |
| name = enum.get('name') |
| #print('found enum: ' + name) |
| |
| # Create a variant of the name that precedes underscores with |
| # "zero width" spaces. This causes some long names to be |
| # broken at more intuitive places. |
| htmlName = name[:3] + name[3:].replace("_", "_<wbr>") |
| otherName = name[:3] + name[3:].replace("_", "_​") |
| |
| # Example with link: |
| # |
| # // CL_MEM_READ_ONLY |
| #:CL_MEM_READ_ONLY_label: pass:q[`CL_MEM_READ_ONLY`] |
| #:CL_MEM_READ_ONLY: <<CL_MEM_READ_ONLY,{CL_MEM_READ_ONLY_label}>> |
| #:CL_MEM_READ_ONLY_anchor: [[CL_MEM_READ_ONLY]]{CL_MEM_READ_ONLY} |
| linkFile.write('// ' + name + '\n') |
| linkFile.write('ifdef::backend-html5[]\n') |
| linkFile.write(':' + name + '_label: pass:q[`' + htmlName + '`]\n') |
| linkFile.write('endif::[]\n') |
| linkFile.write('ifndef::backend-html5[]\n') |
| linkFile.write(':' + name + '_label: pass:q[`' + otherName + '`]\n') |
| linkFile.write('endif::[]\n') |
| linkFile.write(':' + name + ': <<' + name + ',{' + name + '_label}>>\n') |
| linkFile.write(':' + name + '_anchor: [[' + name + ']]{' + name + '}\n') |
| linkFile.write('\n') |
| |
| # Example without link: |
| # |
| # // CL_MEM_READ_ONLY |
| #:CL_MEM_READ_ONLY: pass:q[`CL_MEM_READ_ONLY`] |
| #:CL_MEM_READ_ONLY_anchor: {CL_MEM_READ_ONLY} |
| nolinkFile.write('// ' + name + '\n') |
| nolinkFile.write('ifdef::backend-html5[]\n') |
| nolinkFile.write(':' + name + ': pass:q[`' + htmlName + '`]\n') |
| nolinkFile.write('endif::[]\n') |
| nolinkFile.write('ifndef::backend-html5[]\n') |
| nolinkFile.write(':' + name + ': pass:q[`' + otherName + '`]\n') |
| nolinkFile.write('endif::[]\n') |
| nolinkFile.write(':' + name + '_anchor: {' + name + '}\n') |
| nolinkFile.write('\n') |
| |
| numberOfEnums = numberOfEnums + 1 |
| |
| print('Found ' + str(numberOfEnums) + ' API enumerations.') |
| |
| # Generate the API types dictionaries: |
| |
| numberOfTypes = 0 |
| |
| for types in spec.findall('types'): |
| for type in types.findall('type'): |
| addLink = False |
| name = "" |
| category = type.get('category') |
| if category == 'basetype': |
| name = type.get('name') |
| elif category == 'struct': |
| addLink = True |
| name = type.get('name') |
| elif category == 'define': |
| name = type.find('name').text |
| else: |
| continue |
| |
| #print('found type: ' +name) |
| |
| # Create a variant of the name that precedes underscores with |
| # "zero width" spaces. This causes some long names to be |
| # broken at more intuitive places. |
| if name.endswith('_t'): |
| htmlName = name |
| otherName = name |
| else: |
| htmlName = name[:3] + name[3:].replace("_", "_<wbr>") |
| otherName = name[:3] + name[3:].replace("_", "_​") |
| |
| # Some types can have spaces in the name (such as unsigned char), |
| # but Asciidoctor attributes cannot. So, replace spaces with |
| # underscores for the attribute name. |
| attribName = name.replace(" ", "_") |
| |
| # Append the type suffix for disambiguation, since asciidoctor |
| # attributes are not case-sensitive (currently). |
| attribName = attribName + "_TYPE" |
| |
| # Example with link: |
| # |
| # // cl_image_desc |
| # :cl_image_desc_TYPE_label: pass:q[`cl_image_desc`] |
| # :cl_image_desc_TYPE: <<cl_image_desc,{cl_image_desc_TYPE_label}>> |
| linkFile.write('// ' + name + '\n') |
| if addLink: |
| linkFile.write('ifdef::backend-html5[]\n') |
| linkFile.write(':' + attribName + '_label: pass:q[`' + htmlName + '`]\n') |
| linkFile.write('endif::[]\n') |
| linkFile.write('ifndef::backend-html5[]\n') |
| linkFile.write(':' + attribName + '_label: pass:q[`' + otherName + '`]\n') |
| linkFile.write('endif::[]\n') |
| linkFile.write(':' + attribName + ': <<' + name + ',{' + attribName + '_label}>>\n') |
| else: |
| linkFile.write('ifdef::backend-html5[]\n') |
| linkFile.write(':' + attribName + ': pass:q[`' + htmlName + '`]\n') |
| linkFile.write('endif::[]\n') |
| linkFile.write('ifndef::backend-html5[]\n') |
| linkFile.write(':' + attribName + ': pass:q[`' + otherName + '`]\n') |
| linkFile.write('endif::[]\n') |
| linkFile.write('\n') |
| |
| # // cl_image_desc |
| # :cl_image_desc_TYPE: pass:q[`cl_image_desc`] |
| nolinkFile.write('// ' + name + '\n') |
| nolinkFile.write('ifdef::backend-html5[]\n') |
| nolinkFile.write(':' + attribName + ': pass:q[`' + htmlName + '`]\n') |
| nolinkFile.write('endif::[]\n') |
| nolinkFile.write('ifndef::backend-html5[]\n') |
| nolinkFile.write(':' + attribName + ': pass:q[`' + otherName + '`]\n') |
| nolinkFile.write('endif::[]\n') |
| nolinkFile.write('\n') |
| |
| numberOfTypes = numberOfTypes + 1 |
| |
| print('Found ' + str(numberOfTypes) + ' API types.') |
| |
| linkFile.write( GetFooter() ) |
| linkFile.close() |
| nolinkFile.write( GetFooter() ) |
| nolinkFile.close() |
| |
| print('Successfully generated file: ' + linkFileName) |
| print('Successfully generated file: ' + nolinkFileName) |
| |