From 9d5f7fdd9ce9bd9f8b87b8f31e92e3b0c1d20f3d Mon Sep 17 00:00:00 2001 From: albexk Date: Fri, 18 Aug 2023 17:32:16 +0300 Subject: [PATCH] Refactor and improve Android build script and GitHub action --- .github/workflows/deploy.yml | 79 +++++++++++++------ CMakeLists.txt | 4 - deploy/build_android.sh | 148 ++++++++++++++++++++++++++--------- 3 files changed, 165 insertions(+), 66 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4d3d5316..f5eed51d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -94,7 +94,7 @@ jobs: - name: 'Setup mvsc' uses: ilammy/msvc-dev-cmd@v1 with: - arch: 'x64' + arch: 'x64' - name: 'Build project' shell: cmd @@ -173,10 +173,10 @@ jobs: - name: 'Setup ccache' uses: hendrikmuhs/ccache-action@v1.2 - + - name: Install dependencies run: pip install jsonschema jinja2 - + - name: 'Build project' run: | git submodule update --init --recursive @@ -193,7 +193,7 @@ jobs: APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }} IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }} IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }} - + # - name: 'Upload appstore .ipa and dSYMs to artifacts' # uses: actions/upload-artifact@v3 # with: @@ -266,10 +266,22 @@ jobs: Build-Android: name: 'Build-Android' runs-on: ubuntu-latest - continue-on-error: true + + strategy: + matrix: + include: + - abi: 'x86_64' + qt_arch: 'android_x86_64' + - abi: 'x86' + qt_arch: 'android_x86' + - abi: 'armeabi-v7a' + qt_arch: 'android_armv7' + - abi: 'arm64-v8a' + qt_arch: 'android_arm64_v8a' env: - QT_VERSION: 6.5.1 + QT_VERSION: 6.5.2 + ANDROID_BUILD_PLATFORM: android-33 steps: - name: 'Install desktop Qt' @@ -291,23 +303,22 @@ jobs: version: ${{ env.QT_VERSION }} host: 'linux' target: 'android' - arch: 'android_x86_64' + arch: ${{ matrix.qt_arch }} modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools' dir: ${{ runner.temp }} setup-python: 'true' set-env: 'true' extra: '--external 7z' - - name: 'Install GO' - uses: actions/setup-go@v2 - with: - go-version: 1.17 + - name: 'Grant execute permission for qt-cmake' + shell: bash + run: | + chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/${{ matrix.qt_arch }}/bin/qt-cmake - name: 'Get sources' uses: actions/checkout@v3 with: submodules: 'true' - fetch-depth: 10 - name: 'Setup ccache' uses: hendrikmuhs/ccache-action@v1.2 @@ -317,20 +328,36 @@ jobs: with: distribution: 'temurin' java-version: '11' + cache: 'gradle' + + - name: 'Setup Android NDK' + id: setup-ndk + uses: nttld/setup-ndk@v1 + with: + ndk-version: 'r25c' + local-cache: 'true' + + - name: 'Decode keystore secret to file' + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }} + shell: bash + run: | + echo $KEYSTORE_BASE64 | base64 --decode > android.keystore - name: 'Build project' - run: | - export QT_HOST_PATH="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64" - export NDK_VERSION=23c - export ANDROID_NDK_PLATFORM=android-23 - export ANDROID_NDK_HOME=${{ runner.temp }}/android-ndk-r${NDK_VERSION} - export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME - export ANDROID_CURRENT_ARCH=android_x86_64 + env: + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64 + QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore + QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }} + QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} + QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} + shell: bash + run: ./deploy/build_android.sh --apk ${{ matrix.abi }} --platform ${{ env.ANDROID_BUILD_PLATFORM }} - if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then - wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux.zip -qO ${{ runner.temp }}/ndk.zip && - unzip -q -d ${{ runner.temp }} ${{ runner.temp }}/ndk.zip ; - fi - - export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin - bash deploy/build_android.sh + - name: 'Upload apk' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN-android-${{ matrix.abi }} + path: deploy/build/AmneziaVPN-${{ matrix.abi }}-release-signed.apk + retention-days: 7 diff --git a/CMakeLists.txt b/CMakeLists.txt index ad9866e0..94884e92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,10 +27,6 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if(ANDROID) - set(QT_ANDROID_BUILD_ALL_ABIS ON) -endif() - if(APPLE AND NOT IOS) set(CMAKE_OSX_ARCHITECTURES "x86_64") endif() diff --git a/deploy/build_android.sh b/deploy/build_android.sh index ed6dfcf2..55da33f1 100755 --- a/deploy/build_android.sh +++ b/deploy/build_android.sh @@ -1,59 +1,135 @@ #!/bin/bash - -echo "Build script started ..." +# shellcheck disable=SC2086 set -o errexit -o nounset -# Hold on to current directory +usage() { + cat < Build APK for the specified ABI + Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + -p, --platform The SDK platform used for building the Java code of the application + By default, the latest available platform is used + -m, --move Move the build result to the root of the build directory + -h, --help Display this help + +EOT +} + +BUILD_TYPE="release" +AAB=1 + +opts=$(getopt -l debug,apk:,platform:,move,help -o "da:p:mh" -- "$@") +eval set -- "$opts" +while true; do + case "$1" in + -d | --debug) BUILD_TYPE="debug"; shift;; + -a | --apk) ABI=$2; unset AAB; shift 2;; + -p | --platform) ANDROID_PLATFORM=$2; shift 2;; + -m | --move) MOVE_RESULT=1; shift;; + -h | --help) usage; exit 0;; + --) shift; break;; + esac +done + +if [[ -v ABI && ! "$ABI" =~ ^(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then + echo "The 'abi' option must be one of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'], but is '$ABI'" + exit 1 +fi + +echo "Build script started..." + PROJECT_DIR=$(pwd) DEPLOY_DIR=$PROJECT_DIR/deploy mkdir -p $DEPLOY_DIR/build BUILD_DIR=$DEPLOY_DIR/build - -echo "Project dir: ${PROJECT_DIR}" -echo "Build dir: ${BUILD_DIR}" - -APP_NAME=AmneziaVPN -APP_FILENAME=$APP_NAME.app -APP_DOMAIN=org.amneziavpn.package - OUT_APP_DIR=$BUILD_DIR/client -BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME -# Search Qt -if [ -z "${QT_VERSION+x}" ]; then -QT_VERSION=6.4.1; -QT_BIN_DIR=$HOME/Qt/$QT_VERSION/$ANDROID_CURRENT_ARCH/bin +echo "Project dir: $PROJECT_DIR" +echo "Build dir: $BUILD_DIR" + +if [ -v AAB ]; then + qt_bin_dir_suffix="x86_64" +else + case $ABI in + "armeabi-v7a") qt_bin_dir_suffix="armv7";; + "arm64-v8a") qt_bin_dir_suffix="arm64_v8a";; + *) qt_bin_dir_suffix=$ABI;; + esac fi +# get real path +# calls on paths containing '..' may result in a 'Permission denied' +QT_BIN_DIR=$(cd $QT_HOST_PATH/../android_$qt_bin_dir_suffix/bin && pwd) +echo "Building App..." + +echo "Qt host: $QT_HOST_PATH" echo "Using Qt in $QT_BIN_DIR" echo "Using Android SDK in $ANDROID_SDK_ROOT" echo "Using Android NDK in $ANDROID_NDK_ROOT" -# Build App -echo "Building App..." -cd $BUILD_DIR +# Run qt-cmake to configure build +qt_cmake_opts=() -echo "HOST Qt: $QT_HOST_PATH" +if [ -v AAB ]; then + qt_cmake_opts+=(-DQT_ANDROID_BUILD_ALL_ABIS=ON) +else + qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABI") +fi -$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR \ - -DQT_NO_GLOBAL_APK_TARGET_PART_OF_ALL="ON" \ - -DQT_HOST_PATH=$QT_HOST_PATH \ - -DCMAKE_BUILD_TYPE="Release" +# QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON - Skip building apks as part of the default 'ALL' target +# We'll build apks during androiddeployqt +$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -B $BUILD_DIR \ + -DQT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + "${qt_cmake_opts[@]}" -cmake --build . --config release +# Build app +cmake --build $BUILD_DIR --config $BUILD_TYPE -echo "............APK generation.................." -cd $OUT_APP_DIR +# Build and package APK or AAB. If this is a release, then additionally sign the result. +echo "Building APK/AAB..." + +deployqt_opts=() + +if [ -v AAB ]; then + deployqt_opts+=(--aab) +fi + +if [ -v ANDROID_PLATFORM ]; then + deployqt_opts+=(--android-platform "$ANDROID_PLATFORM") +fi + +if [ "$BUILD_TYPE" = "release" ]; then + deployqt_opts+=(--release --sign) +fi $QT_HOST_PATH/bin/androiddeployqt \ - --output $OUT_APP_DIR/android-build \ - --gradle \ - --release \ - --input android-AmneziaVPN-deployment-settings.json \ - --android-platform android-33 - -echo "............Copy apk.................." -cp $OUT_APP_DIR/android-build/build/outputs/apk/release/android-build-release-unsigned.apk \ - $PROJECT_DIR/AmneziaVPN-release-unsigned.apk + --input $OUT_APP_DIR/android-AmneziaVPN-deployment-settings.json \ + --output $OUT_APP_DIR/android-build \ + --gradle \ + "${deployqt_opts[@]}" + +if [[ -v CI || -v MOVE_RESULT ]]; then + echo "Moving APK/AAB..." + if [ -v AAB ]; then + mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/android-build-$BUILD_TYPE.aab \ + $PROJECT_DIR/deploy/build/AmneziaVPN-$BUILD_TYPE.aab + else + if [ "$BUILD_TYPE" = "release" ]; then + build_suffix="release-signed" + else + build_suffix=$BUILD_TYPE + fi + mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/android-build-$build_suffix.apk \ + $PROJECT_DIR/deploy/build/AmneziaVPN-$ABI-$build_suffix.apk + fi +fi