1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the qmake application of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "project.h"
43
#include "property.h"
44
#include "option.h"
45
#include "cachekeys.h"
46
#include "generators/metamakefile.h"
47
48
#include <qdatetime.h>
49
#include <qfile.h>
50
#include <qfileinfo.h>
51
#include <qdir.h>
52
#include <qregexp.h>
53
#include <qtextstream.h>
54
#include <qstack.h>
55
#include <qhash.h>
56
#include <qdebug.h>
57
#ifdef Q_OS_UNIX
58
#include <unistd.h>
59
#include <sys/utsname.h>
60
#elif defined(Q_OS_WIN32)
61
#include <windows.h>
62
#endif
63
#include <stdio.h>
64
#include <stdlib.h>
65
66
// Included from tools/shared
67
#include <symbian/epocroot_p.h>
68
69
#ifdef Q_OS_WIN32
70
#define QT_POPEN _popen
71
#define QT_PCLOSE _pclose
72
#else
73
#define QT_POPEN popen
74
#define QT_PCLOSE pclose
75
#endif
76
77
QT_BEGIN_NAMESPACE
78
79
//expand functions
80
enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
81
                  E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
82
                  E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
83
                  E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_REPLACE,
84
                  E_SIZE, E_GENERATE_UID };
85
QMap<QString, ExpandFunc> qmake_expandFunctions()
86
{
87
    static QMap<QString, ExpandFunc> *qmake_expand_functions = 0;
88
    if(!qmake_expand_functions) {
89
        qmake_expand_functions = new QMap<QString, ExpandFunc>;
90
        qmakeAddCacheClear(qmakeDeleteCacheClear<QMap<QString, ExpandFunc> >, (void**)&qmake_expand_functions);
91
        qmake_expand_functions->insert("member", E_MEMBER);
92
        qmake_expand_functions->insert("first", E_FIRST);
93
        qmake_expand_functions->insert("last", E_LAST);
94
        qmake_expand_functions->insert("cat", E_CAT);
95
        qmake_expand_functions->insert("fromfile", E_FROMFILE);
96
        qmake_expand_functions->insert("eval", E_EVAL);
97
        qmake_expand_functions->insert("list", E_LIST);
98
        qmake_expand_functions->insert("sprintf", E_SPRINTF);
99
        qmake_expand_functions->insert("join", E_JOIN);
100
        qmake_expand_functions->insert("split", E_SPLIT);
101
        qmake_expand_functions->insert("basename", E_BASENAME);
102
        qmake_expand_functions->insert("dirname", E_DIRNAME);
103
        qmake_expand_functions->insert("section", E_SECTION);
104
        qmake_expand_functions->insert("find", E_FIND);
105
        qmake_expand_functions->insert("system", E_SYSTEM);
106
        qmake_expand_functions->insert("unique", E_UNIQUE);
107
        qmake_expand_functions->insert("quote", E_QUOTE);
108
        qmake_expand_functions->insert("escape_expand", E_ESCAPE_EXPAND);
109
        qmake_expand_functions->insert("upper", E_UPPER);
110
        qmake_expand_functions->insert("lower", E_LOWER);
111
        qmake_expand_functions->insert("re_escape", E_RE_ESCAPE);
112
        qmake_expand_functions->insert("files", E_FILES);
113
        qmake_expand_functions->insert("prompt", E_PROMPT);
114
        qmake_expand_functions->insert("replace", E_REPLACE);
115
        qmake_expand_functions->insert("size", E_SIZE);
116
        qmake_expand_functions->insert("generate_uid", E_GENERATE_UID);
117
    }
118
    return *qmake_expand_functions;
119
}
120
//replace functions
121
enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
122
                T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
123
                T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
124
                T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_ERROR,
125
                T_MESSAGE, T_WARNING, T_IF, T_OPTION };
126
QMap<QString, TestFunc> qmake_testFunctions()
127
{
128
    static QMap<QString, TestFunc> *qmake_test_functions = 0;
129
    if(!qmake_test_functions) {
130
        qmake_test_functions = new QMap<QString, TestFunc>;
131
        qmake_test_functions->insert("requires", T_REQUIRES);
132
        qmake_test_functions->insert("greaterThan", T_GREATERTHAN);
133
        qmake_test_functions->insert("lessThan", T_LESSTHAN);
134
        qmake_test_functions->insert("equals", T_EQUALS);
135
        qmake_test_functions->insert("isEqual", T_EQUALS);
136
        qmake_test_functions->insert("exists", T_EXISTS);
137
        qmake_test_functions->insert("export", T_EXPORT);
138
        qmake_test_functions->insert("clear", T_CLEAR);
139
        qmake_test_functions->insert("unset", T_UNSET);
140
        qmake_test_functions->insert("eval", T_EVAL);
141
        qmake_test_functions->insert("CONFIG", T_CONFIG);
142
        qmake_test_functions->insert("if", T_IF);
143
        qmake_test_functions->insert("isActiveConfig", T_CONFIG);
144
        qmake_test_functions->insert("system", T_SYSTEM);
145
        qmake_test_functions->insert("return", T_RETURN);
146
        qmake_test_functions->insert("break", T_BREAK);
147
        qmake_test_functions->insert("next", T_NEXT);
148
        qmake_test_functions->insert("defined", T_DEFINED);
149
        qmake_test_functions->insert("contains", T_CONTAINS);
150
        qmake_test_functions->insert("infile", T_INFILE);
151
        qmake_test_functions->insert("count", T_COUNT);
152
        qmake_test_functions->insert("isEmpty", T_ISEMPTY);
153
        qmake_test_functions->insert("include", T_INCLUDE);
154
        qmake_test_functions->insert("load", T_LOAD);
155
        qmake_test_functions->insert("debug", T_DEBUG);
156
        qmake_test_functions->insert("error", T_ERROR);
157
        qmake_test_functions->insert("message", T_MESSAGE);
158
        qmake_test_functions->insert("warning", T_WARNING);
159
        qmake_test_functions->insert("option", T_OPTION);
160
    }
161
    return *qmake_test_functions;
162
}
163
164
struct parser_info {
165
    QString file;
166
    int line_no;
167
    bool from_file;
168
} parser;
169
170
static QString remove_quotes(const QString &arg)
171
{
172
    const ushort SINGLEQUOTE = '\'';
173
    const ushort DOUBLEQUOTE = '"';
174
175
    const QChar *arg_data = arg.data();
176
    const ushort first = arg_data->unicode();
177
    const int arg_len = arg.length();
178
    if(first == SINGLEQUOTE || first == DOUBLEQUOTE) {
179
        const ushort last = (arg_data+arg_len-1)->unicode();
180
        if(last == first)
181
            return arg.mid(1, arg_len-2);
182
    }
183
    return arg;
184
}
185
186
static QString varMap(const QString &x)
187
{
188
    QString ret(x);
189
    if(ret == "INTERFACES")
190
        ret = "FORMS";
191
    else if(ret == "QMAKE_POST_BUILD")
192
        ret = "QMAKE_POST_LINK";
193
    else if(ret == "TARGETDEPS")
194
        ret = "POST_TARGETDEPS";
195
    else if(ret == "LIBPATH")
196
        ret = "QMAKE_LIBDIR";
197
    else if(ret == "QMAKE_EXT_MOC")
198
        ret = "QMAKE_EXT_CPP_MOC";
199
    else if(ret == "QMAKE_MOD_MOC")
200
        ret = "QMAKE_H_MOD_MOC";
201
    else if(ret == "QMAKE_LFLAGS_SHAPP")
202
        ret = "QMAKE_LFLAGS_APP";
203
    else if(ret == "PRECOMPH")
204
        ret = "PRECOMPILED_HEADER";
205
    else if(ret == "PRECOMPCPP")
206
        ret = "PRECOMPILED_SOURCE";
207
    else if(ret == "INCPATH")
208
        ret = "INCLUDEPATH";
209
    else if(ret == "QMAKE_EXTRA_WIN_COMPILERS" || ret == "QMAKE_EXTRA_UNIX_COMPILERS")
210
        ret = "QMAKE_EXTRA_COMPILERS";
211
    else if(ret == "QMAKE_EXTRA_WIN_TARGETS" || ret == "QMAKE_EXTRA_UNIX_TARGETS")
212
        ret = "QMAKE_EXTRA_TARGETS";
213
    else if(ret == "QMAKE_EXTRA_UNIX_INCLUDES")
214
        ret = "QMAKE_EXTRA_INCLUDES";
215
    else if(ret == "QMAKE_EXTRA_UNIX_VARIABLES")
216
        ret = "QMAKE_EXTRA_VARIABLES";
217
    else if(ret == "QMAKE_RPATH")
218
        ret = "QMAKE_LFLAGS_RPATH";
219
    else if(ret == "QMAKE_FRAMEWORKDIR")
220
        ret = "QMAKE_FRAMEWORKPATH";
221
    else if(ret == "QMAKE_FRAMEWORKDIR_FLAGS")
222
        ret = "QMAKE_FRAMEWORKPATH_FLAGS";
223
    else
224
        return ret;
225
    warn_msg(WarnDeprecated, "%s:%d: Variable %s is deprecated; use %s instead.",
226
             parser.file.toLatin1().constData(), parser.line_no,
227
             x.toLatin1().constData(), ret.toLatin1().constData());
228
    return ret;
229
}
230
231
static QStringList split_arg_list(const QString &params)
232
{
233
    int quote = 0;
234
    QStringList args;
235
236
    const ushort LPAREN = '(';
237
    const ushort RPAREN = ')';
238
    const ushort SINGLEQUOTE = '\'';
239
    const ushort DOUBLEQUOTE = '"';
240
    const ushort BACKSLASH = '\\';
241
    const ushort COMMA = ',';
242
    const ushort SPACE = ' ';
243
    //const ushort TAB = '\t';
244
245
    const QChar *params_data = params.data();
246
    const int params_len = params.length();
247
    for(int last = 0; ;) {
248
        while(last < params_len && (params_data[last].unicode() == SPACE
249
                                    /*|| params_data[last].unicode() == TAB*/))
250
            ++last;
251
        for(int x = last, parens = 0; ; x++) {
252
            if(x == params_len) {
253
                while(x > last && params_data[x-1].unicode() == SPACE)
254
                    --x;
255
                args << params.mid(last, x - last);
256
                // Could do a check for unmatched parens here, but split_value_list()
257
                // is called on all our output, so mistakes will be caught anyway.
258
                return args;
259
            }
260
            ushort unicode = params_data[x].unicode();
261
            if(x != (int)params_len-1 && unicode == BACKSLASH &&
262
                (params_data[x+1].unicode() == SINGLEQUOTE || params_data[x+1].unicode() == DOUBLEQUOTE)) {
263
                x++; //get that 'escape'
264
            } else if(quote && unicode == quote) {
265
                quote = 0;
266
            } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
267
                quote = unicode;
268
            } else if(unicode == RPAREN) {
269
                --parens;
270
            } else if(unicode == LPAREN) {
271
                ++parens;
272
            }
273
            if(!parens && !quote && unicode == COMMA) {
274
                int prev = last;
275
                last = x+1;
276
                while(x > prev && params_data[x-1].unicode() == SPACE)
277
                    --x;
278
                args << params.mid(prev, x - prev);
279
                break;
280
            }
281
        }
282
    }
283
}
284
285
static QStringList split_value_list(const QString &vals)
286
{
287
    QString build;
288
    QStringList ret;
289
    ushort quote = 0;
290
    int parens = 0;
291
292
    const ushort LPAREN = '(';
293
    const ushort RPAREN = ')';
294
    const ushort SINGLEQUOTE = '\'';
295
    const ushort DOUBLEQUOTE = '"';
296
    const ushort BACKSLASH = '\\';
297
298
    ushort unicode;
299
    const QChar *vals_data = vals.data();
300
    const int vals_len = vals.length();
301
    for(int x = 0; x < vals_len; x++) {
302
        unicode = vals_data[x].unicode();
303
        if(x != (int)vals_len-1 && unicode == BACKSLASH &&
304
            (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
305
            build += vals_data[x++]; //get that 'escape'
306
        } else if(quote && unicode == quote) {
307
            quote = 0;
308
        } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
309
            quote = unicode;
310
        } else if(unicode == RPAREN) {
311
            --parens;
312
        } else if(unicode == LPAREN) {
313
            ++parens;
314
        }
315
316
        if(!parens && !quote && (vals_data[x] == Option::field_sep)) {
317
            ret << build;
318
            build.clear();
319
        } else {
320
            build += vals_data[x];
321
        }
322
    }
323
    if(!build.isEmpty())
324
        ret << build;
325
    if (parens)
326
        warn_msg(WarnDeprecated, "%s:%d: Unmatched parentheses are deprecated.",
327
                 parser.file.toLatin1().constData(), parser.line_no);
328
    // Could do a check for unmatched quotes here, but doVariableReplaceExpand()
329
    // is called on all our output, so mistakes will be caught anyway.
330
    return ret;
331
}
332
333
//just a parsable entity
334
struct ParsableBlock
335
{
336
    ParsableBlock() : ref_cnt(1) { }
337
    virtual ~ParsableBlock() { }
338
339
    struct Parse {
340
        QString text;
341
        parser_info pi;
342
        Parse(const QString &t) : text(t){ pi = parser; }
343
    };
344
    QList<Parse> parselist;
345
346
    inline int ref() { return ++ref_cnt; }
347
    inline int deref() { return --ref_cnt; }
348
349
protected:
350
    int ref_cnt;
351
    virtual bool continueBlock() = 0;
352
    bool eval(QMakeProject *p, QMap<QString, QStringList> &place);
353
};
354
355
bool ParsableBlock::eval(QMakeProject *p, QMap<QString, QStringList> &place)
356
{
357
    //save state
358
    parser_info pi = parser;
359
    const int block_count = p->scope_blocks.count();
360
361
    //execute
362
    bool ret = true;
363
    for(int i = 0; i < parselist.count(); i++) {
364
        parser = parselist.at(i).pi;
365
        if(!(ret = p->parse(parselist.at(i).text, place)) || !continueBlock())
366
            break;
367
    }
368
369
    //restore state
370
    parser = pi;
371
    while(p->scope_blocks.count() > block_count)
372
        p->scope_blocks.pop();
373
    return ret;
374
}
375
376
//defined functions
377
struct FunctionBlock : public ParsableBlock
378
{
379
    FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { }
380
381
    QMap<QString, QStringList> vars;
382
    QMap<QString, QStringList> *calling_place;
383
    QStringList return_value;
384
    int scope_level;
385
    bool cause_return;
386
387
    bool exec(const QList<QStringList> &args,
388
              QMakeProject *p, QMap<QString, QStringList> &place, QStringList &functionReturn);
389
    virtual bool continueBlock() { return !cause_return; }
390
};
391
392
bool FunctionBlock::exec(const QList<QStringList> &args,
393
                         QMakeProject *proj, QMap<QString, QStringList> &place,
394
                         QStringList &functionReturn)
