xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/driver_trace/enums2names.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#!/usr/bin/env python3
2# coding=utf-8
3##########################################################################
4#
5# enums2names - Parse and convert enums to translator code
6# (C) Copyright 2021 Matti 'ccr' Hämäläinen <[email protected]>
7#
8# Permission is hereby granted, free of charge, to any person obtaining a
9# copy of this software and associated documentation files (the
10# "Software"), to deal in the Software without restriction, including
11# without limitation the rights to use, copy, modify, merge, publish,
12# distribute, sub license, and/or sell copies of the Software, and to
13# permit persons to whom the Software is furnished to do so, subject to
14# the following conditions:
15#
16# The above copyright notice and this permission notice (including the
17# next paragraph) shall be included in all copies or substantial portions
18# of the Software.
19#
20# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
23# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
24# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27#
28##########################################################################
29
30import sys
31import os.path
32import re
33import signal
34import argparse
35import textwrap
36
37assert sys.version_info >= (3, 6)
38
39
40#
41# List of enums we wish to include in output.
42# NOTE: This needs to be updated if such enums are added.
43#
44lst_enum_include = [
45    "pipe_texture_target",
46    "pipe_shader_cap",
47    "pipe_shader_ir",
48    "pipe_map_flags",
49    "pipe_cap",
50    "pipe_capf",
51    "pipe_compute_cap",
52    "pipe_video_cap",
53    "pipe_video_profile",
54    "pipe_video_entrypoint",
55    "pipe_video_vpp_orientation",
56    "pipe_video_vpp_blend_mode",
57    "pipe_resource_param",
58    "pipe_fd_type",
59    "pipe_blendfactor",
60    "pipe_blend_func",
61    "pipe_logicop",
62]
63
64
65###
66### Utility functions
67###
68## Fatal error handler
69def pkk_fatal(smsg):
70    print("ERROR: "+ smsg)
71    sys.exit(1)
72
73
74## Handler for SIGINT signals
75def pkk_signal_handler(signal, frame):
76    print("\nQuitting due to SIGINT / Ctrl+C!")
77    sys.exit(1)
78
79
80## Argument parser subclass
81class PKKArgumentParser(argparse.ArgumentParser):
82    def print_help(self):
83        print("enums2names - Parse and convert enums to translator code\n"
84        "(C) Copyright 2021 Matti 'ccr' Hämäläinen <[email protected]>\n")
85        super().print_help()
86
87    def error(self, msg):
88        self.print_help()
89        print(f"\nERROR: {msg}", file=sys.stderr)
90        sys.exit(2)
91
92
93def pkk_get_argparser():
94    optparser = PKKArgumentParser(
95        usage="%(prog)s [options] <infile|->\n"
96        "example: %(prog)s ../../include/pipe/p_defines.h -C tr_util.c -H tr_util.h"
97        )
98
99    optparser.add_argument("in_files",
100        type=str,
101        metavar="infiles",
102        nargs="+",
103        help="path to input header files (or '-' for stdin)")
104
105    optparser.add_argument("-C",
106        type=str,
107        metavar="outfile",
108        dest="out_source",
109        help="output C source file")
110
111    optparser.add_argument("-H",
112        type=str,
113        metavar="outfile",
114        dest="out_header",
115        help="output C header file")
116
117    optparser.add_argument("-I",
118        type=str,
119        metavar="include",
120        dest="include_file",
121        help="include file / path used for C source output")
122
123    return optparser
124
125
126class PKKHeaderParser:
127
128    def __init__(self, nfilename):
129        self.filename = nfilename
130        self.enums = {}
131        self.state = 0
132        self.nline = 0
133        self.mdata = []
134        self.start = 0
135        self.name = None
136        self.in_multiline_comment = False
137
138    def error(self, msg):
139        pkk_fatal(f"{self.filename}:{self.nline} : {msg}")
140
141    def parse_line(self, sline: str):
142        start = sline.find('/*')
143        end = sline.find('*/')
144        if not self.in_multiline_comment and start >= 0:
145            if end >= 0:
146                assert end > start
147                sline = sline[:start] + sline[end + 2:]
148            else:
149                sline = sline[:start]
150                self.in_multiline_comment = True
151        elif self.in_multiline_comment and end >= 0:
152            self.in_multiline_comment = False
153            sline = sline[end + 2:]
154        elif self.in_multiline_comment:
155            return
156        # A kingdom for Py3.8 := operator ...
157        smatch = re.match(r'^enum\s+([A-Za-z0-9_]+)\s+.*;', sline)
158        if smatch:
159            pass
160        else:
161            smatch = re.match(r'^enum\s+([A-Za-z0-9_]+)', sline)
162            if smatch:
163                stmp = smatch.group(1)
164
165                if self.state != 0:
166                    self.error(f"enum '{stmp}' starting inside another enum '{self.name}'")
167
168                self.name = stmp
169                self.state = 1
170                self.start = self.nline
171                self.mdata = []
172            else:
173                smatch = re.match(r'^}(\s*|\s*[A-Z][A-Z_]+\s*);', sline)
174                if smatch:
175                    if self.state == 1:
176                        if self.name in self.enums:
177                            self.error("duplicate enum definition '{}', lines {} - {} vs {} - {}".format(
178                            self.name, self.enums[self.name]["start"], self.enums[self.name]["end"],
179                            self.start, self.nline))
180
181                        self.enums[self.name] = {
182                            "data": self.mdata,
183                            "start": self.start,
184                            "end": self.nline
185                        }
186
187                    self.state = 0
188
189                elif self.state == 1:
190                    smatch = re.match(r'([A-Za-z0-9_]+)\s*=\s*(.+)\s*,?', sline)
191                    if smatch:
192                        self.mdata.append(smatch.group(1))
193                    else:
194                        smatch = re.match(r'([A-Za-z0-9_]+)\s*,?', sline)
195                        if smatch:
196                            self.mdata.append(smatch.group(1))
197
198    def parse_file(self, fh):
199        self.nline = 0
200        for line in fh:
201            self.nline += 1
202            self.parse_line(line.strip())
203
204        return self.enums
205
206
207def pkk_output_header(fh):
208    prototypes = [f"const char *\n"
209        f"tr_util_{name}_name(enum {name} value);\n" for name in lst_enum_include]
210
211    print(textwrap.dedent("""\
212        /*
213         * File generated with {program}, please do not edit manually.
214         */
215        #ifndef {include_header_guard}
216        #define {include_header_guard}
217
218
219        #include "pipe/p_defines.h"
220        #include "pipe/p_video_enums.h"
221
222
223        #ifdef __cplusplus
224        extern "C" {{
225        #endif
226
227        {prototypes}
228
229        #ifdef __cplusplus
230        }}
231        #endif
232
233        #endif /* {include_header_guard} */\
234        """).format(
235            program=pkk_progname,
236            include_header_guard=re.sub(r'[^A-Z]', '_', os.path.basename(pkk_cfg.out_header).upper()),
237            prototypes="".join(prototypes)
238            ), file=fh)
239
240
241def pkk_output_source(fh):
242    if pkk_cfg.include_file == None:
243        pkk_fatal("Output C source enabled, but include file is not set (-I option).")
244
245    print(textwrap.dedent("""\
246        /*
247         * File generated with {program}, please do not edit manually.
248         */
249        #include "{include_file}"
250        """).format(
251            program=pkk_progname,
252            include_file=pkk_cfg.include_file,
253            ), file=fh)
254
255    for name in lst_enum_include:
256        cases = [f"      case {eid}: return \"{eid}\";\n"
257            for eid in enums[name]["data"]]
258
259        print(textwrap.dedent("""\
260
261            const char *
262            tr_util_{name}_name(enum {name} value)
263            {{
264               switch (value) {{
265            {cases}
266                  default: return "{ucname}_UNKNOWN";
267               }}
268            }}
269            """).format(
270                name=name,
271                ucname=name.upper(),
272                cases="".join(cases)
273                ), file=fh)
274
275###
276### Main program starts
277###
278if __name__ == "__main__":
279    signal.signal(signal.SIGINT, pkk_signal_handler)
280
281    ### Parse arguments
282    pkk_progname = sys.argv[0]
283    optparser = pkk_get_argparser()
284    pkk_cfg = optparser.parse_args()
285
286    ### Parse input files
287    enums = {}
288    for file in pkk_cfg.in_files:
289        hdrparser = PKKHeaderParser(file)
290
291        try:
292            if file != "-":
293                with open(file, "r", encoding="UTF-8") as fh:
294                    enums.update(hdrparser.parse_file(fh))
295            else:
296                enums.update(hdrparser.parse_file(sys.stdin))
297                break
298        except OSError as e:
299            pkk_fatal(str(e))
300
301    ### Check if any of the required enums are missing
302    errors = False
303    for name in lst_enum_include:
304        if name not in enums:
305            print(f"ERROR: Missing enum '{name}'!")
306            errors = True
307
308    if errors:
309        pkk_fatal(f"Errors in input. Edit this script ({pkk_progname}) to add/remove included enums.")
310
311    ### Perform output
312    if pkk_cfg.out_header:
313        with open(pkk_cfg.out_header, "w", encoding="UTF-8") as fh:
314            pkk_output_header(fh)
315
316    if pkk_cfg.out_source:
317        with open(pkk_cfg.out_source, "w", encoding="UTF-8") as fh:
318            pkk_output_source(fh)
319