diff options
author | Raphaël Cotty <raphael.cotty@gmail.com> | 2020-01-20 15:22:11 +0100 |
---|---|---|
committer | Raphaël Cotty <raphael.cotty@gmail.com> | 2020-02-04 12:40:58 +0000 |
commit | e130e492ebe6ad266a74df7b7d5efdef55dc0ff7 (patch) | |
tree | d46ae28bb6471b9dca6cccff316ac6842742ff6a /share | |
parent | 7811e70970294ff4c4c1fd76221727696e479cfb (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>
Diffstat (limited to 'share')
-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 |
7 files changed, 306 insertions, 113 deletions
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; |