diff --git a/src/shared/proparser/abstractproitemvisitor.h b/src/shared/proparser/abstractproitemvisitor.h
index 9e8e0582b28c6b04da178cf8737bddf91e9cc32b..85bfe42ea615d14f2e534da22f44d3e97e3091eb 100644
--- a/src/shared/proparser/abstractproitemvisitor.h
+++ b/src/shared/proparser/abstractproitemvisitor.h
@@ -41,6 +41,9 @@ struct AbstractProItemVisitor
     virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0;
     virtual void visitEndProBlock(ProBlock *block) = 0;
 
+    virtual ProItem::ProItemReturn visitProLoopIteration() = 0;
+    virtual void visitProLoopCleanup() = 0;
+
     virtual void visitBeginProVariable(ProVariable *variable) = 0;
     virtual void visitEndProVariable(ProVariable *variable) = 0;
 
diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp
index 5887cc3f252786074896fc0454ddf88946ea8278..58d737221e5811c1b03a6cd9afee8683ae329c2c 100644
--- a/src/shared/proparser/profileevaluator.cpp
+++ b/src/shared/proparser/profileevaluator.cpp
@@ -159,6 +159,8 @@ public:
     // implementation of AbstractProItemVisitor
     ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
     void visitEndProBlock(ProBlock *block);
+    ProItem::ProItemReturn visitProLoopIteration();
+    void visitProLoopCleanup();
     void visitBeginProVariable(ProVariable *variable);
     void visitEndProVariable(ProVariable *variable);
     ProItem::ProItemReturn visitBeginProFile(ProFile *value);
@@ -180,6 +182,7 @@ public:
     void expandPatternHelper(const QString &relName, const QString &absName,
         QStringList &sources_out);
     QStringList expandVariableReferences(const QString &value);
+    void doVariableReplace(QString *str);
     QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
     QString format(const char *format) const;
 
@@ -211,6 +214,14 @@ public:
     QString m_origfile;
     QString m_oldPath;                              // To restore the current path to the path
     QStack<ProFile*> m_profileStack;                // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
+    struct ProLoop {
+        QString variable;
+        QStringList oldVarVal;
+        QStringList list;
+        int index;
+        bool infinite;
+    };
+    QStack<ProLoop> m_loopStack;
 
     // we need the following two variables for handling
     // CONFIG = foo bar $$CONFIG
@@ -238,6 +249,7 @@ public:
 };
 
 Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
 
 ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
   : q(q_)
@@ -618,6 +630,36 @@ void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
     }
 }
 
+ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration()
+{
+    ProLoop &loop = m_loopStack.top();
+
+    if (loop.infinite) {
+        if (!loop.variable.isEmpty())
+            m_valuemap[loop.variable] = QStringList(QString::number(loop.index++));
+        if (loop.index > 1000) {
+            q->errorMessage(format("ran into infinite loop (> 1000 iterations)."));
+            return ProItem::ReturnFalse;
+        }
+    } else {
+        QString val;
+        do {
+            if (loop.index >= loop.list.count())
+                return ProItem::ReturnFalse;
+            val = loop.list.at(loop.index++);
+        } while (val.isEmpty()); // stupid, but qmake is like that
+        m_valuemap[loop.variable] = QStringList(val);
+    }
+    return ProItem::ReturnTrue;
+}
+
+void ProFileEvaluator::Private::visitProLoopCleanup()
+{
+    ProLoop &loop = m_loopStack.top();
+    m_valuemap[loop.variable] = loop.oldVarVal;
+    m_loopStack.pop_back();
+}
+
 void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
 {
     m_lastVarName = variable->variable();
@@ -1037,6 +1079,11 @@ QString ProFileEvaluator::Private::currentDirectory() const
     return cur->directoryName();
 }
 
+void ProFileEvaluator::Private::doVariableReplace(QString *str)
+{
+    *str = expandVariableReferences(*str).join(QString(Option::field_sep));
+}
+
 QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
 {
     QStringList ret;
@@ -1738,7 +1785,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
                     T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
                     T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
                     T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF,
-                    T_DEFINE_TEST, T_DEFINE_REPLACE };
+                    T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE };
 
     static QHash<QString, int> *functions = 0;
     if (!functions) {
@@ -1771,6 +1818,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
         functions->insert(QLatin1String("message"), T_MESSAGE);   //v
         functions->insert(QLatin1String("warning"), T_MESSAGE);   //v
         functions->insert(QLatin1String("error"), T_MESSAGE);     //v
+        functions->insert(QLatin1String("for"), T_FOR);     //v
         functions->insert(QLatin1String("defineTest"), T_DEFINE_TEST);        //v
         functions->insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE);  //v
     }
@@ -1835,9 +1883,79 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
         case T_INFILE:
         case T_REQUIRES:
         case T_EVAL:
