diff --git a/BUILD.md b/BUILD.md index 54ced810a5..e4f31151a8 100644 --- a/BUILD.md +++ b/BUILD.md @@ -274,18 +274,18 @@ To build ONNX Runtime with the NN API EP, first install Android NDK (see [Androi #### Build Instructions -The basic build commands are below. There are also some other parameters for building the Android version under See [Android Build instructions](#android) for more details. +The basic build commands are below. There are also some other parameters for building the Android version. See [Android Build instructions](#android) for more details. ##### Cross compiling on Windows ```bash -./build.bat --android --android_ndk_path --dnnlibrary +./build.bat --android --android_sdk_path --android_ndk_path --dnnlibrary ``` ##### Cross compiling on Linux ```bash -./build.sh --android --android_ndk_path --dnnlibrary +./build.sh --android --android_sdk_path --android_ndk_path --dnnlibrary ``` --- @@ -580,21 +580,22 @@ ls -l /code/onnxruntime/build/Linux/MinSizeRel/dist/*.whl #### Pre-Requisites -Install Android NDK from https://developer.android.com/ndk/downloads +Install Android NDK in Android Studio or https://developer.android.com/ndk/downloads #### Build Instructions ##### Cross compiling on Windows ```bash -./build.bat --android --android_ndk_path --android_abi --android_api +./build.bat --android --android_sdk_path --android_ndk_path --android_abi --android_api ``` ##### Cross compiling on Linux ```bash -./build.sh --android --android_ndk_path --android_abi --android_api +./build.sh --android --android_sdk_path --android_ndk_path --android_abi --android_api ``` -If you want to use NNAPI Execution Provider on Android, see [docs/execution_providers/NNAPI-ExecutionProvider.md](/docs/execution_providers/NNAPI-ExecutionProvider.md). +Android Archive (AAR) files, which can be imported directly in Android Studio, will be generated in your_build_dir/java/build/outputs/aar. +If you want to use NNAPI Execution Provider on Android, see [docs/execution_providers/NNAPI-ExecutionProvider.md](/docs/execution_providers/NNAPI-ExecutionProvider.md). diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 3cee60f3f8..7238b2350a 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -279,7 +279,6 @@ if(onnxruntime_BUILD_SHARED_LIB OR onnxruntime_ENABLE_PYTHON) find_package(PythonLibs 3.5 REQUIRED) else() find_package(PythonInterp 3.4 REQUIRED) - find_package(PythonLibs 3.4 REQUIRED) endif() endif() diff --git a/cmake/onnxruntime_java.cmake b/cmake/onnxruntime_java.cmake index 13102faaf5..07e40ad809 100644 --- a/cmake/onnxruntime_java.cmake +++ b/cmake/onnxruntime_java.cmake @@ -6,9 +6,11 @@ # Setup Java compilation include(FindJava) find_package(Java REQUIRED) -find_package(JNI REQUIRED) include(UseJava) -include_directories(${JNI_INCLUDE_DIRS}) +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Android") + find_package(JNI REQUIRED) + include_directories(${JNI_INCLUDE_DIRS}) +endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") set(JAVA_ROOT ${REPO_ROOT}/java) @@ -81,8 +83,13 @@ onnxruntime_add_include_to_target(onnxruntime4j_jni onnxruntime_session) target_include_directories(onnxruntime4j_jni PRIVATE ${REPO_ROOT}/include ${JAVA_ROOT}/build/headers) target_link_libraries(onnxruntime4j_jni PUBLIC onnxruntime) +set(JAVA_PACKAGE_OUTPUT_DIR ${JAVA_OUTPUT_DIR}/build) +file(MAKE_DIRECTORY ${JAVA_PACKAGE_OUTPUT_DIR}) +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + set(ANDROID_PACKAGE_OUTPUT_DIR ${JAVA_PACKAGE_OUTPUT_DIR}/android) + file(MAKE_DIRECTORY ${ANDROID_PACKAGE_OUTPUT_DIR}) +endif() # expose native libraries to the gradle build process -file(MAKE_DIRECTORY ${JAVA_OUTPUT_DIR}/build) set(JAVA_PACKAGE_DIR ai/onnxruntime/native/) set(JAVA_NATIVE_LIB_DIR ${JAVA_OUTPUT_DIR}/native-lib) set(JAVA_NATIVE_JNI_DIR ${JAVA_OUTPUT_DIR}/native-jni) @@ -90,7 +97,20 @@ set(JAVA_PACKAGE_LIB_DIR ${JAVA_NATIVE_LIB_DIR}/${JAVA_PACKAGE_DIR}) set(JAVA_PACKAGE_JNI_DIR ${JAVA_NATIVE_JNI_DIR}/${JAVA_PACKAGE_DIR}) file(MAKE_DIRECTORY ${JAVA_PACKAGE_LIB_DIR}) file(MAKE_DIRECTORY ${JAVA_PACKAGE_JNI_DIR}) +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + set(ANDROID_PACKAGE_JNILIBS_DIR ${JAVA_OUTPUT_DIR}/android) + set(ANDROID_PACKAGE_ABI_DIR ${ANDROID_PACKAGE_JNILIBS_DIR}/${ANDROID_ABI}) + file(MAKE_DIRECTORY ${ANDROID_PACKAGE_JNILIBS_DIR}) + file(MAKE_DIRECTORY ${ANDROID_PACKAGE_ABI_DIR}) +endif() add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${JAVA_PACKAGE_LIB_DIR}/$) add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${JAVA_PACKAGE_JNI_DIR}/$) +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${ANDROID_PACKAGE_ABI_DIR}/$) + add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${ANDROID_PACKAGE_ABI_DIR}/$) +endif() # run the build process (this copies the results back into CMAKE_CURRENT_BINARY_DIR) add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${GRADLE_EXECUTABLE} cmakeBuild -DcmakeBuildDir=${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${JAVA_ROOT}) +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${GRADLE_EXECUTABLE} -b build-android.gradle -c settings-android.gradle build -DjniLibsDir=${ANDROID_PACKAGE_JNILIBS_DIR} -DbuildDir=${ANDROID_PACKAGE_OUTPUT_DIR} WORKING_DIRECTORY ${JAVA_ROOT}) +endif() diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index d1bd347c5d..9def63556b 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -574,6 +574,13 @@ add_test(NAME onnx_test_pytorch_converted add_test(NAME onnx_test_pytorch_operator COMMAND onnx_test_runner ${PROJECT_SOURCE_DIR}/external/onnx/onnx/backend/test/data/pytorch-operator) +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + list(APPEND android_shared_libs log android) + if (onnxruntime_USE_NNAPI) + list(APPEND android_shared_libs neuralnetworks) + endif() +endif() + #perf test runner set(onnxruntime_perf_test_src_dir ${TEST_SRC_DIR}/perftest) set(onnxruntime_perf_test_src_patterns @@ -611,6 +618,9 @@ if (onnxruntime_BUILD_SHARED_LIB) if(NOT WIN32) list(APPEND onnxruntime_perf_test_libs nsync_cpp) endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Android") + list(APPEND onnxruntime_perf_test_libs ${android_shared_libs}) + endif() target_link_libraries(onnxruntime_perf_test PRIVATE ${onnxruntime_perf_test_libs} Threads::Threads) if(WIN32) target_link_libraries(onnxruntime_perf_test PRIVATE debug dbghelp advapi32) @@ -650,6 +660,9 @@ if (onnxruntime_BUILD_SHARED_LIB) if(NOT WIN32) list(APPEND onnxruntime_shared_lib_test_LIBS nsync_cpp) endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Android") + list(APPEND onnxruntime_shared_lib_test_LIBS ${android_shared_libs}) + endif() AddTest(DYN TARGET onnxruntime_shared_lib_test SOURCES ${onnxruntime_shared_lib_test_SRC} ${TEST_SRC_DIR}/providers/test_main.cc diff --git a/java/build-android.gradle b/java/build-android.gradle new file mode 100644 index 0000000000..a359938d03 --- /dev/null +++ b/java/build-android.gradle @@ -0,0 +1,69 @@ +apply plugin: 'com.android.library' + +def jniLibsDir = System.properties['jniLibsDir'] +def buildDir = System.properties['buildDir'] + +project.buildDir = buildDir + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + defaultConfig { + minSdkVersion 24 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + } + + android { + lintOptions { + abortOnError false + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + + sourceSets { + main { + jniLibs.srcDirs = [jniLibsDir] + } + } +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.1' + testImplementation 'com.google.protobuf:protobuf-java:3.10.0' +} diff --git a/java/settings-android.gradle b/java/settings-android.gradle new file mode 100644 index 0000000000..0a3340238e --- /dev/null +++ b/java/settings-android.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'onnxruntime' +rootProject.buildFileName = 'build-android.gradle' diff --git a/java/src/main/AndroidManifest.xml b/java/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..7803ad9963 --- /dev/null +++ b/java/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/java/src/main/java/ai/onnxruntime/OnnxRuntime.java b/java/src/main/java/ai/onnxruntime/OnnxRuntime.java index 696e534f3d..a82bc00a02 100644 --- a/java/src/main/java/ai/onnxruntime/OnnxRuntime.java +++ b/java/src/main/java/ai/onnxruntime/OnnxRuntime.java @@ -44,14 +44,16 @@ final class OnnxRuntime { if (loaded) { return; } - Path tempDirectory = Files.createTempDirectory("onnxruntime-java"); + Path tempDirectory = isAndroid() ? null : Files.createTempDirectory("onnxruntime-java"); try { load(tempDirectory, ONNXRUNTIME_LIBRARY_NAME); load(tempDirectory, ONNXRUNTIME_JNI_LIBRARY_NAME); ortApiHandle = initialiseAPIBase(ORT_API_VERSION_1); loaded = true; } finally { - cleanUp(tempDirectory.toFile()); + if (!isAndroid()) { + cleanUp(tempDirectory.toFile()); + } } } @@ -72,6 +74,15 @@ final class OnnxRuntime { } } + private static boolean isAndroid() { + try { + Class.forName("android.app.Activity"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + /** * Load a shared library by name. * @@ -80,6 +91,12 @@ final class OnnxRuntime { * @throws IOException If the file failed to read or write. */ private static void load(Path tempDirectory, String library) throws IOException { + // On Android, we simply use System.loadLibrary + if (isAndroid()) { + System.loadLibrary("onnxruntime4j_jni"); + return; + } + // 1) The user may skip loading of this library: String skip = System.getProperty("onnxruntime.native." + library + ".skip"); if (Boolean.TRUE.toString().equalsIgnoreCase(skip)) { diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py index 67663fb6c1..7751425544 100755 --- a/tools/ci_build/build.py +++ b/tools/ci_build/build.py @@ -120,6 +120,7 @@ Use the individual flags to only run the specified stages. help='') parser.add_argument("--android_api", type=int, default=27, help='Android API Level, e.g. 21') + parser.add_argument("--android_sdk_path", type=str, help='Path to the Android SDK') parser.add_argument("--android_ndk_path", default="", help="Path to the Android NDK") # Arguments needed by CI @@ -443,7 +444,7 @@ def clean_targets(cmake_path, build_dir, configs): run_subprocess(cmd_args) -def build_targets(cmake_path, build_dir, configs, parallel): +def build_targets(args, cmake_path, build_dir, configs, parallel): for config in configs: log.info("Building targets for %s configuration", config) build_dir2 = get_config_build_dir(build_dir, config) @@ -463,7 +464,11 @@ def build_targets(cmake_path, build_dir, configs, parallel): cmd_args += [ "--" ] cmd_args += build_tool_args - run_subprocess(cmd_args) + env = {} + if args.android: + env['ANDROID_SDK_ROOT']=args.android_sdk_path + + run_subprocess(cmd_args, env=env) def add_dir_if_exists(dir, dir_list): if (os.path.isdir(dir)): @@ -994,7 +999,7 @@ def main(): setup_dml_build(args, cmake_path, build_dir, configs) if (args.build): - build_targets(cmake_path, build_dir, configs, args.parallel) + build_targets(args, cmake_path, build_dir, configs, args.parallel) if args.test : run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs, diff --git a/tools/ci_build/github/azure-pipelines/android-x86_64-crosscompile-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/android-x86_64-crosscompile-ci-pipeline.yml index 757d522936..f28bbef196 100644 --- a/tools/ci_build/github/azure-pipelines/android-x86_64-crosscompile-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/android-x86_64-crosscompile-ci-pipeline.yml @@ -10,6 +10,6 @@ jobs: displayName: Clone submodules - script: echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'ndk-bundle' displayName: Install Android NDK - - script: tools/ci_build/build.py --android --build_dir build --android_ndk $ANDROID_HOME/ndk-bundle --android_abi=x86_64 --skip_submodule_sync --parallel --use_dnnlibrary + - script: tools/ci_build/build.py --android --build_dir build --android_sdk_path $ANDROID_HOME --android_ndk_path $ANDROID_HOME/ndk-bundle --android_abi=x86_64 --skip_submodule_sync --parallel --use_dnnlibrary displayName: Build and Test on Android Emulator