~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to qmake/project.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the qmake application of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "project.h"
 
30
#include "property.h"
 
31
#include "option.h"
 
32
#include <qdatetime.h>
 
33
#include <qfile.h>
 
34
#include <qfileinfo.h>
 
35
#include <qdir.h>
 
36
#include <qregexp.h>
 
37
#include <qtextstream.h>
 
38
#include <qstack.h>
 
39
#include <qhash.h>
 
40
#ifdef Q_OS_UNIX
 
41
# include <unistd.h>
 
42
#endif
 
43
#include <stdio.h>
 
44
#include <stdlib.h>
 
45
 
 
46
#ifdef Q_OS_WIN32
 
47
#define QT_POPEN _popen
 
48
#else
 
49
#define QT_POPEN popen
 
50
#endif
 
51
 
 
52
struct parser_info {
 
53
    QString file;
 
54
    int line_no;
 
55
    bool from_file;
 
56
} parser;
 
57
 
 
58
static QString remove_quotes(const QString &arg)
 
59
{
 
60
    static bool symbols_init = false;
 
61
    enum { SINGLEQUOTE, DOUBLEQUOTE };
 
62
    static ushort symbols[2];
 
63
    if(!symbols_init) {
 
64
        symbols_init = true;
 
65
        symbols[SINGLEQUOTE] = QChar('\'').unicode();
 
66
        symbols[DOUBLEQUOTE] = QChar('"').unicode();
 
67
    }
 
68
 
 
69
    const QChar *arg_data = arg.data();
 
70
    const ushort first = arg_data->unicode();
 
71
    const int arg_len = arg.length();
 
72
    if(first == symbols[SINGLEQUOTE] || first == symbols[DOUBLEQUOTE]) {
 
73
        const ushort last = (arg_data+arg_len-1)->unicode();
 
74
        if(last == first)
 
75
            return arg.mid(1, arg_len-2);
 
76
    }
 
77
    return arg;
 
78
}
 
79
 
 
80
//just a parsable entity
 
81
struct ParsableBlock
 
82
{
 
83
    ParsableBlock() { }
 
84
    virtual ~ParsableBlock() { }
 
85
 
 
86
    struct Parse {
 
87
        QString text;
 
88
        parser_info pi;
 
89
        Parse(const QString &t) : text(t){ pi = ::parser; }
 
90
    };
 
91
    QList<Parse> parser;
 
92
 
 
93
protected:
 
94
    virtual bool continueBlock() = 0;
 
95
    bool eval(QMakeProject *p, QMap<QString, QStringList> &place);
 
96
};
 
97
 
 
98
bool ParsableBlock::eval(QMakeProject *p, QMap<QString, QStringList> &place)
 
99
{
 
100
    //save state
 
101
    parser_info pi = ::parser;
 
102
 
 
103
    //execute
 
104
    bool ret = true;
 
105
    for(int i = 0; i < parser.count(); i++) {
 
106
        ::parser = parser.at(i).pi;
 
107
        if(!(ret = p->parse(parser.at(i).text, place)) || !continueBlock())
 
108
            break;
 
109
    }
 
110
 
 
111
    //restore state
 
112
    ::parser = pi;
 
113
    return ret;
 
114
}
 
115
 
 
116
//defined functions
 
117
struct FunctionBlock : public ParsableBlock
 
118
{
 
119
    FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { }
 
120
 
 
121
    QMap<QString, QStringList> vars;
 
122
    QMap<QString, QStringList> *calling_place;
 
123
    QString return_value;
 
124
    int scope_level;
 
125
    bool cause_return;
 
126
 
 
127
    bool exec(const QStringList &args,
 
128
              QMakeProject *p, QMap<QString, QStringList> &place, QString &functionReturn);
 
129
    virtual bool continueBlock() { return !cause_return; }
 
130
};
 
131
 
 
132
bool FunctionBlock::exec(const QStringList &args,
 
133
                         QMakeProject *proj, QMap<QString, QStringList> &place, QString &functionReturn)
 
134
{
 
135
    //save state
 
136
#if 0
 
137
    calling_place = &place;
 
138
#else
 
139
    calling_place = &proj->variables();
 
140
#endif
 
141
    return_value = "";
 
142
    cause_return = false;
 
143
 
 
144
    //execute
 
145
    vars = proj->variables();
 
146
    vars["ARGS"] = args;
 
147
    for(int i = 0; i < args.count(); i++)
 
148
        vars[QString::number(i+1)] = QStringList(args[i]);
 
149
    bool ret = ParsableBlock::eval(proj, vars);
 
150
    functionReturn = return_value;
 
151
 
 
152
    //restore state
 
153
    calling_place = 0;
 
154
    return_value.clear();
 
155
    vars.clear();
 
156
    return ret;
 
157
}
 
158
 
 
159
//loops
 
160
struct IteratorBlock : public ParsableBlock
 
161
{
 
162
    IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { }
 
163
 
 
164
    int scope_level;
 
165
 
 
166
    struct Test {
 
167
        QString func;
 
168
        QStringList args;
 
169
        bool invert;
 
170
        parser_info pi;
 
171
        Test(const QString &f, QStringList &a, bool i) : func(f), args(a), invert(i) { pi = ::parser; }
 
172
    };
 
173
    QList<Test> test;
 
174
 
 
175
    QString variable;
 
176
 
 
177
    bool loop_forever, cause_break, cause_next;
 
178
    QStringList list;
 
179
 
 
180
    bool exec(QMakeProject *p, QMap<QString, QStringList> &place);
 
181
    virtual bool continueBlock() { return !cause_next && !cause_break; }
 
182
};
 
183
bool IteratorBlock::exec(QMakeProject *p, QMap<QString, QStringList> &place)
 
184
{
 
185
    bool ret = true;
 
186
    QStringList::Iterator it;
 
187
    if(!loop_forever)
 
188
        it = list.begin();
 
189
    int iterate_count = 0;
 
190
    //save state
 
191
    IteratorBlock *saved_iterator = p->iterator;
 
192
    p->iterator = this;
 
193
 
 
194
    //do the loop
 
195
    while(loop_forever || it != list.end()) {
 
196
        cause_next = cause_break = false;
 
197
        if(!loop_forever && (*it).isEmpty()) { //ignore empty items
 
198
            ++it;
 
199
            continue;
 
200
        }
 
201
 
 
202
        //set up the loop variable
 
203
        QStringList va;
 
204
        if(!variable.isEmpty()) {
 
205
            va = place[variable];
 
206
            if(loop_forever)
 
207
                place[variable] = QStringList(QString::number(iterate_count));
 
208
            else
 
209
                place[variable] = QStringList(*it);
 
210
        }
 
211
        //do the iterations
 
212
        bool succeed = true;
 
213
        for(QList<Test>::Iterator test_it = test.begin(); test_it != test.end(); ++test_it) {
 
214
            ::parser = (*test_it).pi;
 
215
            succeed = p->doProjectTest((*test_it).func, (*test_it).args, place);
 
216
            if((*test_it).invert)
 
217
                succeed = !succeed;
 
218
            if(!succeed)
 
219
                break;
 
220
        }
 
221
        if(succeed)
 
222
            ret = ParsableBlock::eval(p, place);
 
223
        //restore the variable in the map
 
224
        if(!variable.isEmpty())
 
225
            place[variable] = va;
 
226
        //loop counters
 
227
        if(!loop_forever)
 
228
            ++it;
 
229
        iterate_count++;
 
230
        if(!ret || cause_break)
 
231
            break;
 
232
    }
 
233
 
 
234
    //restore state
 
235
    p->iterator = saved_iterator;
 
236
    return ret;
 
237
}
 
238
 
 
239
QMakeProject::ScopeBlock::~ScopeBlock()
 
240
{
 
241
#if 0
 
242
    if(iterate)
 
243
        delete iterate;
 
244
#endif
 
245
}
 
246
 
 
247
static void qmake_error_msg(const QString &msg)
 
248
{
 
249
    fprintf(stderr, "%s:%d: %s\n", parser.file.toLatin1().constData(), parser.line_no,
 
250
            msg.toLatin1().constData());
 
251
}
 
252
 
 
253
/*
 
254
   1) environment variable QMAKEFEATURES (as separated by colons)
 
255
   2) property variable QMAKEFEATURES (as separated by colons)
 
256
   3) <project_root> (where .qmake.cache lives) + FEATURES_DIR
 
257
   4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR
 
258
   5) your QMAKESPEC/features dir
 
259
   6) your data_install/mkspecs/FEATURES_DIR
 
260
   7) your QMAKESPEC/../FEATURES_DIR dir
 
261
 
 
262
   FEATURES_DIR is defined as:
 
263
 
 
264
   1) features/(unix|win32|macx)/
 
265
   2) features/
 
266
*/
 
267
QStringList qmake_feature_paths(QMakeProperty *prop=0)
 
268
{
 
269
    QStringList concat;
 
270
    {
 
271
        const QString base_concat = QDir::separator() + QString("features");
 
272
        switch(Option::target_mode) {
 
273
        case Option::TARG_MACX_MODE:                     //also a unix
 
274
            concat << base_concat + QDir::separator() + "macx";
 
275
            concat << base_concat + QDir::separator() + "unix";
 
276
            break;
 
277
        case Option::TARG_UNIX_MODE:
 
278
            concat << base_concat + QDir::separator() + "unix";
 
279
            break;
 
280
        case Option::TARG_WIN_MODE:
 
281
            concat << base_concat + QDir::separator() + "win32";
 
282
            break;
 
283
        case Option::TARG_MAC9_MODE:
 
284
            concat << base_concat + QDir::separator() + "mac9";
 
285
            break;
 
286
        case Option::TARG_QNX6_MODE: //also a unix
 
287
            concat << base_concat + QDir::separator() + "qnx6";
 
288
            concat << base_concat + QDir::separator() + "unix";
 
289
            break;
 
290
        }
 
291
        concat << base_concat;
 
292
    }
 
293
    const QString mkspecs_concat = QDir::separator() + QString("mkspecs");
 
294
    QStringList feature_roots;
 
295
    QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
 
296
    if(!mkspec_path.isNull()) {
 
297
#ifdef Q_OS_WIN
 
298
        QStringList lst = QString::fromLocal8Bit(mkspec_path).split(';');
 
299
        for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it)
 
300
            feature_roots += (*it).split(':');
 
301
#else
 
302
        feature_roots += QString::fromLocal8Bit(mkspec_path).split(':');
 
303
#endif
 
304
    }
 
305
    if(prop) {
 
306
#ifdef Q_OS_WIN
 
307
        QStringList lst = prop->value("QMAKEFEATURES").split(';');
 
308
        for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it)
 
309
            feature_roots += (*it).split(':');
 
310
#else
 
311
        feature_roots += prop->value("QMAKEFEATURES").split(':');
 
312
#endif
 
313
    }
 
314
    if(!Option::mkfile::cachefile.isEmpty()) {
 
315
        QString path;
 
316
        int last_slash = Option::mkfile::cachefile.lastIndexOf(Option::dir_sep);
 
317
        if(last_slash != -1)
 
318
            path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash));
 
319
        for(QStringList::Iterator concat_it = concat.begin();
 
320
            concat_it != concat.end(); ++concat_it)
 
321
            feature_roots << (path + (*concat_it));
 
322
    }
 
323
    QByteArray qmakepath = qgetenv("QMAKEPATH");
 
324
    if (!qmakepath.isNull()) {
 
325
#ifdef Q_OS_WIN
 
326
        QStringList lst = QString::fromLocal8Bit(qmakepath).split(';');
 
327
        for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it) {
 
328
            QStringList lst2 = (*it).split(':');
 
329
            for(QStringList::Iterator it2 = lst2.begin(); it2 != lst2.end(); ++it2) {
 
330
                for(QStringList::Iterator concat_it = concat.begin();
 
331
                    concat_it != concat.end(); ++concat_it)
 
332
                    feature_roots << ((*it2) + mkspecs_concat + (*concat_it));
 
333
            }
 
334
        }
 
335
#else
 
336
        QStringList lst = QString::fromLocal8Bit(qmakepath).split(':');
 
337
        for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it) {
 
338
            for(QStringList::Iterator concat_it = concat.begin();
 
339
                concat_it != concat.end(); ++concat_it)
 
340
                feature_roots << ((*it) + mkspecs_concat + (*concat_it));
 
341
        }
 
342
#endif
 
343
    }
 
344
    if(!Option::mkfile::qmakespec.isEmpty())
 
345
        feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
 
346
    if(!Option::mkfile::qmakespec.isEmpty()) {
 
347
        QFileInfo specfi(Option::mkfile::qmakespec);
 
348
        QDir specdir(specfi.absoluteFilePath());
 
349
        while(!specdir.isRoot()) {
 
350
            if(!specdir.cdUp() || specdir.isRoot())
 
351
                break;
 
352
            if(QFile::exists(specdir.path() + QDir::separator() + "features")) {
 
353
                for(QStringList::Iterator concat_it = concat.begin();
 
354
                    concat_it != concat.end(); ++concat_it)
 
355
                    feature_roots << (specdir.path() + (*concat_it));
 
356
                break;
 
357
            }
 
358
        }
 
359
    }
 
360
    for(QStringList::Iterator concat_it = concat.begin();
 
361
        concat_it != concat.end(); ++concat_it)
 
362
        feature_roots << (QLibraryInfo::location(QLibraryInfo::PrefixPath) +
 
363
                          mkspecs_concat + (*concat_it));
 
364
    for(QStringList::Iterator concat_it = concat.begin();
 
365
        concat_it != concat.end(); ++concat_it)
 
366
        feature_roots << (QLibraryInfo::location(QLibraryInfo::DataPath) +
 
367
                          mkspecs_concat + (*concat_it));
 
368
    return feature_roots;
 
369
}
 
370
 
 
371
QStringList qmake_mkspec_paths()
 
372
{
 
373
    QStringList ret;
 
374
    const QString concat = QDir::separator() + QString("mkspecs");
 
375
    QByteArray qmakepath = qgetenv("QMAKEPATH");
 
376
    if (!qmakepath.isEmpty()) {
 
377
#ifdef Q_OS_WIN
 
378
        QStringList lst = QString::fromLocal8Bit(qmakepath).split(';');
 
379
        for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it) {
 
380
            QStringList lst2 = (*it).split(':');
 
381
            for(QStringList::Iterator it2 = lst2.begin(); it2 != lst2.end(); ++it2)
 
382
                ret << ((*it2) + concat);
 
383
        }
 
384
#else
 
385
        QStringList lst = QString::fromLocal8Bit(qmakepath).split(':');
 
386
        for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it)
 
