Refactoring build

Move to gradle kotlin DSL
Use gradle version catalog
All android build parameters are set via cmake files
Use gradle abi split to build APKs
Improve local development in the android project folder
This commit is contained in:
albexk 2023-11-14 22:58:47 +03:00
parent fcabf08e74
commit e1eec55f62
12 changed files with 461 additions and 291 deletions

View file

@ -273,21 +273,10 @@ jobs:
name: 'Build-Android' name: 'Build-Android'
runs-on: ubuntu-latest runs-on: ubuntu-latest
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: env:
QT_VERSION: 6.5.2 ANDROID_BUILD_PLATFORM: android-34
ANDROID_BUILD_PLATFORM: android-33 QT_VERSION: 6.5.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'
@ -297,29 +286,58 @@ jobs:
host: 'linux' host: 'linux'
target: 'desktop' target: 'desktop'
arch: 'gcc_64' arch: 'gcc_64'
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools' modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android Qt' - name: 'Install android_x86_64 Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
target: 'android' target: 'android'
arch: ${{ matrix.qt_arch }} arch: 'android_x86_64'
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools' modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake' - name: 'Grant execute permission for qt-cmake'
shell: bash shell: bash
run: | run: |
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/${{ matrix.qt_arch }}/bin/qt-cmake chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -333,14 +351,14 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '11' java-version: '17'
cache: 'gradle' cache: 'gradle'
- name: 'Setup Android NDK' - name: 'Setup Android NDK'
id: setup-ndk id: setup-ndk
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
with: with:
ndk-version: 'r25c' ndk-version: 'r26b'
local-cache: 'true' local-cache: 'true'
- name: 'Decode keystore secret to file' - name: 'Decode keystore secret to file'
@ -354,16 +372,36 @@ jobs:
env: env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64 QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64
QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }} ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
shell: bash shell: bash
run: ./deploy/build_android.sh --apk ${{ matrix.abi }} --platform ${{ env.ANDROID_BUILD_PLATFORM }} run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
- name: 'Upload apk' - name: 'Upload x86_64 apk'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: AmneziaVPN-android-${{ matrix.abi }} name: AmneziaVPN-android-x86_64
path: deploy/build/AmneziaVPN-${{ matrix.abi }}-release-signed.apk path: deploy/build/AmneziaVPN-x86_64-release.apk
retention-days: 7
- name: 'Upload x86 apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-x86
path: deploy/build/AmneziaVPN-x86-release.apk
retention-days: 7
- name: 'Upload arm64-v8a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-arm64-v8a
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
retention-days: 7
- name: 'Upload armeabi-v7a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-armeabi-v7a
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
retention-days: 7 retention-days: 7

View file

@ -11,6 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 39)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")

View file

@ -1,153 +1,5 @@
apply plugin: 'com.github.ben-manes.versions' // dummy file for androiddeployqt
buildscript {
ext{
kotlin_version = "1.7.22"
// for libwg
appcompatVersion = '1.1.0'
annotationsVersion = '1.0.1'
databindingVersion = '3.3.1'
jsr305Version = '3.0.2'
streamsupportVersion = '1.7.0'
threetenabpVersion = '1.1.1'
groupName = 'org.amnezia.vpn'
minSdkVer = '24'
cmakeMinVersion = "3.25.0+"
}
repositories {
google()
jcenter()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation group: 'org.json', name: 'json', version: '20220924'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation project(path: ':shadowsocks')
// CameraX core library using the camera2 implementation
def camerax_version = "1.2.1"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("androidx.camera:camera-extensions:${camerax_version}")
def camerax_ml_version = "1.2.0-beta02"
def ml_kit_version = "17.0.3"
implementation("androidx.camera:camera-mlkit-vision:${camerax_ml_version}")
implementation("com.google.mlkit:barcode-scanning:${ml_kit_version}")
}
androidExtensions {
experimental = true
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
// Extract native libraries from the APK
packagingOptions.jniLibs.useLegacyPackaging true
dexOptions {
javaMaxHeapSize "3g"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
androidTest.assets.srcDirs += files("${qtAndroidDir}/schemas".toString())
}
}
tasks.withType(JavaCompile) {
options.incremental = true
}
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
defaultConfig {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 34
versionCode 39 // Change to a higher number
versionName "4.1.0" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
]
}
}
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging

View file

@ -0,0 +1,92 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("property-delegate")
}
kotlin {
jvmToolchain(17)
}
// get values from gradle or local properties
val qtTargetSdkVersion: String by gradleProperties
val qtTargetAbiList: String by gradleProperties
val qtAndroidDir: String by gradleProperties
android {
namespace = "org.amnezia.vpn"
buildFeatures {
buildConfig = true
viewBinding = true
}
androidResources {
// don't compress Qt binary resources file
noCompress += "rcc"
}
packaging {
// compress .so binary libraries
jniLibs.useLegacyPackaging = true
}
defaultConfig {
applicationId = "org.amnezia.vpn"
targetSdk = qtTargetSdkVersion.toInt()
// keeps language resources for only the locales specified below
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
}
sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("$qtAndroidDir/src", "src"))
res.setSrcDirs(listOf("$qtAndroidDir/res", "res"))
assets.setSrcDirs(listOf("assets"))
jniLibs.setSrcDirs(listOf("libs"))
}
}
signingConfigs {
register("release") {
storeFile = providers.environmentVariable("ANDROID_KEYSTORE_PATH").orNull?.let { file(it) }
storePassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
keyAlias = providers.environmentVariable("ANDROID_KEYSTORE_KEY_ALIAS").orNull
keyPassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
}
}
buildTypes {
release {
// exclude coroutine debug resource from release build
packaging {
resources.excludes += "DebugProbesKt.bin"
}
signingConfig = signingConfigs["release"]
}
}
splits {
abi {
isEnable = true
reset()
include(*qtTargetAbiList.split(',').toTypedArray())
isUniversalApk = false
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.security.crypto)
implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
implementation(project(":shadowsocks"))
// todo: remove after finish refactoring
implementation(libs.androidx.constraintlayout)
}

View file

@ -1,27 +1,46 @@
# Project-wide Gradle settings. # Specifies the JVM arguments used for the daemon process
# For more details on how to configure your build environment visit org.gradle.jvmargs=-Xms512m -Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError \
# http://www.gradle.org/docs/current/userguide/build_environment.html -Dfile.encoding=UTF-8
# Specifies the JVM arguments used for the daemon process. org.gradle.caching=true
# The setting is particularly useful for tweaking memory settings. org.gradle.parallel=true
org.gradle.jvmargs=-Xmx1536m org.gradle.configureondemand=true
# Gradle caching allows reusing the build artifacts from a previous
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true
# Use AndroidX library instead of a Support Library
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX # Disable adding android:testOnly attribute to the manifest
android.enableJetifier=true android.injected.testOnly=false
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
android.bundle.enableUncompressedNativeLibs=false # Disable providing custom values to resources from buildscript by default
androidBuildToolsVersion=30.0.2 android.defaults.buildfeatures.resvalues=false
androidCompileSdkVersion=30 # Disable compileShaders tasks by default
org.gradle.caching=true android.defaults.buildfeatures.shaders=false
org.gradle.parallel=true # Disable Android resource processing for libraries by default
android.enableJetifier=true android.library.defaults.buildfeatures.androidresources=false
android.injected.testOnly=false
kapt.use.worker.api=false # Qt variables
kapt.incremental.apt=false # At build time androiddeployqt replaces these values with:
# androidCompileSdkVersion - androiddeployqt --android-platform parameter
# androidBuildToolsVersion - QT_ANDROID_SDK_BUILD_TOOLS_REVISION cmake target parameter
# qtMinSdkVersion - QT_ANDROID_MIN_SDK_VERSION cmake target parameter
# qtTargetSdkVersion - QT_ANDROID_TARGET_SDK_VERSION cmake target parameter
# androidNdkVersion - version from ANDROID_NDK_ROOT environment variable
# qtTargetAbiList - qt-cmake QT_ANDROID_ABIS parameter
# qtAndroidDir - path to qt binding java source code
# buildDir - hardcoded "build" value in androiddeployqt
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
#qtAndroidDir=/QT_BASE/android_ABI/src/android/java
#buildDir=build
# Note about qtAndroidDir:
# Some IDEs (for example, IntelliJ IDEA) may index all data from a common root of the project and qtAndroidDir.
# Therefore, it's recommended to copy qt android files to a directory inside the project
# and specify the path to that directory in qtAndroidDir.

View file

