From 02533e61cf617157257e048fc79812c4fb880f35 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Sep 2017 11:32:39 +0200 Subject: [PATCH] CMake: Fix project parsing notification This builds on top of 08677c0b014cc44d944e32d462f502a67c948404 and fixes one more code path to go through a common entry/exit point. Change-Id: I1d00fa9242f247028e5d3b0ef3b5fe1d3f4cb03d Reviewed-by: hjk Reviewed-by: Tim Jenssen --- .../cmakeprojectmanager/builddirmanager.cpp | 253 +++++++-------- .../cmakeprojectmanager/builddirmanager.h | 66 ++-- .../builddirparameters.cpp | 82 +++++ .../cmakeprojectmanager/builddirparameters.h | 75 +++++ .../cmakeprojectmanager/builddirreader.cpp | 56 +--- .../cmakeprojectmanager/builddirreader.h | 50 +-- .../cmakebuildconfiguration.cpp | 178 ++-------- .../cmakebuildconfiguration.h | 39 +-- .../cmakeprojectmanager/cmakebuildinfo.h | 2 +- .../cmakebuildsettingswidget.cpp | 8 +- .../cmakeprojectmanager/cmakebuildstep.cpp | 5 +- .../cmakeprojectmanager/cmakebuildtarget.cpp | 46 +++ .../cmakeprojectmanager/cmakebuildtarget.h | 64 ++++ .../cmakeprojectmanager/cmakeproject.cpp | 307 ++++++++++++------ .../cmakeprojectmanager/cmakeproject.h | 48 +-- .../cmakeprojectimporter.cpp | 2 +- .../cmakeprojectmanager.cpp | 16 +- .../cmakeprojectmanager.pro | 4 + .../cmakeprojectmanager/configmodel.cpp | 36 ++ src/plugins/cmakeprojectmanager/configmodel.h | 6 +- .../cmakeprojectmanager/servermodereader.cpp | 51 +-- .../cmakeprojectmanager/servermodereader.h | 18 +- .../cmakeprojectmanager/tealeafreader.cpp | 39 ++- .../cmakeprojectmanager/tealeafreader.h | 9 +- 24 files changed, 812 insertions(+), 648 deletions(-) create mode 100644 src/plugins/cmakeprojectmanager/builddirparameters.cpp create mode 100644 src/plugins/cmakeprojectmanager/builddirparameters.h create mode 100644 src/plugins/cmakeprojectmanager/cmakebuildtarget.cpp create mode 100644 src/plugins/cmakeprojectmanager/cmakebuildtarget.h diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index b64898d2b4f..a8b5e0b56a1 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -58,18 +58,13 @@ namespace Internal { // BuildDirManager: // -------------------------------------------------------------------- -BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) : - m_buildConfiguration(bc) -{ - QTC_ASSERT(bc, return); -} - +BuildDirManager::BuildDirManager() = default; BuildDirManager::~BuildDirManager() = default; -const Utils::FileName BuildDirManager::workDirectory() const +Utils::FileName BuildDirManager::workDirectory(const BuildDirParameters ¶meters) const { - const Utils::FileName bdir = m_buildConfiguration->buildDirectory(); - const CMakeTool *cmake = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); + const Utils::FileName bdir = parameters.buildDirectory; + const CMakeTool *cmake = parameters.cmakeTool; if (bdir.exists()) { return bdir; } else { @@ -102,21 +97,21 @@ void BuildDirManager::emitErrorOccured(const QString &message) const m_isHandlingError = false; } -void BuildDirManager::updateReaderType(std::function todo) +void BuildDirManager::updateReaderType(const BuildDirParameters &p, + std::function todo) { - BuildDirReader::Parameters p(m_buildConfiguration); - p.buildDirectory = workDirectory(); - if (!m_reader || !m_reader->isCompatible(p)) { m_reader.reset(BuildDirReader::createReader(p)); connect(m_reader.get(), &BuildDirReader::configurationStarted, - this, &BuildDirManager::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); } + QTC_ASSERT(m_reader, return); + m_reader->setParameters(p); if (m_reader->isReady()) @@ -125,27 +120,7 @@ void BuildDirManager::updateReaderType(std::function todo) connect(m_reader.get(), &BuildDirReader::isReadyNow, this, todo); } -void BuildDirManager::updateReaderData() -{ - BuildDirReader::Parameters p(m_buildConfiguration); - p.buildDirectory = workDirectory(); - - m_reader->setParameters(p); -} - -void BuildDirManager::parseOnceReaderReady(bool force, bool checkForChanges) -{ - TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - - m_buildTargets.clear(); - m_cmakeCache.clear(); - if (checkForChanges) - checkConfiguration(); - m_reader->stop(); - m_reader->parse(force); -} - -void BuildDirManager::maybeForceReparseOnceReaderReady() +bool BuildDirManager::hasConfigChanged() { checkConfiguration(); @@ -158,14 +133,13 @@ void BuildDirManager::maybeForceReparseOnceReaderReady() const QByteArrayList criticalKeys = {GENERATOR_KEY, CMAKE_COMMAND_KEY, CMAKE_C_COMPILER_KEY, CMAKE_CXX_COMPILER_KEY}; - const CMakeConfig currentConfig = parsedConfiguration(); + const CMakeConfig currentConfig = takeCMakeConfiguration(); - Kit *k = m_buildConfiguration->target()->kit(); - const CMakeTool *tool = CMakeKitInformation::cmakeTool(k); - QTC_ASSERT(tool, return); // No cmake... we should not have ended up here in the first place - const QString extraKitGenerator = CMakeGeneratorKitInformation::extraGenerator(k); - const QString mainKitGenerator = CMakeGeneratorKitInformation::generator(k); - CMakeConfig targetConfig = m_buildConfiguration->cmakeConfiguration(); + const CMakeTool *tool = m_parameters.cmakeTool; + 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; targetConfig.append(CMakeConfigItem(GENERATOR_KEY, CMakeConfigItem::INTERNAL, QByteArray(), mainKitGenerator.toUtf8())); if (!extraKitGenerator.isEmpty()) @@ -183,8 +157,8 @@ void BuildDirManager::maybeForceReparseOnceReaderReady() if (ccit->key == kcit->key) { if (ccit->value != kcit->value) { if (criticalKeys.contains(kcit->key)) { - clearCache(); - return; + clearCache(); + return false; // no need to trigger a new reader, clearCache will do that } mustReparse = true; } @@ -204,8 +178,7 @@ void BuildDirManager::maybeForceReparseOnceReaderReady() // // The critical keys *must* be set in cmake configuration, so those were already // handled above. - if (mustReparse || kcit != targetConfig.constEnd()) - emit requestReparse(true); + return mustReparse || kcit != targetConfig.constEnd(); } bool BuildDirManager::isParsing() const @@ -213,83 +186,96 @@ bool BuildDirManager::isParsing() const return m_reader && m_reader->isParsing(); } +void BuildDirManager::setParametersAndRequestParse(const BuildDirParameters ¶meters, + int newReaderReparseOptions, + int existingReaderReparseOptions) +{ + QTC_ASSERT(parameters.isValid(), return); + + if (m_reader) + m_reader->stop(); + + BuildDirReader *old = m_reader.get(); + + m_parameters = parameters; + m_parameters.buildDirectory = workDirectory(parameters); + + updateReaderType(m_parameters, + [this, old, newReaderReparseOptions, existingReaderReparseOptions]() { + if (old != m_reader.get()) + emit requestReparse(newReaderReparseOptions); + else + emit requestReparse(existingReaderReparseOptions); + }); +} + +CMakeBuildConfiguration *BuildDirManager::buildConfiguration() const +{ + return m_parameters.buildConfiguration; +} + void BuildDirManager::becameDirty() { if (isParsing()) return; - Target *t = m_buildConfiguration->target()->project()->activeTarget(); - BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr; - - if (bc != m_buildConfiguration) + if (!m_parameters.buildConfiguration || !m_parameters.buildConfiguration->isActive()) return; - const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); + const CMakeTool *tool = m_parameters.cmakeTool; if (!tool->isAutoRun()) return; - emit requestReparse(false); -} - -void BuildDirManager::forceReparse() -{ - forceReparseImpl(true); -} - -void BuildDirManager::forceReparseWithoutCheckingForChanges() -{ - forceReparseImpl(false); -} - -void BuildDirManager::forceReparseImpl(bool checkForChanges) -{ - QTC_ASSERT(!m_isHandlingError, return); - - if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration) - return; - - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); - QTC_ASSERT(tool, return); - - m_reader.reset(); // Force reparse by forcing in a new reader - updateReaderType([this, checkForChanges]() { parseOnceReaderReady(true, checkForChanges); }); + emit requestReparse(REPARSE_CHECK_CONFIGURATION); } void BuildDirManager::resetData() { - QTC_ASSERT(!m_isHandlingError, return); - if (m_reader) m_reader->resetData(); - - m_cmakeCache.clear(); - m_reader.reset(); - - m_buildTargets.clear(); } bool BuildDirManager::persistCMakeState() { + QTC_ASSERT(m_parameters.isValid(), return false); + if (!m_tempDir) return false; - const QString buildDir = m_buildConfiguration->buildDirectory().toString(); - QDir dir(buildDir); - dir.mkpath(buildDir); + const Utils::FileName buildDir = m_parameters.buildDirectory; + QDir dir(buildDir.toString()); + dir.mkpath(buildDir.toString()); m_tempDir.reset(nullptr); - QTimer::singleShot(0, this, &BuildDirManager::parse); // make sure signals only happen afterwards! + emit requestReparse(REPARSE_URGENT | REPARSE_FORCE_CONFIGURATION | REPARSE_CHECK_CONFIGURATION); return true; } -void BuildDirManager::generateProjectTree(CMakeProjectNode *root, const QList &allFiles) +void BuildDirManager::parse(int reparseParameters) +{ + QTC_ASSERT(m_parameters.isValid(), return); + QTC_ASSERT(m_reader, return); + QTC_ASSERT((reparseParameters & REPARSE_FAIL) == 0, return); + QTC_ASSERT((reparseParameters & REPARSE_IGNORE) == 0, return); + + m_reader->stop(); + + TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); + + if (reparseParameters & REPARSE_CHECK_CONFIGURATION) { + if (checkConfiguration()) + reparseParameters |= REPARSE_FORCE_CONFIGURATION; + } + + m_reader->parse(reparseParameters & REPARSE_FORCE_CONFIGURATION); +} + +void BuildDirManager::generateProjectTree(CMakeProjectNode *root, const QList &allFiles) const { QTC_ASSERT(!m_isHandlingError, return); QTC_ASSERT(m_reader, return); - const Utils::FileName projectFile = m_buildConfiguration->target()->project()->projectFilePath(); - m_reader->generateProjectTree(root, allFiles); } @@ -300,17 +286,13 @@ void BuildDirManager::updateCodeModel(CppTools::RawProjectParts &rpps) return m_reader->updateCodeModel(rpps); } -void BuildDirManager::parse() -{ - updateReaderType([this]() { parseOnceReaderReady(false); }); -} - void BuildDirManager::clearCache() { + QTC_ASSERT(m_parameters.isValid(), return); QTC_ASSERT(!m_isHandlingError, return); - auto cmakeCache = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeCache.txt")); - auto cmakeFiles = Utils::FileName(workDirectory()).appendPath(QLatin1String("CMakeFiles")); + auto cmakeCache = workDirectory(m_parameters).appendPath("CMakeCache.txt"); + auto cmakeFiles = workDirectory(m_parameters).appendPath("CMakeFiles"); const bool mustCleanUp = cmakeCache.exists() || cmakeFiles.exists(); if (!mustCleanUp) @@ -319,7 +301,7 @@ void BuildDirManager::clearCache() Utils::FileUtils::removeRecursively(cmakeCache); Utils::FileUtils::removeRecursively(cmakeFiles); - forceReparse(); + m_reader.reset(); } static CMakeBuildTarget utilityTarget(const QString &title, const BuildDirManager *bdm) @@ -334,44 +316,41 @@ static CMakeBuildTarget utilityTarget(const QString &title, const BuildDirManage return target; } -QList BuildDirManager::buildTargets() const +QList BuildDirManager::takeBuildTargets() const { - QTC_ASSERT(!m_isHandlingError, return {}); + QList 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) - return QList(); - if (m_buildTargets.isEmpty()) { - m_buildTargets.append(utilityTarget(CMakeBuildStep::allTarget(), this)); - m_buildTargets.append(utilityTarget(CMakeBuildStep::cleanTarget(), this)); - m_buildTargets.append(utilityTarget(CMakeBuildStep::installTarget(), this)); - m_buildTargets.append(utilityTarget(CMakeBuildStep::testTarget(), this)); - - m_buildTargets.append(Utils::filtered(m_reader->buildTargets(), [](const CMakeBuildTarget &bt) { + if (m_reader) { + result.append(Utils::filtered(m_reader->takeBuildTargets(), [](const CMakeBuildTarget &bt) { return bt.title != CMakeBuildStep::allTarget() && bt.title != CMakeBuildStep::cleanTarget() && bt.title != CMakeBuildStep::installTarget() && bt.title != CMakeBuildStep::testTarget(); })); } - return m_buildTargets; + return result; } -CMakeConfig BuildDirManager::parsedConfiguration() const +CMakeConfig BuildDirManager::takeCMakeConfiguration() const { QTC_ASSERT(!m_isHandlingError, return {}); if (!m_reader) - return m_cmakeCache; - if (m_cmakeCache.isEmpty()) - m_cmakeCache = m_reader->takeParsedConfiguration(); + return CMakeConfig(); - for (auto &ci : m_cmakeCache) + CMakeConfig result = m_reader->takeParsedConfiguration(); + for (auto &ci : result) ci.inCMakeCache = true; - return m_cmakeCache; + return result; } -CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile, QString *errorMessage) +CMakeConfig BuildDirManager::parseCMakeConfiguration(const Utils::FileName &cacheFile, + QString *errorMessage) { if (!cacheFile.exists()) { if (errorMessage) @@ -384,25 +363,26 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile return result; } -void BuildDirManager::checkConfiguration() +bool BuildDirManager::checkConfiguration() { - if (m_tempDir) // always throw away changes in the tmpdir! - return; + QTC_ASSERT(m_parameters.isValid(), return false); - Kit *k = m_buildConfiguration->target()->kit(); - const CMakeConfig cache = parsedConfiguration(); + if (m_tempDir) // always throw away changes in the tmpdir! + return false; + + const CMakeConfig cache = m_parameters.buildConfiguration->configurationFromCMake(); if (cache.isEmpty()) - return; // No cache file yet. + return false; // No cache file yet. CMakeConfig newConfig; QSet changedKeys; QSet removedKeys; - foreach (const CMakeConfigItem &iBc, m_buildConfiguration->cmakeConfiguration()) { + foreach (const CMakeConfigItem &iBc, m_parameters.configuration) { 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)) { + } else if (QString::fromUtf8(iCache.value) != iBc.expandedValue(m_parameters.expander)) { changedKeys << QString::fromUtf8(iBc.key); newConfig.append(iCache); } else { @@ -435,22 +415,15 @@ void BuildDirManager::checkConfiguration() box->setDefaultButton(defaultButton); box->exec(); - if (box->clickedButton() == applyButton) - m_buildConfiguration->setCMakeConfiguration(newConfig); + if (box->clickedButton() == applyButton) { + m_parameters.configuration = newConfig; + QSignalBlocker blocker(m_parameters.buildConfiguration); + m_parameters.buildConfiguration->setConfigurationForCMake(newConfig); + return false; + } else if (box->clickedButton() == defaultButton) + return true; } -} - -void BuildDirManager::maybeForceReparse() -{ - if (m_isHandlingError) - return; - - if (!m_reader || !m_reader->hasData()) { - emit requestReparse(true); - return; - } - - updateReaderType([this]() { maybeForceReparseOnceReaderReady(); }); + return false; } } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index 4db167d0428..e1762fc461c 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -25,9 +25,13 @@ #pragma once +#include "builddirparameters.h" #include "builddirreader.h" +#include "cmakebuildtarget.h" #include "cmakeconfigitem.h" +#include + #include #include @@ -37,12 +41,7 @@ #include #include -namespace ProjectExplorer { -class FileNode; -class IOutputParser; -class Kit; -class Task; -} // namespace ProjectExplorer +namespace ProjectExplorer { class FileNode; } namespace CMakeProjectManager { @@ -50,6 +49,7 @@ class CMakeTool; namespace Internal { +class CMakeProjectNode; class CMakeBuildConfiguration; class BuildDirManager : public QObject @@ -57,61 +57,63 @@ class BuildDirManager : public QObject Q_OBJECT public: - BuildDirManager(CMakeBuildConfiguration *bc); + BuildDirManager(); ~BuildDirManager() final; bool isParsing() const; + void setParametersAndRequestParse(const BuildDirParameters ¶meters, + int newReaderReparseOptions, int existingReaderReparseOptions); + CMakeBuildConfiguration *buildConfiguration() const; + void clearCache(); - void forceReparse(); - void forceReparseWithoutCheckingForChanges(); - void maybeForceReparse(); // Only reparse if the configuration has changed... + void resetData(); bool persistCMakeState(); + void parse(int reparseParameters); + void generateProjectTree(CMakeProjectNode *root, - const QList &allFiles); + const QList &allFiles) const; void updateCodeModel(CppTools::RawProjectParts &rpps); - QList buildTargets() const; - CMakeConfig parsedConfiguration() const; + QList takeBuildTargets() const; + CMakeConfig takeCMakeConfiguration() const; - static CMakeConfig parseConfiguration(const Utils::FileName &cacheFile, - QString *errorMessage); + static CMakeConfig parseCMakeConfiguration(const Utils::FileName &cacheFile, + QString *errorMessage); - CMakeBuildConfiguration *buildConfiguration() const { return m_buildConfiguration; } + enum ReparseParameters { REPARSE_DEFAULT = 0, // use defaults + REPARSE_URGENT = 1, // Do not wait for more requests, start ASAP + REPARSE_FORCE_CONFIGURATION = 2, // Force configuration arguments to cmake + REPARSE_CHECK_CONFIGURATION = 4, // Check and warn if on-disk config and QtC config differ + REPARSE_IGNORE = 8, // Do not reparse:-) + REPARSE_FAIL = 16 // Do not reparse and raise a warning + }; signals: - void requestReparse(bool urgent) const; - void configurationStarted() const; + void requestReparse(int reparseParameters) const; + void parsingStarted() const; void dataAvailable() const; void errorOccured(const QString &err) const; private: void emitDataAvailable(); void emitErrorOccured(const QString &message) const; - void checkConfiguration(); + bool checkConfiguration(); - const Utils::FileName workDirectory() const; + Utils::FileName workDirectory(const BuildDirParameters ¶meters) const; - void updateReaderType(std::function todo); - void updateReaderData(); + void updateReaderType(const BuildDirParameters &p, std::function todo); - void forceReparseImpl(bool checkForChanges); - void parseOnceReaderReady(bool force, bool checkForChanges = true); - void maybeForceReparseOnceReaderReady(); + bool hasConfigChanged(); - void parse(); void becameDirty(); - CMakeBuildConfiguration *m_buildConfiguration = nullptr; + BuildDirParameters m_parameters; mutable std::unique_ptr m_tempDir = nullptr; - mutable CMakeConfig m_cmakeCache; - - std::unique_ptr m_reader; - - mutable QList m_buildTargets; + mutable std::unique_ptr m_reader; mutable bool m_isHandlingError = false; }; diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.cpp b/src/plugins/cmakeprojectmanager/builddirparameters.cpp new file mode 100644 index 00000000000..4af170b4907 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/builddirparameters.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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 "builddirparameters.h" + +#include "cmakebuildconfiguration.h" +#include "cmakekitinformation.h" + +#include +#include +#include +#include + +using namespace ProjectExplorer; + +namespace CMakeProjectManager { +namespace Internal { + +BuildDirParameters::BuildDirParameters() = default; + +BuildDirParameters::BuildDirParameters(CMakeBuildConfiguration *bc) +{ + buildConfiguration = bc; + + const Kit *k = bc->target()->kit(); + + projectName = bc->target()->project()->displayName(); + + sourceDirectory = bc->target()->project()->projectDirectory(); + buildDirectory = bc->buildDirectory(); + + environment = bc->environment(); + + cmakeTool = CMakeKitInformation::cmakeTool(k); + + auto tc = ToolChainKitInformation::toolChain(k, Constants::CXX_LANGUAGE_ID); + if (tc) + cxxToolChainId = tc->id(); + tc = ToolChainKitInformation::toolChain(k, Constants::C_LANGUAGE_ID); + if (tc) + cToolChainId = tc->id(); + sysRoot = SysRootKitInformation::sysRoot(k); + + expander = k->macroExpander(); + + configuration = bc->configurationForCMake(); + + generator = CMakeGeneratorKitInformation::generator(k); + extraGenerator = CMakeGeneratorKitInformation::extraGenerator(k); + platform = CMakeGeneratorKitInformation::platform(k); + toolset = CMakeGeneratorKitInformation::toolset(k); + generatorArguments = CMakeGeneratorKitInformation::generatorArguments(k); +} + +bool BuildDirParameters::isValid() const { return buildConfiguration && cmakeTool; } + +BuildDirParameters::BuildDirParameters(const BuildDirParameters &) = default; + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.h b/src/plugins/cmakeprojectmanager/builddirparameters.h new file mode 100644 index 00000000000..08056085eea --- /dev/null +++ b/src/plugins/cmakeprojectmanager/builddirparameters.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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 "cmakeconfigitem.h" +#include "cmaketool.h" + +#include +#include +#include + +#include + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeBuildConfiguration; + +class BuildDirParameters { +public: + BuildDirParameters(); + BuildDirParameters(CMakeBuildConfiguration *bc); + BuildDirParameters(const BuildDirParameters &other); + + bool isValid() const; + + CMakeBuildConfiguration *buildConfiguration = nullptr; + QString projectName; + + Utils::FileName sourceDirectory; + Utils::FileName buildDirectory; + Utils::Environment environment; + CMakeTool *cmakeTool = nullptr; + + QByteArray cxxToolChainId; + QByteArray cToolChainId; + + Utils::FileName sysRoot; + + Utils::MacroExpander *expander = nullptr; + + CMakeConfig configuration; + + QString generator; + QString extraGenerator; + QString platform; + QString toolset; + QStringList generatorArguments; +}; + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/builddirreader.cpp b/src/plugins/cmakeprojectmanager/builddirreader.cpp index 2d0ce4c815a..1d15eb7f7f4 100644 --- a/src/plugins/cmakeprojectmanager/builddirreader.cpp +++ b/src/plugins/cmakeprojectmanager/builddirreader.cpp @@ -25,14 +25,10 @@ #include "builddirreader.h" -#include "cmakebuildconfiguration.h" -#include "cmakekitinformation.h" #include "servermodereader.h" #include "tealeafreader.h" -#include -#include -#include +#include using namespace ProjectExplorer; @@ -43,57 +39,15 @@ namespace Internal { // BuildDirReader: // -------------------------------------------------------------------- -BuildDirReader::Parameters::Parameters() = default; - -BuildDirReader::Parameters::Parameters(const CMakeBuildConfiguration *bc) +BuildDirReader *BuildDirReader::createReader(const BuildDirParameters &p) { - const ProjectExplorer::Kit *k = bc->target()->kit(); - - projectName = bc->target()->project()->displayName(); - - sourceDirectory = bc->target()->project()->projectDirectory(); - buildDirectory = bc->buildDirectory(); - - environment = bc->environment(); - - CMakeTool *cmake = CMakeKitInformation::cmakeTool(k); - cmakeVersion = cmake->version(); - cmakeHasServerMode = cmake->hasServerMode(); - cmakeExecutable = cmake->cmakeExecutable(); - - pathMapper = cmake->pathMapper(); - isAutorun = cmake->isAutoRun(); - - auto tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); - if (tc) - cxxToolChainId = tc->id(); - tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID); - if (tc) - cToolChainId = tc->id(); - sysRoot = ProjectExplorer::SysRootKitInformation::sysRoot(k); - - expander = k->macroExpander(); - - configuration = bc->cmakeConfiguration(); - - generator = CMakeGeneratorKitInformation::generator(k); - extraGenerator = CMakeGeneratorKitInformation::extraGenerator(k); - platform = CMakeGeneratorKitInformation::platform(k); - toolset = CMakeGeneratorKitInformation::toolset(k); - generatorArguments = CMakeGeneratorKitInformation::generatorArguments(k); -} - -BuildDirReader::Parameters::Parameters(const BuildDirReader::Parameters &) = default; - - -BuildDirReader *BuildDirReader::createReader(const BuildDirReader::Parameters &p) -{ - if (p.cmakeHasServerMode) + QTC_ASSERT(p.isValid() && p.cmakeTool, return nullptr); + if (p.cmakeTool->hasServerMode()) return new ServerModeReader; return new TeaLeafReader; } -void BuildDirReader::setParameters(const BuildDirReader::Parameters &p) +void BuildDirReader::setParameters(const BuildDirParameters &p) { m_parameters = p; } diff --git a/src/plugins/cmakeprojectmanager/builddirreader.h b/src/plugins/cmakeprojectmanager/builddirreader.h index 42630c5e13a..e10109701d7 100644 --- a/src/plugins/cmakeprojectmanager/builddirreader.h +++ b/src/plugins/cmakeprojectmanager/builddirreader.h @@ -25,8 +25,9 @@ #pragma once +#include "builddirparameters.h" +#include "cmakebuildtarget.h" #include "cmakeconfigitem.h" -#include "cmakeproject.h" #include "cmaketool.h" #include @@ -38,6 +39,8 @@ #include #include +namespace ProjectExplorer { class FileNode; } + namespace CMakeProjectManager { namespace Internal { @@ -49,52 +52,19 @@ class BuildDirReader : public QObject Q_OBJECT public: - struct Parameters { - Parameters(); - Parameters(const CMakeBuildConfiguration *bc); - Parameters(const Parameters &other); + static BuildDirReader *createReader(const BuildDirParameters &p); + virtual void setParameters(const BuildDirParameters &p); - QString projectName; - - Utils::FileName sourceDirectory; - Utils::FileName buildDirectory; - Utils::Environment environment; - Utils::FileName cmakeExecutable; - CMakeTool::Version cmakeVersion; - bool cmakeHasServerMode = false; - CMakeTool::PathMapper pathMapper; - - QByteArray cxxToolChainId; - QByteArray cToolChainId; - - Utils::FileName sysRoot; - - Utils::MacroExpander *expander = nullptr; - - CMakeConfig configuration; - - QString generator; - QString extraGenerator; - QString platform; - QString toolset; - QStringList generatorArguments; - bool isAutorun = false; - }; - - static BuildDirReader *createReader(const BuildDirReader::Parameters &p); - virtual void setParameters(const Parameters &p); - - virtual bool isCompatible(const Parameters &p) = 0; + virtual bool isCompatible(const BuildDirParameters &p) = 0; virtual void resetData() = 0; - virtual void parse(bool force) = 0; + virtual void parse(bool forceConfiguration) = 0; virtual void stop() = 0; virtual bool isReady() const { return true; } virtual bool isParsing() const = 0; - virtual bool hasData() const = 0; + virtual QList takeBuildTargets() = 0; virtual CMakeConfig takeParsedConfiguration() = 0; - virtual QList buildTargets() const = 0; virtual void generateProjectTree(CMakeProjectNode *root, const QList &allFiles) = 0; virtual void updateCodeModel(CppTools::RawProjectParts &rpps) = 0; @@ -107,7 +77,7 @@ signals: void errorOccured(const QString &message) const; protected: - Parameters m_parameters; + BuildDirParameters m_parameters; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 399438680ce..f707e2ef2b6 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -64,17 +64,11 @@ const char INITIAL_ARGUMENTS[] = "CMakeProjectManager.CMakeBuildConfiguration.In const char CONFIGURATION_KEY[] = "CMake.Configuration"; CMakeBuildConfiguration::CMakeBuildConfiguration(ProjectExplorer::Target *parent) : - BuildConfiguration(parent, Core::Id(Constants::CMAKE_BC_ID)), - m_buildDirManager(new BuildDirManager(this)) + BuildConfiguration(parent, Core::Id(Constants::CMAKE_BC_ID)) { ctor(); } -CMakeBuildConfiguration::~CMakeBuildConfiguration() -{ - m_buildDirManager->deleteLater(); // Do not block while waiting for cmake... -} - bool CMakeBuildConfiguration::isEnabled() const { return m_error.isEmpty() && !isParsing(); @@ -88,8 +82,7 @@ QString CMakeBuildConfiguration::disabledReason() const CMakeBuildConfiguration::CMakeBuildConfiguration(ProjectExplorer::Target *parent, CMakeBuildConfiguration *source) : BuildConfiguration(parent, source), - m_configuration(source->m_configuration), - m_buildDirManager(new BuildDirManager(this)) + m_configurationForCMake(source->m_configurationForCMake) { ctor(); cloneSteps(source); @@ -99,7 +92,7 @@ QVariantMap CMakeBuildConfiguration::toMap() const { QVariantMap map(ProjectExplorer::BuildConfiguration::toMap()); const QStringList config - = Utils::transform(m_configuration, [](const CMakeConfigItem &i) { return i.toString(); }); + = Utils::transform(m_configurationForCMake, [](const CMakeConfigItem &i) { return i.toString(); }); map.insert(QLatin1String(CONFIGURATION_KEY), config); return map; } @@ -130,110 +123,28 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map) } // End Legacy - setCMakeConfiguration(legacyConf + conf); + setConfigurationForCMake(legacyConf + conf); return true; } void CMakeBuildConfiguration::ctor() { - auto project = static_cast(target()->project()); - setBuildDirectory(shadowBuildDirectory(project->projectFilePath(), + auto p = static_cast(project()); + setBuildDirectory(shadowBuildDirectory(p->projectFilePath(), target()->kit(), displayName(), BuildConfiguration::Unknown)); - - connect(m_buildDirManager.get(), &BuildDirManager::requestReparse, - this, [this](bool urgent) { emit requestReparse(this, urgent); }); - connect(m_buildDirManager.get(), &BuildDirManager::dataAvailable, - this, [this, project]() { - clearError(); - project->handleParsingSuccess(this); - }); - connect(m_buildDirManager.get(), &BuildDirManager::errorOccured, - this, [this, project](const QString &msg) { - setError(msg); - project->handleParsingError(this); - }); - connect(m_buildDirManager.get(), &BuildDirManager::configurationStarted, - this, [this]() { - clearError(ForceEnabledChanged::True); - emit parsingStarted(this); - }); - - connect(this, &CMakeBuildConfiguration::environmentChanged, - m_buildDirManager.get(), &BuildDirManager::forceReparse); - connect(this, &CMakeBuildConfiguration::buildDirectoryChanged, - m_buildDirManager.get(), &BuildDirManager::forceReparse); -} - -void CMakeBuildConfiguration::maybeForceReparse() -{ - clearError(); - m_buildDirManager->maybeForceReparse(); + connect(p, &Project::parsingFinished, this, &BuildConfiguration::enabledChanged); } bool CMakeBuildConfiguration::isParsing() const { - return m_buildDirManager && m_buildDirManager->isParsing(); -} - -void CMakeBuildConfiguration::resetData() -{ - clearError(); - m_buildDirManager->resetData(); -} - -bool CMakeBuildConfiguration::persistCMakeState() -{ - return m_buildDirManager->persistCMakeState(); -} - -bool CMakeBuildConfiguration::updateCMakeStateBeforeBuild() -{ - return static_cast(project())->mustUpdateCMakeStateBeforeBuild(); -} - -void CMakeBuildConfiguration::runCMake() -{ - if (!m_buildDirManager || m_buildDirManager->isParsing()) - return; - - clearError(); - m_buildDirManager->forceReparse(); -} - -void CMakeBuildConfiguration::clearCache() -{ - if (m_buildDirManager) - m_buildDirManager->clearCache(); + return project()->isParsing() && isActive(); } QList CMakeBuildConfiguration::buildTargets() const { - if (!m_buildDirManager || m_buildDirManager->isParsing()) - return QList(); - - return m_buildDirManager->buildTargets(); -} - -CMakeProjectNode * -CMakeBuildConfiguration::generateProjectTree(const QList &allFiles) const -{ - if (!m_buildDirManager || m_buildDirManager->isParsing()) - return nullptr; - - auto root = new CMakeProjectNode(target()->project()->projectDirectory()); - m_buildDirManager->generateProjectTree(root, allFiles); - if (root->isEmpty()) { - delete root; - return nullptr; - } - return root; -} - -void CMakeBuildConfiguration::updateCodeModel(CppTools::RawProjectParts &rpps) -{ - m_buildDirManager->updateCodeModel(rpps); + return m_buildTargets; } FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath, @@ -273,50 +184,23 @@ void CMakeBuildConfiguration::buildTarget(const QString &buildTarget) cmBs->setBuildTarget(originalBuildTarget); } -QList CMakeBuildConfiguration::completeCMakeConfiguration() const +CMakeConfig CMakeBuildConfiguration::configurationFromCMake() const { - if (!m_buildDirManager || m_buildDirManager->isParsing()) - return QList(); - - return Utils::transform(m_buildDirManager->parsedConfiguration(), - [](const CMakeConfigItem &i) { - ConfigModel::DataItem j; - j.key = QString::fromUtf8(i.key); - j.value = QString::fromUtf8(i.value); - j.description = QString::fromUtf8(i.documentation); - j.values = i.values; - j.inCMakeCache = i.inCMakeCache; - - j.isAdvanced = i.isAdvanced; - j.isHidden = i.type == CMakeConfigItem::INTERNAL || i.type == CMakeConfigItem::STATIC; - - switch (i.type) { - case CMakeConfigItem::FILEPATH: - j.type = ConfigModel::DataItem::FILE; - break; - case CMakeConfigItem::PATH: - j.type = ConfigModel::DataItem::DIRECTORY; - break; - case CMakeConfigItem::BOOL: - j.type = ConfigModel::DataItem::BOOLEAN; - break; - case CMakeConfigItem::STRING: - j.type = ConfigModel::DataItem::STRING; - break; - default: - j.type = ConfigModel::DataItem::UNKNOWN; - break; - } - - return j; - }); + return m_configurationFromCMake; } -void CMakeBuildConfiguration::setCurrentCMakeConfiguration(const QList &items) +void CMakeBuildConfiguration::setConfigurationFromCMake(const CMakeConfig &config) { - if (!m_buildDirManager || m_buildDirManager->isParsing()) - return; + m_configurationFromCMake = config; +} +void CMakeBuildConfiguration::setBuildTargets(const QList &targets) +{ + m_buildTargets = targets; +} + +void CMakeBuildConfiguration::setConfigurationForCMake(const QList &items) +{ const CMakeConfig newConfig = Utils::transform(items, [](const ConfigModel::DataItem &i) { CMakeConfigItem ni; ni.key = i.key.toUtf8(); @@ -346,10 +230,8 @@ void CMakeBuildConfiguration::setCurrentCMakeConfiguration(const QListforceReparseWithoutCheckingForChanges(); + const CMakeConfig config = configurationForCMake() + newConfig; + setConfigurationForCMake(config); } void CMakeBuildConfiguration::clearError(ForceEnabledChanged fec) @@ -383,14 +265,14 @@ static CMakeConfig removeDuplicates(const CMakeConfig &config) return result; } -void CMakeBuildConfiguration::setCMakeConfiguration(const CMakeConfig &config) +void CMakeBuildConfiguration::setConfigurationForCMake(const CMakeConfig &config) { - m_configuration = removeDuplicates(config); + m_configurationForCMake = removeDuplicates(config); const Kit *k = target()->kit(); CMakeConfig kitConfig = CMakeConfigurationKitInformation::configuration(k); bool hasKitOverride = false; - foreach (const CMakeConfigItem &i, m_configuration) { + foreach (const CMakeConfigItem &i, m_configurationForCMake) { const QString b = CMakeConfigItem::expandedValueOf(k, i.key, kitConfig); if (!b.isNull() && i.expandedValue(k) != b) { hasKitOverride = true; @@ -402,11 +284,13 @@ void CMakeBuildConfiguration::setCMakeConfiguration(const CMakeConfig &config) setWarning(tr("CMake configuration set by the kit was overridden in the project.")); else setWarning(QString()); + + emit configurationForCMakeChanged(); } -CMakeConfig CMakeBuildConfiguration::cmakeConfiguration() const +CMakeConfig CMakeBuildConfiguration::configurationForCMake() const { - return removeDuplicates(CMakeConfigurationKitInformation::configuration(target()->kit()) + m_configuration); + return removeDuplicates(CMakeConfigurationKitInformation::configuration(target()->kit()) + m_configurationForCMake); } void CMakeBuildConfiguration::setError(const QString &message) @@ -556,7 +440,7 @@ ProjectExplorer::BuildConfiguration *CMakeBuildConfigurationFactory::create(Proj cleanSteps->insertStep(0, cleanStep); bc->setBuildDirectory(copy.buildDirectory); - bc->setCMakeConfiguration(copy.configuration); + bc->setConfigurationForCMake(copy.configuration); return bc; } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index 45a45ede73d..df488bca328 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -52,11 +52,9 @@ class CMakeProjectNode; class CMakeBuildConfiguration : public ProjectExplorer::BuildConfiguration { Q_OBJECT - friend class CMakeBuildConfigurationFactory; public: CMakeBuildConfiguration(ProjectExplorer::Target *parent); - ~CMakeBuildConfiguration(); bool isEnabled() const override; QString disabledReason() const override; @@ -69,25 +67,13 @@ public: void emitBuildTypeChanged(); - void setCMakeConfiguration(const CMakeConfig &config); - bool hasCMakeConfiguration() const; - CMakeConfig cmakeConfiguration() const; + CMakeConfig configurationForCMake() const; + CMakeConfig configurationFromCMake() const; QString error() const; QString warning() const; - bool isParsing() const; - - void maybeForceReparse(); - void resetData(); - bool persistCMakeState(); - bool updateCMakeStateBeforeBuild(); - void runCMake(); - void clearCache(); - QList buildTargets() const; - CMakeProjectManager::Internal::CMakeProjectNode *generateProjectTree(const QList &allFiles) const; - void updateCodeModel(CppTools::RawProjectParts &rpps); static Utils::FileName shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k, @@ -97,12 +83,11 @@ public: void buildTarget(const QString &buildTarget); signals: - void requestReparse(CMakeBuildConfiguration *, bool isUrgent); - void parsingStarted(CMakeBuildConfiguration *); - void errorOccured(const QString &message); void warningOccured(const QString &message); + void configurationForCMakeChanged(); + protected: CMakeBuildConfiguration(ProjectExplorer::Target *parent, CMakeBuildConfiguration *source); bool fromMap(const QVariantMap &map) override; @@ -110,22 +95,30 @@ protected: private: void ctor(); + bool isParsing() const; + enum ForceEnabledChanged : quint8 { False, True }; void clearError(ForceEnabledChanged fec = ForceEnabledChanged::False); - QList completeCMakeConfiguration() const; - void setCurrentCMakeConfiguration(const QList &items); + + void setBuildTargets(const QList &targets); + void setConfigurationFromCMake(const CMakeConfig &config); + void setConfigurationForCMake(const QList &items); + void setConfigurationForCMake(const CMakeConfig &config); void setError(const QString &message); void setWarning(const QString &message); - CMakeConfig m_configuration; + CMakeConfig m_configurationForCMake; QString m_error; QString m_warning; - std::unique_ptr m_buildDirManager; + CMakeConfig m_configurationFromCMake; + QList m_buildTargets; + friend class CMakeBuildConfigurationFactory; friend class CMakeBuildSettingsWidget; friend class CMakeProjectManager::CMakeProject; + friend class BuildDirManager; }; class CMakeProjectImporter; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildinfo.h b/src/plugins/cmakeprojectmanager/cmakebuildinfo.h index 45fc90594a2..cbdae548085 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildinfo.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildinfo.h @@ -52,7 +52,7 @@ public: QTC_ASSERT(bc->target()->project(), return); sourceDirectory = bc->target()->project()->projectDirectory().toString(); - configuration = bc->cmakeConfiguration(); + configuration = bc->configurationForCMake(); } bool operator==(const BuildInfo &o) const final diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp index 8985a0799e3..3dd52c0bed9 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp @@ -227,13 +227,13 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) if (m_buildConfiguration->isParsing()) m_showProgressTimer.start(); else { - m_configModel->setConfiguration(m_buildConfiguration->completeCMakeConfiguration()); + m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake()); m_configView->expandAll(); } - connect(m_buildConfiguration->target()->project(), &ProjectExplorer::Project::parsingFinished, + connect(project, &ProjectExplorer::Project::parsingFinished, this, [this, buildDirChooser, stretcher]() { - m_configModel->setConfiguration(m_buildConfiguration->completeCMakeConfiguration()); + m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake()); m_configView->expandAll(); stretcher->stretch(); updateButtonState(); @@ -264,7 +264,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) connect(m_resetButton, &QPushButton::clicked, m_configModel, &ConfigModel::resetAllChanges); connect(m_reconfigureButton, &QPushButton::clicked, this, [this]() { - m_buildConfiguration->setCurrentCMakeConfiguration(m_configModel->configurationChanges()); + m_buildConfiguration->setConfigurationForCMake(m_configModel->configurationChanges()); }); connect(m_editButton, &QPushButton::clicked, this, [this]() { QModelIndex idx = m_configView->currentIndex(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index d512c5138bc..80185212780 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -269,10 +269,11 @@ void CMakeBuildStep::run(QFutureInterface &fi) QTC_ASSERT(bc, return); bool mustDelay = false; - if (bc->persistCMakeState()) { + auto p = static_cast(bc->project()); + if (p->persistCMakeState()) { emit addOutput(tr("Persisting CMake state..."), BuildStep::OutputFormat::NormalMessage); mustDelay = true; - } else if (bc->updateCMakeStateBeforeBuild()) { + } else if (p->mustUpdateCMakeStateBeforeBuild()) { emit addOutput(tr("Running CMake in preparation to build..."), BuildStep::OutputFormat::NormalMessage); mustDelay = true; } else { diff --git a/src/plugins/cmakeprojectmanager/cmakebuildtarget.cpp b/src/plugins/cmakeprojectmanager/cmakebuildtarget.cpp new file mode 100644 index 00000000000..357f66f6a5a --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakebuildtarget.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** 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 "cmakebuildtarget.h" + +using namespace Utils; + +namespace CMakeProjectManager { + +void CMakeBuildTarget::clear() +{ + executable.clear(); + makeCommand.clear(); + workingDirectory.clear(); + sourceDirectory.clear(); + title.clear(); + targetType = UtilityType; + includeFiles.clear(); + compilerOptions.clear(); + macros.clear(); + files.clear(); +} + +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildtarget.h b/src/plugins/cmakeprojectmanager/cmakebuildtarget.h new file mode 100644 index 00000000000..1b3f5fb7002 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakebuildtarget.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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 "cmake_global.h" + +#include + +#include + +#include + +namespace CMakeProjectManager { + +enum TargetType { + ExecutableType = 0, + StaticLibraryType = 2, + DynamicLibraryType = 3, + UtilityType = 64 +}; + +class CMAKE_EXPORT CMakeBuildTarget +{ +public: + QString title; + Utils::FileName executable; // TODO: rename to output? + TargetType targetType = UtilityType; + Utils::FileName workingDirectory; + Utils::FileName sourceDirectory; + Utils::FileName makeCommand; + + // code model + QList includeFiles; + QStringList compilerOptions; + ProjectExplorer::Macros macros; + QList files; + + void clear(); +}; + +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 2ab0037bd0e..5aac57556ed 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,11 @@ namespace CMakeProjectManager { using namespace Internal; +static CMakeBuildConfiguration *activeBc(const CMakeProject *p) +{ + return qobject_cast(p->activeTarget() ? p->activeTarget()->activeBuildConfiguration() : nullptr); +} + // QtCreator CMake Generator wishlist: // Which make targets we need to build to get all executables // What is the actual compiler executable @@ -77,27 +83,129 @@ using namespace Internal; CMakeProject::CMakeProject(const FileName &fileName) : Project(Constants::CMAKEMIMETYPE, fileName), m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)) { - m_delayedParsingTimer.setSingleShot(true); - - connect(&m_delayedParsingTimer, &QTimer::timeout, - this, [this]() { startParsingProject(PARSE); }); - setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID); + setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setDisplayName(projectDirectory().fileName()); - connect(this, &Project::activeProjectConfigurationChanged, - this, &CMakeProject::handleActiveProjectConfigurationChanged); + // Timer: + m_delayedParsingTimer.setSingleShot(true); - subscribeSignal(&CMakeBuildConfiguration::requestReparse, - this, [this](CMakeBuildConfiguration *bc, bool isUrgent) { - if (bc->isActive()) { - m_delayedParsingTimer.setInterval(isUrgent ? 0 : 1000); - m_delayedParsingTimer.start(); + connect(&m_delayedParsingTimer, &QTimer::timeout, + this, [this]() { startParsing(m_delayedParsingParameters); }); + + // BuildDirManager: + connect(&m_buildDirManager, &BuildDirManager::requestReparse, + this, &CMakeProject::handleReparseRequest); + connect(&m_buildDirManager, &BuildDirManager::dataAvailable, + this, [this]() { + CMakeBuildConfiguration *bc = activeBc(this); + if (bc && bc == m_buildDirManager.buildConfiguration()) { + bc->clearError(); + handleParsingSuccess(bc); + } + }); + connect(&m_buildDirManager, &BuildDirManager::errorOccured, + this, [this](const QString &msg) { + CMakeBuildConfiguration *bc = activeBc(this); + if (bc && bc == m_buildDirManager.buildConfiguration()) { + bc->setError(msg); + handleParsingError(bc); + } + }); + connect(&m_buildDirManager, &BuildDirManager::parsingStarted, + this, [this]() { + CMakeBuildConfiguration *bc = activeBc(this); + if (bc && bc == m_buildDirManager.buildConfiguration()) + bc->clearError(CMakeBuildConfiguration::ForceEnabledChanged::True); + }); + + // Kit changed: + connect(KitManager::instance(), &KitManager::kitUpdated, + this, [this](Kit *k) { + CMakeBuildConfiguration *bc = activeBc(this); + if (!bc || k != bc->target()->kit()) + return; // not for us... + + // Build configuration has not changed, but Kit settings might have: + // reparse and check the configuration, independent of whether the reader has changed + m_buildDirManager.setParametersAndRequestParse( + BuildDirParameters(bc), + BuildDirManager::REPARSE_CHECK_CONFIGURATION, + BuildDirManager::REPARSE_CHECK_CONFIGURATION); + }); + + // Target switched: + connect(this, &Project::activeTargetChanged, this, [this]() { + CMakeBuildConfiguration *bc = activeBc(this); + + // Target has switched, so the kit has changed, too. + // * run cmake with configuration arguments if the reader needs to be switched + // * run cmake without configuration arguments if the reader stays + m_buildDirManager.setParametersAndRequestParse( + BuildDirParameters(bc), + BuildDirManager::REPARSE_CHECK_CONFIGURATION, + BuildDirManager::REPARSE_CHECK_CONFIGURATION); + }); + + // BuildConfiguration switched: + subscribeSignal(&Target::activeBuildConfigurationChanged, this, [this]() { + CMakeBuildConfiguration *bc = activeBc(this); + + // Build configuration has switched: + // * Error out if the reader updates, can not happen since all BCs share a target/kit. + // * run cmake without configuration arguments if the reader stays + m_buildDirManager.setParametersAndRequestParse( + BuildDirParameters(bc), + BuildDirManager::REPARSE_FAIL, + BuildDirManager::REPARSE_CHECK_CONFIGURATION); + }); + + // BuildConfiguration changed: + subscribeSignal(&CMakeBuildConfiguration::environmentChanged, this, [this]() { + auto senderBc = qobject_cast(sender()); + + if (senderBc && senderBc->isActive()) { + // The environment on our BC has changed: + // * Error out if the reader updates, can not happen since all BCs share a target/kit. + // * run cmake without configuration arguments if the reader stays + m_buildDirManager.setParametersAndRequestParse( + BuildDirParameters(senderBc), + BuildDirManager::REPARSE_FAIL, + BuildDirManager::REPARSE_CHECK_CONFIGURATION); + } + }); + subscribeSignal(&CMakeBuildConfiguration::buildDirectoryChanged, this, [this]() { + auto senderBc = qobject_cast(sender()); + + if (senderBc && senderBc->isActive() && senderBc == m_buildDirManager.buildConfiguration()) { + // The build directory of our BC has changed: + // * Error out if the reader updates, can not happen since all BCs share a target/kit. + // * run cmake without configuration arguments if the reader stays + // If no configuration exists, then the arguments will get added automatically by + // the reader. + m_buildDirManager.setParametersAndRequestParse( + BuildDirParameters(senderBc), + BuildDirManager::REPARSE_FAIL, + BuildDirManager::REPARSE_CHECK_CONFIGURATION); + } + }); + subscribeSignal(&CMakeBuildConfiguration::configurationForCMakeChanged, this, [this]() { + auto senderBc = qobject_cast(sender()); + + if (senderBc && senderBc->isActive() && senderBc == m_buildDirManager.buildConfiguration()) { + // The CMake configuration has changed on our BC: + // * Error out if the reader updates, can not happen since all BCs share a target/kit. + // * run cmake with configuration arguments if the reader stays + m_buildDirManager.setParametersAndRequestParse( + BuildDirParameters(senderBc), + BuildDirManager::REPARSE_FAIL, + BuildDirManager::REPARSE_FORCE_CONFIGURATION); } }); + // TreeScanner: connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished); m_treeScanner.setFilter([this](const Utils::MimeType &mimeType, const Utils::FileName &fn) { @@ -148,15 +256,19 @@ CMakeProject::~CMakeProject() void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) { - Target *const t = activeTarget(); + const CMakeBuildConfiguration *aBc = activeBc(this); QTC_ASSERT(bc, return); - QTC_ASSERT(bc == (t ? t->activeBuildConfiguration() : nullptr), return); - QTC_ASSERT(m_treeScanner.isFinished() && !bc->isParsing(), return); + QTC_ASSERT(bc == aBc, return); + QTC_ASSERT(m_treeScanner.isFinished() && !m_buildDirManager.isParsing(), return); + Target *t = bc->target(); Kit *k = t->kit(); - auto newRoot = bc->generateProjectTree(m_allFiles); + bc->setBuildTargets(m_buildDirManager.takeBuildTargets()); + bc->setConfigurationFromCMake(m_buildDirManager.takeCMakeConfiguration()); + + auto newRoot = generateProjectTree(m_allFiles); if (newRoot) { setDisplayName(newRoot->displayName()); setRootProjectNode(newRoot); @@ -184,7 +296,7 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) } CppTools::RawProjectParts rpps; - bc->updateCodeModel(rpps); + m_buildDirManager.updateCodeModel(rpps); for (CppTools::RawProjectPart &rpp : rpps) { // TODO: Set the Qt version only if target actually depends on Qt. @@ -197,6 +309,8 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) updateQmlJSCodeModel(); + m_buildDirManager.resetData(); + emit fileListChanged(); emit bc->emitBuildTypeChanged(); @@ -220,10 +334,10 @@ void CMakeProject::updateQmlJSCodeModel() if (!bc) return; - const QList &cm = bc->completeCMakeConfiguration(); - foreach (const ConfigModel::DataItem &di, cm) { - if (di.key.contains(QStringLiteral("QML_IMPORT_PATH"))) { - cmakeImports = di.value; + const CMakeConfig &cm = bc->configurationFromCMake(); + foreach (const CMakeConfigItem &di, cm) { + if (di.key.contains("QML_IMPORT_PATH")) { + cmakeImports = QString::fromUtf8(di.value); break; } } @@ -234,6 +348,16 @@ void CMakeProject::updateQmlJSCodeModel() modelManager->updateProjectInfo(projectInfo, this); } +CMakeProjectNode *CMakeProject::generateProjectTree(const QList &allFiles) const +{ + if (m_buildDirManager.isParsing()) + return nullptr; + + auto root = std::make_unique(projectDirectory()); + m_buildDirManager.generateProjectTree(root.get(), allFiles); + return root ? root.release() : nullptr; +} + bool CMakeProject::needsConfiguration() const { return targets().isEmpty(); @@ -264,53 +388,28 @@ void CMakeProject::runCMake() if (isParsing()) return; - startParsingProject(PARSE); + CMakeBuildConfiguration *bc = activeBc(this); + if (bc) { + BuildDirParameters parameters(bc); + m_buildDirManager.setParametersAndRequestParse(parameters, + BuildDirManager::REPARSE_CHECK_CONFIGURATION, + BuildDirManager::REPARSE_CHECK_CONFIGURATION); + } } void CMakeProject::runCMakeAndScanProjectTree() { - if (isParsing()) + if (!m_treeScanner.isFinished()) return; - startParsingProject(static_cast(PARSE | SCAN)); -} - -void CMakeProject::startParsingProject(const CMakeProject::DataCollectionAction action) -{ - const bool runParse = action & PARSE; - const bool runScan = action & SCAN; - - CMakeBuildConfiguration *bc = activeTarget() - ? qobject_cast(activeTarget()->activeBuildConfiguration()) - : nullptr; - if (!bc) - return; - - if (!m_treeScanner.isFinished() || m_waitingForScan) - return; - - emitParsingStarted(); - - m_waitingForParse = runParse; - m_waitingForScan = runScan; - m_combinedScanAndParseResult = true; - - if (runParse) - bc->runCMake(); - - if (runScan) { - m_treeScanner.asyncScanForFiles(projectDirectory()); - Core::ProgressManager::addTask(m_treeScanner.future(), - tr("Scan \"%1\" project tree").arg(displayName()), - "CMake.Scan.Tree"); - } + m_waitingForScan = true; + runCMake(); } void CMakeProject::buildCMakeTarget(const QString &buildTarget) { QTC_ASSERT(!buildTarget.isEmpty(), return); - Target *t = activeTarget(); - auto bc = qobject_cast(t ? t->activeBuildConfiguration() : nullptr); + CMakeBuildConfiguration *bc = activeBc(this); if (bc) bc->buildTarget(buildTarget); } @@ -322,15 +421,60 @@ ProjectImporter *CMakeProject::projectImporter() const return m_projectImporter.get(); } +bool CMakeProject::persistCMakeState() +{ + return m_buildDirManager.persistCMakeState(); +} + +void CMakeProject::clearCMakeCache() +{ + m_buildDirManager.clearCache(); +} + QList CMakeProject::buildTargets() const { - CMakeBuildConfiguration *bc = nullptr; - if (activeTarget()) - bc = qobject_cast(activeTarget()->activeBuildConfiguration()); + CMakeBuildConfiguration *bc = activeBc(this); return bc ? bc->buildTargets() : QList(); } +void CMakeProject::handleReparseRequest(int reparseParameters) +{ + QTC_ASSERT(!(reparseParameters & BuildDirManager::REPARSE_FAIL), return); + if (reparseParameters & BuildDirManager::REPARSE_IGNORE) + return; + + m_delayedParsingTimer.setInterval((reparseParameters & BuildDirManager::REPARSE_URGENT) ? 0 : 1000); + m_delayedParsingTimer.start(); + m_delayedParsingParameters = m_delayedParsingParameters | reparseParameters; +} + +void CMakeProject::startParsing(int reparseParameters) +{ + m_delayedParsingParameters = BuildDirManager::REPARSE_DEFAULT; + + QTC_ASSERT((reparseParameters & BuildDirManager::REPARSE_FAIL) == 0, return); + if (reparseParameters & BuildDirManager::REPARSE_IGNORE) + return; + + QTC_ASSERT(activeBc(this), return); + + emitParsingStarted(); + + 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"); + } + + m_buildDirManager.parse(reparseParameters); +} + QStringList CMakeProject::buildTargetTitles(bool runnable) const { const QList targets @@ -361,33 +505,9 @@ bool CMakeProject::setupTarget(Target *t) if (t->buildConfigurations().isEmpty()) return false; t->updateDefaultDeployConfigurations(); - return true; } -void CMakeProject::handleActiveProjectConfigurationChanged(ProjectConfiguration *pc) -{ - if (auto bc = qobject_cast(pc)) { - if (!bc->isActive()) - return; - } else if (!qobject_cast(pc)) { - return; - } - - for (Target *t : targets()) { - for (BuildConfiguration *bc : t->buildConfigurations()) { - auto i = qobject_cast(bc); - QTC_ASSERT(i, continue); - if (i->isActive()) { - m_waitingForParse = true; - i->maybeForceReparse(); - } else { - i->resetData(); - } - } - } -} - void CMakeProject::handleTreeScanningFinished() { QTC_CHECK(m_waitingForScan); @@ -395,8 +515,7 @@ void CMakeProject::handleTreeScanningFinished() qDeleteAll(m_allFiles); m_allFiles = Utils::transform(m_treeScanner.release(), [](const FileNode *fn) { return fn; }); - auto t = activeTarget(); - auto bc = qobject_cast(t ? t->activeBuildConfiguration() : nullptr); + CMakeBuildConfiguration *bc = activeBc(this); QTC_ASSERT(bc, return); m_combinedScanAndParseResult = m_combinedScanAndParseResult && true; @@ -407,7 +526,7 @@ void CMakeProject::handleTreeScanningFinished() void CMakeProject::handleParsingSuccess(CMakeBuildConfiguration *bc) { - QTC_CHECK(m_waitingForParse); + QTC_ASSERT(m_waitingForParse, return); if (!bc || !bc->isActive()) return; @@ -626,18 +745,4 @@ void CMakeProject::createGeneratedCodeModelSupport() CppTools::GeneratedCodeModelSupport::update(m_extraCompilers); } -void CMakeBuildTarget::clear() -{ - executable.clear(); - makeCommand.clear(); - workingDirectory.clear(); - sourceDirectory.clear(); - title.clear(); - targetType = UtilityType; - includeFiles.clear(); - compilerOptions.clear(); - macros.clear(); - files.clear(); -} - } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 40d3382a64b..a7eafb30cce 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -26,8 +26,11 @@ #pragma once #include "cmake_global.h" +#include "builddirmanager.h" +#include "cmakebuildtarget.h" #include "cmakeprojectimporter.h" #include "treescanner.h" +#include "builddirmanager.h" #include #include @@ -42,40 +45,16 @@ #include namespace CppTools { class CppProjectUpdater; } +namespace ProjectExplorer { class FileNode; } namespace CMakeProjectManager { namespace Internal { class CMakeBuildConfiguration; class CMakeBuildSettingsWidget; +class CMakeProjectNode; } // namespace Internal -enum TargetType { - ExecutableType = 0, - StaticLibraryType = 2, - DynamicLibraryType = 3, - UtilityType = 64 -}; - -class CMAKE_EXPORT CMakeBuildTarget -{ -public: - QString title; - Utils::FileName executable; // TODO: rename to output? - TargetType targetType = UtilityType; - Utils::FileName workingDirectory; - Utils::FileName sourceDirectory; - Utils::FileName makeCommand; - - // code model - QList includeFiles; - QStringList compilerOptions; - ProjectExplorer::Macros macros; - QList files; - - void clear(); -}; - class CMAKE_EXPORT CMakeProject : public ProjectExplorer::Project { Q_OBJECT @@ -103,6 +82,10 @@ public: ProjectExplorer::ProjectImporter *projectImporter() const final; + bool persistCMakeState(); + void clearCMakeCache(); + bool mustUpdateCMakeStateBeforeBuild(); + protected: RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final; bool setupTarget(ProjectExplorer::Target *t) final; @@ -110,10 +93,10 @@ protected: private: QList buildTargets() const; - enum DataCollectionAction { PARSE = 1, SCAN = 2 }; - void startParsingProject(const DataCollectionAction a); + void handleReparseRequest(int reparseParameters); + + void startParsing(int reparseParameters); - void handleActiveProjectConfigurationChanged(ProjectExplorer::ProjectConfiguration *pc); void handleTreeScanningFinished(); void handleParsingSuccess(Internal::CMakeBuildConfiguration *bc); void handleParsingError(Internal::CMakeBuildConfiguration *bc); @@ -121,19 +104,21 @@ private: void updateProjectData(Internal::CMakeBuildConfiguration *bc); void updateQmlJSCodeModel(); + Internal::CMakeProjectNode * + generateProjectTree(const QList &allFiles) const; + void createGeneratedCodeModelSupport(); QStringList filesGeneratedFrom(const QString &sourceFile) const final; void updateTargetRunConfigurations(ProjectExplorer::Target *t); void updateApplicationAndDeploymentTargets(); - bool mustUpdateCMakeStateBeforeBuild(); - // TODO probably need a CMake specific node structure QList m_buildTargets; CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; QList m_extraCompilers; Internal::TreeScanner m_treeScanner; + Internal::BuildDirManager m_buildDirManager; bool m_waitingForScan = false; bool m_waitingForParse = false; @@ -144,6 +129,7 @@ private: mutable std::unique_ptr m_projectImporter; QTimer m_delayedParsingTimer; + int m_delayedParsingParameters = 0; friend class Internal::CMakeBuildConfiguration; friend class Internal::CMakeBuildSettingsWidget; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 69416b3ad74..b5291273a16 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -243,7 +243,7 @@ QList CMakeProjectImporter::examineDirectory(const Utils::FileName &impo } QString errorMessage; - const CMakeConfig config = BuildDirManager::parseConfiguration(cacheFile, &errorMessage); + const CMakeConfig config = BuildDirManager::parseCMakeConfiguration(cacheFile, &errorMessage); if (config.isEmpty() || !errorMessage.isEmpty()) { qCDebug(cmInputLog()) << "Failed to read configuration from" << cacheFile << errorMessage; return { }; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 420e0d0bd2d..c4db94a6898 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -24,7 +24,6 @@ ****************************************************************************/ #include "cmakeprojectmanager.h" -#include "builddirmanager.h" #include "cmakebuildconfiguration.h" #include "cmakekitinformation.h" #include "cmakeprojectconstants.h" @@ -43,9 +42,6 @@ #include #include -#include -#include - #include #include #include @@ -121,19 +117,15 @@ void CMakeManager::updateCmakeActions() void CMakeManager::clearCMakeCache(Project *project) { - if (!project || !project->activeTarget()) - return; - auto bc = qobject_cast(project->activeTarget()->activeBuildConfiguration()); - if (!bc) + CMakeProject *cmakeProject = qobject_cast(project); + if (!cmakeProject || !cmakeProject->activeTarget() || !cmakeProject->activeTarget()->activeBuildConfiguration()) return; - bc->clearCache(); + cmakeProject->clearCMakeCache(); } void CMakeManager::runCMake(Project *project) { - if (!project) - return; CMakeProject *cmakeProject = qobject_cast(project); if (!cmakeProject || !cmakeProject->activeTarget() || !cmakeProject->activeTarget()->activeBuildConfiguration()) return; @@ -146,8 +138,6 @@ void CMakeManager::runCMake(Project *project) void CMakeManager::rescanProject(Project *project) { - if (!project) - return; CMakeProject *cmakeProject = qobject_cast(project); if (!cmakeProject || !cmakeProject->activeTarget() || !cmakeProject->activeTarget()->activeBuildConfiguration()) return; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro index a8be7ef5442..7467e7dbc54 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -2,9 +2,11 @@ DEFINES += CMAKEPROJECTMANAGER_LIBRARY include(../../qtcreatorplugin.pri) HEADERS = builddirmanager.h \ + builddirparameters.h \ builddirreader.h \ cmakebuildinfo.h \ cmakebuildstep.h \ + cmakebuildtarget.h \ cmakeconfigitem.h \ cmakeproject.h \ cmakeprojectimporter.h \ @@ -36,8 +38,10 @@ HEADERS = builddirmanager.h \ treescanner.h SOURCES = builddirmanager.cpp \ + builddirparameters.cpp \ builddirreader.cpp \ cmakebuildstep.cpp \ + cmakebuildtarget.cpp \ cmakeconfigitem.cpp \ cmakeproject.cpp \ cmakeprojectimporter.cpp \ diff --git a/src/plugins/cmakeprojectmanager/configmodel.cpp b/src/plugins/cmakeprojectmanager/configmodel.cpp index ff43d0cc63e..b90f35cff31 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.cpp +++ b/src/plugins/cmakeprojectmanager/configmodel.cpp @@ -200,6 +200,42 @@ QList ConfigModel::configurationChanges() const }); } +void ConfigModel::setConfiguration(const CMakeConfig &config) +{ + setConfiguration(Utils::transform(config, [](const CMakeConfigItem &i) { + ConfigModel::DataItem j; + j.key = QString::fromUtf8(i.key); + j.value = QString::fromUtf8(i.value); + j.description = QString::fromUtf8(i.documentation); + j.values = i.values; + j.inCMakeCache = i.inCMakeCache; + + j.isAdvanced = i.isAdvanced; + j.isHidden = i.type == CMakeConfigItem::INTERNAL || i.type == CMakeConfigItem::STATIC; + + switch (i.type) { + case CMakeConfigItem::FILEPATH: + j.type = ConfigModel::DataItem::FILE; + break; + case CMakeConfigItem::PATH: + j.type = ConfigModel::DataItem::DIRECTORY; + break; + case CMakeConfigItem::BOOL: + j.type = ConfigModel::DataItem::BOOLEAN; + break; + case CMakeConfigItem::STRING: + j.type = ConfigModel::DataItem::STRING; + break; + default: + j.type = ConfigModel::DataItem::UNKNOWN; + break; + } + + return j; + })); +} + + void ConfigModel::setConfiguration(const QList &config) { QList tmp = config; diff --git a/src/plugins/cmakeprojectmanager/configmodel.h b/src/plugins/cmakeprojectmanager/configmodel.h index 626d487ac63..6fb96e29900 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.h +++ b/src/plugins/cmakeprojectmanager/configmodel.h @@ -25,6 +25,8 @@ #pragma once +#include "cmakeconfigitem.h" + #include #include @@ -65,6 +67,7 @@ public: const DataItem::Type type = DataItem::UNKNOWN, const QString &description = QString(), const QStringList &values = QStringList()); + void setConfiguration(const CMakeConfig &config); void setConfiguration(const QList &config); void setKitConfiguration(const QHash &kitConfig); void flush(); @@ -80,6 +83,7 @@ public: QList configurationChanges() const; + private: class InternalDataItem : public DataItem { @@ -97,9 +101,9 @@ private: QString kitValue; }; - void setConfiguration(const QList &config); void generateTree(); + void setConfiguration(const QList &config); QList m_configuration; QHash m_kitConfiguration; diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp index bc95970aa77..74a9f43bd38 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -97,12 +97,15 @@ ServerModeReader::~ServerModeReader() stop(); } -void ServerModeReader::setParameters(const BuildDirReader::Parameters &p) +void ServerModeReader::setParameters(const BuildDirParameters &p) { + QTC_ASSERT(p.cmakeTool, return); + BuildDirReader::setParameters(p); if (!m_cmakeServer) { m_cmakeServer.reset(new ServerMode(p.environment, - p.sourceDirectory, p.buildDirectory, p.cmakeExecutable, + p.sourceDirectory, p.buildDirectory, + p.cmakeTool->cmakeExecutable(), p.generator, p.extraGenerator, p.platform, p.toolset, true, 1)); connect(m_cmakeServer.get(), &ServerMode::errorOccured, @@ -135,14 +138,17 @@ void ServerModeReader::setParameters(const BuildDirReader::Parameters &p) } } -bool ServerModeReader::isCompatible(const BuildDirReader::Parameters &p) +bool ServerModeReader::isCompatible(const BuildDirParameters &p) { - // Server mode connection got lost, reset... - if (!m_parameters.cmakeExecutable.isEmpty() && !m_cmakeServer) + if (!p.cmakeTool) return false; - return p.cmakeHasServerMode - && p.cmakeExecutable == m_parameters.cmakeExecutable + // Server mode connection got lost, reset... + if (!m_parameters.cmakeTool->cmakeExecutable().isEmpty() && !m_cmakeServer) + return false; + + return p.cmakeTool->hasServerMode() + && p.cmakeTool->cmakeExecutable() == m_parameters.cmakeTool->cmakeExecutable() && p.environment == m_parameters.environment && p.generator == m_parameters.generator && p.extraGenerator == m_parameters.extraGenerator @@ -154,16 +160,22 @@ bool ServerModeReader::isCompatible(const BuildDirReader::Parameters &p) void ServerModeReader::resetData() { - m_hasData = false; + m_cmakeConfiguration.clear(); + // m_cmakeFiles: Keep these! + m_cmakeInputsFileNodes.clear(); + qDeleteAll(m_projects); // Also deletes targets and filegroups that are its children! + m_projects.clear(); + m_targets.clear(); + m_fileGroups.clear(); } -void ServerModeReader::parse(bool force) +void ServerModeReader::parse(bool forceConfiguration) { emit configurationStarted(); QTC_ASSERT(m_cmakeServer, return); QVariantMap extra; - if (force || !QDir(m_parameters.buildDirectory.toString()).exists("CMakeCache.txt")) { + if (forceConfiguration || !QDir(m_parameters.buildDirectory.toString()).exists("CMakeCache.txt")) { QStringList cacheArguments = transform(m_parameters.configuration, [this](const CMakeConfigItem &i) { return i.toArgument(m_parameters.expander); @@ -208,12 +220,7 @@ bool ServerModeReader::isParsing() const return static_cast(m_future); } -bool ServerModeReader::hasData() const -{ - return m_hasData; -} - -QList ServerModeReader::buildTargets() const +QList ServerModeReader::takeBuildTargets() { const QList result = transform(m_targets, [](const Target *t) -> CMakeBuildTarget { CMakeBuildTarget ct; @@ -246,8 +253,8 @@ QList ServerModeReader::buildTargets() const CMakeConfig ServerModeReader::takeParsedConfiguration() { - CMakeConfig config = m_cmakeCache; - m_cmakeCache.clear(); + CMakeConfig config = m_cmakeConfiguration; + m_cmakeConfiguration.clear(); return config; } @@ -372,11 +379,6 @@ void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps) : CppTools::ProjectPart::Library); rpps.append(rpp); } - - qDeleteAll(m_projects); // Not used anymore! - m_projects.clear(); - m_targets.clear(); - m_fileGroups.clear(); } void ServerModeReader::handleReply(const QVariantMap &data, const QString &inReplyTo) @@ -415,7 +417,6 @@ void ServerModeReader::handleReply(const QVariantMap &data, const QString &inRep m_future->reportFinished(); m_future.reset(); } - m_hasData = true; Core::MessageManager::write(tr("CMake Project was parsed successfully.")); emit dataAvailable(); } @@ -672,7 +673,7 @@ void ServerModeReader::extractCacheData(const QVariantMap &data) item.values = CMakeConfigItem::cmakeSplitValue(properties.value("STRINGS").toString(), true); config.append(item); } - m_cmakeCache = config; + m_cmakeConfiguration = config; } void ServerModeReader::fixTarget(ServerModeReader::Target *target) const diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h index bf5fe7337e7..bfde93ca1c9 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -33,10 +33,11 @@ #include +namespace ProjectExplorer { class ProjectNode; } + namespace CMakeProjectManager { namespace Internal { - class ServerModeReader : public BuildDirReader { Q_OBJECT @@ -45,18 +46,17 @@ public: ServerModeReader(); ~ServerModeReader() final; - void setParameters(const Parameters &p) final; + void setParameters(const BuildDirParameters &p) final; - bool isCompatible(const Parameters &p) final; + bool isCompatible(const BuildDirParameters &p) final; void resetData() final; - void parse(bool force) final; + void parse(bool forceConfiguration) final; void stop() final; bool isReady() const final; bool isParsing() const final; - bool hasData() const final; - QList buildTargets() const final; + QList takeBuildTargets() final; CMakeConfig takeParsedConfiguration() final; void generateProjectTree(CMakeProjectNode *root, const QList &allFiles) final; @@ -160,21 +160,19 @@ private: const QList knownHeaders, const QList &allFiles); - bool m_hasData = false; - std::unique_ptr m_cmakeServer; std::unique_ptr> m_future; int m_progressStepMinimum = 0; int m_progressStepMaximum = 1000; - CMakeConfig m_cmakeCache; + CMakeConfig m_cmakeConfiguration; QSet m_cmakeFiles; QList m_cmakeInputsFileNodes; QList m_projects; - mutable QList m_targets; + QList m_targets; QList m_fileGroups; CMakeParser m_parser; diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp index 2fbe12d28d0..99374c0719f 100644 --- a/src/plugins/cmakeprojectmanager/tealeafreader.cpp +++ b/src/plugins/cmakeprojectmanager/tealeafreader.cpp @@ -130,7 +130,9 @@ TeaLeafReader::TeaLeafReader() { connect(EditorManager::instance(), &EditorManager::aboutToSave, this, [this](const IDocument *document) { - if (m_cmakeFiles.contains(document->filePath()) || !m_parameters.isAutorun) + if (m_cmakeFiles.contains(document->filePath()) + || !m_parameters.cmakeTool + || !m_parameters.cmakeTool->isAutoRun()) emit dirty(); }); @@ -150,15 +152,15 @@ TeaLeafReader::~TeaLeafReader() resetData(); } -bool TeaLeafReader::isCompatible(const BuildDirReader::Parameters &p) +bool TeaLeafReader::isCompatible(const BuildDirParameters &p) { - return !p.cmakeHasServerMode; + if (!p.cmakeTool) + return false; + return !p.cmakeTool->hasServerMode(); } void TeaLeafReader::resetData() { - m_hasData = false; - qDeleteAll(m_watchedFiles); m_watchedFiles.clear(); @@ -188,11 +190,11 @@ static QString findCbpFile(const QDir &directory) return file; } -void TeaLeafReader::parse(bool force) +void TeaLeafReader::parse(bool forceConfiguration) { const QString cbpFile = findCbpFile(QDir(m_parameters.buildDirectory.toString())); const QFileInfo cbpFileFi = cbpFile.isEmpty() ? QFileInfo() : QFileInfo(cbpFile); - if (!cbpFileFi.exists() || force) { + if (!cbpFileFi.exists() || forceConfiguration) { // Initial create: startCMake(toArguments(m_parameters.configuration, m_parameters.expander)); return; @@ -206,7 +208,6 @@ void TeaLeafReader::parse(bool force) startCMake(QStringList()); } else { extractData(); - m_hasData = true; emit dataAvailable(); } } @@ -228,12 +229,7 @@ bool TeaLeafReader::isParsing() const return m_cmakeProcess && m_cmakeProcess->state() != QProcess::NotRunning; } -bool TeaLeafReader::hasData() const -{ - return m_hasData; -} - -QList TeaLeafReader::buildTargets() const +QList TeaLeafReader::takeBuildTargets() { return m_buildTargets; } @@ -247,7 +243,7 @@ CMakeConfig TeaLeafReader::takeParsedConfiguration() return { }; QString errorMessage; - CMakeConfig result = BuildDirManager::parseConfiguration(cacheFile, &errorMessage); + CMakeConfig result = BuildDirManager::parseCMakeConfiguration(cacheFile, &errorMessage); if (!errorMessage.isEmpty()) { emit errorOccured(errorMessage); @@ -412,6 +408,8 @@ void TeaLeafReader::cleanUpProcess() void TeaLeafReader::extractData() { + QTC_ASSERT(m_parameters.isValid() && m_parameters.cmakeTool, return); + const FileName srcDir = m_parameters.sourceDirectory; const FileName bldDir = m_parameters.buildDirectory; const FileName topCMake = Utils::FileName(srcDir).appendPath("CMakeLists.txt"); @@ -437,7 +435,7 @@ void TeaLeafReader::extractData() // setFolderName CMakeCbpParser cbpparser; // Parsing - if (!cbpparser.parseCbpFile(m_parameters.pathMapper, cbpFile, srcDir)) + if (!cbpparser.parseCbpFile(m_parameters.cmakeTool->pathMapper(), cbpFile, srcDir)) return; m_projectName = cbpparser.projectName(); @@ -460,6 +458,8 @@ void TeaLeafReader::extractData() void TeaLeafReader::startCMake(const QStringList &configurationArguments) { + QTC_ASSERT(m_parameters.isValid() && m_parameters.cmakeTool, return); + const FileName buildDirectory = m_parameters.buildDirectory; QTC_ASSERT(!m_cmakeProcess, return); QTC_ASSERT(!m_parser, return); @@ -503,7 +503,7 @@ void TeaLeafReader::startCMake(const QStringList &configurationArguments) TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); MessageManager::write(tr("Running \"%1 %2\" in %3.") - .arg(m_parameters.cmakeExecutable.toUserOutput()) + .arg(m_parameters.cmakeTool->cmakeExecutable().toUserOutput()) .arg(args) .arg(buildDirectory.toUserOutput())); @@ -513,7 +513,7 @@ void TeaLeafReader::startCMake(const QStringList &configurationArguments) tr("Configuring \"%1\"").arg(m_parameters.projectName), "CMake.Configure"); - m_cmakeProcess->setCommand(m_parameters.cmakeExecutable.toString(), args); + m_cmakeProcess->setCommand(m_parameters.cmakeTool->cmakeExecutable().toString(), args); emit configurationStarted(); m_cmakeProcess->start(); } @@ -549,7 +549,6 @@ void TeaLeafReader::cmakeFinished(int code, QProcess::ExitStatus status) delete m_future; m_future = nullptr; - m_hasData = true; emit dataAvailable(); } @@ -663,7 +662,7 @@ bool TeaLeafReader::extractFlagsFromNinja(const CMakeBuildTarget &buildTarget, // found // Get "all" target's working directory QByteArray ninjaFile; - QString buildNinjaFile = buildTargets().at(0).workingDirectory.toString(); + QString buildNinjaFile = takeBuildTargets().at(0).workingDirectory.toString(); buildNinjaFile += "/build.ninja"; QFile buildNinja(buildNinjaFile); if (buildNinja.exists()) { diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.h b/src/plugins/cmakeprojectmanager/tealeafreader.h index 9853a1b0a2b..2e699aecf16 100644 --- a/src/plugins/cmakeprojectmanager/tealeafreader.h +++ b/src/plugins/cmakeprojectmanager/tealeafreader.h @@ -46,15 +46,14 @@ public: TeaLeafReader(); ~TeaLeafReader() final; - bool isCompatible(const Parameters &p) final; + bool isCompatible(const BuildDirParameters &p) final; void resetData() final; - void parse(bool force) final; + void parse(bool forceConfiguration) final; void stop() final; bool isParsing() const final; - bool hasData() const final; - QList buildTargets() const final; + QList takeBuildTargets() final; CMakeConfig takeParsedConfiguration() final; void generateProjectTree(CMakeProjectNode *root, const QList &allFiles) final; @@ -80,8 +79,6 @@ private: ProjectExplorer::IOutputParser *m_parser = nullptr; QFutureInterface *m_future = nullptr; - bool m_hasData = false; - QSet m_cmakeFiles; QString m_projectName; QList m_buildTargets;