Skip to content

Commit

Permalink
ProjectExplorer: Optionally deploy dependent projects
Browse files Browse the repository at this point in the history
... rather than just building them.

Fixes: QTCREATORBUG-27406
Change-Id: I5ddbb9ee4a05c6843bd78cf01746c57c6cacda3c
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
  • Loading branch information
ckandeler committed Jun 28, 2024
1 parent 691c4ad commit d79d4f2
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 49 deletions.
138 changes: 89 additions & 49 deletions src/plugins/projectexplorer/buildmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
#include <QVBoxLayout>
#include <QVariant>

#include <utility>

using namespace Core;
using namespace Tasking;
using namespace Utils;
Expand Down Expand Up @@ -202,20 +204,46 @@ static const QList<BuildConfiguration *> buildConfigsForSelection(const Target *
return {};
}

static int queue(const QList<Project *> &projects, const QList<Id> &stepIds,
ConfigSelection configSelection, const RunConfiguration *forRunConfig = nullptr,
RunControl *starter = nullptr)
using ProjectAndStepIds = std::pair<Project *, QList<Id>>;
using ProjectsAndStepIds = QList<ProjectAndStepIds>;
static ProjectsAndStepIds projectWithDependencies(
const Project *mainProject, const QList<Id> &mainStepIds)
{
QList<Id> depStepIds = mainStepIds;
if (ProjectManager::deployProjectDependencies()
&& depStepIds.contains(Constants::BUILDSTEPS_BUILD)
&& !depStepIds.contains(Constants::BUILDSTEPS_DEPLOY)) {
depStepIds << Constants::BUILDSTEPS_DEPLOY;
}
ProjectsAndStepIds result
= Utils::transform(ProjectManager::projectOrder(mainProject), [&](Project *p) {
return std::make_pair(p, depStepIds);
});

// Shouldn't be necessary, but see the weird check at the end of
// ProjectManagerPrivate::dependencies().
if (QTC_GUARD(result.last().first == mainProject))
result.last().second = mainStepIds;

return result;
}

