qmakeevaluator.cpp 55.3 KB
Newer Older
1
2
3
4
5
6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
**
**
** GNU Lesser General Public License Usage
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/

#include "qmakeevaluator.h"

#include "qmakeglobals.h"
#include "qmakeparser.h"
#include "qmakeevaluator_p.h"
#include "ioutils.h"

#include <QByteArray>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QRegExp>
#include <QSet>
#include <QStack>
#include <QString>
#include <QStringList>
#ifdef PROEVALUATOR_THREAD_SAFE
# include <QThreadPool>
#endif

#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/utsname.h>
#else
Yuchen Deng's avatar
Yuchen Deng committed
58
#include <windows.h>
59
60
61
62
63
64
65
66
67
68
#endif
#include <stdio.h>
#include <stdlib.h>

using namespace ProFileEvaluatorInternal;

QT_BEGIN_NAMESPACE

#define fL1S(s) QString::fromLatin1(s)

69

70
71
72
73
74
75
76
QMakeBaseKey::QMakeBaseKey(const QString &_root, bool _hostBuild)
    : root(_root), hostBuild(_hostBuild)
{
}

uint qHash(const QMakeBaseKey &key)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
77
    return qHash(key.root) ^ (uint)key.hostBuild;
78
79
80
81
82
83
84
}

bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
{
    return one.root == two.root && one.hostBuild == two.hostBuild;
}

85
86
87
88
89
90
91
92
93
94
95
96
97
QMakeBaseEnv::QMakeBaseEnv()
    : evaluator(0)
{
#ifdef PROEVALUATOR_THREAD_SAFE
    inProgress = false;
#endif
}

QMakeBaseEnv::~QMakeBaseEnv()
{
    delete evaluator;
}

98
99
100
101
102
103
104
105
106
107
108
109
namespace ProFileEvaluatorInternal {
QMakeStatics statics;
}