395
{
396
    //save state
397
#if 1
398
    calling_place = &place;
399
#else
400
    calling_place = &proj->variables();
401
#endif
402
    return_value.clear();
403
    cause_return = false;
404
405
    //execute
406
#if 0
407
    vars = proj->variables(); // should be place so that local variables can be inherited
408
#else
409
    vars = place;
410
#endif
411
    vars["ARGS"].clear();
412
    for(int i = 0; i < args.count(); i++) {
413
        vars["ARGS"] += args[i];
414
        vars[QString::number(i+1)] = args[i];
415
    }
416
    bool ret = ParsableBlock::eval(proj, vars);
417
    functionReturn = return_value;
418
419
    //restore state
420
    calling_place = 0;
421
    return_value.clear();
422
    vars.clear();
423
    return ret;
424
}
425
426
//loops
427
struct IteratorBlock : public ParsableBlock
428
{
429
    IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { }
430
431
    int scope_level;
432
433
    struct Test {
434
        QString func;
435
        QStringList args;
436
        bool invert;
437
        parser_info pi;
438
        Test(const QString &f, QStringList &a, bool i) : func(f), args(a), invert(i) { pi = parser; }
439
    };
440
    QList<Test> test;
441
442
    QString variable;
443
444
    bool loop_forever, cause_break, cause_next;
445
    QStringList list;
446
447
    bool exec(QMakeProject *p, QMap<QString, QStringList> &place);
448
    virtual bool continueBlock() { return !cause_next && !cause_break; }
449
};
450
bool IteratorBlock::exec(QMakeProject *p, QMap<QString, QStringList> &place)
451
{
452
    bool ret = true;
453
    QStringList::Iterator it;
454
    if(!loop_forever)
455
        it = list.begin();
456
    int iterate_count = 0;
457
    //save state
458
    IteratorBlock *saved_iterator = p->iterator;
459
    p->iterator = this;
460
461
    //do the loop
462
    while(loop_forever || it != list.end()) {
463
        cause_next = cause_break = false;
464
        if(!loop_forever && (*it).isEmpty()) { //ignore empty items
465
            ++it;
466
            continue;
467
        }
468
469
        //set up the loop variable
470
        QStringList va;
471
        if(!variable.isEmpty()) {
472
            va = place[variable];
473
            if(loop_forever)
474
                place[variable] = QStringList(QString::number(iterate_count));
475
            else
476
                place[variable] = QStringList(*it);
477
        }
478
        //do the iterations
479
        bool succeed = true;
480
        for(QList<Test>::Iterator test_it = test.begin(); test_it != test.end(); ++test_it) {
481
            parser = (*test_it).pi;
482
            succeed = p->doProjectTest((*test_it).func, (*test_it).args, place);
483
            if((*test_it).invert)
484
                succeed = !succeed;
485
            if(!succeed)
486
                break;
487
        }
488
        if(succeed)
489
            ret = ParsableBlock::eval(p, place);
490
        //restore the variable in the map
491
        if(!variable.isEmpty())
492
            place[variable] = va;
493
        //loop counters
494
        if(!loop_forever)
495
            ++it;
496
        iterate_count++;
497
        if(!ret || cause_break)
498
            break;
499
    }
500
501
    //restore state
502
    p->iterator = saved_iterator;
503
    return ret;
504
}
505
506
QMakeProject::ScopeBlock::~ScopeBlock()
507
{
508
#if 0
509
    if(iterate)
510
        delete iterate;
511
#endif
512
}
513
514
static void qmake_error_msg(const QString &msg)
515
{
516
    fprintf(stderr, "%s:%d: %s\n", parser.file.toLatin1().constData(), parser.line_no,
517
            msg.toLatin1().constData());
518
}
519
520
/*
521
   1) environment variable QMAKEFEATURES (as separated by colons)
522
   2) property variable QMAKEFEATURES (as separated by colons)
523
   3) <project_root> (where .qmake.cache lives) + FEATURES_DIR
524
   4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR
525
   5) your QMAKESPEC/features dir
526
   6) your data_install/mkspecs/FEATURES_DIR
527
   7) your QMAKESPEC/../FEATURES_DIR dir
528
529
   FEATURES_DIR is defined as:
530
531
   1) features/(unix|win32|macx)/
532
   2) features/
533
*/
534
QStringList qmake_feature_paths(QMakeProperty *prop=0)
535
{
536
    QStringList concat;
537
    {
538
        const QString base_concat = QDir::separator() + QString("features");
539
        switch(Option::target_mode) {
540
        case Option::TARG_MACX_MODE:                     //also a unix
541
            concat << base_concat + QDir::separator() + "mac";
542
            concat << base_concat + QDir::separator() + "macx";
543
            concat << base_concat + QDir::separator() + "unix";
544
            break;
545
        default: // Can't happen, just make the compiler shut up
546
        case Option::TARG_UNIX_MODE:
547
            concat << base_concat + QDir::separator() + "unix";
548
            break;
549
        case Option::TARG_WIN_MODE:
550
            concat << base_concat + QDir::separator() + "win32";
551
            break;
552
        case Option::TARG_SYMBIAN_MODE:
553
            concat << base_concat + QDir::separator() + "symbian";
554
            break;
555
        }
556
        concat << base_concat;
557
    }
558
    const QString mkspecs_concat = QDir::separator() + QString("mkspecs");
559
    QStringList feature_roots;
560
    QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
561
    if(!mkspec_path.isNull())
562
        feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
563
    if(prop)
564
        feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
565
    if(!Option::mkfile::cachefile.isEmpty()) {
566
        QString path;
567
        int last_slash = Option::mkfile::cachefile.lastIndexOf(QDir::separator());
568
        if(last_slash != -1)
569
            path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash), false);
570
        for(QStringList::Iterator concat_it = concat.begin();
571
            concat_it != concat.end(); ++concat_it)
572
            feature_roots << (path + (*concat_it));
573
    }
574
    QByteArray qmakepath = qgetenv("QMAKEPATH");
575
    if (!qmakepath.isNull()) {
576
        const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
577
        for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) {
578
            for(QStringList::Iterator concat_it = concat.begin();
579
                concat_it != concat.end(); ++concat_it)
580
                    feature_roots << ((*it) + mkspecs_concat + (*concat_it));
581
        }
582
    }
583
    if(!Option::mkfile::qmakespec.isEmpty())
584
        feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
585
    if(!Option::mkfile::qmakespec.isEmpty()) {
586
        QFileInfo specfi(Option::mkfile::qmakespec);
587
        QDir specdir(specfi.absoluteFilePath());
588
        while(!specdir.isRoot()) {
589
            if(!specdir.cdUp() || specdir.isRoot())
590
                break;
591
            if(QFile::exists(specdir.path() + QDir::separator() + "features")) {
592
                for(QStringList::Iterator concat_it = concat.begin();
593
                    concat_it != concat.end(); ++concat_it)
594
                    feature_roots << (specdir.path() + (*concat_it));
595
                break;
596
            }
597
        }
598
    }
599
    for(QStringList::Iterator concat_it = concat.begin();
600
        concat_it != concat.end(); ++concat_it)
601
        feature_roots << (QLibraryInfo::location(QLibraryInfo::PrefixPath) +
602
                          mkspecs_concat + (*concat_it));
603
    for(QStringList::Iterator concat_it = concat.begin();
604
        concat_it != concat.end(); ++concat_it)
605
        feature_roots << (QLibraryInfo::location(QLibraryInfo::DataPath) +
606
                          mkspecs_concat + (*concat_it));
607
    return feature_roots;
608
}
609
610
QStringList qmake_mkspec_paths()
611
{
612
    QStringList ret;
613
    const QString concat = QDir::separator() + QString("mkspecs");
614
    QByteArray qmakepath = qgetenv("QMAKEPATH");
615
    if (!qmakepath.isEmpty()) {
616
        const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
617
        for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it)
618
            ret << ((*it) + concat);
619
    }
620
    ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat;
621
622
    return ret;
623
}
624
625
QMakeProject::~QMakeProject()
626
{
627
    if(own_prop)
628
        delete prop;
629
    for(QMap<QString, FunctionBlock*>::iterator it = replaceFunctions.begin(); it != replaceFunctions.end(); ++it) {
630
        if(!it.value()->deref())
631
            delete it.value();
632
    }
633
    replaceFunctions.clear();
634
    for(QMap<QString, FunctionBlock*>::iterator it = testFunctions.begin(); it != testFunctions.end(); ++it) {
635
        if(!it.value()->deref())
636
            delete it.value();
637
    }
638
    testFunctions.clear();
639
}
640
641
642
void
643
QMakeProject::init(QMakeProperty *p, const QMap<QString, QStringList> *vars)
644
{
645
    if(vars)
646
        base_vars = *vars;
647
    if(!p) {
648
        prop = new QMakeProperty;
649
        own_prop = true;
650
    } else {
651
        prop = p;
652
        own_prop = false;
653
    }
654
    recursive = false;
655
    reset();
656
}
657
658
QMakeProject::QMakeProject(QMakeProject *p, const QMap<QString, QStringList> *vars)
659
{
660
    init(p->properties(), vars ? vars : &p->variables());
661
    for(QMap<QString, FunctionBlock*>::iterator it = p->replaceFunctions.begin(); it != p->replaceFunctions.end(); ++it) {
662
        it.value()->ref();
663
        replaceFunctions.insert(it.key(), it.value());
664
    }
665
    for(QMap<QString, FunctionBlock*>::iterator it = p->testFunctions.begin(); it != p->testFunctions.end(); ++it) {
666
        it.value()->ref();
667
        testFunctions.insert(it.key(), it.value());
668
    }
669
}
670
671
void
672
QMakeProject::reset()
673
{
674
    // scope_blocks starts with one non-ignoring entity
675
    scope_blocks.clear();
676
    scope_blocks.push(ScopeBlock());
677
    iterator = 0;
678
    function = 0;
679
    backslashWarned = false;
680
}
681
682
bool
683
QMakeProject::parse(const QString &t, QMap<QString, QStringList> &place, int numLines)
684
{
685
    // To preserve the integrity of any UTF-8 characters in .pro file, temporarily replace the
686
    // non-breaking space (0xA0) characters with another non-space character, so that
687
    // QString::simplified() call will not replace it with space.
688
    // Note: There won't be any two byte characters in .pro files, so 0x10A0 should be a safe
689
    // replacement character.
690
    static QChar nbsp(0xA0);
691
    static QChar nbspFix(0x01A0);
692
    QString s;
693
    if (t.indexOf(nbsp) != -1) {
694
        s = t;
695
        s.replace(nbsp, nbspFix);
696
        s = s.simplified();
697
        s.replace(nbspFix, nbsp);
698
    } else {
699
        s = t.simplified();
700
    }
701
702
    int hash_mark = s.indexOf("#");
703
    if(hash_mark != -1) //good bye comments
704
        s = s.left(hash_mark);
705
    if(s.isEmpty()) // blank_line
706
        return true;
707
708
    if(scope_blocks.top().ignore) {
709
        bool continue_parsing = false;
710
        // adjust scope for each block which appears on a single line
711
        for(int i = 0; i < s.length(); i++) {
712
            if(s[i] == '{') {
713
                scope_blocks.push(ScopeBlock(true));
714
            } else if(s[i] == '}') {
715
                if(scope_blocks.count() == 1) {
716
                    fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
717
                    return false;
718
                }
719
                ScopeBlock sb = scope_blocks.pop();
720
                if(sb.iterate) {
721
                    sb.iterate->exec(this, place);
722
                    delete sb.iterate;
723
                    sb.iterate = 0;
724
                }
725
                if(!scope_blocks.top().ignore) {
726
                    debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
727
                              parser.line_no, scope_blocks.count()+1);
728
                    s = s.mid(i+1).trimmed();
729
                    continue_parsing = !s.isEmpty();
730
                    break;
731
                }
732
            }
733
        }
734
        if(!continue_parsing) {
735
            debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
736
                      parser.file.toLatin1().constData(), parser.line_no);
737
            return true;
738
        }
739
    }
740
741
    if(function) {
742
        QString append;
743
        int d_off = 0;
744
        const QChar *d = s.unicode();
745
        bool function_finished = false;
746
        while(d_off < s.length()) {
747
            if(*(d+d_off) == QLatin1Char('}')) {
748
                function->scope_level--;
749
                if(!function->scope_level) {
750
                    function_finished = true;
751
                    break;
752
                }
753
            } else if(*(d+d_off) == QLatin1Char('{')) {
754
                function->scope_level++;
755
            }
756
            append += *(d+d_off);
757
            ++d_off;
758
        }
759
        if(!append.isEmpty())
760
            function->parselist.append(IteratorBlock::Parse(append));
761
        if(function_finished) {
762
            function = 0;
763
            s = QString(d+d_off, s.length()-d_off);
764
        } else {
765
            return true;
766
        }
767
    } else if(IteratorBlock *it = scope_blocks.top().iterate) {
768
        QString append;
769
        int d_off = 0;
770
        const QChar *d = s.unicode();
771
        bool iterate_finished = false;
772
        while(d_off < s.length()) {
773
            if(*(d+d_off) == QLatin1Char('}')) {
774
                it->scope_level--;
775
                if(!it->scope_level) {
776
                    iterate_finished = true;
777
                    break;
778
                }
779
            } else if(*(d+d_off) == QLatin1Char('{')) {
780
                it->scope_level++;
781
            }
782
            append += *(d+d_off);
783
            ++d_off;
784
        }
785
        if(!append.isEmpty())
786
            scope_blocks.top().iterate->parselist.append(IteratorBlock::Parse(append));
787
        if(iterate_finished) {
788
            scope_blocks.top().iterate = 0;
789
            bool ret = it->exec(this, place);
790
            delete it;
791
            if(!ret)
792
                return false;
793
            s = s.mid(d_off);
794
        } else {
795
            return true;
796
        }
797
    }
798
799
    QString scope, var, op;
800
    QStringList val;
801
#define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o
802
    const QChar *d = s.unicode();
803
    int d_off = 0;
804
    SKIP_WS(d, d_off, s.length());
805
    IteratorBlock *iterator = 0;
806
    bool scope_failed = false, else_line = false, or_op=false;
807
    QChar quote = 0;
808
    int parens = 0, scope_count=0, start_block = 0;
809
    while(d_off < s.length()) {
810
        if(!parens) {
811
            if(*(d+d_off) == QLatin1Char('='))
812
                break;
813
            if(*(d+d_off) == QLatin1Char('+') || *(d+d_off) == QLatin1Char('-') ||
814
               *(d+d_off) == QLatin1Char('*') || *(d+d_off) == QLatin1Char('~')) {
815
                if(*(d+d_off+1) == QLatin1Char('=')) {
816
                    break;
817
                } else if(*(d+d_off+1) == QLatin1Char(' ')) {
818
                    const QChar *k = d+d_off+1;
819
                    int k_off = 0;
820
                    SKIP_WS(k, k_off, s.length()-d_off);
821
                    if(*(k+k_off) == QLatin1Char('=')) {
822
                        QString msg;
823
                        qmake_error_msg(QString(d+d_off, 1) + "must be followed immediately by =");
824
                        return false;
825
                    }
826
                }
827
            }
828
        }
829
830
        if(!quote.isNull()) {
831
            if(*(d+d_off) == quote)
832
                quote = QChar();
833
        } else if(*(d+d_off) == '(') {
834
            ++parens;
835
        } else if(*(d+d_off) == ')') {
836
            --parens;
837
        } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) {
838
            quote = *(d+d_off);
839
        }
