From patchwork Fri Jan 26 06:10:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 136174 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 20951439CC; Fri, 26 Jan 2024 07:15:01 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 8014D42E3A; Fri, 26 Jan 2024 07:14:17 +0100 (CET) Received: from szxga03-in.huawei.com (szxga03-in.huawei.com [45.249.212.189]) by mails.dpdk.org (Postfix) with ESMTP id 4E6D44113C for ; Fri, 26 Jan 2024 07:14:06 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.88.105]) by szxga03-in.huawei.com (SkyGuard) with ESMTP id 4TLnS62nwPzNlcB; Fri, 26 Jan 2024 14:13:10 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 36B661402CD; Fri, 26 Jan 2024 14:14:05 +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; Fri, 26 Jan 2024 14:14:04 +0800 From: Chengwen Feng To: , , , CC: Subject: [PATCH v3 2/8] argparse: add argparse library Date: Fri, 26 Jan 2024 06:10:07 +0000 Message-ID: <20240126061013.53608-3-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240126061013.53608-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20240126061013.53608-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) 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 --- MAINTAINERS | 4 + doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/prog_guide/argparse_lib.rst | 185 ++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_24_03.rst | 5 + lib/argparse/meson.build | 7 + lib/argparse/rte_argparse.c | 14 ++ lib/argparse/rte_argparse.h | 190 +++++++++++++++++++++++++ lib/argparse/version.map | 7 + lib/meson.build | 1 + 11 files changed, 416 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/MAINTAINERS b/MAINTAINERS index 0d1c8126e3..09fdb87a25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1650,6 +1650,10 @@ F: doc/guides/sample_app_ug/qos_metering.rst Other libraries --------------- +Argument parsing +M: Chengwen Feng +F: lib/argparse/ + Configuration file M: Cristian Dumitrescu F: lib/cfgfile/ 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..00d4860ca1 --- /dev/null +++ b/doc/guides/prog_guide/argparse_lib.rst @@ -0,0 +1,185 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2024 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 ways: 1) autosave: used for parsing known value + types; 2) callback: will invoke user callback to parse. + +Usage Guide +----------- + +The following code demonstrates how to use: + +.. code-block:: C + + static int + argparse_user_callback(uint32_t index, const char *value, void *opaque) + { + if (index == 1) { + /* process "--ddd" argument, because it is configured as no-value, + * the parameter 'value' is NULL. + */ + ... + } else if (index == 2) { + /* process "--eee" argument, because it is configured as + * required-value, the parameter 'value' must not be NULL. + */ + ... + } else if (index == 3) { + /* process "--fff" argument, because it is configured as + * 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 be NULL. + */ + ... + } else { + return -EINVAL; + } + } + + static 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", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--bbb", "-b", "bbb argument", &bbb_val, NULL, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, + { "--ccc", "-c", "ccc argument", &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", &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); + ... + } + +In this example, the arguments which start with a hyphen (-) are optional +arguments (they're "--aaa"/"--bbb"/"--ccc"/"--ddd"/"--eee"/"--fff"); and the +arguments which don't start with a hyper (-) are positional arguments (they're +"ooo"/"ppp"). + +Every argument must be set whether to carry a value (one of +``RTE_ARGPARSE_ARG_NO_VALUE``, ``RTE_ARGPARSE_ARG_REQUIRED_VALUE`` and +``RTE_ARGPARSE_ARG_OPTIONAL_VALUE``). + +.. note:: + + Positional argument much be set ``RTE_ARGPARSE_ARG_REQUIRED_VALUE``. + +User Input Requirements +~~~~~~~~~~~~~~~~~~~~~~~ + +For optional arguments which take no-value, the following mode is supported +(take above "--aaa" as an example): + +- The single mode: "--aaa" or "-a". + +For optional arguments which take required-value, the following two modes are +supported (take above "--bbb" as an example): + +- The kv mode: "--bbb=1234" or "-b=1234". + +- The split mode: "--bbb 1234" or "-b 1234". + +For optional arguments which take optional-value, the following two modes are +supported (take above "--ccc" as an example): + +- The single mode: "--ccc" or "-c". + +- The kv mode: "--ccc=123" or "-c=123". + +For positional arguments which must take required-value, their values are +parsing in the order defined. + +.. note:: + + The compact mode is not supported. Take above "-a" and "-d" as an example, + don't support "-ad" input. + +Parsing by autosave way +~~~~~~~~~~~~~~~~~~~~~~~ + +Argument of known value type (e.g. ``RTE_ARGPARSE_ARG_VALUE_INT``) could be +parsed using this autosave way, and its result will save in the ``val_saver`` +field. + +In the above example, the arguments "--aaa"/"--bbb"/"--ccc" and "ooo" both use +this way, the parsing is as follows: + +- For argument "--aaa", it is configured as no-value, so the ``aaa_val`` will + be set to ``val_set`` field which is 100 in the above example. + +- For argument "--bbb", it is configured as required-value, so the ``bbb_val`` + will be set to user input's value (e.g. will be set to 1234 with input + "--bbb 1234"). + +- For argument "--ccc", it is configured as optional-value, if user only input + "--ccc" then the ``ccc_val`` will be set to ``val_set`` field which is 200 in + the above example; if user input "--ccc=123", then the ``ccc_val`` will be set + to 123. + +- For argument "ooo", it is positional argument, the ``ooo_val`` will be set + to user input's value. + +Parsing by callback way +~~~~~~~~~~~~~~~~~~~~~~~ + +It could also choose to use callback to parse, just define a unique index for +the argument and make the ``val_save`` field to be NULL also zero value-type. + +In the above example, the arguments "--ddd"/"--eee"/"--fff" and "ppp" both use +this way. + +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 the ``flags`` field. For +example: + +.. code-block:: C + + ... + { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI }, + ... + +Then the user input could contain multiple "--xyz" arguments. + +.. note:: + + The multiple times argument only support with optional argument and must be + parsed by callback way. 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/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst index 6f8ad27808..724b7b673e 100644 --- a/doc/guides/rel_notes/release_24_03.rst +++ b/doc/guides/rel_notes/release_24_03.rst @@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **Introduce argument parse library.** + + Introduce argparse library, compared with getopt, it makes it easy to write + user-friendly command-like program. + Removed Items ------------- diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..b6a08ca049 --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 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..3471c5e757 --- /dev/null +++ b/lib/argparse/rte_argparse.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 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..8285e812f0 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 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 +#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. + */ + + /** The argument has no value. */ + RTE_ARGPARSE_ARG_NO_VALUE = RTE_SHIFT_VAL64(1, 0), + /** The argument must have a value. */ + RTE_ARGPARSE_ARG_REQUIRED_VALUE = RTE_SHIFT_VAL64(2, 0), + /** The argument has optional value. */ + RTE_ARGPARSE_ARG_OPTIONAL_VALUE = RTE_SHIFT_VAL64(3, 0), + + + /* + * Bit2-9: represent the value type which used when autosave + */ + + /** The argument's value is int type. */ + RTE_ARGPARSE_ARG_VALUE_INT = RTE_SHIFT_VAL64(1, 2), + /** Max value type. */ + RTE_ARGPARSE_ARG_VALUE_MAX = RTE_SHIFT_VAL64(2, 2), + + + /** + * Bit10: 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 = RTE_BIT64(10), + + /** + * Bit48-63: reserved for this library implement usage. + */ + RTE_ARGPARSE_ARG_RESERVED_FIELD = RTE_GENMASK64(63, 48), +}; + +/** + * 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 be set to val_set. + * 2) If argument has optional value but doesn't take value this + * time, *val_saver will be set to val_set. + * 3) Other case it should be set NULL. + */ + void *val_set; + + /** @see rte_argparse_flag */ + uint64_t flags; +}; + +/** + * Callback prototype used by parsing specified arguments. + * + * @param index + * The argument's index, coming from argument's val_set field. + * @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; + /** Reserved field used for future extension. */ + void *reserved[16]; + /** 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',