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