ProjectExplorer: Let users provide project-specific environment

Add a new project panel where users can set environment variables for
the current project in all configurations.

Fixes: QTCREATORBUG-21862
Change-Id: Id98c7f1cf579927a8004a63172f193f943556f44
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2019-12-10 17:09:49 +01:00
parent 0dbe6f8e3c
commit 6e0d44b0ff
6 changed files with 70 additions and 4 deletions

View File

@@ -116,6 +116,8 @@ BuildConfiguration::BuildConfiguration(Target *target, Core::Id id)
this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged); this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged);
connect(this, &BuildConfiguration::environmentChanged, connect(this, &BuildConfiguration::environmentChanged,
this, &BuildConfiguration::emitBuildDirectoryChanged); this, &BuildConfiguration::emitBuildDirectoryChanged);
connect(target->project(), &Project::environmentChanged,
this, &BuildConfiguration::environmentChanged);
// Many macroexpanders are based on the current project, so they may change the environment: // Many macroexpanders are based on the current project, so they may change the environment:
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged); this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged);
@@ -343,6 +345,7 @@ Utils::Environment BuildConfiguration::baseEnvironment() const
result = Utils::Environment::systemEnvironment(); result = Utils::Environment::systemEnvironment();
addToEnvironment(result); addToEnvironment(result);
target()->kit()->addToEnvironment(result); target()->kit()->addToEnvironment(result);
result.modify(project()->additionalEnvironment());
return result; return result;
} }

View File

