profileevaluator.cpp 129 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** 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.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
32

con's avatar
con committed
33
#include "profileevaluator.h"
34
35

#include "profileparser.h"
36
#include "ioutils.h"
con's avatar
con committed
37
38

#include <QtCore/QByteArray>
39
#include <QtCore/QDateTime>
con's avatar
con committed
40
41
42
43
44
45
46
47
48
49
50
#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>
51
#ifdef PROEVALUATOR_THREAD_SAFE
52
53
# include <QtCore/QThreadPool>
#endif
con's avatar
con committed
54

55
56
57
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/utsname.h>
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
58
#else
59
60
61
62
63
#include <Windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>

con's avatar
con committed
64
65
#ifdef Q_OS_WIN32
#define QT_POPEN _popen
66
#define QT_PCLOSE _pclose
con's avatar
con committed
67
68
#else
#define QT_POPEN popen
69
#define QT_PCLOSE pclose
con's avatar
con committed
70
71
#endif

72
73
using namespace ProFileEvaluatorInternal;

con's avatar
con committed
74
75
QT_BEGIN_NAMESPACE

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
76
77
78
using namespace ProStringConstants;


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

81
82
///////////////////////////////////////////////////////////////////////
//
83
// ProFileOption
84
85
86
//
///////////////////////////////////////////////////////////////////////

87
ProFileOption::ProFileOption()
88
89
90
91
92
93
94
95
{
#ifdef Q_OS_WIN
    dirlist_sep = QLatin1Char(';');
    dir_sep = QLatin1Char('\\');
#else
    dirlist_sep = QLatin1Char(':');
    dir_sep = QLatin1Char('/');
#endif
96
    qmakespec = getEnv(QLatin1String("QMAKESPEC"));
97

98
99
100
    host_mode = HOST_UNKNOWN_MODE;
    target_mode = TARG_UNKNOWN_MODE;

101
102
103
104
105
106
107
108
109
#ifdef PROEVALUATOR_THREAD_SAFE
    base_inProgress = false;
#endif
}

ProFileOption::~ProFileOption()
{
}

110
111
void ProFileOption::setCommandLineArguments(const QStringList &args)
{
112
113
    QStringList _precmds, _preconfigs, _postcmds, _postconfigs;
    bool after = false;
114

115
116
117
118
119
120
121
122
123
124
125
126
127
128
    bool isConf = false;
    foreach (const QString &arg, args) {
        if (isConf) {
            isConf = false;
            if (after)
                _postconfigs << arg;
            else
                _preconfigs << arg;
        } else if (arg.startsWith(QLatin1Char('-'))) {
            if (arg == QLatin1String("-after")) {
                after = true;
            } else if (arg == QLatin1String("-config")) {
                isConf = true;
            } else if (arg == QLatin1String("-win32")) {
129
                host_mode = HOST_WIN_MODE;
130
131
                target_mode = TARG_WIN_MODE;
            } else if (arg == QLatin1String("-unix")) {
132
                host_mode = HOST_UNIX_MODE;
133
134
                target_mode = TARG_UNIX_MODE;
            } else if (arg == QLatin1String("-macx")) {
135
                host_mode = HOST_MACX_MODE;
136
                target_mode = TARG_MACX_MODE;
137
            }
138
139
140
141
142
        } else if (arg.contains(QLatin1Char('='))) {
            if (after)
                _postcmds << arg;
            else
                _precmds << arg;
143
144
        }
    }
145
146
147
148
149
150
151

    if (!_preconfigs.isEmpty())
        _precmds << (fL1S("CONFIG += ") + _preconfigs.join(fL1S(" ")));
    precmds = _precmds.join(fL1S("\n"));
    if (!_postconfigs.isEmpty())
        _postcmds << (fL1S("CONFIG += ") + _postconfigs.join(fL1S(" ")));
    postcmds = _postcmds.join(fL1S("\n"));
152
153
154

    if (host_mode != HOST_UNKNOWN_MODE)
        applyHostMode();
155
156
}

157
void ProFileOption::applyHostMode()
158
{
159
160
161
162
163
   if (host_mode == HOST_WIN_MODE) {
       dir_sep = fL1S("\\");
   } else {
       dir_sep = fL1S("/");
   }
164
165
}

