diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 250d9641c46..497d57dde1c 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "builddirmanager.h" +#include "cmakebuildconfiguration.h" #include "cmakekitinformation.h" #include "cmakeparser.h" #include "cmakeprojectmanager.h" @@ -32,8 +33,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -88,43 +91,49 @@ static QStringList toArguments(const CMakeConfig &config) { // BuildDirManager: // -------------------------------------------------------------------- -BuildDirManager::BuildDirManager(const Utils::FileName &sourceDir, const ProjectExplorer::Kit *k, - const CMakeConfig &inputConfig, const Utils::Environment &env, - const Utils::FileName &buildDir) : - m_sourceDir(sourceDir), - m_buildDir(buildDir), - m_kit(k), - m_environment(env), - m_inputConfig(inputConfig), +BuildDirManager::BuildDirManager(const CMakeBuildConfiguration *bc) : + m_buildConfiguration(bc), m_watcher(new QFileSystemWatcher(this)) { - QTC_CHECK(!sourceDir.isEmpty()); - m_projectName = m_sourceDir.fileName(); - if (m_buildDir.isEmpty()) { - m_tempDir = new QTemporaryDir(QLatin1String("cmake-tmp-XXXXXX")); - m_buildDir = Utils::FileName::fromString(m_tempDir->path()); - } - QTC_CHECK(!m_buildDir.isEmpty()); - QTC_CHECK(k); + QTC_ASSERT(bc, return); + m_projectName = sourceDirectory().fileName(); m_reparseTimer.setSingleShot(true); m_reparseTimer.setInterval(500); connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::forceReparse); connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this]() { - if (!isBusy()) + if (!isParsing()) m_reparseTimer.start(); }); - - QTimer::singleShot(0, this, &BuildDirManager::parse); } BuildDirManager::~BuildDirManager() { - delete m_tempDir; + resetData(); } -bool BuildDirManager::isBusy() const +const ProjectExplorer::Kit *BuildDirManager::kit() const +{ + return m_buildConfiguration->target()->kit(); +} + +const Utils::FileName BuildDirManager::buildDirectory() const +{ + return m_buildConfiguration->buildDirectory(); +} + +const Utils::FileName BuildDirManager::sourceDirectory() const +{ + return m_buildConfiguration->target()->project()->projectDirectory(); +} + +const CMakeConfig BuildDirManager::cmakeConfiguration() const +{ + return m_buildConfiguration->cmakeConfiguration(); +} + +bool BuildDirManager::isParsing() const { if (m_cmakeProcess) return m_cmakeProcess->state() != QProcess::NotRunning; @@ -133,53 +142,59 @@ bool BuildDirManager::isBusy() const void BuildDirManager::forceReparse() { - if (isBusy()) { - m_cmakeProcess->disconnect(); - m_cmakeProcess->deleteLater(); - m_cmakeProcess = nullptr; - } + stopProcess(); - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_kit); - const QString generator = CMakeGeneratorKitInformation::generator(m_kit); + CMakeTool *tool = CMakeKitInformation::cmakeTool(kit()); + const QString generator = CMakeGeneratorKitInformation::generator(kit()); QTC_ASSERT(tool, return); QTC_ASSERT(!generator.isEmpty(), return); - startCMake(tool, generator, m_inputConfig); + startCMake(tool, generator, cmakeConfiguration()); } -void BuildDirManager::setInputConfiguration(const CMakeConfig &config) +void BuildDirManager::resetData() { - m_inputConfig = config; - forceReparse(); + m_hasData = false; + + m_projectName.clear(); + m_buildTargets.clear(); + m_watchedFiles.clear(); + qDeleteAll(m_files); + m_files.clear(); + + const QStringList watchedFiles = m_watcher->files(); + if (!watchedFiles.isEmpty()) + m_watcher->removePaths(watchedFiles); } void BuildDirManager::parse() { - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_kit); - const QString generator = CMakeGeneratorKitInformation::generator(m_kit); + CMakeTool *tool = CMakeKitInformation::cmakeTool(kit()); + const QString generator = CMakeGeneratorKitInformation::generator(kit()); QTC_ASSERT(tool, return); QTC_ASSERT(!generator.isEmpty(), return); // Pop up a dialog asking the user to rerun cmake - QString cbpFile = CMakeManager::findCbpFile(QDir(m_buildDir.toString())); + QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory().toString())); QFileInfo cbpFileFi(cbpFile); if (!cbpFileFi.exists()) { // Initial create: - startCMake(tool, generator, m_inputConfig); + startCMake(tool, generator, cmakeConfiguration()); return; } - const bool mustUpdate - = Utils::anyOf(m_watchedFiles, [&cbpFileFi](const Utils::FileName &f) { + const bool mustUpdate = m_watchedFiles.isEmpty() + || Utils::anyOf(m_watchedFiles, [&cbpFileFi](const Utils::FileName &f) { return f.toFileInfo().lastModified() > cbpFileFi.lastModified(); }); if (mustUpdate) { startCMake(tool, generator, CMakeConfig()); } else { extractData(); + m_hasData = true; emit dataAvailable(); } } @@ -199,7 +214,7 @@ QList BuildDirManager::buildTargets() const return m_buildTargets; } -QList BuildDirManager::files() const +QList BuildDirManager::files() { return m_files; } @@ -211,25 +226,45 @@ void BuildDirManager::clearFiles() CMakeConfig BuildDirManager::configuration() const { + if (!m_hasData) + return CMakeConfig(); + return parseConfiguration(); } +void BuildDirManager::stopProcess() +{ + if (m_cmakeProcess) { + m_cmakeProcess->disconnect(); + + if (m_cmakeProcess->state() == QProcess::Running) { + m_cmakeProcess->terminate(); + if (!m_cmakeProcess->waitForFinished(500)) + m_cmakeProcess->kill(); + } + delete m_cmakeProcess; + m_cmakeProcess = nullptr; + + // Delete issue parser: + m_parser->flush(); + delete m_parser; + m_parser = nullptr; + } +} + void BuildDirManager::extractData() { const Utils::FileName topCMake - = Utils::FileName::fromString(m_sourceDir.toString() + QLatin1String("/CMakeLists.txt")); + = Utils::FileName::fromString(sourceDirectory().toString() + QLatin1String("/CMakeLists.txt")); - m_projectName = m_sourceDir.fileName(); - m_buildTargets.clear(); - m_watchedFiles.clear(); - m_files.clear(); + resetData(); + + m_projectName = sourceDirectory().fileName(); m_files.append(new ProjectExplorer::FileNode(topCMake, ProjectExplorer::ProjectFileType, false)); m_watchedFiles.insert(topCMake); - m_watcher->removePaths(m_watcher->files()); - // Find cbp file - QString cbpFile = CMakeManager::findCbpFile(m_buildDir.toString()); + QString cbpFile = CMakeManager::findCbpFile(buildDirectory().toString()); if (cbpFile.isEmpty()) return; @@ -238,7 +273,7 @@ void BuildDirManager::extractData() // setFolderName CMakeCbpParser cbpparser; // Parsing - if (!cbpparser.parseCbpFile(m_kit, cbpFile, m_sourceDir.toString())) + if (!cbpparser.parseCbpFile(kit(), cbpFile, sourceDirectory().toString())) return; m_projectName = cbpparser.projectName(); @@ -271,12 +306,12 @@ void BuildDirManager::startCMake(CMakeTool *tool, const QString &generator, QTC_ASSERT(!m_future, return); // Make sure m_buildDir exists: - const QString buildDirStr = m_buildDir.toString(); + const QString buildDirStr = buildDirectory().toString(); QDir bDir = QDir(buildDirStr); bDir.mkpath(buildDirStr); m_parser = new CMakeParser; - QDir source = QDir(m_sourceDir.toString()); + QDir source = QDir(sourceDirectory().toString()); connect(m_parser, &ProjectExplorer::IOutputParser::addTask, m_parser, [source](const ProjectExplorer::Task &task) { if (task.file.isEmpty() || task.file.toFileInfo().isAbsolute()) { @@ -290,11 +325,11 @@ void BuildDirManager::startCMake(CMakeTool *tool, const QString &generator, // Always use the sourceDir: If we are triggered because the build directory is getting deleted // then we are racing against CMakeCache.txt also getting deleted. - const QString srcDir = m_sourceDir.toString(); + const QString srcDir = sourceDirectory().toString(); m_cmakeProcess = new Utils::QtcProcess(this); m_cmakeProcess->setWorkingDirectory(buildDirStr); - m_cmakeProcess->setEnvironment(m_environment); + m_cmakeProcess->setEnvironment(m_buildConfiguration->environment()); connect(m_cmakeProcess, &QProcess::readyReadStandardOutput, this, &BuildDirManager::processCMakeOutput); @@ -314,17 +349,17 @@ void BuildDirManager::startCMake(CMakeTool *tool, const QString &generator, Core::MessageManager::write(tr("Running '%1 %2' in %3.") .arg(tool->cmakeExecutable().toUserOutput()) .arg(args) - .arg(m_buildDir.toUserOutput())); + .arg(buildDirectory().toUserOutput())); m_future = new QFutureInterface(); m_future->setProgressRange(0, 1); Core::ProgressManager::addTask(m_future->future(), - tr("Configuring \"%1\"").arg(projectName()), + tr("Configuring \"%1\"").arg(m_buildConfiguration->target()->project()->displayName()), "CMake.Configure"); m_cmakeProcess->setCommand(tool->cmakeExecutable().toString(), args); m_cmakeProcess->start(); - emit parsingStarted(); + emit configurationStarted(); } void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status) @@ -335,12 +370,7 @@ void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status) processCMakeOutput(); processCMakeError(); - m_parser->flush(); - delete m_parser; - m_parser = nullptr; - - m_cmakeProcess->deleteLater(); - m_cmakeProcess = nullptr; + stopProcess(); extractData(); // try even if cmake failed... @@ -363,6 +393,7 @@ void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status) delete m_future; m_future = 0; + m_hasData = true; emit dataAvailable(); } @@ -433,7 +464,7 @@ static CMakeConfigItem::Type fromByteArray(const QByteArray &type) { CMakeConfig BuildDirManager::parseConfiguration() const { CMakeConfig result; - const QString cacheFile = QDir(m_buildDir.toString()).absoluteFilePath(QLatin1String("CMakeCache.txt")); + const QString cacheFile = QDir(buildDirectory().toString()).absoluteFilePath(QLatin1String("CMakeCache.txt")); QFile cache(cacheFile); if (!cache.open(QIODevice::ReadOnly | QIODevice::Text)) return CMakeConfig(); @@ -470,7 +501,7 @@ CMakeConfig BuildDirManager::parseConfiguration() const // Sanity checks: if (key == "CMAKE_HOME_DIRECTORY") { const Utils::FileName actualSourceDir = Utils::FileName::fromUserInput(QString::fromUtf8(value)); - if (actualSourceDir != m_sourceDir) + if (actualSourceDir != sourceDirectory()) emit errorOccured(tr("Build directory contains a build of the wrong project (%1).") .arg(actualSourceDir.toUserOutput())); } diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index a05c90358d1..b6c9e59586d 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -57,39 +57,40 @@ class CMakeTool; namespace Internal { +class CMakeBuildConfiguration; + class BuildDirManager : public QObject { Q_OBJECT public: - BuildDirManager(const Utils::FileName &sourceDir, const ProjectExplorer::Kit *k, - const CMakeConfig &inputConfig, const Utils::Environment &env, - const Utils::FileName &buildDir); + BuildDirManager(const CMakeBuildConfiguration *bc); ~BuildDirManager() override; - const ProjectExplorer::Kit *kit() const { return m_kit; } - const Utils::FileName buildDirectory() const { return m_buildDir; } - const Utils::FileName sourceDirectory() const { return m_sourceDir; } - bool isBusy() const; + const ProjectExplorer::Kit *kit() const; + const Utils::FileName buildDirectory() const; + const Utils::FileName sourceDirectory() const; + const CMakeConfig cmakeConfiguration() const; + bool isParsing() const; void parse(); void forceReparse(); - - void setInputConfiguration(const CMakeConfig &config); + void resetData(); bool isProjectFile(const Utils::FileName &fileName) const; QString projectName() const; QList buildTargets() const; - QList files() const; + QList files(); void clearFiles(); CMakeConfig configuration() const; signals: - void parsingStarted() const; + void configurationStarted() const; void dataAvailable() const; void errorOccured(const QString &err) const; private: + void stopProcess(); void extractData(); void startCMake(CMakeTool *tool, const QString &generator, const CMakeConfig &config); @@ -100,14 +101,9 @@ private: CMakeConfig parseConfiguration() const; - const Utils::FileName m_sourceDir; - Utils::FileName m_buildDir; - Utils::FileName m_parsedSourceDir; - const ProjectExplorer::Kit *const m_kit; - Utils::Environment m_environment; - CMakeConfig m_inputConfig; + bool m_hasData = false; - QTemporaryDir *m_tempDir = nullptr; + const CMakeBuildConfiguration *m_buildConfiguration = nullptr; Utils::QtcProcess *m_cmakeProcess = nullptr; QSet m_watchedFiles; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 0d554ff4dd7..ecf038cb289 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -25,6 +25,7 @@ #include "cmakebuildconfiguration.h" +#include "builddirmanager.h" #include "cmakebuildinfo.h" #include "cmakebuildstep.h" #include "cmakekitinformation.h" @@ -75,10 +76,32 @@ static FileName shadowBuildDirectory(const FileName &projectFilePath, const Kit CMakeBuildConfiguration::CMakeBuildConfiguration(ProjectExplorer::Target *parent) : BuildConfiguration(parent, Core::Id(Constants::CMAKE_BC_ID)) { - CMakeProject *project = static_cast(parent->project()); + auto project = static_cast(parent->project()); setBuildDirectory(shadowBuildDirectory(project->projectFilePath(), parent->kit(), displayName(), BuildConfiguration::Unknown)); + + m_buildDirManager = new BuildDirManager(this); + connect(m_buildDirManager, &BuildDirManager::dataAvailable, + this, &CMakeBuildConfiguration::dataAvailable); + connect(m_buildDirManager, &BuildDirManager::errorOccured, + this, &CMakeBuildConfiguration::setError); + connect(m_buildDirManager, &BuildDirManager::configurationStarted, + this, [this]() { m_completeConfigurationCache.clear(); emit parsingStarted(); }); + + connect(this, &CMakeBuildConfiguration::environmentChanged, + m_buildDirManager, &BuildDirManager::forceReparse); + connect(this, &CMakeBuildConfiguration::buildDirectoryChanged, + m_buildDirManager, &BuildDirManager::parse); + connect(target(), &Target::kitChanged, m_buildDirManager, &BuildDirManager::forceReparse); + + connect(this, &CMakeBuildConfiguration::parsingStarted, project, &CMakeProject::handleParsingStarted); + connect(this, &CMakeBuildConfiguration::dataAvailable, project, &CMakeProject::parseCMakeOutput); +} + +CMakeBuildConfiguration::~CMakeBuildConfiguration() +{ + m_buildDirManager->deleteLater(); // Do not block while waiting for cmake... } bool CMakeBuildConfiguration::isEnabled() const @@ -139,6 +162,102 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map) return true; } +BuildDirManager *CMakeBuildConfiguration::buildDirManager() const +{ + return m_buildDirManager; +} + +bool CMakeBuildConfiguration::isParsing() const +{ + return m_buildDirManager && m_buildDirManager->isParsing(); +} + +void CMakeBuildConfiguration::parse() +{ + m_buildDirManager->parse(); +} + +void CMakeBuildConfiguration::resetData() +{ + m_buildDirManager->resetData(); +} + +QList CMakeBuildConfiguration::completeCMakeConfiguration() const +{ + if (m_buildDirManager->isParsing()) + return QList(); + + if (m_completeConfigurationCache.isEmpty()) + m_completeConfigurationCache = m_buildDirManager->configuration(); + + return Utils::transform(m_completeConfigurationCache, [](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.isAdvanced = i.isAdvanced; + 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 CMakeBuildConfiguration::setCurrentCMakeConfiguration(const QList &items) +{ + if (m_buildDirManager->isParsing()) + return; + + const CMakeConfig newConfig = Utils::transform(items, [](const ConfigModel::DataItem &i) { + CMakeConfigItem ni; + ni.key = i.key.toUtf8(); + ni.value = i.value.toUtf8(); + ni.documentation = i.description.toUtf8(); + ni.isAdvanced = i.isAdvanced; + switch (i.type) { + case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: + ni.type = CMakeConfigItem::BOOL; + break; + case CMakeProjectManager::ConfigModel::DataItem::FILE: + ni.type = CMakeConfigItem::FILEPATH; + break; + case CMakeProjectManager::ConfigModel::DataItem::DIRECTORY: + ni.type = CMakeConfigItem::PATH; + break; + case CMakeProjectManager::ConfigModel::DataItem::STRING: + ni.type = CMakeConfigItem::STRING; + break; + case CMakeProjectManager::ConfigModel::DataItem::UNKNOWN: + default: + ni.type = CMakeConfigItem::INTERNAL; + break; + } + return ni; + }); + + // There is a buildDirManager, so there must also be an active BC: + const CMakeConfig config = cmakeConfiguration() + newConfig; + setCMakeConfiguration(config); + + m_buildDirManager->forceReparse(); +} + void CMakeBuildConfiguration::emitBuildTypeChanged() { emit buildTypeChanged(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index ff5eb0b87f9..2ab4944c818 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -26,6 +26,7 @@ #pragma once #include "cmakeconfigitem.h" +#include "configmodel.h" #include #include @@ -38,7 +39,9 @@ class CMakeProject; namespace Internal { +class BuildDirManager; class CMakeBuildConfigurationFactory; +class CMakeBuildSettingsWidget; class CMakeBuildConfiguration : public ProjectExplorer::BuildConfiguration { @@ -47,6 +50,7 @@ class CMakeBuildConfiguration : public ProjectExplorer::BuildConfiguration public: CMakeBuildConfiguration(ProjectExplorer::Target *parent); + ~CMakeBuildConfiguration(); bool isEnabled() const override; QString disabledReason() const override; @@ -60,24 +64,44 @@ public: void emitBuildTypeChanged(); void setCMakeConfiguration(const CMakeConfig &config); + bool hasCMakeConfiguration() const; CMakeConfig cmakeConfiguration() const; - void setError(const QString &message); QString error() const; + BuildDirManager *buildDirManager() const; + + bool isParsing() const; + + void parse(); + void resetData(); + signals: void errorOccured(const QString &message); + void parsingStarted(); + void dataAvailable(); + protected: CMakeBuildConfiguration(ProjectExplorer::Target *parent, CMakeBuildConfiguration *source); bool fromMap(const QVariantMap &map) override; private: + QList completeCMakeConfiguration() const; + void setCurrentCMakeConfiguration(const QList &items); + + void setError(const QString &message); + QString m_initialArguments; CMakeConfig m_configuration; QString m_error; - friend class CMakeProjectManager::CMakeProject; + mutable QList m_completeConfigurationCache; + + BuildDirManager *m_buildDirManager = nullptr; + + friend class CMakeBuildSettingsWidget; + friend class CMakeProject; }; class CMakeBuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp index 2a348a82883..1fa335b8bde 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp @@ -84,9 +84,9 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) buildDirChooser->setBaseFileName(project->projectDirectory()); buildDirChooser->setFileName(bc->buildDirectory()); connect(buildDirChooser, &Utils::PathChooser::rawPathChanged, this, - [this, project](const QString &path) { + [this](const QString &path) { m_configModel->flush(); // clear out config cache... - project->changeBuildDirectory(m_buildConfiguration, path); + m_buildConfiguration->setBuildDirectory(Utils::FileName::fromString(path)); }); int row = 0; @@ -123,7 +123,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) m_configView->setMinimumHeight(300); m_configView->setRootIsDecorated(false); m_configView->setUniformRowHeights(true); - new Utils::HeaderViewStretcher(m_configView->header(), 1); + auto stretcher = new Utils::HeaderViewStretcher(m_configView->header(), 1); m_configView->setSelectionMode(QAbstractItemView::SingleSelection); m_configView->setSelectionBehavior(QAbstractItemView::SelectItems); m_configView->setFrameShape(QFrame::NoFrame); @@ -166,13 +166,18 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) updateButtonState(); m_showProgressTimer.start(); }); - connect(project, &CMakeProject::buildDirectoryDataAvailable, - this, [this, project, buildDirChooser](ProjectExplorer::BuildConfiguration *bc) { + + if (m_buildConfiguration->isParsing()) + m_showProgressTimer.start(); + else + m_configModel->setConfiguration(m_buildConfiguration->completeCMakeConfiguration()); + + connect(m_buildConfiguration, &CMakeBuildConfiguration::dataAvailable, + this, [this, buildDirChooser, stretcher]() { updateButtonState(); - if (m_buildConfiguration == bc) { - m_configModel->setConfiguration(project->currentCMakeConfiguration()); - buildDirChooser->triggerChanged(); // refresh valid state... - } + m_configModel->setConfiguration(m_buildConfiguration->completeCMakeConfiguration()); + stretcher->stretch(); + buildDirChooser->triggerChanged(); // refresh valid state... m_showProgressTimer.stop(); m_progressIndicator->hide(); }); @@ -186,8 +191,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) this, &CMakeBuildSettingsWidget::updateAdvancedCheckBox); connect(m_resetButton, &QPushButton::clicked, m_configModel, &ConfigModel::resetAllChanges); - connect(m_reconfigureButton, &QPushButton::clicked, this, [this, project]() { - project->setCurrentCMakeConfiguration(m_configModel->configurationChanges()); + connect(m_reconfigureButton, &QPushButton::clicked, this, [this]() { + m_buildConfiguration->setCurrentCMakeConfiguration(m_configModel->configurationChanges()); }); connect(m_editButton, &QPushButton::clicked, this, [this]() { QModelIndex idx = m_configView->currentIndex(); @@ -218,8 +223,7 @@ void CMakeBuildSettingsWidget::setError(const QString &message) void CMakeBuildSettingsWidget::updateButtonState() { - auto project = static_cast(m_buildConfiguration->target()->project()); - const bool isParsing = project->isParsing(); + const bool isParsing = m_buildConfiguration->isParsing(); const bool hasChanges = m_configModel->hasChanges(); m_resetButton->setEnabled(hasChanges && !isParsing); m_reconfigureButton->setEnabled((hasChanges || m_configModel->hasCMakeChanges()) && !isParsing); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 5de059b24ea..71c4041b502 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -72,12 +72,12 @@ const char ADD_RUNCONFIGURATION_TEXT[] = "Current executable"; CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl) : AbstractProcessStep(bsl, Core::Id(MS_ID)) { - ctor(); + ctor(bsl); } CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Core::Id id) : AbstractProcessStep(bsl, id) { - ctor(); + ctor(bsl); } CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, CMakeBuildStep *bs) : @@ -86,10 +86,10 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, CMakeBuildStep *bs) : m_toolArguments(bs->m_toolArguments), m_addRunConfigurationArgument(bs->m_addRunConfigurationArgument) { - ctor(); + ctor(bsl); } -void CMakeBuildStep::ctor() +void CMakeBuildStep::ctor(BuildStepList *bsl) { m_percentProgress = QRegExp(QLatin1String("^\\[\\s*(\\d*)%\\]")); m_ninjaProgress = QRegExp(QLatin1String("^\\[\\s*(\\d*)/\\s*(\\d*)")); @@ -97,9 +97,15 @@ void CMakeBuildStep::ctor() //: Default display name for the cmake make step. setDefaultDisplayName(tr("Make")); + auto bc = qobject_cast(bsl->parent()); + if (!bc) { + auto t = qobject_cast(bsl->parent()->parent()); + QTC_ASSERT(t, return); + bc = qobject_cast(t->activeBuildConfiguration()); + } + connect(target(), &Target::kitChanged, this, &CMakeBuildStep::cmakeCommandChanged); - connect(static_cast(project()), &CMakeProject::buildDirectoryDataAvailable, - this, &CMakeBuildStep::buildTargetsChanged); + connect(bc, &CMakeBuildConfiguration::dataAvailable, this, &CMakeBuildStep::handleBuildTargetChanges); } CMakeBuildConfiguration *CMakeBuildStep::cmakeBuildConfiguration() const @@ -117,12 +123,13 @@ CMakeRunConfiguration *CMakeBuildStep::targetsActiveRunConfiguration() const return qobject_cast(target()->activeRunConfiguration()); } -void CMakeBuildStep::buildTargetsChanged() +void CMakeBuildStep::handleBuildTargetChanges() { const QStringList filteredTargets = Utils::filtered(static_cast(project())->buildTargetTitles(), [this](const QString &s) { return m_buildTargets.contains(s); }); setBuildTargets(filteredTargets); + emit buildTargetsChanged(); } QVariantMap CMakeBuildStep::toMap() const @@ -401,7 +408,7 @@ CMakeBuildStepConfigWidget::CMakeBuildStepConfigWidget(CMakeBuildStep *buildStep connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, this, &CMakeBuildStepConfigWidget::updateDetails); - connect(pro, &CMakeProject::buildDirectoryDataAvailable, this, &CMakeBuildStepConfigWidget::buildTargetsChanged); + connect(m_buildStep, &CMakeBuildStep::buildTargetsChanged, this, &CMakeBuildStepConfigWidget::buildTargetsChanged); connect(m_buildStep, &CMakeBuildStep::targetsToBuildChanged, this, &CMakeBuildStepConfigWidget::selectedBuildTargetsChanged); connect(pro, &CMakeProject::environmentChanged, this, &CMakeBuildStepConfigWidget::updateDetails); } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h index 92809316ed7..b56564ab685 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h @@ -83,6 +83,7 @@ public: signals: void cmakeCommandChanged(); void targetsToBuildChanged(); + void buildTargetsChanged(); protected: void processStarted() override; @@ -97,9 +98,9 @@ protected: void stdOutput(const QString &line) override; private: - void ctor(); + void ctor(ProjectExplorer::BuildStepList *bsl); - void buildTargetsChanged(); + void handleBuildTargetChanges(); CMakeRunConfiguration *targetsActiveRunConfiguration() const; QRegExp m_percentProgress; diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 8824434ab99..1bb32c84c02 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -67,19 +67,18 @@ #include #include -using namespace CMakeProjectManager; -using namespace CMakeProjectManager::Internal; using namespace ProjectExplorer; using namespace Utils; +namespace CMakeProjectManager { + +using namespace Internal; + // QtCreator CMake Generator wishlist: // Which make targets we need to build to get all executables // What is the actual compiler executable // DEFINES -// Open Questions -// Who sets up the environment for cl.exe ? INCLUDEPATH and so on - /*! \class CMakeProject */ @@ -93,19 +92,6 @@ CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName) setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); rootProjectNode()->setDisplayName(fileName.parentDir().fileName()); - - connect(this, &CMakeProject::buildDirectoryDataAvailable, this, &CMakeProject::updateRunConfigurations); - connect(this, &Project::activeTargetChanged, this, &CMakeProject::activeTargetHasChanged); - connect(this, &CMakeProject::environmentChanged, this, [this]() { - BuildConfiguration *bc = nullptr; - if (activeTarget()) - bc = activeTarget()->activeBuildConfiguration(); - changeActiveBuildConfiguration(bc); // Does a clean reset of the builddirmanager - }); - - connect(this, &Project::addedTarget, this, [this](Target *t) { - connect(t, &Target::kitChanged, this, &CMakeProject::handleKitChanges); - }); } CMakeProject::~CMakeProject() @@ -115,71 +101,6 @@ CMakeProject::~CMakeProject() qDeleteAll(m_extraCompilers); } -void CMakeProject::changeActiveBuildConfiguration(ProjectExplorer::BuildConfiguration *bc) -{ - if (m_buildDirManager) { - m_buildDirManager->disconnect(); - m_buildDirManager->deleteLater(); - } - m_buildDirManager = nullptr; - - Kit *k = nullptr; - CMakeConfig config; - Utils::FileName buildDir; - - CMakeBuildConfiguration *cmakebc = qobject_cast(bc); - if (!cmakebc) { - k = KitManager::defaultKit(); - config = CMakeConfigurationKitInformation::configuration(k); - } else { - k = cmakebc->target()->kit(); - config = cmakebc->cmakeConfiguration(); - buildDir = cmakebc->buildDirectory(); - } - if (k) { - m_buildDirManager = new Internal::BuildDirManager(projectDirectory(), k, config, - cmakebc->environment(), buildDir); - connect(m_buildDirManager, &BuildDirManager::parsingStarted, - this, &CMakeProject::parsingStarted); - connect(m_buildDirManager, &BuildDirManager::dataAvailable, - this, &CMakeProject::parseCMakeOutput); - connect(m_buildDirManager, &BuildDirManager::errorOccured, - cmakebc, &CMakeBuildConfiguration::setError); - } -} - -void CMakeProject::activeTargetHasChanged(Target *target) -{ - if (m_activeTarget) { - disconnect(m_activeTarget, &Target::activeBuildConfigurationChanged, - this, &CMakeProject::changeActiveBuildConfiguration); - } - - m_activeTarget = target; - - if (!m_activeTarget) - return; - - connect(m_activeTarget, &Target::activeBuildConfigurationChanged, - this, &CMakeProject::changeActiveBuildConfiguration); - changeActiveBuildConfiguration(m_activeTarget->activeBuildConfiguration()); -} - -void CMakeProject::changeBuildDirectory(CMakeBuildConfiguration *bc, const QString &newBuildDirectory) -{ - bc->setBuildDirectory(FileName::fromString(newBuildDirectory)); - if (activeTarget() && activeTarget()->activeBuildConfiguration() == bc) - changeActiveBuildConfiguration(bc); -} - -void CMakeProject::handleKitChanges() -{ - const Target *t = qobject_cast(sender()); - if (t && t != activeTarget()) - return; - changeActiveBuildConfiguration(t->activeBuildConfiguration()); // force proper refresh -} - QStringList CMakeProject::getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash &cache) { @@ -279,23 +200,25 @@ bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, void CMakeProject::parseCMakeOutput() { - QTC_ASSERT(m_buildDirManager, return); - QTC_ASSERT(activeTarget() && activeTarget()->activeBuildConfiguration(), return); + auto cmakeBc = qobject_cast(sender()); + QTC_ASSERT(cmakeBc, return); + if (!activeTarget() || activeTarget()->activeBuildConfiguration() != cmakeBc) + return; - auto activeBC = static_cast(activeTarget()->activeBuildConfiguration()); + BuildDirManager *bdm = cmakeBc->buildDirManager(); + QTC_ASSERT(bdm, return); - rootProjectNode()->setDisplayName(m_buildDirManager->projectName()); + rootProjectNode()->setDisplayName(bdm->projectName()); - buildTree(static_cast(rootProjectNode()), m_buildDirManager->files()); - m_buildDirManager->clearFiles(); // Some of the FileNodes in files() were deleted! + buildTree(static_cast(rootProjectNode()), bdm->files()); + bdm->clearFiles(); // Some of the FileNodes in files() were deleted! updateApplicationAndDeploymentTargets(); createGeneratedCodeModelSupport(); - ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(m_buildDirManager->kit()); + ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(cmakeBc->target()->kit()); if (!tc) { - emit buildDirectoryDataAvailable(activeBC); emit fileListChanged(); return; } @@ -305,7 +228,7 @@ void CMakeProject::parseCMakeOutput() CppTools::ProjectPartBuilder ppBuilder(pinfo); CppTools::ProjectPart::QtVersion activeQtVersion = CppTools::ProjectPart::NoQt; - if (QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(m_buildDirManager->kit())) { + if (QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(cmakeBc->target()->kit())) { if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0)) activeQtVersion = CppTools::ProjectPart::Qt4; else @@ -336,10 +259,11 @@ void CMakeProject::parseCMakeOutput() m_codeModelFuture = modelmanager->updateProjectInfo(pinfo); emit displayNameChanged(); - emit buildDirectoryDataAvailable(activeBC); emit fileListChanged(); - emit activeBC->emitBuildTypeChanged(); + emit cmakeBc->emitBuildTypeChanged(); + + updateRunConfigurations(); } bool CMakeProject::needsConfiguration() const @@ -364,105 +288,26 @@ bool CMakeProject::supportsKit(Kit *k, QString *errorMessage) const void CMakeProject::runCMake() { - if (m_buildDirManager && !m_buildDirManager->isBusy()) - m_buildDirManager->forceReparse(); -} + CMakeBuildConfiguration *bc = nullptr; + if (activeTarget()) + bc = qobject_cast(activeTarget()->activeBuildConfiguration()); -bool CMakeProject::isParsing() const -{ - return m_buildDirManager && m_buildDirManager->isBusy(); -} - -QList CMakeProject::currentCMakeConfiguration() const -{ - if (!m_buildDirManager || m_buildDirManager->isBusy()) - return QList(); - const QList cmakeItems = m_buildDirManager->configuration(); - return Utils::transform(cmakeItems, [](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.isAdvanced = i.isAdvanced; - 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 CMakeProject::setCurrentCMakeConfiguration(const QList &items) -{ - if (!m_buildDirManager || m_buildDirManager->isBusy()) + if (!bc) return; - const CMakeConfig newConfig = Utils::transform(items, [](const ConfigModel::DataItem &i) { - CMakeConfigItem ni; - ni.key = i.key.toUtf8(); - ni.value = i.value.toUtf8(); - ni.documentation = i.description.toUtf8(); - ni.isAdvanced = i.isAdvanced; - switch (i.type) { - case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: - ni.type = CMakeConfigItem::BOOL; - break; - case CMakeProjectManager::ConfigModel::DataItem::FILE: - ni.type = CMakeConfigItem::FILEPATH; - break; - case CMakeProjectManager::ConfigModel::DataItem::DIRECTORY: - ni.type = CMakeConfigItem::PATH; - break; - case CMakeProjectManager::ConfigModel::DataItem::STRING: - ni.type = CMakeConfigItem::STRING; - break; - case CMakeProjectManager::ConfigModel::DataItem::UNKNOWN: - default: - ni.type = CMakeConfigItem::INTERNAL; - break; - } - return ni; - }); - - // There is a buildDirManager, so there must also be an active BC: - QTC_ASSERT(activeTarget(), return); - QTC_ASSERT(activeTarget()->activeBuildConfiguration(), return); - - auto bc = static_cast(activeTarget()->activeBuildConfiguration()); - QTC_ASSERT(bc, return); - const CMakeConfig config = bc->cmakeConfiguration() + newConfig; - bc->setCMakeConfiguration(config); - - m_buildDirManager->setInputConfiguration(config); -} - -bool CMakeProject::isProjectFile(const FileName &fileName) -{ - if (!m_buildDirManager) - return false; - return m_buildDirManager->isProjectFile(fileName); + BuildDirManager *bdm = bc->buildDirManager(); + if (bdm && !bdm->isParsing()) + bdm->forceReparse(); } QList CMakeProject::buildTargets() const { - if (!m_buildDirManager) + BuildDirManager *bdm = nullptr; + if (activeTarget() && activeTarget()->activeBuildConfiguration()) + bdm = static_cast(activeTarget()->activeBuildConfiguration())->buildDirManager(); + if (!bdm) return QList(); - return m_buildDirManager->buildTargets(); + return bdm->buildTargets(); } QStringList CMakeProject::buildTargetTitles(bool runnable) const @@ -571,26 +416,22 @@ QString CMakeProject::displayName() const QStringList CMakeProject::files(FilesMode fileMode) const { - QStringList result; - if (m_buildDirManager) { - QList nodes; - gatherFileNodes(rootProjectNode(), nodes); - nodes = Utils::filtered(nodes, [fileMode](const FileNode *fn) { - const bool isGenerated = fn->isGenerated(); - switch (fileMode) - { - case ProjectExplorer::Project::SourceFiles: - return !isGenerated; - case ProjectExplorer::Project::GeneratedFiles: - return isGenerated; - case ProjectExplorer::Project::AllFiles: - default: - return true; - } - }); - result = Utils::transform(nodes, [fileMode](const FileNode* fn) { return fn->filePath().toString(); }); - } - return result; + QList nodes; + gatherFileNodes(rootProjectNode(), nodes); + nodes = Utils::filtered(nodes, [fileMode](const FileNode *fn) { + const bool isGenerated = fn->isGenerated(); + switch (fileMode) + { + case ProjectExplorer::Project::SourceFiles: + return !isGenerated; + case ProjectExplorer::Project::GeneratedFiles: + return isGenerated; + case ProjectExplorer::Project::AllFiles: + default: + return true; + } + }); + return Utils::transform(nodes, [fileMode](const FileNode* fn) { return fn->filePath().toString(); }); } Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *errorMessage) @@ -599,13 +440,8 @@ Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *er if (result != RestoreResult::Ok) return result; - m_activeTarget = activeTarget(); - if (m_activeTarget) { - connect(m_activeTarget, &Target::activeBuildConfigurationChanged, - this, &CMakeProject::changeActiveBuildConfiguration); - if (BuildConfiguration *bc = m_activeTarget->activeBuildConfiguration()) - changeActiveBuildConfiguration(bc); - } + handleActiveTargetChanged(); + handleActiveBuildConfigurationChanged(); return RestoreResult::Ok; } @@ -620,6 +456,46 @@ bool CMakeProject::setupTarget(Target *t) return true; } +void CMakeProject::handleActiveTargetChanged() +{ + if (m_connectedTarget) { + disconnect(m_connectedTarget, &Target::activeBuildConfigurationChanged, + this, &CMakeProject::handleActiveBuildConfigurationChanged); + + } + + m_connectedTarget = activeTarget(); + + if (m_connectedTarget) { + connect(m_connectedTarget, &Target::activeBuildConfigurationChanged, + this, &CMakeProject::handleActiveBuildConfigurationChanged); + } +} + +void CMakeProject::handleActiveBuildConfigurationChanged() +{ + if (!activeTarget() || !activeTarget()->activeBuildConfiguration()) + return; + auto activeBc = qobject_cast(activeTarget()->activeBuildConfiguration()); + + foreach (Target *t, targets()) { + foreach (BuildConfiguration *bc, t->buildConfigurations()) { + auto i = qobject_cast(bc); + QTC_ASSERT(i, continue); + if (i == activeBc) + i->parse(); + else + i->resetData(); + } + } +} + +void CMakeProject::handleParsingStarted() +{ + if (activeTarget() && activeTarget()->activeBuildConfiguration() == sender()) + emit parsingStarted(); +} + CMakeBuildTarget CMakeProject::buildTargetForTitle(const QString &title) { foreach (const CMakeBuildTarget &ct, buildTargets()) @@ -807,3 +683,5 @@ void CMakeBuildTarget::clear() compilerOptions.clear(); defines.clear(); } + +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index f6a966375c9..fabdc6da146 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -27,7 +27,6 @@ #include "cmake_global.h" #include "cmakeprojectnodes.h" -#include "configmodel.h" #include #include @@ -104,39 +103,27 @@ public: CMakeBuildTarget buildTargetForTitle(const QString &title); - bool isProjectFile(const Utils::FileName &fileName); - bool needsConfiguration() const override; bool requiresTargetPanel() const override; bool supportsKit(ProjectExplorer::Kit *k, QString *errorMessage = 0) const override; void runCMake(); - bool isParsing() const; - - QList currentCMakeConfiguration() const; - void setCurrentCMakeConfiguration(const QList &items); signals: - /// emitted when parsing starts: + /// emitted when cmake is running: void parsingStarted(); - /// emitted after parsing - void buildDirectoryDataAvailable(ProjectExplorer::BuildConfiguration *bc); protected: RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override; bool setupTarget(ProjectExplorer::Target *t) override; - // called by CMakeBuildSettingsWidget - void changeBuildDirectory(Internal::CMakeBuildConfiguration *bc, const QString &newBuildDirectory); - private: - void handleKitChanges(); + void handleActiveTargetChanged(); + void handleActiveBuildConfigurationChanged(); + void handleParsingStarted(); void parseCMakeOutput(); - void activeTargetHasChanged(ProjectExplorer::Target *target); - void changeActiveBuildConfiguration(ProjectExplorer::BuildConfiguration*); - void updateRunConfigurations(); void buildTree(Internal::CMakeProjectNode *rootNode, QList list); @@ -150,13 +137,14 @@ private: bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash &cache); bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash &cache); - ProjectExplorer::Target *m_activeTarget = 0; - Internal::BuildDirManager *m_buildDirManager = 0; + ProjectExplorer::Target *m_connectedTarget = nullptr; // TODO probably need a CMake specific node structure QList m_buildTargets; QFuture m_codeModelFuture; QList m_extraCompilers; + + friend class Internal::CMakeBuildConfiguration; }; } // namespace CMakeProjectManager