840
841
        if(!parens && quote.isNull() &&
842
           (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('{') ||
843
            *(d+d_off) == QLatin1Char(')') || *(d+d_off) == QLatin1Char('|'))) {
844
            scope_count++;
845
            scope = var.trimmed();
846
            if(*(d+d_off) == QLatin1Char(')'))
847
                scope += *(d+d_off); // need this
848
            var = "";
849
850
            bool test = scope_failed;
851
            if(scope.isEmpty()) {
852
                test = true;
853
            } else if(scope.toLower() == "else") { //else is a builtin scope here as it modifies state
854
                if(scope_count != 1 || scope_blocks.top().else_status == ScopeBlock::TestNone) {
855
                    qmake_error_msg(("Unexpected " + scope + " ('" + s + "')").toLatin1());
856
                    return false;
857
                }
858
                else_line = true;
859
                test = (scope_blocks.top().else_status == ScopeBlock::TestSeek);
860
                debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser.file.toLatin1().constData(), parser.line_no,
861
                          scope == "else" ? "" : QString(" (" + scope + ")").toLatin1().constData(),
862
                          test ? "considered" : "excluded");
863
            } else {
864
                QString comp_scope = scope;
865
                bool invert_test = (comp_scope.at(0) == QLatin1Char('!'));
866
                if(invert_test)
867
                    comp_scope = comp_scope.mid(1);
868
                int lparen = comp_scope.indexOf('(');
869
                if(or_op == scope_failed) {
870
                    if(lparen != -1) { // if there is an lparen in the scope, it IS a function
871
                        int rparen = comp_scope.lastIndexOf(')');
872
                        if(rparen == -1) {
873
                            qmake_error_msg("Function missing right paren: " + comp_scope);
874
                            return false;
875
                        }
876
                        QString func = comp_scope.left(lparen);
877
                        QStringList args = split_arg_list(comp_scope.mid(lparen+1, rparen - lparen - 1));
878
                        if(function) {
879
                            fprintf(stderr, "%s:%d: No tests can come after a function definition!\n",
880
                                    parser.file.toLatin1().constData(), parser.line_no);
881
                            return false;
882
                        } else if(func == "for") { //for is a builtin function here, as it modifies state
883
                            if(args.count() > 2 || args.count() < 1) {
884
                                fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n",
885
                                        parser.file.toLatin1().constData(), parser.line_no);
886
                                return false;
887
                            } else if(iterator) {
888
                                fprintf(stderr, "%s:%d unexpected nested for()\n",
889
                                        parser.file.toLatin1().constData(), parser.line_no);
890
                                return false;
891
                            }
892
893
                            iterator = new IteratorBlock;
894
                            QString it_list;
895
                            if(args.count() == 1) {
896
                                doVariableReplace(args[0], place);
897
                                it_list = args[0];
898
                                if(args[0] != "ever") {
899
                                    delete iterator;
900
                                    iterator = 0;
901
                                    fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n",
902
                                            parser.file.toLatin1().constData(), parser.line_no);
903
                                    return false;
904
                                }
905
                                it_list = "forever";
906
                            } else if(args.count() == 2) {
907
                                iterator->variable = args[0];
908
                                doVariableReplace(args[1], place);
909
                                it_list = args[1];
910
                            }
911
                            QStringList list = place[it_list];
912
                            if(list.isEmpty()) {
913
                                if(it_list == "forever") {
914
                                    iterator->loop_forever = true;
915
                                } else {
916
                                    int dotdot = it_list.indexOf("..");
917
                                    if(dotdot != -1) {
918
                                        bool ok;
919
                                        int start = it_list.left(dotdot).toInt(&ok);
920
                                        if(ok) {
921
                                            int end = it_list.mid(dotdot+2).toInt(&ok);
922
                                            if(ok) {
923
                                                if(start < end) {
924
                                                    for(int i = start; i <= end; i++)
925
                                                        list << QString::number(i);
926
                                                } else {
927
                                                    for(int i = start; i >= end; i--)
928
                                                        list << QString::number(i);
929
                                                }
930
                                            }
931
                                        }
932
                                    }
933
                                }
934
                            }
935
                            iterator->list = list;
936
                            test = !invert_test;
937
                        } else if(iterator) {
938
                            iterator->test.append(IteratorBlock::Test(func, args, invert_test));
939
                            test = !invert_test;
940
                        } else if(func == "defineTest" || func == "defineReplace") {
941
                            if(!function_blocks.isEmpty()) {
942
                                fprintf(stderr,
943
                                        "%s:%d: cannot define a function within another definition.\n",
944
                                        parser.file.toLatin1().constData(), parser.line_no);
945
                                return false;
946
                            }
947
                            if(args.count() != 1) {
948
                                fprintf(stderr, "%s:%d: %s(function_name) requires one argument.\n",
949
                                        parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
950
                                return false;
951
                            }
952
                            QMap<QString, FunctionBlock*> *map = 0;
953
                            if(func == "defineTest")
954
                                map = &testFunctions;
955
                            else
956
                                map = &replaceFunctions;
957
#if 0
958
                            if(!map || map->contains(args[0])) {
959
                                fprintf(stderr, "%s:%d: Function[%s] multiply defined.\n",
960
                                        parser.file.toLatin1().constData(), parser.line_no, args[0].toLatin1().constData());
961
                                return false;
962
                            }
963
#endif
964
                            function = new FunctionBlock;
965
                            map->insert(args[0], function);
966
                            test = true;
967
                        } else {
968
                            test = doProjectTest(func, args, place);
969
                            if(*(d+d_off) == QLatin1Char(')') && d_off == s.length()-1) {
970
                                if(invert_test)
971
                                    test = !test;
972
                                scope_blocks.top().else_status =
973
                                    (test ? ScopeBlock::TestFound : ScopeBlock::TestSeek);
974
                                return true;  // assume we are done
975
                            }
976
                        }
977
                    } else {
978
                        QString cscope = comp_scope.trimmed();
979
                        doVariableReplace(cscope, place);
980
                        test = isActiveConfig(cscope.trimmed(), true, &place);
981
                    }
982
                    if(invert_test)
983
                        test = !test;
984
                }
985
            }
986
            if(!test && !scope_failed)
987
                debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser.file.toLatin1().constData(),
988
                          parser.line_no, scope.toLatin1().constData());
989
            if(test == or_op)
990
                scope_failed = !test;
991
            or_op = (*(d+d_off) == QLatin1Char('|'));
992
993
            if(*(d+d_off) == QLatin1Char('{')) { // scoping block
994
                start_block++;
995
                if(iterator) {
996
                    for(int off = 0, braces = 0; true; ++off) {
997
                        if(*(d+d_off+off) == QLatin1Char('{'))
998
                            ++braces;
999
                        else if(*(d+d_off+off) == QLatin1Char('}') && braces)
1000
                            --braces;
1001
                        if(!braces || d_off+off == s.length()) {
1002
                            iterator->parselist.append(s.mid(d_off, off-1));
1003
                            if(braces > 1)
1004
                                iterator->scope_level += braces-1;
1005
                            d_off += off-1;
1006
                            break;
1007
                        }
1008
                    }
1009
                }
1010
            }
1011
        } else if(!parens && *(d+d_off) == QLatin1Char('}')) {
1012
            if(start_block) {
1013
                --start_block;
1014
            } else if(!scope_blocks.count()) {
1015
                warn_msg(WarnParser, "Possible braces mismatch %s:%d", parser.file.toLatin1().constData(), parser.line_no);
1016
            } else {
1017
                if(scope_blocks.count() == 1) {
1018
                    fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
1019
                    return false;
1020
                }
1021
                debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
1022
                          parser.line_no, scope_blocks.count());
1023
                ScopeBlock sb = scope_blocks.pop();
1024
                if(sb.iterate)
1025
                    sb.iterate->exec(this, place);
1026
            }
1027
        } else {
1028
            var += *(d+d_off);
1029
        }
1030
        ++d_off;
1031
    }
1032
    var = var.trimmed();
1033
1034
    if(!else_line || (else_line && !scope_failed))
1035
        scope_blocks.top().else_status = (!scope_failed ? ScopeBlock::TestFound : ScopeBlock::TestSeek);
1036
    if(start_block) {
1037
        ScopeBlock next_block(scope_failed);
1038
        next_block.iterate = iterator;
1039
        if(iterator)
1040
            next_block.else_status = ScopeBlock::TestNone;
1041
        else if(scope_failed)
1042
            next_block.else_status = ScopeBlock::TestSeek;
1043
        else
1044
            next_block.else_status = ScopeBlock::TestFound;
1045
        scope_blocks.push(next_block);
1046
        debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser.file.toLatin1().constData(),
1047
                  parser.line_no, scope_blocks.count(), scope_failed, s.toLatin1().constData());
1048
    } else if(iterator) {
1049
        iterator->parselist.append(var+s.mid(d_off));
1050
        bool ret = iterator->exec(this, place);
1051
        delete iterator;
1052
        return ret;
1053
    }
1054
1055
    if((!scope_count && !var.isEmpty()) || (scope_count == 1 && else_line))
1056
        scope_blocks.top().else_status = ScopeBlock::TestNone;
1057
    if(d_off == s.length()) {
1058
        if(!var.trimmed().isEmpty())
1059
            qmake_error_msg(("Parse Error ('" + s + "')").toLatin1());
1060
        return var.isEmpty(); // allow just a scope
1061
    }
1062
1063
    SKIP_WS(d, d_off, s.length());
1064
    for(; d_off < s.length() && op.indexOf('=') == -1; op += *(d+(d_off++)))
1065
        ;
1066
    op.replace(QRegExp("\\s"), "");
1067
1068
    SKIP_WS(d, d_off, s.length());
1069
    QString vals = s.mid(d_off); // vals now contains the space separated list of values
1070
    int rbraces = vals.count('}'), lbraces = vals.count('{');
1071
    if(scope_blocks.count() > 1 && rbraces - lbraces == 1 && vals.endsWith('}')) {
1072
        debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
1073
                  parser.line_no, scope_blocks.count());
1074
        ScopeBlock sb = scope_blocks.pop();
1075
        if(sb.iterate)
1076
            sb.iterate->exec(this, place);
1077
        vals.truncate(vals.length()-1);
1078
    } else if(rbraces != lbraces) {
1079
        warn_msg(WarnParser, "Possible braces mismatch {%s} %s:%d",
1080
                 vals.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
1081
    }
1082
    if(scope_failed)
1083
        return true; // oh well
1084
#undef SKIP_WS
1085
1086
    doVariableReplace(var, place);
1087
    var = varMap(var); //backwards compatibility
1088
    if(!var.isEmpty() && Option::mkfile::do_preprocess) {
1089
        static QString last_file("*none*");
1090
        if(parser.file != last_file) {
1091
            fprintf(stdout, "#file %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
1092
            last_file = parser.file;
1093
        }
1094
        fprintf(stdout, "%s %s %s\n", var.toLatin1().constData(), op.toLatin1().constData(), vals.toLatin1().constData());
1095
    }
1096
1097
    if(vals.contains('=') && numLines > 1)
1098
        warn_msg(WarnParser, "Possible accidental line continuation: {%s} at %s:%d",
1099
                 var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
1100
1101
    QStringList &varlist = place[var]; // varlist is the list in the symbol table
1102
1103
    if(Option::debug_level >= 1) {
1104
        QString tmp_vals = vals;
1105
        doVariableReplace(tmp_vals, place);
1106
        debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser.file.toLatin1().constData(), parser.line_no,
1107
                  var.toLatin1().constData(), op.toLatin1().constData(), tmp_vals.toLatin1().constData());
1108
    }
1109
1110
    // now do the operation
1111
    if(op == "~=") {
1112
        doVariableReplace(vals, place);
1113
        if(vals.length() < 4 || vals.at(0) != 's') {
1114
            qmake_error_msg(("~= operator only can handle s/// function ('" +
1115
                            s + "')").toLatin1());
1116
            return false;
1117
        }
1118
        QChar sep = vals.at(1);
1119
        QStringList func = vals.split(sep);
1120
        if(func.count() < 3 || func.count() > 4) {
1121
            qmake_error_msg(("~= operator only can handle s/// function ('" +
1122
                s + "')").toLatin1());
1123
            return false;
1124
        }
1125
        bool global = false, case_sense = true, quote = false;
1126
        if(func.count() == 4) {
1127
            global = func[3].indexOf('g') != -1;
1128
            case_sense = func[3].indexOf('i') == -1;
1129
            quote = func[3].indexOf('q') != -1;
1130
        }
1131
        QString from = func[1], to = func[2];
1132
        if(quote)
1133
            from = QRegExp::escape(from);
1134
        QRegExp regexp(from, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
1135
        for(QStringList::Iterator varit = varlist.begin(); varit != varlist.end();) {
1136
            if((*varit).contains(regexp)) {
1137
                (*varit) = (*varit).replace(regexp, to);
1138
                if ((*varit).isEmpty())
1139
                    varit = varlist.erase(varit);
1140
                else
1141
                    ++varit;
1142
                if(!global)
1143
                    break;
1144
            } else
1145
                ++varit;
1146
        }
1147
    } else {
1148
        QStringList vallist;
1149
        {
1150
            //doVariableReplace(vals, place);
1151
            QStringList tmp = split_value_list(vals);
1152
            for(int i = 0; i < tmp.size(); ++i)
1153
                vallist += doVariableReplaceExpand(tmp[i], place);
1154
        }
1155
1156
        if(op == "=") {
1157
            if(!varlist.isEmpty()) {
1158
                bool send_warning = false;
1159
                if(var != "TEMPLATE" && var != "TARGET") {
1160
                    QSet<QString> incoming_vals = vallist.toSet();
1161
                    for(int i = 0; i < varlist.size(); ++i) {
1162
                        const QString var = varlist.at(i).trimmed();
1163
                        if(!var.isEmpty() && !incoming_vals.contains(var)) {
1164
                            send_warning = true;
1165
                            break;
1166
                        }
1167
                    }
1168
                }
1169
                if(send_warning)
1170
                    warn_msg(WarnParser, "Operator=(%s) clears variables previously set: %s:%d",
1171
                             var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
1172
            }
1173
            varlist.clear();
1174
        }
1175
        for(QStringList::ConstIterator valit = vallist.begin();
1176
            valit != vallist.end(); ++valit) {
1177
            if((*valit).isEmpty())
1178
                continue;
1179
            if((op == "*=" && !varlist.contains((*valit))) ||
1180
               op == "=" || op == "+=")
1181
                varlist.append((*valit));
1182
            else if(op == "-=")
1183
                varlist.removeAll((*valit));
1184
        }
1185
        if(var == "REQUIRES") // special case to get communicated to backends!
1186
            doProjectCheckReqs(vallist, place);
1187
    }
1188
    return true;
1189
}
1190
1191
bool
1192
QMakeProject::read(QTextStream &file, QMap<QString, QStringList> &place)
1193
{
1194
    int numLines = 0;
1195
    bool ret = true;
1196
    QString s;
1197
    while(!file.atEnd()) {
1198
        parser.line_no++;
1199
        QString line = file.readLine().trimmed();
1200
        int prelen = line.length();
1201
1202
        int hash_mark = line.indexOf("#");
1203
        if(hash_mark != -1) //good bye comments
1204
            line = line.left(hash_mark).trimmed();
1205
        if(!line.isEmpty() && line.right(1) == "\\") {
1206
            if(!line.startsWith("#")) {
1207
                line.truncate(line.length() - 1);
1208
                s += line + Option::field_sep;
1209
                ++numLines;
1210
            }
1211
        } else if(!line.isEmpty() || (line.isEmpty() && !prelen)) {
1212
            if(s.isEmpty() && line.isEmpty())
1213
                continue;
1214
            if(!line.isEmpty()) {
1215
                s += line;
1216
                ++numLines;
1217
            }
1218
            if(!s.isEmpty()) {
1219
                if(!(ret = parse(s, place, numLines))) {
1220
                    s = "";
1221
                    numLines = 0;
1222
                    break;
1223
                }
1224
                s = "";
1225
                numLines = 0;
1226
            }
1227
        }
1228
    }
1229
    if (!s.isEmpty())
1230
        ret = parse(s, place, numLines);
1231
    return ret;
1232
}
1233
1234
bool
1235
QMakeProject::read(const QString &file, QMap<QString, QStringList> &place)
1236
{
1237
    parser_info pi = parser;
1238
    reset();
1239
1240
    const QString oldpwd = qmake_getpwd();
1241
    QString filename = Option::fixPathToLocalOS(file, false);
1242
    bool ret = false, using_stdin = false;
1243
    QFile qfile;
1244
    if(!strcmp(filename.toLatin1(), "-")) {
1245
        qfile.setFileName("");
1246
        ret = qfile.open(stdin, QIODevice::ReadOnly);
1247
        using_stdin = true;
1248
    } else if(QFileInfo(file).isDir()) {
1249
        return false;
1250
    } else {
1251
        qfile.setFileName(filename);
1252
        ret = qfile.open(QIODevice::ReadOnly);
1253
        qmake_setpwd(QFileInfo(filename).absolutePath());
1254
    }
1255
    if(ret) {
1256
        parser_info pi = parser;
1257
        parser.from_file = true;
1258
        parser.file = filename;
1259
        parser.line_no = 0;
1260
        QTextStream t(&qfile);
1261
        ret = read(t, place);
1262
        if(!using_stdin)
1263
            qfile.close();
1264
    }
1265
    if(scope_blocks.count() != 1) {
1266
        qmake_error_msg("Unterminated conditional block at end of file");
1267
        ret = false;
1268
    }
1269
    parser = pi;
1270
    qmake_setpwd(oldpwd);
1271
    return ret;
1272
}
1273
1274
bool
1275
QMakeProject::read(const QString &project, uchar cmd)
1276
{
1277
    pfile = QFileInfo(project).absoluteFilePath();
1278
    return read(cmd);
1279
}
1280
1281
bool
1282
QMakeProject::read(uchar cmd)
1283
{
1284
    if(cfile.isEmpty()) {
1285
        // hack to get the Option stuff in there
1286
        base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext;
1287
        base_vars["QMAKE_EXT_C"] = Option::c_ext;
1288
        base_vars["QMAKE_EXT_H"] = Option::h_ext;
1289
        base_vars["QMAKE_SH"] = Option::shellPath;
1290
        if(!Option::user_template_prefix.isEmpty())
1291
            base_vars["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix);
1292
1293
        if(cmd & ReadCache && Option::mkfile::do_cache) {        // parse the cache
1294
            int cache_depth = -1;
1295
            QString qmake_cache = Option::mkfile::cachefile;
1296
            if(qmake_cache.isEmpty())  { //find it as it has not been specified
1297
                QString dir = QDir::toNativeSeparators(Option::output_dir);
1298
                while(!QFile::exists((qmake_cache = dir + QDir::separator() + ".qmake.cache"))) {
1299
                    dir = dir.left(dir.lastIndexOf(QDir::separator()));
1300
                    if(dir.isEmpty() || dir.indexOf(QDir::separator()) == -1) {
1301
                        qmake_cache = "";
1302
                        break;
1303
                    }
1304
                    if(cache_depth == -1)
1305
                        cache_depth = 1;
1306
                    else
1307
                        cache_depth++;
1308
                }
1309
            } else {
1310
                QString abs_cache = QFileInfo(Option::mkfile::cachefile).absoluteDir().path();
1311
                if(Option::output_dir.startsWith(abs_cache))
1312
                    cache_depth = Option::output_dir.mid(abs_cache.length()).count('/');
1313
            }
1314
            if(!qmake_cache.isEmpty()) {
1315
                if(read(qmake_cache, cache)) {
1316
                    Option::mkfile::cachefile_depth = cache_depth;
1317
                    Option::mkfile::cachefile = qmake_cache;
1318
                    if(Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty())
1319
                        Option::mkfile::qmakespec = cache["QMAKESPEC"].first();
1320
                }
1321
            }
1322
        }
1323
        if(cmd & ReadConf) {             // parse mkspec
1324
            QString qmakespec = fixEnvVariables(Option::mkfile::qmakespec);
1325
            QStringList mkspec_roots = qmake_mkspec_paths();
1326
            debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec.toLatin1().constData(),
1327
                      mkspec_roots.join("::").toLatin1().constData());
1328
            if(qmakespec.isEmpty()) {
1329
                for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
1330
                    QString mkspec = (*it) + QDir::separator() + "default";
1331
                    QFileInfo default_info(mkspec);
1332
                    if(default_info.exists() && default_info.isDir()) {
1333
                        qmakespec = mkspec;
1334
                        break;
1335
                    }
1336
                }
1337
                if(qmakespec.isEmpty()) {
1338
                    fprintf(stderr, "QMAKESPEC has not been set, so configuration cannot be deduced.\n");
1339
                    return false;
1340
                }
1341
                Option::mkfile::qmakespec = qmakespec;
1342
            }
1343
1344
            if(QDir::isRelativePath(qmakespec)) {
1345
                if (QFile::exists(Option::output_dir+"/"+qmakespec+"/qmake.conf")) {
1346
                    qmakespec = Option::mkfile::qmakespec = QFileInfo(Option::output_dir+"/"+qmakespec).absoluteFilePath();
1347
                } else if (QFile::exists(qmakespec+"/qmake.conf")) {
1348
                    Option::mkfile::qmakespec = QFileInfo(Option::mkfile::qmakespec).absoluteFilePath();
1349
                } else {
1350
                    bool found_mkspec = false;
1351
                    for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
1352
                        QString mkspec = (*it) + QDir::separator() + qmakespec;
1353
                        if(QFile::exists(mkspec)) {
1354
                            found_mkspec = true;
1355
                            Option::mkfile::qmakespec = qmakespec = mkspec;
1356
                            break;
1357
                        }
1358
                    }
1359
                    if(!found_mkspec) {
1360
                        fprintf(stderr, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n",
1361
                                qmakespec.toLatin1().constData(), mkspec_roots.join("\n\t").toLatin1().constData());
1362
                        return false;
1363
                    }
1364
                }
1365
            }
1366
1367
            // parse qmake configuration
1368
            while(qmakespec.endsWith(QString(QChar(QDir::separator()))))
1369
                qmakespec.truncate(qmakespec.length()-1);
1370
            QString spec = qmakespec + QDir::separator() + "qmake.conf";
1371
            debug_msg(1, "QMAKESPEC conf: reading %s", spec.toLatin1().constData());
1372
            if(!read(spec, base_vars)) {
1373
                fprintf(stderr, "Failure to read QMAKESPEC conf file %s.\n", spec.toLatin1().constData());
1374
                return false;
1375
            }
1376
            validateModes();
1377
1378
            if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty()) {
1379
                debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile.toLatin1().constData());
1380
                read(Option::mkfile::cachefile, base_vars);
1381
            }
1382
        }
1383
1384
        if(cmd & ReadFeatures) {
1385
            debug_msg(1, "Processing default_pre: %s", vars["CONFIG"].join("::").toLatin1().constData());
1386
            if(doProjectInclude("default_pre", IncludeFlagFeature, base_vars) == IncludeNoExist)
1387
                doProjectInclude("default", IncludeFlagFeature, base_vars);
1388
        }
1389
    }
