profileevaluator.cpp 104 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** 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.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
26
** contact the sales department at http://www.qtsoftware.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
29

con's avatar
con committed
30
31
32
33
#include "profileevaluator.h"
#include "proitems.h"

#include <QtCore/QByteArray>
34
#include <QtCore/QDateTime>
con's avatar
con committed
35
36
37
38
39
40
41
42
43
44
45
46
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QList>
#include <QtCore/QRegExp>
#include <QtCore/QSet>
#include <QtCore/QStack>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>

47
48
49
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/utsname.h>
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
50
#else
51
52
53
54
55
#include <Windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>

con's avatar
con committed
56
57
#ifdef Q_OS_WIN32
#define QT_POPEN _popen
58
#define QT_PCLOSE _pclose
con's avatar
con committed
59
60
#else
#define QT_POPEN popen
61
#define QT_PCLOSE pclose
con's avatar
con committed
62
63
64
65
#endif

QT_BEGIN_NAMESPACE

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
66
67
68
69
70
71
72
73
74
75
76
77
namespace {
    template<class K, class T> void updateHash(QHash<K,T> *out, const QHash<K,T> &in)
    {
        typename QHash<K,T>::const_iterator i = in.begin();
        while (i != in.end()) {
            out->insert(i.key(), i.value());
            ++i;
        }
    }
} // anon namespace


78
79
80
81
82
83
///////////////////////////////////////////////////////////////////////
//
// Option
//
///////////////////////////////////////////////////////////////////////

84
85
86
87
88
89
90
91
92
93
94
ProFileEvaluator::Option::Option()
{
#ifdef Q_OS_WIN
    dirlist_sep = QLatin1Char(';');
    dir_sep = QLatin1Char('\\');
#else
    dirlist_sep = QLatin1Char(':');
    dir_sep = QLatin1Char('/');
#endif
    qmakespec = QString::fromLatin1(qgetenv("QMAKESPEC").data());

95
#if defined(Q_OS_WIN32)
96
    target_mode = TARG_WIN_MODE;
97
#elif defined(Q_OS_MAC)
98
    target_mode = TARG_MACX_MODE;
99
#elif defined(Q_OS_QNX6)
100
    target_mode = TARG_QNX6_MODE;
101
#else
102
    target_mode = TARG_UNIX_MODE;
103
104
#endif

105
106
107
108
    field_sep = QLatin1String(" ");
}

QString ProFileEvaluator::Option::field_sep;
109

con's avatar
con committed
110
111
112
113
114
115
116
117
118
///////////////////////////////////////////////////////////////////////
//
// ProFileEvaluator::Private
//
///////////////////////////////////////////////////////////////////////

class ProFileEvaluator::Private : public AbstractProItemVisitor
{
public:
119
    Private(ProFileEvaluator *q_, ProFileEvaluator::Option *option);
con's avatar
con committed
120

121
122
123
124
125
126
    ProFileEvaluator *q;
    int m_lineNo;                                   // Error reporting
    bool m_verbose;

    /////////////// Reading pro file

con's avatar
con committed
127
    bool read(ProFile *pro);
128
129
    bool read(ProFile *pro, const QString &content);
    bool read(ProFile *pro, QTextStream *ts);
con's avatar
con committed
130
131
132

    ProBlock *currentBlock();
    void updateItem();
133
    void insertVariable(const ushort **pCur, const ushort *end);
con's avatar
con committed
134
135
136
137
138
139
    void insertOperator(const char op);
    void insertComment(const QString &comment);
    void enterScope(bool multiLine);
    void leaveScope();
    void finalizeBlock();

140
141
142
143
144
145
    QStack<ProBlock *> m_blockstack;
    ProBlock *m_block;

    ProItem *m_commentItem;
    QString m_proitem;
    QString m_pendingComment;
146
    ushort *m_proitemPtr;
147

148
    enum StrState { NeverStarted, NotStarted, Started, PutSpace };
149

150
151
    /////////////// Evaluating pro file contents

con's avatar
con committed
152
    // implementation of AbstractProItemVisitor
153
    ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
154
    void visitEndProBlock(ProBlock *block);
155
156
    ProItem::ProItemReturn visitProLoopIteration();
    void visitProLoopCleanup();
157
158
    void visitBeginProVariable(ProVariable *variable);
    void visitEndProVariable(ProVariable *variable);
159
160
    ProItem::ProItemReturn visitBeginProFile(ProFile *value);
    ProItem::ProItemReturn visitEndProFile(ProFile *value);
161
    void visitProValue(ProValue *value);
162
    ProItem::ProItemReturn visitProFunction(ProFunction *function);
163
164
    void visitProOperator(ProOperator *oper);
    void visitProCondition(ProCondition *condition);
con's avatar
con committed
165

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
166
    QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
con's avatar
con committed
167
168
    QStringList values(const QString &variableName) const;
    QStringList values(const QString &variableName, const ProFile *pro) const;
169
170
    QStringList values(const QString &variableName, const QHash<QString, QStringList> &place,
                       const ProFile *pro) const;
171
    QString propertyValue(const QString &val, bool complain = true) const;
con's avatar
con committed
172

173
174
    QStringList split_value_list(const QString &vals, bool do_semicolon = false);
    QStringList split_arg_list(const QString &params);
con's avatar
con committed
175
176
    bool isActiveConfig(const QString &config, bool regex = false);
    QStringList expandVariableReferences(const QString &value);
177
    void doVariableReplace(QString *str);
con's avatar
con committed
178
179
180
181
    QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
    QString format(const char *format) const;

