diff --git a/doc/src/howto/creator-external-tools.qdoc b/doc/src/howto/creator-external-tools.qdoc index 1d29cd5ef428c72bcacbc197a5a7b2ab6755d135..60e769fb83c7616fcf9d83dff2b97220d5f017b0 100644 --- a/doc/src/howto/creator-external-tools.qdoc +++ b/doc/src/howto/creator-external-tools.qdoc @@ -115,6 +115,11 @@ \li In the \uicontrol {Working directory} field, specify the path to the 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 tool. You can ignore the output, view it in the \uicontrol {General Messages} output pane, or replace the selected text with the diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp index 35ba33a297b4073bb9f35d28eaa7f9487a46127d..80a69539a50d7662643458cd4c7985f6329c8fab 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp @@ -41,9 +41,11 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/variablechooser.h> +#include <QDialogButtonBox> #include <QTextStream> #include <QMimeData> #include <QMenu> +#include <QPlainTextEdit> using namespace Core; using namespace Core::Internal; @@ -396,6 +398,44 @@ void ExternalToolModel::removeTool(const QModelIndex &modelIndex) 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 ExternalToolConfig::ExternalToolConfig(QWidget *parent) : @@ -423,6 +463,7 @@ ExternalToolConfig::ExternalToolConfig(QWidget *parent) : connect(ui->arguments, SIGNAL(editingFinished()), this, SLOT(updateEffectiveArguments())); connect(ui->workingDirectory, SIGNAL(editingFinished()), 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->errorOutputBehavior, SIGNAL(activated(int)), 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->setArguments(ui->arguments->text()); tool->setWorkingDirectory(ui->workingDirectory->rawPath()); + tool->setEnvironment(Utils::EnvironmentItem::fromStringList(m_environment)); tool->setOutputHandling((ExternalTool::OutputHandling)ui->outputBehavior->currentIndex()); tool->setErrorHandling((ExternalTool::OutputHandling)ui->errorOutputBehavior->currentIndex()); tool->setModifiesCurrentDocument(ui->modifiesDocumentCheckbox->checkState()); @@ -527,6 +569,7 @@ void ExternalToolConfig::showInfoForItem(const QModelIndex &index) ui->workingDirectory->setPath(QString()); ui->inputText->clear(); ui->infoWidget->setEnabled(false); + m_environment.clear(); return; } ui->infoWidget->setEnabled(true); @@ -537,6 +580,7 @@ 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 = Utils::EnvironmentItem::toStringList(tool->environment()); bool blocked = ui->inputText->blockSignals(true); ui->inputText->setPlainText(tool->input()); @@ -544,6 +588,7 @@ void ExternalToolConfig::showInfoForItem(const QModelIndex &index) ui->description->setCursorPosition(0); ui->arguments->setCursorPosition(0); + updateEnvironmentLabel(); updateEffectiveArguments(); } @@ -596,3 +641,21 @@ void ExternalToolConfig::updateEffectiveArguments() { 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); +} diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.h b/src/plugins/coreplugin/dialogs/externaltoolconfig.h index 40ac508f8aeabb5183f887414ef0d47a5425e866..f68b15c6a912d73d5b7a834491e3939a4f9c72c5 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.h +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.h @@ -35,6 +35,9 @@ #include <QWidget> #include <QAbstractItemModel> +#include <QDialog> + +QT_FORWARD_DECLARE_CLASS(QPlainTextEdit) namespace Core { namespace Internal { @@ -82,6 +85,17 @@ private: 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 { @@ -106,9 +120,12 @@ private slots: void removeTool(); void addCategory(); void updateEffectiveArguments(); + void editEnvironmentChanges(); + void updateEnvironmentLabel(); private: Ui::ExternalToolConfig *ui; + QStringList m_environment; ExternalToolModel *m_model; }; diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui index c3302139787b20da9bca0f75c0e00e632f1743a9..39beea463978743cf9de25acc3bb4567d71dc298 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui @@ -210,7 +210,41 @@ </item> </widget> </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"> + <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"> <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> @@ -220,7 +254,7 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="8" 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> @@ -230,7 +264,7 @@ </property> </widget> </item> - <item row="7" column="1"> + <item row="8" column="1"> <widget class="QPlainTextEdit" name="inputText"> <property name="lineWrapMode"> <enum>QPlainTextEdit::NoWrap</enum> diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index 13e696d12a86a7883d157ea7af328e258ee047f6..a006f4de6194a0e80dcb4a93f7081a266fd501f6 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -66,6 +66,7 @@ const char kPath[] = "path"; const char kArguments[] = "arguments"; const char kInput[] = "input"; const char kWorkingDirectory[] = "workingdirectory"; +const char kEnvironment[] = "environment"; const char kXmlLang[] = "xml:lang"; const char kOutput[] = "output"; @@ -100,6 +101,7 @@ ExternalTool::ExternalTool(const ExternalTool *other) m_arguments(other->m_arguments), m_input(other->m_input), m_workingDirectory(other->m_workingDirectory), + m_environment(other->m_environment), m_outputHandling(other->m_outputHandling), m_errorHandling(other->m_errorHandling), m_modifiesCurrentDocument(other->m_modifiesCurrentDocument), @@ -119,6 +121,7 @@ ExternalTool &ExternalTool::operator=(const ExternalTool &other) m_arguments = other.m_arguments; m_input = other.m_input; m_workingDirectory = other.m_workingDirectory; + m_environment = other.m_environment; m_outputHandling = other.m_outputHandling; m_errorHandling = other.m_errorHandling; m_modifiesCurrentDocument = other.m_modifiesCurrentDocument; @@ -177,6 +180,11 @@ QString ExternalTool::workingDirectory() const return m_workingDirectory; } +QList<Utils::EnvironmentItem> ExternalTool::environment() const +{ + return m_environment; +} + ExternalTool::OutputHandling ExternalTool::outputHandling() const { return m_outputHandling; @@ -274,6 +282,11 @@ void ExternalTool::setWorkingDirectory(const QString &workingDirectory) m_workingDirectory = workingDirectory; } +void ExternalTool::setEnvironment(const QList<Utils::EnvironmentItem> &items) +{ + m_environment = items; +} + static QStringList splitLocale(const QString &locale) { QString value = locale; @@ -408,6 +421,15 @@ ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *error break; } 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 { reader.raiseError(QString::fromLatin1("Unknown element <%1> as subelement of <%2>").arg( reader.qualifiedName().toString(), QLatin1String(kExecutable))); @@ -484,6 +506,12 @@ bool ExternalTool::save(QString *errorMessage) const out.writeTextElement(QLatin1String(kInput), m_input); if (!m_workingDirectory.isEmpty()) 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.writeEndDocument(); @@ -504,6 +532,7 @@ bool ExternalTool::operator==(const ExternalTool &other) const && m_arguments == other.m_arguments && m_input == other.m_input && m_workingDirectory == other.m_workingDirectory + && m_environment == other.m_environment && m_outputHandling == other.m_outputHandling && m_modifiesCurrentDocument == other.m_modifiesCurrentDocument && m_errorHandling == other.m_errorHandling @@ -544,14 +573,19 @@ bool ExternalToolRunner::resolve() m_resolvedExecutable.clear(); m_resolvedArguments.clear(); m_resolvedWorkingDirectory.clear(); + m_resolvedEnvironment = Utils::Environment::systemEnvironment(); + MacroExpander *expander = globalMacroExpander(); - { // executable + m_resolvedEnvironment.modify(m_tool->environment()); + + { + // executable QStringList expandedExecutables; /* for error message */ foreach (const QString &executable, m_tool->executables()) { QString expanded = expander->expand(executable); expandedExecutables.append(expanded); - m_resolvedExecutable = Environment::systemEnvironment().searchInPath(expanded); + m_resolvedExecutable = m_resolvedEnvironment.searchInPath(expanded); if (!m_resolvedExecutable.isEmpty()) break; } @@ -601,6 +635,7 @@ void ExternalToolRunner::run() if (!m_resolvedWorkingDirectory.isEmpty()) m_process->setWorkingDirectory(m_resolvedWorkingDirectory); m_process->setCommand(m_resolvedExecutable.toString(), m_resolvedArguments); + m_process->setEnvironment(m_resolvedEnvironment); MessageManager::write(tr("Starting external tool \"%1\" %2") .arg(m_resolvedExecutable.toUserOutput(), m_resolvedArguments), MessageManager::Silent); diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h index 636b4c1bd2dfa3485ef55fd3feeba8e543099d45..4846b2b15e365c071e8a44e0acdfe3d39213719b 100644 --- a/src/plugins/coreplugin/externaltool.h +++ b/src/plugins/coreplugin/externaltool.h @@ -32,6 +32,7 @@ #define EXTERNALTOOL_H #include <utils/fileutils.h> +#include <utils/environment.h> #include <QObject> #include <QStringList> @@ -72,6 +73,7 @@ public: QString arguments() const; QString input() const; QString workingDirectory() const; + QList<Utils::EnvironmentItem> environment() const; void setFileName(const QString &fileName); void setPreset(QSharedPointer<ExternalTool> preset); @@ -100,6 +102,7 @@ public: void setArguments(const QString &arguments); void setInput(const QString &input); void setWorkingDirectory(const QString &workingDirectory); + void setEnvironment(const QList<Utils::EnvironmentItem> &items); private: QString m_id; @@ -111,6 +114,7 @@ private: QString m_arguments; QString m_input; QString m_workingDirectory; + QList<Utils::EnvironmentItem> m_environment; OutputHandling m_outputHandling; OutputHandling m_errorHandling; bool m_modifiesCurrentDocument; @@ -146,6 +150,7 @@ private: QString m_resolvedArguments; QString m_resolvedInput; QString m_resolvedWorkingDirectory; + Utils::Environment m_resolvedEnvironment; Utils::QtcProcess *m_process; QTextCodec *m_outputCodec; QTextCodec::ConverterState m_outputCodecState;