Allow external tools to use build or run environment

Add mechanism for registering environment providers, since just
registering the environment as a prefix variable in the macro expander
doesn't really fit the use case of using more than an individual
variable.

Project explorer registers providers for the current build and run
environments.

External tools can refer to a provider by id and then use that as base
environment.

Task-number: QTCREATORBUG-18394
Task-number: QTCREATORBUG-19892
Change-Id: I19fc4dfa8286b2f1e4679c6406f285dcc9514ba3
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Eike Ziller
2018-09-25 13:56:46 +02:00
parent 217e944485
commit d69470c15c
7 changed files with 147 additions and 15 deletions

View File

@@ -37,6 +37,8 @@
Q_GLOBAL_STATIC_WITH_ARGS(Utils::Environment, staticSystemEnvironment,
(QProcessEnvironment::systemEnvironment().toStringList()))
Q_GLOBAL_STATIC(QVector<Utils::EnvironmentProvider>, environmentProviders)
static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, Utils::OsType osType,
const QString &key)
{
@@ -691,4 +693,22 @@ QStringList Environment::expandVariables(const QStringList &variables) const
return Utils::transform(variables, [this](const QString &i) { return expandVariables(i); });
}
void EnvironmentProvider::addProvider(EnvironmentProvider &&provider)
{
environmentProviders->append(std::move(provider));
}
const QVector<EnvironmentProvider> EnvironmentProvider::providers()
{
return *environmentProviders;
}
optional<EnvironmentProvider> EnvironmentProvider::provider(const QByteArray &id)
{
const int index = indexOf(*environmentProviders, equal(&EnvironmentProvider::id, id));
if (index >= 0)
return make_optional(environmentProviders->at(index));
return nullopt;
}
} // namespace Utils

View File

@@ -27,6 +27,7 @@
#include "fileutils.h"
#include "hostosinfo.h"
#include "optional.h"
#include "utils_global.h"
#include <QMap>
@@ -148,4 +149,16 @@ private:
OsType m_osType;
};
class QTCREATOR_UTILS_EXPORT EnvironmentProvider
{
public:
QByteArray id;
QString displayName;
std::function<Environment()> environment;
static void addProvider(EnvironmentProvider &&provider);
static const QVector<EnvironmentProvider> providers();
static optional<EnvironmentProvider> provider(const QByteArray &id);
};
} // namespace Utils

View File

