From 0c74f6cc21ae7fccfd844efdd9365e72fa507a23 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 3 Jun 2019 17:36:02 +0200 Subject: [PATCH] ProjectExplorer: Add "build for current run config" menu entry This lets users build the executable corresponding to the currently active run configuration. It's functionally equivalent to locating the corresponding node in the project tree and choosing "Build" from the context menu. Fixes: QTCREATORBUG-22403 Change-Id: Ic2b729c7ce17f1ad944dc06746bb9d6db90b6c61 Reviewed-by: hjk --- .../cmakeprojectmanager/cmakeproject.cpp | 1 + .../cmakeprojectmanager/cmakeprojectnodes.cpp | 6 +++ .../cmakeprojectmanager/cmakeprojectnodes.h | 2 + src/plugins/projectexplorer/project.cpp | 11 ++++++ src/plugins/projectexplorer/project.h | 3 ++ .../projectexplorer/projectexplorer.cpp | 38 +++++++++++++++++++ src/plugins/projectexplorer/projectnodes.cpp | 9 +++++ src/plugins/projectexplorer/projectnodes.h | 9 +++++ src/plugins/qbsprojectmanager/qbsnodes.cpp | 7 ++++ src/plugins/qbsprojectmanager/qbsnodes.h | 1 + src/plugins/qbsprojectmanager/qbsproject.cpp | 1 + .../qbsprojectmanagerplugin.cpp | 6 +++ .../qbsprojectmanagerplugin.h | 8 +++- .../qmakeprojectmanager/qmakenodes.cpp | 7 ++++ src/plugins/qmakeprojectmanager/qmakenodes.h | 2 + .../qmakeprojectmanager/qmakeproject.cpp | 1 + .../qmakeprojectmanager.cpp | 5 +++ .../qmakeprojectmanager/qmakeprojectmanager.h | 4 +- 18 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index da349f8e1c5..d064120f295 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -87,6 +87,7 @@ CMakeProject::CMakeProject(const FilePath &fileName) : Project(Constants::CMAKEM setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setDisplayName(projectDirectory().fileName()); + setCanBuildProducts(); // Timer: m_delayedParsingTimer.setSingleShot(true); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp index f14f627b34f..ea218c22574 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp @@ -26,6 +26,7 @@ #include "cmakeprojectnodes.h" #include "cmakeconfigitem.h" +#include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmakeprojectplugin.h" @@ -264,6 +265,11 @@ Utils::optional CMakeTargetNode::visibleAfterAddFileAction() co return filePath().pathAppended("CMakeLists.txt"); } +void CMakeTargetNode::build() +{ + static_cast(getProject())->buildCMakeTarget(displayName()); +} + void CMakeTargetNode::setTargetInformation(const QList &artifacts, const QString &type) { diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h index 5a617379258..b924ec62a8c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h @@ -76,6 +76,8 @@ public: bool addFiles(const QStringList &filePaths, QStringList *notAdded) override; Utils::optional visibleAfterAddFileAction() const override; + void build() override; + QVariant data(Core::Id role) const override; void setConfig(const CMakeConfig &config); diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 16ae9a38dce..026cb381ba5 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -164,6 +164,7 @@ public: bool m_isParsing = false; bool m_hasParsingData = false; bool m_needsInitialExpansion = false; + bool m_canBuildProducts = false; std::unique_ptr m_document; std::unique_ptr m_rootProjectNode; std::unique_ptr m_containerNode; @@ -227,6 +228,11 @@ QString Project::mimeType() const return document()->mimeType(); } +bool Project::canBuildProducts() const +{ + return d->m_canBuildProducts; +} + Core::IDocument *Project::document() const { QTC_CHECK(d->m_document); @@ -938,6 +944,11 @@ void Project::setRequiredKitPredicate(const Kit::Predicate &predicate) d->m_requiredKitPredicate = predicate; } +void Project::setCanBuildProducts() +{ + d->m_canBuildProducts = true; +} + Kit::Predicate Project::preferredKitPredicate() const { return d->m_preferredKitPredicate; diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index e66e049c0ca..a7e2ad2a926 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -99,6 +99,7 @@ public: Core::Id id() const; QString mimeType() const; + bool canBuildProducts() const; Core::IDocument *document() const; Utils::FilePath projectFilePath() const; @@ -249,6 +250,8 @@ protected: // The predicate used to select kits available in TargetSetupPage. void setRequiredKitPredicate(const Kit::Predicate &predicate); + void setCanBuildProducts(); + void setId(Core::Id id); void setRootProjectNode(std::unique_ptr &&root); // takes ownership! void setProjectLanguages(Core::Context language); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 8169fab0a0c..bb6f77f0332 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -420,6 +420,7 @@ public: QAction *m_closeAllProjects; QAction *m_buildProjectOnlyAction; Utils::ParameterAction *m_buildAction; + Utils::ParameterAction *m_buildForRunConfigAction; Utils::ProxyAction *m_modeBarBuildAction; QAction *m_buildActionContextMenu; QAction *m_buildDependenciesActionContextMenu; @@ -1043,6 +1044,17 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd->m_modeBarBuildAction->setAction(cmd->action()); ModeManager::addAction(dd->m_modeBarBuildAction, Constants::P_ACTION_BUILDPROJECT); + // build for run config + dd->m_buildForRunConfigAction = new Utils::ParameterAction( + tr("Build for Run Configuration"), tr("Build for Run Configuration \"%1\""), + Utils::ParameterAction::EnabledWithParameter, this); + dd->m_buildForRunConfigAction->setIcon(buildIcon); + cmd = ActionManager::registerAction(dd->m_buildForRunConfigAction, + "ProjectExplorer.BuildForRunConfig"); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDescription(dd->m_buildForRunConfigAction->text()); + mbuild->addAction(cmd, Constants::G_BUILD_BUILD); + // deploy action dd->m_deployAction = new Utils::ParameterAction(tr("Deploy Project"), tr("Deploy Project \"%1\""), Utils::ParameterAction::AlwaysEnabled, this); @@ -1391,6 +1403,21 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(dd->m_buildActionContextMenu, &QAction::triggered, dd, [] { dd->queue({ ProjectTree::currentProject() }, { Id(Constants::BUILDSTEPS_BUILD) }); }); + connect(dd->m_buildForRunConfigAction, &QAction::triggered, dd, [] { + const Project * const project = SessionManager::startupProject(); + QTC_ASSERT(project, return); + const Target * const target = project->activeTarget(); + QTC_ASSERT(target, return); + const RunConfiguration * const runConfig = target->activeRunConfiguration(); + QTC_ASSERT(runConfig, return); + const auto buildKeyMatcher = [runConfig](const ProjectNode *candidate) { + return candidate->buildKey() == runConfig->buildKey(); + }; + ProjectNode * const productNode + = project->rootProjectNode()->findProjectNode(buildKeyMatcher); + QTC_ASSERT(productNode->isProduct(), return); + productNode->build(); + }); connect(dd->m_buildDependenciesActionContextMenu, &QAction::triggered, dd, [] { dd->queue(SessionManager::projectOrder(ProjectTree::currentProject()), { Id(Constants::BUILDSTEPS_BUILD) }); @@ -2436,8 +2463,13 @@ void ProjectExplorerPluginPrivate::updateActions() ? Icons::CANCELBUILD_FLAT.icon() : buildAction->icon()); + const RunConfiguration * const runConfig = project && project->activeTarget() + ? project->activeTarget()->activeRunConfiguration() : nullptr; + // Normal actions m_buildAction->setParameter(projectName); + if (runConfig) + m_buildForRunConfigAction->setParameter(runConfig->displayName()); m_rebuildAction->setParameter(projectName); m_cleanAction->setParameter(projectName); @@ -2445,6 +2477,11 @@ void ProjectExplorerPluginPrivate::updateActions() m_rebuildAction->setEnabled(buildActionState.first); m_cleanAction->setEnabled(buildActionState.first); + // The last condition is there to prevent offering this action for custom run configurations. + m_buildForRunConfigAction->setEnabled(buildActionState.first + && runConfig && project->canBuildProducts() + && !runConfig->buildTargetInfo().projectFilePath.isEmpty()); + m_buildAction->setToolTip(buildActionState.second); m_rebuildAction->setToolTip(buildActionState.second); m_cleanAction->setToolTip(buildActionState.second); @@ -2935,6 +2972,7 @@ void ProjectExplorerPluginPrivate::activeRunConfigurationChanged() rc = startupProject->activeTarget()->activeRunConfiguration(); if (rc == previousRunConfiguration) return; + updateActions(); emit m_instance->updateRunActions(); } diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index e6825e98be9..f8747e529cf 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -212,6 +212,15 @@ const ProjectNode *Node::managingProject() const return const_cast(this)->managingProject(); } +Project *Node::getProject() const +{ + if (const ContainerNode * const cn = asContainerNode()) + return cn->project(); + if (!m_parentFolderNode) + return nullptr; + return m_parentFolderNode->getProject(); +} + /*! The path of the file or folder in the filesystem the node represents. */ diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index ce36882aa9c..8660080a2b3 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -121,6 +121,8 @@ public: // or node->parentProjectNode() for all other cases. const ProjectNode *managingProject() const; // see above. + Project *getProject() const; + const Utils::FilePath &filePath() const; // file system path int line() const; virtual QString displayName() const; @@ -359,6 +361,13 @@ public: bool isProduct() const { return m_isProduct; } + // TODO: Currently used only for "Build for current run config" functionality, but we should + // probably use it to centralize the node-specific "Build" functionality that + // currently each project manager plugin adds to the context menu by itself. + // The function should then move up to the Node class, so it can also serve the + // "build single file" case. + virtual void build() {} + protected: void setIsProduct() { m_isProduct = true; } diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp index e5071cabba1..27aabb710c0 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp @@ -28,6 +28,7 @@ #include "qbsnodetreebuilder.h" #include "qbsproject.h" #include "qbsprojectmanagerconstants.h" +#include "qbsprojectmanagerplugin.h" #include "qbsrunconfiguration.h" #include @@ -391,6 +392,12 @@ bool QbsProductNode::renameFile(const QString &filePath, const QString &newFileP return prjNode->project()->renameFileInProduct(filePath, newFilePath, m_qbsProductData, grp); } +void QbsProductNode::build() +{ + QbsProjectManagerPlugin::buildNamedProduct(static_cast(getProject()), + QbsProject::uniqueProductName(qbsProductData())); +} + QStringList QbsProductNode::targetApplications() const { return QStringList{m_qbsProductData.targetExecutable()}; diff --git a/src/plugins/qbsprojectmanager/qbsnodes.h b/src/plugins/qbsprojectmanager/qbsnodes.h index 3d8f90c3df4..6927c00db24 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.h +++ b/src/plugins/qbsprojectmanager/qbsnodes.h @@ -70,6 +70,7 @@ public: bool addFiles(const QStringList &filePaths, QStringList *notAdded = nullptr) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = nullptr) override; bool renameFile(const QString &filePath, const QString &newFilePath) override; + void build() override; QStringList targetApplications() const override; QString buildKey() const override; diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 209e87b4bca..788d8bc9f95 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -129,6 +129,7 @@ QbsProject::QbsProject(const FilePath &fileName) : setId(Constants::PROJECT_ID); setProjectLanguages(Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + setCanBuildProducts(); rebuildProjectTree(); diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp index f8eb477e649..c30add24d97 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp @@ -578,5 +578,11 @@ void QbsProjectManagerPlugin::reparseProject(QbsProject *project) project->parseCurrentBuildConfiguration(); } +void QbsProjectManagerPlugin::buildNamedProduct(QbsProject *project, const QString &product) +{ + QbsProjectManagerPlugin::runStepsForProducts(project, QStringList(product), + {Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD)}); +} + } // namespace Internal } // namespace QbsProjectManager diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h index f7b37e8d809..267b7777ae3 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h @@ -42,6 +42,10 @@ class QbsProjectManagerPlugin : public ExtensionSystem::IPlugin Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QbsProjectManager.json") +public: + static void buildNamedProduct(QbsProject *project, const QString &product); + +private: ~QbsProjectManagerPlugin() final; bool initialize(const QStringList &arguments, QString *errorMessage) final; @@ -77,8 +81,8 @@ class QbsProjectManagerPlugin : public ExtensionSystem::IPlugin const QStringList &activeFileTags); void buildSingleFile(QbsProject *project, const QString &file); - void runStepsForProducts(QbsProject *project, const QStringList &products, - const QList &stepTypes); + static void runStepsForProducts(QbsProject *project, const QStringList &products, + const QList &stepTypes); QbsProjectManagerPluginPrivate *d = nullptr; QAction *m_reparseQbs = nullptr; diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index a71ab8c3d6a..9370519f39b 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -24,7 +24,9 @@ ****************************************************************************/ #include "qmakenodes.h" + #include "qmakeproject.h" +#include "qmakeprojectmanager.h" #include #include @@ -255,6 +257,11 @@ bool QmakeProFileNode::validParse() const return pro && pro->validParse(); } +void QmakeProFileNode::build() +{ + QmakeManager::buildProduct(getProject(), this); +} + QStringList QmakeProFileNode::targetApplications() const { QStringList apps; diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.h b/src/plugins/qmakeprojectmanager/qmakenodes.h index 92b84fbcb02..b7432bcc14d 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.h +++ b/src/plugins/qmakeprojectmanager/qmakenodes.h @@ -97,6 +97,8 @@ public: bool parseInProgress() const override; bool validParse() const override; + void build() override; + QStringList targetApplications() const override; AddNewInformation addNewInformation(const QStringList &files, Node *context) const override; QVariant data(Core::Id role) const override; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 3b5e4531b6a..8730d534525 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -129,6 +129,7 @@ QmakeProject::QmakeProject(const FilePath &fileName) : setId(Constants::QMAKEPROJECT_ID); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setDisplayName(fileName.toFileInfo().completeBaseName()); + setCanBuildProducts(); const QTextCodec *codec = Core::EditorManager::defaultTextCodec(); m_qmakeVfs->setTextCodec(codec); diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp index adbc1709fb8..fc6f56d6787 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp @@ -194,6 +194,11 @@ void QmakeManager::buildFile() } } +void QmakeManager::buildProduct(Project *project, Node *proFileNode) +{ + handleSubDirContextMenu(BUILD, false, project, proFileNode, nullptr); +} + void QmakeManager::handleSubDirContextMenu(QmakeManager::Action action, bool isFileBuild) { handleSubDirContextMenu(action, diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h index ab163f1a764..722ab075773 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h @@ -62,9 +62,11 @@ public: void buildFileContextMenu(); void buildFile(); + static void buildProduct(ProjectExplorer::Project *project, ProjectExplorer::Node *proFileNode); + private: void handleSubDirContextMenu(Action action, bool isFileBuild); - void handleSubDirContextMenu(QmakeManager::Action action, bool isFileBuild, + static void handleSubDirContextMenu(QmakeManager::Action action, bool isFileBuild, ProjectExplorer::Project *contextProject, ProjectExplorer::Node *contextProFileNode, ProjectExplorer::FileNode *buildableFile);