ProjectExplorer: Support changing the run environment

... in EnvironmentKitAspect.

Fixes: QTCREATORBUG-31906
Change-Id: I11faf955ca6f7a7e01563cbf408bcfd9b2319e0a
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-11-20 12:16:17 +01:00
parent cb23332ae5
commit 7d858ff8aa
8 changed files with 141 additions and 64 deletions

View File

@@ -8,14 +8,18 @@
namespace Utils { namespace Utils {
std::optional<EnvironmentItems> EnvironmentDialog::getEnvironmentItems( std::optional<EnvironmentItems> EnvironmentDialog::getEnvironmentItems(
QWidget *parent, const EnvironmentItems &initial, const QString &placeholderText, Polisher polisher) QWidget *parent,
const EnvironmentItems &initial,
const QString &placeholderText,
Polisher polisher,
const QString &dialogTitle)
{ {
return getNameValueItems( return getNameValueItems(
parent, parent,
initial, initial,
placeholderText, placeholderText,
polisher, polisher,
Tr::tr("Edit Environment")); dialogTitle.isEmpty() ? Tr::tr("Edit Environment") : dialogTitle);
} }
} // namespace Utils } // namespace Utils

View File

@@ -13,10 +13,12 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public NameValuesDialog class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public NameValuesDialog
{ {
public: public:
static std::optional<EnvironmentItems> getEnvironmentItems(QWidget *parent = nullptr, static std::optional<EnvironmentItems> getEnvironmentItems(
QWidget *parent = nullptr,
const EnvironmentItems &initial = {}, const EnvironmentItems &initial = {},
const QString &placeholderText = {}, const QString &placeholderText = {},
Polisher polish = {}); Polisher polish = {},
const QString &dialogTitle = {});
}; };
} // namespace Utils } // namespace Utils

View File

@@ -247,7 +247,7 @@ public:
if (McuSupportOptions::kitsNeedQtVersion()) if (McuSupportOptions::kitsNeedQtVersion())
changes.append({QLatin1String("LD_LIBRARY_PATH"), "%{Qt:QT_INSTALL_LIBS}"}); changes.append({QLatin1String("LD_LIBRARY_PATH"), "%{Qt:QT_INSTALL_LIBS}"});
EnvironmentKitAspect::setEnvironmentChanges(k, changes); EnvironmentKitAspect::setBuildEnvChanges(k, changes);
} }
static void setKitCMakeOptions(Kit *k, static void setKitCMakeOptions(Kit *k,

View File

@@ -5,6 +5,7 @@
#include "buildconfiguration.h" #include "buildconfiguration.h"
#include "environmentaspectwidget.h" #include "environmentaspectwidget.h"
#include "environmentkitaspect.h"
#include "kit.h" #include "kit.h"
#include "projectexplorer.h" #include "projectexplorer.h"
#include "projectexplorersettings.h" #include "projectexplorersettings.h"
@@ -30,8 +31,11 @@ EnvironmentAspect::EnvironmentAspect(AspectContainer *container)
setId("EnvironmentAspect"); setId("EnvironmentAspect");
setConfigWidgetCreator([this] { return new EnvironmentAspectWidget(this); }); setConfigWidgetCreator([this] { return new EnvironmentAspectWidget(this); });
addDataExtractor(this, &EnvironmentAspect::environment, &Data::environment); addDataExtractor(this, &EnvironmentAspect::environment, &Data::environment);
if (qobject_cast<RunConfiguration *>(container)) { if (const auto runConfig = qobject_cast<RunConfiguration *>(container)) {
addModifier([](Environment &env) { env.modify(projectExplorerSettings().appEnvChanges); }); addModifier([runConfig](Environment &env) {
env.modify(projectExplorerSettings().appEnvChanges);
env.modify(EnvironmentKitAspect::runEnvChanges(runConfig->kit()));
});
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
this, &EnvironmentAspect::environmentChanged); this, &EnvironmentAspect::environmentChanged);
} }

