Show a patch.

GET /api/1.1/patches/20857/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 20857,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/20857/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/20857/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20240809005914.20662-8-laurent.pinchart@ideasonboard.com>",
    "date": "2024-08-09T00:59:11",
    "name": "[07/10] utils: codegen: gen-controls.py: Convert to jinja2 templates",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "a35f51c23655389f19f72f47ac17c4c0c96abba1",
    "submitter": {
        "id": 2,
        "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api",
        "name": "Laurent Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/20857/mbox/",
    "series": [
        {
            "id": 4506,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4506/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4506",
            "date": "2024-08-09T00:59:04",
            "name": "libcamera: Improve code generation for controls",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/4506/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/20857/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/20857/checks/",
    "tags": {},
    "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 19578C324E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  9 Aug 2024 00:59:55 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C177A633CA;\n\tFri,  9 Aug 2024 02:59:54 +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 57226633C1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  9 Aug 2024 02:59:49 +0200 (CEST)",
            "from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3B239B7E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  9 Aug 2024 02:58:55 +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=\"RfscQgkX\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1723165135;\n\tbh=m2BDCSxnwKOufznelIctS8HRcKjM8GxloMDipDWtv5Q=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=RfscQgkX7URDYY3kAEUOnlpgQT/fmrr+N/C/z1v+fGv+HE23t3ZvscwCK7lI9o3Ta\n\tXlOvw6RnpWg6PmA0t9i8v0UyA3lF09HHvAk7MZTBhYL6QKTYw1tJ/DT2ZHAR1d9YBr\n\twUkBtbYGg945u5lYZotuJUI8uQYrmXNoX6MTpOgI=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Subject": "[PATCH 07/10] utils: codegen: gen-controls.py: Convert to jinja2\n\ttemplates",
        "Date": "Fri,  9 Aug 2024 03:59:11 +0300",
        "Message-ID": "<20240809005914.20662-8-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.44.2",
        "In-Reply-To": "<20240809005914.20662-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20240809005914.20662-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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>"
    },
    "content": "Jinja2 templates help separating the logic related to the template from\nthe generation of the data. The python code gets much clearer as a\nresult.\n\nAs an added bonus, we can use a single template file for both controls\nand properties.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/control_ids.h.in  |  40 +++-\n include/libcamera/meson.build       |   2 +-\n include/libcamera/property_ids.h.in |  34 ----\n src/libcamera/control_ids.cpp.in    | 101 ++++++++--\n src/libcamera/meson.build           |   5 +-\n src/libcamera/property_ids.cpp.in   |  48 -----\n utils/codegen/gen-controls.py       | 285 ++++++----------------------\n 7 files changed, 176 insertions(+), 339 deletions(-)\n delete mode 100644 include/libcamera/property_ids.h.in\n delete mode 100644 src/libcamera/property_ids.cpp.in",
    "diff": "diff --git a/include/libcamera/control_ids.h.in b/include/libcamera/control_ids.h.in\nindex 293ba966fbc4..858ef872e9ee 100644\n--- a/include/libcamera/control_ids.h.in\n+++ b/include/libcamera/control_ids.h.in\n@@ -2,7 +2,7 @@\n /*\n  * Copyright (C) 2019, Google Inc.\n  *\n- * Control ID list\n+ * {{mode|capitalize}} ID list\n  *\n  * This file is auto-generated. Do not edit.\n  */\n@@ -18,18 +18,42 @@\n \n namespace libcamera {\n \n-namespace controls {\n+namespace {{mode}} {\n+\n+extern const ControlIdMap {{mode}};\n+\n+{%- for vendor, ctrls in controls -%}\n+\n+{% if vendor != 'libcamera' %}\n+namespace {{vendor}} {\n+\n+#define LIBCAMERA_HAS_{{vendor|upper}}_VENDOR_{{mode|upper}}\n+{%- endif %}\n \n enum {\n-${ids}\n+{%- for ctrl in ctrls %}\n+\t{{ctrl.name|snake_case|upper}} = {{ctrl.id}},\n+{%- endfor %}\n };\n \n-${controls}\n+{% for ctrl in ctrls -%}\n+{% if ctrl.is_enum -%}\n+enum {{ctrl.name}}Enum {\n+{%- for enum in ctrl.enum_values %}\n+\t{{enum.name}} = {{enum.value}},\n+{%- endfor %}\n+};\n+extern const std::array<const ControlValue, {{ctrl.enum_values_count}}> {{ctrl.name}}Values;\n+extern const std::map<std::string, {{ctrl.type}}> {{ctrl.name}}NameValueMap;\n+{% endif -%}\n+extern const Control<{{ctrl.type}}> {{ctrl.name}};\n+{% endfor -%}\n \n-extern const ControlIdMap controls;\n+{% if vendor != 'libcamera' %}\n+} /* namespace {{vendor}} */\n+{% endif -%}\n \n-${vendor_controls}\n-\n-} /* namespace controls */\n+{% endfor %}\n+} /* namespace {{mode}} */\n \n } /* namespace libcamera */\ndiff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\nindex 87b9a9412fe7..d90a8615e52d 100644\n--- a/include/libcamera/meson.build\n+++ b/include/libcamera/meson.build\n@@ -80,7 +80,7 @@ foreach mode, entry : controls_map\n         properties_files_names += files_list\n     endif\n \n-    template_file = files(outfile + '.in')\n+    template_file = files('control_ids.h.in')\n     ranges_file = files('../../src/libcamera/control_ranges.yaml')\n     control_headers += custom_target(header + '_h',\n                                      input : input_files,\ndiff --git a/include/libcamera/property_ids.h.in b/include/libcamera/property_ids.h.in\ndeleted file mode 100644\nindex e6edabca771f..000000000000\n--- a/include/libcamera/property_ids.h.in\n+++ /dev/null\n@@ -1,34 +0,0 @@\n-/* SPDX-License-Identifier: LGPL-2.1-or-later */\n-/*\n- * Copyright (C) 2019, Google Inc.\n- *\n- * Property ID list\n- *\n- * This file is auto-generated. Do not edit.\n- */\n-\n-#pragma once\n-\n-#include <map>\n-#include <stdint.h>\n-#include <string>\n-\n-#include <libcamera/controls.h>\n-\n-namespace libcamera {\n-\n-namespace properties {\n-\n-enum {\n-${ids}\n-};\n-\n-${controls}\n-\n-extern const ControlIdMap properties;\n-\n-${vendor_controls}\n-\n-} /* namespace properties */\n-\n-} /* namespace libcamera */\ndiff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in\nindex 0b028c92d852..05c8fb385d20 100644\n--- a/src/libcamera/control_ids.cpp.in\n+++ b/src/libcamera/control_ids.cpp.in\n@@ -2,51 +2,120 @@\n /*\n  * Copyright (C) 2019, Google Inc.\n  *\n- * Control ID list\n+ * {{mode}} ID list\n  *\n  * This file is auto-generated. Do not edit.\n  */\n \n-#include <libcamera/control_ids.h>\n+#include <libcamera/{{filename}}.h>\n #include <libcamera/controls.h>\n \n /**\n- * \\file control_ids.h\n- * \\brief Camera control identifiers\n+ * \\file {{filename}}.h\n+ * \\brief Camera {{mode}} identifiers\n  */\n \n namespace libcamera {\n \n /**\n- * \\brief Namespace for libcamera controls\n+ * \\brief Namespace for libcamera {{mode}}\n  */\n-namespace controls {\n+namespace {{mode}} {\n \n-${controls_doc}\n+{%- for vendor, ctrls in controls -%}\n \n-${vendor_controls_doc}\n+{%- if vendor != 'libcamera' %}\n+/**\n+ * \\brief Namespace for {{vendor}} {{mode}}\n+ */\n+namespace {{vendor}} {\n+{%- endif -%}\n+\n+{% for ctrl in ctrls %}\n+\n+{% if ctrl.is_enum -%}\n+/**\n+ * \\enum {{ctrl.name}}Enum\n+ * \\brief Supported {{ctrl.name}} values\n+{%- for enum in ctrl.enum_values %}\n+ *\n+ * \\var {{enum.name}}\n+ * \\brief {{enum.description|format_description}}\n+{%- endfor %}\n+ */\n+\n+/**\n+ * \\var {{ctrl.name}}Values\n+ * \\brief List of all {{ctrl.name}} supported values\n+ */\n+\n+/**\n+ * \\var {{ctrl.name}}NameValueMap\n+ * \\brief Map of all {{ctrl.name}} supported value names (in std::string format) to value\n+ */\n+\n+{% endif -%}\n+/**\n+ * \\var {{ctrl.name}}\n+ * \\brief {{ctrl.description|format_description}}\n+ */\n+{%- endfor %}\n+{% if vendor != 'libcamera' %}\n+} /* namespace {{vendor}} */\n+{% endif -%}\n+\n+{%- endfor %}\n \n #ifndef __DOXYGEN__\n /*\n- * Keep the controls definitions hidden from doxygen as it incorrectly parses\n+ * Keep the {{mode}} definitions hidden from doxygen as it incorrectly parses\n  * them as functions.\n  */\n-${controls_def}\n+{% for vendor, ctrls in controls -%}\n \n-${vendor_controls_def}\n+{% if vendor != 'libcamera' %}\n+namespace {{vendor}} {\n+{% endif %}\n \n-#endif\n+{%- for ctrl in ctrls %}\n+{% if ctrl.is_enum -%}\n+extern const std::array<const ControlValue, {{ctrl.enum_values_count}}> {{ctrl.name}}Values = {\n+{%- for enum in ctrl.enum_values %}\n+\tstatic_cast<{{ctrl.type}}>({{enum.name}}),\n+{%- endfor %}\n+};\n+extern const std::map<std::string, {{ctrl.type}}> {{ctrl.name}}NameValueMap = {\n+{%- for enum in ctrl.enum_values %}\n+\t{ \"{{enum.name}}\", {{enum.name}} },\n+{%- endfor %}\n+};\n+{% endif -%}\n+extern const Control<{{ctrl.type}}> {{ctrl.name}}({{ctrl.name|snake_case|upper}}, \"{{ctrl.name}}\");\n+{%- endfor %}\n+\n+{% if vendor != 'libcamera' %}\n+} /* namespace {{vendor}} */\n+{% endif -%}\n+\n+{%- endfor %}\n+#endif /* __DOXYGEN__ */\n \n /**\n- * \\brief List of all supported libcamera controls\n+ * \\brief List of all supported libcamera {{mode}}\n+{%- if mode == 'controls' %}\n  *\n  * Unless otherwise stated, all controls are bi-directional, i.e. they can be\n  * set through Request::controls() and returned out through Request::metadata().\n+{%- endif %}\n  */\n-extern const ControlIdMap controls {\n-${controls_map}\n+extern const ControlIdMap {{mode}} {\n+{%- for vendor, ctrls in controls -%}\n+{%- for ctrl in ctrls %}\n+\t{ {{ctrl.namespace}}{{ctrl.name|snake_case|upper}}, &{{ctrl.namespace}}{{ctrl.name}} },\n+{%- endfor -%}\n+{%- endfor %}\n };\n \n-} /* namespace controls */\n+} /* namespace {{mode}} */\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex e5e959d9c7bd..3fd3a87e9f95 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -143,9 +143,10 @@ foreach mode, inout_files : controls_mode_files\n     input_files = inout_files[0]\n     output_file = inout_files[1]\n \n-    template_file = files(output_file + '.in')\n+    template_file = files('control_ids.cpp.in')\n     ranges_file = files('control_ranges.yaml')\n-    control_sources += custom_target(mode + '_cpp',\n+\n+    control_sources += custom_target(mode + '_ids_cpp',\n                                      input : input_files,\n                                      output : output_file,\n                                      command : [gen_controls, '-o', '@OUTPUT@',\ndiff --git a/src/libcamera/property_ids.cpp.in b/src/libcamera/property_ids.cpp.in\ndeleted file mode 100644\nindex 2d3f192eb6ef..000000000000\n--- a/src/libcamera/property_ids.cpp.in\n+++ /dev/null\n@@ -1,48 +0,0 @@\n-/* SPDX-License-Identifier: LGPL-2.1-or-later */\n-/*\n- * Copyright (C) 2019, Google Inc.\n- *\n- * Property ID list\n- *\n- * This file is auto-generated. Do not edit.\n- */\n-\n-#include <libcamera/property_ids.h>\n-\n-/**\n- * \\file property_ids.h\n- * \\brief Camera property identifiers\n- */\n-\n-namespace libcamera {\n-\n-/**\n- * \\brief Namespace for libcamera properties\n- */\n-namespace properties {\n-\n-${controls_doc}\n-\n-${vendor_controls_doc}\n-\n-#ifndef __DOXYGEN__\n-/*\n- * Keep the properties definitions hidden from doxygen as it incorrectly parses\n- * them as functions.\n- */\n-${controls_def}\n-\n-${vendor_controls_def}\n-\n-#endif\n-\n-/**\n- * \\brief List of all supported libcamera properties\n- */\n-extern const ControlIdMap properties {\n-${controls_map}\n-};\n-\n-} /* namespace properties */\n-\n-} /* namespace libcamera */\ndiff --git a/utils/codegen/gen-controls.py b/utils/codegen/gen-controls.py\nindex 56315f5089b4..685ef7a00d5f 100755\n--- a/utils/codegen/gen-controls.py\n+++ b/utils/codegen/gen-controls.py\n@@ -7,12 +7,10 @@\n # Generate control definitions from YAML\n \n import argparse\n-from functools import reduce\n-import operator\n-import string\n+import jinja2\n+import os\n import sys\n import yaml\n-import os\n \n \n class ControlEnum(object):\n@@ -81,6 +79,13 @@ class Control(object):\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@@ -119,221 +124,23 @@ def snake_case(s):\n \n def format_description(description):\n     description = description.strip('\\n').split('\\n')\n-    description[0] = '\\\\brief ' + description[0]\n-    return '\\n'.join([(line and ' * ' or ' *') + line for line in description])\n+    for i in range(1, len(description)):\n+        line = description[i]\n+        description[i] = (line and ' * ' or ' *') + line\n+    return '\\n'.join(description)\n \n \n-def generate_cpp(controls):\n-    enum_doc_start_template = string.Template('''/**\n- * \\\\enum ${name}Enum\n- * \\\\brief Supported ${name} values''')\n-    enum_doc_value_template = string.Template(''' * \\\\var ${value}\n-${description}''')\n-    doc_template = string.Template('''/**\n- * \\\\var ${name}\n-${description}\n- */''')\n-    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\");')\n-    enum_values_doc = string.Template('''/**\n- * \\\\var ${name}Values\n- * \\\\brief List of all $name supported values\n- */''')\n-    enum_values_start = string.Template('''extern const std::array<const ControlValue, ${size}> ${name}Values = {''')\n-    enum_values_values = string.Template('''\\tstatic_cast<int32_t>(${name}),''')\n-    name_value_map_doc = string.Template('''/**\n- * \\\\var ${name}NameValueMap\n- * \\\\brief Map of all $name supported value names (in std::string format) to value\n- */''')\n-    name_value_map_start = string.Template('''extern const std::map<std::string, ${type}> ${name}NameValueMap = {''')\n-    name_value_values = string.Template('''\\t{ \"${name}\", ${name} },''')\n+def extend_control(ctrl, id, ranges):\n+    ctrl.id = ranges[ctrl.vendor] + id + 1\n \n-    ctrls_doc = {}\n-    ctrls_def = {}\n-    ctrls_map = []\n+    if ctrl.vendor != 'libcamera':\n+        ctrl.namespace = f'{ctrl.vendor}::'\n+    else:\n+        ctrl.namespace = ''\n \n-    for ctrl in controls:\n-        id_name = snake_case(ctrl.name).upper()\n+    ctrl.documentation = format_description(ctrl.description)\n \n-        vendor = ctrl.vendor\n-        if vendor not in ctrls_doc:\n-            ctrls_doc[vendor] = []\n-            ctrls_def[vendor] = []\n-\n-        info = {\n-            'name': ctrl.name,\n-            'type': ctrl.type,\n-            'description': format_description(ctrl.description),\n-            'id_name': id_name,\n-        }\n-\n-        target_doc = ctrls_doc[vendor]\n-        target_def = ctrls_def[vendor]\n-\n-        if ctrl.is_enum:\n-            enum_doc = []\n-            enum_doc.append(enum_doc_start_template.substitute(info))\n-\n-            num_entries = 0\n-            for enum in ctrl.enum_values:\n-                value_info = {\n-                    'name': ctrl.name,\n-                    'value': enum.name,\n-                    'description': format_description(enum.description),\n-                }\n-                enum_doc.append(enum_doc_value_template.substitute(value_info))\n-                num_entries += 1\n-\n-            enum_doc = '\\n *\\n'.join(enum_doc)\n-            enum_doc += '\\n */'\n-            target_doc.append(enum_doc)\n-\n-            values_info = {\n-                'name': info['name'],\n-                'type': ctrl.type,\n-                'size': num_entries,\n-            }\n-            target_doc.append(enum_values_doc.substitute(values_info))\n-            target_def.append(enum_values_start.substitute(values_info))\n-            for enum in ctrl.enum_values:\n-                value_info = {\n-                    'name': enum.name\n-                }\n-                target_def.append(enum_values_values.substitute(value_info))\n-            target_def.append(\"};\")\n-\n-            target_doc.append(name_value_map_doc.substitute(values_info))\n-            target_def.append(name_value_map_start.substitute(values_info))\n-            for enum in ctrl.enum_values:\n-                value_info = {\n-                    'name': enum.name\n-                }\n-                target_def.append(name_value_values.substitute(value_info))\n-            target_def.append(\"};\")\n-\n-        target_doc.append(doc_template.substitute(info))\n-        target_def.append(def_template.substitute(info))\n-\n-        vendor_ns = vendor + '::' if vendor != \"libcamera\" else ''\n-        ctrls_map.append('\\t{ ' + vendor_ns + id_name + ', &' + vendor_ns + ctrl.name + ' },')\n-\n-    vendor_ctrl_doc_sub = []\n-    vendor_ctrl_template = string.Template('''\n-/**\n- * \\\\brief Namespace for ${vendor} controls\n- */\n-namespace ${vendor} {\n-\n-${vendor_controls_str}\n-\n-} /* namespace ${vendor} */''')\n-\n-    for vendor in [v for v in ctrls_doc.keys() if v not in ['libcamera']]:\n-        vendor_ctrl_doc_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\\n\\n'.join(ctrls_doc[vendor])}))\n-\n-    vendor_ctrl_def_sub = []\n-    for vendor in [v for v in ctrls_def.keys() if v not in ['libcamera']]:\n-        vendor_ctrl_def_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\\n'.join(ctrls_def[vendor])}))\n-\n-    return {\n-        'controls_doc': '\\n\\n'.join(ctrls_doc['libcamera']),\n-        'controls_def': '\\n'.join(ctrls_def['libcamera']),\n-        'controls_map': '\\n'.join(ctrls_map),\n-        'vendor_controls_doc': '\\n'.join(vendor_ctrl_doc_sub),\n-        'vendor_controls_def': '\\n'.join(vendor_ctrl_def_sub),\n-    }\n-\n-\n-def generate_h(controls, mode, ranges):\n-    enum_template_start = string.Template('''enum ${name}Enum {''')\n-    enum_value_template = string.Template('''\\t${name} = ${value},''')\n-    enum_values_template = string.Template('''extern const std::array<const ControlValue, ${size}> ${name}Values;''')\n-    name_value_map_template = string.Template('''extern const std::map<std::string, ${type}> ${name}NameValueMap;''')\n-    template = string.Template('''extern const Control<${type}> ${name};''')\n-\n-    ctrls = {}\n-    ids = {}\n-    id_value = {}\n-\n-    for ctrl in controls:\n-        id_name = snake_case(ctrl.name).upper()\n-\n-        vendor = ctrl.vendor\n-        if vendor not in ctrls:\n-            if vendor not in ranges.keys():\n-                raise RuntimeError(f'Control id range is not defined for vendor {vendor}')\n-            id_value[vendor] = ranges[vendor] + 1\n-            ids[vendor] = []\n-            ctrls[vendor] = []\n-\n-        target_ids = ids[vendor]\n-        target_ids.append('\\t' + id_name + ' = ' + str(id_value[vendor]) + ',')\n-\n-        info = {\n-            'name': ctrl.name,\n-            'type': ctrl.type,\n-        }\n-\n-        target_ctrls = ctrls[vendor]\n-\n-        if ctrl.is_enum:\n-            target_ctrls.append(enum_template_start.substitute(info))\n-\n-            num_entries = 0\n-            for enum in ctrl.enum_values:\n-                value_info = {\n-                    'name': enum.name,\n-                    'value': enum.value,\n-                }\n-                target_ctrls.append(enum_value_template.substitute(value_info))\n-                num_entries += 1\n-            target_ctrls.append(\"};\")\n-\n-            values_info = {\n-                'name': info['name'],\n-                'type': ctrl.type,\n-                'size': num_entries,\n-            }\n-            target_ctrls.append(enum_values_template.substitute(values_info))\n-            target_ctrls.append(name_value_map_template.substitute(values_info))\n-\n-        target_ctrls.append(template.substitute(info))\n-        id_value[vendor] += 1\n-\n-    vendor_template = string.Template('''\n-namespace ${vendor} {\n-\n-#define LIBCAMERA_HAS_${vendor_def}_VENDOR_${mode}\n-\n-enum {\n-${vendor_enums}\n-};\n-\n-${vendor_controls}\n-\n-} /* namespace ${vendor} */\n-''')\n-\n-    vendor_sub = []\n-    for vendor in [v for v in ctrls.keys() if v != 'libcamera']:\n-        vendor_sub.append(vendor_template.substitute({'mode': mode.upper(),\n-                                                      'vendor': vendor,\n-                                                      'vendor_def': vendor.upper(),\n-                                                      'vendor_enums': '\\n'.join(ids[vendor]),\n-                                                      'vendor_controls': '\\n'.join(ctrls[vendor])}))\n-\n-    return {\n-        'ids': '\\n'.join(ids['libcamera']),\n-        'controls': '\\n'.join(ctrls['libcamera']),\n-        'vendor_controls': '\\n'.join(vendor_sub)\n-    }\n-\n-\n-def fill_template(template, data):\n-\n-    template = open(template, 'rb').read()\n-    template = template.decode('utf-8')\n-    template = string.Template(template)\n-    return template.substitute(data)\n+    return ctrl\n \n \n def main(argv):\n@@ -358,29 +165,47 @@ def main(argv):\n         data = open(args.ranges, 'rb').read()\n         ranges = yaml.safe_load(data)['ranges']\n \n-    controls = []\n+    controls = {}\n     for input in args.input:\n-        with open(input, 'rb') as f:\n-            data = f.read()\n-            vendor = yaml.safe_load(data)['vendor']\n-            ctrls = yaml.safe_load(data)['controls']\n-            controls = controls + [Control(*ctrl.popitem(), vendor) for ctrl in ctrls]\n+        data = yaml.safe_load(open(input, 'rb').read())\n \n-    if args.template.endswith('.cpp.in'):\n-        data = generate_cpp(controls)\n-    elif args.template.endswith('.h.in'):\n-        data = generate_h(controls, args.mode, ranges)\n-    else:\n-        raise RuntimeError('Unknown template type')\n+        vendor = data['vendor']\n+        if vendor not in ranges.keys():\n+            raise RuntimeError(f'Control id range is not defined for vendor {vendor}')\n \n-    data = fill_template(args.template, data)\n+        ctrls = controls.setdefault(vendor, [])\n+\n+        for i, ctrl in enumerate(data['controls']):\n+            ctrl = Control(*ctrl.popitem(), vendor)\n+            ctrls.append(extend_control(ctrl, i, ranges))\n+\n+    # Sort the vendors by range numerical value\n+    controls = [[vendor, ctrls] for vendor, ctrls in controls.items()]\n+    controls.sort(key=lambda item: ranges[item[0]])\n+\n+    filename = {\n+        'controls': 'control_ids',\n+        'properties': 'property_ids',\n+    }[args.mode]\n+\n+    data = {\n+        'filename': filename,\n+        'mode': args.mode,\n+        'controls': controls,\n+    }\n+\n+    env = jinja2.Environment()\n+    env.filters['format_description'] = format_description\n+    env.filters['snake_case'] = snake_case\n+    template = env.from_string(open(args.template, 'r', encoding='utf-8').read())\n+    string = template.render(data)\n \n     if args.output:\n-        output = open(args.output, 'wb')\n-        output.write(data.encode('utf-8'))\n+        output = open(args.output, 'w', encoding='utf-8')\n+        output.write(string)\n         output.close()\n     else:\n-        sys.stdout.write(data)\n+        sys.stdout.write(string)\n \n     return 0\n \n",
    "prefixes": [
        "07/10"
    ]
}