mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-20 19:05:05 +03:00
py: gen-py-controls: Convert to jinja2 templates
Jinja2 templates help separate the logic related to the template from the generation of the data. The python code becomes much clearer as a result. As an added bonus, we can use a single template file for both controls and properties. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Acked-by: Paul Elder <paul.elder@ideasonboard.com>
This commit is contained in:
parent
5c1cb5e5bc
commit
39f01e9185
4 changed files with 78 additions and 109 deletions
|
@ -4,7 +4,7 @@
|
||||||
# Generate Python bindings controls from YAML
|
# Generate Python bindings controls from YAML
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import string
|
import jinja2
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
@ -23,35 +23,16 @@ def find_common_prefix(strings):
|
||||||
return prefix
|
return prefix
|
||||||
|
|
||||||
|
|
||||||
def generate_py(controls, mode):
|
def extend_control(ctrl, mode):
|
||||||
out = ''
|
if ctrl.vendor != 'libcamera':
|
||||||
|
ctrl.klass = ctrl.vendor
|
||||||
vendors_class_def = []
|
ctrl.namespace = f'{ctrl.vendor}::'
|
||||||
vendor_defs = []
|
|
||||||
vendors = []
|
|
||||||
for vendor, ctrl_list in controls.items():
|
|
||||||
for ctrl in ctrl_list:
|
|
||||||
if vendor not in vendors and vendor != 'libcamera':
|
|
||||||
vendor_mode_str = f'{vendor.capitalize()}{mode.capitalize()}'
|
|
||||||
vendors_class_def.append('class Py{}\n{{\n}};\n'.format(vendor_mode_str))
|
|
||||||
vendor_defs.append('\tauto {} = py::class_<Py{}>(controls, \"{}\");'.format(vendor, vendor_mode_str, vendor))
|
|
||||||
vendors.append(vendor)
|
|
||||||
|
|
||||||
if vendor != 'libcamera':
|
|
||||||
ns = 'libcamera::{}::{}::'.format(mode, vendor)
|
|
||||||
container = vendor
|
|
||||||
else:
|
else:
|
||||||
ns = 'libcamera::{}::'.format(mode)
|
ctrl.klass = mode
|
||||||
container = 'controls'
|
ctrl.namespace = ''
|
||||||
|
|
||||||
out += f'\t{container}.def_readonly_static("{ctrl.name}", static_cast<const libcamera::ControlId *>(&{ns}{ctrl.name}));\n\n'
|
|
||||||
|
|
||||||
if not ctrl.is_enum:
|
if not ctrl.is_enum:
|
||||||
continue
|
return ctrl
|
||||||
|
|
||||||
cpp_enum = ctrl.name + 'Enum'
|
|
||||||
|
|
||||||
out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum)
|
|
||||||
|
|
||||||
if mode == 'controls':
|
if mode == 'controls':
|
||||||
# Adjustments for controls
|
# Adjustments for controls
|
||||||
|
@ -63,27 +44,18 @@ def generate_py(controls, mode):
|
||||||
# Adjustments for properties
|
# Adjustments for properties
|
||||||
prefix = find_common_prefix([e.name for e in ctrl.enum_values])
|
prefix = find_common_prefix([e.name for e in ctrl.enum_values])
|
||||||
|
|
||||||
for entry in ctrl.enum_values:
|
for enum in ctrl.enum_values:
|
||||||
cpp_enum = entry.name
|
enum.py_name = enum.name[len(prefix):]
|
||||||
py_enum = entry.name[len(prefix):]
|
|
||||||
|
|
||||||
out += '\t\t.value(\"{}\", {}{})\n'.format(py_enum, ns, cpp_enum)
|
return ctrl
|
||||||
|
|
||||||
out += '\t;\n\n'
|
|
||||||
|
|
||||||
return {'controls': out,
|
|
||||||
'vendors_class_def': '\n'.join(vendors_class_def),
|
|
||||||
'vendors_defs': '\n'.join(vendor_defs)}
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
def main(argv):
|
||||||
|
headers = {
|
||||||
|
'controls': 'control_ids.h',
|
||||||
|
'properties': 'property_ids.h',
|
||||||
|
}
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--mode', '-m', type=str, required=True,
|
parser.add_argument('--mode', '-m', type=str, required=True,
|
||||||
|
@ -96,27 +68,41 @@ def main(argv):
|
||||||
help='Input file name.')
|
help='Input file name.')
|
||||||
args = parser.parse_args(argv[1:])
|
args = parser.parse_args(argv[1:])
|
||||||
|
|
||||||
if args.mode not in ['controls', 'properties']:
|
if not headers.get(args.mode):
|
||||||
print(f'Invalid mode option "{args.mode}"', file=sys.stderr)
|
print(f'Invalid mode option "{args.mode}"', file=sys.stderr)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
controls = {}
|
controls = []
|
||||||
|
vendors = []
|
||||||
|
|
||||||
for input in args.input:
|
for input in args.input:
|
||||||
data = yaml.safe_load(open(input, 'rb').read())
|
data = yaml.safe_load(open(input, 'rb').read())
|
||||||
|
|
||||||
vendor = data['vendor']
|
vendor = data['vendor']
|
||||||
ctrls = data['controls']
|
if vendor != 'libcamera':
|
||||||
controls[vendor] = [Control(*ctrl.popitem(), vendor) for ctrl in ctrls]
|
vendors.append(vendor)
|
||||||
|
|
||||||
data = generate_py(controls, args.mode)
|
for ctrl in data['controls']:
|
||||||
|
ctrl = Control(*ctrl.popitem(), vendor)
|
||||||
|
controls.append(extend_control(ctrl, args.mode))
|
||||||
|
|
||||||
data = fill_template(args.template, data)
|
data = {
|
||||||
|
'mode': args.mode,
|
||||||
|
'header': headers[args.mode],
|
||||||
|
'vendors': vendors,
|
||||||
|
'controls': controls,
|
||||||
|
}
|
||||||
|
|
||||||
|
env = jinja2.Environment()
|
||||||
|
template = env.from_string(open(args.template, 'r', encoding='utf-8').read())
|
||||||
|
string = template.render(data)
|
||||||
|
|
||||||
if args.output:
|
if args.output:
|
||||||
output = open(args.output, 'wb')
|
output = open(args.output, 'w', encoding='utf-8')
|
||||||
output.write(data.encode('utf-8'))
|
output.write(string)
|
||||||
output.close()
|
output.close()
|
||||||
else:
|
else:
|
||||||
sys.stdout.write(data)
|
sys.stdout.write(string)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ pycamera_sources = files([
|
||||||
'py_transform.cpp',
|
'py_transform.cpp',
|
||||||
])
|
])
|
||||||
|
|
||||||
# Generate controls
|
# Generate controls and properties
|
||||||
|
|
||||||
gen_py_controls_template = files('py_controls_generated.cpp.in')
|
gen_py_controls_template = files('py_controls_generated.cpp.in')
|
||||||
gen_py_controls = files('gen-py-controls.py')
|
gen_py_controls = files('gen-py-controls.py')
|
||||||
|
@ -38,15 +38,11 @@ pycamera_sources += custom_target('py_gen_controls',
|
||||||
'-t', gen_py_controls_template, '@INPUT@'],
|
'-t', gen_py_controls_template, '@INPUT@'],
|
||||||
env : py_build_env)
|
env : py_build_env)
|
||||||
|
|
||||||
# Generate properties
|
|
||||||
|
|
||||||
gen_py_properties_template = files('py_properties_generated.cpp.in')
|
|
||||||
|
|
||||||
pycamera_sources += custom_target('py_gen_properties',
|
pycamera_sources += custom_target('py_gen_properties',
|
||||||
input : properties_files,
|
input : properties_files,
|
||||||
output : ['py_properties_generated.cpp'],
|
output : ['py_properties_generated.cpp'],
|
||||||
command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@',
|
command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@',
|
||||||
'-t', gen_py_properties_template, '@INPUT@'],
|
'-t', gen_py_controls_template, '@INPUT@'],
|
||||||
env : py_build_env)
|
env : py_build_env)
|
||||||
|
|
||||||
# Generate formats
|
# Generate formats
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
|
* Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
|
||||||
*
|
*
|
||||||
* Python bindings - Auto-generated controls
|
* Python bindings - Auto-generated {{mode}}
|
||||||
*
|
*
|
||||||
* This file is auto-generated. Do not edit.
|
* This file is auto-generated. Do not edit.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libcamera/control_ids.h>
|
#include <libcamera/{{header}}>
|
||||||
|
|
||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
@ -15,16 +15,33 @@
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
|
|
||||||
class PyControls
|
class Py{{mode|capitalize}}
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
${vendors_class_def}
|
{% for vendor in vendors -%}
|
||||||
|
class Py{{vendor|capitalize}}{{mode|capitalize}}
|
||||||
void init_py_controls_generated(py::module& m)
|
|
||||||
{
|
{
|
||||||
auto controls = py::class_<PyControls>(m, "controls");
|
};
|
||||||
${vendors_defs}
|
|
||||||
|
|
||||||
${controls}
|
{% endfor -%}
|
||||||
|
|
||||||
|
void init_py_{{mode}}_generated(py::module& m)
|
||||||
|
{
|
||||||
|
auto {{mode}} = py::class_<Py{{mode|capitalize}}>(m, "{{mode}}");
|
||||||
|
{%- for vendor in vendors %}
|
||||||
|
auto {{vendor}} = py::class_<Py{{vendor|capitalize}}{{mode|capitalize}}>({{mode}}, "{{vendor}}");
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
{% for ctrl in controls %}
|
||||||
|
{{ctrl.klass}}.def_readonly_static("{{ctrl.name}}", static_cast<const libcamera::ControlId *>(&libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}));
|
||||||
|
{%- if ctrl.is_enum %}
|
||||||
|
|
||||||
|
py::enum_<libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}Enum>({{ctrl.klass}}, "{{ctrl.name}}Enum")
|
||||||
|
{%- for enum in ctrl.enum_values %}
|
||||||
|
.value("{{enum.py_name}}", libcamera::{{mode}}::{{ctrl.namespace}}{{enum.name}})
|
||||||
|
{%- endfor %}
|
||||||
|
;
|
||||||
|
{%- endif %}
|
||||||
|
{% endfor -%}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
|
|
||||||
*
|
|
||||||
* Python bindings - Auto-generated properties
|
|
||||||
*
|
|
||||||
* This file is auto-generated. Do not edit.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libcamera/property_ids.h>
|
|
||||||
|
|
||||||
#include <pybind11/pybind11.h>
|
|
||||||
|
|
||||||
#include "py_main.h"
|
|
||||||
|
|
||||||
namespace py = pybind11;
|
|
||||||
|
|
||||||
class PyProperties
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
${vendors_class_def}
|
|
||||||
|
|
||||||
void init_py_properties_generated(py::module& m)
|
|
||||||
{
|
|
||||||
auto controls = py::class_<PyProperties>(m, "properties");
|
|
||||||
${vendors_defs}
|
|
||||||
|
|
||||||
${controls}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue