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