[{"id":38283,"web_url":"https://patchwork.libcamera.org/comment/38283/","msgid":"<177193144058.1513537.5909791852823881804@t16>","date":"2026-02-24T11:10:40","subject":"Re: [PATCH v6 1/8] libcamera: ipa_manager: Factor out .so file\n\tsearching","submitter":{"id":215,"url":"https://patchwork.libcamera.org/api/people/215/","name":"Isaac Scott","email":"isaac.scott@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch!\n\nQuoting Paul Elder (2026-01-29 08:28:07)\n> In the near future we will add support for layers/shoes on top of\n> libcamera, which will need to search for shared object files similar to\n> how IPAManager does. To avoid code duplication, move this code out of\n> IPAManager into internal utils.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> No change in v6\n> \n> No change in v5\n> \n> No change in v4\n> \n> Changes in v3:\n> - rename addDir to findSharedObjects\n> - make parseDir static\n> \n> No change in v2\n> ---\n>  include/libcamera/internal/ipa_manager.h |   4 -\n>  include/libcamera/internal/meson.build   |   1 +\n>  include/libcamera/internal/utils.h       |  23 +++++\n>  src/libcamera/ipa_manager.cpp            | 107 ++++-------------------\n>  src/libcamera/meson.build                |   1 +\n>  src/libcamera/utils.cpp                  | 107 +++++++++++++++++++++++\n>  6 files changed, 151 insertions(+), 92 deletions(-)\n>  create mode 100644 include/libcamera/internal/utils.h\n>  create mode 100644 src/libcamera/utils.cpp\n> \n> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\n> index f8ce780169e6..dcfcf8d69da5 100644\n> --- a/include/libcamera/internal/ipa_manager.h\n> +++ b/include/libcamera/internal/ipa_manager.h\n> @@ -68,10 +68,6 @@ public:\n>  #endif\n>  \n>  private:\n> -       void parseDir(const char *libDir, unsigned int maxDepth,\n> -                     std::vector<std::string> &files);\n> -       unsigned int addDir(const char *libDir, unsigned int maxDepth = 0);\n> -\n>         IPAModule *module(PipelineHandler *pipe, uint32_t minVersion,\n>                           uint32_t maxVersion);\n>  \n> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> index 4d2a09bd7041..ed145a6dd7cd 100644\n> --- a/include/libcamera/internal/meson.build\n> +++ b/include/libcamera/internal/meson.build\n> @@ -43,6 +43,7 @@ libcamera_internal_headers = files([\n>      'shared_mem_object.h',\n>      'source_paths.h',\n>      'sysfs.h',\n> +    'utils.h',\n>      'v4l2_device.h',\n>      'v4l2_pixelformat.h',\n>      'v4l2_request.h',\n> diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\n> new file mode 100644\n> index 000000000000..5b7957b5d77e\n> --- /dev/null\n> +++ b/include/libcamera/internal/utils.h\n> @@ -0,0 +1,23 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2018, Google Inc.\n> + *\n> + * Miscellaneous utility functions\n> + */\n> +\n> +#pragma once\n> +\n> +#include <functional>\n> +#include <string>\n> +#include <vector>\n> +\n> +namespace libcamera {\n> +\n> +namespace utils {\n> +\n> +unsigned int findSharedObjects(const char *libDir, unsigned int maxDepth,\n> +                              std::function<int(const std::string &)> func);\n> +\n> +} /* namespace utils */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp\n> index 35171d097136..244a8ee67f05 100644\n> --- a/src/libcamera/ipa_manager.cpp\n> +++ b/src/libcamera/ipa_manager.cpp\n> @@ -8,7 +8,7 @@\n>  #include \"libcamera/internal/ipa_manager.h\"\n>  \n>  #include <algorithm>\n> -#include <dirent.h>\n> +#include <functional>\n>  #include <string.h>\n>  #include <string>\n>  #include <sys/types.h>\n> @@ -22,6 +22,7 @@\n>  #include \"libcamera/internal/ipa_module.h\"\n>  #include \"libcamera/internal/ipa_proxy.h\"\n>  #include \"libcamera/internal/pipeline_handler.h\"\n> +#include \"libcamera/internal/utils.h\"\n>  \n>  /**\n>   * \\file ipa_manager.h\n> @@ -118,6 +119,20 @@ IPAManager::IPAManager(const GlobalConfiguration &configuration)\n>  \n>         unsigned int ipaCount = 0;\n>  \n> +       auto &modules = modules_;\n> +       std::function<int(const std::string &)> soHandler =\n> +       [&modules](const std::string &file) {\n> +               auto ipaModule = std::make_unique<IPAModule>(file);\n> +               if (!ipaModule->isValid())\n> +                       return 0;\n> +\n> +               LOG(IPAManager, Debug) << \"Loaded IPA module '\" << file << \"'\";\n> +\n> +               modules.push_back(std::move(ipaModule));\n> +\n> +               return 1;\n> +       };\n> +\n>         /* User-specified paths take precedence. */\n>         const auto modulePaths =\n>                 configuration.envListOption(\n> @@ -127,7 +142,7 @@ IPAManager::IPAManager(const GlobalConfiguration &configuration)\n>                 if (dir.empty())\n>                         continue;\n>  \n> -               ipaCount += addDir(dir.c_str());\n> +               ipaCount += utils::findSharedObjects(dir.c_str(), 0, soHandler);\n>         }\n>  \n>         if (!modulePaths.empty() && !ipaCount)\n> @@ -148,11 +163,11 @@ IPAManager::IPAManager(const GlobalConfiguration &configuration)\n>                         << \"libcamera is not installed. Adding '\"\n>                         << ipaBuildPath << \"' to the IPA search path\";\n>  \n> -               ipaCount += addDir(ipaBuildPath.c_str(), maxDepth);\n> +               ipaCount += utils::findSharedObjects(ipaBuildPath.c_str(), maxDepth, soHandler);\n>         }\n>  \n>         /* Finally try to load IPAs from the installed system path. */\n> -       ipaCount += addDir(IPA_MODULE_DIR);\n> +       ipaCount += utils::findSharedObjects(IPA_MODULE_DIR, 0, soHandler);\n>  \n>         if (!ipaCount)\n>                 LOG(IPAManager, Warning)\n> @@ -161,90 +176,6 @@ IPAManager::IPAManager(const GlobalConfiguration &configuration)\n>  \n>  IPAManager::~IPAManager() = default;\n>  \n> -/**\n> - * \\brief Identify shared library objects within a directory\n> - * \\param[in] libDir The directory to search for shared objects\n> - * \\param[in] maxDepth The maximum depth of sub-directories to parse\n> - * \\param[out] files A vector of paths to shared object library files\n> - *\n> - * Search a directory for .so files, allowing recursion down to sub-directories\n> - * no further than the depth specified by \\a maxDepth.\n> - *\n> - * Discovered shared objects are added to the \\a files vector.\n> - */\n> -void IPAManager::parseDir(const char *libDir, unsigned int maxDepth,\n> -                         std::vector<std::string> &files)\n> -{\n> -       struct dirent *ent;\n> -       DIR *dir;\n> -\n> -       dir = opendir(libDir);\n> -       if (!dir)\n> -               return;\n> -\n> -       while ((ent = readdir(dir)) != nullptr) {\n> -               if (ent->d_type == DT_DIR && maxDepth) {\n> -                       if (strcmp(ent->d_name, \".\") == 0 ||\n> -                           strcmp(ent->d_name, \"..\") == 0)\n> -                               continue;\n> -\n> -                       std::string subdir = std::string(libDir) + \"/\" + ent->d_name;\n> -\n> -                       /* Recursion is limited to maxDepth. */\n> -                       parseDir(subdir.c_str(), maxDepth - 1, files);\n> -\n> -                       continue;\n> -               }\n> -\n> -               int offset = strlen(ent->d_name) - 3;\n> -               if (offset < 0)\n> -                       continue;\n> -               if (strcmp(&ent->d_name[offset], \".so\"))\n> -                       continue;\n> -\n> -               files.push_back(std::string(libDir) + \"/\" + ent->d_name);\n> -       }\n> -\n> -       closedir(dir);\n> -}\n> -\n> -/**\n> - * \\brief Load IPA modules from a directory\n> - * \\param[in] libDir The directory to search for IPA modules\n> - * \\param[in] maxDepth The maximum depth of sub-directories to search\n> - *\n> - * This function tries to create an IPAModule instance for every shared object\n> - * found in \\a libDir, and skips invalid IPA modules.\n> - *\n> - * Sub-directories are searched up to a depth of \\a maxDepth. A \\a maxDepth\n> - * value of 0 only searches the directory specified in \\a libDir.\n> - *\n> - * \\return Number of modules loaded by this call\n> - */\n> -unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth)\n> -{\n> -       std::vector<std::string> files;\n> -\n> -       parseDir(libDir, maxDepth, files);\n> -\n> -       /* Ensure a stable ordering of modules. */\n> -       std::sort(files.begin(), files.end());\n> -\n> -       unsigned int count = 0;\n> -       for (const std::string &file : files) {\n> -               auto ipaModule = std::make_unique<IPAModule>(file);\n> -               if (!ipaModule->isValid())\n> -                       continue;\n> -\n> -               LOG(IPAManager, Debug) << \"Loaded IPA module '\" << file << \"'\";\n> -\n> -               modules_.push_back(std::move(ipaModule));\n> -               count++;\n> -       }\n> -\n> -       return count;\n> -}\n> -\n>  /**\n>   * \\brief Retrieve an IPA module that matches a given pipeline handler\n>   * \\param[in] pipe The pipeline handler\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index d15943586300..186d5c48ccd5 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -52,6 +52,7 @@ libcamera_internal_sources = files([\n>      'shared_mem_object.cpp',\n>      'source_paths.cpp',\n>      'sysfs.cpp',\n> +    'utils.cpp',\n>      'v4l2_device.cpp',\n>      'v4l2_pixelformat.cpp',\n>      'v4l2_request.cpp',\n> diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\n> new file mode 100644\n> index 000000000000..2ce62f236cea\n> --- /dev/null\n> +++ b/src/libcamera/utils.cpp\n> @@ -0,0 +1,107 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * Miscellaneous utility functions (internal)\n> + */\n> +\n> +#include \"libcamera/internal/utils.h\"\n> +\n> +#include <algorithm>\n> +#include <dirent.h>\n> +#include <functional>\n> +#include <string.h>\n> +#include <string>\n> +#include <sys/types.h>\n> +#include <vector>\n> +\n> +/**\n> + * \\file internal/utils.h\n> + * \\brief Miscellaneous utility functions (internal)\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace utils {\n> +\n> +/**\n> + * \\brief Identify shared library objects within a directory\n> + * \\param[in] libDir The directory to search for shared objects\n> + * \\param[in] maxDepth The maximum depth of sub-directories to parse\n> + * \\param[out] files A vector of paths to shared object library files\n> + *\n> + * Search a directory for .so files, allowing recursion down to sub-directories\n> + * no further than the depth specified by \\a maxDepth.\n> + *\n> + * Discovered shared objects are added to the \\a files vector.\n> + */\n> +static void parseDir(const char *libDir, unsigned int maxDepth,\n> +                    std::vector<std::string> &files)\n> +{\n> +       struct dirent *ent;\n> +       DIR *dir;\n> +\n> +       dir = opendir(libDir);\n> +       if (!dir)\n> +               return;\n> +\n> +       while ((ent = readdir(dir)) != nullptr) {\n> +               if (ent->d_type == DT_DIR && maxDepth) {\n> +                       if (strcmp(ent->d_name, \".\") == 0 ||\n> +                           strcmp(ent->d_name, \"..\") == 0)\n> +                               continue;\n> +\n> +                       std::string subdir = std::string(libDir) + \"/\" + ent->d_name;\n> +\n> +                       /* Recursion is limited to maxDepth. */\n> +                       parseDir(subdir.c_str(), maxDepth - 1, files);\n> +\n> +                       continue;\n> +               }\n> +\n> +               int offset = strlen(ent->d_name) - 3;\n> +               if (offset < 0)\n> +                       continue;\n> +               if (strcmp(&ent->d_name[offset], \".so\"))\n> +                       continue;\n> +\n> +               files.push_back(std::string(libDir) + \"/\" + ent->d_name);\n> +       }\n> +\n> +       closedir(dir);\n> +}\n\nI was a little confused at first as to where addDir has gone, but it\nseems you've baked it into findSharedObjects(), nice!\n\nReviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n\nBest wishes,\nIsaac\n\n> +\n> +/**\n> + * \\brief Execute some function on shared object files from a directory\n> + * \\param[in] libDir The directory to search for shared objects\n> + * \\param[in] maxDepth The maximum depth of sub-directories to search\n> + * \\param[in] func The function to execute on every shared object\n> + *\n> + * This function tries to execute the given function \\a func for every shared\n> + * object found in \\a libDir.\n> + *\n> + * Sub-directories are searched up to a depth of \\a maxDepth. A \\a maxDepth\n> + * value of 0 only searches the directory specified in \\a libDir.\n> + *\n> + * \\return Number of shared objects loaded by this call\n> + */\n> +unsigned int findSharedObjects(const char *libDir, unsigned int maxDepth,\n> +                              std::function<int(const std::string &)> func)\n> +{\n> +       std::vector<std::string> files;\n> +\n> +       parseDir(libDir, maxDepth, files);\n> +\n> +       /* Ensure a stable ordering of modules. */\n> +       std::sort(files.begin(), files.end());\n> +\n> +       unsigned int count = 0;\n> +       for (const std::string &file : files)\n> +               count += func(file);\n> +\n> +       return count;\n> +}\n> +\n> +} /* namespace utils */\n> +\n> +} /* namespace libcamera */\n> -- \n> 2.47.2\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 E3423C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 24 Feb 2026 11:10:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id ECCD062294;\n\tTue, 24 Feb 2026 12:10:45 +0100 (CET)","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 7C73162080\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 24 Feb 2026 12:10:44 +0100 (CET)","from thinkpad.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9170EAB4;\n\tTue, 24 Feb 2026 12:09:47 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"OyocBo62\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1771931387;\n\tbh=kikdDKr9gH3uUJa51Egy7W5VU9qVdySjISIYE1EvvOs=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=OyocBo62+i82LFXLyd3ZKscUUuQsjPNtFj1/+yuMBukB+0OHwUKEtcYAyT4cZgHq3\n\t9pUj+jmtLoJXYA0qAoAvbP4bAK1Q5utZzD7Ju9Vqay8WdBN7tS8+Vcydh0jL8kVDeQ\n\tFPI+/OtwNcNuKp3jIh7zFJ+obtHFhVYmh62ZvMA0=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260129082814.1777779-2-paul.elder@ideasonboard.com>","References":"<20260129082814.1777779-1-paul.elder@ideasonboard.com>\n\t<20260129082814.1777779-2-paul.elder@ideasonboard.com>","Subject":"Re: [PATCH v6 1/8] libcamera: ipa_manager: Factor out .so file\n\tsearching","From":"Isaac Scott <isaac.scott@ideasonboard.com>","Cc":"Paul Elder <paul.elder@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 24 Feb 2026 11:10:40 +0000","Message-ID":"<177193144058.1513537.5909791852823881804@t16>","User-Agent":"alot/0.10","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>"}}]