Merge remote-tracking branch 'origin/10.0'

Conflicts:
	src/plugins/android/androidrunnerworker.cpp
	src/plugins/qtsupport/exampleslistmodel.cpp

Change-Id: I1628528dbc0ffe874b49bbe022da5933b1348057
This commit is contained in:
Eike Ziller
2023-04-18 12:53:45 +02:00
15 changed files with 247 additions and 120 deletions

View File

@@ -51,6 +51,7 @@ namespace Android {
namespace Internal { namespace Internal {
static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done"); 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 int APP_START_TIMEOUT = 45000;
static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, 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; return pid;
} }
static void findProcessPID(QPromise<qint64> &promise, QStringList selector, static void findProcessPIDAndUser(QPromise<PidUserPair> &promise,
const QString &packageName, bool preNougat) QStringList selector,
const QString &packageName,
bool preNougat)
{ {
if (packageName.isEmpty()) if (packageName.isEmpty())
return; return;
@@ -108,8 +111,32 @@ static void findProcessPID(QPromise<qint64> &promise, QStringList selector,
} while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !promise.isCanceled()); } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !promise.isCanceled());
qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat; 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()) if (!promise.isCanceled())
promise.addResult(processPID); promise.addResult(PidUserPair(processPID, processUser));
} }
static void deleter(QProcess *p) static void deleter(QProcess *p)
@@ -325,13 +352,16 @@ bool AndroidRunnerWorker::uploadDebugServer(const QString &debugServerFileName)
return false; 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 // 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"; qCDebug(androidRunWorkerLog) << "Debug server copy from temp directory failed";
return false; 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."); QTC_ASSERT(ok, qCDebug(androidRunWorkerLog) << "Debug server chmod 777 failed.");
return true; return true;
} }
@@ -346,7 +376,11 @@ bool AndroidRunnerWorker::deviceFileExists(const QString &filePath)
bool AndroidRunnerWorker::packageFileExists(const QString &filePath) bool AndroidRunnerWorker::packageFileExists(const QString &filePath)
{ {
QString output; 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(); return success && !output.trimmed().isEmpty();
} }
@@ -513,62 +547,8 @@ void AndroidRunnerWorker::asyncStartHelper()
QStringList args({"shell", "am", "start"}); QStringList args({"shell", "am", "start"});
args << "-n" << m_intentName; args << "-n" << m_intentName;
if (m_useCppDebugger) { if (m_useCppDebugger)
args << "-D"; args << "-D";
// run-as <package-name> 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) { if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
// currently forward to same port on device and host // currently forward to same port on device and host
@@ -624,17 +604,79 @@ void AndroidRunnerWorker::asyncStartHelper()
} }
} }
void AndroidRunnerWorker::startNativeDebugging()
{
// run-as <package-name> 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, bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir,
const QString &debugServerFile, const QString &debugServerFile,
QString *errorStr) QString *errorStr)
{ {
QStringList adbArgs = {"shell", "run-as", m_packageName};
if (m_processUser > 0)
adbArgs << "--user" << QString::number(m_processUser);
if (m_useLldb) { if (m_useLldb) {
QString lldbServerErr; QString lldbServerErr;
QStringList lldbServerArgs = selector(); QStringList lldbServerArgs = selector();
lldbServerArgs << "shell" << "run-as" << m_packageName << debugServerFile lldbServerArgs += adbArgs;
<< "platform" lldbServerArgs << debugServerFile
// << "--server" // Can lead to zombie servers << "platform"
<< "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString()); // << "--server" // Can lead to zombie servers
<< "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString());
m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(lldbServerArgs, &lldbServerErr)); m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(lldbServerArgs, &lldbServerErr));
if (!m_debugServerProcess) { if (!m_debugServerProcess) {
@@ -648,12 +690,13 @@ bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir,
} else { } else {
QString gdbServerSocket = packageDir + "/debug-socket"; QString gdbServerSocket = packageDir + "/debug-socket";
runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket}); runAdb(adbArgs + QStringList({"rm", gdbServerSocket}));
QString gdbProcessErr; QString gdbProcessErr;
QStringList gdbServerErr = selector(); QStringList gdbServerErr = selector();
gdbServerErr << "shell" << "run-as" << m_packageName << debugServerFile gdbServerErr += adbArgs;
<< "--multi" << "+" + gdbServerSocket; gdbServerErr << debugServerFile
<< "--multi" << "+" + gdbServerSocket;
m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerErr, &gdbProcessErr)); m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerErr, &gdbProcessErr));
if (!m_debugServerProcess) { if (!m_debugServerProcess) {
@@ -683,9 +726,12 @@ void AndroidRunnerWorker::asyncStart()
{ {
asyncStartHelper(); asyncStartHelper();
m_pidFinder = Utils::onResultReady( m_pidFinder = Utils::onResultReady(Utils::asyncRun(findProcessPIDAndUser,
Utils::asyncRun(findProcessPID, selector(),m_packageName, m_isPreNougat), this, selector(),
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); m_packageName,
m_isPreNougat),
this,
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
} }
void AndroidRunnerWorker::asyncStop() 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 // Don't write to m_psProc from a different thread
QTC_ASSERT(QThread::currentThread() == thread(), return); QTC_ASSERT(QThread::currentThread() == thread(), return);
qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID
<< "to:" << pid; << "to:" << pid;
m_processPID = pid; m_processPID = pid;
m_processUser = user;
if (pid == -1) { if (pid == -1) {
emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.") emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.")
.arg(m_packageName)); .arg(m_packageName));
@@ -813,6 +862,8 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
for (const QString &entry: std::as_const(m_afterFinishAdbCommands)) for (const QString &entry: std::as_const(m_afterFinishAdbCommands))
runAdb(entry.split(' ', Qt::SkipEmptyParts)); runAdb(entry.split(' ', Qt::SkipEmptyParts));
} else { } else {
if (m_useCppDebugger)
startNativeDebugging();
// In debugging cases this will be funneled to the engine to actually start // In debugging cases this will be funneled to the engine to actually start
// and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. // and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
emit remoteProcessStarted(m_localDebugServerPort, m_qmlServer, m_processPID); emit remoteProcessStarted(m_localDebugServerPort, m_qmlServer, m_processPID);
@@ -824,7 +875,7 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
m_psIsAlive->setObjectName("IsAliveProcess"); m_psIsAlive->setObjectName("IsAliveProcess");
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
connect(m_psIsAlive.get(), &QProcess::finished, connect(m_psIsAlive.get(), &QProcess::finished,
this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, -1)); this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, PidUserPair(-1, -1)));
} }
} }

