source: trunk/qmake/project.cpp@ 506

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

qmake: Fixed: Don't enclose required code within Q_ASSERT() because it won't be executed in release builds [vendor bug].

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