@ -0,0 +1,35 @@
[versions]
agp = "8.1.3"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-appcompat = "1.6.1"
androidx-camera = "1.2.3"
androidx-security-crypto = "1.1.0-alpha06"
kotlinx-coroutines = "1.7.3"
google-mlkit = "17.2.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
# todo: remove after finish refactoring
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
[bundles]
androidx-camera = [
"androidx-camera-core",
"androidx-camera-lifecycle",
"androidx-camera-view",
"androidx-camera-camera2"
]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View file

@ -0,0 +1,33 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
kotlin {
jvmToolchain(17)
}
gradlePlugin {
plugins {
register("settingsGradlePropertyDelegate") {
id = "settings-property-delegate"
implementationClass = "SettingsPropertyDelegate"
}
register("projectGradlePropertyDelegate") {
id = "property-delegate"
implementationClass = "ProjectPropertyDelegate"
}
}
}
// stop Gradle running by androiddeployqt
gradle.taskGraph.whenReady {
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
&& !providers.systemProperty("explicitRun").isPresent) {
tasks.forEach { it.enabled = false }
}
}

View file

@ -0,0 +1,49 @@
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import java.util.Properties
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.provider.ProviderFactory
private fun localProperties(rootDir: File) = Properties().apply {
val localProperties = File(rootDir, "local.properties")
if (localProperties.isFile) {
InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use {
load(it)
}
}
}
private class PropertyDelegate(
rootDir: File,
private val providers: ProviderFactory,
private val localProperties: Properties = localProperties(rootDir)
) : ReadOnlyProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name)
}
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>
private lateinit var projectPropertyDelegate: ReadOnlyProperty<Any?, String>
class SettingsPropertyDelegate : Plugin<Settings> {
override fun apply(settings: Settings) {
settingsPropertyDelegate = PropertyDelegate(settings.rootDir, settings.providers)
}
}
class ProjectPropertyDelegate : Plugin<Project> {
override fun apply(project: Project) {
projectPropertyDelegate = PropertyDelegate(project.rootDir, project.providers)
}
}
val Settings.gradleProperties: ReadOnlyProperty<Any?, String>
get() = settingsPropertyDelegate
val Project.gradleProperties: ReadOnlyProperty<Any?, String>
get() = projectPropertyDelegate

View file

@ -1,21 +0,0 @@
pluginManagement {
repositories {
google()
mavenCentral()
jcenter()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
}
}
include ':shadowsocks'

View file

@ -0,0 +1,58 @@
import com.android.build.api.dsl.SettingsExtension
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
includeBuild("./gradle/plugins")
}
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
}
includeBuild("./gradle/plugins")
plugins {
id("com.android.settings") version "8.1.3"
id("settings-property-delegate")
}
rootProject.name = "AmneziaVPN"
rootProject.buildFileName = "build.gradle.kts"
include(":shadowsocks")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties
val androidCompileSdkVersion: String by gradleProperties
val androidNdkVersion: String by gradleProperties
val qtMinSdkVersion: String by gradleProperties
// set default values for all modules
configure<SettingsExtension> {
buildToolsVersion = androidBuildToolsVersion
compileSdk = androidCompileSdkVersion.substringAfter('-').toInt()
minSdk = qtMinSdkVersion.toInt()
ndkVersion = androidNdkVersion
}
// stop Gradle running by androiddeployqt
gradle.taskGraph.whenReady {
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
&& !providers.systemProperty("explicitRun").isPresent) {
allTasks.forEach { it.enabled = false }
}
}

View file

