xref: /dpdk/buildtools/dpdk-cmdline-gen.py (revision 620b269600244c269e1f3698b18c47c84d9d2a25)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2023 Intel Corporation
4#
5"""
6Script to automatically generate boilerplate for using DPDK cmdline library.
7"""
8
9import argparse
10import shlex
11import sys
12
13PARSE_FN_PARAMS = "void *parsed_result, struct cmdline *cl, void *data"
14PARSE_FN_BODY = """
15    /* TODO: command action */
16    RTE_SET_USED(parsed_result);
17    RTE_SET_USED(cl);
18    RTE_SET_USED(data);
19"""
20NUMERIC_TYPES = [
21    "UINT8",
22    "UINT16",
23    "UINT32",
24    "UINT64",
25    "INT8",
26    "INT16",
27    "INT32",
28    "INT64",
29]
30
31
32def process_command(lineno, tokens, comment):
33    """Generate the structures and definitions for a single command."""
34    out = []
35    cfile_out = []
36
37    if tokens[0].startswith("<"):
38        raise ValueError(f"Error line {lineno + 1}: command must start with a literal string")
39
40    name_tokens = []
41    for t in tokens:
42        if t.startswith("<"):
43            # stop processing the name building at a variable token,
44            # UNLESS the token name starts with "__"
45            t_type, t_name = t[1:].split(">")
46            if not t_name.startswith("__"):
47                break
48            t = t_name[2:]   # strip off the leading '__'
49        name_tokens.append(t)
50    name = "_".join(name_tokens)
51
52    result_struct = []
53    initializers = []
54    token_list = []
55    for t in tokens:
56        if t.startswith("<"):
57            t_type, t_name = t[1:].split(">")
58            t_val = "NULL"
59            if t_name.startswith("__"):
60                t_name = t_name[2:]
61        else:
62            t_type = "STRING"
63            t_name = t
64            t_val = f'"{t}"'
65
66        if t_type == "STRING":
67            result_struct.append(f"\tcmdline_fixed_string_t {t_name};")
68            initializers.append(
69                f"static cmdline_parse_token_string_t cmd_{name}_{t_name}_tok =\n"
70                f"\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});"
71            )
72        elif t_type in NUMERIC_TYPES:
73            result_struct.append(f"\t{t_type.lower()}_t {t_name};")
74            initializers.append(
75                f"static cmdline_parse_token_num_t cmd_{name}_{t_name}_tok =\n"
76                f"\tTOKEN_NUM_INITIALIZER(struct cmd_{name}_result, {t_name}, RTE_{t_type});"
77            )
78        elif t_type in ["IP", "IP_ADDR", "IPADDR"]:
79            result_struct.append(f"\tcmdline_ipaddr_t {t_name};")
80            initializers.append(
81                f"static cmdline_parse_token_ipaddr_t cmd_{name}_{t_name}_tok =\n"
82                f"\tTOKEN_IPADDR_INITIALIZER(struct cmd_{name}_result, {t_name});"
83            )
84        elif t_type in ["IPV4", "IPv4", "IPV4_ADDR"]:
85            result_struct.append(f"\tcmdline_ipaddr_t {t_name};")
86            initializers.append(
87                f"static cmdline_parse_token_ipaddr_t cmd_{name}_{t_name}_tok =\n"
88                f"\tTOKEN_IPV4_INITIALIZER(struct cmd_{name}_result, {t_name});"
89            )
90        elif t_type in ["IPV6", "IPv6", "IPV6_ADDR"]:
91            result_struct.append(f"\tcmdline_ipaddr_t {t_name};")
92            initializers.append(
93                f"static cmdline_parse_token_ipaddr_t cmd_{name}_{t_name}_tok =\n"
94                f"\tTOKEN_IPV6_INITIALIZER(struct cmd_{name}_result, {t_name});"
95            )
96        elif t_type.startswith("(") and t_type.endswith(")"):
97            result_struct.append(f"\tcmdline_fixed_string_t {t_name};")
98            t_val = f'"{t_type[1:-1].replace(",","#")}"'
99            initializers.append(
100                f"static cmdline_parse_token_string_t cmd_{name}_{t_name}_tok =\n"
101                f"\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});"
102            )
103        else:
104            raise TypeError(f"Error line {lineno + 1}: unknown token type '{t_type}'")
105        token_list.append(f"cmd_{name}_{t_name}_tok")
106
107    out.append(f'/* Auto-generated handling for command "{" ".join(tokens)}" */')
108    # output function prototype
109    func_sig = f"void\ncmd_{name}_parsed({PARSE_FN_PARAMS})"
110    out.append(f"extern {func_sig};\n")
111    # output result data structure
112    out.append(f"struct cmd_{name}_result {{\n" + "\n".join(result_struct) + "\n};\n")
113    # output the initializer tokens
114    out.append("\n".join(initializers) + "\n")
115    # output the instance structure
116    inst_elems = "\n".join([f"\t\t(void *)&{t}," for t in token_list])
117    out.append(
118        f"""\
119static cmdline_parse_inst_t cmd_{name} = {{
120\t.f = cmd_{name}_parsed,
121\t.data = NULL,
122\t.help_str = "{comment}",
123\t.tokens = {{
124{inst_elems}
125\t\tNULL,
126\t}}
127}};
128"""
129    )
130    # output function template if C file being written
131    cfile_out.append(f"{func_sig}\n{{{PARSE_FN_BODY}}}\n")
132
133    # return the instance structure name
134    return (f"cmd_{name}", out, cfile_out)
135
136
137def process_commands(infile, hfile, cfile, ctxname):
138    """Generate boilerplate output for a list of commands from infile."""
139    instances = []
140
141    hfile.write(
142        f"""\
143/* File autogenerated by {sys.argv[0]} */
144#ifndef GENERATED_COMMANDS_H
145#define GENERATED_COMMANDS_H
146#include <rte_common.h>
147#include <cmdline.h>
148#include <cmdline_parse_string.h>
149#include <cmdline_parse_num.h>
150#include <cmdline_parse_ipaddr.h>
151
152"""
153    )
154
155    for lineno, line in enumerate(infile.readlines()):
156        tokens = shlex.split(line, comments=True)
157        if not tokens:
158            continue
159        if "#" in line:
160            comment = line.split("#", 1)[-1].strip()
161        else:
162            comment = ""
163        cmd_inst, h_out, c_out = process_command(lineno, tokens, comment)
164        hfile.write("\n".join(h_out))
165        if cfile:
166            cfile.write("\n".join(c_out))
167        instances.append(cmd_inst)
168
169    inst_join_str = ",\n\t&"
170    hfile.write(
171        f"""
172static __rte_used cmdline_parse_ctx_t {ctxname}[] = {{
173\t&{inst_join_str.join(instances)},
174\tNULL
175}};
176
177#endif /* GENERATED_COMMANDS_H */
178"""
179    )
180
181
182def main():
183    """Application main entry point."""
184    ap = argparse.ArgumentParser(description=__doc__)
185    ap.add_argument(
186        "--stubs",
187        action="store_true",
188        help="Produce C file with empty function stubs for each command",
189    )
190    ap.add_argument(
191        "--output-file",
192        "-o",
193        default="-",
194        help="Output header filename [default to stdout]",
195    )
196    ap.add_argument(
197        "--context-name",
198        default="ctx",
199        help="Name given to the cmdline context variable in the output header [default=ctx]",
200    )
201    ap.add_argument("infile", type=argparse.FileType("r"), help="File with list of commands")
202    args = ap.parse_args()
203
204    if not args.stubs:
205        if args.output_file == "-":
206            process_commands(args.infile, sys.stdout, None, args.context_name)
207        else:
208            with open(args.output_file, "w") as hfile:
209                process_commands(args.infile, hfile, None, args.context_name)
210    else:
211        if not args.output_file.endswith(".h"):
212            ap.error(
213                "-o/--output-file: specify an output filename ending with .h when creating stubs"
214            )
215
216        cfilename = args.output_file[:-2] + ".c"
217        with open(args.output_file, "w") as hfile:
218            with open(cfilename, "w") as cfile:
219                cfile.write(f'#include "{args.output_file}"\n\n')
220                process_commands(args.infile, hfile, cfile, args.context_name)
221
222
223if __name__ == "__main__":
224    main()
225