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