View File

@@ -10,6 +10,7 @@
#include <utils/port.h> #include <utils/port.h>
#include <QFuture> #include <QFuture>
#include <utility>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QProcess; class QProcess;
@@ -26,6 +27,8 @@ namespace Internal {
const int MIN_SOCKET_HANDSHAKE_PORT = 20001; const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
using PidUserPair = std::pair<qint64, qint64>;
class AndroidRunnerWorker : public QObject class AndroidRunnerWorker : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -61,6 +64,7 @@ signals:
private: private:
void asyncStartHelper(); void asyncStartHelper();
void startNativeDebugging();
bool startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr = nullptr); bool startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr = nullptr);
bool deviceFileExists(const QString &filePath); bool deviceFileExists(const QString &filePath);
bool packageFileExists(const QString& filePath); bool packageFileExists(const QString& filePath);
@@ -72,7 +76,7 @@ private:
Waiting, Waiting,
Settled Settled
}; };
void onProcessIdChanged(qint64 pid); void onProcessIdChanged(PidUserPair pidUser);
using Deleter = void (*)(QProcess *); using Deleter = void (*)(QProcess *);
// Create the processes and timer in the worker thread, for correct thread affinity // Create the processes and timer in the worker thread, for correct thread affinity
@@ -83,11 +87,12 @@ private:
QStringList m_afterFinishAdbCommands; QStringList m_afterFinishAdbCommands;
QStringList m_amStartExtraArgs; QStringList m_amStartExtraArgs;
qint64 m_processPID = -1; qint64 m_processPID = -1;
qint64 m_processUser = -1;
std::unique_ptr<QProcess, Deleter> m_adbLogcatProcess; std::unique_ptr<QProcess, Deleter> m_adbLogcatProcess;
std::unique_ptr<QProcess, Deleter> m_psIsAlive; std::unique_ptr<QProcess, Deleter> m_psIsAlive;
QByteArray m_stdoutBuffer; QByteArray m_stdoutBuffer;
QByteArray m_stderrBuffer; QByteArray m_stderrBuffer;
QFuture<qint64> m_pidFinder; QFuture<PidUserPair> m_pidFinder;
bool m_useCppDebugger = false; bool m_useCppDebugger = false;
bool m_useLldb = false; // FIXME: Un-implemented currently. bool m_useLldb = false; // FIXME: Un-implemented currently.
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;

