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 {
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(
parent,
initial,
placeholderText,
polisher,
Tr::tr("Edit Environment"));
dialogTitle.isEmpty() ? Tr::tr("Edit Environment") : dialogTitle);
}
} // namespace Utils

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,6 @@
#include "toolchain.h"
#include <utils/algorithm.h>
#include <utils/elidinglabel.h>
#include <utils/environment.h>
#include <utils/environmentdialog.h>
#include <utils/guard.h>
@@ -23,8 +22,8 @@
#include <utils/variablechooser.h>
#include <QCheckBox>
#include <QHBoxLayout>
#include <QPushButton>
#include <QVBoxLayout>
using namespace Utils;
@@ -41,57 +40,76 @@ static bool enforcesMSVCEnglish(const EnvironmentItems &changes)
return changes.contains(forceMSVCEnglishItem());
}
static Id buildEnvId() { return "PE.Profile.Environment"; }
static Id runEnvId() { return "PE.Profile.RunEnvironment"; }
namespace Internal {
class EnvironmentKitAspectImpl final : public KitAspect
{
public:
EnvironmentKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory)
: KitAspect(workingCopy, factory),
m_summaryLabel(createSubWidget<ElidingLabel>()),
m_manageButton(createSubWidget<QPushButton>()),
m_mainWidget(createSubWidget<QWidget>())
m_mainWidget(createSubWidget<QWidget>()),
m_buildEnvButton(createSubWidget<QPushButton>()),
m_runEnvButton(createSubWidget<QPushButton>())
{
auto *layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_summaryLabel);
addMutableAction(m_mainWidget);
if (HostOsInfo::isWindowsHost())
initMSVCOutputSwitch(layout);
m_mainWidget->setLayout(layout);
initMSVCOutputSwitch();
refresh();
m_manageButton->setText(Tr::tr("Change..."));
connect(m_manageButton, &QAbstractButton::clicked,
this, &EnvironmentKitAspectImpl::editEnvironmentChanges);
m_buildEnvButton->setText(Tr::tr("Edit Build Environment..."));
m_buildEnvButton->setIcon({});
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:
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_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
{
const EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(kit());
const QString shortSummary = EnvironmentItem::toStringList(changes).join("; ");
m_summaryLabel->setText(shortSummary.isEmpty() ? Tr::tr("No changes to apply.") : shortSummary);
const auto toString = [](const EnvironmentItems &changes) {
return EnvironmentItem::toStringList(changes).join("\n");
};
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(
m_summaryLabel, EnvironmentKitAspect::environmentChanges(kit()), QString(), polisher);
m_mainWidget,
EnvironmentKitAspect::buildEnvChanges(kit()),
QString(),
polisher(),
Tr::tr("Edit Build Environment"));
if (!changes)
return;
if (HostOsInfo::isWindowsHost()) {
if (m_vslangCheckbox) {
// re-add what envWithoutMSVCEnglishEnforcement removed
// or update vslang checkbox if user added it manually
if (m_vslangCheckbox->isChecked() && !enforcesMSVCEnglish(*changes))
@@ -99,33 +117,51 @@ private:
else if (enforcesMSVCEnglish(*changes))
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"));
layout->addWidget(m_vslangCheckbox);
if (const auto changes = EnvironmentDialog::getEnvironmentItems(
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 "
"just forces UTF-8 output (may vary depending on the used MSVC "
"compiler)."));
m_vslangCheckbox->setChecked(
enforcesMSVCEnglish(EnvironmentKitAspect::environmentChanges(kit())));
enforcesMSVCEnglish(EnvironmentKitAspect::buildEnvChanges(kit())));
connect(m_vslangCheckbox, &QCheckBox::clicked, this, [this](bool checked) {
EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(kit());
EnvironmentItems changes = EnvironmentKitAspect::buildEnvChanges(kit());
const bool hasVsLangEntry = enforcesMSVCEnglish(changes);
if (checked && !hasVsLangEntry)
changes.append(forceMSVCEnglishItem());
else if (!checked && hasVsLangEntry)
changes.removeAll(forceMSVCEnglishItem());
EnvironmentKitAspect::setEnvironmentChanges(kit(), changes);
EnvironmentKitAspect::setBuildEnvChanges(kit(), changes);
});
}
ElidingLabel *m_summaryLabel;
QPushButton *m_manageButton;
QCheckBox *m_vslangCheckbox;
QWidget *m_mainWidget;
QWidget * const m_mainWidget;
QPushButton * const m_buildEnvButton;
QPushButton * const m_runEnvButton;
QCheckBox *m_vslangCheckbox = nullptr;
};
class EnvironmentKitAspectFactory : public KitAspectFactory
@@ -157,7 +193,7 @@ Tasks EnvironmentKitAspectFactory::validate(const Kit *k) const
Tasks 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)))
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);
const QVariant variant = k->value(EnvironmentKitAspect::id());
if (!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) {
qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName()));
EnvironmentKitAspect::setEnvironmentChanges(k, EnvironmentItems());
if (const QVariant variant = k->value(buildEnvId());
!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) {
qWarning("Kit \"%s\" has a wrong build environment value set.", qPrintable(k->displayName()));
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
{
const QStringList values
= transform(EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)),
= transform(EnvironmentItem::toStringList(EnvironmentKitAspect::buildEnvChanges(k)),
[k](const QString &v) { return k->macroExpander()->expand(v); });
env.modify(EnvironmentItem::fromStringList(values));
}
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
@@ -196,8 +240,14 @@ KitAspect *EnvironmentKitAspectFactory::createKitAspect(Kit *k) const
KitAspectFactory::ItemList EnvironmentKitAspectFactory::toUserOutput(const Kit *k) const
{
return {{Tr::tr("Environment"),
EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)).join("<br>")}};
ItemList list;
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;
@@ -206,20 +256,33 @@ const EnvironmentKitAspectFactory theEnvironmentKitAspectFactory;
Id EnvironmentKitAspect::id()
{
return "PE.Profile.Environment";
return buildEnvId();
}
EnvironmentItems EnvironmentKitAspect::environmentChanges(const Kit *k)
EnvironmentItems EnvironmentKitAspect::buildEnvChanges(const Kit *k)
{
if (k)
return EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList());
return EnvironmentItem::fromStringList(k->value(buildEnvId()).toStringList());
return {};
}
void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const EnvironmentItems &changes)
void EnvironmentKitAspect::setBuildEnvChanges(Kit *k, const EnvironmentItems &changes)
{
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

View File

@@ -16,8 +16,12 @@ class PROJECTEXPLORER_EXPORT EnvironmentKitAspect
{
public:
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

View File

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

View File

@@ -283,7 +283,7 @@ void QnxConfiguration::createKit(const QnxTarget &target)
k->setSticky(DebuggerKitAspect::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