166
167
QString ProFileOption::getEnv(const QString &var) const
{
168
#ifndef QT_BOOTSTRAPPED
169
170
171
172
173
174
    if (!environment.isEmpty())
        return environment.value(var);
#endif
    return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
}

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#ifdef PROEVALUATOR_INIT_PROPS
bool ProFileOption::initProperties(const QString &qmake)
{
    QByteArray data;
#ifndef QT_BOOTSTRAPPED
    QProcess proc;
    proc.start(qmake, QStringList() << QLatin1String("-query"));
    if (!proc.waitForFinished())
        return false;
    data = proc.readAll();
#else
    if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake) + QLatin1String(" -query"))
                              .toLocal8Bit(), "r")) {
        char buff[1024];
        while (!feof(proc))
            data.append(buff, int(fread(buff, 1, 1023, proc)));
        QT_PCLOSE(proc);
    }
#endif
    foreach (QByteArray line, data.split('\n'))
        if (!line.startsWith("QMAKE_")) {
            int off = line.indexOf(':');
            if (off < 0) // huh?
                continue;
            if (line.endsWith('\r'))
                line.chop(1);
            properties.insert(QString::fromLatin1(line.left(off)),
                              QString::fromLocal8Bit(line.mid(off + 1)));
        }
    return true;
}
#endif

