mirror of
https://github.com/mappu/miqt.git
synced 2025-05-07 04:20:22 +00:00
android: major rework for all-in-one android support
This commit is contained in:
parent
687579d76f
commit
a4a4074948
@ -1,66 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# android-mktemplate generates a template json file suitable for use with the
|
|
||||||
# androiddeployqt tool.
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
# QT_PATH is already pre-set in our docker container environment. Includes trailing slash.
|
|
||||||
QT_PATH=${QT_PATH:-/usr/local/Qt-5.15.13/}
|
|
||||||
QT_ANDROID=${QT_ANDROID:-$QT_PATH}
|
|
||||||
|
|
||||||
ndk_version() {
|
|
||||||
ls /opt/android-sdk/ndk/ | tail -n1
|
|
||||||
}
|
|
||||||
|
|
||||||
target_sdk_version() {
|
|
||||||
ls /opt/android-sdk/platforms | tail -n1 | sed -re 's/android-//'
|
|
||||||
}
|
|
||||||
|
|
||||||
build_tools_version() {
|
|
||||||
ls /opt/android-sdk/build-tools | tail -n1
|
|
||||||
}
|
|
||||||
|
|
||||||
extra_libs() {
|
|
||||||
if [[ -d /opt/android_openssl ]] ; then
|
|
||||||
# Our miqt Qt5 container includes these extra .so libraries
|
|
||||||
# However, the aqtinstall-based Qt 6 container does not use them
|
|
||||||
echo "/opt/android_openssl/ssl_1.1/arm64-v8a/libssl_1_1.so,/opt/android_openssl/ssl_1.1/arm64-v8a/libcrypto_1_1.so"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
|
|
||||||
if [[ $# -ne 2 ]] ; then
|
|
||||||
echo "Usage: android-mktemplate.sh appname output.json" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
local ARG_APPNAME="$1"
|
|
||||||
local ARG_DESTFILE="$2"
|
|
||||||
|
|
||||||
# Available fields are documented in the template file at
|
|
||||||
# @ref /usr/local/Qt-5.15.13/mkspecs/features/android/android_deployment_settings.prf
|
|
||||||
cat > "${ARG_DESTFILE}" <<EOF
|
|
||||||
{
|
|
||||||
"_description": "Generated by miqt/android-mktemplate",
|
|
||||||
"application-binary": "${ARG_APPNAME}",
|
|
||||||
"architectures": {
|
|
||||||
"arm64-v8a" : "aarch64-linux-android"
|
|
||||||
},
|
|
||||||
"android-extra-libs": "$(extra_libs)",
|
|
||||||
"android-min-sdk-version": "23",
|
|
||||||
"android-target-sdk-version": "$(target_sdk_version)",
|
|
||||||
"ndk": "/opt/android-sdk/ndk/$(ndk_version)",
|
|
||||||
"ndk-host": "linux-x86_64",
|
|
||||||
"qt": "${QT_ANDROID}",
|
|
||||||
"sdk": "/opt/android-sdk",
|
|
||||||
"sdkBuildToolsRevision": "$(build_tools_version)",
|
|
||||||
"stdcpp-path": "/opt/android-sdk/ndk/$(ndk_version)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/",
|
|
||||||
"tool-prefix": "llvm",
|
|
||||||
"toolchain-prefix": "llvm",
|
|
||||||
"useLLVM": true
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
@ -1,106 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# android-stub-gen generates a .so that runs an exported function from another
|
|
||||||
# so file. This is because Qt for Android always tries to run `main`, but Go's
|
|
||||||
# c-shared build mode cannot export a function named `main`.
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
main() {
|
|
||||||
|
|
||||||
if [[ $# -ne 3 && $# -ne 4 ]] ; then
|
|
||||||
echo "Usage: android-gen-stub.sh src.so function-name dest.so [--qt6|--qt5]" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
local ARG_SOURCE_SOFILE="$1"
|
|
||||||
local ARG_FUNCTIONNAME="$2"
|
|
||||||
local ARG_DEST_SOFILE="$3"
|
|
||||||
local ARG_QTVERSION="${4:---qt5}"
|
|
||||||
|
|
||||||
local tmpdir=$(mktemp -d)
|
|
||||||
trap "rm -r ${tmpdir}" EXIT
|
|
||||||
|
|
||||||
echo "- Using temporary directory: ${tmpdir}"
|
|
||||||
|
|
||||||
echo "Generating stub..."
|
|
||||||
|
|
||||||
cat > $tmpdir/miqtstub.cpp <<EOF
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef void goMainFunc_t();
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "Starting up");
|
|
||||||
|
|
||||||
void* handle = dlopen("$(basename "$ARG_SOURCE_SOFILE")", RTLD_LAZY);
|
|
||||||
if (handle == NULL) {
|
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: null handle opening so: %s", dlerror());
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* goMain = dlsym(handle, "${ARG_FUNCTIONNAME}");
|
|
||||||
if (goMain == NULL) {
|
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: null handle looking for function: %s", dlerror());
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: Found target, calling");
|
|
||||||
|
|
||||||
// Cast to function pointer and call
|
|
||||||
goMainFunc_t* f = (goMainFunc_t*)goMain;
|
|
||||||
f();
|
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: Target function returned");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Compile
|
|
||||||
# Link with Qt libraries so that androiddeployqt detects us as being the
|
|
||||||
# main shared library
|
|
||||||
|
|
||||||
if [[ $ARG_QTVERSION == '--qt5' ]] ; then
|
|
||||||
|
|
||||||
# QT_PATH is already pre-set in our docker container environment. Includes trailing slash.
|
|
||||||
QT_PATH=${QT_PATH:-/usr/local/Qt-5.15.13/}
|
|
||||||
echo "- Found Qt path: ${QT_PATH}"
|
|
||||||
|
|
||||||
$CXX -shared \
|
|
||||||
-ldl \
|
|
||||||
-llog \
|
|
||||||
-L${QT_PATH}plugins/platforms -lplugins_platforms_qtforandroid_arm64-v8a \
|
|
||||||
$(pkg-config --libs Qt5Widgets) \
|
|
||||||
$(pkg-config --libs Qt5AndroidExtras) \
|
|
||||||
$tmpdir/miqtstub.cpp \
|
|
||||||
"-Wl,-soname,$(basename "$ARG_DEST_SOFILE")" \
|
|
||||||
-o "$ARG_DEST_SOFILE"
|
|
||||||
|
|
||||||
elif [[ $ARG_QTVERSION == '--qt6' ]] ; then
|
|
||||||
|
|
||||||
# QT_ANDROID is already pre-set in our docker container environment. Does NOT include trailing slash
|
|
||||||
QT_ANDROID=${QT_ANDROID:-/opt/Qt/6.6.1/android_arm64_v8a}
|
|
||||||
echo "- Found Qt path: ${QT_ANDROID}"
|
|
||||||
|
|
||||||
# There is no AndroidExtras in Qt 6
|
|
||||||
|
|
||||||
$CXX -shared \
|
|
||||||
-ldl \
|
|
||||||
-llog \
|
|
||||||
-L${QT_ANDROID}/plugins/platforms -lplugins_platforms_qtforandroid_arm64-v8a \
|
|
||||||
$(pkg-config --libs Qt6Widgets) \
|
|
||||||
$tmpdir/miqtstub.cpp \
|
|
||||||
"-Wl,-soname,$(basename "$ARG_DEST_SOFILE")" \
|
|
||||||
-o "$ARG_DEST_SOFILE"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Unknown Qt version argument "${ARG_QTVERSION}" (expected --qt5 or --qt6)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Done."
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
415
cmd/miqt-docker/android-build.sh
Normal file
415
cmd/miqt-docker/android-build.sh
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# android-build.sh allows building a MIQT Go application for Android.
|
||||||
|
# For details, see the top-level README.md file.
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
# QT_PATH is pre-set in the Qt 5 docker container environment. Includes trailing slash
|
||||||
|
# QT_ANDROID is pre-set in the Qt 6 docker container environment
|
||||||
|
QT_PATH=${QT_PATH:-/usr/local/Qt-5.15.13/}
|
||||||
|
QT_ANDROID=${QT_ANDROID:-$QT_PATH}
|
||||||
|
|
||||||
|
export LC_ALL=C.UTF-8
|
||||||
|
|
||||||
|
# get_app_name returns the android app's name. This affects the default name
|
||||||
|
# in deployment-settings.json and the generated lib.so names.
|
||||||
|
# You can still customise the package name and the package ID in the xml
|
||||||
|
# files after generation.
|
||||||
|
get_app_name() {
|
||||||
|
basename "$(pwd)"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_stub_soname() {
|
||||||
|
# libRealAppName_arm64-v8a.so
|
||||||
|
echo "lib$(get_app_name)_arm64-v8a.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_go_soname() {
|
||||||
|
echo "libMiqtGolangApp_arm64-v8a.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk_version() {
|
||||||
|
ls /opt/android-sdk/ndk/ | tail -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
target_sdk_version() {
|
||||||
|
ls /opt/android-sdk/platforms | tail -n1 | sed -re 's/android-//'
|
||||||
|
}
|
||||||
|
|
||||||
|
build_tools_version() {
|
||||||
|
ls /opt/android-sdk/build-tools | tail -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
# extra_libs returns a comma-separated list of extra libraries to include in
|
||||||
|
# the apk package
|
||||||
|
extra_libs() {
|
||||||
|
if [[ -d /opt/android_openssl ]] ; then
|
||||||
|
# Our miqt Qt5 container includes these extra .so libraries
|
||||||
|
# However, the aqtinstall-based Qt 6 container does not use them
|
||||||
|
echo "/opt/android_openssl/ssl_1.1/arm64-v8a/libssl_1_1.so,/opt/android_openssl/ssl_1.1/arm64-v8a/libcrypto_1_1.so"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate_template_contents produces a deployment settings JSON file that is
|
||||||
|
# understood by the androiddeployqt program.
|
||||||
|
# Available fields are documented in the template file at:
|
||||||
|
# @ref /usr/local/Qt-5.15.13/mkspecs/features/android/android_deployment_settings.prf
|
||||||
|
generate_template_contents() {
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"_description": "Generated by miqt/android-mktemplate",
|
||||||
|
"application-binary": "$(get_app_name)",
|
||||||
|
"architectures": {
|
||||||
|
"arm64-v8a" : "aarch64-linux-android"
|
||||||
|
},
|
||||||
|
"android-extra-libs": "$(extra_libs)",
|
||||||
|
"android-min-sdk-version": "23",
|
||||||
|
"android-target-sdk-version": "$(target_sdk_version)",
|
||||||
|
"ndk": "/opt/android-sdk/ndk/$(ndk_version)",
|
||||||
|
"ndk-host": "linux-x86_64",
|
||||||
|
"qt": "${QT_ANDROID}",
|
||||||
|
"sdk": "/opt/android-sdk",
|
||||||
|
"sdkBuildToolsRevision": "$(build_tools_version)",
|
||||||
|
"stdcpp-path": "/opt/android-sdk/ndk/$(ndk_version)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/",
|
||||||
|
"tool-prefix": "llvm",
|
||||||
|
"toolchain-prefix": "llvm",
|
||||||
|
"useLLVM": true
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkg_config_dependencies echoes the pkg-config packages that are needed as
|
||||||
|
# a baseline for the detected Qt version.
|
||||||
|
# It's not smart enough to discover other Qt packages yet.
|
||||||
|
pkg_config_dependencies() {
|
||||||
|
local QT_VERSION=$(detect_miqt_qt_version)
|
||||||
|
if [[ $QT_VERSION == 'qt5' ]] ; then
|
||||||
|
echo Qt5Widgets
|
||||||
|
echo Qt5AndroidExtras
|
||||||
|
|
||||||
|
elif [[ $QT_VERSION == 'qt6' ]] ; then
|
||||||
|
# There is no AndroidExtras in Qt 6
|
||||||
|
echo Qt6Widgets
|
||||||
|
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# android_stub_gen generates a .so that runs an exported function from another
|
||||||
|
# so file. This is because Qt for Android always tries to run `main`, but Go's
|
||||||
|
# c-shared build mode cannot export a function named `main`.
|
||||||
|
android_stub_gen() {
|
||||||
|
|
||||||
|
local STUBNAME="miqt-stub-$(date +%s).cpp"
|
||||||
|
|
||||||
|
cat > $STUBNAME <<EOF
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef void goMainFunc_t();
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "Starting up");
|
||||||
|
|
||||||
|
void* handle = dlopen("$(get_go_soname)", RTLD_LAZY);
|
||||||
|
if (handle == NULL) {
|
||||||
|
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: null handle opening so: %s", dlerror());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* goMain = dlsym(handle, "AndroidMain");
|
||||||
|
if (goMain == NULL) {
|
||||||
|
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: null handle looking for function: %s", dlerror());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: Found target, calling");
|
||||||
|
|
||||||
|
// Cast to function pointer and call
|
||||||
|
goMainFunc_t* f = (goMainFunc_t*)goMain;
|
||||||
|
f();
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_VERBOSE, "miqt_stub", "miqt_stub: Target function returned");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Compile
|
||||||
|
# Link with Qt libraries so that androiddeployqt detects us as being the
|
||||||
|
# main shared library
|
||||||
|
|
||||||
|
$CXX -shared \
|
||||||
|
-ldl \
|
||||||
|
-llog \
|
||||||
|
-L${QT_ANDROID}/plugins/platforms -lplugins_platforms_qtforandroid_arm64-v8a \
|
||||||
|
$(pkg-config --libs $(pkg_config_dependencies)) \
|
||||||
|
${STUBNAME} \
|
||||||
|
"-Wl,-soname,$(basename "$(get_stub_soname)")" \
|
||||||
|
-o "android-build/libs/arm64-v8a/$(get_stub_soname)"
|
||||||
|
|
||||||
|
rm "${STUBNAME}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# require_is_main_package verifies that this is the main Go package.
|
||||||
|
require_is_main_package() {
|
||||||
|
if ! grep -Fq 'package main' *.go ; then
|
||||||
|
echo "This doesn't seem to be the main package" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# patch_app_main sets up the startup go files so that the go program can be
|
||||||
|
# built either as c-shared for Android or as a normal program for desktop OSes.
|
||||||
|
patch_app_main() {
|
||||||
|
|
||||||
|
# Replace func main() with app_main()
|
||||||
|
|
||||||
|
for srcfile in *.go ; do
|
||||||
|
sed -i -re 's/^func main\(\) \{/func app_main() {/' "$srcfile"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Add shim startup files
|
||||||
|
|
||||||
|
cat <<EOF > startup_android.go
|
||||||
|
//go:build android
|
||||||
|
// +build android
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "C" // Required for export support
|
||||||
|
|
||||||
|
//export AndroidMain
|
||||||
|
func AndroidMain() {
|
||||||
|
app_main()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Must be empty
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > startup_other.go
|
||||||
|
//go:build !android
|
||||||
|
// +build !android
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app_main()
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
gofmt -w startup_android.go || true
|
||||||
|
gofmt -w startup_other.go || true
|
||||||
|
|
||||||
|
# Done
|
||||||
|
}
|
||||||
|
|
||||||
|
# unpatch_app_main undoes the transformation from patch_app_main.
|
||||||
|
unpatch_app_main() {
|
||||||
|
|
||||||
|
# Replace func main() with app_main()
|
||||||
|
|
||||||
|
for srcfile in *.go ; do
|
||||||
|
sed -i -re 's/^func app_main\(\) \{/func main() {/' "$srcfile"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove extra startup files
|
||||||
|
|
||||||
|
rm startup_android.go || true
|
||||||
|
rm startup_other.go || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# build_app_so compiles the Go app as a c-shared .so.
|
||||||
|
build_app_so() {
|
||||||
|
go build \
|
||||||
|
-buildmode c-shared \
|
||||||
|
-ldflags "-s -w -extldflags -Wl,-soname,$(get_go_soname)" \
|
||||||
|
-o "android-build/libs/arm64-v8a/$(get_go_soname)"
|
||||||
|
}
|
||||||
|
|
||||||
|
sdkmanager() {
|
||||||
|
echo /opt/android-sdk/cmdline-tools/*/bin/sdkmanager
|
||||||
|
}
|
||||||
|
|
||||||
|
# build_apk calls androiddeployqt to package the android-build directory into
|
||||||
|
# the final apk.
|
||||||
|
build_apk() {
|
||||||
|
|
||||||
|
# Qt 6 androiddeployqt: Understands the QT_ANDROID_KEYSTORE_STORE_PASS in env
|
||||||
|
# Qt 5 androiddeployqt: Doesn't - any use of `--sign keystore alias` here
|
||||||
|
# requires stdin prompt but doesn't pass androiddeployqt's stdin through
|
||||||
|
# to jarsigner subprocess
|
||||||
|
# Either way, don't sign the app here, rely on separate jarsigner command
|
||||||
|
|
||||||
|
# Work around an issue with Qt 6 sporadically failing to detect that
|
||||||
|
# we have a valid Qt platform plugin
|
||||||
|
# TODO why does this happen? Is it related to file sort ordering?
|
||||||
|
# It really does fix itself after multiple attempts (usually less than 5)
|
||||||
|
# - When it fails: Error: qmlimportscanner not found at /qmlimportscanner
|
||||||
|
# - When it works: Error: qmlimportscanner not found at libexec/qmlimportscanner
|
||||||
|
while ! androiddeployqt \
|
||||||
|
--input ./deployment-settings.json \
|
||||||
|
--output ./android-build/ \
|
||||||
|
--release \
|
||||||
|
2> >(tee /tmp/androiddeployqt.stderr.log >&2) ; do
|
||||||
|
|
||||||
|
if grep -Fq 'Make sure the app links to Qt Gui library' /tmp/androiddeployqt.stderr.log ; then
|
||||||
|
echo "Detected temporary problem with Qt plugin detection. Retrying..."
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
else
|
||||||
|
# real error
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
local OUTAPK=$(get_app_name).apk
|
||||||
|
rm "$OUTAPK" || true
|
||||||
|
|
||||||
|
# Zipalign
|
||||||
|
echo "Zipalign..."
|
||||||
|
/opt/android-sdk/build-tools/*/zipalign \
|
||||||
|
-p 4 \
|
||||||
|
./android-build/build/outputs/apk/release/android-build-release-unsigned.apk \
|
||||||
|
"$OUTAPK"
|
||||||
|
|
||||||
|
# Sign
|
||||||
|
echo "Signing..."
|
||||||
|
#jarsigner \
|
||||||
|
# -verbose \
|
||||||
|
# -sigalg SHA256withRSA -digestalg SHA256 \
|
||||||
|
# -keystore ./android.keystore \
|
||||||
|
# "$OUTAPK" \
|
||||||
|
# "${QT_ANDROID_KEYSTORE_ALIAS}" \
|
||||||
|
# -storepass:env QT_ANDROID_KEYSTORE_STORE_PASS \
|
||||||
|
# -keypass:env QT_ANDROID_KEYSTORE_KEY_PASS
|
||||||
|
|
||||||
|
/opt/android-sdk/build-tools/*/apksigner \
|
||||||
|
sign \
|
||||||
|
--ks ./android.keystore \
|
||||||
|
--ks-key-alias "${QT_ANDROID_KEYSTORE_ALIAS}" \
|
||||||
|
--ks-pass env:QT_ANDROID_KEYSTORE_STORE_PASS \
|
||||||
|
--key-pass env:QT_ANDROID_KEYSTORE_KEY_PASS \
|
||||||
|
"$OUTAPK"
|
||||||
|
}
|
||||||
|
|
||||||
|
# detect_env_qt_version detects the system's current Qt version.
|
||||||
|
detect_env_qt_version() {
|
||||||
|
if qmake --version | fgrep -q 'Qt version 5' ; then
|
||||||
|
echo "qt5"
|
||||||
|
return 0
|
||||||
|
|
||||||
|
elif qmake --version | fgrep -q 'Qt version 6' ; then
|
||||||
|
echo "qt6"
|
||||||
|
return 0
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Missing Qt tools in PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# detect_miqt_qt_version echoes either "qt5", "qt6", or exits bash.
|
||||||
|
detect_miqt_qt_version() {
|
||||||
|
local IS_QT5=false
|
||||||
|
if grep -qF '"github.com/mappu/miqt/qt"' *.go ; then
|
||||||
|
IS_QT5=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
local IS_QT6=false
|
||||||
|
if grep -qF '"github.com/mappu/miqt/qt6"' *.go ; then
|
||||||
|
IS_QT6=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $IS_QT5 == true && $IS_QT6 == true ]] ; then
|
||||||
|
echo "Found qt5 and qt6 imports, confused about what to do next" >&2
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
elif [[ $IS_QT5 == true ]] ; then
|
||||||
|
echo "qt5"
|
||||||
|
return 0
|
||||||
|
|
||||||
|
elif [[ $IS_QT6 == true ]] ; then
|
||||||
|
echo "qt6"
|
||||||
|
return 0
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Found neither qt5 nor qt6 imports. Is this a MIQT Qt app?" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_default_keystore() {
|
||||||
|
|
||||||
|
local GENPASS=storepass_$(cat /dev/urandom | head -c64 | md5sum | cut -d' ' -f1)
|
||||||
|
|
||||||
|
keytool \
|
||||||
|
-genkeypair \
|
||||||
|
-dname "cn=Miqt, ou=Miqt, o=Miqt, c=US" \
|
||||||
|
-keyalg RSA \
|
||||||
|
-alias miqt \
|
||||||
|
-keypass "${GENPASS}" \
|
||||||
|
-keystore ./android.keystore \
|
||||||
|
-storepass "${GENPASS}" \
|
||||||
|
-validity 20000
|
||||||
|
|
||||||
|
echo "QT_ANDROID_KEYSTORE_PATH=./android.keystore" > android.keystore.env
|
||||||
|
echo "QT_ANDROID_KEYSTORE_ALIAS=miqt" >> android.keystore.env
|
||||||
|
echo "QT_ANDROID_KEYSTORE_STORE_PASS=${GENPASS}" >> android.keystore.env
|
||||||
|
echo "QT_ANDROID_KEYSTORE_KEY_PASS=${GENPASS}" >> android.keystore.env
|
||||||
|
}
|
||||||
|
|
||||||
|
# main is the entrypoint for android-build.sh.
|
||||||
|
main() {
|
||||||
|
|
||||||
|
if [[ $(detect_env_qt_version) != $(detect_miqt_qt_version) ]] ; then
|
||||||
|
echo "The system is $(detect_env_qt_version) but the app uses $(detect_miqt_qt_version). Is this the right container?" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_is_main_package
|
||||||
|
|
||||||
|
# Rebuild deployment-settings.json
|
||||||
|
if [[ ! -f deployment-settings.json ]] ; then
|
||||||
|
echo "Generating deployment-settings.json..."
|
||||||
|
generate_template_contents > deployment-settings.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p android-build/libs/arm64-v8a
|
||||||
|
|
||||||
|
if [[ ! -f android-build/libs/arm64-v8a/$(get_stub_soname) ]] ; then
|
||||||
|
echo "Generating stub so..."
|
||||||
|
android_stub_gen
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rebuild miqt_golang_app.so
|
||||||
|
echo "Compiling Go app..."
|
||||||
|
patch_app_main
|
||||||
|
build_app_so
|
||||||
|
unpatch_app_main
|
||||||
|
|
||||||
|
# Keypair
|
||||||
|
if [[ ! -f android.keystore || ! -f android.keystore.env ]] ; then
|
||||||
|
echo "Signing keystore not found, generating a default one..."
|
||||||
|
generate_default_keystore
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load keypair credentials into exported env vars
|
||||||
|
set -o allexport
|
||||||
|
source android.keystore.env
|
||||||
|
set +o allexport
|
||||||
|
|
||||||
|
# Generate .apk
|
||||||
|
echo "Packaging APK..."
|
||||||
|
build_apk
|
||||||
|
|
||||||
|
echo "Complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
@ -213,7 +213,9 @@ func getDockerRunArgsForGlob(dockerfiles []fs.DirEntry, containerNameGlob string
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fullCommand = append(fullCommand, `-v`, basedir+`:/src`, `-w`, filepath.Join(`/src`, relCwd))
|
mountDir := `/src/` + filepath.Base(cwd) // Don't use /src directly, otherwise -android-build will not know the package name for top-level builds
|
||||||
|
|
||||||
|
fullCommand = append(fullCommand, `-v`, basedir+`:`+mountDir, `-w`, filepath.Join(mountDir, relCwd))
|
||||||
|
|
||||||
fullCommand = append(fullCommand, `-e`, `HOME=/tmp`)
|
fullCommand = append(fullCommand, `-e`, `HOME=/tmp`)
|
||||||
|
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed android-build.sh
|
||||||
|
var embedAndroidBuildSh []byte
|
||||||
|
|
||||||
|
var _ embed.FS // Workaround to allow import of package `embed`
|
||||||
|
|
||||||
// evaluateTask turns the supplied process arguments into real arguments to
|
// evaluateTask turns the supplied process arguments into real arguments to
|
||||||
// execute, handling quick command recipes as well as arbitrary execution
|
// execute, handling quick command recipes as well as arbitrary execution
|
||||||
func evaluateTask(taskArgs []string) (retArgs []string, fixup func(*exec.Cmd), allowTty bool, err error) {
|
func evaluateTask(taskArgs []string) (retArgs []string, fixup func(*exec.Cmd), allowTty bool, err error) {
|
||||||
@ -26,6 +33,14 @@ func evaluateTask(taskArgs []string) (retArgs []string, fixup func(*exec.Cmd), a
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
stdinFrom := func(stdinBytes []byte) func(*exec.Cmd) {
|
||||||
|
return func(c *exec.Cmd) {
|
||||||
|
c.Stdin = bytes.NewReader(stdinBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
if taskArgs[0][0] != '-' {
|
if taskArgs[0][0] != '-' {
|
||||||
// Task does not start with a hyphen = plain command
|
// Task does not start with a hyphen = plain command
|
||||||
retArgs = taskArgs
|
retArgs = taskArgs
|
||||||
@ -49,6 +64,13 @@ func evaluateTask(taskArgs []string) (retArgs []string, fixup func(*exec.Cmd), a
|
|||||||
retArgs = append(retArgs, taskArgs[1:]...)
|
retArgs = append(retArgs, taskArgs[1:]...)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case `-android-build`:
|
||||||
|
retArgs = []string{"/bin/bash", "-s"}
|
||||||
|
retArgs = append(retArgs, taskArgs[1:]...)
|
||||||
|
fixup = stdinFrom(embedAndroidBuildSh)
|
||||||
|
allowTty = false
|
||||||
|
return
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, nil, false, fmt.Errorf("Unrecognized task %q", taskArgs[0])
|
return nil, nil, false, fmt.Errorf("Unrecognized task %q", taskArgs[0])
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,6 @@ RUN wget 'https://go.dev/dl/go1.23.1.linux-amd64.tar.gz' && \
|
|||||||
tar x -C /usr/local/ -f go1.23.1.linux-amd64.tar.gz && \
|
tar x -C /usr/local/ -f go1.23.1.linux-amd64.tar.gz && \
|
||||||
rm go1.23.1.linux-amd64.tar.gz
|
rm go1.23.1.linux-amd64.tar.gz
|
||||||
|
|
||||||
COPY cmd/android-stub-gen/android-stub-gen.sh /usr/local/bin/android-stub-gen.sh
|
|
||||||
COPY cmd/android-mktemplate/android-mktemplate.sh /usr/local/bin/android-mktemplate.sh
|
|
||||||
|
|
||||||
ENV PATH=/usr/local/go/bin:/opt/cmake/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/Qt-5.15.13/bin:/opt/android-sdk/cmdline-tools/tools/bin:/opt/android-sdk/tools:/opt/android-sdk/tools/bin:/opt/android-sdk/platform-tools
|
ENV PATH=/usr/local/go/bin:/opt/cmake/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/Qt-5.15.13/bin:/opt/android-sdk/cmdline-tools/tools/bin:/opt/android-sdk/tools:/opt/android-sdk/tools/bin:/opt/android-sdk/platform-tools
|
||||||
|
|
||||||
# The pkg-config definitions were all installed with platform-specific suffixes
|
# The pkg-config definitions were all installed with platform-specific suffixes
|
||||||
@ -14,6 +11,10 @@ ENV PATH=/usr/local/go/bin:/opt/cmake/bin:/usr/local/sbin:/usr/local/bin:/usr/sb
|
|||||||
# This container is targeting armv8-a, so set up simple symlinks
|
# This container is targeting armv8-a, so set up simple symlinks
|
||||||
RUN /bin/bash -c 'cd /usr/local/Qt-5.15.13/lib/pkgconfig ; for f in *_arm64-v8a.pc ; do cp $f "$(basename -s _arm64-v8a.pc "$f").pc"; done'
|
RUN /bin/bash -c 'cd /usr/local/Qt-5.15.13/lib/pkgconfig ; for f in *_arm64-v8a.pc ; do cp $f "$(basename -s _arm64-v8a.pc "$f").pc"; done'
|
||||||
|
|
||||||
|
# This is gross but (A) it's containerized and (B) allows --uid=1000 to perform builds
|
||||||
|
# Only needed for certain problematic versions of the Android SDK; a readonly SDK works in both older+newer SDKs
|
||||||
|
RUN /bin/bash -c 'find /opt/android-sdk/ -type d -exec chmod 777 {} \; && find /opt/android-sdk/ -perm 660 -exec chmod 666 {} \;'
|
||||||
|
|
||||||
ENV CC=/opt/android-sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang
|
ENV CC=/opt/android-sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang
|
||||||
ENV CXX=/opt/android-sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang++
|
ENV CXX=/opt/android-sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang++
|
||||||
ENV CGO_ENABLED=1
|
ENV CGO_ENABLED=1
|
||||||
|
@ -9,9 +9,6 @@ RUN wget 'https://go.dev/dl/go1.23.6.linux-amd64.tar.gz' && \
|
|||||||
tar x -C /usr/local/ -f go1.23.6.linux-amd64.tar.gz && \
|
tar x -C /usr/local/ -f go1.23.6.linux-amd64.tar.gz && \
|
||||||
rm go1.23.6.linux-amd64.tar.gz
|
rm go1.23.6.linux-amd64.tar.gz
|
||||||
|
|
||||||
COPY cmd/android-stub-gen/android-stub-gen.sh /usr/local/bin/android-stub-gen.sh
|
|
||||||
COPY cmd/android-mktemplate/android-mktemplate.sh /usr/local/bin/android-mktemplate.sh
|
|
||||||
|
|
||||||
# Fix up pkg-config definitions:
|
# Fix up pkg-config definitions:
|
||||||
# 1. There are only pkg-config definitions included for gcc_64 (Linux native), not for the android_arm64_v8a target we actually want
|
# 1. There are only pkg-config definitions included for gcc_64 (Linux native), not for the android_arm64_v8a target we actually want
|
||||||
# 2. It looks for `Libs: -L${libdir} -lQt6Widgets` but the file is named libQt6Widgets_arm64-v8a.so
|
# 2. It looks for `Libs: -L${libdir} -lQt6Widgets` but the file is named libQt6Widgets_arm64-v8a.so
|
||||||
|
Loading…
x
Reference in New Issue
Block a user