Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
190 changes: 115 additions & 75 deletions .github/autobuild/android.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
#!/bin/bash
set -eu

# Some of the following version pinnings are semi-automatically checked for
# updates. Update .github/workflows/bump-dependencies.yaml when renaming those:
COMMANDLINETOOLS_VERSION=6858069
ANDROID_NDK_VERSION=r21d
ANDROID_PLATFORM=android-30
ANDROID_BUILD_TOOLS=30.0.2
AQTINSTALL_VERSION=2.2.3
QT_VERSION=5.15.2
## From https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md
# Tools already installed:
# Android Command Line Tools 7.0
# Android SDK Build-tools 33.0.0
# Android SDK Platform-Tools 33.0.3
# Android SDK Platforms android-33 (rev 2)
# Android SDK Tools 26.1.1

# Only variables which are really needed by sub-commands are exported.
# Definitions have to stay in a specific order due to dependencies.
# Env vars set:
# ANDROID_HOME /usr/local/lib/android/sdk
# ANDROID_NDK /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_NDK_HOME /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_NDK_LATEST_HOME /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_NDK_ROOT /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_SDK_ROOT /usr/local/lib/android/sdk

ANDROID_PLATFORM=android-33
AQTINSTALL_VERSION=2.1.0
QT_VERSION=6.3.2
QT_BASEDIR="/opt/Qt"
ANDROID_BASEDIR="/opt/android"
BUILD_DIR=build
export ANDROID_SDK_ROOT="${ANDROID_BASEDIR}/android-sdk"
COMMANDLINETOOLS_DIR="${ANDROID_SDK_ROOT}"/cmdline-tools/latest/
export ANDROID_NDK_ROOT="${ANDROID_BASEDIR}/android-ndk"
ANDROID_NDK_HOST="linux-x86_64"
ANDROID_SDKMANAGER="${COMMANDLINETOOLS_DIR}/bin/sdkmanager"
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/"
ANDROID_NDK_ROOT="${ANDROID_NDK_ROOT}"
Comment thread
pljones marked this conversation as resolved.
# Only variables which are really needed by sub-commands are exported.
export JAVA_HOME=${JAVA_HOME_11_X64}
export PATH="${PATH}:${ANDROID_SDK_ROOT}/tools"
export PATH="${PATH}:${ANDROID_SDK_ROOT}/platform-tools"

Expand All @@ -33,95 +38,130 @@ setup_ubuntu_dependencies() {
export DEBIAN_FRONTEND="noninteractive"

sudo apt-get -qq update
sudo apt-get -qq --no-install-recommends -y install build-essential zip unzip bzip2 p7zip-full curl chrpath openjdk-8-jdk-headless
}