387
            ret << ((*it) + concat);
 
388
#endif
 
389
    }
 
390
    ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat;
 
391
 
 
392
    return ret;
 
393
}
 
394
 
 
395
static QString varMap(const QString &x)
 
396
{
 
397
    QString ret(x);
 
398
    if(ret.startsWith("TMAKE")) //tmake no more!
 
399
        ret = "QMAKE" + ret.mid(5);
 
400
    else if(ret == "INTERFACES")
 
401
        ret = "FORMS";
 
402
    else if(ret == "QMAKE_POST_BUILD")
 
403
        ret = "QMAKE_POST_LINK";
 
404
    else if(ret == "TARGETDEPS")
 
405
        ret = "POST_TARGETDEPS";
 
406
    else if(ret == "LIBPATH")
 
407
        ret = "QMAKE_LIBDIR";
 
408
    else if(ret == "QMAKE_EXT_MOC")
 
409
        ret = "QMAKE_EXT_CPP_MOC";
 
410
    else if(ret == "QMAKE_MOD_MOC")
 
411
        ret = "QMAKE_H_MOD_MOC";
 
412
    else if(ret == "QMAKE_LFLAGS_SHAPP")
 
413
        ret = "QMAKE_LFLAGS_APP";
 
414
    else if(ret == "PRECOMPH")
 
415
        ret = "PRECOMPILED_HEADER";
 
416
    else if(ret == "PRECOMPCPP")
 
417
        ret = "PRECOMPILED_SOURCE";
 
418
    else if(ret == "INCPATH")
 
419
        ret = "INCLUDEPATH";
 
420
    else if(ret == "QMAKE_EXTRA_WIN_COMPILERS" || ret == "QMAKE_EXTRA_UNIX_COMPILERS")
 
421
        ret = "QMAKE_EXTRA_COMPILERS";
 
422
    else if(ret == "QMAKE_EXTRA_WIN_TARGETS" || ret == "QMAKE_EXTRA_UNIX_TARGETS")
 
423
        ret = "QMAKE_EXTRA_TARGETS";
 
424
    else if(ret == "QMAKE_EXTRA_UNIX_INCLUDES")
 
425
        ret = "QMAKE_EXTRA_INCLUDES";
 
426
    else if(ret == "QMAKE_EXTRA_UNIX_VARIABLES")
 
427
        ret = "QMAKE_EXTRA_VARIABLES";
 
428
    else if(ret == "QMAKE_RPATH")
 
429
        ret = "QMAKE_LFLAGS_RPATH";
 
430
    return ret;
 
431
}
 
432
 
 
433
static QStringList split_arg_list(QString params)
 
434
{
 
435
    int quote = 0;
 
436
    QStringList args;
 
437
 
 
438
    static bool symbols_init = false;
 
439
    enum { LPAREN, RPAREN, SINGLEQUOTE, DOUBLEQUOTE, COMMA, SPACE, TAB  };
 
440
    static ushort symbols[7];
 
441
    if(!symbols_init) {
 
442
        symbols_init = true;
 
443
        symbols[LPAREN] = QChar('(').unicode();
 
444
        symbols[RPAREN] = QChar(')').unicode();
 
445
        symbols[SINGLEQUOTE] = QChar('\'').unicode();
 
446
        symbols[DOUBLEQUOTE] = QChar('"').unicode();
 
447
        symbols[COMMA] = QChar(',').unicode();
 
448
        symbols[SPACE] = QChar(' ').unicode();
 
449
        symbols[TAB] = QChar('\t').unicode();
 
450
    }
 
451
 
 
452
    ushort unicode;
 
453
    const QChar *params_data = params.data();
 
454
    const int params_len = params.length();
 
455
    int last = 0;
 
456
    while(last < params_len && ((params_data+last)->unicode() == symbols[SPACE]
 
457
                                /*|| (params_data+last)->unicode() == symbols[TAB]*/))
 
458
        ++last;
 
459
    for(int x = last, parens = 0; x <= params_len; x++) {
 
460
        unicode = (params_data+x)->unicode();
 
461
        if(x == params_len) {
 
462
            while(x && (params_data+(x-1))->unicode() == symbols[SPACE])
 
463
                --x;
 
464
            QString mid(params_data+last, x-last);
 
465
            if(quote) {
 
466
                if(mid[0] == quote && mid[(int)mid.length()-1] == quote)
 
467
                    mid = mid.mid(1, mid.length()-2);
 
468
                quote = 0;
 
469
            }
 
470
            args << mid;
 
471
            break;
 
472
        }
 
473
        if(unicode == symbols[LPAREN]) {
 
474
            --parens;
 
475
        } else if(unicode == symbols[RPAREN]) {
 
476
            ++parens;
 
477
        } else if(quote && unicode == quote) {
 
478
            quote = 0;
 
479
        } else if(!quote && (unicode == symbols[SINGLEQUOTE] || unicode == symbols[DOUBLEQUOTE])) {
 
480
            quote = unicode;
 
481
        } else if(!parens && !quote && unicode == symbols[COMMA]) {
 
482
            QString mid = params.mid(last, x - last).trimmed();
 
483
            if(quote) {
 
484
                if(mid[0] == quote && mid[(int)mid.length()-1] == quote)
 
485
                    mid = mid.mid(1, mid.length()-2);
 
486
                quote = 0;
 
487
            }
 
488
            args << mid;
 
489
            last = x+1;
 
490
            while(last < params_len && ((params_data+last)->unicode() == symbols[SPACE]
 
491
                                        /*|| (params_data+last)->unicode() == symbols[TAB]*/))
 
492
                ++last;
 
493
        }
 
494
    }
 
495
    for(int i = 0; i < args.count(); i++)
 
496
        args[i] = remove_quotes(args[i]);
 
497
    return args;
 
498
}
 
499
 
 
500
static QStringList split_value_list(const QString &vals, bool do_semicolon=false)
 
501
{
 
502
    QString build;
 
503
    QStringList ret;
 
504
    QStack<char> quote;
 
505
 
 
506
    static bool symbols_init = false;
 
507
    enum { LPAREN, RPAREN, SINGLEQUOTE, DOUBLEQUOTE, SLASH, SEMICOLON };
 
508
    static ushort symbols[6];
 
509
    if(!symbols_init) {
 
510
        symbols_init = true;
 
511
        symbols[LPAREN] = QChar('(').unicode();
 
512
        symbols[RPAREN] = QChar(')').unicode();
 
513
        symbols[SINGLEQUOTE] = QChar('\'').unicode();
 
514
        symbols[DOUBLEQUOTE] = QChar('"').unicode();
 
515
        symbols[SLASH] = QChar('\\').unicode();
 
516
        symbols[SEMICOLON] = QChar(';').unicode();
 
517
    }
 
518
 
 
519
 
 
520
    ushort unicode;
 
521
    const QChar *vals_data = vals.data();
 
522
    const int vals_len = vals.length();
 
523
    for(int x = 0, parens = 0; x < vals_len; x++) {
 
524
        unicode = (vals_data+x)->unicode();
 
525
        if(x != (int)vals_len-1 && unicode == symbols[SLASH] &&
 
526
           ((vals_data+(x+1))->unicode() == '\'' || (vals_data+(x+1))->unicode() == symbols[DOUBLEQUOTE])) {
 
527
            build += *(vals_data+(x++)); //get that 'escape'
 
528
        } else if(!quote.isEmpty() && unicode == quote.top()) {
 
529
            quote.pop();
 
530
        } else if(unicode == symbols[SINGLEQUOTE] || unicode == symbols[DOUBLEQUOTE]) {
 
531
            quote.push(unicode);
 
532
        } else if(unicode == symbols[RPAREN]) {
 
533
            --parens;
 
534
        } else if(unicode == symbols[LPAREN]) {
 
535
            ++parens;
 
536
        }
 
537
 
 
538
        if(!parens && quote.isEmpty() && ((do_semicolon && unicode == symbols[SEMICOLON]) ||
 
539
                                          *(vals_data+x) == Option::field_sep)) {
 
540
            ret << build;
 
541
            build = "";
 
542
        } else {
 
543
            build += *(vals_data+x);
 
544
        }
 
545
    }
 
546
    if(!build.isEmpty())
 
547
        ret << build;
 
548
    return ret;
 
549
}
 
550
 
 
551
QMakeProject::~QMakeProject()
 
552
{
 
553
    if(own_prop)
 
554
        delete prop;
 
555
}
 
556
 
 
557
 
 
558
void
 
559
QMakeProject::init(QMakeProperty *p, const QMap<QString, QStringList> *vars)
 
560
{
 
561
    if(vars)
 
562
        base_vars = *vars;
 
563
    if(!p) {
 
564
        prop = new QMakeProperty;
 
565
        own_prop = true;
 
566
    } else {
 
567
        prop = p;
 
568
        own_prop = false;
 
569
    }
 
570
    reset();
 
571
}
 
572
 
 
573
void
 
574
QMakeProject::reset()
 
575
{
 
576
    // scope_blocks starts with one non-ignoring entity
 
577
    scope_blocks.clear();
 
578
    scope_blocks.push(ScopeBlock());
 
579
    iterator = 0;
 
580
    function = 0;
 
581
}
 
582
 
 
583
bool
 
584
QMakeProject::parse(const QString &t, QMap<QString, QStringList> &place)
 
585
{
 
586
    QString s = t.simplified();
 
587
    int hash_mark = s.indexOf("#");
 
588
    if(hash_mark != -1) //good bye comments
 
589
        s = s.left(hash_mark);
 
590
    if(s.isEmpty()) // blank_line
 
591
        return true;
 
592
 
 
593
    if(scope_blocks.top().ignore) {
 
594
        bool continue_parsing = false;
 
595
        // adjust scope for each block which appears on a single line
 
596
        for(int i = 0; i < s.length(); i++) {
 
597
            if(s[i] == '{') {
 
598
                scope_blocks.push(ScopeBlock(true));
 
599
            } else if(s[i] == '}') {
 
600
                if(scope_blocks.count() == 1) {
 
601
                    fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
 
602
                    return false;
 
603
                }
 
604
                ScopeBlock sb = scope_blocks.pop();
 
605
                if(sb.iterate)
 
606
                    sb.iterate->exec(this, place);
 
607
                if(!scope_blocks.top().ignore) {
 
608
                    debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
 
609
                              parser.line_no, scope_blocks.count()+1);
 
610
                    s = s.mid(i+1).trimmed();
 
611
                    continue_parsing = !s.isEmpty();
 
612
                    break;
 
613
                }
 
614
            }
 
615
        }
 
616
        if(!continue_parsing) {
 
617
            debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
 
618
                      parser.file.toLatin1().constData(), parser.line_no);
 
619
            return true;
 
620
        }
 
621
    }
 
622
 
 
623
    if(function) {
 
624
        QString append;
 
625
        QByteArray dd = s.toLatin1();
 
626
        const char *d = dd.constData();
 
627
        bool function_finished = false;
 
628
        while(*d) {
 
629
            if((*d) == '}') {
 
630
                function->scope_level--;
 
631
                if(!function->scope_level) {
 
632
                    function_finished = true;
 
633
                    break;
 
634
                }
 
635
            } else if((*d) == '{') {
 
636
                function->scope_level++;
 
637
            }
 
638
            append += *d;
 
639
            d++;
 
640
        }
 
641
        if(!append.isEmpty())
 
642
            function->parser.append(IteratorBlock::Parse(append));
 
643
        if(function_finished) {
 
644
            function = 0;
 
645
            s = QString(d);
 
646
        } else {
 
647
            return true;
 
648
        }
 
649
    } else if(IteratorBlock *it = scope_blocks.top().iterate) {
 
650
        QString append;
 
651
        QByteArray dd = s.toLatin1();
 
652
        const char *d = dd;
 
653
        bool iterate_finished = false;
 
654
        while(*d) {
 
655
            if((*d) == '}') {
 
656
                it->scope_level--;
 
657
                if(!it->scope_level) {
 
658
                    iterate_finished = true;
 
659
                    break;
 
660
                }
 
661
            } else if((*d) == '{') {
 
662
                it->scope_level++;
 
663
            }
 
664
            append += *d;
 
665
            d++;
 
666
        }
 
667
        if(!append.isEmpty())
 
668
            scope_blocks.top().iterate->parser.append(IteratorBlock::Parse(append));
 
669
        if(iterate_finished) {
 
670
            scope_blocks.top().iterate = 0;
 
671
            bool ret = it->exec(this, place);
 
672
            delete it;
 
673
            if(!ret)
 
674
                return false;
 
675
            s = QString(d);
 
676
        } else {
 
677
            return true;
 
678
        }
 
679
    }
 
680
 
 
681
    QString scope, var, op;
 
682
    QStringList val;
 
683
#define SKIP_WS(d) while(*d && (*d == ' ' || *d == '\t')) d++
 
684
    QByteArray dd = s.toLatin1();
 
685
    const char *d = dd;
 
686
    SKIP_WS(d);
 
687
    IteratorBlock *iterator = 0;
 
688
    bool scope_failed = false, else_line = false, or_op=false;
 
689
    char quote = 0;
 
690
    int parens = 0, scope_count=0, start_block = 0;
 
