aboutsummaryrefslogtreecommitdiffstats
path: root/share
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 /share
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>
Diffstat (limited to 'share')
-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
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;