setup_android_sdk() {
mkdir -p "${ANDROID_BASEDIR}"

if [[ -d "${COMMANDLINETOOLS_DIR}" ]]; then
echo "Using commandlinetools installation from previous run (actions/cache)"
else
mkdir -p "${COMMANDLINETOOLS_DIR}"
curl -s -o downloadfile "https://dl.google.com/android/repository/commandlinetools-linux-${COMMANDLINETOOLS_VERSION}_latest.zip"
unzip -q downloadfile
mv cmdline-tools/* "${COMMANDLINETOOLS_DIR}"
fi

yes | "${ANDROID_SDKMANAGER}" --licenses
"${ANDROID_SDKMANAGER}" --update
"${ANDROID_SDKMANAGER}" "platforms;${ANDROID_PLATFORM}"
"${ANDROID_SDKMANAGER}" "build-tools;${ANDROID_BUILD_TOOLS}"
sudo apt-get -qq --no-install-recommends -y install \
build-essential zip unzip bzip2 p7zip-full curl chrpath openjdk-11-jdk-headless
}

setup_android_ndk() {
mkdir -p "${ANDROID_BASEDIR}"
if [[ -d "${ANDROID_NDK_ROOT}" ]]; then
echo "Using NDK installation from previous run (actions/cache)"
else
curl -s -o downloadfile "https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip"
unzip -q downloadfile
mv "android-ndk-${ANDROID_NDK_VERSION}" "${ANDROID_NDK_ROOT}"
fi
}

setup_qt() {
if [[ -d "${QT_BASEDIR}" ]]; then
echo "Using Qt installation from previous run (actions/cache)"
else
echo "Installing Qt..."
python3 -m pip install "aqtinstall==${AQTINSTALL_VERSION}"
local qtmultimedia=()
if [[ ! "${QT_VERSION}" =~ 5\..* ]]; then
# From Qt6 onwards, qtmultimedia is a module and cannot be installed
# as an archive anymore.
qtmultimedia=("--modules")
fi
qtmultimedia+=("qtmultimedia")

python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux android "${QT_VERSION}" \
--archives qtbase qttools qttranslations qtandroidextras \
"${qtmultimedia[@]}"
# Delete libraries, which we don't use, but which bloat the resulting package and might introduce unwanted dependencies.
find "${QT_BASEDIR}" -name 'libQt5*Quick*.so' -delete
rm -r "${QT_BASEDIR}/${QT_VERSION}/android/qml/"
# icu needs explicit installation
# otherwise: "qmake: error while loading shared libraries: libicui18n.so.56: cannot open shared object file: No such file or directory"
python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux desktop "${QT_VERSION}" \
--archives qtbase qtdeclarative qtsvg qttools icu \
--modules qtmultimedia

# - 64bit required for Play Store
python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux android "${QT_VERSION}" android_arm64_v8a \
--archives qtbase qtdeclarative qtsvg qttools \
--modules qtmultimedia

# Also install for arm_v7 to build for 32bit devices
python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux android "${QT_VERSION}" android_armv7 \
--archives qtbase qtdeclarative qtsvg qttools \
--modules qtmultimedia

fi
}

build_app_as_apk() {
local QT_DIR="${QT_BASEDIR}/${QT_VERSION}/android"
build_app() {
local ARCH_ABI="${1}"

local MAKE="${ANDROID_NDK_ROOT}/prebuilt/${ANDROID_NDK_HOST}/bin/make"

"${QT_DIR}/bin/qmake" -spec android-clang
echo "${GOOGLE_RELEASE_KEYSTORE}" | base64 --decode > android/android_release.keystore

echo ">>> Compiling for ${ARCH_ABI} ..."
# if ARCH_ABI=android_armv7 we need to override ANDROID_ABIS for qmake
# note: seems ANDROID_ABIS can be set here at cmdline, but ANDROID_VERSION_CODE cannot - must be in qmake file
if [ "${ARCH_ABI}" == "android_armv7" ]; then
echo ">>> Running qmake with ANDROID_ABIS=armeabi-v7a ..."
ANDROID_ABIS=armeabi-v7a \
"${QT_BASEDIR}/${QT_VERSION}/${ARCH_ABI}/bin/qmake" -spec android-clang
else
echo ">>> Running qmake with ANDROID_ABIS=arm64-v8a ..."
ANDROID_ABIS=arm64-v8a \
"${QT_BASEDIR}/${QT_VERSION}/${ARCH_ABI}/bin/qmake" -spec android-clang
fi
"${MAKE}" -j "$(nproc)"
"${MAKE}" INSTALL_ROOT="${BUILD_DIR}" -f Makefile install
"${QT_DIR}"/bin/androiddeployqt --input android-Jamulus-deployment-settings.json --output "${BUILD_DIR}" \
--android-platform "${ANDROID_PLATFORM}" --jdk "${JAVA_HOME}" --gradle
"${MAKE}" INSTALL_ROOT="${BUILD_DIR}_${ARCH_ABI}" -f Makefile install
}

build_make_clean() {
echo ">>> Doing make clean ..."
local MAKE="${ANDROID_NDK_ROOT}/prebuilt/${ANDROID_NDK_HOST}/bin/make"
"${MAKE}" clean
rm -f Makefile
}

build_aab() {
local ARCH_ABI="${1}"

if [ "${ARCH_ABI}" == "android_armv7" ]; then
TARGET_ABI=armeabi-v7a
else
TARGET_ABI=arm64-v8a
fi
echo ">>> Building .aab file for ${TARGET_ABI}...."

ANDROID_ABIS=${TARGET_ABI} ${QT_BASEDIR}/${QT_VERSION}/gcc_64/bin/androiddeployqt --input android-Jamulus-deployment-settings.json \
--verbose \
--output "${BUILD_DIR}_${ARCH_ABI}" \
--aab \
--release \
--sign android/android_release.keystore jamulus \
--storepass "${GOOGLE_KEYSTORE_PASS}" \
--android-platform "${ANDROID_PLATFORM}" \
--jdk "${JAVA_HOME}" \
--gradle
}

pass_artifact_to_job() {
mkdir deploy
local artifact="jamulus_${JAMULUS_BUILD_VERSION}_android.apk"
echo "Moving ${BUILD_DIR}/build/outputs/apk/debug/build-debug.apk to deploy/${artifact}"
mv "./${BUILD_DIR}/build/outputs/apk/debug/build-debug.apk" "./deploy/${artifact}"
echo "::set-output name=artifact_1::${artifact}"
local ARCH_ABI="${1}"
echo ">>> Deploying .aab file for ${ARCH_ABI}...."

if [ "${ARCH_ABI}" == "android_armv7" ]; then
NUM="1"
BUILDNAME="arm"
else
NUM="2"
BUILDNAME="arm64"
fi

mkdir -p deploy
local artifact="Jamulus_${JAMULUS_BUILD_VERSION}_android_${BUILDNAME}.aab"
# debug to check for filenames
ls -alR "${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/"
ls -al "${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/build_${ARCH_ABI}-release.aab"
echo ">>> Moving ${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/build_${ARCH_ABI}-release.aab to deploy/${artifact}"
mv "./${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/build_${ARCH_ABI}-release.aab" "./deploy/${artifact}"
echo ">>> Moved .aab file to deploy/${artifact}"
echo ">>> Artifact number is: ${NUM}"
echo ">>> Setting output as such: name=artifact_${NUM}::${artifact}"
echo "::set-output name=artifact_${NUM}::${artifact}"
}

case "${1:-}" in
setup)
setup_ubuntu_dependencies
setup_android_ndk
setup_android_sdk
setup_qt
;;
build)
build_app_as_apk
build_app "android_armv7"
build_aab "android_armv7"
build_make_clean
build_app "android_arm64_v8a"
build_aab "android_arm64_v8a"
;;
get-artifacts)
pass_artifact_to_job
pass_artifact_to_job "android_armv7"
pass_artifact_to_job "android_arm64_v8a"
;;
*)
echo "Unknown stage '${1:-}'"
exit 1
;;
esac
21 changes: 21 additions & 0 deletions .github/workflows/autobuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ jobs:
MACOS_CERTIFICATE_ID: ${{ secrets.MACOS_CERT_ID }}
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
GOOGLE_RELEASE_KEYSTORE: ${{ secrets.GOOGLE_KEYSTORE }}
GOOGLE_KEYSTORE_PASS: ${{ secrets.GOOGLE_KEYSTORE_PASS }}

- name: Post-Build for ${{ matrix.config.config_name }}
id: get-artifacts
Expand Down Expand Up @@ -403,6 +405,25 @@ jobs:
with:
product-path: deploy/${{ steps.get-artifacts.outputs.artifact_1 }}

## RELEASE PROCEDURE FOR:
## - Android Play Store - aab
## Requirement: Service Account JSON setup:
## - Google Play Console -> Setup -> API Access -> Create/Link Google Cloud Project
## - Google Cloud console -> IAM & Admin -> Service Accounts -> Create (Wizard). Then create JSON key and export/save
- name: Publish arm, arm64 Android builds to Play Store
if: >-
needs.create_release.outputs.publish_to_release == 'true' &&
matrix.config.target_os == 'android'
id: publish_android
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
packageName: io.jamulus.jamulus
releaseFiles: deploy/${{ steps.get-artifacts.outputs.artifact_1 }},deploy/${{ steps.get-artifacts.outputs.artifact_2 }}
releaseName: ${{ needs.create_release.outputs.build_version }}
track: beta
status: draft

- name: Upload Artifact 1 to Release
# Every build job has at least one artifact. Therefore, no `if artifact_1` condition here.
# If the artifact is missing, this should fail.
Expand Down
32 changes: 28 additions & 4 deletions Jamulus.pro
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,42 @@ win32 {
LIBS += -framework AVFoundation \
-framework AudioToolbox
} else:android {
ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64
# ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64
# note: only armeabi-v7a arm64-v8a are targeted, others are dead/deprecated
Comment thread
ann0see marked this conversation as resolved.
Outdated

# get ANDROID_ABIS from environment - passed directly to qmake
ANDROID_ABIS = $$getenv(ANDROID_ABIS)

# if ANDROID_ABIS is passed as env var to qmake, will override this
# !defined(ANDROID_ABIS, var):ANDROID_ABIS = arm64-v8a

# Experiments show likely better results with Android 10+ devices
ANDROID_MIN_SDK_VERSION = 29
ANDROID_TARGET_SDK_VERSION = 32
ANDROID_VERSION_NAME = $$VERSION
ANDROID_VERSION_CODE = $$system(git log --oneline | wc -l)

# date-based unique value - prevent clashes in Play Store build numbering
!defined(ANDROID_VERSION_CODE, var):ANDROID_VERSION_CODE = $$system(date +%s | cut -c 2-)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will not work on windows I suppose? Wouldn't the git commit hash or something else be better?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The CI is obviously targeted purely for Linux.
For local building on Windows we can just hardcode any int value.
ANDROID_VERSION_CODE = 123456

Android Version Code requires an integer <=2100000000.
So you'd need some custom logic to convert the shorthash to int first.

I don't like relying on git log because you need a separate version code for armeabi7 and armv8 builds.
The above date output guarantees a unique, valid value for each Play Store submission.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The CI is obviously targeted purely for Linux.

But as it's in Jamulus.pro we should ensure cross compatibility. Even if it's a noop.


# make separate version codes for each abi build otherwise Play Store rejects
contains (ANDROID_ABIS, armeabi-v7a) {
ANDROID_VERSION_CODE = $$num_add($$ANDROID_VERSION_CODE, 1)
message("Setting for armeabi-v7a: ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")
} else {
message("Setting for armv8a: ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")
}

message("Setting ANDROID_VERSION_NAME=$${ANDROID_VERSION_NAME} ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")

# liboboe requires C++17 for std::timed_mutex
CONFIG += c++17

QT += androidextras
# For device recording permissions
# Qt6:
QT += core-private

# enabled only for debugging on android devices
DEFINES += ANDROIDDEBUG
#DEFINES += ANDROIDDEBUG

target.path = /tmp/your_executable # path on device
INSTALLS += target
Expand Down