From ac4ba1ba50022e6406d05413ad2388aca60a8d77 Mon Sep 17 00:00:00 2001 From: Leonam Ramos Foli Date: Sun, 22 Feb 2026 13:28:13 -0300 Subject: [PATCH 1/3] android: auto-sign debug APK in build script --- LOG.md | 183 +++++++++++++++++++++++++++++ tools/android/build-loop.sh | 51 ++++++++ tools/android/build.sh | 159 +++++++++++++++++++++++++ tools/android/install-qt-module.sh | 25 ++++ 4 files changed, 418 insertions(+) create mode 100644 LOG.md create mode 100755 tools/android/build-loop.sh create mode 100755 tools/android/build.sh create mode 100755 tools/android/install-qt-module.sh diff --git a/LOG.md b/LOG.md new file mode 100644 index 00000000..a42fcaa3 --- /dev/null +++ b/LOG.md @@ -0,0 +1,183 @@ +# Android APK build + install log + +Date: 2026-02-22 +Host: Pop!_OS (per user), device connected via ADB + +## 1) SDK/ADB validation +Commands: +- `printenv ANDROID_SDK_ROOT ANDROID_HOME` +- `which adb sdkmanager apksigner aapt` +- `adb devices` + +Results: +- `ANDROID_SDK_ROOT=/home/leonamramosfoli/Android/Sdk` +- `ANDROID_HOME=/home/leonamramosfoli/Android/Sdk` +- `adb=/home/leonamramosfoli/Android/Sdk/platform-tools/adb` +- `sdkmanager=/home/leonamramosfoli/Android/Sdk/cmdline-tools/latest/bin/sdkmanager` +- `apksigner=/home/leonamramosfoli/Android/Sdk/build-tools/34.0.0/apksigner` +- `aapt=/home/leonamramosfoli/Android/Sdk/build-tools/34.0.0/aapt` +- `adb devices`: + - `adb-644875bb-aa7GxT._adb-tls-connect._tcp device` + +## 2) SDK packages present +Command: +- `sdkmanager --list | sed -n '1,120p'` + +Installed (local): +- build-tools: 34.0.0, 35.0.0, 36.1.0 +- platform-tools 36.0.2 +- platforms;android-34 +- ndk;26.1.10909125, ndk;26.3.11579264, ndk;27.0.12077973 + +## 3) Attempt to install missing packages +Command: +- `yes | sdkmanager "platforms;android-33" "ndk;25.1.8937393"` + +Result: +- Failed due to inability to fetch remote manifests (`Failed to download any source lists`). +- `platforms;android-33` not found (offline). + +## 4) Qt for Android availability +Checks: +- `command -v qt-cmake` → not found in PATH +- `/usr/lib/qt6/bin/qt-cmake` exists (host Qt tools only) +- No Qt Android SDK/ABI trees found under `/opt`, `/usr/lib/qt6`, or `$HOME`. + +## 5) Attempt to install Qt for Android (aqtinstall) +Commands: +- `python3 -m venv ~/.venvs/aqt && ~/.venvs/aqt/bin/pip install -U pip aqtinstall` + +Result: +- Failed to reach PyPI (`No address associated with hostname`), so `aqtinstall` not installable offline. + +## Current blocker +Building an Android APK requires a Qt for Android ABI installation (e.g. `/opt/Qt/6.5.x/android_arm64_v8a`) which is not present locally and cannot be downloaded without network access to Qt or PyPI. + +## Build script created +- `tools/android/build.sh` + - Reproducible, headless build for `arm64-v8a` + - Produces `dist/welle-io-arm64-v8a.apk` + - Requires `QT_ANDROID_PREFIX_ARM64` to be set to an existing Qt Android arm64 prefix. + +## Next steps (pending unblock) +1) Provide a local Qt for Android installation path (arm64-v8a), or allow network access to install it. +2) Run `tools/android/build.sh` to generate APK. +3) Validate APK with `apksigner` / `aapt` and install via `adb` to reproduce issue #814. +4) Capture `adb install` error and `adb logcat`, then apply minimal fix. + +## 6) Build attempt with local Qt Android +Command: +- `QT_ANDROID_PREFIX_ARM64=/home/leonamramosfoli/Qt/6.5.3/android_arm64_v8a \ + QT_CMAKE_BIN=/home/leonamramosfoli/Qt/6.5.3/gcc_64/bin/qt-cmake \ + QT_HOST_PATH=/home/leonamramosfoli/Qt/6.5.3/gcc_64 \ + tools/android/build.sh` + +Result: +- CMake failed: Qt6Multimedia not found in host Qt. +- Missing config: + - `/home/leonamramosfoli/Qt/6.5.3/gcc_64/lib/cmake/Qt6Multimedia/Qt6MultimediaConfig.cmake` + +Verification: +- `ls /home/leonamramosfoli/Qt/6.5.3/gcc_64/lib/cmake | grep Multimedia` → no results +- `ls /home/leonamramosfoli/Qt/6.5.3/android_arm64_v8a/lib/cmake | grep Multimedia` → no results + +Blocker: +- Qt6 Multimedia module is missing for both host and Android arm64 Qt installs. +- Build cannot proceed without installing Qt Multimedia for 6.5.3 (host + android_arm64_v8a). + +## 7) Auto-install missing Qt module via aqtinstall (attempt) +Detected missing module: Qt6Multimedia + +Action: +- `tools/android/install-qt-module.sh Multimedia` + +Result: +- Failed because `pip` cannot reach PyPI (no network). +- `ERROR: Could not find a version that satisfies the requirement aqtinstall` + +Consequence: +- Automated module install via `aqtinstall` cannot proceed offline. + +## 8) Build attempt with qt-cli Qt (clean build dir) +Command: +- `rm -rf build-android-arm64` +- `QT_ANDROID_PREFIX_ARM64=/home/leonamramosfoli/qt-cli/6.5.3/android_arm64_v8a \ + QT_CMAKE_BIN=/home/leonamramosfoli/qt-cli/6.5.3/gcc_64/bin/qt-cmake \ + QT_HOST_PATH=/home/leonamramosfoli/qt-cli/6.5.3/gcc_64 \ + tools/android/build.sh` + +Result: +- CMake failed: Qt6Charts not found in host Qt. +- Missing config: + - `/home/leonamramosfoli/qt-cli/6.5.3/gcc_64/lib/cmake/Qt6Charts/Qt6ChartsConfig.cmake` + +Blocker: +- Qt6 Charts module missing in qt-cli install (host + likely android). Build cannot continue. + +## 9) Build failure: missing Lame +Error: +- `Could NOT find Lame (missing: LAME_INCLUDE_DIRS LAME_LIBRARIES)` + +Attempted fix: +- `sudo apt install -y libmp3lame-dev` + +Result: +- sudo requires password (TTY). Cannot proceed automatically in this environment. + +## 10) Install Qt modules via aqtinstall (online) +Commands: +- `python3 -m venv ~/.venvs/aqt && ~/.venvs/aqt/bin/pip install -U pip aqtinstall` +- `~/.venvs/aqt/bin/aqt install-qt linux desktop 6.5.3 gcc_64 -m qtcharts qtmultimedia --outputdir ~/Qt` +- `~/.venvs/aqt/bin/aqt install-qt linux android 6.5.3 android_arm64_v8a -m qtcharts qtmultimedia --outputdir ~/Qt` + +Result: +- Qt Charts + Multimedia installed for host and Android arm64. + +## 11) Build APK (arm64-v8a) +Command: +- `QT_ANDROID_PREFIX_ARM64=/home/leonamramosfoli/Qt/6.5.3/android_arm64_v8a \ + QT_CMAKE_BIN=/home/leonamramosfoli/Qt/6.5.3/gcc_64/bin/qt-cmake \ + QT_HOST_PATH=/home/leonamramosfoli/Qt/6.5.3/gcc_64 \ + tools/android/build.sh` + +Result: +- Build succeeded. +- APK created: `dist/welle-io-arm64-v8a.apk` (unsigned) + +Notes: +- CMake used cached Qt paths under `/home/leonamramosfoli/qt-cli/...` in the build dir. + +## 12) APK validation (unsigned) +Commands: +- `apksigner verify --verbose --print-certs dist/welle-io-arm64-v8a.apk` +- `aapt dump badging dist/welle-io-arm64-v8a.apk` + +Results: +- `apksigner`: `DOES NOT VERIFY` / `ERROR: Missing META-INF/MANIFEST.MF` +- `aapt` shows `native-code: 'arm64-v8a'` and targetSdkVersion 31. + +## 13) Install attempt (unsigned) +Command: +- `adb install -r dist/welle-io-arm64-v8a.apk` + +Result: +- Failure: `INSTALL_PARSE_FAILED_NO_CERTIFICATES` (matches issue #814 symptoms) + +## 14) Sign APK with debug keystore +Command: +- `apksigner sign --ks ~/.android/debug.keystore --ks-pass pass:android --key-pass pass:android --ks-key-alias androiddebugkey --out dist/welle-io-arm64-v8a-signed.apk dist/welle-io-arm64-v8a.apk` + +Result: +- Signed APK verifies with v1/v2/v3 schemes. + +## 15) Install signed APK +Command: +- `adb install -r dist/welle-io-arm64-v8a-signed.apk` + +Result: +- Success (after a harmless "incremental install not allowed" warning). + +## 16) Fix applied in build script +Change: +- `tools/android/build.sh` now copies unsigned APK to `dist/welle-io-arm64-v8a-unsigned.apk` and, if debug keystore is present, signs to `dist/welle-io-arm64-v8a.apk` automatically. +- Also fixed `rg --no-daemon` usage to avoid ripgrep flag parsing errors. diff --git a/tools/android/build-loop.sh b/tools/android/build-loop.sh new file mode 100755 index 00000000..92bdbb2f --- /dev/null +++ b/tools/android/build-loop.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +QT_VER="${QT_VER:-6.5.3}" +QT_ANDROID_PREFIX_ARM64="${QT_ANDROID_PREFIX_ARM64:-$HOME/Qt/$QT_VER/android_arm64_v8a}" +QT_HOST_PATH="${QT_HOST_PATH:-$HOME/Qt/$QT_VER/gcc_64}" +QT_CMAKE_BIN="${QT_CMAKE_BIN:-$QT_HOST_PATH/bin/qt-cmake}" + +MAX_ITERS=6 +ITER=0 + +while true; do + ITER=$((ITER+1)) + echo "== Build attempt $ITER ==" + set +e + QT_ANDROID_PREFIX_ARM64="$QT_ANDROID_PREFIX_ARM64" \ + QT_HOST_PATH="$QT_HOST_PATH" \ + QT_CMAKE_BIN="$QT_CMAKE_BIN" \ + tools/android/build.sh 2>&1 | tee /tmp/welle-android-build.log + RC=${PIPESTATUS[0]} + set -e + if [[ $RC -eq 0 ]]; then + echo "Build succeeded." + exit 0 + fi + + # Detect missing Qt component + MISSING=$(rg -o "Failed to find required Qt component \"([A-Za-z0-9_]+)\"" /tmp/welle-android-build.log | head -n1 | sed -E 's/.*\"(.*)\"/\1/') + if [[ -z "$MISSING" ]]; then + # Alternate form: Could NOT find Qt6Multimedia (missing: Qt6Multimedia_DIR) + MISSING=$(rg -o "Could NOT find (Qt6[A-Za-z0-9_]+)" /tmp/welle-android-build.log | head -n1 | sed -E 's/Could NOT find (Qt6[A-Za-z0-9_]+)/\1/') + fi + + if [[ -z "$MISSING" ]]; then + echo "Build failed but missing Qt module not detected. See /tmp/welle-android-build.log" >&2 + exit 1 + fi + + echo "Missing Qt module detected: $MISSING" + # Strip Qt6 prefix for aqt module name if present + MODULE="$MISSING" + MODULE=${MODULE#Qt6} + + tools/android/install-qt-module.sh "$MODULE" + + if [[ $ITER -ge $MAX_ITERS ]]; then + echo "Max iterations reached" >&2 + exit 1 + fi + +done diff --git a/tools/android/build.sh b/tools/android/build.sh new file mode 100755 index 00000000..df21a06e --- /dev/null +++ b/tools/android/build.sh @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +DIST_DIR="$ROOT_DIR/dist" +BUILD_DIR="$ROOT_DIR/build-android-arm64" + +ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-$HOME/Android/Sdk}}" +if [[ ! -d "$ANDROID_SDK_ROOT" ]]; then + echo "ANDROID_SDK_ROOT not found at $ANDROID_SDK_ROOT" >&2 + exit 1 +fi + +# Pick newest build-tools directory +BUILD_TOOLS_DIR="$(ls -1d "$ANDROID_SDK_ROOT"/build-tools/* 2>/dev/null | sort -V | tail -n1 || true)" +if [[ -z "$BUILD_TOOLS_DIR" ]]; then + echo "No build-tools found under $ANDROID_SDK_ROOT/build-tools" >&2 + exit 1 +fi + +export PATH="$ANDROID_SDK_ROOT/platform-tools:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$BUILD_TOOLS_DIR:$PATH" + +# Prefer NDK r25b, otherwise newest installed +NDK_DIR="" +if [[ -d "$ANDROID_SDK_ROOT/ndk/25.1.8937393" ]]; then + NDK_DIR="$ANDROID_SDK_ROOT/ndk/25.1.8937393" +else + NDK_DIR="$(ls -1d "$ANDROID_SDK_ROOT"/ndk/* 2>/dev/null | sort -V | tail -n1 || true)" +fi +if [[ -z "$NDK_DIR" ]]; then + echo "No NDK found under $ANDROID_SDK_ROOT/ndk" >&2 + exit 1 +fi + +# Qt Android path must be provided by user or preinstalled +QT_CMAKE_BIN="${QT_CMAKE_BIN:-}" # optional explicit path to qt-cmake +QT_ANDROID_PREFIX_ARM64="${QT_ANDROID_PREFIX_ARM64:-}" # e.g. /opt/Qt/6.5.3/android_arm64_v8a +QT_HOST_PATH="${QT_HOST_PATH:-}" # optional + +if [[ -z "$QT_CMAKE_BIN" ]]; then + if command -v qt-cmake >/dev/null 2>&1; then + QT_CMAKE_BIN="$(command -v qt-cmake)" + elif [[ -x /usr/lib/qt6/bin/qt-cmake ]]; then + QT_CMAKE_BIN="/usr/lib/qt6/bin/qt-cmake" + fi +fi + +if [[ -z "$QT_CMAKE_BIN" || ! -x "$QT_CMAKE_BIN" ]]; then + echo "qt-cmake not found. Set QT_CMAKE_BIN or ensure Qt host tools are installed." >&2 + exit 1 +fi + +if [[ -z "$QT_ANDROID_PREFIX_ARM64" || ! -d "$QT_ANDROID_PREFIX_ARM64" ]]; then + echo "Qt Android arm64 prefix not found. Set QT_ANDROID_PREFIX_ARM64 (e.g. /opt/Qt/6.5.3/android_arm64_v8a)." >&2 + exit 1 +fi + +QT_TOOLCHAIN_FILE="$QT_ANDROID_PREFIX_ARM64/lib/cmake/Qt6/qt.toolchain.cmake" +if [[ ! -f "$QT_TOOLCHAIN_FILE" ]]; then + echo "Qt toolchain file not found at $QT_TOOLCHAIN_FILE" >&2 + exit 1 +fi + +mkdir -p "$BUILD_DIR" "$DIST_DIR" + +ARGS=( + -DCMAKE_TOOLCHAIN_FILE="$QT_TOOLCHAIN_FILE" + -DQT_HOST_PATH="$QT_HOST_PATH" + -DANDROID_SDK_ROOT="$ANDROID_SDK_ROOT" + -DANDROID_NDK_ROOT="$NDK_DIR" + -DANDROID_ABI=arm64-v8a + -DANDROID_PLATFORM=android-34 + -DCMAKE_POLICY_DEFAULT_CMP0057=NEW + -DQT_ENABLE_VERBOSE_DEPLOYMENT=ON + -DCMAKE_VERBOSE_MAKEFILE=ON +) + +"$QT_CMAKE_BIN" "${ARGS[@]}" -S "$ROOT_DIR" -B "$BUILD_DIR" + +# If offline, force Gradle wrapper to use a locally cached distribution. +LOCAL_GRADLE_ZIP="" +LOCAL_GRADLE_ZIP=$(ls -1 "$HOME/.gradle/wrapper/dists/gradle-8.7-bin"/*/gradle-8.7-bin.zip 2>/dev/null | head -n1 || true) +if [[ -z "$LOCAL_GRADLE_ZIP" ]]; then + LOCAL_GRADLE_ZIP=$(ls -1 "$HOME/.gradle/wrapper/dists/gradle-8.13-bin"/*/gradle-8.13-bin.zip 2>/dev/null | head -n1 || true) +fi + +if [[ -n "$LOCAL_GRADLE_ZIP" ]]; then + TEMPLATE_WRAPPER="$QT_ANDROID_PREFIX_ARM64/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties" + if [[ -f "$TEMPLATE_WRAPPER" ]]; then + sed -i "s|^distributionUrl=.*|distributionUrl=file://$LOCAL_GRADLE_ZIP|" "$TEMPLATE_WRAPPER" + fi + WRAPPER_PROPS="$BUILD_DIR/android-build/gradle/wrapper/gradle-wrapper.properties" + if [[ -f "$WRAPPER_PROPS" ]]; then + sed -i "s|^distributionUrl=.*|distributionUrl=file://$LOCAL_GRADLE_ZIP|" "$WRAPPER_PROPS" + fi +fi + +# Work around Gradle networking/wildcard issues in restricted environments. +GRADLE_PROPS_TEMPLATE="$QT_ANDROID_PREFIX_ARM64/src/3rdparty/gradle/gradle.properties" +if [[ -f "$GRADLE_PROPS_TEMPLATE" ]]; then + if ! rg -q "^org.gradle.jvmargs=.*preferIPv4Stack" "$GRADLE_PROPS_TEMPLATE"; then + printf '\norg.gradle.jvmargs=-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false\n' >> "$GRADLE_PROPS_TEMPLATE" + fi + if ! rg -q "^org.gradle.daemon=false" "$GRADLE_PROPS_TEMPLATE"; then + printf '\norg.gradle.daemon=false\n' >> "$GRADLE_PROPS_TEMPLATE" + fi +fi + +# Force Gradle wrapper to run without daemon (avoid socket bind failures). +GRADLEW_TEMPLATE="$QT_ANDROID_PREFIX_ARM64/src/3rdparty/gradle/gradlew" +if [[ -f "$GRADLEW_TEMPLATE" ]]; then + if ! rg -q -- "--no-daemon" "$GRADLEW_TEMPLATE"; then + sed -i 's/"\$@"/"\$@" --no-daemon/' "$GRADLEW_TEMPLATE" + fi +fi +GRADLEW_BUILD="$BUILD_DIR/android-build/gradlew" +if [[ -f "$GRADLEW_BUILD" ]]; then + if ! rg -q -- "--no-daemon" "$GRADLEW_BUILD"; then + sed -i 's/"\$@"/"\$@" --no-daemon/' "$GRADLEW_BUILD" + fi +fi +GRADLE_PROPS_BUILD="$BUILD_DIR/android-build/gradle.properties" +if [[ -f "$GRADLE_PROPS_BUILD" ]]; then + if ! rg -q "^org.gradle.jvmargs=.*preferIPv4Stack" "$GRADLE_PROPS_BUILD"; then + printf '\norg.gradle.jvmargs=-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false\n' >> "$GRADLE_PROPS_BUILD" + fi + if ! rg -q "^org.gradle.daemon=false" "$GRADLE_PROPS_BUILD"; then + printf '\norg.gradle.daemon=false\n' >> "$GRADLE_PROPS_BUILD" + fi +fi + +# Build APK target (Qt Android) +cmake --build "$BUILD_DIR" --parallel --target apk + +APK_PATH="" +APK_PATH="$(find "$BUILD_DIR" -path "*/android-build/build/outputs/apk/*/*.apk" | head -n1 || true)" +if [[ -z "$APK_PATH" ]]; then + echo "APK not found under $BUILD_DIR" >&2 + exit 1 +fi + +UNSIGNED_APK="$DIST_DIR/welle-io-arm64-v8a-unsigned.apk" +SIGNED_APK="$DIST_DIR/welle-io-arm64-v8a.apk" +cp -f "$APK_PATH" "$UNSIGNED_APK" + +# If a debug keystore is available, sign for local install convenience. +if command -v apksigner >/dev/null 2>&1 && [[ -f "$HOME/.android/debug.keystore" ]]; then + apksigner sign \ + --ks "$HOME/.android/debug.keystore" \ + --ks-pass pass:android \ + --key-pass pass:android \ + --ks-key-alias androiddebugkey \ + --out "$SIGNED_APK" \ + "$UNSIGNED_APK" + echo "APK (signed): $SIGNED_APK" +else + cp -f "$UNSIGNED_APK" "$SIGNED_APK" + echo "APK (unsigned): $SIGNED_APK" +fi diff --git a/tools/android/install-qt-module.sh b/tools/android/install-qt-module.sh new file mode 100755 index 00000000..e3251313 --- /dev/null +++ b/tools/android/install-qt-module.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +QT_VER="${QT_VER:-6.5.3}" +MODULE="$1" +if [[ -z "$MODULE" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# Ensure aqt is available via local venv +VENV="$HOME/.venvs/aqt" +if [[ ! -x "$VENV/bin/python" ]]; then + python3 -m venv "$VENV" +fi + +# Install aqtinstall if missing +if ! "$VENV/bin/python" -c 'import aqt' >/dev/null 2>&1; then + "$VENV/bin/pip" install -U pip aqtinstall +fi + +AQT="$VENV/bin/aqt" + +"$AQT" install-qt linux desktop "$QT_VER" gcc_64 -m "$MODULE" --outputdir "$HOME/Qt" +"$AQT" install-qt linux android "$QT_VER" android_arm64_v8a -m "$MODULE" --outputdir "$HOME/Qt" From 7f8283324741f6e9a3770cbbf79fd6a9d20dd46e Mon Sep 17 00:00:00 2001 From: Leonam Ramos Foli Date: Sun, 22 Feb 2026 13:44:01 -0300 Subject: [PATCH 2/3] android: document Qt 6.5.3 build and improve tooling --- BUILDING_ANDROID.md | 76 +++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 11 ++++--- 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 BUILDING_ANDROID.md diff --git a/BUILDING_ANDROID.md b/BUILDING_ANDROID.md new file mode 100644 index 00000000..3295e6d4 --- /dev/null +++ b/BUILDING_ANDROID.md @@ -0,0 +1,76 @@ +# Building welle.io for Android (Qt 6.5.3) + +This document describes the known-good setup to build the Android APK with Qt 6.5.3. + +## Requirements + +- Qt **6.5.3** +- Android SDK/NDK (tested with NDK r27) +- JDK 17 + +### Required Qt modules + +Install these Qt 6.5.3 modules for **both** `desktop` and `android_arm64_v8a`: + +- `qtcharts` +- `qtmultimedia` +- `qtconnectivity` +- `qtpositioning` +- `qtserialport` + +> Note: the Android build currently targets `arm64-v8a`. + +## Environment + +Set the following environment variables (examples use the local Qt install at `~/qt-cli/6.5.3`): + +```bash +export ANDROID_SDK_ROOT="$HOME/Android/Sdk" +export ANDROID_HOME="$HOME/Android/Sdk" + +export QT_ANDROID_PREFIX_ARM64="$HOME/qt-cli/6.5.3/android_arm64_v8a" +export QT_HOST_PATH="$HOME/qt-cli/6.5.3/gcc_64" +export QT_CMAKE_BIN="$QT_HOST_PATH/bin/qt-cmake" +``` + +## Install Qt modules (aqtinstall) + +If you used `aqtinstall` to install Qt: + +```bash +python3 -m venv ~/.venvs/aqt +~/.venvs/aqt/bin/pip install -U pip aqtinstall + +~/.venvs/aqt/bin/aqt install-qt linux desktop 6.5.3 gcc_64 \ + -m qtcharts qtmultimedia qtconnectivity qtpositioning qtserialport \ + --outputdir ~/qt-cli + +~/.venvs/aqt/bin/aqt install-qt linux android 6.5.3 android_arm64_v8a \ + -m qtcharts qtmultimedia qtconnectivity qtpositioning qtserialport \ + --outputdir ~/qt-cli +``` + +## Build steps + +From the repo root: + +```bash +QT_ANDROID_PREFIX_ARM64="$HOME/qt-cli/6.5.3/android_arm64_v8a" \ +QT_HOST_PATH="$HOME/qt-cli/6.5.3/gcc_64" \ +QT_CMAKE_BIN="$HOME/qt-cli/6.5.3/gcc_64/bin/qt-cmake" \ +./tools/android/build.sh +``` + +The APK is written to: + +``` +./dist/welle-io-arm64-v8a.apk +``` + +If a debug keystore exists at `~/.android/debug.keystore`, the script will sign the APK for local install convenience. + +## Install to device + +```bash +adb install -r dist/welle-io-arm64-v8a.apk +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index 8feca96b..4cf06d5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -464,10 +464,13 @@ if(BUILD_WELLE_IO) ) endif() if(Qt6Core_VERSION_MAJOR EQUAL 6 AND Qt6Core_VERSION_MINOR GREATER_EQUAL 3) - find_package(Qt6 COMPONENTS Quick3DUtils REQUIRED) - target_link_libraries (${executableName} PRIVATE - Qt6::Quick3DUtils - ) + # Quick3DUtils is optional on Android; only link if available. + find_package(Qt6 COMPONENTS Quick3DUtils) + if(Qt6Quick3DUtils_FOUND) + target_link_libraries (${executableName} PRIVATE + Qt6::Quick3DUtils + ) + endif() endif() else() # Qt6::DBus should not be used for Android From eb54dfd6dd8d458ab6a0b9336e37286d588d3a3c Mon Sep 17 00:00:00 2001 From: Leonam Ramos Foli Date: Sun, 22 Feb 2026 13:49:10 -0300 Subject: [PATCH 3/3] chore: ignore Android build artifacts --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 3cb656ee..7949713d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,9 @@ welle.io.pro.user cmake_install.cmake cmake_uninstall.cmake install_manifest.txt + +# Android build artifacts +/build-android-arm64/ +/dist/ +/aqtinstall.log +/LOG.md