Commit 0641ed0e authored by Oswald Buddenhagen's avatar Oswald Buddenhagen

change Environment::expandVariables() semantics

instead of being os-agnostic, interpret the os-native expansion style,
so it is consistent with proper (shell) command lines.

don't interpret quotes, as this function is meant for expanding isolated
filepaths, where nobody would expect quoting. instead, use the windows
style of simply not doing an expansion if a referenced variable is not
found, which should be good enough - it's rather unlikely that something
which happens to be an expansion of an existing variable is actually not
meant to be one.
parent fb47cfda
......@@ -3213,8 +3213,9 @@
\image qtcreator-build-steps-custom.png "Custom Process Step"
You can use any environment variables as values in the fields. For a list
of variable names, click \gui {Build Environment > Details}. You can specify
variables as ${VARNAME} or %VARNAME%. For example, ${BUILDDIR} or %BUILDDIR%.
of variable names, click \gui {Build Environment > Details}. Variables are
referenced using the platform's native syntax: $VARNAME or ${VARNAME} on
Unix and %VARNAME% on Windows.
\note Qt Creator sets SOURCEDIR and BUILDDIR as part of the build environment.
For more information, see \l{Build Environment}.
......
......@@ -388,69 +388,79 @@ QString Environment::joinArgumentList(const QStringList &arguments)
return result;
}
enum State { BASE, VARIABLE, OPTIONALVARIABLEBRACE, STRING };
/** Expand environment variables in a string.
*
* Environment variables are accepted in the following forms:
* $SOMEVAR, ${SOMEVAR} and %SOMEVAR%.
*
* Strings enclosed in '"' characters do not get varaibles
* substituted.
* $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
* No escapes and quoting are supported.
* If a variable is not found, it is not substituted.
*/
QString Environment::expandVariables(const QString &input) const
{
QString result;
QString variable;
QChar endVariable;
State state = BASE;
QString result = input;
#ifdef Q_OS_WIN
for (int vStart = -1, i = 0; i < result.length(); ) {
if (result.at(i++) == QLatin1Char('%')) {
if (vStart > 0) {
const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1).toUpper());
if (it != m_values.constEnd()) {
result.replace(vStart - 1, i - vStart + 1, *it);
i = vStart - 1 + it->length();
vStart = -1;
} else {
vStart = i;
}
} else {
vStart = i;
}
}
}
#else
enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
int vStart = -1;
int length = input.count();
for (int i = 0; i < length; ++i) {
QChar c = input.at(i);
for (int i = 0; i < result.length();) {
QChar c = result.at(i++);
if (state == BASE) {
if (c == '$') {
if (c == QLatin1Char('$'))
state = OPTIONALVARIABLEBRACE;
variable.clear();
endVariable = QChar(0);
} else if (c == '%') {
} else if (state == OPTIONALVARIABLEBRACE) {
if (c == QLatin1Char('{')) {
state = BRACEDVARIABLE;
vStart = i;
} else if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
state = VARIABLE;
variable.clear();
endVariable = '%';
} else if (c == '\"') {
state = STRING;
result += c;
vStart = i - 1;
} else {
result += c;
}
} else if (state == VARIABLE) {
if (c == endVariable) {
result += value(variable);
state = BASE;
} else if (c.isLetterOrNumber() || c == '_') {
variable += c;
} else {
result += value(variable);
result += c;
}
} else if (state == BRACEDVARIABLE) {
if (c == QLatin1Char('}')) {
const_iterator it = m_values.constFind(result.mid(vStart, i - 1 - vStart));
if (it != constEnd()) {
result.replace(vStart - 2, i - vStart + 2, *it);
i = vStart - 2 + it->length();
}
state = BASE;
}
} else if (state == OPTIONALVARIABLEBRACE) {
if (c == '{')
endVariable = '}';
else
variable = c;
state = VARIABLE;
} else if (state == STRING) {
if (c == '\"') {
} else if (state == VARIABLE) {
if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1));
if (it != constEnd()) {
result.replace(vStart - 1, i - vStart, *it);
i = vStart - 1 + it->length();
}
state = BASE;
result += c;
} else {
result += c;
}
}
}
if (state == VARIABLE)
result += value(variable);
if (state == VARIABLE) {
const_iterator it = m_values.constFind(result.mid(vStart));
if (it != constEnd())
result.replace(vStart - 1, result.length() - vStart + 1, *it);
}
#endif
return result;
}
......
......@@ -57,7 +57,11 @@ const char * const USE_TERMINAL_KEY("ProjectExplorer.CustomExecutableRunConfigur
const char * const USER_ENVIRONMENT_CHANGES_KEY("ProjectExplorer.CustomExecutableRunConfiguration.UserEnvironmentChanges");
const char * const BASE_ENVIRONMENT_BASE_KEY("ProjectExplorer.CustomExecutableRunConfiguration.BaseEnvironmentBase");
#ifdef Q_OS_WIN
const char * const DEFAULT_WORKING_DIR("%BUILDDIR%");
#else
const char * const DEFAULT_WORKING_DIR("$BUILDDIR");
#endif
}
void CustomExecutableRunConfiguration::ctor()
......
......@@ -48,6 +48,12 @@ const char * const PROCESS_COMMAND_KEY("ProjectExplorer.ProcessStep.Command");
const char * const PROCESS_WORKINGDIRECTORY_KEY("ProjectExplorer.ProcessStep.WorkingDirectory");
const char * const PROCESS_ARGUMENTS_KEY("ProjectExplorer.ProcessStep.Arguments");
const char * const PROCESS_ENABLED_KEY("ProjectExplorer.ProcessStep.Enabled");
#ifdef Q_OS_WIN
const char * const DEFAULT_WORKING_DIR("%BUILDDIR%");
#else
const char * const DEFAULT_WORKING_DIR("$BUILDDIR");
#endif
}
ProcessStep::ProcessStep(BuildStepList *bsl) :
......@@ -78,7 +84,7 @@ void ProcessStep::ctor()
//: Default ProcessStep display name
setDefaultDisplayName(tr("Custom Process Step"));
if (m_workingDirectory.isEmpty())
m_workingDirectory = QLatin1String("$BUILDDIR");
m_workingDirectory = QLatin1String(DEFAULT_WORKING_DIR);
}
ProcessStep::~ProcessStep()
......@@ -151,7 +157,7 @@ void ProcessStep::setEnabled(bool enabled)
void ProcessStep::setWorkingDirectory(const QString &workingDirectory)
{
if (workingDirectory.isEmpty())
m_workingDirectory = QLatin1String("$BUILDDIR");
m_workingDirectory = QLatin1String(DEFAULT_WORKING_DIR);
else
m_workingDirectory = workingDirectory;
}
......
......@@ -6,6 +6,7 @@ SUBDIRS += \
cplusplus \
debugger \
extensionsystem \
environment \
fakevim \
generichighlighter \
# icheckbuild \
......
CONFIG += qtestlib testcase
TEMPLATE = app
CONFIG -= app_bundle
DEFINES += QTCREATOR_UTILS_LIB
UTILS_PATH = ../../../src/libs/utils
INCLUDEPATH += $$UTILS_PATH/..
SOURCES += \
tst_environment.cpp \
$$UTILS_PATH/environment.cpp
HEADERS += \
$$UTILS_PATH/environment.h \
$$UTILS_PATH/utils_global.h
TARGET = tst_$$TARGET
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include <utils/environment.h>
#include <QtTest/QtTest>
using namespace Utils;
class tst_Environment : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void environment_data();
void environment();
private:
Environment env;
};
void tst_Environment::initTestCase()
{
env.set("word", "hi");
}
void tst_Environment::environment_data()
{
QTest::addColumn<QString>("in");
QTest::addColumn<QString>("out");
static const struct {
const char * const in;
const char * const out;
} vals[] = {
#ifdef Q_OS_WIN
{ "", "" },
{ "hi", "hi" },
{ "hi%", "hi%" },
{ "hi% ho", "hi% ho" },
{ "hi%here ho", "hi%here ho" },
{ "hi%here%ho", "hi%here%ho" },
{ "hi%word%", "hihi" },
{ "hi%foo%word%", "hi%foohi" },
{ "%word%word%ho", "hiword%ho" },
{ "hi%word%x%word%ho", "hihixhiho" },
{ "hi%word%xx%word%ho", "hihixxhiho" },
{ "hi%word%%word%ho", "hihihiho" },
#else
{ "", "" },
{ "hi", "hi" },
{ "hi$", "hi$" },
{ "hi${", "hi${" },
{ "hi${word", "hi${word" },
{ "hi${word}", "hihi" },
{ "hi${word}ho", "hihiho" },
{ "hi$wo", "hi$wo" },
{ "hi$word", "hihi" },
{ "hi$word ho", "hihi ho" },
{ "$word", "hi" },
{ "hi${word}$word", "hihihi" },
#endif
};
for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++)
QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in)
<< QString::fromLatin1(vals[i].out);
}
void tst_Environment::environment()
{
QFETCH(QString, in);
QFETCH(QString, out);
QCOMPARE(env.expandVariables(in), out);
}
QTEST_MAIN(tst_Environment)
#include "tst_environment.moc"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment