source: trunk/qmake/project.cpp@ 728

Last change on this file since 728 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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