From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 4FA2B43155; Fri, 13 Oct 2023 14:43:52 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 1D7FA402D3; Fri, 13 Oct 2023 14:43:52 +0200 (CEST) Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.151]) by mails.dpdk.org (Postfix) with ESMTP id D9263402CF for ; Fri, 13 Oct 2023 14:43:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1697201029; x=1728737029; h=date:from:to:cc:subject:message-id:references: in-reply-to:mime-version; bh=MZgmVzGtqfRx6woMZtzOXDtPKbFpZoTDVK1JIYJeaE8=; b=XIdzW32/1EErQYRjIunqraXeGm3yU0qTJTFCSgYNduLITyLZa2MZFY82 2LswAGk+JC2uFySe/ceRl2ZIcQsr7c8l4i85SFxcX0Ev0nYaYmFUQ7o54 Q+n5zRsC0f+BJ1TOQOC6NfNzM7z41jZntbrOi5BZODBqPsGMYWnHfyUyi IXo7c2X30EP3kOrkCs2kSuJ9M5wajId9HkrQUCZZyPkfnkPz20M9bxxhw RLMWPtnuuKkw4R1l934KAsZ/rhAh4l4PT/9M6KfBmd2OJ6Um7ZN8GaeVB ahzz5L2wLJUPToEGauyRBnF+Rl2tyU9+rmhja2BiHj84lIzFW0tZAPL73 w==; X-IronPort-AV: E=McAfee;i="6600,9927,10862"; a="365433012" X-IronPort-AV: E=Sophos;i="6.03,222,1694761200"; d="scan'208";a="365433012" Received: from fmsmga006.fm.intel.com ([10.253.24.20]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Oct 2023 05:43:41 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10862"; a="1001940566" X-IronPort-AV: E=Sophos;i="6.03,222,1694761200"; d="scan'208";a="1001940566" Received: from orsmsx603.amr.corp.intel.com ([10.22.229.16]) by fmsmga006.fm.intel.com with ESMTP/TLS/AES256-GCM-SHA384; 13 Oct 2023 05:43:41 -0700 Received: from orsmsx611.amr.corp.intel.com (10.22.229.24) by ORSMSX603.amr.corp.intel.com (10.22.229.16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.32; Fri, 13 Oct 2023 05:43:40 -0700 Received: from orsmsx611.amr.corp.intel.com (10.22.229.24) by ORSMSX611.amr.corp.intel.com (10.22.229.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.32; Fri, 13 Oct 2023 05:43:40 -0700 Received: from orsedg603.ED.cps.intel.com (10.7.248.4) by orsmsx611.amr.corp.intel.com (10.22.229.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.32 via Frontend Transport; Fri, 13 Oct 2023 05:43:40 -0700 Received: from NAM10-MW2-obe.outbound.protection.outlook.com (104.47.55.100) by edgegateway.intel.com (134.134.137.100) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.32; Fri, 13 Oct 2023 05:43:40 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Fz+9J567eSMOtNrersQzAwgCWL7nxbJWyjMO1HquQjUG8qZzHTLQfd3YFpqXKRCLhvtAUtq3jcikmf+FeEfHyxGYRcVrPHFn+7F4cFeNAasvaOmAejBqHQ87Tz3IIJZAkjBK6JaTGNGcuA+8UQXTNm7TwK+84Q6wxzUgeIGmC289iezq6QhEmQY87eDoim4llKCAW3bsRv/2EO3TNn8kqPeUfNo/qbDUNfOSXOrGQApVH4UAnAoBPzSMiD0CFEZxFAgasF3qln89XpU4nnjYRKN0EOq0gSZIoqXbI3WajAH7XZ+KEC7vMrlDmi/ZzwgczHhD/SZhJWVIvGybeluvqQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=z7T7pMuA80WQHhykjT3tidVwol11K6xHfG1O68y/mdo=; b=CaFE8C5gQJkudBOTrb0yIGg9AoSLgijMUWdUVQuFdVXu4yLkwlbut2eBgO3i89zZ3+qKzrAwWbfNe0VBTJ+GcY6ZOPu7MV8k7rmU201GroLhM5TSkVfSgaFqXsA6WL46fUlPbgwXNbWzebJ+cWuLME76r3lkZJmPgo4337uPZxqbdaVrgCE3hZ1cPCkXRzBYlXgcu68zQ7UJcK5aHVO6UES84BPCLS08IzseSUegQF9Fxur5I1A/6sTaeeveE2pEx69lg9YEEQps5fWsjeFg4QPgIPvlwfugcAz6kECA/9CzhH7sLSwPHWVDzy7HRd7dD+CkbJEOSBTA1d/YundDBg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; Received: from DS0PR11MB7309.namprd11.prod.outlook.com (2603:10b6:8:13e::17) by SN7PR11MB7139.namprd11.prod.outlook.com (2603:10b6:806:2a2::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6863.37; Fri, 13 Oct 2023 12:43:38 +0000 Received: from DS0PR11MB7309.namprd11.prod.outlook.com ([fe80::aa85:ead1:baa8:c652]) by DS0PR11MB7309.namprd11.prod.outlook.com ([fe80::aa85:ead1:baa8:c652%2]) with mapi id 15.20.6863.043; Fri, 13 Oct 2023 12:43:38 +0000 Date: Fri, 13 Oct 2023 13:43:34 +0100 From: Bruce Richardson To: Robin Jarry CC: Subject: Re: [PATCH v3 2/5] buildtools: script to generate cmdline boilerplate Message-ID: References: <20230802170052.955323-1-bruce.richardson@intel.com> <20231011133357.111058-1-bruce.richardson@intel.com> <20231011133357.111058-3-bruce.richardson@intel.com> Content-Type: text/plain; charset="us-ascii" Content-Disposition: inline In-Reply-To: X-ClientProxiedBy: DUZPR01CA0209.eurprd01.prod.exchangelabs.com (2603:10a6:10:4b6::22) To DS0PR11MB7309.namprd11.prod.outlook.com (2603:10b6:8:13e::17) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DS0PR11MB7309:EE_|SN7PR11MB7139:EE_ X-MS-Office365-Filtering-Correlation-Id: 4cbce262-d2ca-4f55-c112-08dbcbea0582 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 1bEftx8q00fdczTuPvn3RspjaFOd1RY37/YDPlzdFl1cKRbFoBwDM0TREt3UDipvoOF1bCby4gwp1KsDiFYKkPtDjAVaalEyxHVCOHZ7CxTYu1MqMEmiSL+aLsCws2amhnHBZ/sGxCftMdPs9r0qbJIOyC0MzMrfTMplOUsUG7esSzChEcRdZFpSqj85usN49sQUKT18VUU/XT3/0J+YX0YUaBsWVbCdnfiZqtrqap8pa5TllsRJDg3/HiXHycEgZYBFZMFKo2WDz+opnDGGsrsOj6sM/btNKAnWRBp1tOez48yG+UoVQ7hMJ0J6f/lAEmmhsR52qX0YrJeHuTn1RuHmUkxG2u9KJ7/pQ8PZWgiHudWgoG7096TtdH0NG/aELEeY4VCall9GqavrRtClGzGO8uSmEPk6ZwPCuUzIetbcGyGjRglpVgTu8HVr+lNwK4eCYFXB5q7J/ReO2dMVtevW+2QJ0lk/DhLbWZBTyPBIJ1OLqSc9eIXvskAWlgpHzs+F74rQ7rhetPwnLBESqB+jXS8K0pbLzfNKLgnuvfqfS6z1QSMJbXCxufsusNQZ X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DS0PR11MB7309.namprd11.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(396003)(366004)(136003)(346002)(376002)(39860400002)(230922051799003)(186009)(451199024)(1800799009)(64100799003)(82960400001)(5660300002)(4326008)(41300700001)(44832011)(8676002)(8936002)(86362001)(2906002)(30864003)(478600001)(6666004)(6512007)(6506007)(83380400001)(66556008)(26005)(66946007)(6916009)(316002)(38100700002)(66476007)(6486002); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?/Ciaw2ppedSlUtP+7ROxuSVQ/fB7uU26u1EjP8ns2N+OqkQhK5o45G+fQtlb?= =?us-ascii?Q?RlAaKsEYBxzfk3nRAwY+ffKLl/G01iMztlzSb0biDvyyaSySghVGidG046kI?= =?us-ascii?Q?Z9ay3q27PD2MVyTIDNDmPV1cTOC5IBBUcxcva1em6RNZGk4sqpv9ATyLj3UN?= =?us-ascii?Q?t4wSKhaDyFc1iKryxIqmxsodCB7zfOniAKrvrHoWVqp8b3hLoP+i7oGLdqjM?= =?us-ascii?Q?hm+1UawJnzuix7UAH3rgHTsai3AZDHB9svH3sOpbVrtyHi8mGiMkzFi2/7Lh?= =?us-ascii?Q?QpvvOQrmopCKI7AUFbg7YVwDcaVLZcaY9WF0jK8jPHY58zxbHLVcnD8HjVRM?= =?us-ascii?Q?7VAsK2mO8UH1wIf1uR+7yhsCzpO/4g2OwaIgZvj54UPirLlfXGPDKKlDCDrn?= =?us-ascii?Q?tG7jFW4qwI+jst1EQhOLTP7SLVfMKjiF3hDtH2BrB7z3vW9+3zLQtLjNpWyY?= =?us-ascii?Q?Y5dr87c4Vkf/xJCwavk5DoOC5Imvei1yunONEhWemLbaacqHLyp7PzUaDP9P?= =?us-ascii?Q?uY0l/A31gzajwgpEUzEg3GVDtsgAY9/z2QyJDCtiT+/uuczOP54a6mCLIs+J?= =?us-ascii?Q?TV/iRXe5jmg7mQblme4MfTqEM+5+KxiIvi7Z5nRIsSXw8PJyTY2OsCykh59j?= =?us-ascii?Q?c0D444TQmUVEzVNZ128MMjGn6/StKMKH9DcFfAumoQ5wYathcVVa6Jk8bRwc?= =?us-ascii?Q?Bvv/Y4vHojlJzF1MiKwwSJcnnkj2uJA/I+0WhxwelmiknXCa7n26TT7jZ1PU?= =?us-ascii?Q?QsyskPNJAtUN1Wf1MYQe3mLe2z2fBRYBEo4z4gsdqbOssgde8dyRK54uIeVg?= =?us-ascii?Q?ztXYXcCgO9OvuKBo3btAd1rdjqmNhHknn/ZWPYUqXnmiby5Ike9Ahs7reY2G?= =?us-ascii?Q?UGO1ScQoHdXtbvy0+C1Br2fiWM4hVvB9GAUIiIzf2L+Fjx5pne6UmidkojXN?= =?us-ascii?Q?fdNTd2sthnLlJFhcUQ8D9Ge/PdS3Aj9cwR5N6W+fcaT2WFkgXcZUmc8sktUT?= =?us-ascii?Q?QfFUH5tmlVmlo/fBIf6uQnqH0is8UuWWtqH9bbzzE6W+RwAOJ5gviadzPJkJ?= =?us-ascii?Q?55A+vmiCbK3olzh5lLNX1mV879CSaJrxScd8AYzB3qMzhS8lnwyc0DO3ZsXc?= =?us-ascii?Q?QiMfWBdWb0F1VIwSdAPMIv7S0bVX+U4OcU30z2d47smIgaYy2XyyaTW22NDF?= =?us-ascii?Q?UmyMAly294kfqDsIkmFyJ2edwj8aaqVelN8z5cyKrc1JB21cJFKjdO23tRSC?= =?us-ascii?Q?NydXNVncsecuXy3FrM0oQl0xDJl5i1kXnxa/GdF/zBhLrWtUAspqKCtsj1w8?= =?us-ascii?Q?xXbtH5f0/mtG6R52VBHqGSlscahyFIZeDsFrP3H0jkcrPTRDrzmL/UrAiTWJ?= =?us-ascii?Q?od2iRPLyOW3DrAZGFZl05o7FyYwX/Gwx9rT7UCvsn+nUJEPCiTQ+rHaTyjfG?= =?us-ascii?Q?5mAqE70YP0/r51O46aBQhW8Uge1p2wUmI5YTbquHKLru5a3XIqwdjyY3TLMQ?= =?us-ascii?Q?1eAh/vRcjPe3sHxjoB13skUTre9zi0w2OI+0cHSEbU4tXfqSnrJI05O2npvR?= =?us-ascii?Q?ytTmRQZQAq+R/6G05pqR6XAtiWz1dpRWbQ3q98veKRqGpxYhSNMAca/aZZZt?= =?us-ascii?Q?tQ=3D=3D?= X-MS-Exchange-CrossTenant-Network-Message-Id: 4cbce262-d2ca-4f55-c112-08dbcbea0582 X-MS-Exchange-CrossTenant-AuthSource: DS0PR11MB7309.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Oct 2023 12:43:38.0608 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: e0fbiZQHZObWgV48D3cFNPR5byzm3tmQ47jWOinj91p7+MhNOL6yha9UHxGG3fuAwo8cBOhIN3IEukWVS4GLwS/VTaGQoaouu9jkG+ou9sc= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN7PR11MB7139 X-OriginatorOrg: intel.com X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org On Fri, Oct 13, 2023 at 02:23:13PM +0200, Robin Jarry wrote: > Bruce Richardson, Oct 11, 2023 at 15:33: > > Provide a "dpdk-cmdline-gen.py" script for application developers to > > quickly generate the boilerplate code necessary for using the cmdline > > library. > > > > Example of use: > > The script takes an input file with a list of commands the user wants in > > the app, where the parameter variables are tagged with the type. > > For example: > > > > $ cat commands.list > > list > > add x y > > echo message > > add socket path > > quit > > > > When run through the script as "./dpdk-cmdline-gen.py commands.list", > > the output will be the contents of a header file with all the > > boilerplate necessary for a commandline instance with those commands. > > > > If the flag --stubs is passed, an output header filename must also be > > passed, in which case both a header file with the definitions and a C > > file with function stubs in it is written to disk. The separation is so > > that the header file can be rewritten at any future point to add more > > commands, while the C file can be kept as-is and extended by the user > > with any additional functions needed. > > > > Signed-off-by: Bruce Richardson > > --- > > Hi Bruce, > > this is a nice addition, I have a few python style remarks below. > > In general, I would advise formatting your code with black[1] to avoid > debates over coding style. It makes all code homogeneous and lets you focus > on more important things :) > Thanks for the feedback. I'll look into black. > > buildtools/dpdk-cmdline-gen.py | 167 ++++++++++++++++++++++++++++++ > > buildtools/meson.build | 7 ++ > > doc/guides/prog_guide/cmdline.rst | 131 ++++++++++++++++++++++- > > 3 files changed, 304 insertions(+), 1 deletion(-) > > create mode 100755 buildtools/dpdk-cmdline-gen.py > > > > diff --git a/buildtools/dpdk-cmdline-gen.py b/buildtools/dpdk-cmdline-gen.py > > new file mode 100755 > > index 0000000000..3b41fb0493 > > --- /dev/null > > +++ b/buildtools/dpdk-cmdline-gen.py > > @@ -0,0 +1,167 @@ > > +#!/usr/bin/env python3 > > +# SPDX-License-Identifier: BSD-3-Clause > > +# Copyright(c) 2023 Intel Corporation > > +# > > +"""Script to automatically generate boilerplate for using DPDK cmdline library.""" > > Multi line (or single line) doc strings are usually formatted as follows: > > """ > Script to automatically generate boilerplate for using DPDK cmdline library. > """ > > It makes adding new lines more readable and saves a bit of characters per > line. > Sure. > > + > > +import argparse > > +import sys > > + > > +PARSE_FN_PARAMS = 'void *parsed_result, struct cmdline *cl, void *data' > > +PARSE_FN_BODY = """ > > + /* TODO: command action */ > > + RTE_SET_USED(parsed_result); > > + RTE_SET_USED(cl); > > + RTE_SET_USED(data); > > +""" > > + > > + > > +def process_command(tokens, cfile, comment): > > + """Generate the structures and definitions for a single command.""" > > + name = [] > > + > > + if tokens[0].startswith('<'): > > + print('Error: each command must start with at least one literal string', file=sys.stderr) > > + sys.exit(1) > > It would be better to raise an exception here and handle it in main() for > error reporting. > I'll look into that. I probably should include the offending line/token in the error report too. > > + for t in tokens: > > + if t.startswith('<'): > > + break > > + name.append(t) > > + name = '_'.join(name) > > + > > + result_struct = [] > > + initializers = [] > > + token_list = [] > > + for t in tokens: > > + if t.startswith('<'): > > + t_type, t_name = t[1:].split('>') > > + t_val = 'NULL' > > + else: > > + t_type = 'STRING' > > + t_name = t > > + t_val = f'"{t}"' > > + > > + if t_type == 'STRING': > > + result_struct.append(f'\tcmdline_fixed_string_t {t_name};') > > + initializers.append( > > + f'static cmdline_parse_token_string_t cmd_{name}_{t_name}_tok =\n' + > > + f'\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});') > > + elif t_type in ['UINT8', 'UINT16', 'UINT32', 'UINT64', 'INT8', 'INT16', 'INT32', 'INT64']: > > + result_struct.append(f'\t{t_type.lower()}_t {t_name};') > > + initializers.append( > > + f'static cmdline_parse_token_num_t cmd_{name}_{t_name}_tok =\n' + > > + f'\tTOKEN_NUM_INITIALIZER(struct cmd_{name}_result, {t_name}, RTE_{t_type});') > > + elif t_type in ['IP', 'IP_ADDR', 'IPADDR']: > > + result_struct.append(f'\tcmdline_ipaddr_t {t_name};') > > + initializers.append( > > + f'cmdline_parse_token_ipaddr_t cmd_{name}_{t_name}_tok =\n' + > > + f'\tTOKEN_IPV4_INITIALIZER(struct cmd_{name}_result, {t_name});') > > + else: > > + print(f'Error: unknown token-type {t}', file=sys.stderr) > > + sys.exit(1) > > + token_list.append(f'cmd_{name}_{t_name}_tok') > > + > > + print(f'/* Auto-generated handling for command "{" ".join(tokens)}" */') > > + # output function prototype > > + func_sig = f'void\ncmd_{name}_parsed({PARSE_FN_PARAMS})' > > + print(f'extern {func_sig};\n') > > + # output function template if C file being written > > + if (cfile): > > + print(f'{func_sig}\n{{{PARSE_FN_BODY}}}\n', file=cfile) > > + # output result data structure > > + print( > > + f'struct cmd_{name}_result {{\n' + > > + '\n'.join(result_struct) + > > + '\n};\n') > > + # output the initializer tokens > > + print('\n'.join(initializers) + '\n') > > + # output the instance structure > > + print( > > + f'static cmdline_parse_inst_t cmd_{name} = {{\n' + > > + f'\t.f = cmd_{name}_parsed,\n' + > > + '\t.data = NULL,\n' + > > + f'\t.help_str = "{comment}",\n' + > > + '\t.tokens = {') > > + for t in token_list: > > + print(f'\t\t(void *)&{t},') > > + print('\t\tNULL\n' + '\t}\n' + '};\n') > > + > > + # return the instance structure name > > + return f'cmd_{name}' > > + > > + > > +def process_commands(infile, hfile, cfile, ctxname): > > + """Generate boilerplate output for a list of commands from infile.""" > > + instances = [] > > + > > + # redirect stdout to output the header, to save passing file= each print > > + old_sys_stdout = sys.stdout > > + sys.stdout = hfile > > Why not use hfile.write()? > > I think the main issue here is to use print() in process_commands(). It > would probably be cleaner to have process_command() return a list of lines > and print them in this function. > Good idea, let me look into that. Just using print was shorter than hfile.write, and I felt it would save me some line-breaking. > > + > > + print(f'/* File autogenerated by {sys.argv[0]} */') > > + print('#ifndef GENERATED_COMMANDS_H') > > + print('#define GENERATED_COMMANDS_H') > > + print('#include ') > > + print('#include ') > > + print('#include ') > > + print('#include ') > > + print('#include ') > > + print('') > > You can use a multi-line f-string here with a single print/write. > > hfile.write(f"""/* File autogenerated by {sys.argv[0]} */ > #ifndef GENERATED_COMMANDS_H > #define GENERATED_COMMANDS_H > > #include > #include > #include > #include > #include > > """) > Yes, I was aware of that. However, I deliberately didn't use it, because the indentation looks completely wrong with it (at least to my eyes). I really don't like having each line start at column zero in the middle of a block of code. > > + > > + for line in infile.readlines(): > > + if line.lstrip().startswith('#'): > > + continue > > + if '#' not in line: > > + line = line + '#' # ensure split always works, even if no help text > > + tokens, comment = line.split('#', 1) > > + instances.append(process_command(tokens.strip().split(), cfile, comment.strip())) > > If process_command returns a name and a list of lines, that could be > transformed as: > > name, lines = process_command(tokens.strip().split(), cfile, comment.strip()) > instances.append(name) > hfile.write("\n".join(lines) + "\n") > Will investigate > > + > > + print(f'static __rte_used cmdline_parse_ctx_t {ctxname}[] = {{') > > + for inst in instances: > > + print(f'\t&{inst},') > > + print('\tNULL') > > + print('};\n') > > + print('#endif /* GENERATED_COMMANDS_H */') > > also multi line print here: > > hfile.write("""\tNULL > }; > #endif /* GENERATED_COMMANDS_H */ > """) > As above, I didn't like the non-indented nature of it. > > + > > + sys.stdout = old_sys_stdout > > + > > + > > +def main(): > > + """Application main entry point.""" > > + ap = argparse.ArgumentParser() > > Nit to have a nice description of the command with --help: > > ap = argparse.ArgumentParser(description=__doc__) > > > + ap.add_argument( > > + '--stubs', action='store_true', > > + help='Produce C file with empty function stubs for each command') > > + ap.add_argument( > > + '--output-file', '-o', default='-', > > + help='Output header filename [default to stdout]') > > + ap.add_argument( > > + '--context-name', default='ctx', > > + help='Name given to the cmdline context variable in the output header [default=ctx]') > > + ap.add_argument( > > + 'infile', type=argparse.FileType('r'), > > + help='File with list of commands') > > + args = ap.parse_args() > > + > > + if not args.stubs: > > + if args.output_file == '-': > > + process_commands(args.infile, sys.stdout, None, args.context_name) > > + else: > > + with open(args.output_file, 'w') as hfile: > > + process_commands(args.infile, hfile, None, args.context_name) > > + else: > > + if not args.output_file.endswith('.h'): > > + print( > > + 'Error: output filename must end with ".h" extension when creating stubs', > > + file=sys.stderr) > > + sys.exit(1) > > You can replace print to stderr + exit with: > > ap.error("-o/--output-file: must end with .h extension when creating stubs") > Ack. > > + > > + cfilename = args.output_file[:-2] + '.c' > > + with open(args.output_file, 'w') as hfile: > > + with open(cfilename, 'w') as cfile: > > + print(f'#include "{args.output_file}"\n', file=cfile) > > + process_commands(args.infile, hfile, cfile, args.context_name) > > + > > + > > +if __name__ == '__main__': > > + main() > > I'll stop here ;) Thanks! >