static int queue(
const ProjectsAndStepIds &projectsAndStepIds,
ConfigSelection configSelection,
const RunConfiguration *forRunConfig = nullptr,
RunControl *starter = nullptr)
{
if (!ProjectExplorerPlugin::saveModifiedFiles())
return -1;

const StopBeforeBuild stopBeforeBuild = projectExplorerSettings().stopBeforeBuild;
if (stopBeforeBuild != StopBeforeBuild::None
&& stepIds.contains(Constants::BUILDSTEPS_BUILD)) {
if (stopBeforeBuild != StopBeforeBuild::None && !projectsAndStepIds.isEmpty()
&& projectsAndStepIds.last().second.contains(Constants::BUILDSTEPS_BUILD)) {
StopBeforeBuild stopCondition = stopBeforeBuild;
if (stopCondition == StopBeforeBuild::SameApp && !forRunConfig)
stopCondition = StopBeforeBuild::SameBuildDir;
const auto isStoppableRc = [&projects, stopCondition, configSelection, forRunConfig,
const auto isStoppableRc = [&projectsAndStepIds, stopCondition, configSelection, forRunConfig,
starter](RunControl *rc) {
if (rc == starter)
return false;
Expand All @@ -228,24 +256,27 @@ static int queue(const QList<Project *> &projects, const QList<Id> &stepIds,
case StopBeforeBuild::All:
return true;
case StopBeforeBuild::SameProject:
return projects.contains(rc->project());
return Utils::contains(projectsAndStepIds, [rc](const ProjectAndStepIds &p) {
return p.first == rc->project();
});
case StopBeforeBuild::SameBuildDir:
return Utils::contains(projects, [rc, configSelection](Project *p) {
const FilePath executable = rc->commandLine().executable();
IDevice::ConstPtr device = DeviceManager::deviceForPath(executable);
for (const Target * const t : targetsForSelection(p, configSelection)) {
if (!device)
device = DeviceKitAspect::device(t->kit());
if (!device || device->type() != Constants::DESKTOP_DEVICE_TYPE)
continue;
for (const BuildConfiguration * const bc
: buildConfigsForSelection(t, configSelection)) {
if (executable.isChildOf(bc->buildDirectory()))
return true;
return Utils::contains(
projectsAndStepIds, [rc, configSelection](const ProjectAndStepIds &p) {
const FilePath executable = rc->commandLine().executable();
IDevice::ConstPtr device = DeviceManager::deviceForPath(executable);
for (const Target *const t : targetsForSelection(p.first, configSelection)) {
if (!device)
device = DeviceKitAspect::device(t->kit());
if (!device || device->type() != Constants::DESKTOP_DEVICE_TYPE)
continue;
for (const BuildConfiguration *const bc :
buildConfigsForSelection(t, configSelection)) {
if (executable.isChildOf(bc->buildDirectory()))
return true;
}
}
}
return false;
});
return false;
});
case StopBeforeBuild::SameApp:
QTC_ASSERT(forRunConfig, return false);
return forRunConfig->buildTargetInfo().targetFilePath
Expand Down Expand Up @@ -285,36 +316,39 @@ static int queue(const QList<Project *> &projects, const QList<Id> &stepIds,
QList<BuildStepList *> stepLists;
QStringList preambleMessage;

for (const Project *pro : projects) {
if (pro && pro->needsConfiguration()) {
for (const ProjectAndStepIds &p : projectsAndStepIds) {
if (p.first && p.first->needsConfiguration()) {
preambleMessage.append(
Tr::tr("The project %1 is not configured, skipping it.")
.arg(pro->displayName()) + QLatin1Char('\n'));
.arg(p.first->displayName()) + QLatin1Char('\n'));
}
}
for (const Project *pro : projects) {
for (const Target *const t : targetsForSelection(pro, configSelection)) {
for (const ProjectAndStepIds &p : projectsAndStepIds) {
for (const Target *const t : targetsForSelection(p.first, configSelection)) {
for (const BuildConfiguration *bc : buildConfigsForSelection(t, configSelection)) {
const IDevice::Ptr device = std::const_pointer_cast<IDevice>(
BuildDeviceKitAspect::device(bc->kit()));
if (device && !device->prepareForBuild(t)) {
preambleMessage.append(
Tr::tr("The build device failed to prepare for the build of %1 (%2).")
.arg(pro->displayName())
.arg(p.first->displayName())
.arg(t->displayName())
+ QLatin1Char('\n'));
}
}
}
}

for (const Id id : stepIds) {
const bool isBuild = id == Constants::BUILDSTEPS_BUILD;
const bool isClean = id == Constants::BUILDSTEPS_CLEAN;
const bool isDeploy = id == Constants::BUILDSTEPS_DEPLOY;
for (const Project *pro : projects) {
if (!pro || pro->needsConfiguration())
continue;
for (const ProjectAndStepIds &p : projectsAndStepIds) {
const Project * const pro = p.first;
if (!pro || pro->needsConfiguration())
continue;

for (const Id id : p.second) {
const bool isBuild = id == Constants::BUILDSTEPS_BUILD;
const bool isClean = id == Constants::BUILDSTEPS_CLEAN;
const bool isDeploy = id == Constants::BUILDSTEPS_DEPLOY;

BuildStepList *bsl = nullptr;
for (const Target * target : targetsForSelection(pro, configSelection)) {
if (isBuild || isClean) {
Expand Down Expand Up @@ -462,54 +496,60 @@ void BuildManager::extensionsInitialized()

void BuildManager::buildProjectWithoutDependencies(Project *project)
{
queue({project}, {Id(Constants::BUILDSTEPS_BUILD)}, ConfigSelection::Active);
queue({std::make_pair(project, QList<Id>{Constants::BUILDSTEPS_BUILD})}, ConfigSelection::Active);
}

void BuildManager::cleanProjectWithoutDependencies(Project *project)
{
queue({project}, {Id(Constants::BUILDSTEPS_CLEAN)}, ConfigSelection::Active);
queue({std::make_pair(project, QList<Id>{Constants::BUILDSTEPS_CLEAN})}, ConfigSelection::Active);
}

void BuildManager::rebuildProjectWithoutDependencies(Project *project)
{
queue({project}, {Id(Constants::BUILDSTEPS_CLEAN), Id(Constants::BUILDSTEPS_BUILD)},
queue({std::make_pair(project, QList<Id>{Constants::BUILDSTEPS_CLEAN, Constants::BUILDSTEPS_BUILD})},
ConfigSelection::Active);
}

void BuildManager::buildProjectWithDependencies(Project *project, ConfigSelection configSelection,
RunControl *starter)
{
queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)},
queue(projectWithDependencies(project, {Id(Constants::BUILDSTEPS_BUILD)}),
configSelection, nullptr, starter);
}

void BuildManager::cleanProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)},
configSelection);
queue(projectWithDependencies(project, {Id(Constants::BUILDSTEPS_CLEAN)}), configSelection);
}

void BuildManager::rebuildProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
queue(ProjectManager::projectOrder(project),
{Id(Constants::BUILDSTEPS_CLEAN), Id(Constants::BUILDSTEPS_BUILD)},
configSelection);
queue(
projectWithDependencies(
project, QList<Id>{Constants::BUILDSTEPS_CLEAN, Constants::BUILDSTEPS_BUILD}),
configSelection);
}

static ProjectsAndStepIds projectsWithStepIds(
const QList<Project *> &projects, const QList<Id> &stepIds)
{
return Utils::transform(projects, [&](Project *p) { return std::make_pair(p, stepIds); });
}

void BuildManager::buildProjects(const QList<Project *> &projects, ConfigSelection configSelection)
{
queue(projects, {Id(Constants::BUILDSTEPS_BUILD)}, configSelection);
queue(projectsWithStepIds(projects, {Constants::BUILDSTEPS_BUILD}), configSelection);
}

void BuildManager::cleanProjects(const QList<Project *> &projects, ConfigSelection configSelection)
{
queue(projects, {Id(Constants::BUILDSTEPS_CLEAN)}, configSelection);
queue(projectsWithStepIds(projects, {Constants::BUILDSTEPS_CLEAN}), configSelection);
}

void BuildManager::rebuildProjects(const QList<Project *> &projects,
ConfigSelection configSelection)
{
queue(projects, {Id(Constants::BUILDSTEPS_CLEAN), Id(Constants::BUILDSTEPS_BUILD)},
queue(projectsWithStepIds(projects, {Constants::BUILDSTEPS_CLEAN, Constants::BUILDSTEPS_BUILD}),
configSelection);
}

Expand All @@ -519,7 +559,7 @@ void BuildManager::deployProjects(const QList<Project *> &projects)
if (projectExplorerSettings().buildBeforeDeploy != BuildBeforeRunMode::Off)
steps << Id(Constants::BUILDSTEPS_BUILD);
steps << Id(Constants::BUILDSTEPS_DEPLOY);
queue(projects, steps, ConfigSelection::Active);
queue(projectsWithStepIds(projects, steps), ConfigSelection::Active);
}

BuildForRunConfigStatus BuildManager::potentiallyBuildForRunConfig(RunConfiguration *rc)
Expand All @@ -544,7 +584,7 @@ BuildForRunConfigStatus BuildManager::potentiallyBuildForRunConfig(RunConfigurat
}

Project * const pro = rc->target()->project();
const int queueCount = queue(ProjectManager::projectOrder(pro), stepIds,
const int queueCount = queue(projectWithDependencies(pro, stepIds),
ConfigSelection::Active, rc);
if (rc->target()->activeBuildConfiguration())
rc->target()->activeBuildConfiguration()->restrictNextBuild(nullptr);
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/projectexplorer/dependenciespanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,21 @@ class DependenciesWidget : public ProjectSettingsWidget
connect(m_cascadeSetActiveCheckBox, &QCheckBox::toggled,
ProjectManager::instance(), &ProjectManager::setProjectConfigurationCascading);
layout->addWidget(m_cascadeSetActiveCheckBox, 1, 0, 2, 1);
m_deployCheckBox = new QCheckBox;
m_deployCheckBox->setText(Tr::tr("Deploy dependencies"));
m_deployCheckBox->setToolTip(
Tr::tr("Do not just build dependencies, but deploy them as well."));
m_deployCheckBox->setChecked(ProjectManager::deployProjectDependencies());
connect(m_deployCheckBox, &QCheckBox::toggled,
ProjectManager::instance(), &ProjectManager::setDeployProjectDependencies);
layout->addWidget(m_deployCheckBox, 3, 0, 2, 1);
}

private:
DependenciesModel m_model;
Utils::DetailsWidget *m_detailsContainer;
QCheckBox *m_cascadeSetActiveCheckBox;
QCheckBox *m_deployCheckBox;
};

class DependenciesProjectPanelFactory final : public ProjectPanelFactory
Expand Down
16 changes: 16 additions & 0 deletions src/plugins/projectexplorer/projectmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ProjectManagerPrivate
bool hasProjects() const { return !m_projects.isEmpty(); }

bool m_casadeSetActive = false;
bool m_deployProjectDependencies = false;

Project *m_startupProject = nullptr;
QList<Project *> m_projects;
Expand Down Expand Up @@ -227,6 +228,17 @@ void ProjectManager::setProjectConfigurationCascading(bool b)
SessionManager::markSessionFileDirty();
}

bool ProjectManager::deployProjectDependencies()
{
return d->m_deployProjectDependencies;
}

void ProjectManager::setDeployProjectDependencies(bool deploy)
{
d->m_deployProjectDependencies = deploy;
SessionManager::markSessionFileDirty();
}

void ProjectManager::setStartupProject(Project *startupProject)
{
QTC_ASSERT((!startupProject && d->m_projects.isEmpty())
Expand Down Expand Up @@ -337,6 +349,7 @@ void ProjectManagerPrivate::saveSession()
Utils::transform<QStringList>(projectFiles,
&FilePath::toString));
SessionManager::setSessionValue("CascadeSetActive", m_casadeSetActive);
SessionManager::setSessionValue("DeployProjectDependencies", m_deployProjectDependencies);

QVariantMap depMap;
auto i = m_depMap.constBegin();
Expand Down Expand Up @@ -667,6 +680,7 @@ void ProjectManagerPrivate::loadSession()
d->m_failedProjects.clear();
d->m_depMap.clear();
d->m_casadeSetActive = false;
d->m_deployProjectDependencies = false;

// not ideal that this is in ProjectManager
Id modeId = Id::fromSetting(SessionManager::value("ActiveMode"));
Expand Down Expand Up @@ -705,6 +719,8 @@ void ProjectManagerPrivate::loadSession()
ModeManager::setFocusToCurrentMode();

d->m_casadeSetActive = SessionManager::sessionValue("CascadeSetActive", false).toBool();
d->m_deployProjectDependencies
= SessionManager::sessionValue("DeployProjectDependencies", false).toBool();

// Starts a event loop, better do that at the very end
QMetaObject::invokeMethod(m_instance, [this] { askUserAboutFailedProjects(); });
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/projectexplorer/projectmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class PROJECTEXPLORER_EXPORT ProjectManager : public QObject
static bool isProjectConfigurationCascading();
static void setProjectConfigurationCascading(bool b);

static bool deployProjectDependencies();
static void setDeployProjectDependencies(bool deploy);

static Project *startupProject();
static Target *startupTarget();
static BuildSystem *startupBuildSystem();
Expand Down

0 comments on commit d79d4f2

Please sign in to comment.