con's avatar
con committed
208
209
210
211
212
213
///////////////////////////////////////////////////////////////////////
//
// ProFileEvaluator::Private
//
///////////////////////////////////////////////////////////////////////

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
214
class ProFileEvaluator::Private
con's avatar
con committed
215
216
{
public:
217
    static void initStatics();
218
    Private(ProFileEvaluator *q_, ProFileOption *option, ProFileParser *parser,
219
            ProFileEvaluatorHandler *handler);
220
    ~Private();
con's avatar
con committed
221

222
223
    ProFileEvaluator *q;

224
225
226
227
228
229
230
231
    enum VisitReturn {
        ReturnFalse,
        ReturnTrue,
        ReturnBreak,
        ReturnNext,
        ReturnReturn
    };

232
233
234
    static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
    ProString getStr(const ushort *&tokPtr);
    ProString getHashStr(const ushort *&tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
235
    void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
236
237
    static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
    static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
238
    void skipExpression(const ushort *&tokPtr);
239

240
    void visitCmdLine(const QString &cmds);
241
242
    VisitReturn visitProFile(ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
                             ProFileEvaluator::LoadFlags flags);
243
    VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
244
    VisitReturn visitProBlock(const ushort *tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
245
    VisitReturn visitProLoop(const ProString &variable, const ushort *exprPtr,
246
247
                             const ushort *tokPtr);
    void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
248
    void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
con's avatar
con committed
249

250
    static inline const ProString &map(const ProString &var);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
251
252
253
254
255
    QHash<ProString, ProStringList> *findValues(const ProString &variableName,
                                                QHash<ProString, ProStringList>::Iterator *it);
    ProStringList &valuesRef(const ProString &variableName);
    ProStringList valuesDirect(const ProString &variableName) const;
    ProStringList values(const ProString &variableName) const;
256
    QString propertyValue(const QString &val, bool complain) const;
con's avatar
con committed
257

258
    ProStringList split_value_list(const QString &vals, const ProFile *source = 0);
con's avatar
con committed
259
    bool isActiveConfig(const QString &config, bool regex = false);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
260
    ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
261
    ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
262
    ProStringList evaluateExpandFunction(const ProString &function, const ProString &arguments);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
263
264
    ProStringList evaluateExpandFunction(const ProString &function, const ushort *&tokPtr);
    ProStringList evaluateExpandFunction(const ProString &function, const ProStringList &args);
265
    void evalError(const QString &msg) const;
con's avatar
con committed
266
267

    QString currentFileName() const;
268
    QString currentDirectory() const;
con's avatar
con committed
269
    ProFile *currentProFile() const;
270
271
    QString resolvePath(const QString &fileName) const
        { return IoUtils::resolvePath(currentDirectory(), fileName); }
con's avatar
con committed
272

273
    VisitReturn evaluateConditionalFunction(const ProString &function, const ProString &arguments);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
274
275
    VisitReturn evaluateConditionalFunction(const ProString &function, const ushort *&tokPtr);
    VisitReturn evaluateConditionalFunction(const ProString &function, const ProStringList &args);
276
277
278
279
    bool evaluateFileDirect(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
                            ProFileEvaluator::LoadFlags flags);
    bool evaluateFile(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
                      ProFileEvaluator::LoadFlags flags);
280
    bool evaluateFeatureFile(const QString &fileName);
281
    enum EvalIntoMode { EvalProOnly, EvalWithDefaults, EvalWithSetup };
282
    bool evaluateFileInto(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
283
284
                          QHash<ProString, ProStringList> *values, FunctionDefs *defs,
                          EvalIntoMode mode); // values are output-only, defs are input-only
con's avatar
con committed
285

286
    static ALWAYS_INLINE VisitReturn returnBool(bool b)
287
        { return b ? ReturnTrue : ReturnFalse; }
288

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
289
    QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
290
    QList<ProStringList> prepareFunctionArgs(const ProString &arguments);
291
    ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok);
292
293
    VisitReturn evaluateBoolFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList,
                                     const ProString &function);
294

295
296
297
    bool modesForGenerator(const QString &gen,
            ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const;
    void validateModes() const;
298
299
    QStringList qmakeMkspecPaths() const;
    QStringList qmakeFeaturePaths() const;
con's avatar
con committed
300

301
302
    QString expandEnvVars(const QString &str) const;
    QString fixPathToLocalOS(const QString &str) const;
303
304
    QString sysrootify(const QString &path, const QString &baseDir) const;

305
306
307
308
#ifndef QT_BOOTSTRAPPED
    void runProcess(QProcess *proc, const QString &command, QProcess::ProcessChannel chan) const;
#endif

309
    int m_skipLevel;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
310
    int m_loopLevel; // To report unexpected break() and next()s
311
#ifdef PROEVALUATOR_CUMULATIVE
312
    bool m_cumulative;
313
314
315
#else
    enum { m_cumulative = 0 };
#endif
316
317
318
319
320
321
322
323
324
325
326

    struct Location {
        Location() : pro(0), line(0) {}
        Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {}
        ProFile *pro;
        int line;
    };

    Location m_current; // Currently evaluated location
    QStack<Location> m_locationStack; // All execution location changes
    QStack<ProFile *> m_profileStack; // Includes only
con's avatar
con committed
327

328
    QString m_outputDir;
con's avatar
con committed
329

330
    int m_listCount;
331
    FunctionDefs m_functionDefs;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
332
333
334
    ProStringList m_returnValue;
    QStack<QHash<ProString, ProStringList> > m_valuemapStack;         // VariableName must be us-ascii, the content however can be non-us-ascii.
    QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
335

336
    ProFileOption *m_option;
337
    ProFileParser *m_parser;
338
    ProFileEvaluatorHandler *m_handler;
339
340

    enum ExpandFunc {
341
        E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
342
343
344
345
346
347
348
        E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
        E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
        E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
        E_REPLACE
    };

    enum TestFunc {
349
        T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
350
351
        T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
        T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
352
        T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF
353
354
355
356
357
358
359
360
361
362
363
    };

    enum VarName {
        V_LITERAL_DOLLAR, V_LITERAL_HASH, V_LITERAL_WHITESPACE,
        V_DIRLIST_SEPARATOR, V_DIR_SEPARATOR,
        V_OUT_PWD, V_PWD, V_IN_PWD,
        V__FILE_, V__LINE_, V__PRO_FILE_, V__PRO_FILE_PWD_,
        V_QMAKE_HOST_arch, V_QMAKE_HOST_name, V_QMAKE_HOST_os,
        V_QMAKE_HOST_version, V_QMAKE_HOST_version_string,
        V__DATE_, V__QMAKE_CACHE_
    };
con's avatar
con committed
364
365
};

366
367
static struct {
    QString field_sep;
368
369
370
371
372
373
    QString strtrue;
    QString strfalse;
    QString strunix;
    QString strmacx;
    QString strmac;
    QString strwin32;
374
    QString strsymbian;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
375
376
    ProString strCONFIG;
    ProString strARGS;
377
378
379
380
    QString strDot;
    QString strDotDot;
    QString strever;
    QString strforever;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
381
382
    ProString strTEMPLATE;
    ProString strQMAKE_DIR_SEP;
383
    QHash<ProString, int> expands;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
384
385
386
    QHash<ProString, int> functions;
    QHash<ProString, int> varList;
    QHash<ProString, ProString> varMap;
387
    QRegExp reg_variableName;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
388
    ProStringList fakeValue;
389
390
391
392
393
394
395
396
} statics;

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

    statics.field_sep = QLatin1String(" ");
397
398
399
400
401
402
    statics.strtrue = QLatin1String("true");
    statics.strfalse = QLatin1String("false");
    statics.strunix = QLatin1String("unix");
    statics.strmacx = QLatin1String("macx");
    statics.strmac = QLatin1String("mac");
    statics.strwin32 = QLatin1String("win32");
403
    statics.strsymbian = QLatin1String("symbian");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
404
405
    statics.strCONFIG = ProString("CONFIG");
    statics.strARGS = ProString("ARGS");
406
407
408
409
    statics.strDot = QLatin1String(".");
    statics.strDotDot = QLatin1String("..");
    statics.strever = QLatin1String("ever");
    statics.strforever = QLatin1String("forever");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
410
411
    statics.strTEMPLATE = ProString("TEMPLATE");
    statics.strQMAKE_DIR_SEP = ProString("QMAKE_DIR_SEP");
412
413
414

    statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
    statics.reg_variableName.setMinimal(true);
415

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
416
417
    statics.fakeValue.detach(); // It has to have a unique begin() value

418
419
420
421
422
423
424
    static const struct {
        const char * const name;
        const ExpandFunc func;
    } expandInits[] = {
        { "member", E_MEMBER },
        { "first", E_FIRST },
        { "last", E_LAST },
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
425
        { "size", E_SIZE },
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
        { "cat", E_CAT },
        { "fromfile", E_FROMFILE },
        { "eval", E_EVAL },
        { "list", E_LIST },
        { "sprintf", E_SPRINTF },
        { "join", E_JOIN },
        { "split", E_SPLIT },
        { "basename", E_BASENAME },
        { "dirname", E_DIRNAME },
        { "section", E_SECTION },
        { "find", E_FIND },
        { "system", E_SYSTEM },
        { "unique", E_UNIQUE },
        { "quote", E_QUOTE },
        { "escape_expand", E_ESCAPE_EXPAND },
        { "upper", E_UPPER },
        { "lower", E_LOWER },
        { "re_escape", E_RE_ESCAPE },
        { "files", E_FILES },
        { "prompt", E_PROMPT }, // interactive, so cannot be implemented
        { "replace", E_REPLACE }
    };
    for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
449
        statics.expands.insert(ProString(expandInits[i].name), expandInits[i].func);
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

    static const struct {
        const char * const name;
        const TestFunc func;
    } testInits[] = {
        { "requires", T_REQUIRES },
        { "greaterThan", T_GREATERTHAN },
        { "lessThan", T_LESSTHAN },
        { "equals", T_EQUALS },
        { "isEqual", T_EQUALS },
        { "exists", T_EXISTS },
        { "export", T_EXPORT },
        { "clear", T_CLEAR },
        { "unset", T_UNSET },
        { "eval", T_EVAL },
        { "CONFIG", T_CONFIG },
        { "if", T_IF },
        { "isActiveConfig", T_CONFIG },
        { "system", T_SYSTEM },
        { "return", T_RETURN },
        { "break", T_BREAK },
        { "next", T_NEXT },
        { "defined", T_DEFINED },
        { "contains", T_CONTAINS },
        { "infile", T_INFILE },
        { "count", T_COUNT },
        { "isEmpty", T_ISEMPTY },
        { "load", T_LOAD },
        { "include", T_INCLUDE },
        { "debug", T_DEBUG },
        { "message", T_MESSAGE },
        { "warning", T_MESSAGE },
        { "error", T_MESSAGE },
    };
    for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
485
        statics.functions.insert(ProString(testInits[i].name), testInits[i].func);
486
487
488
489
490
491
492
493
494
495
496

    static const char * const names[] = {
        "LITERAL_DOLLAR", "LITERAL_HASH", "LITERAL_WHITESPACE",
        "DIRLIST_SEPARATOR", "DIR_SEPARATOR",
        "OUT_PWD", "PWD", "IN_PWD",
        "_FILE_", "_LINE_", "_PRO_FILE_", "_PRO_FILE_PWD_",
        "QMAKE_HOST.arch", "QMAKE_HOST.name", "QMAKE_HOST.os",
        "QMAKE_HOST.version", "QMAKE_HOST.version_string",
        "_DATE_", "_QMAKE_CACHE_"
    };
    for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
497
        statics.varList.insert(ProString(names[i]), i);
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

    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" },
        { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }
    };
    for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
