diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp
index 625e80e1d6a72568498c403b80568f3761753e95..48844363422a03703cc5d621e296a6047255ffc3 100644
--- a/src/plugins/projectexplorer/userfileaccessor.cpp
+++ b/src/plugins/projectexplorer/userfileaccessor.cpp
@@ -41,6 +41,7 @@
 #include <coreplugin/icore.h>
 #include <coreplugin/ifile.h>
 #include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
 
 #include <QtGui/QApplication>
 #include <QtGui/QMainWindow>
@@ -218,6 +219,28 @@ public:
     QVariantMap update(Project *project, const QVariantMap &map);
 };
 
+// Version 8 reflects the change of environment variable expansion rules,
+// turning some env variables into expandos, the change of argument quoting rules,
+// and the change of VariableManager's expansion syntax.
+class Version8Handler : public UserFileVersionHandler
+{
+public:
+    int userFileVersion() const
+    {
+        return 8;
+    }
+
+    QString displayUserFileVersion() const
+    {
+        // pre5 because we renamed 2.2 to 2.1 later, so people already have 2.2pre4 files
+        return QLatin1String("2.2pre5");
+    }
+
+    QVariantMap update(Project *project, const QVariantMap &map);
+};
+
+} // namespace
+
 //
 // Helper functions:
 //
@@ -237,7 +260,57 @@ static QString fileNameFor(const QString &name)
     return baseName + QLatin1String(PROJECT_FILE_POSTFIX);
 }
 
-} // namespace
+QT_BEGIN_NAMESPACE
+
+class HandlerNode {
+public:
+    QSet<QString> strings;
+    QHash<QString, HandlerNode> children;
+};
+Q_DECLARE_TYPEINFO(HandlerNode, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+static HandlerNode buildHandlerNodes(const char * const **keys)
+{
+    HandlerNode ret;
+    while (const char *rname = *(*keys)++) {
+        QString name = QLatin1String(rname);
+        if (name.endsWith(QLatin1Char('.'))) {
+            HandlerNode sub = buildHandlerNodes(keys);
+            foreach (const QString &key, name.split(QLatin1Char('|')))
+                ret.children.insert(key, sub);
+        } else {
+            ret.strings.insert(name);
+        }
+    }
+    return ret;
+}
+
+static QVariantMap processHandlerNodes(const HandlerNode &node, const QVariantMap &map,
+                                       QVariant (*handler)(const QVariant &var))
+{
+    QVariantMap result;
+    QMapIterator<QString, QVariant> it(map);
+    while (it.hasNext()) {
+        it.next();
+        const QString &key = it.key();
+        if (node.strings.contains(key)) {
+            result.insert(key, handler(it.value()));
+            goto handled;
+        }
+        if (it.value().type() == QVariant::Map)
+            for (QHash<QString, HandlerNode>::ConstIterator subit = node.children.constBegin();
+                 subit != node.children.constEnd(); ++subit)
+                if (key.startsWith(subit.key())) {
+                    result.insert(key, processHandlerNodes(subit.value(), it.value().toMap(), handler));
+                    goto handled;
+                }
+        result.insert(key, it.value());
+      handled: ;
+    }
+    return result;
+}
 
 // -------------------------------------------------------------------------
 // UserFileVersionHandler
@@ -292,6 +365,7 @@ UserFileAccessor::UserFileAccessor() :
     addVersionHandler(new Version5Handler);
     addVersionHandler(new Version6Handler);
     addVersionHandler(new Version7Handler);
+    addVersionHandler(new Version8Handler);
 }
 
 UserFileAccessor::~UserFileAccessor()
@@ -1419,3 +1493,306 @@ QVariantMap Version7Handler::update(Project *, const QVariantMap &map)
     }
     return result;
 }