@ -1,4 +1,20 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build") message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 24)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)
set_target_properties(${PROJECT} PROPERTIES
QT_ANDROID_VERSION_NAME ${CMAKE_PROJECT_VERSION}
QT_ANDROID_VERSION_CODE ${APP_ANDROID_VERSION_CODE}
QT_ANDROID_MIN_SDK_VERSION ${APP_ANDROID_MIN_SDK}
QT_ANDROID_TARGET_SDK_VERSION 34
QT_ANDROID_SDK_BUILD_TOOLS_REVISION 34.0.0
QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
)
set(QT_ANDROID_MULTI_ABI_FORWARD_VARS "QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL;CMAKE_BUILD_TYPE")
# We need to include qtprivate api's # We need to include qtprivate api's
# As QAndroidBinder is not yet implemented with a public api # As QAndroidBinder is not yet implemented with a public api
set(LIBS ${LIBS} Qt6::CorePrivate) set(LIBS ${LIBS} Qt6::CorePrivate)
@ -23,39 +39,6 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
) )
add_custom_command(
TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/build.gradle
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle/wrapper/gradle-wrapper.jar
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle/wrapper/gradle-wrapper.properties
${CMAKE_CURRENT_SOURCE_DIR}/android/gradlew
${CMAKE_CURRENT_SOURCE_DIR}/android/gradlew.bat
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle.properties
${CMAKE_CURRENT_SOURCE_DIR}/android/res/values/libs.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/res/xml/fileprovider.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/AuthHelper.java
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/IPCContract.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/NotificationUtil.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/Prefs.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNLogger.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNService.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNServiceBinder.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/AmneziaApp.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
${CMAKE_CURRENT_BINARY_DIR}
)
set_property(TARGET ${PROJECT} PROPERTY
QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/android
)
foreach(abi IN ITEMS ${QT_ANDROID_ABIS}) foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/android/${abi}/libwg.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/android/${abi}/libwg.so

View file