    QString currentFileName() const;
182
    QString currentDirectory() const;
con's avatar
con committed
183
184
    ProFile *currentProFile() const;

185
    ProItem::ProItemReturn evaluateConditionalFunction(const QString &function, const QString &arguments);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
186
    bool evaluateFile(const QString &fileName);
187
188
    bool evaluateFeatureFile(const QString &fileName, QHash<QString, QStringList> *values = 0);
    bool evaluateFileInto(const QString &fileName, QHash<QString, QStringList> *values);
con's avatar
con committed
189

190
191
192
193
194
    static inline ProItem::ProItemReturn returnBool(bool b)
        { return b ? ProItem::ReturnTrue : ProItem::ReturnFalse; }

    QStringList evaluateFunction(ProBlock *funcPtr, const QStringList &argumentsList, bool *ok);

195
196
    QStringList qmakeMkspecPaths() const;
    QStringList qmakeFeaturePaths() const;
con's avatar
con committed
197

198
    struct State {
199
200
        bool condition;
        bool prevCondition;
201
        QStringList varVal;
202
203
    } m_sts;
    bool m_invertNext; // Short-lived, so not in State
204
205
    int m_skipLevel;
    bool m_cumulative;
206
    QStack<QString> m_oldPathStack;                 // To restore the current path to the path
con's avatar
con committed
207
    QStack<ProFile*> m_profileStack;                // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
208
209
210
211
212
213
214
215
    struct ProLoop {
        QString variable;
        QStringList oldVarVal;
        QStringList list;
        int index;
        bool infinite;
    };
    QStack<ProLoop> m_loopStack;
con's avatar
con committed
216
217
218

    QHash<QString, QStringList> m_valuemap;         // VariableName must be us-ascii, the content however can be non-us-ascii.
    QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
219
    QString m_outputDir;
con's avatar
con committed
220

221
222
223
224
225
226
227
228
    bool m_definingTest;
    QString m_definingFunc;
    QHash<QString, ProBlock *> m_testFunctions;
    QHash<QString, ProBlock *> m_replaceFunctions;
    QStringList m_returnValue;
    QStack<QHash<QString, QStringList> > m_valuemapStack;
    QStack<QHash<const ProFile*, QHash<QString, QStringList> > > m_filevaluemapStack;

229
230
    QStringList m_addUserConfigCmdArgs;
    QStringList m_removeUserConfigCmdArgs;
231
    bool m_parsePreAndPostFiles;
232
233

    Option *m_option;
con's avatar
con committed
234
235
};

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
236
#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
237
Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
238
Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
239
#endif
240

241
242
ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileEvaluator::Option *option)
  : q(q_), m_option(option)
con's avatar
con committed
243
{
244
    // Configuration, more or less
con's avatar
con committed
245
    m_verbose = true;
246
    m_cumulative = true;
247
    m_parsePreAndPostFiles = true;
248
249

    // Evaluator state
250
251
    m_sts.condition = false;
    m_sts.prevCondition = false;
252
253
    m_invertNext = false;
    m_skipLevel = 0;
254
    m_definingFunc.clear();
con's avatar
con committed
255
256
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
257
258
////////// Parser ///////////

con's avatar
con committed
259
260
261
262
263
264
265
266
bool ProFileEvaluator::Private::read(ProFile *pro)
{
    QFile file(pro->fileName());
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        q->errorMessage(format("%1 not readable.").arg(pro->fileName()));
        return false;
    }

267
268
269
270
271
272
273
274
275
276
277
278
279
    QTextStream ts(&file);
    return read(pro, &ts);
}

bool ProFileEvaluator::Private::read(ProFile *pro, const QString &content)
{
    QString str(content);
    QTextStream ts(&str, QIODevice::ReadOnly | QIODevice::Text);
    return read(pro, &ts);
}

bool ProFileEvaluator::Private::read(ProFile *pro, QTextStream *ts)
{
280
281
282
    // Parser state
    m_block = 0;
    m_commentItem = 0;
con's avatar
con committed
283
    m_lineNo = 1;
284
    m_blockstack.clear();
con's avatar
con committed
285
286
    m_blockstack.push(pro);

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
287
288
    int parens = 0;
    bool inQuote = false;
289
290
    while (!ts->atEnd()) {
        QString line = ts->readLine();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
291
        const ushort *cur = (const ushort *)line.unicode(),
292
293
                     *end = cur + line.length(),
                     *cmtptr = 0;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
294
295
        m_proitem.reserve(line.length());
        m_proitemPtr = (ushort *)m_proitem.unicode();
296
297
298
        enum { NotEscaped, Escaped, PostEscaped } escaped = NotEscaped;
        StrState sts = NeverStarted;
        goto startItem;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
299
      nextItem:
300
301
302
303
        escaped = NotEscaped;
      nextItem1:
        sts = NotStarted;
      startItem:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
304
305
306
307
        ushort *ptr = m_proitemPtr;
        while (cur < end) {
            ushort c = *cur++;
            if (c == '#') { // Yep - no escaping possible
308
309
                cmtptr = cur;
                break;
con's avatar
con committed
310
            }
311
            if (escaped != Escaped) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
312
                if (c == '\\') {
313
                    escaped = Escaped;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
314
315
316
                    goto putch;
                } else if (c == '"') {
                    inQuote = !inQuote;
317
                    goto putch1;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
318
319
320
321
322
323
324
325
326
327
328
329
                }
            }
            if (!inQuote) {
                if (c == '(') {
                    ++parens;
                } else if (c == ')') {
                    --parens;
                } else if (!parens) {
                    if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
                        if (c == ' ' || c == '\t') {
                            m_proitemPtr = ptr;
                            updateItem();
330
331
332
                            if (escaped == Escaped)
                                escaped = PostEscaped;
                            goto nextItem1;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
                        }
                    } else {
                        if (c == ':') {
                            m_proitemPtr = ptr;
                            enterScope(false);
                            goto nextItem;
                        }
                        if (c == '{') {
                            m_proitemPtr = ptr;
                            enterScope(true);
                            goto nextItem;
                        }
                        if (c == '}') {
                            m_proitemPtr = ptr;
                            leaveScope();
                            goto nextItem;
                        }
                        if (c == '=') {
                            m_proitemPtr = ptr;
                            insertVariable(&cur, end);
                            goto nextItem;
                        }
                        if (c == '|' || c == '!') {
                            m_proitemPtr = ptr;
                            insertOperator(c);
                            goto nextItem;
                        }
360
                    }
361
                }
con's avatar
con committed
362
363
            }

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
364
            if (c == ' ' || c == '\t') {
365
                if (sts == Started) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
366
                    sts = PutSpace;
367
368
369
                    if (escaped == Escaped)
                        escaped = PostEscaped;
                }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
370
            } else {
371
372
              putch1:
                escaped = NotEscaped;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
373
374
375
376
377
378
379
              putch:
                if (sts == PutSpace)
                    *ptr++ = ' ';
                *ptr++ = c;
                sts = Started;
            }
        }
380
381
382
383
384
        if (escaped != NotEscaped) {
            --ptr;
            if (ptr != (ushort *)m_proitem.unicode() && *(ptr - 1) == ' ')
                --ptr;
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
385
        m_proitemPtr = ptr;
386
387
388
389
        updateItem();
        if (cmtptr)
            insertComment(line.right(end - cmtptr).simplified());
        if (sts != NeverStarted && escaped == NotEscaped)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
390
391
            finalizeBlock();
        ++m_lineNo;
con's avatar
con committed
392
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
393
    m_proitem.clear(); // Throw away pre-allocation
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
394
    return true;
con's avatar
con committed
395
396
397
398
}

void ProFileEvaluator::Private::finalizeBlock()
{
399
400
401
402
    if (m_blockstack.top()->blockKind() & ProBlock::SingleLine)
        leaveScope();
    m_block = 0;
    m_commentItem = 0;
con's avatar
con committed
403
404
}

405
void ProFileEvaluator::Private::insertVariable(const ushort **pCur, const ushort *end)
con's avatar
con committed
406
407
{
    ProVariable::VariableOperator opkind;
408
409
    ushort *uc = (ushort *)m_proitem.unicode();
    ushort *ptr = m_proitemPtr;
con's avatar
con committed
410

411
    if (ptr == uc) // Line starting with '=', like a conflict marker
412
413
        return;

414
    switch (*(ptr - 1)) {
con's avatar
con committed
415
        case '+':
416
            --ptr;
con's avatar
con committed
417
418
419
            opkind = ProVariable::AddOperator;
            break;
        case '-':
420
            --ptr;
con's avatar
con committed
421
422
423
            opkind = ProVariable::RemoveOperator;
            break;
        case '*':
424
            --ptr;
con's avatar
con committed
425
426
427
            opkind = ProVariable::UniqueAddOperator;
            break;
        case '~':
428
            --ptr;
con's avatar
con committed
429
430
431
432
433
434
            opkind = ProVariable::ReplaceOperator;
            break;
        default:
            opkind = ProVariable::SetOperator;
    }

435
436
437
438
439
440
    while (ptr != uc && *(ptr - 1) == ' ')
        --ptr;
    m_proitem.resize(ptr - uc);
    QString proVar = m_proitem;
    proVar.detach();

con's avatar
con committed
441
    ProBlock *block = m_blockstack.top();
442
    ProVariable *variable = new ProVariable(proVar, block);
con's avatar
con committed
443
444
445
446
447
448
449
450
451
452
453
454
455
    variable->setLineNumber(m_lineNo);
    variable->setVariableOperator(opkind);
    block->appendItem(variable);
    m_block = variable;

    if (!m_pendingComment.isEmpty()) {
        m_block->setComment(m_pendingComment);
        m_pendingComment.clear();
    }
    m_commentItem = variable;

    if (opkind == ProVariable::ReplaceOperator) {
        // skip util end of line or comment
456
457
458
459
460
461
        StrState sts = NotStarted;
        ptr = uc;
        const ushort *cur = *pCur;
        while (cur < end) {
            ushort c = *cur;
            if (c == '#') // comment?
con's avatar
con committed
462
                break;
463
464
465
466
467
468
469
470
471
472
            ++cur;

            if (c == ' ' || c == '\t') {
                if (sts == Started)
                    sts = PutSpace;
            } else {
                if (sts == PutSpace)
                    *ptr++ = ' ';
                *ptr++ = c;
                sts = Started;
con's avatar
con committed
473
474
            }
        }
475
476
477
478
        *pCur = cur;
        m_proitemPtr = ptr;
    } else {
        m_proitemPtr = uc;
con's avatar
con committed
479
480
481
482
483
484
485
486
    }
}

void ProFileEvaluator::Private::insertOperator(const char op)
{
    updateItem();

    ProOperator::OperatorKind opkind;
hjk's avatar
hjk committed
487
    switch (op) {
con's avatar
con committed
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
        case '!':
            opkind = ProOperator::NotOperator;
            break;
        case '|':
            opkind = ProOperator::OrOperator;
            break;
        default:
            opkind = ProOperator::OrOperator;
    }

    ProBlock * const block = currentBlock();
    ProOperator * const proOp = new ProOperator(opkind);
    proOp->setLineNumber(m_lineNo);
    block->appendItem(proOp);
    m_commentItem = proOp;
}