View File

@@ -177,6 +177,7 @@ clang::format::FormatStyle qtcStyle()
style.Standard = FormatStyle::LS_Cpp11; style.Standard = FormatStyle::LS_Cpp11;
style.TabWidth = 4; style.TabWidth = 4;
style.UseTab = FormatStyle::UT_Never; style.UseTab = FormatStyle::UT_Never;
style.Standard = FormatStyle::LS_Auto;
return style; return style;
} }

View File

@@ -519,8 +519,8 @@ ClangTool::ClangTool(const QString &name, Utils::Id id)
mainLayout->addWidget(m_infoBarWidget); mainLayout->addWidget(m_infoBarWidget);
mainLayout->addWidget(m_diagnosticView); mainLayout->addWidget(m_diagnosticView);
auto mainWidget = new QWidget; auto mainWidget = new QWidget;
mainWidget->setObjectName("ClangTidyClazyIssuesView"); mainWidget->setObjectName(id.toString() + "IssuesView");
mainWidget->setWindowTitle(Tr::tr("Clang-Tidy and Clazy")); mainWidget->setWindowTitle(name);
mainWidget->setLayout(mainLayout); mainWidget->setLayout(mainLayout);
m_perspective.addWindow(mainWidget, Perspective::SplitVertical, nullptr); m_perspective.addWindow(mainWidget, Perspective::SplitVertical, nullptr);

View File

