diff options
Diffstat (limited to 'src/tools')
| -rw-r--r-- | src/tools/androidtestrunner/main.cpp | 59 | ||||
| -rw-r--r-- | src/tools/configure.cmake | 2 | ||||
| -rw-r--r-- | src/tools/macdeployqt/shared/shared.cpp | 27 |
3 files changed, 68 insertions, 20 deletions
diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index b517d85c5fb..161d95db49c 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -28,7 +28,16 @@ using namespace Qt::StringLiterals; -#define EXIT_ERROR -1 + +// QTest-based test processes may exit with up to 127 for normal test failures +static constexpr int HIGHEST_QTEST_EXITCODE = 127; +// Something went wrong in androidtestrunner, in general +static constexpr int EXIT_ERROR = 254; +// More specific exit codes for failures in androidtestrunner: +static constexpr int EXIT_NOEXITCODE = 253; // Failed to transfer exit code from device +static constexpr int EXIT_ANR = 252; // Android ANR error (Application Not Responding) +static constexpr int EXIT_NORESULTS = 251; // Failed to transfer result files from device + struct Options { @@ -71,6 +80,13 @@ struct TestInfo static TestInfo g_testInfo; +// QTest-based processes return 0 if all tests PASSed, or the number of FAILs up to 127. +// Other exitcodes signify abnormal termination and are system-dependent. +static bool isTestExitCodeNormal(const int ec) +{ + return (ec >= 0 && ec <= HIGHEST_QTEST_EXITCODE); +} + static bool execCommand(const QString &program, const QStringList &args, QByteArray *output = nullptr, bool verbose = false) { @@ -744,9 +760,9 @@ void printLogcatCrash(const QByteArray &logcat) } if (!crashLogcat.startsWith("********** Crash dump")) - qDebug() << "********** Crash dump: **********"; + qDebug() << "[androidtestrunner] ********** BEGIN crash dump **********"; qDebug().noquote() << crashLogcat.trimmed(); - qDebug() << "********** End crash dump **********"; + qDebug() << "[androidtestrunner] ********** END crash dump **********"; } void analyseLogcat(const QString &timeStamp, int *exitCode) @@ -781,10 +797,13 @@ void analyseLogcat(const QString &timeStamp, int *exitCode) // Check for ANRs const bool anrOccurred = logcat.contains("ANR in %1"_L1.arg(g_options.package).toUtf8()); if (anrOccurred) { - // Treat a found ANR as a test failure. - *exitCode = *exitCode < 1 ? 1 : *exitCode; - qCritical("An ANR has occurred while running the test %s. The logcat will include " - "additional logs from the system_server process.", + // Rather improbable, but if the test managed to return a non-crash exitcode then overwrite + // it to signify that something blew up. Same if we didn't manage to collect an exit code. + // Preserve all other exitcodes, they might be useful crash information from the device. + if (isTestExitCodeNormal(*exitCode) || *exitCode == EXIT_NOEXITCODE) + *exitCode = EXIT_ANR; + qCritical("[androidtestrunner] An ANR has occurred while running the test '%s';" + " consult logcat for additional logs from the system_server process", qPrintable(g_options.package)); } @@ -818,13 +837,14 @@ void analyseLogcat(const QString &timeStamp, int *exitCode) } } - // If we have a crash, attempt to print both logcat and the crash buffer which - // includes the crash stacktrace that is not included in the default logcat. - const bool testCrashed = *exitCode == EXIT_ERROR && !g_testInfo.isTestRunnerInterrupted.load(); + // If we have an unpredictable exitcode, possibly a crash, attempt to print both logcat and the + // crash buffer which includes the crash stacktrace that is not included in the default logcat. + const bool testCrashed = ( !isTestExitCodeNormal(*exitCode) + && !g_testInfo.isTestRunnerInterrupted.load()); if (testCrashed) { - qDebug() << "********** logcat dump **********"; + qDebug() << "[androidtestrunner] ********** BEGIN logcat dump **********"; qDebug().noquote() << testLogcat.join(u'\n').trimmed(); - qDebug() << "********** End logcat dump **********"; + qDebug() << "[androidtestrunner] ********** END logcat dump **********"; if (!crashLogcat.isEmpty()) printLogcatCrash(crashLogcat); @@ -839,7 +859,7 @@ static QString getCurrentTimeString() QStringList dateArgs = { "shell"_L1, "date"_L1, "+'%1'"_L1.arg(timeFormat) }; QByteArray output; if (!execAdbCommand(dateArgs, &output, false)) { - qWarning() << "Date/time adb command failed"; + qWarning() << "[androidtestrunner] ERROR in command: adb shell date"; return {}; } @@ -851,14 +871,15 @@ static int testExitCode() QByteArray exitCodeOutput; const QString exitCodeCmd = "cat files/qtest_last_exit_code 2> /dev/null"_L1; if (!execAdbCommand({ "shell"_L1, runCommandAsUserArgs(exitCodeCmd) }, &exitCodeOutput, false)) { - qCritical() << "Failed to retrieve the test exit code."; - return EXIT_ERROR; + qCritical() << "[androidtestrunner] ERROR in command: adb shell cat files/qtest_last_exit_code"; + return EXIT_NOEXITCODE; } + qDebug() << "[androidtestrunner] Test exitcode: " << exitCodeOutput; bool ok; int exitCode = exitCodeOutput.toInt(&ok); - return ok ? exitCode : EXIT_ERROR; + return ok ? exitCode : EXIT_NOEXITCODE; } static bool uninstallTestPackage() @@ -899,7 +920,7 @@ void sigHandler(int signal) // a main event loop. Since, there's no other alternative to do this, // let's do the cleanup anyway. if (!g_testInfo.isPackageInstalled.load()) - _exit(-1); + _exit(EXIT_ERROR); g_testInfo.isTestRunnerInterrupted.store(true); } @@ -1031,7 +1052,9 @@ int main(int argc, char *argv[]) if (g_options.showLogcatOutput) analyseLogcat(formattedStartTime, &exitCode); - exitCode = pullResults() ? exitCode : EXIT_ERROR; + const bool pullRes = pullResults(); + if (!pullRes && isTestExitCodeNormal(exitCode)) + exitCode = EXIT_NORESULTS; if (!uninstallTestPackage()) return EXIT_ERROR; diff --git a/src/tools/configure.cmake b/src/tools/configure.cmake index 27ea90b89ac..07e11dd935b 100644 --- a/src/tools/configure.cmake +++ b/src/tools/configure.cmake @@ -37,7 +37,7 @@ qt_feature("qmake" PRIVATE QT_FEATURE_datestring AND QT_FEATURE_regularexpression AND QT_FEATURE_temporaryfile) qt_feature("qtwaylandscanner" PRIVATE - CONDITION TARGET Wayland::Scanner + CONDITION TARGET Wayland::Scanner AND NOT INTEGRITY AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT QNX AND NOT VXWORKS ) qt_configure_add_summary_section(NAME "Core tools") diff --git a/src/tools/macdeployqt/shared/shared.cpp b/src/tools/macdeployqt/shared/shared.cpp index 0731fb616ed..7f8590ae894 100644 --- a/src/tools/macdeployqt/shared/shared.cpp +++ b/src/tools/macdeployqt/shared/shared.cpp @@ -1008,6 +1008,31 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, rpathsUsed.append(framework.rpathUsed); } + // To properly find all dependencies of the current framework / library further down in + // getQtFrameworks, we need to get its rpaths, resolve them in the context of its original + // location before it is copied, and add them as candidate rpaths. + // + // This is necessary to handle cases like + // (1) QtNetwork.framework -> (2) libbrotlidec.dylib -> (3) libbrotlicommon.1.dylib + // to correctly resolve the path to (3) when it is referenced as + // '@rpath/libbrotlicommon.1.dylib' and (2) has an LC_RPATH of '@loader_path/../lib', and + // no other absolute rpaths. So the '@loader_path/../lib' will be resolved relative + // to (2)'s original location and its LC_RPATH. + // + // Otherwise we'd only have the Qt prefix and the current bundle app dir as rpath + // candidates, and once (2) is copied into the app bundle, there's no way + // '@rpath/libbrotlicommon.1.dylib' could resolve to the real path on disk from the two + // candidates above. + if (!framework.sourceFilePath.isEmpty()) { + const QList<QString> sourceRPaths = getBinaryRPaths(framework.sourceFilePath, true); + for (const QString &sourceRPath : sourceRPaths) { + const QDir sourceRPathDir(sourceRPath); + if (sourceRPathDir.exists() && !rpathsUsed.contains(sourceRPath)) { + rpathsUsed.append(sourceRPath); + } + } + } + // Copy the framework/dylib to the app bundle. const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath) : copyFramework(framework, bundlePath); @@ -1032,7 +1057,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, for (const FrameworkInfo &dependency : dependencies) { if (dependency.rpathUsed.isEmpty()) { changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath); - } else { + } else if (!rpathsUsed.contains(dependency.rpathUsed)) { rpathsUsed.append(dependency.rpathUsed); } |
