Allow tweaking of environment for external tools

Task-number: QTCREATORBUG-4995
Change-Id: If8a6f6715165cf12809ae9c2a3a629bc4f464c16
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@theqtcompany.com>
Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
Kai Koehne
2015-01-06 16:41:44 +01:00
committed by hjk
parent 739cc9f053
commit 582ad0fc3f
6 changed files with 163 additions and 4 deletions

View File

@@ -115,6 +115,11 @@
\li In the \uicontrol {Working directory} field, specify the path to the \li In the \uicontrol {Working directory} field, specify the path to the
working directory. working directory.
\li In the \uicontrol Environment field, select \uicontrol Change to modify
environment variable values for build and run environments in
the \uicontrol {Edit Environment Changes} dialog. For more information
about how to add and remove variable values, see \l{Batch Editing}.
\li In the \uicontrol {Output pane}, select how to handle output from the \li In the \uicontrol {Output pane}, select how to handle output from the
tool. You can ignore the output, view it in the \uicontrol {General tool. You can ignore the output, view it in the \uicontrol {General
Messages} output pane, or replace the selected text with the Messages} output pane, or replace the selected text with the

View File

@@ -41,9 +41,11 @@
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
#include <coreplugin/variablechooser.h> #include <coreplugin/variablechooser.h>
#include <QDialogButtonBox>
#include <QTextStream> #include <QTextStream>
#include <QMimeData> #include <QMimeData>
#include <QMenu> #include <QMenu>
#include <QPlainTextEdit>
using namespace Core; using namespace Core;
using namespace Core::Internal; using namespace Core::Internal;
@@ -396,6 +398,44 @@ void ExternalToolModel::removeTool(const QModelIndex &modelIndex)
delete tool; delete tool;
} }
EnvironmentChangesDialog::EnvironmentChangesDialog(QWidget *parent) :
QDialog(parent),
m_editor(0)
{
setWindowTitle(tr("Edit Environment Changes"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setModal(true);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *label = new QLabel(this);
label->setText(tr("Change system environment by assigning one environment variable per line:"));
layout->addWidget(label);
m_editor = new QPlainTextEdit(this);
if (Utils::HostOsInfo::isWindowsHost())
m_editor->setPlaceholderText(tr("PATH=C:\\dev\\bin;${PATH}"));
else
m_editor->setPlaceholderText(tr("PATH=/opt/bin:${PATH}"));
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
layout->addWidget(m_editor);
layout->addWidget(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, &EnvironmentChangesDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &EnvironmentChangesDialog::reject);
}
QStringList EnvironmentChangesDialog::changes() const
{
return m_editor->toPlainText().split(QLatin1Char('\n'));
}
void EnvironmentChangesDialog::setChanges(const QStringList &changes)
{
m_editor->setPlainText(changes.join(QLatin1Char('\n')));
}
// #pragma mark -- ExternalToolConfig // #pragma mark -- ExternalToolConfig
ExternalToolConfig::ExternalToolConfig(QWidget *parent) : ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
@@ -423,6 +463,7 @@ ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
connect(ui->arguments, SIGNAL(editingFinished()), this, SLOT(updateEffectiveArguments())); connect(ui->arguments, SIGNAL(editingFinished()), this, SLOT(updateEffectiveArguments()));
connect(ui->workingDirectory, SIGNAL(editingFinished()), this, SLOT(updateCurrentItem())); connect(ui->workingDirectory, SIGNAL(editingFinished()), this, SLOT(updateCurrentItem()));
connect(ui->workingDirectory, SIGNAL(browsingFinished()), this, SLOT(updateCurrentItem())); connect(ui->workingDirectory, SIGNAL(browsingFinished()), this, SLOT(updateCurrentItem()));
connect(ui->environmentButton, SIGNAL(clicked()), this, SLOT(editEnvironmentChanges()));
connect(ui->outputBehavior, SIGNAL(activated(int)), this, SLOT(updateCurrentItem())); connect(ui->outputBehavior, SIGNAL(activated(int)), this, SLOT(updateCurrentItem()));
connect(ui->errorOutputBehavior, SIGNAL(activated(int)), this, SLOT(updateCurrentItem())); connect(ui->errorOutputBehavior, SIGNAL(activated(int)), this, SLOT(updateCurrentItem()));
connect(ui->modifiesDocumentCheckbox, SIGNAL(clicked()), this, SLOT(updateCurrentItem())); connect(ui->modifiesDocumentCheckbox, SIGNAL(clicked()), this, SLOT(updateCurrentItem()));
@@ -510,6 +551,7 @@ void ExternalToolConfig::updateItem(const QModelIndex &index)
tool->setExecutables(executables); tool->setExecutables(executables);
tool->setArguments(ui->arguments->text()); tool->setArguments(ui->arguments->text());
tool->setWorkingDirectory(ui->workingDirectory->rawPath()); tool->setWorkingDirectory(ui->workingDirectory->rawPath());
tool->setEnvironment(Utils::EnvironmentItem::fromStringList(m_environment));
tool->setOutputHandling((ExternalTool::OutputHandling)ui->outputBehavior->currentIndex()); tool->setOutputHandling((ExternalTool::OutputHandling)ui->outputBehavior->currentIndex());
tool->setErrorHandling((ExternalTool::OutputHandling)ui->errorOutputBehavior->currentIndex()); tool->setErrorHandling((ExternalTool::OutputHandling)ui->errorOutputBehavior->currentIndex());
tool->setModifiesCurrentDocument(ui->modifiesDocumentCheckbox->checkState()); tool->setModifiesCurrentDocument(ui->modifiesDocumentCheckbox->checkState());
@@ -527,6 +569,7 @@ void ExternalToolConfig::showInfoForItem(const QModelIndex &index)
ui->workingDirectory->setPath(QString()); ui->workingDirectory->setPath(QString());
ui->inputText->clear(); ui->inputText->clear();
ui->infoWidget->setEnabled(false); ui->infoWidget->setEnabled(false);
m_environment.clear();
return; return;
} }
ui->infoWidget->setEnabled(true); ui->infoWidget->setEnabled(true);
@@ -537,6 +580,7 @@ void ExternalToolConfig::showInfoForItem(const QModelIndex &index)
ui->outputBehavior->setCurrentIndex((int)tool->outputHandling()); ui->outputBehavior->setCurrentIndex((int)tool->outputHandling());
ui->errorOutputBehavior->setCurrentIndex((int)tool->errorHandling()); ui->errorOutputBehavior->setCurrentIndex((int)tool->errorHandling());
ui->modifiesDocumentCheckbox->setChecked(tool->modifiesCurrentDocument()); ui->modifiesDocumentCheckbox->setChecked(tool->modifiesCurrentDocument());
m_environment = Utils::EnvironmentItem::toStringList(tool->environment());
bool blocked = ui->inputText->blockSignals(true); bool blocked = ui->inputText->blockSignals(true);
ui->inputText->setPlainText(tool->input()); ui->inputText->setPlainText(tool->input());
@@ -544,6 +588,7 @@ void ExternalToolConfig::showInfoForItem(const QModelIndex &index)
ui->description->setCursorPosition(0); ui->description->setCursorPosition(0);
ui->arguments->setCursorPosition(0); ui->arguments->setCursorPosition(0);
updateEnvironmentLabel();
updateEffectiveArguments(); updateEffectiveArguments();
} }
@@ -596,3 +641,21 @@ void ExternalToolConfig::updateEffectiveArguments()
{ {
ui->arguments->setToolTip(Utils::globalMacroExpander()->expandProcessArgs(ui->arguments->text())); ui->arguments->setToolTip(Utils::globalMacroExpander()->expandProcessArgs(ui->arguments->text()));
} }
void ExternalToolConfig::editEnvironmentChanges()
{
EnvironmentChangesDialog dialog(ui->environmentLabel);
dialog.setChanges(m_environment);
if (dialog.exec() == QDialog::Accepted) {
m_environment = dialog.changes();
updateEnvironmentLabel();
}
}
void ExternalToolConfig::updateEnvironmentLabel()
{
QString shortSummary = m_environment.join(QLatin1String("; "));
QFontMetrics fm(ui->environmentLabel->font());
shortSummary = fm.elidedText(shortSummary, Qt::ElideRight, ui->environmentLabel->width());
ui->environmentLabel->setText(shortSummary.isEmpty() ? tr("No Changes to apply") : shortSummary);
}

