Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[android] Update internal documentation on CoreCLR on Android experimental support #112034

Merged
merged 11 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
190 changes: 131 additions & 59 deletions docs/workflow/building/coreclr/android.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,172 @@
Cross Compilation for Android on Linux
======================================
# Experimental support of CoreCLR on Android

Through cross compilation, on Linux it is possible to build CoreCLR for arm64 Android.
This is the internal documentation which outlines experimental support of CoreCLR on Android and includes instructions on how to:
- [Build CoreCLR for Android](./android.md#building-coreclr-for-android)
- [Build and run a sample application with CoreCLR](./android.md#building-and-running-a-sample-app)
- [Debug the sample app and the runtime](./android.md#debugging-the-runtime-and-the-sample-app)

Requirements
------------
## Prerequisite

You'll need to generate a toolchain and a sysroot for Android. There's a script which takes care of the required steps.
Download and install `Android Studio` and the following:
- Android SDK (minimum supported API level is 21)
- Android NDK r27

Generating the rootfs
---------------------
## Building CoreCLR for Android

To generate the rootfs, run the following command in the `coreclr` folder:
Supported host systems for building CoreCLR for Android:
- [MacOS](./android.md#macos-and-linux) ✔
- [Linux](./android.md#macos-and-linux) ✔
- [Windows](./android.md#windows) ❌ (only through WSL)

Supported target architectures:
- x86 ❌
- x64 ✔
- arm ❌
- arm64 ✔

### MacOS and Linux

#### Requirements

Set the following environment variables:
- ANDROID_SDK_ROOT=`<full-path-to-android-sdk>`
- ANDROID_NDK_ROOT=`<full-path-to-android-ndk>`

#### Building the runtime, libraries and tools

To build CoreCLR runtime, libraries and tools for local development, run the following command from `<repo-root>`:

```
cross/init-android-rootfs.sh
./build.sh clr.runtime+clr.alljits+clr.corelib+clr.nativecorelib+clr.tools+clr.packages+libs -os android -arch <x64|arm64> -c <Debug|Release>
```

This will download the NDK and any packages required to compile Android on your system. It's over 1 GB of data, so it may take a while.
To build CoreCLR runtime NuGet packages, run the following command from `<repo-root>`:

```
./build.sh clr.runtime+clr.alljits+clr.corelib+clr.nativecorelib+clr.tools+clr.packages+libs+host+packs -os android -arch <x64|arm64> -c <Debug|Release>
```

Cross compiling CoreCLR
-----------------------
Once the rootfs has been generated, it will be possible to cross compile CoreCLR.
NOTE: The runtime packages will be located at: `<repo-root>/artifacts/packages/<configuration>/Shipping/`

When cross compiling, you need to set both the `CONFIG_DIR` and `ROOTFS_DIR` variables.
### Windows

To compile for arm64, run:
Building on Windows is not directly supported yet. However it is possible to use WSL2 for this purpose.

#### WSL2

##### Requirements

1. Install the Android SDK and NDK in WSL per the [prerequisites](#prerequisite). This can be done by downloading the archives or using Android Studio:
- Archives:
- [NDK](https://developer.android.com/ndk/downloads) and [command-line tools](https://developer.android.com/studio#command-line-tools-only) (use `sdkmanager` to download the SDK)
- For an automated script, see in [Testing Libraries on Android](../../testing/libraries/testing-android.md#using-a-terminal)
- Android Studio:
- Make sure WSL is updated: from Windows host, `wsl --update`
- [Enabled systemd](https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/#set-the-systemd-flag-set-in-your-wsl-distro-settings)
- `sudo snap install android-studio --classic`
2. Set the following environment variables:
- ANDROID_SDK_ROOT=`<full-path-to-android-sdk>`
- ANDROID_NDK_ROOT=`<full-path-to-android-ndk>`

#### Building the runtime, libraries and tools

To build CoreCLR runtime, libraries and tools, run the following command from `<repo-root>`:

```
CONFIG_DIR=`realpath cross/android/arm64` ROOTFS_DIR=`realpath cross/android-rootfs/toolchain/arm64/sysroot` ./build.sh cross arm64 cmakeargs -DENABLE_LLDBPLUGIN=0
./build.sh clr.runtime+clr.alljits+clr.corelib+clr.nativecorelib+clr.tools+clr.packages+libs -os android -arch <x64|arm64> -c <Debug|Release>
```

The resulting binaries will be found in `artifacts/bin/coreclr/Linux.BuildArch.BuildType/`
## Building and running a sample app

To demonstrate building and running an Android sample application with CoreCLR, we will use:
- the [HelloAndroid sample app](../../../../src/mono/sample/Android/AndroidSampleApp.csproj).
- a functional tests [Android.Device_Emulator.JIT.Test](../../../../src/tests/FunctionalTests/Android/Device_Emulator/JIT/Android.Device_Emulator.JIT.Test.csproj)

A prerequisite for building and running samples locally is to have CoreCLR successfully built for desired Android platform.

Running the PAL tests on Android
--------------------------------
### Building HelloAndroid sample

You can run the PAL tests on an Android device. To run the tests, you first copy the PAL tests to your Android phone using
`adb`, and then run them in an interactive Android shell using `adb shell`:
To build `HelloAndroid`, run the following command from `<repo_root>`:

To copy the PAL tests over to an Android phone:
```
adb push artifacts/obj/coreclr/Linux.arm64.Debug/src/pal/tests/palsuite/ /data/local/tmp/coreclr/pal/tests/palsuite
adb push cross/android/toolchain/arm64/sysroot/usr/lib/libandroid-support.so /data/local/tmp/coreclr/lib/
adb push cross/android/toolchain/arm64/sysroot/usr/lib/libandroid-glob.so /data/local/tmp/coreclr/lib/
adb push src/pal/tests/palsuite/paltestlist.txt /data/local/tmp/coreclr
adb push src/pal/tests/palsuite/runpaltests.sh /data/local/tmp/coreclr/
make BUILD_CONFIG=<Debug|Release> TARGET_ARCH=<x64|arm64> RUNTIME_FLAVOR=CoreCLR DEPLOY_AND_RUN=false run -C src/mono/sample/Android
```

Then, use `adb shell` to launch a shell on Android. Inside that shell, you can launch the PAL tests:
On successful execution, the command will output the `HelloAndroid.apk` at:
```
LD_LIBRARY_PATH=/data/local/tmp/coreclr/lib ./runpaltests.sh /data/local/tmp/coreclr/
<repo-root>artifacts/bin/AndroidSampleApp/<x64|arm64>/<Debug|Release>/android-<x64|arm64>/Bundle/bin/HelloAndroid.apk
```

Debugging coreclr on Android
----------------------------
### Running HelloAndroid sample on an emulator

You can debug coreclr on Android using a remote lldb server which you run on your Android device.
To run the sample on an emulator, the emulator first needs to be up and running.

First, push the lldb server to Android:
Creating an emulator (ADV - Android Virtual Device) can be achieved through [Android Studio - Device Manager](https://developer.android.com/studio/run/managing-avds).

After its creation, the emulator needs to be booted up and running, so that we can run the `HelloAndroid` sample on it via:
```
adb push cross/android/lldb/2.2/android/arm64-v8a/lldb-server /data/local/tmp/
make BUILD_CONFIG=<Debug|Release> TARGET_ARCH=<x64|arm64> RUNTIME_FLAVOR=CoreCLR DEPLOY_AND_RUN=true run -C src/mono/sample/Android
```

Then, launch the lldb server on the Android device. Open a shell using `adb shell` and run:

NOTE: Emulators can be also started from the terminal via:
```
adb shell
cd /data/local/tmp
./lldb-server platform --listen *:1234
$ANDROID_SDK_ROOT/emulator/emulator -avd <emulator-name>
```

After that, you'll need to forward port 1234 from your Android device to your PC:
#### WSL2

The app can be run on an emulator running on the Windows host.
1. Install Android Studio on the Windows host (same versions as in [prerequisites](#prerequisite))
2. In Windows, create and start an emulator
3. In WSL, swap the `adb` from the Android SDK in WSL2 with that from Windows
- `mv $ANDROID_SDK_ROOT/platform-tools/adb $ANDROID_SDK_ROOT/platform-tools/adb-orig`
- `ln -s /mnt/<path-to-adb-on-host> $ANDROID_SDK_ROOT/platform-tools/adb`
4. In WSL, Make xharness use the `adb` corresponding to the Windows host:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you really need to symlink or would it be enough to set xharness ADB_EXE_PATH to /mnt/<path-to-adb-on-host> ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to symlink it somewhere. It didn't necessarily need to be the path from the Android SDK in WSL2, but I found that convenient for just using adb from the command line in WSL.

When I directly set ADB_EXE_PATH=/mnt/<path-to-adb-on-host>, running with xharness would fail with:

Exit code: 1
Std out:
Performing Streamed Install
        
Std err:
adb: failed to stat /home/elinor/runtime/artifacts/bin/AndroidSampleApp/x64/Release/android-x64/Bundle//bin/HelloAndroid.apk: No such file or directory

I admittedly didn't really spend time looking into why.

Directly running /mnt/<path-to-adb-on-host> install <path-to-apk-in-wsl> from my WSL terminal works fine, so I guess it may be something with how it gets launched with xharness?

- `export ADB_EXE_PATH=$ANDROID_SDK_ROOT/platform-tools/adb`
5. In WSL, run the `make` command as [above](#running-helloandroid-sample-on-an-emulator)

### Building and running functional tests on an emulator

Similarly to the `HelloAndroid` sample, it is possible to build and run a functional test on Android with CoreCLR on an emulator.

To build and run a functional test on Android with CoreCLR, run the following command from `<repo_root>`:

```
adb forward tcp:1234 tcp:1234
./dotnet.sh build -c Release src/tests/FunctionalTests/Android/Device_Emulator/JIT/Android.Device_Emulator.JIT.Test.csproj /p:TargetOS=android /p:TargetArchitecture=arm64 /t:Test /p:RuntimeFlavor=coreclr
```

Finally, install lldb on your PC and connect to the debug server running on your Android device:
NOTE: Similarly to the `HelloAndroid` sample the emulator needs to be up and running.

```
lldb-3.9
(lldb) platform select remote-android
Platform: remote-android
Connected: no
(lldb) platform connect connect://localhost:1234
Platform: remote-android
Triple: aarch64-*-linux-android
OS Version: 23.0.0 (3.10.84-perf-gf38969a)
Kernel: #1 SMP PREEMPT Fri Sep 16 11:29:29 2016
Hostname: localhost
Connected: yes
WorkingDir: /data/local/tmp
### Useful make commands

(lldb) target create coreclr/pal/tests/palsuite/file_io/CopyFileA/test4/paltest_copyfilea_test4
(lldb) env LD_LIBRARY_PATH=/data/local/tmp/coreclr/lib
(lldb) run
For convenience it is possible to run a single make command which builds all required dependencies, the app and runs it:
```
make BUILD_CONFIG=<Debug|Release> TARGET_ARCH=<x64|arm64> RUNTIME_FLAVOR=CoreCLR DEPLOY_AND_RUN=true all -C src/mono/sample/Android
```

## Debugging the runtime and the sample app

Managed debugging is currently not supported, but we can debug:
- Java portion of the sample app
- Native code for the CoreCLR host and the runtime it self

This can be achieved in `Android Studio` via `Profile or Debug APK`.

### Steps

1. Build the runtime and `HelloAndroid` sample app in `Debug` configuration targeting `arm64` target architecture.
2. Rename the debug symbols file of the runtime library from `libcoreclr.so.dbg` into `libcoreclr.so.so`, the file is located at: `<repo_root>/artifacts/bin/AndroidSampleApp/arm64/Debug/android-arm64/publish/libcoreclr.so.dbg`
3. Open Android Studio and select `Profile or Debug APK` project.
4. Find and select the desired `.apk` file (example: `<repo_root>/artifacts/bin/AndroidSampleApp/arm64/Debug/android-arm64/Bundle/bin/HelloAndroid.apk`)
5. In the project pane, expand `HelloAndroid->cpp->libcoreclr` and double-click `libcoreclr.so`
![Adding debug symbols](./android-studio-coreclr-debug-symbols-adding.png)
6. From the `Debug Symbols` pane on the right, select `Add`
7. Navigate to the renamed file from step 2. and select it `<repo_root>/artifacts/bin/AndroidSampleApp/arm64/Debug/android-arm64/publish/libcoreclr.so.so`
8. Once loaded it will show all the source files under `HelloAndroid->cpp->libcoreclr`
![Debug symbols loaded](./android-studio-coreclr-debug-symbols-adding.png)
9. Find the `exports.cpp` and set a breakpoint in `coreclr_initialize` function and launch the debug session
![Debugging CoreCLR](./android-studio-coreclr-debugging.png)

## See also

Similar instructions for debugging Android apps with Mono runtime can be found [here](../../debugging/mono/android-debugging.md).
2 changes: 1 addition & 1 deletion src/mono/sample/Android/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ runtimepack:
$(BUILD_SCRIPT) mono+libs -os android -arch $(TARGET_ARCH) -c $(BUILD_CONFIG) -bl
else
runtimepack:
$(BUILD_SCRIPT) clr.runtime+clr.alljits+clr.corelib+clr.nativecorelib+clr.tools+clr.packages+libs -os android -arch $(TARGET_ARCH) -c $(BUILD_CONFIG) -cross -bl
$(BUILD_SCRIPT) clr.runtime+clr.alljits+clr.corelib+clr.nativecorelib+clr.tools+clr.packages+libs -os android -arch $(TARGET_ARCH) -c $(BUILD_CONFIG) -bl
endif

run: appbuilder
Expand Down
Loading