CMake: Use ServerModeReader to retrieve data

Change-Id: I415dbf7ca79c909eea23ef3dc3a1d87438e9f261
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Tobias Hunger
2016-10-17 14:38:38 +02:00
parent c75210034c
commit 36cfa32a86
6 changed files with 211 additions and 194 deletions

View File

@@ -80,17 +80,23 @@ BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) :
connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse);
}
BuildDirManager::~BuildDirManager() = default;
const Utils::FileName BuildDirManager::workDirectory() const
{
const Utils::FileName bdir = m_buildConfiguration->buildDirectory();
if (bdir.exists())
return bdir;
if (!m_tempDir)
m_tempDir.reset(new QTemporaryDir(QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX")));
if (!m_tempDir) {
const QString path = QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX");
m_tempDir.reset(new QTemporaryDir(path));
if (!m_tempDir->isValid())
emit errorOccured(tr("Failed to create temporary directory using template \"%1\".").arg(path));
}
return Utils::FileName::fromString(m_tempDir->path());
}
void BuildDirManager::updateReader()
void BuildDirManager::updateReaderType(std::function<void()> todo)
{
BuildDirReader::Parameters p(m_buildConfiguration);
p.buildDirectory = workDirectory();
@@ -106,184 +112,29 @@ void BuildDirManager::updateReader()
connect(m_reader.get(), &BuildDirReader::dirty, this, &BuildDirManager::becameDirty);
}
m_reader->setParameters(p);
}
bool BuildDirManager::isParsing() const
{
return m_reader && m_reader->isParsing();
}
void BuildDirManager::becameDirty()
{
if (isParsing())
return;
Target *t = m_buildConfiguration->target()->project()->activeTarget();
BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr;
if (bc != m_buildConfiguration)
return;
const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit());
if (!tool->isAutoRun())
return;
m_reparseTimer.start(1000);
}
void BuildDirManager::forceReparse()
{
if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration)
return;
CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit());
QTC_ASSERT(tool, return);
m_reader->stop();
m_reader->parse(true);
}
void BuildDirManager::resetData()
{
if (m_reader)
m_reader->resetData();
m_cmakeCache.clear();
m_reader.reset(nullptr);
}
bool BuildDirManager::updateCMakeStateBeforeBuild()
{
return m_reparseTimer.isActive();
}
bool BuildDirManager::persistCMakeState()
{
if (!m_tempDir)
return false;
const QString buildDir = m_buildConfiguration->buildDirectory().toString();
QDir dir(buildDir);
dir.mkpath(buildDir);
m_tempDir.reset(nullptr);
QTimer::singleShot(0, this, &BuildDirManager::parse); // make sure signals only happen afterwards!
return true;
}
void BuildDirManager::generateProjectTree(CMakeProjectNode *root)
{
QTC_ASSERT(m_reader, return);
m_reader->generateProjectTree(root);
}
QSet<Core::Id> BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
{
QTC_ASSERT(m_reader, return QSet<Core::Id>());
return m_reader->updateCodeModel(ppBuilder);
}
void BuildDirManager::parse()
{
updateReader();
checkConfiguration();
QTC_ASSERT(m_reader, return);
m_reader->parse(false);
}
void BuildDirManager::clearCache()
{
auto cmakeCache = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeCache.txt"));
auto cmakeFiles = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeFiles"));
const bool mustCleanUp = cmakeCache.exists() || cmakeFiles.exists();
if (!mustCleanUp)
return;
Utils::FileUtils::removeRecursively(cmakeCache);
Utils::FileUtils::removeRecursively(cmakeFiles);
forceReparse();
}
QList<CMakeBuildTarget> BuildDirManager::buildTargets() const
{
QTC_ASSERT(m_reader, return QList<CMakeBuildTarget>());
return m_reader->buildTargets();
}
CMakeConfig BuildDirManager::parsedConfiguration() const
{
QTC_ASSERT(m_reader, return m_cmakeCache);
if (m_cmakeCache.isEmpty())
m_cmakeCache = m_reader->parsedConfiguration();
return m_cmakeCache;
}
void BuildDirManager::checkConfiguration()
{
if (m_tempDir) // always throw away changes in the tmpdir!
return;
updateReader();
Kit *k = m_buildConfiguration->target()->kit();
const CMakeConfig cache = m_reader->parsedConfiguration();
if (cache.isEmpty())
return; // No cache file yet.
CMakeConfig newConfig;
QSet<QString> changedKeys;
QSet<QString> removedKeys;
foreach (const CMakeConfigItem &iBc, m_buildConfiguration->cmakeConfiguration()) {
const CMakeConfigItem &iCache
= Utils::findOrDefault(cache, [&iBc](const CMakeConfigItem &i) { return i.key == iBc.key; });
if (iCache.isNull()) {
removedKeys << QString::fromUtf8(iBc.key);
} else if (QString::fromUtf8(iCache.value) != iBc.expandedValue(k)) {
changedKeys << QString::fromUtf8(iBc.key);
newConfig.append(iCache);
} else {
newConfig.append(iBc);
}
}
if (!changedKeys.isEmpty() || !removedKeys.isEmpty()) {
QSet<QString> total = removedKeys + changedKeys;
QStringList keyList = total.toList();
Utils::sort(keyList);
QString table = QLatin1String("<table>");
foreach (const QString &k, keyList) {
QString change;
if (removedKeys.contains(k))
change = tr("<removed>");
if (m_reader->isReady())
todo();
else
change = QString::fromUtf8(CMakeConfigItem::valueOf(k.toUtf8(), cache)).trimmed();
if (change.isEmpty())
change = tr("<empty>");
table += QString::fromLatin1("\n<tr><td>%1</td><td>%2</td></tr>").arg(k).arg(change.toHtmlEscaped());
}
table += QLatin1String("\n</table>");
QPointer<QMessageBox> box = new QMessageBox(Core::ICore::mainWindow());
box->setText(tr("CMake configuration has changed on disk."));
box->setInformativeText(tr("The CMakeCache.txt file has changed: %1").arg(table));
auto *defaultButton = box->addButton(tr("Overwrite Changes in CMake"), QMessageBox::RejectRole);
box->addButton(tr("Apply Changes to Project"), QMessageBox::AcceptRole);
box->setDefaultButton(defaultButton);
int ret = box->exec();
if (ret == QMessageBox::Apply) {
m_buildConfiguration->setCMakeConfiguration(newConfig);
updateReader(); // Apply changes to reader
}
}
connect(m_reader.get(), &BuildDirReader::isReadyNow, this, todo);
}
void BuildDirManager::maybeForceReparse()
void BuildDirManager::updateReaderData()
{
BuildDirReader::Parameters p(m_buildConfiguration);
p.buildDirectory = workDirectory();
m_reader->setParameters(p);
}
void BuildDirManager::parseOnceReaderReady(bool force)
{
checkConfiguration();
m_reader->stop();
m_reader->parse(force);
}
void BuildDirManager::maybeForceReparseOnceReaderReady()
{
checkConfiguration();
@@ -346,8 +197,181 @@ void BuildDirManager::maybeForceReparse()
// The critical keys *must* be set in cmake configuration, so those were already
// handled above.
if (mustReparse || kcit != targetConfig.constEnd())
parseOnceReaderReady(true);
}
bool BuildDirManager::isParsing() const
{
return m_reader && m_reader->isParsing();
}
void BuildDirManager::becameDirty()
{
if (isParsing())
return;
Target *t = m_buildConfiguration->target()->project()->activeTarget();
BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr;
if (bc != m_buildConfiguration)
return;
const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit());
if (!tool->isAutoRun())
return;
m_reparseTimer.start(1000);
}
void BuildDirManager::forceReparse()
{
if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration)
return;
CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit());
QTC_ASSERT(tool, return);
updateReaderType([this]() { parseOnceReaderReady(true); });
}
void BuildDirManager::resetData()
{
if (m_reader)
m_reader->resetData();
m_cmakeCache.clear();
m_reader.reset(nullptr);
}
bool BuildDirManager::updateCMakeStateBeforeBuild()
{
return m_reparseTimer.isActive();
}
bool BuildDirManager::persistCMakeState()
{
if (!m_tempDir)
return false;
const QString buildDir = m_buildConfiguration->buildDirectory().toString();
QDir dir(buildDir);
dir.mkpath(buildDir);
m_tempDir.reset(nullptr);
QTimer::singleShot(0, this, &BuildDirManager::parse); // make sure signals only happen afterwards!
return true;
}
void BuildDirManager::generateProjectTree(CMakeProjectNode *root)
{
QTC_ASSERT(m_reader, return);
m_reader->generateProjectTree(root);
}
QSet<Core::Id> BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
{
QTC_ASSERT(m_reader, return QSet<Core::Id>());
return m_reader->updateCodeModel(ppBuilder);
}
void BuildDirManager::parse()
{
updateReaderType([this]() { parseOnceReaderReady(false); });
}
void BuildDirManager::clearCache()
{
auto cmakeCache = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeCache.txt"));
auto cmakeFiles = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeFiles"));
const bool mustCleanUp = cmakeCache.exists() || cmakeFiles.exists();
if (!mustCleanUp)
return;
Utils::FileUtils::removeRecursively(cmakeCache);
Utils::FileUtils::removeRecursively(cmakeFiles);
forceReparse();
}
QList<CMakeBuildTarget> BuildDirManager::buildTargets() const
{
QTC_ASSERT(m_reader, return QList<CMakeBuildTarget>());
return m_reader->buildTargets();
}
CMakeConfig BuildDirManager::parsedConfiguration() const
{
QTC_ASSERT(m_reader, return m_cmakeCache);
if (m_cmakeCache.isEmpty())
m_cmakeCache = m_reader->parsedConfiguration();
return m_cmakeCache;
}
void BuildDirManager::checkConfiguration()
{
if (m_tempDir) // always throw away changes in the tmpdir!
return;
Kit *k = m_buildConfiguration->target()->kit();
const CMakeConfig cache = m_reader->parsedConfiguration();
if (cache.isEmpty())
return; // No cache file yet.
CMakeConfig newConfig;
QSet<QString> changedKeys;
QSet<QString> removedKeys;
foreach (const CMakeConfigItem &iBc, m_buildConfiguration->cmakeConfiguration()) {
const CMakeConfigItem &iCache
= Utils::findOrDefault(cache, [&iBc](const CMakeConfigItem &i) { return i.key == iBc.key; });
if (iCache.isNull()) {
removedKeys << QString::fromUtf8(iBc.key);
} else if (QString::fromUtf8(iCache.value) != iBc.expandedValue(k)) {
changedKeys << QString::fromUtf8(iBc.key);
newConfig.append(iCache);
} else {
newConfig.append(iBc);
}
}
if (!changedKeys.isEmpty() || !removedKeys.isEmpty()) {
QSet<QString> total = removedKeys + changedKeys;
QStringList keyList = total.toList();
Utils::sort(keyList);
QString table = QLatin1String("<table>");
foreach (const QString &k, keyList) {
QString change;
if (removedKeys.contains(k))
change = tr("<removed>");
else
change = QString::fromUtf8(CMakeConfigItem::valueOf(k.toUtf8(), cache)).trimmed();
if (change.isEmpty())
change = tr("<empty>");
table += QString::fromLatin1("\n<tr><td>%1</td><td>%2</td></tr>").arg(k).arg(change.toHtmlEscaped());
}
table += QLatin1String("\n</table>");
QPointer<QMessageBox> box = new QMessageBox(Core::ICore::mainWindow());
box->setText(tr("CMake configuration has changed on disk."));
box->setInformativeText(tr("The CMakeCache.txt file has changed: %1").arg(table));
auto *defaultButton = box->addButton(tr("Overwrite Changes in CMake"), QMessageBox::RejectRole);
box->addButton(tr("Apply Changes to Project"), QMessageBox::AcceptRole);
box->setDefaultButton(defaultButton);
int ret = box->exec();
if (ret == QMessageBox::Apply) {
m_buildConfiguration->setCMakeConfiguration(newConfig);
updateReaderData(); // Apply changes to reader
}
}
}
void BuildDirManager::maybeForceReparse()
{
updateReaderType([this]() { maybeForceReparseOnceReaderReady(); });
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -34,6 +34,7 @@
#include <QTemporaryDir>
#include <QTimer>
#include <functional>
#include <memory>
namespace CppTools { class ProjectPartBuilder; }
@@ -59,6 +60,7 @@ class BuildDirManager : public QObject
public:
BuildDirManager(CMakeBuildConfiguration *bc);
~BuildDirManager() final;
bool isParsing() const;
@@ -75,8 +77,6 @@ public:
QList<CMakeBuildTarget> buildTargets() const;
CMakeConfig parsedConfiguration() const;
void checkConfiguration();
signals:
void configurationStarted() const;
void dataAvailable() const;
@@ -89,7 +89,13 @@ protected:
const Utils::FileName workDirectory() const;
private:
void updateReader();
void checkConfiguration();
void updateReaderType(std::function<void()> todo);
void updateReaderData();
void parseOnceReaderReady(bool force);
void maybeForceReparseOnceReaderReady();
void parse();

View File

@@ -27,6 +27,7 @@
#include "cmakebuildconfiguration.h"
#include "cmakekitinformation.h"
#include "servermodereader.h"
#include "tealeafreader.h"
#include <projectexplorer/kitinformation.h>
@@ -82,7 +83,8 @@ BuildDirReader::Parameters::Parameters(const BuildDirReader::Parameters &other)
BuildDirReader *BuildDirReader::createReader(const BuildDirReader::Parameters &p)
{
Q_UNUSED(p); // FIXME
if (p.cmakeHasServerMode)
return new ServerModeReader;
return new TeaLeafReader;
}

View File

@@ -185,7 +185,6 @@ void CMakeBuildConfiguration::runCMake()
if (!m_buildDirManager || m_buildDirManager->isParsing())
return;
m_buildDirManager->checkConfiguration();
m_buildDirManager->forceReparse();
}