523
524
        statics.varMap.insert(ProString(mapInits[i].oldname),
                              ProString(mapInits[i].newname));
525
526
}

527
const ProString &ProFileEvaluator::Private::map(const ProString &var)
528
{
529
530
    QHash<ProString, ProString>::ConstIterator it = statics.varMap.constFind(var);
    return (it != statics.varMap.constEnd()) ? it.value() : var;
531
532
533
}


534
ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option,
535
536
                                   ProFileParser *parser, ProFileEvaluatorHandler *handler)
  : q(q_), m_option(option), m_parser(parser), m_handler(handler)
con's avatar
con committed
537
{
538
539
540
    // So that single-threaded apps don't have to call initialize() for now.
    initStatics();

541
    // Configuration, more or less
542
#ifdef PROEVALUATOR_CUMULATIVE
543
    m_cumulative = true;
544
#endif
545
546

    // Evaluator state
547
    m_skipLevel = 0;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
548
    m_loopLevel = 0;
549
    m_listCount = 0;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
550
    m_valuemapStack.push(QHash<ProString, ProStringList>());
con's avatar
con committed
551
552
}

553
554
555
556
ProFileEvaluator::Private::~Private()
{
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
557
558
//////// Evaluator tools /////////

559
560
561
562
563
564
565
566
567
568
uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr)
{
    uint len = *tokPtr++;
    len |= (uint)*tokPtr++ << 16;
    return len;
}

ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr)
{
    uint len = *tokPtr++;
569
    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, NoHash);
