{"id":2060,"url":"https://patchwork.libcamera.org/api/patches/2060/?format=json","web_url":"https://patchwork.libcamera.org/patch/2060/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20190929190254.18920-6-laurent.pinchart@ideasonboard.com>","date":"2019-09-29T19:02:46","name":"[libcamera-devel,v2,05/13] libcamera: controls: Auto-generate control_ids.h and control_ids.cpp","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"aee2493aff7fb782d3a134f0367291d622e0b30d","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/2060/mbox/","series":[{"id":512,"url":"https://patchwork.libcamera.org/api/series/512/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=512","date":"2019-09-29T19:02:41","name":"Improve the application-facing controls API","version":2,"mbox":"https://patchwork.libcamera.org/series/512/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/2060/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/2060/checks/","tags":{},"headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 130E76170D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 29 Sep 2019 21:03:13 +0200 (CEST)","from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id A283A813\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 29 Sep 2019 21:03:12 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1569783792;\n\tbh=QP82eA1FSJEOJh66iNGvuR7L6ETSutojYIEf77g0Qrg=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=GYkNPu5h8rHtQzfUdYTI88qMq9KBs1hEcQmSM+x9oWthfAlBePQv3h2TfaD2ddv9F\n\tB8zhY1EILf58cwesB4anjcsonLmcOoGLzNpFxi89ufztEzFAu9n6KIlTqj4Xc1z+ea\n\tAak3ZyxbrVfYqTBT9PTeHgFT0qduWnLyZUMbotzs=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Sun, 29 Sep 2019 22:02:46 +0300","Message-Id":"<20190929190254.18920-6-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.21.0","In-Reply-To":"<20190929190254.18920-1-laurent.pinchart@ideasonboard.com>","References":"<20190929190254.18920-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v2 05/13] libcamera: controls:\n\tAuto-generate control_ids.h and control_ids.cpp","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>","X-List-Received-Date":"Sun, 29 Sep 2019 19:03:14 -0000"},"content":"Bring back auto-generation of control ids. In this version, both the\nheader and the source files are generated from a single YAML file that\nstores all control definitions. This allows centralising controls in a\nsingle file, while the previous version required keeping both\ndeclarations (in a header) and documentation (in a the source) in sync\nmanually.\n\nUsing YAML as a format to store control definitions is a trade-off\nbetween ease of use (there are many YAML parsers available) and\nsimplicity (XML was considered, but would have lead to more complex\nprocessing). A new build time dependency is added on python3-yaml, which\nshould be available as a package in all distributions and build\nenvironments.\n\nThe YAML format is likely to change over time as we improve\ndocumentation of controls, the first version simply copies the\ninformation currently available. Future improvements should also include\na YAML schema to validate the YAML source file.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n Documentation/Doxyfile.in                     |   4 +-\n README.rst                                    |   2 +-\n .../{control_ids.h => control_ids.h.in}       |  16 +--\n include/libcamera/gen-header.sh               |   2 +-\n include/libcamera/meson.build                 |  18 ++-\n .../libcamera/libcamera-9999.ebuild           |   9 +-\n src/libcamera/control_ids.cpp                 |  52 --------\n src/libcamera/control_ids.cpp.in              |  25 ++++\n src/libcamera/control_ids.yaml                |  35 ++++++\n src/libcamera/gen-controls.py                 | 114 ++++++++++++++++++\n src/libcamera/meson.build                     |  12 +-\n 11 files changed, 215 insertions(+), 74 deletions(-)\n rename include/libcamera/{control_ids.h => control_ids.h.in} (53%)\n delete mode 100644 src/libcamera/control_ids.cpp\n create mode 100644 src/libcamera/control_ids.cpp.in\n create mode 100644 src/libcamera/control_ids.yaml\n create mode 100755 src/libcamera/gen-controls.py","diff":"diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in\nindex 28a9c2da1ad4..5237cf60854f 100644\n--- a/Documentation/Doxyfile.in\n+++ b/Documentation/Doxyfile.in\n@@ -793,7 +793,9 @@ WARN_LOGFILE           =\n \n INPUT                  = \"@TOP_SRCDIR@/include/ipa\" \\\n \t\t\t \"@TOP_SRCDIR@/include/libcamera\" \\\n-\t\t\t \"@TOP_SRCDIR@/src/libcamera\"\n+\t\t\t \"@TOP_SRCDIR@/src/libcamera\" \\\n+\t\t\t \"@TOP_BUILDDIR@/include/libcamera\" \\\n+\t\t\t \"@TOP_BUILDDIR@/src/libcamera\"\n \n # This tag can be used to specify the character encoding of the source files\n # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\ndiff --git a/README.rst b/README.rst\nindex 169837e41a4e..2ccf7cbec40a 100644\n--- a/README.rst\n+++ b/README.rst\n@@ -40,7 +40,7 @@ A C++ toolchain: [required]\n \tEither {g++, clang}\n \n for libcamera: [required]\n-\tmeson ninja-build\n+\tmeson ninja-build python3-yaml\n \n for device hotplug enumeration: [optional]\n \tpkg-config libudev-dev\ndiff --git a/include/libcamera/control_ids.h b/include/libcamera/control_ids.h.in\nsimilarity index 53%\nrename from include/libcamera/control_ids.h\nrename to include/libcamera/control_ids.h.in\nindex 54235f1aea95..1d0bc791e559 100644\n--- a/include/libcamera/control_ids.h\n+++ b/include/libcamera/control_ids.h.in\n@@ -3,6 +3,8 @@\n  * Copyright (C) 2019, Google Inc.\n  *\n  * control_ids.h : Control ID list\n+ *\n+ * This file is auto-generated. Do not edit.\n  */\n \n #ifndef __LIBCAMERA_CONTROL_IDS_H__\n@@ -17,20 +19,10 @@ namespace libcamera {\n namespace controls {\n \n enum {\n-\tAWB_ENABLE = 1,\n-\tBRIGHTNESS = 2,\n-\tCONTRAST = 3,\n-\tSATURATION = 4,\n-\tMANUAL_EXPOSURE = 5,\n-\tMANUAL_GAIN = 6,\n+${ids}\n };\n \n-extern const Control<bool> AwbEnable;\n-extern const Control<int32_t> Brightness;\n-extern const Control<int32_t> Contrast;\n-extern const Control<int32_t> Saturation;\n-extern const Control<int32_t> ManualExposure;\n-extern const Control<int32_t> ManualGain;\n+${controls}\n \n } /* namespace controls */\n \ndiff --git a/include/libcamera/gen-header.sh b/include/libcamera/gen-header.sh\nindex a69fe8e982a1..7f7816c9f879 100755\n--- a/include/libcamera/gen-header.sh\n+++ b/include/libcamera/gen-header.sh\n@@ -19,7 +19,7 @@ EOF\n headers=$(for header in \"$src_dir\"/*.h ; do\n \theader=$(basename \"$header\")\n \techo \"$header\"\n-done ; echo \"version.h\" | sort)\n+done ; echo \"control_ids.h\" ; echo \"version.h\" | sort)\n \n for header in $headers ; do\n \techo \"#include <libcamera/$header>\" >> \"$dst_file\"\ndiff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\nindex 868f1a6bf1ab..4ffbdab3b173 100644\n--- a/include/libcamera/meson.build\n+++ b/include/libcamera/meson.build\n@@ -3,7 +3,6 @@ libcamera_api = files([\n     'buffer.h',\n     'camera.h',\n     'camera_manager.h',\n-    'control_ids.h',\n     'controls.h',\n     'event_dispatcher.h',\n     'event_notifier.h',\n@@ -18,6 +17,20 @@ libcamera_api = files([\n \n include_dir = join_paths(libcamera_include_dir, 'libcamera')\n \n+install_headers(libcamera_api,\n+                subdir : include_dir)\n+\n+gen_controls = files('../../src/libcamera/gen-controls.py')\n+\n+control_ids_h = custom_target('control_ids_h',\n+                              input : files('../../src/libcamera/control_ids.yaml', 'control_ids.h.in'),\n+                              output : 'control_ids.h',\n+                              depend_files : gen_controls,\n+                              command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'],\n+                              install_dir : join_paths('include', include_dir))\n+\n+libcamera_api += control_ids_h\n+\n gen_header = files('gen-header.sh')\n \n libcamera_h = custom_target('gen-header',\n@@ -37,6 +50,3 @@ configure_file(input : 'version.h.in',\n                output : 'version.h',\n                configuration : libcamera_version_config,\n                install_dir : join_paths('include', include_dir))\n-\n-install_headers(libcamera_api,\n-                subdir : include_dir)\ndiff --git a/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild b/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild\nindex fed2b409a91b..fc241b1f5584 100644\n--- a/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild\n+++ b/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild\n@@ -2,7 +2,9 @@\n # Distributed under the terms of the GNU General Public License v2\n \n EAPI=6\n-inherit git-r3 meson\n+PYTHON_COMPAT=( python3_{5,6,7} )\n+\n+inherit git-r3 meson python-any-r1\n \n DESCRIPTION=\"Camera support library for Linux\"\n HOMEPAGE=\"http://libcamera.org\"\n@@ -15,7 +17,10 @@ KEYWORDS=\"*\"\n IUSE=\"udev\"\n \n RDEPEND=\"udev? ( virtual/libudev )\"\n-DEPEND=\"${RDEPEND}\"\n+DEPEND=\"\n+\t${RDEPEND}\n+\t$(python_gen_any_dep 'dev-python/pyyaml[${PYTHON_USEDEP}]')\n+\"\n \n src_configure() {\n \tlocal emesonargs=(\ndiff --git a/src/libcamera/control_ids.cpp b/src/libcamera/control_ids.cpp\ndeleted file mode 100644\nindex 3af23a458862..000000000000\n--- a/src/libcamera/control_ids.cpp\n+++ /dev/null\n@@ -1,52 +0,0 @@\n-/* SPDX-License-Identifier: LGPL-2.1-or-later */\n-/*\n- * Copyright (C) 2019, Google Inc.\n- *\n- * control_ids.cpp : Control ID list\n- */\n-\n-#include <libcamera/control_ids.h>\n-\n-/**\n- * \\file control_ids.h\n- * \\brief Camera control identifiers\n- */\n-\n-namespace libcamera {\n-\n-namespace controls {\n-\n-/**\n- * \\brief Enables or disables the AWB.\n- * \\sa ManualGain\n- */\n-extern const Control<bool> AwbEnable(AWB_ENABLE, \"AwbEnable\");\n-\n-/**\n- * \\brief Specify a fixed brightness parameter.\n- */\n-extern const Control<int32_t> Brightness(BRIGHTNESS, \"Brightness\");\n-\n-/**\n- * \\brief Specify a fixed contrast parameter.\n- */\n-extern const Control<int32_t> Contrast(CONTRAST, \"Contrast\");\n-\n-/**\n- * \\brief Specify a fixed saturation parameter.\n- */\n-extern const Control<int32_t> Saturation(SATURATION, \"Saturation\");\n-\n-/**\n- * \\brief Specify a fixed exposure time in milli-seconds\n- */\n-extern const Control<int32_t> ManualExposure(MANUAL_EXPOSURE, \"ManualExposure\");\n-\n-/**\n- * \\brief Specify a fixed gain parameter\n- */\n-extern const Control<int32_t> ManualGain(MANUAL_GAIN, \"ManualGain\");\n-\n-} /* namespace controls */\n-\n-} /* namespace libcamera */\ndiff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in\nnew file mode 100644\nindex 000000000000..f699ac9eea54\n--- /dev/null\n+++ b/src/libcamera/control_ids.cpp.in\n@@ -0,0 +1,25 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * control_ids.cpp : Control ID list\n+ *\n+ * This file is auto-generated. Do not edit.\n+ */\n+\n+#include <libcamera/control_ids.h>\n+\n+/**\n+ * \\file control_ids.h\n+ * \\brief Camera control identifiers\n+ */\n+\n+namespace libcamera {\n+\n+namespace controls {\n+\n+${controls}\n+\n+} /* namespace controls */\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml\nnew file mode 100644\nindex 000000000000..819a5967a2fc\n--- /dev/null\n+++ b/src/libcamera/control_ids.yaml\n@@ -0,0 +1,35 @@\n+# SPDX-License-Identifier: LGPL-2.1-or-later\n+#\n+# Copyright (C) 2019, Google Inc.\n+#\n+%YAML 1.2\n+---\n+controls:\n+  - AwbEnable:\n+      type: bool\n+      description: |\n+        Enables or disables the AWB.\n+\n+        \\sa ManualGain\n+\n+  - Brightness:\n+      type: int32_t\n+      description: Specify a fixed brightness parameter\n+\n+  - Contrast:\n+      type: int32_t\n+      description: Specify a fixed contrast parameter\n+\n+  - Saturation:\n+      type: int32_t\n+      description: Specify a fixed saturation parameter\n+\n+  - ManualExposure:\n+      type: int32_t\n+      description: Specify a fixed exposure time in milli-seconds\n+\n+  - ManualGain:\n+      type: int32_t\n+      description: Specify a fixed gain parameter\n+\n+...\ndiff --git a/src/libcamera/gen-controls.py b/src/libcamera/gen-controls.py\nnew file mode 100755\nindex 000000000000..0899e40b4080\n--- /dev/null\n+++ b/src/libcamera/gen-controls.py\n@@ -0,0 +1,114 @@\n+#!/usr/bin/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+# gen-controls.py - Generate control definitions from YAML\n+\n+import argparse\n+import string\n+import sys\n+import yaml\n+\n+\n+def snake_case(s):\n+    return ''.join([c.isupper() and ('_' + c) or c for c in s]).strip('_')\n+\n+\n+def generate_cpp(controls):\n+    template = string.Template('''/**\n+${description}\n+ */\n+extern const Control<${type}> ${name}(${id_name}, \"${name}\");''')\n+\n+    ctrls = []\n+\n+    for ctrl in controls:\n+        name, ctrl = ctrl.popitem()\n+        id_name = snake_case(name).upper()\n+\n+        description = ctrl['description'].strip('\\n').split('\\n')\n+        description[0] = '\\\\brief ' + description[0]\n+        description = '\\n'.join([(line and ' * ' or ' *') + line for line in description])\n+\n+        info = {\n+            'name': name,\n+            'type': ctrl['type'],\n+            'description': description,\n+            'id_name': id_name,\n+        }\n+\n+        ctrls.append(template.substitute(info))\n+\n+    return {'controls': '\\n\\n'.join(ctrls)}\n+\n+\n+def generate_h(controls):\n+    template = string.Template('''extern const Control<${type}> ${name};''')\n+\n+    ctrls = []\n+    ids = []\n+    id_value = 1\n+\n+    for ctrl in controls:\n+        name, ctrl = ctrl.popitem()\n+        id_name = snake_case(name).upper()\n+\n+        ids.append('\\t' + id_name + ' = ' + str(id_value) + ',')\n+\n+        info = {\n+            'name': name,\n+            'type': ctrl['type'],\n+        }\n+\n+        ctrls.append(template.substitute(info))\n+        id_value += 1\n+\n+    return {'ids': '\\n'.join(ids), 'controls': '\\n'.join(ctrls)}\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+\n+\n+def main(argv):\n+\n+    # Parse command line arguments\n+    parser = argparse.ArgumentParser()\n+    parser.add_argument('-o', dest='output', metavar='file', type=str,\n+                        help='Output file name. Defaults to standard output if not specified.')\n+    parser.add_argument('input', type=str,\n+                        help='Input file name.')\n+    parser.add_argument('template', type=str,\n+                        help='Template file name.')\n+    args = parser.parse_args(argv[1:])\n+\n+    data = open(args.input, 'rb').read()\n+    controls = yaml.safe_load(data)['controls']\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)\n+    else:\n+        raise RuntimeError('Unknown template type')\n+\n+    data = fill_template(args.template, data)\n+\n+    if args.output:\n+        output = open(args.output, 'wb')\n+        output.write(data.encode('utf-8'))\n+        output.close()\n+    else:\n+        sys.stdout.write(data)\n+\n+    return 0\n+\n+\n+if __name__ == '__main__':\n+    sys.exit(main(sys.argv))\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 8123d1d5bee9..6df48365266d 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -5,7 +5,6 @@ libcamera_sources = files([\n     'camera_manager.cpp',\n     'camera_sensor.cpp',\n     'controls.cpp',\n-    'control_ids.cpp',\n     'device_enumerator.cpp',\n     'device_enumerator_sysfs.cpp',\n     'event_dispatcher.cpp',\n@@ -58,6 +57,17 @@ if libudev.found()\n     ])\n endif\n \n+gen_controls = files('gen-controls.py')\n+\n+control_ids_cpp = custom_target('control_ids_cpp',\n+                                input : files('control_ids.yaml', 'control_ids.cpp.in'),\n+                                output : 'control_ids.cpp',\n+                                depend_files : gen_controls,\n+                                command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'])\n+\n+libcamera_sources += control_ids_cpp\n+#libcamera_sources += control_ids_h\n+\n gen_version = join_paths(meson.source_root(), 'utils', 'gen-version.sh')\n \n version_cpp = vcs_tag(command : [gen_version, meson.build_root()],\n","prefixes":["libcamera-devel","v2","05/13"]}