View File

@@ -35,6 +35,9 @@
#include <QWidget> #include <QWidget>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QDialog>
QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
namespace Core { namespace Core {
namespace Internal { namespace Internal {
@@ -82,6 +85,17 @@ private:
QMap<QString, QList<ExternalTool *> > m_tools; QMap<QString, QList<ExternalTool *> > m_tools;
}; };
class EnvironmentChangesDialog : public QDialog
{
Q_OBJECT
public:
explicit EnvironmentChangesDialog(QWidget *parent = 0);
QStringList changes() const;
void setChanges(const QStringList &changes);
private:
QPlainTextEdit *m_editor;
};
class ExternalToolConfig : public QWidget class ExternalToolConfig : public QWidget
{ {
@@ -106,9 +120,12 @@ private slots:
void removeTool(); void removeTool();
void addCategory(); void addCategory();
void updateEffectiveArguments(); void updateEffectiveArguments();
void editEnvironmentChanges();
void updateEnvironmentLabel();
private: private:
Ui::ExternalToolConfig *ui; Ui::ExternalToolConfig *ui;
QStringList m_environment;
ExternalToolModel *m_model; ExternalToolModel *m_model;
}; };

View File

@@ -210,7 +210,41 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Environment:</string>
</property>
</widget>
</item>
<item row="6" column="1"> <item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="environmentLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>No Changes to apply.</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="environmentButton">
<property name="text">
<string>Change...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="modifiesDocumentCheckbox"> <widget class="QCheckBox" name="modifiesDocumentCheckbox">
<property name="toolTip"> <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> <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>
@@ -220,7 +254,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="inputLabel"> <widget class="QLabel" name="inputLabel">
<property name="toolTip"> <property name="toolTip">
<string>Text to pass to the executable via standard input. Leave empty if the executable should not receive any input.</string> <string>Text to pass to the executable via standard input. Leave empty if the executable should not receive any input.</string>
@@ -230,7 +264,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QPlainTextEdit" name="inputText"> <widget class="QPlainTextEdit" name="inputText">
<property name="lineWrapMode"> <property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum> <enum>QPlainTextEdit::NoWrap</enum>

View File

@@ -66,6 +66,7 @@ const char kPath[] = "path";
const char kArguments[] = "arguments"; const char kArguments[] = "arguments";
const char kInput[] = "input"; const char kInput[] = "input";
const char kWorkingDirectory[] = "workingdirectory"; const char kWorkingDirectory[] = "workingdirectory";
const char kEnvironment[] = "environment";
const char kXmlLang[] = "xml:lang"; const char kXmlLang[] = "xml:lang";
const char kOutput[] = "output"; const char kOutput[] = "output";
@@ -100,6 +101,7 @@ ExternalTool::ExternalTool(const ExternalTool *other)
m_arguments(other->m_arguments), m_arguments(other->m_arguments),
m_input(other->m_input), m_input(other->m_input),
m_workingDirectory(other->m_workingDirectory), m_workingDirectory(other->m_workingDirectory),
m_environment(other->m_environment),
m_outputHandling(other->m_outputHandling), m_outputHandling(other->m_outputHandling),
m_errorHandling(other->m_errorHandling), m_errorHandling(other->m_errorHandling),
m_modifiesCurrentDocument(other->m_modifiesCurrentDocument), m_modifiesCurrentDocument(other->m_modifiesCurrentDocument),
@@ -119,6 +121,7 @@ ExternalTool &ExternalTool::operator=(const ExternalTool &other)
m_arguments = other.m_arguments; m_arguments = other.m_arguments;
m_input = other.m_input; m_input = other.m_input;
m_workingDirectory = other.m_workingDirectory; m_workingDirectory = other.m_workingDirectory;
m_environment = other.m_environment;
m_outputHandling = other.m_outputHandling; m_outputHandling = other.m_outputHandling;
m_errorHandling = other.m_errorHandling; m_errorHandling = other.m_errorHandling;
m_modifiesCurrentDocument = other.m_modifiesCurrentDocument; m_modifiesCurrentDocument = other.m_modifiesCurrentDocument;
@@ -177,6 +180,11 @@ QString ExternalTool::workingDirectory() const
return m_workingDirectory; return m_workingDirectory;
} }
QList<Utils::EnvironmentItem> ExternalTool::environment() const
{
return m_environment;
}
ExternalTool::OutputHandling ExternalTool::outputHandling() const ExternalTool::OutputHandling ExternalTool::outputHandling() const
{ {
return m_outputHandling; return m_outputHandling;
@@ -274,6 +282,11 @@ void ExternalTool::setWorkingDirectory(const QString &workingDirectory)
m_workingDirectory = workingDirectory; m_workingDirectory = workingDirectory;
} }
void ExternalTool::setEnvironment(const QList<Utils::EnvironmentItem> &items)
{
m_environment = items;
}
static QStringList splitLocale(const QString &locale) static QStringList splitLocale(const QString &locale)
{ {
QString value = locale; QString value = locale;
@@ -408,6 +421,15 @@ ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *error
break; break;
} }
tool->m_workingDirectory = reader.readElementText(); tool->m_workingDirectory = reader.readElementText();
} else if (reader.name() == QLatin1String(kEnvironment)) {
if (!tool->m_environment.isEmpty()) {
reader.raiseError(QLatin1String("only one <environment> element allowed"));
break;
}
QStringList lines = reader.readElementText().split(QLatin1Char(';'));
for (auto iter = lines.begin(); iter != lines.end(); ++iter)
*iter = QString::fromUtf8(QByteArray::fromPercentEncoding(iter->toUtf8()));
tool->m_environment = Utils::EnvironmentItem::fromStringList(lines);
} else { } else {
reader.raiseError(QString::fromLatin1("Unknown element <%1> as subelement of <%2>").arg( reader.raiseError(QString::fromLatin1("Unknown element <%1> as subelement of <%2>").arg(
reader.qualifiedName().toString(), QLatin1String(kExecutable))); reader.qualifiedName().toString(), QLatin1String(kExecutable)));
@@ -484,6 +506,12 @@ bool ExternalTool::save(QString *errorMessage) const
out.writeTextElement(QLatin1String(kInput), m_input); out.writeTextElement(QLatin1String(kInput), m_input);
if (!m_workingDirectory.isEmpty()) if (!m_workingDirectory.isEmpty())
out.writeTextElement(QLatin1String(kWorkingDirectory), m_workingDirectory); out.writeTextElement(QLatin1String(kWorkingDirectory), m_workingDirectory);
if (!m_environment.isEmpty()) {
QStringList envLines = Utils::EnvironmentItem::toStringList(m_environment);
for (auto iter = envLines.begin(); iter != envLines.end(); ++iter)
*iter = QString::fromUtf8(iter->toUtf8().toPercentEncoding());
out.writeTextElement(QLatin1String(kEnvironment), envLines.join(QLatin1Char(';')));
}
out.writeEndElement(); out.writeEndElement();
out.writeEndDocument(); out.writeEndDocument();
@@ -504,6 +532,7 @@ bool ExternalTool::operator==(const ExternalTool &other) const
&& m_arguments == other.m_arguments && m_arguments == other.m_arguments
&& m_input == other.m_input && m_input == other.m_input
&& m_workingDirectory == other.m_workingDirectory && m_workingDirectory == other.m_workingDirectory
&& m_environment == other.m_environment
&& m_outputHandling == other.m_outputHandling && m_outputHandling == other.m_outputHandling
&& m_modifiesCurrentDocument == other.m_modifiesCurrentDocument && m_modifiesCurrentDocument == other.m_modifiesCurrentDocument
&& m_errorHandling == other.m_errorHandling && m_errorHandling == other.m_errorHandling
@@ -544,14 +573,19 @@ bool ExternalToolRunner::resolve()
m_resolvedExecutable.clear(); m_resolvedExecutable.clear();
m_resolvedArguments.clear(); m_resolvedArguments.clear();
m_resolvedWorkingDirectory.clear(); m_resolvedWorkingDirectory.clear();
m_resolvedEnvironment = Utils::Environment::systemEnvironment();
MacroExpander *expander = globalMacroExpander(); MacroExpander *expander = globalMacroExpander();
{ // executable m_resolvedEnvironment.modify(m_tool->environment());
{
// executable
QStringList expandedExecutables; /* for error message */ QStringList expandedExecutables; /* for error message */
foreach (const QString &executable, m_tool->executables()) { foreach (const QString &executable, m_tool->executables()) {
QString expanded = expander->expand(executable); QString expanded = expander->expand(executable);
expandedExecutables.append(expanded); expandedExecutables.append(expanded);
m_resolvedExecutable = Environment::systemEnvironment().searchInPath(expanded); m_resolvedExecutable = m_resolvedEnvironment.searchInPath(expanded);
if (!m_resolvedExecutable.isEmpty()) if (!m_resolvedExecutable.isEmpty())
break; break;
} }
@@ -601,6 +635,7 @@ void ExternalToolRunner::run()
if (!m_resolvedWorkingDirectory.isEmpty()) if (!m_resolvedWorkingDirectory.isEmpty())
m_process->setWorkingDirectory(m_resolvedWorkingDirectory); m_process->setWorkingDirectory(m_resolvedWorkingDirectory);
m_process->setCommand(m_resolvedExecutable.toString(), m_resolvedArguments); m_process->setCommand(m_resolvedExecutable.toString(), m_resolvedArguments);
m_process->setEnvironment(m_resolvedEnvironment);
MessageManager::write(tr("Starting external tool \"%1\" %2") MessageManager::write(tr("Starting external tool \"%1\" %2")
.arg(m_resolvedExecutable.toUserOutput(), m_resolvedArguments), .arg(m_resolvedExecutable.toUserOutput(), m_resolvedArguments),
MessageManager::Silent); MessageManager::Silent);

View File

@@ -32,6 +32,7 @@
#define EXTERNALTOOL_H #define EXTERNALTOOL_H
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/environment.h>
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
@@ -72,6 +73,7 @@ public:
QString arguments() const; QString arguments() const;
QString input() const; QString input() const;
QString workingDirectory() const; QString workingDirectory() const;
QList<Utils::EnvironmentItem> environment() const;
void setFileName(const QString &fileName); void setFileName(const QString &fileName);
void setPreset(QSharedPointer<ExternalTool> preset); void setPreset(QSharedPointer<ExternalTool> preset);
@@ -100,6 +102,7 @@ public:
void setArguments(const QString &arguments); void setArguments(const QString &arguments);
void setInput(const QString &input); void setInput(const QString &input);
void setWorkingDirectory(const QString &workingDirectory); void setWorkingDirectory(const QString &workingDirectory);
void setEnvironment(const QList<Utils::EnvironmentItem> &items);
private: private:
QString m_id; QString m_id;
@@ -111,6 +114,7 @@ private:
QString m_arguments; QString m_arguments;
QString m_input; QString m_input;
QString m_workingDirectory; QString m_workingDirectory;
QList<Utils::EnvironmentItem> m_environment;
OutputHandling m_outputHandling; OutputHandling m_outputHandling;
OutputHandling m_errorHandling; OutputHandling m_errorHandling;
bool m_modifiesCurrentDocument; bool m_modifiesCurrentDocument;
@@ -146,6 +150,7 @@ private:
QString m_resolvedArguments; QString m_resolvedArguments;
QString m_resolvedInput; QString m_resolvedInput;
QString m_resolvedWorkingDirectory; QString m_resolvedWorkingDirectory;
Utils::Environment m_resolvedEnvironment;
Utils::QtcProcess *m_process; Utils::QtcProcess *m_process;
QTextCodec *m_outputCodec; QTextCodec *m_outputCodec;
QTextCodec::ConverterState m_outputCodecState; QTextCodec::ConverterState m_outputCodecState;