diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c33b5913d62d1c..3bdbd2609551e5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -42,6 +42,18 @@ RUN useradd -s /bin/bash -u $USER_UID -g $USER_GID -G docker -m $USERNAME RUN echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME RUN chmod 0440 /etc/sudoers.d/$USERNAME +# Python 3.9 and PIP +RUN set -x \ + && sudo apt-get install -y libgirepository1.0-dev \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common \ + && sudo add-apt-repository universe \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y python3.9 python3.9-venv python3.9-dev\ + && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \ + && python3.9 get-pip.py \ + && sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 \ + && rm -rf /var/lib/apt/lists/ \ + && : # last line + RUN mkdir -p /var/downloads RUN cd /var/downloads RUN curl -JL https://github.com/microsoft/vscode-cpptools/releases/download/0.27.0/cpptools-linux.vsix > extension.zip diff --git a/.gitmodules b/.gitmodules index fabee45d3c765c..3db3a2073379b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -95,3 +95,7 @@ path = third_party/mbed-os-posix-socket/repo url = https://github.com/ARMmbed/mbed-os-posix-socket.git branch = main +[submodule "third_party/pybind11/repo"] + path = third_party/pybind11/repo + url = ../../pybind/pybind11 + branch = stable diff --git a/BUILD.gn b/BUILD.gn index ee06f5a3240b87..18547278376d1c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -112,6 +112,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { deps += [ "${chip_root}/src/tools/chip-cert" ] } if (chip_enable_python_modules) { + deps += [ "${chip_root}/src/pybindings/pycontroller" ] deps += [ "${chip_root}/src/controller/python" ] } } @@ -129,6 +130,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { data_deps = [ "${chip_root}/examples/chip-tool" ] if (chip_enable_python_modules) { + data_deps += [ "${chip_root}/src/pybindings/pycontroller" ] data_deps += [ "${chip_root}/src/controller/python" ] } diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 9514db60332582..d6a1377159a8ab 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -356,6 +356,11 @@ config("sanitize_default") { config("fuzzing_default") { } +declare_args() { + # Enable Runtime Type Information (RTTI) + enable_rtti = false +} + config("no_rtti") { cflags_cc = [ "-fno-rtti" ] } @@ -365,7 +370,11 @@ config("rtti") { } config("rtti_default") { - configs = [ ":no_rtti" ] + if (enable_rtti) { + configs = [ ":rtti" ] + } else { + configs = [ ":no_rtti" ] + } } config("no_exceptions") { diff --git a/integrations/docker/images/chip-build/Dockerfile b/integrations/docker/images/chip-build/Dockerfile index 5b5d4b331747a7..f4bcc06e1039f2 100644 --- a/integrations/docker/images/chip-build/Dockerfile +++ b/integrations/docker/images/chip-build/Dockerfile @@ -50,13 +50,13 @@ RUN set -x \ ninja-build \ openjdk-8-jdk \ pkg-config \ - python3 \ - python3-dev \ - python3-pip \ - python3-venv \ + python3.9 \ + python3.9-dev \ + python3.9-venv \ rsync \ shellcheck \ strace \ + sudo \ systemd \ udev \ unzip \ @@ -74,14 +74,17 @@ RUN set -x \ && exec bash \ && : # last line -# Python 2 and PIP +RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo +# Python 3.9 and PIP RUN set -x \ - && apt-get update \ + && sudo apt-get update \ + && sudo apt-get install -y libgirepository1.0-dev \ && DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common \ && add-apt-repository universe \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y python python2 \ - && curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py \ - && python2 get-pip.py \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y python3.9 python3.9-venv python3.9-dev\ + && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \ + && python3.9 get-pip.py \ + && update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 \ && rm -rf /var/lib/apt/lists/ \ && : # last line diff --git a/scripts/build_python.sh b/scripts/build_python.sh index e1a29a3ffe64a6..897cc1aba07301 100755 --- a/scripts/build_python.sh +++ b/scripts/build_python.sh @@ -39,12 +39,13 @@ OUTPUT_ROOT="$CHIP_ROOT/out/python_lib" ENVIRONMENT_ROOT="$CHIP_ROOT/out/python_env" declare chip_detail_logging=false +declare enable_pybindinds=false declare chip_mdns declare clusters=true help() { - echo "Usage: $file_name [ options ... ] [ -chip_detail_logging ChipDetailLoggingValue ] [ -chip_mdns ChipMDNSValue ]" + echo "Usage: $file_name [ options ... ] [ -chip_detail_logging ChipDetailLoggingValue ] [ -chip_mdns ChipMDNSValue ] [-enable_pybindinds EnableValue]" echo "General Options: -h, --help Display this information. @@ -55,6 +56,7 @@ Input Options: By default it is minimal. -c, --clusters_for_ip_commissioning true/false Specify whether to use clusters for IP commissioning. By default it is true. + -p, --enable_pybindinds EnableValue Specify whether to enable pybindings as python controller. " } @@ -78,6 +80,10 @@ while (($#)); do clusters=$2 shift ;; + --enable_pybindinds | -p) + enable_pybindinds=$2 + shift + ;; -*) help echo "Unknown Option \"$1\"" @@ -88,25 +94,43 @@ while (($#)); do done # Print input values -echo "Input values: chip_detail_logging = $chip_detail_logging , chip_mdns = \"$chip_mdns\"" +echo "Input values: chip_detail_logging = $chip_detail_logging , chip_mdns = \"$chip_mdns\", enable_pybindinds = $enable_pybindinds" # Ensure we have a compilation environment source "$CHIP_ROOT/scripts/activate.sh" # Generates ninja files [[ -n "$chip_mdns" ]] && chip_mdns_arg="chip_mdns=\"$chip_mdns\"" || chip_mdns_arg="" -gn --root="$CHIP_ROOT" gen "$OUTPUT_ROOT" --args="chip_detail_logging=$chip_detail_logging chip_use_clusters_for_ip_commissioning=$clusters $chip_mdns_arg" + +gn --root="$CHIP_ROOT" gen "$OUTPUT_ROOT" --args="chip_detail_logging=$chip_detail_logging enable_rtti=$enable_pybindinds chip_use_clusters_for_ip_commissioning=$clusters $chip_mdns_arg" # Compiles python files -ninja -C "$OUTPUT_ROOT" python +# Check pybindings was requested +if [ $enable_pybindinds == true ] +then +ninja -v -C "$OUTPUT_ROOT" pycontroller +else +ninja -v -C "$OUTPUT_ROOT" python +fi + + # Create a virtual environment that has access to the built python tools virtualenv --clear "$ENVIRONMENT_ROOT" # Activate the new enviroment to register the python WHL + +if [ $enable_pybindinds == true ] +then +WHEEL="$OUTPUT_ROOT"/pybindings/pycontroller/pybindings-*.whl +else +WHEEL="$OUTPUT_ROOT"/controller/python/chip-*.whl +fi + source "$ENVIRONMENT_ROOT"/bin/activate "$ENVIRONMENT_ROOT"/bin/python -m pip install --upgrade pip -"$ENVIRONMENT_ROOT"/bin/pip install --upgrade --force-reinstall --no-cache-dir "$OUTPUT_ROOT"/controller/python/chip-*.whl +"$ENVIRONMENT_ROOT"/bin/pip install --upgrade --force-reinstall --no-cache-dir $WHEEL + echo "" echo_green "Compilation completed and WHL package installed in: " diff --git a/scripts/tools/zap_regen_all.py b/scripts/tools/zap_regen_all.py index 4bdaca6afe55d4..fb652eab156a1d 100755 --- a/scripts/tools/zap_regen_all.py +++ b/scripts/tools/zap_regen_all.py @@ -39,6 +39,7 @@ def getSpecificTemplatesTargets(): targets.append(['src/controller/data_model/controller-clusters.zap', '-t', 'src/app/common/templates/templates.json']) targets.append(['src/controller/data_model/controller-clusters.zap', '-t', 'examples/chip-tool/templates/templates.json']) targets.append(['src/controller/data_model/controller-clusters.zap', '-t', 'src/controller/python/templates/templates.json']) + targets.append(['src/controller/data_model/controller-clusters.zap', '-t', 'src/pybindings/pycontroller/templates/templates.json']) targets.append(['src/controller/data_model/controller-clusters.zap', '-t', 'src/darwin/Framework/CHIP/templates/templates.json']) targets.append(['src/controller/data_model/controller-clusters.zap', '-t', 'src/controller/java/templates/templates.json']) return targets diff --git a/src/pybindings/Bnder_Code_Gen.md b/src/pybindings/Bnder_Code_Gen.md new file mode 100644 index 00000000000000..0dcc0f9cc9b19b --- /dev/null +++ b/src/pybindings/Bnder_Code_Gen.md @@ -0,0 +1,68 @@ +# Generating PyBind with Binder + +## Install Binder + +- Installation instructions can be found + [here](https://cppbinder.readthedocs.io/en/latest/install.html). + +## Binder Command + +binder --config `./CHIPDeviceController.config` --suppress-errors --root-module +PyChip --prefix `connectedhomeip/src/pybindings/pycontroller/GeneratedFiles` +--flat `all_header.hpp` -- -DCHIP_HAVE_CONFIG_H=1 +-DCHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK=1 -target `x86_64-apple-macos10.15` -x c++ +-std=c++11 +`-I/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/usr/include/` +-Iconnectedhomeip/src -Iconnectedhomeip/src/lib +-Iconnectedhomeip/out/python_lib/gen/include -Iconnectedhomeip/config/standalone +-Iconnectedhomeip/third_party/pigweed/repo/pw_minimal_cpp_stdlib/public +-Iconnectedhomeip/third_party/pigweed/repo/pw_polyfill/standard_library_public +-Iconnectedhomeip/src/include/ + +- Include paths may vary and `highlighted` text may also vary. + +## Binder Output + +- Binder will output several files with prefix PyChip and suffix \_# +- The output will be based on the config and hpp file passed in. +- Since binder is new to this project, manual conversion is necessary. +- Example of the output below: + +![alt text](https://raw.githubusercontent.com/krypton36/images/master/ss3.png) + +## Binder Conversions + +- If the file contains everythin you want then the file can be copied over and + added. +- If there is just a class and it can be added to the existing, the class + binding can be added. + +Example of conversion of Logging: + +- Search for the module: + + alt text + +- Combined these two to a single Logging file + + alt text + -> + alt text + +- Rename function to bind_PyChip_Logging: + + alt text + +- Add the logging binding to Main + + alt text + + alt text-> alt text-> alt text->alt text + +- Add to BUILD GN file: + +alt text + +- Lastly Test: + + alt text diff --git a/src/pybindings/pycontroller/BUILD.gn b/src/pybindings/pycontroller/BUILD.gn new file mode 100644 index 00000000000000..debb877af30ceb --- /dev/null +++ b/src/pybindings/pycontroller/BUILD.gn @@ -0,0 +1,183 @@ +# Copyright (c) 2021 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/pigweed.gni") + +import("$dir_pw_build/python.gni") + +import("${chip_root}/build/chip/tools.gni") +import("${chip_root}/src/platform/device.gni") +import("${dir_pw_unit_test}/test.gni") + +if (current_os == "mac") { + import("${build_root}/config/mac/mac_sdk.gni") +} + +config("controller_wno_deprecate") { + cflags = [ + "-Wno-deprecated-declarations", + "-Wno-shadow", + "-Wno-unused-result", + "-Wsign-compare", + "-Wunreachable-code", + "-Wno-macro-redefined", + ] +} + +shared_library("CHIPController") { + configs -= [ "//build/config/compiler:exceptions_default" ] + + output_name = "PyChip" + output_dir = "${target_out_dir}/pybindings" + include_dirs = [ "${chip_root}/third_party/pybind11/repo/include" ] + if (current_os == "mac") { + include_dirs += + [ "${chip_root}/.environment/cipd/python/include/python3.8" ] + } else if (current_os == "linux") { + include_dirs += [ "/usr/include/python3.9" ] + } else { + assert(false, "OS not supprted.") + } + + sources = [ + "${root_gen_dir}/include/pybindings/pycontroller/CHIPErrorToExceptionBindings.cpp", + "${root_gen_dir}/include/pybindings/pycontroller/CHIPErrorToExceptionBindings.h", + "ControllerBindings/PyChip_Main.cpp", + ] + + public_deps = [ + ":build_exceptions", + "${chip_root}/src/app", + "${chip_root}/src/controller/data_model", + "${chip_root}/src/lib", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/mdns", + "${chip_root}/src/lib/support", + "${chip_root}/src/platform", + "${chip_root}/src/setup_payload", + "${chip_root}/src/transport", + ] + configs += [ ":controller_wno_deprecate" ] + ldflags = [ "-frtti" ] + if (current_os == "mac") { + ldflags += [ + "-undefined", + "dynamic_lookup", + ] + } + if (current_os == "linux") { + libs = [ "python3.9" ] + } +} +pw_python_action("build_exceptions") { + script = "create_error_wrapper.py" + + header_file = "${root_gen_dir}/include/pybindings/pycontroller/CHIPErrorToExceptionBindings.h" + cpp_file = "${root_gen_dir}/include/pybindings/pycontroller/CHIPErrorToExceptionBindings.cpp" + outputs = [ + header_file, + cpp_file, + ] + + args = [ + "--output_cpp_file=" + rebase_path(cpp_file, root_build_dir), + "--output_header_file=" + rebase_path(header_file, root_build_dir), + "--error_header=" + + rebase_path("${chip_root}/src/lib/core/CHIPError.h", root_build_dir), + "--config_header=" + + rebase_path("${chip_root}/src/lib/core/CHIPConfig.h", root_build_dir), + ] +} + +pw_python_action("pycontroller") { + script = "build-chip-wheel.py" + + _py_manifest_files = [ + { + src_dir = "." + sources = [ + ] + }, + { + src_dir = target_out_dir + sources = [ "${target_out_dir}/pybindings/PyChip.so" ] + }, + { + src_dir = "//" + sources = [ "//LICENSE" ] + }, + ] + + _py_manifest_file = "${target_gen_dir}/${target_name}.py_manifest.json" + + inputs = [] + _py_manifest_files_rebased = [] + foreach(_manifest_entry, _py_manifest_files) { + inputs += _manifest_entry.sources + _py_manifest_files_rebased += [ + { + src_dir = rebase_path(_manifest_entry.src_dir, + get_path_info(_py_manifest_file, "dir")) + sources = rebase_path(_manifest_entry.sources, _manifest_entry.src_dir) + }, + ] + } + + _py_manifest = { + files = _py_manifest_files_rebased + } + + write_file(_py_manifest_file, _py_manifest, "json") + + _dist_dir = "${root_out_dir}/pybindings/pycontroller" + + if (current_cpu == "x64") { + cpu_tag = "x86_64" + } else if (current_cpu == "arm64") { + cpu_tag = "aarch64" + } else { + cpu_tag = current_cpu + } + + if (current_os == "mac") { + platform_tag = "macosx_" + string_replace(mac_deployment_target, ".", "_") + } else { + platform_tag = current_os + } + + platform_tag = platform_tag + "_" + cpu_tag + + tags = "cp37-abi3-" + platform_tag + + args = [ + "--package_name", + "pybindings", + "--build_number", + "0.0", + "--build_dir", + rebase_path("${target_gen_dir}/${target_name}.py_build", root_build_dir), + "--dist_dir", + rebase_path(_dist_dir, root_build_dir), + "--manifest", + rebase_path(_py_manifest_file, root_build_dir), + "--plat-name", + platform_tag, + ] + + public_deps = [ ":CHIPController" ] + + output_name = "pybindings-0.0.dist-info-0.0-${tags}.whl" + outputs = [ "${_dist_dir}/$output_name" ] +} diff --git a/src/pybindings/pycontroller/ControllerBindings/PyChip_Main.cpp b/src/pybindings/pycontroller/ControllerBindings/PyChip_Main.cpp new file mode 100644 index 00000000000000..c268cb45526934 --- /dev/null +++ b/src/pybindings/pycontroller/ControllerBindings/PyChip_Main.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +#include + +typedef std::function ModuleGetter; + +void bind_CHIPController_ChipExceptions(std::function & M); + +PYBIND11_MODULE(PyChip, root_module) +{ + root_module.doc() = "PyChip module"; + + std::map modules; + ModuleGetter M = [&](std::string const & namespace_) -> pybind11::module & { + auto it = modules.find(namespace_); + if (it == modules.end()) + throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + + " before it was created!!!"); + return it->second; + }; + + modules[""] = root_module; + + std::vector> sub_modules{ + { "", "ChipExceptions" } + }; + for (auto & p : sub_modules) + modules[p.first.size() ? p.first + "::" + p.second : p.second] = + modules[p.first].def_submodule(p.second.c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str()); + + bind_CHIPController_ChipExceptions(M); +} diff --git a/src/pybindings/pycontroller/build-chip-wheel.py b/src/pybindings/pycontroller/build-chip-wheel.py new file mode 100644 index 00000000000000..60fd3f132a626b --- /dev/null +++ b/src/pybindings/pycontroller/build-chip-wheel.py @@ -0,0 +1,185 @@ +# +# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2019 Google LLC. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Description: +# Builds a Python wheel package for CHIP. +# + +from __future__ import absolute_import +from datetime import datetime +from setuptools import setup +from wheel.bdist_wheel import bdist_wheel + +import argparse +import json +import os +import platform +import shutil + + +parser = argparse.ArgumentParser(description='build the pip package for chip using chip components generated during the build and python source code') +parser.add_argument('--package_name', default='chip', help='configure the python package name') +parser.add_argument('--build_number', default='0.0', help='configure the chip build number') +parser.add_argument('--build_dir', help='directory to build in') +parser.add_argument('--dist_dir', help='directory to place distribution in') +parser.add_argument('--manifest', help='list of files to package') +parser.add_argument('--plat-name', help='platform name to embed in generated filenames') + +args = parser.parse_args() + +class InstalledScriptInfo: + """Information holder about a script that is to be installed.""" + + def __init__(self, name): + self.name = name + self.installName = os.path.splitext(name)[0] + + +chipDLLName = 'PyChip.so' +packageName = args.package_name +chipPackageVer = args.build_number + +installScripts = [ + # InstalledScriptInfo('chip-device-ctrl.py'), + # InstalledScriptInfo('chip-repl.py'), +] + +# Record the current directory at the start of execution. +curDir = os.curdir + +manifestFile = os.path.abspath(args.manifest) +buildDir = os.path.abspath(args.build_dir) +distDir = os.path.abspath(args.dist_dir) + +# Use a temporary directory within the build directory to assemble the components +# for the installable package. +tmpDir = os.path.join(buildDir, 'chip-wheel-components') + +manifest = json.load(open(manifestFile, 'r')) + +try: + + # + # Perform a series of setup steps prior to creating the chip package... + # + + # Create the temporary components directory. + if os.path.isdir(tmpDir): + shutil.rmtree(tmpDir) + os.makedirs(tmpDir, exist_ok=True) + + # Switch to the temporary directory. (Foolishly, setuptools relies on the current directory + # for many of its features.) + os.chdir(tmpDir) + + manifestBase = os.path.dirname(manifestFile) + for entry in manifest['files']: + srcDir = os.path.join(manifestBase, entry['src_dir']) + for path in entry['sources']: + srcFile = os.path.join(srcDir, path) + dstFile = os.path.join(tmpDir, path) + os.makedirs(os.path.dirname(dstFile), exist_ok=True) + shutil.copyfile(srcFile, dstFile) + + for script in installScripts: + os.rename(os.path.join(tmpDir, script.name), + os.path.join(tmpDir, script.installName)) + + # Define a custom version of the bdist_wheel command that configures the + # resultant wheel as platform-specific (i.e. not "pure"). + class bdist_wheel_override(bdist_wheel): + def finalize_options(self): + bdist_wheel.finalize_options(self) + self.root_is_pure = False + + requiredPackages = [ + "coloredlogs", + 'construct', + 'ipython', + ] + + if platform.system() == 'Darwin': + requiredPackages.append('pyobjc-framework-corebluetooth') + + if platform.system() == 'Linux': + requiredPackages.append('dbus-python') + requiredPackages.append('pygobject') + + # + # Build the chip package... + # + packages=[ + 'pybindings', + ] + + # Invoke the setuptools 'bdist_wheel' command to generate a wheel containing + # the CHIP python packages, shared libraries and scripts. + setup( + name=packageName, + version=chipPackageVer, + description='Python-base APIs and tools for CHIP.', + url='https://github.com/project-chip/connectedhomeip', + license='Apache', + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + ], + python_requires='>=2.7', + packages=packages, + package_dir={ + '':tmpDir, # By default, look in the tmp directory for packages/modules to be included. + }, + package_data={ + packageName:[ + chipDLLName # Include the wrapper DLL as package data in the "chip" package. + ] + }, + scripts = [name for name in map( + lambda script: os.path.join(tmpDir, script.installName), + installScripts + )], + install_requires=requiredPackages, + options={ + 'bdist_wheel':{ + 'universal':False, + 'dist_dir':distDir, # Place the generated .whl in the dist directory. + 'py_limited_api':'cp37', + 'plat_name':args.plat_name, + }, + 'egg_info':{ + 'egg_base':tmpDir # Place the .egg-info subdirectory in the tmp directory. + } + }, + cmdclass={ + 'bdist_wheel':bdist_wheel_override + }, + script_args=[ 'clean', '--all', 'bdist_wheel' ] + ) + +finally: + + # Switch back to the initial current directory. + os.chdir(curDir) + + # Remove the temporary directory. + if os.path.isdir(tmpDir): + shutil.rmtree(tmpDir) diff --git a/src/pybindings/pycontroller/create_error_wrapper.py b/src/pybindings/pycontroller/create_error_wrapper.py new file mode 100644 index 00000000000000..3a147a03d5622b --- /dev/null +++ b/src/pybindings/pycontroller/create_error_wrapper.py @@ -0,0 +1,150 @@ +import sys +import re +import os +import argparse + + +class ErrorCode(object): + def __init__(self, error_name, error_code): + self.name = error_name.replace('_', ' ').title().replace(' ', '') + self.message = error_name + self.code = int(error_code, 16) + + def __str__(self): + return ''.format(self.name, self.code) + + def __repr__(self): + return ''.format(self.name, self.code) + + def create_exception_class(self): + return ''' +struct : public std::exception +{ + const char * what () const throw () + { + return ""; + } +}; +'''.replace('', self.name).replace('', self.message) + + def create_py_bind(self, module_name): + return 'py::register_exception({}, "{}");'.format(self.name, module_name, self.name) + + def create_else_if(self, var_name): + return ''' + else if ( == ) { + throw chip::PythonBindings::(); + }'''.replace('', var_name).replace('', str(self.code)).replace('', self.name) + + +def generate_header(error_codes): + structs = "".join([x.create_exception_class() for x in error_codes]) + return ''' +#include +#include + +namespace chip { +namespace PythonBindings { + + +void CHIPErrorToException(CHIP_ERROR err); +} +} +'''.replace('', structs) + + +def generate_cpp(error_codes): + exception_binding = '\n\t'.join( + [x.create_py_bind('M("ChipExceptions")') for x in error_codes]) + elseifs = ''.join([x.create_else_if('err') for x in error_codes]) + return ''' +#include "pybind11/pybind11.h" +#include "CHIPErrorToExceptionBindings.h" +#include + +namespace py = pybind11; + +void CHIPErrorToException(CHIP_ERROR err) { + if (err == CHIP_NO_ERROR) { + //Do Nothing + } +} + +void bind_CHIPController_ChipExceptions(std::function< pybind11::module &(std::string const &namespace_) > &M) +{ + M("ChipExceptions").def("CHIPErrorToException", &CHIPErrorToException, "Converts a CHIP_ERROR (int) to an Exception"); + + +} +'''.replace('', exception_binding).replace('', elseifs) + + +def create_error_bindings(error_path, config_path, header_out, cpp_out): + error_header = None + config_header = None + + with open(error_path, 'r') as f: + error_header = f.read() + + with open(config_path, 'r') as f: + config_header = f.read() + + if error_header != None and config_header != None: + error_code_re = re.compile(r'#define ([^\s]+).*CHIP_CORE_ERROR\((0[xX][0-9a-fA-F]+)\)') + error_code_tuples = error_code_re.findall(error_header) + error_codes = [ErrorCode(x[0], x[1]) + for x in error_code_tuples] + header = generate_header(error_codes) + cpp = generate_cpp(error_codes) + + print("Creating Header file: {}".format(header_out)) + with open(header_out, 'w') as f: + f.write(header) + + print("Creating CPP file {}".format(cpp_out)) + with open(cpp_out, 'w') as f: + f.write(cpp) + else: + print("Unable to open required files :(") + sys.exit(1) + + +def main(): + p = argparse.ArgumentParser() + + p.add_argument('--output_cpp_file', required=True, + help='Output location for .cpp file') + p.add_argument('--output_header_file', required=True, + help='Output location for .h file') + p.add_argument('--error_header', required=False, + help='Location of Error Header file (ex CHIPError.h)') + p.add_argument('--config_header', required=False, + help='Location of Config Header file (ex CHIPConfig.h)') + + args = p.parse_args() + + output_cpp_file = args.output_cpp_file + output_header_file = args.output_header_file + + error_path = '../../lib/core/CHIPError.h' + config_path = '../../lib/core/CHIPConfig.h' + + if args.error_header != None and args.error_header != "": + error_path = os.path.abspath(args.error_header) + + if not os.path.exists(error_path): + print("Error Header Path {} does not exist".format(error_path)) + sys.exit(1) + + if args.config_header != None and args.config_header != "": + config_path = os.path.abspath(args.config_header) + + if not os.path.exists(config_path): + print("Config Header Path {} does not exist!".format(config_path)) + + create_error_bindings(error_path, config_path, + output_header_file, output_cpp_file) + + +if __name__ == '__main__': + main() diff --git a/third_party/pybind11/repo b/third_party/pybind11/repo new file mode 160000 index 00000000000000..8de7772cc72dac --- /dev/null +++ b/third_party/pybind11/repo @@ -0,0 +1 @@ +Subproject commit 8de7772cc72daca8e947b79b83fea46214931604