From 6fdf398ab4b32c7a70af08191963c4e4f5a885fe Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Fri, 2 Nov 2018 11:41:32 +0200 Subject: [PATCH] Android: Prepare for the worst, when the NDK will remove all GNU tools In NDKr18 Google removed GCC, most probably the massacre will not end there and they will remove all GNU tools, so we need to start using LLVM ones. This patch still keeps the compatibility with GNU tools if the Qt was built with android-g++ mkspec. Change-Id: Ibe1979577e08ce63604d55fc5bbd5f64b3737675 Reviewed-by: Oswald Buddenhagen Reviewed-by: Eskil Abrahamsen Blomfeldt --- mkspecs/android-clang/qmake.conf | 2 + mkspecs/android-g++/qmake.conf | 1 + .../android/android_deployment_settings.prf | 26 +++-- src/tools/androiddeployqt/main.cpp | 97 +++++++++++-------- 4 files changed, 78 insertions(+), 48 deletions(-) diff --git a/mkspecs/android-clang/qmake.conf b/mkspecs/android-clang/qmake.conf index 1f5e690329..a0a369bb44 100644 --- a/mkspecs/android-clang/qmake.conf +++ b/mkspecs/android-clang/qmake.conf @@ -41,6 +41,8 @@ ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$A ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so +ANDROID_USE_LLVM = true + exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \ ANDROID_CXX_STL_LIBS = -lc++ else: \ diff --git a/mkspecs/android-g++/qmake.conf b/mkspecs/android-g++/qmake.conf index e72c802405..0cb3558f96 100644 --- a/mkspecs/android-g++/qmake.conf +++ b/mkspecs/android-g++/qmake.conf @@ -16,6 +16,7 @@ QMAKE_LINK = $$QMAKE_CXX ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/gnu-libstdc++/$$NDK_TOOLCHAIN_VERSION/libs/$$ANDROID_TARGET_ARCH ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libgnustl_shared.so ANDROID_CXX_STL_LIBS = -lgnustl_shared -lgcc +ANDROID_USE_LLVM = false exists($$NDK_ROOT/sysroot/usr/include): \ QMAKE_CFLAGS += --sysroot=$$NDK_ROOT/sysroot \ diff --git a/mkspecs/features/android/android_deployment_settings.prf b/mkspecs/features/android/android_deployment_settings.prf index e6b2431f9a..0db3230ce7 100644 --- a/mkspecs/features/android/android_deployment_settings.prf +++ b/mkspecs/features/android/android_deployment_settings.prf @@ -17,17 +17,22 @@ contains(TEMPLATE, ".*app"):!build_pass:!android-embedded { isEmpty(NDK_ROOT): NDK_ROOT = $$DEFAULT_ANDROID_NDK_ROOT FILE_CONTENT += " \"ndk\": $$emitString($$NDK_ROOT)," - NDK_TOOLCHAIN_PREFIX = $$(ANDROID_NDK_TOOLCHAIN_PREFIX) - isEmpty(NDK_TOOLCHAIN_PREFIX) { - equals(ANDROID_TARGET_ARCH, x86): NDK_TOOLCHAIN_PREFIX = x86 - else: equals(ANDROID_TARGET_ARCH, x86_64): NDK_TOOLCHAIN_PREFIX = x86_64 - else: equals(ANDROID_TARGET_ARCH, mips): NDK_TOOLCHAIN_PREFIX = mipsel-linux-android - else: equals(ANDROID_TARGET_ARCH, mips64): NDK_TOOLCHAIN_PREFIX = mips64el-linux-android - else: equals(ANDROID_TARGET_ARCH, arm64-v8a): NDK_TOOLCHAIN_PREFIX = aarch64-linux-android - else: NDK_TOOLCHAIN_PREFIX = arm-linux-androideabi + equals(ANDROID_USE_LLVM, true) { + FILE_CONTENT += " \"toolchain-prefix\": \"llvm\"," + FILE_CONTENT += " \"tool-prefix\": \"llvm\"," + } else { + NDK_TOOLCHAIN_PREFIX = $$(ANDROID_NDK_TOOLCHAIN_PREFIX) + isEmpty(NDK_TOOLCHAIN_PREFIX) { + equals(ANDROID_TARGET_ARCH, x86): NDK_TOOLCHAIN_PREFIX = x86 + else: equals(ANDROID_TARGET_ARCH, x86_64): NDK_TOOLCHAIN_PREFIX = x86_64 + else: equals(ANDROID_TARGET_ARCH, mips): NDK_TOOLCHAIN_PREFIX = mipsel-linux-android + else: equals(ANDROID_TARGET_ARCH, mips64): NDK_TOOLCHAIN_PREFIX = mips64el-linux-android + else: equals(ANDROID_TARGET_ARCH, arm64-v8a): NDK_TOOLCHAIN_PREFIX = aarch64-linux-android + else: NDK_TOOLCHAIN_PREFIX = arm-linux-androideabi + } + FILE_CONTENT += " \"toolchain-prefix\": $$emitString($$NDK_TOOLCHAIN_PREFIX)," + FILE_CONTENT += " \"tool-prefix\": $$emitString($$NDK_TOOLS_PREFIX)," } - FILE_CONTENT += " \"toolchain-prefix\": $$emitString($$NDK_TOOLCHAIN_PREFIX)," - FILE_CONTENT += " \"tool-prefix\": $$emitString($$NDK_TOOLS_PREFIX)," NDK_TOOLCHAIN_VERSION = $$(ANDROID_NDK_TOOLCHAIN_VERSION) isEmpty(NDK_TOOLCHAIN_VERSION): NDK_TOOLCHAIN_VERSION = $$DEFAULT_ANDROID_NDK_TOOLCHAIN_VERSION @@ -63,6 +68,7 @@ contains(TEMPLATE, ".*app"):!build_pass:!android-embedded { QML_ROOT_PATH = $$_PRO_FILE_PWD_ FILE_CONTENT += " \"qml-root-path\": $$emitString($$QML_ROOT_PATH)," FILE_CONTENT += " \"stdcpp-path\": $$emitString($$ANDROID_STDCPP_PATH)," + FILE_CONTENT += " \"useLLVM\": $$ANDROID_USE_LLVM," FILE_CONTENT += " \"application-binary\": $$emitString($$absolute_path($$DESTDIR, $$OUT_PWD)/$$TARGET)" FILE_CONTENT += "}" diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 11213872f3..587ae21e4f 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -157,6 +157,7 @@ struct Options QString toolchainVersion; QString toolchainPrefix; QString toolPrefix; + bool useLLVM = false; QString ndkHost; // Package information @@ -808,6 +809,11 @@ bool readInputFile(Options *options) options->ndkPath = ndk.toString(); } + { + const QJsonValue value = jsonObject.value(QStringLiteral("useLLVM")); + options->useLLVM = value.toBool(false); + } + { const QJsonValue toolchainPrefix = jsonObject.value(QStringLiteral("toolchain-prefix")); if (toolchainPrefix.isUndefined()) { @@ -827,7 +833,7 @@ bool readInputFile(Options *options) } } - { + if (!options->useLLVM) { const QJsonValue toolchainVersion = jsonObject.value(QStringLiteral("toolchain-version")); if (toolchainVersion.isUndefined()) { fprintf(stderr, "No toolchain version defined in json file.\n"); @@ -863,15 +869,17 @@ bool readInputFile(Options *options) { const QJsonValue stdcppPath = jsonObject.value(QStringLiteral("stdcpp-path")); - if (!stdcppPath.isUndefined()) { - options->stdCppPath = stdcppPath.toString(); - auto name = QFileInfo(options->stdCppPath).baseName(); - if (!name.startsWith(QLatin1String("lib"))) { - fprintf(stderr, "Invalid STD C++ library name.\n"); - return false; - } - options->stdCppName = name.mid(3); + if (stdcppPath.isUndefined()) { + fprintf(stderr, "No stdcpp-path defined in json file.\n"); + return false; } + options->stdCppPath = stdcppPath.toString(); + auto name = QFileInfo(options->stdCppPath).baseName(); + if (!name.startsWith(QLatin1String("lib"))) { + fprintf(stderr, "Invalid STD C++ library name.\n"); + return false; + } + options->stdCppName = name.mid(3); } { @@ -1546,14 +1554,16 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) { QString readElf = options.ndkPath + QLatin1String("/toolchains/") - + options.toolchainPrefix - + QLatin1Char('-') - + options.toolchainVersion - + QLatin1String("/prebuilt/") + + options.toolchainPrefix; + + if (!options.useLLVM) + readElf += QLatin1Char('-') + options.toolchainVersion; + + readElf += QLatin1String("/prebuilt/") + options.ndkHost + QLatin1String("/bin/") - + options.toolPrefix - + QLatin1String("-readelf"); + + options.toolPrefix + + (options.useLLVM ? QLatin1String("-readobj") : QLatin1String("-readelf")); #if defined(Q_OS_WIN32) readElf += QLatin1String(".exe"); #endif @@ -1563,27 +1573,40 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) return QStringList(); } - readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf)).arg(shellQuote(fileName)); + if (options.useLLVM) + readElf = QString::fromLatin1("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName)); + else + readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf), shellQuote(fileName)); FILE *readElfCommand = openProcess(readElf); - if (readElfCommand == 0) { + if (!readElfCommand) { fprintf(stderr, "Cannot execute command %s", qPrintable(readElf)); return QStringList(); } QStringList ret; + bool readLibs = false; char buffer[512]; while (fgets(buffer, sizeof(buffer), readElfCommand) != 0) { QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer)); - if (line.contains("(NEEDED)") && line.contains("Shared library:") ) { - const int pos = line.lastIndexOf('[') + 1; - QString libraryName = QLatin1String("lib/") + QString::fromLatin1(line.mid(pos, line.length() - pos - 2)); - if (QFile::exists(absoluteFilePath(&options, libraryName))) { - ret += libraryName; + QString library; + if (options.useLLVM) { + line = line.trimmed(); + if (!readLibs) { + readLibs = line.startsWith("NeededLibraries"); + continue; } - + if (!line.startsWith("lib")) + continue; + library = QString::fromLatin1(line); + } else if (line.contains("(NEEDED)") && line.contains("Shared library:")) { + const int pos = line.lastIndexOf('[') + 1; + library = QString::fromLatin1(line.mid(pos, line.length() - pos - 2)); } + QString libraryName = QLatin1String("lib/") + library; + if (QFile::exists(absoluteFilePath(&options, libraryName))) + ret += libraryName; } pclose(readElfCommand); @@ -1837,10 +1860,12 @@ bool stripFile(const Options &options, const QString &fileName) { QString strip = options.ndkPath + QLatin1String("/toolchains/") - + options.toolchainPrefix - + QLatin1Char('-') - + options.toolchainVersion - + QLatin1String("/prebuilt/") + + options.toolchainPrefix; + + if (!options.useLLVM) + strip += QLatin1Char('-') + options.toolchainVersion; + + strip += QLatin1String("/prebuilt/") + options.ndkHost + QLatin1String("/bin/") + options.toolPrefix @@ -1854,7 +1879,10 @@ bool stripFile(const Options &options, const QString &fileName) return false; } - strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip)).arg(shellQuote(fileName)); + if (options.useLLVM) + strip = QString::fromLatin1("%1 -strip-all -strip-all-gnu %2").arg(shellQuote(strip), shellQuote(fileName)); + else + strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip), shellQuote(fileName)); FILE *stripCommand = openProcess(strip); if (stripCommand == 0) { @@ -2423,22 +2451,15 @@ bool copyStdCpp(Options *options) if (options->verbose) fprintf(stdout, "Copying STL library\n"); - QString filePath = !options->stdCppPath.isEmpty() ? options->stdCppPath - : options->ndkPath - + QLatin1String("/sources/cxx-stl/gnu-libstdc++/") - + options->toolchainVersion - + QLatin1String("/libs/") - + options->architecture - + QLatin1String("/libgnustl_shared.so"); - if (!QFile::exists(filePath)) { - fprintf(stderr, "STL library does not exist at %s\n", qPrintable(filePath)); + if (!QFile::exists(options->stdCppPath)) { + fprintf(stderr, "STL library does not exist at %s\n", qPrintable(options->stdCppPath)); return false; } const QString destinationDirectory = options->outputDirectory + QLatin1String("/libs/") + options->architecture; - if (!copyFileIfNewer(filePath, destinationDirectory + QLatin1String("/lib") + if (!copyFileIfNewer(options->stdCppPath, destinationDirectory + QLatin1String("/lib") + options->stdCppName + QLatin1String(".so"), options->verbose)) { return false;