570
    ret.setSource(m_current.pro);
571
572
573
574
575
576
577
578
    tokPtr += len;
    return ret;
}

ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr)
{
    uint hash = getBlockLen(tokPtr);
    uint len = *tokPtr++;
579
    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
    tokPtr += len;
    return ret;
}

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

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
597
598
// FIXME: this should not build new strings for direct sections.
// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
599
ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals, const ProFile *source)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
600
601
{
    QString build;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
602
    ProStringList ret;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
603
604
    QStack<char> quote;

605
    const ushort SPACE = ' ';
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
606
607
608
609
610
611
    const ushort LPAREN = '(';
    const ushort RPAREN = ')';
    const ushort SINGLEQUOTE = '\'';
    const ushort DOUBLEQUOTE = '"';
    const ushort BACKSLASH = '\\';

612
613
614
    if (!source)
        source = currentProFile();

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
    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;
        }

633
        if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
634
            ret << ProString(build, NoHash).setSource(source);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
635
636
637
638
639
640
            build.clear();
        } else {
            build += vals_data[x];
        }
    }
    if (!build.isEmpty())
641
        ret << ProString(build, NoHash).setSource(source);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
642
643
644
    return ret;
}

645
646
647
648
649
650
651
static void zipEmpty(ProStringList *value)
{
    for (int i = value->size(); --i >= 0;)
        if (value->at(i).isEmpty())
            value->remove(i);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
652
static void insertUnique(ProStringList *varlist, const ProStringList &value)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
653
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
654
    foreach (const ProString &str, value)
655
        if (!str.isEmpty() && !varlist->contains(str))
656
            varlist->append(str);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
657
658
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
659
static void removeAll(ProStringList *varlist, const ProString &value)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
660
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
661
662
663
    for (int i = varlist->size(); --i >= 0; )
        if (varlist->at(i) == value)
            varlist->remove(i);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
664
665
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
666
static void removeEach(ProStringList *varlist, const ProStringList &value)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
667
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
668
    foreach (const ProString &str, value)
669
670
        if (!str.isEmpty())
            removeAll(varlist, str);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
671
672
673
674
675
676
677
678
679
680
681
}

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);
        if (!val.isSharedWith(copy)) {
            if (val.isEmpty()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
682
                varit = varlist->erase(varit);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
683
            } else {
684
                (*varit).setValue(val, NoHash);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
685
                ++varit;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
686
            }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
687
688
689
690
691
692
693
694
            if (!global)
                break;
        } else {
            ++varit;
        }
    }
}

