NNAPI EP Update (#1483)

* Update DNNLibrary

* Allow fp16 by default

* Add nnapi build in ci

* Fix nnapi ep after #1268

* Remove unused variables

* Support nnapi in onnx_test_runner

* Update DNNLibrary to fix tests

* Update build.py for android build support, solve conflict of
tools/ci_build/build.py

* Support non-ARM Android build, solve conflict of tools/ci_build/build.py

* Enable android test by x86_64 android emulator

* Add dnnlibrary/NNAPI support in build.py

* suppress the verbose adb output

* Remove debug logs

* Install cmake by pip

* Fix undefined host_protoc_path

* cmake==3.13.2 in pypi is actually 3.12.2, so install 3.13.2.post1 instead

* Fix Android ARM64 build

* Use android ndk r20 instead of r19c, fix conflicts in install_deps_android.sh
This commit is contained in:
daquexian 2019-07-25 04:20:05 +08:00 committed by Ke Zhang
parent c0f927c57c
commit ec3c553501
12 changed files with 163 additions and 115 deletions

@ -1 +1 @@
Subproject commit 90cb0f8d60b07e96ca7f0ba92fa50884010599ad
Subproject commit ab22710a3f0166f31c9c14feab98c04bfb86b71b

View file

@ -79,70 +79,65 @@ if(MSVC)
)
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
if(CMAKE_ANDROID_ARCH_ABI MATCHES "^arm.*")
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon")
else()
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
if (CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a")
set(ARM TRUE)
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
set(ARM TRUE) # Android NDK fails to compile sgemma.s
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
set(X86_64 TRUE)
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86")
set(X86 TRUE)
endif()
set(mlas_platform_srcs
${ONNXRUNTIME_ROOT}/core/mlas/lib/arm/sgemmc.cpp
)
else()
execute_process(
COMMAND ${CMAKE_C_COMPILER} -dumpmachine
OUTPUT_VARIABLE dumpmachine_output
ERROR_QUIET
)
message(FATAL_ERROR "Android build is not supported on non-ARM platform now")
if(dumpmachine_output MATCHES "^arm.*")
set(ARM TRUE)
elseif(dumpmachine_output MATCHES "^aarch64.*")
set(ARM64 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86?)$")
set(X86 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(X86_64 TRUE)
endif()
endif()
else()
execute_process(
COMMAND ${CMAKE_C_COMPILER} -dumpmachine
OUTPUT_VARIABLE dumpmachine_output
ERROR_QUIET
)
if(dumpmachine_output MATCHES "^arm.*")
if (ARM)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon")
set(mlas_platform_srcs
${ONNXRUNTIME_ROOT}/core/mlas/lib/arm/sgemmc.cpp
)
elseif(dumpmachine_output MATCHES "^aarch64.*")
)
elseif (ARM64)
enable_language(ASM)
set(mlas_platform_srcs
${ONNXRUNTIME_ROOT}/core/mlas/lib/aarch64/sgemma.s
)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86?)$")
)
elseif (X86)
enable_language(ASM)
set(mlas_platform_srcs_sse2
${ONNXRUNTIME_ROOT}/core/mlas/lib/x86/SgemmKernelSse2.S
)
)
set_source_files_properties(${mlas_platform_srcs_sse2} PROPERTIES COMPILE_FLAGS "-msse2")
set(mlas_platform_srcs_avx
${ONNXRUNTIME_ROOT}/core/mlas/lib/x86/SgemmKernelAvx.S
)
)
set_source_files_properties(${mlas_platform_srcs_avx} PROPERTIES COMPILE_FLAGS "-mavx")
set(mlas_platform_srcs
${mlas_platform_srcs_sse2}
${mlas_platform_srcs_avx}
)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
)
elseif (X86_64)
enable_language(ASM)
# The LLVM assmebler does not support the .arch directive to enable instruction

View file