691
    while(*d) {
 
692
        if(!parens) {
 
693
            if(*d == '=')
 
694
                break;
 
695
            if(*d == '+' || *d == '-' || *d == '*' || *d == '~') {
 
696
                if(*(d+1) == '=') {
 
697
                    break;
 
698
                } else if(*(d+1) == ' ') {
 
699
                    const char *k = d + 1;
 
700
                    SKIP_WS(k);
 
701
                    if(*k == '=') {
 
702
                        QString msg;
 
703
                        qmake_error_msg(*d + "must be followed immediately by =");
 
704
                        return false;
 
705
                    }
 
706
                }
 
707
            }
 
708
        }
 
709
 
 
710
        if(quote) {
 
711
            if(*d == quote)
 
712
                quote = 0;
 
713
        } else if(*d == '(') {
 
714
            ++parens;
 
715
        } else if(*d == ')') {
 
716
            --parens;
 
717
        } else if(*d == '"' /*|| *d == '\''*/) {
 
718
            quote = *d;
 
719
        }
 
720
 
 
721
        if(!parens && !quote && (*d == ':' || *d == '{' || *d == ')' || *d == '|')) {
 
722
            scope_count++;
 
723
            scope = var.trimmed();
 
724
            if(*d == ')')
 
725
                scope += *d; // need this
 
726
            var = "";
 
727
 
 
728
            bool test = scope_failed;
 
729
            if(scope.toLower() == "else") { //else is a builtin scope here as it modifies state
 
730
                if(scope_count != 1 || scope_blocks.top().else_status == ScopeBlock::TestNone) {
 
731
                    qmake_error_msg(("Unexpected " + scope + " ('" + s + "')").toLatin1());
 
732
                    return false;
 
733
                }
 
734
                else_line = true;
 
735
                test = (scope_blocks.top().else_status == ScopeBlock::TestSeek);
 
736
                debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser.file.toLatin1().constData(), parser.line_no,
 
737
                          scope == "else" ? "" : QString(" (" + scope + ")").toLatin1().constData(),
 
738
                          test ? "considered" : "excluded");
 
739
            } else {
 
740
                QString comp_scope = scope;
 
741
                bool invert_test = (comp_scope.left(1) == "!");
 
742
                if(invert_test)
 
743
                    comp_scope = comp_scope.right(comp_scope.length()-1);
 
744
                int lparen = comp_scope.indexOf('(');
 
745
                if(or_op == scope_failed) {
 
746
                    if(lparen != -1) { // if there is an lparen in the scope, it IS a function
 
747
                        int rparen = comp_scope.lastIndexOf(')');
 
748
                        if(rparen == -1) {
 
749
                            QByteArray error;
 
750
                            error.reserve(256);
 
751
#if defined(_MSC_VER) && _MSC_VER >= 1400
 
752
                            sprintf_s(error.data(), 256, "Function missing right paren: %s ('%s')",
 
753
                                    comp_scope.toLatin1().constData(), s.toLatin1().constData());
 
754
#else
 
755
                            sprintf(error.data(), "Function missing right paren: %s ('%s')",
 
756
                                    comp_scope.toLatin1().constData(), s.toLatin1().constData());
 
757
#endif
 
758
                            qmake_error_msg(error);
 
759
                            return false;
 
760
                        }
 
761
                        QString func = comp_scope.left(lparen);
 
762
                        QStringList args = split_arg_list(comp_scope.mid(lparen+1, rparen - lparen - 1));
 
763
                        for(int i = 0; i < args.size(); ++i)
 
764
                            args[i] = remove_quotes(args[i].trimmed());
 
765
                        if(function) {
 
766
                            fprintf(stderr, "%s:%d: No tests can come after a function definition!\n",
 
767
                                    parser.file.toLatin1().constData(), parser.line_no);
 
768
                            return false;
 
769
                        } else if(func == "for") { //for is a builtin function here, as it modifies state
 
770
                            if(args.count() > 2 || args.count() < 1) {
 
771
                                fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n",
 
772
                                        parser.file.toLatin1().constData(), parser.line_no);
 
773
                                return false;
 
774
                            } else if(iterator) {
 
775
                                fprintf(stderr, "%s:%d unexpected nested for()\n",
 
776
                                        parser.file.toLatin1().constData(), parser.line_no);
 
777
                                return false;
 
778
                            }
 
779
 
 
780
                            iterator = new IteratorBlock;
 
781
                            QString it_list;
 
782
                            if(args.count() == 1) {
 
783
                                doVariableReplace(args[0], place);
 
784
                                it_list = args[0];
 
785
                                if(args[0] != "ever") {
 
786
                                    delete iterator;
 
787
                                    iterator = 0;
 
788
                                    fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n",
 
789
                                            parser.file.toLatin1().constData(), parser.line_no);
 
790
                                    return false;
 
791
                                }
 
792
                                it_list = "forever";
 
793
                            } else if(args.count() == 2) {
 
794
                                iterator->variable = args[0];
 
795
                                doVariableReplace(args[1], place);
 
796
                                it_list = args[1];
 
797
                            }
 
798
                            QStringList list = place[it_list];
 
799
                            if(list.isEmpty()) {
 
800
                                if(it_list == "forever") {
 
801
                                    iterator->loop_forever = true;
 
802
                                } else {
 
803
                                    int dotdot = it_list.indexOf("..");
 
804
                                    if(dotdot != -1) {
 
805
                                        bool ok;
 
806
                                        int start = it_list.left(dotdot).toInt(&ok);
 
807
                                        if(ok) {
 
808
                                            int end = it_list.mid(dotdot+2).toInt(&ok);
 
809
                                            if(ok) {
 
810
                                                if(start < end) {
 
811
                                                    for(int i = start; i <= end; i++)
 
812
                                                        list << QString::number(i);
 
813
                                                } else {
 
814
                                                    for(int i = start; i >= end; i--)
 
815
                                                        list << QString::number(i);
 
816
                                                }
 
817
                                            }
 
818
                                        }
 
819
                                    }
 
820
                                }
 
821
                            }
 
822
                            iterator->list = list;
 
823
                            test = !invert_test;
 
824
                        } else if(iterator) {
 
825
                            iterator->test.append(IteratorBlock::Test(func, args, invert_test));
 
826
                            test = !invert_test;
 
827
                        } else if(func == "defineTest" || func == "defineReplace") {
 
828
                            if(!function_blocks.isEmpty()) {
 
829
                                fprintf(stderr,
 
830
                                        "%s:%d: cannot define a function within another definition.\n",
 
831
                                        parser.file.toLatin1().constData(), parser.line_no);
 
832
                                return false;
 
833
                            }
 
834
                            if(args.count() != 1) {
 
835
                                fprintf(stderr, "%s:%d: %s(function_name) requires one argument.\n",
 
836
                                        parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
 
837
                                return false;
 
838
                            }
 
839
                            QMap<QString, FunctionBlock*> *map = 0;
 
840
                            if(func == "defineTest")
 
841
                                map = &testFunctions;
 
842
                            else
 
843
                                map = &replaceFunctions;
 
844
                            if(!map || map->contains(args[0])) {
 
845
                                fprintf(stderr, "%s:%d: Function[%s] multiply defined.\n",
 
846
                                        parser.file.toLatin1().constData(), parser.line_no, args[0].toLatin1().constData());
 
847
                                return false;
 
848
                            }
 
849
                            function = new FunctionBlock;
 
850
                            map->insert(args[0], function);
 
851
                            test = true;
 
852
                        } else {
 
853
                            test = doProjectTest(func, args, place);
 
854
                            if(*d == ')' && !*(d+1)) {
 
855
                                if(invert_test)
 
856
                                    test = !test;
 
857
                                scope_blocks.top().else_status =
 
858
                                    (test ? ScopeBlock::TestFound : ScopeBlock::TestSeek);
 
859
                                return true;  // assume we are done
 
860
                            }
 
861
                        }
 
862
                    } else {
 
863
                        QString cscope = comp_scope.trimmed();
 
864
                        doVariableReplace(cscope, place);
 
865
                        test = isActiveConfig(cscope.trimmed(), true, &place);
 
866
                    }
 
867
                    if(invert_test)
 
868
                        test = !test;
 
869
                }
 
870
            }
 
871
            if(!test && !scope_failed)
 
872
                debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser.file.toLatin1().constData(),
 
873
                          parser.line_no, scope.toLatin1().constData());
 
874
            if(test == or_op)
 
875
                scope_failed = !test;
 
876
            or_op = (*d == '|');
 
877
 
 
878
            if(*d == '{') { // scoping block
 
879
                start_block++;
 
880
                if(iterator) {
 
881
                    for(int off = 0, braces = 0; true; ++off) {
 
882
                        if(*(d+off) == '{')
 
883
                            ++braces;
 
884
                        else if(*(d+off) == '}' && braces)
 
885
                            --braces;
 
886
                        if(!braces || !*(d+off)) {
 
887
                            iterator->parser.append(QString(QByteArray(d+1, off-1)));
 
888
                            if(braces > 1)
 
889
                                iterator->scope_level += braces-1;
 
890
                            d += off-1;
 
891
                            break;
 
892
                        }
 
893
                    }
 
894
                }
 
895
            }
 
896
        } else if(!parens && *d == '}') {
 
897
            if(start_block) {
 
898
                --start_block;
 
899
            } else if(!scope_blocks.count()) {
 
900
                warn_msg(WarnParser, "Possible braces mismatch %s:%d", parser.file.toLatin1().constData(), parser.line_no);
 
901
            } else {
 
902
                if(scope_blocks.count() == 1) {
 
903
                    fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
 
904
                    return false;
 
905
                }
 
906
                debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
 
907
                          parser.line_no, scope_blocks.count());
 
908
                ScopeBlock sb = scope_blocks.pop();
 
909
                if(sb.iterate)
 
910
                    sb.iterate->exec(this, place);
 
911
            }
 
912
        } else {
 
913
            var += *d;
 
914
        }
 
915
        d++;
 
916
    }
 
917
    var = var.trimmed();
 
918
 
 
919
    if(!else_line || (else_line && !scope_failed))
 
920
        scope_blocks.top().else_status = (!scope_failed ? ScopeBlock::TestFound : ScopeBlock::TestSeek);
 
921
    if(start_block) {
 
922
        ScopeBlock next_block(scope_failed);
 
923
        next_block.iterate = iterator;
 
924
        if(iterator)
 
925
            next_block.else_status = ScopeBlock::TestNone;
 
926
        else if(scope_failed)
 
927
            next_block.else_status = ScopeBlock::TestSeek;
 
928
        else
 
929
            next_block.else_status = ScopeBlock::TestFound;
 
930
        scope_blocks.push(next_block);
 
931
        debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d).", parser.file.toLatin1().constData(),
 
932
                  parser.line_no, scope_blocks.count(), scope_failed);
 
933
    } else if(iterator) {
 
934
        iterator->parser.append(var+QString(d));
 
935
        bool ret = iterator->exec(this, place);
 
936
        delete iterator;
 
937
        return ret;
 
938
    }
 
939
 
 
940
    if((!scope_count && !var.isEmpty()) || (scope_count == 1 && else_line))
 
941
        scope_blocks.top().else_status = ScopeBlock::TestNone;
 
942
    if(!*d) {
 
943
        if(!var.trimmed().isEmpty())
 
944
            qmake_error_msg(("Parse Error ('" + s + "')").toLatin1());
 
945
        return var.isEmpty(); // allow just a scope
 
946
    }
 
947
 
 
948
    SKIP_WS(d);
 
949
    for(; *d && op.indexOf('=') == -1; op += *(d++))
 
950
        ;
 
951
    op.replace(QRegExp("\\s"), "");
 
952
 
 
953
    SKIP_WS(d);
 
954
    QString vals(d); // vals now contains the space separated list of values
 
955
    int rbraces = vals.count('}'), lbraces = vals.count('{');
 
956
    if(scope_blocks.count() > 1 && rbraces - lbraces == 1) {
 
957
        debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(),
 
958
                  parser.line_no, scope_blocks.count());
 
959
        ScopeBlock sb = scope_blocks.pop();
 
960
        if(sb.iterate)
 
961
            sb.iterate->exec(this, place);
 
962
        vals.truncate(vals.length()-1);
 
