CMake: Move code from CMakeProject into CMakeBuildSystem

Introduce BuildSystem to implement functionality common to all
build systems out there. This includes things like delaying the
parsing by 1s.

The actual CMake specific code is then moved into a derived
class CMakeBuildSystem.

Change-Id: I84f4344430f19a44e16534db294382c436169ed5
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Tobias Hunger
2019-08-06 12:41:46 +02:00
parent fcffb5f2fc
commit d115276b34
11 changed files with 718 additions and 422 deletions

View File

@@ -10,6 +10,7 @@ add_qtc_plugin(CMakeProjectManager
cmakeautocompleter.cpp cmakeautocompleter.h
cmakebuildconfiguration.cpp cmakebuildconfiguration.h
cmakebuildsettingswidget.cpp cmakebuildsettingswidget.h
cmakebuildsystem.cpp cmakebuildsystem.h
cmakebuildstep.cpp cmakebuildstep.h
cmakebuildtarget.h
cmakecbpparser.cpp cmakecbpparser.h

View File

@@ -27,6 +27,7 @@
#include "builddirparameters.h"
#include "builddirreader.h"
#include "cmakebuildsystem.h"
#include "cmakebuildtarget.h"
#include "cmakeconfigitem.h"
@@ -87,16 +88,21 @@ public:
CMakeConfig takeCMakeConfiguration(QString &errorMessage) const;
static CMakeConfig parseCMakeConfiguration(const Utils::FilePath &cacheFile,
QString *errorMessage);
QString *errorMessage);
enum ReparseParameters {
REPARSE_DEFAULT = 0, // use defaults
REPARSE_URGENT = 1, // Do not wait for more requests, start ASAP
REPARSE_FORCE_CMAKE_RUN = 2, // Force cmake to run
REPARSE_FORCE_CONFIGURATION = 4, // Force configuration arguments to cmake
REPARSE_CHECK_CONFIGURATION = 8, // Check and warn if on-disk config and QtC config differ
REPARSE_SCAN = 16,
REPARSE_IGNORE = 32, // Do not reparse:-)
REPARSE_DEFAULT = BuildSystem::PARAM_DEFAULT, // use defaults
REPARSE_URGENT = BuildSystem::PARAM_URGENT, // Do not wait for more requests, start ASAP
REPARSE_IGNORE = BuildSystem::PARAM_IGNORE,
REPARSE_FORCE_CMAKE_RUN = (1
<< (BuildSystem::PARAM_CUSTOM_OFFSET + 0)), // Force cmake to run
REPARSE_FORCE_CONFIGURATION = (1 << (BuildSystem::PARAM_CUSTOM_OFFSET
+ 1)), // Force configuration arguments to cmake
REPARSE_CHECK_CONFIGURATION
= (1 << (BuildSystem::PARAM_CUSTOM_OFFSET
+ 2)), // Check and warn if on-disk config and QtC config differ
REPARSE_SCAN = (1 << (BuildSystem::PARAM_CUSTOM_OFFSET + 3)), // Run filesystem scan
};
static QString flagsString(int reparseFlags);

View File

