diff --git a/BUILD.md b/BUILD.md
index d214681fa9..18877bbb87 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -83,7 +83,7 @@ For other system requirements and other dependencies, please see [this section](
|Description|Command|Additional description|
|-----------|-----------|-----------|
|**Basic build**|build.bat (Windows)
./build.sh (Linux)||
-|**Debug build**|--config RelWithDebInfo|Debug build|
+|**Release build**|--config Release|Release build. Other valid config values are RelWithDebInfo and Debug.|
|**Use OpenMP**|--use_openmp|OpenMP will parallelize some of the code for potential performance improvements. This is not recommended for running on single threads.|
|**Build using parallel processing**|--parallel|This is strongly recommended to speed up the build.|
|**Build Shared Library**|--build_shared_lib||
@@ -829,19 +829,90 @@ ls -l /code/onnxruntime/build/Linux/MinSizeRel/dist/*.whl
#### Pre-Requisites
-Install Android NDK in Android Studio or https://developer.android.com/ndk/downloads
+The SDK and NDK packages can be installed via Android Studio or the sdkmanager command line tool.
+Android Studio is more convenient but a larger installation.
+The command line tools are smaller and usage can be scripted, but are a little more complicated to setup. They also require a Java runtime environment to be available.
+
+General Info:
+ - API levels: https://developer.android.com/guide/topics/manifest/uses-sdk-element.html
+ - Android ABIs: https://developer.android.com/ndk/guides/abis
+ - System Images: https://developer.android.com/topic/generic-system-image
+
+##### Android Studio
+
+Install Android Studio from https://developer.android.com/studio
+
+Install any additional SDK Platforms if necessary
+ - File->Settings->Appearance & Behavior->System Settings->Android SDK to see what is currently installed
+ - Note that the SDK path you need to use as --android_sdk_path when building ORT is also on this configuration page
+ - Most likely you don't require additional SDK Platform packages as the latest platform can target earlier API levels.
+
+Install an NDK version
+ - File->Settings->Appearance & Behavior->System Settings->Android SDK
+ - 'SDK Tools' tab
+ - Select 'Show package details' checkbox at the bottom to see specific versions.
+ By default the latest will be installed which should be fine.
+ - The NDK path will be the 'ndk/{version}' subdirectory of the SDK path shown
+ - e.g. if 21.1.6352462 is installed it will be {SDK path}/ndk/21.1.6352462
+
+##### sdkmanager from command line tools
+ - If necessary install the Java Runtime Environment and set the JAVA_HOME environment variable to point to it
+ - https://www.java.com/en/download/
+ - Windows note: You MUST install the 64-bit version (https://www.java.com/en/download/manual.jsp) otherwise sdkmanager will only list x86 packages
+ and the latest NDK is x64 only.
+ - For sdkmanager to work it needs a certain directory structure.
+ First create the top level directory for the Android infrastructure.
+ - in our example we'll call that `.../Android/`
+ - Download the command line tools from the 'Command line tools only' section towards the bottom
+ of https://developer.android.com/studio
+ - Create a directory called 'cmdline-tools' under your top level directory
+ - giving `.../Android/cmdline-tools`
+ - extract the 'tools' directory from the command line tools zip file into this directory
+ - giving `.../Android/cmdline-tools/tools`
+ - Windows note: preferably extract using 7-zip.
+ If using the built in Windows zip extract tool you will need to fix the directory structure
+ by moving the jar files from `tools\lib\_` up to `tools\lib`
+ - See https://stackoverflow.com/questions/27364963/could-not-find-or-load-main-class-com-android-sdkmanager-main
+ - you should now be able to run Android/cmdline-tools/bin/sdkmanager[.bat] successfully
+ - if you see an error about it being unable to save settings and the sdkmanager help text,
+ your directory structure is incorrect.
+ - see the final steps in this answer to double check: https://stackoverflow.com/a/61176718
+
+ - Run `.../Android/cmdline-tools/bin/sdkmanager --list` to see the packages available
+
+ - Install the SDK Platform
+ - Generally installing the latest is fine. You pick an API level when compiling the code and the latest platform will support many recent API levels
+ - e.g. `sdkmanager --install "platforms;android-29"`
+ - This will install into the 'platforms' directory of our top level directory
+ - so the 'Android' directory in our example
+ - The SDK path to use as --android_sdk_path when building is this top level directory
+
+ - Install the NDK
+ - Find the available NDK versions by running `sdkmanager --list`
+ - Install
+ - you can install a specific version or the latest (called 'ndk-bundle')
+ - e.g. `sdkmanager --install "ndk;21.1.6352462"`
+ - NDK path in our example with this install would be `.../Android/ndk/21.1.6352462`
+ - NOTE: If you install the ndk-bundle package the path will be `.../Android/ndk-bundle` as there's no version number
#### Build Instructions
##### Cross compiling on Windows
-```bash
-./build.bat --android --android_sdk_path --android_ndk_path --android_abi --android_api
+The [Ninja](https://ninja-build.org/) generator needs to be used to build on Windows as the Visual Studio generator doesn't support Android.
+
+```
+./build.bat --android --android_sdk_path --android_ndk_path --android_abi --android_api --cmake_generator Ninja
+```
+
+e.g. using the paths from our example
+```
+./build.bat --android --android_sdk_path .../Android --android_ndk_path .../Android/ndk/21.1.6352462 --android_abi arm64-v8a --android_api 27 --cmake_generator Ninja
```
##### Cross compiling on Linux
-```bash
+```
./build.sh --android --android_sdk_path --android_ndk_path --android_abi --android_api
```
diff --git a/docs/Android_testing.md b/docs/Android_testing.md
new file mode 100644
index 0000000000..2caffa2a24
--- /dev/null
+++ b/docs/Android_testing.md
@@ -0,0 +1,81 @@
+# Testing Android Changes using the Emulator
+
+See [BUILD.md](../Build.md#Android) for Android build instructions and information on the locations of the various files referred to here.
+
+## Install the emulator
+
+If using Android Studio this is included in the base install.
+
+If using sdkmanager install the emulator by running
+ - `sdkmanager[.bat] --install "emulator"`
+
+The emulator will emulate the Android device not its processor, so you need to build onnxruntime
+with an ABI that's valid for the host machine, and install a system image that matches.
+For example you can emulate a Pixel 3 device on an Intel 64-bit host, but it will require a binary built against x86_64
+rather than the arm64-v8a ABI of the real device.
+
+e.g. on Intel 64-bit you would build with `--android_abi x86_64` to create onnxruntime libraries/executables that can be run on the Android emulator
+
+## Create the device to emulate
+
+### Android Studio
+
+Tools->AVD Manager->Create Virtual Device...
+
+Once created the emulator can be started using the 'play' button in AVD Manager.
+
+### sdkmanager
+
+First install a system image. Use `sdkmanager --list` to see the available system images.
+
+e.g. `sdkmanager --install "system-images;android-27;default;x86_64`
+
+Create the virtual device using avdmanager[.bat] (which should be in the same directory as sdkmanager[.bat]).
+
+e.g. `avdmanager create avd -n android27_emulator -k "system-images;android-27;default;x86_64"`
+
+Run the emulator
+e.g. `.../Android/emulator/emulator -avd android27_emulator -partition-size 2048 -no-snapshot -no-audio`
+
+## Testing running a model on the emulator directly
+
+Use ADB to copy files and execute commands
+
+https://developer.android.com/studio/command-line/adb
+
+ADB is located in the 'platform-tools' folder of the SDK directory.
+
+Copy onnx_test_runner and the directory of the model to test (in ONNX test directory format) to /data/local/tmp.
+
+```
+adb push /build///onnx_test_runner /data/local/tmp/
+adb push /build///testdata/transform/gemm_activation_fusion /data/local/tmp/
+```
+
+e.g. on Windows that might be
+```
+\platform-tools\adb.exe push \build\Windows\Debug\onnx_test_runner /data/local/tmp/testdata
+\platform-tools\adb.exe push \build\Windows\Debug\testdata\transform\gemm_activation_fusion /data/local/tmp/
+```
+
+You may need to change permissions to make onnx_test_runner executable:
+`\platform-tools\adb.exe shell chmod +x /data/local/tmp/onnx_test_runner`
+
+Run onnx_test_runner with the model directory:
+`\platform-tools\adb.exe shell 'cd /data/local/tmp && ./onnx_test_runner gemm_activation_fusion'`
+
+The output should look something like this:
+
+```
+D:\Android\platform-tools> .\adb.exe shell 'cd /data/local/tmp && ./onnx_test_runner gemm_activation_fusion'
+result:
+ Models: 1
+ Total test cases: 1
+ Succeeded: 1
+ Not implemented: 0
+ Failed: 0
+ Stats by Operator type:
+ Not implemented(0):
+ Failed:
+Failed Test Cases:
+```
\ No newline at end of file
diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py
index 066c453d69..3095ab266f 100755
--- a/tools/ci_build/build.py
+++ b/tools/ci_build/build.py
@@ -831,7 +831,7 @@ def build_targets(args, cmake_path, build_dir, configs, parallel):
build_tool_args = []
if parallel:
num_cores = str(multiprocessing.cpu_count())
- if is_windows():
+ if is_windows() and args.cmake_generator != 'Ninja':
build_tool_args += [
"/maxcpucount:" + num_cores,
# if nodeReuse is true, msbuild processes will stay around for a bit after the build completes
@@ -1417,8 +1417,11 @@ def build_protoc_for_host(cmake_path, source_dir, build_dir, args):
'-Dprotobuf_WITH_ZLIB_DEFAULT=OFF',
'-Dprotobuf_BUILD_SHARED_LIBS=OFF'
]
+
+ is_ninja = args.cmake_generator == 'Ninja'
+
if is_windows():
- if args.cmake_generator != 'Ninja':
+ if not is_ninja:
cmd_args += ['-T', 'host=x64']
cmd_args += ['-G', args.cmake_generator]
elif is_macOS() and args.use_xcode:
@@ -1433,14 +1436,19 @@ def build_protoc_for_host(cmake_path, source_dir, build_dir, args):
run_subprocess(cmd_args)
# 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, 'Release', 'protoc')
- if is_macOS() and args.use_xcode
- else os.path.join(protoc_build_dir, 'protoc')))
+ config_dir = ''
+ suffix = ''
+
+ if (is_windows() and not is_ninja) or (is_macOS() and args.use_xcode):
+ config_dir = 'Release'
+
+ if is_windows():
+ suffix = '.exe'
+
+ expected_protoc_path = os.path.join(protoc_build_dir, config_dir, 'protoc' + suffix)
+
if not os.path.exists(expected_protoc_path):
- raise BuildError("Couldn't build protoc for host. Failing build.")
+ raise BuildError("Couldn't find {}. Host build of protoc failed.".format(expected_protoc_path))
return expected_protoc_path