1390
1391
    vars = base_vars; // start with the base
1392
1393
    //get a default
1394
    if(pfile != "-" && vars["TARGET"].isEmpty())
1395
        vars["TARGET"].append(QFileInfo(pfile).baseName());
1396
1397
    //before commandline
1398
    if(cmd & ReadCmdLine) {
1399
        cfile = pfile;
1400
        parser.file = "(internal)";
1401
        parser.from_file = false;
1402
        parser.line_no = 1; //really arg count now.. duh
1403
        reset();
1404
        for(QStringList::ConstIterator it = Option::before_user_vars.begin();
1405
            it != Option::before_user_vars.end(); ++it) {
1406
            if(!parse((*it), vars)) {
1407
                fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData());
1408
                return false;
1409
            }
1410
            parser.line_no++;
1411
        }
1412
    }
1413
1414
    //commandline configs
1415
    if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) {
1416
        parser.file = "(configs)";
1417
        parser.from_file = false;
1418
        parser.line_no = 1; //really arg count now.. duh
1419
        parse("CONFIG += " + Option::user_configs.join(" "), vars);
1420
    }
1421
1422
    if(cmd & ReadProFile) { // parse project file
1423
        debug_msg(1, "Project file: reading %s", pfile.toLatin1().constData());
1424
        if(pfile != "-" && !QFile::exists(pfile) && !pfile.endsWith(Option::pro_ext))
1425
            pfile += Option::pro_ext;
1426
        if(!read(pfile, vars))
1427
            return false;
1428
    }
1429
1430
    if(cmd & ReadCmdLine) {
1431
        parser.file = "(internal)";
1432
        parser.from_file = false;
1433
        parser.line_no = 1; //really arg count now.. duh
1434
        reset();
1435
        for(QStringList::ConstIterator it = Option::after_user_vars.begin();
1436
            it != Option::after_user_vars.end(); ++it) {
1437
            if(!parse((*it), vars)) {
1438
                fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData());
1439
                return false;
1440
            }
1441
            parser.line_no++;
1442
        }
1443
    }
1444
1445
    //after configs (set in BUILDS)
1446
    if(cmd & ReadConfigs && !Option::after_user_configs.isEmpty()) {
1447
        parser.file = "(configs)";
1448
        parser.from_file = false;
1449
        parser.line_no = 1; //really arg count now.. duh
1450
        parse("CONFIG += " + Option::after_user_configs.join(" "), vars);
1451
    }
1452
1453
    if(pfile != "-" && vars["TARGET"].isEmpty())
1454
        vars["TARGET"].append(QFileInfo(pfile).baseName());
1455
1456
    if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) {
1457
        parser.file = "(configs)";
1458
        parser.from_file = false;
1459
        parser.line_no = 1; //really arg count now.. duh
1460
        parse("CONFIG += " + Option::user_configs.join(" "), base_vars);
1461
    }
1462
1463
    if(cmd & ReadFeatures) {
1464
        debug_msg(1, "Processing default_post: %s", vars["CONFIG"].join("::").toLatin1().constData());
1465
        doProjectInclude("default_post", IncludeFlagFeature, vars);
1466
1467
        QHash<QString, bool> processed;
1468
        const QStringList &configs = vars["CONFIG"];
1469
        debug_msg(1, "Processing CONFIG features: %s", configs.join("::").toLatin1().constData());
1470
        while(1) {
1471
            bool finished = true;
1472
            for(int i = configs.size()-1; i >= 0; --i) {
1473
		const QString config = configs[i].toLower();
1474
                if(!processed.contains(config)) {
1475
                    processed.insert(config, true);
1476
                    if(doProjectInclude(config, IncludeFlagFeature, vars) == IncludeSuccess) {
1477
                        finished = false;
1478
                        break;
1479
                    }
1480
                }
1481
            }
1482
            if(finished)
1483
                break;
1484
        }
1485
    }
1486
    Option::postProcessProject(this);   // let Option post-process
1487
    return true;
1488
}
1489
1490
void QMakeProject::validateModes()
1491
{
1492
    if (Option::host_mode == Option::HOST_UNKNOWN_MODE
1493
        || Option::target_mode == Option::TARG_UNKNOWN_MODE) {
1494
        Option::HOST_MODE host_mode;
1495
        Option::TARG_MODE target_mode;
1496
        const QStringList &gen = base_vars.value("MAKEFILE_GENERATOR");
1497
        if (gen.isEmpty()) {
1498
            fprintf(stderr, "%s:%d: Using OS scope before setting MAKEFILE_GENERATOR\n",
1499
                            parser.file.toLatin1().constData(), parser.line_no);
1500
        } else if (MetaMakefileGenerator::modesForGenerator(gen.first(),
1501
                                                            &host_mode, &target_mode)) {
1502
            if (Option::host_mode == Option::HOST_UNKNOWN_MODE) {
1503
                Option::host_mode = host_mode;
1504
                Option::applyHostMode();
1505
            }
1506
1507
            if (Option::target_mode == Option::TARG_UNKNOWN_MODE) {
1508
                const QStringList &tgt = base_vars.value("TARGET_PLATFORM");
1509
                if (!tgt.isEmpty()) {
1510
                    const QString &os = tgt.first();
1511
                    if (os == "unix")
1512
                        Option::target_mode = Option::TARG_UNIX_MODE;
1513
                    else if (os == "macx")
1514
                        Option::target_mode = Option::TARG_MACX_MODE;
1515
                    else if (os == "symbian")
1516
                        Option::target_mode = Option::TARG_SYMBIAN_MODE;
1517
                    else if (os == "win32")
1518
                        Option::target_mode = Option::TARG_WIN_MODE;
1519
                    else
1520
                        fprintf(stderr, "Unknown target platform specified: %s\n",
1521
                                os.toLatin1().constData());
1522
                } else {
1523
                    Option::target_mode = target_mode;
1524
                }
1525
            }
1526
        }
1527
    }
1528
}
1529
1530
bool
1531
QMakeProject::isActiveConfig(const QString &x, bool regex, QMap<QString, QStringList> *place)
1532
{
1533
    if(x.isEmpty())
1534
        return true;
1535
1536
    //magic types for easy flipping
1537
    if(x == "true")
1538
        return true;
1539
    else if(x == "false")
1540
        return false;
1541
1542
    if (x == "unix") {
1543
        validateModes();
1544
        return Option::target_mode == Option::TARG_UNIX_MODE
1545
               || Option::target_mode == Option::TARG_MACX_MODE
1546
               || Option::target_mode == Option::TARG_SYMBIAN_MODE;
1547
    } else if (x == "macx" || x == "mac") {
1548
        validateModes();
1549
        return Option::target_mode == Option::TARG_MACX_MODE;
1550
    } else if (x == "symbian") {
1551
        validateModes();
1552
        return Option::target_mode == Option::TARG_SYMBIAN_MODE;
1553
    } else if (x == "win32") {
1554
        validateModes();
1555
        return Option::target_mode == Option::TARG_WIN_MODE;
1556
    }
1557
1558
    //mkspecs
1559
    static QString spec;
1560
    if(spec.isEmpty())
1561
        spec = QFileInfo(Option::mkfile::qmakespec).fileName();
1562
    QRegExp re(x, Qt::CaseSensitive, QRegExp::Wildcard);
1563
    if((regex && re.exactMatch(spec)) || (!regex && spec == x))
1564
        return true;
1565
#ifdef Q_OS_UNIX
1566
    else if(spec == "default") {
1567
        static char *buffer = NULL;
1568
        if(!buffer) {
1569
            buffer = (char *)malloc(1024);
1570
            qmakeAddCacheClear(qmakeFreeCacheClear, (void**)&buffer);
1571
        }
1572
        int l = readlink(Option::mkfile::qmakespec.toLatin1(), buffer, 1024);
1573
        if(l != -1) {
1574
            buffer[l] = '\0';
1575
            QString r = buffer;
1576
            if(r.lastIndexOf('/') != -1)
1577
                r = r.mid(r.lastIndexOf('/') + 1);
1578
            if((regex && re.exactMatch(r)) || (!regex && r == x))
1579
                return true;
1580
        }
1581
    }
1582
#elif defined(Q_OS_WIN)
1583
    else if(spec == "default") {
1584
        // We can't resolve symlinks as they do on Unix, so configure.exe puts the source of the
1585
        // qmake.conf at the end of the default/qmake.conf in the QMAKESPEC_ORG variable.
1586
        const QStringList &spec_org = (place ? (*place)["QMAKESPEC_ORIGINAL"]
1587
                                             : vars["QMAKESPEC_ORIGINAL"]);
1588
        if (!spec_org.isEmpty()) {
1589
            spec = spec_org.at(0);
1590
            int lastSlash = spec.lastIndexOf('/');
1591
            if(lastSlash != -1)
1592
                spec = spec.mid(lastSlash + 1);
1593
            if((regex && re.exactMatch(spec)) || (!regex && spec == x))
1594
                return true;
1595
        }
1596
    }
1597
#endif
1598
1599
    //simple matching
1600
    const QStringList &configs = (place ? (*place)["CONFIG"] : vars["CONFIG"]);
1601
    for(QStringList::ConstIterator it = configs.begin(); it != configs.end(); ++it) {
1602
        if(((regex && re.exactMatch((*it))) || (!regex && (*it) == x)) && re.exactMatch((*it)))
1603
            return true;
1604
    }
1605
    return false;
1606
}
1607
1608
bool
1609
QMakeProject::doProjectTest(QString str, QMap<QString, QStringList> &place)
1610
{
1611
    QString chk = remove_quotes(str);
1612
    if(chk.isEmpty())
1613
        return true;
1614
    bool invert_test = (chk.left(1) == "!");
1615
    if(invert_test)
1616
        chk = chk.mid(1);
1617
1618
    bool test=false;
1619
    int lparen = chk.indexOf('(');
1620
    if(lparen != -1) { // if there is an lparen in the chk, it IS a function
1621
        int rparen = chk.indexOf(')', lparen);
1622
        if(rparen == -1) {
1623
            qmake_error_msg("Function missing right paren: " + chk);
1624
        } else {
1625
            QString func = chk.left(lparen);
1626
            test = doProjectTest(func, chk.mid(lparen+1, rparen - lparen - 1), place);
1627
        }
1628
    } else {
1629
        test = isActiveConfig(chk, true, &place);
1630
    }
1631
    if(invert_test)
1632
        return !test;
1633
    return test;
1634
}
1635
1636
bool
1637
QMakeProject::doProjectTest(QString func, const QString &params,
1638
                            QMap<QString, QStringList> &place)