void ProFileEvaluator::Private::insertComment(const QString &comment)
{
    QString strComment;
    if (!m_commentItem)
        strComment = m_pendingComment;
    else
        strComment = m_commentItem->comment();

    if (strComment.isEmpty())
        strComment = comment;
    else {
        strComment += QLatin1Char('\n');
        strComment += comment.trimmed();
    }

    strComment = strComment.trimmed();

    if (!m_commentItem)
        m_pendingComment = strComment;
    else
        m_commentItem->setComment(strComment);
}

void ProFileEvaluator::Private::enterScope(bool multiLine)
{
    updateItem();

    ProBlock *parent = currentBlock();
    ProBlock *block = new ProBlock(parent);
    block->setLineNumber(m_lineNo);
    parent->setBlockKind(ProBlock::ScopeKind);

    parent->appendItem(block);

    if (multiLine)
        block->setBlockKind(ProBlock::ScopeContentsKind);
    else
        block->setBlockKind(ProBlock::ScopeContentsKind|ProBlock::SingleLine);

    m_blockstack.push(block);
    m_block = 0;
}

void ProFileEvaluator::Private::leaveScope()
{
    updateItem();
551
552
553
554
    if (m_blockstack.count() == 1)
        q->errorMessage(format("Excess closing brace."));
    else
        m_blockstack.pop();
con's avatar
con committed
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
    finalizeBlock();
}

ProBlock *ProFileEvaluator::Private::currentBlock()
{
    if (m_block)
        return m_block;

    ProBlock *parent = m_blockstack.top();
    m_block = new ProBlock(parent);
    m_block->setLineNumber(m_lineNo);
    parent->appendItem(m_block);

    if (!m_pendingComment.isEmpty()) {
        m_block->setComment(m_pendingComment);
        m_pendingComment.clear();
    }

    m_commentItem = m_block;

    return m_block;
}

void ProFileEvaluator::Private::updateItem()
{
580
581
582
583
    ushort *uc = (ushort *)m_proitem.unicode();
    ushort *ptr = m_proitemPtr;

    if (ptr == uc)
con's avatar
con committed
584
585
        return;

586
587
588
589
590
    m_proitem.resize(ptr - uc);
    m_proitemPtr = uc;
    QString proItem = m_proitem;
    proItem.detach();

con's avatar
con committed
591
592
    ProBlock *block = currentBlock();
    if (block->blockKind() & ProBlock::VariableKind) {
593
594
595
        m_commentItem = new ProValue(proItem, static_cast<ProVariable*>(block));
    } else if (proItem.endsWith(QLatin1Char(')'))) {
        m_commentItem = new ProFunction(proItem);
con's avatar
con committed
596
    } else {
597
        m_commentItem = new ProCondition(proItem);
con's avatar
con committed
598
599
600
601
602
    }
    m_commentItem->setLineNumber(m_lineNo);
    block->appendItem(m_commentItem);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
603
604
//////// Evaluator tools /////////

605
QStringList ProFileEvaluator::Private::split_arg_list(const QString &params)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
{
    int quote = 0;
    QStringList args;

    const ushort LPAREN = '(';
    const ushort RPAREN = ')';
    const ushort SINGLEQUOTE = '\'';
    const ushort DOUBLEQUOTE = '"';
    const ushort COMMA = ',';
    const ushort SPACE = ' ';
    //const ushort TAB = '\t';

    ushort unicode;
    const QChar *params_data = params.data();
    const int params_len = params.length();
    int last = 0;
    while (last < params_len && ((params_data+last)->unicode() == SPACE
                                /*|| (params_data+last)->unicode() == TAB*/))
        ++last;
    for (int x = last, parens = 0; x <= params_len; x++) {
        unicode = (params_data+x)->unicode();
        if (x == params_len) {
            while (x && (params_data+(x-1))->unicode() == SPACE)
                --x;
            QString mid(params_data+last, x-last);
            if (quote) {
                if (mid[0] == quote && mid[(int)mid.length()-1] == quote)
                    mid = mid.mid(1, mid.length()-2);
                quote = 0;
            }
            args << mid;
            break;
        }
        if (unicode == LPAREN) {
            --parens;
        } else if (unicode == RPAREN) {
            ++parens;
        } else if (quote && unicode == quote) {
            quote = 0;
        } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
            quote = unicode;
        }
        if (!parens && !quote && unicode == COMMA) {
            QString mid = params.mid(last, x - last).trimmed();
            args << mid;
            last = x+1;
            while (last < params_len && ((params_data+last)->unicode() == SPACE
                                        /*|| (params_data+last)->unicode() == TAB*/))
                ++last;
        }
    }
    return args;
}

660
QStringList ProFileEvaluator::Private::split_value_list(const QString &vals, bool do_semicolon)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
661
662
663
664
665
{
    QString build;
    QStringList ret;
    QStack<char> quote;

666
    const ushort SPACE = ' ';
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
    const ushort LPAREN = '(';
    const ushort RPAREN = ')';
    const ushort SINGLEQUOTE = '\'';
    const ushort DOUBLEQUOTE = '"';
    const ushort BACKSLASH = '\\';
    const ushort SEMICOLON = ';';

    ushort unicode;
    const QChar *vals_data = vals.data();
    const int vals_len = vals.length();
    for (int x = 0, parens = 0; x < vals_len; x++) {
        unicode = vals_data[x].unicode();
        if (x != (int)vals_len-1 && unicode == BACKSLASH &&
            (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
            build += vals_data[x++]; //get that 'escape'
        } else if (!quote.isEmpty() && unicode == quote.top()) {
            quote.pop();
        } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
            quote.push(unicode);
        } else if (unicode == RPAREN) {
            --parens;
        } else if (unicode == LPAREN) {
            ++parens;
        }

        if (!parens && quote.isEmpty() && ((do_semicolon && unicode == SEMICOLON) ||
693
                                           vals_data[x] == SPACE)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
            ret << build;
            build.clear();
        } else {
            build += vals_data[x];
        }
    }
    if (!build.isEmpty())
        ret << build;
    return ret;
}

