diff --git a/pythonforandroid/recipes/pybind11/__init__.py b/pythonforandroid/recipes/pybind11/__init__.py new file mode 100644 index 0000000000..989ed5cb82 --- /dev/null +++ b/pythonforandroid/recipes/pybind11/__init__.py @@ -0,0 +1,14 @@ +from pythonforandroid.recipe import Recipe +from os.path import join + + +class Pybind11Recipe(Recipe): + + version = '2.9.0' + url = 'https://github.com/pybind/pybind11/archive/refs/tags/v{version}.zip' + + def get_include_dir(self, arch): + return join(self.get_build_dir(arch.arch), 'include') + + +recipe = Pybind11Recipe() diff --git a/pythonforandroid/recipes/tflite-runtime/CMakeLists.patch b/pythonforandroid/recipes/tflite-runtime/CMakeLists.patch new file mode 100644 index 0000000000..f39d9b3292 --- /dev/null +++ b/pythonforandroid/recipes/tflite-runtime/CMakeLists.patch @@ -0,0 +1,28 @@ +--- tflite-runtime/tensorflow/lite/CMakeLists.txt 2022-01-27 17:29:49.460000000 -1000 ++++ CMakeLists.txt 2022-02-21 15:03:09.568367300 -1000 +@@ -220,6 +220,9 @@ + if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "iOS") + list(FILTER TFLITE_SRCS EXCLUDE REGEX ".*minimal_logging_ios\\.cc$") + endif() ++if("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") ++ list(FILTER TFLITE_SRCS EXCLUDE REGEX ".*minimal_logging_default\\.cc$") ++endif() + populate_tflite_source_vars("core" TFLITE_CORE_SRCS) + populate_tflite_source_vars("core/api" TFLITE_CORE_API_SRCS) + populate_tflite_source_vars("c" TFLITE_C_SRCS) +@@ -505,6 +508,7 @@ + ruy + ${CMAKE_DL_LIBS} + ${TFLITE_TARGET_DEPENDENCIES} ++ ${ANDROID_LOG_LIB} + ) + + if (NOT BUILD_SHARED_LIBS) +@@ -550,6 +554,7 @@ + tensorflow-lite + ${CMAKE_DL_LIBS} + ) ++ + target_compile_options(_pywrap_tensorflow_interpreter_wrapper + PUBLIC ${TFLITE_TARGET_PUBLIC_OPTIONS} + PRIVATE ${TFLITE_TARGET_PRIVATE_OPTIONS} diff --git a/pythonforandroid/recipes/tflite-runtime/__init__.py b/pythonforandroid/recipes/tflite-runtime/__init__.py new file mode 100644 index 0000000000..0627b7ae69 --- /dev/null +++ b/pythonforandroid/recipes/tflite-runtime/__init__.py @@ -0,0 +1,100 @@ +from pythonforandroid.recipe import PythonRecipe, current_directory,\ + shprint, info_main, warning +from pythonforandroid.logger import error +from os.path import join +import sh + + +class TFLiteRuntimeRecipe(PythonRecipe): + + ############################################################### + # + # tflite-runtime README: + # https://github.com/Android-for-Python/c4k_tflite_example/blob/main/README.md + # + # Recipe build references: + # https://developer.android.com/ndk/guides/cmake + # https://developer.android.com/ndk/guides/cpu-arm-neon#cmake + # https://www.tensorflow.org/lite/guide/build_cmake + # https://www.tensorflow.org/lite/guide/build_cmake_arm + # + # Tested using cmake 3.16.3 probably requires cmake >= 3.13 + # + # THIS RECIPE DOES NOT BUILD x86_64, USE X86 FOR AN EMULATOR + # + ############################################################### + + version = '2.8.0' + url = 'https://github.com/tensorflow/tensorflow/archive/refs/tags/v{version}.zip' + depends = ['pybind11', 'numpy'] + patches = ['CMakeLists.patch', 'build_with_cmake.patch'] + site_packages_name = 'tflite-runtime' + call_hostpython_via_targetpython = False + + def build_arch(self, arch): + if arch.arch == 'x86_64': + warning("******** tflite-runtime x86_64 will not be built *******") + warning("Expect one of these app run time error messages:") + warning("ModuleNotFoundError: No module named 'tensorflow'") + warning("ModuleNotFoundError: No module named 'tflite_runtime'") + warning("Use x86 not x86_64") + return + + env = self.get_recipe_env(arch) + + # Directories + root_dir = self.get_build_dir(arch.arch) + script_dir = join(root_dir, + 'tensorflow', 'lite', 'tools', 'pip_package') + build_dir = join(script_dir, 'gen', 'tflite_pip', 'python3') + + # Includes + python_include_dir = self.ctx.python_recipe.include_root(arch.arch) + pybind11_recipe = self.get_recipe('pybind11', self.ctx) + pybind11_include_dir = pybind11_recipe.get_include_dir(arch) + numpy_include_dir = join(self.ctx.get_site_packages_dir(arch), + 'numpy', 'core', 'include') + includes = ' -I' + python_include_dir +\ + ' -I' + numpy_include_dir +\ + ' -I' + pybind11_include_dir + + # Scripts + build_script = join(script_dir, 'build_pip_package_with_cmake.sh') + toolchain = join(self.ctx.ndk_dir, + 'build', 'cmake', 'android.toolchain.cmake') + + # Build + ######## + with current_directory(root_dir): + env.update({ + 'TENSORFLOW_TARGET': 'android', + 'CMAKE_TOOLCHAIN_FILE': toolchain, + 'ANDROID_PLATFORM': str(self.ctx.ndk_api), + 'ANDROID_ABI': arch.arch, + 'WRAPPER_INCLUDES': includes, + 'CMAKE_SHARED_LINKER_FLAGS': env['LDFLAGS'], + }) + + try: + info_main('tflite-runtime is building...') + info_main('Expect this to take at least 5 minutes...') + cmd = sh.Command(build_script) + cmd(_env=env) + except sh.ErrorReturnCode as e: + error(str(e.stderr)) + exit(1) + + # Install + ########## + info_main('Installing tflite-runtime into site-packages') + with current_directory(build_dir): + hostpython = sh.Command(self.hostpython_location) + install_dir = self.ctx.get_python_install_dir(arch.arch) + env['PACKAGE_VERSION'] = self.version + shprint(hostpython, 'setup.py', 'install', '-O2', + '--root={}'.format(install_dir), + '--install-lib=.', + _env=env) + + +recipe = TFLiteRuntimeRecipe() diff --git a/pythonforandroid/recipes/tflite-runtime/build_with_cmake.patch b/pythonforandroid/recipes/tflite-runtime/build_with_cmake.patch new file mode 100644 index 0000000000..9670e1865f --- /dev/null +++ b/pythonforandroid/recipes/tflite-runtime/build_with_cmake.patch @@ -0,0 +1,48 @@ +--- tflite-runtime/tensorflow/lite/tools/pip_package/build_pip_package_with_cmake.sh 2022-01-22 08:57:16.000000000 -1000 ++++ build_pip_package_with_cmake.sh 2022-03-02 18:19:05.185550500 -1000 +@@ -28,7 +28,7 @@ + export TENSORFLOW_TARGET="armhf" + fi + PYTHON_INCLUDE=$(${PYTHON} -c "from sysconfig import get_paths as gp; print(gp()['include'])") +-PYBIND11_INCLUDE=$(${PYTHON} -c "import pybind11; print (pybind11.get_include())") ++# PYBIND11_INCLUDE=$(${PYTHON} -c "import pybind11; print (pybind11.get_include())") + export CROSSTOOL_PYTHON_INCLUDE_PATH=${PYTHON_INCLUDE} + + # Fix container image for cross build. +@@ -58,7 +58,7 @@ + "${TENSORFLOW_LITE_DIR}/python/metrics/metrics_portable.py" \ + "${BUILD_DIR}/tflite_runtime" + echo "__version__ = '${PACKAGE_VERSION}'" >> "${BUILD_DIR}/tflite_runtime/__init__.py" +-echo "__git_version__ = '$(git -C "${TENSORFLOW_DIR}" describe)'" >> "${BUILD_DIR}/tflite_runtime/__init__.py" ++echo "__git_version__ = '${PACKAGE_VERSION}'" >> "${BUILD_DIR}/tflite_runtime/__init__.py" + + # Build python interpreter_wrapper. + mkdir -p "${BUILD_DIR}/cmake_build" +@@ -111,6 +111,18 @@ + -DCMAKE_CXX_FLAGS="${BUILD_FLAGS}" \ + "${TENSORFLOW_LITE_DIR}" + ;; ++ android) ++ BUILD_FLAGS=${BUILD_FLAGS:-"${WRAPPER_INCLUDES}"} ++ cmake \ ++ -DCMAKE_SYSTEM_NAME=Android \ ++ -DANDROID_ARM_NEON=ON \ ++ -DCMAKE_CXX_FLAGS="${BUILD_FLAGS}" \ ++ -DCMAKE_SHARED_LINKER_FLAGS="${CMAKE_SHARED_LINKER_FLAGS}" \ ++ -DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}" \ ++ -DANDROID_PLATFORM="${ANDROID_PLATFORM}" \ ++ -DANDROID_ABI="${ANDROID_ABI}" \ ++ "${TENSORFLOW_LITE_DIR}" ++ ;; + *) + BUILD_FLAGS=${BUILD_FLAGS:-"-I${PYTHON_INCLUDE} -I${PYBIND11_INCLUDE}"} + cmake \ +@@ -162,7 +174,7 @@ + ${PYTHON} setup.py bdist --plat-name=${WHEEL_PLATFORM_NAME} \ + bdist_wheel --plat-name=${WHEEL_PLATFORM_NAME} + else +- ${PYTHON} setup.py bdist bdist_wheel ++ ${PYTHON} setup.py bdist + fi + ;; + esac