@@ -32,6 +32,7 @@
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/fancylineedit.h>
#include <utils/environment.h>
#include <utils/environmentdialog.h>
#include <coreplugin/coreconstants.h>
@@ -396,6 +397,14 @@ void ExternalToolModel::removeTool(const QModelIndex &modelIndex)
// #pragma mark -- ExternalToolConfig
static void fillBaseEnvironmentComboBox(QComboBox *box)
{
box->clear();
box->addItem(ExternalTool::tr("System Environment"), QByteArray());
for (const Utils::EnvironmentProvider &provider : Utils::EnvironmentProvider::providers())
box->addItem(provider.displayName, Id::fromName(provider.id).toSetting());
}
ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
QWidget(parent),
ui(new Ui::ExternalToolConfig),
@@ -414,6 +423,8 @@ ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
chooser->addSupportedWidget(ui->workingDirectory->lineEdit());
chooser->addSupportedWidget(ui->inputText);
fillBaseEnvironmentComboBox(ui->baseEnvironment);
connect(ui->description, &QLineEdit::editingFinished,
this, &ExternalToolConfig::updateCurrentItem);
connect(ui->executable, &Utils::PathChooser::editingFinished,
@@ -519,7 +530,8 @@ void ExternalToolConfig::updateItem(const QModelIndex &index)
tool->setExecutables(executables);
tool->setArguments(ui->arguments->text());
tool->setWorkingDirectory(ui->workingDirectory->rawPath());
tool->setEnvironment(m_environment);
tool->setBaseEnvironmentProviderId(Id::fromSetting(ui->baseEnvironment->currentData()));
tool->setEnvironmentUserChanges(m_environment);
tool->setOutputHandling((ExternalTool::OutputHandling)ui->outputBehavior->currentIndex());
tool->setErrorHandling((ExternalTool::OutputHandling)ui->errorOutputBehavior->currentIndex());
tool->setModifiesCurrentDocument(ui->modifiesDocumentCheckbox->checkState());
@@ -548,7 +560,10 @@ void ExternalToolConfig::showInfoForItem(const QModelIndex &index)
ui->outputBehavior->setCurrentIndex((int)tool->outputHandling());
ui->errorOutputBehavior->setCurrentIndex((int)tool->errorHandling());
ui->modifiesDocumentCheckbox->setChecked(tool->modifiesCurrentDocument());
m_environment = tool->environment();
const int baseEnvironmentIndex = ui->baseEnvironment->findData(
tool->baseEnvironmentProviderId().toSetting());
ui->baseEnvironment->setCurrentIndex(std::max(0, baseEnvironmentIndex));
m_environment = tool->environmentUserChanges();
{
QSignalBlocker blocker(ui->inputText);

View File

@@ -93,7 +93,16 @@
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
@@ -210,14 +219,14 @@
</item>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Environment:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
@@ -244,7 +253,7 @@
</item>
</layout>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QCheckBox" name="modifiesDocumentCheckbox">
<property name="toolTip">
<string>If the tool modifies the current document, set this flag to ensure that the document is saved before running the tool and is reloaded after the tool finished.</string>
@@ -254,7 +263,7 @@
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QLabel" name="inputLabel">
<property name="toolTip">
<string>Text to pass to the executable via standard input. Leave empty if the executable should not receive any input.</string>
@@ -264,13 +273,30 @@
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QPlainTextEdit" name="inputText">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="baseEnvironmentLabel">
<property name="text">
<string>Base environment:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="baseEnvironment">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -64,6 +64,7 @@ const char kPath[] = "path";
const char kArguments[] = "arguments";
const char kInput[] = "input";
const char kWorkingDirectory[] = "workingdirectory";
const char kBaseEnvironmentId[] = "baseEnvironmentId";
const char kEnvironment[] = "environment";
const char kXmlLang[] = "xml:lang";
@@ -95,6 +96,7 @@ ExternalTool::ExternalTool(const ExternalTool *other)
m_arguments(other->m_arguments),
m_input(other->m_input),
m_workingDirectory(other->m_workingDirectory),
m_baseEnvironmentProviderId(other->m_baseEnvironmentProviderId),
m_environment(other->m_environment),
m_outputHandling(other->m_outputHandling),
m_errorHandling(other->m_errorHandling),
@@ -172,7 +174,23 @@ QString ExternalTool::workingDirectory() const
return m_workingDirectory;
}
QList<EnvironmentItem> ExternalTool::environment() const
Id ExternalTool::baseEnvironmentProviderId() const
{
return m_baseEnvironmentProviderId;
}
Environment ExternalTool::baseEnvironment() const
{
if (m_baseEnvironmentProviderId.isValid()) {
const optional<EnvironmentProvider> provider = EnvironmentProvider::provider(
m_baseEnvironmentProviderId.name());
if (provider && provider->environment)
return provider->environment();
}
return Environment::systemEnvironment();
}
QList<EnvironmentItem> ExternalTool::environmentUserChanges() const
{
return m_environment;
}
@@ -274,7 +292,12 @@ void ExternalTool::setWorkingDirectory(const QString &workingDirectory)
m_workingDirectory = workingDirectory;
}
void ExternalTool::setEnvironment(const QList<EnvironmentItem> &items)
void ExternalTool::setBaseEnvironmentProviderId(Id id)
{
m_baseEnvironmentProviderId = id;
}
void ExternalTool::setEnvironmentUserChanges(const QList<EnvironmentItem> &items)
{
m_environment = items;
}
@@ -413,6 +436,12 @@ ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *error
break;
}
tool->m_workingDirectory = reader.readElementText();
} else if (reader.name() == kBaseEnvironmentId) {
if (tool->m_baseEnvironmentProviderId.isValid()) {
reader.raiseError(QLatin1String("only one <baseEnvironmentId> element allowed"));
break;
}
tool->m_baseEnvironmentProviderId = Id::fromString(reader.readElementText());
} else if (reader.name() == QLatin1String(kEnvironment)) {
if (!tool->m_environment.isEmpty()) {
reader.raiseError(QLatin1String("only one <environment> element allowed"));
@@ -498,6 +527,8 @@ bool ExternalTool::save(QString *errorMessage) const
out.writeTextElement(QLatin1String(kInput), m_input);
if (!m_workingDirectory.isEmpty())
out.writeTextElement(QLatin1String(kWorkingDirectory), m_workingDirectory);
if (m_baseEnvironmentProviderId.isValid())
out.writeTextElement(kBaseEnvironmentId, m_baseEnvironmentProviderId.toString());
if (!m_environment.isEmpty()) {
QStringList envLines = EnvironmentItem::toStringList(m_environment);
for (auto iter = envLines.begin(); iter != envLines.end(); ++iter)
@@ -524,6 +555,7 @@ bool ExternalTool::operator==(const ExternalTool &other) const
&& m_arguments == other.m_arguments
&& m_input == other.m_input
&& m_workingDirectory == other.m_workingDirectory
&& m_baseEnvironmentProviderId == other.m_baseEnvironmentProviderId
&& m_environment == other.m_environment
&& m_outputHandling == other.m_outputHandling
&& m_modifiesCurrentDocument == other.m_modifiesCurrentDocument
@@ -565,12 +597,11 @@ bool ExternalToolRunner::resolve()
m_resolvedExecutable.clear();
m_resolvedArguments.clear();
m_resolvedWorkingDirectory.clear();
m_resolvedEnvironment = Environment::systemEnvironment();
m_resolvedEnvironment = m_tool->baseEnvironment();
MacroExpander *expander = globalMacroExpander();
QList<EnvironmentItem> expandedEnvironment
= Utils::transform(m_tool->environment(), [expander](const EnvironmentItem &item) {
= Utils::transform(m_tool->environmentUserChanges(), [expander](const EnvironmentItem &item) {
return EnvironmentItem(item.name, expander->expand(item.value), item.operation);
});
m_resolvedEnvironment.modify(expandedEnvironment);

View File

@@ -25,6 +25,8 @@
#pragma once
#include "id.h"
#include <utils/fileutils.h>
#include <utils/environment.h>
@@ -67,7 +69,9 @@ public:
QString arguments() const;
QString input() const;
QString workingDirectory() const;
QList<Utils::EnvironmentItem> environment() const;
Id baseEnvironmentProviderId() const;
Utils::Environment baseEnvironment() const;
QList<Utils::EnvironmentItem> environmentUserChanges() const;
void setFileName(const QString &fileName);
void setPreset(QSharedPointer<ExternalTool> preset);
@@ -96,7 +100,8 @@ public:
void setArguments(const QString &arguments);
void setInput(const QString &input);
void setWorkingDirectory(const QString &workingDirectory);
void setEnvironment(const QList<Utils::EnvironmentItem> &items);
void setBaseEnvironmentProviderId(Id id);
void setEnvironmentUserChanges(const QList<Utils::EnvironmentItem> &items);
private:
QString m_id;
@@ -108,6 +113,7 @@ private:
QString m_arguments;
QString m_input;
QString m_workingDirectory;
Id m_baseEnvironmentProviderId;
QList<Utils::EnvironmentItem> m_environment;
OutputHandling m_outputHandling = ShowInPane;
OutputHandling m_errorHandling = ShowInPane;

View File

@@ -271,6 +271,12 @@ static BuildConfiguration *activeBuildConfiguration()
return target ? target->activeBuildConfiguration() : nullptr;
}
static RunConfiguration *activeRunConfiguration()
{
Target *target = activeTarget();
return target ? target->activeRunConfiguration() : nullptr;
}
static Kit *currentKit()
{
Target *target = activeTarget();
@@ -1554,6 +1560,21 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
return QString();
});
Utils::EnvironmentProvider::addProvider(
{Constants::VAR_CURRENTBUILD_ENV, tr("Current Build Environment"), []() {
if (BuildConfiguration *bc = activeBuildConfiguration())
return bc->environment();
return Utils::Environment::systemEnvironment();
}});
Utils::EnvironmentProvider::addProvider(
{"CurrentRun:Env", tr("Current Run Environment"), []() {
if (RunConfiguration *rc = activeRunConfiguration())
if (auto envAspect = rc->extraAspect<EnvironmentAspect>())
return envAspect->environment();
return Utils::Environment::systemEnvironment();
}});
QString fileDescription = tr("File where current session is saved.");
auto fileHandler = [] { return SessionManager::sessionNameToFileName(SessionManager::activeSession()).toString(); };
expander->registerFileVariables("Session", fileDescription, fileHandler);