static void insertUnique(QHash<QString, QStringList> *map,
    const QString &key, const QStringList &value)
{
    QStringList &sl = (*map)[key];
    foreach (const QString &str, value)
        if (!sl.contains(str))
            sl.append(str);
}

static void removeEach(QHash<QString, QStringList> *map,
    const QString &key, const QStringList &value)
{
    QStringList &sl = (*map)[key];
    foreach (const QString &str, value)
        sl.removeAll(str);
}

static void replaceInList(QStringList *varlist,
        const QRegExp &regexp, const QString &replace, bool global)
{
    for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
        if ((*varit).contains(regexp)) {
            (*varit).replace(regexp, replace);
            if ((*varit).isEmpty())
                varit = varlist->erase(varit);
            else
                ++varit;
            if (!global)
                break;
        } else {
            ++varit;
        }
    }
}

740
741
742
743
744
745
746
747
748
749
750
751
static QString expandEnvVars(const QString &str)
{
    QString string = str;
    int rep;
    QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)"));
    reg_variableName.setMinimal(true);
    while ((rep = reg_variableName.indexIn(string)) != -1)
        string.replace(rep, reg_variableName.matchedLength(),
                       QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
    return string;
}

752
753
754
755
756
757
758
759
static QStringList expandEnvVars(const QStringList &x)
{
    QStringList ret;
    foreach (const QString &str, x)
        ret << expandEnvVars(str);
    return ret;
}

760
761
762
763
764
// This is braindead, but we want qmake compat
static QString fixPathToLocalOS(const QString &str)
{
    QString string = str;

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
765
    if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
766
767
768
769
770
771
772
773
774
775
        string[0] = string[0].toLower();

#if defined(Q_OS_WIN32)
    string.replace(QLatin1Char('/'), QLatin1Char('\\'));
#else
    string.replace(QLatin1Char('\\'), QLatin1Char('/'));
#endif
    return string;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
776
//////// Evaluator /////////
con's avatar
con committed
777

778
ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
con's avatar
con committed
779
{
780
    if (block->blockKind() & ProBlock::ScopeContentsKind) {
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
        if (!m_definingFunc.isEmpty()) {
            if (!m_skipLevel || m_cumulative) {
                QHash<QString, ProBlock *> *hash =
                        (m_definingTest ? &m_testFunctions : &m_replaceFunctions);
                if (ProBlock *def = hash->value(m_definingFunc))
                    def->deref();
                hash->insert(m_definingFunc, block);
                block->ref();
                block->setBlockKind(block->blockKind() | ProBlock::FunctionBodyKind);
            }
            m_definingFunc.clear();
            return ProItem::ReturnSkip;
        } else if (!(block->blockKind() & ProBlock::FunctionBodyKind)) {
            if (!m_sts.condition)
                ++m_skipLevel;
            else
                Q_ASSERT(!m_skipLevel);
        }
799
800
801
802
803
804
805
806
807
    } else {
        if (!m_skipLevel) {
            if (m_sts.condition) {
                m_sts.prevCondition = true;
                m_sts.condition = false;
            }
        } else {
            Q_ASSERT(!m_sts.condition);
        }
con's avatar
con committed
808
    }
809
    return ProItem::ReturnTrue;
con's avatar
con committed
810
811
}

812
void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
con's avatar
con committed
813
{
814
815
    if ((block->blockKind() & ProBlock::ScopeContentsKind)
        && !(block->blockKind() & ProBlock::FunctionBodyKind)) {
816
        if (m_skipLevel) {
817
            Q_ASSERT(!m_sts.condition);
818
            --m_skipLevel;
819
        } else if (!(block->blockKind() & ProBlock::SingleLine)) {
820
821
            // Conditionals contained inside this block may have changed the state.
            // So we reset it here to make an else following us do the right thing.
822
            m_sts.condition = true;
823
        }
824
    }
con's avatar
con committed
825
826
}

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
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();
}

857
void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *)
con's avatar
con committed
858
{
859
    m_sts.varVal.clear();
con's avatar
con committed
860
861
}

