From patchwork Tue Nov 21 12:26:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 134517 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id C01744338E; Tue, 21 Nov 2023 13:29:47 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id AC71042EA6; Tue, 21 Nov 2023 13:29:47 +0100 (CET) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id 52E8B42E9D for ; Tue, 21 Nov 2023 13:29:45 +0100 (CET) Received: from dggpeml100024.china.huawei.com (unknown [172.30.72.53]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4SZNwc4lQgzvR1n; Tue, 21 Nov 2023 20:29:20 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml100024.china.huawei.com (7.185.36.115) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Tue, 21 Nov 2023 20:29:43 +0800 From: Chengwen Feng To: , , Subject: [24.03 RFC] argparse: add argparse library Date: Tue, 21 Nov 2023 12:26:51 +0000 Message-ID: <20231121122651.7078-1-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml100024.china.huawei.com (7.185.36.115) X-CFilter-Loop: Reflected 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 Introduce argparse library (which was inspired by the thread [1]), compared with getopt, the argparse has following advantages: 1) Set the help information when defining parameters. 2) Support positional parameters. The parameters parsing according following: 1) positional: use callback to parse (passed the long-name as the key for callback). 2) optional: In addition to callback to parse, but also support: 2.1) no-val: support set default value to saver. 2.2) has-val: support set value to saver, the value must be conform RTE_ARGPARSE_ARG_VAL_xxx. 2.3) opt-val: if current without value then treat as no-val, else could treat as has-val. Examples: (take dmafwd as example): 1) If parse with callback: static int func(const char *key, const char *value, const char *opaque) { if (!strcmp("--mac-updating", key)) { mac_updating = 1; } else if (!strcmp("--no-mac-updating", key)) { mac_updating = 0; } else if (!strcmp("--portmask", key)) { dma_enabled_port_mask = dma_parse_portmask(optarg); if (dma_enabled_port_mask & ~default_port_mask || dma_enabled_port_mask <= 0) { ... } } else { ... } } static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { static struct rte_argparse opts[] = { .prog = "dma", .usage = NULL, .descriptor = "dma and nic fwd example", .epilog = NULL, .exit_on_error = true, .opt = { { "--mac-updating", NULL, "Enable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, { "--no-mac-updating", NULL, "disable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, { "--portmask", "-p", "hexadecimal bitmask of ports to configure", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} } }; return rte_argparse_parse(opts, argc, argv); } 2) If parse with value: static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { static struct rte_argparse opts[] = { .prog = "dma", .usage = NULL, .descriptor = "dma and nic fwd example", .epilog = NULL, .exit_on_error = true, .opt = { { "--mac-updating", NULL, "Enable MAC addresses updating", NULL, 0, &mac_updating, (void *)1, RTE_ARGPARSE_ARG_NO_VAL }, { "--no-mac-updating", NULL, "disable MAC addresses updating", NULL, 0, &mac_updating, (void *)0, RTE_ARGPARSE_ARG_NO_VAL }, { "--portmask", "-p", "hexadecimal bitmask of ports to configure", NULL, 0, &dma_enabled_port_mask, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} } }; int ret; ret = rte_argparse_parse(opts, argc, argv); if (ret != 0) return ret; if (dma_enabled_port_mask & ~default_port_mask || dma_enabled_port_mask <= 0) { ... } } 3) Also could mix parse with func and with value. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng --- lib/argparse/meson.build | 6 ++ lib/argparse/rte_argparse.h | 139 ++++++++++++++++++++++++++++++++++++ lib/argparse/version.map | 7 ++ lib/meson.build | 1 + 4 files changed, 153 insertions(+) create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..ac4a883b8d --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited. + +headers = files('rte_argparse.h') + +deps += ['kvargs'] diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..21157a9436 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * This API provides argparse function. + * + * Compare with getopt, the argparse has following advantages: + * 1) Set the help information when defining parameters. + * 2) Support positional parameters. + * + * The parameters parsing according following: + * 1) positional: use callback to parse (passed the long-name as the key for + * callback). + * 2) optional: + * In addition to callback to parse, but also support: + * 2.1) no-val: support set default value to saver. + * 2.2) has-val: support set value to saver, the value must be conform + * RTE_ARGPARSE_ARG_VAL_xxx. + * 2.3) opt-val: if current without value then treat as no-val, else could + * treat as has-val. + * + */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum rte_argparse_arg_flags { + /** + * Bit0-1 represent whether has value + */ + RTE_ARGPARSE_ARG_NO_VAL = 1u << 0, /**< The arg has no value. */ + RTE_ARGPARSE_ARG_HAS_VAL = 2u << 0, /**< The arg has value. */ + RTE_ARGPARSE_ARG_OPT_VAL = 3u << 0, /**< The arg has optional value. */ + + /** + * Bit2-4 represent the value type + */ + RTE_ARGPARSE_ARG_VAL_INT = 1u << 2, /**< The arg's value is int type. */ + RTE_ARGPARSE_ARG_VAL_FLOAT = 2u << 2, /**< The arg's value is float type. */ + RTE_ARGPARSE_ARG_VAL_BOOL = 3u << 2, /**< The arg's value is bool type. */ + RTE_ARGPARSE_ARG_VAL_STRING = 4u << 2, /**< The arg's value is string type. */ +}; + +/** + * A structure used to hold opt config. + */ +struct rte_argparse_arg { + /** + * The long name of arg: + * 1) If the arg is optional, it must start with '--', + * 2) If it is a positional arg, it must not start with '-'. + * Note: only one '-' will treat as error. + */ + const char *long_name; + /** + * The short name of arg: + * 1) This field could be set only if long_name is optional, and must + * start with only one '-' and one letter, + * 2) Other case it should be set NULL. + */ + const char *short_name; + /** The help info of arg. */ + const char *help; + + /* + * Parse the arg's callback, it will be used to parse the arg if + * it is not NULL. + */ + arg_handler_t callback; + /** The opaque which used to invoke callback */ + void *opaque; + + /* + * The saver for the arg. If not NULL, set value to default_val or + * parse from input. + * Note: the flags of the arg must be RTE_ARGPARSE_VAL_* if this value + * is not NULL. + */ + void *saver; + /* + * Default value for the arg, cover following case: + * 1) The arg don't require value, the saver will set to default_val + * when option found. + * 2) The arg has option value but don't take value this time, the + * saver will set to default_val when option found. + */ + void *default_val; + + /** @see rte_argparse_arg_flags. */ + uint32_t flags; +}; + +/** + * A structure used to hold argparse basic info. + */ +struct rte_argparse { + const char *prog; /**< Program name */ + const char *usage; /**< How to use the program */ + const char *descriptor; /**< Explain what the program does */ + const char *epilog; /**< Text at the bottom of help */ + bool exit_on_error; /**< Whether exit when error */ + struct rte_argparse_arg opt[]; /**< */ +}; + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters + * + * @param self + * Parser handler. + * @param argc + * Parameters count + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *self, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..36b0902167 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring',