forked from qt-creator/qt-creator
Fix UI freeze while building with multiple cores / distributed build
When parallelizing the build, and a verbose compiler error/warning is printed (i.e. a template overload error), the IDE wastes a lot of time parsing always the same file patterns, and that freezes completely the UI. The only workaround is to kill the build process using a terminal. Implement a file cache, thus mitigating the freeze issue. Change-Id: Ibcbdb6e6161af7cef424e90f7cfdc2fc34f6d7c1 Reviewed-by: Tobias Hunger <tobias.hunger@qt.io> Reviewed-by: Antonio Di Monaco <tony@becrux.com> Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
@@ -36,6 +36,13 @@
|
||||
#include <QTimer>
|
||||
#include <QDir>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
const int CACHE_SOFT_LIMIT = 500;
|
||||
const int CACHE_HARD_LIMIT = 1000;
|
||||
} // namespace
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
/*!
|
||||
@@ -153,6 +160,12 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b)
|
||||
bool AbstractProcessStep::init(QList<const BuildStep *> &earlierSteps)
|
||||
{
|
||||
Q_UNUSED(earlierSteps);
|
||||
|
||||
m_candidates.clear();
|
||||
const Utils::FileNameList fl = project()->files(Project::AllFiles);
|
||||
for (const Utils::FileName &file : fl)
|
||||
m_candidates[file.fileName()].push_back(file);
|
||||
|
||||
return !m_process;
|
||||
}
|
||||
|
||||
@@ -365,42 +378,46 @@ void AbstractProcessStep::taskAdded(const Task &task, int linkedOutputLines, int
|
||||
|
||||
Task editable(task);
|
||||
QString filePath = task.file.toString();
|
||||
if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) {
|
||||
|
||||
auto it = m_filesCache.find(filePath);
|
||||
if (it != m_filesCache.end()) {
|
||||
editable.file = it.value().first;
|
||||
it.value().second = ++m_cacheCounter;
|
||||
} else if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) {
|
||||
// We have no save way to decide which file in which subfolder
|
||||
// is meant. Therefore we apply following heuristics:
|
||||
// 1. Check if file is unique in whole project
|
||||
// 2. Otherwise try again without any ../
|
||||
// 3. give up.
|
||||
|
||||
QList<QFileInfo> possibleFiles;
|
||||
QString fileName = Utils::FileName::fromString(filePath).fileName();
|
||||
foreach (const Utils::FileName &file, project()->files(Project::AllFiles)) {
|
||||
QFileInfo candidate = file.toFileInfo();
|
||||
if (candidate.fileName() == fileName)
|
||||
possibleFiles << candidate;
|
||||
}
|
||||
QString sourceFilePath = filePath;
|
||||
Utils::FileNameList possibleFiles = m_candidates.value(Utils::FileName::fromString(filePath).fileName());
|
||||
|
||||
if (possibleFiles.count() == 1) {
|
||||
editable.file = Utils::FileName(possibleFiles.first());
|
||||
editable.file = possibleFiles.first();
|
||||
} else {
|
||||
// More then one filename, so do a better compare
|
||||
// Chop of any "../"
|
||||
while (filePath.startsWith("../"))
|
||||
filePath.remove(0, 3);
|
||||
|
||||
int count = 0;
|
||||
QString possibleFilePath;
|
||||
foreach (const QFileInfo &fi, possibleFiles) {
|
||||
if (fi.filePath().endsWith(filePath)) {
|
||||
possibleFilePath = fi.filePath();
|
||||
Utils::FileName possibleFilePath;
|
||||
foreach (const Utils::FileName &fn, possibleFiles) {
|
||||
if (fn.endsWith(filePath)) {
|
||||
possibleFilePath = fn;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count == 1)
|
||||
editable.file = Utils::FileName::fromString(possibleFilePath);
|
||||
editable.file = possibleFilePath;
|
||||
else
|
||||
qWarning() << "Could not find absolute location of file " << filePath;
|
||||
}
|
||||
|
||||
insertInCache(sourceFilePath, editable.file);
|
||||
}
|
||||
|
||||
emit addTask(editable, linkedOutputLines, skipLines);
|
||||
}
|
||||
|
||||
@@ -425,5 +442,25 @@ void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus)
|
||||
for (const QString &l : stdOutLine.split('\n'))
|
||||
stdError(l);
|
||||
|
||||
purgeCache(true);
|
||||
cleanUp(process);
|
||||
}
|
||||
|
||||
void AbstractProcessStep::purgeCache(bool useSoftLimit)
|
||||
{
|
||||
const int limit = useSoftLimit ? CACHE_SOFT_LIMIT : CACHE_HARD_LIMIT;
|
||||
if (m_filesCache.size() <= limit)
|
||||
return;
|
||||
|
||||
const quint64 minCounter = m_cacheCounter - static_cast<quint64>(limit);
|
||||
std::remove_if(m_filesCache.begin(), m_filesCache.end(),
|
||||
[minCounter](const QPair<Utils::FileName, quint64> &entry) {
|
||||
return entry.second <= minCounter;
|
||||
});
|
||||
}
|
||||
|
||||
void AbstractProcessStep::insertInCache(const QString &relativePath, const Utils::FileName &absPath)
|
||||
{
|
||||
purgeCache(false);
|
||||
m_filesCache.insert(relativePath, qMakePair(absPath, ++m_cacheCounter));
|
||||
}
|
||||
|
@@ -31,9 +31,12 @@
|
||||
#include <projectexplorer/ioutputparser.h>
|
||||
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QHash>
|
||||
#include <QPair>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -87,11 +90,17 @@ private:
|
||||
|
||||
void outputAdded(const QString &string, BuildStep::OutputFormat format);
|
||||
|
||||
void purgeCache(bool useSoftLimit);
|
||||
void insertInCache(const QString &relativePath, const Utils::FileName &absPath);
|
||||
|
||||
QTimer m_timer;
|
||||
QFutureInterface<bool> *m_futureInterface = nullptr;
|
||||
std::unique_ptr<Utils::QtcProcess> m_process;
|
||||
std::unique_ptr<IOutputParser> m_outputParserChain;
|
||||
ProcessParameters m_param;
|
||||
QHash<QString, QPair<Utils::FileName, quint64>> m_filesCache;
|
||||
QHash<QString, Utils::FileNameList> m_candidates;
|
||||
quint64 m_cacheCounter = 0;
|
||||
bool m_ignoreReturnValue = false;
|
||||
bool m_skipFlush = false;
|
||||
};
|
||||
|
Reference in New Issue
Block a user