diff --git a/tensorflow/lite/tools/pip_package/README.md b/tensorflow/lite/tools/pip_package/README.md index dac8ce02ca1..8a2be59b980 100644 --- a/tensorflow/lite/tools/pip_package/README.md +++ b/tensorflow/lite/tools/pip_package/README.md @@ -49,6 +49,52 @@ BUILD_DEB=y to the make command (only for python3): make BASE_IMAGE=debian:buster PYTHON=python3 TENSORFLOW_TARGET=rpi BUILD_DEB=y docker-build ``` +## Alternative build with Bazel (experimental) + +There is another build steps to build a binary wheel which uses Bazel instead of +Makefile. You don't need to install additional dependencies. +This approach can leverage TF's ci_build.sh for ARM cross builds. + +### Native build for your workstation + +```sh +tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh +``` + +### Cross build for armhf Python 3.5 + +```sh +CI_DOCKER_EXTRA_PARAMS="-e CI_BUILD_PYTHON=python3 -e CROSSTOOL_PYTHON_INCLUDE_PATH=/usr/include/python3.5" \ + tensorflow/tools/ci_build/ci_build.sh PI-PYTHON3 \ + tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh armhf +``` + +### Cross build for armhf Python 3.7 + +```sh +CI_DOCKER_EXTRA_PARAMS="-e CI_BUILD_PYTHON=python3 -e CROSSTOOL_PYTHON_INCLUDE_PATH=/usr/include/python3.7" \ + tensorflow/tools/ci_build/ci_build.sh PI-PYTHON37 \ + tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh armhf +``` + +### Cross build for aarch64 Python 3.5 + +```sh + CI_DOCKER_EXTRA_PARAMS="-e CI_BUILD_PYTHON=python3 -e CROSSTOOL_PYTHON_INCLUDE_PATH=/usr/include/python3.5" \ + tensorflow/tools/ci_build/ci_build.sh PI-PYTHON3 \ + tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh aarch64 +``` + +### Cross build for aarch64 Python 3.7 + +```sh +CI_DOCKER_EXTRA_PARAMS="-e CI_BUILD_PYTHON=python3 -e CROSSTOOL_PYTHON_INCLUDE_PATH=/usr/include/python3.7" \ + tensorflow/tools/ci_build/ci_build.sh PI-PYTHON37 \ + tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh aarch64 +``` + +## Usage + Note, unlike tensorflow this will be installed to a tflite_runtime namespace. You can then use the Tensorflow Lite interpreter as. diff --git a/tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh b/tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh new file mode 100755 index 00000000000..69afb2f6b80 --- /dev/null +++ b/tensorflow/lite/tools/pip_package/build_pip_package_with_bazel.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# Copyright 2020 The TensorFlow Authors. 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. +# ============================================================================== +set -ex + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PYTHON="${PYTHON:-python3}" +VERSION_SUFFIX=${VERSION_SUFFIX:-} +export TENSORFLOW_DIR="${SCRIPT_DIR}/../../../.." +TENSORFLOW_LITE_DIR="${TENSORFLOW_DIR}/tensorflow/lite" +TENSORFLOW_VERSION=$(grep "_VERSION = " "${TENSORFLOW_DIR}/tensorflow/tools/pip_package/setup.py" | cut -d= -f2 | sed "s/[ '-]//g") +export PACKAGE_VERSION="${TENSORFLOW_VERSION}${VERSION_SUFFIX}" +BUILD_DIR="${SCRIPT_DIR}/gen/tflite_pip/${PYTHON}" +TENSORFLOW_TARGET=$1 + +# Build source tree. +rm -rf "${BUILD_DIR}" && mkdir -p "${BUILD_DIR}/tflite_runtime" +cp -r "${TENSORFLOW_LITE_DIR}/tools/pip_package/debian" \ + "${TENSORFLOW_LITE_DIR}/tools/pip_package/setup_with_bazel.py" \ + "${TENSORFLOW_LITE_DIR}/tools/pip_package/MANIFEST.in" \ + "${TENSORFLOW_LITE_DIR}/python/interpreter_wrapper" \ + "${BUILD_DIR}" +cp "${TENSORFLOW_LITE_DIR}/python/interpreter.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" + +# Build python interpreter_wrapper. +cd "${BUILD_DIR}" +case "${TENSORFLOW_TARGET}" in + rpi|armhf) + BAZEL_FLAGS="--config=elinux_armhf + --copt=-march=armv7-a --copt=-mfpu=neon-vfpv4 + --copt=-O3 --copt=-fno-tree-pre --copt=-fpermissive + --define=raspberry_pi_with_neon=true" + ;; + aarch64) + BAZEL_FLAGS="--config=elinux_aarch64 + --copt=-O3" + ;; + *) + ;; +esac + +# We need to pass down the environment variable with a possible alternate Python +# include path for Python 3.x builds to work. +export CROSSTOOL_PYTHON_INCLUDE_PATH + +bazel build -c opt -s --config=monolithic ${BAZEL_FLAGS} //tensorflow/lite/python/interpreter_wrapper:_pywrap_tensorflow_interpreter_wrapper +cp "${TENSORFLOW_DIR}/bazel-bin/tensorflow/lite/python/interpreter_wrapper/_pywrap_tensorflow_interpreter_wrapper.so" \ + "${BUILD_DIR}/tflite_runtime" + +# Build python wheel. +cd "${BUILD_DIR}" +case "${TENSORFLOW_TARGET}" in + rpi|armhf) + ${PYTHON} setup_with_bazel.py bdist --plat-name=linux-armv7l \ + bdist_wheel --plat-name=linux-armv7l + ;; + aarch64) + ${PYTHON} setup_with_bazel.py bdist --plat-name=linux-aarch64 \ + bdist_wheel --plat-name=linux-aarch64 + ;; + *) + if [[ -n "${TENSORFLOW_TARGET}" ]] && [[ -n "${TENSORFLOW_TARGET_ARCH}" ]]; then + ${PYTHON} setup_with_bazel.py bdist --plat-name=${TENSORFLOW_TARGET}-${TENSORFLOW_TARGET_ARCH} \ + bdist_wheel --plat-name=${TENSORFLOW_TARGET}-${TENSORFLOW_TARGET_ARCH} + else + ${PYTHON} setup_with_bazel.py bdist bdist_wheel + fi + ;; +esac + +echo "Output can be found here:" +find "${BUILD_DIR}" + +# Build debian package. +if [[ "${BUILD_DEB}" != "y" ]]; then + exit 0 +fi + +PYTHON_VERSION=$(${PYTHON} -c "import sys;print(sys.version_info.major)") +if [[ ${PYTHON_VERSION} != 3 ]]; then + echo "Debian package can only be generated for python3." >&2 + exit 1 +fi + +DEB_VERSION=$(dpkg-parsechangelog --show-field Version | cut -d- -f1) +if [[ "${DEB_VERSION}" != "${PACKAGE_VERSION}" ]]; then + cat << EOF > "${BUILD_DIR}/debian/changelog" +tflite-runtime (${PACKAGE_VERSION}-1) unstable; urgency=low + + * Bump version to ${PACKAGE_VERSION}. + + -- TensorFlow team $(date -R) + +$(<"${BUILD_DIR}/debian/changelog") +EOF +fi + +case "${TENSORFLOW_TARGET}" in + rpi|armhf) + dpkg-buildpackage -b -rfakeroot -us -uc -tc -d -a armhf + ;; + aarch64) + dpkg-buildpackage -b -rfakeroot -us -uc -tc -d -a arm64 + ;; + *) + dpkg-buildpackage -b -rfakeroot -us -uc -tc -d + ;; +esac + +cat "${BUILD_DIR}/debian/changelog" + diff --git a/tensorflow/lite/tools/pip_package/setup_with_bazel.py b/tensorflow/lite/tools/pip_package/setup_with_bazel.py new file mode 100644 index 00000000000..e3e9a35a62e --- /dev/null +++ b/tensorflow/lite/tools/pip_package/setup_with_bazel.py @@ -0,0 +1,70 @@ +# Copyright 2020 The TensorFlow Authors. 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. +# ============================================================================== +"""TensorFlow Lite is for mobile and embedded devices. + +TensorFlow Lite is the official solution for running machine learning models on +mobile and embedded devices. It enables on-device machine learning inference +with low latency and a small binary size on Android, iOS, and other operating +systems. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +from setuptools import find_packages +from setuptools import setup +PACKAGE_NAME = 'tflite_runtime' +PACKAGE_VERSION = os.environ['PACKAGE_VERSION'] +DOCLINES = __doc__.split('\n') + +setup( + name=PACKAGE_NAME.replace('_', '-'), + version=PACKAGE_VERSION, + description=DOCLINES[0], + long_description='\n'.join(DOCLINES[2:]), + url='https://www.tensorflow.org/lite/', + author='Google, LLC', + author_email='packages@tensorflow.org', + license='Apache 2.0', + include_package_data=True, + keywords='tflite tensorflow tensor machine learning', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: Education', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Mathematics', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Software Development', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + packages=find_packages(exclude=[]), + package_dir={'': '.'}, + package_data={'': ['*.so']}, + install_requires=[ + 'numpy >= 1.16.0', + 'pybind11 >= 2.4.3', + ]) diff --git a/tensorflow/tools/ci_build/install/install_pi_python37_toolchain.sh b/tensorflow/tools/ci_build/install/install_pi_python37_toolchain.sh index 7688a081d6f..3bda56af648 100755 --- a/tensorflow/tools/ci_build/install/install_pi_python37_toolchain.sh +++ b/tensorflow/tools/ci_build/install/install_pi_python37_toolchain.sh @@ -15,12 +15,14 @@ # ============================================================================== dpkg --add-architecture armhf -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial-updates main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial-security main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial-backports main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +dpkg --add-architecture arm64 +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial-updates main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial-security main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial-backports main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list sed -i 's#deb http://archive.ubuntu.com/ubuntu/#deb [arch=amd64] http://archive.ubuntu.com/ubuntu/#g' /etc/apt/sources.list yes | add-apt-repository ppa:deadsnakes/ppa apt-get update apt-get install -y python3.7 python3-numpy python3.7-dev python3-pip apt-get install -y libpython3.7-dev:armhf +apt-get install -y libpython3.7-dev:arm64 diff --git a/tensorflow/tools/ci_build/install/install_pi_python3_toolchain.sh b/tensorflow/tools/ci_build/install/install_pi_python3_toolchain.sh index 7c87a3fc7c5..b02c35c612d 100755 --- a/tensorflow/tools/ci_build/install/install_pi_python3_toolchain.sh +++ b/tensorflow/tools/ci_build/install/install_pi_python3_toolchain.sh @@ -15,11 +15,13 @@ # ============================================================================== dpkg --add-architecture armhf -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial-updates main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial-security main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list -echo 'deb [arch=armhf] http://ports.ubuntu.com/ xenial-backports main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +dpkg --add-architecture arm64 +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial-updates main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial-security main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list +echo 'deb [arch=arm64,armhf] http://ports.ubuntu.com/ xenial-backports main restricted universe multiverse' >> /etc/apt/sources.list.d/armhf.list sed -i 's#deb http://archive.ubuntu.com/ubuntu/#deb [arch=amd64] http://archive.ubuntu.com/ubuntu/#g' /etc/apt/sources.list apt-get update apt-get install -y libpython3-all-dev:armhf +apt-get install -y libpython3-all-dev:arm64 apt-get install -y python3 python3-numpy python3-dev python3-pip diff --git a/third_party/toolchains/embedded/arm-linux/arm_linux_toolchain_configure.bzl b/third_party/toolchains/embedded/arm-linux/arm_linux_toolchain_configure.bzl index da4282d0215..af34133f27c 100644 --- a/third_party/toolchains/embedded/arm-linux/arm_linux_toolchain_configure.bzl +++ b/third_party/toolchains/embedded/arm-linux/arm_linux_toolchain_configure.bzl @@ -10,6 +10,16 @@ def _tpl(repository_ctx, tpl, substitutions = {}, out = None): ) def _arm_linux_toolchain_configure_impl(repository_ctx): + # We need to find a cross-compilation include directory for Python, so look + # for an environment variable. Be warned, this crosstool template is only + # regenerated on the first run of Bazel, so if you change the variable after + # it may not be reflected in later builds. Doing a shutdown and clean of Bazel + # doesn't fix this, you'll need to delete the generated file at something like: + # external/local_config_arm_compiler/CROSSTOOL in your Bazel install. + if "CROSSTOOL_PYTHON_INCLUDE_PATH" in repository_ctx.os.environ: + python_include_path = repository_ctx.os.environ["CROSSTOOL_PYTHON_INCLUDE_PATH"] + else: + python_include_path = "/usr/include/python3.5" _tpl(repository_ctx, "cc_config.bzl", { "%{AARCH64_COMPILER_PATH}%": str(repository_ctx.path( repository_ctx.attr.aarch64_repo, @@ -17,6 +27,7 @@ def _arm_linux_toolchain_configure_impl(repository_ctx): "%{ARMHF_COMPILER_PATH}%": str(repository_ctx.path( repository_ctx.attr.armhf_repo, )), + "%{PYTHON_INCLUDE_PATH}%": python_include_path, }) repository_ctx.symlink(repository_ctx.attr.build_file, "BUILD") diff --git a/third_party/toolchains/embedded/arm-linux/cc_config.bzl.tpl b/third_party/toolchains/embedded/arm-linux/cc_config.bzl.tpl index 06aaaecfa74..afbea6a3e34 100644 --- a/third_party/toolchains/embedded/arm-linux/cc_config.bzl.tpl +++ b/third_party/toolchains/embedded/arm-linux/cc_config.bzl.tpl @@ -252,6 +252,10 @@ def _impl(ctx): "%{AARCH64_COMPILER_PATH}%/aarch64-linux-gnu/include/c++/8.3.0/", "-isystem", "%{AARCH64_COMPILER_PATH}%/aarch64-linux-gnu/libc/usr/include/", + "-isystem", + "%{PYTHON_INCLUDE_PATH}%", + "-isystem", + "/usr/include/", ], ), ], @@ -347,6 +351,10 @@ def _impl(ctx): "%{ARMHF_COMPILER_PATH}%/arm-linux-gnueabihf/include/c++/8.3.0/", "-isystem", "%{ARMHF_COMPILER_PATH}%/arm-linux-gnueabihf/libc/usr/include/", + "-isystem", + "%{PYTHON_INCLUDE_PATH}%", + "-isystem", + "/usr/include/", ], ), ], @@ -466,6 +474,7 @@ def _impl(ctx): "%{AARCH64_COMPILER_PATH}%/lib/gcc/aarch64-linux-gnu/8.3.0/include-fixed", "%{AARCH64_COMPILER_PATH}%/aarch64-linux-gnu/include/c++/8.3.0/", "%{AARCH64_COMPILER_PATH}%/aarch64-linux-gnu/libc/usr/include/", + "/usr/include", ] elif (ctx.attr.cpu == "armhf"): cxx_builtin_include_directories = [ @@ -473,6 +482,7 @@ def _impl(ctx): "%{ARMHF_COMPILER_PATH}%/lib/gcc/arm-linux-gnueabihf/8.3.0/include-fixed", "%{ARMHF_COMPILER_PATH}%/arm-linux-gnueabihf/include/c++/8.3.0/", "%{ARMHF_COMPILER_PATH}%/arm-linux-gnueabihf/libc/usr/include/", + "/usr/include", ] else: fail("Unreachable")