1639
{
1640
    return doProjectTest(func, split_arg_list(params), place);
1641
}
1642
1643
QMakeProject::IncludeStatus
1644
QMakeProject::doProjectInclude(QString file, uchar flags, QMap<QString, QStringList> &place)
1645
{
1646
    enum { UnknownFormat, ProFormat, JSFormat } format = UnknownFormat;
1647
    if(flags & IncludeFlagFeature) {
1648
        if(!file.endsWith(Option::prf_ext))
1649
            file += Option::prf_ext;
1650
        validateModes(); // init dir_sep
1651
        if(file.indexOf(Option::dir_sep) == -1 || !QFile::exists(file)) {
1652
            static QStringList *feature_roots = 0;
1653
            if(!feature_roots) {
1654
                feature_roots = new QStringList(qmake_feature_paths(prop));
1655
                qmakeAddCacheClear(qmakeDeleteCacheClear<QStringList>, (void**)&feature_roots);
1656
            }
1657
            debug_msg(2, "Looking for feature '%s' in (%s)", file.toLatin1().constData(),
1658
			feature_roots->join("::").toLatin1().constData());
1659
            int start_root = 0;
1660
            if(parser.from_file) {
1661
                QFileInfo currFile(parser.file), prfFile(file);
1662
                if(currFile.fileName() == prfFile.fileName()) {
1663
                    currFile = QFileInfo(currFile.canonicalFilePath());
1664
                    for(int root = 0; root < feature_roots->size(); ++root) {
1665
                        prfFile = QFileInfo(feature_roots->at(root) +
1666
                                            QDir::separator() + file).canonicalFilePath();
1667
                        if(prfFile == currFile) {
1668
                            start_root = root+1;
1669
                            break;
1670
                        }
1671
                    }
1672
                }
1673
            }
1674
            for(int root = start_root; root < feature_roots->size(); ++root) {
1675
                QString prf(feature_roots->at(root) + QDir::separator() + file);
1676
                if(QFile::exists(prf + Option::js_ext)) {
1677
                    format = JSFormat;
1678
                    file = prf + Option::js_ext;
1679
                    break;
1680
                } else if(QFile::exists(prf)) {
1681
                    format = ProFormat;
1682
                    file = prf;
1683
                    break;
1684
                }
1685
            }
1686
            if(format == UnknownFormat)
1687
                return IncludeNoExist;
1688
        }
1689
        if(place["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file) != -1)
1690
            return IncludeFeatureAlreadyLoaded;
1691
        place["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file);
1692
    }
1693
    if(QDir::isRelativePath(file)) {
1694
        QStringList include_roots;
1695
        if(Option::output_dir != qmake_getpwd())
1696
            include_roots << qmake_getpwd();
1697
        include_roots << Option::output_dir;
1698
        for(int root = 0; root < include_roots.size(); ++root) {
1699
            QString testName = QDir::toNativeSeparators(include_roots[root]);
1700
            if (!testName.endsWith(QString(QDir::separator())))
1701
                testName += QDir::separator();
1702
            testName += file;
1703
            if(QFile::exists(testName)) {
1704
                file = testName;
1705
                break;
1706
            }
1707
        }
1708
    }
1709
    if(format == UnknownFormat) {
1710
        if(QFile::exists(file)) {
1711
            if(file.endsWith(Option::js_ext))
1712
                format = JSFormat;
1713
            else
1714
                format = ProFormat;
1715
        } else {
1716
            return IncludeNoExist;
1717
        }
1718
    }
1719
    if(Option::mkfile::do_preprocess) //nice to see this first..
1720
        fprintf(stderr, "#switching file %s(%s) - %s:%d\n", (flags & IncludeFlagFeature) ? "load" : "include",
1721
                file.toLatin1().constData(),
1722
                parser.file.toLatin1().constData(), parser.line_no);
1723
    debug_msg(1, "Project Parser: %s'ing file %s.", (flags & IncludeFlagFeature) ? "load" : "include",
1724
              file.toLatin1().constData());
1725
1726
    QString orig_file = file;
1727
    int di = file.lastIndexOf(QDir::separator());
1728
    QString oldpwd = qmake_getpwd();
1729
    if(di != -1) {
1730
        if(!qmake_setpwd(file.left(file.lastIndexOf(QDir::separator())))) {
1731
            fprintf(stderr, "Cannot find directory: %s\n", file.left(di).toLatin1().constData());
1732
            return IncludeFailure;
1733
        }
1734
    }
1735
    bool parsed = false;
1736
    parser_info pi = parser;
1737
    if(format == JSFormat) {
1738
        warn_msg(WarnParser, "%s:%d: QtScript support disabled for %s.",
1739
                 pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData());
1740
    } else {
1741
        QStack<ScopeBlock> sc = scope_blocks;
1742
        IteratorBlock *it = iterator;
1743
        FunctionBlock *fu = function;
1744
        if(flags & (IncludeFlagNewProject|IncludeFlagNewParser)) {
1745
            // The "project's variables" are used in other places (eg. export()) so it's not
1746
            // possible to use "place" everywhere. Instead just set variables and grab them later
1747
            QMakeProject proj(this, &place);
1748
            if(flags & IncludeFlagNewParser) {
1749
#if 1
1750
                if(proj.doProjectInclude("default_pre", IncludeFlagFeature, proj.variables()) == IncludeNoExist)
1751
                    proj.doProjectInclude("default", IncludeFlagFeature, proj.variables());
1752
#endif
1753
                parsed = proj.read(file, proj.variables()); // parse just that file (fromfile, infile)
1754
            } else {
1755
                parsed = proj.read(file); // parse all aux files (load/include into)
1756
            }
1757
            place = proj.variables();
1758
        } else {
1759
            parsed = read(file, place);
1760
        }
1761
        iterator = it;
1762
        function = fu;
1763
        scope_blocks = sc;
1764
    }
1765
    if(parsed) {
1766
        if(place["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file) == -1)
1767
            place["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file);
1768
    } else {
1769
        warn_msg(WarnParser, "%s:%d: Failure to include file %s.",
1770
                 pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData());
1771
    }
1772
    parser = pi;
1773
    qmake_setpwd(oldpwd);
1774
    if(!parsed)
1775
        return IncludeParseFailure;
1776
    return IncludeSuccess;
1777
}
1778
1779
QStringList
1780
QMakeProject::doProjectExpand(QString func, const QString &params,
1781
                              QMap<QString, QStringList> &place)
1782
{
1783
    return doProjectExpand(func, split_arg_list(params), place);
1784
}
1785
1786
QStringList
1787
QMakeProject::doProjectExpand(QString func, QStringList args,
1788
                              QMap<QString, QStringList> &place)
1789
{
1790
    QList<QStringList> args_list;
1791
    for(int i = 0; i < args.size(); ++i) {
1792
        QStringList arg = split_value_list(args[i]), tmp;
1793
        for(int i = 0; i < arg.size(); ++i)
1794
            tmp += doVariableReplaceExpand(arg[i], place);;
1795
        args_list += tmp;
1796
    }
1797
    return doProjectExpand(func, args_list, place);
1798
}
1799
1800
// defined in symbian generator
1801
extern QString generate_test_uid(const QString& target);
1802
1803
QStringList
1804
QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
1805
                              QMap<QString, QStringList> &place)
1806
{
1807
    func = func.trimmed();
1808
    if(replaceFunctions.contains(func)) {
1809
        FunctionBlock *defined = replaceFunctions[func];
1810
        function_blocks.push(defined);
1811
        QStringList ret;
1812
        defined->exec(args_list, this, place, ret);
1813
        Q_ASSERT(function_blocks.pop() == defined);
1814
        return ret;
1815
    }
1816
1817
    QStringList args; //why don't the builtin functions just use args_list? --Sam
1818
    for(int i = 0; i < args_list.size(); ++i)
1819
        args += args_list[i].join(QString(Option::field_sep));
1820
1821
    ExpandFunc func_t = qmake_expandFunctions().value(func);
1822
    if (!func_t && (func_t = qmake_expandFunctions().value(func.toLower())))
1823
        warn_msg(WarnDeprecated, "%s:%d: Using uppercased builtin functions is deprecated.",
1824
                 parser.file.toLatin1().constData(), parser.line_no);
1825
    debug_msg(1, "Running project expand: %s(%s) [%d]",
1826
              func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
1827
1828
    QStringList ret;
1829
    switch(func_t) {
1830
    case E_MEMBER: {
1831
        if(args.count() < 1 || args.count() > 3) {
1832
            fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n",
1833
                    parser.file.toLatin1().constData(), parser.line_no);
1834
        } else {
1835
            bool ok = true;
1836
            const QStringList &var = values(args.first(), place);
1837
            int start = 0, end = 0;
1838
            if(args.count() >= 2) {
1839
                QString start_str = args[1];
1840
                start = start_str.toInt(&ok);
1841
                if(!ok) {
1842
                    if(args.count() == 2) {
1843
                        int dotdot = start_str.indexOf("..");
1844
                        if(dotdot != -1) {
1845
                            start = start_str.left(dotdot).toInt(&ok);
1846
                            if(ok)
1847
                                end = start_str.mid(dotdot+2).toInt(&ok);
1848
                        }
1849
                    }
1850
                    if(!ok)
1851
                        fprintf(stderr, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
1852
                                parser.file.toLatin1().constData(), parser.line_no,
1853
                                start_str.toLatin1().constData());
1854
                } else {
1855
                    end = start;
1856
                    if(args.count() == 3)
1857
                        end = args[2].toInt(&ok);
1858
                    if(!ok)
1859
                        fprintf(stderr, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
1860
                                parser.file.toLatin1().constData(), parser.line_no,
1861
                                args[2].toLatin1().constData());
1862
                }
1863
            }
1864
            if(ok) {
1865
                if(start < 0)
1866
                    start += var.count();
1867
                if(end < 0)
1868
                    end += var.count();
1869
                if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
1870
                    //nothing
1871
                } else if(start < end) {
1872
                    for(int i = start; i <= end && (int)var.count() >= i; i++)
1873
                        ret += var[i];
1874
                } else {
1875
                    for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--)
1876
                        ret += var[i];
1877
                }
1878
            }
1879
        }
1880
        break; }
1881
    case E_FIRST:
1882
    case E_LAST: {
1883
        if(args.count() != 1) {
1884
            fprintf(stderr, "%s:%d: %s(var) requires one argument.\n",
1885
                    parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
1886
        } else {
1887
            const QStringList &var = values(args.first(), place);
1888
            if(!var.isEmpty()) {
1889
                if(func_t == E_FIRST)
1890
                    ret = QStringList(var[0]);
1891
                else
1892
                    ret = QStringList(var[var.size()-1]);
1893
            }
1894
        }
1895
        break; }
1896
    case E_CAT: {
1897
        if(args.count() < 1 || args.count() > 2) {
1898
            fprintf(stderr, "%s:%d: cat(file) requires one argument.\n",
1899
                    parser.file.toLatin1().constData(), parser.line_no);
1900
        } else {
1901
            QString file = args[0];
1902
            file = Option::fixPathToLocalOS(file);
1903
1904
            bool singleLine = true;
1905
            if(args.count() > 1)
1906
                singleLine = (args[1].toLower() == "true");
1907
1908
            QFile qfile(file);
1909
            if(qfile.open(QIODevice::ReadOnly)) {
1910
                QTextStream stream(&qfile);
1911
                while(!stream.atEnd()) {
1912
                    ret += split_value_list(stream.readLine().trimmed());
1913
                    if(!singleLine)
1914
                        ret += "\n";
1915
                }
1916
                qfile.close();
1917
            }
1918
        }
1919
        break; }
1920
    case E_FROMFILE: {
1921
        if(args.count() != 2) {
1922
            fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n",
1923
                    parser.file.toLatin1().constData(), parser.line_no);
1924
        } else {
1925
            QString file = args[0], seek_var = args[1];
1926
            file = Option::fixPathToLocalOS(file);
1927
1928
            QMap<QString, QStringList> tmp;
1929
            if(doProjectInclude(file, IncludeFlagNewParser, tmp) == IncludeSuccess) {
1930
                if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
1931
                    QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"];
1932
                    const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
1933
                    for(int i = 0; i < in.size(); ++i) {
1934
                        if(out.indexOf(in[i]) == -1)
1935
                            out += in[i];
1936
                    }
1937
                }
1938
                ret = tmp[seek_var];
1939
            }
1940
        }
1941
        break; }
1942
    case E_EVAL: {
1943
        if(args.count() < 1 || args.count() > 2) {
1944
            fprintf(stderr, "%s:%d: eval(variable) requires one argument.\n",
1945
                    parser.file.toLatin1().constData(), parser.line_no);
1946
1947
        } else {
1948
            const QMap<QString, QStringList> *source = &place;
1949
            if(args.count() == 2) {
1950
                if(args.at(1) == "Global") {
1951
                    source = &vars;
1952
                } else if(args.at(1) == "Local") {
1953
                    source = &place;
1954
                } else {
1955
                    fprintf(stderr, "%s:%d: unexpected source to eval.\n", parser.file.toLatin1().constData(),
1956
                            parser.line_no);
1957
                }
1958
            }
1959
            ret += source->value(args.at(0));
1960
        }
1961
        break; }
1962
    case E_LIST: {
1963
        static int x = 0;
1964
        QString tmp;
1965
        tmp.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++);
1966
        ret = QStringList(tmp);
1967
        QStringList &lst = (*((QMap<QString, QStringList>*)&place))[tmp];
1968
        lst.clear();
1969
        for(QStringList::ConstIterator arg_it = args.begin();
1970
            arg_it != args.end(); ++arg_it)
1971
            lst += split_value_list((*arg_it));
1972
        break; }
1973
    case E_SPRINTF: {
1974
        if(args.count() < 1) {
1975
            fprintf(stderr, "%s:%d: sprintf(format, ...) requires one argument.\n",
1976
                    parser.file.toLatin1().constData(), parser.line_no);
1977
        } else {
1978
            QString tmp = args.at(0);
1979
            for(int i = 1; i < args.count(); ++i)
1980
                tmp = tmp.arg(args.at(i));
1981
            ret = split_value_list(tmp);
1982
        }
1983
        break; }
1984
    case E_JOIN: {
1985
        if(args.count() < 1 || args.count() > 4) {
1986
            fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four"
1987
                    "arguments.\n", parser.file.toLatin1().constData(), parser.line_no);
1988
        } else {
1989
            QString glue, before, after;
1990
            if(args.count() >= 2)
1991
                glue = args[1];
1992
            if(args.count() >= 3)
1993
                before = args[2];
1994
            if(args.count() == 4)
1995
                after = args[3];
1996
            const QStringList &var = values(args.first(), place);
1997
            if(!var.isEmpty())
1998
                ret = split_value_list(before + var.join(glue) + after);
1999
        }
2000
        break; }
2001
    case E_SPLIT: {
2002
        if(args.count() < 1 || args.count() > 2) {
2003
            fprintf(stderr, "%s:%d split(var, sep) requires one or two arguments\n",
2004
                    parser.file.toLatin1().constData(), parser.line_no);
2005
        } else {
2006
            QString sep = QString(Option::field_sep);
2007
            if(args.count() >= 2)
2008
                sep = args[1];
2009
            QStringList var = values(args.first(), place);
2010
            for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) {
2011
                QStringList lst = (*vit).split(sep);
2012
                for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit)
2013
                    ret += (*spltit);
2014
            }
2015
        }
2016
        break; }
