diff options
Diffstat (limited to 'codegen/vulkan/scripts/docgenerator.py')
-rw-r--r-- | codegen/vulkan/scripts/docgenerator.py | 482 |
1 files changed, 0 insertions, 482 deletions
diff --git a/codegen/vulkan/scripts/docgenerator.py b/codegen/vulkan/scripts/docgenerator.py deleted file mode 100644 index bb86eb50..00000000 --- a/codegen/vulkan/scripts/docgenerator.py +++ /dev/null @@ -1,482 +0,0 @@ -#!/usr/bin/python3 -i -# -# Copyright 2013-2021 The Khronos Group Inc. -# -# SPDX-License-Identifier: Apache-2.0 - -from pathlib import Path - -from generator import GeneratorOptions, OutputGenerator, noneStr, write - -ENUM_TABLE_PREFIX = """ -[cols=",",options="header",] -|======================================================================= -|Enum |Description""" - -ENUM_TABLE_SUFFIX = """|=======================================================================""" - -FLAG_BLOCK_PREFIX = """.Flag Descriptions -****""" - -FLAG_BLOCK_SUFFIX = """****""" - -def orgLevelKey(name): - # Sort key for organization levels of features / extensions - # From highest to lowest, core versions, KHR extensions, EXT extensions, - # and vendor extensions - - prefixes = ( - 'VK_VERSION_', - 'VKSC_VERSION_', - 'VK_KHR_', - 'VK_EXT_') - - i = 0 - for prefix in prefixes: - if name.startswith(prefix): - return i - i += 1 - - # Everything else (e.g. vendor extensions) is least important - return i - -class DocGeneratorOptions(GeneratorOptions): - """DocGeneratorOptions - subclass of GeneratorOptions for - generating declaration snippets for the spec. - - Shares many members with CGeneratorOptions, since - both are writing C-style declarations.""" - - def __init__(self, - prefixText="", - apicall='', - apientry='', - apientryp='', - indentFuncProto=True, - indentFuncPointer=False, - alignFuncParam=0, - secondaryInclude=False, - expandEnumerants=True, - extEnumerantAdditions=False, - extEnumerantFormatString=" (Added by the {} extension)", - **kwargs): - """Constructor. - - Since this generator outputs multiple files at once, - the filename is just a "stamp" to indicate last generation time. - - Shares many parameters/members with CGeneratorOptions, since - both are writing C-style declarations: - - - prefixText - list of strings to prefix generated header with - (usually a copyright statement + calling convention macros). - - apicall - string to use for the function declaration prefix, - such as APICALL on Windows. - - apientry - string to use for the calling convention macro, - in typedefs, such as APIENTRY. - - apientryp - string to use for the calling convention macro - in function pointer typedefs, such as APIENTRYP. - - indentFuncProto - True if prototype declarations should put each - parameter on a separate line - - indentFuncPointer - True if typedefed function pointers should put each - parameter on a separate line - - alignFuncParam - if nonzero and parameters are being put on a - separate line, align parameter names at the specified column - - Additional parameters/members: - - - expandEnumerants - if True, add BEGIN/END_RANGE macros in enumerated - type declarations - - secondaryInclude - if True, add secondary (no xref anchor) versions - of generated files - - extEnumerantAdditions - if True, include enumerants added by extensions - in comment tables for core enumeration types. - - extEnumerantFormatString - A format string for any additional message for - enumerants from extensions if extEnumerantAdditions is True. The correctly- - marked-up extension name will be passed. - """ - GeneratorOptions.__init__(self, **kwargs) - self.prefixText = prefixText - """list of strings to prefix generated header with (usually a copyright statement + calling convention macros).""" - - self.apicall = apicall - """string to use for the function declaration prefix, such as APICALL on Windows.""" - - self.apientry = apientry - """string to use for the calling convention macro, in typedefs, such as APIENTRY.""" - - self.apientryp = apientryp - """string to use for the calling convention macro in function pointer typedefs, such as APIENTRYP.""" - - self.indentFuncProto = indentFuncProto - """True if prototype declarations should put each parameter on a separate line""" - - self.indentFuncPointer = indentFuncPointer - """True if typedefed function pointers should put each parameter on a separate line""" - - self.alignFuncParam = alignFuncParam - """if nonzero and parameters are being put on a separate line, align parameter names at the specified column""" - - self.secondaryInclude = secondaryInclude - """if True, add secondary (no xref anchor) versions of generated files""" - - self.expandEnumerants = expandEnumerants - """if True, add BEGIN/END_RANGE macros in enumerated type declarations""" - - self.extEnumerantAdditions = extEnumerantAdditions - """if True, include enumerants added by extensions in comment tables for core enumeration types.""" - - self.extEnumerantFormatString = extEnumerantFormatString - """A format string for any additional message for - enumerants from extensions if extEnumerantAdditions is True. The correctly- - marked-up extension name will be passed.""" - - -class DocOutputGenerator(OutputGenerator): - """DocOutputGenerator - subclass of OutputGenerator. - - Generates AsciiDoc includes with C-language API interfaces, for reference - pages and the corresponding specification. Similar to COutputGenerator, - but each interface is written into a different file as determined by the - options, only actual C types are emitted, and none of the boilerplate - preprocessor code is emitted.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # Keep track of all extension numbers - self.extension_numbers = set() - - def beginFile(self, genOpts): - OutputGenerator.beginFile(self, genOpts) - - # This should be a separate conventions property rather than an - # inferred type name pattern for different APIs. - self.result_type = genOpts.conventions.type_prefix + "Result" - - def endFile(self): - OutputGenerator.endFile(self) - - def beginFeature(self, interface, emit): - # Start processing in superclass - OutputGenerator.beginFeature(self, interface, emit) - - # Decide if we're in a core <feature> or an <extension> - self.in_core = (interface.tag == 'feature') - - # Verify that each <extension> has a unique number during doc - # generation - # TODO move this to consistency_tools - if not self.in_core: - extension_number = interface.get('number') - if extension_number is not None and extension_number != "0": - if extension_number in self.extension_numbers: - self.logMsg('error', 'Duplicate extension number ', extension_number, ' detected in feature ', interface.get('name'), '\n') - exit(1) - else: - self.extension_numbers.add(extension_number) - - def endFeature(self): - # Finish processing in superclass - OutputGenerator.endFeature(self) - - def genRequirements(self, name, mustBeFound = True): - """Generate text showing what core versions and extensions introduce - an API. This relies on the map in api.py, which may be loaded at - runtime into self.apidict. If not present, no message is - generated. - - - name - name of the API - - mustBeFound - If True, when requirements for 'name' cannot be - determined, a warning comment is generated. - """ - - if self.apidict: - if name in self.apidict.requiredBy: - # It's possible to get both 'A with B' and 'B with A' for - # the same API. - # To simplify this, sort the (base,dependency) requirements - # and put them in a set to ensure they're unique. - features = set() - for (base,dependency) in self.apidict.requiredBy[name]: - if dependency is not None: - l = sorted( - sorted((base, dependency)), - key=orgLevelKey) - features.add(' with '.join(l)) - else: - features.add(base) - # Sort the overall dependencies so core versions are first - provider = ', '.join(sorted(features, key=orgLevelKey)) - return f'// Provided by {provider}\n' - else: - if mustBeFound: - self.logMsg('warn', 'genRequirements: API {} not found'.format(name)) - return '' - else: - # No API dictionary available, return nothing - return '' - - def writeInclude(self, directory, basename, contents): - """Generate an include file. - - - directory - subdirectory to put file in - - basename - base name of the file - - contents - contents of the file (Asciidoc boilerplate aside)""" - # Create subdirectory, if needed - directory = self.genOpts.directory + '/' + directory - self.makeDir(directory) - - # Create file - filename = directory + '/' + basename + '.txt' - self.logMsg('diag', '# Generating include file:', filename) - fp = open(filename, 'w', encoding='utf-8') - - # Asciidoc anchor - write(self.genOpts.conventions.warning_comment, file=fp) - write('[[{0},{0}]]'.format(basename), file=fp) - - if self.genOpts.conventions.generate_index_terms: - index_terms = [] - if basename.startswith(self.conventions.command_prefix): - index_terms.append(basename[2:] + " (function)") - elif basename.startswith(self.conventions.type_prefix): - index_terms.append(basename[2:] + " (type)") - elif basename.startswith(self.conventions.api_prefix): - index_terms.append(basename[len(self.conventions.api_prefix):] + " (define)") - index_terms.append(basename) - write('indexterm:[{}]'.format(','.join(index_terms)), file=fp) - - write('[source,c++]', file=fp) - write('----', file=fp) - write(contents, file=fp) - write('----', file=fp) - fp.close() - - if self.genOpts.secondaryInclude: - # Create secondary no cross-reference include file - filename = directory + '/' + basename + '.no-xref.txt' - self.logMsg('diag', '# Generating include file:', filename) - fp = open(filename, 'w', encoding='utf-8') - - # Asciidoc anchor - write(self.genOpts.conventions.warning_comment, file=fp) - write('// Include this no-xref version without cross reference id for multiple includes of same file', file=fp) - write('[source,c++]', file=fp) - write('----', file=fp) - write(contents, file=fp) - write('----', file=fp) - fp.close() - - def writeTable(self, basename, values): - """Output a table of enumerants.""" - directory = Path(self.genOpts.directory) / 'enums' - self.makeDir(str(directory)) - - filename = str(directory / '{}.comments.txt'.format(basename)) - self.logMsg('diag', '# Generating include file:', filename) - - with open(filename, 'w', encoding='utf-8') as fp: - write(self.conventions.warning_comment, file=fp) - write(ENUM_TABLE_PREFIX, file=fp) - - for data in values: - write("|ename:{}".format(data['name']), file=fp) - write("|{}".format(data['comment']), file=fp) - - write(ENUM_TABLE_SUFFIX, file=fp) - - def writeFlagBox(self, basename, values): - """Output a box of flag bit comments.""" - directory = Path(self.genOpts.directory) / 'enums' - self.makeDir(str(directory)) - - filename = str(directory / '{}.comments.txt'.format(basename)) - self.logMsg('diag', '# Generating include file:', filename) - - with open(filename, 'w', encoding='utf-8') as fp: - write(self.conventions.warning_comment, file=fp) - write(FLAG_BLOCK_PREFIX, file=fp) - - for data in values: - write("* ename:{} -- {}".format(data['name'], - data['comment']), - file=fp) - - write(FLAG_BLOCK_SUFFIX, file=fp) - - def genType(self, typeinfo, name, alias): - """Generate type.""" - OutputGenerator.genType(self, typeinfo, name, alias) - typeElem = typeinfo.elem - # If the type is a struct type, traverse the embedded <member> tags - # generating a structure. Otherwise, emit the tag text. - category = typeElem.get('category') - - if category in ('struct', 'union'): - # If the type is a struct type, generate it using the - # special-purpose generator. - self.genStruct(typeinfo, name, alias) - elif category not in OutputGenerator.categoryToPath: - # If there's no path, don't write output - self.logMsg('diag', 'NOT writing include for {} category {}'.format( - name, category)) - else: - body = self.genRequirements(name) - if alias: - # If the type is an alias, just emit a typedef declaration - body += 'typedef ' + alias + ' ' + name + ';\n' - self.writeInclude(OutputGenerator.categoryToPath[category], - name, body) - else: - # Replace <apientry /> tags with an APIENTRY-style string - # (from self.genOpts). Copy other text through unchanged. - # If the resulting text is an empty string, don't emit it. - body += noneStr(typeElem.text) - for elem in typeElem: - if elem.tag == 'apientry': - body += self.genOpts.apientry + noneStr(elem.tail) - else: - body += noneStr(elem.text) + noneStr(elem.tail) - - if body: - self.writeInclude(OutputGenerator.categoryToPath[category], - name, body + '\n') - else: - self.logMsg('diag', 'NOT writing empty include file for type', name) - - def genStruct(self, typeinfo, typeName, alias): - """Generate struct.""" - OutputGenerator.genStruct(self, typeinfo, typeName, alias) - - typeElem = typeinfo.elem - - body = self.genRequirements(typeName) - if alias: - body += 'typedef ' + alias + ' ' + typeName + ';\n' - else: - body += 'typedef ' + typeElem.get('category') + ' ' + typeName + ' {\n' - - targetLen = self.getMaxCParamTypeLength(typeinfo) - for member in typeElem.findall('.//member'): - body += self.makeCParamDecl(member, targetLen + 4) - body += ';\n' - body += '} ' + typeName + ';' - - self.writeInclude('structs', typeName, body) - - def genEnumTable(self, groupinfo, groupName): - """Generate tables of enumerant values and short descriptions from - the XML.""" - - values = [] - got_comment = False - missing_comments = [] - for elem in groupinfo.elem.findall('enum'): - if not elem.get('required'): - continue - name = elem.get('name') - - data = { - 'name': name, - } - - (numVal, strVal) = self.enumToValue(elem, True) - data['value'] = numVal - - extname = elem.get('extname') - - added_by_extension_to_core = (extname is not None and self.in_core) - if added_by_extension_to_core and not self.genOpts.extEnumerantAdditions: - # We're skipping such values - continue - - comment = elem.get('comment') - if comment: - got_comment = True - elif name.endswith('_UNKNOWN') and numVal == 0: - # This is a placeholder for 0-initialization to be clearly invalid. - # Just skip this silently - continue - else: - # Skip but record this in case it's an odd-one-out missing a comment. - missing_comments.append(name) - continue - - if added_by_extension_to_core and self.genOpts.extEnumerantFormatString: - # Add a note to the comment - comment += self.genOpts.extEnumerantFormatString.format( - self.conventions.formatExtension(extname)) - - data['comment'] = comment - values.append(data) - - if got_comment: - # If any had a comment, output it. - - if missing_comments: - self.logMsg('warn', 'The following values for', groupName, - 'were omitted from the table due to missing comment attributes:', - ', '.join(missing_comments)) - - group_type = groupinfo.elem.get('type') - if groupName == self.result_type: - # Split this into success and failure - self.writeTable(groupName + '.success', - (data for data in values - if data['value'] >= 0)) - self.writeTable(groupName + '.error', - (data for data in values - if data['value'] < 0)) - elif group_type == 'bitmask': - self.writeFlagBox(groupName, values) - elif group_type == 'enum': - self.writeTable(groupName, values) - else: - raise RuntimeError("Unrecognized enums type: " + str(group_type)) - - def genGroup(self, groupinfo, groupName, alias): - """Generate group (e.g. C "enum" type).""" - OutputGenerator.genGroup(self, groupinfo, groupName, alias) - - body = self.genRequirements(groupName) - if alias: - # If the group name is aliased, just emit a typedef declaration - # for the alias. - body += 'typedef ' + alias + ' ' + groupName + ';\n' - else: - expand = self.genOpts.expandEnumerants - (_, enumbody) = self.buildEnumCDecl(expand, groupinfo, groupName) - body += enumbody - if self.genOpts.conventions.generate_enum_table: - self.genEnumTable(groupinfo, groupName) - - self.writeInclude('enums', groupName, body) - - def genEnum(self, enuminfo, name, alias): - """Generate the C declaration for a constant (a single <enum> value).""" - - OutputGenerator.genEnum(self, enuminfo, name, alias) - - body = self.buildConstantCDecl(enuminfo, name, alias) - - self.writeInclude('enums', name, body) - - def genCmd(self, cmdinfo, name, alias): - "Generate command." - OutputGenerator.genCmd(self, cmdinfo, name, alias) - - return_type = cmdinfo.elem.find('proto/type') - if self.genOpts.conventions.requires_error_validation(return_type): - # This command returns an API result code, so check that it - # returns at least the required errors. - # TODO move this to consistency_tools - required_errors = set(self.genOpts.conventions.required_errors) - errorcodes = cmdinfo.elem.get('errorcodes').split(',') - if not required_errors.issubset(set(errorcodes)): - self.logMsg('error', 'Missing required error code for command: ', name, '\n') - exit(1) - - body = self.genRequirements(name) - decls = self.makeCDecls(cmdinfo.elem) - body += decls[0] - self.writeInclude('protos', name, body) |