+
+// -------------------------------------------------------------------------
+// Version8Handler
+// -------------------------------------------------------------------------
+
+// Argument list reinterpretation
+
+static const char * const argListKeys[] = {
+    "ProjectExplorer.Project.Target.",
+        "ProjectExplorer.Target.BuildConfiguration."
+        "|ProjectExplorer.Target.DeployConfiguration.",
+            "ProjectExplorer.BuildConfiguration.BuildStepList.",
+                "ProjectExplorer.BuildStepList.Step.",
+                    "GenericProjectManager.GenericMakeStep.MakeArguments",
+                    "QtProjectManager.QMakeBuildStep.QMakeArguments",
+                    "Qt4ProjectManager.MakeStep.MakeArguments",
+                    "CMakeProjectManager.MakeStep.AdditionalArguments",
+                    0,
+                0,
+            0,
+        "ProjectExplorer.Target.RunConfiguration.",
+            "ProjectExplorer.CustomExecutableRunConfiguration.Arguments",
+            "Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments",
+            "CMakeProjectManager.CMakeRunConfiguration.Arguments",
+            0,
+        0,
+    0
+};
+
+static const char * const lameArgListKeys[] = {
+    "ProjectExplorer.Project.Target.",
+        "ProjectExplorer.Target.BuildConfiguration."
+        "|ProjectExplorer.Target.DeployConfiguration.",
+            "ProjectExplorer.BuildConfiguration.BuildStepList.",
+                "ProjectExplorer.BuildStepList.Step.",
+                    "ProjectExplorer.ProcessStep.Arguments",
+                    0,
+                0,
+            0,
+        "ProjectExplorer.Target.RunConfiguration.",
+            "Qt4ProjectManager.MaemoRunConfiguration.Arguments",
+            "Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments",
+            "QmlProjectManager.QmlRunConfiguration.QDeclarativeViewerArguments",
+            0,
+        0,
+    0
+};
+
+#ifdef Q_OS_UNIX
+inline static bool isSpecialChar(ushort c)
+{
+    // Chars that should be quoted (TM). This includes:
+    static const uchar iqm[] = {
+        0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
+        0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
+    }; // 0-32 \'"$`<>|;&(){}*?#!~[]
+
+    return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
+}
+
+inline static bool hasSpecialChars(const QString &arg)
+{
+    for (int x = arg.length() - 1; x >= 0; --x)
+        if (isSpecialChar(arg.unicode()[x].unicode()))
+            return true;
+    return false;
+}
+#endif
+
+// These were split according to sane (even if a bit arcane) rules
+static QVariant version8ArgNodeHandler(const QVariant &var)
+{
+    QString ret;
+    foreach (const QVariant &svar, var.toList()) {
+#ifdef Q_OS_UNIX
+        // We don't just addArg, so we don't disarm existing env expansions.
+        // This is a bit fuzzy logic ...
+        QString s = svar.toString();
+        s.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
+        s.replace(QLatin1Char('"'), QLatin1String("\\\""));
+        s.replace(QLatin1Char('`'), QLatin1String("\\`"));
+        if (s != svar.toString() || hasSpecialChars(s))
+            s.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
+        Utils::QtcProcess::addArgs(&ret, s);
+#else
+        // Under windows, env expansions cannot be quoted anyway.
+        Utils::QtcProcess::addArg(&ret, svar.toString());
+#endif
+    }
+    return QVariant(ret);
+}
+
+// These were just split on whitespace
+static QVariant version8LameArgNodeHandler(const QVariant &var)
+{
+    QString ret;
+    foreach (const QVariant &svar, var.toList())
+        Utils::QtcProcess::addArgs(&ret, svar.toString());
+    return QVariant(ret);
+}
+
+// Environment variable reinterpretation
+
+static const char * const envExpandedKeys[] = {
+    "ProjectExplorer.Project.Target.",
+        "ProjectExplorer.Target.BuildConfiguration."
+        "|ProjectExplorer.Target.DeployConfiguration.",
+            "ProjectExplorer.BuildConfiguration.BuildStepList.",
+                "ProjectExplorer.BuildStepList.Step.",
+                    "ProjectExplorer.ProcessStep.WorkingDirectory",
+                    "ProjectExplorer.ProcessStep.Command",
+                    "ProjectExplorer.ProcessStep.Arguments",
+                    "GenericProjectManager.GenericMakeStep.MakeCommand",
+                    "GenericProjectManager.GenericMakeStep.MakeArguments",
+                    "GenericProjectManager.GenericMakeStep.BuildTargets",
+                    "QtProjectManager.QMakeBuildStep.QMakeArguments",
+                    "Qt4ProjectManager.MakeStep.MakeCommand",
+                    "Qt4ProjectManager.MakeStep.MakeArguments",
+                    "CMakeProjectManager.MakeStep.AdditionalArguments",
+                    "CMakeProjectManager.MakeStep.BuildTargets",
+                    0,
+                0,
+            "Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory",
+            0,
+        "ProjectExplorer.Target.RunConfiguration.",
+            "ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory",
+            "ProjectExplorer.CustomExecutableRunConfiguration.Executable",
+            "ProjectExplorer.CustomExecutableRunConfiguration.Arguments",
+            "Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory",
+            "Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments",
+            "Qt4ProjectManager.MaemoRunConfiguration.Arguments",
+            "Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments",
+            "CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory",
+            "CMakeProjectManager.CMakeRunConfiguration.Arguments",
+            0,
+        0,
+    0
+};
+
+static QString version8NewVar(const QString &old)
+{
+    QString ret = old;
+#ifdef Q_OS_UNIX
+    ret.prepend(QLatin1String("${"));
+    ret.append(QLatin1Char('}'));
+#else
+    ret.prepend(QLatin1Char('%'));
+    ret.append(QLatin1Char('%'));
+#endif
+    return ret;
+}
+
+// Translate DOS-like env var expansions into Unix-like ones and vice versa.
+// On the way, change {SOURCE,BUILD}DIR env expansions to %{}-expandos
+static QVariant version8EnvNodeTransform(const QVariant &var)
+{
+    QString result = var.toString();
+
+    result.replace(QRegExp(QLatin1String("%SOURCEDIR%|\\$(SOURCEDIR\\b|{SOURCEDIR})")),
+                   QLatin1String("%{sourceDir}"));
+    result.replace(QRegExp(QLatin1String("%BUILDDIR%|\\$(BUILDDIR\\b|{BUILDDIR})")),
+                   QLatin1String("%{buildDir}"));
+#ifdef Q_OS_UNIX
+    for (int vStart = -1, i = 0; i < result.length(); ) {
+        QChar c = result.at(i++);
+        if (c == QLatin1Char('%')) {
+            if (vStart > 0 && vStart < i - 1) {
+                QString nv = version8NewVar(result.mid(vStart, i - 1 - vStart));
+                result.replace(vStart - 1, i - vStart + 1, nv);
+                i = vStart - 1 + nv.length();
+                vStart = -1;
+            } else {
+                vStart = i;
+            }
+        } else if (vStart > 0) {
+            // Sanity check so we don't catch too much garbage
+            if (!c.isLetterOrNumber() && c != QLatin1Char('_'))
+                vStart = -1;
+        }
+    }
+#else
+    enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
+    int vStart = -1;
+
+    for (int i = 0; i < result.length();) {
+        QChar c = result.at(i++);
+        if (state == BASE) {
+            if (c == QLatin1Char('$'))
+                state = OPTIONALVARIABLEBRACE;
+        } else if (state == OPTIONALVARIABLEBRACE) {
+            if (c == QLatin1Char('{')) {
+                state = BRACEDVARIABLE;
+                vStart = i;
+            } else if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
+                state = VARIABLE;
+                vStart = i - 1;
+            } else {
+                state = BASE;
+            }
+        } else if (state == BRACEDVARIABLE) {
+            if (c == QLatin1Char('}')) {
+                QString nv = version8NewVar(result.mid(vStart, i - 1 - vStart));
+                result.replace(vStart - 2, i - vStart + 2, nv);
+                i = vStart + nv.length();
+                state = BASE;
+            }
+        } else if (state == VARIABLE) {
+            if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
+                QString nv = version8NewVar(result.mid(vStart, i - 1 - vStart));
+                result.replace(vStart - 1, i - vStart, nv);
+                i = vStart - 1 + nv.length(); // On the same char - could be next expansion.
+                state = BASE;
+            }
+        }
+    }
+    if (state == VARIABLE) {
+        QString nv = version8NewVar(result.mid(vStart));
+        result.truncate(vStart - 1);
+        result += nv;
+    }
+#endif
+
+    return QVariant(result);
+}
+
+static QVariant version8EnvNodeHandler(const QVariant &var)
+{
+    if (var.type() != QVariant::List)
+        return version8EnvNodeTransform(var);
+
+    QVariantList vl;
+    foreach (const QVariant &svar, var.toList())
+        vl << version8EnvNodeTransform(svar);
+    return vl;
+}
+
+// VariableManager expando reinterpretation
+
+static const char * const varExpandedKeys[] = {
+    "ProjectExplorer.Project.Target.",
+        "ProjectExplorer.Target.BuildConfiguration."
+        "|ProjectExplorer.Target.DeployConfiguration.",
+            "ProjectExplorer.BuildConfiguration.BuildStepList.",
+                "ProjectExplorer.BuildStepList.Step.",
+                    "GenericProjectManager.GenericMakeStep.MakeCommand",
+                    "GenericProjectManager.GenericMakeStep.MakeArguments",
+                    "GenericProjectManager.GenericMakeStep.BuildTargets",
+                    0,
+                0,
+            0,
+        0,
+    0
+};
+
+// Translate old-style ${} var expansions into new-style %{} ones
+static QVariant version8VarNodeHandler(const QVariant &var)
+{
+    static const char * const vars[] = {
+        "absoluteFilePath",
+        "absolutePath",
+        "baseName",
+        "canonicalPath",
+        "canonicalFilePath",
+        "completeBaseName",
+        "completeSuffix",
+        "fileName",
+        "filePath",
+        "path",
+        "suffix"
+    };
+    static QSet<QString> map;
+    if (map.isEmpty())
+        for (unsigned i = 0; i < sizeof(vars)/sizeof(vars[0]); i++)
+            map.insert(QLatin1String("CURRENT_DOCUMENT:") + QLatin1String(vars[i]));
+
+    QString str = var.toString();
+    int pos = 0;
+    forever {
+        int openPos = str.indexOf(QLatin1String("${"), pos);
+        if (openPos < 0)
+            break;
+        int varPos = openPos + 2;
+        int closePos = str.indexOf(QLatin1Char('}'), varPos);
+        if (closePos < 0)
+            break;
+        if (map.contains(str.mid(varPos, closePos - varPos)))
+            str[openPos] = QLatin1Char('%');
+        pos = closePos + 1;
+    }
+    return QVariant(str);
+}
+
+QVariantMap Version8Handler::update(Project *, const QVariantMap &map)
+{
+    const char * const *p1 = argListKeys;
+    QVariantMap rmap1 = processHandlerNodes(buildHandlerNodes(&p1), map, version8ArgNodeHandler);
+    const char * const *p2 = lameArgListKeys;
+    QVariantMap rmap2 = processHandlerNodes(buildHandlerNodes(&p2), rmap1, version8LameArgNodeHandler);
+    const char * const *p3 = envExpandedKeys;
+    QVariantMap rmap3 = processHandlerNodes(buildHandlerNodes(&p3), rmap2, version8EnvNodeHandler);
+    const char * const *p4 = varExpandedKeys;
+    return processHandlerNodes(buildHandlerNodes(&p4), rmap3, version8VarNodeHandler);
+}