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 {
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<qint64> &promise, QStringList selector,
const QString &packageName, bool preNougat)
static void findProcessPIDAndUser(QPromise<PidUserPair> &promise,
QStringList selector,
const QString &packageName,
bool preNougat)
{
if (packageName.isEmpty())
return;
@@ -108,8 +111,32 @@ static void findProcessPID(QPromise<qint64> &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 <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) {
// currently forward to same port on device and host
@@ -624,14 +604,76 @@ 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,
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
lldbServerArgs += adbArgs;
lldbServerArgs << debugServerFile
<< "platform"
// << "--server" // Can lead to zombie servers
<< "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString());
@@ -648,11 +690,12 @@ 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
gdbServerErr += adbArgs;
gdbServerErr << debugServerFile
<< "--multi" << "+" + gdbServerSocket;
m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerErr, &gdbProcessErr));
@@ -683,8 +726,11 @@ void AndroidRunnerWorker::asyncStart()
{
asyncStartHelper();
m_pidFinder = Utils::onResultReady(
Utils::asyncRun(findProcessPID, selector(),m_packageName, m_isPreNougat), this,
m_pidFinder = Utils::onResultReady(Utils::asyncRun(findProcessPIDAndUser,
selector(),
m_packageName,
m_isPreNougat),
this,
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
}
@@ -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)));
}
}

View File

@@ -10,6 +10,7 @@
#include <utils/port.h>
#include <QFuture>
#include <utility>
QT_BEGIN_NAMESPACE
class QProcess;
@@ -26,6 +27,8 @@ namespace Internal {
const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
using PidUserPair = std::pair<qint64, qint64>;
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<QProcess, Deleter> m_adbLogcatProcess;
std::unique_ptr<QProcess, Deleter> m_psIsAlive;
QByteArray m_stdoutBuffer;
QByteArray m_stderrBuffer;
QFuture<qint64> m_pidFinder;
QFuture<PidUserPair> m_pidFinder;
bool m_useCppDebugger = false;
bool m_useLldb = false; // FIXME: Un-implemented currently.
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -1256,6 +1256,12 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
const QString presetItemArg = presetItem.toArgument();
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(),
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();

View File

@@ -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;
});

View File

@@ -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<void *> 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<void *> 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");

View File

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

View File

@@ -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.

View File

@@ -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<QString> extraManifestDirs;
QHash<FilePath, int> 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<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");
const bool useCategories = qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES");
const bool useCategories = sortIntoCategories
|| qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES");
QList<ExampleItem *> other;
QMap<QString, QList<ExampleItem *>> 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<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) {
m_view->addSection(sections.at(i).first,
static_container_cast<ListItem *>(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;
}
}

View File

@@ -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<ExtraExampleSet> 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:

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

View File

@@ -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)

View File

@@ -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 = []