From patchwork Mon Dec 4 07:50:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 134786 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 AB8284365D; Mon, 4 Dec 2023 08:54:35 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 18EA940DCA; Mon, 4 Dec 2023 08:54:06 +0100 (CET) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id 3B2D24067D for ; Mon, 4 Dec 2023 08:53:59 +0100 (CET) Received: from dggpeml500024.china.huawei.com (unknown [172.30.72.54]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4SkGB66Pl5zvRWN; Mon, 4 Dec 2023 15:53:18 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Mon, 4 Dec 2023 15:53:55 +0800 From: Chengwen Feng To: , , , CC: Subject: [RFC v2 1/6] argparse: add argparse library Date: Mon, 4 Dec 2023 07:50:43 +0000 Message-ID: <20231204075048.894-2-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20231204075048.894-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20231204075048.894-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems701-chm.china.huawei.com (10.3.19.178) To dggpeml500024.china.huawei.com (7.185.36.10) 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]). This commit provides public API and doc. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng --- doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 142 ++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 179 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 9 files changed, 353 insertions(+) create mode 100644 doc/guides/prog_guide/argparse_lib.rst create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.c create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a6a768bd7c..fe41fba6ec 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -220,6 +220,7 @@ The public API headers are grouped by topics: [random](@ref rte_random.h), [config file](@ref rte_cfgfile.h), [key/value args](@ref rte_kvargs.h), + [argument parse](@ref rte_argparse.h), [string](@ref rte_string_fns.h), [thread](@ref rte_thread.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index e94c9e4e46..76f89afe71 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -28,6 +28,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/eal/include \ @TOPDIR@/lib/eal/include/generic \ @TOPDIR@/lib/acl \ + @TOPDIR@/lib/argparse \ @TOPDIR@/lib/bbdev \ @TOPDIR@/lib/bitratestats \ @TOPDIR@/lib/bpf \ diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst new file mode 100644 index 0000000000..d9813cbeff --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,142 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright 2023 HiSilicon Limited + +Argparse Library +================ + +The argparse library provides argument parse functionality, this library makes +it easy to write user-friendly command-line program. + +Features and Capabilities +------------------------- + +- Support parse optional argument (which could take with no-value, + required-value and optional-value). + +- Support parse positional argument (which must take with required-value). + +- Support automatic generate usage information. + +- Support issue errors when provide with invalid arguments. + +- Support parse argument by two way: 1) autosave: for which known value types, + this way can be used; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to initialize: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it has no-value, the parameter value is NULL. */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it has required-value, the parameter value must not NULL. */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it has optional-value, the parameter value maybe NULL or not NULL, depend on input. */ + ... + } else if (index == 300) { + /* process "ppp" argument, because it's a positional argument, the parameter value must not NULL. */ + ... + } else { + return -EINVAL; + } + } + + int aaa_val, bbb_val, ccc_val, ooo_val; + + static struct rte_argparse obj = { + .prog_name = "test-demo", + .usage = "[EAL options] -- [optional parameters] [positional parameters]", + .descriptor = NULL, + .epilog = NULL, + .exit_on_error = true, + .callback = argparse_user_callback, + .args = { + { "--aaa", "-a", "aaa argument", (void *)&aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", (void *)&bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", (void *)&ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ddd", "-d", "ddd argument", NULL, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE }, + { "--eee", "-e", "eee argument", NULL, (void *)2, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + { "--fff", "-f", "fff argument", NULL, (void *)3, RTE_ARGPARSE_ARG_OPTIONAL_VALUE }, + { "ooo", NULL, "ooo argument", (void *)&ooo_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "ppp", NULL, "ppp argument", NULL, (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE }, + }, + }; + + int + main(int argc, char **argv) + { + ... + ret = rte_argparse_parse(&obj, argc, argv); + ... + } + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +For which known value types (just like ``RTE_ARGPARSE_ARG_VALUE_INT``"), could +parse by autosave way, just like above "--aaa"/"--bbb"/"--ccc" optional +arguments: + +If the user input parameter are: "program --aaa --bbb 1234 --ccc=20 ...", then +the aaa_val will equal 100, the bbb_val will equal 1234 and the ccc_val will +equal 20. + +If the user input parameter are: "program --ccc ...", then the aaa_val and +bbb_val will not modify, and ccc_val will equal 200. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the field val_save to be NULL also zero value-type. Just +like above "--ddd"/"--eee"/"--fff" optional arguments: + +If the user input parameter are: "program --ddd --eee 2345 --fff=30 ...", the +function argparse_user_callback() will be invoke to parse the value. + +Positional arguments +~~~~~~~~~~~~~~~~~~~~ + +The positional arguments could not start with a hyphen (-). The above code show +that there are two positional arguments "ooo"/"ppp", it must be flags with +``RTE_ARGPARSE_ARG_REQUIRED_VALUE``, and it also could use autosave or callback +to parsing: + +If the user input parameter are: "program [optionals] 456 789", then the ooo_val +will equal 456, and ppp_val will equal 789. + +Multiple times argument +~~~~~~~~~~~~~~~~~~~~~~~ + +If want to support the ability to enter the same argument multiple times, then +should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in flags field. For examples: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input parameter could be: "program --xyz 123 --xyz 456 ...". + +It's important to note that the multiple times flag only support with optional +argument and must be parsing by callback way. + +Other Notes +~~~~~~~~~~~ + +For optional arguments, short-name can be defined or not defined. For arguments +that have required value, the following inputs are supported: +"program --bbb=123 --eee 456 ..." or "program -b=123 -e 456 ...". + +For arguments that have optional value, the following inputs are supported: +"program --ccc --fff=100 ..." or "program -c -f=100". + diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 94964357ff..d09d958e6c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -13,6 +13,7 @@ Programmer's Guide source_org env_abstraction_layer log_lib + argparse_lib cmdline service_cores trace_lib diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..14ea735fc0 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited. + +sources = files('rte_argparse.c') +headers = files('rte_argparse.h') + +deps += ['log'] diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c new file mode 100644 index 0000000000..bf14c56858 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include "rte_argparse.h" + +int +rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) +{ + (void)obj; + (void)argc; + (void)argv; + return 0; +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..45f7a58607 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * Argument parse API. + * + * The argument parse API makes it easy to write user-friendly command-line + * program. The program defines what arguments it requires, and the API + * will parse those arguments which described in [argc, argv]. + * + * The API provides following functions: + * 1) Support parse optional argument (which could take with no-value, + * required-value and optional-value. + * 2) Support parse positional argument (which must take with required-value). + * 3) Support automatic generate usage information. + * 4) Support issue errors when provided with invalid arguments. + * + * There are two ways to parse arguments: + * 1) AutoSave: for which known value types, the way can be used. + * 2) Callback: will invoke user callback to parse. + * + */ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Flag definition (in bitmask form) for an argument. + */ +enum rte_argparse_flag { + /** + * Bit0-1: represent the argument whether has value. + */ + RTE_ARGPARSE_ARG_NO_VALUE = 1u << 0, /**< The argument has no value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = 2u << 0, /**< The argument must have a value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = 3u << 0, /**< The argument has optional value. */ + + /** + * Bit2-10: represent the value type which used when autosave + */ + RTE_ARGPARSE_ARG_VALUE_INT = 1u << 2, /**< The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = 2u << 2, /**< Max value type. */ + + /** + * Bit16: flag for that argument support occur multiple times. + * This flag can be set only when the argument is optional. + * When this flag is set, the callback type must be used for parsing. + */ + RTE_ARGPARSE_ARG_SUPPORT_MULTI = 1u << 16, + + /** + * Bit30-31: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = 3u << 30, +}; + +/** + * A structure used to hold argument's configuration. + */ +struct rte_argparse_arg { + /** + * Long name of the argument: + * 1) If the argument is optional, it must start with '--'. + * 2) If the argument is positional, it must not start with '-'. + * 3) Other case will be considered as error. + */ + const char *name_long; + /** + * Short name of the argument: + * 1) This field could be set only when name_long is optional, and + * must start with a hyphen (-) followed by an English letter. + * 2) Other case it should be set NULL. + */ + const char *name_short; + + /** Help information of the argument, must not be NULL. */ + const char *help; + + /** + * Saver for the argument's value. + * 1) If the filed is NULL, the callback way is used for parsing + * argument. + * 2) If the field is not NULL, the autosave way is used for parsing + * argument. + */ + void *val_saver; + /** + * If val_saver is NULL, this filed (cast as (uint32_t)val_set) will be + * used as the first parameter to invoke callback. + * + * If val_saver is not NULL, then: + * 1) If argument has no value, val_saver will set to val_set when + * argument found. + * 2) If argument has optional value but doesn't take value this + * time, val_saver will set to val_set when argument found. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint32_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set. + * @param value + * The value corresponding to the argument, it may be NULL (e.g. the + * argument has no value, or the argument has optional value but doesn't + * provided value). + * @param opaque + * An opaque pointer coming from the caller. + * @return + * 0 on success. Otherwise negative value is returned. + */ +typedef int (*arg_parser_t)(uint32_t index, const char *value, void *opaque); + +/** + * A structure used to hold argparse's configuration. + */ +struct rte_argparse { + /** Program name. Must not be NULL. */ + const char *prog_name; + /** How to use the program. Must not be NULL. */ + const char *usage; + /** Explain what the program does. Could be NULL. */ + const char *descriptor; + /** Text at the bottom of help. Could be NULL. */ + const char *epilog; + /** Whether exit when error. */ + bool exit_on_error; + /** User callback for parsing arguments. */ + arg_parser_t callback; + /** Opaque which used to invoke callback. */ + void *opaque; + /** Arguments configuration. Must ended with ARGPARSE_ARG_END(). */ + struct rte_argparse_arg args[]; +}; + +#define ARGPARSE_ARG_END() { NULL } + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters. + * + * @param obj + * Parser object. + * @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 *obj, 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..1c176f69e9 --- /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',