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"
|
2017-03-13 14:22:16 +01:00
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
#include "cmakebuildconfiguration.h"
|
2017-04-26 15:29:54 +02:00
|
|
|
#include "cmakebuildstep.h"
|
2016-01-20 12:19:16 +01:00
|
|
|
#include "cmakekitinformation.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-01-20 12:19:16 +01:00
|
|
|
#include <projectexplorer/kit.h>
|
2016-10-10 10:22:06 +02:00
|
|
|
#include <projectexplorer/kitinformation.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>
|
2016-02-24 18:00:24 +01:00
|
|
|
#include <projectexplorer/target.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <projectexplorer/taskhub.h>
|
2016-10-10 10:22:06 +02:00
|
|
|
#include <projectexplorer/toolchain.h>
|
2016-01-20 12:19:16 +01:00
|
|
|
|
|
|
|
|
#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>
|
|
|
|
|
|
2017-09-07 14:00:29 +02:00
|
|
|
#include <QDir>
|
2019-08-09 12:20:49 +02:00
|
|
|
#include <QLoggingCategory>
|
2016-05-13 16:19:31 +02:00
|
|
|
#include <QMessageBox>
|
2019-08-09 11:22:49 +02:00
|
|
|
#include <QPointer>
|
2017-07-16 10:41:54 +02:00
|
|
|
#include <QPushButton>
|
2016-01-20 12:19:16 +01:00
|
|
|
#include <QSet>
|
|
|
|
|
|
2019-10-09 17:15:36 +02:00
|
|
|
#include <app/app_version.h>
|
|
|
|
|
|
2016-10-06 11:31:15 +02:00
|
|
|
using namespace ProjectExplorer;
|
2019-05-15 14:54:25 +02:00
|
|
|
using namespace Utils;
|
2016-10-06 11:31:15 +02:00
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
namespace CMakeProjectManager {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2019-08-09 12:20:49 +02:00
|
|
|
Q_LOGGING_CATEGORY(cmakeBuildDirManagerLog, "qtc.cmake.builddirmanager", QtWarningMsg);
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
// BuildDirManager:
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
BuildDirManager::BuildDirManager(CMakeBuildSystem *buildSystem)
|
|
|
|
|
: m_buildSystem(buildSystem)
|
|
|
|
|
{
|
|
|
|
|
assert(buildSystem);
|
|
|
|
|
}
|
2019-06-06 13:38:28 +02:00
|
|
|
|
2016-10-17 14:38:38 +02:00
|
|
|
BuildDirManager::~BuildDirManager() = default;
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
FilePath BuildDirManager::workDirectory(const BuildDirParameters ¶meters) const
|
2016-02-25 14:18:05 +01:00
|
|
|
{
|
2019-05-28 13:49:26 +02:00
|
|
|
const Utils::FilePath bdir = parameters.buildDirectory;
|
2018-05-24 16:57:31 +02:00
|
|
|
const CMakeTool *cmake = parameters.cmakeTool();
|
2017-09-07 14:00:29 +02:00
|
|
|
if (bdir.exists()) {
|
2017-10-18 13:05:44 +02:00
|
|
|
m_buildDirToTempDir.erase(bdir);
|
2016-02-25 14:18:05 +01:00
|
|
|
return bdir;
|
2017-09-07 14:00:29 +02:00
|
|
|
}
|
2019-10-25 09:55:32 +02:00
|
|
|
|
|
|
|
|
if (cmake && cmake->autoCreateBuildDirectory()) {
|
|
|
|
|
if (!QDir().mkpath(bdir.toString()))
|
|
|
|
|
emitErrorOccured(tr("Failed to create build directory \"%1\".").arg(bdir.toUserOutput()));
|
|
|
|
|
return bdir;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 13:05:44 +02:00
|
|
|
auto tmpDirIt = m_buildDirToTempDir.find(bdir);
|
|
|
|
|
if (tmpDirIt == m_buildDirToTempDir.end()) {
|
|
|
|
|
auto ret = m_buildDirToTempDir.emplace(std::make_pair(bdir, std::make_unique<Utils::TemporaryDirectory>("qtc-cmake-XXXXXXXX")));
|
|
|
|
|
QTC_ASSERT(ret.second, return bdir);
|
|
|
|
|
tmpDirIt = ret.first;
|
|
|
|
|
|
|
|
|
|
if (!tmpDirIt->second->isValid()) {
|
2017-09-07 14:00:29 +02:00
|
|
|
emitErrorOccured(tr("Failed to create temporary directory \"%1\".")
|
2017-10-18 13:05:44 +02:00
|
|
|
.arg(QDir::toNativeSeparators(tmpDirIt->second->path())));
|
|
|
|
|
return bdir;
|
2017-09-07 14:00:29 +02:00
|
|
|
}
|
2016-10-17 14:38:38 +02:00
|
|
|
}
|
2019-05-28 13:49:26 +02:00
|
|
|
return Utils::FilePath::fromString(tmpDirIt->second->path());
|
2016-02-25 14:18:05 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
void BuildDirManager::updateReparseParameters(const int parameters)
|
|
|
|
|
{
|
|
|
|
|
m_reparseParameters |= parameters;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BuildDirManager::takeReparseParameters()
|
|
|
|
|
{
|
|
|
|
|
int result = m_reparseParameters;
|
|
|
|
|
m_reparseParameters = REPARSE_DEFAULT;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-28 17:20:07 +02:00
|
|
|
void BuildDirManager::emitDataAvailable()
|
|
|
|
|
{
|
2016-12-04 03:36:12 +10:00
|
|
|
if (!isParsing())
|
2016-10-28 17:20:07 +02:00
|
|
|
emit dataAvailable();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-24 19:35:13 +01:00
|
|
|
void BuildDirManager::emitErrorOccured(const QString &message) const
|
|
|
|
|
{
|
|
|
|
|
m_isHandlingError = true;
|
|
|
|
|
emit errorOccured(message);
|
|
|
|
|
m_isHandlingError = false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
void BuildDirManager::emitReparseRequest() const
|
|
|
|
|
{
|
2019-10-08 12:53:23 +02:00
|
|
|
if (m_reparseParameters & REPARSE_URGENT) {
|
|
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "emitting requestReparse";
|
2019-08-09 11:22:49 +02:00
|
|
|
emit requestReparse();
|
2019-10-08 12:53:23 +02:00
|
|
|
} else {
|
|
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "emitting requestDelayedReparse";
|
2019-08-09 11:22:49 +02:00
|
|
|
emit requestDelayedReparse();
|
2019-10-08 12:53:23 +02:00
|
|
|
}
|
2019-08-09 11:22:49 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
void BuildDirManager::updateReaderType(const BuildDirParameters &p,
|
|
|
|
|
std::function<void()> todo)
|
2016-02-24 18:00:24 +01:00
|
|
|
{
|
2019-10-08 12:53:23 +02:00
|
|
|
if (!m_reader || !m_reader->isCompatible(p)) {
|
2019-10-08 12:37:44 +02:00
|
|
|
if (m_reader) {
|
|
|
|
|
stopParsingAndClearState();
|
|
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "Creating new reader do to incompatible parameters";
|
|
|
|
|
} else {
|
|
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "Creating first reader";
|
|
|
|
|
}
|
2019-06-05 15:42:46 +02:00
|
|
|
m_reader = BuildDirReader::createReader(p);
|
2019-06-05 15:56:37 +02:00
|
|
|
|
2019-10-08 12:37:44 +02:00
|
|
|
connect(m_reader.get(),
|
|
|
|
|
&BuildDirReader::configurationStarted,
|
|
|
|
|
this,
|
|
|
|
|
&BuildDirManager::parsingStarted);
|
|
|
|
|
connect(m_reader.get(),
|
|
|
|
|
&BuildDirReader::dataAvailable,
|
|
|
|
|
this,
|
|
|
|
|
&BuildDirManager::emitDataAvailable);
|
|
|
|
|
connect(m_reader.get(),
|
|
|
|
|
&BuildDirReader::errorOccured,
|
|
|
|
|
this,
|
|
|
|
|
&BuildDirManager::emitErrorOccured);
|
|
|
|
|
connect(m_reader.get(), &BuildDirReader::dirty, this, &BuildDirManager::becameDirty);
|
|
|
|
|
connect(m_reader.get(), &BuildDirReader::isReadyNow, this, todo);
|
|
|
|
|
}
|
2017-09-28 11:32:39 +02:00
|
|
|
|
2019-10-08 12:37:44 +02:00
|
|
|
QTC_ASSERT(m_reader, return );
|
2016-10-17 14:38:38 +02:00
|
|
|
|
2019-06-05 15:56:37 +02:00
|
|
|
m_reader->setParameters(p);
|
2016-10-17 14:38:38 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
bool BuildDirManager::hasConfigChanged()
|
2016-10-17 14:38:38 +02:00
|
|
|
{
|
|
|
|
|
checkConfiguration();
|
|
|
|
|
|
|
|
|
|
const QByteArray GENERATOR_KEY = "CMAKE_GENERATOR";
|
|
|
|
|
const QByteArray EXTRA_GENERATOR_KEY = "CMAKE_EXTRA_GENERATOR";
|
|
|
|
|
const QByteArray CMAKE_COMMAND_KEY = "CMAKE_COMMAND";
|
2016-12-01 12:15:40 +01:00
|
|
|
const QByteArray CMAKE_C_COMPILER_KEY = "CMAKE_C_COMPILER";
|
|
|
|
|
const QByteArray CMAKE_CXX_COMPILER_KEY = "CMAKE_CXX_COMPILER";
|
2016-10-17 14:38:38 +02:00
|
|
|
|
|
|
|
|
const QByteArrayList criticalKeys
|
2017-02-22 15:09:35 +01:00
|
|
|
= {GENERATOR_KEY, CMAKE_COMMAND_KEY, CMAKE_C_COMPILER_KEY, CMAKE_CXX_COMPILER_KEY};
|
2016-10-17 14:38:38 +02:00
|
|
|
|
2019-06-07 17:13:49 +02:00
|
|
|
QString errorMessage;
|
|
|
|
|
const CMakeConfig currentConfig = takeCMakeConfiguration(errorMessage);
|
|
|
|
|
if (!errorMessage.isEmpty())
|
|
|
|
|
return false;
|
2016-10-17 14:38:38 +02:00
|
|
|
|
2018-05-24 16:57:31 +02:00
|
|
|
const CMakeTool *tool = m_parameters.cmakeTool();
|
2017-09-28 11:32:39 +02:00
|
|
|
QTC_ASSERT(tool, return false); // No cmake... we should not have ended up here in the first place
|
|
|
|
|
const QString extraKitGenerator = m_parameters.extraGenerator;
|
|
|
|
|
const QString mainKitGenerator = m_parameters.generator;
|
|
|
|
|
CMakeConfig targetConfig = m_parameters.configuration;
|
2016-10-17 14:38:38 +02:00
|
|
|
targetConfig.append(CMakeConfigItem(GENERATOR_KEY, CMakeConfigItem::INTERNAL,
|
|
|
|
|
QByteArray(), mainKitGenerator.toUtf8()));
|
|
|
|
|
if (!extraKitGenerator.isEmpty())
|
|
|
|
|
targetConfig.append(CMakeConfigItem(EXTRA_GENERATOR_KEY, CMakeConfigItem::INTERNAL,
|
|
|
|
|
QByteArray(), extraKitGenerator.toUtf8()));
|
|
|
|
|
targetConfig.append(CMakeConfigItem(CMAKE_COMMAND_KEY, CMakeConfigItem::INTERNAL,
|
|
|
|
|
QByteArray(), tool->cmakeExecutable().toUserOutput().toUtf8()));
|
|
|
|
|
Utils::sort(targetConfig, CMakeConfigItem::sortOperator());
|
|
|
|
|
|
|
|
|
|
bool mustReparse = false;
|
|
|
|
|
auto ccit = currentConfig.constBegin();
|
|
|
|
|
auto kcit = targetConfig.constBegin();
|
|
|
|
|
|
|
|
|
|
while (ccit != currentConfig.constEnd() && kcit != targetConfig.constEnd()) {
|
|
|
|
|
if (ccit->key == kcit->key) {
|
|
|
|
|
if (ccit->value != kcit->value) {
|
|
|
|
|
if (criticalKeys.contains(kcit->key)) {
|
2017-09-28 11:32:39 +02:00
|
|
|
clearCache();
|
|
|
|
|
return false; // no need to trigger a new reader, clearCache will do that
|
2016-10-17 14:38:38 +02:00
|
|
|
}
|
|
|
|
|
mustReparse = true;
|
|
|
|
|
}
|
|
|
|
|
++ccit;
|
|
|
|
|
++kcit;
|
|
|
|
|
} else {
|
|
|
|
|
if (ccit->key < kcit->key) {
|
|
|
|
|
++ccit;
|
|
|
|
|
} else {
|
|
|
|
|
++kcit;
|
|
|
|
|
mustReparse = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2017-09-28 11:32:39 +02:00
|
|
|
return mustReparse || kcit != targetConfig.constEnd();
|
2016-02-24 18:00:24 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-17 15:20:23 +02:00
|
|
|
void BuildDirManager::writeConfigurationIntoBuildDirectory(const Utils::MacroExpander *expander)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(expander, return );
|
|
|
|
|
|
|
|
|
|
const FilePath buildDir = workDirectory(m_parameters);
|
|
|
|
|
QTC_ASSERT(buildDir.exists(), return );
|
|
|
|
|
|
|
|
|
|
const FilePath settingsFile = buildDir.pathAppended("qtcsettings.cmake");
|
|
|
|
|
|
|
|
|
|
QByteArray contents;
|
|
|
|
|
contents.append("# This file is managed by Qt Creator, do not edit!\n\n");
|
|
|
|
|
contents.append(
|
|
|
|
|
transform(m_parameters.configuration,
|
|
|
|
|
[expander](const CMakeConfigItem &item) { return item.toCMakeSetLine(expander); })
|
|
|
|
|
.join('\n')
|
|
|
|
|
.toUtf8());
|
|
|
|
|
|
|
|
|
|
QFile file(settingsFile.toString());
|
|
|
|
|
QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return );
|
|
|
|
|
file.write(contents);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 18:00:24 +01:00
|
|
|
bool BuildDirManager::isParsing() const
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
2016-10-17 13:55:54 +02:00
|
|
|
return m_reader && m_reader->isParsing();
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-05 15:56:03 +02:00
|
|
|
void BuildDirManager::stopParsingAndClearState()
|
|
|
|
|
{
|
2019-10-08 12:53:23 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "stopping parsing run!";
|
2019-08-05 16:35:07 +02:00
|
|
|
if (m_reader) {
|
2019-10-08 12:43:56 +02:00
|
|
|
if (m_reader->isParsing())
|
|
|
|
|
m_reader->errorOccured(tr("Parsing has been canceled."));
|
2019-08-05 16:35:07 +02:00
|
|
|
disconnect(m_reader.get(), nullptr, this, nullptr);
|
2019-08-05 15:56:03 +02:00
|
|
|
m_reader->stop();
|
2019-08-05 16:35:07 +02:00
|
|
|
}
|
2019-08-05 15:56:03 +02:00
|
|
|
m_reader.reset();
|
2019-08-05 16:35:07 +02:00
|
|
|
resetData();
|
2019-08-05 15:56:03 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
void BuildDirManager::setParametersAndRequestParse(const BuildDirParameters ¶meters,
|
2019-08-09 11:22:49 +02:00
|
|
|
const int reparseParameters)
|
2016-06-27 14:49:35 +02:00
|
|
|
{
|
2019-10-08 12:53:23 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "setting parameters and requesting reparse";
|
2018-05-24 16:57:31 +02:00
|
|
|
if (!parameters.cmakeTool()) {
|
2018-05-17 13:01:39 +02:00
|
|
|
TaskHub::addTask(Task::Error,
|
|
|
|
|
tr("The kit needs to define a CMake tool to parse this project."),
|
|
|
|
|
ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-08-05 15:56:03 +02:00
|
|
|
QTC_ASSERT(parameters.isValid(), return );
|
2016-10-17 13:55:54 +02:00
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
m_parameters = parameters;
|
2017-10-18 13:05:44 +02:00
|
|
|
m_parameters.workDirectory = workDirectory(parameters);
|
2019-08-09 11:22:49 +02:00
|
|
|
updateReparseParameters(reparseParameters);
|
2016-06-27 14:49:35 +02:00
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
updateReaderType(m_parameters, [this]() { emitReparseRequest(); });
|
2017-07-16 10:05:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
CMakeBuildSystem *BuildDirManager::buildSystem() const
|
2017-07-16 10:05:21 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
return m_buildSystem;
|
2017-07-16 10:05:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-06 13:38:28 +02:00
|
|
|
FilePath BuildDirManager::buildDirectory() const
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
return m_parameters.buildDirectory;
|
2019-06-06 13:38:28 +02:00
|
|
|
}
|
2017-03-24 19:35:13 +01:00
|
|
|
|
2019-06-06 13:38:28 +02:00
|
|
|
void BuildDirManager::becameDirty()
|
|
|
|
|
{
|
2019-10-08 12:53:23 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "BuildDirManager: becameDirty was triggered.";
|
2019-10-25 09:55:32 +02:00
|
|
|
if (isParsing() || !buildSystem())
|
2016-03-16 16:33:54 +01:00
|
|
|
return;
|
|
|
|
|
|
2018-05-24 16:57:31 +02:00
|
|
|
const CMakeTool *tool = m_parameters.cmakeTool();
|
2017-09-28 11:32:39 +02:00
|
|
|
if (!tool->isAutoRun())
|
|
|
|
|
return;
|
2016-01-20 12:19:16 +01:00
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
updateReparseParameters(REPARSE_CHECK_CONFIGURATION | REPARSE_SCAN);
|
|
|
|
|
emit requestReparse();
|
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-10-17 13:55:54 +02:00
|
|
|
if (m_reader)
|
|
|
|
|
m_reader->resetData();
|
2016-01-27 10:22:07 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-25 14:18:05 +01:00
|
|
|
bool BuildDirManager::persistCMakeState()
|
|
|
|
|
{
|
2017-09-28 11:32:39 +02:00
|
|
|
QTC_ASSERT(m_parameters.isValid(), return false);
|
|
|
|
|
|
2017-10-18 13:05:44 +02:00
|
|
|
if (m_parameters.workDirectory == m_parameters.buildDirectory)
|
2016-02-25 14:18:05 +01:00
|
|
|
return false;
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
const Utils::FilePath buildDir = m_parameters.buildDirectory;
|
2017-09-28 11:32:39 +02:00
|
|
|
QDir dir(buildDir.toString());
|
|
|
|
|
dir.mkpath(buildDir.toString());
|
2016-02-25 14:18:05 +01:00
|
|
|
|
2017-10-18 13:05:44 +02:00
|
|
|
BuildDirParameters newParameters = m_parameters;
|
|
|
|
|
newParameters.workDirectory.clear();
|
2019-12-06 11:29:36 +01:00
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "Requesting parse due to persisting CMake State";
|
2019-06-06 15:07:17 +02:00
|
|
|
setParametersAndRequestParse(newParameters,
|
2019-08-09 13:11:03 +02:00
|
|
|
REPARSE_URGENT | REPARSE_FORCE_CMAKE_RUN
|
|
|
|
|
| REPARSE_FORCE_CONFIGURATION | REPARSE_CHECK_CONFIGURATION);
|
2016-02-25 14:18:05 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
void BuildDirManager::requestFilesystemScan()
|
2016-10-06 11:31:15 +02:00
|
|
|
{
|
2019-08-09 11:22:49 +02:00
|
|
|
updateReparseParameters(REPARSE_SCAN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BuildDirManager::isFilesystemScanRequested() const
|
|
|
|
|
{
|
|
|
|
|
return m_reparseParameters & REPARSE_SCAN;
|
|
|
|
|
}
|
2019-08-09 12:20:49 +02:00
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
void BuildDirManager::parse()
|
|
|
|
|
{
|
2019-10-08 12:53:23 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "parsing!";
|
2019-08-09 12:20:49 +02:00
|
|
|
QTC_ASSERT(m_parameters.isValid(), return );
|
2019-10-08 12:43:56 +02:00
|
|
|
QTC_ASSERT(m_reader, return );
|
|
|
|
|
QTC_ASSERT(!m_reader->isParsing(), return );
|
2016-11-04 09:31:46 +01:00
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
m_reader->stop();
|
2016-11-04 09:31:46 +01:00
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
|
|
|
|
|
2019-08-09 11:22:49 +02:00
|
|
|
int reparseParameters = takeReparseParameters();
|
|
|
|
|
|
|
|
|
|
qCDebug(cmakeBuildDirManagerLog)
|
|
|
|
|
<< "Parse called with flags:" << flagsString(reparseParameters);
|
|
|
|
|
|
2019-08-09 13:28:26 +02:00
|
|
|
const QString cache = m_parameters.workDirectory.pathAppended("CMakeCache.txt").toString();
|
|
|
|
|
if (!QFileInfo::exists(cache)) {
|
2019-06-07 17:11:03 +02:00
|
|
|
reparseParameters |= REPARSE_FORCE_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN;
|
2019-08-09 12:20:49 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog)
|
2019-08-09 13:28:26 +02:00
|
|
|
<< "No" << cache << "file found, new flags:" << flagsString(reparseParameters);
|
2019-06-07 17:11:03 +02:00
|
|
|
} else if (reparseParameters & REPARSE_CHECK_CONFIGURATION) {
|
2019-08-09 12:20:49 +02:00
|
|
|
if (checkConfiguration()) {
|
2019-06-06 15:07:17 +02:00
|
|
|
reparseParameters |= REPARSE_FORCE_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN;
|
2019-08-09 12:20:49 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog)
|
|
|
|
|
<< "Config check triggered flags change:" << flagsString(reparseParameters);
|
|
|
|
|
}
|
2017-09-28 11:32:39 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-17 15:20:23 +02:00
|
|
|
writeConfigurationIntoBuildDirectory(m_parameters.expander);
|
|
|
|
|
|
2019-10-08 12:53:23 +02:00
|
|
|
qCDebug(cmakeBuildDirManagerLog) << "Asking reader to parse";
|
2019-06-06 15:07:17 +02:00
|
|
|
m_reader->parse(reparseParameters & REPARSE_FORCE_CMAKE_RUN,
|
|
|
|
|
reparseParameters & REPARSE_FORCE_CONFIGURATION);
|
2016-10-06 11:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-15 12:22:09 +02:00
|
|
|
QVector<FilePath> BuildDirManager::takeProjectFilesToWatch()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!m_isHandlingError, return {});
|
|
|
|
|
QTC_ASSERT(m_reader, return {});
|
|
|
|
|
|
|
|
|
|
Utils::FilePath sourceDir = m_parameters.sourceDirectory;
|
|
|
|
|
Utils::FilePath buildDir = m_parameters.workDirectory;
|
|
|
|
|
|
2019-09-13 14:01:58 +02:00
|
|
|
return Utils::filtered(m_reader->takeProjectFilesToWatch(),
|
|
|
|
|
[&sourceDir,
|
|
|
|
|
&buildDir](const Utils::FilePath &p) {
|
|
|
|
|
return p.isChildOf(sourceDir)
|
|
|
|
|
|| p.isChildOf(buildDir);
|
|
|
|
|
});
|
2019-08-15 12:22:09 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-13 14:24:04 +02:00
|
|
|
std::unique_ptr<CMakeProjectNode> BuildDirManager::generateProjectTree(
|
|
|
|
|
const QList<const FileNode *> &allFiles, QString &errorMessage) const
|
2016-10-10 10:22:06 +02:00
|
|
|
{
|
2019-06-13 14:24:04 +02:00
|
|
|
QTC_ASSERT(!m_isHandlingError, return {});
|
|
|
|
|
QTC_ASSERT(m_reader, return {});
|
2017-09-28 11:32:39 +02:00
|
|
|
|
2019-06-13 14:24:04 +02:00
|
|
|
return m_reader->generateProjectTree(allFiles, errorMessage);
|
2016-10-10 10:22:06 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-28 18:22:45 +02:00
|
|
|
RawProjectParts BuildDirManager::createRawProjectParts(QString &errorMessage) const
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
2019-01-20 15:25:18 +01:00
|
|
|
QTC_ASSERT(!m_isHandlingError, return {});
|
|
|
|
|
QTC_ASSERT(m_reader, return {});
|
2019-06-07 17:13:49 +02:00
|
|
|
return m_reader->createRawProjectParts(errorMessage);
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-10 12:01:30 +01:00
|
|
|
void BuildDirManager::clearCache()
|
|
|
|
|
{
|
2017-09-28 11:32:39 +02:00
|
|
|
QTC_ASSERT(m_parameters.isValid(), return);
|
2017-03-24 19:35:13 +01:00
|
|
|
QTC_ASSERT(!m_isHandlingError, return);
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
const FilePath cmakeCache = m_parameters.workDirectory.pathAppended("CMakeCache.txt");
|
|
|
|
|
const FilePath cmakeFiles = m_parameters.workDirectory.pathAppended("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);
|
|
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
m_reader.reset();
|
2016-03-10 12:01:30 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-26 15:29:54 +02:00
|
|
|
static CMakeBuildTarget utilityTarget(const QString &title, const BuildDirManager *bdm)
|
|
|
|
|
{
|
|
|
|
|
CMakeBuildTarget target;
|
|
|
|
|
|
|
|
|
|
target.title = title;
|
|
|
|
|
target.targetType = UtilityType;
|
2019-06-06 13:38:28 +02:00
|
|
|
target.workingDirectory = bdm->buildDirectory();
|
2019-10-25 09:55:32 +02:00
|
|
|
target.sourceDirectory = bdm->buildSystem()->project()->projectDirectory();
|
2017-04-26 15:29:54 +02:00
|
|
|
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-07 17:13:49 +02:00
|
|
|
QList<CMakeBuildTarget> BuildDirManager::takeBuildTargets(QString &errorMessage) const
|
2016-01-20 12:19:16 +01:00
|
|
|
{
|
2017-09-28 11:32:39 +02:00
|
|
|
QList<CMakeBuildTarget> result = { utilityTarget(CMakeBuildStep::allTarget(), this),
|
|
|
|
|
utilityTarget(CMakeBuildStep::cleanTarget(), this),
|
|
|
|
|
utilityTarget(CMakeBuildStep::installTarget(), this),
|
|
|
|
|
utilityTarget(CMakeBuildStep::testTarget(), this) };
|
|
|
|
|
QTC_ASSERT(!m_isHandlingError, return result);
|
|
|
|
|
|
|
|
|
|
if (m_reader) {
|
2019-07-24 13:42:23 +02:00
|
|
|
QList<CMakeBuildTarget> readerTargets
|
|
|
|
|
= Utils::filtered(m_reader->takeBuildTargets(errorMessage),
|
|
|
|
|
[](const CMakeBuildTarget &bt) {
|
|
|
|
|
return bt.title != CMakeBuildStep::allTarget()
|
|
|
|
|
&& bt.title != CMakeBuildStep::cleanTarget()
|
|
|
|
|
&& bt.title != CMakeBuildStep::installTarget()
|
|
|
|
|
&& bt.title != CMakeBuildStep::testTarget();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Guess at the target definition position when no details are known
|
|
|
|
|
for (CMakeBuildTarget &t : readerTargets) {
|
2019-07-26 13:57:24 +02:00
|
|
|
if (t.backtrace.isEmpty()) {
|
|
|
|
|
t.backtrace.append(
|
|
|
|
|
FolderNode::LocationInfo(tr("CMakeLists.txt in source directory"),
|
|
|
|
|
t.sourceDirectory.pathAppended("CMakeLists.txt")));
|
2019-07-24 13:42:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result.append(readerTargets);
|
2017-04-26 15:29:54 +02:00
|
|
|
}
|
2017-09-28 11:32:39 +02:00
|
|
|
return result;
|
2016-01-20 12:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
2019-06-07 17:13:49 +02:00
|
|
|
CMakeConfig BuildDirManager::takeCMakeConfiguration(QString &errorMessage) const
|
2016-01-26 17:26:46 +01:00
|
|
|
{
|
2016-11-04 11:10:42 +01:00
|
|
|
if (!m_reader)
|
2017-09-28 11:32:39 +02:00
|
|
|
return CMakeConfig();
|
2017-01-18 16:53:01 +01:00
|
|
|
|
2019-06-07 17:13:49 +02:00
|
|
|
CMakeConfig result = m_reader->takeParsedConfiguration(errorMessage);
|
2017-09-28 11:32:39 +02:00
|
|
|
for (auto &ci : result)
|
2017-01-18 16:53:01 +01:00
|
|
|
ci.inCMakeCache = true;
|
|
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
return result;
|
2016-01-26 17:26:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
CMakeConfig BuildDirManager::parseCMakeConfiguration(const Utils::FilePath &cacheFile,
|
2017-09-28 11:32:39 +02:00
|
|
|
QString *errorMessage)
|
2017-02-07 17:23:30 +01:00
|
|
|
{
|
|
|
|
|
if (!cacheFile.exists()) {
|
|
|
|
|
if (errorMessage)
|
|
|
|
|
*errorMessage = tr("CMakeCache.txt file not found.");
|
|
|
|
|
return { };
|
|
|
|
|
}
|
|
|
|
|
CMakeConfig result = CMakeConfigItem::itemsFromFile(cacheFile, errorMessage);
|
|
|
|
|
if (!errorMessage->isEmpty())
|
|
|
|
|
return { };
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 12:20:49 +02:00
|
|
|
QString BuildDirManager::flagsString(int reparseFlags)
|
|
|
|
|
{
|
|
|
|
|
QString result;
|
|
|
|
|
if (reparseFlags == REPARSE_DEFAULT) {
|
|
|
|
|
result = "<NONE>";
|
|
|
|
|
} else {
|
|
|
|
|
if (reparseFlags & REPARSE_URGENT)
|
|
|
|
|
result += " URGENT";
|
|
|
|
|
if (reparseFlags & REPARSE_FORCE_CMAKE_RUN)
|
|
|
|
|
result += " FORCE_CMAKE_RUN";
|
|
|
|
|
if (reparseFlags & REPARSE_FORCE_CONFIGURATION)
|
|
|
|
|
result += " FORCE_CONFIG";
|
|
|
|
|
if (reparseFlags & REPARSE_CHECK_CONFIGURATION)
|
|
|
|
|
result += " CHECK_CONFIG";
|
|
|
|
|
if (reparseFlags & REPARSE_SCAN)
|
|
|
|
|
result += " SCAN";
|
|
|
|
|
}
|
|
|
|
|
return result.trimmed();
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-28 11:32:39 +02:00
|
|
|
bool BuildDirManager::checkConfiguration()
|
2016-05-13 16:19:31 +02:00
|
|
|
{
|
2017-10-18 13:05:44 +02:00
|
|
|
if (m_parameters.workDirectory != m_parameters.buildDirectory) // always throw away changes in the tmpdir!
|
2017-09-28 11:32:39 +02:00
|
|
|
return false;
|
2016-05-13 16:19:31 +02:00
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
const CMakeConfig cache = m_buildSystem->cmakeBuildConfiguration()->configurationFromCMake();
|
2016-05-13 16:19:31 +02:00
|
|
|
if (cache.isEmpty())
|
2017-09-28 11:32:39 +02:00
|
|
|
return false; // No cache file yet.
|
2016-05-13 16:19:31 +02:00
|
|
|
|
|
|
|
|
CMakeConfig newConfig;
|
2017-09-29 23:34:52 +02:00
|
|
|
QHash<QString, QPair<QString, QString>> changedKeys;
|
|
|
|
|
foreach (const CMakeConfigItem &projectItem, m_parameters.configuration) {
|
|
|
|
|
const QString projectKey = QString::fromUtf8(projectItem.key);
|
|
|
|
|
const QString projectValue = projectItem.expandedValue(m_parameters.expander);
|
|
|
|
|
const CMakeConfigItem &cmakeItem
|
|
|
|
|
= Utils::findOrDefault(cache, [&projectItem](const CMakeConfigItem &i) { return i.key == projectItem.key; });
|
|
|
|
|
const QString iCacheValue = QString::fromUtf8(cmakeItem.value);
|
|
|
|
|
if (cmakeItem.isNull()) {
|
|
|
|
|
changedKeys.insert(projectKey, qMakePair(tr("<removed>"), projectValue));
|
|
|
|
|
} else if (iCacheValue != projectValue) {
|
|
|
|
|
changedKeys.insert(projectKey, qMakePair(iCacheValue, projectValue));
|
|
|
|
|
newConfig.append(cmakeItem);
|
2016-05-13 16:19:31 +02:00
|
|
|
} else {
|
2017-09-29 23:34:52 +02:00
|
|
|
newConfig.append(projectItem);
|
2016-05-13 16:19:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-29 23:34:52 +02:00
|
|
|
if (!changedKeys.isEmpty()) {
|
|
|
|
|
QStringList keyList = changedKeys.keys();
|
2016-05-13 16:19:31 +02:00
|
|
|
Utils::sort(keyList);
|
2017-09-29 23:34:52 +02:00
|
|
|
QString table = QString::fromLatin1("<table><tr><th>%1</th><th>%2</th><th>%3</th></tr>")
|
2019-10-09 17:15:36 +02:00
|
|
|
.arg(tr("Key"))
|
|
|
|
|
.arg(tr("%1 Project").arg(Core::Constants::IDE_DISPLAY_NAME))
|
|
|
|
|
.arg(tr("Changed value"));
|
2016-05-13 16:19:31 +02:00
|
|
|
foreach (const QString &k, keyList) {
|
2017-09-29 23:34:52 +02:00
|
|
|
const QPair<QString, QString> data = changedKeys.value(k);
|
|
|
|
|
table += QString::fromLatin1("\n<tr><td>%1</td><td>%2</td><td>%3</td></tr>")
|
2019-10-09 17:15:36 +02:00
|
|
|
.arg(k)
|
|
|
|
|
.arg(data.second.toHtmlEscaped())
|
|
|
|
|
.arg(data.first.toHtmlEscaped());
|
2016-05-13 16:19:31 +02:00
|
|
|
}
|
|
|
|
|
table += QLatin1String("\n</table>");
|
|
|
|
|
|
|
|
|
|
QPointer<QMessageBox> box = new QMessageBox(Core::ICore::mainWindow());
|
2019-10-09 17:15:36 +02:00
|
|
|
box->setText(tr("The project has been changed outside of %1.")
|
|
|
|
|
.arg(Core::Constants::IDE_DISPLAY_NAME));
|
2017-09-29 23:34:52 +02:00
|
|
|
box->setInformativeText(table);
|
2019-10-09 17:15:36 +02:00
|
|
|
auto *defaultButton = box->addButton(tr("Discard external changes"),
|
|
|
|
|
QMessageBox::RejectRole);
|
|
|
|
|
auto *applyButton = box->addButton(tr("Adapt %1 project to changes")
|
|
|
|
|
.arg(Core::Constants::IDE_DISPLAY_NAME),
|
|
|
|
|
QMessageBox::ApplyRole);
|
2016-10-17 13:47:41 +02:00
|
|
|
box->setDefaultButton(defaultButton);
|
2016-05-13 16:19:31 +02:00
|
|
|
|
2017-07-16 10:41:54 +02:00
|
|
|
box->exec();
|
2017-09-28 11:32:39 +02:00
|
|
|
if (box->clickedButton() == applyButton) {
|
|
|
|
|
m_parameters.configuration = newConfig;
|
2019-10-25 09:55:32 +02:00
|
|
|
QSignalBlocker blocker(m_buildSystem->buildConfiguration());
|
|
|
|
|
m_buildSystem->cmakeBuildConfiguration()->setConfigurationForCMake(newConfig);
|
2017-09-28 11:32:39 +02:00
|
|
|
return false;
|
|
|
|
|
} else if (box->clickedButton() == defaultButton)
|
|
|
|
|
return true;
|
2016-01-26 17:26:46 +01:00
|
|
|
}
|
2017-09-28 11:32:39 +02:00
|
|
|
return false;
|
2016-03-10 17:34:47 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-20 12:19:16 +01:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace CMakeProjectManager
|