2019-06-13 14:24:04 +02: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 "fileapireader.h"
|
|
|
|
|
|
|
|
|
|
#include "cmakebuildconfiguration.h"
|
|
|
|
|
#include "cmakeprojectconstants.h"
|
|
|
|
|
#include "cmakeprojectmanager.h"
|
|
|
|
|
#include "fileapidataextractor.h"
|
|
|
|
|
#include "projecttreehelper.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/fileiconprovider.h>
|
|
|
|
|
#include <coreplugin/messagemanager.h>
|
|
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
|
|
|
|
#include <projectexplorer/projectexplorer.h>
|
|
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <projectexplorer/task.h>
|
|
|
|
|
#include <projectexplorer/taskhub.h>
|
|
|
|
|
#include <projectexplorer/toolchain.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/optional.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/runextensions.h>
|
|
|
|
|
|
|
|
|
|
#include <QDateTime>
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace CMakeProjectManager {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
Q_LOGGING_CATEGORY(cmakeFileApiMode, "qtc.cmake.fileApiMode", QtWarningMsg);
|
|
|
|
|
|
|
|
|
|
using namespace FileApiDetails;
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
// FileApiReader:
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
FileApiReader::FileApiReader()
|
|
|
|
|
{
|
|
|
|
|
connect(Core::EditorManager::instance(),
|
|
|
|
|
&Core::EditorManager::aboutToSave,
|
|
|
|
|
this,
|
|
|
|
|
[this](const Core::IDocument *document) {
|
2019-06-21 14:14:31 +02:00
|
|
|
if (m_cmakeFiles.contains(document->filePath())) {
|
2019-06-13 14:24:04 +02:00
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: DIRTY SIGNAL";
|
|
|
|
|
emit dirty();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FileApiReader::~FileApiReader()
|
|
|
|
|
{
|
|
|
|
|
stop();
|
|
|
|
|
resetData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::setParameters(const BuildDirParameters &p)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(cmakeFileApiMode)
|
|
|
|
|
<< "\n\n\n\n\n=============================================================\n";
|
|
|
|
|
|
|
|
|
|
// Update:
|
|
|
|
|
m_parameters = p;
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "Work directory:" << m_parameters.workDirectory.toUserOutput();
|
|
|
|
|
|
|
|
|
|
resetData();
|
|
|
|
|
|
|
|
|
|
m_fileApi = std::make_unique<FileApiParser>(m_parameters.sourceDirectory, m_parameters.workDirectory);
|
|
|
|
|
connect(m_fileApi.get(), &FileApiParser::dirty, this, [this]() {
|
|
|
|
|
if (!m_isParsing)
|
|
|
|
|
emit dirty();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: IS READY NOW SIGNAL";
|
|
|
|
|
emit isReadyNow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileApiReader::isCompatible(const BuildDirParameters &p)
|
|
|
|
|
{
|
|
|
|
|
const CMakeTool *cmakeTool = p.cmakeTool();
|
2019-06-18 11:31:46 +02:00
|
|
|
return cmakeTool && cmakeTool->readerType() == CMakeTool::FileApi;
|
2019-06-13 14:24:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::resetData()
|
|
|
|
|
{
|
|
|
|
|
m_cmakeFiles.clear();
|
|
|
|
|
if (!m_parameters.sourceDirectory.isEmpty())
|
|
|
|
|
m_cmakeFiles.insert(m_parameters.sourceDirectory.pathAppended("CMakeLists.txt"));
|
|
|
|
|
|
|
|
|
|
m_cache.clear();
|
|
|
|
|
m_buildTargets.clear();
|
|
|
|
|
m_projectParts.clear();
|
|
|
|
|
m_rootProjectNode.reset();
|
|
|
|
|
m_knownHeaders.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::parse(bool forceCMakeRun, bool forceConfiguration)
|
|
|
|
|
{
|
2019-08-09 12:20:49 +02:00
|
|
|
qCDebug(cmakeFileApiMode) << "Parse called with arguments: ForceCMakeRun:" << forceCMakeRun
|
2019-06-13 14:24:04 +02:00
|
|
|
<< " - forceConfiguration:" << forceConfiguration;
|
|
|
|
|
startState();
|
|
|
|
|
|
|
|
|
|
if (forceConfiguration) {
|
|
|
|
|
// Initial create:
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: Starting CMake with forced configuration.";
|
2019-10-17 15:20:23 +02:00
|
|
|
const FilePath path = m_parameters.buildDirectory.pathAppended("qtcsettings.cmake");
|
|
|
|
|
startCMakeState(QStringList({QString("-C"), path.toUserOutput()}));
|
2019-06-13 14:24:04 +02:00
|
|
|
// Keep m_isParsing enabled!
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QFileInfo replyFi = m_fileApi->scanForCMakeReplyFile();
|
2019-06-21 14:14:31 +02:00
|
|
|
// Only need to update when one of the following conditions is met:
|
|
|
|
|
// * The user forces the update,
|
|
|
|
|
// * There is no reply file,
|
|
|
|
|
// * One of the cmakefiles is newer than the replyFile and the user asked
|
|
|
|
|
// for creator to run CMake as needed,
|
|
|
|
|
// * A query files are newer than the reply file
|
|
|
|
|
const bool mustUpdate = forceCMakeRun || !replyFi.exists()
|
|
|
|
|
|| (m_parameters.cmakeTool() && m_parameters.cmakeTool()->isAutoRun()
|
|
|
|
|
&& anyOf(m_cmakeFiles,
|
|
|
|
|
[&replyFi](const FilePath &f) {
|
|
|
|
|
return f.toFileInfo().lastModified()
|
|
|
|
|
> replyFi.lastModified();
|
|
|
|
|
}))
|
|
|
|
|
|| anyOf(m_fileApi->cmakeQueryFilePaths(), [&replyFi](const QString &qf) {
|
|
|
|
|
return QFileInfo(qf).lastModified() > replyFi.lastModified();
|
2019-06-13 14:24:04 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (mustUpdate) {
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: Starting CMake with no arguments.";
|
|
|
|
|
startCMakeState(QStringList());
|
|
|
|
|
// Keep m_isParsing enabled!
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
endState(replyFi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::stop()
|
|
|
|
|
{
|
|
|
|
|
m_cmakeProcess.reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileApiReader::isParsing() const
|
|
|
|
|
{
|
|
|
|
|
return m_isParsing;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-15 12:22:09 +02:00
|
|
|
QVector<FilePath> FileApiReader::takeProjectFilesToWatch()
|
|
|
|
|
{
|
2019-08-29 16:38:24 +02:00
|
|
|
return QVector<FilePath>::fromList(Utils::toList(m_cmakeFiles));
|
2019-08-15 12:22:09 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-13 14:24:04 +02:00
|
|
|
QList<CMakeBuildTarget> FileApiReader::takeBuildTargets(QString &errorMessage){
|
|
|
|
|
Q_UNUSED(errorMessage)
|
|
|
|
|
|
|
|
|
|
auto result = std::move(m_buildTargets);
|
|
|
|
|
m_buildTargets.clear();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CMakeConfig FileApiReader::takeParsedConfiguration(QString &errorMessage)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(errorMessage)
|
|
|
|
|
|
|
|
|
|
CMakeConfig cache = m_cache;
|
|
|
|
|
m_cache.clear();
|
|
|
|
|
return cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<CMakeProjectNode> FileApiReader::generateProjectTree(
|
|
|
|
|
const QList<const FileNode *> &allFiles, QString &errorMessage)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(errorMessage)
|
|
|
|
|
|
|
|
|
|
addHeaderNodes(m_rootProjectNode.get(), m_knownHeaders, allFiles);
|
|
|
|
|
return std::move(m_rootProjectNode);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 18:22:45 +02:00
|
|
|
RawProjectParts FileApiReader::createRawProjectParts(QString &errorMessage)
|
2019-06-13 14:24:04 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(errorMessage)
|
|
|
|
|
|
2019-08-28 18:22:45 +02:00
|
|
|
RawProjectParts result = std::move(m_projectParts);
|
2019-06-13 14:24:04 +02:00
|
|
|
m_projectParts.clear();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::startState()
|
|
|
|
|
{
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: START STATE.";
|
|
|
|
|
QTC_ASSERT(!m_isParsing, return );
|
|
|
|
|
QTC_ASSERT(!m_future.has_value(), return );
|
|
|
|
|
|
|
|
|
|
m_isParsing = true;
|
|
|
|
|
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: CONFIGURATION STARTED SIGNAL";
|
|
|
|
|
emit configurationStarted();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::endState(const QFileInfo &replyFi)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: END STATE.";
|
|
|
|
|
QTC_ASSERT(m_isParsing, return );
|
|
|
|
|
QTC_ASSERT(!m_future.has_value(), return );
|
|
|
|
|
|
|
|
|
|
const FilePath sourceDirectory = m_parameters.sourceDirectory;
|
|
|
|
|
const FilePath buildDirectory = m_parameters.workDirectory;
|
|
|
|
|
|
2019-07-25 15:16:55 +02:00
|
|
|
m_fileApi->setParsedReplyFilePath(replyFi.filePath());
|
|
|
|
|
|
2019-06-13 14:24:04 +02:00
|
|
|
m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
|
|
|
|
|
[replyFi, sourceDirectory, buildDirectory]() {
|
|
|
|
|
auto result = std::make_unique<FileApiQtcData>();
|
|
|
|
|
FileApiData data = FileApiParser::parseData(replyFi,
|
|
|
|
|
result->errorMessage);
|
|
|
|
|
if (!result->errorMessage.isEmpty()) {
|
|
|
|
|
qWarning() << result->errorMessage;
|
|
|
|
|
return result.release();
|
|
|
|
|
}
|
|
|
|
|
*result = extractData(data, sourceDirectory, buildDirectory);
|
|
|
|
|
if (!result->errorMessage.isEmpty()) {
|
|
|
|
|
qWarning() << result->errorMessage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result.release();
|
|
|
|
|
});
|
|
|
|
|
onFinished(m_future.value(), this, [this](const QFuture<FileApiQtcData *> &f) {
|
|
|
|
|
std::unique_ptr<FileApiQtcData> value(f.result()); // Adopt the pointer again:-)
|
|
|
|
|
|
|
|
|
|
m_future = {};
|
|
|
|
|
m_isParsing = false;
|
|
|
|
|
m_cache = std::move(value->cache);
|
|
|
|
|
m_cmakeFiles = std::move(value->cmakeFiles);
|
|
|
|
|
m_buildTargets = std::move(value->buildTargets);
|
|
|
|
|
m_projectParts = std::move(value->projectParts);
|
|
|
|
|
m_rootProjectNode = std::move(value->rootProjectNode);
|
|
|
|
|
m_knownHeaders = std::move(value->knownHeaders);
|
|
|
|
|
|
|
|
|
|
if (value->errorMessage.isEmpty()) {
|
|
|
|
|
emit this->dataAvailable();
|
|
|
|
|
} else {
|
|
|
|
|
emit this->errorOccured(value->errorMessage);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::startCMakeState(const QStringList &configurationArguments)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: START CMAKE STATE.";
|
|
|
|
|
QTC_ASSERT(!m_cmakeProcess, return );
|
|
|
|
|
|
|
|
|
|
m_cmakeProcess = std::make_unique<CMakeProcess>();
|
|
|
|
|
|
|
|
|
|
connect(m_cmakeProcess.get(), &CMakeProcess::finished, this, &FileApiReader::cmakeFinishedState);
|
|
|
|
|
|
|
|
|
|
qCDebug(cmakeFileApiMode) << ">>>>>> Running cmake with arguments:" << configurationArguments;
|
|
|
|
|
m_cmakeProcess->run(m_parameters, configurationArguments);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileApiReader::cmakeFinishedState(int code, QProcess::ExitStatus status)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(cmakeFileApiMode) << "FileApiReader: CMAKE FINISHED STATE.";
|
|
|
|
|
|
|
|
|
|
Q_UNUSED(code)
|
|
|
|
|
Q_UNUSED(status)
|
|
|
|
|
|
2019-10-08 12:45:06 +02:00
|
|
|
m_cmakeProcess.release()->deleteLater();
|
|
|
|
|
|
2019-06-13 14:24:04 +02:00
|
|
|
endState(m_fileApi->scanForCMakeReplyFile());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace CMakeProjectManager
|