2017
    case E_BASENAME:
2018
    case E_DIRNAME:
2019
    case E_SECTION: {
2020
        bool regexp = false;
2021
        QString sep, var;
2022
        int beg=0, end=-1;
2023
        if(func_t == E_SECTION) {
2024
            if(args.count() != 3 && args.count() != 4) {
2025
                fprintf(stderr, "%s:%d section(var, sep, begin, end) requires three argument\n",
2026
                        parser.file.toLatin1().constData(), parser.line_no);
2027
            } else {
2028
                var = args[0];
2029
                sep = args[1];
2030
                beg = args[2].toInt();
2031
                if(args.count() == 4)
2032
                    end = args[3].toInt();
2033
            }
2034
        } else {
2035
            if(args.count() != 1) {
2036
                fprintf(stderr, "%s:%d %s(var) requires one argument.\n",
2037
                        parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
2038
            } else {
2039
                var = args[0];
2040
                regexp = true;
2041
                sep = "[" + QRegExp::escape(Option::dir_sep) + "/]";
2042
                if(func_t == E_DIRNAME)
2043
                    end = -2;
2044
                else
2045
                    beg = -1;
2046
            }
2047
        }
2048
        if(!var.isNull()) {
2049
            const QStringList &l = values(var, place);
2050
            for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
2051
                QString separator = sep;
2052
                if(regexp)
2053
                    ret += (*it).section(QRegExp(separator), beg, end);
2054
                else
2055
                    ret += (*it).section(separator, beg, end);
2056
            }
2057
        }
2058
        break; }
2059
    case E_FIND: {
2060
        if(args.count() != 2) {
2061
            fprintf(stderr, "%s:%d find(var, str) requires two arguments\n",
2062
                    parser.file.toLatin1().constData(), parser.line_no);
2063
        } else {
2064
            QRegExp regx(args[1]);
2065
            const QStringList &var = values(args.first(), place);
2066
            for(QStringList::ConstIterator vit = var.begin();
2067
                vit != var.end(); ++vit) {
2068
                if(regx.indexIn(*vit) != -1)
2069
                    ret += (*vit);
2070
            }
2071
        }
2072
        break;  }
2073
    case E_SYSTEM: {
2074
        if(args.count() < 1 || args.count() > 2) {
2075
            fprintf(stderr, "%s:%d system(execut) requires one argument.\n",
2076
                    parser.file.toLatin1().constData(), parser.line_no);
2077
        } else {
2078
            char buff[256];
2079
            bool singleLine = true;
2080
            if(args.count() > 1)
2081
                singleLine = (args[1].toLower() == "true");
2082
            QString output;
2083
            FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
2084
            while(proc && !feof(proc)) {
2085
                int read_in = int(fread(buff, 1, 255, proc));
2086
                if(!read_in)
2087
                    break;
2088
                for(int i = 0; i < read_in; i++) {
2089
                    if((singleLine && buff[i] == '\n') || buff[i] == '\t')
2090
                        buff[i] = ' ';
2091
                }
2092
                buff[read_in] = '\0';
2093
                output += buff;
2094
            }
2095
            ret += split_value_list(output);
2096
            if(proc)
2097
                QT_PCLOSE(proc);
2098
        }
2099
        break; }
2100
    case E_UNIQUE: {
2101
        if(args.count() != 1) {
2102
            fprintf(stderr, "%s:%d unique(var) requires one argument.\n",
2103
                    parser.file.toLatin1().constData(), parser.line_no);
2104
        } else {
2105
            const QStringList &var = values(args.first(), place);
2106
            for(int i = 0; i < var.count(); i++) {
2107
                if(!ret.contains(var[i]))
2108
                    ret.append(var[i]);
2109
            }
2110
        }
2111
        break; }
2112
    case E_QUOTE:
2113
        ret = args;
2114
        break;
2115
    case E_ESCAPE_EXPAND: {
2116
        for(int i = 0; i < args.size(); ++i) {
2117
            QChar *i_data = args[i].data();
2118
            int i_len = args[i].length();
2119
            for(int x = 0; x < i_len; ++x) {
2120
                if(*(i_data+x) == '\\' && x < i_len-1) {
2121
                    if(*(i_data+x+1) == '\\') {
2122
                        ++x;
2123
                    } else {
2124
                        struct {
2125
                            char in, out;
2126
                        } mapped_quotes[] = {
2127
                            { 'n', '\n' },
2128
                            { 't', '\t' },
2129
                            { 'r', '\r' },
2130
                            { 0, 0 }
2131
                        };
2132
                        for(int i = 0; mapped_quotes[i].in; ++i) {
2133
                            if(*(i_data+x+1) == mapped_quotes[i].in) {
2134
                                *(i_data+x) = mapped_quotes[i].out;
2135
                                if(x < i_len-2)
2136
                                    memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
2137
                                --i_len;
2138
                                break;
2139
                            }
2140
                        }
2141
                    }
2142
                }
2143
            }
2144
            ret.append(QString(i_data, i_len));
2145
        }
2146
        break; }
2147
    case E_RE_ESCAPE: {
2148
        for(int i = 0; i < args.size(); ++i)
2149
            ret += QRegExp::escape(args[i]);
2150
        break; }
2151
    case E_UPPER:
2152
    case E_LOWER: {
2153
        for(int i = 0; i < args.size(); ++i) {
2154
            if(func_t == E_UPPER)
2155
                ret += args[i].toUpper();
2156
            else
2157
                ret += args[i].toLower();
2158
        }
2159
        break; }
2160
    case E_FILES: {
2161
        if(args.count() != 1 && args.count() != 2) {
2162
            fprintf(stderr, "%s:%d files(pattern) requires one argument.\n",
2163
                    parser.file.toLatin1().constData(), parser.line_no);
2164
        } else {
2165
            bool recursive = false;
2166
            if(args.count() == 2)
2167
                recursive = (args[1].toLower() == "true" || args[1].toInt());
2168
            QStringList dirs;
2169
            QString r = Option::fixPathToLocalOS(args[0]);
2170
            int slash = r.lastIndexOf(QDir::separator());
2171
            if(slash != -1) {
2172
                dirs.append(r.left(slash));
2173
                r = r.mid(slash+1);
2174
            } else {
2175
                dirs.append("");
2176
            }
2177
2178
            const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
2179
            for(int d = 0; d < dirs.count(); d++) {
2180
                QString dir = dirs[d];
2181
                if(!dir.isEmpty() && !dir.endsWith(QDir::separator()))
2182
                    dir += "/";
2183
2184
                QDir qdir(dir);
2185
                for(int i = 0; i < (int)qdir.count(); ++i) {
2186
                    if(qdir[i] == "." || qdir[i] == "..")
2187
                        continue;
2188
                    QString fname = dir + qdir[i];
2189
                    if(QFileInfo(fname).isDir()) {
2190
                        if(recursive)
2191
                            dirs.append(fname);
2192
                    }
2193
                    if(regex.exactMatch(qdir[i]))
2194
                        ret += fname;
2195
                }
2196
            }
2197
        }
2198
        break; }
2199
    case E_PROMPT: {
2200
        if(args.count() != 1) {
2201
            fprintf(stderr, "%s:%d prompt(question) requires one argument.\n",
2202
                    parser.file.toLatin1().constData(), parser.line_no);
2203
        } else if(pfile == "-") {
2204
            fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
2205
                    parser.file.toLatin1().constData(), parser.line_no);
2206
        } else {
2207
            QString msg = fixEnvVariables(args.first());
2208
            if(!msg.endsWith("?"))
2209
                msg += "?";
2210
            fprintf(stderr, "Project %s: %s ", func.toUpper().toLatin1().constData(),
2211
                    msg.toLatin1().constData());
2212
2213
            QFile qfile;
2214
            if(qfile.open(stdin, QIODevice::ReadOnly)) {
2215
                QTextStream t(&qfile);
2216
                ret = split_value_list(t.readLine());
2217
            }
2218
        }
2219
        break; }
2220
    case E_REPLACE: {
2221
        if(args.count() != 3 ) {
2222
            fprintf(stderr, "%s:%d replace(var, before, after) requires three arguments\n",
2223
                    parser.file.toLatin1().constData(), parser.line_no);
2224
        } else {
2225
            const QRegExp before( args[1] );
2226
            const QString after( args[2] );
2227
            QStringList var = values(args.first(), place);
2228
            for(QStringList::Iterator it = var.begin(); it != var.end(); ++it)
2229
                ret += it->replace(before, after);
2230
        }
2231
        break; }
2232
    case E_SIZE: {
2233
        if(args.count() != 1) {
2234
            fprintf(stderr, "%s:%d: size(var) requires one argument.\n",
2235
                    parser.file.toLatin1().constData(), parser.line_no);
2236
        } else {
2237
            int size = values(args[0], place).size();
2238
            ret += QString::number(size);
2239
        }
2240
        break; }
2241
    case E_GENERATE_UID:
2242
        if (args.count() != 1) {
2243
            fprintf(stderr, "%s:%d: generate_uid(var) requires one argument.\n",
2244
                    parser.file.toLatin1().constData(), parser.line_no);
2245
        } else {
2246
            ret += generate_test_uid(args.first());
2247
        }
2248
        break;
2249
    default: {
2250
        fprintf(stderr, "%s:%d: Unknown replace function: %s\n",
2251
                parser.file.toLatin1().constData(), parser.line_no,
2252
                func.toLatin1().constData());
2253
        break; }
2254
    }
2255
    return ret;
2256
}
2257
2258
bool
2259
QMakeProject::doProjectTest(QString func, QStringList args, QMap<QString, QStringList> &place)
2260
{
2261
    QList<QStringList> args_list;
2262
    for(int i = 0; i < args.size(); ++i) {
2263
        QStringList arg = split_value_list(args[i]), tmp;
2264
        for(int i = 0; i < arg.size(); ++i)
2265
            tmp += doVariableReplaceExpand(arg[i], place);
2266
        args_list += tmp;
2267
    }
2268
    return doProjectTest(func, args_list, place);
2269
}
2270
2271
bool
2272
QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QMap<QString, QStringList> &place)
2273
{
2274
    func = func.trimmed();
2275
2276
    if(testFunctions.contains(func)) {
2277
        FunctionBlock *defined = testFunctions[func];
2278
        QStringList ret;
2279
        function_blocks.push(defined);
2280
        defined->exec(args_list, this, place, ret);
2281
        Q_ASSERT(function_blocks.pop() == defined);
2282
2283
        if(ret.isEmpty()) {
2284
            return true;
2285
        } else {
2286
            if(ret.first() == "true") {
2287
                return true;
2288
            } else if(ret.first() == "false") {
2289
                return false;
2290
            } else {
2291
                bool ok;
2292
                int val = ret.first().toInt(&ok);
2293
                if(ok)
2294
                    return val;
2295
                fprintf(stderr, "%s:%d Unexpected return value from test %s [%s].\n",
2296
                        parser.file.toLatin1().constData(),
2297
                        parser.line_no, func.toLatin1().constData(),
2298
                        ret.join("::").toLatin1().constData());
2299
            }
2300
            return false;
2301
        }
2302
        return false;
2303
    }
2304
2305
    QStringList args; //why don't the builtin functions just use args_list? --Sam
2306
    for(int i = 0; i < args_list.size(); ++i)
2307
        args += args_list[i].join(QString(Option::field_sep));
2308
2309
    TestFunc func_t = qmake_testFunctions().value(func);
2310
    debug_msg(1, "Running project test: %s(%s) [%d]",
2311
              func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
2312
2313
    switch(func_t) {
2314
    case T_REQUIRES:
2315
        return doProjectCheckReqs(args, place);
2316
    case T_LESSTHAN:
2317
    case T_GREATERTHAN: {
2318
        if(args.count() != 2) {
2319
            fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
2320
                    parser.line_no, func.toLatin1().constData());
2321
            return false;
2322
        }
2323
        QString rhs(args[1]), lhs(values(args[0], place).join(QString(Option::field_sep)));
2324
        bool ok;
2325
        int rhs_int = rhs.toInt(&ok);
2326
        if(ok) { // do integer compare
2327
            int lhs_int = lhs.toInt(&ok);
2328
            if(ok) {
2329
                if(func_t == T_GREATERTHAN)
2330
                    return lhs_int > rhs_int;
2331
                return lhs_int < rhs_int;
2332
            }
2333
        }
2334
        if(func_t == T_GREATERTHAN)
2335
            return lhs > rhs;
2336
        return lhs < rhs; }
2337
    case T_IF: {
2338
        if(args.count() != 1) {
2339
            fprintf(stderr, "%s:%d: if(condition) requires one argument.\n", parser.file.toLatin1().constData(),
2340
                    parser.line_no);
2341
            return false;
2342
        }
2343
        const QString cond = args.first();
2344
        const QChar *d = cond.unicode();
2345
        QChar quote = 0;
2346
        bool ret = true, or_op = false;
2347
        QString test;
2348
        for(int d_off = 0, parens = 0, d_len = cond.size(); d_off < d_len; ++d_off) {
2349
            if(!quote.isNull()) {
2350
                if(*(d+d_off) == quote)
2351
                    quote = QChar();
2352
            } else if(*(d+d_off) == '(') {
2353
                ++parens;
2354
            } else if(*(d+d_off) == ')') {
2355
                --parens;
2356
            } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) {
2357
                quote = *(d+d_off);
2358
            }
2359
            if(!parens && quote.isNull() && (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('|') || d_off == d_len-1)) {
2360
                if(d_off == d_len-1)
2361
                    test += *(d+d_off);
2362
                if(!test.isEmpty()) {
2363
                    if (or_op != ret)
2364
                        ret = doProjectTest(test, place);
2365
                    test.clear();
2366
                }
2367
                if(*(d+d_off) == QLatin1Char(':')) {
2368
                    or_op = false;
2369
                } else if(*(d+d_off) == QLatin1Char('|')) {
2370
                    or_op = true;
2371
                }
2372
            } else {
2373
                test += *(d+d_off);
2374
            }
2375
        }
2376
        return ret; }
2377
    case T_EQUALS:
2378
        if(args.count() != 2) {
2379
            fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
2380
                    parser.line_no, func.toLatin1().constData());
2381
            return false;
2382
        }
2383
        return values(args[0], place).join(QString(Option::field_sep)) == args[1];
2384
    case T_EXISTS: {
2385
        if(args.count() != 1) {
2386
            fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.toLatin1().constData(),
2387
                    parser.line_no);
2388
            return false;
2389
        }
2390
        QString file = args.first();
2391
        file = Option::fixPathToLocalOS(file);
2392
2393
        if(QFile::exists(file))
2394
            return true;
2395
        //regular expression I guess
2396
        QString dirstr = qmake_getpwd();
2397
        int slsh = file.lastIndexOf(QDir::separator());
2398
        if(slsh != -1) {
2399
            dirstr = file.left(slsh+1);
2400
            file = file.right(file.length() - slsh - 1);
2401
        }
2402
        return QDir(dirstr).entryList(QStringList(file)).count(); }
2403
    case T_EXPORT:
2404
        if(args.count() != 1) {
2405
            fprintf(stderr, "%s:%d: export(variable) requires one argument.\n", parser.file.toLatin1().constData(),
2406
                    parser.line_no);
2407
            return false;
2408
        }
