aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaphaël Cotty <raphael.cotty@gmail.com>2020-01-20 15:22:11 +0100
committerRaphaël Cotty <raphael.cotty@gmail.com>2020-02-04 12:40:58 +0000
commite130e492ebe6ad266a74df7b7d5efdef55dc0ff7 (patch)
treed46ae28bb6471b9dca6cccff316ac6842742ff6a
parent7811e70970294ff4c4c1fd76221727696e479cfb (diff)
Android: Update support to qt 5.14 and multi-arch apks for Qt apps
Before qt 5.14. The profile property "moduleProviders.Qt.qmakeFilePaths" is set with one or more paths to the different android architectures. Each qmake path belongs to a different android architecture installation. So each qmake is used to generated the Qt module corresponding to the architecture. Although qbs can generate multi-arch apks using multiplex mode, this is not possible when the project depends on qt libraries. This is because of the restriction of the qt tool androiddeployqt used by qbs. Now with qt 5.14. All android architectures are installed in the same directory. So the profile property "moduleProviders.Qt.qmakeFilePaths" is set with one qmake path. This directly impacts the qbs-setup-android tool and the generation of the Qt modules. Because qt libraries are installed in the the same directory, they have the abi in their name (libQt5Core_armeabi-v7a.so). So the rules that generate the apks are also impacted. The new androiddeployqt have a new interface (json config file format and requires to have the input libraries installed in the deployment directory) which allows the generation of multi-arch apks. So Qt.android_support modules needs to be updated as well. Fixes: QBS-1497 Change-Id: Ibd546f356c38a05f42dfcac0a4ec92bd82d6f700 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--.travis.yml4
-rwxr-xr-xscripts/test-qt-for-android.sh23
-rw-r--r--share/qbs/imports/qbs/base/QtApplication.qbs4
-rw-r--r--share/qbs/imports/qbs/base/QtGuiApplication.qbs2
-rw-r--r--share/qbs/module-providers/Qt/setup-qt.js225
-rw-r--r--share/qbs/module-providers/Qt/templates/android_support.qbs150
-rw-r--r--share/qbs/modules/Android/sdk/sdk.qbs29
-rw-r--r--share/qbs/modules/Android/sdk/utils.js7
-rw-r--r--share/qbs/modules/cpp/android-gcc.qbs2
-rw-r--r--src/app/qbs-setup-android/android-setup.cpp37
-rw-r--r--tests/auto/blackbox/find/find-android.qbs49
-rw-r--r--tests/auto/blackbox/tst_blackboxandroid.cpp253
-rw-r--r--tests/auto/blackbox/tst_blackboxbase.cpp18
-rw-r--r--tests/auto/blackbox/tst_blackboxbase.h1
14 files changed, 610 insertions, 194 deletions
diff --git a/.travis.yml b/.travis.yml
index d941d4cff..1853fbe0c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -60,13 +60,15 @@ jobs:
- docker-compose run --rm bionic scripts/run-analyzer.sh
- <<: *build-on-bionic
- name: With Qbs and with Qt for Android (Qt 5.13)
+ name: With Qbs and with Qt for Android
before_install:
- docker-compose pull bionic
- docker-compose pull bionic-android-513
+ - docker-compose pull bionic-android-514
script:
- docker-compose run bionic qbs build modules.cpp.compilerWrapper:ccache modules.qbsbuildconfig.enableBundledQt:true config:release
- docker-compose run bionic-android-513 scripts/test-qt-for-android.sh release/install-root/usr/local/bin
+ - docker-compose run bionic-android-514 scripts/test-qt-for-android.sh release/install-root/usr/local/bin
- &build-on-macos
stage: Build Qbs and and run autotests
diff --git a/scripts/test-qt-for-android.sh b/scripts/test-qt-for-android.sh
index 877afbc30..44a64db21 100755
--- a/scripts/test-qt-for-android.sh
+++ b/scripts/test-qt-for-android.sh
@@ -50,16 +50,33 @@ echo "Android SDK installed at ${ANDROID_SDK_ROOT}"
echo "Android NDK installed at ${ANDROID_NDK_ROOT}"
echo "Qt installed at ${QT_INSTALL_DIR}"
+# Cleaning profiles
+qbs config --unset profiles.qbs_autotests-android
+qbs config --unset profiles.qbs_autotests-android-qt
+
+# Setting auto test profiles
qbs setup-android --ndk-dir ${ANDROID_HOME}/ndk-bundle --sdk-dir ${ANDROID_HOME} qbs_autotests-android
qbs setup-android --ndk-dir ${ANDROID_HOME}/ndk-bundle --sdk-dir ${ANDROID_HOME} --qt-dir ${QT_INSTALL_DIR} qbs_autotests-android-qt
+export QBS_AUTOTEST_PROFILE=qbs_autotests-android
+export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
+
+if [ ! "${QT_VERSION}" \< "5.14.0" ]; then
+ echo "Using multi-arch data sets for qml tests (only for qt version >= 5.14) with all architectures"
+ qbs config --list
+ tst_blackbox-android
+
+ echo "Using multi-arch data sets for qml tests (only for qt version >= 5.14) with only armv7a and x86_64"
+ qbs config profiles.qbs_autotests-android-qt.qbs.architectures '["armv7a","x86_64"]'
+ qbs config --list
+ tst_blackbox-android
+fi;
+
+echo "Using single-arch (armv7a) data sets for qml tests"
qbs config --unset profiles.qbs_autotests-android-qt.qbs.architectures
qbs config profiles.qbs_autotests-android-qt.qbs.architecture armv7a
qbs config --list
-export QBS_AUTOTEST_PROFILE=qbs_autotests-android
-export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
-
tst_blackbox-android
diff --git a/share/qbs/imports/qbs/base/QtApplication.qbs b/share/qbs/imports/qbs/base/QtApplication.qbs
index 32800d294..b6776dca0 100644
--- a/share/qbs/imports/qbs/base/QtApplication.qbs
+++ b/share/qbs/imports/qbs/base/QtApplication.qbs
@@ -30,4 +30,8 @@
CppApplication {
Depends { name: "Qt.core" }
+ Properties {
+ condition: isForAndroid && Qt.android_support._multiAbi
+ targetName: name + "_" + Android.ndk.abi
+ }
}
diff --git a/share/qbs/imports/qbs/base/QtGuiApplication.qbs b/share/qbs/imports/qbs/base/QtGuiApplication.qbs
index 61bc69752..7b2abf017 100644
--- a/share/qbs/imports/qbs/base/QtGuiApplication.qbs
+++ b/share/qbs/imports/qbs/base/QtGuiApplication.qbs
@@ -28,6 +28,6 @@
**
****************************************************************************/
-CppApplication {
+QtApplication {
Depends { name: "Qt.gui" }
}
diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js
index 3ddc214d3..6960b15d9 100644
--- a/share/qbs/module-providers/Qt/setup-qt.js
+++ b/share/qbs/module-providers/Qt/setup-qt.js
@@ -219,6 +219,19 @@ function fillEntryPointLibs(qtProps, debug) {
return result;
}
+function abiToArchitecture(abi) {
+ switch (abi) {
+ case "armeabi-v7a":
+ return "armv7a";
+ case "arm64-v8a":
+ return "arm64";
+ case "x86":
+ case "x86_64":
+ default:
+ return abi;
+ }
+}
+
function getQtProperties(qmakeFilePath, qbs) {
var queryResult = queryQmake(qmakeFilePath);
var qtProps = {};
@@ -297,6 +310,14 @@ function getQtProperties(qmakeFilePath, qbs) {
if (!File.exists(qtProps.mkspecPath))
throw "mkspec '" + toNative(qtProps.mkspecPath) + "' does not exist";
+ // Starting with qt 5.14, android sdk provides multi-abi
+ if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0
+ && qtProps.mkspecPath.contains("android")) {
+ var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath,
+ "qdevice.pri"));
+ qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' ');
+ }
+
// determine MSVC version
if (isMsvcQt(qtProps)) {
var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION");
@@ -591,7 +612,7 @@ function replaceQtLibNamesWithFilePath(modules, qtProps) {
}
}
-function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles) {
+function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, androidAbi) {
if (!modInfo.hasLibrary)
return; // Can happen for Qt4 convenience modules, like "widgets".
@@ -644,7 +665,15 @@ function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles) {
&& !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5;
if (isNonStaticQt4OnWindows)
prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number...
+ if (androidAbi.length > 0
+ && modInfo.name !== "QtBootstrap"
+ && modInfo.name !== "QtQmlDevTools") {
+ prlFilePath += "_";
+ prlFilePath += androidAbi;
+ }
+
prlFilePath += ".prl";
+
try {
var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly);
while (!prlFile.atEof()) {
@@ -738,9 +767,9 @@ function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles) {
modInfo.libFilePathRelease = libFilePath;
}
-function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles) {
- doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles);
- doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles);
+function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles, androidAbi) {
+ doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi);
+ doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi);
}
function allQt4Modules(qtProps) {
@@ -862,7 +891,7 @@ function allQt4Modules(qtProps) {
module.mustExist = false;
if (qtProps.staticBuild)
module.isStaticLibrary = true;
- setupLibraries(module, qtProps, nonExistingPrlFiles);
+ setupLibraries(module, qtProps, nonExistingPrlFiles, "");
}
replaceQtLibNamesWithFilePath(modules, qtProps);
@@ -1003,7 +1032,7 @@ function removeDuplicatedDependencyLibs(modules) {
traverse(rootModules[i], []);
}
-function allQt5Modules(qtProps) {
+function allQt5Modules(qtProps, androidAbi) {
var nonExistingPrlFiles = [];
var modules = [];
var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules");
@@ -1120,7 +1149,7 @@ function allQt5Modules(qtProps) {
}
}
- setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles);
+ setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles, androidAbi);
modules.push(moduleInfo);
if (moduleInfo.qbsName === "testlib")
@@ -1144,7 +1173,6 @@ function extractQbsArchs(module, qtProps) {
var qbsArch = Utilities.canonicalArchitecture(qtProps.architecture);
if (qbsArch === "arm" && qtProps.mkspecPath.contains("android"))
qbsArch = "armv7a";
-
// Qt4 has "QT_ARCH = windows" in qconfig.pri for both MSVC and mingw.
if (qbsArch === "windows")
return []
@@ -1286,9 +1314,14 @@ function minVersionJsString(minVersion) {
return !minVersion ? "original" : toJSLiteral(minVersion);
}
-function replaceSpecialValues(content, module, qtProps) {
+function replaceSpecialValues(content, module, qtProps, abi) {
+ var architectures = [];
+ if (abi.length > 0)
+ architectures.push(abiToArchitecture(abi));
+ else
+ architectures = extractQbsArchs(module, qtProps);
var dict = {
- archs: toJSLiteral(extractQbsArchs(module, qtProps)),
+ archs: toJSLiteral(architectures),
targetPlatform: toJSLiteral(qbsTargetPlatformFromQtMkspec(qtProps)),
config: toJSLiteral(qtProps.configItems),
qtConfig: toJSLiteral(qtProps.qtConfigItems),
@@ -1399,8 +1432,8 @@ function replaceSpecialValues(content, module, qtProps) {
return content;
}
-function copyTemplateFile(fileName, targetDirectory, qtProps, location, allFiles, module, pluginMap,
- nonEssentialPlugins)
+function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, allFiles, module,
+ pluginMap, nonEssentialPlugins)
{
if (!File.makePath(targetDirectory)) {
throw "Cannot create directory '" + toNative(targetDirectory) + "'.";
@@ -1409,12 +1442,13 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, location, allFiles
TextFile.ReadOnly);
var newContent = sourceFile.readAll();
if (module) {
- newContent = replaceSpecialValues(newContent, module, qtProps);
+ newContent = replaceSpecialValues(newContent, module, qtProps, abi);
} else {
newContent = newContent.replace("@allPluginsByType@",
'(' + toJSLiteral(pluginMap) + ')');
newContent = newContent.replace("@nonEssentialPlugins@",
toJSLiteral(nonEssentialPlugins));
+ newContent = newContent.replace("@version@", toJSLiteral(qtProps.qtVersion));
}
sourceFile.close();
var targetPath = FileInfo.joinPaths(targetDirectory, fileName);
@@ -1428,74 +1462,102 @@ function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) {
if (!File.exists(qmakeFilePath))
throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist.";
var qtProps = getQtProperties(qmakeFilePath, qbs);
- var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : allQt5Modules(qtProps);
- var pluginsByType = {};
- var nonEssentialPlugins = [];
- for (var i = 0; i < modules.length; ++i) {
- var m = modules[i];
- if (m.isPlugin) {
- if (!pluginsByType[m.pluginData.type])
- pluginsByType[m.pluginData.type] = [];
- pluginsByType[m.pluginData.type].push(m.name);
- if (!m.pluginData.autoLoad)
- nonEssentialPlugins.push(m.name);
+ var androidAbis = [];
+ if (qtProps.androidAbis !== undefined)
+ // Multiple androidAbis detected: Qt >= 5.14
+ androidAbis = qtProps.androidAbis;
+ else
+ // Single abi detected: Qt < 5.14
+ androidAbis.push('');
+ if (androidAbis.length > 1)
+ console.info("Qt with multiple abi detected: '" + androidAbis + "'");
+
+ var relativeSearchPaths = [];
+ for (a = 0; a < androidAbis.length; ++a) {
+ if (androidAbis.length > 1)
+ console.info("Configuring abi '" + androidAbis[a] + "'...");
+ var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) :
+ allQt5Modules(qtProps,androidAbis[a]);
+ var pluginsByType = {};
+ var nonEssentialPlugins = [];
+ for (var i = 0; i < modules.length; ++i) {
+ var m = modules[i];
+ if (m.isPlugin) {
+ if (!pluginsByType[m.pluginData.type])
+ pluginsByType[m.pluginData.type] = [];
+ pluginsByType[m.pluginData.type].push(m.name);
+ if (!m.pluginData.autoLoad)
+ nonEssentialPlugins.push(m.name);
+ }
}
- }
-
- var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : "";
- var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt");
- if (File.exists(qbsQtModuleBaseDir))
- File.remove(qbsQtModuleBaseDir);
- var allFiles = [];
- copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles);
- copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles);
- copyTemplateFile("plugin_support.qbs", FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"),
- qtProps, location, allFiles, undefined, pluginsByType, nonEssentialPlugins);
+ var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : "";
+ relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbis[a]);
+ var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath,
+ "modules", "Qt");
+ if (File.exists(qbsQtModuleBaseDir))
+ File.remove(qbsQtModuleBaseDir);
+
+ var allFiles = [];
+ copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location,
+ allFiles);
+ copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location,
+ allFiles);
+ copyTemplateFile("plugin_support.qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps,
+ androidAbis[a], location, allFiles, undefined, pluginsByType,
+ nonEssentialPlugins);
- for (i = 0; i < modules.length; ++i) {
- var module = modules[i];
- var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName);
- var moduleTemplateFileName;
- if (module.qbsName === "core") {
- moduleTemplateFileName = "core.qbs";
- copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, location, allFiles);
- copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, location, allFiles);
- } else if (module.qbsName === "gui") {
- moduleTemplateFileName = "gui.qbs";
- } else if (module.qbsName === "scxml") {
- moduleTemplateFileName = "scxml.qbs";
- } else if (module.qbsName === "dbus") {
- moduleTemplateFileName = "dbus.qbs";
- copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, location, allFiles);
- } else if (module.qbsName === "qml") {
- moduleTemplateFileName = "qml.qbs";
- copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, location, allFiles);
- var qmlcacheStr = "qmlcache";
- if (File.exists(FileInfo.joinPaths(qtProps.binaryPath,
- "qmlcachegen" + exeSuffix(qbs)))) {
- copyTemplateFile(qmlcacheStr + ".qbs",
- FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps,
- location, allFiles);
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName);
+ var moduleTemplateFileName;
+ if (module.qbsName === "core") {
+ moduleTemplateFileName = "core.qbs";
+ copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbis[a], location,
+ allFiles);
+ copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbis[a], location,
+ allFiles);
+ } else if (module.qbsName === "gui") {
+ moduleTemplateFileName = "gui.qbs";
+ } else if (module.qbsName === "scxml") {
+ moduleTemplateFileName = "scxml.qbs";
+ } else if (module.qbsName === "dbus") {
+ moduleTemplateFileName = "dbus.qbs";
+ copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbis[a], location,
+ allFiles);
+ } else if (module.qbsName === "qml") {
+ moduleTemplateFileName = "qml.qbs";
+ copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbis[a], location,
+ allFiles);
+ var qmlcacheStr = "qmlcache";
+ if (File.exists(FileInfo.joinPaths(qtProps.binaryPath,
+ "qmlcachegen" + exeSuffix(qbs)))) {
+ copyTemplateFile(qmlcacheStr + ".qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps,
+ androidAbis[a], location, allFiles);
+ }
+ } else if (module.qbsName === "quick") {
+ moduleTemplateFileName = "quick.qbs";
+ copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbis[a], location,
+ allFiles);
+ } else if (module.isPlugin) {
+ moduleTemplateFileName = "plugin.qbs";
+ } else {
+ moduleTemplateFileName = "module.qbs";
}
- } else if (module.qbsName === "quick") {
- moduleTemplateFileName = "quick.qbs";
- copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, location, allFiles);
- } else if (module.isPlugin) {
- moduleTemplateFileName = "plugin.qbs";
- } else {
- moduleTemplateFileName = "module.qbs";
+ copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbis[a],
+ location, allFiles, module);
}
- copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, location, allFiles,
- module);
- }
- // Note that it's not strictly necessary to copy this one, as it has no variable content.
- // But we do it anyway for consistency.
- copyTemplateFile("android_support.qbs",
- FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"),
- qtProps, location, allFiles);
- return relativeSearchPath;
+ // Note that it's not strictly necessary to copy this one, as it has no variable content.
+ // But we do it anyway for consistency.
+ copyTemplateFile("android_support.qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"),
+ qtProps, androidAbis[a], location, allFiles);
+ relativeSearchPaths.push(relativeSearchPath)
+ }
+ return relativeSearchPaths;
}
function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) {
@@ -1503,19 +1565,20 @@ function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) {
if (!qmakeFilePaths || qmakeFilePaths.length === 0)
return [];
var uniquifySearchPath = qmakeFilePaths.length > 1;
- var searchPaths = [];
+ var allSearchPaths = [];
for (var i = 0; i < qmakeFilePaths.length; ++i) {
try {
console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'...");
- var searchPath = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath,
- location, qbs);
- if (searchPath !== undefined) {
- searchPaths.push(searchPath);
+ var searchPaths = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath,
+ location, qbs);
+ if (searchPaths.length > 0) {
+ for (var j = 0; j < searchPaths.length; ++j )
+ allSearchPaths.push(searchPaths[j]);
console.info("Qt was set up successfully.");
}
} catch (e) {
console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e);
}
}
- return searchPaths;
+ return allSearchPaths;
}
diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs
index c5f842a1f..342018321 100644
--- a/share/qbs/module-providers/Qt/templates/android_support.qbs
+++ b/share/qbs/module-providers/Qt/templates/android_support.qbs
@@ -3,8 +3,10 @@ import qbs.FileInfo
import qbs.ModUtils
import qbs.TextFile
import qbs.Utilities
+import qbs.Process
Module {
+ version: @version@
property bool useMinistro: false
property string qmlRootDir: product.sourceDirectory
property stringList extraPrefixDirs
@@ -22,6 +24,8 @@ Module {
property string _templatesBaseDir: FileInfo.joinPaths(_qtInstallDir, "src", "android")
property string _deployQtOutDir: FileInfo.joinPaths(product.buildDirectory, "deployqt_out")
+ property bool _multiAbi: Utilities.versionCompare(version, "5.14") >= 0
+
Depends { name: "Android.sdk"; condition: _enableSdkSupport }
Depends { name: "Android.ndk"; condition: _enableNdkSupport }
Depends { name: "java"; condition: _enableSdkSupport }
@@ -44,6 +48,11 @@ Module {
condition: _enableNdkSupport && (Android.ndk.abi === "armeabi-v7a" || Android.ndk.abi === "x86")
cpp.defines: "ANDROID_HAS_WSTRING"
}
+ Properties {
+ condition: _enableSdkSupport
+ Android.sdk._archInName: _multiAbi
+ Android.sdk._bundledInAssets: _multiAbi
+ }
Rule {
condition: _enableSdkSupport
@@ -61,30 +70,55 @@ Module {
cmd.sourceCode = function() {
var theBinary;
var nativeLibs = inputs["android.nativelibrary"];
+ var architectures = [];
+ var triples = [];
+ var hostArch;
+ var targetArchitecture;
if (nativeLibs.length === 1) {
theBinary = nativeLibs[0];
+ hostArch = theBinary.Android.ndk.hostArch;
+ targetArchitecture = theBinary.Android.ndk.abi;
+ if (product.Qt.android_support._multiAbi) {
+ architectures.push(theBinary.Android.ndk.abi);
+ triples.push(theBinary.cpp.toolchainTriple);
+ }
} else {
for (i = 0; i < nativeLibs.length; ++i) {
var candidate = nativeLibs[i];
- if (!candidate.fileName.contains(candidate.product.targetName))
- continue;
- if (!theBinary) {
- theBinary = candidate;
- continue;
- }
- if (theBinary.product.name === product.name
- && candidate.product.name !== product.name) {
- continue; // We already have a better match.
- }
- if (candidate.product.name === product.name
- && theBinary.product.name !== product.name) {
- theBinary = candidate; // The new candidate is a better match.
- continue;
+ if (product.Qt.android_support._multiAbi) {
+ if (candidate.product.name === product.name) {
+ architectures.push(candidate.Android.ndk.abi);
+ triples.push(candidate.cpp.toolchainTriple);
+ hostArch = candidate.Android.ndk.hostArch;
+ targetArchitecture = candidate.Android.ndk.abi;
+ theBinary = candidate;
+ }
+ } else {
+ if (!candidate.fileName.contains(candidate.product.targetName))
+ continue;
+ if (!theBinary) {
+ theBinary = candidate;
+ hostArch = theBinary.Android.ndk.hostArch;
+ targetArchitecture = theBinary.Android.ndk.abi;
+ continue;
+ }
+ if (theBinary.product.name === product.name
+ && candidate.product.name !== product.name) {
+ continue; // We already have a better match.
+ }
+ if (candidate.product.name === product.name
+ && theBinary.product.name !== product.name) {
+ theBinary = candidate; // The new candidate is a better match.
+ hostArch = theBinary.Android.ndk.hostArch;
+ targetArchitecture = theBinary.Android.ndk.abi;
+ continue;
+ }
+
+ throw "Qt applications for Android support only one native binary "
+ + "per package.\n"
+ + "In particular, you cannot build a Qt app for more than "
+ + "one architecture at the same time.";
}
- throw "Qt applications for Android support only one native binary "
- + "per package.\n"
- + "In particular, you cannot build a Qt app for more than "
- + "one architecture at the same time.";
}
}
var f = new TextFile(output.filePath, TextFile.WriteOnly);
@@ -99,8 +133,20 @@ Module {
f.writeLine('"toolchain-prefix": "llvm",');
f.writeLine('"tool-prefix": "llvm",');
f.writeLine('"useLLVM": true,');
- f.writeLine('"ndk-host": "' + theBinary.Android.ndk.hostArch + '",');
- f.writeLine('"target-architecture": "' + theBinary.Android.ndk.abi + '",');
+ f.writeLine('"ndk-host": "' + hostArch + '",');
+ if (!product.Qt.android_support._multiAbi) {
+ f.writeLine('"target-architecture": "' + targetArchitecture + '",');
+ }
+ else {
+ var line = '"architectures": {';
+ for (var i in architectures) {
+ line = line + '"' + architectures[i] + '":"' + triples[i] + '"';
+ if (i < architectures.length-1)
+ line = line + ',';
+ }
+ line = line + "},";
+ f.writeLine(line);
+ }
f.writeLine('"qml-root-path": "' + product.Qt.android_support.qmlRootDir + '",');
var deploymentDeps = product.Qt.android_support.deploymentDependencies;
if (deploymentDeps && deploymentDeps.length > 0)
@@ -124,11 +170,16 @@ Module {
f.writeLine('"qml-import-paths": "' + product.qmlImportPaths.join(',') + '",');
// QBS-1429
- f.writeLine('"stdcpp-path": "' + (product.cpp.sharedStlFilePath
+ if (!product.Qt.android_support._multiAbi) {
+ f.writeLine('"stdcpp-path": "' + (product.cpp.sharedStlFilePath
? product.cpp.sharedStlFilePath : product.cpp.staticStlFilePath)
+ '",');
-
- f.writeLine('"application-binary": "' + theBinary.filePath + '"');
+ f.writeLine('"application-binary": "' + theBinary.filePath + '"');
+ } else {
+ f.writeLine('"stdcpp-path": "' + product.Android.sdk.ndkDir +
+ '/toolchains/llvm/prebuilt/' + hostArch + '/sysroot/usr/lib/",');
+ f.writeLine('"application-binary": "' + theBinary.product.name + '"');
+ }
f.writeLine("}");
f.close();
};
@@ -167,7 +218,12 @@ Module {
Rule {
condition: _enableSdkSupport
multiplex: true
- inputs: ["qt_androiddeployqt_input", "android.manifest_processed"]
+ property stringList defaultInputs: ["qt_androiddeployqt_input",
+ "android.manifest_processed"]
+ property stringList allInputs: ["qt_androiddeployqt_input", "android.manifest_processed",
+ "android.nativelibrary"]
+ inputsFromDependencies: "android.nativelibrary"
+ inputs: product.aggregate ? defaultInputs : allInputs
outputFileTags: [
"android.manifest_final", "android.resources", "android.assets", "bundled_jar",
"android.deployqt_list",
@@ -216,9 +272,21 @@ Module {
File.copy(product.Qt.android_support._templatesBaseDir
+ "/templates/res/values/libs.xml",
product.Qt.android_support._deployQtOutDir + "/res/values/libs.xml");
- try {
- File.remove(FileInfo.path(outputs["android.assets"][0].filePath));
- } catch (e) {
+ if (!product.Qt.android_support._multiAbi) {
+ try {
+ File.remove(FileInfo.path(outputs["android.assets"][0].filePath));
+ } catch (e) {
+ }
+ }
+ else {
+ for (var i in inputs["android.nativelibrary"]) {
+ var input = inputs["android.nativelibrary"][i];
+ File.copy(input.filePath,
+ FileInfo.joinPaths(product.Qt.android_support._deployQtOutDir,
+ "libs",
+ input.Android.ndk.abi,
+ input.fileName));
+ }
}
};
var androidDeployQtArgs = [
@@ -228,7 +296,7 @@ Module {
"--android-platform", product.Android.sdk.platform,
];
if (product.Qt.android_support.verboseAndroidDeployQt)
- args.push("--verbose");
+ androidDeployQtArgs.push("--verbose");
var androidDeployQtCmd = new Command(
product.Qt.android_support._androidDeployQtFilePath, androidDeployQtArgs);
androidDeployQtCmd.description = "running androiddeployqt";
@@ -286,7 +354,31 @@ Module {
File.remove(oldLibs[i]);
}
};
- return [copyCmd, androidDeployQtCmd, moveCmd];
+
+ // androiddeployqt doesn't strip the deployed libraries anymore so it has to done here
+ var stripLibsCmd = new JavaScriptCommand();
+ stripLibsCmd.description = "Stripping unneeded symbols from deployed qt libraries";
+ stripLibsCmd.sourceCode = function() {
+ var stripArgs = ["--strip-all"];
+ var architectures = [];
+ for (var i in inputs["android.nativelibrary"])
+ architectures.push(inputs["android.nativelibrary"][i].Android.ndk.abi);
+ for (var i in architectures) {
+ var abiDirPath = FileInfo.joinPaths(product.Android.sdk.apkContentsDir,
+ "lib", architectures[i]);
+ var files = File.directoryEntries(abiDirPath, File.Files);
+ for (var i = 0; i < files.length; ++i) {
+ var filePath = FileInfo.joinPaths(abiDirPath, files[i]);
+ if (FileInfo.suffix(filePath) == "so") {
+ stripArgs.push(filePath);
+ }
+ }
+ }
+ var process = new Process();
+ process.exec(product.cpp.stripPath, stripArgs, false);
+ }
+
+ return [copyCmd, androidDeployQtCmd, moveCmd, stripLibsCmd];
}
}
diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs
index f8a046c3a..ecf64a188 100644
--- a/share/qbs/modules/Android/sdk/sdk.qbs
+++ b/share/qbs/modules/Android/sdk/sdk.qbs
@@ -84,6 +84,9 @@ Module {
property bool _enableRules: !product.multiplexConfigurationId && !!packageName
+ property bool _archInName: false
+ property bool _bundledInAssets: true
+
Group {
name: "java sources"
condition: Android.sdk.automaticSources
@@ -277,6 +280,30 @@ Module {
rootElem.setAttribute("android:versionCode", product.Android.sdk.versionCode);
if (product.Android.sdk.versionName !== undefined)
rootElem.setAttribute("android:versionName", product.Android.sdk.versionName);
+
+ if (product.Android.sdk._bundledInAssets) {
+ // Remove <meta-data android:name="android.app.bundled_in_assets_resource_id"
+ // android:resource="@array/bundled_in_assets"/>
+ // custom AndroidManifest.xml because assets are in rcc files for qt >= 5.14
+ var appElem = rootElem.firstChild("application");
+ if (!appElem || !appElem.isElement() || appElem.tagName() != "application")
+ throw "No application tag found in '" + input.filePath + "'.";
+ var activityElem = appElem.firstChild("activity");
+ if (!activityElem || !activityElem.isElement() ||
+ activityElem.tagName() != "activity")
+ throw "No activity tag found in '" + input.filePath + "'.";
+ var metaDataElem = activityElem.firstChild("meta-data");
+ while (metaDataElem && metaDataElem.isElement()) {
+ if (SdkUtils.elementHasBundledAttributes(metaDataElem)) {
+ var elemToRemove = metaDataElem;
+ metaDataElem = metaDataElem.nextSibling("meta-data");
+ activityElem.removeChild(elemToRemove);
+ } else {
+ metaDataElem = metaDataElem.nextSibling("meta-data");
+ }
+ }
+ }
+
manifestData.save(output.filePath, 4);
}
return cmd;
@@ -415,7 +442,7 @@ Module {
var deploymentData = SdkUtils.gdbserverOrStlDeploymentData(product, inputs);
for (var i = 0; i < deploymentData.uniqueInputs.length; ++i) {
var input = deploymentData.uniqueInputs[i];
- var stripArgs = ["--strip-unneeded", "-o", deploymentData.outputFilePaths[i],
+ var stripArgs = ["--strip-all", "-o", deploymentData.outputFilePaths[i],
input.filePath];
var cmd = new Command(input.cpp.stripPath, stripArgs);
cmd.description = "deploying " + input.fileName;
diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js
index 90c8bc1bf..6d3837d57 100644
--- a/share/qbs/modules/Android/sdk/utils.js
+++ b/share/qbs/modules/Android/sdk/utils.js
@@ -222,3 +222,10 @@ function gdbserverOrStlDeploymentData(product, inputs, type)
}
return data;
}
+
+function elementHasBundledAttributes(element)
+{
+ return element.hasAttribute("android:name") &&
+ (element.attribute("android:name") === "android.app.bundled_in_assets_resource_id") ||
+ (element.attribute("android:name") === "android.app.bundled_in_lib_resource_id");
+}
diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs
index 3e44f4ff3..10190308a 100644
--- a/share/qbs/modules/cpp/android-gcc.qbs
+++ b/share/qbs/modules/cpp/android-gcc.qbs
@@ -182,7 +182,7 @@ LinuxGCC {
fileTags: "android.nativelibrary"
}
prepare: {
- var stripArgs = ["--strip-unneeded", "-o", output.filePath, input.filePath];
+ var stripArgs = ["--strip-all", "-o", output.filePath, input.filePath];
var stripCmd = new Command(product.cpp.stripPath, stripArgs);
stripCmd.description = "Stripping unneeded symbols from " + input.fileName;
return stripCmd;
diff --git a/src/app/qbs-setup-android/android-setup.cpp b/src/app/qbs-setup-android/android-setup.cpp
index 029628419..a0a9d2948 100644
--- a/src/app/qbs-setup-android/android-setup.cpp
+++ b/src/app/qbs-setup-android/android-setup.cpp
@@ -45,6 +45,7 @@
#include <tools/profile.h>
#include <tools/settings.h>
#include <tools/version.h>
+#include <tools/qttools.h>
#include <QtCore/qbytearraylist.h>
#include <QtCore/qcoreapplication.h>
@@ -94,10 +95,10 @@ static QString mapArch(const QString &androidName)
}
struct QtAndroidInfo {
- bool isValid() const { return !arch.isEmpty(); }
+ bool isValid() const { return !archs.isEmpty(); }
QString qmakePath;
- QString arch;
+ QStringList archs;
QString platform;
};
@@ -111,19 +112,30 @@ static QtAndroidInfo getInfoForQtDir(const QString &qtDir)
if (!qdevicepri.open(QIODevice::ReadOnly))
return info;
while (!qdevicepri.atEnd()) {
+ // For Qt < 5.14 use DEFAULT_ANDROID_TARGET_ARCH (which is the abi) to compute
+ // the architecture
+ // DEFAULT_ANDROID_ABIS doesn't exit
+ // For Qt >= 5.14:
+ // DEFAULT_ANDROID_TARGET_ARCH doesn't exist, use DEFAULT_ANDROID_ABIS to compute
+ // the architectures
const QByteArray line = qdevicepri.readLine().simplified();
const bool isArchLine = line.startsWith("DEFAULT_ANDROID_TARGET_ARCH");
+ const bool isAbisLine = line.startsWith("DEFAULT_ANDROID_ABIS");
const bool isPlatformLine = line.startsWith("DEFAULT_ANDROID_PLATFORM");
- if (!isArchLine && !isPlatformLine)
+ if (!isArchLine && !isPlatformLine && !isAbisLine)
continue;
const QList<QByteArray> elems = line.split('=');
if (elems.size() != 2)
continue;
const QString rhs = QString::fromLatin1(elems.at(1).trimmed());
- if (isArchLine)
- info.arch = mapArch(rhs);
- else
+ if (isArchLine) {
+ info.archs << mapArch(rhs);
+ } else if (isAbisLine) {
+ for (const QString &abi: rhs.split(QLatin1Char(' ')))
+ info.archs << mapArch(abi);
+ } else {
info.platform = rhs;
+ }
}
return info;
}
@@ -136,13 +148,16 @@ static QtInfoPerArch getQtAndroidInfo(const QString &qtSdkDir)
return archs;
QStringList qtDirs(qtSdkDir);
- QDirIterator dit(qtSdkDir, QStringList() << QStringLiteral("android_*"), QDir::Dirs);
+ const QStringList nameFilters{QStringLiteral("android_*"), QStringLiteral("android")};
+ QDirIterator dit(qtSdkDir, nameFilters, QDir::Dirs);
while (dit.hasNext())
qtDirs << dit.next();
for (const auto &qtDir : qtDirs) {
const QtAndroidInfo info = getInfoForQtDir(qtDir);
- if (info.isValid())
- archs.insert(info.arch, info);
+ if (info.isValid()) {
+ for (const QString &arch: info.archs)
+ archs.insert(arch, info);
+ }
}
return archs;
}
@@ -224,8 +239,10 @@ static void setupNdk(qbs::Settings *settings, const QString &profileName, const
qmakeFilePaths << qtAndroidInfo.qmakePath;
platform = maximumPlatform(platform, qtAndroidInfo.platform);
}
- if (!qmakeFilePaths.empty())
+ if (!qmakeFilePaths.empty()) {
+ qmakeFilePaths.removeDuplicates();
mainProfile.setValue(qls("moduleProviders.Qt.qmakeFilePaths"), qmakeFilePaths);
+ }
if (!platform.isEmpty())
mainProfile.setValue(qls("Android.ndk.platform"), platform);
}
diff --git a/tests/auto/blackbox/find/find-android.qbs b/tests/auto/blackbox/find/find-android.qbs
index 26dedc60f..de5c78d10 100644
--- a/tests/auto/blackbox/find/find-android.qbs
+++ b/tests/auto/blackbox/find/find-android.qbs
@@ -3,12 +3,23 @@ import qbs.TextFile
Product {
property string packageName: ""
qbs.targetPlatform: "android"
+ multiplexByQbsProperties: ["architectures"]
+
+ Properties {
+ condition: qbs.architectures && qbs.architectures.length > 1
+ aggregate: true
+ multiplexedType: "json_arch"
+ }
Depends { name: "Android.sdk"; required: false }
Depends { name: "Android.ndk"; required: false }
type: ["json"]
+
Rule {
multiplex: true
+ property stringList inputTags: "json_arch"
+ inputsFromDependencies: inputTags
+ inputs: product.aggregate ? [] : inputTags
Artifact {
filePath: ["android.json"]
fileTags: ["json"]
@@ -18,17 +29,50 @@ Product {
cmd.description = output.filePath;
cmd.sourceCode = function() {
var tools = {};
+
+ for (var i in inputs["json_arch"]) {
+ var tf = new TextFile(inputs["json_arch"][i].filePath, TextFile.ReadOnly);
+ var json = JSON.parse(tf.readAll());
+ tools["ndk"] = json["ndk"];
+ tools["ndk-samples"] = json["ndk-samples"];
+ tf.close();
+ }
+
if (product.moduleProperty("Android.sdk", "present")) {
tools["sdk"] = product.moduleProperty("Android.sdk", "sdkDir");
tools["sdk-build-tools-dx"] = product.Android.sdk.dxFilePath;
}
+ if (product.java && product.java.present)
+ tools["jar"] = product.java.jarFilePath;
+
+ var tf;
+ try {
+ tf = new TextFile(output.filePath, TextFile.WriteOnly);
+ tf.writeLine(JSON.stringify(tools, undefined, 4));
+ } finally {
+ if (tf)
+ tf.close();
+ }
+ };
+ return cmd;
+ }
+ }
+ Rule {
+ multiplex: true
+ Artifact {
+ filePath: ["android_arch.json"]
+ fileTags: ["json_arch"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = output.filePath;
+ cmd.sourceCode = function() {
+ var tools = {};
if (product.moduleProperty("Android.ndk", "present")) {
tools["ndk"] = product.moduleProperty("Android.ndk", "ndkDir");
tools["ndk-samples"] = product.Android.ndk.ndkSamplesDir;
}
- if (product.java && product.java.present)
- tools["jar"] = product.java.jarFilePath;
var tf;
try {
@@ -43,3 +87,4 @@ Product {
}
}
}
+
diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp
index 371d15f10..eb0303a07 100644
--- a/tests/auto/blackbox/tst_blackboxandroid.cpp
+++ b/tests/auto/blackbox/tst_blackboxandroid.cpp
@@ -79,7 +79,7 @@ void TestBlackboxAndroid::android()
QFETCH(QString, projectDir);
QFETCH(QStringList, productNames);
QFETCH(QList<QByteArrayList>, expectedFilesLists);
- QFETCH(QStringList, customProperties);
+ QFETCH(QStringList, qmlAppCustomProperties);
const SettingsPtr s = settings();
Profile p(theProfileName(projectDir == "qml-app"), s.get());
@@ -110,7 +110,7 @@ void TestBlackboxAndroid::android()
auto currentExpectedFilesLists = expectedFilesLists;
const QString configArgument = "config:" + configName;
QbsRunParameters resolveParams("resolve");
- resolveParams.arguments << configArgument << customProperties;
+ resolveParams.arguments << configArgument << qmlAppCustomProperties;
resolveParams.profile = p.name();
QCOMPARE(runQbs(resolveParams), 0);
QbsRunParameters buildParams(QStringList{"--command-echo-mode", "command-line",
@@ -206,14 +206,41 @@ void TestBlackboxAndroid::android_data()
.toString() == "clang";
return QByteArray("lib/${ARCH}/") + (usesClang ? "libc++_shared.so" : oldcxxLib);
};
- const QByteArrayList archsForQt = { pQt.value("qbs.architecture").toString().toUtf8() };
- QByteArrayList ndkArchsForQt = archsForQt;
- if (ndkArchsForQt.first() == "armv7a")
- ndkArchsForQt.first() = "armeabi-v7a";
- else if (ndkArchsForQt.first() == "armv5te")
- ndkArchsForQt.first() = "armeabi";
- else if (ndkArchsForQt.first() == "arm64")
- ndkArchsForQt.first() = "arm64-v8a";
+
+ bool usingOldQt = true;
+ QStringList qmakeFilePaths = pQt.value(QStringLiteral("moduleProviders.Qt.qmakeFilePaths")).
+ toStringList();
+ if (qmakeFilePaths.size() == 1) {
+ qbs::Version version = TestBlackboxBase::qmakeVersion(qmakeFilePaths[0]);
+ if (version.isValid() && version >= qbs::Version(5, 14))
+ usingOldQt = false;
+ }
+
+ QByteArrayList archsForQt;
+ if (usingOldQt) {
+ archsForQt = { pQt.value("qbs.architecture").toString().toUtf8() };
+ if (archsStringList.empty())
+ archsStringList << QStringLiteral("armv7a"); // must match default in common.qbs
+ } else {
+ QStringList archsForQtStringList = pQt.value(QStringLiteral("qbs.architectures"))
+ .toStringList();
+ if (archsForQtStringList.empty())
+ archsForQtStringList << pQt.value("qbs.architecture").toString();
+ std::transform(archsForQtStringList.begin(),
+ archsForQtStringList.end(),
+ std::back_inserter(archsForQt),
+ [] (const QString &s) {
+ return s.toUtf8();
+ });
+ }
+
+ QByteArrayList ndkArchsForQt;
+ std::transform(archsForQt.begin(), archsForQt.end(), std::back_inserter(ndkArchsForQt),
+ [] (const QString &s) {
+ return s.toUtf8().replace("armv7a", "armeabi-v7a")
+ .replace("armv5te", "armeabi")
+ .replace("arm64", "arm64-v8a");
+ });
auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst) {
const QByteArray &archPlaceHolder = "${ARCH}";
@@ -237,7 +264,7 @@ void TestBlackboxAndroid::android_data()
QTest::addColumn<QString>("projectDir");
QTest::addColumn<QStringList>("productNames");
QTest::addColumn<QList<QByteArrayList>>("expectedFilesLists");
- QTest::addColumn<QStringList>("customProperties");
+ QTest::addColumn<QStringList>("qmlAppCustomProperties");
QTest::newRow("teapot")
<< "teapot" << QStringList("TeapotNativeActivity")
<< (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
@@ -257,9 +284,12 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libdependency.so"}))
<< QStringList{"products.minimalnative.multiplexByQbsProperties:[]",
"modules.qbs.architecture:" + archsStringList.first()};
- QTest::newRow("qml app")
- << "qml-app" << QStringList("qmlapp")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, {
+ QByteArrayList qmlAppExpectedFiles;
+ QByteArrayList qmlAppMinistroExpectedFiles;
+ QByteArrayList qmlAppCustomMetaDataExpectedFiles;
+ QStringList qmlAppCustomProperties;
+ if (usingOldQt) {
+ qmlAppExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, {
"resources.arsc",
"assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
"assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
@@ -298,64 +328,157 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libQt5QuickParticles.so",
"lib/${ARCH}/libQt5Quick.so",
"lib/${ARCH}/libqmlapp.so",
- "res/layout/splash.xml"}))
- << QStringList{"modules.Android.sdk.automaticSources:false",
- "modules.qbs.architecture:" + archsForQt.first()};
- QTest::newRow("qml app using Ministro")
- << "qml-app" << QStringList("qmlapp")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, {
+ "res/layout/splash.xml"});
+ qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, {
+ "resources.arsc",
+ "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
+ "lib/${ARCH}/libgdbserver.so",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libqmlapp.so",
+ "res/layout/splash.xml"});
+ qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, {
+ "resources.arsc",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir",
+ "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
+ "assets/dummyasset.txt",
+ "lib/${ARCH}/libgdbserver.so",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so",
+ "lib/${ARCH}/libplugins_imageformats_libqgif.so",
+ "lib/${ARCH}/libplugins_imageformats_libqicns.so",
+ "lib/${ARCH}/libplugins_imageformats_libqico.so",
+ "lib/${ARCH}/libplugins_imageformats_libqjpeg.so",
+ "lib/${ARCH}/libplugins_imageformats_libqtga.so",
+ "lib/${ARCH}/libplugins_imageformats_libqtiff.so",
+ "lib/${ARCH}/libplugins_imageformats_libqwbmp.so",
+ "lib/${ARCH}/libplugins_imageformats_libqwebp.so",
+ "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so",
+ "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so",
+ "lib/${ARCH}/libQt5Core.so",
+ "lib/${ARCH}/libQt5Gui.so",
+ "lib/${ARCH}/libQt5Network.so",
+ "lib/${ARCH}/libQt5Qml.so",
+ "lib/${ARCH}/libQt5QuickParticles.so",
+ "lib/${ARCH}/libQt5Quick.so",
+ "lib/${ARCH}/libqmlapp.so",
+ "res/layout/splash.xml"});
+ qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false",
+ "modules.qbs.architecture:" + archsForQt.first()};
+ } else {
+ qmlAppExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, {
"resources.arsc",
- "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
+ "assets/android_rcc_bundle.rcc",
"lib/${ARCH}/libgdbserver.so",
cxxLibPath("libgnustl_shared.so", true),
- "lib/${ARCH}/libqmlapp.so",
- "res/layout/splash.xml"}))
+ "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so",
+ "lib/${ARCH}/libQt5Core_${ARCH}.so",
+ "lib/${ARCH}/libQt5Gui_${ARCH}.so",
+ "lib/${ARCH}/libQt5Network_${ARCH}.so",
+ "lib/${ARCH}/libQt5Qml_${ARCH}.so",
+ "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so",
+ "lib/${ARCH}/libQt5Quick_${ARCH}.so",
+ "lib/${ARCH}/libQt5QmlModels_${ARCH}.so",
+ "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so",
+ "lib/${ARCH}/libqmlapp_${ARCH}.so",
+ "res/layout/splash.xml"});
+ qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, {
+ "resources.arsc",
+ "assets/android_rcc_bundle.rcc",
+ "lib/${ARCH}/libgdbserver.so",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libqmlapp_${ARCH}.so",
+ "res/layout/splash.xml"});
+ qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, {
+ "resources.arsc",
+ "assets/android_rcc_bundle.rcc",
+ "assets/dummyasset.txt",
+ "lib/${ARCH}/libgdbserver.so",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so",
+ "lib/${ARCH}/libQt5Core_${ARCH}.so",
+ "lib/${ARCH}/libQt5Gui_${ARCH}.so",
+ "lib/${ARCH}/libQt5Network_${ARCH}.so",
+ "lib/${ARCH}/libQt5Qml_${ARCH}.so",
+ "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so",
+ "lib/${ARCH}/libQt5Quick_${ARCH}.so",
+ "lib/${ARCH}/libQt5QmlModels_${ARCH}.so",
+ "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so",
+ "lib/${ARCH}/libqmlapp_${ARCH}.so",
+ "res/layout/splash.xml"});
+ qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false"};
+ }
+ QTest::newRow("qml app")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << qmlAppExpectedFiles)
+ << qmlAppCustomProperties;
+ QTest::newRow("qml app using Ministro")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << qmlAppMinistroExpectedFiles)
<< QStringList{"modules.Qt.android_support.useMinistro:true",
"modules.Android.sdk.automaticSources:false"};
QTest::newRow("qml app with custom metadata")
<< "qml-app" << QStringList("qmlapp")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, {
- "resources.arsc",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir",
- "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
- "assets/dummyasset.txt",
- "lib/${ARCH}/libgdbserver.so",
- cxxLibPath("libgnustl_shared.so", true),
- "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so",
- "lib/${ARCH}/libplugins_imageformats_libqgif.so",
- "lib/${ARCH}/libplugins_imageformats_libqicns.so",
- "lib/${ARCH}/libplugins_imageformats_libqico.so",
- "lib/${ARCH}/libplugins_imageformats_libqjpeg.so",
- "lib/${ARCH}/libplugins_imageformats_libqtga.so",
- "lib/${ARCH}/libplugins_imageformats_libqtiff.so",
- "lib/${ARCH}/libplugins_imageformats_libqwbmp.so",
- "lib/${ARCH}/libplugins_imageformats_libqwebp.so",
- "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so",
- "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so",
- "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so",
- "lib/${ARCH}/libQt5Core.so",
- "lib/${ARCH}/libQt5Gui.so",
- "lib/${ARCH}/libQt5Network.so",
- "lib/${ARCH}/libQt5Qml.so",
- "lib/${ARCH}/libQt5QuickParticles.so",
- "lib/${ARCH}/libQt5Quick.so",
- "lib/${ARCH}/libqmlapp.so",
- "res/layout/splash.xml"}))
- << QStringList("modules.Android.sdk.automaticSources:true");
+ << (QList<QByteArrayList>() << qmlAppCustomMetaDataExpectedFiles)
+ << QStringList("modules.Android.sdk.automaticSources:true");
QTest::newRow("no native")
<< "no-native"
<< QStringList("com.example.android.basicmediadecoder")
diff --git a/tests/auto/blackbox/tst_blackboxbase.cpp b/tests/auto/blackbox/tst_blackboxbase.cpp
index 61b0271f6..4550edcac 100644
--- a/tests/auto/blackbox/tst_blackboxbase.cpp
+++ b/tests/auto/blackbox/tst_blackboxbase.cpp
@@ -232,3 +232,21 @@ QMap<QString, QString> TestBlackboxBase::findJdkTools(int *status)
{"jar", QDir::fromNativeSeparators(tools["jar"].toString())}
};
}
+
+qbs::Version TestBlackboxBase::qmakeVersion(const QString &qmakeFilePath)
+{
+ QStringList arguments;
+ arguments << "-query" << "QT_VERSION";
+ QProcess qmakeProcess;
+ qmakeProcess.start(qmakeFilePath, arguments);
+ if (!qmakeProcess.waitForStarted() || !qmakeProcess.waitForFinished()
+ || qmakeProcess.exitStatus() != QProcess::NormalExit) {
+ qDebug() << "qmake '" << qmakeFilePath << "' could not be run.";
+ return qbs::Version();
+ }
+ QByteArray result = qmakeProcess.readAll().simplified();
+ qbs::Version version = qbs::Version::fromString(result);
+ if (!version.isValid())
+ qDebug() << "qmake '" << qmakeFilePath << "' version is not valid.";
+ return version;
+}
diff --git a/tests/auto/blackbox/tst_blackboxbase.h b/tests/auto/blackbox/tst_blackboxbase.h
index c1c4d39b2..518cc80d0 100644
--- a/tests/auto/blackbox/tst_blackboxbase.h
+++ b/tests/auto/blackbox/tst_blackboxbase.h
@@ -92,6 +92,7 @@ protected:
static void ccp(const QString &sourceDirPath, const QString &targetDirPath);
static QString findExecutable(const QStringList &fileNames);
QMap<QString, QString> findJdkTools(int *status);
+ static qbs::Version qmakeVersion(const QString &qmakeFilePath);
const QString testDataDir;
const QString testSourceDir;