From 6a226e95d1354e8adfffd18497280b5902fed701 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Thu, 30 Mar 2023 12:23:17 -0700 Subject: [PATCH 01/11] WIP --- ...gen-py-controls.py => codegen_controls.py} | 8 +- .../gen-py-formats.py => codegen_formats.py} | 0 src/py/libcamera/meson.build | 100 ------------------ src/py/meson.build | 24 ++++- src/py/setup.py | 58 ++++++++++ 5 files changed, 84 insertions(+), 106 deletions(-) rename src/py/{libcamera/gen-py-controls.py => codegen_controls.py} (94%) rename src/py/{libcamera/gen-py-formats.py => codegen_formats.py} (100%) delete mode 100644 src/py/libcamera/meson.build create mode 100644 src/py/setup.py diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/codegen_controls.py similarity index 94% rename from src/py/libcamera/gen-py-controls.py rename to src/py/codegen_controls.py index 99f3bbcf5..0d6eb24f0 100755 --- a/src/py/libcamera/gen-py-controls.py +++ b/src/py/codegen_controls.py @@ -77,7 +77,7 @@ def fill_template(template, data): def main(argv): # Parse command line arguments - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog="codegen_controls.py") 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, @@ -85,13 +85,11 @@ def main(argv): parser.add_argument('template', type=str, help='Template file name.') parser.add_argument('--mode', type=str, required=True, + choices=['controls', 'properties'], help='Mode is either "controls" or "properties"') + print(argv) args = parser.parse_args(argv[1:]) - if args.mode not in ['controls', 'properties']: - print(f'Invalid mode option "{args.mode}"', file=sys.stderr) - return -1 - data = open(args.input, 'rb').read() controls = yaml.safe_load(data)['controls'] diff --git a/src/py/libcamera/gen-py-formats.py b/src/py/codegen_formats.py similarity index 100% rename from src/py/libcamera/gen-py-formats.py rename to src/py/codegen_formats.py diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build deleted file mode 100644 index af19ffdd2..000000000 --- a/src/py/libcamera/meson.build +++ /dev/null @@ -1,100 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -py3_dep = dependency('python3', required : get_option('pycamera')) - -if not py3_dep.found() - pycamera_enabled = false - subdir_done() -endif - -pycamera_enabled = true - -pybind11_proj = subproject('pybind11') -pybind11_dep = pybind11_proj.get_variable('pybind11_dep') - -pycamera_sources = files([ - 'py_camera_manager.cpp', - 'py_enums.cpp', - 'py_geometry.cpp', - 'py_helpers.cpp', - 'py_main.cpp', -]) - -# Generate controls - -gen_py_controls_input_files = files([ - '../../libcamera/control_ids.yaml', - 'py_controls_generated.cpp.in', -]) - -gen_py_controls = files('gen-py-controls.py') - -pycamera_sources += custom_target('py_gen_controls', - input : gen_py_controls_input_files, - output : ['py_controls_generated.cpp'], - command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@']) - -# Generate properties - -gen_py_property_enums_input_files = files([ - '../../libcamera/property_ids.yaml', - 'py_properties_generated.cpp.in', -]) - -pycamera_sources += custom_target('py_gen_properties', - input : gen_py_property_enums_input_files, - output : ['py_properties_generated.cpp'], - command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@']) - -# Generate formats - -gen_py_formats_input_files = files([ - '../../libcamera/formats.yaml', - 'py_formats_generated.cpp.in', -]) - -gen_py_formats = files('gen-py-formats.py') - -pycamera_sources += custom_target('py_gen_formats', - input : gen_py_formats_input_files, - output : ['py_formats_generated.cpp'], - command : [gen_py_formats, '-o', '@OUTPUT@', '@INPUT@']) - -pycamera_deps = [ - libcamera_private, - py3_dep, - pybind11_dep, -] - -pycamera_args = [ - '-fvisibility=hidden', - '-Wno-shadow', - '-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT', -] - -destdir = get_option('libdir') / ('python' + py3_dep.version()) / 'site-packages' / 'libcamera' - -pycamera = shared_module('_libcamera', - pycamera_sources, - install : true, - install_dir : destdir, - name_prefix : '', - dependencies : pycamera_deps, - cpp_args : pycamera_args) - -# Create symlinks from the build dir to the source dir so that we can use the -# Python module directly from the build dir. - -run_command('ln', '-fsrT', files('__init__.py'), - meson.current_build_dir() / '__init__.py', - check: true) - -run_command('ln', '-fsrT', meson.current_source_dir() / 'utils', - meson.current_build_dir() / 'utils', - check: true) - -install_data(['__init__.py'], install_dir : destdir) - -# \todo Generate stubs when building. See https://peps.python.org/pep-0484/#stub-files -# Note: Depends on pybind11-stubgen. To generate pylibcamera stubs: -# $ PYTHONPATH=build/src/py pybind11-stubgen --no-setup-py -o build/src/py libcamera diff --git a/src/py/meson.build b/src/py/meson.build index a4586b4ae..2a8397978 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -1,3 +1,25 @@ # SPDX-License-Identifier: CC0-1.0 -subdir('libcamera') +py3_dep = dependency('python3', required : get_option('pycamera')) + +if not py3_dep.found() + pycamera_enabled = false + subdir_done() +endif + +pycamera_enabled = true + +# python = find_program('python', required : true) +py_setup_build = run_target('pycamera_setup_build', + command : ['python', 'setup.py', 'build'], + depends : [libcamera_h], +) + +custom_target('libcamera_python_bindings', + command: ['python', 'setup.py', 'build'], + depend_files: [libcamera_h], + output: ['libcamera/_libcamera.so'], +) + +meson.add_install_script('python', 'setup.py', 'install') + diff --git a/src/py/setup.py b/src/py/setup.py new file mode 100644 index 000000000..7a11987b5 --- /dev/null +++ b/src/py/setup.py @@ -0,0 +1,58 @@ +from glob import glob +from setuptools import setup +from pybind11.setup_helpers import Pybind11Extension + +from codegen_controls import main as gen_controls +from codegen_formats import main as gen_formats +from pybind11_stubgen import main as gen_stub + + +# NOTE(meawoppl) These can be vastly simplified to not go through +# the argparse and assorted tomfoolery that remains +gen_controls(["placeholder", + '--mode', 'controls', + '-o', "libcamera/py_controls_generated.cpp", + '../libcamera/control_ids.yaml', + 'libcamera/py_controls_generated.cpp.in', +]) + +gen_controls(["placeholder", + '--mode', 'properties', + '-o', 'libcamera/py_properties_generated.cpp', + '../libcamera/property_ids.yaml', + 'libcamera/py_properties_generated.cpp.in' +]) + +gen_formats(["placeholder", + '-o', 'libcamera/py_formats_generated.cpp', + '../libcamera/formats.yaml', + 'libcamera/py_formats_generated.cpp.in', +]) + +#gen_stub(["pybind11-stubgen", "--no-setup-py", "-o", "libcamera", "libcamera"]) + + +ext_modules = [ + Pybind11Extension( + "libcamera", + sorted(glob("libcamera/*.cpp")), # Sort source files for reproducibility + include_dirs=['../../include'], + extra_compile_args=[ + '-fvisibility=hidden', + '-Wno-shadow', + '-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT', + ], + + ), +] + +setup( + name='libcamera', + version='0.0.4', + description='Python wrapper to `libcamera`', + author='Libcamera Developers', + author_email=' libcamera-devel@lists.libcamera.org', + url='https://libcamera.org/', + packages=['libcamera', 'libcamera.utils'], + ext_modules=ext_modules +) \ No newline at end of file From 1ec6f1a80b7e03928d50e2aeebcd3001822801d7 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Thu, 30 Mar 2023 17:52:31 -0700 Subject: [PATCH 02/11] WIP --- src/py/libcamera/meson.build | 100 +++++++++++++++++++++++++++++++++++ src/py/meson.build | 41 +++++++++----- 2 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 src/py/libcamera/meson.build diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build new file mode 100644 index 000000000..efb0bee74 --- /dev/null +++ b/src/py/libcamera/meson.build @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: CC0-1.0 + +py3_dep = dependency('python3', required : get_option('pycamera')) + +if not py3_dep.found() + pycamera_enabled = false + subdir_done() +endif + +pycamera_enabled = true + +pybind11_proj = subproject('pybind11') +pybind11_dep = pybind11_proj.get_variable('pybind11_dep') + +pycamera_sources = files([ + 'py_camera_manager.cpp', + 'py_enums.cpp', + 'py_geometry.cpp', + 'py_helpers.cpp', + 'py_main.cpp', +]) + +# Generate controls + +gen_py_controls_input_files = files([ + '../../libcamera/control_ids.yaml', + 'py_controls_generated.cpp.in', +]) + +gen_py_controls = files('../codegen_controls.py') + +pycamera_sources += custom_target('py_gen_controls', + input : gen_py_controls_input_files, + output : ['py_controls_generated.cpp'], + command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@']) + +# Generate properties + +gen_py_property_enums_input_files = files([ + '../../libcamera/property_ids.yaml', + 'py_properties_generated.cpp.in', +]) + +pycamera_sources += custom_target('py_gen_properties', + input : gen_py_property_enums_input_files, + output : ['py_properties_generated.cpp'], + command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@']) + +# Generate formats + +gen_py_formats_input_files = files([ + '../../libcamera/formats.yaml', + 'py_formats_generated.cpp.in', +]) + +gen_py_formats = files('../codegen_formats.py') + +pycamera_sources += custom_target('py_gen_formats', + input : gen_py_formats_input_files, + output : ['py_formats_generated.cpp'], + command : [gen_py_formats, '-o', '@OUTPUT@', '@INPUT@']) + +pycamera_deps = [ + libcamera_private, + py3_dep, + pybind11_dep, +] + +pycamera_args = [ + '-fvisibility=hidden', + '-Wno-shadow', + '-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT', +] + +destdir = get_option('libdir') / ('python' + py3_dep.version()) / 'site-packages' / 'libcamera' + +pycamera = shared_module('_libcamera', + pycamera_sources, + install : true, + install_dir : destdir, + name_prefix : '', + dependencies : pycamera_deps, + cpp_args : pycamera_args) + +# Create symlinks from the build dir to the source dir so that we can use the +# Python module directly from the build dir. + +run_command('ln', '-fsrT', files('__init__.py'), + meson.current_build_dir() / '__init__.py', + check: true) + +run_command('ln', '-fsrT', meson.current_source_dir() / 'utils', + meson.current_build_dir() / 'utils', + check: true) + +install_data(['__init__.py'], install_dir : destdir) + +# \todo Generate stubs when building. See https://peps.python.org/pep-0484/#stub-files +# Note: Depends on pybind11-stubgen. To generate pylibcamera stubs: +# $ PYTHONPATH=build/src/py pybind11-stubgen --no-setup-py -o build/src/py libcamera diff --git a/src/py/meson.build b/src/py/meson.build index 2a8397978..8bd703ed0 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -1,25 +1,40 @@ # SPDX-License-Identifier: CC0-1.0 -py3_dep = dependency('python3', required : get_option('pycamera')) +py3 = find_program('python', required : get_option('pycamera')) -if not py3_dep.found() + +if not py3.found() + pycamera_enabled = false + subdir_done() +endif + +# Check to make sure version is sufficient +is_py3 = run_command(py3, '--version').stdout().strip().split()[1].split('.')[0].to_int() >= 3 + +if not is_py3 pycamera_enabled = false subdir_done() endif pycamera_enabled = true -# python = find_program('python', required : true) -py_setup_build = run_target('pycamera_setup_build', - command : ['python', 'setup.py', 'build'], - depends : [libcamera_h], -) +subdir('libcamera') +# # python = find_program('python', required : true) +# py_setup_build = run_target('pycamera_setup_build', +# command : [py3, 'setup.py', 'build'], +# depends : [libcamera_h], +# ) + +# # custom_target('libcamera_python_bindings', +# # command: ['python', 'setup.py', 'build'], +# # depend_files: [libcamera_h], +# # output: ['libcamera/_libcamera.so'], +# # ) -custom_target('libcamera_python_bindings', - command: ['python', 'setup.py', 'build'], - depend_files: [libcamera_h], - output: ['libcamera/_libcamera.so'], -) +# run_target('pycamera_install', +# command : ['python', 'setup.py', 'install'], +# depends : [py_setup_build], +# ) -meson.add_install_script('python', 'setup.py', 'install') +# meson.add_install_script('python', 'setup.py', 'install') From c0e7f25ab713702a782490b55a5a67f484cf1044 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Tue, 4 Apr 2023 13:15:14 -0700 Subject: [PATCH 03/11] WIP --- src/py/codegen_controls.py | 1 - src/py/libcamera/meson.build | 55 +++++++++++++++++++++++++++--------- src/py/meson.build | 51 +++++++++++++++++++++++++++++++-- src/py/setup.py | 47 +++++++++++++++++------------- 4 files changed, 117 insertions(+), 37 deletions(-) diff --git a/src/py/codegen_controls.py b/src/py/codegen_controls.py index 0d6eb24f0..ce6d85bba 100755 --- a/src/py/codegen_controls.py +++ b/src/py/codegen_controls.py @@ -87,7 +87,6 @@ def main(argv): parser.add_argument('--mode', type=str, required=True, choices=['controls', 'properties'], help='Mode is either "controls" or "properties"') - print(argv) args = parser.parse_args(argv[1:]) data = open(args.input, 'rb').read() diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index efb0bee74..97dbf4776 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -1,27 +1,25 @@ # SPDX-License-Identifier: CC0-1.0 -py3_dep = dependency('python3', required : get_option('pycamera')) - -if not py3_dep.found() - pycamera_enabled = false - subdir_done() -endif - -pycamera_enabled = true - -pybind11_proj = subproject('pybind11') -pybind11_dep = pybind11_proj.get_variable('pybind11_dep') - pycamera_sources = files([ + 'py_camera_manager.h', 'py_camera_manager.cpp', 'py_enums.cpp', 'py_geometry.cpp', + 'py_helpers.h', 'py_helpers.cpp', + 'py_main.h', 'py_main.cpp', ]) -# Generate controls +foreach f : pycamera_sources + configure_file( + input: f, + output: '@PLAINNAME@', + copy: true, + ) +endforeach +# Generate controls gen_py_controls_input_files = files([ '../../libcamera/control_ids.yaml', 'py_controls_generated.cpp.in', @@ -32,7 +30,9 @@ gen_py_controls = files('../codegen_controls.py') pycamera_sources += custom_target('py_gen_controls', input : gen_py_controls_input_files, output : ['py_controls_generated.cpp'], - command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@']) + command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@'], + build_always: true + ) # Generate properties @@ -60,6 +60,33 @@ pycamera_sources += custom_target('py_gen_formats', output : ['py_formats_generated.cpp'], command : [gen_py_formats, '-o', '@OUTPUT@', '@INPUT@']) + +run_command('cp', files('__init__.py'), + meson.current_build_dir() / '__init__.py', + check: true) + +run_command('cp', '-r', meson.current_source_dir() / 'utils', + meson.current_build_dir() / 'utils', + check: true) + +subdir_done() + + + + + + +pycamera_sources = files([ + 'py_camera_manager.cpp', + 'py_enums.cpp', + 'py_geometry.cpp', + 'py_helpers.cpp', + 'py_main.cpp', +]) + + + + pycamera_deps = [ libcamera_private, py3_dep, diff --git a/src/py/meson.build b/src/py/meson.build index 8bd703ed0..cebb3304d 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -2,23 +2,70 @@ py3 = find_program('python', required : get_option('pycamera')) - if not py3.found() pycamera_enabled = false + warning('Python 3 not found, disabling pycamera') subdir_done() endif # Check to make sure version is sufficient -is_py3 = run_command(py3, '--version').stdout().strip().split()[1].split('.')[0].to_int() >= 3 +version_string = run_command(py3, '--version', check: true).stdout().strip() +message('Building against Python version: ' + version_string) +is_py3 = version_string.split()[1].split('.')[0].to_int() >= 3 if not is_py3 pycamera_enabled = false + warning('Python 3 not found, disabling pycamera found: ' + version_string) subdir_done() endif pycamera_enabled = true subdir('libcamera') + +configure_file(input: 'setup.py', output: 'setup.py', copy: true) + + +setup_py = files('setup.py') + + +pycamera = custom_target('python_setup_build', + input: pycamera_sources, + command: [py3, setup_py, 'bdist_wheel'], + output: ['libcamera.cpython-39-x86_64-linux-gnu.so'], + # depend_files: pycamera_sources, + build_by_default: true, +) + +# message('cbd:', meson.current_build_dir()) +# install_data( +# setup_py, +# sources: pycamera_sources, +# install_dir: meson.current_build_dir()) + +# install_data(sources: pycamera_sources, install_dir: meson.current_build_dir()) + + +# run_python_build = custom_target('run_python_build', +# input: ['setup.py'], +# output: [join_paths('libcamera', '_libcamera.so')], +# command: [py3, 'setup.py', 'build'], +# build_by_default: true, +# depend_files: [pycamera_sources], +# ) + + + +# pycamera_deps = declare_dependency( +# sources: pycamera_sources +# ) + +# pycamera = run_target('pycamera', +# command : [py3, 'setup.py', 'build'], +# depends : [libcamera_h, pycamera_deps], +# ) + +# subdir('libcamera') # # python = find_program('python', required : true) # py_setup_build = run_target('pycamera_setup_build', # command : [py3, 'setup.py', 'build'], diff --git a/src/py/setup.py b/src/py/setup.py index 7a11987b5..2c3b30fa2 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -9,33 +9,40 @@ # NOTE(meawoppl) These can be vastly simplified to not go through # the argparse and assorted tomfoolery that remains -gen_controls(["placeholder", - '--mode', 'controls', - '-o', "libcamera/py_controls_generated.cpp", - '../libcamera/control_ids.yaml', - 'libcamera/py_controls_generated.cpp.in', -]) - -gen_controls(["placeholder", - '--mode', 'properties', - '-o', 'libcamera/py_properties_generated.cpp', - '../libcamera/property_ids.yaml', - 'libcamera/py_properties_generated.cpp.in' -]) - -gen_formats(["placeholder", - '-o', 'libcamera/py_formats_generated.cpp', - '../libcamera/formats.yaml', - 'libcamera/py_formats_generated.cpp.in', -]) +# gen_controls(["placeholder", +# '--mode', 'controls', +# '-o', "libcamera/py_controls_generated.cpp", +# '../libcamera/control_ids.yaml', +# 'libcamera/py_controls_generated.cpp.in', +# ]) + +# gen_controls(["placeholder", +# '--mode', 'properties', +# '-o', 'libcamera/py_properties_generated.cpp', +# '../libcamera/property_ids.yaml', +# 'libcamera/py_properties_generated.cpp.in' +# ]) + +# gen_formats(["placeholder", +# '-o', 'libcamera/py_formats_generated.cpp', +# '../libcamera/formats.yaml', +# 'libcamera/py_formats_generated.cpp.in', +# ]) #gen_stub(["pybind11-stubgen", "--no-setup-py", "-o", "libcamera", "libcamera"]) +# Sort source files for reproducibility + +import os +this_dir = os.path.dirname(os.path.abspath(__file__)) +os.chdir(this_dir) + +cpp_sources = list(sorted(glob("libcamera/*.cpp"))) ext_modules = [ Pybind11Extension( "libcamera", - sorted(glob("libcamera/*.cpp")), # Sort source files for reproducibility + cpp_sources, include_dirs=['../../include'], extra_compile_args=[ '-fvisibility=hidden', From 95a69cc1329bd61ce4f9f9259490bdedf74a7cfb Mon Sep 17 00:00:00 2001 From: meawoppl Date: Fri, 14 Apr 2023 12:41:25 -0700 Subject: [PATCH 04/11] Builds wheels --- include/libcamera/camera.h | 6 +- src/py/libcamera/py_camera_manager.h | 2 +- src/py/libcamera/py_controls_generated.cpp.in | 2 +- src/py/libcamera/py_enums.cpp | 2 +- src/py/libcamera/py_formats_generated.cpp.in | 2 +- src/py/libcamera/py_geometry.cpp | 2 +- src/py/libcamera/py_helpers.h | 2 +- src/py/libcamera/py_main.cpp | 146 +++++++++--------- .../libcamera/py_properties_generated.cpp.in | 2 +- src/py/meson.build | 8 +- src/py/setup.py | 11 +- 11 files changed, 91 insertions(+), 94 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 5bb065847..22a025afb 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -82,8 +82,7 @@ class CameraConfiguration std::vector config_; }; -class Camera final : public Object, public std::enable_shared_from_this, - public Extensible +class Camera final : public Object, public std::enable_shared_from_this, public Extensible { LIBCAMERA_DECLARE_PRIVATE() @@ -114,12 +113,13 @@ class Camera final : public Object, public std::enable_shared_from_this, int start(const ControlList *controls = nullptr); int stop(); + ~Camera(); + private: LIBCAMERA_DISABLE_COPY(Camera) Camera(std::unique_ptr d, const std::string &id, const std::set &streams); - ~Camera(); friend class PipelineHandler; void disconnect(); diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 3525057d9..3574db236 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -9,7 +9,7 @@ #include -#include +#include using namespace libcamera; diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in index cb8442bae..18fa57d94 100644 --- a/src/py/libcamera/py_controls_generated.cpp.in +++ b/src/py/libcamera/py_controls_generated.cpp.in @@ -9,7 +9,7 @@ #include -#include +#include namespace py = pybind11; diff --git a/src/py/libcamera/py_enums.cpp b/src/py/libcamera/py_enums.cpp index 96d4beef4..803c4e7ee 100644 --- a/src/py/libcamera/py_enums.cpp +++ b/src/py/libcamera/py_enums.cpp @@ -7,7 +7,7 @@ #include -#include +#include namespace py = pybind11; diff --git a/src/py/libcamera/py_formats_generated.cpp.in b/src/py/libcamera/py_formats_generated.cpp.in index b88807f3a..a3f7f94d5 100644 --- a/src/py/libcamera/py_formats_generated.cpp.in +++ b/src/py/libcamera/py_formats_generated.cpp.in @@ -9,7 +9,7 @@ #include -#include +#include namespace py = pybind11; diff --git a/src/py/libcamera/py_geometry.cpp b/src/py/libcamera/py_geometry.cpp index 84b0cb087..5c2aeac48 100644 --- a/src/py/libcamera/py_geometry.cpp +++ b/src/py/libcamera/py_geometry.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include namespace py = pybind11; diff --git a/src/py/libcamera/py_helpers.h b/src/py/libcamera/py_helpers.h index cd31e2cc5..983969dff 100644 --- a/src/py/libcamera/py_helpers.h +++ b/src/py/libcamera/py_helpers.h @@ -7,7 +7,7 @@ #include -#include +#include pybind11::object controlValueToPy(const libcamera::ControlValue &cv); libcamera::ControlValue pyToControlValue(const pybind11::object &ob, libcamera::ControlType type); diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index d14e18e25..05b5df793 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -61,8 +61,8 @@ PYBIND11_MODULE(_libcamera, m) * https://pybind11.readthedocs.io/en/latest/advanced/misc.html#avoiding-c-types-in-docstrings */ - auto pyCameraManager = py::class_(m, "CameraManager"); - auto pyCamera = py::class_(m, "Camera"); + auto pyCameraManager = py::class_>(m, "CameraManager"); + auto pyCamera = py::class_>(m, "Camera"); auto pyCameraConfiguration = py::class_(m, "CameraConfiguration"); auto pyCameraConfigurationStatus = py::enum_(pyCameraConfiguration, "Status"); auto pyStreamConfiguration = py::class_(m, "StreamConfiguration"); @@ -116,30 +116,31 @@ PYBIND11_MODULE(_libcamera, m) .def_property_readonly("id", &Camera::id) .def("acquire", &Camera::acquire) .def("release", &Camera::release) - .def("start", [](Camera &self, - const std::unordered_map &controls) { - /* \todo What happens if someone calls start() multiple times? */ + .def( + "start", [](Camera &self, const std::unordered_map &controls) { + /* \todo What happens if someone calls start() multiple times? */ - auto cm = gCameraManager.lock(); - ASSERT(cm); + auto cm = gCameraManager.lock(); + ASSERT(cm); - self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); + self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); - ControlList controlList(self.controls()); + ControlList controlList(self.controls()); - for (const auto& [id, obj]: controls) { - auto val = pyToControlValue(obj, id->type()); - controlList.set(id->id(), val); - } + for (const auto &[id, obj] : controls) { + auto val = pyToControlValue(obj, id->type()); + controlList.set(id->id(), val); + } - int ret = self.start(&controlList); - if (ret) { - self.requestCompleted.disconnect(); - return ret; - } + int ret = self.start(&controlList); + if (ret) { + self.requestCompleted.disconnect(); + return ret; + } - return 0; - }, py::arg("controls") = std::unordered_map()) + return 0; + }, + py::arg("controls") = std::unordered_map()) .def("stop", [](Camera &self) { int ret = self.stop(); @@ -215,15 +216,16 @@ PYBIND11_MODULE(_libcamera, m) }); pyCameraConfiguration - .def("__iter__", [](CameraConfiguration &self) { - return py::make_iterator(self); - }, py::keep_alive<0, 1>()) + .def( + "__iter__", [](CameraConfiguration &self) { + return py::make_iterator(self); + }, + py::keep_alive<0, 1>()) .def("__len__", [](CameraConfiguration &self) { return self.size(); }) .def("validate", &CameraConfiguration::validate) - .def("at", py::overload_cast(&CameraConfiguration::at), - py::return_value_policy::reference_internal) + .def("at", py::overload_cast(&CameraConfiguration::at), py::return_value_policy::reference_internal) .def_property_readonly("size", &CameraConfiguration::size) .def_property_readonly("empty", &CameraConfiguration::empty) .def_readwrite("transform", &CameraConfiguration::transform); @@ -276,13 +278,15 @@ PYBIND11_MODULE(_libcamera, m) pyFrameBufferPlane .def(py::init()) .def(py::init([](int fd, unsigned int offset, unsigned int length) { - auto p = FrameBuffer::Plane(); - p.fd = SharedFD(fd); - p.offset = offset; - p.length = length; - return p; - }), py::arg("fd"), py::arg("offset"), py::arg("length")) - .def_property("fd", + auto p = FrameBuffer::Plane(); + p.fd = SharedFD(fd); + p.offset = offset; + p.length = length; + return p; + }), + py::arg("fd"), py::arg("offset"), py::arg("length")) + .def_property( + "fd", [](const FrameBuffer::Plane &self) { return self.fd.get(); }, @@ -329,9 +333,11 @@ PYBIND11_MODULE(_libcamera, m) pyRequest /* \todo Fence is not supported, so we cannot expose addBuffer() directly */ - .def("add_buffer", [](Request &self, const Stream *stream, FrameBuffer *buffer) { - return self.addBuffer(stream, buffer); - }, py::keep_alive<1, 3>()) /* Request keeps Framebuffer alive */ + .def( + "add_buffer", [](Request &self, const Stream *stream, FrameBuffer *buffer) { + return self.addBuffer(stream, buffer); + }, + py::keep_alive<1, 3>()) /* Request keeps Framebuffer alive */ .def_property_readonly("status", &Request::status) .def_property_readonly("buffers", &Request::buffers) .def_property_readonly("cookie", &Request::cookie) @@ -390,55 +396,44 @@ PYBIND11_MODULE(_libcamera, m) pyTransform .def(py::init([](int rotation, bool hflip, bool vflip, bool transpose) { - bool ok; - - Transform t = transformFromRotation(rotation, &ok); - if (!ok) - throw std::invalid_argument("Invalid rotation"); - - if (hflip) - t ^= Transform::HFlip; - if (vflip) - t ^= Transform::VFlip; - if (transpose) - t ^= Transform::Transpose; - return t; - }), py::arg("rotation") = 0, py::arg("hflip") = false, - py::arg("vflip") = false, py::arg("transpose") = false) + bool ok; + + Transform t = transformFromRotation(rotation, &ok); + if (!ok) + throw std::invalid_argument("Invalid rotation"); + + if (hflip) + t ^= Transform::HFlip; + if (vflip) + t ^= Transform::VFlip; + if (transpose) + t ^= Transform::Transpose; + return t; + }), + py::arg("rotation") = 0, py::arg("hflip") = false, + py::arg("vflip") = false, py::arg("transpose") = false) .def(py::init([](Transform &other) { return other; })) .def("__str__", [](Transform &self) { return ""; }) - .def_property("hflip", - [](Transform &self) { - return !!(self & Transform::HFlip); - }, - [](Transform &self, bool hflip) { + .def_property( + "hflip", [](Transform &self) { return !!(self & Transform::HFlip); }, [](Transform &self, bool hflip) { if (hflip) self |= Transform::HFlip; else - self &= ~Transform::HFlip; - }) - .def_property("vflip", - [](Transform &self) { - return !!(self & Transform::VFlip); - }, - [](Transform &self, bool vflip) { + self &= ~Transform::HFlip; }) + .def_property( + "vflip", [](Transform &self) { return !!(self & Transform::VFlip); }, [](Transform &self, bool vflip) { if (vflip) self |= Transform::VFlip; else - self &= ~Transform::VFlip; - }) - .def_property("transpose", - [](Transform &self) { - return !!(self & Transform::Transpose); - }, - [](Transform &self, bool transpose) { + self &= ~Transform::VFlip; }) + .def_property( + "transpose", [](Transform &self) { return !!(self & Transform::Transpose); }, [](Transform &self, bool transpose) { if (transpose) self |= Transform::Transpose; else - self &= ~Transform::Transpose; - }) + self &= ~Transform::Transpose; }) .def("inverse", [](Transform &self) { return -self; }) .def("invert", [](Transform &self) { self = -self; @@ -452,9 +447,10 @@ PYBIND11_MODULE(_libcamera, m) ColorSpace::TransferFunction transferFunction, ColorSpace::YcbcrEncoding ycbcrEncoding, ColorSpace::Range range) { - return ColorSpace(primaries, transferFunction, ycbcrEncoding, range); - }), py::arg("primaries"), py::arg("transferFunction"), - py::arg("ycbcrEncoding"), py::arg("range")) + return ColorSpace(primaries, transferFunction, ycbcrEncoding, range); + }), + py::arg("primaries"), py::arg("transferFunction"), + py::arg("ycbcrEncoding"), py::arg("range")) .def(py::init([](ColorSpace &other) { return other; })) .def("__str__", [](ColorSpace &self) { return ""; diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in index 044b2b2a6..e49b6e91b 100644 --- a/src/py/libcamera/py_properties_generated.cpp.in +++ b/src/py/libcamera/py_properties_generated.cpp.in @@ -9,7 +9,7 @@ #include -#include +#include namespace py = pybind11; diff --git a/src/py/meson.build b/src/py/meson.build index cebb3304d..47fd93006 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -23,17 +23,17 @@ pycamera_enabled = true subdir('libcamera') -configure_file(input: 'setup.py', output: 'setup.py', copy: true) +setup_py_copy = configure_file(input: 'setup.py', output: 'setup.py', copy: true) -setup_py = files('setup.py') +#setup_py = files('setup.py') pycamera = custom_target('python_setup_build', input: pycamera_sources, - command: [py3, setup_py, 'bdist_wheel'], + command: [py3, setup_py_copy, 'bdist_wheel'], output: ['libcamera.cpython-39-x86_64-linux-gnu.so'], - # depend_files: pycamera_sources, + depends: libcamera, build_by_default: true, ) diff --git a/src/py/setup.py b/src/py/setup.py index 2c3b30fa2..60146adb2 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -2,9 +2,9 @@ from setuptools import setup from pybind11.setup_helpers import Pybind11Extension -from codegen_controls import main as gen_controls -from codegen_formats import main as gen_formats -from pybind11_stubgen import main as gen_stub +# from codegen_controls import main as gen_controls +# from codegen_formats import main as gen_formats +# from pybind11_stubgen import main as gen_stub # NOTE(meawoppl) These can be vastly simplified to not go through @@ -35,6 +35,7 @@ import os this_dir = os.path.dirname(os.path.abspath(__file__)) +print("Running from ", this_dir) os.chdir(this_dir) cpp_sources = list(sorted(glob("libcamera/*.cpp"))) @@ -43,11 +44,11 @@ Pybind11Extension( "libcamera", cpp_sources, - include_dirs=['../../include'], + include_dirs=['../../include', "../../../include"], extra_compile_args=[ '-fvisibility=hidden', '-Wno-shadow', - '-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT', + '-DLIBCAMERA_BASE_PRIVATE', ], ), From 7c51182edc2f1a7f8ca993ec752d9d2a2cda5dc3 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Fri, 14 Apr 2023 13:16:25 -0700 Subject: [PATCH 05/11] Simplify the things --- src/py/{codegen_controls.py => codegen.py} | 49 ++++++++++++------- src/py/codegen_formats.py | 56 ---------------------- src/py/libcamera/meson.build | 8 +--- src/py/meson.build | 50 +------------------ 4 files changed, 35 insertions(+), 128 deletions(-) rename src/py/{codegen_controls.py => codegen.py} (78%) delete mode 100755 src/py/codegen_formats.py diff --git a/src/py/codegen_controls.py b/src/py/codegen.py similarity index 78% rename from src/py/codegen_controls.py rename to src/py/codegen.py index ce6d85bba..6e5bb65cc 100755 --- a/src/py/codegen_controls.py +++ b/src/py/codegen.py @@ -9,6 +9,25 @@ import yaml +def generate(formats): + fmts = [] + + for format in formats: + name, format = format.popitem() + fmts.append(f'\t\t.def_readonly_static("{name}", &libcamera::formats::{name})') + + return {'formats': '\n'.join(fmts)} + + +def fill_template(template, data): + with open(template, encoding='utf-8') as f: + template = f.read() + + template = string.Template(template) + return template.substitute(data) + + + def find_common_prefix(strings): prefix = strings[0] @@ -68,14 +87,7 @@ def generate_py(controls, mode): return {'controls': out} -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(): # Parse command line arguments parser = argparse.ArgumentParser(prog="codegen_controls.py") parser.add_argument('-o', dest='output', metavar='file', type=str, @@ -85,26 +97,29 @@ def main(argv): parser.add_argument('template', type=str, help='Template file name.') parser.add_argument('--mode', type=str, required=True, - choices=['controls', 'properties'], + choices=['controls', 'properties', 'formats'], help='Mode is either "controls" or "properties"') - args = parser.parse_args(argv[1:]) + args = parser.parse_args() - data = open(args.input, 'rb').read() - controls = yaml.safe_load(data)['controls'] + with open(args.input, 'rb') as f: + input_yml = yaml.safe_load(f) - data = generate_py(controls, args.mode) + if args.mode == 'formats': + data = generate(input_yml['formats']) + else: + data = generate_py(input_yml['controls'], args.mode) - data = fill_template(args.template, data) + formatted = fill_template(args.template, data) if args.output: output = open(args.output, 'wb') - output.write(data.encode('utf-8')) + output.write(formatted.encode('utf-8')) output.close() else: - sys.stdout.write(data) + sys.stdout.write(formatted) return 0 if __name__ == '__main__': - sys.exit(main(sys.argv)) + main() diff --git a/src/py/codegen_formats.py b/src/py/codegen_formats.py deleted file mode 100755 index 0ff1d12ac..000000000 --- a/src/py/codegen_formats.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Generate Python format definitions from YAML - -import argparse -import string -import sys -import yaml - - -def generate(formats): - fmts = [] - - for format in formats: - name, format = format.popitem() - fmts.append(f'\t\t.def_readonly_static("{name}", &libcamera::formats::{name})') - - return {'formats': '\n'.join(fmts)} - - -def fill_template(template, data): - with open(template, encoding='utf-8') as f: - template = f.read() - - template = string.Template(template) - return template.substitute(data) - - -def main(argv): - 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:]) - - with open(args.input, encoding='utf-8') as f: - formats = yaml.safe_load(f)['formats'] - - data = generate(formats) - data = fill_template(args.template, data) - - if args.output: - with open(args.output, 'w', encoding='utf-8') as f: - f.write(data) - else: - sys.stdout.write(data) - - return 0 - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index 97dbf4776..f16266cec 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -25,7 +25,7 @@ gen_py_controls_input_files = files([ 'py_controls_generated.cpp.in', ]) -gen_py_controls = files('../codegen_controls.py') +gen_py_controls = files('../codegen.py') pycamera_sources += custom_target('py_gen_controls', input : gen_py_controls_input_files, @@ -35,7 +35,6 @@ pycamera_sources += custom_target('py_gen_controls', ) # Generate properties - gen_py_property_enums_input_files = files([ '../../libcamera/property_ids.yaml', 'py_properties_generated.cpp.in', @@ -47,18 +46,15 @@ pycamera_sources += custom_target('py_gen_properties', command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@']) # Generate formats - gen_py_formats_input_files = files([ '../../libcamera/formats.yaml', 'py_formats_generated.cpp.in', ]) -gen_py_formats = files('../codegen_formats.py') - pycamera_sources += custom_target('py_gen_formats', input : gen_py_formats_input_files, output : ['py_formats_generated.cpp'], - command : [gen_py_formats, '-o', '@OUTPUT@', '@INPUT@']) + command : [gen_py_controls, '--mode', 'formats', '-o', '@OUTPUT@', '@INPUT@']) run_command('cp', files('__init__.py'), diff --git a/src/py/meson.build b/src/py/meson.build index 47fd93006..e582e08e7 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -26,8 +26,6 @@ subdir('libcamera') setup_py_copy = configure_file(input: 'setup.py', output: 'setup.py', copy: true) -#setup_py = files('setup.py') - pycamera = custom_target('python_setup_build', input: pycamera_sources, @@ -37,51 +35,5 @@ pycamera = custom_target('python_setup_build', build_by_default: true, ) -# message('cbd:', meson.current_build_dir()) -# install_data( -# setup_py, -# sources: pycamera_sources, -# install_dir: meson.current_build_dir()) - -# install_data(sources: pycamera_sources, install_dir: meson.current_build_dir()) - - -# run_python_build = custom_target('run_python_build', -# input: ['setup.py'], -# output: [join_paths('libcamera', '_libcamera.so')], -# command: [py3, 'setup.py', 'build'], -# build_by_default: true, -# depend_files: [pycamera_sources], -# ) - - - -# pycamera_deps = declare_dependency( -# sources: pycamera_sources -# ) - -# pycamera = run_target('pycamera', -# command : [py3, 'setup.py', 'build'], -# depends : [libcamera_h, pycamera_deps], -# ) - -# subdir('libcamera') -# # python = find_program('python', required : true) -# py_setup_build = run_target('pycamera_setup_build', -# command : [py3, 'setup.py', 'build'], -# depends : [libcamera_h], -# ) - -# # custom_target('libcamera_python_bindings', -# # command: ['python', 'setup.py', 'build'], -# # depend_files: [libcamera_h], -# # output: ['libcamera/_libcamera.so'], -# # ) - -# run_target('pycamera_install', -# command : ['python', 'setup.py', 'install'], -# depends : [py_setup_build], -# ) - -# meson.add_install_script('python', 'setup.py', 'install') +meson.add_install_script(py3, setup_py_copy, 'install') From 27a81cf9380f9069fd3379ec8d926fe630f78792 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Fri, 14 Apr 2023 16:55:55 -0700 Subject: [PATCH 06/11] WIP --- src/py/codegen.py | 4 +- src/py/libcamera/meson.build | 90 +++++-------------- src/py/meson.build | 2 - src/py/setup.py | 17 ++-- .../py_controls_generated.cpp.in | 0 .../py_formats_generated.cpp.in | 0 .../py_properties_generated.cpp.in | 0 subprojects/packagefiles/pybind11/meson.build | 7 -- subprojects/pybind11.wrap | 11 --- 9 files changed, 31 insertions(+), 100 deletions(-) rename src/py/{libcamera => templates}/py_controls_generated.cpp.in (100%) rename src/py/{libcamera => templates}/py_formats_generated.cpp.in (100%) rename src/py/{libcamera => templates}/py_properties_generated.cpp.in (100%) delete mode 100644 subprojects/packagefiles/pybind11/meson.build delete mode 100644 subprojects/pybind11.wrap diff --git a/src/py/codegen.py b/src/py/codegen.py index 6e5bb65cc..1a976ab72 100755 --- a/src/py/codegen.py +++ b/src/py/codegen.py @@ -87,7 +87,7 @@ def generate_py(controls, mode): return {'controls': out} -def main(): +def main(argv: list[str] = sys.argv[1:]): # Parse command line arguments parser = argparse.ArgumentParser(prog="codegen_controls.py") parser.add_argument('-o', dest='output', metavar='file', type=str, @@ -99,7 +99,7 @@ def main(): parser.add_argument('--mode', type=str, required=True, choices=['controls', 'properties', 'formats'], help='Mode is either "controls" or "properties"') - args = parser.parse_args() + args = parser.parse_args(argv) with open(args.input, 'rb') as f: input_yml = yaml.safe_load(f) diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index f16266cec..0fb1c6014 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -22,40 +22,43 @@ endforeach # Generate controls gen_py_controls_input_files = files([ '../../libcamera/control_ids.yaml', - 'py_controls_generated.cpp.in', + '../templates/py_controls_generated.cpp.in', ]) gen_py_controls = files('../codegen.py') -pycamera_sources += custom_target('py_gen_controls', - input : gen_py_controls_input_files, - output : ['py_controls_generated.cpp'], - command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@'], - build_always: true - ) +custom_target('py_gen_controls', + input : gen_py_controls_input_files, + output : ['py_controls_generated.cpp'], + command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@'], + build_always: true +) # Generate properties gen_py_property_enums_input_files = files([ '../../libcamera/property_ids.yaml', - 'py_properties_generated.cpp.in', + '../templates/py_properties_generated.cpp.in', ]) -pycamera_sources += custom_target('py_gen_properties', - input : gen_py_property_enums_input_files, - output : ['py_properties_generated.cpp'], - command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@']) +custom_target('py_gen_properties', + input : gen_py_property_enums_input_files, + output : ['py_properties_generated.cpp'], + command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@'], + build_always: true +) # Generate formats gen_py_formats_input_files = files([ '../../libcamera/formats.yaml', - 'py_formats_generated.cpp.in', + '../templates/py_formats_generated.cpp.in', ]) -pycamera_sources += custom_target('py_gen_formats', - input : gen_py_formats_input_files, - output : ['py_formats_generated.cpp'], - command : [gen_py_controls, '--mode', 'formats', '-o', '@OUTPUT@', '@INPUT@']) - +custom_target('py_gen_formats', + input : gen_py_formats_input_files, + output : ['py_formats_generated.cpp'], + command : [gen_py_controls, '--mode', 'formats', '-o', '@OUTPUT@', '@INPUT@'], + build_always: true +) run_command('cp', files('__init__.py'), meson.current_build_dir() / '__init__.py', @@ -67,57 +70,6 @@ run_command('cp', '-r', meson.current_source_dir() / 'utils', subdir_done() - - - - - -pycamera_sources = files([ - 'py_camera_manager.cpp', - 'py_enums.cpp', - 'py_geometry.cpp', - 'py_helpers.cpp', - 'py_main.cpp', -]) - - - - -pycamera_deps = [ - libcamera_private, - py3_dep, - pybind11_dep, -] - -pycamera_args = [ - '-fvisibility=hidden', - '-Wno-shadow', - '-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT', -] - -destdir = get_option('libdir') / ('python' + py3_dep.version()) / 'site-packages' / 'libcamera' - -pycamera = shared_module('_libcamera', - pycamera_sources, - install : true, - install_dir : destdir, - name_prefix : '', - dependencies : pycamera_deps, - cpp_args : pycamera_args) - -# Create symlinks from the build dir to the source dir so that we can use the -# Python module directly from the build dir. - -run_command('ln', '-fsrT', files('__init__.py'), - meson.current_build_dir() / '__init__.py', - check: true) - -run_command('ln', '-fsrT', meson.current_source_dir() / 'utils', - meson.current_build_dir() / 'utils', - check: true) - -install_data(['__init__.py'], install_dir : destdir) - # \todo Generate stubs when building. See https://peps.python.org/pep-0484/#stub-files # Note: Depends on pybind11-stubgen. To generate pylibcamera stubs: # $ PYTHONPATH=build/src/py pybind11-stubgen --no-setup-py -o build/src/py libcamera diff --git a/src/py/meson.build b/src/py/meson.build index e582e08e7..190919e66 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -25,8 +25,6 @@ subdir('libcamera') setup_py_copy = configure_file(input: 'setup.py', output: 'setup.py', copy: true) - - pycamera = custom_target('python_setup_build', input: pycamera_sources, command: [py3, setup_py_copy, 'bdist_wheel'], diff --git a/src/py/setup.py b/src/py/setup.py index 60146adb2..bbb9bcfbd 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -2,28 +2,28 @@ from setuptools import setup from pybind11.setup_helpers import Pybind11Extension -# from codegen_controls import main as gen_controls -# from codegen_formats import main as gen_formats +# from codegen import main as codegen # from pybind11_stubgen import main as gen_stub # NOTE(meawoppl) These can be vastly simplified to not go through # the argparse and assorted tomfoolery that remains -# gen_controls(["placeholder", +# codegen([ # '--mode', 'controls', # '-o', "libcamera/py_controls_generated.cpp", # '../libcamera/control_ids.yaml', # 'libcamera/py_controls_generated.cpp.in', # ]) -# gen_controls(["placeholder", +# codegen([ # '--mode', 'properties', # '-o', 'libcamera/py_properties_generated.cpp', # '../libcamera/property_ids.yaml', # 'libcamera/py_properties_generated.cpp.in' # ]) -# gen_formats(["placeholder", +# codegen(["placeholder", +# '--mode', 'formats', # '-o', 'libcamera/py_formats_generated.cpp', # '../libcamera/formats.yaml', # 'libcamera/py_formats_generated.cpp.in', @@ -31,7 +31,6 @@ #gen_stub(["pybind11-stubgen", "--no-setup-py", "-o", "libcamera", "libcamera"]) -# Sort source files for reproducibility import os this_dir = os.path.dirname(os.path.abspath(__file__)) @@ -42,15 +41,15 @@ ext_modules = [ Pybind11Extension( - "libcamera", + "libcamera._libcamera", cpp_sources, + libraries=['camera'], include_dirs=['../../include', "../../../include"], extra_compile_args=[ '-fvisibility=hidden', '-Wno-shadow', '-DLIBCAMERA_BASE_PRIVATE', ], - ), ] @@ -63,4 +62,4 @@ url='https://libcamera.org/', packages=['libcamera', 'libcamera.utils'], ext_modules=ext_modules -) \ No newline at end of file +) diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/templates/py_controls_generated.cpp.in similarity index 100% rename from src/py/libcamera/py_controls_generated.cpp.in rename to src/py/templates/py_controls_generated.cpp.in diff --git a/src/py/libcamera/py_formats_generated.cpp.in b/src/py/templates/py_formats_generated.cpp.in similarity index 100% rename from src/py/libcamera/py_formats_generated.cpp.in rename to src/py/templates/py_formats_generated.cpp.in diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/templates/py_properties_generated.cpp.in similarity index 100% rename from src/py/libcamera/py_properties_generated.cpp.in rename to src/py/templates/py_properties_generated.cpp.in diff --git a/subprojects/packagefiles/pybind11/meson.build b/subprojects/packagefiles/pybind11/meson.build deleted file mode 100644 index 1be47ca45..000000000 --- a/subprojects/packagefiles/pybind11/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -project('pybind11', 'cpp', - version : '2.9.1', - license : 'BSD-3-Clause') - -pybind11_incdir = include_directories('include') - -pybind11_dep = declare_dependency(include_directories : pybind11_incdir) diff --git a/subprojects/pybind11.wrap b/subprojects/pybind11.wrap deleted file mode 100644 index dd02687b5..000000000 --- a/subprojects/pybind11.wrap +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -[wrap-git] -url = https://github.com/pybind/pybind11.git -# This is the head of 'smart_holder' branch -revision = aebdf00cd060b871c5a1e0c2cf4a333503dd0431 -depth = 1 -patch_directory = pybind11 - -[provide] -pybind11 = pybind11_dep From 1cfe92d85bd7e4d03a13d6ae771a82346f83023e Mon Sep 17 00:00:00 2001 From: meawoppl Date: Sat, 15 Apr 2023 13:43:12 -0700 Subject: [PATCH 07/11] WIP --- src/py/meson.build | 6 +++--- src/py/setup.py | 22 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/py/meson.build b/src/py/meson.build index 190919e66..3c6a85aef 100644 --- a/src/py/meson.build +++ b/src/py/meson.build @@ -23,15 +23,15 @@ pycamera_enabled = true subdir('libcamera') -setup_py_copy = configure_file(input: 'setup.py', output: 'setup.py', copy: true) +setup_py = configure_file(input: 'setup.py', output: 'setup.py', copy: true) pycamera = custom_target('python_setup_build', input: pycamera_sources, - command: [py3, setup_py_copy, 'bdist_wheel'], + command: [py3, setup_py, 'bdist_wheel'], output: ['libcamera.cpython-39-x86_64-linux-gnu.so'], depends: libcamera, build_by_default: true, ) -meson.add_install_script(py3, setup_py_copy, 'install') +meson.add_install_script(py3, setup_py, 'install') diff --git a/src/py/setup.py b/src/py/setup.py index bbb9bcfbd..8c7c3033a 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -3,7 +3,7 @@ from pybind11.setup_helpers import Pybind11Extension # from codegen import main as codegen -# from pybind11_stubgen import main as gen_stub +from pybind11_stubgen import main as gen_stub # NOTE(meawoppl) These can be vastly simplified to not go through @@ -29,7 +29,6 @@ # 'libcamera/py_formats_generated.cpp.in', # ]) -#gen_stub(["pybind11-stubgen", "--no-setup-py", "-o", "libcamera", "libcamera"]) import os @@ -53,6 +52,22 @@ ), ] +from setuptools import setup, Extension, find_packages, Command + +# Custom command to generate stubs using pybind11-stubgen +class GenerateStubs(Command): + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + gen_stub(["libcamera", "-o", "libcamera"]) + + setup( name='libcamera', version='0.0.4', @@ -61,5 +76,6 @@ author_email=' libcamera-devel@lists.libcamera.org', url='https://libcamera.org/', packages=['libcamera', 'libcamera.utils'], - ext_modules=ext_modules + ext_modules=ext_modules, + cmdclass={"generate_stubs": GenerateStubs}, ) From a513cfe2418e2483c41f2e36fa85b4eec0875c7e Mon Sep 17 00:00:00 2001 From: meawoppl Date: Wed, 17 May 2023 13:48:37 -0700 Subject: [PATCH 08/11] Add flag --- src/py/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/py/setup.py b/src/py/setup.py index 8c7c3033a..4f9fcd8b9 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -48,6 +48,7 @@ '-fvisibility=hidden', '-Wno-shadow', '-DLIBCAMERA_BASE_PRIVATE', + '-std=c++17', ], ), ] From 6dd7d5de94155ccc18e87f22bc7badf7ebc10949 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Wed, 17 May 2023 13:58:13 -0700 Subject: [PATCH 09/11] Find the libs --- src/py/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/py/setup.py b/src/py/setup.py index 4f9fcd8b9..c8920e029 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -44,6 +44,7 @@ cpp_sources, libraries=['camera'], include_dirs=['../../include', "../../../include"], + library_dirs=['../libcamera'], extra_compile_args=[ '-fvisibility=hidden', '-Wno-shadow', From 54127dcebb6cdbf3d94209d8024c416c90f27a41 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Wed, 17 May 2023 14:04:02 -0700 Subject: [PATCH 10/11] Version bump --- src/py/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py/setup.py b/src/py/setup.py index c8920e029..ba13228d4 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -72,7 +72,7 @@ def run(self): setup( name='libcamera', - version='0.0.4', + version='0.0.5', description='Python wrapper to `libcamera`', author='Libcamera Developers', author_email=' libcamera-devel@lists.libcamera.org', From 9dc9382258486b1782afb1bc71005e43d4e22ef7 Mon Sep 17 00:00:00 2001 From: meawoppl Date: Wed, 17 May 2023 18:02:19 -0700 Subject: [PATCH 11/11] Revert overwrites --- src/py/libcamera/py_main.cpp | 140 ++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 68 deletions(-) diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 05b5df793..5092b6a5e 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -116,31 +116,30 @@ PYBIND11_MODULE(_libcamera, m) .def_property_readonly("id", &Camera::id) .def("acquire", &Camera::acquire) .def("release", &Camera::release) - .def( - "start", [](Camera &self, const std::unordered_map &controls) { - /* \todo What happens if someone calls start() multiple times? */ + .def("start", [](Camera &self, + const std::unordered_map &controls) { + /* \todo What happens if someone calls start() multiple times? */ - auto cm = gCameraManager.lock(); - ASSERT(cm); + auto cm = gCameraManager.lock(); + ASSERT(cm); - self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); + self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); - ControlList controlList(self.controls()); + ControlList controlList(self.controls()); - for (const auto &[id, obj] : controls) { - auto val = pyToControlValue(obj, id->type()); - controlList.set(id->id(), val); - } + for (const auto& [id, obj]: controls) { + auto val = pyToControlValue(obj, id->type()); + controlList.set(id->id(), val); + } - int ret = self.start(&controlList); - if (ret) { - self.requestCompleted.disconnect(); - return ret; - } + int ret = self.start(&controlList); + if (ret) { + self.requestCompleted.disconnect(); + return ret; + } - return 0; - }, - py::arg("controls") = std::unordered_map()) + return 0; + }, py::arg("controls") = std::unordered_map()) .def("stop", [](Camera &self) { int ret = self.stop(); @@ -216,16 +215,15 @@ PYBIND11_MODULE(_libcamera, m) }); pyCameraConfiguration - .def( - "__iter__", [](CameraConfiguration &self) { - return py::make_iterator(self); - }, - py::keep_alive<0, 1>()) + .def("__iter__", [](CameraConfiguration &self) { + return py::make_iterator(self); + }, py::keep_alive<0, 1>()) .def("__len__", [](CameraConfiguration &self) { return self.size(); }) .def("validate", &CameraConfiguration::validate) - .def("at", py::overload_cast(&CameraConfiguration::at), py::return_value_policy::reference_internal) + .def("at", py::overload_cast(&CameraConfiguration::at), + py::return_value_policy::reference_internal) .def_property_readonly("size", &CameraConfiguration::size) .def_property_readonly("empty", &CameraConfiguration::empty) .def_readwrite("transform", &CameraConfiguration::transform); @@ -278,15 +276,13 @@ PYBIND11_MODULE(_libcamera, m) pyFrameBufferPlane .def(py::init()) .def(py::init([](int fd, unsigned int offset, unsigned int length) { - auto p = FrameBuffer::Plane(); - p.fd = SharedFD(fd); - p.offset = offset; - p.length = length; - return p; - }), - py::arg("fd"), py::arg("offset"), py::arg("length")) - .def_property( - "fd", + auto p = FrameBuffer::Plane(); + p.fd = SharedFD(fd); + p.offset = offset; + p.length = length; + return p; + }), py::arg("fd"), py::arg("offset"), py::arg("length")) + .def_property("fd", [](const FrameBuffer::Plane &self) { return self.fd.get(); }, @@ -333,11 +329,9 @@ PYBIND11_MODULE(_libcamera, m) pyRequest /* \todo Fence is not supported, so we cannot expose addBuffer() directly */ - .def( - "add_buffer", [](Request &self, const Stream *stream, FrameBuffer *buffer) { - return self.addBuffer(stream, buffer); - }, - py::keep_alive<1, 3>()) /* Request keeps Framebuffer alive */ + .def("add_buffer", [](Request &self, const Stream *stream, FrameBuffer *buffer) { + return self.addBuffer(stream, buffer); + }, py::keep_alive<1, 3>()) /* Request keeps Framebuffer alive */ .def_property_readonly("status", &Request::status) .def_property_readonly("buffers", &Request::buffers) .def_property_readonly("cookie", &Request::cookie) @@ -396,44 +390,55 @@ PYBIND11_MODULE(_libcamera, m) pyTransform .def(py::init([](int rotation, bool hflip, bool vflip, bool transpose) { - bool ok; - - Transform t = transformFromRotation(rotation, &ok); - if (!ok) - throw std::invalid_argument("Invalid rotation"); - - if (hflip) - t ^= Transform::HFlip; - if (vflip) - t ^= Transform::VFlip; - if (transpose) - t ^= Transform::Transpose; - return t; - }), - py::arg("rotation") = 0, py::arg("hflip") = false, - py::arg("vflip") = false, py::arg("transpose") = false) + bool ok; + + Transform t = transformFromRotation(rotation, &ok); + if (!ok) + throw std::invalid_argument("Invalid rotation"); + + if (hflip) + t ^= Transform::HFlip; + if (vflip) + t ^= Transform::VFlip; + if (transpose) + t ^= Transform::Transpose; + return t; + }), py::arg("rotation") = 0, py::arg("hflip") = false, + py::arg("vflip") = false, py::arg("transpose") = false) .def(py::init([](Transform &other) { return other; })) .def("__str__", [](Transform &self) { return ""; }) - .def_property( - "hflip", [](Transform &self) { return !!(self & Transform::HFlip); }, [](Transform &self, bool hflip) { + .def_property("hflip", + [](Transform &self) { + return !!(self & Transform::HFlip); + }, + [](Transform &self, bool hflip) { if (hflip) self |= Transform::HFlip; else - self &= ~Transform::HFlip; }) - .def_property( - "vflip", [](Transform &self) { return !!(self & Transform::VFlip); }, [](Transform &self, bool vflip) { + self &= ~Transform::HFlip; + }) + .def_property("vflip", + [](Transform &self) { + return !!(self & Transform::VFlip); + }, + [](Transform &self, bool vflip) { if (vflip) self |= Transform::VFlip; else - self &= ~Transform::VFlip; }) - .def_property( - "transpose", [](Transform &self) { return !!(self & Transform::Transpose); }, [](Transform &self, bool transpose) { + self &= ~Transform::VFlip; + }) + .def_property("transpose", + [](Transform &self) { + return !!(self & Transform::Transpose); + }, + [](Transform &self, bool transpose) { if (transpose) self |= Transform::Transpose; else - self &= ~Transform::Transpose; }) + self &= ~Transform::Transpose; + }) .def("inverse", [](Transform &self) { return -self; }) .def("invert", [](Transform &self) { self = -self; @@ -447,10 +452,9 @@ PYBIND11_MODULE(_libcamera, m) ColorSpace::TransferFunction transferFunction, ColorSpace::YcbcrEncoding ycbcrEncoding, ColorSpace::Range range) { - return ColorSpace(primaries, transferFunction, ycbcrEncoding, range); - }), - py::arg("primaries"), py::arg("transferFunction"), - py::arg("ycbcrEncoding"), py::arg("range")) + return ColorSpace(primaries, transferFunction, ycbcrEncoding, range); + }), py::arg("primaries"), py::arg("transferFunction"), + py::arg("ycbcrEncoding"), py::arg("range")) .def(py::init([](ColorSpace &other) { return other; })) .def("__str__", [](ColorSpace &self) { return "";