[v3,2/8] argparse: add argparse library

Message ID 20240126061013.53608-3-fengchengwen@huawei.com (mailing list archive)
State Accepted, archived
Delegated to: Thomas Monjalon
Headers
Series add argparse library |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

fengchengwen Jan. 26, 2024, 6:10 a.m. UTC
  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 <fengchengwen@huawei.com>
---
 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
  

Patch

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 <fengchengwen@huawei.com>
+F: lib/argparse/
+
 Configuration file
 M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 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 <stdbool.h>
+#include <stdint.h>
+
+#include <rte_bitops.h>
+#include <rte_compat.h>
+
+#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',