diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index c27965a9508..b8462ac8c63 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -51,6 +51,7 @@ namespace Android { namespace Internal { static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done"); +static const QRegularExpression userIdPattern("u(\\d+)_a"); static int APP_START_TIMEOUT = 45000; static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, @@ -77,8 +78,10 @@ static qint64 extractPID(const QString &output, const QString &packageName) return pid; } -static void findProcessPID(QPromise &promise, QStringList selector, - const QString &packageName, bool preNougat) +static void findProcessPIDAndUser(QPromise &promise, + QStringList selector, + const QString &packageName, + bool preNougat) { if (packageName.isEmpty()) return; @@ -108,8 +111,32 @@ static void findProcessPID(QPromise &promise, QStringList selector, } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !promise.isCanceled()); qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat; + + qint64 processUser = 0; + if (processPID > 0 && !promise.isCanceled()) { + args = {selector}; + args.append({"shell", "ps", "-o", "user", "-p"}); + args.append(QString::number(processPID)); + QtcProcess proc; + proc.setCommand({adbPath, args}); + proc.runBlocking(); + const QString out = proc.allOutput(); + if (!out.isEmpty()) { + QRegularExpressionMatch match; + qsizetype matchPos = out.indexOf(userIdPattern, 0, &match); + if (matchPos >= 0 && match.capturedLength(1) > 0) { + bool ok = false; + processUser = match.captured(1).toInt(&ok); + if (!ok) + processUser = 0; + } + } + } + + qCDebug(androidRunWorkerLog) << "USER found:" << processUser; + if (!promise.isCanceled()) - promise.addResult(processPID); + promise.addResult(PidUserPair(processPID, processUser)); } static void deleter(QProcess *p) @@ -325,13 +352,16 @@ bool AndroidRunnerWorker::uploadDebugServer(const QString &debugServerFileName) return false; } + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); // Copy gdbserver from temp location to app directory - if (!runAdb({"shell", "run-as", m_packageName, "cp" , tempDebugServerPath, debugServerFileName})) { + if (!runAdb(adbArgs + QStringList({"cp" , tempDebugServerPath, debugServerFileName}))) { qCDebug(androidRunWorkerLog) << "Debug server copy from temp directory failed"; return false; } - const bool ok = runAdb({"shell", "run-as", m_packageName, "chmod", "777", debugServerFileName}); + const bool ok = runAdb(adbArgs + QStringList({"chmod", "777", debugServerFileName})); QTC_ASSERT(ok, qCDebug(androidRunWorkerLog) << "Debug server chmod 777 failed."); return true; } @@ -346,7 +376,11 @@ bool AndroidRunnerWorker::deviceFileExists(const QString &filePath) bool AndroidRunnerWorker::packageFileExists(const QString &filePath) { QString output; - const bool success = runAdb({"shell", "run-as", m_packageName, "ls", filePath, "2>/dev/null"}, &output); + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); + const bool success = runAdb(adbArgs + QStringList({"ls", filePath, "2>/dev/null"}), + &output); return success && !output.trimmed().isEmpty(); } @@ -513,62 +547,8 @@ void AndroidRunnerWorker::asyncStartHelper() QStringList args({"shell", "am", "start"}); args << "-n" << m_intentName; - if (m_useCppDebugger) { + if (m_useCppDebugger) args << "-D"; - // run-as pwd fails on API 22 so route the pwd through shell. - QString packageDir; - if (!runAdb({"shell", "run-as", m_packageName, "/system/bin/sh", "-c", "pwd"}, - &packageDir)) { - emit remoteProcessFinished(Tr::tr("Failed to find application directory.")); - return; - } - - // Add executable flag to package dir. Gdb can't connect to running server on device on - // e.g. on Android 8 with NDK 10e - runAdb({"shell", "run-as", m_packageName, "chmod", "a+x", packageDir.trimmed()}); - - if (!m_debugServerPath.exists()) { - QString msg = Tr::tr("Cannot find C++ debug server in NDK installation."); - if (m_useLldb) - msg += "\n" + Tr::tr("The lldb-server binary has not been found."); - emit remoteProcessFinished(msg); - return; - } - - QString debugServerFile; - if (m_useLldb) { - debugServerFile = "./lldb-server"; - runAdb({"shell", "run-as", m_packageName, "killall", "lldb-server"}); - if (!uploadDebugServer(debugServerFile)) { - emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); - return; - } - } else { - if (packageFileExists("./lib/gdbserver")) { - debugServerFile = "./lib/gdbserver"; - qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; - runAdb({"shell", "run-as", m_packageName, "killall", "gdbserver"}); - } else if (packageFileExists("./lib/libgdbserver.so")) { - debugServerFile = "./lib/libgdbserver.so"; - qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; - runAdb({"shell", "run-as", m_packageName, "killall", "libgdbserver.so"}); - } else { - // Armv8. symlink lib is not available. - debugServerFile = "./gdbserver"; - // Kill the previous instances of gdbserver. Do this before copying the gdbserver. - runAdb({"shell", "run-as", m_packageName, "killall", "gdbserver"}); - if (!uploadDebugServer("./gdbserver")) { - emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); - return; - } - } - } - QString debuggerServerErr; - if (!startDebuggerServer(packageDir, debugServerFile, &debuggerServerErr)) { - emit remoteProcessFinished(debuggerServerErr); - return; - } - } if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { // currently forward to same port on device and host @@ -624,17 +604,79 @@ void AndroidRunnerWorker::asyncStartHelper() } } +void AndroidRunnerWorker::startNativeDebugging() +{ + // run-as pwd fails on API 22 so route the pwd through shell. + QString packageDir; + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); + if (!runAdb(adbArgs + QStringList({"/system/bin/sh", "-c", "pwd"}), + &packageDir)) { + emit remoteProcessFinished(Tr::tr("Failed to find application directory.")); + return; + } + // Add executable flag to package dir. Gdb can't connect to running server on device on + // e.g. on Android 8 with NDK 10e + runAdb(adbArgs + QStringList({"chmod", "a+x", packageDir.trimmed()})); + if (!m_debugServerPath.exists()) { + QString msg = Tr::tr("Cannot find C++ debug server in NDK installation."); + if (m_useLldb) + msg += "\n" + Tr::tr("The lldb-server binary has not been found."); + emit remoteProcessFinished(msg); + return; + } + + QString debugServerFile; + if (m_useLldb) { + debugServerFile = "./lldb-server"; + runAdb(adbArgs + QStringList({"killall", "lldb-server"})); + if (!uploadDebugServer(debugServerFile)) { + emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); + return; + } + } else { + if (packageFileExists("./lib/gdbserver")) { + debugServerFile = "./lib/gdbserver"; + qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; + runAdb(adbArgs + QStringList({"killall", "gdbserver"})); + } else if (packageFileExists("./lib/libgdbserver.so")) { + debugServerFile = "./lib/libgdbserver.so"; + qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; + runAdb(adbArgs + QStringList({"killall", "libgdbserver.so"})); + } else { + // Armv8. symlink lib is not available. + debugServerFile = "./gdbserver"; + // Kill the previous instances of gdbserver. Do this before copying the gdbserver. + runAdb(adbArgs + QStringList({"killall", "gdbserver"})); + if (!uploadDebugServer("./gdbserver")) { + emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); + return; + } + } + } + QString debuggerServerErr; + if (!startDebuggerServer(packageDir, debugServerFile, &debuggerServerErr)) { + emit remoteProcessFinished(debuggerServerErr); + return; + } +} + bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr) { + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); if (m_useLldb) { QString lldbServerErr; QStringList lldbServerArgs = selector(); - lldbServerArgs << "shell" << "run-as" << m_packageName << debugServerFile - << "platform" - // << "--server" // Can lead to zombie servers - << "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString()); + lldbServerArgs += adbArgs; + lldbServerArgs << debugServerFile + << "platform" + // << "--server" // Can lead to zombie servers + << "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString()); m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(lldbServerArgs, &lldbServerErr)); if (!m_debugServerProcess) { @@ -648,12 +690,13 @@ bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir, } else { QString gdbServerSocket = packageDir + "/debug-socket"; - runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket}); + runAdb(adbArgs + QStringList({"rm", gdbServerSocket})); QString gdbProcessErr; QStringList gdbServerErr = selector(); - gdbServerErr << "shell" << "run-as" << m_packageName << debugServerFile - << "--multi" << "+" + gdbServerSocket; + gdbServerErr += adbArgs; + gdbServerErr << debugServerFile + << "--multi" << "+" + gdbServerSocket; m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerErr, &gdbProcessErr)); if (!m_debugServerProcess) { @@ -683,9 +726,12 @@ void AndroidRunnerWorker::asyncStart() { asyncStartHelper(); - m_pidFinder = Utils::onResultReady( - Utils::asyncRun(findProcessPID, selector(),m_packageName, m_isPreNougat), this, - bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); + m_pidFinder = Utils::onResultReady(Utils::asyncRun(findProcessPIDAndUser, + selector(), + m_packageName, + m_isPreNougat), + this, + bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); } void AndroidRunnerWorker::asyncStop() @@ -793,13 +839,16 @@ void AndroidRunnerWorker::removeForwardPort(const QString &port) } } -void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) +void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser) { + qint64 pid = pidUser.first; + qint64 user = pidUser.second; // Don't write to m_psProc from a different thread QTC_ASSERT(QThread::currentThread() == thread(), return); qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID << "to:" << pid; m_processPID = pid; + m_processUser = user; if (pid == -1) { emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.") .arg(m_packageName)); @@ -813,6 +862,8 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) for (const QString &entry: std::as_const(m_afterFinishAdbCommands)) runAdb(entry.split(' ', Qt::SkipEmptyParts)); } else { + if (m_useCppDebugger) + startNativeDebugging(); // In debugging cases this will be funneled to the engine to actually start // and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. emit remoteProcessStarted(m_localDebugServerPort, m_qmlServer, m_processPID); @@ -824,7 +875,7 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) m_psIsAlive->setObjectName("IsAliveProcess"); m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); connect(m_psIsAlive.get(), &QProcess::finished, - this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, -1)); + this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, PidUserPair(-1, -1))); } } diff --git a/src/plugins/android/androidrunnerworker.h b/src/plugins/android/androidrunnerworker.h index e8c340d1a37..7e41d2e9c73 100644 --- a/src/plugins/android/androidrunnerworker.h +++ b/src/plugins/android/androidrunnerworker.h @@ -10,6 +10,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QProcess; @@ -26,6 +27,8 @@ namespace Internal { const int MIN_SOCKET_HANDSHAKE_PORT = 20001; +using PidUserPair = std::pair; + class AndroidRunnerWorker : public QObject { Q_OBJECT @@ -61,6 +64,7 @@ signals: private: void asyncStartHelper(); + void startNativeDebugging(); bool startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr = nullptr); bool deviceFileExists(const QString &filePath); bool packageFileExists(const QString& filePath); @@ -72,7 +76,7 @@ private: Waiting, Settled }; - void onProcessIdChanged(qint64 pid); + void onProcessIdChanged(PidUserPair pidUser); using Deleter = void (*)(QProcess *); // Create the processes and timer in the worker thread, for correct thread affinity @@ -83,11 +87,12 @@ private: QStringList m_afterFinishAdbCommands; QStringList m_amStartExtraArgs; qint64 m_processPID = -1; + qint64 m_processUser = -1; std::unique_ptr m_adbLogcatProcess; std::unique_ptr m_psIsAlive; QByteArray m_stdoutBuffer; QByteArray m_stderrBuffer; - QFuture m_pidFinder; + QFuture m_pidFinder; bool m_useCppDebugger = false; bool m_useLldb = false; // FIXME: Un-implemented currently. QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index a7a766768eb..51011dad3de 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -177,6 +177,7 @@ clang::format::FormatStyle qtcStyle() style.Standard = FormatStyle::LS_Cpp11; style.TabWidth = 4; style.UseTab = FormatStyle::UT_Never; + style.Standard = FormatStyle::LS_Auto; return style; } diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 565d899d838..a4d26750651 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -519,8 +519,8 @@ ClangTool::ClangTool(const QString &name, Utils::Id id) mainLayout->addWidget(m_infoBarWidget); mainLayout->addWidget(m_diagnosticView); auto mainWidget = new QWidget; - mainWidget->setObjectName("ClangTidyClazyIssuesView"); - mainWidget->setWindowTitle(Tr::tr("Clang-Tidy and Clazy")); + mainWidget->setObjectName(id.toString() + "IssuesView"); + mainWidget->setWindowTitle(name); mainWidget->setLayout(mainLayout); m_perspective.addWindow(mainWidget, Perspective::SplitVertical, nullptr); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 0b193251a4c..6649a65ed71 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -1256,6 +1256,12 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume const QString presetItemArg = presetItem.toArgument(); const QString presetItemArgNoType = presetItemArg.left(presetItemArg.indexOf(":")); + static QSet defaultKitMacroValues{"CMAKE_C_COMPILER", + "CMAKE_CXX_COMPILER", + "QT_QMAKE_EXECUTABLE", + "QT_HOST_PATH", + "CMAKE_PROJECT_INCLUDE_BEFORE"}; + auto it = std::find_if(initialArguments.begin(), initialArguments.end(), [presetItemArgNoType](const QString &arg) { @@ -1266,6 +1272,11 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume QString &arg = *it; CMakeConfigItem argItem = CMakeConfigItem::fromString(arg.mid(2)); // skip -D + // These values have Qt Creator macro names pointing to the Kit values + // which are preset expanded values used when the Kit was created + if (defaultKitMacroValues.contains(argItem.key) && argItem.value.startsWith("%{")) + continue; + // For multi value path variables append the non Qt path if (argItem.key == "CMAKE_PREFIX_PATH" || argItem.key == "CMAKE_FIND_ROOT_PATH") { QStringList presetValueList = presetItem.expandedValue(k).split(";"); @@ -1276,7 +1287,7 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume QStringList argItemPaths = argItemExpandedValue.split(";"); for (const QString &argPath : argItemPaths) { const FilePath argFilePath = FilePath::fromString(argPath); - const FilePath presetFilePath = FilePath::fromString(presetPath); + const FilePath presetFilePath = FilePath::fromUserInput(presetPath); if (argFilePath == presetFilePath) return true; @@ -1291,12 +1302,10 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume } arg = argItem.toArgument(); - } else if (argItem.key == "CMAKE_C_COMPILER" || argItem.key == "CMAKE_CXX_COMPILER" - || argItem.key == "QT_QMAKE_EXECUTABLE" || argItem.key == "QT_HOST_PATH" - || argItem.key == "CMAKE_PROJECT_INCLUDE_BEFORE" - || argItem.key == "CMAKE_TOOLCHAIN_FILE") { + } else if (argItem.key == "CMAKE_TOOLCHAIN_FILE") { const FilePath argFilePath = FilePath::fromString(argItem.expandedValue(k)); - const FilePath presetFilePath = FilePath::fromUtf8(presetItem.value); + const FilePath presetFilePath = FilePath::fromUserInput( + QString::fromUtf8(presetItem.value)); if (argFilePath != presetFilePath) arg = presetItem.toArgument(); diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 13764fc3677..21ee60f365c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -103,7 +103,9 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP auto resolveInherits = [](auto &presetsHash, auto &presetsList) { Utils::sort(presetsList, [](const auto &left, const auto &right) { - if (!left.inherits || left.inherits.value().contains(right.name)) + const bool sameInheritance = left.inherits && right.inherits + && left.inherits.value() == right.inherits.value(); + if (!left.inherits || left.inherits.value().contains(right.name) || sameInheritance) return false; return true; }); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index a89ae82e1ff..dc752f0a9f9 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -209,23 +209,11 @@ static CMakeConfig configurationFromPresetProbe( ? configurePreset.cacheVariables.value() : CMakeConfig(); - auto expandCacheValue = - [configurePreset, env, sourceDirectory, cache](const QString &key) -> QString { - QString result = cache.stringValueOf(key.toUtf8()); - CMakePresets::Macros::expand(configurePreset, env, sourceDirectory, result); - - // all usages involve file paths, so make sure they are cleaned up - const FilePaths paths = transform(result.split(";"), &FilePath::fromUserInput); - result = transform(paths, &FilePath::path).join(";"); - - return result; - }; - - const QString cmakeMakeProgram = expandCacheValue("CMAKE_MAKE_PROGRAM"); - const QString toolchainFile = expandCacheValue("CMAKE_TOOLCHAIN_FILE"); - const QString prefixPath = expandCacheValue("CMAKE_PREFIX_PATH"); - const QString findRootPath = expandCacheValue("CMAKE_FIND_ROOT_PATH"); - const QString qtHostPath = expandCacheValue("QT_HOST_PATH"); + const QString cmakeMakeProgram = cache.stringValueOf("CMAKE_MAKE_PROGRAM"); + const QString toolchainFile = cache.stringValueOf("CMAKE_TOOLCHAIN_FILE"); + const QString prefixPath = cache.stringValueOf("CMAKE_PREFIX_PATH"); + const QString findRootPath = cache.stringValueOf("CMAKE_FIND_ROOT_PATH"); + const QString qtHostPath = cache.stringValueOf("QT_HOST_PATH"); if (!cmakeMakeProgram.isEmpty()) { args.emplace_back( @@ -662,6 +650,8 @@ QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, projectDirectory(), data->buildDirectory); + CMakePresets::Macros::updateCacheVariables(configurePreset, env, projectDirectory()); + const CMakeConfig cache = configurePreset.cacheVariables ? configurePreset.cacheVariables.value() : CMakeConfig(); @@ -710,10 +700,7 @@ QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, data->cmakePresetDefaultConfigHash = CMakeConfigurationKitAspect::computeDefaultConfigHash(config, data->cmakeBinary); - QString cmakeBuildType = QString::fromUtf8(cache.valueOf("CMAKE_BUILD_TYPE")); - CMakePresets::Macros::expand(configurePreset, env, projectDirectory(), cmakeBuildType); - - QByteArrayList buildConfigurationTypes = {cmakeBuildType.toUtf8()}; + QByteArrayList buildConfigurationTypes = {cache.valueOf("CMAKE_BUILD_TYPE")}; if (buildConfigurationTypes.front().isEmpty()) { buildConfigurationTypes.clear(); QByteArray buildConfigurationTypesString = cache.valueOf("CMAKE_CONFIGURATION_TYPES"); diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.cpp b/src/plugins/cmakeprojectmanager/presetsmacros.cpp index 3b750d4599c..c96b0eed7aa 100644 --- a/src/plugins/cmakeprojectmanager/presetsmacros.cpp +++ b/src/plugins/cmakeprojectmanager/presetsmacros.cpp @@ -4,6 +4,7 @@ #include "presetsmacros.h" #include "presetsparser.h" +#include #include #include #include @@ -295,6 +296,47 @@ void updateInstallDir(PresetsDetails::ConfigurePreset &configurePreset, } +void updateCacheVariables(PresetsDetails::ConfigurePreset &configurePreset, + const Utils::Environment &env, + const Utils::FilePath &sourceDirectory) +{ + using namespace Utils; + + if (!configurePreset.cacheVariables) + return; + + CMakeConfig cache = configurePreset.cacheVariables.value(); + + static const QSet pathKeys{"CMAKE_C_COMPILER", + "CMAKE_CXX_COMPILER", + "CMAKE_PREFIX_PATH", + "CMAKE_FIND_ROOT_PATH", + "CMAKE_MAKE_PROGRAM", + "CMAKE_TOOLCHAIN_FILE", + "QT_HOST_PATH", + "QT_QMAKE_EXECUTABLE", + "CMAKE_SYSROOT"}; + + auto expandCacheValue = + [configurePreset, env, sourceDirectory, cache](const QByteArray &key) { + QString result = cache.stringValueOf(key); + CMakePresets::Macros::expand(configurePreset, env, sourceDirectory, result); + + if (pathKeys.contains(key)) { + const FilePaths paths = transform(result.split(";"), &FilePath::fromUserInput); + result = transform(paths, &FilePath::path).join(";"); + } + + return result.toUtf8(); + }; + + for (auto &item : cache) + item.value = expandCacheValue(item.key); + + configurePreset.cacheVariables = cache; +} + + template void expandConditionValues(const PresetType &preset, const Utils::Environment &env, diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.h b/src/plugins/cmakeprojectmanager/presetsmacros.h index 7d71514739a..49e47a71b1c 100644 --- a/src/plugins/cmakeprojectmanager/presetsmacros.h +++ b/src/plugins/cmakeprojectmanager/presetsmacros.h @@ -60,6 +60,14 @@ void updateToolchainFile(PresetsDetails::ConfigurePreset &configurePreset, void updateInstallDir(PresetsDetails::ConfigurePreset &configurePreset, const Utils::Environment &env, const Utils::FilePath &sourceDirectory); + +/** + * Updates the cacheVariables parameter of the configurePreset with the expanded prameter values. + * Including macro expansion and relative paths resolving. + */ +void updateCacheVariables(PresetsDetails::ConfigurePreset &configurePreset, + const Utils::Environment &env, + const Utils::FilePath &sourceDirectory); /** * Expands the condition values and then evaluates the condition object of the preset and returns * the boolean result. diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index 33817e08c40..3478634815c 100644 --- a/src/plugins/qtsupport/exampleslistmodel.cpp +++ b/src/plugins/qtsupport/exampleslistmodel.cpp @@ -44,6 +44,7 @@ static bool debugExamples() } static const char kSelectedExampleSetKey[] = "WelcomePage/SelectedExampleSet"; +Q_GLOBAL_STATIC_WITH_ARGS(QVersionNumber, minQtVersionForCategories, (6, 5, 1)); void ExampleSetModel::writeCurrentIdToSettings(int currentIndex) const { @@ -117,7 +118,7 @@ void ExampleSetModel::recreateModel(const QtVersions &qtVersions) beginResetModel(); clear(); - QSet extraManifestDirs; + QHash extraManifestDirs; for (int i = 0; i < m_extraExampleSets.size(); ++i) { const ExtraExampleSet &set = m_extraExampleSets.at(i); auto newItem = new QStandardItem(); @@ -127,14 +128,19 @@ void ExampleSetModel::recreateModel(const QtVersions &qtVersions) newItem->setData(i, Qt::UserRole + 3); appendRow(newItem); - extraManifestDirs.insert(set.manifestPath); + extraManifestDirs.insert(FilePath::fromUserInput(set.manifestPath), i); } for (QtVersion *version : qtVersions) { - // sanitize away qt versions that have already been added through extra sets - if (extraManifestDirs.contains(version->docsPath().toString())) { + // Sanitize away qt versions that have already been added through extra sets. + // This way we do not have entries for Qt/Android, Qt/Desktop, Qt/MinGW etc pp, + // but only the one "QtX X.Y.Z" entry that is registered as an example set by the installer. + if (extraManifestDirs.contains(version->docsPath())) { + m_extraExampleSets[extraManifestDirs.value(version->docsPath())].qtVersion + = version->qtVersion(); if (debugExamples()) { - qWarning() << "Not showing Qt version because manifest path is already added through InstalledExamples settings:" + qWarning() << "Not showing Qt version because manifest path is already added " + "through InstalledExamples settings:" << version->displayName(); } continue; @@ -330,10 +336,11 @@ static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) } static QList>> getCategories( - const QList &items) + const QList &items, bool sortIntoCategories) { static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples"); - const bool useCategories = qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES"); + const bool useCategories = sortIntoCategories + || qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES"); QList other; QMap> categoryMap; if (useCategories) { @@ -378,10 +385,11 @@ void ExamplesViewController::updateExamples() { QString examplesInstallPath; QString demosInstallPath; + QVersionNumber qtVersion; const QStringList sources = m_exampleSetModel->exampleSources(&examplesInstallPath, - &demosInstallPath); - + &demosInstallPath, + &qtVersion); m_view->clear(); QList items; @@ -418,7 +426,9 @@ void ExamplesViewController::updateExamples() } } - const QList>> sections = getCategories(items); + const bool sortIntoCategories = qtVersion >= *minQtVersionForCategories; + const QList>> sections + = getCategories(items, sortIntoCategories); for (int i = 0; i < sections.size(); ++i) { m_view->addSection(sections.at(i).first, static_container_cast(sections.at(i).second)); @@ -486,7 +496,9 @@ QtVersion *ExampleSetModel::findHighestQtVersion(const QtVersions &versions) con return newVersion; } -QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QString *demosInstallPath) +QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, + QString *demosInstallPath, + QVersionNumber *qtVersion) { QStringList sources; @@ -504,6 +516,8 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QStrin manifestScanPath = exampleSet.manifestPath; examplesPath = exampleSet.examplesPath; demosPath = exampleSet.examplesPath; + if (qtVersion) + *qtVersion = exampleSet.qtVersion; } else if (currentType == ExampleSetModel::QtExampleSet) { const int qtId = getQtId(m_selectedExampleSetIndex); const QtVersions versions = QtVersionManager::versions(); @@ -512,6 +526,8 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QStrin manifestScanPath = version->docsPath().toString(); examplesPath = version->examplesPath().toString(); demosPath = version->demosPath().toString(); + if (qtVersion) + *qtVersion = version->qtVersion(); break; } } diff --git a/src/plugins/qtsupport/exampleslistmodel.h b/src/plugins/qtsupport/exampleslistmodel.h index 0047bf45e3e..c444f9bc1e7 100644 --- a/src/plugins/qtsupport/exampleslistmodel.h +++ b/src/plugins/qtsupport/exampleslistmodel.h @@ -28,6 +28,10 @@ public: QString displayName; QString manifestPath; QString examplesPath; + // qtVersion is set by recreateModel for extra sets that correspond to actual Qt versions. + // This is needed for the decision to show categories or not based on the Qt version + // (which is not ideal). + QVersionNumber qtVersion; }; static QVector pluginRegisteredExampleSets(); @@ -35,7 +39,9 @@ public: int selectedExampleSet() const { return m_selectedExampleSetIndex; } void selectExampleSet(int index); - QStringList exampleSources(QString *examplesInstallPath, QString *demosInstallPath); + QStringList exampleSources(QString *examplesInstallPath, + QString *demosInstallPath, + QVersionNumber *qtVersion); bool selectedQtSupports(const Utils::Id &target) const; signals: diff --git a/src/shared/qbs b/src/shared/qbs index 289aac0aa1c..e002680feb4 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 289aac0aa1cccb2ef66a17f572aeb59236c6bd29 +Subproject commit e002680feb415ee7d26e3a31d70afeef0d50dea2 diff --git a/tests/system/README b/tests/system/README index 45fd2e498be..43d65ec70ad 100644 --- a/tests/system/README +++ b/tests/system/README @@ -12,7 +12,6 @@ of the Qt installation from the online installer. It's easiest to use installations of the official opensource Qt packages. Just install the Qt version for the respective toolchain with the components (if available): - (Desktop) , e.g. Desktop gcc 64-bit -- Qt Quick Controls (if available) - Qt Script (Qt5 only) The exact versions and toolchains are: diff --git a/tests/system/suite_debugger/tst_build_new_project/test.py b/tests/system/suite_debugger/tst_build_new_project/test.py index ca0d6f70879..b29678ffcf6 100644 --- a/tests/system/suite_debugger/tst_build_new_project/test.py +++ b/tests/system/suite_debugger/tst_build_new_project/test.py @@ -18,6 +18,7 @@ def main(): expectBuildToFail = [] if platform.system() in ('Microsoft', 'Windows'): expectConfigureToFail = [ Targets.DESKTOP_5_4_1_GCC ] # gcc 4.9 does not know C++17 + expectBuildToFail = [ Targets.DESKTOP_5_10_1_DEFAULT ] # fails to handle constexpr correctly for kit, config in availableConfigs: selectBuildConfig(kit, config) diff --git a/tests/system/suite_general/tst_default_settings/test.py b/tests/system/suite_general/tst_default_settings/test.py index 10a31534535..866720b27b8 100644 --- a/tests/system/suite_general/tst_default_settings/test.py +++ b/tests/system/suite_general/tst_default_settings/test.py @@ -35,7 +35,7 @@ def __checkKits__(): if llvmForBuild is not None: internalClangExe = os.path.join(llvmForBuild, "bin", "clang") if platform.system() in ("Microsoft", "Windows"): - internalClangExe.append(".exe") + internalClangExe += ".exe" if os.path.exists(internalClangExe): expectedCompilers.append(internalClangExe) foundCompilers = []