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);
if (m_reader->isReady())
todo();
else
connect(m_reader.get(), &BuildDirReader::isReadyNow, this, todo);
}
bool BuildDirManager::isParsing() const
void BuildDirManager::updateReaderData()
{
return m_reader && m_reader->isParsing();
BuildDirReader::Parameters p(m_buildConfiguration);
p.buildDirectory = workDirectory();
m_reader->setParameters(p);
}
void BuildDirManager::becameDirty()
void BuildDirManager::parseOnceReaderReady(bool force)
{
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);
m_reader->stop();
m_reader->parse(force);
}
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>");
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
}
}
}
void BuildDirManager::maybeForceReparse()
void BuildDirManager::maybeForceReparseOnceReaderReady()
{
checkConfiguration();
@@ -346,7 +197,180 @@ void BuildDirManager::maybeForceReparse()
// The critical keys *must* be set in cmake configuration, so those were already
// handled above.
if (mustReparse || kcit != targetConfig.constEnd())
forceReparse();
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