862
void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
con's avatar
con committed
863
{
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
    QString varName = variable->variable();

    switch (variable->variableOperator()) {
        case ProVariable::SetOperator:          // =
            if (!m_cumulative) {
                if (!m_skipLevel) {
                    m_valuemap[varName] = m_sts.varVal;
                    m_filevaluemap[currentProFile()][varName] = m_sts.varVal;
                }
            } else {
                // We are greedy for values.
                m_valuemap[varName] += m_sts.varVal;
                m_filevaluemap[currentProFile()][varName] += m_sts.varVal;
            }
            break;
        case ProVariable::UniqueAddOperator:    // *=
            if (!m_skipLevel || m_cumulative) {
                insertUnique(&m_valuemap, varName, m_sts.varVal);
                insertUnique(&m_filevaluemap[currentProFile()], varName, m_sts.varVal);
            }
            break;
        case ProVariable::AddOperator:          // +=
            if (!m_skipLevel || m_cumulative) {
                m_valuemap[varName] += m_sts.varVal;
                m_filevaluemap[currentProFile()][varName] += m_sts.varVal;
            }
            break;
        case ProVariable::RemoveOperator:       // -=
            if (!m_cumulative) {
                if (!m_skipLevel) {
                    removeEach(&m_valuemap, varName, m_sts.varVal);
                    removeEach(&m_filevaluemap[currentProFile()], varName, m_sts.varVal);
                }
            } else {
                // We are stingy with our values, too.
            }
            break;
        case ProVariable::ReplaceOperator:      // ~=
            {
                // DEFINES ~= s/a/b/?[gqi]

                QString val = m_sts.varVal.first();
                doVariableReplace(&val);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
907
                if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
                    q->logMessage(format("the ~= operator can handle only the s/// function."));
                    break;
                }
                QChar sep = val.at(1);
                QStringList func = val.split(sep);
                if (func.count() < 3 || func.count() > 4) {
                    q->logMessage(format("the s/// function expects 3 or 4 arguments."));
                    break;
                }

                bool global = false, quote = false, case_sense = false;
                if (func.count() == 4) {
                    global = func[3].indexOf(QLatin1Char('g')) != -1;
                    case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
                    quote = func[3].indexOf(QLatin1Char('q')) != -1;
                }
                QString pattern = func[1];
                QString replace = func[2];
                if (quote)
                    pattern = QRegExp::escape(pattern);

                QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);

                if (!m_skipLevel || m_cumulative) {
                    // We could make a union of modified and unmodified values,
                    // but this will break just as much as it fixes, so leave it as is.
                    replaceInList(&m_valuemap[varName], regexp, replace, global);
                    replaceInList(&m_filevaluemap[currentProFile()][varName], regexp, replace, global);

                }
            }
            break;
    }
con's avatar
con committed
941
942
}

943
void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
con's avatar
con committed
944
945
946
947
{
    m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
}

948
void ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
con's avatar
con committed
949
{
950
    if (!m_skipLevel) {
951
        if (!cond->text().compare(QLatin1String("else"), Qt::CaseInsensitive)) {
952
953
954
955
956
            m_sts.condition = !m_sts.prevCondition;
        } else {
            m_sts.prevCondition = false;
            if (!m_sts.condition && isActiveConfig(cond->text(), true) ^ m_invertNext)
                m_sts.condition = true;
957
        }
con's avatar
con committed
958
    }
959
    m_invertNext = false;
con's avatar
con committed
960
961
}

962
ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
con's avatar
con committed
963
964
{
    m_lineNo = pro->lineNumber();
965
966
967
968
969
970
971

    m_oldPathStack.push(QDir::currentPath());
    if (!QDir::setCurrent(pro->directoryName())) {
        m_oldPathStack.pop();
        return ProItem::ReturnFalse;
    }

972
973
    m_profileStack.push(pro);
    if (m_profileStack.count() == 1) {
974
        // Do this only for the initial profile we visit, since
con's avatar
con committed
975
976
977
        // that is *the* profile. All the other times we reach this function will be due to
        // include(file) or load(file)

978
        if (m_parsePreAndPostFiles) {
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062

            if (m_option->base_valuemap.isEmpty()) {
                // ### init QMAKE_QMAKE, QMAKE_SH
                // ### init QMAKE_EXT_{C,H,CPP,OBJ}
                // ### init TEMPLATE_PREFIX

                QString qmake_cache = m_option->cachefile;
                if (qmake_cache.isEmpty() && !m_outputDir.isEmpty())  { //find it as it has not been specified
                    QDir dir(m_outputDir);
                    forever {
                        qmake_cache = dir.filePath(QLatin1String(".qmake.cache"));
                        if (QFile::exists(qmake_cache))
                            break;
                        dir.cdUp();
                        if (dir.isRoot()) {
                            qmake_cache.clear();
                            break;
                        }
                    }
                }
                if (!qmake_cache.isEmpty()) {
                    qmake_cache = QDir::cleanPath(qmake_cache);
                    if (evaluateFileInto(qmake_cache, &m_option->cache_valuemap)) {
                        m_option->cachefile = qmake_cache;
                        if (m_option->qmakespec.isEmpty()) {
                            const QStringList &vals = m_option->cache_valuemap.value(QLatin1String("QMAKESPEC"));
                            if (!vals.isEmpty())
                                m_option->qmakespec = vals.first();
                        }
                    }
                }

                QStringList mkspec_roots = qmakeMkspecPaths();

                QString qmakespec = expandEnvVars(m_option->qmakespec);
                if (qmakespec.isEmpty()) {
                    foreach (const QString &root, mkspec_roots) {
                        QString mkspec = root + QLatin1String("/default");
                        QFileInfo default_info(mkspec);
                        if (default_info.exists() && default_info.isDir()) {
                            qmakespec = mkspec;
                            break;
                        }
                    }
                    if (qmakespec.isEmpty()) {
                        q->errorMessage(format("Could not find qmake configuration directory"));
                        // Unlike in qmake, not finding the spec is not critical ...
                    }
                }

                if (QDir::isRelativePath(qmakespec)) {
                    if (QFile::exists(qmakespec + QLatin1String("/qmake.conf"))) {
                        qmakespec = QFileInfo(qmakespec).absoluteFilePath();
                    } else if (!m_outputDir.isEmpty()
                               && QFile::exists(m_outputDir + QLatin1Char('/') + qmakespec
                                                + QLatin1String("/qmake.conf"))) {
                        qmakespec = m_outputDir + QLatin1Char('/') + qmakespec;
                    } else {
                        foreach (const QString &root, mkspec_roots) {
                            QString mkspec = root + QLatin1Char('/') + qmakespec;
                            if (QFile::exists(mkspec)) {
                                qmakespec = mkspec;
                                goto cool;
                            }
                        }
                        q->errorMessage(format("Could not find qmake configuration file"));
                        // Unlike in qmake, a missing config is not critical ...
                        qmakespec.clear();
                      cool: ;
                    }
                }

                if (!qmakespec.isEmpty()) {
                    m_option->qmakespec = QDir::cleanPath(qmakespec);

                    QString spec = m_option->qmakespec + QLatin1String("/qmake.conf");
                    if (!evaluateFileInto(spec, &m_option->base_valuemap)) {
                        q->errorMessage(format("Could not read qmake configuration file %1").arg(spec));
                    } else {
                        updateHash(&m_option->base_valuemap, m_option->cache_valuemap);
                    }
                }

                evaluateFeatureFile(QLatin1String("default_pre.prf"), &m_option->base_valuemap);
1063
            }
1064
1065

            m_valuemap = m_option->base_valuemap;
1066

1067
1068
1069
1070
            QStringList &tgt = m_valuemap[QLatin1String("TARGET")];
            if (tgt.isEmpty())
                tgt.append(QFileInfo(pro->fileName()).baseName());

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1071
            QStringList &tmp = m_valuemap[QLatin1String("CONFIG")];
1072
            tmp.append(m_addUserConfigCmdArgs);
1073
            foreach (const QString &remove, m_removeUserConfigCmdArgs)
1074
                tmp.removeAll(remove);
con's avatar
con committed
1075
1076
1077
        }
    }

1078
    return ProItem::ReturnTrue;
con's avatar
con committed
1079
1080
}