View File

@@ -10,7 +10,6 @@
#include "toolchain.h" #include "toolchain.h"
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/elidinglabel.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/environmentdialog.h> #include <utils/environmentdialog.h>
#include <utils/guard.h> #include <utils/guard.h>
@@ -23,8 +22,8 @@
#include <utils/variablechooser.h> #include <utils/variablechooser.h>
#include <QCheckBox> #include <QCheckBox>
#include <QHBoxLayout>
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout>
using namespace Utils; using namespace Utils;
@@ -41,57 +40,76 @@ static bool enforcesMSVCEnglish(const EnvironmentItems &changes)
return changes.contains(forceMSVCEnglishItem()); return changes.contains(forceMSVCEnglishItem());
} }
static Id buildEnvId() { return "PE.Profile.Environment"; }
static Id runEnvId() { return "PE.Profile.RunEnvironment"; }
namespace Internal { namespace Internal {
class EnvironmentKitAspectImpl final : public KitAspect class EnvironmentKitAspectImpl final : public KitAspect
{ {
public: public:
EnvironmentKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) EnvironmentKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory)
: KitAspect(workingCopy, factory), : KitAspect(workingCopy, factory),
m_summaryLabel(createSubWidget<ElidingLabel>()), m_mainWidget(createSubWidget<QWidget>()),
m_manageButton(createSubWidget<QPushButton>()), m_buildEnvButton(createSubWidget<QPushButton>()),
m_mainWidget(createSubWidget<QWidget>()) m_runEnvButton(createSubWidget<QPushButton>())
{ {
auto *layout = new QVBoxLayout; addMutableAction(m_mainWidget);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_summaryLabel);
if (HostOsInfo::isWindowsHost()) if (HostOsInfo::isWindowsHost())
initMSVCOutputSwitch(layout); initMSVCOutputSwitch();
m_mainWidget->setLayout(layout);
refresh(); refresh();
m_manageButton->setText(Tr::tr("Change...")); m_buildEnvButton->setText(Tr::tr("Edit Build Environment..."));
connect(m_manageButton, &QAbstractButton::clicked, m_buildEnvButton->setIcon({});
this, &EnvironmentKitAspectImpl::editEnvironmentChanges); m_runEnvButton->setText(Tr::tr("Edit Run Environment..."));
connect(m_buildEnvButton, &QAbstractButton::clicked,
this, &EnvironmentKitAspectImpl::editBuildEnvironmentChanges);
connect(m_runEnvButton, &QAbstractButton::clicked,
this, &EnvironmentKitAspectImpl::editRunEnvironmentChanges);
} }
private: private:
void addToInnerLayout(Layouting::Layout &layout) override void addToInnerLayout(Layouting::Layout &layout) override
{ {
addMutableAction(m_mainWidget); Layouting::Layout box(new QHBoxLayout);
box.setContentsMargins(0, 0, 0, 0);
box.attachTo(m_mainWidget);
if (m_vslangCheckbox)
box.addItem(m_vslangCheckbox);
box.addItems({m_buildEnvButton, m_runEnvButton});
box.addItem(Layouting::Stretch(1));
layout.addItem(m_mainWidget); layout.addItem(m_mainWidget);
layout.addItem(m_manageButton);
} }
void makeReadOnly() override { m_manageButton->setEnabled(false); } void makeReadOnly() override
{
if (m_vslangCheckbox)
m_vslangCheckbox->setEnabled(false);
m_buildEnvButton->setEnabled(false);
m_runEnvButton->setEnabled(false);
}
void refresh() override void refresh() override
{ {
const EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(kit()); const auto toString = [](const EnvironmentItems &changes) {
const QString shortSummary = EnvironmentItem::toStringList(changes).join("; "); return EnvironmentItem::toStringList(changes).join("\n");
m_summaryLabel->setText(shortSummary.isEmpty() ? Tr::tr("No changes to apply.") : shortSummary); };
m_buildEnvButton->setToolTip(toString(EnvironmentKitAspect::buildEnvChanges(kit())));
m_runEnvButton->setToolTip(toString(EnvironmentKitAspect::runEnvChanges(kit())));
// TODO: Set an icon on the button representing whether there are changes or not.
} }
void editEnvironmentChanges() void editBuildEnvironmentChanges()
{ {
MacroExpander *expander = kit()->macroExpander();
EnvironmentDialog::Polisher polisher = [expander](QWidget *w) {
VariableChooser::addSupportForChildWidgets(w, expander);
};
auto changes = EnvironmentDialog::getEnvironmentItems( auto changes = EnvironmentDialog::getEnvironmentItems(
m_summaryLabel, EnvironmentKitAspect::environmentChanges(kit()), QString(), polisher); m_mainWidget,
EnvironmentKitAspect::buildEnvChanges(kit()),
QString(),
polisher(),
Tr::tr("Edit Build Environment"));
if (!changes) if (!changes)
return; return;
if (HostOsInfo::isWindowsHost()) { if (m_vslangCheckbox) {
// re-add what envWithoutMSVCEnglishEnforcement removed // re-add what envWithoutMSVCEnglishEnforcement removed
// or update vslang checkbox if user added it manually // or update vslang checkbox if user added it manually
if (m_vslangCheckbox->isChecked() && !enforcesMSVCEnglish(*changes)) if (m_vslangCheckbox->isChecked() && !enforcesMSVCEnglish(*changes))
@@ -99,33 +117,51 @@ private:
else if (enforcesMSVCEnglish(*changes)) else if (enforcesMSVCEnglish(*changes))
m_vslangCheckbox->setChecked(true); m_vslangCheckbox->setChecked(true);
} }
EnvironmentKitAspect::setEnvironmentChanges(kit(), *changes); EnvironmentKitAspect::setBuildEnvChanges(kit(), *changes);
} }
void initMSVCOutputSwitch(QVBoxLayout *layout) void editRunEnvironmentChanges()
{ {
m_vslangCheckbox = new QCheckBox(Tr::tr("Force UTF-8 MSVC compiler output")); if (const auto changes = EnvironmentDialog::getEnvironmentItems(
layout->addWidget(m_vslangCheckbox); m_mainWidget,
EnvironmentKitAspect::runEnvChanges(kit()),
QString(),
polisher(),
Tr::tr("Edit Run Environment"))) {
EnvironmentKitAspect::setRunEnvChanges(kit(), *changes);
}
}
EnvironmentDialog::Polisher polisher() const
{
return [expander = kit()->macroExpander()](QWidget *w) {
VariableChooser::addSupportForChildWidgets(w, expander);
};
}
void initMSVCOutputSwitch()
{
m_vslangCheckbox = new QCheckBox(Tr::tr("Force UTF-8 MSVC output"));
m_vslangCheckbox->setToolTip(Tr::tr("Either switches MSVC to English or keeps the language and " m_vslangCheckbox->setToolTip(Tr::tr("Either switches MSVC to English or keeps the language and "
"just forces UTF-8 output (may vary depending on the used MSVC " "just forces UTF-8 output (may vary depending on the used MSVC "
"compiler).")); "compiler)."));
m_vslangCheckbox->setChecked( m_vslangCheckbox->setChecked(
enforcesMSVCEnglish(EnvironmentKitAspect::environmentChanges(kit()))); enforcesMSVCEnglish(EnvironmentKitAspect::buildEnvChanges(kit())));
connect(m_vslangCheckbox, &QCheckBox::clicked, this, [this](bool checked) { connect(m_vslangCheckbox, &QCheckBox::clicked, this, [this](bool checked) {
EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(kit()); EnvironmentItems changes = EnvironmentKitAspect::buildEnvChanges(kit());
const bool hasVsLangEntry = enforcesMSVCEnglish(changes); const bool hasVsLangEntry = enforcesMSVCEnglish(changes);
if (checked && !hasVsLangEntry) if (checked && !hasVsLangEntry)
changes.append(forceMSVCEnglishItem()); changes.append(forceMSVCEnglishItem());
else if (!checked && hasVsLangEntry) else if (!checked && hasVsLangEntry)
changes.removeAll(forceMSVCEnglishItem()); changes.removeAll(forceMSVCEnglishItem());
EnvironmentKitAspect::setEnvironmentChanges(kit(), changes); EnvironmentKitAspect::setBuildEnvChanges(kit(), changes);
}); });
} }
ElidingLabel *m_summaryLabel; QWidget * const m_mainWidget;
QPushButton *m_manageButton; QPushButton * const m_buildEnvButton;
QCheckBox *m_vslangCheckbox; QPushButton * const m_runEnvButton;
QWidget *m_mainWidget; QCheckBox *m_vslangCheckbox = nullptr;
}; };
class EnvironmentKitAspectFactory : public KitAspectFactory class EnvironmentKitAspectFactory : public KitAspectFactory
@@ -157,7 +193,7 @@ Tasks EnvironmentKitAspectFactory::validate(const Kit *k) const
Tasks result; Tasks result;
QTC_ASSERT(k, return result); QTC_ASSERT(k, return result);
const QVariant variant = k->value(EnvironmentKitAspect::id()); const QVariant variant = k->value(buildEnvId());
if (!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) if (!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList)))
result << BuildSystemTask(Task::Error, Tr::tr("The environment setting value is invalid.")); result << BuildSystemTask(Task::Error, Tr::tr("The environment setting value is invalid."));
@@ -168,24 +204,32 @@ void EnvironmentKitAspectFactory::fix(Kit *k)
{ {
QTC_ASSERT(k, return); QTC_ASSERT(k, return);
const QVariant variant = k->value(EnvironmentKitAspect::id()); if (const QVariant variant = k->value(buildEnvId());
if (!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) { !variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) {
qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName())); qWarning("Kit \"%s\" has a wrong build environment value set.", qPrintable(k->displayName()));
EnvironmentKitAspect::setEnvironmentChanges(k, EnvironmentItems()); EnvironmentKitAspect::setBuildEnvChanges(k, EnvironmentItems());
}
if (const QVariant variant = k->value(runEnvId());
!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) {
qWarning("Kit \"%s\" has a wrong run environment value set.", qPrintable(k->displayName()));
EnvironmentKitAspect::setRunEnvChanges(k, EnvironmentItems());
} }
} }
void EnvironmentKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const void EnvironmentKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const
{ {
const QStringList values const QStringList values
= transform(EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)), = transform(EnvironmentItem::toStringList(EnvironmentKitAspect::buildEnvChanges(k)),
[k](const QString &v) { return k->macroExpander()->expand(v); }); [k](const QString &v) { return k->macroExpander()->expand(v); });
env.modify(EnvironmentItem::fromStringList(values)); env.modify(EnvironmentItem::fromStringList(values));
} }
void EnvironmentKitAspectFactory::addToRunEnvironment(const Kit *k, Environment &env) const void EnvironmentKitAspectFactory::addToRunEnvironment(const Kit *k, Environment &env) const
{ {
addToBuildEnvironment(k, env); const QStringList values
= transform(EnvironmentItem::toStringList(EnvironmentKitAspect::runEnvChanges(k)),
[k](const QString &v) { return k->macroExpander()->expand(v); });
env.modify(EnvironmentItem::fromStringList(values));
} }
KitAspect *EnvironmentKitAspectFactory::createKitAspect(Kit *k) const KitAspect *EnvironmentKitAspectFactory::createKitAspect(Kit *k) const
@@ -196,8 +240,14 @@ KitAspect *EnvironmentKitAspectFactory::createKitAspect(Kit *k) const
KitAspectFactory::ItemList EnvironmentKitAspectFactory::toUserOutput(const Kit *k) const KitAspectFactory::ItemList EnvironmentKitAspectFactory::toUserOutput(const Kit *k) const
{ {
return {{Tr::tr("Environment"), ItemList list;
EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)).join("<br>")}}; const auto addIfNotEmpty = [&](const QString &displayName, const EnvironmentItems &changes) {
if (!changes.isEmpty())
list.emplaceBack(displayName, EnvironmentItem::toStringList(changes).join("<br>"));
};
addIfNotEmpty(Tr::tr("Build Environment"), EnvironmentKitAspect::buildEnvChanges(k));
addIfNotEmpty(Tr::tr("Run Environment"), EnvironmentKitAspect::runEnvChanges(k));
return list;
} }
const EnvironmentKitAspectFactory theEnvironmentKitAspectFactory; const EnvironmentKitAspectFactory theEnvironmentKitAspectFactory;
@@ -206,20 +256,33 @@ const EnvironmentKitAspectFactory theEnvironmentKitAspectFactory;
Id EnvironmentKitAspect::id() Id EnvironmentKitAspect::id()
{ {
return "PE.Profile.Environment"; return buildEnvId();
} }
EnvironmentItems EnvironmentKitAspect::environmentChanges(const Kit *k) EnvironmentItems EnvironmentKitAspect::buildEnvChanges(const Kit *k)
{ {
if (k) if (k)
return EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList()); return EnvironmentItem::fromStringList(k->value(buildEnvId()).toStringList());
return {}; return {};
} }
void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const EnvironmentItems &changes) void EnvironmentKitAspect::setBuildEnvChanges(Kit *k, const EnvironmentItems &changes)
{ {
if (k) if (k)
k->setValue(EnvironmentKitAspect::id(), EnvironmentItem::toStringList(changes)); k->setValue(buildEnvId(), EnvironmentItem::toStringList(changes));
}
EnvironmentItems EnvironmentKitAspect::runEnvChanges(const Kit *k)
{
if (k)
return EnvironmentItem::fromStringList(k->value(runEnvId()).toStringList());
return {};
}
void EnvironmentKitAspect::setRunEnvChanges(Kit *k, const Utils::EnvironmentItems &changes)
{
if (k)
k->setValue(runEnvId(), EnvironmentItem::toStringList(changes));
} }
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -16,8 +16,12 @@ class PROJECTEXPLORER_EXPORT EnvironmentKitAspect
{ {
public: public:
static Utils::Id id(); static Utils::Id id();
static Utils::EnvironmentItems environmentChanges(const Kit *k);
static void setEnvironmentChanges(Kit *k, const Utils::EnvironmentItems &changes); static Utils::EnvironmentItems buildEnvChanges(const Kit *k);
static void setBuildEnvChanges(Kit *k, const Utils::EnvironmentItems &changes);
static Utils::EnvironmentItems runEnvChanges(const Kit *k);
static void setRunEnvChanges(Kit *k, const Utils::EnvironmentItems &changes);
}; };
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -277,7 +277,7 @@ Environment ExtraCompiler::buildEnvironment() const
if (BuildConfiguration *bc = target->activeBuildConfiguration()) if (BuildConfiguration *bc = target->activeBuildConfiguration())
return bc->environment(); return bc->environment();
const EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(target->kit()); const EnvironmentItems changes = EnvironmentKitAspect::buildEnvChanges(target->kit());
Environment env = Environment::systemEnvironment(); Environment env = Environment::systemEnvironment();
env.modify(changes); env.modify(changes);
return env; return env;

View File

@@ -283,7 +283,7 @@ void QnxConfiguration::createKit(const QnxTarget &target)
k->setSticky(DebuggerKitAspect::id(), true); k->setSticky(DebuggerKitAspect::id(), true);
k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true); k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true);
EnvironmentKitAspect::setEnvironmentChanges(k, qnxEnvironmentItems()); EnvironmentKitAspect::setBuildEnvChanges(k, qnxEnvironmentItems());
}; };
// add kit with device and qt version not sticky // add kit with device and qt version not sticky