Implementation of the gradle-android Docker file and build_android_library github action. (#7143)
This implements a new Github action `.github/workflows/build_android_library.yml` that runs our workflow to build IREE's TFLite Java bindings via `build_tools/gradle/build_tflite_android_library.sh`.
The Github Action uses a new `gradle-android/Docker` file to produce consistent repeatable builds locally and remote.
On some systems, gradle fails to run the `externalNativeBuild` task with an unexplained NullPointer error. To get around this. We manually build the native IREE TFLite compatibility shim with [Android cross-compilation](https://google.github.io/iree/building-from-source/android/). The resulting library is copied into the `jniLibs` folder to be included directly in the final library, while native build tasks are skipped by gradle.
Additional changes to fix recent issues with the gradle build:
* Switching to the support Android library for the `NonNull` annotation
* Encapsulating `mlir-tablegen` target with the `IREE_BUILD_COMPILER ` in `build_tools/cmake/iree_copts.cmake`. This caused build failure when compiling the host-tooling without compiler targets (avoid building all of LLVM) since tablegen targets can't compile without other compiler dependencies hidden behind this flag.
diff --git a/.github/workflows/android_tflite_oneshot_build.yml b/.github/workflows/android_tflite_oneshot_build.yml
new file mode 100644
index 0000000..1c48213
--- /dev/null
+++ b/.github/workflows/android_tflite_oneshot_build.yml
@@ -0,0 +1,27 @@
+# Builds the TFLite Java Bindings Android Library using the gradle-android
+# Docker image.
+name: Build TFLite Android Library
+
+on:
+ workflow_dispatch:
+
+jobs:
+ build_android_with_docker:
+ runs-on: ubuntu-latest
+ env:
+ ANDROID_CONTAINER: "gcr.io/iree-oss/gradle-android@sha256:54cddc42e13d63da5adf495acb4e96c2773b66a1169cf8a9aac26a21f4166e2e"
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ - name: Execute Android Build
+ run: |
+ docker run --rm \
+ -w=/work \
+ -v $PWD:/work \
+ "${ANDROID_CONTAINER}" \
+ bash -c build_tools/gradle/build_tflite_android_library.sh
+ - uses: actions/upload-artifact@v2
+ with:
+ path: ./bindings/tflite/java/build/outputs/aar/*.aar
+ retention-days: 1
diff --git a/bindings/tflite/java/build.gradle b/bindings/tflite/java/build.gradle
index fe521a1..325f6f1 100644
--- a/bindings/tflite/java/build.gradle
+++ b/bindings/tflite/java/build.gradle
@@ -54,6 +54,7 @@
exclude('tests/**') // Don't build tests into the library
}
jni.srcDirs = ['org/tensorflow/lite/native']
+ jniLibs.srcDirs = ['jniLibs/']
}
}
diff --git a/bindings/tflite/java/org/tensorflow/lite/Interpreter.java b/bindings/tflite/java/org/tensorflow/lite/Interpreter.java
index 80c0b05..4500939 100644
--- a/bindings/tflite/java/org/tensorflow/lite/Interpreter.java
+++ b/bindings/tflite/java/org/tensorflow/lite/Interpreter.java
@@ -8,7 +8,7 @@
package org.tensorflow.lite;
-import androidx.annotation.NonNull;
+import android.support.annotation.NonNull;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.HashMap;
diff --git a/build_tools/cmake/iree_copts.cmake b/build_tools/cmake/iree_copts.cmake
index 5a92b6f..e477eea 100644
--- a/build_tools/cmake/iree_copts.cmake
+++ b/build_tools/cmake/iree_copts.cmake
@@ -391,12 +391,12 @@
#-------------------------------------------------------------------------------
# Third party: llvm-project
#-------------------------------------------------------------------------------
-
-set(MLIR_TABLEGEN_EXE mlir-tblgen)
-# iree-tblgen is not defined using the add_tablegen mechanism as other TableGen
-# tools in LLVM.
-iree_get_executable_path(IREE_TABLEGEN_EXE iree-tblgen)
-
+if(IREE_BUILD_COMPILER)
+ set(MLIR_TABLEGEN_EXE mlir-tblgen)
+ # iree-tblgen is not defined using the add_tablegen mechanism as other TableGen
+ # tools in LLVM.
+ iree_get_executable_path(IREE_TABLEGEN_EXE iree-tblgen)
+endif()
#-------------------------------------------------------------------------------
# Third party: mlir-emitc
#-------------------------------------------------------------------------------
diff --git a/build_tools/docker/gradle-android/Dockerfile b/build_tools/docker/gradle-android/Dockerfile
new file mode 100644
index 0000000..fb7b98b
--- /dev/null
+++ b/build_tools/docker/gradle-android/Dockerfile
@@ -0,0 +1,63 @@
+# Copyright 2021 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# An image for cross-compiling IREE's TFLite Java Bindings with Gradle and
+# CMake.
+
+ARG JDK_VERSION=11
+
+ARG GRADLE_VERSION=7.1.1
+ARG GRADLE_DIST=bin
+
+ARG ANDROID_SDK_VERSION=7583922
+ARG ANDROID_NDK_VERSION=21.4.7075529
+
+FROM gcr.io/iree-oss/util@sha256:40846b4aea5886af3250399d6adfdb3e1195a8b0177706bb0375e812d62dc49c AS install-deps
+ARG GRADLE_VERSION
+ARG GRADLE_DIST
+ARG ANDROID_SDK_VERSION
+
+# Download and install Gradle
+RUN cd /opt && \
+ wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-${GRADLE_DIST}.zip && \
+ unzip gradle*.zip && \
+ rm gradle*.zip && \
+ ln -s /opt/gradle-${GRADLE_VERSION}/bin/gradle /usr/bin/gradle
+
+# Download and install Android SDK
+# Note: Uses the latest SDK version from https://developer.android.com/studio,
+# however Gradle will automatically download any additional SDK/tooling versions
+# as necessary.
+ENV ANDROID_SDK_ROOT /opt/android-sdk
+RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
+ wget -q https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \
+ unzip *tools*linux*.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \
+ mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/tools && \
+ rm *tools*linux*.zip
+
+FROM gcr.io/iree-oss/cmake@sha256:9d9953acf5ca0cf1ff3e8de32f10f24dfab1c4e8ec5d1fc047f556024ee4bed6 as final
+ARG ANDROID_SDK_VERSION
+ARG ANDROID_NDK_VERSION
+ARG GRADLE_VERSION
+ENV ANDROID_SDK_ROOT /opt/android-sdk
+
+# Download and install openjdk-11
+ARG JDK_VERSION
+RUN apt-get update && apt-get install -y openjdk-11-jdk
+
+# Copy /opt/ (Gradle + Android SDK) thenlink Gradle bin
+COPY --from=install-deps /opt/ /opt/
+RUN ln -s /opt/gradle-${GRADLE_VERSION}/bin/gradle /usr/bin/gradle
+
+# Accept the license agreements of the Android SDK components
+RUN yes | ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --licenses
+
+# Install the Android NDK
+RUN /opt/android-sdk/cmdline-tools/tools/bin/sdkmanager --install "ndk;$ANDROID_NDK_VERSION"
+
+# Define environment variables for the NDK/SDK
+ENV ANDROID_HOME ${ANDROID_SDK_ROOT}
+ENV ANDROID_NDK /opt/android-sdk/ndk/${ANDROID_NDK_VERSION}
diff --git a/build_tools/docker/manage_images.py b/build_tools/docker/manage_images.py
index cce0114..03dfbbe 100755
--- a/build_tools/docker/manage_images.py
+++ b/build_tools/docker/manage_images.py
@@ -60,6 +60,7 @@
'cmake-bazel-frontends-vulkan', 'swiftshader'
],
'cmake-bazel-frontends-nvidia': ['cmake-bazel-frontends-vulkan'],
+ 'gradle-android': ['cmake'],
'rbe-toolchain': ['vulkan'],
'samples': ['cmake-python-swiftshader'],
'swiftshader': ['cmake'],
diff --git a/build_tools/docker/prod_digests.txt b/build_tools/docker/prod_digests.txt
index a5ec718..c7e88ec 100644
--- a/build_tools/docker/prod_digests.txt
+++ b/build_tools/docker/prod_digests.txt
@@ -19,3 +19,4 @@
gcr.io/iree-oss/samples@sha256:d9ca1dbdcf4fa2b9dd8f032b42b9a74b9b5c5210d823528ea5874dfeec13444a
gcr.io/iree-oss/cmake-emscripten@sha256:6dd6228cd482f7bbc64bca1ee7618a55eda408148e6d6588c58e98f89780a279
gcr.io/iree-oss/cmake-gcc@sha256:49bd84535b82012e83de15a8f1ca847076b359a7bd5a8dabda7e76abb43f32e3
+gcr.io/iree-oss/gradle-android@sha256:54cddc42e13d63da5adf495acb4e96c2773b66a1169cf8a9aac26a21f4166e2e
diff --git a/build_tools/gradle/build_tflite_android_library.sh b/build_tools/gradle/build_tflite_android_library.sh
new file mode 100755
index 0000000..e49c6da
--- /dev/null
+++ b/build_tools/gradle/build_tflite_android_library.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+# Copyright 2021 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Cross-compile IREE's TFLite Java Bindings with Gradle and CMake. Produces an
+# iree-tflite-bindings-debug.aar and an iree-tflite-bindings-release.aar
+# library under bindings/tflite/java/output. Designed for CI, but can be run
+# manually.
+
+set -x
+set -e
+set -o pipefail
+
+# --------------------------------------------------------------------------- #
+ROOT_DIR=$(git rev-parse --show-toplevel)
+
+CMAKE_BIN=${CMAKE_BIN:-$(which cmake)}
+
+"${CMAKE_BIN?}" --version
+ninja --version
+gradle --version
+
+cd ${ROOT_DIR?}
+
+# --------------------------------------------------------------------------- #
+# Build the host libraries
+
+HOST_BUILD_DIR=${ROOT_DIR?}/build-host
+HOST_INSTALL_DIR=${HOST_BUILD_DIR}/install
+
+cmake -G Ninja -B ${HOST_BUILD_DIR} \
+ -DIREE_BUILD_COMPILER=OFF \
+ -DIREE_BUILD_TESTS=OFF \
+ -DIREE_BUILD_SAMPLES=OFF \
+ -DCMAKE_C_COMPILER=clang \
+ -DCMAKE_CXX_COMPILER=clang++ \
+ -DCMAKE_INSTALL_PREFIX=${HOST_INSTALL_DIR} .
+
+cmake --build ${HOST_BUILD_DIR} --target install
+
+# --------------------------------------------------------------------------- #
+# Build native libraries
+
+ANDROID_BUILD_DIR=${ROOT_DIR?}/build-android
+
+# Todo: Support multiple ABIs. For now we build a singe abi: arm64.
+ANDROID_ABI=arm64-v8a
+
+cmake -G Ninja -B ${ANDROID_BUILD_DIR} \
+ -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \
+ -DIREE_HOST_BINARY_ROOT="${HOST_INSTALL_DIR}" \
+ -DANDROID_ABI="${ANDROID_ABI}" \
+ -DANDROID_PLATFORM="android-29" \
+ -DIREE_BUILD_COMPILER=OFF \
+ -DIREE_BUILD_BINDINGS_TFLITE=ON \
+ -DIREE_BUILD_BINDINGS_TFLITE_JAVA=ON \
+ -DIREE_BUILD_TESTS=OFF \
+ -DIREE_BUILD_SAMPLES=OFF \
+ -DIREE_BUILD_PYTHON_BINDINGS=OFF .
+
+cmake --build ${ANDROID_BUILD_DIR} --target iree-tflite-bindings
+
+NATIVE_LIBRARY=${ANDROID_BUILD_DIR}/bindings/tflite/java/org/tensorflow/lite/native/libiree-tflite-bindings.so
+
+# --------------------------------------------------------------------------- #
+# Setup the gradle build with native libraries
+
+BINDINGS_DIR=${ROOT_DIR?}/bindings/tflite/java
+cd ${BINDINGS_DIR}
+
+# Copy the native library(s) to the jniLibs folder
+mkdir -p jniLibs/${ANDROID_ABI}
+cp ${NATIVE_LIBRARY} jniLibs/${ANDROID_ABI}
+
+# --------------------------------------------------------------------------- #
+# Build the Android library
+
+gradle wrapper
+
+# Note: since we're providing the native libraries, we omit the tasks that
+# generate them from the build.gradle.
+./gradlew build \
+ -x externalNativeBuildDebug \
+ -x externalNativeBuildCleanDebug \
+ -x externalNativeBuildRelease \
+ -x externalNativeBuildCleanRelease \
+ -x cmakeConfigureHost \
+ -x cmakeBuildHost
+
+echo "Android Library Artifacts:"
+ls build/outputs/aar/