void QMakeEvaluator::initStatics()
{
    if (!statics.field_sep.isNull())
        return;

    statics.field_sep = QLatin1String(" ");
    statics.strtrue = QLatin1String("true");
    statics.strfalse = QLatin1String("false");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
110
111
    statics.strCONFIG = ProKey("CONFIG");
    statics.strARGS = ProKey("ARGS");
112
113
114
115
    statics.strDot = QLatin1String(".");
    statics.strDotDot = QLatin1String("..");
    statics.strever = QLatin1String("ever");
    statics.strforever = QLatin1String("forever");
116
    statics.strhost_build = QLatin1String("host_build");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
117
    statics.strTEMPLATE = ProKey("TEMPLATE");
118
#ifdef PROEVALUATOR_FULL
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
119
    statics.strREQUIRES = ProKey("REQUIRES");
120
#endif
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

    statics.fakeValue = ProStringList(ProString("_FAKE_")); // It has to have a unique begin() value

    initFunctionStatics();

    static const struct {
        const char * const oldname, * const newname;
    } mapInits[] = {
        { "INTERFACES", "FORMS" },
        { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
        { "TARGETDEPS", "POST_TARGETDEPS" },
        { "LIBPATH", "QMAKE_LIBDIR" },
        { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
        { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
        { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
        { "PRECOMPH", "PRECOMPILED_HEADER" },
        { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
        { "INCPATH", "INCLUDEPATH" },
        { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
        { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
        { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
        { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
        { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
        { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
        { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
        { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
147
148
        { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" },
        { "IN_PWD", "PWD" }
149
150
    };
    for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
151
        statics.varMap.insert(ProKey(mapInits[i].oldname), ProKey(mapInits[i].newname));
152
153
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
154
const ProKey &QMakeEvaluator::map(const ProKey &var)
155
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
156
    QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(var);
157
158
    if (it == statics.varMap.constEnd())
        return var;
Robert Loehning's avatar
Robert Loehning committed
159
    deprecationWarning(fL1S("Variable %1 is deprecated; use %2 instead.")
160
161
                       .arg(var.toQString(), it.value().toQString()));
    return it.value();
162
163
164
165
}


QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option,
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
166
                               QMakeParser *parser, QMakeHandler *handler)
167
168
169
170
171
172
  : m_option(option), m_parser(parser), m_handler(handler)
{
    // So that single-threaded apps don't have to call initialize() for now.
    initStatics();

    // Configuration, more or less
173
    m_caller = 0;
174
#ifdef PROEVALUATOR_CUMULATIVE
175
    m_cumulative = false;
176
#endif
177
    m_hostBuild = false;
178
179

    // Evaluator state
180
#ifdef PROEVALUATOR_CUMULATIVE
181
    m_skipLevel = 0;
182
#endif
183
184
    m_loopLevel = 0;
    m_listCount = 0;
185
    m_valuemapStack.push(ProValueMap());
186
    m_valuemapInited = false;
187
188
189
190
191
192
}

QMakeEvaluator::~QMakeEvaluator()
{
}

193
194
195
196
197
void QMakeEvaluator::initFrom(const QMakeEvaluator &other)
{
    Q_ASSERT_X(&other, "QMakeEvaluator::visitProFile", "Project not prepared");
    m_functionDefs = other.m_functionDefs;
    m_valuemapStack = other.m_valuemapStack;
198
    m_valuemapInited = true;
199
    m_qmakespec = other.m_qmakespec;
200
    m_qmakespecFull = other.m_qmakespecFull;
201
    m_qmakespecName = other.m_qmakespecName;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
202
    m_mkspecPaths = other.m_mkspecPaths;
203
    m_featureRoots = other.m_featureRoots;
204
    m_dirSep = other.m_dirSep;
205
206
}

207
208
209
210
211
212
213
214
215
216
217
218
//////// Evaluator tools /////////

uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr)
{
    uint len = *tokPtr++;
    len |= (uint)*tokPtr++ << 16;
    return len;
}

ProString QMakeEvaluator::getStr(const ushort *&tokPtr)
{
    uint len = *tokPtr++;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
219
    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len);
220
221
222
223
224
    ret.setSource(m_current.pro);
    tokPtr += len;
    return ret;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
225
ProKey QMakeEvaluator::getHashStr(const ushort *&tokPtr)
226
227
228
{
    uint hash = getBlockLen(tokPtr);
    uint len = *tokPtr++;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
229
    ProKey ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
    tokPtr += len;
    return ret;
}

void QMakeEvaluator::skipStr(const ushort *&tokPtr)
{
    uint len = *tokPtr++;
    tokPtr += len;
}

void QMakeEvaluator::skipHashStr(const ushort *&tokPtr)
{
    tokPtr += 2;
    uint len = *tokPtr++;
    tokPtr += len;
}

// FIXME: this should not build new strings for direct sections.
// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFile *source)
{
    QString build;
    ProStringList ret;
    QStack<char> quote;

    const ushort SPACE = ' ';
    const ushort LPAREN = '(';
    const ushort RPAREN = ')';
    const ushort SINGLEQUOTE = '\'';
    const ushort DOUBLEQUOTE = '"';
    const ushort BACKSLASH = '\\';

    if (!source)
        source = currentProFile();

    ushort unicode;
    const QChar *vals_data = vals.data();
    const int vals_len = vals.length();
268
269
    int parens = 0;
    for (int x = 0; x < vals_len; x++) {
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
        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() && vals_data[x] == SPACE) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
285
            ret << ProString(build).setSource(source);
286
287
288
289
290
291
            build.clear();
        } else {
            build += vals_data[x];
        }
    }
    if (!build.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
292
        ret << ProString(build).setSource(source);
293
294
    if (parens)
        deprecationWarning(fL1S("Unmatched parentheses are deprecated."));
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
    return ret;
}

static void zipEmpty(ProStringList *value)
{
    for (int i = value->size(); --i >= 0;)
        if (value->at(i).isEmpty())
            value->remove(i);
}

static void insertUnique(ProStringList *varlist, const ProStringList &value)
{
    foreach (const ProString &str, value)
        if (!str.isEmpty() && !varlist->contains(str))
            varlist->append(str);
}

static void removeAll(ProStringList *varlist, const ProString &value)
{
    for (int i = varlist->size(); --i >= 0; )
        if (varlist->at(i) == value)
            varlist->remove(i);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
319
void QMakeEvaluator::removeEach(ProStringList *varlist, const ProStringList &value)
320
321
322
323
324
325
326
327
328
329
330
331
332
{
    foreach (const ProString &str, value)
        if (!str.isEmpty())
            removeAll(varlist, str);
}

static void replaceInList(ProStringList *varlist,
        const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
{
    for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
        QString val = varit->toQString(tmp);
        QString copy = val; // Force detach and have a reference value
        val.replace(regexp, replace);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
333
        if (!val.isSharedWith(copy) && val != copy) {
334
335
336
            if (val.isEmpty()) {
                varit = varlist->erase(varit);
            } else {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
337
                (*varit).setValue(val);
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
                ++varit;
            }
            if (!global)
                break;
        } else {
            ++varit;
        }
    }
}

//////// Evaluator /////////

static ALWAYS_INLINE void addStr(
        const ProString &str, ProStringList *ret, bool &pending, bool joined)
{
    if (joined) {
        ret->last().append(str, &pending);
    } else {
        if (!pending) {
            pending = true;
            *ret << str;
        } else {
            ret->last().append(str);
        }
    }
}

static ALWAYS_INLINE void addStrList(
        const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
{
    if (!list.isEmpty()) {
        if (joined) {
            ret->last().append(list, &pending, !(tok & TokQuoted));
        } else {
            if (tok & TokQuoted) {
                if (!pending) {
                    pending = true;
                    *ret << ProString();
                }
                ret->last().append(list);
            } else {
                if (!pending) {
                    // Another qmake bizzarity: if nothing is pending and the
                    // first element is empty, it will be eaten
                    if (!list.at(0).isEmpty()) {
                        // The common case
                        pending = true;
                        *ret += list;
                        return;
                    }
                } else {
                    ret->last().append(list.at(0));
                }
                // This is somewhat slow, but a corner case
                for (int j = 1; j < list.size(); ++j) {
                    pending = true;
                    *ret << list.at(j);
                }
            }
        }
    }
}

void QMakeEvaluator::evaluateExpression(
        const ushort *&tokPtr, ProStringList *ret, bool joined)
{
    if (joined)
        *ret << ProString();
    bool pending = false;
    forever {
        ushort tok = *tokPtr++;
        if (tok & TokNewStr)
            pending = false;
        ushort maskedTok = tok & TokMask;
        switch (maskedTok) {
        case TokLine:
            m_current.line = *tokPtr++;
            break;
        case TokLiteral:
            addStr(getStr(tokPtr), ret, pending, joined);
            break;
        case TokHashLiteral:
            addStr(getHashStr(tokPtr), ret, pending, joined);
            break;
        case TokVariable:
            addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined);
            break;
        case TokProperty:
426
            addStr(propertyValue(getHashStr(tokPtr)).setSource(currentProFile()),
427
428
429
430
431
432
433
                   ret, pending, joined);
            break;
        case TokEnvVar:
            addStrList(split_value_list(m_option->getEnv(getStr(tokPtr).toQString(m_tmp1))),
                       tok, ret, pending, joined);
            break;
        case TokFuncName: {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
434
            const ProKey &func = getHashStr(tokPtr);
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
            addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
            break; }
        default:
            tokPtr--;
            return;
        }
    }
}

void QMakeEvaluator::skipExpression(const ushort *&pTokPtr)
{
    const ushort *tokPtr = pTokPtr;
    forever {
        ushort tok = *tokPtr++;
        switch (tok) {
        case TokLine:
            m_current.line = *tokPtr++;
            break;
        case TokValueTerminator:
        case TokFuncTerminator:
            pTokPtr = tokPtr;
            return;
        case TokArgSeparator:
            break;
        default:
            switch (tok & TokMask) {
            case TokLiteral:
            case TokEnvVar:
                skipStr(tokPtr);
                break;
            case TokHashLiteral:
            case TokVariable:
467
            case TokProperty:
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
                skipHashStr(tokPtr);
                break;
            case TokFuncName:
                skipHashStr(tokPtr);
                pTokPtr = tokPtr;
                skipExpression(pTokPtr);
                tokPtr = pTokPtr;
                break;
            default:
                Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
                break;
            }
        }
    }
}

QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
        ProFile *pro, const ushort *tokPtr)
{
    m_current.pro = pro;
    m_current.line = 0;
    return visitProBlock(tokPtr);
}

QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
        const ushort *tokPtr)
{
    ProStringList curr;
    bool okey = true, or_op = false, invert = false;
    uint blockLen;
    while (ushort tok = *tokPtr++) {
499
        VisitReturn ret;
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
        switch (tok) {
        case TokLine:
            m_current.line = *tokPtr++;
            continue;
        case TokAssign:
        case TokAppend:
        case TokAppendUnique:
        case TokRemove:
        case TokReplace:
            visitProVariable(tok, curr, tokPtr);
            curr.clear();
            continue;
        case TokBranch:
            blockLen = getBlockLen(tokPtr);
            if (m_cumulative) {
515
#ifdef PROEVALUATOR_CUMULATIVE
516
517
518
519
520
521
522
523
524
525
526
527
528
                if (!okey)
                    m_skipLevel++;
                ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
                tokPtr += blockLen;
                blockLen = getBlockLen(tokPtr);
                if (!okey)
                    m_skipLevel--;
                else
                    m_skipLevel++;
                if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
                    ret = visitProBlock(tokPtr);
                if (okey)
                    m_skipLevel--;
529
#endif
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
            } else {
                if (okey)
                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
                tokPtr += blockLen;
                blockLen = getBlockLen(tokPtr);
                if (!okey)
                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
            }
            tokPtr += blockLen;
            okey = true, or_op = false; // force next evaluation
            break;
        case TokForLoop:
            if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
                skipHashStr(tokPtr);
                uint exprLen = getBlockLen(tokPtr);
                tokPtr += exprLen;
                blockLen = getBlockLen(tokPtr);
                ret = visitProBlock(tokPtr);
            } else if (okey != or_op) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
549
                const ProKey &variable = getHashStr(tokPtr);
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
                uint exprLen = getBlockLen(tokPtr);
                const ushort *exprPtr = tokPtr;
                tokPtr += exprLen;
                blockLen = getBlockLen(tokPtr);
                ret = visitProLoop(variable, exprPtr, tokPtr);
            } else {
                skipHashStr(tokPtr);
                uint exprLen = getBlockLen(tokPtr);
                tokPtr += exprLen;
                blockLen = getBlockLen(tokPtr);
                ret = ReturnTrue;
            }
            tokPtr += blockLen;
            okey = true, or_op = false; // force next evaluation
            break;
        case TokTestDef:
        case TokReplaceDef:
            if (m_cumulative || okey != or_op) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
568
                const ProKey &name = getHashStr(tokPtr);
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
                blockLen = getBlockLen(tokPtr);
                visitProFunctionDef(tok, name, tokPtr);
            } else {
                skipHashStr(tokPtr);
                blockLen = getBlockLen(tokPtr);
            }
            tokPtr += blockLen;
            okey = true, or_op = false; // force next evaluation
            continue;
        case TokNot:
            invert ^= true;
            continue;
        case TokAnd:
            or_op = false;
            continue;
        case TokOr:
            or_op = true;
            continue;
        case TokCondition:
            if (!m_skipLevel && okey != or_op) {
                if (curr.size() != 1) {
                    if (!m_cumulative || !curr.isEmpty())
                        evalError(fL1S("Conditional must expand to exactly one word."));
                    okey = false;
                } else {
                    okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert;
                }
            }
            or_op = !okey; // tentatively force next evaluation
            invert = false;
            curr.clear();
            continue;
        case TokTestCall:
            if (!m_skipLevel && okey != or_op) {
                if (curr.size() != 1) {
                    if (!m_cumulative || !curr.isEmpty())
                        evalError(fL1S("Test name must expand to exactly one word."));
                    skipExpression(tokPtr);
                    okey = false;
                } else {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
609
                    ret = evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
610
611
612
613
614
615
616
617
                    switch (ret) {
                    case ReturnTrue: okey = true; break;
                    case ReturnFalse: okey = false; break;
                    default: return ret;
                    }
                    okey ^= invert;
                }
            } else if (m_cumulative) {
618
#ifdef PROEVALUATOR_CUMULATIVE
619
620
621
622
                m_skipLevel++;
                if (curr.size() != 1)
                    skipExpression(tokPtr);
                else
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
623
                    evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
624
                m_skipLevel--;
625
#endif
626
627
628
629
630
631
632
633
634
635
636
637
638
639
            } else {
                skipExpression(tokPtr);
            }
            or_op = !okey; // tentatively force next evaluation
            invert = false;
            curr.clear();
            continue;
        default: {
                const ushort *oTokPtr = --tokPtr;
                evaluateExpression(tokPtr, &curr, false);
                if (tokPtr != oTokPtr)
                    continue;
            }
            Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
640
            continue;
641
642
        }
        if (ret != ReturnTrue && ret != ReturnFalse)
643
            return ret;
644
    }
645
    return returnBool(okey);
646
647
648
649
}


void QMakeEvaluator::visitProFunctionDef(
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
650
        ushort tok, const ProKey &name, const ushort *tokPtr)
651
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
652
    QHash<ProKey, ProFunctionDef> *hash =
653
654
655
656
657
658
659
            (tok == TokTestDef
             ? &m_functionDefs.testFunctions
             : &m_functionDefs.replaceFunctions);
    hash->insert(name, ProFunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
}

QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
660
        const ProKey &_variable, const ushort *exprPtr, const ushort *tokPtr)
661
662
663
664
{
    VisitReturn ret = ReturnTrue;
    bool infinite = false;
    int index = 0;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
665
    ProKey variable;
666
667
668
669
670
671
672
673
674
675
    ProStringList oldVarVal;
    ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
    if (_variable.isEmpty()) {
        if (it_list != statics.strever) {
            evalError(fL1S("Invalid loop expression."));
            return ReturnFalse;
        }
        it_list = ProString(statics.strforever);
    } else {
        variable = map(_variable);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
676
        oldVarVal = values(variable);
677
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
678
    ProStringList list = values(it_list.toKey());
679
680
681
682
683
684
685
686
687
688
689
690
691
692
    if (list.isEmpty()) {
        if (it_list == statics.strforever) {
            infinite = true;
        } else {
            const QString &itl = it_list.toQString(m_tmp1);
            int dotdot = itl.indexOf(statics.strDotDot);
            if (dotdot != -1) {
                bool ok;
                int start = itl.left(dotdot).toInt(&ok);
                if (ok) {
                    int end = itl.mid(dotdot+2).toInt(&ok);
                    if (ok) {
                        if (start < end) {
                            for (int i = start; i <= end; i++)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
693
                                list << ProString(QString::number(i));
694
695
                        } else {
                            for (int i = start; i >= end; i--)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
696
                                list << ProString(QString::number(i));
697
698
699
700
701
702
703
704
705
706
707
                        }
                    }
                }
            }
        }
    }

    m_loopLevel++;
    forever {
        if (infinite) {
            if (!variable.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
708
                m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++)));
709
            if (index > 1000) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
710
                evalError(fL1S("Ran into infinite loop (> 1000 iterations)."));
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
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
                break;
            }
        } else {
            ProString val;
            do {
                if (index >= list.count())
                    goto do_break;
                val = list.at(index++);
            } while (val.isEmpty()); // stupid, but qmake is like that
            m_valuemapStack.top()[variable] = ProStringList(val);
        }

        ret = visitProBlock(tokPtr);
        switch (ret) {
        case ReturnTrue:
        case ReturnFalse:
            break;
        case ReturnNext:
            ret = ReturnTrue;
            break;
        case ReturnBreak:
            ret = ReturnTrue;
            goto do_break;
        default:
            goto do_break;
        }
    }
  do_break:
    m_loopLevel--;

    if (!variable.isEmpty())
        m_valuemapStack.top()[variable] = oldVarVal;
    return ret;
}

void QMakeEvaluator::visitProVariable(
        ushort tok, const ProStringList &curr, const ushort *&tokPtr)
{
    int sizeHint = *tokPtr++;

    if (curr.size() != 1) {
        skipExpression(tokPtr);
        if (!m_cumulative || !curr.isEmpty())
            evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
        return;
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
757
    const ProKey &varName = map(curr.first());
758
759
760
761
762
763
764

    if (tok == TokReplace) {      // ~=
        // DEFINES ~= s/a/b/?[gqi]

        const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
        const QString &val = varVal.at(0).toQString(m_tmp1);
        if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
765
            evalError(fL1S("The ~= operator can handle only the s/// function."));
766
767
768
769
770
            return;
        }
        QChar sep = val.at(1);
        QStringList func = val.split(sep);
        if (func.count() < 3 || func.count() > 4) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
771
            evalError(fL1S("The s/// function expects 3 or 4 arguments."));
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
            return;
        }

        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);

788
789
790
        // 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(&valuesRef(varName), regexp, replace, global, m_tmp2);
791
792
793
794
795
    } else {
        ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
        switch (tok) {
        default: // whatever - cannot happen
        case TokAssign:          // =
796
            zipEmpty(&varVal);
797
            if (!m_cumulative) {
798
799
800
                // FIXME: add check+warning about accidental value removal.
                // This may be a bit too noisy, though.
                m_valuemapStack.top()[varName] = varVal;
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
            } else {
                if (!varVal.isEmpty()) {
                    // We are greedy for values. But avoid exponential growth.
                    ProStringList &v = valuesRef(varName);
                    if (v.isEmpty()) {
                        v = varVal;
                    } else {
                        ProStringList old = v;
                        v = varVal;
                        QSet<ProString> has;
                        has.reserve(v.size());
                        foreach (const ProString &s, v)
                            has.insert(s);
                        v.reserve(v.size() + old.size());
                        foreach (const ProString &s, old)
                            if (!has.contains(s))
                                v << s;
                    }
                }
            }
            break;
        case TokAppendUnique:    // *=
823
            insertUnique(&valuesRef(varName), varVal);
824
825
            break;
        case TokAppend:          // +=
826
827
            zipEmpty(&varVal);
            valuesRef(varName) += varVal;
828
829
830
            break;
        case TokRemove:       // -=
            if (!m_cumulative) {
831
                removeEach(&valuesRef(varName), varVal);
832
833
834
835
836
837
            } else {
                // We are stingy with our values, too.
            }
            break;
        }
    }
838
839
840

    if (varName == statics.strTEMPLATE)
        setTemplate();
841
842
843
844
#ifdef PROEVALUATOR_FULL
    else if (varName == statics.strREQUIRES)
        checkRequirements(values(varName));
#endif
845
846
847
848
849
}

void QMakeEvaluator::setTemplate()
{
    ProStringList &values = valuesRef(statics.strTEMPLATE);
850
851
    if (!m_option->user_template.isEmpty()) {
        // Don't allow override
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
852
        values = ProStringList(ProString(m_option->user_template));
853
854
    } else {
        if (values.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
855
            values.append(ProString("app"));
856
857
858
859
860
861
862
        else
            values.erase(values.begin() + 1, values.end());
    }
    if (!m_option->user_template_prefix.isEmpty()) {
        QString val = values.first().toQString(m_tmp1);
        if (!val.startsWith(m_option->user_template_prefix)) {
            val.prepend(m_option->user_template_prefix);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
863
            values = ProStringList(ProString(val));
864
865
        }
    }
866
867
}

868
869
870
871
void QMakeEvaluator::loadDefaults()
{
    ProValueMap &vars = m_valuemapStack.top();

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
872
873
874
    vars[ProKey("DIR_SEPARATOR")] << ProString(m_option->dir_sep);
    vars[ProKey("DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep);
    vars[ProKey("_DATE_")] << ProString(QDateTime::currentDateTime().toString());
875
    if (!m_option->qmake_abslocation.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
876
        vars[ProKey("QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation);
877
#if defined(Q_OS_WIN32)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
878
    vars[ProKey("QMAKE_HOST.os")] << ProString("Windows");
879
880
881
882

    DWORD name_length = 1024;
    wchar_t name[1024];
    if (GetComputerName(name, &name_length))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
883
        vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name));
884
885

    QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
886
    vars[ProKey("QMAKE_HOST.version")] << ProString(QString::number(ver));
887
888
    ProString verStr;
    switch (ver) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
889
890
891
892
893
894
895
896
897
    case QSysInfo::WV_Me: verStr = ProString("WinMe"); break;
    case QSysInfo::WV_95: verStr = ProString("Win95"); break;
    case QSysInfo::WV_98: verStr = ProString("Win98"); break;
    case QSysInfo::WV_NT: verStr = ProString("WinNT"); break;
    case QSysInfo::WV_2000: verStr = ProString("Win2000"); break;
    case QSysInfo::WV_2003: verStr = ProString("Win2003"); break;
    case QSysInfo::WV_XP: verStr = ProString("WinXP"); break;
    case QSysInfo::WV_VISTA: verStr = ProString("WinVista"); break;
    default: verStr = ProString("Unknown"); break;
898
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
899
    vars[ProKey("QMAKE_HOST.version_string")] << verStr;
900
901
902
903
904
905
906

    SYSTEM_INFO info;
    GetSystemInfo(&info);
    ProString archStr;
    switch (info.wProcessorArchitecture) {
# ifdef PROCESSOR_ARCHITECTURE_AMD64
    case PROCESSOR_ARCHITECTURE_AMD64:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
907
        archStr = ProString("x86_64");
908
909
910
        break;
# endif
    case PROCESSOR_ARCHITECTURE_INTEL:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
911
        archStr = ProString("x86");
912
913
914
915
916
        break;
    case PROCESSOR_ARCHITECTURE_IA64:
# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
    case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
# endif
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
917
        archStr = ProString("IA64");
918
919
        break;
    default:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
920
        archStr = ProString("Unknown");
921
922
        break;
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
923
    vars[ProKey("QMAKE_HOST.arch")] << archStr;
924
925

# if defined(Q_CC_MSVC) // ### bogus condition, but nobody x-builds for msvc with a different qmake
926
927
928
929
930
931
932
933
934
935
    QLatin1Char backslash('\\');
    QString paths = m_option->getEnv(QLatin1String("PATH"));
    QString vcBin64 = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
    if (!vcBin64.endsWith(backslash))
        vcBin64.append(backslash);
    vcBin64.append(QLatin1String("bin\\amd64"));
    QString vcBinX86_64 = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
    if (!vcBinX86_64.endsWith(backslash))
        vcBinX86_64.append(backslash);
    vcBinX86_64.append(QLatin1String("bin\\x86_amd64"));
936
937
    if (paths.contains(vcBin64, Qt::CaseInsensitive)
            || paths.contains(vcBinX86_64, Qt::CaseInsensitive))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
938
        vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86_64");
939
    else
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
940
        vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86");
941
# endif
942
943
944
#elif defined(Q_OS_UNIX)
    struct utsname name;
    if (!uname(&name)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
945
946
947
948
949
        vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
        vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename));
        vars[ProKey("QMAKE_HOST.version")] << ProString(name.release);
        vars[ProKey("QMAKE_HOST.version_string")] << ProString(name.version);
        vars[ProKey("QMAKE_HOST.arch")] << ProString(name.machine);
950
951
    }
#endif
952
953

    m_valuemapInited = true;
954
955
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
956
bool QMakeEvaluator::prepareProject(const QString &inDir)
957
{
958
    QString superdir;
959
    if (m_option->do_cache) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
960
        QString conffile;
961
962
963
964
        QString cachefile = m_option->cachefile;
        if (cachefile.isEmpty())  { //find it as it has not been specified
            if (m_outputDir.isEmpty())
                goto no_cache;
965
            superdir = m_outputDir;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
966
967
968
969
970
971
972
973
974
975
976
977
978
            forever {
                QString superfile = superdir + QLatin1String("/.qmake.super");
                if (IoUtils::exists(superfile)) {
                    m_superfile = superfile;
                    break;
                }
                QFileInfo qdfi(superdir);
                if (qdfi.isRoot()) {
                    superdir.clear();
                    break;
                }
                superdir = qdfi.path();
            }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
979
            QString sdir = inDir;
980
            QString dir = m_outputDir;
981
            forever {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
982
983
984
                conffile = sdir + QLatin1String("/.qmake.conf");
                if (!IoUtils::exists(conffile))
                    conffile.clear();
985
                cachefile = dir + QLatin1String("/.qmake.cache");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
986
987
988
                if (!IoUtils::exists(cachefile))
                    cachefile.clear();
                if (!conffile.isEmpty() || !cachefile.isEmpty()) {
989
990
                    if (dir != sdir)
                        m_sourceRoot = sdir;
991
                    m_buildRoot = dir;
992
993
                    break;
                }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
994
995
                if (dir == superdir)
                    goto no_cache;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
996
                QFileInfo qsdfi(sdir);
997
                QFileInfo qdfi(dir);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
998
                if (qsdfi.isRoot() || qdfi.isRoot())
999
                    goto no_cache;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1000
                sdir = qsdfi.path();
1001
                dir = qdfi.path();
1002
            }
1003
1004
        } else {
            m_buildRoot = QFileInfo(cachefile).path();
1005
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1006
        m_conffile = conffile;
1007
        m_cachefile = cachefile;
1008
    }
1009
  no_cache:
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031

    // Look for mkspecs/ in source and build. First to win determines the root.
    QString sdir = inDir;
    QString dir = m_outputDir;
    while (dir != m_buildRoot) {
        if ((dir != sdir && QFileInfo(sdir, QLatin1String("mkspecs")).isDir())
                || QFileInfo(dir, QLatin1String("mkspecs")).isDir()) {
            if (dir != sdir)
                m_sourceRoot = sdir;
            m_buildRoot = dir;
            break;
        }
        if (dir == superdir)
            break;
        QFileInfo qsdfi(sdir);
        QFileInfo qdfi(dir);
        if (qsdfi.isRoot() || qdfi.isRoot())
            break;
        sdir = qsdfi.path();
        dir = qdfi.path();
    }

1032
    return true;
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
bool QMakeEvaluator::loadSpecInternal()
{
    if (!evaluateFeatureFile(QLatin1String("spec_pre.prf")))
        return false;
    QString spec = m_qmakespec + QLatin1String("/qmake.conf");
    if (!evaluateFileDirect(spec, QMakeHandler::EvalConfigFile, LoadProOnly)) {
        evalError(fL1S("Could not read qmake configuration file %1.").arg(spec));
        return false;
    }
#ifdef Q_OS_UNIX
    m_qmakespecFull = QFileInfo(m_qmakespec).canonicalFilePath();
#else
    // We can't resolve symlinks as they do on Unix, so configure.exe puts
    // the source of the qmake.conf at the end of the default/qmake.conf in
    // the QMAKESPEC_ORIGINAL variable.
    const ProString &orig_spec = first(ProKey("QMAKESPEC_ORIGINAL"));
    m_qmakespecFull = orig_spec.isEmpty() ? m_qmakespec : orig_spec.toQString();
#endif
    valuesRef(ProKey("QMAKESPEC")) << ProString(m_qmakespecFull);
    m_qmakespecName = IoUtils::fileName(m_qmakespecFull).toString();
    if (!evaluateFeatureFile(QLatin1String("spec_post.prf")))
        return false;
    // The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it
    m_dirSep = first(ProKey("QMAKE_DIR_SEP"));
    return true;
}

1062
bool QMakeEvaluator::loadSpec()
1063
{
1064
1065
    QString qmakespec = m_option->expandEnvVars(
                m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
1066
1067
1068

    {
        QMakeEvaluator evaluator(m_option, m_parser, m_handler);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1069
        if (!m_superfile.isEmpty()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1070
            valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1071
1072
1073
            if (!evaluator.evaluateFileDirect(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly))
                return false;
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1074
        if (!m_conffile.isEmpty()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1075
            valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1076
1077
1078
            if (!evaluator.evaluateFileDirect(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly))
                return false;
        }
1079
        if (!m_cachefile.isEmpty()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1080
            valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
1081
1082
1083
            if (!evaluator.evaluateFileDirect(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly))
                return false;
        }
1084
1085
        if (qmakespec.isEmpty()) {
            if (!m_hostBuild)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1086
                qmakespec = evaluator.first(ProKey("XQMAKESPEC")).toQString();
1087
            if (qmakespec.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1088
                qmakespec = evaluator.first(ProKey("QMAKESPEC")).toQString();
1089
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1090
1091
        m_qmakepath = evaluator.values(ProKey("QMAKEPATH")).toQStringList();
        m_qmakefeatures = evaluator.values(ProKey("QMAKEFEATURES")).toQStringList();
1092
1093
    }

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1094
    updateMkspecPaths();
1095
    if (qmakespec.isEmpty())
1096
        qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
1097
    if (IoUtils::isRelativePath(qmakespec)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1098
        foreach (const QString &root, m_mkspecPaths) {
1099
1100
1101
1102
1103
1104
            QString mkspec = root + QLatin1Char('/') + qmakespec;
            if (IoUtils::exists(mkspec)) {
                qmakespec = mkspec;
                goto cool;
            }
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1105
        evalError(fL1S("Could not find qmake configuration file %1.").arg(qmakespec));
1106
        return false;
1107
1108
    }
  cool:
1109
    m_qmakespec = QDir::cleanPath(qmakespec);
1110

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1111
1112
1113
1114
    if (!m_superfile.isEmpty()
        && !evaluateFileDirect(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
        return false;
    }
1115
    if (!loadSpecInternal())
1116
        return false;
1117
    updateFeaturePaths(); // The spec extends the feature search path, so rebuild the cache.
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1118
1119
1120
1121
    if (!m_conffile.isEmpty()
        && !evaluateFileDirect(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
        return false;
    }
1122
1123
    if (!m_cachefile.isEmpty()
        && !evaluateFileDirect(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
1124
1125
1126
        return false;
    }
    return true;
1127
1128
}

1129
1130
1131
void QMakeEvaluator::setupProject()
{
    setTemplate();
1132
    ProValueMap &vars = m_valuemapStack.top();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1133
1134
1135
1136
    vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName());
    vars[ProKey("_PRO_FILE_")] << ProString(currentFileName());
    vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory());
    vars[ProKey("OUT_PWD")] << ProString(m_outputDir);
1137
1138
}

1139
1140
1141
1142
void QMakeEvaluator::visitCmdLine(const QString &cmds)
{
    if (!cmds.isEmpty()) {
        if (ProFile *pro = m_parser->parsedProBlock(fL1S("(command line)"), cmds)) {
1143
1144
1145
1146
1147
            if (pro->isOk()) {
                m_locationStack.push(m_current);
                visitProBlock(pro, pro->tokPtr());
                m_current = m_locationStack.pop();
            }
1148
1149
1150
1151
1152
1153
            pro->deref();
        }
    }
}

QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1154
        ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags)
1155
1156
1157
1158
{
    if (!m_cumulative && !pro->isOk())
        return ReturnFalse;

1159
    if (flags & LoadPreFiles) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1160
        if (!prepareProject(pro->directoryName()))
1161
1162
            return ReturnFalse;

1163
1164
        m_hostBuild = pro->isHostBuild();

1165
1166
1167
#ifdef PROEVALUATOR_THREAD_SAFE
        m_option->mutex.lock();
#endif
1168
        QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_hostBuild)];
1169
1170
1171
1172
        if (!*baseEnvPtr)
            *baseEnvPtr = new QMakeBaseEnv;
        QMakeBaseEnv *baseEnv = *baseEnvPtr;

1173
1174
#ifdef PROEVALUATOR_THREAD_SAFE
        {
1175
1176
1177
            QMutexLocker locker(&baseEnv->mutex);
            m_option->mutex.unlock();
            if (baseEnv->inProgress) {
1178
                QThreadPool::globalInstance()->releaseThread();
1179
                baseEnv->cond.wait(&baseEnv->mutex);
1180
                QThreadPool::globalInstance()->reserveThread();
1181
                if (!baseEnv->isOk)
1182
                    return ReturnFalse;
1183
1184
            } else
#endif
1185
            if (!baseEnv->evaluator) {
1186
#ifdef PROEVALUATOR_THREAD_SAFE
1187
                baseEnv->inProgress = true;
1188
1189
1190
                locker.unlock();
#endif

1191
1192
                QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_handler);
                baseEnv->evaluator = baseEval;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1193
                baseEval->m_superfile = m_superfile;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1194
                baseEval->m_conffile = m_conffile;
1195
                baseEval->m_cachefile = m_cachefile;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1196
                baseEval->m_sourceRoot = m_sourceRoot;
1197
                baseEval->m_buildRoot = m_buildRoot;
1198
                baseEval->m_hostBuild = m_hostBuild;
1199
                bool ok = baseEval->loadSpec();
1200
1201
1202

#ifdef PROEVALUATOR_THREAD_SAFE
                locker.relock();
1203
1204
1205
                baseEnv->isOk = ok;
                baseEnv->inProgress = false;
                baseEnv->cond.wakeAll();
1206
#endif
1207
1208
1209

                if (!ok)
                    return ReturnFalse;
1210
1211
1212
1213
1214
            }
#ifdef PROEVALUATOR_THREAD_SAFE
        }
#endif

1215
        initFrom(*baseEnv->evaluator);
1216
    } else {
1217
        if (!m_valuemapInited)
1218
            loadDefaults();
1219
1220
1221
1222
    }

    m_handler->aboutToEval(currentProFile(), pro, type);
    m_profileStack.push(pro);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1223
    valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1224
    if (flags & LoadPreFiles) {
1225
1226
        setupProject();

1227
1228
1229
1230
1231
1232
1233
        evaluateFeatureFile(QLatin1String("default_pre.prf"));

        visitCmdLine(m_option->precmds);
    }

    visitProBlock(pro, pro->tokPtr());

1234
    if (flags & LoadPostFiles) {
1235
1236
1237
1238
1239
1240
1241
        visitCmdLine(m_option->postcmds);

        evaluateFeatureFile(QLatin1String("default_post.prf"));

        QSet<QString> processed;
        forever {
            bool finished = true;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1242
            ProStringList configs = values(statics.strCONFIG);
1243
1244
1245
1246
1247
            for (int i = configs.size() - 1; i >= 0; --i) {
                QString config = configs.at(i).toQString(m_tmp1).toLower();
                if (!processed.contains(config)) {
                    config.detach();
                    processed.insert(config);
1248
                    if (evaluateFeatureFile(config, true)) {
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
                        finished = false;
                        break;
                    }
                }
            }
            if (finished)
                break;
        }
    }
    m_profileStack.pop();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1259
    valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1260
1261
1262
1263
1264
1265
    m_handler->doneWithEval(currentProFile());

    return ReturnTrue;
}


Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1266
void QMakeEvaluator::updateMkspecPaths()
1267
1268
1269
1270
{
    QStringList ret;
    const QString concat = QLatin1String("/mkspecs");

1271
1272
    foreach (const QString &it, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))
        ret << it + concat;
1273

1274
1275
1276
    foreach (const QString &it, m_qmakepath)
        ret << it + concat;

1277
1278
1279
1280
    if (!m_buildRoot.isEmpty())
        ret << m_buildRoot + concat;
    if (!m_sourceRoot.isEmpty())
        ret << m_sourceRoot + concat;
1281

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1282
    ret << m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + concat;
1283
1284

    ret.removeDuplicates();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1285
    m_mkspecPaths = ret;
1286
1287
}

1288
void QMakeEvaluator::updateFeaturePaths()
1289
1290
{
    QString mkspecs_concat = QLatin1String("/mkspecs");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1291
    QString features_concat = QLatin1String("/features/");
1292
1293
1294

    QStringList feature_roots;

1295
    foreach (const QString &f, m_option->getPathListEnv(QLatin1String("QMAKEFEATURES")))
1296
        feature_roots += f;
1297

1298
1299
    feature_roots += m_qmakefeatures;

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1300
    feature_roots += m_option->propertyValue(ProKey("QMAKEFEATURES")).toQString(m_mtmp).split(
1301
1302
            m_option->dirlist_sep, QString::SkipEmptyParts);

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1303
    QStringList feature_bases;
1304
1305
1306
1307
    if (!m_buildRoot.isEmpty())
        feature_bases << m_buildRoot;
    if (!m_sourceRoot.isEmpty())
        feature_bases << m_sourceRoot;
1308

1309
    foreach (const QString &item, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))