feat: support android platform cross build#90
Conversation
| runs-on: ubuntu-22.04 | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| # abi: [arm64-v8a, armeabi-v7a, x86_64] | ||
| abi: [x86_64] | ||
| api: [21] | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Cache dependencies | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: | | ||
| ~/.ccache | ||
| key: ${{ runner.os }}-dependencies-cache-${{ hashFiles('**/CMakeLists.txt', 'thirdparty/**') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-dependencies-cache- | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y --no-install-recommends \ | ||
| cmake ninja-build git ca-certificates python3 \ | ||
| build-essential make ccache | ||
|
|
||
| - name: Setup Java 17 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: '17' | ||
|
|
||
| - name: Setup Android NDK | ||
| uses: android-actions/setup-android@v3 | ||
|
|
||
| - name: Install NDK (side by side) | ||
| shell: bash | ||
| run: | | ||
| # yes | sdkmanager --licenses | ||
| sdkmanager "ndk;26.1.10909125" | ||
|
|
||
| - name: Cache host protoc build | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: build-host | ||
| key: ${{ runner.os }}-host-protoc-${{ hashFiles('src/**', 'CMakeLists.txt') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-host-protoc- | ||
|
|
||
| - name: Use host env to compile protoc | ||
| shell: bash | ||
| run: | | ||
| git submodule update --init | ||
| if [ ! -d "build-host" ]; then | ||
| # Setup ccache for host build | ||
| export CCACHE_BASEDIR="$GITHUB_WORKSPACE" | ||
| export CCACHE_NOHASHDIR=1 | ||
| export CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_mtime,locale,time_macros | ||
|
|
||
| cmake -S . -B build-host -G Ninja \ | ||
| -DCMAKE_C_COMPILER_LAUNCHER=ccache \ | ||
| -DCMAKE_CXX_COMPILER_LAUNCHER=ccache | ||
| cmake --build build-host --target protoc --parallel | ||
| else | ||
| echo "Using cached host protoc build" | ||
| fi | ||
|
|
||
| - name: Cache Android build | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: build-android-${{ matrix.abi }} | ||
| key: ${{ runner.os }}-android-build-${{ matrix.abi }}-${{ hashFiles('src/**', 'CMakeLists.txt', 'cmake/**') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-android-build-${{ matrix.abi }}- | ||
|
|
||
| - name: Configure and Build | ||
| shell: bash | ||
| run: | | ||
| git submodule foreach --recursive 'git stash --include-untracked' | ||
|
|
||
| export ANDROID_SDK_ROOT="$ANDROID_HOME" | ||
| export ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/26.1.10909125" | ||
|
|
||
| # Setup ccache | ||
| export CCACHE_BASEDIR="$GITHUB_WORKSPACE" | ||
| export CCACHE_NOHASHDIR=1 | ||
| export CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_mtime,locale,time_macros | ||
|
|
||
| if [ ! -d "build-android-${{ matrix.abi }}" ]; then | ||
| cmake -S . -B build-android-${{ matrix.abi }} -G Ninja \ | ||
| -DCMAKE_BUILD_TYPE=Release \ | ||
| -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ | ||
| -DANDROID_ABI=${{ matrix.abi }} \ | ||
| -DANDROID_PLATFORM=android-${{ matrix.api }} \ | ||
| -DANDROID_STL=c++_shared \ | ||
| -DBUILD_PYTHON_BINDINGS=OFF \ | ||
| -DBUILD_TOOLS=OFF \ | ||
| -DGLOBAL_CC_PROTOBUF_PROTOC="$GITHUB_WORKSPACE/build-host/bin/protoc" \ | ||
| -DCMAKE_C_COMPILER_LAUNCHER=ccache \ | ||
| -DCMAKE_CXX_COMPILER_LAUNCHER=ccache | ||
| cmake --build build-android-${{ matrix.abi }} --parallel | ||
| else | ||
| echo "Using cached Android build directory" | ||
| fi | ||
|
|
||
|
|
||
| - name: Cache examples build | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: examples/c++/build-android-examples-${{ matrix.abi }} | ||
| key: ${{ runner.os }}-examples-build-${{ matrix.abi }}-${{ hashFiles('examples/c++/**', 'CMakeLists.txt', 'src/**') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-examples-build-${{ matrix.abi }}- | ||
|
|
||
| - name: Build examples | ||
| shell: bash | ||
| run: | | ||
| if [ ! -d "examples/c++/build-android-examples-${{ matrix.abi }}" ]; then | ||
| cmake -S examples/c++ -B examples/c++/build-android-examples-${{ matrix.abi }} -G Ninja \ | ||
| -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ | ||
| -DANDROID_ABI=${{ matrix.abi }} \ | ||
| -DANDROID_PLATFORM=android-${{ matrix.api }} \ | ||
| -DANDROID_STL=c++_shared \ | ||
| -DCMAKE_BUILD_TYPE=Release \ | ||
| -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ | ||
| -DHOST_BUILD_DIR="build-android-${{ matrix.abi }}" \ | ||
| -DCMAKE_C_COMPILER_LAUNCHER=ccache \ | ||
| -DCMAKE_CXX_COMPILER_LAUNCHER=ccache | ||
| cmake --build examples/c++/build-android-examples-${{ matrix.abi }} --parallel | ||
| else | ||
| echo "Using cached examples build" | ||
| fi | ||
|
|
||
| - name: Install ADB and setup Android emulator | ||
| uses: reactivecircus/android-emulator-runner@v2 | ||
| with: | ||
| api-level: ${{ matrix.api }} | ||
| arch: ${{ matrix.abi }} | ||
| target: google_apis | ||
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim | ||
| disable-animations: true | ||
| script: | | ||
| # Wait for device to be ready | ||
| adb wait-for-device | ||
|
|
||
| # Check file sizes before pushing | ||
| echo "Checking binary sizes:" | ||
| ls -lah examples/c++/build-android-examples-${{ matrix.abi }}/ | ||
|
|
||
| # Check device architecture | ||
| echo "Device architecture info:" | ||
| adb shell 'getprop ro.product.cpu.abi' | ||
| adb shell 'getprop ro.product.cpu.abilist' | ||
|
|
||
| # Push executables to device | ||
| adb push examples/c++/build-android-examples-${{ matrix.abi }}/ailego-example /data/local/tmp/ | ||
| adb push examples/c++/build-android-examples-${{ matrix.abi }}/core-example /data/local/tmp/ | ||
| adb push examples/c++/build-android-examples-${{ matrix.abi }}/db-example /data/local/tmp/ | ||
|
|
||
| # Make executables executable | ||
| adb shell 'chmod a+x /data/local/tmp/ailego-example' | ||
| adb shell 'chmod a+x /data/local/tmp/core-example' | ||
| adb shell 'chmod a+x /data/local/tmp/db-example' | ||
|
|
||
| # Verify file integrity | ||
| echo "File info on device:" | ||
| adb shell 'ls -la /data/local/tmp/ailego-example' | ||
| adb shell 'ls -la /data/local/tmp/core-example' | ||
| adb shell 'ls -la /data/local/tmp/db-example' | ||
|
|
||
| echo "Running ailego example:" | ||
| adb shell 'cd /data/local/tmp && ./ailego-example' | ||
| echo "Exit code: $?" | ||
|
|
||
| echo "Running core example:" | ||
| adb shell 'cd /data/local/tmp && ./core-example' | ||
| echo "Exit code: $?" | ||
|
|
||
| echo "Running db example:" | ||
| adb shell 'cd /data/local/tmp && ./db-example' | ||
| echo "Exit code: $?" |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
In general, fix this by explicitly specifying permissions for the GITHUB_TOKEN in the workflow, either at the top level (applies to all jobs) or per-job, and restricting them to the minimum needed. For this build-focused workflow, all steps only need to read repository contents (the checkout action and cache keys based on hashFiles), so contents: read is sufficient.
The best minimal fix without changing functionality is to add a top-level permissions block right after the name: declaration (or before jobs:) in .github/workflows/android_build.yml. This will apply to the build-android job and any future jobs that do not override permissions. No additional imports or methods are needed; this is purely a YAML configuration change. Concretely, insert:
permissions:
contents: readnear the top of the file, keeping indentation consistent with other top-level keys.
| @@ -1,5 +1,8 @@ | ||
| name: android-cross-build | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ "main" ] |
resolve #93