2409
        for(int i = 0; i < function_blocks.size(); ++i) {
2410
            FunctionBlock *f = function_blocks.at(i);
2411
            f->vars[args[0]] = values(args[0], place);
2412
            if(!i && f->calling_place)
2413
                (*f->calling_place)[args[0]] = values(args[0], place);
2414
        }
2415
        return true;
2416
    case T_CLEAR:
2417
        if(args.count() != 1) {
2418
            fprintf(stderr, "%s:%d: clear(variable) requires one argument.\n", parser.file.toLatin1().constData(),
2419
                    parser.line_no);
2420
            return false;
2421
        }
2422
        if(!place.contains(args[0]))
2423
            return false;
2424
        place[args[0]].clear();
2425
        return true;
2426
    case T_UNSET:
2427
        if(args.count() != 1) {
2428
            fprintf(stderr, "%s:%d: unset(variable) requires one argument.\n", parser.file.toLatin1().constData(),
2429
                    parser.line_no);
2430
            return false;
2431
        }
2432
        if(!place.contains(args[0]))
2433
            return false;
2434
        place.remove(args[0]);
2435
        return true;
2436
    case T_EVAL: {
2437
        if(args.count() < 1 && 0) {
2438
            fprintf(stderr, "%s:%d: eval(project) requires one argument.\n", parser.file.toLatin1().constData(),
2439
                    parser.line_no);
2440
            return false;
2441
        }
2442
        QString project = args.join(" ");
2443
        parser_info pi = parser;
2444
        parser.from_file = false;
2445
        parser.file = "(eval)";
2446
        parser.line_no = 0;
2447
        QTextStream t(&project, QIODevice::ReadOnly);
2448
        bool ret = read(t, place);
2449
        parser = pi;
2450
        return ret; }
2451
    case T_CONFIG: {
2452
        if(args.count() < 1 || args.count() > 2) {
2453
            fprintf(stderr, "%s:%d: CONFIG(config) requires one argument.\n", parser.file.toLatin1().constData(),
2454
                    parser.line_no);
2455
            return false;
2456
        }
2457
        if(args.count() == 1)
2458
            return isActiveConfig(args[0]);
2459
        const QStringList mutuals = args[1].split('|');
2460
        const QStringList &configs = values("CONFIG", place);
2461
        for(int i = configs.size()-1; i >= 0; i--) {
2462
            for(int mut = 0; mut < mutuals.count(); mut++) {
2463
                if(configs[i] == mutuals[mut].trimmed())
2464
                    return (configs[i] == args[0]);
2465
            }
2466
        }
2467
        return false; }
2468
    case T_SYSTEM:
2469
        if(args.count() < 1 || args.count() > 2) {
2470
            fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.toLatin1().constData(),
2471
                    parser.line_no);
2472
            return false;
2473
        }
2474
        if(args.count() == 2) {
2475
            const QString sarg = args[1];
2476
            if (sarg.toLower() == "true" || sarg.toInt())
2477
                warn_msg(WarnParser, "%s:%d: system()'s second argument is now hard-wired to false.\n",
2478
                         parser.file.toLatin1().constData(), parser.line_no);
2479
        }
2480
        return system(args[0].toLatin1().constData()) == 0;
2481
    case T_RETURN:
2482
        if(function_blocks.isEmpty()) {
2483
            fprintf(stderr, "%s:%d unexpected return()\n",
2484
                    parser.file.toLatin1().constData(), parser.line_no);
2485
        } else {
2486
            FunctionBlock *f = function_blocks.top();
2487
            f->cause_return = true;
2488
            if(args_list.count() >= 1)
2489
                f->return_value += args_list[0];
2490
        }
2491
        return true;
2492
    case T_BREAK:
2493
        if(iterator)
2494
            iterator->cause_break = true;
2495
        else
2496
            fprintf(stderr, "%s:%d unexpected break()\n",
2497
                    parser.file.toLatin1().constData(), parser.line_no);
2498
        return true;
2499
    case T_NEXT:
2500
        if(iterator)
2501
            iterator->cause_next = true;
2502
        else
2503
            fprintf(stderr, "%s:%d unexpected next()\n",
2504
                    parser.file.toLatin1().constData(), parser.line_no);
2505
        return true;
2506
    case T_DEFINED:
2507
        if(args.count() < 1 || args.count() > 2) {
2508
            fprintf(stderr, "%s:%d: defined(function) requires one argument.\n",
2509
                    parser.file.toLatin1().constData(), parser.line_no);
2510
        } else {
2511
           if(args.count() > 1) {
2512
               if(args[1] == "test")
2513
                   return testFunctions.contains(args[0]);
2514
               else if(args[1] == "replace")
2515
                   return replaceFunctions.contains(args[0]);
2516
               fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n",
2517
                       parser.file.toLatin1().constData(), parser.line_no,
2518
                       args[1].toLatin1().constData());
2519
            } else {
2520
                if(replaceFunctions.contains(args[0]) || testFunctions.contains(args[0]))
2521
                    return true;
2522
            }
2523
        }
2524
        return false;
2525
    case T_CONTAINS: {
2526
        if(args.count() < 2 || args.count() > 3) {
2527
            fprintf(stderr, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n",
2528
                    parser.file.toLatin1().constData(), parser.line_no);
2529
            return false;
2530
        }
2531
        QRegExp regx(args[1]);
2532
        const QStringList &l = values(args[0], place);
2533
        if(args.count() == 2) {
2534
            for(int i = 0; i < l.size(); ++i) {
2535
                const QString val = l[i];
2536
                if(regx.exactMatch(val) || val == args[1])
2537
                    return true;
2538
            }
2539
        } else {
2540
            const QStringList mutuals = args[2].split('|');
2541
            for(int i = l.size()-1; i >= 0; i--) {
2542
                const QString val = l[i];
2543
                for(int mut = 0; mut < mutuals.count(); mut++) {
2544
                    if(val == mutuals[mut].trimmed())
2545
                        return (regx.exactMatch(val) || val == args[1]);
2546
                }
2547
            }
2548
        }
2549
        return false; }
2550
    case T_INFILE: {
2551
        if(args.count() < 2 || args.count() > 3) {
2552
            fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
2553
                    parser.file.toLatin1().constData(), parser.line_no);
2554
            return false;
2555
        }
2556
2557
        bool ret = false;
2558
        QMap<QString, QStringList> tmp;
2559
        if(doProjectInclude(Option::fixPathToLocalOS(args[0]), IncludeFlagNewParser, tmp) == IncludeSuccess) {
2560
            if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2561
                QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"];
2562
                const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
2563
                for(int i = 0; i < in.size(); ++i) {
2564
                    if(out.indexOf(in[i]) == -1)
2565
                        out += in[i];
2566
                }
2567
            }
2568
            if(args.count() == 2) {
2569
                ret = tmp.contains(args[1]);
2570
            } else {
2571
                QRegExp regx(args[2]);
2572
                const QStringList &l = tmp[args[1]];
2573
                for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
2574
                    if(regx.exactMatch((*it)) || (*it) == args[2]) {
2575
                        ret = true;
2576
                        break;
2577
                    }
2578
                }
2579
            }
2580
        }
2581
        return ret; }
2582
    case T_COUNT:
2583
        if(args.count() != 2 && args.count() != 3) {
2584
            fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.toLatin1().constData(),
2585
                    parser.line_no);
2586
            return false;
2587
        }
2588
        if(args.count() == 3) {
2589
            QString comp = args[2];
2590
            if(comp == ">" || comp == "greaterThan")
2591
                return values(args[0], place).count() > args[1].toInt();
2592
            if(comp == ">=")
2593
                return values(args[0], place).count() >= args[1].toInt();
2594
            if(comp == "<" || comp == "lessThan")
2595
                return values(args[0], place).count() < args[1].toInt();
2596
            if(comp == "<=")
2597
                return values(args[0], place).count() <= args[1].toInt();
2598
            if(comp == "equals" || comp == "isEqual" || comp == "=" || comp == "==")
2599
                return values(args[0], place).count() == args[1].toInt();
2600
            fprintf(stderr, "%s:%d: unexpected modifier to count(%s)\n", parser.file.toLatin1().constData(),
2601
                    parser.line_no, comp.toLatin1().constData());
2602
            return false;
2603
        }
2604
        return values(args[0], place).count() == args[1].toInt();
2605
    case T_ISEMPTY:
2606
        if(args.count() != 1) {
2607
            fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.toLatin1().constData(),
2608
                    parser.line_no);
2609
            return false;
2610
        }
2611
        return values(args[0], place).isEmpty();
2612
    case T_INCLUDE:
2613
    case T_LOAD: {
2614
        QString parseInto;
2615
        const bool include_statement = (func_t == T_INCLUDE);
2616
        bool ignore_error = false;
2617
        if(args.count() >= 2) {
2618
            if(func_t == T_INCLUDE) {
2619
                parseInto = args[1];
2620
                if (args.count() == 3){
2621
                    QString sarg = args[2];
2622
                    if (sarg.toLower() == "true" || sarg.toInt())
2623
                        ignore_error = true;
2624
                }
2625
            } else {
2626
                QString sarg = args[1];
2627
                ignore_error = (sarg.toLower() == "true" || sarg.toInt());
2628
            }
2629
        } else if(args.count() != 1) {
2630
            QString func_desc = "load(feature)";
2631
            if(include_statement)
2632
                func_desc = "include(file)";
2633
            fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.toLatin1().constData(),
2634
                    parser.line_no, func_desc.toLatin1().constData());
2635
            return false;
2636
        }
2637
        QString file = args.first();
2638
        file = Option::fixPathToLocalOS(file);
2639
        uchar flags = IncludeFlagNone;
2640
        if(!include_statement)
2641
            flags |= IncludeFlagFeature;
2642
        IncludeStatus stat = IncludeFailure;
2643
        if(!parseInto.isEmpty()) {
2644
            QMap<QString, QStringList> symbols;
2645
            stat = doProjectInclude(file, flags|IncludeFlagNewProject, symbols);
2646
            if(stat == IncludeSuccess) {
2647
                QMap<QString, QStringList> out_place;
2648
                for(QMap<QString, QStringList>::ConstIterator it = place.begin(); it != place.end(); ++it) {
2649
                    const QString var = it.key();
2650
                    if(var != parseInto && !var.startsWith(parseInto + "."))
2651
                        out_place.insert(var, it.value());
2652
                }
2653
                for(QMap<QString, QStringList>::ConstIterator it = symbols.begin(); it != symbols.end(); ++it) {
2654
                    const QString var = it.key();
2655
                    if(!var.startsWith("."))
2656
                        out_place.insert(parseInto + "." + it.key(), it.value());
2657
                }
2658
                place = out_place;
2659
            }
2660
        } else {
2661
            stat = doProjectInclude(file, flags, place);
2662
        }
2663
        if(stat == IncludeFeatureAlreadyLoaded) {
2664
            warn_msg(WarnParser, "%s:%d: Duplicate of loaded feature %s",
2665
                     parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
2666
        } else if(stat == IncludeNoExist && !ignore_error) {
2667
            warn_msg(WarnAll, "%s:%d: Unable to find file for inclusion %s",
2668
                     parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
2669
            return false;
2670
        } else if(stat >= IncludeFailure) {
2671
            if(!ignore_error) {
2672
                printf("Project LOAD(): Feature %s cannot be found.\n", file.toLatin1().constData());
2673
                if (!ignore_error)
2674
#if defined(QT_BUILD_QMAKE_LIBRARY)
2675
                    return false;
2676
#else
2677
                    exit(3);
2678
#endif
2679
            }
2680
            return false;
2681
        }
2682
        return true; }
2683
    case T_DEBUG: {
2684
        if(args.count() != 2) {
2685
            fprintf(stderr, "%s:%d: debug(level, message) requires one argument.\n", parser.file.toLatin1().constData(),
2686
                    parser.line_no);
2687
            return false;
2688
        }
2689
        QString msg = fixEnvVariables(args[1]);
2690
        debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData());
2691
        return true; }
2692
    case T_ERROR:
2693
    case T_MESSAGE:
2694
    case T_WARNING: {
2695
        if(args.count() != 1) {
2696
            fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.toLatin1().constData(),
2697
                    parser.line_no, func.toLatin1().constData());
2698
            return false;
2699
        }
2700
        QString msg = fixEnvVariables(args.first());
2701
        fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData());
2702
        if(func == "error")
2703
#if defined(QT_BUILD_QMAKE_LIBRARY)
2704
            return false;
2705
#else
2706
            exit(2);
2707
#endif
2708
        return true; }
2709
    case T_OPTION:
2710
        if (args.count() != 1) {
2711
            fprintf(stderr, "%s:%d: option() requires one argument.\n",
2712
                    parser.file.toLatin1().constData(), parser.line_no);
2713
            return false;
2714
        }
2715
        if (args.first() == "recursive") {
2716
            recursive = true;
2717
        } else {
2718
            fprintf(stderr, "%s:%d: unrecognized option() argument '%s'.\n",
2719
                    parser.file.toLatin1().constData(), parser.line_no,
2720
                    args.first().toLatin1().constData());
2721
            return false;
2722
        }
2723
        return true;
2724
    default:
2725
        fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no,
2726
                func.toLatin1().constData());
2727
    }
2728
    return false;
2729
}
2730
2731
bool
2732
QMakeProject::doProjectCheckReqs(const QStringList &deps, QMap<QString, QStringList> &place)
2733
{
2734
    bool ret = false;
2735
    for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) {
2736
        bool test = doProjectTest((*it), place);
2737
        if(!test) {
2738
            debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
2739
                      parser.file.toLatin1().constData(), parser.line_no,
2740
                      (*it).toLatin1().constData());
2741
            place["QMAKE_FAILED_REQUIREMENTS"].append((*it));
2742
            ret = false;
2743
        }
2744
    }
2745
    return ret;