963
    } else if(rbraces != lbraces) {
 
964
        warn_msg(WarnParser, "Possible braces mismatch {%s} %s:%d",
 
965
                 vals.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
 
966
    }
 
967
    if(scope_failed)
 
968
        return true; // oh well
 
969
#undef SKIP_WS
 
970
 
 
971
    doVariableReplace(vals, place);
 
972
    doVariableReplace(var, place);
 
973
    var = varMap(var); //backwards compatability
 
974
    if(!var.isEmpty() && Option::mkfile::do_preprocess) {
 
975
        static QString last_file("*none*");
 
976
        if(parser.file != last_file) {
 
977
            fprintf(stdout, "#file %s:%d\n", parser.file.toLatin1().constData(), parser.line_no);
 
978
            last_file = parser.file;
 
979
        }
 
980
        fprintf(stdout, "%s %s %s\n", var.toLatin1().constData(), op.toLatin1().constData(), vals.toLatin1().constData());
 
981
    }
 
982
 
 
983
    // vallist is the broken up list of values
 
984
    QStringList vallist = split_value_list(vals, (var == "DEPENDPATH" || var == "INCLUDEPATH"));
 
985
    if(!vallist.filter("=").isEmpty())
 
986
        warn_msg(WarnParser, "Detected possible line continuation: {%s} %s:%d",
 
987
                 var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
 
988
 
 
989
    QStringList &varlist = place[var]; // varlist is the list in the symbol table
 
990
    debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser.file.toLatin1().constData(), parser.line_no,
 
991
              var.toLatin1().constData(), op.toLatin1().constData(), vallist.isEmpty() ? "" : vallist.join(" :: ").toLatin1().constData());
 
992
 
 
993
    // now do the operation
 
994
    if(op == "~=") {
 
995
        if(vals.length() < 4 || vals.at(0) != 's') {
 
996
            qmake_error_msg(("~= operator only can handle s/// function ('" +
 
997
                            s + "')").toLatin1());
 
998
            return false;
 
999
        }
 
1000
        QChar sep = vals.at(1);
 
1001
        QStringList func = vals.split(sep);
 
1002
        if(func.count() < 3 || func.count() > 4) {
 
1003
            qmake_error_msg(("~= operator only can handle s/// function ('" +
 
1004
                s + "')").toLatin1());
 
1005
            return false;
 
1006
        }
 
1007
        bool global = false, case_sense = true, quote = false;
 
1008
        if(func.count() == 4) {
 
1009
            global = func[3].indexOf('g') != -1;
 
1010
            case_sense = func[3].indexOf('i') == -1;
 
1011
            quote = func[3].indexOf('q') != -1;
 
1012
        }
 
1013
        QString from = func[1], to = func[2];
 
1014
        if(quote)
 
1015
            from = QRegExp::escape(from);
 
1016
        QRegExp regexp(from, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
 
1017
        for(QStringList::Iterator varit = varlist.begin(); varit != varlist.end();) {
 
1018
            if((*varit).contains(regexp)) {
 
1019
                (*varit) = (*varit).replace(regexp, to);
 
1020
                if ((*varit).isEmpty())
 
1021
                    varit = varlist.erase(varit);
 
1022
                else
 
1023
                    ++varit;
 
1024
                if(!global)
 
1025
                    break;
 
1026
            } else
 
1027
                ++varit;
 
1028
        }
 
1029
    } else {
 
1030
        if(op == "=") {
 
1031
            if(!varlist.isEmpty())
 
1032
                warn_msg(WarnParser, "Operator=(%s) clears variables previously set: %s:%d",
 
1033
                         var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no);
 
1034
            varlist.clear();
 
1035
        }
 
1036
        for(QStringList::ConstIterator valit = vallist.begin();
 
1037
            valit != vallist.end(); ++valit) {
 
1038
            if((*valit).isEmpty())
 
1039
                continue;
 
1040
            if((op == "*=" && !varlist.contains((*valit))) ||
 
1041
               op == "=" || op == "+=")
 
1042
                varlist.append((*valit));
 
1043
            else if(op == "-=")
 
1044
                varlist.removeAll((*valit));
 
1045
        }
 
1046
    }
 
1047
    if(var == "REQUIRES") // special case to get communicated to backends!
 
1048
        doProjectCheckReqs(vallist, place);
 
1049
    return true;
 
1050
}
 
1051
 
 
1052
bool
 
1053
QMakeProject::read(QTextStream &file, QMap<QString, QStringList> &place)
 
1054
{
 
1055
    bool ret = true;
 
1056
    QString s, line;
 
1057
    while(!file.atEnd()) {
 
1058
        parser.line_no++;
 
1059
        line = file.readLine().trimmed();
 
1060
        int prelen = line.length();
 
1061
 
 
1062
        int hash_mark = line.indexOf("#");
 
1063
        if(hash_mark != -1) //good bye comments
 
1064
            line = line.left(hash_mark).trimmed();
 
1065
        if(!line.isEmpty() && line.right(1) == "\\") {
 
1066
            if(!line.startsWith("#")) {
 
1067
                line.truncate(line.length() - 1);
 
1068
                s += line + Option::field_sep;
 
1069
            }
 
1070
        } else if(!line.isEmpty() || (line.isEmpty() && !prelen)) {
 
1071
            if(s.isEmpty() && line.isEmpty())
 
1072
                continue;
 
1073
            if(!line.isEmpty())
 
1074
                s += line;
 
1075
            if(!s.isEmpty()) {
 
1076
                if(!(ret = parse(s, place))) {
 
1077
                    s = "";
 
1078
                    break;
 
1079
                }
 
1080
                s = "";
 
1081
            }
 
1082
        }
 
1083
    }
 
1084
    if (!s.isEmpty())
 
1085
        ret = parse(s, place);
 
1086
    return ret;
 
1087
}
 
1088
 
 
1089
bool
 
1090
QMakeProject::read(const QString &file, QMap<QString, QStringList> &place)
 
1091
{
 
1092
    parser_info pi = parser;
 
1093
    reset();
 
1094
 
 
1095
    QString filename = Option::fixPathToLocalOS(file);
 
1096
    doVariableReplace(filename, place);
 
1097
    bool ret = false, using_stdin = false;
 
1098
    QFile qfile;
 
1099
    if(!strcmp(filename.toLatin1(), "-")) {
 
1100
        qfile.setFileName("");
 
1101
        ret = qfile.open(stdin, QIODevice::ReadOnly);
 
1102
        using_stdin = true;
 
1103
    } else if(QFileInfo(file).isDir()) {
 
1104
        return false;
 
1105
    } else {
 
1106
        qfile.setFileName(filename);
 
1107
        ret = qfile.open(QIODevice::ReadOnly);
 
1108
    }
 
1109
    if(ret) {
 
1110
        parser_info pi = parser;
 
1111
        parser.from_file = true;
 
1112
        parser.file = filename;
 
1113
        parser.line_no = 0;
 
1114
        QTextStream t(&qfile);
 
1115
        ret = read(t, place);
 
1116
        if(!using_stdin)
 
1117
            qfile.close();
 
1118
        parser = pi;
 
1119
    }
 
1120
    parser = pi;
 
1121
    if(scope_blocks.count() != 1)
 
1122
        warn_msg(WarnParser, "%s: Unterminated conditional at end of file.",
 
1123
                 file.toLatin1().constData());
 
1124
    return ret;
 
1125
}
 
1126
 
 
1127
bool
 
1128
QMakeProject::read(const QString &project, uchar cmd)
 
1129
{
 
1130
    pfile = project;
 
1131
    return read(cmd);
 
1132
}
 
1133
 
 
1134
bool
 
1135
QMakeProject::read(uchar cmd)
 
1136
{
 
1137
    if(cfile.isEmpty()) {
 
1138
        // hack to get the Option stuff in there
 
1139
        base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext;
 
1140
        base_vars["QMAKE_EXT_H"] = Option::h_ext;
 
1141
        if(!Option::user_template_prefix.isEmpty())
 
1142
            base_vars["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix);
 
1143
 
 
1144
        if(cmd & ReadCache && Option::mkfile::do_cache) {        // parse the cache
 
1145
            int cache_depth = -1;
 
1146
            QString qmake_cache = Option::mkfile::cachefile;
 
1147
            if(qmake_cache.isEmpty())  { //find it as it has not been specified
 
1148
                QString dir = QDir::convertSeparators(Option::output_dir);
 
1149
                while(!QFile::exists((qmake_cache = dir + QDir::separator() + ".qmake.cache"))) {
 
1150
                    dir = dir.left(dir.lastIndexOf(QDir::separator()));
 
1151
                    if(dir.isEmpty() || dir.indexOf(QDir::separator()) == -1) {
 
1152
                        qmake_cache = "";
 
1153
                        break;
 
1154
                    }
 
1155
                    if(cache_depth == -1)
 
1156
                        cache_depth = 1;
 
1157
                    else
 
1158
                        cache_depth++;
 
1159
                }
 
1160
            } else {
 
1161
                QString abs_cache = QFileInfo(Option::mkfile::cachefile).absoluteDir().path();
 
1162
                if(Option::output_dir.startsWith(abs_cache))
 
1163
                    cache_depth = Option::output_dir.mid(abs_cache.length()).count('/');
 
1164
            }
 
1165
            if(!qmake_cache.isEmpty()) {
 
1166
                if(read(qmake_cache, cache)) {
 
1167
                    Option::mkfile::cachefile_depth = cache_depth;
 
1168
                    Option::mkfile::cachefile = qmake_cache;
 
1169
                    if(Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty())
 
1170
                        Option::mkfile::qmakespec = cache["QMAKESPEC"].first();
 
1171
                }
 
1172
            }
 
1173
        }
 
1174
        if(cmd & ReadConf) {             // parse mkspec
 
1175
            QStringList mkspec_roots = qmake_mkspec_paths();
 
1176
            debug_msg(2, "Looking for mkspec %s in (%s)", Option::mkfile::qmakespec.toLatin1().constData(),
 
1177
                      mkspec_roots.join("::").toLatin1().constData());
 
1178
            if(Option::mkfile::qmakespec.isEmpty()) {
 
1179
                for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
 
1180
                    QString mkspec = (*it) + QDir::separator() + "default";
 
1181
                    QFileInfo default_info(mkspec);
 
1182
                    if(default_info.exists() && default_info.isSymLink()) {
 
1183
                        Option::mkfile::qmakespec = mkspec;
 
1184
                        break;
 
1185
                    }
 
1186
                }
 
1187
                if(Option::mkfile::qmakespec.isEmpty()) {
 
1188
                    fprintf(stderr, "QMAKESPEC has not been set, so configuration cannot be deduced.\n");
 
1189
                    return false;
 
1190
                }
 
1191
            }
 
1192
 
 
1193
            if(QDir::isRelativePath(Option::mkfile::qmakespec)) {
 
1194
                bool found_mkspec = false;
 
1195
                for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
 
1196
                    QString mkspec = (*it) + QDir::separator() + Option::mkfile::qmakespec;
 
1197
                    if(QFile::exists(mkspec)) {
 
1198
                        found_mkspec = true;
 
1199
                        Option::mkfile::qmakespec = mkspec;
 
1200
                        break;
 
1201
                    }
 
1202
                }
 
1203
                if(!found_mkspec) {
 
1204
                    fprintf(stderr, "Could not find mkspecs for your QMAKESPEC after trying:\n\t%s\n",
 
1205
                            mkspec_roots.join("\n\t").toLatin1().constData());
 
1206
                    return false;
 
1207
                }
 
1208
            }
 
1209
 
 
1210
            // parse qmake configuration
 
1211
            while(Option::mkfile::qmakespec.endsWith(QString(QChar(QDir::separator()))))
 
1212
                Option::mkfile::qmakespec.truncate(Option::mkfile::qmakespec.length()-1);
 
1213
            QString spec = Option::mkfile::qmakespec + QDir::separator() + "qmake.conf";
 
1214
            if(!QFile::exists(spec) &&
 
1215
               QFile::exists(Option::mkfile::qmakespec + QDir::separator() + "tmake.conf"))
 
1216
                spec = Option::mkfile::qmakespec + QDir::separator() + "tmake.conf";
 
1217
            debug_msg(1, "QMAKESPEC conf: reading %s", spec.toLatin1().constData());
 
1218
            if(!read(spec, base_vars)) {
 
1219
                fprintf(stderr, "Failure to read QMAKESPEC conf file %s.\n", spec.toLatin1().constData());
 
1220
                return false;
 
1221
            }
 
1222
            if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty()) {
 
1223
                debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile.toLatin1().constData());
 
1224
                read(Option::mkfile::cachefile, base_vars);
 
1225
            }
 
1226
        }
 
1227
 
 
1228
        if(cmd & ReadFeatures) {
 
1229
            debug_msg(1, "Processing default_pre: %s", vars["CONFIG"].join("::").toLatin1().constData());
 
1230
            if(doProjectInclude("default_pre", IncludeFlagFeature, base_vars) == IncludeNoExist)
 
1231
                doProjectInclude("default", IncludeFlagFeature, base_vars);
 
1232
        }
 
1233
    }
 
1234
 
 
1235
    vars = base_vars; // start with the base
 
1236
 
 
1237
    //get a default
 
1238
    if(pfile != "-" && vars["TARGET"].isEmpty())
 
1239
        vars["TARGET"].append(QFileInfo(pfile).baseName());
 
1240
 
 
1241
    //before commandline
 
1242
    if(cmd & ReadCmdLine) {
 
1243
        cfile = pfile;
 
1244
        parser.file = "(internal)";
 
1245
        parser.from_file = false;
 
1246
        parser.line_no = 1; //really arg count now.. duh
 
1247
        reset();
 
1248
        for(QStringList::ConstIterator it = Option::before_user_vars.begin();
 
1249
            it != Option::before_user_vars.end(); ++it) {
 
1250
            if(!parse((*it), vars)) {
 
1251
                fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData());
 
1252
                return false;
 
1253
            }
 
1254
            parser.line_no++;
 
1255
        }
 
1256
    }
 
1257
 
 
1258
    //commandline configs
 
1259
    if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) {
 
1260
        parser.file = "(configs)";
 
1261
        parser.from_file = false;
 
1262
        parser.line_no = 1; //really arg count now.. duh
 
1263
        parse("CONFIG += " + Option::user_configs.join(" "), vars);
 
1264
    }
 
1265
 
 
1266
    if(cmd & ReadProFile) { // parse project file
 
1267
        debug_msg(1, "Project file: reading %s", pfile.toLatin1().constData());
 
1268
        if(pfile != "-" && !QFile::exists(pfile) && !pfile.endsWith(".pro"))
 
1269
            pfile += ".pro";
 
1270
        if(!read(pfile, vars))
 
1271
            return false;
 
1272
    }
 
1273
 
 
1274
    if(cmd & ReadPostFiles) { // parse post files
 
1275
        const QStringList l = vars["QMAKE_POST_INCLUDE_FILES"];
 
1276
        for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
 
1277
            if(read((*it), vars)) {
 
1278
                if(vars["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf((*it)) == -1)
 
1279
                    vars["QMAKE_INTERNAL_INCLUDED_FILES"].append((*it));
 
1280
            }
 
1281
        }
 
1282
    }
 
1283
 
 
1284
    if(cmd & ReadCmdLine) {
 
1285
        parser.file = "(internal)";
 
1286
        parser.from_file = false;
 
1287
        parser.line_no = 1; //really arg count now.. duh
 
1288
        reset();
 
1289
        for(QStringList::ConstIterator it = Option::after_user_vars.begin();
 
1290
            it != Option::after_user_vars.end(); ++it) {
 
1291
            if(!parse((*it), vars)) {
 
1292
                fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData());
 
1293
                return false;
 
1294
            }
 
1295
            parser.line_no++;
 
1296
        }
 
1297
    }
 
1298
 
 
1299
    //after configs (set in BUILDS)
 
1300
    if(cmd & ReadConfigs && !Option::after_user_configs.isEmpty()) {
 
1301
        parser.file = "(configs)";
 
1302
        parser.from_file = false;
 
1303
        parser.line_no = 1; //really arg count now.. duh
 
1304
        parse("CONFIG += " + Option::after_user_configs.join(" "), vars);
 
1305
    }
 
1306
 
 
1307
    if(pfile != "-" && vars["TARGET"].isEmpty())
 
1308
        vars["TARGET"].append(QFileInfo(pfile).baseName());
 
1309
 
 
1310
    if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) {
 
1311
        parser.file = "(configs)";
 
1312
        parser.from_file = false;
 
1313
        parser.line_no = 1; //really arg count now.. duh
 
1314
        parse("CONFIG += " + Option::user_configs.join(" "), base_vars);
 
1315
    }
 
1316
 
 
1317
    if(cmd & ReadFeatures) {
 
1318
        debug_msg(1, "Processing default_post: %s", vars["CONFIG"].join("::").toLatin1().constData());
 
1319
        doProjectInclude("default_post", IncludeFlagFeature, vars);
 
1320
 
 
1321
        QHash<QString, bool> processed;
 
1322
        const QStringList &configs = vars["CONFIG"];
 
1323
        debug_msg(1, "Processing CONFIG features: %s", configs.join("::").toLatin1().constData());
 
1324
        while(1) {
 
1325
            bool finished = true;
 
1326
            for(int i = configs.size()-1; i >= 0; --i) {
 
1327
                if(!processed.contains(configs[i])) {
 
1328
                    processed.insert(configs[i], true);
 
1329
                    if(doProjectInclude(configs[i], IncludeFlagFeature, vars) == IncludeSuccess) {
 
1330
                        finished = false;
 
1331
                        break;
 
1332
                    }
 
1333
                }
 
1334
            }
 
1335
            if(finished)
 
1336
                break;
 
1337
        }
 
1338
    }
 