View File

@@ -432,20 +432,7 @@ void ServerModeReader::extractCacheData(const QVariantMap &data)
CMakeConfigItem item;
item.key = eData.value("key").toByteArray();
item.value = eData.value("value").toByteArray();
item.type = CMakeConfigItem::STRING;
const QByteArray typeId = eData.value("type").toByteArray();
if (typeId == "FILEPATH")
item.type = CMakeConfigItem::STRING;
else if (typeId == "PATH")
item.type = CMakeConfigItem::PATH;
else if (typeId == "BOOL")
item.type = CMakeConfigItem::BOOL;
else if (typeId == "INTERNAL")
item.type = CMakeConfigItem::INTERNAL;
else if (typeId == "STRING")
item.type = CMakeConfigItem::STRING;
else if (typeId == "STATIC")
item.type = CMakeConfigItem::STATIC;
item.type = CMakeConfigItem::typeStringToType(eData.value("type").toByteArray());
const QVariantMap properties = eData.value("properties").toMap();
item.isAdvanced = properties.value("ADVANCED", false).toBool();
item.documentation = properties.value("HELPSTRING").toByteArray();

View File

@@ -164,8 +164,7 @@ TeaLeafReader::~TeaLeafReader()
bool TeaLeafReader::isCompatible(const BuildDirReader::Parameters &p)
{
Q_UNUSED(p);
return true; // FIXME: Needs to take server mode into account!
return !p.cmakeHasServerMode;
}
void TeaLeafReader::resetData()