@@ -91,25 +91,24 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *parent, Core::Id id)
BuildConfiguration::Unknown));
connect(project(), &Project::parsingFinished, this, &BuildConfiguration::enabledChanged);
BuildSystem *bs = qobject_cast<CMakeBuildSystem *>(project()->buildSystem());
// BuildDirManager:
connect(&m_buildDirManager, &BuildDirManager::requestReparse, this, [this](int options) {
connect(&m_buildDirManager, &BuildDirManager::requestReparse, this, [this, bs](int options) {
if (isActive()) {
qCDebug(cmakeBuildConfigurationLog)
<< "Passing on reparse request with flags" << BuildDirManager::flagsString(options);
project()->requestReparse(options);
bs->requestParse(options);
}
});
connect(&m_buildDirManager,
&BuildDirManager::dataAvailable,
this,
&CMakeBuildConfiguration::handleParsingSucceeded);
connect(&m_buildDirManager, &BuildDirManager::errorOccured, this, [this](const QString &msg) {
setError(msg);
QString errorMessage;
setConfigurationFromCMake(m_buildDirManager.takeCMakeConfiguration(errorMessage));
// ignore errorMessage here, we already got one.
project()->handleParsingError(this);
});
connect(&m_buildDirManager,
&BuildDirManager::errorOccured,
this,
&CMakeBuildConfiguration::handleParsingFailed);
connect(&m_buildDirManager, &BuildDirManager::parsingStarted, this, [this]() {
clearError(CMakeBuildConfiguration::ForceEnabledChanged::True);
});
@@ -538,7 +537,18 @@ void CMakeBuildConfiguration::handleParsingSucceeded()
target()->setDeploymentData(deploymentData());
}
project()->handleParsingSuccess(this);
static_cast<CMakeBuildSystem *>(project()->buildSystem())->handleParsingSuccess(this);
}
void CMakeBuildConfiguration::handleParsingFailed(const QString &msg)
{
setError(msg);
QString errorMessage;
setConfigurationFromCMake(m_buildDirManager.takeCMakeConfiguration(errorMessage));
// ignore errorMessage here, we already got one.
static_cast<CMakeBuildSystem *>(project()->buildSystem())->handleParsingError(this);
}
std::unique_ptr<CMakeProjectNode> CMakeBuildConfiguration::generateProjectTree(

View File

@@ -36,6 +36,7 @@
#include <projectexplorer/deploymentdata.h>
namespace CMakeProjectManager {
class CMakeBuildSystem;
class CMakeExtraBuildInfo;
class CMakeProject;
@@ -106,6 +107,7 @@ private:
void setWarning(const QString &message);
void handleParsingSucceeded();
void handleParsingFailed(const QString &msg);
std::unique_ptr<CMakeProjectNode> generateProjectTree(
const QList<const ProjectExplorer::FileNode *> &allFiles);
@@ -123,6 +125,7 @@ private:
QList<CMakeBuildTarget> m_buildTargets;
friend class CMakeBuildSettingsWidget;
friend class CMakeProjectManager::CMakeBuildSystem;
friend class CMakeProjectManager::CMakeProject;
friend class BuildDirManager;
};

View File

@@ -0,0 +1,483 @@
/****************************************************************************
**
** 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 "cmakebuildsystem.h"
#include "cmakebuildconfiguration.h"
#include "cmakeproject.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectnodes.h"
#if 0
#include "cmakebuildstep.h"
#include "cmakekitinformation.h"
#include "cmakeprojectmanager.h"
#include "cmakeprojectnodes.h"
#include <cpptools/cpprawprojectpart.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/projectinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QDir>
#include <QElapsedTimer>
#include <QSet>
#endif
#include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/cppprojectupdater.h>
#include <cpptools/generatedcodemodelsupport.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qtsupport/qtcppkitinfo.h>
#include <utils/fileutils.h>
#include <utils/mimetypes/mimetype.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
using namespace ProjectExplorer;
using namespace Utils;
namespace {
CMakeProjectManager::Internal::CMakeBuildConfiguration *activeBc(Project *p)
{
if (!p)
return nullptr;
return qobject_cast<CMakeProjectManager::Internal::CMakeBuildConfiguration *>(
p->activeTarget() ? p->activeTarget()->activeBuildConfiguration() : nullptr);
}
} // namespace
namespace CMakeProjectManager {
using namespace Internal;
Q_LOGGING_CATEGORY(cmakeBuildSystemLog, "qtc.cmake.buildsystem", QtWarningMsg);
// --------------------------------------------------------------------
// BuildSystem:
// --------------------------------------------------------------------
BuildSystem::BuildSystem(Project *project)
: m_project(project)
{
QTC_CHECK(project);
// Timer:
m_delayedParsingTimer.setSingleShot(true);
connect(&m_delayedParsingTimer, &QTimer::timeout, this, &BuildSystem::triggerParsing);
}
BuildSystem::~BuildSystem() = default;
Project *BuildSystem::project() const
{
return m_project;
}
bool BuildSystem::isWaitingForParse() const
{
return m_delayedParsingTimer.isActive();
}
void BuildSystem::requestParse(int reparseParameters)
{
QTC_ASSERT(!(reparseParameters & PARAM_ERROR), return );
if (reparseParameters & PARAM_IGNORE)
return;
m_delayedParsingTimer.setInterval((reparseParameters & PARAM_URGENT) ? 0 : 1000);
m_delayedParsingTimer.start();
m_delayedParsingParameters = m_delayedParsingParameters | reparseParameters;
}
void BuildSystem::triggerParsing()
{
int parameters = m_delayedParsingParameters;
m_delayedParsingParameters = BuildSystem::PARAM_DEFAULT;
QTC_CHECK(!m_project->isParsing());
QTC_ASSERT((parameters & BuildSystem::PARAM_ERROR) == 0, return );
if (parameters & BuildSystem::PARAM_IGNORE)
return;
// Clear buildsystem specific parameters before passing them on!
parameters = parameters
& ~(BuildSystem::PARAM_ERROR | BuildSystem::PARAM_IGNORE
| BuildSystem::PARAM_URGENT);
{
ParsingContext ctx(m_project->guardParsingRun(), parameters, m_project, activeBc(m_project));
if (validateParsingContext(ctx))
parseProject(std::move(ctx));
}
}
// --------------------------------------------------------------------
// CMakeBuildSystem:
// --------------------------------------------------------------------
CMakeBuildSystem::CMakeBuildSystem(CMakeProject *p)
: BuildSystem(p)
, m_cppCodeModelUpdater(new CppTools::CppProjectUpdater)
{
// TreeScanner:
connect(&m_treeScanner,
&TreeScanner::finished,
this,
&CMakeBuildSystem::handleTreeScanningFinished);
m_treeScanner.setFilter([this, p](const Utils::MimeType &mimeType, const Utils::FilePath &fn) {
// Mime checks requires more resources, so keep it last in check list
auto isIgnored = fn.toString().startsWith(p->projectFilePath().toString() + ".user")
|| TreeScanner::isWellKnownBinary(mimeType, fn);
// Cache mime check result for speed up
if (!isIgnored) {
auto it = m_mimeBinaryCache.find(mimeType.name());
if (it != m_mimeBinaryCache.end()) {
isIgnored = *it;
} else {
isIgnored = TreeScanner::isMimeBinary(mimeType, fn);
m_mimeBinaryCache[mimeType.name()] = isIgnored;
}
}
return isIgnored;
});
m_treeScanner.setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FilePath &fn) {
auto type = TreeScanner::genericFileType(mimeType, fn);
if (type == FileType::Unknown) {
if (mimeType.isValid()) {
const QString mt = mimeType.name();
if (mt == CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE
|| mt == CMakeProjectManager::Constants::CMAKEMIMETYPE)
type = FileType::Project;
}
}
return type;
});
}
CMakeBuildSystem::~CMakeBuildSystem()
{
if (!m_treeScanner.isFinished()) {
auto future = m_treeScanner.future();
future.cancel();
future.waitForFinished();
}
delete m_cppCodeModelUpdater;
qDeleteAll(m_extraCompilers);
qDeleteAll(m_allFiles);
}
bool CMakeBuildSystem::validateParsingContext(const ParsingContext &ctx)
{
return ctx.project && qobject_cast<CMakeBuildConfiguration *>(ctx.buildConfiguration);
}
void CMakeBuildSystem::parseProject(ParsingContext &&ctx)
{
m_currentContext = std::move(ctx);
auto bc = qobject_cast<CMakeBuildConfiguration *>(m_currentContext.buildConfiguration);
int parameters = m_currentContext.parameters;
if (m_allFiles.isEmpty())
parameters |= BuildDirManager::REPARSE_SCAN;
m_waitingForScan = parameters & BuildDirManager::REPARSE_SCAN;
m_waitingForParse = true;
m_combinedScanAndParseResult = true;
if (m_waitingForScan) {
QTC_CHECK(m_treeScanner.isFinished());
m_treeScanner.asyncScanForFiles(m_currentContext.project->projectDirectory());
Core::ProgressManager::addTask(m_treeScanner.future(),
tr("Scan \"%1\" project tree")
.arg(m_currentContext.project->displayName()),
"CMake.Scan.Tree");
}
bc->m_buildDirManager.parse(parameters);
}
void CMakeBuildSystem::handleTreeScanningFinished()
{
QTC_CHECK(m_waitingForScan);
qDeleteAll(m_allFiles);
m_allFiles = Utils::transform(m_treeScanner.release(), [](const FileNode *fn) { return fn; });
m_combinedScanAndParseResult = m_combinedScanAndParseResult && true;
m_waitingForScan = false;
combineScanAndParse();
}
void CMakeBuildSystem::handleParsingSuccess(CMakeBuildConfiguration *bc)
{
if (bc != m_currentContext.buildConfiguration)
return; // Not current information, ignore.
QTC_ASSERT(m_waitingForParse, return );
m_waitingForParse = false;
m_combinedScanAndParseResult = m_combinedScanAndParseResult && true;
combineScanAndParse();
}
void CMakeBuildSystem::handleParsingError(CMakeBuildConfiguration *bc)
{
if (bc != m_currentContext.buildConfiguration)
return; // Not current information, ignore.
QTC_CHECK(m_waitingForParse);
m_waitingForParse = false;
m_combinedScanAndParseResult = false;
combineScanAndParse();
}
void CMakeBuildSystem::combineScanAndParse()
{
auto bc = qobject_cast<CMakeBuildConfiguration *>(m_currentContext.buildConfiguration);
if (bc && bc->isActive()) {
if (m_waitingForParse || m_waitingForScan)
return;
if (m_combinedScanAndParseResult) {
updateProjectData(qobject_cast<CMakeProject *>(m_currentContext.project), bc);
}
}
m_currentContext = BuildSystem::ParsingContext();
}
void CMakeBuildSystem::updateProjectData(CMakeProject *p, CMakeBuildConfiguration *bc)
{
qCDebug(cmakeBuildSystemLog) << "Updating CMake project data";
QTC_ASSERT(m_treeScanner.isFinished() && !bc->m_buildDirManager.isParsing(), return );
CMakeConfig patchedConfig = bc->configurationFromCMake();
{
CMakeConfigItem settingFileItem;
settingFileItem.key = "ANDROID_DEPLOYMENT_SETTINGS_FILE";
settingFileItem.value = bc->buildDirectory()
.pathAppended("android_deployment_settings.json")
.toString()
.toUtf8();
patchedConfig.append(settingFileItem);
}
{
QSet<QString> res;
QStringList apps;
for (const auto &target : bc->buildTargets()) {
if (target.targetType == CMakeProjectManager::DynamicLibraryType) {
res.insert(target.executable.parentDir().toString());
apps.push_back(target.executable.toUserOutput());
}
// ### shall we add also the ExecutableType ?
}
{
CMakeConfigItem paths;
paths.key = "ANDROID_SO_LIBS_PATHS";
paths.values = Utils::toList(res);
patchedConfig.append(paths);
}
apps.sort();
{
CMakeConfigItem appsPaths;
appsPaths.key = "TARGETS_BUILD_PATH";
appsPaths.values = apps;
patchedConfig.append(appsPaths);
}
}
{
auto newRoot = bc->generateProjectTree(m_allFiles);
if (newRoot) {
p->setRootProjectNode(std::move(newRoot));
if (p->rootProjectNode())
p->setDisplayName(p->rootProjectNode()->displayName());
for (const CMakeBuildTarget &bt : bc->buildTargets()) {
const QString buildKey = bt.title;
if (ProjectNode *node = p->findNodeForBuildKey(buildKey)) {
if (auto targetNode = dynamic_cast<CMakeTargetNode *>(node))
targetNode->setConfig(patchedConfig);
}
}
}
}
{
qDeleteAll(m_extraCompilers);
m_extraCompilers = findExtraCompilers(p);
CppTools::GeneratedCodeModelSupport::update(m_extraCompilers);
qCDebug(cmakeBuildSystemLog) << "Extra compilers updated.";
}
QtSupport::CppKitInfo kitInfo(p);
QTC_ASSERT(kitInfo.isValid(), return );
{
QString errorMessage;
CppTools::RawProjectParts rpps = bc->m_buildDirManager.createRawProjectParts(errorMessage);
if (!errorMessage.isEmpty())
bc->setError(errorMessage);
qCDebug(cmakeBuildSystemLog) << "Raw project parts created." << errorMessage;
for (CppTools::RawProjectPart &rpp : rpps) {
rpp.setQtVersion(
kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt.
if (kitInfo.cxxToolChain)
rpp.setFlagsForCxx({kitInfo.cxxToolChain, rpp.flagsForCxx.commandLineFlags});
if (kitInfo.cToolChain)
rpp.setFlagsForC({kitInfo.cToolChain, rpp.flagsForC.commandLineFlags});
}
m_cppCodeModelUpdater->update({p, kitInfo, bc->environment(), rpps});
}
{
updateQmlJSCodeModel(p, bc);
}
emit p->fileListChanged();
emit bc->emitBuildTypeChanged();
bc->m_buildDirManager.resetData();
qCDebug(cmakeBuildSystemLog) << "All CMake project data up to date.";
}
QList<ProjectExplorer::ExtraCompiler *> CMakeBuildSystem::findExtraCompilers(CMakeProject *p)
{
qCDebug(cmakeBuildSystemLog) << "Finding Extra Compilers: start.";
QList<ProjectExplorer::ExtraCompiler *> extraCompilers;
const QList<ExtraCompilerFactory *> factories = ExtraCompilerFactory::extraCompilerFactories();
qCDebug(cmakeBuildSystemLog) << "Finding Extra Compilers: Got factories.";
const QSet<QString> fileExtensions = Utils::transform<QSet>(factories,
&ExtraCompilerFactory::sourceTag);
qCDebug(cmakeBuildSystemLog) << "Finding Extra Compilers: Got file extensions:"
<< fileExtensions;
// Find all files generated by any of the extra compilers, in a rather crude way.
const FilePathList fileList = p->files([&fileExtensions, p](const Node *n) {
if (!p->SourceFiles(n))
return false;
const QString fp = n->filePath().toString();
const int pos = fp.lastIndexOf('.');
return pos >= 0 && fileExtensions.contains(fp.mid(pos + 1));
});
qCDebug(cmakeBuildSystemLog) << "Finding Extra Compilers: Got list of files to check.";
// Generate the necessary information:
for (const FilePath &file : fileList) {
qCDebug(cmakeBuildSystemLog)
<< "Finding Extra Compilers: Processing" << file.toUserOutput();
ExtraCompilerFactory *factory = Utils::findOrDefault(factories,
[&file](const ExtraCompilerFactory *f) {
return file.endsWith(
'.' + f->sourceTag());
});
QTC_ASSERT(factory, continue);
QStringList generated = p->filesGeneratedFrom(file.toString());
qCDebug(cmakeBuildSystemLog)
<< "Finding Extra Compilers: generated files:" << generated;
if (generated.isEmpty())
continue;
const FilePathList fileNames = transform(generated, [](const QString &s) {
return FilePath::fromString(s);
});
extraCompilers.append(factory->create(p, file, fileNames));
qCDebug(cmakeBuildSystemLog)
<< "Finding Extra Compilers: done with" << file.toUserOutput();
}
qCDebug(cmakeBuildSystemLog) << "Finding Extra Compilers: done.";
return extraCompilers;
}
void CMakeBuildSystem::updateQmlJSCodeModel(CMakeProject *p, CMakeBuildConfiguration *bc)
{
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
if (!modelManager)
return;
QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager
->defaultProjectInfoForProject(p);
projectInfo.importPaths.clear();
const CMakeConfig &cm = bc->configurationFromCMake();
const QString cmakeImports = QString::fromUtf8(CMakeConfigItem::valueOf("QML_IMPORT_PATH", cm));
foreach (const QString &cmakeImport, CMakeConfigItem::cmakeSplitValue(cmakeImports))
projectInfo.importPaths.maybeInsert(FilePath::fromString(cmakeImport), QmlJS::Dialect::Qml);
modelManager->updateProjectInfo(projectInfo, p);
}
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,174 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <projectexplorer/project.h>
#include <projectexplorer/treescanner.h>
#include <QTimer>
namespace CppTools {
class CppProjectUpdater;
} // namespace CppTools
namespace ProjectExplorer {
class BuildConfiguration;
class ExtraCompiler;
} // namespace ProjectExplorer
namespace CMakeProjectManager {
class CMakeProject;
namespace Internal {
class CMakeBuildConfiguration;
} // namespace Internal
// --------------------------------------------------------------------
// BuildSystem:
// --------------------------------------------------------------------
class BuildSystem : public QObject
{
Q_OBJECT
public:
const static int PARAM_CUSTOM_OFFSET = 3;
enum Parameters : int {
PARAM_DEFAULT = 0, // use defaults
PARAM_IGNORE = (1 << (PARAM_CUSTOM_OFFSET - 3)), // Ignore this request without raising a fuss
PARAM_ERROR = (1 << (PARAM_CUSTOM_OFFSET - 2)), // Ignore this request and warn
PARAM_URGENT = (1 << (PARAM_CUSTOM_OFFSET - 1)), // Do not wait for more requests, start ASAP
};
explicit BuildSystem(ProjectExplorer::Project *project);
~BuildSystem() override;
BuildSystem(const BuildSystem &other) = delete;
ProjectExplorer::Project *project() const;
bool isWaitingForParse() const;
void requestParse(int reparseParameters); // request a (delayed!) parser run.
protected:
class ParsingContext
{
public:
ParsingContext() = default;
ParsingContext(const ParsingContext &other) = delete;
ParsingContext &operator=(const ParsingContext &other) = delete;
ParsingContext(ParsingContext &&other) = default;
ParsingContext &operator=(ParsingContext &&other) = default;
ProjectExplorer::Project::ParseGuard guard;
int parameters = PARAM_DEFAULT;
ProjectExplorer::Project *project = nullptr;
ProjectExplorer::BuildConfiguration *buildConfiguration = nullptr;
private:
ParsingContext(ProjectExplorer::Project::ParseGuard &&g,
int params,
ProjectExplorer::Project *p,
ProjectExplorer::BuildConfiguration *bc)
: guard(std::move(g))
, parameters(params)
, project(p)
, buildConfiguration(bc)
{}
friend class BuildSystem;
};
virtual bool validateParsingContext(const ParsingContext &ctx)
{
Q_UNUSED(ctx)
return true;
}
virtual void parseProject(ParsingContext &&ctx) = 0; // actual code to parse project
private:
void triggerParsing();
ProjectExplorer::Project *m_project;
QTimer m_delayedParsingTimer;
int m_delayedParsingParameters = PARAM_DEFAULT;
};
// --------------------------------------------------------------------
// CMakeBuildSystem:
// --------------------------------------------------------------------
class CMakeBuildSystem : public BuildSystem
{
Q_OBJECT
public:
explicit CMakeBuildSystem(CMakeProject *project);
~CMakeBuildSystem() final;
protected:
bool validateParsingContext(const ParsingContext &ctx) final;
void parseProject(ParsingContext &&ctx) final;
private:
// Treescanner states:
void handleTreeScanningFinished();
// Parser states:
void handleParsingSuccess(Internal::CMakeBuildConfiguration *bc);
void handleParsingError(Internal::CMakeBuildConfiguration *bc);
// Combining Treescanner and Parser states:
void combineScanAndParse();
void updateProjectData(CMakeProject *p, Internal::CMakeBuildConfiguration *bc);
QList<ProjectExplorer::ExtraCompiler *> findExtraCompilers(CMakeProject *p);
void updateQmlJSCodeModel(CMakeProject *p, Internal::CMakeBuildConfiguration *bc);
ProjectExplorer::TreeScanner m_treeScanner;
QHash<QString, bool> m_mimeBinaryCache;
QList<const ProjectExplorer::FileNode *> m_allFiles;
bool m_waitingForScan = false;
bool m_waitingForParse = false;
bool m_combinedScanAndParseResult = false;
ParsingContext m_currentContext;
CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
friend class Internal::CMakeBuildConfiguration; // For handleParsing* callbacks
};
} // namespace CMakeProjectManager

View File

@@ -67,27 +67,6 @@ using namespace Utils;
namespace CMakeProjectManager {
Q_LOGGING_CATEGORY(cmakeProjectLog, "qtc.cmake.project", QtWarningMsg);
class TraceTimer
{
public:
TraceTimer(const QString &msg)
: m_message(msg)
{
m_timer.start();
}
~TraceTimer()
{
qCInfo(cmakeProjectLog) << QString("%1 (%2ms)").arg(m_message).arg(m_timer.elapsed());
}
private:
QElapsedTimer m_timer;
QString m_message;
};
using namespace Internal;
static CMakeBuildConfiguration *activeBc(const CMakeProject *p)
@@ -105,200 +84,18 @@ static CMakeBuildConfiguration *activeBc(const CMakeProject *p)
*/
CMakeProject::CMakeProject(const FilePath &fileName)
: Project(Constants::CMAKEMIMETYPE, fileName)
, m_cppCodeModelUpdater(new CppTools::CppProjectUpdater)
{
m_buildsystem = std::make_unique<CMakeBuildSystem>(this);
setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID);
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
setDisplayName(projectDirectory().fileName());
setCanBuildProducts();
setKnowsAllBuildExecutables(false);
setHasMakeInstallEquivalent(true);
// Timer:
m_delayedParsingTimer.setSingleShot(true);
connect(&m_delayedParsingTimer, &QTimer::timeout,
this, [this]() { startParsing(m_delayedParsingParameters); });
// TreeScanner:
connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished);
m_treeScanner.setFilter([this](const Utils::MimeType &mimeType, const Utils::FilePath &fn) {
// Mime checks requires more resources, so keep it last in check list
auto isIgnored =
fn.toString().startsWith(projectFilePath().toString() + ".user") ||
TreeScanner::isWellKnownBinary(mimeType, fn);
// Cache mime check result for speed up
if (!isIgnored) {
auto it = m_mimeBinaryCache.find(mimeType.name());
if (it != m_mimeBinaryCache.end()) {
isIgnored = *it;
} else {
isIgnored = TreeScanner::isMimeBinary(mimeType, fn);
m_mimeBinaryCache[mimeType.name()] = isIgnored;
}
}
return isIgnored;
});
m_treeScanner.setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FilePath &fn) {
auto type = TreeScanner::genericFileType(mimeType, fn);
if (type == FileType::Unknown) {
if (mimeType.isValid()) {
const QString mt = mimeType.name();
if (mt == CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE
|| mt == CMakeProjectManager::Constants::CMAKEMIMETYPE)
type = FileType::Project;
}
}
return type;
});
}
CMakeProject::~CMakeProject()
{
if (!m_treeScanner.isFinished()) {
auto future = m_treeScanner.future();
future.cancel();
future.waitForFinished();
}
delete m_cppCodeModelUpdater;
qDeleteAll(m_extraCompilers);
qDeleteAll(m_allFiles);
}
void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc)
{
TraceTimer updateProjectTotalTimer(Q_FUNC_INFO);
qCDebug(cmakeProjectLog) << "Updating CMake project data";
const CMakeBuildConfiguration *aBc = activeBc(this);
QString errorMessage;
QTC_ASSERT(bc, return);
QTC_ASSERT(bc == aBc, return);
QTC_ASSERT(m_treeScanner.isFinished() && !bc->m_buildDirManager.isParsing(), return );
CMakeConfig patchedConfig = bc->configurationFromCMake();
{
CMakeConfigItem settingFileItem;
settingFileItem.key = "ANDROID_DEPLOYMENT_SETTINGS_FILE";
settingFileItem.value = bc->buildDirectory()
.pathAppended("android_deployment_settings.json")
.toString()
.toUtf8();
patchedConfig.append(settingFileItem);
}
{
TraceTimer appsTimer(" application data");
QSet<QString> res;
QStringList apps;
for (const auto &target : bc->buildTargets()) {
if (target.targetType == CMakeProjectManager::DynamicLibraryType) {
res.insert(target.executable.parentDir().toString());
apps.push_back(target.executable.toUserOutput());
}
// ### shall we add also the ExecutableType ?
}
{
CMakeConfigItem paths;
paths.key = "ANDROID_SO_LIBS_PATHS";
paths.values = Utils::toList(res);
patchedConfig.append(paths);
}
apps.sort();
{
CMakeConfigItem appsPaths;
appsPaths.key = "TARGETS_BUILD_PATH";
appsPaths.values = apps;
patchedConfig.append(appsPaths);
}
}
{
TraceTimer projectTreeTimer(" project tree");
auto newRoot = bc->generateProjectTree(m_allFiles);
if (newRoot) {
setRootProjectNode(std::move(newRoot));
if (rootProjectNode())
setDisplayName(rootProjectNode()->displayName());
for (const CMakeBuildTarget &bt : bc->buildTargets()) {
const QString buildKey = bt.title;
if (ProjectNode *node = findNodeForBuildKey(buildKey)) {
if (auto targetNode = dynamic_cast<CMakeTargetNode *>(node))
targetNode->setConfig(patchedConfig);
}
}
}
}
{
TraceTimer projectTreeTimer(" extra compilers");
qDeleteAll(m_extraCompilers);
m_extraCompilers = findExtraCompilers();
CppTools::GeneratedCodeModelSupport::update(m_extraCompilers);
qCDebug(cmakeProjectLog) << "Extra compilers updated.";
}
QtSupport::CppKitInfo kitInfo(this);
QTC_ASSERT(kitInfo.isValid(), return);
{
TraceTimer cxxCodemodelTimer(" cxx codemodel");
CppTools::RawProjectParts rpps = bc->m_buildDirManager.createRawProjectParts(errorMessage);
checkAndReportError(errorMessage);
qCDebug(cmakeProjectLog) << "Raw project parts created.";
for (CppTools::RawProjectPart &rpp : rpps) {
rpp.setQtVersion(
kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt.
if (kitInfo.cxxToolChain)
rpp.setFlagsForCxx({kitInfo.cxxToolChain, rpp.flagsForCxx.commandLineFlags});
if (kitInfo.cToolChain)
rpp.setFlagsForC({kitInfo.cToolChain, rpp.flagsForC.commandLineFlags});
}
m_cppCodeModelUpdater->update({this, kitInfo, activeBuildEnvironment(), rpps});
}
{
TraceTimer qmlCodemodelTimer(" qml codemodel");
updateQmlJSCodeModel(bc);
}
emit fileListChanged();
emit bc->emitBuildTypeChanged();
bc->m_buildDirManager.resetData(); // Clear remaining data
qCDebug(cmakeProjectLog) << "All CMake project data up to date.";
}
void CMakeProject::updateQmlJSCodeModel(CMakeBuildConfiguration *bc)
{
QTC_ASSERT(bc, return );
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
if (!modelManager)
return;
QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
modelManager->defaultProjectInfoForProject(this);
projectInfo.importPaths.clear();
const CMakeConfig &cm = bc->configurationFromCMake();
const QString cmakeImports = QString::fromUtf8(CMakeConfigItem::valueOf("QML_IMPORT_PATH", cm));
foreach (const QString &cmakeImport, CMakeConfigItem::cmakeSplitValue(cmakeImports))
projectInfo.importPaths.maybeInsert(FilePath::fromString(cmakeImport), QmlJS::Dialect::Qml);
modelManager->updateProjectInfo(projectInfo, this);
}
CMakeProject::~CMakeProject() = default;
Tasks CMakeProject::projectIssues(const Kit *k) const
{
@@ -330,7 +127,6 @@ void CMakeProject::runCMakeAndScanProjectTree()
CMakeBuildConfiguration *bc = activeBc(this);
if (isParsing() || !bc)
return;
QTC_ASSERT(m_treeScanner.isFinished(), return);
BuildDirParameters parameters(bc);
bc->m_buildDirManager.setParametersAndRequestParse(parameters,
@@ -366,33 +162,6 @@ void CMakeProject::clearCMakeCache()
bc->m_buildDirManager.clearCache();
}
void CMakeProject::startParsing(int reparseParameters)
{
m_delayedParsingParameters = BuildDirManager::REPARSE_DEFAULT;
if (reparseParameters & BuildDirManager::REPARSE_IGNORE)
return;
CMakeBuildConfiguration *bc = activeBc(this);
QTC_ASSERT(bc, return );
m_parseGuard = std::move(guardParsingRun());
m_waitingForScan = reparseParameters & BuildDirManager::REPARSE_SCAN;
m_waitingForParse = true;
m_combinedScanAndParseResult = true;
if (m_waitingForScan) {
QTC_CHECK(m_treeScanner.isFinished());
m_treeScanner.asyncScanForFiles(projectDirectory());
Core::ProgressManager::addTask(m_treeScanner.future(),
tr("Scan \"%1\" project tree").arg(displayName()),
"CMake.Scan.Tree");
}
bc->m_buildDirManager.parse(reparseParameters);
}
bool CMakeProject::setupTarget(Target *t)
{
t->updateDefaultBuildConfigurations();
@@ -402,80 +171,6 @@ bool CMakeProject::setupTarget(Target *t)
return true;
}
void CMakeProject::reportError(const QString &errorMessage) const
{
CMakeBuildConfiguration *bc = activeBc(this);
if (bc)
bc->setError(errorMessage);
}
void CMakeProject::requestReparse(int reparseParameters)
{
if (reparseParameters & BuildDirManager::REPARSE_IGNORE)
return;
m_delayedParsingTimer.setInterval((reparseParameters & BuildDirManager::REPARSE_URGENT) ? 0
: 1000);
m_delayedParsingTimer.start();
m_delayedParsingParameters = m_delayedParsingParameters | reparseParameters;
if (m_allFiles.isEmpty())
m_delayedParsingParameters |= BuildDirManager::REPARSE_SCAN;
}
void CMakeProject::handleTreeScanningFinished()
{
QTC_CHECK(m_waitingForScan);
qDeleteAll(m_allFiles);
m_allFiles = Utils::transform(m_treeScanner.release(), [](const FileNode *fn) { return fn; });
CMakeBuildConfiguration *bc = activeBc(this);
QTC_ASSERT(bc, return);
m_combinedScanAndParseResult = m_combinedScanAndParseResult && true;
m_waitingForScan = false;
combineScanAndParse(bc);
}
void CMakeProject::handleParsingSuccess(CMakeBuildConfiguration *bc)
{
QTC_ASSERT(m_waitingForParse, return);
m_waitingForParse = false;
m_combinedScanAndParseResult = m_combinedScanAndParseResult && true;
combineScanAndParse(bc);
}
void CMakeProject::handleParsingError(CMakeBuildConfiguration *bc)
{
QTC_CHECK(m_waitingForParse);
m_waitingForParse = false;
m_combinedScanAndParseResult = false;
combineScanAndParse(bc);
}
void CMakeProject::combineScanAndParse(CMakeBuildConfiguration *bc)
{
QTC_ASSERT(bc && bc->isActive(), return);
if (m_waitingForParse || m_waitingForScan)
return;
if (m_combinedScanAndParseResult) {
m_parseGuard.markAsSuccess();
updateProjectData(bc);
}
{
TraceTimer parsingDoneTimer(" parsing finished signal");
m_parseGuard = {};
}
}
QStringList CMakeProject::filesGeneratedFrom(const QString &sourceFile) const
{
if (!activeTarget())
@@ -538,69 +233,9 @@ MakeInstallCommand CMakeProject::makeInstallCommand(const Target *target,
return cmd;
}
bool CMakeProject::mustUpdateCMakeStateBeforeBuild()
bool CMakeProject::mustUpdateCMakeStateBeforeBuild() const
{
return m_delayedParsingTimer.isActive();
}
void CMakeProject::checkAndReportError(QString &errorMessage) const
{
if (!errorMessage.isEmpty()) {
reportError(errorMessage);
errorMessage.clear();
}
}
QList<ProjectExplorer::ExtraCompiler *> CMakeProject::findExtraCompilers() const
{
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: start.";
QList<ProjectExplorer::ExtraCompiler *> extraCompilers;
const QList<ExtraCompilerFactory *> factories = ExtraCompilerFactory::extraCompilerFactories();
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: Got factories.";
const QSet<QString> fileExtensions = Utils::transform<QSet>(factories,
&ExtraCompilerFactory::sourceTag);
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: Got file extensions:" << fileExtensions;
// Find all files generated by any of the extra compilers, in a rather crude way.
const FilePathList fileList = files([&fileExtensions](const Node *n) {
if (!SourceFiles(n))
return false;
const QString fp = n->filePath().toString();
const int pos = fp.lastIndexOf('.');
return pos >= 0 && fileExtensions.contains(fp.mid(pos + 1));
});
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: Got list of files to check.";
// Generate the necessary information:
for (const FilePath &file : fileList) {
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: Processing" << file.toUserOutput();
ExtraCompilerFactory *factory = Utils::findOrDefault(factories,
[&file](const ExtraCompilerFactory *f) {
return file.endsWith(
'.' + f->sourceTag());
});
QTC_ASSERT(factory, continue);
QStringList generated = filesGeneratedFrom(file.toString());
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: generated files:" << generated;
if (generated.isEmpty())
continue;
const FilePathList fileNames
= transform(generated,
[](const QString &s) { return FilePath::fromString(s); });
extraCompilers.append(factory->create(this, file, fileNames));
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: done with" << file.toUserOutput();
}
qCDebug(cmakeProjectLog) << "Finding Extra Compilers: done.";
return extraCompilers;
return buildSystem()->isWaitingForParse();
}
} // namespace CMakeProjectManager

View File

@@ -28,6 +28,7 @@
#include "cmake_global.h"
#include "builddirmanager.h"
#include "cmakebuildsystem.h"
#include "cmakebuildtarget.h"
#include "cmakeprojectimporter.h"
@@ -49,6 +50,8 @@ namespace ProjectExplorer { class FileNode; }
namespace CMakeProjectManager {
class BuildSystem;
namespace Internal {
class CMakeBuildConfiguration;
class CMakeBuildSettingsWidget;
@@ -63,6 +66,8 @@ public:
explicit CMakeProject(const Utils::FilePath &filename);
~CMakeProject() final;
BuildSystem *buildSystem() const { return m_buildsystem.get(); }
ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final;
void runCMake();
@@ -75,53 +80,26 @@ public:
bool persistCMakeState();
void clearCMakeCache();
bool mustUpdateCMakeStateBeforeBuild();
void checkAndReportError(QString &errorMessage) const;
void reportError(const QString &errorMessage) const;
void requestReparse(int reparseParameters);
bool mustUpdateCMakeStateBeforeBuild() const;
protected:
bool setupTarget(ProjectExplorer::Target *t) final;
private:
void startParsing(int reparseParameters);
void handleTreeScanningFinished();
void handleParsingSuccess(Internal::CMakeBuildConfiguration *bc);
void handleParsingError(Internal::CMakeBuildConfiguration *bc);
void combineScanAndParse(Internal::CMakeBuildConfiguration *bc);
void updateProjectData(Internal::CMakeBuildConfiguration *bc);
void updateQmlJSCodeModel(Internal::CMakeBuildConfiguration *bc);
QList<ProjectExplorer::ExtraCompiler *> findExtraCompilers() const;
QStringList filesGeneratedFrom(const QString &sourceFile) const final;
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
ProjectExplorer::MakeInstallCommand makeInstallCommand(const ProjectExplorer::Target *target,
const QString &installRoot) override;
CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
ProjectExplorer::TreeScanner m_treeScanner;
bool m_waitingForScan = false;
bool m_waitingForParse = false;
bool m_combinedScanAndParseResult = false;
QHash<QString, bool> m_mimeBinaryCache;
QList<const ProjectExplorer::FileNode *> m_allFiles;
mutable std::unique_ptr<Internal::CMakeProjectImporter> m_projectImporter;
QTimer m_delayedParsingTimer;
int m_delayedParsingParameters = 0;
std::unique_ptr<CMakeBuildSystem> m_buildsystem;
ParseGuard m_parseGuard;
// friend class Internal::CMakeBuildConfiguration;
// friend class Internal::CMakeBuildSettingsWidget;
friend class Internal::CMakeBuildConfiguration;
friend class Internal::CMakeBuildSettingsWidget;
friend class CMakeBuildSystem;
};
} // namespace CMakeProjectManager

View File

@@ -5,6 +5,7 @@ HEADERS = builddirmanager.h \
builddirparameters.h \
builddirreader.h \
cmakebuildstep.h \
cmakebuildsystem.h \
cmakebuildtarget.h \
cmakeconfigitem.h \
cmakeprocess.h \
@@ -45,6 +46,7 @@ SOURCES = builddirmanager.cpp \
builddirparameters.cpp \
builddirreader.cpp \
cmakebuildstep.cpp \
cmakebuildsystem.cpp \
cmakeconfigitem.cpp \
cmakeprocess.cpp \
cmakeproject.cpp \

View File

@@ -32,6 +32,8 @@ QtcPlugin {
"cmakebuildsettingswidget.h",
"cmakebuildstep.cpp",
"cmakebuildstep.h",
"cmakebuildsystem.cpp",
"cmakebuildsystem.h",
"cmakebuildtarget.h",
"cmakecbpparser.cpp",
"cmakecbpparser.h",

View File

@@ -249,6 +249,9 @@ public:
friend class Project;
};
// FIXME: Make this private and the BuildSystem a friend
ParseGuard guardParsingRun() { return ParseGuard(this); }
signals:
void displayNameChanged();
void fileListChanged();
@@ -280,8 +283,6 @@ signals:
void rootProjectDirectoryChanged();
protected:
ParseGuard guardParsingRun() { return ParseGuard(this); }
virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage);
void createTargetFromMap(const QVariantMap &map, int index);
virtual bool setupTarget(Target *t);
@@ -323,6 +324,7 @@ private:
void handleSubTreeChanged(FolderNode *node);
void setActiveTarget(Target *target);
ProjectPrivate *d;
friend class ContainerNode;