1339
 
 
1340
    // now let the user override the template from an option..
 
1341
    if(!Option::user_template.isEmpty()) {
 
1342
        QString s;
 
1343
        if (!vars["TEMPLATE"].isEmpty())
 
1344
            s = vars["TEMPLATE"].first();
 
1345
        debug_msg(1, "Overriding TEMPLATE (%s) with: %s", s.toLatin1().constData(),
 
1346
                  Option::user_template.toLatin1().constData());
 
1347
        vars["TEMPLATE"].clear();
 
1348
        vars["TEMPLATE"].append(Option::user_template);
 
1349
    }
 
1350
 
 
1351
    QStringList &templ = vars["TEMPLATE"];
 
1352
    if(templ.isEmpty())
 
1353
        templ.append(QString("app"));
 
1354
    else if(vars["TEMPLATE"].first().endsWith(".t"))
 
1355
        templ = QStringList(templ.first().left(templ.first().length() - 2));
 
1356
    if(!Option::user_template_prefix.isEmpty() && !templ.first().startsWith(Option::user_template_prefix))
 
1357
        templ.first().prepend(Option::user_template_prefix);
 
1358
 
 
1359
    QString test_version = QString::fromLocal8Bit(qgetenv("QTESTVERSION"));
 
1360
    if(!test_version.isEmpty()) {
 
1361
        QString s = vars["TARGET"].first();
 
1362
        if(s == "qt" || s == "qt-mt" || s == "qte" || s == "qte-mt") {
 
1363
            QString &ver = vars["VERSION"].first();
 
1364
//            fprintf(stderr,"Current QT version number: " + ver + "\n");
 
1365
            if(!ver.isEmpty() && ver != test_version) {
 
1366
                ver = test_version;
 
1367
                fprintf(stderr,("Changed QT version number to " + test_version + "!\n").toLatin1());
 
1368
            }
 
1369
        }
 
1370
    }
 
1371
    Option::postProcessProject(this);   // let Option post-process
 
1372
    return true;
 
1373
}
 
1374
 
 
1375
bool
 
1376
QMakeProject::isActiveConfig(const QString &x, bool regex, QMap<QString, QStringList> *place)
 
1377
{
 
1378
    if(x.isEmpty())
 
1379
        return true;
 
1380
 
 
1381
    //magic types for easy flipping
 
1382
    if(x == "true")
 
1383
        return true;
 
1384
    else if(x == "false")
 
1385
        return false;
 
1386
 
 
1387
    //mkspecs
 
1388
    if((Option::target_mode == Option::TARG_MACX_MODE || Option::target_mode == Option::TARG_QNX6_MODE ||
 
1389
        Option::target_mode == Option::TARG_UNIX_MODE) && x == "unix")
 
1390
        return true;
 
1391
    else if(Option::target_mode == Option::TARG_MACX_MODE && x == "macx")
 
1392
        return true;
 
1393
    else if(Option::target_mode == Option::TARG_QNX6_MODE && x == "qnx6")
 
1394
        return true;
 
1395
    else if(Option::target_mode == Option::TARG_MAC9_MODE && x == "mac9")
 
1396
        return true;
 
1397
    else if((Option::target_mode == Option::TARG_MAC9_MODE || Option::target_mode == Option::TARG_MACX_MODE) &&
 
1398
            x == "mac")
 
1399
        return true;
 
1400
    else if(Option::target_mode == Option::TARG_WIN_MODE && x == "win32")
 
1401
        return true;
 
1402
    QRegExp re(x, Qt::CaseSensitive, QRegExp::Wildcard);
 
1403
    QString spec = Option::mkfile::qmakespec.right(Option::mkfile::qmakespec.length() -
 
1404
                                                   (Option::mkfile::qmakespec.lastIndexOf(QDir::separator())+1));
 
1405
    if((regex && re.exactMatch(spec)) || (!regex && spec == x))
 
1406
        return true;
 
1407
#ifdef Q_OS_UNIX
 
1408
    else if(spec == "default") {
 
1409
        static char *buffer = NULL;
 
1410
        if(!buffer)
 
1411
            buffer = (char *)malloc(1024);
 
1412
        int l = readlink(Option::mkfile::qmakespec.toLatin1(), buffer, 1024);
 
1413
        if(l != -1) {
 
1414
            buffer[l] = '\0';
 
1415
            QString r = buffer;
 
1416
            if(r.lastIndexOf('/') != -1)
 
1417
                r = r.mid(r.lastIndexOf('/') + 1);
 
1418
            if((regex && re.exactMatch(r)) || (!regex && r == x))
 
1419
                return true;
 
1420
        }
 
1421
    }
 
1422
#endif
 
1423
 
 
1424
    //simple matching
 
1425
    const QStringList &configs = (place ? (*place)["CONFIG"] : vars["CONFIG"]);
 
1426
    for(QStringList::ConstIterator it = configs.begin(); it != configs.end(); ++it) {
 
1427
        if(((regex && re.exactMatch((*it))) || (!regex && (*it) == x)) && re.exactMatch((*it)))
 
1428
            return true;
 
1429
    }
 
1430
    return false;
 
1431
}
 
1432
 
 
1433
bool
 
1434
QMakeProject::doProjectTest(QString str, QMap<QString, QStringList> &place)
 
1435
{
 
1436
    QString chk = remove_quotes(str);
 
1437
    if(chk.isEmpty())
 
1438
        return true;
 
1439
    bool invert_test = (chk.left(1) == "!");
 
1440
    if(invert_test)
 
1441
        chk = chk.right(chk.length() - 1);
 
1442
 
 
1443
    bool test=false;
 
1444
    int lparen = chk.indexOf('(');
 
1445
    if(lparen != -1) { // if there is an lparen in the chk, it IS a function
 
1446
        int rparen = chk.indexOf(')', lparen);
 
1447
        if(rparen == -1) {
 
1448
            QByteArray error;
 
1449
            error.reserve(256);
 
1450
#if defined(_MSC_VER) && _MSC_VER >= 1400
 
1451
            sprintf_s(error.data(), 256, "Function (in REQUIRES) missing right paren: %s",
 
1452
                    chk.toLatin1().constData());
 
1453
#else
 
1454
            sprintf(error.data(), "Function (in REQUIRES) missing right paren: %s",
 
1455
                    chk.toLatin1().constData());
 
1456
#endif
 
1457
            qmake_error_msg(error);
 
1458
        } else {
 
1459
            QString func = chk.left(lparen);
 
1460
            test = doProjectTest(func, chk.mid(lparen+1, rparen - lparen - 1), place);
 
1461
        }
 
1462
    } else {
 
1463
        test = isActiveConfig(chk, true, &place);
 
1464
    }
 
1465
    if(invert_test)
 
1466
        return !test;
 
1467
    return test;
 
1468
}
 
1469
 
 
1470
bool
 
1471
QMakeProject::doProjectTest(QString func, const QString &params,
 
1472
                            QMap<QString, QStringList> &place)
 
1473
{
 
1474
    return doProjectTest(func, split_arg_list(params), place);
 
1475
}
 
1476
 
 
1477
QMakeProject::IncludeStatus
 
1478
QMakeProject::doProjectInclude(QString file, uchar flags, QMap<QString, QStringList> &place)
 