@ -13,9 +13,10 @@ Build AmneziaVPN android client. By default, a signed Android App Bundle (AAB) i
Options: Options:
-d, --debug Build debug version -d, --debug Build debug version
-a, --apk <abi> Build APK for the specified ABI -a, --apk (<abi_list> | all) Build APKs for the specified ABIs or for all available ABIs
Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
-p, --platform <platform> The SDK platform used for building the Java code of the application <abi_list> - list of ABIs delimited by ';'
-b, --build-platform <platform> The SDK platform used for building the Java code of the application
By default, the latest available platform is used By default, the latest available platform is used
-m, --move Move the build result to the root of the build directory -m, --move Move the build result to the root of the build directory
-h, --help Display this help -h, --help Display this help
@ -26,21 +27,25 @@ EOT
BUILD_TYPE="release" BUILD_TYPE="release"
AAB=1 AAB=1
opts=$(getopt -l debug,apk:,platform:,move,help -o "da:p:mh" -- "$@") opts=$(getopt -l debug,apk:,build-platform:,move,help -o "da:b:mh" -- "$@")
eval set -- "$opts" eval set -- "$opts"
while true; do while true; do
case "$1" in case "$1" in
-d | --debug) BUILD_TYPE="debug"; shift;; -d | --debug) BUILD_TYPE="debug"; shift;;
-a | --apk) ABI=$2; unset AAB; shift 2;; -a | --apk) ABIS=$2; unset AAB; shift 2;;
-p | --platform) ANDROID_PLATFORM=$2; shift 2;; -b | --build-platform) ANDROID_BUILD_PLATFORM=$2; shift 2;;
-m | --move) MOVE_RESULT=1; shift;; -m | --move) MOVE_RESULT=1; shift;;
-h | --help) usage; exit 0;; -h | --help) usage; exit 0;;
--) shift; break;; --) shift; break;;
esac esac
done done
if [[ -v ABI && ! "$ABI" =~ ^(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then # Validate ABIS parameter
echo "The 'abi' option must be one of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'], but is '$ABI'" if [[ -v ABIS && \
! "$ABIS" = "all" && \
! "$ABIS" =~ ^((x86|x86_64|armeabi-v7a|arm64-v8a);)*(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then
echo "The 'apk' option must be a list of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a']" \
"delimited by ';' or 'all', but is '$ABIS'"
exit 1 exit 1
fi fi
@ -56,13 +61,19 @@ OUT_APP_DIR=$BUILD_DIR/client
echo "Project dir: $PROJECT_DIR" echo "Project dir: $PROJECT_DIR"
echo "Build dir: $BUILD_DIR" echo "Build dir: $BUILD_DIR"
if [ -v AAB ]; then # Determine path to qt bin folder with qt-cmake
if [[ -v AAB || "$ABIS" = "all" ]]; then
qt_bin_dir_suffix="x86_64" qt_bin_dir_suffix="x86_64"
else else
case $ABI in if [[ $ABIS = *";"* ]]; then
oneOf=$(echo $ABIS | cut -d';' -f 1)
else
oneOf=$ABIS
fi
case $oneOf in
"armeabi-v7a") qt_bin_dir_suffix="armv7";; "armeabi-v7a") qt_bin_dir_suffix="armv7";;
"arm64-v8a") qt_bin_dir_suffix="arm64_v8a";; "arm64-v8a") qt_bin_dir_suffix="arm64_v8a";;
*) qt_bin_dir_suffix=$ABI;; *) qt_bin_dir_suffix=$oneOf;;
esac esac
fi fi
# get real path # get real path
@ -79,10 +90,10 @@ echo "Using Android NDK in $ANDROID_NDK_ROOT"
# Run qt-cmake to configure build # Run qt-cmake to configure build
qt_cmake_opts=() qt_cmake_opts=()
if [ -v AAB ]; then if [[ -v AAB || "$ABIS" = "all" ]]; then
qt_cmake_opts+=(-DQT_ANDROID_BUILD_ALL_ABIS=ON) qt_cmake_opts+=(-DQT_ANDROID_BUILD_ALL_ABIS=ON)
else else
qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABI") qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABIS")
fi fi
# QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON - Skip building apks as part of the default 'ALL' target # QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON - Skip building apks as part of the default 'ALL' target
@ -95,7 +106,7 @@ $QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -B $BUILD_DIR \
# Build app # Build app
cmake --build $BUILD_DIR --config $BUILD_TYPE cmake --build $BUILD_DIR --config $BUILD_TYPE
# Build and package APK or AAB. If this is a release, then additionally sign the result. # Build and package APK or AAB
echo "Building APK/AAB..." echo "Building APK/AAB..."
deployqt_opts=() deployqt_opts=()
@ -104,32 +115,52 @@ if [ -v AAB ]; then
deployqt_opts+=(--aab) deployqt_opts+=(--aab)
fi fi
if [ -v ANDROID_PLATFORM ]; then if [ -v ANDROID_BUILD_PLATFORM ]; then
deployqt_opts+=(--android-platform "$ANDROID_PLATFORM") deployqt_opts+=(--android-platform "$ANDROID_BUILD_PLATFORM")
fi fi
if [ "$BUILD_TYPE" = "release" ]; then if [ "$BUILD_TYPE" = "release" ]; then
deployqt_opts+=(--release --sign) deployqt_opts+=(--release)
fi fi
# for gradle to skip all tasks when it is executed by androiddeployqt
# gradle is started later explicitly
export ANDROIDDEPLOYQT_RUN=1
$QT_HOST_PATH/bin/androiddeployqt \ $QT_HOST_PATH/bin/androiddeployqt \
--input $OUT_APP_DIR/android-AmneziaVPN-deployment-settings.json \ --input $OUT_APP_DIR/android-AmneziaVPN-deployment-settings.json \
--output $OUT_APP_DIR/android-build \ --output $OUT_APP_DIR/android-build \
--gradle \
"${deployqt_opts[@]}" "${deployqt_opts[@]}"
# run gradle
gradle_opts=()
if [ -v AAB ]; then
gradle_opts+=(bundle"${BUILD_TYPE^}")
else
gradle_opts+=(assemble"${BUILD_TYPE^}")
fi
$OUT_APP_DIR/android-build/gradlew \
--project-dir $OUT_APP_DIR/android-build \
-DexplicitRun=1 \
"${gradle_opts[@]}"
if [[ -v CI || -v MOVE_RESULT ]]; then if [[ -v CI || -v MOVE_RESULT ]]; then
echo "Moving APK/AAB..." echo "Moving APK/AAB..."
if [ -v AAB ]; then if [ -v AAB ]; then
mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/android-build-$BUILD_TYPE.aab \ mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/AmneziaVPN-$BUILD_TYPE.aab \
$PROJECT_DIR/deploy/build/AmneziaVPN-$BUILD_TYPE.aab $PROJECT_DIR/deploy/build/
else else
if [ "$BUILD_TYPE" = "release" ]; then if [ "$ABIS" = "all" ]; then
build_suffix="release-signed" ABIS="x86;x86_64;armeabi-v7a;arm64-v8a"
else
build_suffix=$BUILD_TYPE
fi 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 IFS=';' read -r -a abi_array <<< "$ABIS"
for ABI in "${abi_array[@]}"
do
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/AmneziaVPN-$ABI-$BUILD_TYPE.apk \
$PROJECT_DIR/deploy/build/
done
fi fi
fi fi