[{"id":30819,"web_url":"https://patchwork.libcamera.org/comment/30819/","msgid":"<3458d8d0-8fd3-4d6d-8f45-a0356500932b@ideasonboard.com>","date":"2024-08-14T22:34:25","subject":"Re: [PATCH 08/10] utils: codegen: gen-controls.py: Move helper\n\tclasses to separate file","submitter":{"id":156,"url":"https://patchwork.libcamera.org/api/people/156/","name":"Dan Scally","email":"dan.scally@ideasonboard.com"},"content":"Hi Laurent\n\nOn 09/08/2024 01:59, Laurent Pinchart wrote:\n> The ControlEnum and Control helper classes defined in gen-controls.py\n> are useful for other generator scripts. Move them to a separate file to\n> make it possible to share them.\n>\n> Extend the Python build environment to add the path to the new Python\n> module to PYTHONPATH\n\n\nThis doesn't happen in this patch...though given they're in the same directory the import works \nanyway, so it isn't causing harm. With the commit message corrected:\n\n\nReviewed-by: Daniel Scally <dan.scally@ideasonboard.com>\n\n> , and use it when invoking the gen-controls.py\n> script.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>   include/libcamera/meson.build |   1 +\n>   src/libcamera/meson.build     |   3 +-\n>   utils/codegen/controls.py     | 112 ++++++++++++++++++++++++++++++++++\n>   utils/codegen/gen-controls.py | 105 +------------------------------\n>   4 files changed, 116 insertions(+), 105 deletions(-)\n>   create mode 100644 utils/codegen/controls.py\n>\n> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> index d90a8615e52d..a969a95dbf7a 100644\n> --- a/include/libcamera/meson.build\n> +++ b/include/libcamera/meson.build\n> @@ -88,6 +88,7 @@ foreach mode, entry : controls_map\n>                                        command : [gen_controls, '-o', '@OUTPUT@',\n>                                                   '--mode', mode, '-t', template_file,\n>                                                   '-r', ranges_file, '@INPUT@'],\n> +                                     env : py_build_env,\n>                                        install : true,\n>                                        install_dir : libcamera_headers_install_dir)\n>   endforeach\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 3fd3a87e9f95..aa9ab0291854 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -151,7 +151,8 @@ foreach mode, inout_files : controls_mode_files\n>                                        output : output_file,\n>                                        command : [gen_controls, '-o', '@OUTPUT@',\n>                                                   '--mode', mode, '-t', template_file,\n> -                                                '-r', ranges_file, '@INPUT@'])\n> +                                                '-r', ranges_file, '@INPUT@'],\n> +                                     env : py_build_env)\n>   endforeach\n>   \n>   libcamera_public_sources += control_sources\n> diff --git a/utils/codegen/controls.py b/utils/codegen/controls.py\n> new file mode 100644\n> index 000000000000..7bafee599c80\n> --- /dev/null\n> +++ b/utils/codegen/controls.py\n> @@ -0,0 +1,112 @@\n> +#!/usr/bin/env python3\n> +# SPDX-License-Identifier: GPL-2.0-or-later\n> +# Copyright (C) 2019, Google Inc.\n> +#\n> +# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> +#\n> +# Helper classes to handle source code generation for libcamera controls\n> +\n> +\n> +class ControlEnum(object):\n> +    def __init__(self, data):\n> +        self.__data = data\n> +\n> +    @property\n> +    def description(self):\n> +        \"\"\"The enum description\"\"\"\n> +        return self.__data.get('description')\n> +\n> +    @property\n> +    def name(self):\n> +        \"\"\"The enum name\"\"\"\n> +        return self.__data.get('name')\n> +\n> +    @property\n> +    def value(self):\n> +        \"\"\"The enum value\"\"\"\n> +        return self.__data.get('value')\n> +\n> +\n> +class Control(object):\n> +    def __init__(self, name, data, vendor):\n> +        self.__name = name\n> +        self.__data = data\n> +        self.__enum_values = None\n> +        self.__size = None\n> +        self.__vendor = vendor\n> +\n> +        enum_values = data.get('enum')\n> +        if enum_values is not None:\n> +            self.__enum_values = [ControlEnum(enum) for enum in enum_values]\n> +\n> +        size = self.__data.get('size')\n> +        if size is not None:\n> +            if len(size) == 0:\n> +                raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension')\n> +\n> +            # Compute the total number of elements in the array. If any of the\n> +            # array dimension is a string, the array is variable-sized.\n> +            num_elems = 1\n> +            for dim in size:\n> +                if type(dim) is str:\n> +                    num_elems = 0\n> +                    break\n> +\n> +                dim = int(dim)\n> +                if dim <= 0:\n> +                    raise RuntimeError(f'Control `{self.__name}` size must have positive values only')\n> +\n> +                num_elems *= dim\n> +\n> +            self.__size = num_elems\n> +\n> +    @property\n> +    def description(self):\n> +        \"\"\"The control description\"\"\"\n> +        return self.__data.get('description')\n> +\n> +    @property\n> +    def enum_values(self):\n> +        \"\"\"The enum values, if the control is an enumeration\"\"\"\n> +        if self.__enum_values is None:\n> +            return\n> +        for enum in self.__enum_values:\n> +            yield enum\n> +\n> +    @property\n> +    def enum_values_count(self):\n> +        \"\"\"The number of enum values, if the control is an enumeration\"\"\"\n> +        if self.__enum_values is None:\n> +            return 0\n> +        return len(self.__enum_values)\n> +\n> +    @property\n> +    def is_enum(self):\n> +        \"\"\"Is the control an enumeration\"\"\"\n> +        return self.__enum_values is not None\n> +\n> +    @property\n> +    def vendor(self):\n> +        \"\"\"The vendor string, or None\"\"\"\n> +        return self.__vendor\n> +\n> +    @property\n> +    def name(self):\n> +        \"\"\"The control name (CamelCase)\"\"\"\n> +        return self.__name\n> +\n> +    @property\n> +    def type(self):\n> +        typ = self.__data.get('type')\n> +        size = self.__data.get('size')\n> +\n> +        if typ == 'string':\n> +            return 'std::string'\n> +\n> +        if self.__size is None:\n> +            return typ\n> +\n> +        if self.__size:\n> +            return f\"Span<const {typ}, {self.__size}>\"\n> +        else:\n> +            return f\"Span<const {typ}>\"\n> diff --git a/utils/codegen/gen-controls.py b/utils/codegen/gen-controls.py\n> index 685ef7a00d5f..2968eb9a5d4e 100755\n> --- a/utils/codegen/gen-controls.py\n> +++ b/utils/codegen/gen-controls.py\n> @@ -12,110 +12,7 @@ import os\n>   import sys\n>   import yaml\n>   \n> -\n> -class ControlEnum(object):\n> -    def __init__(self, data):\n> -        self.__data = data\n> -\n> -    @property\n> -    def description(self):\n> -        \"\"\"The enum description\"\"\"\n> -        return self.__data.get('description')\n> -\n> -    @property\n> -    def name(self):\n> -        \"\"\"The enum name\"\"\"\n> -        return self.__data.get('name')\n> -\n> -    @property\n> -    def value(self):\n> -        \"\"\"The enum value\"\"\"\n> -        return self.__data.get('value')\n> -\n> -\n> -class Control(object):\n> -    def __init__(self, name, data, vendor):\n> -        self.__name = name\n> -        self.__data = data\n> -        self.__enum_values = None\n> -        self.__size = None\n> -        self.__vendor = vendor\n> -\n> -        enum_values = data.get('enum')\n> -        if enum_values is not None:\n> -            self.__enum_values = [ControlEnum(enum) for enum in enum_values]\n> -\n> -        size = self.__data.get('size')\n> -        if size is not None:\n> -            if len(size) == 0:\n> -                raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension')\n> -\n> -            # Compute the total number of elements in the array. If any of the\n> -            # array dimension is a string, the array is variable-sized.\n> -            num_elems = 1\n> -            for dim in size:\n> -                if type(dim) is str:\n> -                    num_elems = 0\n> -                    break\n> -\n> -                dim = int(dim)\n> -                if dim <= 0:\n> -                    raise RuntimeError(f'Control `{self.__name}` size must have positive values only')\n> -\n> -                num_elems *= dim\n> -\n> -            self.__size = num_elems\n> -\n> -    @property\n> -    def description(self):\n> -        \"\"\"The control description\"\"\"\n> -        return self.__data.get('description')\n> -\n> -    @property\n> -    def enum_values(self):\n> -        \"\"\"The enum values, if the control is an enumeration\"\"\"\n> -        if self.__enum_values is None:\n> -            return\n> -        for enum in self.__enum_values:\n> -            yield enum\n> -\n> -    @property\n> -    def enum_values_count(self):\n> -        \"\"\"The number of enum values, if the control is an enumeration\"\"\"\n> -        if self.__enum_values is None:\n> -            return 0\n> -        return len(self.__enum_values)\n> -\n> -    @property\n> -    def is_enum(self):\n> -        \"\"\"Is the control an enumeration\"\"\"\n> -        return self.__enum_values is not None\n> -\n> -    @property\n> -    def vendor(self):\n> -        \"\"\"The vendor string, or None\"\"\"\n> -        return self.__vendor\n> -\n> -    @property\n> -    def name(self):\n> -        \"\"\"The control name (CamelCase)\"\"\"\n> -        return self.__name\n> -\n> -    @property\n> -    def type(self):\n> -        typ = self.__data.get('type')\n> -        size = self.__data.get('size')\n> -\n> -        if typ == 'string':\n> -            return 'std::string'\n> -\n> -        if self.__size is None:\n> -            return typ\n> -\n> -        if self.__size:\n> -            return f\"Span<const {typ}, {self.__size}>\"\n> -        else:\n> -            return f\"Span<const {typ}>\"\n> +from controls import Control\n>   \n>   \n>   def snake_case(s):","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 7BA83BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 14 Aug 2024 22:34:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7919C633BE;\n\tThu, 15 Aug 2024 00:34:30 +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 79C8163394\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Aug 2024 00:34:28 +0200 (CEST)","from [192.168.0.43]\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 712BF3A2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Aug 2024 00:33:30 +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=\"SD0gWf+W\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1723674810;\n\tbh=2fWYHGALVBQlJjS97oJItls5xs4cLtLmYqltQWbfoWc=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=SD0gWf+WvSh9PJ+XakIQGE3BifpKQoiq+GdCBOXuQ4v+wFmIkwmhLuBdH8P7ugl8+\n\txyjhs7L6HRLWtcD+5/EEPhQ5kEy1WKaWbPTPgpiZUOsjG74ZAzFUxmQXD8wDgJDS1t\n\t/66CcqfTGpVuS3smRUmJnYQFx//aDET9bWf4nOis=","Message-ID":"<3458d8d0-8fd3-4d6d-8f45-a0356500932b@ideasonboard.com>","Date":"Wed, 14 Aug 2024 23:34:25 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 08/10] utils: codegen: gen-controls.py: Move helper\n\tclasses to separate file","To":"libcamera-devel@lists.libcamera.org","References":"<20240809005914.20662-1-laurent.pinchart@ideasonboard.com>\n\t<20240809005914.20662-9-laurent.pinchart@ideasonboard.com>","Content-Language":"en-US","From":"Dan Scally <dan.scally@ideasonboard.com>","Autocrypt":"addr=dan.scally@ideasonboard.com; keydata=\n\t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","In-Reply-To":"<20240809005914.20662-9-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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>"}},{"id":30832,"web_url":"https://patchwork.libcamera.org/comment/30832/","msgid":"<Zr2CLtj_E_TqcNyo@pyrite.rasen.tech>","date":"2024-08-15T04:21:02","subject":"Re: [PATCH 08/10] utils: codegen: gen-controls.py: Move helper\n\tclasses to separate file","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"On Fri, Aug 09, 2024 at 03:59:12AM +0300, Laurent Pinchart wrote:\n> The ControlEnum and Control helper classes defined in gen-controls.py\n> are useful for other generator scripts. Move them to a separate file to\n> make it possible to share them.\n> \n> Extend the Python build environment to add the path to the new Python\n\nWhere is the python build environment being extended?\n\n\nPaul\n\n> module to PYTHONPATH, and use it when invoking the gen-controls.py\n> script.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/meson.build |   1 +\n>  src/libcamera/meson.build     |   3 +-\n>  utils/codegen/controls.py     | 112 ++++++++++++++++++++++++++++++++++\n>  utils/codegen/gen-controls.py | 105 +------------------------------\n>  4 files changed, 116 insertions(+), 105 deletions(-)\n>  create mode 100644 utils/codegen/controls.py\n> \n> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> index d90a8615e52d..a969a95dbf7a 100644\n> --- a/include/libcamera/meson.build\n> +++ b/include/libcamera/meson.build\n> @@ -88,6 +88,7 @@ foreach mode, entry : controls_map\n>                                       command : [gen_controls, '-o', '@OUTPUT@',\n>                                                  '--mode', mode, '-t', template_file,\n>                                                  '-r', ranges_file, '@INPUT@'],\n> +                                     env : py_build_env,\n>                                       install : true,\n>                                       install_dir : libcamera_headers_install_dir)\n>  endforeach\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 3fd3a87e9f95..aa9ab0291854 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -151,7 +151,8 @@ foreach mode, inout_files : controls_mode_files\n>                                       output : output_file,\n>                                       command : [gen_controls, '-o', '@OUTPUT@',\n>                                                  '--mode', mode, '-t', template_file,\n> -                                                '-r', ranges_file, '@INPUT@'])\n> +                                                '-r', ranges_file, '@INPUT@'],\n> +                                     env : py_build_env)\n>  endforeach\n>  \n>  libcamera_public_sources += control_sources\n> diff --git a/utils/codegen/controls.py b/utils/codegen/controls.py\n> new file mode 100644\n> index 000000000000..7bafee599c80\n> --- /dev/null\n> +++ b/utils/codegen/controls.py\n> @@ -0,0 +1,112 @@\n> +#!/usr/bin/env python3\n> +# SPDX-License-Identifier: GPL-2.0-or-later\n> +# Copyright (C) 2019, Google Inc.\n> +#\n> +# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> +#\n> +# Helper classes to handle source code generation for libcamera controls\n> +\n> +\n> +class ControlEnum(object):\n> +    def __init__(self, data):\n> +        self.__data = data\n> +\n> +    @property\n> +    def description(self):\n> +        \"\"\"The enum description\"\"\"\n> +        return self.__data.get('description')\n> +\n> +    @property\n> +    def name(self):\n> +        \"\"\"The enum name\"\"\"\n> +        return self.__data.get('name')\n> +\n> +    @property\n> +    def value(self):\n> +        \"\"\"The enum value\"\"\"\n> +        return self.__data.get('value')\n> +\n> +\n> +class Control(object):\n> +    def __init__(self, name, data, vendor):\n> +        self.__name = name\n> +        self.__data = data\n> +        self.__enum_values = None\n> +        self.__size = None\n> +        self.__vendor = vendor\n> +\n> +        enum_values = data.get('enum')\n> +        if enum_values is not None:\n> +            self.__enum_values = [ControlEnum(enum) for enum in enum_values]\n> +\n> +        size = self.__data.get('size')\n> +        if size is not None:\n> +            if len(size) == 0:\n> +                raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension')\n> +\n> +            # Compute the total number of elements in the array. If any of the\n> +            # array dimension is a string, the array is variable-sized.\n> +            num_elems = 1\n> +            for dim in size:\n> +                if type(dim) is str:\n> +                    num_elems = 0\n> +                    break\n> +\n> +                dim = int(dim)\n> +                if dim <= 0:\n> +                    raise RuntimeError(f'Control `{self.__name}` size must have positive values only')\n> +\n> +                num_elems *= dim\n> +\n> +            self.__size = num_elems\n> +\n> +    @property\n> +    def description(self):\n> +        \"\"\"The control description\"\"\"\n> +        return self.__data.get('description')\n> +\n> +    @property\n> +    def enum_values(self):\n> +        \"\"\"The enum values, if the control is an enumeration\"\"\"\n> +        if self.__enum_values is None:\n> +            return\n> +        for enum in self.__enum_values:\n> +            yield enum\n> +\n> +    @property\n> +    def enum_values_count(self):\n> +        \"\"\"The number of enum values, if the control is an enumeration\"\"\"\n> +        if self.__enum_values is None:\n> +            return 0\n> +        return len(self.__enum_values)\n> +\n> +    @property\n> +    def is_enum(self):\n> +        \"\"\"Is the control an enumeration\"\"\"\n> +        return self.__enum_values is not None\n> +\n> +    @property\n> +    def vendor(self):\n> +        \"\"\"The vendor string, or None\"\"\"\n> +        return self.__vendor\n> +\n> +    @property\n> +    def name(self):\n> +        \"\"\"The control name (CamelCase)\"\"\"\n> +        return self.__name\n> +\n> +    @property\n> +    def type(self):\n> +        typ = self.__data.get('type')\n> +        size = self.__data.get('size')\n> +\n> +        if typ == 'string':\n> +            return 'std::string'\n> +\n> +        if self.__size is None:\n> +            return typ\n> +\n> +        if self.__size:\n> +            return f\"Span<const {typ}, {self.__size}>\"\n> +        else:\n> +            return f\"Span<const {typ}>\"\n> diff --git a/utils/codegen/gen-controls.py b/utils/codegen/gen-controls.py\n> index 685ef7a00d5f..2968eb9a5d4e 100755\n> --- a/utils/codegen/gen-controls.py\n> +++ b/utils/codegen/gen-controls.py\n> @@ -12,110 +12,7 @@ import os\n>  import sys\n>  import yaml\n>  \n> -\n> -class ControlEnum(object):\n> -    def __init__(self, data):\n> -        self.__data = data\n> -\n> -    @property\n> -    def description(self):\n> -        \"\"\"The enum description\"\"\"\n> -        return self.__data.get('description')\n> -\n> -    @property\n> -    def name(self):\n> -        \"\"\"The enum name\"\"\"\n> -        return self.__data.get('name')\n> -\n> -    @property\n> -    def value(self):\n> -        \"\"\"The enum value\"\"\"\n> -        return self.__data.get('value')\n> -\n> -\n> -class Control(object):\n> -    def __init__(self, name, data, vendor):\n> -        self.__name = name\n> -        self.__data = data\n> -        self.__enum_values = None\n> -        self.__size = None\n> -        self.__vendor = vendor\n> -\n> -        enum_values = data.get('enum')\n> -        if enum_values is not None:\n> -            self.__enum_values = [ControlEnum(enum) for enum in enum_values]\n> -\n> -        size = self.__data.get('size')\n> -        if size is not None:\n> -            if len(size) == 0:\n> -                raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension')\n> -\n> -            # Compute the total number of elements in the array. If any of the\n> -            # array dimension is a string, the array is variable-sized.\n> -            num_elems = 1\n> -            for dim in size:\n> -                if type(dim) is str:\n> -                    num_elems = 0\n> -                    break\n> -\n> -                dim = int(dim)\n> -                if dim <= 0:\n> -                    raise RuntimeError(f'Control `{self.__name}` size must have positive values only')\n> -\n> -                num_elems *= dim\n> -\n> -            self.__size = num_elems\n> -\n> -    @property\n> -    def description(self):\n> -        \"\"\"The control description\"\"\"\n> -        return self.__data.get('description')\n> -\n> -    @property\n> -    def enum_values(self):\n> -        \"\"\"The enum values, if the control is an enumeration\"\"\"\n> -        if self.__enum_values is None:\n> -            return\n> -        for enum in self.__enum_values:\n> -            yield enum\n> -\n> -    @property\n> -    def enum_values_count(self):\n> -        \"\"\"The number of enum values, if the control is an enumeration\"\"\"\n> -        if self.__enum_values is None:\n> -            return 0\n> -        return len(self.__enum_values)\n> -\n> -    @property\n> -    def is_enum(self):\n> -        \"\"\"Is the control an enumeration\"\"\"\n> -        return self.__enum_values is not None\n> -\n> -    @property\n> -    def vendor(self):\n> -        \"\"\"The vendor string, or None\"\"\"\n> -        return self.__vendor\n> -\n> -    @property\n> -    def name(self):\n> -        \"\"\"The control name (CamelCase)\"\"\"\n> -        return self.__name\n> -\n> -    @property\n> -    def type(self):\n> -        typ = self.__data.get('type')\n> -        size = self.__data.get('size')\n> -\n> -        if typ == 'string':\n> -            return 'std::string'\n> -\n> -        if self.__size is None:\n> -            return typ\n> -\n> -        if self.__size:\n> -            return f\"Span<const {typ}, {self.__size}>\"\n> -        else:\n> -            return f\"Span<const {typ}>\"\n> +from controls import Control\n>  \n>  \n>  def snake_case(s):\n> -- \n> Regards,\n> \n> Laurent Pinchart\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 56863BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 15 Aug 2024 04:21:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C95B6633BD;\n\tThu, 15 Aug 2024 06:21:11 +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 93DA663382\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Aug 2024 06:21:09 +0200 (CEST)","from pyrite.rasen.tech (h175-177-049-024.catv02.itscom.jp\n\t[175.177.49.24])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 637A9827;\n\tThu, 15 Aug 2024 06:20:10 +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=\"gYNtJ/7g\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1723695611;\n\tbh=SGKsJgFhSaCj5CkkyoxljLt65vXxp6pv0VJ3bUWknOs=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=gYNtJ/7gOQxLQD0igzzoFQgTyhfF1mjYoYGg3GHVDUx6gqfH4gIIFJfe4sr2YHuT/\n\tCPPetYRJMnFBWiGs+NJ+cVmpnbcEa1pGJaf6wAavQGIwL59S6J+7JRwclnpkQd6Vh7\n\tlDS9LkL6PEw7ja+1FFrNamlk/cDdQvieRUNuxgJ4=","Date":"Thu, 15 Aug 2024 13:21:02 +0900","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 08/10] utils: codegen: gen-controls.py: Move helper\n\tclasses to separate file","Message-ID":"<Zr2CLtj_E_TqcNyo@pyrite.rasen.tech>","References":"<20240809005914.20662-1-laurent.pinchart@ideasonboard.com>\n\t<20240809005914.20662-9-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20240809005914.20662-9-laurent.pinchart@ideasonboard.com>","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>"}},{"id":30833,"web_url":"https://patchwork.libcamera.org/comment/30833/","msgid":"<Zr2FPHZsxx6KL6qa@pyrite.rasen.tech>","date":"2024-08-15T04:34:04","subject":"Re: [PATCH 08/10] utils: codegen: gen-controls.py: Move helper\n\tclasses to separate file","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"On Thu, Aug 15, 2024 at 01:21:02PM +0900, Paul Elder wrote:\n> On Fri, Aug 09, 2024 at 03:59:12AM +0300, Laurent Pinchart wrote:\n> > The ControlEnum and Control helper classes defined in gen-controls.py\n> > are useful for other generator scripts. Move them to a separate file to\n> > make it possible to share them.\n> > \n> > Extend the Python build environment to add the path to the new Python\n> \n> Where is the python build environment being extended?\n\nOh I see it's in the next patch.\n\n\nPaul\n\n> \n> > module to PYTHONPATH, and use it when invoking the gen-controls.py\n> > script.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/meson.build |   1 +\n> >  src/libcamera/meson.build     |   3 +-\n> >  utils/codegen/controls.py     | 112 ++++++++++++++++++++++++++++++++++\n> >  utils/codegen/gen-controls.py | 105 +------------------------------\n> >  4 files changed, 116 insertions(+), 105 deletions(-)\n> >  create mode 100644 utils/codegen/controls.py\n> > \n> > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> > index d90a8615e52d..a969a95dbf7a 100644\n> > --- a/include/libcamera/meson.build\n> > +++ b/include/libcamera/meson.build\n> > @@ -88,6 +88,7 @@ foreach mode, entry : controls_map\n> >                                       command : [gen_controls, '-o', '@OUTPUT@',\n> >                                                  '--mode', mode, '-t', template_file,\n> >                                                  '-r', ranges_file, '@INPUT@'],\n> > +                                     env : py_build_env,\n> >                                       install : true,\n> >                                       install_dir : libcamera_headers_install_dir)\n> >  endforeach\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 3fd3a87e9f95..aa9ab0291854 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -151,7 +151,8 @@ foreach mode, inout_files : controls_mode_files\n> >                                       output : output_file,\n> >                                       command : [gen_controls, '-o', '@OUTPUT@',\n> >                                                  '--mode', mode, '-t', template_file,\n> > -                                                '-r', ranges_file, '@INPUT@'])\n> > +                                                '-r', ranges_file, '@INPUT@'],\n> > +                                     env : py_build_env)\n> >  endforeach\n> >  \n> >  libcamera_public_sources += control_sources\n> > diff --git a/utils/codegen/controls.py b/utils/codegen/controls.py\n> > new file mode 100644\n> > index 000000000000..7bafee599c80\n> > --- /dev/null\n> > +++ b/utils/codegen/controls.py\n> > @@ -0,0 +1,112 @@\n> > +#!/usr/bin/env python3\n> > +# SPDX-License-Identifier: GPL-2.0-or-later\n> > +# Copyright (C) 2019, Google Inc.\n> > +#\n> > +# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > +#\n> > +# Helper classes to handle source code generation for libcamera controls\n> > +\n> > +\n> > +class ControlEnum(object):\n> > +    def __init__(self, data):\n> > +        self.__data = data\n> > +\n> > +    @property\n> > +    def description(self):\n> > +        \"\"\"The enum description\"\"\"\n> > +        return self.__data.get('description')\n> > +\n> > +    @property\n> > +    def name(self):\n> > +        \"\"\"The enum name\"\"\"\n> > +        return self.__data.get('name')\n> > +\n> > +    @property\n> > +    def value(self):\n> > +        \"\"\"The enum value\"\"\"\n> > +        return self.__data.get('value')\n> > +\n> > +\n> > +class Control(object):\n> > +    def __init__(self, name, data, vendor):\n> > +        self.__name = name\n> > +        self.__data = data\n> > +        self.__enum_values = None\n> > +        self.__size = None\n> > +        self.__vendor = vendor\n> > +\n> > +        enum_values = data.get('enum')\n> > +        if enum_values is not None:\n> > +            self.__enum_values = [ControlEnum(enum) for enum in enum_values]\n> > +\n> > +        size = self.__data.get('size')\n> > +        if size is not None:\n> > +            if len(size) == 0:\n> > +                raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension')\n> > +\n> > +            # Compute the total number of elements in the array. If any of the\n> > +            # array dimension is a string, the array is variable-sized.\n> > +            num_elems = 1\n> > +            for dim in size:\n> > +                if type(dim) is str:\n> > +                    num_elems = 0\n> > +                    break\n> > +\n> > +                dim = int(dim)\n> > +                if dim <= 0:\n> > +                    raise RuntimeError(f'Control `{self.__name}` size must have positive values only')\n> > +\n> > +                num_elems *= dim\n> > +\n> > +            self.__size = num_elems\n> > +\n> > +    @property\n> > +    def description(self):\n> > +        \"\"\"The control description\"\"\"\n> > +        return self.__data.get('description')\n> > +\n> > +    @property\n> > +    def enum_values(self):\n> > +        \"\"\"The enum values, if the control is an enumeration\"\"\"\n> > +        if self.__enum_values is None:\n> > +            return\n> > +        for enum in self.__enum_values:\n> > +            yield enum\n> > +\n> > +    @property\n> > +    def enum_values_count(self):\n> > +        \"\"\"The number of enum values, if the control is an enumeration\"\"\"\n> > +        if self.__enum_values is None:\n> > +            return 0\n> > +        return len(self.__enum_values)\n> > +\n> > +    @property\n> > +    def is_enum(self):\n> > +        \"\"\"Is the control an enumeration\"\"\"\n> > +        return self.__enum_values is not None\n> > +\n> > +    @property\n> > +    def vendor(self):\n> > +        \"\"\"The vendor string, or None\"\"\"\n> > +        return self.__vendor\n> > +\n> > +    @property\n> > +    def name(self):\n> > +        \"\"\"The control name (CamelCase)\"\"\"\n> > +        return self.__name\n> > +\n> > +    @property\n> > +    def type(self):\n> > +        typ = self.__data.get('type')\n> > +        size = self.__data.get('size')\n> > +\n> > +        if typ == 'string':\n> > +            return 'std::string'\n> > +\n> > +        if self.__size is None:\n> > +            return typ\n> > +\n> > +        if self.__size:\n> > +            return f\"Span<const {typ}, {self.__size}>\"\n> > +        else:\n> > +            return f\"Span<const {typ}>\"\n> > diff --git a/utils/codegen/gen-controls.py b/utils/codegen/gen-controls.py\n> > index 685ef7a00d5f..2968eb9a5d4e 100755\n> > --- a/utils/codegen/gen-controls.py\n> > +++ b/utils/codegen/gen-controls.py\n> > @@ -12,110 +12,7 @@ import os\n> >  import sys\n> >  import yaml\n> >  \n> > -\n> > -class ControlEnum(object):\n> > -    def __init__(self, data):\n> > -        self.__data = data\n> > -\n> > -    @property\n> > -    def description(self):\n> > -        \"\"\"The enum description\"\"\"\n> > -        return self.__data.get('description')\n> > -\n> > -    @property\n> > -    def name(self):\n> > -        \"\"\"The enum name\"\"\"\n> > -        return self.__data.get('name')\n> > -\n> > -    @property\n> > -    def value(self):\n> > -        \"\"\"The enum value\"\"\"\n> > -        return self.__data.get('value')\n> > -\n> > -\n> > -class Control(object):\n> > -    def __init__(self, name, data, vendor):\n> > -        self.__name = name\n> > -        self.__data = data\n> > -        self.__enum_values = None\n> > -        self.__size = None\n> > -        self.__vendor = vendor\n> > -\n> > -        enum_values = data.get('enum')\n> > -        if enum_values is not None:\n> > -            self.__enum_values = [ControlEnum(enum) for enum in enum_values]\n> > -\n> > -        size = self.__data.get('size')\n> > -        if size is not None:\n> > -            if len(size) == 0:\n> > -                raise RuntimeError(f'Control `{self.__name}` size must have at least one dimension')\n> > -\n> > -            # Compute the total number of elements in the array. If any of the\n> > -            # array dimension is a string, the array is variable-sized.\n> > -            num_elems = 1\n> > -            for dim in size:\n> > -                if type(dim) is str:\n> > -                    num_elems = 0\n> > -                    break\n> > -\n> > -                dim = int(dim)\n> > -                if dim <= 0:\n> > -                    raise RuntimeError(f'Control `{self.__name}` size must have positive values only')\n> > -\n> > -                num_elems *= dim\n> > -\n> > -            self.__size = num_elems\n> > -\n> > -    @property\n> > -    def description(self):\n> > -        \"\"\"The control description\"\"\"\n> > -        return self.__data.get('description')\n> > -\n> > -    @property\n> > -    def enum_values(self):\n> > -        \"\"\"The enum values, if the control is an enumeration\"\"\"\n> > -        if self.__enum_values is None:\n> > -            return\n> > -        for enum in self.__enum_values:\n> > -            yield enum\n> > -\n> > -    @property\n> > -    def enum_values_count(self):\n> > -        \"\"\"The number of enum values, if the control is an enumeration\"\"\"\n> > -        if self.__enum_values is None:\n> > -            return 0\n> > -        return len(self.__enum_values)\n> > -\n> > -    @property\n> > -    def is_enum(self):\n> > -        \"\"\"Is the control an enumeration\"\"\"\n> > -        return self.__enum_values is not None\n> > -\n> > -    @property\n> > -    def vendor(self):\n> > -        \"\"\"The vendor string, or None\"\"\"\n> > -        return self.__vendor\n> > -\n> > -    @property\n> > -    def name(self):\n> > -        \"\"\"The control name (CamelCase)\"\"\"\n> > -        return self.__name\n> > -\n> > -    @property\n> > -    def type(self):\n> > -        typ = self.__data.get('type')\n> > -        size = self.__data.get('size')\n> > -\n> > -        if typ == 'string':\n> > -            return 'std::string'\n> > -\n> > -        if self.__size is None:\n> > -            return typ\n> > -\n> > -        if self.__size:\n> > -            return f\"Span<const {typ}, {self.__size}>\"\n> > -        else:\n> > -            return f\"Span<const {typ}>\"\n> > +from controls import Control\n> >  \n> >  \n> >  def snake_case(s):","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 55620C323E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 15 Aug 2024 04:34:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6A436633BD;\n\tThu, 15 Aug 2024 06:34:13 +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 4AAB363382\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Aug 2024 06:34:11 +0200 (CEST)","from pyrite.rasen.tech (h175-177-049-024.catv02.itscom.jp\n\t[175.177.49.24])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 186706CA;\n\tThu, 15 Aug 2024 06:33:11 +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=\"NrkxDkHg\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1723696393;\n\tbh=e8NwR8mIHacjG5MZt9QIsMuhm3ud0NVDWGHASI1Y2CQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=NrkxDkHgLSSkAw0/Hm7TG89+dgK9O1P7jWn9t33LT4xVPajgn+4G9pzhKCCGJ4JWp\n\tCIImGDyl2WK6rPiMg4H0OxVGm723WgJs1PhROZJIzFlduvx5V6AEczQQXid5jDX8SQ\n\tV4kdy8wkaSSfDZWfPXC84/SNXa2wn9r4YVIf1I6U=","Date":"Thu, 15 Aug 2024 13:34:04 +0900","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 08/10] utils: codegen: gen-controls.py: Move helper\n\tclasses to separate file","Message-ID":"<Zr2FPHZsxx6KL6qa@pyrite.rasen.tech>","References":"<20240809005914.20662-1-laurent.pinchart@ideasonboard.com>\n\t<20240809005914.20662-9-laurent.pinchart@ideasonboard.com>\n\t<Zr2CLtj_E_TqcNyo@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<Zr2CLtj_E_TqcNyo@pyrite.rasen.tech>","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>"}}]