+#endif
+        case T_FOR: {
+            if (m_cumulative) // This is a no-win situation, so just pretend it's no loop
+                return ProItem::ReturnTrue;
+            if (m_skipLevel)
+                return ProItem::ReturnFalse;
+            if (args.count() > 2 || args.count() < 1) {
+                q->logMessage(format("for({var, list|var, forever|ever})"
+                                     " requires one or two arguments."));
+                return ProItem::ReturnFalse;
+            }
+            ProLoop loop;
+            loop.infinite = false;
+            loop.index = 0;
+            QString it_list;
+            if (args.count() == 1) {
+                doVariableReplace(&args[0]);
+                it_list = args[0];
+                if (args[0] != QLatin1String("ever")) {
+                    q->logMessage(format("for({var, list|var, forever|ever})"
+                                         " requires one or two arguments."));
+                    return ProItem::ReturnFalse;
+                }
+                it_list = QLatin1String("forever");
+            } else {
+                loop.variable = args[0];
+                loop.oldVarVal = m_valuemap.value(loop.variable);
+                doVariableReplace(&args[1]);
+                it_list = args[1];
+            }
+            loop.list = m_valuemap[it_list];
+            if (loop.list.isEmpty()) {
+                if (it_list == QLatin1String("forever")) {
+                    loop.infinite = true;
+                } else {
+                    int dotdot = it_list.indexOf(QLatin1String(".."));
+                    if (dotdot != -1) {
+                        bool ok;
+                        int start = it_list.left(dotdot).toInt(&ok);
+                        if (ok) {
+                            int end = it_list.mid(dotdot+2).toInt(&ok);
+                            if (ok) {
+                                if (start < end) {
+                                    for (int i = start; i <= end; i++)
+                                        loop.list << QString::number(i);
+                                } else {
+                                    for (int i = start; i >= end; i--)
+                                        loop.list << QString::number(i);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            m_loopStack.push(loop);
+            m_sts.condition = true;
+            return ProItem::ReturnLoop;
+        }
         case T_BREAK:
+            if (m_skipLevel)
+                return ProItem::ReturnFalse;
+            if (!m_loopStack.isEmpty())
+                return ProItem::ReturnBreak;
+            // ### missing: breaking out of multiline blocks
+            q->logMessage(format("unexpected break()."));
+            return ProItem::ReturnFalse;
         case T_NEXT:
-#endif
+            if (m_skipLevel)
+                return ProItem::ReturnFalse;
+            if (!m_loopStack.isEmpty())
+                return ProItem::ReturnNext;
+            q->logMessage(format("unexpected next()."));
+            return ProItem::ReturnFalse;
         case T_IF: {
             if (args.count() != 1) {
                 q->logMessage(format("if(condition) requires one argument."));
diff --git a/src/shared/proparser/proitems.cpp b/src/shared/proparser/proitems.cpp
index 3b52a64a42f0c43b284664cea25f302c6a2ddc0e..0d2a2f688039d85872d9b3765f8dace4f75da1c8 100644
--- a/src/shared/proparser/proitems.cpp
+++ b/src/shared/proparser/proitems.cpp
@@ -108,9 +108,30 @@ ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
     if (visitor->visitBeginProBlock(this) == ReturnSkip)
         return ReturnTrue;
     ProItemReturn rt = ReturnTrue;
-    foreach (ProItem *item, m_proitems)
-        if ((rt = item->Accept(visitor)) != ReturnTrue && rt != ReturnFalse)
+    for (int i = 0; i < m_proitems.count(); ++i) {
+        rt = m_proitems.at(i)->Accept(visitor);
+        if (rt != ReturnTrue && rt != ReturnFalse) {
+            if (rt == ReturnLoop) {
+                rt = ReturnTrue;
+                while (visitor->visitProLoopIteration())
+                    for (int j = i; ++j < m_proitems.count(); ) {
+                        rt = m_proitems.at(j)->Accept(visitor);
+                        if (rt != ReturnTrue && rt != ReturnFalse) {
+                            if (rt == ReturnNext) {
+                                rt = ReturnTrue;
+                                break;
+                            }
+                            if (rt == ReturnBreak)
+                                rt = ReturnTrue;
+                            goto do_break;
+                        }
+                    }
+              do_break:
+                visitor->visitProLoopCleanup();
+            }
             break;
+        }
+    }
     visitor->visitEndProBlock(this);
     return rt;
 }
diff --git a/src/shared/proparser/proitems.h b/src/shared/proparser/proitems.h
index b0aceef817965f71f157a1de1001a59b8fd118bb..db03eccd7730ac3dd586c6667e7d3530a8d43136 100644
--- a/src/shared/proparser/proitems.h
+++ b/src/shared/proparser/proitems.h
@@ -52,6 +52,9 @@ public:
     enum ProItemReturn {
         ReturnFalse,
         ReturnTrue,
+        ReturnBreak,
+        ReturnNext,
+        ReturnLoop,
         ReturnSkip,
         ReturnReturn
    };