[libcamera-devel] [PATCH v2 05/13] libcamera: controls: Auto-generate control_ids.h and control_ids.cpp

Niklas Söderlund niklas.soderlund at ragnatech.se
Thu Oct 3 21:31:14 CEST 2019


Hi Laurent,

Thanks for your patch.

On 2019-09-29 22:02:46 +0300, Laurent Pinchart wrote:
> Bring back auto-generation of control ids. In this version, both the
> header and the source files are generated from a single YAML file that
> stores all control definitions. This allows centralising controls in a
> single file, while the previous version required keeping both
> declarations (in a header) and documentation (in a the source) in sync
> manually.
> 
> Using YAML as a format to store control definitions is a trade-off
> between ease of use (there are many YAML parsers available) and
> simplicity (XML was considered, but would have lead to more complex
> processing). A new build time dependency is added on python3-yaml, which
> should be available as a package in all distributions and build
> environments.
> 
> The YAML format is likely to change over time as we improve
> documentation of controls, the first version simply copies the
> information currently available. Future improvements should also include
> a YAML schema to validate the YAML source file.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
>  Documentation/Doxyfile.in                     |   4 +-
>  README.rst                                    |   2 +-
>  .../{control_ids.h => control_ids.h.in}       |  16 +--
>  include/libcamera/gen-header.sh               |   2 +-
>  include/libcamera/meson.build                 |  18 ++-
>  .../libcamera/libcamera-9999.ebuild           |   9 +-
>  src/libcamera/control_ids.cpp                 |  52 --------
>  src/libcamera/control_ids.cpp.in              |  25 ++++
>  src/libcamera/control_ids.yaml                |  35 ++++++
>  src/libcamera/gen-controls.py                 | 114 ++++++++++++++++++
>  src/libcamera/meson.build                     |  12 +-
>  11 files changed, 215 insertions(+), 74 deletions(-)
>  rename include/libcamera/{control_ids.h => control_ids.h.in} (53%)
>  delete mode 100644 src/libcamera/control_ids.cpp
>  create mode 100644 src/libcamera/control_ids.cpp.in
>  create mode 100644 src/libcamera/control_ids.yaml
>  create mode 100755 src/libcamera/gen-controls.py
> 
> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> index 28a9c2da1ad4..5237cf60854f 100644
> --- a/Documentation/Doxyfile.in
> +++ b/Documentation/Doxyfile.in
> @@ -793,7 +793,9 @@ WARN_LOGFILE           =
>  
>  INPUT                  = "@TOP_SRCDIR@/include/ipa" \
>  			 "@TOP_SRCDIR@/include/libcamera" \
> -			 "@TOP_SRCDIR@/src/libcamera"
> +			 "@TOP_SRCDIR@/src/libcamera" \
> +			 "@TOP_BUILDDIR@/include/libcamera" \
> +			 "@TOP_BUILDDIR@/src/libcamera"
>  
>  # This tag can be used to specify the character encoding of the source files
>  # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
> diff --git a/README.rst b/README.rst
> index 169837e41a4e..2ccf7cbec40a 100644
> --- a/README.rst
> +++ b/README.rst
> @@ -40,7 +40,7 @@ A C++ toolchain: [required]
>  	Either {g++, clang}
>  
>  for libcamera: [required]
> -	meson ninja-build
> +	meson ninja-build python3-yaml
>  
>  for device hotplug enumeration: [optional]
>  	pkg-config libudev-dev
> diff --git a/include/libcamera/control_ids.h b/include/libcamera/control_ids.h.in
> similarity index 53%
> rename from include/libcamera/control_ids.h
> rename to include/libcamera/control_ids.h.in
> index 54235f1aea95..1d0bc791e559 100644
> --- a/include/libcamera/control_ids.h
> +++ b/include/libcamera/control_ids.h.in
> @@ -3,6 +3,8 @@
>   * Copyright (C) 2019, Google Inc.
>   *
>   * control_ids.h : Control ID list
> + *
> + * This file is auto-generated. Do not edit.
>   */
>  
>  #ifndef __LIBCAMERA_CONTROL_IDS_H__
> @@ -17,20 +19,10 @@ namespace libcamera {
>  namespace controls {
>  
>  enum {
> -	AWB_ENABLE = 1,
> -	BRIGHTNESS = 2,
> -	CONTRAST = 3,
> -	SATURATION = 4,
> -	MANUAL_EXPOSURE = 5,
> -	MANUAL_GAIN = 6,
> +${ids}
>  };
>  
> -extern const Control<bool> AwbEnable;
> -extern const Control<int32_t> Brightness;
> -extern const Control<int32_t> Contrast;
> -extern const Control<int32_t> Saturation;
> -extern const Control<int32_t> ManualExposure;
> -extern const Control<int32_t> ManualGain;
> +${controls}
>  
>  } /* namespace controls */
>  
> diff --git a/include/libcamera/gen-header.sh b/include/libcamera/gen-header.sh
> index a69fe8e982a1..7f7816c9f879 100755
> --- a/include/libcamera/gen-header.sh
> +++ b/include/libcamera/gen-header.sh
> @@ -19,7 +19,7 @@ EOF
>  headers=$(for header in "$src_dir"/*.h ; do
>  	header=$(basename "$header")
>  	echo "$header"
> -done ; echo "version.h" | sort)
> +done ; echo "control_ids.h" ; echo "version.h" | sort)
>  
>  for header in $headers ; do
>  	echo "#include <libcamera/$header>" >> "$dst_file"
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index 868f1a6bf1ab..4ffbdab3b173 100644
> --- a/include/libcamera/meson.build
> +++ b/include/libcamera/meson.build
> @@ -3,7 +3,6 @@ libcamera_api = files([
>      'buffer.h',
>      'camera.h',
>      'camera_manager.h',
> -    'control_ids.h',
>      'controls.h',
>      'event_dispatcher.h',
>      'event_notifier.h',
> @@ -18,6 +17,20 @@ libcamera_api = files([
>  
>  include_dir = join_paths(libcamera_include_dir, 'libcamera')
>  
> +install_headers(libcamera_api,
> +                subdir : include_dir)
> +
> +gen_controls = files('../../src/libcamera/gen-controls.py')
> +
> +control_ids_h = custom_target('control_ids_h',
> +                              input : files('../../src/libcamera/control_ids.yaml', 'control_ids.h.in'),
> +                              output : 'control_ids.h',
> +                              depend_files : gen_controls,
> +                              command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'],
> +                              install_dir : join_paths('include', include_dir))
> +
> +libcamera_api += control_ids_h
> +
>  gen_header = files('gen-header.sh')
>  
>  libcamera_h = custom_target('gen-header',
> @@ -37,6 +50,3 @@ configure_file(input : 'version.h.in',
>                 output : 'version.h',
>                 configuration : libcamera_version_config,
>                 install_dir : join_paths('include', include_dir))
> -
> -install_headers(libcamera_api,
> -                subdir : include_dir)
> diff --git a/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild b/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild
> index fed2b409a91b..fc241b1f5584 100644
> --- a/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild
> +++ b/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild
> @@ -2,7 +2,9 @@
>  # Distributed under the terms of the GNU General Public License v2
>  
>  EAPI=6
> -inherit git-r3 meson
> +PYTHON_COMPAT=( python3_{5,6,7} )
> +
> +inherit git-r3 meson python-any-r1
>  
>  DESCRIPTION="Camera support library for Linux"
>  HOMEPAGE="http://libcamera.org"
> @@ -15,7 +17,10 @@ KEYWORDS="*"
>  IUSE="udev"
>  
>  RDEPEND="udev? ( virtual/libudev )"
> -DEPEND="${RDEPEND}"
> +DEPEND="
> +	${RDEPEND}
> +	$(python_gen_any_dep 'dev-python/pyyaml[${PYTHON_USEDEP}]')
> +"
>  
>  src_configure() {
>  	local emesonargs=(
> diff --git a/src/libcamera/control_ids.cpp b/src/libcamera/control_ids.cpp
> deleted file mode 100644
> index 3af23a458862..000000000000
> --- a/src/libcamera/control_ids.cpp
> +++ /dev/null
> @@ -1,52 +0,0 @@
> -/* SPDX-License-Identifier: LGPL-2.1-or-later */
> -/*
> - * Copyright (C) 2019, Google Inc.
> - *
> - * control_ids.cpp : Control ID list
> - */
> -
> -#include <libcamera/control_ids.h>
> -
> -/**
> - * \file control_ids.h
> - * \brief Camera control identifiers
> - */
> -
> -namespace libcamera {
> -
> -namespace controls {
> -
> -/**
> - * \brief Enables or disables the AWB.
> - * \sa ManualGain
> - */
> -extern const Control<bool> AwbEnable(AWB_ENABLE, "AwbEnable");
> -
> -/**
> - * \brief Specify a fixed brightness parameter.
> - */
> -extern const Control<int32_t> Brightness(BRIGHTNESS, "Brightness");
> -
> -/**
> - * \brief Specify a fixed contrast parameter.
> - */
> -extern const Control<int32_t> Contrast(CONTRAST, "Contrast");
> -
> -/**
> - * \brief Specify a fixed saturation parameter.
> - */
> -extern const Control<int32_t> Saturation(SATURATION, "Saturation");
> -
> -/**
> - * \brief Specify a fixed exposure time in milli-seconds
> - */
> -extern const Control<int32_t> ManualExposure(MANUAL_EXPOSURE, "ManualExposure");
> -
> -/**
> - * \brief Specify a fixed gain parameter
> - */
> -extern const Control<int32_t> ManualGain(MANUAL_GAIN, "ManualGain");
> -
> -} /* namespace controls */
> -
> -} /* namespace libcamera */
> diff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in
> new file mode 100644
> index 000000000000..f699ac9eea54
> --- /dev/null
> +++ b/src/libcamera/control_ids.cpp.in
> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * control_ids.cpp : Control ID list
> + *
> + * This file is auto-generated. Do not edit.
> + */
> +
> +#include <libcamera/control_ids.h>
> +
> +/**
> + * \file control_ids.h
> + * \brief Camera control identifiers
> + */
> +
> +namespace libcamera {
> +
> +namespace controls {
> +
> +${controls}
> +
> +} /* namespace controls */
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> new file mode 100644
> index 000000000000..819a5967a2fc
> --- /dev/null
> +++ b/src/libcamera/control_ids.yaml
> @@ -0,0 +1,35 @@
> +# SPDX-License-Identifier: LGPL-2.1-or-later
> +#
> +# Copyright (C) 2019, Google Inc.
> +#
> +%YAML 1.2
> +---
> +controls:
> +  - AwbEnable:
> +      type: bool
> +      description: |
> +        Enables or disables the AWB.
> +
> +        \sa ManualGain
> +
> +  - Brightness:
> +      type: int32_t
> +      description: Specify a fixed brightness parameter
> +
> +  - Contrast:
> +      type: int32_t
> +      description: Specify a fixed contrast parameter
> +
> +  - Saturation:
> +      type: int32_t
> +      description: Specify a fixed saturation parameter
> +
> +  - ManualExposure:
> +      type: int32_t
> +      description: Specify a fixed exposure time in milli-seconds
> +
> +  - ManualGain:
> +      type: int32_t
> +      description: Specify a fixed gain parameter
> +
> +...
> diff --git a/src/libcamera/gen-controls.py b/src/libcamera/gen-controls.py
> new file mode 100755
> index 000000000000..0899e40b4080
> --- /dev/null
> +++ b/src/libcamera/gen-controls.py
> @@ -0,0 +1,114 @@
> +#!/usr/bin/python3
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# Copyright (C) 2019, Google Inc.
> +#
> +# Author: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> +#
> +# gen-controls.py - Generate control definitions from YAML
> +
> +import argparse
> +import string
> +import sys
> +import yaml
> +
> +
> +def snake_case(s):
> +    return ''.join([c.isupper() and ('_' + c) or c for c in s]).strip('_')
> +
> +
> +def generate_cpp(controls):
> +    template = string.Template('''/**
> +${description}
> + */
> +extern const Control<${type}> ${name}(${id_name}, "${name}");''')
> +
> +    ctrls = []
> +
> +    for ctrl in controls:
> +        name, ctrl = ctrl.popitem()
> +        id_name = snake_case(name).upper()
> +
> +        description = ctrl['description'].strip('\n').split('\n')
> +        description[0] = '\\brief ' + description[0]
> +        description = '\n'.join([(line and ' * ' or ' *') + line for line in description])
> +
> +        info = {
> +            'name': name,
> +            'type': ctrl['type'],
> +            'description': description,
> +            'id_name': id_name,
> +        }
> +
> +        ctrls.append(template.substitute(info))
> +
> +    return {'controls': '\n\n'.join(ctrls)}
> +
> +
> +def generate_h(controls):
> +    template = string.Template('''extern const Control<${type}> ${name};''')
> +
> +    ctrls = []
> +    ids = []
> +    id_value = 1
> +
> +    for ctrl in controls:
> +        name, ctrl = ctrl.popitem()
> +        id_name = snake_case(name).upper()
> +
> +        ids.append('\t' + id_name + ' = ' + str(id_value) + ',')
> +
> +        info = {
> +            'name': name,
> +            'type': ctrl['type'],
> +        }
> +
> +        ctrls.append(template.substitute(info))
> +        id_value += 1
> +
> +    return {'ids': '\n'.join(ids), 'controls': '\n'.join(ctrls)}
> +
> +
> +def fill_template(template, data):
> +
> +    template = open(template, 'rb').read()
> +    template = template.decode('utf-8')
> +    template = string.Template(template)
> +    return template.substitute(data)
> +
> +
> +def main(argv):
> +
> +    # Parse command line arguments
> +    parser = argparse.ArgumentParser()
> +    parser.add_argument('-o', dest='output', metavar='file', type=str,
> +                        help='Output file name. Defaults to standard output if not specified.')
> +    parser.add_argument('input', type=str,
> +                        help='Input file name.')
> +    parser.add_argument('template', type=str,
> +                        help='Template file name.')
> +    args = parser.parse_args(argv[1:])
> +
> +    data = open(args.input, 'rb').read()
> +    controls = yaml.safe_load(data)['controls']
> +
> +    if args.template.endswith('.cpp.in'):
> +        data = generate_cpp(controls)
> +    elif args.template.endswith('.h.in'):
> +        data = generate_h(controls)
> +    else:
> +        raise RuntimeError('Unknown template type')
> +
> +    data = fill_template(args.template, data)
> +
> +    if args.output:
> +        output = open(args.output, 'wb')
> +        output.write(data.encode('utf-8'))
> +        output.close()
> +    else:
> +        sys.stdout.write(data)
> +
> +    return 0
> +
> +
> +if __name__ == '__main__':
> +    sys.exit(main(sys.argv))
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 8123d1d5bee9..6df48365266d 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -5,7 +5,6 @@ libcamera_sources = files([
>      'camera_manager.cpp',
>      'camera_sensor.cpp',
>      'controls.cpp',
> -    'control_ids.cpp',
>      'device_enumerator.cpp',
>      'device_enumerator_sysfs.cpp',
>      'event_dispatcher.cpp',
> @@ -58,6 +57,17 @@ if libudev.found()
>      ])
>  endif
>  
> +gen_controls = files('gen-controls.py')
> +
> +control_ids_cpp = custom_target('control_ids_cpp',
> +                                input : files('control_ids.yaml', 'control_ids.cpp.in'),
> +                                output : 'control_ids.cpp',
> +                                depend_files : gen_controls,
> +                                command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'])
> +
> +libcamera_sources += control_ids_cpp
> +#libcamera_sources += control_ids_h

I assume this comment out line should be removed right?

With that fixed,

Reviewed-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>

> +
>  gen_version = join_paths(meson.source_root(), 'utils', 'gen-version.sh')
>  
>  version_cpp = vcs_tag(command : [gen_version, meson.build_root()],
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

-- 
Regards,
Niklas Söderlund


More information about the libcamera-devel mailing list