@ -19,12 +19,12 @@ NnapiExecutionProvider::NnapiExecutionProvider()
DeviceAllocatorRegistrationInfo device_info{OrtMemTypeDefault,
[](int) { return std::make_unique<CPUAllocator>(
std::make_unique<OrtAllocatorInfo>(NNAPI,
OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemTypeDefault)); },
OrtAllocatorType::OrtDeviceAllocator)); },
std::numeric_limits<size_t>::max()};
InsertAllocator(CreateAllocator(device_info));
DeviceAllocatorRegistrationInfo cpu_allocator_info({OrtMemTypeCPUOutput,
[](int) { return std::make_unique<CPUAllocator>(std::make_unique<OrtAllocatorInfo>(NNAPI, OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemTypeCPUOutput)); },
[](int) { return std::make_unique<CPUAllocator>(std::make_unique<OrtAllocatorInfo>(NNAPI, OrtAllocatorType::OrtDeviceAllocator, OrtDevice(), 0, OrtMemTypeCPUOutput)); },
std::numeric_limits<size_t>::max()});
InsertAllocator(CreateAllocator(cpu_allocator_info));
@ -32,22 +32,6 @@ NnapiExecutionProvider::NnapiExecutionProvider()
NnapiExecutionProvider::~NnapiExecutionProvider() {}
Status NnapiExecutionProvider::CopyTensor(const Tensor& src, Tensor& dst) const {
if (!(strcmp(src.Location().name, NNAPI) == 0 && strcmp(dst.Location().name, CPU) == 0) &&
!(strcmp(src.Location().name, CPU) == 0 && strcmp(dst.Location().name, NNAPI) == 0) &&
!(strcmp(src.Location().name, NNAPI) == 0 && strcmp(dst.Location().name, NNAPI) == 0)) {
ORT_NOT_IMPLEMENTED(src.Location().name, " copy to ", dst.Location().name, " is not implemented");
}
// Todo: Copy for now. May optimize later to avoid copy.
size_t bytes = src.DataType()->Size() * src.Shape().Size();
const void* src_data = src.DataRaw();
void* dst_data = dst.MutableDataRaw();
memcpy(dst_data, src_data, bytes);
return Status::OK();
}
std::vector<std::vector<int>> NnapiExecutionProvider::GetSupportedNodes(const ONNX_NAMESPACE::ModelProto& model_proto) const {
dnn::OnnxConverter converter;
return converter.GetSupportedNodes(model_proto);
@ -211,28 +195,6 @@ NnapiExecutionProvider::GetCapability(const onnxruntime::GraphViewer& graph,
common::Status NnapiExecutionProvider::Compile(const std::vector<onnxruntime::Node*>& fused_nodes,
std::vector<NodeComputeInfo>& node_compute_funcs) {
for (const auto* fused_node : fused_nodes) {
std::vector<int> input_indexes;
std::vector<int> input_dim_sizes;
std::vector<int> output_indexes;
std::vector<int> output_dim_sizes;
std::vector<std::vector<int64_t>> output_shapes;
// Build map from input name to its index in input definitions
std::unordered_map<std::string, int> input_map;
const auto& input_defs = fused_node->InputDefs();
input_map.reserve(input_defs.size());
for (int i = 0, end = input_defs.size(); i < end; ++i) {
input_map[input_defs[i]->Name()] = i;
}
// Build map from output name to its index in output definitions
std::unordered_map<std::string, int> output_map;
const auto& output_defs = fused_node->OutputDefs();
output_map.reserve(output_defs.size());
for (int i = 0, end = output_defs.size(); i < end; ++i) {
output_map[output_defs[i]->Name()] = i;
}
// Reconstruct graph proto from fused node's function body
const auto* func_body = fused_node->GetFunctionBody();
if (!func_body) {
@ -248,6 +210,7 @@ common::Status NnapiExecutionProvider::Compile(const std::vector<onnxruntime::No
dnn::OnnxReader onnx_reader;
dnn::ModelBuilder model_builder;
onnx_reader.ReadOnnx(model_proto, model_builder);
model_builder.AllowFp16(true);
auto dnn_model = model_builder.Compile(model_builder.PREFERENCE_SUSTAINED_SPEED);
dnn_models_.emplace(fused_node->Name(), std::move(dnn_model));

View file

@ -18,8 +18,6 @@ class NnapiExecutionProvider : public IExecutionProvider {
common::Status Compile(const std::vector<onnxruntime::Node*>& fused_nodes,
std::vector<NodeComputeInfo>& node_compute_funcs) override;
Status CopyTensor(const Tensor& src, Tensor& dst) const override;
private:
std::unordered_map<std::string, std::unique_ptr<dnn::Model>> dnn_models_;
std::vector<std::vector<int>> GetSupportedNodes(const ONNX_NAMESPACE::ModelProto& model_proto) const;

View file

@ -97,6 +97,7 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
bool enable_tensorrt = false;
bool enable_mem_pattern = true;
bool enable_openvino = false;
bool enable_nnapi = false;
uint32_t graph_optimization_level{};
bool user_graph_optimization_level_set = false;
@ -155,6 +156,8 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
enable_tensorrt = true;
} else if (!CompareCString(optarg, ORT_TSTR("openvino"))) {
enable_openvino = true;
} else if (!CompareCString(optarg, ORT_TSTR("nnapi"))) {
enable_nnapi = true;
} else {
usage();
return -1;
@ -277,6 +280,14 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
return -1;
#endif
}
if (enable_nnapi) {
#ifdef USE_NNAPI
ORT_THROW_ON_ERROR(OrtSessionOptionsAppendExecutionProvider_Nnapi(sf));
#else
fprintf(stderr, "DNNLibrary/NNAPI is not supported in this build");
return -1;
#endif
}
if (user_graph_optimization_level_set) {
sf.SetGraphOptimizationLevel(graph_optimization_level);

View file

@ -24,4 +24,7 @@
#endif
#ifdef USE_OPENVINO
#include "core/providers/openvino/openvino_provider_factory.h"
#endif
#endif
#ifdef USE_NNAPI
#include "core/providers/nnapi/nnapi_provider_factory.h"
#endif

View file

@ -111,6 +111,12 @@ Use the individual flags to only run the specified stages.
parser.add_argument("--arm64", action='store_true',
help="Create ARM64 makefiles. Requires --update and no existing cache CMake setup. Delete CMakeCache.txt if needed")
parser.add_argument("--msvc_toolset", help="MSVC toolset to use. e.g. 14.11")
parser.add_argument("--android", action='store_true', help='Build for Android')
parser.add_argument("--android_abi", type=str, default='arm64-v8a',
help='')
parser.add_argument("--android_api", type=int, default=27,
help='Android API Level, e.g. 21')
parser.add_argument("--android_ndk_path", default="", help="Path to the Android NDK")
# Arguments needed by CI
parser.add_argument("--cmake_path", default="cmake", help="Path to the CMake program.")
@ -124,6 +130,7 @@ Use the individual flags to only run the specified stages.
parser.add_argument("--use_ngraph", action='store_true', help="Build with nGraph.")
parser.add_argument("--use_openvino", nargs="?", const="CPU_FP32",
choices=["CPU_FP32","GPU_FP32","GPU_FP16","VAD-R_FP16","MYRIAD_FP16"], help="Build with OpenVINO for specific hardware.")
parser.add_argument("--use_dnnlibrary", action='store_true', help="Build with DNNLibrary.")
parser.add_argument("--use_nsync", action='store_true', help="Build with NSYNC.")
parser.add_argument("--use_preinstalled_eigen", action='store_true', help="Use pre-installed eigen.")
parser.add_argument("--eigen_path", help="Path to pre-installed eigen.")
@ -334,6 +341,7 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
"-Donnxruntime_USE_OPENVINO_GPU_FP16=" + ("ON" if args.use_openvino == "GPU_FP16" else "OFF"),
"-Donnxruntime_USE_OPENVINO_CPU_FP32=" + ("ON" if args.use_openvino == "CPU_FP32" else "OFF"),
"-Donnxruntime_USE_OPENVINO_VAD_R=" + ("ON" if args.use_openvino == "VAD-R_FP16" else "OFF"),
"-Donnxruntime_USE_NNAPI=" + ("ON" if args.use_dnnlibrary else "OFF"),
"-Donnxruntime_USE_OPENMP=" + ("ON" if args.use_openmp and not args.use_mklml and not args.use_ngraph else "OFF"),
"-Donnxruntime_USE_TVM=" + ("ON" if args.use_tvm else "OFF"),
"-Donnxruntime_USE_LLVM=" + ("ON" if args.use_llvm else "OFF"),
@ -372,6 +380,11 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
cmake_args += ["-Donnxruntime_USE_PREINSTALLED_EIGEN=ON",
"-Deigen_SOURCE_PATH=" + args.eigen_path]
if args.android:
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + args.android_ndk_path + "/build/cmake/android.toolchain.cmake",
"-DANDROID_PLATFORM=android-" + str(args.android_api),
"-DANDROID_ABI=" + str(args.android_abi)]
if path_to_protoc_exe:
cmake_args += ["-DONNX_CUSTOM_PROTOC_EXECUTABLE=%s" % path_to_protoc_exe]
@ -522,10 +535,29 @@ def setup_tensorrt_vars(args):
return tensorrt_home
def adb_push(source_dir, src, dest, **kwargs):
return run_subprocess([os.path.join(source_dir, 'tools', 'ci_build', 'github', 'android', 'adb-push.sh'), src, dest], **kwargs)
def adb_shell(*args, **kwargs):
return run_subprocess(['adb', 'shell', *args], **kwargs)
def run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs, enable_python_tests, enable_tvm = False, enable_tensorrt = False, enable_ngraph = False):
for config in configs:
log.info("Running tests for %s configuration", config)
cwd = get_config_build_dir(build_dir, config)
android_x86_64 = args.android_abi == 'x86_64'
if android_x86_64:
run_subprocess(os.path.join(source_dir, 'tools', 'ci_build', 'github', 'android', 'start_android_emulator.sh'))
adb_push(source_dir, 'testdata', '/data/local/tmp/', cwd=cwd)
adb_push(source_dir, os.path.join(source_dir, 'cmake', 'external', 'onnx', 'onnx', 'backend', 'test'), '/data/local/tmp/', cwd=cwd)
adb_push(source_dir, 'onnxruntime_test_all', '/data/local/tmp/', cwd=cwd)
adb_push(source_dir, 'onnx_test_runner', '/data/local/tmp/', cwd=cwd)
adb_shell('cd /data/local/tmp && /data/local/tmp/onnxruntime_test_all')
if args.use_dnnlibrary:
adb_shell('cd /data/local/tmp && /data/local/tmp/onnx_test_runner -e nnapi /data/local/tmp/test')
else:
adb_shell('cd /data/local/tmp && /data/local/tmp/onnx_test_runner /data/local/tmp/test')
continue
if enable_tvm:
dll_path = os.path.join(build_dir, config, "external", "tvm", config)
elif enable_tensorrt:
@ -711,23 +743,24 @@ def build_python_wheel(source_dir, build_dir, configs, use_cuda, use_ngraph, use
args.append('--use_openvino')
run_subprocess(args, cwd=cwd)
def build_protoc_for_windows_host(cmake_path, source_dir, build_dir, cmake_generator):
if not is_windows():
def build_protoc_for_host(cmake_path, source_dir, build_dir, args):
if (args.arm or args.arm64) and not is_windows():
raise BuildError('Currently only support building protoc for Windows host while cross-compiling for ARM/ARM64 arch')
log.info("Building protoc for host to be used in cross-compiled build process")
protoc_build_dir = os.path.join(build_dir, 'host_protoc')
protoc_build_dir = os.path.join(os.getcwd(), build_dir, 'host_protoc')
os.makedirs(protoc_build_dir, exist_ok=True)
# Generate step
cmd_args = [cmake_path,
os.path.join(source_dir, 'cmake\external\protobuf\cmake'),
'-T',
'host=x64',
'-G',
cmake_generator,
os.path.join(source_dir, 'cmake', 'external', 'protobuf', 'cmake'),
'-Dprotobuf_BUILD_TESTS=OFF',
'-Dprotobuf_WITH_ZLIB_DEFAULT=OFF',
'-Dprotobuf_BUILD_SHARED_LIBS=OFF']
if is_windows():
cmd_args += ['-T',
'host=x64',
'-G',
args.cmake_generator]
run_subprocess(cmd_args, cwd= protoc_build_dir)
# Build step
cmd_args = [cmake_path,
@ -736,8 +769,12 @@ def build_protoc_for_windows_host(cmake_path, source_dir, build_dir, cmake_gener
"--target", "protoc"]
run_subprocess(cmd_args)
if not os.path.exists(os.path.join(build_dir, 'host_protoc', 'Release', 'protoc.exe')):
raise BuildError("Couldn't build protoc.exe for host. Failing build.")
# Absolute protoc path is needed for cmake
expected_protoc_path = os.path.join(protoc_build_dir, 'Release', 'protoc.exe') if is_windows() else os.path.join(protoc_build_dir, 'protoc')
if not os.path.exists(expected_protoc_path):
raise BuildError("Couldn't build protoc for host. Failing build.")
return expected_protoc_path
def generate_documentation(source_dir, build_dir, configs):
operator_doc_path = os.path.join(source_dir, 'docs', 'ContribOperators.md')
@ -767,13 +804,15 @@ def main():
cmake_extra_defines = args.cmake_extra_defines if args.cmake_extra_defines else []
cross_compiling = args.arm or args.arm64 or args.android
# if there was no explicit argument saying what to do, default to update, build and test (for native builds).
if (args.update == False and args.clean == False and args.build == False and args.test == False):
log.debug("Defaulting to running update, build [and test for native builds].")
args.update = True
args.build = True
if args.arm or args.arm64:
args.test = False
if cross_compiling:
args.test = args.android_abi == 'x86_64'
else:
args.test = True
@ -806,13 +845,14 @@ def main():
log.info("Build started")
if (args.update):
cmake_extra_args = []
path_to_protoc_exe = None
if(is_windows()):
if (args.x86):
cmake_extra_args = ['-A','Win32','-T','host=x64','-G', args.cmake_generator]
elif (args.arm or args.arm64):
# Cross-compiling for ARM(64) architecture
# First build protoc for host to use during cross-compilation
build_protoc_for_windows_host(cmake_path, source_dir, build_dir, args.cmake_generator)
path_to_protoc_exe = build_protoc_for_host(cmake_path, source_dir, build_dir, args)
if args.arm:
cmake_extra_args = ['-A', 'ARM']
else:
@ -830,6 +870,9 @@ def main():
toolset += ',cuda=' + args.cuda_version
cmake_extra_args = ['-A','x64','-T', toolset, '-G', args.cmake_generator]
if args.android:
# Cross-compiling for Android
path_to_protoc_exe = build_protoc_for_host(cmake_path, source_dir, build_dir, args)
if is_ubuntu_1604():
if (args.arm or args.arm64):
raise BuildError("Only Windows ARM(64) cross-compiled builds supported currently through this script")
@ -847,12 +890,8 @@ def main():
raise UsageError("The test_data_url and test_data_checksum arguments are required.")
setup_test_data(build_dir, configs, args.test_data_url, args.test_data_checksum, args.azure_sas_key)
path_to_protoc_exe = None
if args.path_to_protoc_exe:
path_to_protoc_exe = args.path_to_protoc_exe
# Need to provide path to protoc.exe built for host to be used in the cross-compiled build process
elif args.arm or args.arm64:
path_to_protoc_exe = os.path.join(build_dir, 'host_protoc', 'Release', 'protoc.exe')
generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home, tensorrt_home, path_to_protoc_exe, configs, cmake_extra_defines,
args, cmake_extra_args)

View file

@ -0,0 +1,17 @@
#! /usr/bin/env bash
# Created by daquexian
# A bash script that push folders recursively, "adb push" doesn't work on some devices
if [[ $# -ne 2 ]]; then
echo "Usage: $0 src dest"
exit 1
fi
src=`realpath $1`
src_basename=`basename $1`
pushd `dirname $src`
if [[ -d $src ]]; then
find $src_basename -type d -print -exec adb shell mkdir -p $2/{} \; > /dev/null
fi
find $src_basename -type f -exec adb push {} $2/{} \; > /dev/null
popd

View file

@ -0,0 +1,25 @@
#! /usr/bin/env bash
# Created by daquexian
set -e
export TERM=xterm
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;android-28;google_apis;x86_64'
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n android_emulator -k 'system-images;android-28;google_apis;x86_64' --force
echo "Starting emulator"
# Start emulator in background
nohup $ANDROID_HOME/emulator/emulator -avd android_emulator -no-snapshot -no-audio &
# start server in advance, so that the result of watch will only change when device gets online
$ANDROID_HOME/platform-tools/adb start-server
echo "Waiting for device to come online"
# Sometimes wait-for-device hangs, so add a timeout here
timeout 60 adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'
echo "Emulator is online"

View file

@ -4,7 +4,7 @@ jobs:
steps:
- template: templates/set-test-data-variables-step.yml
- script: 'tools/ci_build/github/linux/run_dockerbuild.sh -o android -r $(Build.BinariesDirectory) -d cpu -x "--build_wheel"'
- script: 'tools/ci_build/github/linux/run_dockerbuild.sh -o android -r $(Build.BinariesDirectory) -d nnapi -x "--build_wheel"'
displayName: 'Command Line Script'
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0

View file

@ -1,19 +1,12 @@
#!/bin/bash
set -e
mkdir -p /tmp/src
aria2c -q -d /tmp/src https://github.com/Kitware/CMake/releases/download/v3.13.2/cmake-3.13.2.tar.gz
tar -xf /tmp/src/cmake-3.13.2.tar.gz -C /tmp/src
cd /tmp/src/cmake-3.13.2
./configure --prefix=/usr --parallel=`nproc` --system-curl --system-zlib --system-expat
make -j`nproc`
make install
#download Android NDK r19c
aria2c -q -d /tmp https://dl.google.com/android/repository/android-ndk-r19c-linux-x86_64.zip
unzip -oq /tmp/android-ndk-r19c-linux-x86_64.zip -d /tmp/android-ndk && mv /tmp/android-ndk/* /android-ndk
cd /
rm -rf /tmp/src
# cmake==3.13.2 is actually 3.12.2 lol
python3 -m pip install cmake==3.13.2.post1
cmake --version
# Download Android NDK r20, move /temp_ndk/android-ndk-<version> to /android_ndk
wget -qO- -O temp.zip https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip && unzip -oq temp.zip -d /temp-ndk && mv /temp-ndk/* /android-ndk && rm temp.zip && rm -rf /temp-ndk && ls /android-ndk
apt-get -y remove libprotobuf-dev protobuf-compiler

View file

@ -17,7 +17,11 @@ done
if [ $BUILD_OS = "android" ]; then
pushd /onnxruntime_src
mkdir build-android && cd build-android
cmake -DCMAKE_TOOLCHAIN_FILE=/android-ndk/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DONNX_CUSTOM_PROTOC_EXECUTABLE=/usr/bin/protoc ../cmake
if [ $BUILD_DEVICE = "nnapi" ]; then
cmake -DCMAKE_TOOLCHAIN_FILE=/android-ndk/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DONNX_CUSTOM_PROTOC_EXECUTABLE=/usr/bin/protoc -Donnxruntime_USE_NNAPI=ON ../cmake
else
cmake -DCMAKE_TOOLCHAIN_FILE=/android-ndk/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DONNX_CUSTOM_PROTOC_EXECUTABLE=/usr/bin/protoc ../cmake
fi
make -j$(nproc)
else
COMMON_BUILD_ARGS="--skip_submodule_sync --enable_onnx_tests --parallel --build_shared_lib --use_openmp --cmake_path /usr/bin/cmake --ctest_path /usr/bin/ctest"