@@ -1256,6 +1256,12 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
const QString presetItemArg = presetItem.toArgument(); const QString presetItemArg = presetItem.toArgument();
const QString presetItemArgNoType = presetItemArg.left(presetItemArg.indexOf(":")); const QString presetItemArgNoType = presetItemArg.left(presetItemArg.indexOf(":"));
static QSet<QByteArray> defaultKitMacroValues{"CMAKE_C_COMPILER",
"CMAKE_CXX_COMPILER",
"QT_QMAKE_EXECUTABLE",
"QT_HOST_PATH",
"CMAKE_PROJECT_INCLUDE_BEFORE"};
auto it = std::find_if(initialArguments.begin(), auto it = std::find_if(initialArguments.begin(),
initialArguments.end(), initialArguments.end(),
[presetItemArgNoType](const QString &arg) { [presetItemArgNoType](const QString &arg) {
@@ -1266,6 +1272,11 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
QString &arg = *it; QString &arg = *it;
CMakeConfigItem argItem = CMakeConfigItem::fromString(arg.mid(2)); // skip -D 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 // For multi value path variables append the non Qt path
if (argItem.key == "CMAKE_PREFIX_PATH" || argItem.key == "CMAKE_FIND_ROOT_PATH") { if (argItem.key == "CMAKE_PREFIX_PATH" || argItem.key == "CMAKE_FIND_ROOT_PATH") {
QStringList presetValueList = presetItem.expandedValue(k).split(";"); QStringList presetValueList = presetItem.expandedValue(k).split(";");
@@ -1276,7 +1287,7 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
QStringList argItemPaths = argItemExpandedValue.split(";"); QStringList argItemPaths = argItemExpandedValue.split(";");
for (const QString &argPath : argItemPaths) { for (const QString &argPath : argItemPaths) {
const FilePath argFilePath = FilePath::fromString(argPath); const FilePath argFilePath = FilePath::fromString(argPath);
const FilePath presetFilePath = FilePath::fromString(presetPath); const FilePath presetFilePath = FilePath::fromUserInput(presetPath);
if (argFilePath == presetFilePath) if (argFilePath == presetFilePath)
return true; return true;
@@ -1291,12 +1302,10 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
} }
arg = argItem.toArgument(); arg = argItem.toArgument();
} else if (argItem.key == "CMAKE_C_COMPILER" || argItem.key == "CMAKE_CXX_COMPILER" } else if (argItem.key == "CMAKE_TOOLCHAIN_FILE") {
|| argItem.key == "QT_QMAKE_EXECUTABLE" || argItem.key == "QT_HOST_PATH"
|| argItem.key == "CMAKE_PROJECT_INCLUDE_BEFORE"
|| argItem.key == "CMAKE_TOOLCHAIN_FILE") {
const FilePath argFilePath = FilePath::fromString(argItem.expandedValue(k)); 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) if (argFilePath != presetFilePath)
arg = presetItem.toArgument(); arg = presetItem.toArgument();

View File

@@ -103,7 +103,9 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP
auto resolveInherits = [](auto &presetsHash, auto &presetsList) { auto resolveInherits = [](auto &presetsHash, auto &presetsList) {
Utils::sort(presetsList, [](const auto &left, const auto &right) { 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 false;
return true; return true;
}); });

View File

@@ -209,23 +209,11 @@ static CMakeConfig configurationFromPresetProbe(
? configurePreset.cacheVariables.value() ? configurePreset.cacheVariables.value()
: CMakeConfig(); : CMakeConfig();
auto expandCacheValue = const QString cmakeMakeProgram = cache.stringValueOf("CMAKE_MAKE_PROGRAM");
[configurePreset, env, sourceDirectory, cache](const QString &key) -> QString { const QString toolchainFile = cache.stringValueOf("CMAKE_TOOLCHAIN_FILE");
QString result = cache.stringValueOf(key.toUtf8()); const QString prefixPath = cache.stringValueOf("CMAKE_PREFIX_PATH");
CMakePresets::Macros::expand(configurePreset, env, sourceDirectory, result); const QString findRootPath = cache.stringValueOf("CMAKE_FIND_ROOT_PATH");
const QString qtHostPath = cache.stringValueOf("QT_HOST_PATH");
// 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");
if (!cmakeMakeProgram.isEmpty()) { if (!cmakeMakeProgram.isEmpty()) {
args.emplace_back( args.emplace_back(
@@ -662,6 +650,8 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
projectDirectory(), projectDirectory(),
data->buildDirectory); data->buildDirectory);
CMakePresets::Macros::updateCacheVariables(configurePreset, env, projectDirectory());
const CMakeConfig cache = configurePreset.cacheVariables const CMakeConfig cache = configurePreset.cacheVariables
? configurePreset.cacheVariables.value() ? configurePreset.cacheVariables.value()
: CMakeConfig(); : CMakeConfig();
@@ -710,10 +700,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
data->cmakePresetDefaultConfigHash data->cmakePresetDefaultConfigHash
= CMakeConfigurationKitAspect::computeDefaultConfigHash(config, data->cmakeBinary); = CMakeConfigurationKitAspect::computeDefaultConfigHash(config, data->cmakeBinary);
QString cmakeBuildType = QString::fromUtf8(cache.valueOf("CMAKE_BUILD_TYPE")); QByteArrayList buildConfigurationTypes = {cache.valueOf("CMAKE_BUILD_TYPE")};
CMakePresets::Macros::expand(configurePreset, env, projectDirectory(), cmakeBuildType);
QByteArrayList buildConfigurationTypes = {cmakeBuildType.toUtf8()};
if (buildConfigurationTypes.front().isEmpty()) { if (buildConfigurationTypes.front().isEmpty()) {
buildConfigurationTypes.clear(); buildConfigurationTypes.clear();
QByteArray buildConfigurationTypesString = cache.valueOf("CMAKE_CONFIGURATION_TYPES"); QByteArray buildConfigurationTypesString = cache.valueOf("CMAKE_CONFIGURATION_TYPES");

View File

@@ -4,6 +4,7 @@
#include "presetsmacros.h" #include "presetsmacros.h"
#include "presetsparser.h" #include "presetsparser.h"
#include <utils/algorithm.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/filepath.h> #include <utils/filepath.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -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<QByteArray> 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<class PresetType> template<class PresetType>
void expandConditionValues(const PresetType &preset, void expandConditionValues(const PresetType &preset,
const Utils::Environment &env, const Utils::Environment &env,

View File

@@ -60,6 +60,14 @@ void updateToolchainFile(PresetsDetails::ConfigurePreset &configurePreset,
void updateInstallDir(PresetsDetails::ConfigurePreset &configurePreset, void updateInstallDir(PresetsDetails::ConfigurePreset &configurePreset,
const Utils::Environment &env, const Utils::Environment &env,
const Utils::FilePath &sourceDirectory); 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 * Expands the condition values and then evaluates the condition object of the preset and returns
* the boolean result. * the boolean result.

View File

@@ -44,6 +44,7 @@ static bool debugExamples()
} }
static const char kSelectedExampleSetKey[] = "WelcomePage/SelectedExampleSet"; static const char kSelectedExampleSetKey[] = "WelcomePage/SelectedExampleSet";
Q_GLOBAL_STATIC_WITH_ARGS(QVersionNumber, minQtVersionForCategories, (6, 5, 1));
void ExampleSetModel::writeCurrentIdToSettings(int currentIndex) const void ExampleSetModel::writeCurrentIdToSettings(int currentIndex) const
{ {
@@ -117,7 +118,7 @@ void ExampleSetModel::recreateModel(const QtVersions &qtVersions)
beginResetModel(); beginResetModel();
clear(); clear();
QSet<QString> extraManifestDirs; QHash<FilePath, int> extraManifestDirs;
for (int i = 0; i < m_extraExampleSets.size(); ++i) { for (int i = 0; i < m_extraExampleSets.size(); ++i) {
const ExtraExampleSet &set = m_extraExampleSets.at(i); const ExtraExampleSet &set = m_extraExampleSets.at(i);
auto newItem = new QStandardItem(); auto newItem = new QStandardItem();
@@ -127,14 +128,19 @@ void ExampleSetModel::recreateModel(const QtVersions &qtVersions)
newItem->setData(i, Qt::UserRole + 3); newItem->setData(i, Qt::UserRole + 3);
appendRow(newItem); appendRow(newItem);
extraManifestDirs.insert(set.manifestPath); extraManifestDirs.insert(FilePath::fromUserInput(set.manifestPath), i);
} }
for (QtVersion *version : qtVersions) { for (QtVersion *version : qtVersions) {
// sanitize away qt versions that have already been added through extra sets // Sanitize away qt versions that have already been added through extra sets.
if (extraManifestDirs.contains(version->docsPath().toString())) { // 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()) { 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(); << version->displayName();
} }
continue; continue;
@@ -330,10 +336,11 @@ static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second)
} }
static QList<std::pair<Section, QList<ExampleItem *>>> getCategories( static QList<std::pair<Section, QList<ExampleItem *>>> getCategories(
const QList<ExampleItem *> &items) const QList<ExampleItem *> &items, bool sortIntoCategories)
{ {
static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples"); 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<ExampleItem *> other; QList<ExampleItem *> other;
QMap<QString, QList<ExampleItem *>> categoryMap; QMap<QString, QList<ExampleItem *>> categoryMap;
if (useCategories) { if (useCategories) {
@@ -378,10 +385,11 @@ void ExamplesViewController::updateExamples()
{ {
QString examplesInstallPath; QString examplesInstallPath;
QString demosInstallPath; QString demosInstallPath;
QVersionNumber qtVersion;
const QStringList sources = m_exampleSetModel->exampleSources(&examplesInstallPath, const QStringList sources = m_exampleSetModel->exampleSources(&examplesInstallPath,
&demosInstallPath); &demosInstallPath,
&qtVersion);
m_view->clear(); m_view->clear();
QList<ExampleItem *> items; QList<ExampleItem *> items;
@@ -418,7 +426,9 @@ void ExamplesViewController::updateExamples()
} }
} }
const QList<std::pair<Section, QList<ExampleItem *>>> sections = getCategories(items); const bool sortIntoCategories = qtVersion >= *minQtVersionForCategories;
const QList<std::pair<Section, QList<ExampleItem *>>> sections
= getCategories(items, sortIntoCategories);
for (int i = 0; i < sections.size(); ++i) { for (int i = 0; i < sections.size(); ++i) {
m_view->addSection(sections.at(i).first, m_view->addSection(sections.at(i).first,
static_container_cast<ListItem *>(sections.at(i).second)); static_container_cast<ListItem *>(sections.at(i).second));
@@ -486,7 +496,9 @@ QtVersion *ExampleSetModel::findHighestQtVersion(const QtVersions &versions) con
return newVersion; return newVersion;
} }
QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QString *demosInstallPath) QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath,
QString *demosInstallPath,
QVersionNumber *qtVersion)
{ {
QStringList sources; QStringList sources;
@@ -504,6 +516,8 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QStrin
manifestScanPath = exampleSet.manifestPath; manifestScanPath = exampleSet.manifestPath;
examplesPath = exampleSet.examplesPath; examplesPath = exampleSet.examplesPath;
demosPath = exampleSet.examplesPath; demosPath = exampleSet.examplesPath;
if (qtVersion)
*qtVersion = exampleSet.qtVersion;
} else if (currentType == ExampleSetModel::QtExampleSet) { } else if (currentType == ExampleSetModel::QtExampleSet) {
const int qtId = getQtId(m_selectedExampleSetIndex); const int qtId = getQtId(m_selectedExampleSetIndex);
const QtVersions versions = QtVersionManager::versions(); const QtVersions versions = QtVersionManager::versions();
@@ -512,6 +526,8 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QStrin
manifestScanPath = version->docsPath().toString(); manifestScanPath = version->docsPath().toString();
examplesPath = version->examplesPath().toString(); examplesPath = version->examplesPath().toString();
demosPath = version->demosPath().toString(); demosPath = version->demosPath().toString();
if (qtVersion)
*qtVersion = version->qtVersion();
break; break;
} }
} }

View File

@@ -28,6 +28,10 @@ public:
QString displayName; QString displayName;
QString manifestPath; QString manifestPath;
QString examplesPath; 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<ExtraExampleSet> pluginRegisteredExampleSets(); static QVector<ExtraExampleSet> pluginRegisteredExampleSets();
@@ -35,7 +39,9 @@ public:
int selectedExampleSet() const { return m_selectedExampleSetIndex; } int selectedExampleSet() const { return m_selectedExampleSetIndex; }
void selectExampleSet(int index); 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; bool selectedQtSupports(const Utils::Id &target) const;
signals: signals:

View File

@@ -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 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): Qt version for the respective toolchain with the components (if available):
- (Desktop) <toolchain> <bitness>, e.g. Desktop gcc 64-bit - (Desktop) <toolchain> <bitness>, e.g. Desktop gcc 64-bit
- Qt Quick Controls (if available)
- Qt Script (Qt5 only) - Qt Script (Qt5 only)
The exact versions and toolchains are: The exact versions and toolchains are:

View File

@@ -18,6 +18,7 @@ def main():
expectBuildToFail = [] expectBuildToFail = []
if platform.system() in ('Microsoft', 'Windows'): if platform.system() in ('Microsoft', 'Windows'):
expectConfigureToFail = [ Targets.DESKTOP_5_4_1_GCC ] # gcc 4.9 does not know C++17 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: for kit, config in availableConfigs:
selectBuildConfig(kit, config) selectBuildConfig(kit, config)

View File

@@ -35,7 +35,7 @@ def __checkKits__():
if llvmForBuild is not None: if llvmForBuild is not None:
internalClangExe = os.path.join(llvmForBuild, "bin", "clang") internalClangExe = os.path.join(llvmForBuild, "bin", "clang")
if platform.system() in ("Microsoft", "Windows"): if platform.system() in ("Microsoft", "Windows"):
internalClangExe.append(".exe") internalClangExe += ".exe"
if os.path.exists(internalClangExe): if os.path.exists(internalClangExe):
expectedCompilers.append(internalClangExe) expectedCompilers.append(internalClangExe)
foundCompilers = [] foundCompilers = []