1#!/usr/bin/python3 -i
2#
3# Copyright 2013-2023 The Khronos Group Inc.
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import re
8from generator import OutputGenerator, write
9from parse_dependency import dependencyLanguageSpecMacros
10
11def interfaceDocSortKey(item):
12    if item == None:
13        return '\0'
14    else:
15        return item.casefold()
16
17class InterfaceDocGenerator(OutputGenerator):
18    """InterfaceDocGenerator - subclass of OutputGenerator.
19    Generates AsciiDoc includes of the interfaces added by a an API version
20    or extension."""
21
22    def __init__(self, *args, **kwargs):
23        super().__init__(*args, **kwargs)
24        self.features = []
25
26    def beginFile(self, genOpts):
27        OutputGenerator.beginFile(self, genOpts)
28
29        # Create subdirectory, if needed
30        self.makeDir(self.genOpts.directory)
31
32    def beginFeature(self, interface, emit):
33        # Start processing in superclass
34        OutputGenerator.beginFeature(self, interface, emit)
35
36        self.features.append( self.featureName )
37
38    def endFeature(self):
39        # Finish processing in superclass
40        OutputGenerator.endFeature(self)
41
42    def writeNewInterfaces(self, feature, key, title, markup, fp):
43        dict = self.featureDictionary[feature][key]
44
45        parentmarkup = markup
46        if key == 'enumconstant':
47            parentmarkup = 'elink:'
48
49        if dict:
50            write('=== ' + title, file=fp)
51            write('',file=fp)
52
53            # Loop through required blocks, sorted so they start with "core" features
54            for required in sorted(dict, key = interfaceDocSortKey):
55                # 'required' may be a boolean expression of extension
56                # names.
57                # Currently this syntax is the same as asciidoc conditional
58                # syntax, but will eventually become more complex.
59                if required is not None:
60                    # Rewrite with spec macros and xrefs applied to names
61                    requiredlink = dependencyLanguageSpecMacros(required)
62
63                    # @@ A better approach would be to actually evaluate the
64                    # logical expression at generation time.
65                    # If the extensions required are not in the spec build,
66                    # then do not include these requirements.
67                    # This would support arbitrarily complex expressions,
68                    # unlike asciidoc ifdef syntax.
69                    write('ifdef::' + required + '[]', file=fp)
70                    write(f'If {requiredlink} is supported:', file=fp)
71                    write('',file=fp)
72
73                # Commands are relatively straightforward
74                if key == 'command':
75                    for api in sorted(dict[required]):
76                        write('  * ' + markup + api, file=fp)
77                # Types and constants are potentially parented, so need to handle that
78                else:
79                    # Loop through parents, sorted so they start with unparented items
80                    for parent in sorted(dict[required], key = interfaceDocSortKey):
81                        parentstring = ''
82                        if parent:
83                            parentstring = parentmarkup + (', ' + markup).join(parent.split(','))
84                            write('  * Extending ' + parentstring + ':', file=fp)
85                            for api in sorted(dict[required][parent]):
86                                write('  ** ' + markup + api, file=fp)
87                        else:
88                            for api in sorted(dict[required][parent]):
89                                write('  * ' + markup + api, file=fp)
90
91                if required is not None:
92                    write('endif::' + required + '[]', file=fp)
93                write('',file=fp)
94
95    def makeInterfaceFile(self, feature):
96        """Generate a file containing feature interface documentation in
97           asciidoctor markup form.
98
99        - feature - name of the feature being generated"""
100
101        filename = feature + self.genOpts.conventions.file_suffix
102        fp = open(self.genOpts.directory + '/' + filename, 'w', encoding='utf-8')
103
104        # Write out the lists of new interfaces added by the feature
105        self.writeNewInterfaces(feature, 'define',      'New Macros',           'dlink:',   fp)
106        self.writeNewInterfaces(feature, 'basetype',    'New Base Types',       'basetype:',fp)
107        self.writeNewInterfaces(feature, 'handle',      'New Object Types',     'slink:',   fp)
108        self.writeNewInterfaces(feature, 'command',     'New Commands',         'flink:',   fp)
109        self.writeNewInterfaces(feature, 'struct',      'New Structures',       'slink:',   fp)
110        self.writeNewInterfaces(feature, 'union',       'New Unions',           'slink:',   fp)
111        self.writeNewInterfaces(feature, 'funcpointer', 'New Function Pointers','tlink:',   fp)
112        self.writeNewInterfaces(feature, 'enum',        'New Enums',            'elink:',   fp)
113        self.writeNewInterfaces(feature, 'bitmask',     'New Bitmasks',         'tlink:',   fp)
114        self.writeNewInterfaces(feature, 'include',     'New Headers',          'code:',    fp)
115        self.writeNewInterfaces(feature, 'enumconstant','New Enum Constants',   'ename:',   fp)
116
117        fp.close()
118
119    def endFile(self):
120        # Generate metadoc feature files, in refpage and non-refpage form
121        for feature in self.features:
122            self.makeInterfaceFile(feature)
123
124        OutputGenerator.endFile(self)
125