695
QString ProFileEvaluator::Private::expandEnvVars(const QString &str) const
696
697
698
{
    QString string = str;
    int rep;
699
    QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
700
701
    while ((rep = reg_variableName.indexIn(string)) != -1)
        string.replace(rep, reg_variableName.matchedLength(),
702
                       m_option->getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3)));
703
704
705
706
    return string;
}

// This is braindead, but we want qmake compat
707
QString ProFileEvaluator::Private::fixPathToLocalOS(const QString &str) const
708
{
709
    QString string = expandEnvVars(str);
710

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
711
    if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
712
713
714
715
716
717
718
719
720
721
        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
722
static bool isTrue(const ProString &_str, QString &tmp)
723
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
724
    const QString &str = _str.toQString(tmp);
725
726
727
    return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
728
//////// Evaluator /////////
con's avatar
con committed
729

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
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
757
758
759
760
761
762
763
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
764
                        pending = true;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
765
766
767
768
769
770
771
                        *ret += list;
                        return;
                    }
                } else {
                    ret->last().append(list.at(0));
                }
                // This is somewhat slow, but a corner case
772
773
                for (int j = 1; j < list.size(); ++j) {
                    pending = true;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
774
                    *ret << list.at(j);
775
                }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
            }
        }
    }
}

void ProFileEvaluator::Private::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:
794
            m_current.line = *tokPtr++;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
795
796
797
798
799
800
801
802
803
804
805
806
            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:
            addStr(ProString(propertyValue(
807
808
                      getStr(tokPtr).toQString(m_tmp1), true), NoHash).setSource(currentProFile()),
                   ret, pending, joined);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
809
810
            break;
        case TokEnvVar:
811
812
            addStrList(split_value_list(m_option->getEnv(getStr(tokPtr).toQString(m_tmp1))),
                       tok, ret, pending, joined);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
            break;
        case TokFuncName: {
            ProString func = getHashStr(tokPtr);
            addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
            break; }
        default:
            tokPtr--;
            return;
        }
    }
}

void ProFileEvaluator::Private::skipExpression(const ushort *&pTokPtr)
{
    const ushort *tokPtr = pTokPtr;
    forever {
        ushort tok = *tokPtr++;
        switch (tok) {
        case TokLine:
832
            m_current.line = *tokPtr++;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
            break;
        case TokValueTerminator:
        case TokFuncTerminator:
            pTokPtr = tokPtr;
            return;
        case TokArgSeparator:
            break;
        default:
            switch (tok & TokMask) {
            case TokLiteral:
            case TokProperty:
            case TokEnvVar:
                skipStr(tokPtr);
                break;
            case TokHashLiteral:
            case TokVariable:
                skipHashStr(tokPtr);
                break;
            case TokFuncName:
                skipHashStr(tokPtr);
                pTokPtr = tokPtr;
                skipExpression(pTokPtr);
                tokPtr = pTokPtr;
                break;
            default:
                Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
                break;
            }
        }
    }
}

865
866
867
868
869
870
871
872
ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
        ProFile *pro, const ushort *tokPtr)
{
    m_current.pro = pro;
    m_current.line = 0;
    return visitProBlock(tokPtr);
}

873
874
ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
        const ushort *tokPtr)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
875
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
876
    ProStringList curr;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
877
    bool okey = true, or_op = false, invert = false;