1479
{
 
1480
    if(flags & IncludeFlagFeature) {
 
1481
        if(!file.endsWith(Option::prf_ext))
 
1482
            file += Option::prf_ext;
 
1483
        if(file.indexOf(Option::dir_sep) == -1 || !QFile::exists(file)) {
 
1484
            bool found = false;
 
1485
            static QStringList *feature_roots = 0;
 
1486
            if(!feature_roots)
 
1487
                feature_roots = new QStringList(qmake_feature_paths(prop));
 
1488
            debug_msg(2, "Looking for feature '%s' in (%s)", file.toLatin1().constData(),
 
1489
                        feature_roots->join("::").toLatin1().constData());
 
1490
            int start_root = 0;
 
1491
            if(parser.from_file) {
 
1492
                QFileInfo currFile(parser.file), prfFile(file);
 
1493
                if(currFile.fileName() == prfFile.fileName()) {
 
1494
                    currFile = QFileInfo(currFile.canonicalFilePath());
 
1495
                    for(int root = 0; root < feature_roots->size(); ++root) {
 
1496
                        prfFile = QFileInfo(feature_roots->at(root) +
 
1497
                                            QDir::separator() + file).canonicalFilePath();
 
1498
                        if(prfFile == currFile) {
 
1499
                            start_root = root+1;
 
1500
                            break;
 
1501
                        }
 
1502
                    }
 
1503
                }
 
1504
            }
 
1505
            for(int root = start_root; root < feature_roots->size(); ++root) {
 
1506
                QString prf(feature_roots->at(root) + QDir::separator() + file);
 
1507
                if(QFile::exists(prf)) {
 
1508
                    found = true;
 
1509
                    file = prf;
 
1510
                    break;
 
1511
                }
 
1512
            }
 
1513
            if(!found)
 
1514
                return IncludeNoExist;
 
1515
            if(place["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file) != -1)
 
1516
                return IncludeFeatureAlreadyLoaded;
 
1517
            place["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file);
 
1518
        }
 
1519
    }
 
1520
    if(QDir::isRelativePath(file)) {
 
1521
        QStringList include_roots;
 
1522
        include_roots << Option::output_dir;
 
1523
        if(Option::output_dir != qmake_getpwd())
 
1524
            include_roots << qmake_getpwd();
 
1525
        for(int root = 0; root < include_roots.size(); ++root) {
 
1526
                QString testName = QDir::convertSeparators(include_roots[root]);
 
1527
                if (!testName.endsWith(QString(QDir::separator())))
 
1528
                        testName += QDir::separator();
 
1529
                testName += file;
 
1530
 
 
1531
            if(QFile::exists(testName)) {
 
1532
                file = testName;
 
1533
                break;
 
1534
            }
 
1535
        }
 
1536
    }
 
1537
    if(!QFile::exists(file))
 
1538
        return IncludeNoExist;
 
1539
 
 
1540
    if(Option::mkfile::do_preprocess) //nice to see this first..
 
1541
        fprintf(stderr, "#switching file %s(%s) - %s:%d\n", (flags & IncludeFlagFeature) ? "load" : "include",
 
1542
                file.toLatin1().constData(),
 
1543
                parser.file.toLatin1().constData(), parser.line_no);
 
1544
    debug_msg(1, "Project Parser: %s'ing file %s.", (flags & IncludeFlagFeature) ? "load" : "include",
 
1545
              file.toLatin1().constData());
 
1546
    QString orig_file = file;
 
1547
    int di = file.lastIndexOf(QDir::separator());
 
1548
    QString oldpwd = qmake_getpwd();
 
1549
    if(di != -1) {
 
1550
        if(!qmake_setpwd(file.left(file.lastIndexOf(QDir::separator())))) {
 
1551
            fprintf(stderr, "Cannot find directory: %s\n", file.left(di).toLatin1().constData());
 
1552
            return IncludeFailure;
 
1553
        }
 
1554
        file = file.right(file.length() - di - 1);
 
1555
    }
 
1556
    parser_info pi = parser;
 
1557
    QStack<ScopeBlock> sc = scope_blocks;
 
1558
    IteratorBlock *it = iterator;
 
1559
    FunctionBlock *fu = function;
 
1560
    bool parsed = false;
 
1561
    if(flags & IncludeFlagNewProject) {
 
1562
        QMakeProject proj(place);
 
1563
        if(proj.doProjectInclude("default_pre", IncludeFlagFeature, place) == IncludeNoExist)
 
1564
            proj.doProjectInclude("default", IncludeFlagFeature, place);
 
1565
        parsed = proj.read(file, place);
 
1566
    } else {
 
1567
        parsed = read(file, place);
 
1568
    }
 
1569
    if(parsed) {
 
1570
        if(place["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file) == -1)
 
1571
            place["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file);
 
1572
    } else {
 
1573
        warn_msg(WarnParser, "%s:%d: Failure to include file %s.",
 
1574
                 pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData());
 
1575
    }
 
1576
    iterator = it;
 
1577
    function = fu;
 
1578
    parser = pi;
 
1579
    scope_blocks = sc;
 
1580
    qmake_setpwd(oldpwd);
 
1581
    if(!parsed)
 
1582
        return IncludeParseFailure;
 
1583
    return IncludeSuccess;
 
1584
}
 
1585
 
 
1586
QString
 
1587
QMakeProject::doProjectExpand(QString func, const QString &params,
 
1588
                              QMap<QString, QStringList> &place)
 
1589
{
 
1590
    return doProjectExpand(func, split_arg_list(params), place);
 
1591
}
 
1592
 
 
1593
QString
 
1594
QMakeProject::doProjectExpand(QString func, QStringList args,
 
1595
                              QMap<QString, QStringList> &place)
 
1596
{
 
1597
    func = func.trimmed();
 
1598
    for(QStringList::Iterator arit = args.begin(); arit != args.end(); ++arit)
 
1599
        doVariableReplace((*arit), place);
 
1600
 
 
1601
    enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
 
1602
                      E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
 
1603
                      E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_UPPER, E_LOWER, E_FILES,
 
1604
                      E_PROMPT, E_RE_ESCAPE };
 
1605
    static QMap<QString, int> *expands = 0;
 
1606
    if(!expands) {
 
1607
        expands = new QMap<QString, int>;
 
1608
        expands->insert("member", E_MEMBER);
 
1609
        expands->insert("first", E_FIRST);
 
1610
        expands->insert("last", E_LAST);
 
1611
        expands->insert("cat", E_CAT);
 
1612
        expands->insert("fromfile", E_FROMFILE);
 
1613
        expands->insert("eval", E_EVAL);
 
1614
        expands->insert("list", E_LIST);
 
1615
        expands->insert("sprintf", E_SPRINTF);
 
1616
        expands->insert("join", E_JOIN);
 
1617
        expands->insert("split", E_SPLIT);
 
1618
        expands->insert("basename", E_BASENAME);
 
1619
        expands->insert("dirname", E_DIRNAME);
 
1620
        expands->insert("section", E_SECTION);
 
1621
        expands->insert("find", E_FIND);
 
1622
        expands->insert("system", E_SYSTEM);
 
1623
        expands->insert("unique", E_UNIQUE);
 
1624
        expands->insert("quote", E_QUOTE);
 
1625
        expands->insert("upper", E_UPPER);
 
1626
        expands->insert("lower", E_LOWER);
 
1627
        expands->insert("re_escape", E_RE_ESCAPE);
 
1628
        expands->insert("files", E_FILES);
 
1629
        expands->insert("prompt", E_PROMPT);
 
1630
    }
 
1631
    ExpandFunc func_t = (ExpandFunc)expands->value(func.toLower());
 
1632
    debug_msg(1, "Running project expand: %s(%s) [%d]",
 
1633
              func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
 
1634
 
 
1635
    QString ret;
 
1636
    switch(func_t) {
 
1637
    case E_MEMBER: {
 
1638
        if(args.count() < 1 || args.count() > 3) {
 
1639
            fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n",
 
1640
                    parser.file.toLatin1().constData(), parser.line_no);
 
1641
        } else {
 
1642
            bool ok = true;
 
1643
            const QStringList &var = place[varMap(args.first())];
 
1644
            int start = 0, end = 0;
 
1645
            if(args.count() >= 2) {
 
1646
                QString start_str = args[1];
 
1647
                start = start_str.toInt(&ok);
 
1648
                if(!ok) {
 
1649
                    if(args.count() == 2) {
 
1650
                        int dotdot = start_str.indexOf("..");
 
1651
                        if(dotdot != -1) {
 
1652
                            start = start_str.left(dotdot).toInt(&ok);
 
1653
                            if(ok)
 
1654
                                end = start_str.mid(dotdot+2).toInt(&ok);
 
1655
                        }
 
1656
                    }
 
1657
                    if(!ok)
 
1658
                        fprintf(stderr, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
 
1659
                                parser.file.toLatin1().constData(), parser.line_no,
 
1660
                                start_str.toLatin1().constData());
 
1661
                } else {
 
1662
                    end = start;
 
1663
                    if(args.count() == 3)
 
1664
                        end = args[2].toInt(&ok);
 
1665
                    if(!ok)
 
1666
                        fprintf(stderr, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
 
1667
                                parser.file.toLatin1().constData(), parser.line_no,
 
1668
                                args[2].toLatin1().constData());
 
1669
                }
 
1670
            }
 
1671
            if(ok) {
 
1672
                if(start < 0)
 
1673
                    start += var.count();
 
1674
                if(end < 0)
 
1675
                    end += var.count();
 
1676
                if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
 
1677
                    //nothing
 
1678
                } else if(start < end) {
 
1679
                    for(int i = start; i <= end && (int)var.count() >= i; i++) {
 
1680
                        if(!ret.isEmpty())
 
1681
                            ret += Option::field_sep;
 
1682
                        ret += var[i];
 
1683
                    }
 
1684
                } else {
 
1685
                    for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--) {
 
1686
                        if(!ret.isEmpty())
 
1687
                            ret += Option::field_sep;
 
1688
                        ret += var[i];
 
1689
                    }
 
1690
                }
 
1691
            }
 
1692
        }
 
1693
        break; }
 
1694
    case E_FIRST:
 
1695
    case E_LAST: {
 
1696
            if(args.count() != 1) {
 
1697
            fprintf(stderr, "%s:%d: %s(var) requires one argument.\n",
 
1698
                    parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
 
1699
        } else {
 
1700
            const QStringList &var = place[varMap(args.first())];
 
1701
            if(!var.isEmpty()) {
 
1702
                if(func_t == E_FIRST)
 
1703
                    ret = var[0];
 
1704
                else
 
1705
                    ret = var[var.size()-1];
 
1706
            }
 
1707
        }
 
1708
        break; }
 
1709
    case E_CAT: {
 
1710
        if(args.count() < 1 || args.count() > 2) {
 
1711
            fprintf(stderr, "%s:%d: cat(file) requires one argument.\n",
 
1712
                    parser.file.toLatin1().constData(), parser.line_no);
 
1713
        } else {
 
1714
            QString file = args[0];
 
1715
            file = Option::fixPathToLocalOS(file);
 
1716
 
 
1717
            bool singleLine = true;
 
1718
            if(args.count() > 1)
 
1719
                singleLine = (args[1].toLower() == "true");
 
1720
 
 
1721
            QFile qfile(file);
 
1722
            if(qfile.open(QIODevice::ReadOnly)) {
 
1723
                QTextStream stream(&qfile);
 
1724
                while(!stream.atEnd()) {
 
1725
                    ret += stream.readLine().trimmed();
 
1726
                    if(!singleLine)
 
1727
                        ret += "\n";
 
1728
                }
 
1729
                qfile.close();
 
1730
            }
 
1731
        }
 
1732
        break; }
 
1733
    case E_FROMFILE: {
 
1734
        if(args.count() != 2) {
 
1735
            fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n",
 
1736
                    parser.file.toLatin1().constData(), parser.line_no);
 
1737
        } else {
 
1738
            QString file = args[0], seek_var = args[1];
 
1739
            file = Option::fixPathToLocalOS(file);
 
1740
 
 
1741
            QMap<QString, QStringList> tmp;
 
1742
            if(doProjectInclude(file, IncludeFlagNewProject, tmp) == IncludeSuccess) {
 
1743
                if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES"))
 
1744
                    place["QMAKE_INTERNAL_INCLUDED_FILES"] += tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
 
1745
                ret = tmp[seek_var].join(QString(Option::field_sep));
 
1746
            }
 
1747
        }
 
1748
        break; }
 
1749
    case E_EVAL: {
 
1750
        for(QStringList::ConstIterator arg_it = args.begin();
 
1751
            arg_it != args.end(); ++arg_it) {
 
1752
            if(!ret.isEmpty())
 
1753
                ret += Option::field_sep;
 
1754
            ret += place[(*arg_it)].join(QString(Option::field_sep));
 
1755
        }
 
1756
        break; }
 
1757
    case E_LIST: {
 
1758
        static int x = 0;
 
1759
        ret.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++);
 
1760
        QStringList &lst = (*((QMap<QString, QStringList>*)&place))[ret];
 
1761
        lst.clear();
 
1762
        for(QStringList::ConstIterator arg_it = args.begin();
 
1763
            arg_it != args.end(); ++arg_it)
 
1764
            lst += split_value_list((*arg_it));
 
1765
        break; }
 
1766
    case E_SPRINTF: {
 
1767
        if(args.count() < 1) {
 
1768
            fprintf(stderr, "%s:%d: sprintf(format, ...) requires one argument.\n",
 
1769
                    parser.file.toLatin1().constData(), parser.line_no);
 
1770
        } else {
 
1771
            ret = args.at(0);
 
1772
            QStringList::ConstIterator arg_it = args.begin();
 
1773
            ++arg_it;
 
1774
            for(int i = 1; i < args.count(); ++i)
 
1775
                ret = ret.arg(args.at(i));
 
1776
        }
 
1777
        break; }
 
1778
    case E_JOIN: {
 
1779
        if(args.count() < 1 || args.count() > 4) {
 
1780
            fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four"
 
1781
                    "arguments.\n", parser.file.toLatin1().constData(), parser.line_no);
 
1782
        } else {
 
1783
            QString glue, before, after;
 
1784
            if(args.count() >= 2)
 
1785
                glue = args[1];
 
1786
            if(args.count() >= 3)
 
1787
                before = args[2];
 
1788
            if(args.count() == 4)
 
1789
                after = args[3];
 
1790
            const QStringList &var = place[varMap(args.first())];
 
1791
            if(!var.isEmpty())
 
1792
                ret = before + var.join(glue) + after;
 
1793
        }
 
1794
        break; }
 
1795
    case E_SPLIT: {
 
1796
        if(args.count() < 2 || args.count() > 3) {
 
1797
            fprintf(stderr, "%s:%d split(var, sep, join) requires three arguments\n",
 
1798
                    parser.file.toLatin1().constData(), parser.line_no);
 
1799
        } else {
 
1800
            QString sep = args[1], join = QString(Option::field_sep);
 
1801
            if(args.count() == 3)
 
1802
                join = args[2];
 
1803
            QStringList var = place[varMap(args.first())];
 
1804
            for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) {
 
1805
                QStringList lst = (*vit).split(sep);
 
1806
                for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit) {
 
1807
                    if(!ret.isEmpty())
 
1808
                        ret += join;
 
1809
                    ret += (*spltit);
 
1810
                }
 
1811
            }
 
1812
        }
 
1813
        break; }
 
1814
    case E_BASENAME:
 
1815
    case E_DIRNAME:
 
1816
    case E_SECTION: {
 
1817
        QString sep, var;
 
1818
        int beg=0, end=-1;;
 
1819
        if(func_t == E_SECTION) {
 
1820
            if(args.count() != 3 && args.count() != 4) {
 
1821
                fprintf(stderr, "%s:%d section(var, sep, begin, end) requires three argument\n",
 
1822
                        parser.file.toLatin1().constData(), parser.line_no);
 
1823
            } else {
 
1824
                var = args[0];
 
1825
                sep = args[1];
 
1826
                beg = args[2].toInt();
 
1827
                if(args.count() == 4)
 
1828
                    end = args[3].toInt();
 
1829
            }
 
1830
        } else {
 
1831
            if(args.count() != 1) {
 
1832
                fprintf(stderr, "%s:%d %s(var) requires one argument.\n",
 
1833
                        parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
 
1834
            } else {
 
1835
                var = args[0];
 
1836
                sep = Option::dir_sep;
 
1837
                if(func_t == E_DIRNAME)
 
1838
                    end = -2;
 
1839
                else
 
1840
                    beg = -1;
 
1841
            }
 
1842
        }
 
1843
        if(!var.isNull()) {
 
1844
            const QStringList &l = place[varMap(var)];
 
1845
            for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
 
1846
                if(!ret.isEmpty())
 
1847
                    ret += Option::field_sep;
 
1848
                ret += (*it).section(sep, beg, end);
 
1849
            }
 
1850
        }
 
1851
        break; }
 
1852
    case E_FIND: {
 
1853
        if(args.count() != 2) {
 
1854
            fprintf(stderr, "%s:%d find(var, str) requires two arguments\n",
 
1855
                    parser.file.toLatin1().constData(), parser.line_no);
 
1856
        } else {
 
1857
            QRegExp regx(args[1]);
 
1858
            const QStringList &var = place[varMap(args.first())];
 
1859
            for(QStringList::ConstIterator vit = var.begin();
 
1860
                vit != var.end(); ++vit) {
 
1861
                if(regx.indexIn(*vit) != -1) {
 
1862
                    if(!ret.isEmpty())
 
1863
                        ret += Option::field_sep;
 
1864
                    ret += (*vit);
 
1865
                }
 
1866
            }
 
1867
        }
 
1868
        break;  }
 
1869
    case E_SYSTEM: {
 
1870
        if(args.count() < 1 || args.count() > 2) {
 
1871
            fprintf(stderr, "%s:%d system(execut) requires one argument.\n",
 
1872
                    parser.file.toLatin1().constData(), parser.line_no);
 
1873
        } else {
 
1874
#ifdef Q_OS_UNIX
 
1875
            for(QMap<QString, QStringList>::ConstIterator it = place.begin();
 
1876
                it != place.end(); ++it) {
 
1877
                if(!it.key().startsWith("."))
 
1878
                    putenv(const_cast<char*>(QString(Option::sysenv_mod + it.key() + '=' + it.value().join(" ")).toAscii().constData()));
 
1879
            }
 
1880
#endif
 
1881
            char buff[256];
 
1882
            FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
 
1883
            bool singleLine = true;
 
1884
            if(args.count() > 1)
 
1885
                singleLine = (args[1].toLower() == "true");
 
1886
            while(proc && !feof(proc)) {
 
1887
                int read_in = fread(buff, 1, 255, proc);
 
1888
                if(!read_in)
 
1889
                    break;
 
1890
                for(int i = 0; i < read_in; i++) {
 
1891
                    if((singleLine && buff[i] == '\n') || buff[i] == '\t')
 
1892
                        buff[i] = ' ';
 
1893
                }
 
1894
                buff[read_in] = '\0';
 
1895
                ret += buff;
 
1896
            }
 
1897
#ifdef Q_OS_UNIX
 
1898
            for(QMap<QString, QStringList>::ConstIterator it = place.begin();
 
1899
                it != place.end(); ++it) {
 
1900
                if(!it.key().startsWith("."))
 
1901
                    putenv(const_cast<char*>(QString(Option::sysenv_mod
 
1902
                                                     + it.key()).toAscii().constData()));
 
1903
            }
 
1904
#endif
 
1905
        }
 
1906
        break; }
 
1907
    case E_UNIQUE: {
 
1908
        if(args.count() != 1) {
 
1909
            fprintf(stderr, "%s:%d unique(var) requires one argument.\n",
 
1910
                    parser.file.toLatin1().constData(), parser.line_no);
 
1911
        } else {
 
1912
            QStringList uniq;
 
1913
            const QStringList &var = place[varMap(args.first())];
 
1914
            for(int i = 0; i < var.count(); i++) {
 
1915
                if(!uniq.contains(var[i]))
 
1916
                    uniq.append(var[i]);
 
1917
            }
 
1918
            ret = uniq.join(" ");
 
1919
        }
 
1920
        break; }
 
1921
    case E_QUOTE: {
 
1922
        ret = args.join(" ");
 
1923
        ret = ret.replace("\\n", "\n");
 
1924
        ret = ret.replace("\\t", "\t");
 
1925
        ret = ret.replace("\\r", "\r");
 
1926
        break; }
 
1927
    case E_RE_ESCAPE: {
 
1928
        ret = QRegExp::escape(args.join(QString(Option::field_sep)));
 
1929
        break; }
 
1930
    case E_UPPER:
 
1931
    case E_LOWER: {
 
1932
        ret = args.join(QString(Option::field_sep));
 
1933
        if(func_t == E_UPPER)
 
1934
            ret = ret.toUpper();
 
1935
        else
 
1936
            ret = ret.toLower();
 
1937
        break; }
 
1938
    case E_FILES: {
 
1939
        if(args.count() != 1 && args.count() != 2) {
 
1940
            fprintf(stderr, "%s:%d files(pattern) requires one argument.\n",
 
1941
                    parser.file.toLatin1().constData(), parser.line_no);
 
1942
        } else {
 
1943
            bool recursive = false;
 
1944
            if(args.count() == 2)
 
1945
                recursive = (args[1].toLower() == "true" || args[1].toInt());
 
1946
            QStringList dirs;
 
1947
            QString r = Option::fixPathToLocalOS(args[0]);
 
1948
            int slash = r.lastIndexOf(QDir::separator());
 
1949
            if(slash != -1) {
 
1950
                dirs.append(r.left(slash));
 
1951
                r = r.mid(slash+1);
 
1952
            } else {
 
1953
                dirs.append(qmake_getpwd());
 
1954
            }
 
1955
 
 
1956
            const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
 
1957
            for(int d = 0; d < dirs.count(); d++) {
 
1958
                QString dir = dirs[d];
 
1959
                if(!dir.endsWith(Option::dir_sep))
 
1960
                    dir += "/";
 
1961
                QDir qdir(dir);
 
1962
                for(int i = 0; i < (int)qdir.count(); ++i) {
 
1963
                    if(qdir[i] == "." || qdir[i] == "..")
 
1964
                        continue;
 
1965
                    QString fname = dir + qdir[i];
 
1966
                    if(QFileInfo(fname).isDir()) {
 
1967
                        if(recursive)
 
1968
                            dirs.append(fname);
 
1969
                    }
 
1970
                    if(regex.exactMatch(fname)) {
 
1971
                        if(!ret.isEmpty())
 
1972
                            ret += Option::field_sep;
 
1973
                        ret += fname;
 
1974
                    }
 
1975
                }
 
1976
            }
 
1977
        }
 
1978
        break; }
 
1979
    case E_PROMPT: {
 
1980
        if(args.count() != 1) {
 
1981
            fprintf(stderr, "%s:%d prompt(question) requires one argument.\n",
 
1982
                    parser.file.toLatin1().constData(), parser.line_no);
 
1983
        } else if(projectFile() == "-") {
 
1984
            fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
 
1985
                    parser.file.toLatin1().constData(), parser.line_no);
 
1986
        } else {
 
1987
            QString msg = fixEnvVariables(args.first());
 
1988
            if(!msg.endsWith("?"))
 
1989
                msg += "?";
 
1990
            fprintf(stderr, "Project %s: %s ", func.toUpper().toLatin1().constData(),
 
1991
                    msg.toLatin1().constData());
 
1992
 
 
1993
            QFile qfile;
 
1994
            if(qfile.open(stdin, QIODevice::ReadOnly)) {
 
1995
                QTextStream t(&qfile);
 
1996
                ret = t.readLine();
 
1997
            }
 
1998
        }
 
1999
        break;
 
2000
    }
 
