aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlli Vuolteenaho <olli.vuolteenaho@qt.io>2025-07-08 07:16:53 +0300
committerOlli Vuolteenaho <olli.vuolteenaho@qt.io>2025-09-09 10:39:16 +0300
commite6ae3582d5dc6ecfeb590b05e528cd20d6f51581 (patch)
tree0ba9889ff5c8d71cf5dfd6da35d50ec9a6001aae
parentfa8ca1b4737b5e5b6effba3264dda972ebf2f29e (diff)
Improve ABI selection logicHEADdev
The logic regarding extraCMakeArguments, qt.abiPath and multi-ABI builds has been very iffy and potentially confusing for the user. We have had configure commands where the same argument could be defined twice and with different values. Also builds could have had more ABIs selected than the user wanted. The aim is to fix that by improving the parsing of QT_ANDROID_ABIS and QT_ANDROID_BUILD_ALL_ABIS and outputting warnings or errors after bad or useless configurations, including but not limited to: * Setting QT_ANDROID_BUILD_ALL_ABIS=OFF without qt.abiPath, resulting in the first ABI alphabetically, which is probably unwanted * Setting an ABI with QT_ANDROID_ABIS might include another uncontrollable ABI * Setting both qt.abiPath and QT_ANDROID_ABIS may not work as the user intended Fixes: QTTA-407 Fixes: QTTA-350 Change-Id: I09c3fea3b0670a2809bc90e291def7b37933984c Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
-rw-r--r--src/main/groovy/org/qtproject/qt/gradleplugin/QtBuildTask.groovy71
-rw-r--r--src/main/groovy/org/qtproject/qt/gradleplugin/Utils.groovy36
-rw-r--r--src/test/groovy/org/qtproject/qt/gradleplugin/QtBuildTaskTest.groovy2
3 files changed, 78 insertions, 31 deletions
diff --git a/src/main/groovy/org/qtproject/qt/gradleplugin/QtBuildTask.groovy b/src/main/groovy/org/qtproject/qt/gradleplugin/QtBuildTask.groovy
index 53a257e..882cc14 100644
--- a/src/main/groovy/org/qtproject/qt/gradleplugin/QtBuildTask.groovy
+++ b/src/main/groovy/org/qtproject/qt/gradleplugin/QtBuildTask.groovy
@@ -36,6 +36,8 @@ class QtBuildTask extends DefaultTask {
private final String NDK_PATH_CMAKE_ARG = "ANDROID_NDK_ROOT"
private final String SDK_PATH_CMAKE_ARG = "ANDROID_SDK_ROOT"
private final String MAKE_PROGRAM_CMAKE_ARG = "CMAKE_MAKE_PROGRAM"
+ private final String BUILD_ALL_ABIS_ARG = "QT_ANDROID_BUILD_ALL_ABIS"
+ private final String ANDROID_ABIS_ARG = "QT_ANDROID_ABIS"
private String pluginVersion = "NA"
@@ -81,11 +83,8 @@ class QtBuildTask extends DefaultTask {
include: ["${targetName}.aar"]))
}
- // Declared as @Internal so gradle will ignore this path when checking if the task is
- // up-to-date or not.
- @Internal
- def getQtCMakeWrapperPath() {
- def qtCMakeWrapperPath = "$qtKitDir/bin/qt-cmake"
+ def getQtCMakeWrapperPath(File qtAbiPath) {
+ def qtCMakeWrapperPath = "$qtAbiPath/bin/qt-cmake"
if (isWindows())
qtCMakeWrapperPath += '.bat'
return qtCMakeWrapperPath
@@ -204,7 +203,7 @@ class QtBuildTask extends DefaultTask {
// @TODO: QTTA-297 Instead of taking the first ABI found take one that,
// matches with the running device/emulator.
def listAndroidABIsAndSelectFirst() {
- def firstMatch = new File(qtPath).listFiles().find { file ->
+ def firstMatch = new File(qtPath).listFiles().sort().find { file ->
file.name ==~ Utils.androidAbiDirNameFormat()
}
@@ -213,8 +212,18 @@ class QtBuildTask extends DefaultTask {
return new File(firstMatch.path)
}
- def resolveCoreJson() {
- def qtCoreJsonPath = "$qtKitDir/modules/Core.json"
+ def findFirstMatchingAbiDirectory(ArrayList androidABIs) {
+ def firstMatch = new File(qtPath).listFiles().find { file ->
+ file.name in androidABIs
+ }
+
+ if (!firstMatch)
+ Utils.logAndThrowException("No Qt for Android kit from $androidABIs found from: $qtPath")
+ return new File(firstMatch.path)
+ }
+
+ def resolveCoreJson(File qtAbiPath) {
+ def qtCoreJsonPath = "$qtAbiPath/modules/Core.json"
if (!new File(qtCoreJsonPath).exists())
Utils.logAndThrowException("No Core.json file found under $qtCoreJsonPath.")
@@ -250,14 +259,41 @@ class QtBuildTask extends DefaultTask {
}
def configCommand() {
- def buildMultiABI = !qtKitDir
- if (!qtKitDir)
- qtKitDir = listAndroidABIsAndSelectFirst()
- def qtCoreJSon = resolveCoreJson()
+ def buildAllAbis = extractValueFromCMakeArgs(BUILD_ALL_ABIS_ARG)
+ def androidAbis = extractValueFromCMakeArgs(ANDROID_ABIS_ARG)
+ def qtAbiPath = ""
+
+ // if qt.abiPath is set, build a single ABI build and ignore other ABI arguments
+ if (qtKitDir) {
+ qtAbiPath = new File(qtKitDir)
+ for (argument in [BUILD_ALL_ABIS_ARG, ANDROID_ABIS_ARG]) {
+ if (extraCMakeArguments.any { it.contains(argument) } ) {
+ System.err.println("Warning: Setting qt.abiPath means $argument will be ignored.")
+ extraCMakeArguments.removeAll { it.contains(argument) }
+ }
+ }
+
+ } else if (Utils.parseCMakeBoolean(buildAllAbis) == true) {
+ qtAbiPath = listAndroidABIsAndSelectFirst()
+
+ // if QT_ANDROID_ABIS is defined, build a multi-ABI build with those ABIs
+ } else if (androidAbis) {
+ androidAbis = androidAbis.replace("-", "_").split(";").collect{"android_$it".toString()}
+ qtAbiPath = findFirstMatchingAbiDirectory(androidAbis)
+
+ } else if (Utils.parseCMakeBoolean(buildAllAbis) == false) {
+ Utils.logAndThrowException(
+ "Setting $BUILD_ALL_ABIS_ARG=$buildAllAbis without defining $ANDROID_ABIS_ARG " +
+ "or qt.abiPath would result in a single ABI build with the first one in alphabetical " +
+ "order. This is probably not what is wanted."
+ )
- def architecture = getCoreJsonArchitecture(qtCoreJSon)
- def currentABI = Utils.abiFromArchitecture(architecture)
+ // the "standard" all-ABIs build
+ } else {
+ qtAbiPath = listAndroidABIsAndSelectFirst()
+ }
+ def qtCoreJSon = resolveCoreJson(qtAbiPath)
def platform = getCoreJsonPlatform(qtCoreJSon)
if (platform.toString().toLowerCase() != 'android') {
Utils.logAndThrowException(
@@ -265,7 +301,7 @@ class QtBuildTask extends DefaultTask {
"The kit's path: $qtKitDir")
}
- def qtCMakeWrapperPath = getQtCMakeWrapperPath()
+ def qtCMakeWrapperPath = getQtCMakeWrapperPath(qtAbiPath)
def cmd = [
qtCMakeWrapperPath,
@@ -274,10 +310,11 @@ class QtBuildTask extends DefaultTask {
'-G', 'Ninja',
'-DQT_ANDROID_GENERATE_JAVA_QTQUICKVIEW_CONTENTS=ON',
'-DQT_USE_TARGET_ANDROID_BUILD_DIR=ON',
- "-DANDROID_ABI=$currentABI",
- "-DQT_ANDROID_BUILD_ALL_ABIS=${buildMultiABI ? 'ON': 'OFF'}"
]
+ if (!buildAllAbis && !androidAbis && !qtKitDir)
+ cmd += "-DQT_ANDROID_BUILD_ALL_ABIS=ON"
+
def ninjaPathFromExtraCMakeArgs = extractValueFromCMakeArgs(MAKE_PROGRAM_CMAKE_ARG)
if (!ninjaPathFromExtraCMakeArgs)
resolveNinjaPath()
diff --git a/src/main/groovy/org/qtproject/qt/gradleplugin/Utils.groovy b/src/main/groovy/org/qtproject/qt/gradleplugin/Utils.groovy
index d1b2bdb..3eea60f 100644
--- a/src/main/groovy/org/qtproject/qt/gradleplugin/Utils.groovy
+++ b/src/main/groovy/org/qtproject/qt/gradleplugin/Utils.groovy
@@ -8,20 +8,8 @@ import org.gradle.api.Project
@Singleton
class Utils {
- static String ANDROID_ABI_X86_64 = "x86_64"
- static String ANDROID_ABI_X86 = "x86"
-
- static def abis = [
- 'arm64': 'arm64-v8a', 'arm': 'armeabi-v7a',
- 'x86_64': ANDROID_ABI_X86_64, 'i386': ANDROID_ABI_X86
- ]
-
static String androidAbiDirNameFormat() {
- return "android_(arm64_v8a|armv7|$ANDROID_ABI_X86_64|$ANDROID_ABI_X86)"
- }
-
- static String abiFromArchitecture(String architecture) {
- return abis.find { it.key == architecture }?.value
+ return "android_(arm64_v8a|armv7|x86_64|x86)"
}
static def getAndroidBuildTargetName(File buildDir) {
@@ -51,4 +39,26 @@ class Utils {
file = project.rootProject.file(path)
return file
}
+
+ // CMake truthy values are 1, ON, TRUE, Y and non-zero numbers including floats
+ static parseCMakeBoolean(String value) {
+ if (value == "") {
+ return null
+ }
+
+ try {
+ def returnValue = value.toFloat()
+ return returnValue != 0.0
+ }
+ catch(java.lang.NumberFormatException ignored) {
+ // Not a number, proceed to string processing
+ }
+
+ // toBoolean() returns true for "true", "y" or "1" (ignoring the case)
+ // we need to handle the rest
+ return value.toUpperCase()
+ .replace("ON", "TRUE")
+ .replace("YES", "TRUE")
+ .toBoolean()
+ }
}
diff --git a/src/test/groovy/org/qtproject/qt/gradleplugin/QtBuildTaskTest.groovy b/src/test/groovy/org/qtproject/qt/gradleplugin/QtBuildTaskTest.groovy
index 8be3242..a96701a 100644
--- a/src/test/groovy/org/qtproject/qt/gradleplugin/QtBuildTaskTest.groovy
+++ b/src/test/groovy/org/qtproject/qt/gradleplugin/QtBuildTaskTest.groovy
@@ -134,7 +134,7 @@ class QtBuildTaskTest extends Specification {
def mockQtCMakeFile = createQtCMakeFileMock(buildTask)
when: "getQtCMakeWrapperPath is called"
- def cmakeDir = buildTask.getQtCMakeWrapperPath()
+ def cmakeDir = buildTask.getQtCMakeWrapperPath(new File(buildTask.qtKitDir))
then: "qt-cmake file is found at the appropriate path"
assert new File(cmakeDir).exists()