1081
ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
con's avatar
con committed
1082
1083
{
    m_lineNo = pro->lineNumber();
1084

1085
    if (m_profileStack.count() == 1) {
1086
1087
1088
1089
1090
1091
1092
1093
        if (m_parsePreAndPostFiles) {
            evaluateFeatureFile(QLatin1String("default_post.prf"));

            QSet<QString> processed;
            forever {
                bool finished = true;
                QStringList configs = valuesDirect(QLatin1String("CONFIG"));
                for (int i = configs.size() - 1; i >= 0; --i) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1094
                    const QString config = configs.at(i).toLower();
1095
1096
1097
1098
1099
                    if (!processed.contains(config)) {
                        processed.insert(config);
                        if (evaluateFeatureFile(config)) {
                            finished = false;
                            break;
con's avatar
con committed
1100
1101
1102
                        }
                    }
                }
1103
1104
                if (finished)
                    break;
con's avatar
con committed
1105
1106
1107
            }
        }

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1108
1109
1110
1111
1112
1113
        foreach (ProBlock *itm, m_replaceFunctions)
            itm->deref();
        m_replaceFunctions.clear();
        foreach (ProBlock *itm, m_testFunctions)
            itm->deref();
        m_testFunctions.clear();
con's avatar
con committed
1114
    }
1115
1116
    m_profileStack.pop();

1117
    return returnBool(QDir::setCurrent(m_oldPathStack.pop()));
con's avatar
con committed
1118
1119
}

1120
void ProFileEvaluator::Private::visitProValue(ProValue *value)
con's avatar
con committed
1121
1122
{
    m_lineNo = value->lineNumber();
1123
    m_sts.varVal += expandVariableReferences(value->value());
con's avatar
con committed
1124
1125
}

1126
ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
con's avatar
con committed
1127
{
1128
1129
1130
    // Make sure that called subblocks don't inherit & destroy the state
    bool invertThis = m_invertNext;
    m_invertNext = false;
1131
1132
1133
    if (!m_skipLevel)
        m_sts.prevCondition = false;
    if (m_cumulative || !m_sts.condition) {
1134
1135
1136
        QString text = func->text();
        int lparen = text.indexOf(QLatin1Char('('));
        int rparen = text.lastIndexOf(QLatin1Char(')'));
1137
        Q_ASSERT(lparen < rparen);
1138
1139
1140
        QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
        QString funcName = text.left(lparen);
        m_lineNo = func->lineNumber();
1141
1142
1143
1144
        ProItem::ProItemReturn result = evaluateConditionalFunction(funcName.trimmed(), arguments);
        if (result != ProItem::ReturnFalse && result != ProItem::ReturnTrue)
            return result;
        if (!m_skipLevel && ((result == ProItem::ReturnTrue) ^ invertThis))
1145
            m_sts.condition = true;
1146
    }
1147
    return ProItem::ReturnTrue;
con's avatar
con committed
1148
1149
1150
}


1151
QStringList ProFileEvaluator::Private::qmakeMkspecPaths() const
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1152
1153
{
    QStringList ret;
1154
1155
    const QString concat = QLatin1String("/mkspecs");

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1156
    QByteArray qmakepath = qgetenv("QMAKEPATH");
1157
1158
1159
1160
1161
    if (!qmakepath.isEmpty())
        foreach (const QString &it, QString::fromLocal8Bit(qmakepath).split(m_option->dirlist_sep))
            ret << QDir::cleanPath(it) + concat;

    ret << propertyValue(QLatin1String("QT_INSTALL_DATA")) + concat;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1162
1163
1164
1165

    return ret;
}

