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 <QTimer>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int CACHE_SOFT_LIMIT = 500;
|
||||||
|
const int CACHE_HARD_LIMIT = 1000;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -153,6 +160,12 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b)
|
|||||||
bool AbstractProcessStep::init(QList<const BuildStep *> &earlierSteps)
|
bool AbstractProcessStep::init(QList<const BuildStep *> &earlierSteps)
|
||||||
{
|
{
|
||||||
Q_UNUSED(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;
|
return !m_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,42 +378,46 @@ void AbstractProcessStep::taskAdded(const Task &task, int linkedOutputLines, int
|
|||||||
|
|
||||||
Task editable(task);
|
Task editable(task);
|
||||||
QString filePath = task.file.toString();
|
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
|
// We have no save way to decide which file in which subfolder
|
||||||
// is meant. Therefore we apply following heuristics:
|
// is meant. Therefore we apply following heuristics:
|
||||||
// 1. Check if file is unique in whole project
|
// 1. Check if file is unique in whole project
|
||||||
// 2. Otherwise try again without any ../
|
// 2. Otherwise try again without any ../
|
||||||
// 3. give up.
|
// 3. give up.
|
||||||
|
|
||||||
QList<QFileInfo> possibleFiles;
|
QString sourceFilePath = filePath;
|
||||||
QString fileName = Utils::FileName::fromString(filePath).fileName();
|
Utils::FileNameList possibleFiles = m_candidates.value(Utils::FileName::fromString(filePath).fileName());
|
||||||
foreach (const Utils::FileName &file, project()->files(Project::AllFiles)) {
|
|
||||||
QFileInfo candidate = file.toFileInfo();
|
|
||||||
if (candidate.fileName() == fileName)
|
|
||||||
possibleFiles << candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (possibleFiles.count() == 1) {
|
if (possibleFiles.count() == 1) {
|
||||||
editable.file = Utils::FileName(possibleFiles.first());
|
editable.file = possibleFiles.first();
|
||||||
} else {
|
} else {
|
||||||
// More then one filename, so do a better compare
|
// More then one filename, so do a better compare
|
||||||
// Chop of any "../"
|
// Chop of any "../"
|
||||||
while (filePath.startsWith("../"))
|
while (filePath.startsWith("../"))
|
||||||
filePath.remove(0, 3);
|
filePath.remove(0, 3);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
QString possibleFilePath;
|
Utils::FileName possibleFilePath;
|
||||||
foreach (const QFileInfo &fi, possibleFiles) {
|
foreach (const Utils::FileName &fn, possibleFiles) {
|
||||||
if (fi.filePath().endsWith(filePath)) {
|
if (fn.endsWith(filePath)) {
|
||||||
possibleFilePath = fi.filePath();
|
possibleFilePath = fn;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
editable.file = Utils::FileName::fromString(possibleFilePath);
|
editable.file = possibleFilePath;
|
||||||
else
|
else
|
||||||
qWarning() << "Could not find absolute location of file " << filePath;
|
qWarning() << "Could not find absolute location of file " << filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertInCache(sourceFilePath, editable.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit addTask(editable, linkedOutputLines, skipLines);
|
emit addTask(editable, linkedOutputLines, skipLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,5 +442,25 @@ void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus)
|
|||||||
for (const QString &l : stdOutLine.split('\n'))
|
for (const QString &l : stdOutLine.split('\n'))
|
||||||
stdError(l);
|
stdError(l);
|
||||||
|
|
||||||
|
purgeCache(true);
|
||||||
cleanUp(process);
|
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 <projectexplorer/ioutputparser.h>
|
||||||
|
|
||||||
#include <utils/qtcprocess.h>
|
#include <utils/qtcprocess.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -87,11 +90,17 @@ private:
|
|||||||
|
|
||||||
void outputAdded(const QString &string, BuildStep::OutputFormat format);
|
void outputAdded(const QString &string, BuildStep::OutputFormat format);
|
||||||
|
|
||||||
|
void purgeCache(bool useSoftLimit);
|
||||||
|
void insertInCache(const QString &relativePath, const Utils::FileName &absPath);
|
||||||
|
|
||||||
QTimer m_timer;
|
QTimer m_timer;
|
||||||
QFutureInterface<bool> *m_futureInterface = nullptr;
|
QFutureInterface<bool> *m_futureInterface = nullptr;
|
||||||
std::unique_ptr<Utils::QtcProcess> m_process;
|
std::unique_ptr<Utils::QtcProcess> m_process;
|
||||||
std::unique_ptr<IOutputParser> m_outputParserChain;
|
std::unique_ptr<IOutputParser> m_outputParserChain;
|
||||||
ProcessParameters m_param;
|
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_ignoreReturnValue = false;
|
||||||
bool m_skipFlush = false;
|
bool m_skipFlush = false;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user