878
    uint blockLen;
879
    VisitReturn ret = ReturnTrue;
880
881
882
    while (ushort tok = *tokPtr++) {
        switch (tok) {
        case TokLine:
883
            m_current.line = *tokPtr++;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
884
            continue;
885
886
887
888
        case TokAssign:
        case TokAppend:
        case TokAppendUnique:
        case TokRemove:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
889
890
891
892
        case TokReplace:
            visitProVariable(tok, curr, tokPtr);
            curr.clear();
            continue;
893
894
        case TokBranch:
            blockLen = getBlockLen(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
895
896
897
            if (m_cumulative) {
                if (!okey)
                    m_skipLevel++;
898
899
900
                ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
                tokPtr += blockLen;
                blockLen = getBlockLen(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
901
902
903
904
                if (!okey)
                    m_skipLevel--;
                else
                    m_skipLevel++;
905
906
                if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
                    ret = visitProBlock(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
907
908
909
                if (okey)
                    m_skipLevel--;
            } else {
910
911
912
913
914
915
                if (okey)
                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
                tokPtr += blockLen;
                blockLen = getBlockLen(tokPtr);
                if (!okey)
                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
916
            }
917
            tokPtr += blockLen;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
918
919
            okey = true, or_op = false; // force next evaluation
            break;
920
921
922
        case TokForLoop:
            if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
                skipHashStr(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
923
924
                uint exprLen = getBlockLen(tokPtr);
                tokPtr += exprLen;
925
926
927
928
                blockLen = getBlockLen(tokPtr);
                ret = visitProBlock(tokPtr);
            } else if (okey != or_op) {
                const ProString &variable = getHashStr(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
929
930
931
                uint exprLen = getBlockLen(tokPtr);
                const ushort *exprPtr = tokPtr;
                tokPtr += exprLen;
932
                blockLen = getBlockLen(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
933
                ret = visitProLoop(variable, exprPtr, tokPtr);
934
935
            } else {
                skipHashStr(tokPtr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
936
937
                uint exprLen = getBlockLen(tokPtr);
                tokPtr += exprLen;
938
939
940
941
                blockLen = getBlockLen(tokPtr);
                ret = ReturnTrue;
            }
            tokPtr += blockLen;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
942
943
            okey = true, or_op = false; // force next evaluation
            break;
944
945
946
947
948
949
950
951
952
953
954
        case TokTestDef:
        case TokReplaceDef:
            if (m_cumulative || okey != or_op) {
                const ProString &name = getHashStr(tokPtr);
                blockLen = getBlockLen(tokPtr);
                visitProFunctionDef(tok, name, tokPtr);
            } else {
                skipHashStr(tokPtr);
                blockLen = getBlockLen(tokPtr);
            }
            tokPtr += blockLen;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
955
956
            okey = true, or_op = false; // force next evaluation
            continue;
957
        case TokNot:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
958
            invert ^= true;
959
960
            continue;
        case TokAnd:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
961
            or_op = false;
962
963
            continue;
        case TokOr:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
964
            or_op = true;
965
966
            continue;
        case TokCondition:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
967
            if (!m_skipLevel && okey != or_op) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
968
                if (curr.size() != 1) {
969
970
                    if (!m_cumulative || !curr.isEmpty())
                        evalError(fL1S("Conditional must expand to exactly one word."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
971
972
973
974
975
976
977
978
979
980
981
982
                    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) {
983
984
                    if (!m_cumulative || !curr.isEmpty())
                        evalError(fL1S("Test name must expand to exactly one word."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
985
986
987
988
989
990
991
992
993
994
                    skipExpression(tokPtr);
                    okey = false;
                } else {
                    ret = evaluateConditionalFunction(curr.at(0), tokPtr);
                    switch (ret) {
                    case ReturnTrue: okey = true; break;
                    case ReturnFalse: okey = false; break;
                    default: return ret;
                    }
                    okey ^= invert;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
995
996
997
                }
            } else if (m_cumulative) {
                m_skipLevel++;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
998
999
1000
                if (curr.size() != 1)
                    skipExpression(tokPtr);
                else
For faster browsing, not all history is shown. View entire blame