1166
QStringList ProFileEvaluator::Private::qmakeFeaturePaths() const
con's avatar
con committed
1167
{
1168
1169
    QString mkspecs_concat = QLatin1String("/mkspecs");
    QString features_concat = QLatin1String("/features");
con's avatar
con committed
1170
    QStringList concat;
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
    switch (m_option->target_mode) {
    case Option::TARG_MACX_MODE:
        concat << QLatin1String("/features/mac");
        concat << QLatin1String("/features/macx");
        concat << QLatin1String("/features/unix");
        break;
    case Option::TARG_UNIX_MODE:
        concat << QLatin1String("/features/unix");
        break;
    case Option::TARG_WIN_MODE:
        concat << QLatin1String("/features/win32");
        break;
    case Option::TARG_MAC9_MODE:
        concat << QLatin1String("/features/mac");
        concat << QLatin1String("/features/mac9");
        break;
    case Option::TARG_QNX6_MODE:
        concat << QLatin1String("/features/qnx6");
        concat << QLatin1String("/features/unix");
        break;
con's avatar
con committed
1191
    }
1192
1193
    concat << features_concat;

con's avatar
con committed
1194
    QStringList feature_roots;
1195

con's avatar
con committed
1196
    QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
1197
1198
1199
1200
    if (!mkspec_path.isEmpty())
        foreach (const QString &f, QString::fromLocal8Bit(mkspec_path).split(m_option->dirlist_sep))
            feature_roots += QDir::cleanPath(f);

1201
    feature_roots += propertyValue(QLatin1String("QMAKEFEATURES"), false).split(
1202
1203
1204
            m_option->dirlist_sep, QString::SkipEmptyParts);

    if (!m_option->cachefile.isEmpty()) {
con's avatar
con committed
1205
        QString path;
1206
        int last_slash = m_option->cachefile.lastIndexOf((ushort)'/');
con's avatar
con committed
1207
        if (last_slash != -1)
1208
            path = m_option->cachefile.left(last_slash);
con's avatar
con committed
1209
1210
1211
1212
1213
1214
        foreach (const QString &concat_it, concat)
            feature_roots << (path + concat_it);
    }

    QByteArray qmakepath = qgetenv("QMAKEPATH");
    if (!qmakepath.isNull()) {
1215
        const QStringList lst = QString::fromLocal8Bit(qmakepath).split(m_option->dirlist_sep);
con's avatar
con committed
1216
        foreach (const QString &item, lst) {
1217
            QString citem = QDir::cleanPath(item);
con's avatar
con committed
1218
            foreach (const QString &concat_it, concat)
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
                feature_roots << (citem + mkspecs_concat + concat_it);
        }
    }

    if (!m_option->qmakespec.isEmpty()) {
        feature_roots << (m_option->qmakespec + features_concat);

        QDir specdir(m_option->qmakespec);
        while (!specdir.isRoot()) {
            if (!specdir.cdUp() || specdir.isRoot())
                break;
            if (QFile::exists(specdir.path() + features_concat)) {
                foreach (const QString &concat_it, concat)
                    feature_roots << (specdir.path() + concat_it);
                break;
            }
con's avatar
con committed
1235
1236
        }
    }
1237

con's avatar
con committed
1238
1239
1240
1241
1242
1243
    foreach (const QString &concat_it, concat)
        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
                          mkspecs_concat + concat_it);
    foreach (const QString &concat_it, concat)
        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
                          mkspecs_concat + concat_it);
1244
1245
1246
1247
1248

    for (int i = 0; i < feature_roots.count(); ++i)
        if (!feature_roots.at(i).endsWith((ushort)'/'))
            feature_roots[i].append((ushort)'/');

con's avatar
con committed
1249
1250
1251
    return feature_roots;
}

1252
QString ProFileEvaluator::Private::propertyValue(const QString &name, bool complain) const
con's avatar
con committed
1253
{
1254
1255
    if (m_option->properties.contains(name))
        return m_option->properties.value(name);
con's avatar
con committed
1256
    if (name == QLatin1String("QMAKE_MKSPECS"))
1257
        return qmakeMkspecPaths().join(m_option->dirlist_sep);
con's avatar
con committed
1258
1259
    if (name == QLatin1String("QMAKE_VERSION"))
        return QLatin1String("1.0");        //### FIXME
1260
1261
    if (complain)
        q->logMessage(format("Querying unknown property %1").arg(name));
1262
    return QString();
con's avatar
con committed
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
}

ProFile *ProFileEvaluator::Private::currentProFile() const
{
    if (m_profileStack.count() > 0)
        return m_profileStack.top();
    return 0;
}

QString ProFileEvaluator::Private::currentFileName() const
{
    ProFile *pro = currentProFile();
    if (pro)
        return pro->fileName();
    return QString();
}

1280
QString ProFileEvaluator::Private::currentDirectory() const
con's avatar
con committed
1281
1282
{
    ProFile *cur = m_profileStack.top();
1283
    return cur->directoryName();
con's avatar
con committed
1284
1285
}

1286
1287
void ProFileEvaluator::Private::doVariableReplace(QString *str)
{
1288
    *str = expandVariableReferences(*str).join(Option::field_sep);
1289
1290
}

con's avatar
con committed
1291
1292
1293
QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
{
    QStringList ret;
1294
1295
//    if (ok)
//        *ok = true;
con's avatar
con committed
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
    if (str.isEmpty())
        return ret;

    const ushort LSQUARE = '[';
    const ushort RSQUARE = ']';
    const ushort LCURLY = '{';
    const ushort RCURLY = '}';
    const ushort LPAREN = '(';
    const ushort RPAREN = ')';
    const ushort DOLLAR = '$';
    const ushort BACKSLASH = '\\';
    const ushort UNDERSCORE = '_';
    const ushort DOT = '.';
    const ushort SPACE = ' ';
    const ushort TAB = '\t';
1311
1312
    const ushort SINGLEQUOTE = '\'';
    const ushort DOUBLEQUOTE = '"';
con's avatar
con committed
1313

1314
    ushort unicode, quote = 0;
con's avatar
con committed
1315
1316
1317
1318
1319
1320
1321
1322
1323
    const QChar *str_data = str.data();
    const int str_len = str.length();

    ushort term;
    QString var, args;

    int replaced = 0;
    QString current;
    for (int</