2001
    default: {
 
2002
        if(replaceFunctions.contains(func)) {
 
2003
            FunctionBlock *defined = replaceFunctions[func];
 
2004
            function_blocks.push(defined);
 
2005
            defined->exec(args, this, place, ret);
 
2006
            Q_ASSERT(function_blocks.pop() == defined);
 
2007
        } else {
 
2008
            fprintf(stderr, "%s:%d: Unknown replace function: %s\n",
 
2009
                    parser.file.toLatin1().constData(), parser.line_no,
 
2010
                    func.toLatin1().constData());
 
2011
        }
 
2012
        break; }
 
2013
    }
 
2014
    return ret;
 
2015
}
 
2016
 
 
2017
bool
 
2018
QMakeProject::doProjectTest(QString func, QStringList args, QMap<QString, QStringList> &place)
 
2019
{
 
2020
    func = func.trimmed();
 
2021
    for(QStringList::Iterator arit = args.begin(); arit != args.end(); ++arit) {
 
2022
        (*arit) = (*arit).trimmed(); // blah, get rid of space
 
2023
        doVariableReplace((*arit), place);
 
2024
    }
 
2025
    debug_msg(1, "Running project test: %s(%s)", func.toLatin1().constData(), args.join("::").toLatin1().constData());
 
2026
 
 
2027
    if(func == "requires") {
 
2028
        return doProjectCheckReqs(args, place);
 
2029
    } else if(func == "greaterThan" || func == "lessThan") {
 
2030
        if(args.count() != 2) {
 
2031
            fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
 
2032
                    parser.line_no, func.toLatin1().constData());
 
2033
            return false;
 
2034
        }
 
2035
        QString rhs(args[1]), lhs(place[args[0]].join(QString(Option::field_sep)));
 
2036
        bool ok;
 
2037
        int rhs_int = rhs.toInt(&ok);
 
2038
        if(ok) { // do integer compare
 
2039
            int lhs_int = lhs.toInt(&ok);
 
2040
            if(ok) {
 
2041
                if(func == "greaterThan")
 
2042
                    return lhs_int > rhs_int;
 
2043
                return lhs_int < rhs_int;
 
2044
            }
 
2045
        }
 
2046
        if(func == "greaterThan")
 
2047
            return lhs > rhs;
 
2048
        return lhs < rhs;
 
2049
    } else if(func == "equals" || func == "isEqual") {
 
2050
        if(args.count() != 2) {
 
2051
            fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
 
2052
                    parser.line_no, func.toLatin1().constData());
 
2053
            return false;
 
2054
        }
 
2055
        return place[args[0]].join(QString(Option::field_sep)) == args[1];
 
2056
    } else if(func == "exists") {
 
2057
        if(args.count() != 1) {
 
2058
            fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.toLatin1().constData(),
 
2059
                    parser.line_no);
 
2060
            return false;
 
2061
        }
 
2062
        QString file = args.first();
 
2063
        file = Option::fixPathToLocalOS(file);
 
2064
 
 
2065
        if(QFile::exists(file))
 
2066
            return true;
 
2067
        //regular expression I guess
 
2068
        QString dirstr = qmake_getpwd();
 
2069
        int slsh = file.lastIndexOf(Option::dir_sep);
 
2070
        if(slsh != -1) {
 
2071
            dirstr = file.left(slsh+1);
 
2072
            file = file.right(file.length() - slsh - 1);
 
2073
        }
 
2074
        return QDir(dirstr).entryList(QStringList(file)).count();
 
2075
    } else if(func == "export") {
 
2076
        if(args.count() != 1) {
 
2077
            fprintf(stderr, "%s:%d: export(variable) requires one argument.\n", parser.file.toLatin1().constData(),
 
2078
                    parser.line_no);
 
2079
            return false;
 
2080
        }
 
2081
        for(int i = 0; i < function_blocks.size(); ++i) {
 
2082
            FunctionBlock *f = function_blocks.at(i);
 
2083
            f->vars[args[0]] = place[args[0]];
 
2084
            if(!i && f->calling_place)
 
2085
                (*f->calling_place)[args[0]] = place[args[0]];
 
2086
        }
 
2087
        return true;
 
2088
    } else if(func == "clear") {
 
2089
        if(args.count() != 1) {
 
2090
            fprintf(stderr, "%s:%d: clear(variable) requires one argument.\n", parser.file.toLatin1().constData(),
 
2091
                    parser.line_no);
 
2092
            return false;
 
2093
        }
 
2094
        if(!place.contains(args[0]))
 
2095
            return false;
 
2096
        place[args[0]].clear();
 
2097
        return true;
 
2098
    } else if(func == "unset") {
 
2099
        if(args.count() != 1) {
 
2100
            fprintf(stderr, "%s:%d: unset(variable) requires one argument.\n", parser.file.toLatin1().constData(),
 
2101
                    parser.line_no);
 
2102
            return false;
 
2103
        }
 
2104
        if(!place.contains(args[0]))
 
2105
            return false;
 
2106
        place.remove(args[0]);
 
2107
        return true;
 
2108
    } else if(func == "eval") {
 
2109
        if(args.count() < 1 && 0) {
 
2110
            fprintf(stderr, "%s:%d: eval(project) requires one argument.\n", parser.file.toLatin1().constData(),
 
2111
                    parser.line_no);
 
2112
            return false;
 
2113
        }
 
2114
        QString project = args.join(" ");
 
2115
        parser_info pi = parser;
 
2116
        parser.from_file = false;
 
2117
        parser.file = "(eval)";
 
2118
        parser.line_no = 0;
 
2119
        QTextStream t(&project, QIODevice::ReadOnly);
 
2120
        bool ret = read(t, place);
 
2121
        parser = pi;
 
2122
        return ret;
 
2123
    } else if(func == "CONFIG") {
 
2124
        if(args.count() < 1 || args.count() > 2) {
 
2125
            fprintf(stderr, "%s:%d: CONFIG(config) requires one argument.\n", parser.file.toLatin1().constData(),
 
2126
                    parser.line_no);
 
2127
            return false;
 
2128
        }
 
2129
        if(args.count() == 1)
 
2130
            return isActiveConfig(args[0]);
 
2131
        const QStringList mutuals = args[1].split('|');
 
2132
        const QStringList &configs = place["CONFIG"];
 
2133
        for(int i = configs.size()-1; i >= 0; i--) {
 
2134
            for(int mut = 0; mut < mutuals.count(); mut++) {
 
2135
                if(configs[i] == mutuals[mut].trimmed())
 
2136
                    return (configs[i] == args[0]);
 
2137
            }
 
2138
        }
 
2139
        return false;
 
2140
    } else if(func == "system") {
 
2141
        if(args.count() != 1) {
 
2142
            fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.toLatin1().constData(),
 
2143
                    parser.line_no);
 
2144
            return false;
 
2145
        }
 
2146
#ifdef Q_OS_UNIX
 
2147
        for(QMap<QString, QStringList>::ConstIterator it = place.begin();
 
2148
            it != place.end(); ++it) {
 
2149
            if(!it.key().startsWith("."))
 
2150
                putenv((Option::sysenv_mod + it.key() + '=' + it.value().join(" ")).toAscii().data());
 
2151
        }
 
2152
#endif
 
2153
        bool ret = system(args.first().toLatin1().constData()) == 0;
 
2154
#ifdef Q_OS_UNIX
 
2155
        for(QMap<QString, QStringList>::ConstIterator it = place.begin();
 
2156
            it != place.end(); ++it) {
 
2157
            if(!it.key().startsWith("."))
 
2158
                putenv((Option::sysenv_mod + it.key()).toAscii().data());
 
2159
        }
 
2160
#endif
 
2161
        return ret;
 
2162
    } else if(func == "return") {
 
2163
        if(function_blocks.isEmpty()) {
 
2164
            fprintf(stderr, "%s:%d unexpected return()\n",
 
2165
                    parser.file.toLatin1().constData(), parser.line_no);
 
2166
        } else {
 
2167
            FunctionBlock *f = function_blocks.top();
 
2168
            f->cause_return = true;
 
2169
            if(args.count() >= 1)
 
2170
                f->return_value = args[0];
 
2171
        }
 
2172
        return true;
 
2173
    } else if(func == "break") {
 
2174
        if(iterator)
 
2175
            iterator->cause_break = true;
 
2176
        else if(!scope_blocks.isEmpty())
 
2177
            scope_blocks.top().ignore = true;
 
2178
        else
 
2179
            fprintf(stderr, "%s:%d unexpected break()\n",
 
2180
                    parser.file.toLatin1().constData(), parser.line_no);
 
2181
        return true;
 
2182
    } else if(func == "next") {
 
2183
        if(iterator)
 
2184
            iterator->cause_next = true;
 
2185
        else
 
2186
            fprintf(stderr, "%s:%d unexpected next()\n",
 
2187
                    parser.file.toLatin1().constData(), parser.line_no);
 
2188
        return true;
 
2189
    } else if(func == "defined") {
 
2190
        if(args.count() < 1 || args.count() > 2) {
 
2191
            fprintf(stderr, "%s:%d: defined(function) requires one argument.\n",
 
2192
                    parser.file.toLatin1().constData(), parser.line_no);
 
2193
        } else {
 
2194
           if(args.count() > 1) {
 
2195
               if(args[1] == "test")
 
2196
                   return testFunctions.contains(args[0]);
 
2197
               else if(args[1] == "replace")
 
2198
                   return replaceFunctions.contains(args[0]);
 
2199
               fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n",
 
2200
                       parser.file.toLatin1().constData(), parser.line_no,
 
2201
                       args[1].toLatin1().constData());
 
2202
            } else {
 
2203
                if(replaceFunctions.contains(args[0]) || testFunctions.contains(args[0]))
 
2204
                    return true;
 
2205
            }
 
2206
        }
 
2207
        return false;
 
2208
    } else if(func == "contains") {
 
2209
        if(args.count() != 2) {
 
2210
            fprintf(stderr, "%s:%d: contains(var, val) requires two arguments.\n", parser.file.toLatin1().constData(),
 
2211
                    parser.line_no);
 
2212
            return false;
 
2213
        }
 
2214
        QRegExp regx(args[1]);
 
2215
        const QStringList &l = place[args[0]];
 
2216
        for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
 
2217
            if(regx.exactMatch((*it)) || (*it) == args[1])
 
2218
                return true;
 
2219
        }
 
2220
        return false;
 
2221
    } else if(func == "infile") {
 
2222
        if(args.count() < 2 || args.count() > 3) {
 
2223
            fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
 
2224
                    parser.file.toLatin1().constData(), parser.line_no);
 
2225
            return false;
 
2226
        }
 
2227
 
 
2228
        bool ret = false;
 
2229
        QMap<QString, QStringList> tmp;
 
2230
        if(doProjectInclude(Option::fixPathToLocalOS(args[0]), IncludeFlagNewProject, tmp) == IncludeSuccess) {
 
2231
            if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES"))
 
2232
                place["QMAKE_INTERNAL_INCLUDED_FILES"] += tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
 
2233
            if(args.count() == 2) {
 
2234
                ret = tmp.contains(args[1]);
 
2235
            } else {
 
2236
                QRegExp regx(args[2]);
 
2237
                const QStringList &l = tmp[args[1]];
 
2238
                for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
 
2239
                    if(regx.exactMatch((*it)) || (*it) == args[2]) {
 
2240
                        ret = true;
 
2241
                        break;
 
2242
                    }
 
2243
                }
 
2244
            }
 
2245
        }
 
2246
        return ret;
 
2247
    } else if(func == "count") {
 
2248
        if(args.count() != 2 && args.count() != 3) {
 
2249
            fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.toLatin1().constData(),
 
2250
                    parser.line_no);
 
2251
            return false;
 
2252
        }
 
