[{"id":35776,"web_url":"https://patchwork.libcamera.org/comment/35776/","msgid":"<175758846810.2127323.12625227322574382135@neptunite.rasen.tech>","date":"2025-09-11T11:01:08","subject":"Re: [PATCH v17 03/12] config: Add configuration retrieval helpers","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Milan,\n\nThanks for the patch.\n\nQuoting Milan Zamazal (2025-09-11 18:29:33)\n> Let's add some helpers to make accessing simple configuration values\n> simpler.  The helpers are used in the followup patches.\n> \n> GlobalConfiguration::option ensures that no value is returned rather\n> than a value of YamlObject::empty.\n> \n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n\nLooks good to me.\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> ---\n>  .../libcamera/internal/global_configuration.h | 28 ++++++\n>  src/libcamera/global_configuration.cpp        | 99 +++++++++++++++++++\n>  2 files changed, 127 insertions(+)\n> \n> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n> index f695498c4..8d09517ed 100644\n> --- a/include/libcamera/internal/global_configuration.h\n> +++ b/include/libcamera/internal/global_configuration.h\n> @@ -8,6 +8,11 @@\n>  #pragma once\n>  \n>  #include <filesystem>\n> +#include <optional>\n> +#include <string>\n> +#include <string_view>\n> +\n> +#include <libcamera/base/utils.h>\n>  \n>  #include \"libcamera/internal/yaml_parser.h\"\n>  \n> @@ -23,6 +28,29 @@ public:\n>         unsigned int version() const;\n>         Configuration configuration() const;\n>  \n> +       template<typename T>\n> +       std::optional<T> option(\n> +               const std::initializer_list<std::string_view> confPath) const\n> +       {\n> +               const YamlObject *c = &configuration();\n> +               for (auto part : confPath) {\n> +                       c = &(*c)[part];\n> +                       if (!*c)\n> +                               return {};\n> +               }\n> +               return c->get<T>();\n> +       }\n> +\n> +       std::optional<std::vector<std::string>> listOption(\n> +               const std::initializer_list<std::string_view> confPath) const;\n> +       std::optional<std::string> envOption(\n> +               const char *const envVariable,\n> +               const std::initializer_list<std::string_view> confPath) const;\n> +       std::optional<std::vector<std::string>> envListOption(\n> +               const char *const envVariable,\n> +               const std::initializer_list<std::string_view> confPath,\n> +               const std::string delimiter = \":\") const;\n> +\n>  private:\n>         bool loadFile(const std::filesystem::path &fileName);\n>         void load();\n> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n> index d02668111..592edcf30 100644\n> --- a/src/libcamera/global_configuration.cpp\n> +++ b/src/libcamera/global_configuration.cpp\n> @@ -8,8 +8,12 @@\n>  #include \"libcamera/internal/global_configuration.h\"\n>  \n>  #include <filesystem>\n> +#include <memory>\n> +#include <optional>\n> +#include <string>\n>  #include <string_view>\n>  #include <sys/types.h>\n> +#include <vector>\n>  \n>  #include <libcamera/base/file.h>\n>  #include <libcamera/base/log.h>\n> @@ -43,6 +47,12 @@ LOG_DEFINE_CATEGORY(Configuration)\n>   * If the first found configuration file cannot be opened or parsed, an error is\n>   * reported and no configuration file is used. This is to prevent libcamera from\n>   * using an unintended configuration file.\n> + *\n> + * The configuration can be accessed using the provided helpers. Namely\n> + * GlobalConfiguration::option(), GlobalConfiguration::envOption(),\n> + * GlobalConfiguration::listOption(), and GlobalConfiguration::envListOption()\n> + * to access individual options, or GlobalConfiguration::configuration() to\n> + * access the whole configuration.\n>   */\n>  \n>  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n> @@ -110,6 +120,95 @@ GlobalConfiguration::GlobalConfiguration()\n>   * the underlying type.\n>   */\n>  \n> +/**\n> + * \\fn std::optional<T> GlobalConfiguration::option(const std::initializer_list<std::string_view> &confPath) const\n> + * \\brief Return value of the configuration option identified by \\a confPath\n> + * \\param[in] confPath Sequence of the YAML section names (excluding\n> + * `configuration') leading to the requested option\n> + * \\return The value of the configuration item corresponding to \\a confPath if\n> + * it exists in the configuration file, or no value otherwise\n> + */\n> +\n> +/**\n> + * \\brief Return values of the configuration option identified by \\a confPath\n> + * \\tparam T The type of the retrieved configuration value\n> + * \\param[in] confPath Sequence of the YAML section names (excluding\n> + * `configuration') leading to the requested list option, separated by dots\n> + * \\return A vector of strings or no value if not found\n> + */\n> +std::optional<std::vector<std::string>> GlobalConfiguration::listOption(\n> +       const std::initializer_list<std::string_view> confPath) const\n> +{\n> +       const YamlObject *c = &configuration();\n> +       for (auto part : confPath) {\n> +               c = &(*c)[part];\n> +               if (!*c)\n> +                       return {};\n> +       }\n> +       return c->getList<std::string>();\n> +}\n> +\n> +/**\n> + * \\brief Return value of environment variable with a fallback on the configuration file\n> + * \\param[in] envVariable Environment variable to get the value from\n> + * \\param[in] confPath The sequence of YAML section names to fall back on when\n> + * \\a envVariable is unavailable\n> + *\n> + * This helper looks first at the given environment variable and if it is\n> + * defined then it returns its value (even if it is empty). Otherwise it looks\n> + * for \\a confPath the same way as in GlobalConfiguration::option. Only string\n> + * values are supported.\n> + *\n> + * \\note Support for using environment variables to configure libcamera behavior\n> + * is provided here mostly for backward compatibility reasons. Introducing new\n> + * configuration environment variables is discouraged.\n> + *\n> + * \\return The value retrieved from the given environment if it is set,\n> + * otherwise the value from the configuration file if it exists, or no value if\n> + * it does not\n> + */\n> +std::optional<std::string> GlobalConfiguration::envOption(\n> +       const char *envVariable,\n> +       const std::initializer_list<std::string_view> confPath) const\n> +{\n> +       const char *envValue = utils::secure_getenv(envVariable);\n> +       if (envValue)\n> +               return std::optional{ std::string{ envValue } };\n> +       return option<std::string>(confPath);\n> +}\n> +\n> +/**\n> + * \\brief Return values of the configuration option from a file or environment\n> + * \\param[in] envVariable Environment variable to get the value from\n> + * \\param[in] confPath The same as in GlobalConfiguration::option\n> + * \\param[in] delimiter Items separator in the environment variable\n> + *\n> + * This helper looks first at the given environment variable and if it is\n> + * defined (even if it is empty) then it splits its value by semicolons and\n> + * returns the resulting list of strings. Otherwise it looks for \\a confPath the\n> + * same way as in GlobalConfiguration::option, value of which must be a list of\n> + * strings.\n> + *\n> + * \\note Support for using environment variables to configure libcamera behavior\n> + * is provided here mostly for backward compatibility reasons. Introducing new\n> + * configuration environment variables is discouraged.\n> + *\n> + * \\return A vector of strings retrieved from the given environment option or\n> + * configuration file or no value if not found; the vector may be empty\n> + */\n> +std::optional<std::vector<std::string>> GlobalConfiguration::envListOption(\n> +       const char *const envVariable,\n> +       const std::initializer_list<std::string_view> confPath,\n> +       const std::string delimiter) const\n> +{\n> +       const char *envValue = utils::secure_getenv(envVariable);\n> +       if (envValue) {\n> +               auto items = utils::split(envValue, delimiter);\n> +               return std::vector<std::string>(items.begin(), items.end());\n> +       }\n> +       return listOption(confPath);\n> +}\n> +\n>  /**\n>   * \\brief Return configuration version\n>   *\n> -- \n> 2.51.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id C620BC324E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 11 Sep 2025 11:01:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 951326936E;\n\tThu, 11 Sep 2025 13:01:18 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5193B69339\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 11 Sep 2025 13:01:17 +0200 (CEST)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:5e49:1615:2276:f31c])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 4662782E;\n\tThu, 11 Sep 2025 13:00:02 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"OxsnQU+m\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1757588402;\n\tbh=G6Fv5/m0llyG59xx3EkwARNruCD2GAtnJkIEAR1XewE=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=OxsnQU+mtJL7r27fbG3WlFH7zMDO9UXYWMpILBcxXuYdAQcahhT6Xty1lmkVJXmlP\n\tWbD27X2CUMGGw4MWLmcMRDHyoT1HaI9y4HyCK23cITwvtFRkFYacS85XTpeQpU1mw2\n\tsiLVS4m79P+8SixHqrhPV3hF2KlWwyEn8ucxiwY8=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20250911092945.16517-4-mzamazal@redhat.com>","References":"<20250911092945.16517-1-mzamazal@redhat.com>\n\t<20250911092945.16517-4-mzamazal@redhat.com>","Subject":"Re: [PATCH v17 03/12] config: Add configuration retrieval helpers","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"Milan Zamazal <mzamazal@redhat.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<barnabas.pocze@ideasonboard.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>","To":"Milan Zamazal <mzamazal@redhat.com>, libcamera-devel@lists.libcamera.org","Date":"Thu, 11 Sep 2025 20:01:08 +0900","Message-ID":"<175758846810.2127323.12625227322574382135@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]