diff options
-rw-r--r-- | .travis.yml | 4 | ||||
-rwxr-xr-x | scripts/test-qt-for-android.sh | 23 | ||||
-rw-r--r-- | share/qbs/imports/qbs/base/QtApplication.qbs | 4 | ||||
-rw-r--r-- | share/qbs/imports/qbs/base/QtGuiApplication.qbs | 2 | ||||
-rw-r--r-- | share/qbs/module-providers/Qt/setup-qt.js | 225 | ||||
-rw-r--r-- | share/qbs/module-providers/Qt/templates/android_support.qbs | 150 | ||||
-rw-r--r-- | share/qbs/modules/Android/sdk/sdk.qbs | 29 | ||||
-rw-r--r-- | share/qbs/modules/Android/sdk/utils.js | 7 | ||||
-rw-r--r-- | share/qbs/modules/cpp/android-gcc.qbs | 2 | ||||
-rw-r--r-- | src/app/qbs-setup-android/android-setup.cpp | 37 | ||||
-rw-r--r-- | tests/auto/blackbox/find/find-android.qbs | 49 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackboxandroid.cpp | 253 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackboxbase.cpp | 18 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackboxbase.h | 1 |
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; |