2253
        if(args.count() == 3) {
 
2254
            QString comp = args[2];
 
2255
            if(comp == ">" || comp == "greaterThan")
 
2256
                return place[args[0]].count() > args[1].toInt();
 
2257
            if(comp == ">=")
 
2258
                return place[args[0]].count() >= args[1].toInt();
 
2259
            if(comp == "<" || comp == "lessThan")
 
2260
                return place[args[0]].count() < args[1].toInt();
 
2261
            if(comp == "<=")
 
2262
                return place[args[0]].count() <= args[1].toInt();
 
2263
            if(comp == "equals" || comp == "isEqual" || comp == "=" || comp == "==")
 
2264
                return place[args[0]].count() == args[1].toInt();
 
2265
            fprintf(stderr, "%s:%d: unexpected modifier to count(%s)\n", parser.file.toLatin1().constData(),
 
2266
                    parser.line_no, comp.toLatin1().constData());
 
2267
            return false;
 
2268
        }
 
2269
        return place[args[0]].count() == args[1].toInt();
 
2270
    } else if(func == "isEmpty") {
 
2271
        if(args.count() != 1) {
 
2272
            fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.toLatin1().constData(),
 
2273
                    parser.line_no);
 
2274
            return false;
 
2275
        }
 
2276
        return place[args[0]].isEmpty();
 
2277
    } else if(func == "include" || func == "load") {
 
2278
        QString seek_var;
 
2279
        const bool include_statement = (func == "include");
 
2280
        bool ignore_error = include_statement;
 
2281
        if(args.count() == 2) {
 
2282
            if(func == "include") {
 
2283
                seek_var = args[1];
 
2284
            } else {
 
2285
                QString sarg = args[1];
 
2286
                ignore_error = (sarg.toLower() == "true" || sarg.toInt());
 
2287
            }
 
2288
        } else if(args.count() != 1) {
 
2289
            QString func_desc = "load(feature)";
 
2290
            if(include_statement)
 
2291
                func_desc = "include(file)";
 
2292
            fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.toLatin1().constData(),
 
2293
                    parser.line_no, func_desc.toLatin1().constData());
 
2294
            return false;
 
2295
        }
 
2296
        QString file = args.first();
 
2297
        file = Option::fixPathToLocalOS(file);
 
2298
        uchar flags = IncludeFlagNone;
 
2299
        if(!include_statement)
 
2300
            flags |= IncludeFlagFeature;
 
2301
        IncludeStatus stat = doProjectInclude(file, flags, place);
 
2302
        if(stat == IncludeFeatureAlreadyLoaded) {
 
2303
            warn_msg(WarnParser, "%s:%d: Duplicate of loaded feature %s",
 
2304
                     parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
 
2305
        } else if(stat == IncludeNoExist && include_statement) {
 
2306
            warn_msg(WarnParser, "%s:%d: Unable to find file for inclusion %s",
 
2307
                     parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
 
2308
        } else if(stat >= IncludeFailure) {
 
2309
            if(!ignore_error) {
 
2310
                printf("Project LOAD(): Feature %s cannot be found.\n", file.toLatin1().constData());
 
2311
                if (!ignore_error)
 
2312
                    exit(3);
 
2313
            }
 
2314
            return false;
 
2315
        }
 
2316
        return true;
 
2317
    } else if(func == "debug") {
 
2318
        if(args.count() != 2) {
 
2319
            fprintf(stderr, "%s:%d: debug(level, message) requires one argument.\n", parser.file.toLatin1().constData(),
 
2320
                    parser.line_no);
 
2321
            return false;
 
2322
        }
 
2323
        QString msg = fixEnvVariables(args[1]);
 
2324
        debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData());
 
2325
        return true;
 
2326
    } else if(func == "error" || func == "message" || func == "warning") {
 
2327
        if(args.count() != 1) {
 
2328
            fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.toLatin1().constData(),
 
2329
                    parser.line_no, func.toLatin1().constData());
 
2330
            return false;
 
2331
        }
 
2332
        QString msg = fixEnvVariables(args.first());
 
2333
        fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData());
 
2334
        if(func == "error")
 
2335
            exit(2);
 
2336
        return true;
 
2337
    } else if(testFunctions.contains(func)) {
 
2338
        FunctionBlock *defined = testFunctions[func];
 
2339
        QString ret;
 
2340
        function_blocks.push(defined);
 
2341
        defined->exec(args, this, place, ret);
 
2342
        Q_ASSERT(function_blocks.pop() == defined);
 
2343
 
 
2344
        if(ret.isEmpty()) {
 
2345
            return true;
 
2346
        } else {
 
2347
            if(ret == "true") {
 
2348
                return true;
 
2349
            } else if(ret == "false") {
 
2350
                return false;
 
2351
            } else {
 
2352
                bool ok;
 
2353
                int val = ret.toInt(&ok);
 
2354
                if(ok)
 
2355
                    return val;
 
2356
                fprintf(stderr, "%s:%d Unexpected return value from test %s [%s].\n", parser.file.toLatin1().constData(),
 
2357
                        parser.line_no, func.toLatin1().constData(), ret.toLatin1().constData());
 
2358
            }
 
2359
            return false;
 
2360
        }
 
2361
        return false;
 
2362
    } else {
 
2363
        fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no,
 
2364
                func.toLatin1().constData());
 
2365
    }
 
2366
    return false;
 
2367
}
 
2368
 
 
2369
bool
 
2370
QMakeProject::doProjectCheckReqs(const QStringList &deps, QMap<QString, QStringList> &place)
 
2371
{
 
2372
    bool ret = false;
 
2373
    for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) {
 
2374
        bool test = doProjectTest((*it), place);
 
2375
        if(!test) {
 
2376
            debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
 
2377
                      parser.file.toLatin1().constData(), parser.line_no,
 
2378
                      (*it).toLatin1().constData());
 
2379
            place["QMAKE_FAILED_REQUIREMENTS"].append((*it));
 
2380
            ret = false;
 
2381
        }
 
2382
    }
 
2383
    return ret;
 
2384
}
 
2385
 
 
2386
bool
 
2387
QMakeProject::test(const QString &v)
 
2388
{
 
2389
    QMap<QString, QStringList> tmp = vars;
 
2390
    return doProjectTest(v, tmp);
 
2391
}
 
2392
 
 
2393
bool
 
2394
QMakeProject::test(const QString &func, const QStringList &args)
 
2395
{
 
2396
    QMap<QString, QStringList> tmp = vars;
 
2397
    return doProjectTest(func, args, tmp);
 
2398
}
 
2399
 
 
2400
QString
 
2401
QMakeProject::expand(const QString &str)
 
2402
{
 
2403
    QMap<QString, QStringList> tmp = vars;
 
2404
    QString ret = str;
 
2405
    if(!doVariableReplace(ret, tmp))
 
2406
        return str;
 
2407
    return ret;
 
2408
}
 
2409
 
 
2410
bool
 
2411
QMakeProject::doVariableReplace(QString &str, QMap<QString, QStringList> &place)
 
2412
{
 
2413
    if(str.isEmpty())
 
2414
        return true;
 
2415
 
 
2416
    static bool symbols_init = false;
 
2417
    enum { LSQUARE, RSQUARE, LCURLY, RCURLY, LPAREN, RPAREN, DOLLAR, SLASH, UNDERSCORE, DOT };
 
2418
    static ushort symbols[10];
 
2419
    if(!symbols_init) {
 
2420
        symbols_init = true;
 
2421
        symbols[LSQUARE] = QChar('[').unicode();
 
2422
        symbols[RSQUARE] = QChar(']').unicode();
 
2423
        symbols[LCURLY] = QChar('{').unicode();
 
2424
        symbols[RCURLY] = QChar('}').unicode();
 
2425
        symbols[LPAREN] = QChar('(').unicode();
 
2426
        symbols[RPAREN] = QChar(')').unicode();
 
2427
        symbols[DOLLAR] = QChar('$').unicode();
 
2428
        symbols[SLASH] = QChar('\\').unicode();
 
2429
        symbols[UNDERSCORE] = QChar('_').unicode();
 
2430
        symbols[DOT] = QChar('.').unicode();
 
2431
    }
 
2432
 
 
2433
    ushort unicode;
 
2434
    const QChar *str_data = str.data();
 
2435
    const int str_len = str.length();
 
2436
 
 
2437
    ushort term;
 
2438
    QString replacement;
 
2439
    QString var, args;
 
2440
 
 
2441
    QString ret;
 
2442
    int replaced = 0;
 
2443
    for(int i = 0; i < str_len; ++i) {
 
2444
        unicode = (str_data+i)->unicode();
 
2445
        const int start_var = i;
 
2446
        if(unicode == symbols[SLASH]) {
 
2447
            if(*(str_data+i+1) == symbols[DOLLAR]) {
 
2448
                i++;
 
2449
                if(!(replaced++))
 
2450
                    ret = str.left(start_var);
 
2451
                ret.append(str.at(i));
 
2452
            } else if(replaced) {
 
2453
                ret.append(QChar(unicode));
 
2454
            }
 
2455
            continue;
 
2456
        }
 
2457
        if(unicode == symbols[DOLLAR] && str_len > i+2) {
 
2458
            unicode = (str_data+(++i))->unicode();
 
2459
            if(unicode == symbols[DOLLAR]) {
 
2460
                term = 0;
 
2461
                var.clear();
 
2462
                args.clear();
 
2463
                enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
 
2464
                unicode = (str_data+(++i))->unicode();
 
2465
                if(unicode == symbols[LSQUARE]) {
 
2466
                    unicode = (str_data+(++i))->unicode();
 
2467
                    term = symbols[RSQUARE];
 
2468
                    var_type = PROPERTY;
 
2469
                } else if(unicode == symbols[LCURLY]) {
 
2470
                    unicode = (str_data+(++i))->unicode();
 
2471
                    var_type = VAR;
 
2472
                    term = symbols[RCURLY];
 
2473
                } else if(unicode == symbols[LPAREN]) {
 
2474
                    unicode = (str_data+(++i))->unicode();
 
2475
                    var_type = ENVIRON;
 
2476
                    term = symbols[RPAREN];
 
2477
                }
 
2478
                while(1) {
 
2479
                    if(!(unicode & (0xFF<<8)) &&
 
2480
                       unicode != symbols[DOT] && unicode != symbols[UNDERSCORE] &&
 
2481
                       (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
 
2482
                       (unicode < '0' || unicode > '9'))
 
2483
                        break;
 
2484
                    var.append(QChar(unicode));
 
2485
                    if(++i == str_len)
 
2486
                        break;
 
2487
                    unicode = (str_data+i)->unicode();
 
2488
                }
 
2489
                if(var_type == VAR && unicode == symbols[LPAREN]) {
 
2490
                    var_type = FUNCTION;
 
2491
                    int depth = 0;
 
2492
                    while(1) {
 
2493
                        if(++i == str_len)
 
2494
                            break;
 
2495
                        unicode = (str_data+i)->unicode();
 
2496
                        if(unicode == symbols[LPAREN]) {
 
2497
                            depth++;
 
2498
                        } else if(unicode == symbols[RPAREN]) {
 
2499
                            if(!depth)
 
2500
                                break;
 
2501
                            --depth;
 
2502
                        }
 
2503
                        args.append(QChar(unicode));
 
2504
                    }
 
2505
                    if(i < str_len-1)
 
2506
                        unicode = (str_data+(++i))->unicode();
 
2507
                    else
 
2508
                        unicode = 0;
 
2509
                }
 
2510
                if(term) {
 
2511
                    if(unicode != term) {
 
2512
                        qmake_error_msg("Missing " + QChar(term) + " terminator [found " + QChar(unicode) + "]");
 
2513
                        return false;
 
2514
                    }
 
2515
                    unicode = 0;
 
2516
                } else if(i > str_len-1) {
 
2517
                    unicode = 0;
 
2518
                }
 
2519
 
 
2520
                replacement.clear();
 
2521
                if(var_type == ENVIRON) {
 
2522
                    replacement = QString::fromLocal8Bit(qgetenv(var.toLatin1().constData()));
 
2523
                } else if(var_type == PROPERTY) {
 
2524
                    if(prop)
 
2525
                        replacement = prop->value(var);
 
2526
                } else if(var_type == FUNCTION) {
 
2527
                    replacement = doProjectExpand(var, args, place);
 
2528
                } else if(var_type == VAR) {
 
2529
                    if(var == QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token)
 
2530
                        replacement = QLatin1String("\t");
 
2531
                    } else if(var == QLatin1String("LITERAL_DOLLAR")) { //a real $
 
2532
                        replacement = "$";
 
2533
                    } else if(var == QLatin1String("LITERAL_HASH")) { //a real #
 
2534
                        replacement = "#";
 
2535
                    } else if(var == QLatin1String("OUT_PWD")) { //the out going dir
 
2536
                        replacement = Option::output_dir;
 
2537
                    } else if(var == QLatin1String("PWD") ||  //current working dir (of _FILE_)
 
2538
                              var == QLatin1String("IN_PWD")) {
 
2539
                        replacement = qmake_getpwd();
 
2540
                    } else if(var == QLatin1String("DIR_SEPARATOR")) {
 
2541
                        replacement = Option::dir_sep;
 
2542
                    } else if(var == QLatin1String("_LINE_")) { //parser line number
 
2543
                        replacement = QString::number(parser.line_no);
 
2544
                    } else if(var == QLatin1String("_FILE_")) { //parser file
 
2545
                        replacement = parser.file;
 
2546
                    } else if(var == QLatin1String("_DATE_")) { //current date/time
 
2547
                        replacement = QDateTime::currentDateTime().toString();
 
2548
                    } else if(var == QLatin1String("_QMAKE_CACHE_")) {
 
2549
                        if(Option::mkfile::do_cache)
 
2550
                            replacement = Option::mkfile::cachefile;
 
2551
                    } else {
 
2552
                        replacement = place[varMap(var)].join(QString(Option::field_sep));
 
2553
                    }
 
2554
                }
 
2555
                if(!(replaced++))
 
2556
                    ret = str.left(start_var);
 
2557
                ret.append(replacement);
 
2558
                debug_msg(2, "Project Parser [var replace]: %s -> %s",
 
2559
                          str.toLatin1().constData(), var.toLatin1().constData(),
 
2560
                          replacement.toLatin1().constData());
 
2561
            } else {
 
2562
                if(replaced)
 
2563
                    ret.append("$");
 
2564
            }
 
2565
        }
 
2566
        if(replaced && unicode)
 
2567
            ret.append(QChar(unicode));
 
2568
    }
 
2569
    if(replaced)
 
2570
        str = ret;
 
2571
    return true;
 
2572
}
 
2573
 
 
2574