2016-01-20 12:19:16 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "builddirmanager.h"
|
2016-02-24 18:00:24 +01:00
|
|
|
#include "cmakebuildconfiguration.h"
|
2016-01-20 12:19:16 +01:00
|
|
|
#include "cmakekitinformation.h"
|
2016-01-22 09:43:09 +01:00
|
|
|
#include "cmakeparser.h"
|
2016-01-20 12:19:16 +01:00
|
|
|
#include "cmakeprojectmanager.h"
|
2016-10-06 11:31:15 +02:00
|
|
|
#include "cmakeprojectnodes.h"
|
2016-01-20 12:19:16 +01:00
|
|
|
#include "cmaketool.h"
|
|
|
|
|
|
2016-05-13 16:19:31 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2016-10-06 11:31:15 +02:00
|
|
|
#include <coreplugin/documentmanager.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <coreplugin/messagemanager.h>
|
2016-10-06 15:55:55 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2016-01-22 10:49:26 +01:00
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <projectexplorer/kit.h>
|
2016-02-24 18:00:24 +01:00
|
|
|
#include <projectexplorer/project.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <projectexplorer/projectnodes.h>
|
2016-02-24 18:00:24 +01:00
|
|
|
#include <projectexplorer/target.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <projectexplorer/taskhub.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
2016-02-15 15:50:26 +01:00
|
|
|
#include <utils/fileutils.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2016-01-22 10:49:26 +01:00
|
|
|
#include <utils/qtcprocess.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <utils/synchronousprocess.h>
|
|
|
|
|
|
|
|
|
|
#include <QDateTime>
|
2016-01-26 17:26:46 +01:00
|
|
|
#include <QFile>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <QFileInfo>
|
2016-05-13 16:19:31 +02:00
|
|
|
#include <QMessageBox>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <QRegularExpression>
|
|
|
|
|
#include <QSet>
|
|
|
|
|
#include <QTemporaryDir>
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
// Helper:
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
namespace CMakeProjectManager {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
static QStringList toArguments(const CMakeConfig &config, const Kit *k) {
|
2016-03-16 13:39:41 +01:00
|
|
|
return Utils::transform(config, [k](const CMakeConfigItem &i) -> QString {
|
2016-05-28 23:09:14 +03:00
|
|
|
QString a = QString::fromLatin1("-D");
|
|
|
|
|
a.append(QString::fromUtf8(i.key));
|
|
|
|
|
switch (i.type) {
|
|
|
|
|
case CMakeConfigItem::FILEPATH:
|
|
|
|
|
a.append(QLatin1String(":FILEPATH="));
|
|
|
|
|
break;
|
|
|
|
|
case CMakeConfigItem::PATH:
|
|
|
|
|
a.append(QLatin1String(":PATH="));
|
|
|
|
|
break;
|
|
|
|
|
case CMakeConfigItem::BOOL:
|
|
|
|
|
a.append(QLatin1String(":BOOL="));
|
|
|
|
|
break;
|
|
|
|
|
case CMakeConfigItem::STRING:
|
|
|
|
|
a.append(QLatin1String(":STRING="));
|
|
|
|
|
break;
|
|
|
|
|
case CMakeConfigItem::INTERNAL:
|
|
|
|
|
a.append(QLatin1String(":INTERNAL="));
|
|
|
|
|
break;
|
2016-05-28 23:10:27 +03:00
|
|
|
case CMakeConfigItem::STATIC:
|
|
|
|
|
a.append(QLatin1String(":STATIC="));
|
|
|
|
|
break;
|
2016-05-28 23:09:14 +03:00
|
|
|
}
|
|
|
|
|
a.append(i.expandedValue(k));
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-05-28 23:09:14 +03:00
|
|
|
return a;
|
|
|
|
|
});
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
// BuildDirManager:
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
2016-05-13 16:19:31 +02:00
|
|
|
BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) :
|
2016-06-27 14:49:35 +02:00
|
|
|
m_buildConfiguration(bc)
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
2016-02-24 18:00:24 +01:00
|
|
|
QTC_ASSERT(bc, return);
|
|
|
|
|
m_projectName = sourceDirectory().fileName();
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-02-15 10:48:46 +01:00
|
|
|
m_reparseTimer.setSingleShot(true);
|
2016-10-06 15:55:55 +02:00
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse);
|
2016-10-06 15:55:55 +02:00
|
|
|
connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave,
|
|
|
|
|
this, &BuildDirManager::handleDocumentSaves);
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BuildDirManager::~BuildDirManager()
|
|
|
|
|
{
|
2016-03-16 16:33:34 +01:00
|
|
|
stopProcess();
|
2016-02-24 18:00:24 +01:00
|
|
|
resetData();
|
2016-10-06 11:31:15 +02:00
|
|
|
qDeleteAll(m_watchedFiles);
|
2016-02-25 14:18:05 +01:00
|
|
|
delete m_tempDir;
|
2016-02-24 18:00:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
const Kit *BuildDirManager::kit() const
|
2016-02-24 18:00:24 +01:00
|
|
|
{
|
|
|
|
|
return m_buildConfiguration->target()->kit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Utils::FileName BuildDirManager::buildDirectory() const
|
|
|
|
|
{
|
|
|
|
|
return m_buildConfiguration->buildDirectory();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
const Utils::FileName BuildDirManager::workDirectory() const
|
|
|
|
|
{
|
|
|
|
|
const Utils::FileName bdir = buildDirectory();
|
|
|
|
|
if (bdir.exists())
|
|
|
|
|
return bdir;
|
|
|
|
|
if (m_tempDir)
|
|
|
|
|
return Utils::FileName::fromString(m_tempDir->path());
|
|
|
|
|
return bdir;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
const Utils::FileName BuildDirManager::sourceDirectory() const
|
|
|
|
|
{
|
|
|
|
|
return m_buildConfiguration->target()->project()->projectDirectory();
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-10 17:31:30 +01:00
|
|
|
const CMakeConfig BuildDirManager::intendedConfiguration() const
|
2016-02-24 18:00:24 +01:00
|
|
|
{
|
|
|
|
|
return m_buildConfiguration->cmakeConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BuildDirManager::isParsing() const
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
|
|
|
|
if (m_cmakeProcess)
|
|
|
|
|
return m_cmakeProcess->state() != QProcess::NotRunning;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-27 14:49:35 +02:00
|
|
|
void BuildDirManager::cmakeFilesChanged()
|
|
|
|
|
{
|
|
|
|
|
if (isParsing())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit());
|
|
|
|
|
if (!tool->isAutoRun())
|
|
|
|
|
return;
|
|
|
|
|
|
2016-10-06 15:55:55 +02:00
|
|
|
m_reparseTimer.start(1000);
|
2016-06-27 14:49:35 +02:00
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
void BuildDirManager::forceReparse()
|
|
|
|
|
{
|
2016-03-16 16:33:54 +01:00
|
|
|
if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
stopProcess();
|
2016-02-15 10:48:46 +01:00
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
CMakeTool *tool = CMakeKitInformation::cmakeTool(kit());
|
2016-01-20 12:19:16 +01:00
|
|
|
QTC_ASSERT(tool, return);
|
|
|
|
|
|
2016-09-27 14:53:51 +02:00
|
|
|
startCMake(tool, CMakeGeneratorKitInformation::generatorArguments(kit()), intendedConfiguration());
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
void BuildDirManager::resetData()
|
2016-01-27 10:22:07 +01:00
|
|
|
{
|
2016-02-24 18:00:24 +01:00
|
|
|
m_hasData = false;
|
|
|
|
|
|
2016-05-13 13:34:18 +02:00
|
|
|
m_cmakeCache.clear();
|
2016-02-24 18:00:24 +01:00
|
|
|
m_projectName.clear();
|
|
|
|
|
m_buildTargets.clear();
|
|
|
|
|
qDeleteAll(m_files);
|
|
|
|
|
m_files.clear();
|
2016-01-27 10:22:07 +01:00
|
|
|
}
|
|
|
|
|
|
2016-10-06 15:55:55 +02:00
|
|
|
bool BuildDirManager::updateCMakeStateBeforeBuild()
|
|
|
|
|
{
|
|
|
|
|
return m_reparseTimer.isActive();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
bool BuildDirManager::persistCMakeState()
|
|
|
|
|
{
|
|
|
|
|
if (!m_tempDir)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
QDir dir(buildDirectory().toString());
|
|
|
|
|
dir.mkpath(buildDirectory().toString());
|
|
|
|
|
|
|
|
|
|
delete m_tempDir;
|
|
|
|
|
m_tempDir = nullptr;
|
|
|
|
|
|
2016-05-23 11:38:06 +02:00
|
|
|
resetData();
|
|
|
|
|
QTimer::singleShot(0, this, &BuildDirManager::parse); // make sure signals only happen afterwards!
|
2016-02-25 14:18:05 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
void BuildDirManager::generateProjectTree(CMakeProjectNode *root)
|
|
|
|
|
{
|
|
|
|
|
root->setDisplayName(m_projectName);
|
|
|
|
|
|
|
|
|
|
// Delete no longer necessary file watcher:
|
|
|
|
|
const QSet<Utils::FileName> currentWatched
|
|
|
|
|
= Utils::transform(m_watchedFiles, [](CMakeFile *cmf) { return cmf->filePath(); });
|
|
|
|
|
const QSet<Utils::FileName> toWatch = m_cmakeFiles;
|
|
|
|
|
QSet<Utils::FileName> toDelete = currentWatched;
|
|
|
|
|
toDelete.subtract(toWatch);
|
|
|
|
|
m_watchedFiles = Utils::filtered(m_watchedFiles, [&toDelete](Internal::CMakeFile *cmf) {
|
|
|
|
|
if (toDelete.contains(cmf->filePath())) {
|
|
|
|
|
delete cmf;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add new file watchers:
|
|
|
|
|
QSet<Utils::FileName> toAdd = toWatch;
|
|
|
|
|
toAdd.subtract(currentWatched);
|
|
|
|
|
foreach (const Utils::FileName &fn, toAdd) {
|
|
|
|
|
CMakeFile *cm = new CMakeFile(this, fn);
|
|
|
|
|
Core::DocumentManager::addDocument(cm);
|
|
|
|
|
m_watchedFiles.insert(cm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<FileNode *> fileNodes = m_files;
|
|
|
|
|
root->buildTree(fileNodes);
|
|
|
|
|
m_files.clear(); // Some of the FileNodes in files() were deleted!
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
void BuildDirManager::parse()
|
|
|
|
|
{
|
2016-05-13 16:19:31 +02:00
|
|
|
checkConfiguration();
|
|
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
CMakeTool *tool = CMakeKitInformation::cmakeTool(kit());
|
2016-09-27 14:53:51 +02:00
|
|
|
const QStringList generatorArgs = CMakeGeneratorKitInformation::generatorArguments(kit());
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
QTC_ASSERT(tool, return);
|
|
|
|
|
|
2016-05-23 11:39:49 +02:00
|
|
|
const QString cbpFile = CMakeManager::findCbpFile(QDir(workDirectory().toString()));
|
|
|
|
|
const QFileInfo cbpFileFi = cbpFile.isEmpty() ? QFileInfo() : QFileInfo(cbpFile);
|
2016-01-20 12:19:16 +01:00
|
|
|
if (!cbpFileFi.exists()) {
|
|
|
|
|
// Initial create:
|
2016-09-27 14:53:51 +02:00
|
|
|
startCMake(tool, generatorArgs, intendedConfiguration());
|
2016-01-20 12:19:16 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-27 14:49:35 +02:00
|
|
|
const bool mustUpdate = m_cmakeFiles.isEmpty()
|
|
|
|
|
|| Utils::anyOf(m_cmakeFiles, [&cbpFileFi](const Utils::FileName &f) {
|
2016-05-23 11:40:30 +02:00
|
|
|
return f.toFileInfo().lastModified() > cbpFileFi.lastModified();
|
|
|
|
|
});
|
2016-01-20 12:19:16 +01:00
|
|
|
if (mustUpdate) {
|
2016-09-27 14:53:51 +02:00
|
|
|
startCMake(tool, generatorArgs, CMakeConfig());
|
2016-01-20 12:19:16 +01:00
|
|
|
} else {
|
|
|
|
|
extractData();
|
2016-02-24 18:00:24 +01:00
|
|
|
m_hasData = true;
|
2016-01-20 12:19:16 +01:00
|
|
|
emit dataAvailable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-10 12:01:30 +01:00
|
|
|
void BuildDirManager::clearCache()
|
|
|
|
|
{
|
2016-05-12 12:24:56 +02:00
|
|
|
auto cmakeCache = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeCache.txt"));
|
|
|
|
|
auto cmakeFiles = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeFiles"));
|
2016-03-10 12:01:30 +01:00
|
|
|
|
|
|
|
|
const bool mustCleanUp = cmakeCache.exists() || cmakeFiles.exists();
|
|
|
|
|
if (!mustCleanUp)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Utils::FileUtils::removeRecursively(cmakeCache);
|
|
|
|
|
Utils::FileUtils::removeRecursively(cmakeFiles);
|
|
|
|
|
|
|
|
|
|
forceReparse();
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
QList<CMakeBuildTarget> BuildDirManager::buildTargets() const
|
|
|
|
|
{
|
|
|
|
|
return m_buildTargets;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-10 17:31:30 +01:00
|
|
|
CMakeConfig BuildDirManager::parsedConfiguration() const
|
2016-01-26 17:26:46 +01:00
|
|
|
{
|
2016-05-13 13:34:18 +02:00
|
|
|
if (m_cmakeCache.isEmpty()) {
|
|
|
|
|
Utils::FileName cacheFile = workDirectory();
|
|
|
|
|
cacheFile.appendPath(QLatin1String("CMakeCache.txt"));
|
2016-05-13 16:19:31 +02:00
|
|
|
if (!cacheFile.exists())
|
|
|
|
|
return m_cmakeCache;
|
2016-05-13 13:34:18 +02:00
|
|
|
QString errorMessage;
|
|
|
|
|
m_cmakeCache = parseConfiguration(cacheFile, &errorMessage);
|
|
|
|
|
if (!errorMessage.isEmpty())
|
|
|
|
|
emit errorOccured(errorMessage);
|
|
|
|
|
const Utils::FileName sourceOfBuildDir
|
|
|
|
|
= Utils::FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", m_cmakeCache));
|
2016-06-18 15:11:12 +02:00
|
|
|
const Utils::FileName canonicalSourceOfBuildDir = Utils::FileUtils::canonicalPath(sourceOfBuildDir);
|
|
|
|
|
const Utils::FileName canonicalSourceDirectory = Utils::FileUtils::canonicalPath(sourceDirectory());
|
|
|
|
|
if (canonicalSourceOfBuildDir != canonicalSourceDirectory) // Uses case-insensitive compare where appropriate
|
|
|
|
|
emit errorOccured(tr("The build directory is not for %1 but for %2")
|
|
|
|
|
.arg(canonicalSourceOfBuildDir.toUserOutput(),
|
|
|
|
|
canonicalSourceDirectory.toUserOutput()));
|
2016-05-13 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
return m_cmakeCache;
|
2016-01-26 17:26:46 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
void BuildDirManager::stopProcess()
|
|
|
|
|
{
|
2016-02-25 14:18:05 +01:00
|
|
|
if (!m_cmakeProcess)
|
|
|
|
|
return;
|
2016-02-24 18:00:24 +01:00
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
m_cmakeProcess->disconnect();
|
|
|
|
|
|
|
|
|
|
if (m_cmakeProcess->state() == QProcess::Running) {
|
|
|
|
|
m_cmakeProcess->terminate();
|
2016-04-29 16:52:58 +02:00
|
|
|
if (!m_cmakeProcess->waitForFinished(500) && m_cmakeProcess->state() == QProcess::Running)
|
2016-02-25 14:18:05 +01:00
|
|
|
m_cmakeProcess->kill();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cleanUpProcess();
|
2016-02-24 18:00:24 +01:00
|
|
|
|
2016-03-12 23:29:43 +02:00
|
|
|
if (!m_future)
|
|
|
|
|
return;
|
2016-02-25 14:18:05 +01:00
|
|
|
m_future->reportCanceled();
|
|
|
|
|
m_future->reportFinished();
|
|
|
|
|
delete m_future;
|
|
|
|
|
m_future = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuildDirManager::cleanUpProcess()
|
|
|
|
|
{
|
|
|
|
|
if (!m_cmakeProcess)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(m_cmakeProcess->state() == QProcess::NotRunning, return);
|
|
|
|
|
|
|
|
|
|
m_cmakeProcess->disconnect();
|
|
|
|
|
|
|
|
|
|
if (m_cmakeProcess->state() == QProcess::Running) {
|
|
|
|
|
m_cmakeProcess->terminate();
|
2016-04-29 16:52:58 +02:00
|
|
|
if (!m_cmakeProcess->waitForFinished(500) && m_cmakeProcess->state() == QProcess::Running)
|
2016-02-25 14:18:05 +01:00
|
|
|
m_cmakeProcess->kill();
|
2016-02-24 18:00:24 +01:00
|
|
|
}
|
2016-04-29 16:52:58 +02:00
|
|
|
m_cmakeProcess->waitForFinished();
|
2016-02-25 14:18:05 +01:00
|
|
|
delete m_cmakeProcess;
|
|
|
|
|
m_cmakeProcess = nullptr;
|
|
|
|
|
|
|
|
|
|
// Delete issue parser:
|
|
|
|
|
m_parser->flush();
|
|
|
|
|
delete m_parser;
|
|
|
|
|
m_parser = nullptr;
|
2016-02-24 18:00:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
void BuildDirManager::extractData()
|
|
|
|
|
{
|
|
|
|
|
const Utils::FileName topCMake
|
2016-02-24 18:00:24 +01:00
|
|
|
= Utils::FileName::fromString(sourceDirectory().toString() + QLatin1String("/CMakeLists.txt"));
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
resetData();
|
|
|
|
|
|
|
|
|
|
m_projectName = sourceDirectory().fileName();
|
2016-10-06 11:31:15 +02:00
|
|
|
m_files.append(new FileNode(topCMake, ProjectFileType, false));
|
2016-06-27 14:49:35 +02:00
|
|
|
// Do not insert topCMake into m_cmakeFiles: The project already watches that!
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
// Find cbp file
|
2016-02-25 14:18:05 +01:00
|
|
|
QString cbpFile = CMakeManager::findCbpFile(workDirectory().toString());
|
2016-01-20 12:19:16 +01:00
|
|
|
if (cbpFile.isEmpty())
|
|
|
|
|
return;
|
2016-06-27 14:49:35 +02:00
|
|
|
m_cmakeFiles.insert(Utils::FileName::fromString(cbpFile));
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-06-27 14:49:35 +02:00
|
|
|
// Add CMakeCache.txt file:
|
|
|
|
|
Utils::FileName cacheFile = workDirectory();
|
|
|
|
|
cacheFile.appendPath(QLatin1String("CMakeCache.txt"));
|
|
|
|
|
if (cacheFile.toFileInfo().exists())
|
|
|
|
|
m_cmakeFiles.insert(cacheFile);
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
// setFolderName
|
|
|
|
|
CMakeCbpParser cbpparser;
|
|
|
|
|
// Parsing
|
2016-02-24 18:00:24 +01:00
|
|
|
if (!cbpparser.parseCbpFile(kit(), cbpFile, sourceDirectory().toString()))
|
2016-01-20 12:19:16 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_projectName = cbpparser.projectName();
|
|
|
|
|
|
|
|
|
|
m_files = cbpparser.fileList();
|
|
|
|
|
if (cbpparser.hasCMakeFiles()) {
|
|
|
|
|
m_files.append(cbpparser.cmakeFileList());
|
2016-10-06 11:31:15 +02:00
|
|
|
foreach (const FileNode *node, cbpparser.cmakeFileList())
|
2016-06-27 14:49:35 +02:00
|
|
|
m_cmakeFiles.insert(node->filePath());
|
2016-08-16 17:31:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure the top cmakelists.txt file is always listed:
|
2016-10-06 11:31:15 +02:00
|
|
|
if (!Utils::contains(m_files, [topCMake](FileNode *fn) { return fn->filePath() == topCMake; })) {
|
|
|
|
|
m_files.append(new FileNode(topCMake, ProjectFileType, false));
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_buildTargets = cbpparser.buildTargets();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-27 14:53:51 +02:00
|
|
|
void BuildDirManager::startCMake(CMakeTool *tool, const QStringList &generatorArgs,
|
2016-01-20 12:19:16 +01:00
|
|
|
const CMakeConfig &config)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(tool && tool->isValid(), return);
|
2016-02-25 14:18:05 +01:00
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
QTC_ASSERT(!m_cmakeProcess, return);
|
2016-01-22 09:43:09 +01:00
|
|
|
QTC_ASSERT(!m_parser, return);
|
2016-01-22 10:49:26 +01:00
|
|
|
QTC_ASSERT(!m_future, return);
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
// Find a directory to set up into:
|
|
|
|
|
if (!buildDirectory().exists()) {
|
|
|
|
|
if (!m_tempDir)
|
|
|
|
|
m_tempDir = new QTemporaryDir(QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX"));
|
|
|
|
|
QTC_ASSERT(m_tempDir->isValid(), return);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure work directory exists:
|
|
|
|
|
QTC_ASSERT(workDirectory().exists(), return);
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-01-22 09:43:09 +01:00
|
|
|
m_parser = new CMakeParser;
|
2016-02-24 18:00:24 +01:00
|
|
|
QDir source = QDir(sourceDirectory().toString());
|
2016-10-06 11:31:15 +02:00
|
|
|
connect(m_parser, &IOutputParser::addTask, m_parser,
|
|
|
|
|
[source](const Task &task) {
|
2016-01-22 09:43:09 +01:00
|
|
|
if (task.file.isEmpty() || task.file.toFileInfo().isAbsolute()) {
|
2016-10-06 11:31:15 +02:00
|
|
|
TaskHub::addTask(task);
|
2016-01-22 09:43:09 +01:00
|
|
|
} else {
|
2016-10-06 11:31:15 +02:00
|
|
|
Task t = task;
|
2016-01-22 09:43:09 +01:00
|
|
|
t.file = Utils::FileName::fromString(source.absoluteFilePath(task.file.toString()));
|
2016-10-06 11:31:15 +02:00
|
|
|
TaskHub::addTask(t);
|
2016-01-22 09:43:09 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
// Always use the sourceDir: If we are triggered because the build directory is getting deleted
|
|
|
|
|
// then we are racing against CMakeCache.txt also getting deleted.
|
2016-02-24 18:00:24 +01:00
|
|
|
const QString srcDir = sourceDirectory().toString();
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
m_cmakeProcess = new Utils::QtcProcess(this);
|
2016-02-25 14:18:05 +01:00
|
|
|
m_cmakeProcess->setWorkingDirectory(workDirectory().toString());
|
2016-02-24 18:00:24 +01:00
|
|
|
m_cmakeProcess->setEnvironment(m_buildConfiguration->environment());
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
connect(m_cmakeProcess, &QProcess::readyReadStandardOutput,
|
|
|
|
|
this, &BuildDirManager::processCMakeOutput);
|
2016-01-22 09:43:09 +01:00
|
|
|
connect(m_cmakeProcess, &QProcess::readyReadStandardError,
|
|
|
|
|
this, &BuildDirManager::processCMakeError);
|
2016-01-20 12:19:16 +01:00
|
|
|
connect(m_cmakeProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
|
|
|
|
this, &BuildDirManager::cmakeFinished);
|
|
|
|
|
|
|
|
|
|
QString args;
|
|
|
|
|
Utils::QtcProcess::addArg(&args, srcDir);
|
2016-09-27 14:53:51 +02:00
|
|
|
Utils::QtcProcess::addArgs(&args, generatorArgs);
|
2016-03-16 13:39:41 +01:00
|
|
|
Utils::QtcProcess::addArgs(&args, toArguments(config, kit()));
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-03-17 14:43:55 +01:00
|
|
|
Core::MessageManager::write(tr("Running \"%1 %2\" in %3.")
|
2016-01-22 10:49:26 +01:00
|
|
|
.arg(tool->cmakeExecutable().toUserOutput())
|
|
|
|
|
.arg(args)
|
2016-02-25 14:18:05 +01:00
|
|
|
.arg(workDirectory().toUserOutput()));
|
2016-01-22 10:49:26 +01:00
|
|
|
|
|
|
|
|
m_future = new QFutureInterface<void>();
|
|
|
|
|
m_future->setProgressRange(0, 1);
|
|
|
|
|
Core::ProgressManager::addTask(m_future->future(),
|
2016-02-24 18:00:24 +01:00
|
|
|
tr("Configuring \"%1\"").arg(m_buildConfiguration->target()->project()->displayName()),
|
2016-01-22 10:49:26 +01:00
|
|
|
"CMake.Configure");
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
m_cmakeProcess->setCommand(tool->cmakeExecutable().toString(), args);
|
|
|
|
|
m_cmakeProcess->start();
|
2016-02-24 18:00:24 +01:00
|
|
|
emit configurationStarted();
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_cmakeProcess, return);
|
|
|
|
|
|
|
|
|
|
// process rest of the output:
|
2016-01-22 09:43:09 +01:00
|
|
|
processCMakeOutput();
|
|
|
|
|
processCMakeError();
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
cleanUpProcess();
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
extractData(); // try even if cmake failed...
|
|
|
|
|
|
2016-01-22 09:43:09 +01:00
|
|
|
QString msg;
|
2016-01-20 12:19:16 +01:00
|
|
|
if (status != QProcess::NormalExit)
|
2016-01-22 09:43:09 +01:00
|
|
|
msg = tr("*** cmake process crashed!");
|
2016-01-20 12:19:16 +01:00
|
|
|
else if (code != 0)
|
2016-01-22 09:43:09 +01:00
|
|
|
msg = tr("*** cmake process exited with exit code %1.").arg(code);
|
|
|
|
|
|
|
|
|
|
if (!msg.isEmpty()) {
|
|
|
|
|
Core::MessageManager::write(msg);
|
2016-10-06 11:31:15 +02:00
|
|
|
TaskHub::addTask(Task::Error, msg, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
2016-01-22 10:49:26 +01:00
|
|
|
m_future->reportCanceled();
|
|
|
|
|
} else {
|
|
|
|
|
m_future->setProgressValue(1);
|
2016-01-22 09:43:09 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-22 10:49:26 +01:00
|
|
|
m_future->reportFinished();
|
|
|
|
|
delete m_future;
|
2016-02-25 14:18:05 +01:00
|
|
|
m_future = nullptr;
|
2016-01-22 10:49:26 +01:00
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
m_hasData = true;
|
2016-01-20 12:19:16 +01:00
|
|
|
emit dataAvailable();
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-22 09:43:09 +01:00
|
|
|
static QString lineSplit(const QString &rest, const QByteArray &array, std::function<void(const QString &)> f)
|
|
|
|
|
{
|
|
|
|
|
QString tmp = rest + Utils::SynchronousProcess::normalizeNewlines(QString::fromLocal8Bit(array));
|
|
|
|
|
int start = 0;
|
|
|
|
|
int end = tmp.indexOf(QLatin1Char('\n'), start);
|
|
|
|
|
while (end >= 0) {
|
|
|
|
|
f(tmp.mid(start, end - start));
|
|
|
|
|
start = end + 1;
|
|
|
|
|
end = tmp.indexOf(QLatin1Char('\n'), start);
|
|
|
|
|
}
|
|
|
|
|
return tmp.mid(start);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
void BuildDirManager::processCMakeOutput()
|
|
|
|
|
{
|
2016-01-22 09:43:09 +01:00
|
|
|
static QString rest;
|
|
|
|
|
rest = lineSplit(rest, m_cmakeProcess->readAllStandardOutput(), [this](const QString &s) { Core::MessageManager::write(s); });
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-22 09:43:09 +01:00
|
|
|
void BuildDirManager::processCMakeError()
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
2016-01-22 09:43:09 +01:00
|
|
|
static QString rest;
|
|
|
|
|
rest = lineSplit(rest, m_cmakeProcess->readAllStandardError(), [this](const QString &s) {
|
|
|
|
|
m_parser->stdError(s);
|
|
|
|
|
Core::MessageManager::write(s);
|
|
|
|
|
});
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2016-05-13 16:19:31 +02:00
|
|
|
void BuildDirManager::checkConfiguration()
|
|
|
|
|
{
|
|
|
|
|
if (m_tempDir) // always throw away changes in the tmpdir!
|
|
|
|
|
return;
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
Kit *k = m_buildConfiguration->target()->kit();
|
2016-05-13 16:19:31 +02:00
|
|
|
const CMakeConfig cache = parsedConfiguration();
|
|
|
|
|
if (cache.isEmpty())
|
|
|
|
|
return; // No cache file yet.
|
|
|
|
|
|
|
|
|
|
CMakeConfig newConfig;
|
|
|
|
|
QSet<QString> changedKeys;
|
|
|
|
|
QSet<QString> removedKeys;
|
|
|
|
|
foreach (const CMakeConfigItem &iBc, intendedConfiguration()) {
|
|
|
|
|
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));
|
|
|
|
|
box->setStandardButtons(QMessageBox::Discard | QMessageBox::Apply);
|
|
|
|
|
box->setDefaultButton(QMessageBox::Discard);
|
|
|
|
|
|
|
|
|
|
int ret = box->exec();
|
|
|
|
|
if (ret == QMessageBox::Apply)
|
|
|
|
|
m_buildConfiguration->setCMakeConfiguration(newConfig);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-06 15:55:55 +02:00
|
|
|
void BuildDirManager::handleDocumentSaves(Core::IDocument *document)
|
|
|
|
|
{
|
|
|
|
|
if (!m_cmakeFiles.contains(document->filePath()))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_reparseTimer.start(100);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-26 17:26:46 +01:00
|
|
|
static QByteArray trimCMakeCacheLine(const QByteArray &in) {
|
|
|
|
|
int start = 0;
|
|
|
|
|
while (start < in.count() && (in.at(start) == ' ' || in.at(start) == '\t'))
|
|
|
|
|
++start;
|
|
|
|
|
|
|
|
|
|
return in.mid(start, in.count() - start - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QByteArrayList splitCMakeCacheLine(const QByteArray &line) {
|
|
|
|
|
const int colonPos = line.indexOf(':');
|
|
|
|
|
if (colonPos < 0)
|
|
|
|
|
return QByteArrayList();
|
|
|
|
|
|
|
|
|
|
const int equalPos = line.indexOf('=', colonPos + 1);
|
|
|
|
|
if (equalPos < colonPos)
|
|
|
|
|
return QByteArrayList();
|
|
|
|
|
|
|
|
|
|
return QByteArrayList() << line.mid(0, colonPos)
|
|
|
|
|
<< line.mid(colonPos + 1, equalPos - colonPos - 1)
|
|
|
|
|
<< line.mid(equalPos + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CMakeConfigItem::Type fromByteArray(const QByteArray &type) {
|
|
|
|
|
if (type == "BOOL")
|
|
|
|
|
return CMakeConfigItem::BOOL;
|
|
|
|
|
if (type == "STRING")
|
|
|
|
|
return CMakeConfigItem::STRING;
|
|
|
|
|
if (type == "FILEPATH")
|
|
|
|
|
return CMakeConfigItem::FILEPATH;
|
|
|
|
|
if (type == "PATH")
|
|
|
|
|
return CMakeConfigItem::PATH;
|
|
|
|
|
QTC_CHECK(type == "INTERNAL" || type == "STATIC");
|
|
|
|
|
|
|
|
|
|
return CMakeConfigItem::INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-29 15:47:54 +01:00
|
|
|
CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile,
|
|
|
|
|
QString *errorMessage)
|
2016-01-26 17:26:46 +01:00
|
|
|
{
|
|
|
|
|
CMakeConfig result;
|
2016-02-29 15:47:54 +01:00
|
|
|
QFile cache(cacheFile.toString());
|
|
|
|
|
if (!cache.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
|
if (errorMessage)
|
|
|
|
|
*errorMessage = tr("Failed to open %1 for reading.").arg(cacheFile.toUserOutput());
|
2016-01-26 17:26:46 +01:00
|
|
|
return CMakeConfig();
|
2016-02-29 15:47:54 +01:00
|
|
|
}
|
2016-01-26 17:26:46 +01:00
|
|
|
|
|
|
|
|
QSet<QByteArray> advancedSet;
|
|
|
|
|
QByteArray documentation;
|
|
|
|
|
while (!cache.atEnd()) {
|
|
|
|
|
const QByteArray line = trimCMakeCacheLine(cache.readLine());
|
|
|
|
|
|
|
|
|
|
if (line.isEmpty() || line.startsWith('#'))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (line.startsWith("//")) {
|
|
|
|
|
documentation = line.mid(2);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QByteArrayList pieces = splitCMakeCacheLine(line);
|
|
|
|
|
if (pieces.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(pieces.count() == 3, continue);
|
|
|
|
|
const QByteArray key = pieces.at(0);
|
|
|
|
|
const QByteArray type = pieces.at(1);
|
|
|
|
|
const QByteArray value = pieces.at(2);
|
|
|
|
|
|
|
|
|
|
if (key.endsWith("-ADVANCED") && value == "1") {
|
|
|
|
|
advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */));
|
|
|
|
|
} else {
|
|
|
|
|
CMakeConfigItem::Type t = fromByteArray(type);
|
2016-03-10 17:33:45 +01:00
|
|
|
result << CMakeConfigItem(key, t, documentation, value);
|
2016-01-26 17:26:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set advanced flags:
|
|
|
|
|
for (int i = 0; i < result.count(); ++i) {
|
|
|
|
|
CMakeConfigItem &item = result[i];
|
|
|
|
|
item.isAdvanced = advancedSet.contains(item.key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Utils::sort(result, CMakeConfigItem::sortOperator());
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
void BuildDirManager::handleCmakeFileChange()
|
|
|
|
|
{
|
|
|
|
|
Target *t = m_buildConfiguration->target()->project()->activeTarget();
|
|
|
|
|
BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr;
|
|
|
|
|
|
|
|
|
|
if (m_buildConfiguration->target() == t && m_buildConfiguration == bc)
|
|
|
|
|
cmakeFilesChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-10 17:34:47 +01:00
|
|
|
void BuildDirManager::maybeForceReparse()
|
|
|
|
|
{
|
2016-05-13 16:19:31 +02:00
|
|
|
checkConfiguration();
|
|
|
|
|
|
2016-03-10 17:34:47 +01:00
|
|
|
const QByteArray GENERATOR_KEY = "CMAKE_GENERATOR";
|
|
|
|
|
const QByteArray EXTRA_GENERATOR_KEY = "CMAKE_EXTRA_GENERATOR";
|
|
|
|
|
const QByteArray CMAKE_COMMAND_KEY = "CMAKE_COMMAND";
|
|
|
|
|
|
2016-04-08 14:16:20 +02:00
|
|
|
const QByteArrayList criticalKeys
|
2016-04-13 11:09:42 +02:00
|
|
|
= QByteArrayList() << GENERATOR_KEY << CMAKE_COMMAND_KEY;
|
2016-04-08 14:16:20 +02:00
|
|
|
|
2016-03-30 13:58:51 +02:00
|
|
|
if (!m_hasData) {
|
|
|
|
|
forceReparse();
|
2016-03-10 17:34:47 +01:00
|
|
|
return;
|
2016-03-30 13:58:51 +02:00
|
|
|
}
|
2016-03-10 17:34:47 +01:00
|
|
|
|
|
|
|
|
const CMakeConfig currentConfig = parsedConfiguration();
|
|
|
|
|
|
2016-04-08 14:16:20 +02:00
|
|
|
const CMakeTool *tool = CMakeKitInformation::cmakeTool(kit());
|
|
|
|
|
QTC_ASSERT(tool, return); // No cmake... we should not have ended up here in the first place
|
2016-09-27 14:53:51 +02:00
|
|
|
const QString extraKitGenerator = CMakeGeneratorKitInformation::extraGenerator(kit());
|
|
|
|
|
const QString mainKitGenerator = CMakeGeneratorKitInformation::generator(kit());
|
2016-03-10 17:34:47 +01:00
|
|
|
CMakeConfig targetConfig = m_buildConfiguration->cmakeConfiguration();
|
|
|
|
|
targetConfig.append(CMakeConfigItem(GENERATOR_KEY, CMakeConfigItem::INTERNAL,
|
2016-04-11 16:14:43 +02:00
|
|
|
QByteArray(), mainKitGenerator.toUtf8()));
|
2016-03-10 17:34:47 +01:00
|
|
|
if (!extraKitGenerator.isEmpty())
|
|
|
|
|
targetConfig.append(CMakeConfigItem(EXTRA_GENERATOR_KEY, CMakeConfigItem::INTERNAL,
|
2016-04-08 14:16:20 +02:00
|
|
|
QByteArray(), extraKitGenerator.toUtf8()));
|
|
|
|
|
targetConfig.append(CMakeConfigItem(CMAKE_COMMAND_KEY, CMakeConfigItem::INTERNAL,
|
|
|
|
|
QByteArray(), tool->cmakeExecutable().toUserOutput().toUtf8()));
|
2016-03-10 17:34:47 +01:00
|
|
|
Utils::sort(targetConfig, CMakeConfigItem::sortOperator());
|
|
|
|
|
|
2016-04-08 14:16:20 +02:00
|
|
|
bool mustReparse = false;
|
2016-03-10 17:34:47 +01:00
|
|
|
auto ccit = currentConfig.constBegin();
|
|
|
|
|
auto kcit = targetConfig.constBegin();
|
2016-04-08 14:16:20 +02:00
|
|
|
|
2016-03-10 17:34:47 +01:00
|
|
|
while (ccit != currentConfig.constEnd() && kcit != targetConfig.constEnd()) {
|
|
|
|
|
if (ccit->key == kcit->key) {
|
2016-04-08 14:16:20 +02:00
|
|
|
if (ccit->value != kcit->value) {
|
|
|
|
|
if (criticalKeys.contains(kcit->key)) {
|
|
|
|
|
clearCache();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mustReparse = true;
|
|
|
|
|
}
|
2016-03-10 17:34:47 +01:00
|
|
|
++ccit;
|
|
|
|
|
++kcit;
|
|
|
|
|
} else {
|
2016-04-08 14:16:20 +02:00
|
|
|
if (ccit->key < kcit->key) {
|
2016-03-10 17:34:47 +01:00
|
|
|
++ccit;
|
2016-04-08 14:16:20 +02:00
|
|
|
} else {
|
|
|
|
|
++kcit;
|
|
|
|
|
mustReparse = true;
|
|
|
|
|
}
|
2016-03-10 17:34:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-08 14:16:20 +02:00
|
|
|
// If we have keys that do not exist yet, then reparse.
|
|
|
|
|
//
|
|
|
|
|
// The critical keys *must* be set in cmake configuration, so those were already
|
|
|
|
|
// handled above.
|
|
|
|
|
if (mustReparse || kcit != targetConfig.constEnd())
|
|
|
|
|
forceReparse();
|
2016-03-10 17:34:47 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace CMakeProjectManager
|