2746
}
2747
2748
bool
2749
QMakeProject::test(const QString &v)
2750
{
2751
    QMap<QString, QStringList> tmp = vars;
2752
    return doProjectTest(v, tmp);
2753
}
2754
2755
bool
2756
QMakeProject::test(const QString &func, const QList<QStringList> &args)
2757
{
2758
    QMap<QString, QStringList> tmp = vars;
2759
    return doProjectTest(func, args, tmp);
2760
}
2761
2762
QStringList
2763
QMakeProject::expand(const QString &str)
2764
{
2765
    bool ok;
2766
    QMap<QString, QStringList> tmp = vars;
2767
    const QStringList ret = doVariableReplaceExpand(str, tmp, &ok);
2768
    if(ok)
2769
        return ret;
2770
    return QStringList();
2771
}
2772
2773
QString
2774
QMakeProject::expand(const QString &str, const QString &file, int line)
2775
{
2776
    bool ok;
2777
    parser_info pi = parser;
2778
    parser.file = file;
2779
    parser.line_no = line;
2780
    parser.from_file = false;
2781
    QMap<QString, QStringList> tmp = vars;
2782
    const QStringList ret = doVariableReplaceExpand(str, tmp, &ok);
2783
    parser = pi;
2784
    return ok ? ret.join(QString(Option::field_sep)) : QString();
2785
}
2786
2787
QStringList
2788
QMakeProject::expand(const QString &func, const QList<QStringList> &args)
2789
{
2790
    QMap<QString, QStringList> tmp = vars;
2791
    return doProjectExpand(func, args, tmp);
2792
}
2793
2794
bool
2795
QMakeProject::doVariableReplace(QString &str, QMap<QString, QStringList> &place)
2796
{
2797
    bool ret;
2798
    str = doVariableReplaceExpand(str, place, &ret).join(QString(Option::field_sep));
2799
    return ret;
2800
}
2801
2802
QStringList
2803
QMakeProject::doVariableReplaceExpand(const QString &str, QMap<QString, QStringList> &place, bool *ok)
2804
{
2805
    QStringList ret;
2806
    if(ok)
2807
        *ok = true;
2808
    if(str.isEmpty())
2809
        return ret;
2810
2811
    const ushort LSQUARE = '[';
2812
    const ushort RSQUARE = ']';
2813
    const ushort LCURLY = '{';
2814
    const ushort RCURLY = '}';
2815
    const ushort LPAREN = '(';
2816
    const ushort RPAREN = ')';
2817
    const ushort DOLLAR = '$';
2818
    const ushort SLASH = '\\';
2819
    const ushort UNDERSCORE = '_';
2820
    const ushort DOT = '.';
2821
    const ushort SPACE = ' ';
2822
    const ushort TAB = '\t';
2823
    const ushort SINGLEQUOTE = '\'';
2824
    const ushort DOUBLEQUOTE = '"';
2825
2826
    ushort unicode, quote = 0;
2827
    const QChar *str_data = str.data();
2828
    const int str_len = str.length();
2829
2830
    ushort term;
2831
    QString var, args;
2832
2833
    int replaced = 0;
2834
    QString current;
2835
    for(int i = 0; i < str_len; ++i) {
2836
        unicode = str_data[i].unicode();
2837
        const int start_var = i;
2838
        if(unicode == DOLLAR && str_len > i+2) {
2839
            unicode = str_data[++i].unicode();
2840
            if(unicode == DOLLAR) {
2841
                term = 0;
2842
                var.clear();
2843
                args.clear();
2844
                enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
2845
                unicode = str_data[++i].unicode();
2846
                if(unicode == LSQUARE) {
2847
                    unicode = str_data[++i].unicode();
2848
                    term = RSQUARE;
2849
                    var_type = PROPERTY;
2850
                } else if(unicode == LCURLY) {
2851
                    unicode = str_data[++i].unicode();
2852
                    var_type = VAR;
2853
                    term = RCURLY;
2854
                } else if(unicode == LPAREN) {
2855
                    unicode = str_data[++i].unicode();
2856
                    var_type = ENVIRON;
2857
                    term = RPAREN;
2858
                }
2859
                while(1) {
2860
                    if(!(unicode & (0xFF<<8)) &&
2861
                       unicode != DOT && unicode != UNDERSCORE &&
2862
                       //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
2863
                       (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
2864
                       (unicode < '0' || unicode > '9'))
2865
                        break;
2866
                    var.append(QChar(unicode));
2867
                    if(++i == str_len)
2868
                        break;
2869
                    unicode = str_data[i].unicode();
2870
                    // at this point, i points to either the 'term' or 'next' character (which is in unicode)
2871
                }
2872
                if(var_type == VAR && unicode == LPAREN) {
2873
                    var_type = FUNCTION;
2874
                    int depth = 0;
2875
                    while(1) {
2876
                        if(++i == str_len)
2877
                            break;
2878
                        unicode = str_data[i].unicode();
2879
                        if(unicode == LPAREN) {
2880
                            depth++;
2881
                        } else if(unicode == RPAREN) {
2882
                            if(!depth)
2883
                                break;
2884
                            --depth;
2885
                        }
2886
                        args.append(QChar(unicode));
2887
                    }
2888
                    if(++i < str_len)
2889
                        unicode = str_data[i].unicode();
2890
                    else
2891
                        unicode = 0;
2892
                    // at this point i is pointing to the 'next' character (which is in unicode)
2893
                    // this might actually be a term character since you can do $${func()}
2894
                }
2895
                if(term) {
2896
                    if(unicode != term) {
2897
                        qmake_error_msg("Missing " + QString(term) + " terminator [found " + (unicode?QString(unicode):QString("end-of-line")) + "]");
2898
                        if(ok)
2899
                            *ok = false;
2900
                        return QStringList();
2901
                    }
2902
                } else {
2903
                    // move the 'cursor' back to the last char of the thing we were looking at
2904
                    --i;
2905
                }
2906
                // since i never points to the 'next' character, there is no reason for this to be set
2907
                unicode = 0;
2908
2909
                QStringList replacement;
2910
                if(var_type == ENVIRON) {
2911
                    replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
2912
                } else if(var_type == PROPERTY) {
2913
                    if(prop)
2914
                        replacement = split_value_list(prop->value(var));
2915
                } else if(var_type == FUNCTION) {
2916
                    replacement = doProjectExpand(var, args, place);
2917
                } else if(var_type == VAR) {
2918
                    replacement = values(var, place);
2919
                }
2920
                if(!(replaced++) && start_var)
2921
                    current = str.left(start_var);
2922
                if(!replacement.isEmpty()) {
2923
                    if(quote) {
2924
                        current += replacement.join(QString(Option::field_sep));
2925
                    } else {
2926
                        current += replacement.takeFirst();
2927
                        if(!replacement.isEmpty()) {
2928
                            if(!current.isEmpty())
2929
                                ret.append(current);
2930
                            current = replacement.takeLast();
2931
                            if(!replacement.isEmpty())
2932
                                ret += replacement;
2933
                        }
2934
                    }
2935
                }
2936
                debug_msg(2, "Project Parser [var replace]: %s -> %s",
2937
                          str.toLatin1().constData(), var.toLatin1().constData(),
2938
                          replacement.join("::").toLatin1().constData());
2939
            } else {
2940
                if(replaced)
2941
                    current.append("$");
2942
            }
2943
        }
2944
        if(quote && unicode == quote) {
2945
            unicode = 0;
2946
            quote = 0;
2947
        } else if(unicode == SLASH) {
2948
            bool escape = false;
2949
            const char *symbols = "[]{}()$\\'\"";
2950
            for(const char *s = symbols; *s; ++s) {
2951
                if(str_data[i+1].unicode() == (ushort)*s) {
2952
                    i++;
2953
                    escape = true;
2954
                    if(!(replaced++))
2955
                        current = str.left(start_var);
2956
                    current.append(str.at(i));
2957
                    break;
2958
                }
2959
            }
2960
            if(!escape && !backslashWarned) {
2961
                backslashWarned = true;
2962
                warn_msg(WarnDeprecated, "%s:%d: Unescaped backslashes are deprecated.",
2963
                         parser.file.toLatin1().constData(), parser.line_no);
2964
            }
2965
            if(escape || !replaced)
2966
                unicode =0;
2967
        } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
2968
            quote = unicode;
2969
            unicode = 0;
2970
            if(!(replaced++) && i)
2971
                current = str.left(i);
2972
        } else if(!quote && (unicode == SPACE || unicode == TAB)) {
2973
            unicode = 0;
2974
            if(!current.isEmpty()) {
2975
                ret.append(current);
2976
                current.clear();
2977
            }
2978
        }
2979
        if(replaced && unicode)
2980
            current.append(QChar(unicode));
2981
    }
2982
    if(!replaced)
2983
        ret = QStringList(str);
2984
    else if(!current.isEmpty())
2985
        ret.append(current);
2986
    //qDebug() << "REPLACE" << str << ret;
2987
    if (quote)
2988
        warn_msg(WarnDeprecated, "%s:%d: Unmatched quotes are deprecated.",
2989
                 parser.file.toLatin1().constData(), parser.line_no);
2990
    return ret;
2991
}
2992
2993
QStringList &QMakeProject::values(const QString &_var, QMap<QString, QStringList> &place)
2994
{
2995
    QString var = varMap(_var);
2996
    if(var == QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token)
2997
        var = ".BUILTIN." + var;
2998
        place[var] = QStringList(QLatin1String("\t"));
2999
    } else if(var == QLatin1String("LITERAL_DOLLAR")) { //a real $
3000
        var = ".BUILTIN." + var;
3001
        place[var] = QStringList(QLatin1String("$"));
3002
    } else if(var == QLatin1String("LITERAL_HASH")) { //a real #
3003
        var = ".BUILTIN." + var;
3004
        place[var] = QStringList("#");
3005
    } else if(var == QLatin1String("OUT_PWD")) { //the out going dir
3006
        var = ".BUILTIN." + var;
3007
        place[var] =  QStringList(Option::output_dir);
3008
    } else if(var == QLatin1String("PWD") ||  //current working dir (of _FILE_)
3009
              var == QLatin1String("IN_PWD")) {
3010
        var = ".BUILTIN." + var;
3011
        place[var] = QStringList(qmake_getpwd());
3012
    } else if(var == QLatin1String("DIR_SEPARATOR")) {
3013
        validateModes();
3014
        var = ".BUILTIN." + var;
3015
        place[var] =  QStringList(Option::dir_sep);
3016
    } else if(var == QLatin1String("DIRLIST_SEPARATOR")) {
3017
        var = ".BUILTIN." + var;
3018
        place[var] = QStringList(Option::dirlist_sep);
3019
    } else if(var == QLatin1String("_LINE_")) { //parser line number
3020
        var = ".BUILTIN." + var;
3021
        place[var] = QStringList(QString::number(parser.line_no));
3022
    } else if(var == QLatin1String("_FILE_")) { //parser file
3023
        var = ".BUILTIN." + var;
3024
        place[var] = QStringList(parser.file);
3025
    } else if(var == QLatin1String("_DATE_")) { //current date/time
3026
        var = ".BUILTIN." + var;
3027
        place[var] = QStringList(QDateTime::currentDateTime().toString());
3028
    } else if(var == QLatin1String("_PRO_FILE_")) {
3029
        var = ".BUILTIN." + var;
3030
        place[var] = QStringList(pfile);
3031
    } else if(var == QLatin1String("_PRO_FILE_PWD_")) {
3032
        var = ".BUILTIN." + var;
3033
        place[var] = QStringList(pfile.isEmpty() ? qmake_getpwd() : QFileInfo(pfile).absolutePath());
3034
    } else if(var == QLatin1String("_QMAKE_CACHE_")) {
3035
        var = ".BUILTIN." + var;
3036
        if(Option::mkfile::do_cache)
3037
            place[var] = QStringList(Option::mkfile::cachefile);
3038
    } else if(var == QLatin1String("TEMPLATE")) {
3039
        if(!Option::user_template.isEmpty()) {
3040
            var = ".BUILTIN.USER." + var;
3041
            place[var] =  QStringList(Option::user_template);
3042
        } else {
3043
            QString orig_template, real_template;
3044
            if(!place[var].isEmpty())
3045
                orig_template = place[var].first();
3046
            real_template = orig_template.isEmpty() ? "app" : orig_template;
3047
            if(!Option::user_template_prefix.isEmpty() && !orig_template.startsWith(Option::user_template_prefix))
3048
                real_template.prepend(Option::user_template_prefix);
3049
            if(real_template != orig_template) {
3050
                var = ".BUILTIN." + var;
3051
                place[var] = QStringList(real_template);
3052
            }
3053
        }
3054
    } else if(var.startsWith(QLatin1String("QMAKE_HOST."))) {
3055
        QString ret, type = var.mid(11);
3056
#if defined(Q_OS_WIN32)
3057
        if(type == "os") {
3058
            ret = "Windows";
3059
        } else if(type == "name") {
3060
            DWORD name_length = 1024;
3061
            wchar_t name[1024];
3062
            if (GetComputerName(name, &name_length))
3063
                ret = QString::fromWCharArray(name);
3064
        } else if(type == "version" || type == "version_string") {
3065
            QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
3066
            if(type == "version")
3067
                ret = QString::number(ver);
3068
            else if(ver == QSysInfo::WV_Me)
3069
                ret = "WinMe";
3070
            else if(ver == QSysInfo::WV_95)
3071
                ret = "Win95";
3072
            else if(ver == QSysInfo::WV_98)
3073
                ret = "Win98";
3074
            else if(ver == QSysInfo::WV_NT)
3075
                ret = "WinNT";
3076
            else if(ver == QSysInfo::WV_2000)
3077
                ret = "Win2000";
3078
            else if(ver == QSysInfo::WV_2000)
3079
                ret = "Win2003";
3080
            else if(ver == QSysInfo::WV_XP)
3081
                ret = "WinXP";
3082
            else if(ver == QSysInfo::WV_VISTA)
3083
                ret = "WinVista";
3084
            else
3085
                ret = "Unknown";
3086
        } else if(type == "arch") {
3087
            SYSTEM_INFO info;
3088
            GetSystemInfo(&info);
3089
            switch(info.wProcessorArchitecture) {
3090
#ifdef PROCESSOR_ARCHITECTURE_AMD64
3091
            case PROCESSOR_ARCHITECTURE_AMD64:
3092
                ret = "x86_64";
3093
                break;
3094
#endif
3095
            case PROCESSOR_ARCHITECTURE_INTEL:
3096
                ret = "x86";
3097
                break;
3098
            case PROCESSOR_ARCHITECTURE_IA64:
3099
#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
3100
            case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
3101
#endif
3102
                ret = "IA64";
3103
                break;
3104
            default:
3105
                ret = "Unknown";
3106
                break;
3107
            }
3108
        }
3109
#elif defined(Q_OS_UNIX)
3110
        struct utsname name;
3111
        if(!uname(&name)) {
3112
            if(type == "os")
3113
                ret = name.sysname;
3114
            else if(type == "name")
3115
                ret = name.nodename;
3116
            else if(type == "version")
3117
                ret = name.release;
3118
            else if(type == "version_string")
3119
                ret = name.version;
3120
            else if(type == "arch")
3121
                ret = name.machine;
3122
        }
3123
#endif
3124
        var = ".BUILTIN.HOST." + type;
3125
        place[var] = QStringList(ret);
3126
    } else if (var == QLatin1String("QMAKE_DIR_SEP")) {
3127
        if (place[var].isEmpty())
3128
            return values("DIR_SEPARATOR", place);
3129
    } else if (var == QLatin1String("QMAKE_EXT_OBJ")) {
3130
        if (place[var].isEmpty()) {
3131
            var = ".BUILTIN." + var;
3132
            place[var] = QStringList(Option::obj_ext);
3133
        }
3134
    } else if (var == QLatin1String("QMAKE_QMAKE")) {
3135
        if (place[var].isEmpty())
3136
            place[var] = QStringList(Option::fixPathToTargetOS(
3137
                !Option::qmake_abslocation.isEmpty()
3138
                    ? Option::qmake_abslocation
3139
                    : QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmake",
3140
                false));
3141
    } else if (var == QLatin1String("EPOCROOT")) {
3142
        if (place[var].isEmpty())
3143
            place[var] = QStringList(qt_epocRoot());
3144
    }
3145
#if defined(Q_OS_WIN32) && defined(Q_CC_MSVC)
3146
      else if(var.startsWith(QLatin1String("QMAKE_TARGET."))) {
3147
            QString ret, type = var.mid(13);
3148
            if(type == "arch") {
3149
                QString paths = qgetenv("PATH");
3150
                QString vcBin64 = qgetenv("VCINSTALLDIR");
3151
                if (!vcBin64.endsWith('\\'))
3152
                    vcBin64.append('\\');
3153
                vcBin64.append("bin\\amd64");
3154
                QString vcBinX86_64 = qgetenv("VCINSTALLDIR");
3155
                if (!vcBinX86_64.endsWith('\\'))
3156
                    vcBinX86_64.append('\\');
3157
                vcBinX86_64.append("bin\\x86_amd64");
3158
                if(paths.contains(vcBin64,Qt::CaseInsensitive) || paths.contains(vcBinX86_64,Qt::CaseInsensitive))
3159
                    ret = "x86_64";
3160
                else
3161
                    ret = "x86";
3162
            }
3163
            place[var] = QStringList(ret);
3164
    }
3165
#endif
3166
    //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::")));
3167
    return place[var];
3168
}
3169
3170
bool QMakeProject::isEmpty(const QString &v)
3171
{
3172
    QMap<QString, QStringList>::ConstIterator it = vars.constFind(varMap(v));
3173
    return it == vars.constEnd() || it->isEmpty();
3174
}
3175
3176
QT_END_NAMESPACE