@@ -283,7 +283,12 @@ void EnvironmentWidget::setUserChanges(const Utils::EnvironmentItems &list)
void EnvironmentWidget::setOpenTerminalFunc(const EnvironmentWidget::OpenTerminalFunc &func) void EnvironmentWidget::setOpenTerminalFunc(const EnvironmentWidget::OpenTerminalFunc &func)
{ {
d->m_openTerminalFunc = func; d->m_openTerminalFunc = func;
d->m_terminalButton->setEnabled(bool(func)); d->m_terminalButton->setVisible(bool(func));
}
void EnvironmentWidget::expand()
{
d->m_detailsContainer->setState(Utils::DetailsWidget::Expanded);
} }
void EnvironmentWidget::updateSummaryText() void EnvironmentWidget::updateSummaryText()
@@ -294,6 +299,7 @@ void EnvironmentWidget::updateSummaryText()
QString text; QString text;
foreach (const Utils::EnvironmentItem &item, list) { foreach (const Utils::EnvironmentItem &item, list) {
if (item.name != Utils::EnvironmentModel::tr("<VARIABLE>")) { if (item.name != Utils::EnvironmentModel::tr("<VARIABLE>")) {
if (!d->m_baseEnvironmentText.isEmpty() || !text.isEmpty())
text.append(QLatin1String("<br>")); text.append(QLatin1String("<br>"));
switch (item.operation) { switch (item.operation) {
case Utils::EnvironmentItem::Unset: case Utils::EnvironmentItem::Unset:
@@ -313,10 +319,14 @@ void EnvironmentWidget::updateSummaryText()
if (text.isEmpty()) { if (text.isEmpty()) {
//: %1 is "System Environment" or some such. //: %1 is "System Environment" or some such.
if (!d->m_baseEnvironmentText.isEmpty())
text.prepend(tr("Use <b>%1</b>").arg(d->m_baseEnvironmentText)); text.prepend(tr("Use <b>%1</b>").arg(d->m_baseEnvironmentText));
else
text.prepend(tr("<b>No environment changes</b>"));
} else { } else {
//: Yup, word puzzle. The Set/Unset phrases above are appended to this. //: Yup, word puzzle. The Set/Unset phrases above are appended to this.
//: %1 is "System Environment" or some such. //: %1 is "System Environment" or some such.
if (!d->m_baseEnvironmentText.isEmpty())
text.prepend(tr("Use <b>%1</b> and").arg(d->m_baseEnvironmentText)); text.prepend(tr("Use <b>%1</b> and").arg(d->m_baseEnvironmentText));
} }

View File

@@ -59,6 +59,8 @@ public:
using OpenTerminalFunc = std::function<void(const Utils::Environment &env)>; using OpenTerminalFunc = std::function<void(const Utils::Environment &env)>;
void setOpenTerminalFunc(const OpenTerminalFunc &func); void setOpenTerminalFunc(const OpenTerminalFunc &func);
void expand();
signals: signals:
void userChangesChanged(); void userChangesChanged();
void detailsVisibleChanged(bool visible); void detailsVisibleChanged(bool visible);

View File

@@ -90,6 +90,8 @@ const char TARGET_KEY_PREFIX[] = "ProjectExplorer.Project.Target.";
const char TARGET_COUNT_KEY[] = "ProjectExplorer.Project.TargetCount"; const char TARGET_COUNT_KEY[] = "ProjectExplorer.Project.TargetCount";
const char EDITOR_SETTINGS_KEY[] = "ProjectExplorer.Project.EditorSettings"; const char EDITOR_SETTINGS_KEY[] = "ProjectExplorer.Project.EditorSettings";
const char PLUGIN_SETTINGS_KEY[] = "ProjectExplorer.Project.PluginSettings"; const char PLUGIN_SETTINGS_KEY[] = "ProjectExplorer.Project.PluginSettings";
const char PROJECT_ENV_KEY[] = "ProjectExplorer.Project.Environment";
} // namespace } // namespace
namespace ProjectExplorer { namespace ProjectExplorer {
@@ -867,6 +869,17 @@ void Project::setNamedSettings(const QString &name, const QVariant &value)
d->m_pluginSettings.insert(name, value); d->m_pluginSettings.insert(name, value);
} }
void Project::setAdditionalEnvironment(const Utils::EnvironmentItems &envItems)
{
setNamedSettings(PROJECT_ENV_KEY, Utils::NameValueItem::toStringList(envItems));
emit environmentChanged();
}
Utils::EnvironmentItems Project::additionalEnvironment() const
{
return Utils::NameValueItem::fromStringList(namedSettings(PROJECT_ENV_KEY).toStringList());
}
bool Project::needsConfiguration() const bool Project::needsConfiguration() const
{ {
return d->m_targets.size() == 0; return d->m_targets.size() == 0;

View File

@@ -33,6 +33,7 @@
#include <coreplugin/id.h> #include <coreplugin/id.h>
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
#include <utils/environmentfwd.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <QObject> #include <QObject>
@@ -134,6 +135,9 @@ public:
QVariant namedSettings(const QString &name) const; QVariant namedSettings(const QString &name) const;
void setNamedSettings(const QString &name, const QVariant &value); void setNamedSettings(const QString &name, const QVariant &value);
void setAdditionalEnvironment(const Utils::EnvironmentItems &envItems);
Utils::EnvironmentItems additionalEnvironment() const;
virtual bool needsConfiguration() const; virtual bool needsConfiguration() const;
bool needsBuildConfigurations() const; bool needsBuildConfigurations() const;
virtual void configureAsExampleProject(); virtual void configureAsExampleProject();
@@ -174,6 +178,7 @@ signals:
void displayNameChanged(); void displayNameChanged();
void fileListChanged(); void fileListChanged();
void environmentChanged();
// Note: activeTarget can be 0 (if no targets are defined). // Note: activeTarget can be 0 (if no targets are defined).
void activeTargetChanged(ProjectExplorer::Target *target); void activeTargetChanged(ProjectExplorer::Target *target);

View File

@@ -37,6 +37,7 @@
#include "deployablefile.h" #include "deployablefile.h"
#include "deployconfiguration.h" #include "deployconfiguration.h"
#include "desktoprunconfiguration.h" #include "desktoprunconfiguration.h"
#include "environmentwidget.h"
#include "extraabi.h" #include "extraabi.h"
#include "gcctoolchainfactories.h" #include "gcctoolchainfactories.h"
#ifdef WITH_JOURNALD #ifdef WITH_JOURNALD
@@ -45,6 +46,7 @@
#include "jsonwizard/jsonwizardfactory.h" #include "jsonwizard/jsonwizardfactory.h"
#include "jsonwizard/jsonwizardgeneratorfactory.h" #include "jsonwizard/jsonwizardgeneratorfactory.h"
#include "jsonwizard/jsonwizardpagefactory_p.h" #include "jsonwizard/jsonwizardpagefactory_p.h"
#include "namedwidget.h"
#include "project.h" #include "project.h"
#include "projectexplorersettings.h" #include "projectexplorersettings.h"
#include "projectexplorersettingspage.h" #include "projectexplorersettingspage.h"
@@ -351,6 +353,29 @@ public:
} }
}; };
class ProjectEnvironmentWidget : public NamedWidget
{
Q_DECLARE_TR_FUNCTIONS(ProjectEnvironmentWidget)
public:
explicit ProjectEnvironmentWidget(Project *project) : NamedWidget(tr("Project Environment"))
{
const auto vbox = new QVBoxLayout(this);
vbox->setContentsMargins(0, 0, 0, 0);
const auto envWidget = new EnvironmentWidget(this, EnvironmentWidget::TypeLocal);
envWidget->setOpenTerminalFunc({});
envWidget->expand();
vbox->addWidget(envWidget);
connect(envWidget, &EnvironmentWidget::userChangesChanged,
this, [project, envWidget] {
project->setAdditionalEnvironment(envWidget->userChanges());
});
envWidget->setUserChanges(project->additionalEnvironment());
}
};
class ProjectExplorerPluginPrivate : public QObject class ProjectExplorerPluginPrivate : public QObject
{ {
Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ProjectExplorerPlugin) Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ProjectExplorerPlugin)
@@ -775,6 +800,14 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
panelFactory->setCreateWidgetFunction([](Project *project) { return new DependenciesWidget(project); }); panelFactory->setCreateWidgetFunction([](Project *project) { return new DependenciesWidget(project); });
ProjectPanelFactory::registerFactory(panelFactory); ProjectPanelFactory::registerFactory(panelFactory);
panelFactory = new ProjectPanelFactory;
panelFactory->setPriority(60);
panelFactory->setDisplayName(QCoreApplication::translate("EnvironmentPanelFactory", "Environment"));
panelFactory->setCreateWidgetFunction([](Project *project) {
return new ProjectEnvironmentWidget(project);
});
ProjectPanelFactory::registerFactory(panelFactory);
// context menus // context menus
ActionContainer *msessionContextMenu = ActionContainer *msessionContextMenu =
ActionManager::createMenu(Constants::M_SESSIONCONTEXT); ActionManager::createMenu(Constants::M_SESSIONCONTEXT);