source: trunk/qmake/project.cpp@ 388

Last change on this file since 388 was 388, checked in by Dmitry A. Kuminov, 16 years ago

qmake: Warnings.

File size: 124.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
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 qt-sales@nokia.com.
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 Q_ASSERT(function_blocks.pop() == defined);
1902 return ret;
1903 }
1904
1905 QStringList args; //why don't the builtin functions just use args_list? --Sam
1906 for(int i = 0; i < args_list.size(); ++i)
1907 args += args_list[i].join(QString(Option::field_sep));
1908
1909 ExpandFunc func_t = qmake_expandFunctions().value(func.toLower());
1910 debug_msg(1, "Running project expand: %s(%s) [%d]",
1911 func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
1912
1913 QStringList ret;
1914 switch(func_t) {
1915 case E_MEMBER: {
1916 if(args.count() < 1 || args.count() > 3) {
1917 fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n",
1918 parser.file.toLatin1().constData(), parser.line_no);
1919 } else {
1920 bool ok = true;
1921 const QStringList &var = values(args.first(), place);
1922 int start = 0, end = 0;
1923 if(args.count() >= 2) {
1924 QString start_str = args[1];
1925 start = start_str.toInt(&ok);
1926 if(!ok) {
1927 if(args.count() == 2) {
1928 int dotdot = start_str.indexOf("..");
1929 if(dotdot != -1) {
1930 start = start_str.left(dotdot).toInt(&ok);
1931 if(ok)
1932 end = start_str.mid(dotdot+2).toInt(&ok);
1933 }
1934 }
1935 if(!ok)
1936 fprintf(stderr, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
1937 parser.file.toLatin1().constData(), parser.line_no,
1938 start_str.toLatin1().constData());
1939 } else {
1940 end = start;
1941 if(args.count() == 3)
1942 end = args[2].toInt(&ok);
1943 if(!ok)
1944 fprintf(stderr, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
1945 parser.file.toLatin1().constData(), parser.line_no,
1946 args[2].toLatin1().constData());
1947 }
1948 }
1949 if(ok) {
1950 if(start < 0)
1951 start += var.count();
1952 if(end < 0)
1953 end += var.count();
1954 if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
1955 //nothing
1956 } else if(start < end) {
1957 for(int i = start; i <= end && (int)var.count() >= i; i++)
1958 ret += var[i];
1959 } else {
1960 for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--)
1961 ret += var[i];
1962 }
1963 }
1964 }
1965 break; }
1966 case E_FIRST:
1967 case E_LAST: {
1968 if(args.count() != 1) {
1969 fprintf(stderr, "%s:%d: %s(var) requires one argument.\n",
1970 parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
1971 } else {
1972 const QStringList &var = values(args.first(), place);
1973 if(!var.isEmpty()) {
1974 if(func_t == E_FIRST)
1975 ret = QStringList(var[0]);
1976 else
1977 ret = QStringList(var[var.size()-1]);
1978 }
1979 }
1980 break; }
1981 case E_CAT: {
1982 if(args.count() < 1 || args.count() > 2) {
1983 fprintf(stderr, "%s:%d: cat(file) requires one argument.\n",
1984 parser.file.toLatin1().constData(), parser.line_no);
1985 } else {
1986 QString file = args[0];
1987 file = Option::fixPathToLocalOS(file);
1988
1989 bool singleLine = true;
1990 if(args.count() > 1)
1991 singleLine = (args[1].toLower() == "true");
1992
1993 QFile qfile(file);
1994 if(qfile.open(QIODevice::ReadOnly)) {
1995 QTextStream stream(&qfile);
1996 while(!stream.atEnd()) {
1997 ret += split_value_list(stream.readLine().trimmed());
1998 if(!singleLine)
1999 ret += "\n";
2000 }
2001 qfile.close();
2002 }
2003 }
2004 break; }
2005 case E_FROMFILE: {
2006 if(args.count() != 2) {
2007 fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n",
2008 parser.file.toLatin1().constData(), parser.line_no);
2009 } else {
2010 QString file = args[0], seek_var = args[1];
2011 file = Option::fixPathToLocalOS(file);
2012
2013 QMap<QString, QStringList> tmp;
2014 if(doProjectInclude(file, IncludeFlagNewParser, tmp) == IncludeSuccess) {
2015 if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2016 QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"];
2017 const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
2018 for(int i = 0; i < in.size(); ++i) {
2019 if(out.indexOf(in[i]) == -1)
2020 out += in[i];
2021 }
2022 }
2023 ret = tmp[seek_var];
2024 }
2025 }
2026 break; }
2027 case E_EVAL: {
2028 if(args.count() < 1 || args.count() > 2) {
2029 fprintf(stderr, "%s:%d: eval(variable) requires one argument.\n",
2030 parser.file.toLatin1().constData(), parser.line_no);
2031
2032 } else {
2033 const QMap<QString, QStringList> *source = &place;
2034 if(args.count() == 2) {
2035 if(args.at(1) == "Global") {
2036 source = &vars;
2037 } else if(args.at(1) == "Local") {
2038 source = &place;
2039 } else {
2040 fprintf(stderr, "%s:%d: unexpected source to eval.\n", parser.file.toLatin1().constData(),
2041 parser.line_no);
2042 }
2043 }
2044 ret += source->value(args.at(0));
2045 }
2046 break; }
2047 case E_LIST: {
2048 static int x = 0;
2049 QString tmp;
2050 tmp.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++);
2051 ret = QStringList(tmp);
2052 QStringList &lst = (*((QMap<QString, QStringList>*)&place))[tmp];
2053 lst.clear();
2054 for(QStringList::ConstIterator arg_it = args.begin();
2055 arg_it != args.end(); ++arg_it)
2056 lst += split_value_list((*arg_it));
2057 break; }
2058 case E_SPRINTF: {
2059 if(args.count() < 1) {
2060 fprintf(stderr, "%s:%d: sprintf(format, ...) requires one argument.\n",
2061 parser.file.toLatin1().constData(), parser.line_no);
2062 } else {
2063 QString tmp = args.at(0);
2064 for(int i = 1; i < args.count(); ++i)
2065 tmp = tmp.arg(args.at(i));
2066 ret = split_value_list(tmp);
2067 }
2068 break; }
2069 case E_JOIN: {
2070 if(args.count() < 1 || args.count() > 4) {
2071 fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four"
2072 "arguments.\n", parser.file.toLatin1().constData(), parser.line_no);
2073 } else {
2074 QString glue, before, after;
2075 if(args.count() >= 2)
2076 glue = args[1];
2077 if(args.count() >= 3)
2078 before = args[2];
2079 if(args.count() == 4)
2080 after = args[3];
2081 const QStringList &var = values(args.first(), place);
2082 if(!var.isEmpty())
2083 ret = split_value_list(before + var.join(glue) + after);
2084 }
2085 break; }
2086 case E_SPLIT: {
2087 if(args.count() < 1 || args.count() > 2) {
2088 fprintf(stderr, "%s:%d split(var, sep) requires one or two arguments\n",
2089 parser.file.toLatin1().constData(), parser.line_no);
2090 } else {
2091 QString sep = QString(Option::field_sep);
2092 if(args.count() >= 2)
2093 sep = args[1];
2094 QStringList var = values(args.first(), place);
2095 for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) {
2096 QStringList lst = (*vit).split(sep);
2097 for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit)
2098 ret += (*spltit);
2099 }
2100 }
2101 break; }
2102 case E_BASENAME:
2103 case E_DIRNAME:
2104 case E_SECTION: {
2105 bool regexp = false;
2106 QString sep, var;
2107 int beg=0, end=-1;
2108 if(func_t == E_SECTION) {
2109 if(args.count() != 3 && args.count() != 4) {
2110 fprintf(stderr, "%s:%d section(var, sep, begin, end) requires three argument\n",
2111 parser.file.toLatin1().constData(), parser.line_no);
2112 } else {
2113 var = args[0];
2114 sep = args[1];
2115 beg = args[2].toInt();
2116 if(args.count() == 4)
2117 end = args[3].toInt();
2118 }
2119 } else {
2120 if(args.count() != 1) {
2121 fprintf(stderr, "%s:%d %s(var) requires one argument.\n",
2122 parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData());
2123 } else {
2124 var = args[0];
2125 regexp = true;
2126 sep = "[" + QRegExp::escape(Option::dir_sep) + "/]";
2127 if(func_t == E_DIRNAME)
2128 end = -2;
2129 else
2130 beg = -1;
2131 }
2132 }
2133 if(!var.isNull()) {
2134 const QStringList &l = values(var, place);
2135 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
2136 QString separator = sep;
2137 if(regexp)
2138 ret += (*it).section(QRegExp(separator), beg, end);
2139 else
2140 ret += (*it).section(separator, beg, end);
2141 }
2142 }
2143 break; }
2144 case E_FIND: {
2145 if(args.count() != 2) {
2146 fprintf(stderr, "%s:%d find(var, str) requires two arguments\n",
2147 parser.file.toLatin1().constData(), parser.line_no);
2148 } else {
2149 QRegExp regx(args[1]);
2150 const QStringList &var = values(args.first(), place);
2151 for(QStringList::ConstIterator vit = var.begin();
2152 vit != var.end(); ++vit) {
2153 if(regx.indexIn(*vit) != -1)
2154 ret += (*vit);
2155 }
2156 }
2157 break; }
2158 case E_SYSTEM: {
2159 if(args.count() < 1 || args.count() > 2) {
2160 fprintf(stderr, "%s:%d system(execut) requires one argument.\n",
2161 parser.file.toLatin1().constData(), parser.line_no);
2162 } else {
2163 QMakeProjectEnv env(place);
2164 char buff[256];
2165 FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
2166 bool singleLine = true;
2167 if(args.count() > 1)
2168 singleLine = (args[1].toLower() == "true");
2169 QString output;
2170 while(proc && !feof(proc)) {
2171 int read_in = int(fread(buff, 1, 255, proc));
2172 if(!read_in)
2173 break;
2174 for(int i = 0; i < read_in; i++) {
2175 if((singleLine && buff[i] == '\n') || buff[i] == '\t')
2176 buff[i] = ' ';
2177 }
2178 buff[read_in] = '\0';
2179 output += buff;
2180 }
2181 ret += split_value_list(output);
2182 if(proc)
2183 QT_PCLOSE(proc);
2184 }
2185 break; }
2186 case E_UNIQUE: {
2187 if(args.count() != 1) {
2188 fprintf(stderr, "%s:%d unique(var) requires one argument.\n",
2189 parser.file.toLatin1().constData(), parser.line_no);
2190 } else {
2191 const QStringList &var = values(args.first(), place);
2192 for(int i = 0; i < var.count(); i++) {
2193 if(!ret.contains(var[i]))
2194 ret.append(var[i]);
2195 }
2196 }
2197 break; }
2198 case E_QUOTE:
2199 ret = args;
2200 break;
2201 case E_ESCAPE_EXPAND: {
2202 for(int i = 0; i < args.size(); ++i) {
2203 QChar *i_data = args[i].data();
2204 int i_len = args[i].length();
2205 for(int x = 0; x < i_len; ++x) {
2206 if(*(i_data+x) == '\\' && x < i_len-1) {
2207 if(*(i_data+x+1) == '\\') {
2208 ++x;
2209 } else {
2210 struct {
2211 char in, out;
2212 } mapped_quotes[] = {
2213 { 'n', '\n' },
2214 { 't', '\t' },
2215 { 'r', '\r' },
2216 { 0, 0 }
2217 };
2218 for(int i = 0; mapped_quotes[i].in; ++i) {
2219 if(*(i_data+x+1) == mapped_quotes[i].in) {
2220 *(i_data+x) = mapped_quotes[i].out;
2221 if(x < i_len-2)
2222 memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
2223 --i_len;
2224 break;
2225 }
2226 }
2227 }
2228 }
2229 }
2230 ret.append(QString(i_data, i_len));
2231 }
2232 break; }
2233 case E_RE_ESCAPE: {
2234 for(int i = 0; i < args.size(); ++i)
2235 ret += QRegExp::escape(args[i]);
2236 break; }
2237 case E_UPPER:
2238 case E_LOWER: {
2239 for(int i = 0; i < args.size(); ++i) {
2240 if(func_t == E_UPPER)
2241 ret += args[i].toUpper();
2242 else
2243 ret += args[i].toLower();
2244 }
2245 break; }
2246 case E_FILES: {
2247 if(args.count() != 1 && args.count() != 2) {
2248 fprintf(stderr, "%s:%d files(pattern) requires one argument.\n",
2249 parser.file.toLatin1().constData(), parser.line_no);
2250 } else {
2251 bool recursive = false;
2252 if(args.count() == 2)
2253 recursive = (args[1].toLower() == "true" || args[1].toInt());
2254 QStringList dirs;
2255 QString r = Option::fixPathToLocalOS(args[0]);
2256 int slash = r.lastIndexOf(QDir::separator());
2257 if(slash != -1) {
2258 dirs.append(r.left(slash));
2259 r = r.mid(slash+1);
2260 } else {
2261 dirs.append("");
2262 }
2263
2264 const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
2265 for(int d = 0; d < dirs.count(); d++) {
2266 QString dir = dirs[d];
2267 if(!dir.isEmpty() && !dir.endsWith(Option::dir_sep))
2268 dir += "/";
2269
2270 QDir qdir(dir);
2271 for(int i = 0; i < (int)qdir.count(); ++i) {
2272 if(qdir[i] == "." || qdir[i] == "..")
2273 continue;
2274 QString fname = dir + qdir[i];
2275 if(QFileInfo(fname).isDir()) {
2276 if(recursive)
2277 dirs.append(fname);
2278 }
2279 if(regex.exactMatch(qdir[i]))
2280 ret += fname;
2281 }
2282 }
2283 }
2284 break; }
2285 case E_PROMPT: {
2286 if(args.count() != 1) {
2287 fprintf(stderr, "%s:%d prompt(question) requires one argument.\n",
2288 parser.file.toLatin1().constData(), parser.line_no);
2289 } else if(projectFile() == "-") {
2290 fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
2291 parser.file.toLatin1().constData(), parser.line_no);
2292 } else {
2293 QString msg = fixEnvVariables(args.first());
2294 if(!msg.endsWith("?"))
2295 msg += "?";
2296 fprintf(stderr, "Project %s: %s ", func.toUpper().toLatin1().constData(),
2297 msg.toLatin1().constData());
2298
2299 QFile qfile;
2300 if(qfile.open(stdin, QIODevice::ReadOnly)) {
2301 QTextStream t(&qfile);
2302 ret = split_value_list(t.readLine());
2303 }
2304 }
2305 break; }
2306 case E_REPLACE: {
2307 if(args.count() != 3 ) {
2308 fprintf(stderr, "%s:%d replace(var, before, after) requires three arguments\n",
2309 parser.file.toLatin1().constData(), parser.line_no);
2310 } else {
2311 const QRegExp before( args[1] );
2312 const QString after( args[2] );
2313 QStringList var = values(args.first(), place);
2314 for(QStringList::Iterator it = var.begin(); it != var.end(); ++it)
2315 ret += it->replace(before, after);
2316 }
2317 break; }
2318 default: {
2319#ifdef QTSCRIPT_SUPPORT
2320 {
2321 QScriptValue jsFunc = eng.globalObject().property(func);
2322 if(jsFunc.isFunction()) {
2323 QScriptValueList jsArgs;
2324 for(int i = 0; i < args.size(); ++i)
2325 jsArgs += QScriptValue(&eng, args.at(i));
2326 QScriptValue jsRet = jsFunc.call(eng.globalObject(), jsArgs);
2327 ret = qscriptvalue_cast<QStringList>(jsRet);
2328 break;
2329 }
2330 }
2331#endif
2332 fprintf(stderr, "%s:%d: Unknown replace function: %s\n",
2333 parser.file.toLatin1().constData(), parser.line_no,
2334 func.toLatin1().constData());
2335 break; }
2336 }
2337 return ret;
2338}
2339
2340bool
2341QMakeProject::doProjectTest(QString func, QStringList args, QMap<QString, QStringList> &place)
2342{
2343 QList<QStringList> args_list;
2344 for(int i = 0; i < args.size(); ++i) {
2345 QStringList arg = split_value_list(args[i]), tmp;
2346 for(int i = 0; i < arg.size(); ++i)
2347 tmp += doVariableReplaceExpand(arg[i], place);
2348 args_list += tmp;
2349 }
2350 return doProjectTest(func, args_list, place);
2351}
2352
2353bool
2354QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QMap<QString, QStringList> &place)
2355{
2356 func = func.trimmed();
2357
2358 if(testFunctions.contains(func)) {
2359 FunctionBlock *defined = testFunctions[func];
2360 QStringList ret;
2361 function_blocks.push(defined);
2362 defined->exec(args_list, this, place, ret);
2363 Q_ASSERT(function_blocks.pop() == defined);
2364
2365 if(ret.isEmpty()) {
2366 return true;
2367 } else {
2368 if(ret.first() == "true") {
2369 return true;
2370 } else if(ret.first() == "false") {
2371 return false;
2372 } else {
2373 bool ok;
2374 int val = ret.first().toInt(&ok);
2375 if(ok)
2376 return val;
2377 fprintf(stderr, "%s:%d Unexpected return value from test %s [%s].\n",
2378 parser.file.toLatin1().constData(),
2379 parser.line_no, func.toLatin1().constData(),
2380 ret.join("::").toLatin1().constData());
2381 }
2382 return false;
2383 }
2384 return false;
2385 }
2386
2387 QStringList args; //why don't the builtin functions just use args_list? --Sam
2388 for(int i = 0; i < args_list.size(); ++i)
2389 args += args_list[i].join(QString(Option::field_sep));
2390
2391 TestFunc func_t = qmake_testFunctions().value(func);
2392 debug_msg(1, "Running project test: %s(%s) [%d]",
2393 func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t);
2394
2395 switch(func_t) {
2396 case T_REQUIRES:
2397 return doProjectCheckReqs(args, place);
2398 case T_LESSTHAN:
2399 case T_GREATERTHAN: {
2400 if(args.count() != 2) {
2401 fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
2402 parser.line_no, func.toLatin1().constData());
2403 return false;
2404 }
2405 QString rhs(args[1]), lhs(values(args[0], place).join(QString(Option::field_sep)));
2406 bool ok;
2407 int rhs_int = rhs.toInt(&ok);
2408 if(ok) { // do integer compare
2409 int lhs_int = lhs.toInt(&ok);
2410 if(ok) {
2411 if(func == "greaterThan")
2412 return lhs_int > rhs_int;
2413 return lhs_int < rhs_int;
2414 }
2415 }
2416 if(func_t == T_GREATERTHAN)
2417 return lhs > rhs;
2418 return lhs < rhs; }
2419 case T_IF: {
2420 if(args.count() != 1) {
2421 fprintf(stderr, "%s:%d: if(condition) requires one argument.\n", parser.file.toLatin1().constData(),
2422 parser.line_no);
2423 return false;
2424 }
2425 const QString cond = args.first();
2426 const QChar *d = cond.unicode();
2427 QChar quote = 0;
2428 bool ret = true, or_op = false;
2429 QString test;
2430 for(int d_off = 0, parens = 0, d_len = cond.size(); d_off < d_len; ++d_off) {
2431 if(!quote.isNull()) {
2432 if(*(d+d_off) == quote)
2433 quote = QChar();
2434 } else if(*(d+d_off) == '(') {
2435 ++parens;
2436 } else if(*(d+d_off) == ')') {
2437 --parens;
2438 } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) {
2439 quote = *(d+d_off);
2440 }
2441 if(!parens && quote.isNull() && (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('|') || d_off == d_len-1)) {
2442 if(d_off == d_len-1)
2443 test += *(d+d_off);
2444 if(!test.isEmpty()) {
2445 const bool success = doProjectTest(test, place);
2446 test = "";
2447 if(or_op)
2448 ret = ret || success;
2449 else
2450 ret = ret && success;
2451 }
2452 if(*(d+d_off) == QLatin1Char(':')) {
2453 or_op = false;
2454 } else if(*(d+d_off) == QLatin1Char('|')) {
2455 or_op = true;
2456 }
2457 } else {
2458 test += *(d+d_off);
2459 }
2460 }
2461 return ret; }
2462 case T_EQUALS:
2463 if(args.count() != 2) {
2464 fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(),
2465 parser.line_no, func.toLatin1().constData());
2466 return false;
2467 }
2468 return values(args[0], place).join(QString(Option::field_sep)) == args[1];
2469 case T_EXISTS: {
2470 if(args.count() != 1) {
2471 fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.toLatin1().constData(),
2472 parser.line_no);
2473 return false;
2474 }
2475 QString file = args.first();
2476 file = Option::fixPathToLocalOS(file);
2477
2478 if(QFile::exists(file))
2479 return true;
2480 //regular expression I guess
2481 QString dirstr = qmake_getpwd();
2482 int slsh = file.lastIndexOf(Option::dir_sep);
2483 if(slsh != -1) {
2484 dirstr = file.left(slsh+1);
2485 file = file.right(file.length() - slsh - 1);
2486 }
2487 return QDir(dirstr).entryList(QStringList(file)).count(); }
2488 case T_EXPORT:
2489 if(args.count() != 1) {
2490 fprintf(stderr, "%s:%d: export(variable) requires one argument.\n", parser.file.toLatin1().constData(),
2491 parser.line_no);
2492 return false;
2493 }
2494 for(int i = 0; i < function_blocks.size(); ++i) {
2495 FunctionBlock *f = function_blocks.at(i);
2496 f->vars[args[0]] = values(args[0], place);
2497 if(!i && f->calling_place)
2498 (*f->calling_place)[args[0]] = values(args[0], place);
2499 }
2500 return true;
2501 case T_CLEAR:
2502 if(args.count() != 1) {
2503 fprintf(stderr, "%s:%d: clear(variable) requires one argument.\n", parser.file.toLatin1().constData(),
2504 parser.line_no);
2505 return false;
2506 }
2507 if(!place.contains(args[0]))
2508 return false;
2509 place[args[0]].clear();
2510 return true;
2511 case T_UNSET:
2512 if(args.count() != 1) {
2513 fprintf(stderr, "%s:%d: unset(variable) requires one argument.\n", parser.file.toLatin1().constData(),
2514 parser.line_no);
2515 return false;
2516 }
2517 if(!place.contains(args[0]))
2518 return false;
2519 place.remove(args[0]);
2520 return true;
2521 case T_EVAL: {
2522 if(args.count() < 1 && 0) {
2523 fprintf(stderr, "%s:%d: eval(project) requires one argument.\n", parser.file.toLatin1().constData(),
2524 parser.line_no);
2525 return false;
2526 }
2527 QString project = args.join(" ");
2528 parser_info pi = parser;
2529 parser.from_file = false;
2530 parser.file = "(eval)";
2531 parser.line_no = 0;
2532 QTextStream t(&project, QIODevice::ReadOnly);
2533 bool ret = read(t, place);
2534 parser = pi;
2535 return ret; }
2536 case T_CONFIG: {
2537 if(args.count() < 1 || args.count() > 2) {
2538 fprintf(stderr, "%s:%d: CONFIG(config) requires one argument.\n", parser.file.toLatin1().constData(),
2539 parser.line_no);
2540 return false;
2541 }
2542 if(args.count() == 1)
2543 return isActiveConfig(args[0]);
2544 const QStringList mutuals = args[1].split('|');
2545 const QStringList &configs = values("CONFIG", place);
2546 for(int i = configs.size()-1; i >= 0; i--) {
2547 for(int mut = 0; mut < mutuals.count(); mut++) {
2548 if(configs[i] == mutuals[mut].trimmed())
2549 return (configs[i] == args[0]);
2550 }
2551 }
2552 return false; }
2553 case T_SYSTEM: {
2554 bool setup_env = true;
2555 if(args.count() < 1 || args.count() > 2) {
2556 fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.toLatin1().constData(),
2557 parser.line_no);
2558 return false;
2559 }
2560 if(args.count() == 2) {
2561 const QString sarg = args[1];
2562 setup_env = (sarg.toLower() == "true" || sarg.toInt());
2563 }
2564 QMakeProjectEnv env;
2565 if(setup_env)
2566 env.execute(place);
2567 bool ret = system(args[0].toLatin1().constData()) == 0;
2568 return ret; }
2569 case T_RETURN:
2570 if(function_blocks.isEmpty()) {
2571 fprintf(stderr, "%s:%d unexpected return()\n",
2572 parser.file.toLatin1().constData(), parser.line_no);
2573 } else {
2574 FunctionBlock *f = function_blocks.top();
2575 f->cause_return = true;
2576 if(args_list.count() >= 1)
2577 f->return_value += args_list[0];
2578 }
2579 return true;
2580 case T_BREAK:
2581 if(iterator)
2582 iterator->cause_break = true;
2583 else if(!scope_blocks.isEmpty())
2584 scope_blocks.top().ignore = true;
2585 else
2586 fprintf(stderr, "%s:%d unexpected break()\n",
2587 parser.file.toLatin1().constData(), parser.line_no);
2588 return true;
2589 case T_NEXT:
2590 if(iterator)
2591 iterator->cause_next = true;
2592 else
2593 fprintf(stderr, "%s:%d unexpected next()\n",
2594 parser.file.toLatin1().constData(), parser.line_no);
2595 return true;
2596 case T_DEFINED:
2597 if(args.count() < 1 || args.count() > 2) {
2598 fprintf(stderr, "%s:%d: defined(function) requires one argument.\n",
2599 parser.file.toLatin1().constData(), parser.line_no);
2600 } else {
2601 if(args.count() > 1) {
2602 if(args[1] == "test")
2603 return testFunctions.contains(args[0]);
2604 else if(args[1] == "replace")
2605 return replaceFunctions.contains(args[0]);
2606 fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n",
2607 parser.file.toLatin1().constData(), parser.line_no,
2608 args[1].toLatin1().constData());
2609 } else {
2610 if(replaceFunctions.contains(args[0]) || testFunctions.contains(args[0]))
2611 return true;
2612 }
2613 }
2614 return false;
2615 case T_CONTAINS: {
2616 if(args.count() < 2 || args.count() > 3) {
2617 fprintf(stderr, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n",
2618 parser.file.toLatin1().constData(), parser.line_no);
2619 return false;
2620 }
2621 QRegExp regx(args[1]);
2622 const QStringList &l = values(args[0], place);
2623 if(args.count() == 2) {
2624 for(int i = 0; i < l.size(); ++i) {
2625 const QString val = l[i];
2626 if(regx.exactMatch(val) || val == args[1])
2627 return true;
2628 }
2629 } else {
2630 const QStringList mutuals = args[2].split('|');
2631 for(int i = l.size()-1; i >= 0; i--) {
2632 const QString val = l[i];
2633 for(int mut = 0; mut < mutuals.count(); mut++) {
2634 if(val == mutuals[mut].trimmed())
2635 return (regx.exactMatch(val) || val == args[1]);
2636 }
2637 }
2638 }
2639 return false; }
2640 case T_INFILE: {
2641 if(args.count() < 2 || args.count() > 3) {
2642 fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
2643 parser.file.toLatin1().constData(), parser.line_no);
2644 return false;
2645 }
2646
2647 bool ret = false;
2648 QMap<QString, QStringList> tmp;
2649 if(doProjectInclude(Option::fixPathToLocalOS(args[0]), IncludeFlagNewParser, tmp) == IncludeSuccess) {
2650 if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2651 QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"];
2652 const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"];
2653 for(int i = 0; i < in.size(); ++i) {
2654 if(out.indexOf(in[i]) == -1)
2655 out += in[i];
2656 }
2657 }
2658 if(args.count() == 2) {
2659 ret = tmp.contains(args[1]);
2660 } else {
2661 QRegExp regx(args[2]);
2662 const QStringList &l = tmp[args[1]];
2663 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
2664 if(regx.exactMatch((*it)) || (*it) == args[2]) {
2665 ret = true;
2666 break;
2667 }
2668 }
2669 }
2670 }
2671 return ret; }
2672 case T_COUNT:
2673 if(args.count() != 2 && args.count() != 3) {
2674 fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.toLatin1().constData(),
2675 parser.line_no);
2676 return false;
2677 }
2678 if(args.count() == 3) {
2679 QString comp = args[2];
2680 if(comp == ">" || comp == "greaterThan")
2681 return values(args[0], place).count() > args[1].toInt();
2682 if(comp == ">=")
2683 return values(args[0], place).count() >= args[1].toInt();
2684 if(comp == "<" || comp == "lessThan")
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 == "equals" || comp == "isEqual" || comp == "=" || comp == "==")
2689 return values(args[0], place).count() == args[1].toInt();
2690 fprintf(stderr, "%s:%d: unexpected modifier to count(%s)\n", parser.file.toLatin1().constData(),
2691 parser.line_no, comp.toLatin1().constData());
2692 return false;
2693 }
2694 return values(args[0], place).count() == args[1].toInt();
2695 case T_ISEMPTY:
2696 if(args.count() != 1) {
2697 fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.toLatin1().constData(),
2698 parser.line_no);
2699 return false;
2700 }
2701 return values(args[0], place).isEmpty();
2702 case T_INCLUDE:
2703 case T_LOAD: {
2704 QString parseInto;
2705 const bool include_statement = (func_t == T_INCLUDE);
2706 bool ignore_error = include_statement;
2707 if(args.count() == 2) {
2708 if(func_t == T_INCLUDE) {
2709 parseInto = args[1];
2710 } else {
2711 QString sarg = args[1];
2712 ignore_error = (sarg.toLower() == "true" || sarg.toInt());
2713 }
2714 } else if(args.count() != 1) {
2715 QString func_desc = "load(feature)";
2716 if(include_statement)
2717 func_desc = "include(file)";
2718 fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.toLatin1().constData(),
2719 parser.line_no, func_desc.toLatin1().constData());
2720 return false;
2721 }
2722 QString file = args.first();
2723 file = Option::fixPathToLocalOS(file);
2724 uchar flags = IncludeFlagNone;
2725 if(!include_statement)
2726 flags |= IncludeFlagFeature;
2727 IncludeStatus stat = IncludeFailure;
2728 if(!parseInto.isEmpty()) {
2729 QMap<QString, QStringList> symbols;
2730 stat = doProjectInclude(file, flags|IncludeFlagNewProject, symbols);
2731 if(stat == IncludeSuccess) {
2732 QMap<QString, QStringList> out_place;
2733 for(QMap<QString, QStringList>::ConstIterator it = place.begin(); it != place.end(); ++it) {
2734 const QString var = it.key();
2735 if(var != parseInto && !var.startsWith(parseInto + "."))
2736 out_place.insert(var, it.value());
2737 }
2738 for(QMap<QString, QStringList>::ConstIterator it = symbols.begin(); it != symbols.end(); ++it) {
2739 const QString var = it.key();
2740 if(!var.startsWith("."))
2741 out_place.insert(parseInto + "." + it.key(), it.value());
2742 }
2743 place = out_place;
2744 }
2745 } else {
2746 stat = doProjectInclude(file, flags, place);
2747 }
2748 if(stat == IncludeFeatureAlreadyLoaded) {
2749 warn_msg(WarnParser, "%s:%d: Duplicate of loaded feature %s",
2750 parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
2751 } else if(stat == IncludeNoExist && include_statement) {
2752 warn_msg(WarnParser, "%s:%d: Unable to find file for inclusion %s",
2753 parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData());
2754 return false;
2755 } else if(stat >= IncludeFailure) {
2756 if(!ignore_error) {
2757 printf("Project LOAD(): Feature %s cannot be found.\n", file.toLatin1().constData());
2758 if (!ignore_error)
2759#if defined(QT_BUILD_QMAKE_LIBRARY)
2760 return false;
2761#else
2762 exit(3);
2763#endif
2764 }
2765 return false;
2766 }
2767 return true; }
2768 case T_DEBUG: {
2769 if(args.count() != 2) {
2770 fprintf(stderr, "%s:%d: debug(level, message) requires one argument.\n", parser.file.toLatin1().constData(),
2771 parser.line_no);
2772 return false;
2773 }
2774 QString msg = fixEnvVariables(args[1]);
2775 debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData());
2776 return true; }
2777 case T_ERROR:
2778 case T_MESSAGE:
2779 case T_WARNING: {
2780 if(args.count() != 1) {
2781 fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.toLatin1().constData(),
2782 parser.line_no, func.toLatin1().constData());
2783 return false;
2784 }
2785 QString msg = fixEnvVariables(args.first());
2786 fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData());
2787 if(func == "error")
2788#if defined(QT_BUILD_QMAKE_LIBRARY)
2789 return false;
2790#else
2791 exit(2);
2792#endif
2793 return true; }
2794 default:
2795#ifdef QTSCRIPT_SUPPORT
2796 {
2797 QScriptValue jsFunc = eng.globalObject().property(func);
2798 if(jsFunc.isFunction()) {
2799 QScriptValueList jsArgs;
2800 for(int i = 0; i < args.size(); ++i)
2801 jsArgs += QScriptValue(&eng, args.at(i));
2802 QScriptValue jsRet = jsFunc.call(eng.globalObject(), jsArgs);
2803 if(eng.hasUncaughtException())
2804 return false;
2805 return qscriptvalue_cast<bool>(jsRet);
2806 }
2807 }
2808#endif
2809 fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no,
2810 func.toLatin1().constData());
2811 }
2812 return false;
2813}
2814
2815bool
2816QMakeProject::doProjectCheckReqs(const QStringList &deps, QMap<QString, QStringList> &place)
2817{
2818 bool ret = false;
2819 for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) {
2820 bool test = doProjectTest((*it), place);
2821 if(!test) {
2822 debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
2823 parser.file.toLatin1().constData(), parser.line_no,
2824 (*it).toLatin1().constData());
2825 place["QMAKE_FAILED_REQUIREMENTS"].append((*it));
2826 ret = false;
2827 }
2828 }
2829 return ret;
2830}
2831
2832bool
2833QMakeProject::test(const QString &v)
2834{
2835 QMap<QString, QStringList> tmp = vars;
2836 return doProjectTest(v, tmp);
2837}
2838
2839bool
2840QMakeProject::test(const QString &func, const QList<QStringList> &args)
2841{
2842 QMap<QString, QStringList> tmp = vars;
2843 return doProjectTest(func, args, tmp);
2844}
2845
2846QStringList
2847QMakeProject::expand(const QString &str)
2848{
2849 bool ok;
2850 QMap<QString, QStringList> tmp = vars;
2851 const QStringList ret = doVariableReplaceExpand(str, tmp, &ok);
2852 if(ok)
2853 return ret;
2854 return QStringList();
2855}
2856
2857QStringList
2858QMakeProject::expand(const QString &func, const QList<QStringList> &args)
2859{
2860 QMap<QString, QStringList> tmp = vars;
2861 return doProjectExpand(func, args, tmp);
2862}
2863
2864bool
2865QMakeProject::doVariableReplace(QString &str, QMap<QString, QStringList> &place)
2866{
2867 bool ret;
2868 str = doVariableReplaceExpand(str, place, &ret).join(QString(Option::field_sep));
2869 return ret;
2870}
2871
2872QStringList
2873QMakeProject::doVariableReplaceExpand(const QString &str, QMap<QString, QStringList> &place, bool *ok)
2874{
2875 QStringList ret;
2876 if(ok)
2877 *ok = true;
2878 if(str.isEmpty())
2879 return ret;
2880
2881 const ushort LSQUARE = '[';
2882 const ushort RSQUARE = ']';
2883 const ushort LCURLY = '{';
2884 const ushort RCURLY = '}';
2885 const ushort LPAREN = '(';
2886 const ushort RPAREN = ')';
2887 const ushort DOLLAR = '$';
2888 const ushort SLASH = '\\';
2889 const ushort UNDERSCORE = '_';
2890 const ushort DOT = '.';
2891 const ushort SPACE = ' ';
2892 const ushort TAB = '\t';
2893 const ushort SINGLEQUOTE = '\'';
2894 const ushort DOUBLEQUOTE = '"';
2895
2896 ushort unicode, quote = 0;
2897 const QChar *str_data = str.data();
2898 const int str_len = str.length();
2899
2900 ushort term;
2901 QString var, args;
2902
2903 int replaced = 0;
2904 QString current;
2905 for(int i = 0; i < str_len; ++i) {
2906 unicode = str_data[i].unicode();
2907 const int start_var = i;
2908 if(unicode == DOLLAR && str_len > i+2) {
2909 unicode = str_data[++i].unicode();
2910 if(unicode == DOLLAR) {
2911 term = 0;
2912 var.clear();
2913 args.clear();
2914 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
2915 unicode = str_data[++i].unicode();
2916 if(unicode == LSQUARE) {
2917 unicode = str_data[++i].unicode();
2918 term = RSQUARE;
2919 var_type = PROPERTY;
2920 } else if(unicode == LCURLY) {
2921 unicode = str_data[++i].unicode();
2922 var_type = VAR;
2923 term = RCURLY;
2924 } else if(unicode == LPAREN) {
2925 unicode = str_data[++i].unicode();
2926 var_type = ENVIRON;
2927 term = RPAREN;
2928 }
2929 while(1) {
2930 if(!(unicode & (0xFF<<8)) &&
2931 unicode != DOT && unicode != UNDERSCORE &&
2932 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
2933 (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
2934 (unicode < '0' || unicode > '9'))
2935 break;
2936 var.append(QChar(unicode));
2937 if(++i == str_len)
2938 break;
2939 unicode = str_data[i].unicode();
2940 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
2941 }
2942 if(var_type == VAR && unicode == LPAREN) {
2943 var_type = FUNCTION;
2944 int depth = 0;
2945 while(1) {
2946 if(++i == str_len)
2947 break;
2948 unicode = str_data[i].unicode();
2949 if(unicode == LPAREN) {
2950 depth++;
2951 } else if(unicode == RPAREN) {
2952 if(!depth)
2953 break;
2954 --depth;
2955 }
2956 args.append(QChar(unicode));
2957 }
2958 if(++i < str_len)
2959 unicode = str_data[i].unicode();
2960 else
2961 unicode = 0;
2962 // at this point i is pointing to the 'next' character (which is in unicode)
2963 // this might actually be a term character since you can do $${func()}
2964 }
2965 if(term) {
2966 if(unicode != term) {
2967 qmake_error_msg("Missing " + QString(term) + " terminator [found " + (unicode?QString(unicode):QString("end-of-line")) + "]");
2968 if(ok)
2969 *ok = false;
2970 return QStringList();
2971 }
2972 } else {
2973 // move the 'cursor' back to the last char of the thing we were looking at
2974 --i;
2975 }
2976 // since i never points to the 'next' character, there is no reason for this to be set
2977 unicode = 0;
2978
2979 QStringList replacement;
2980 if(var_type == ENVIRON) {
2981 replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
2982 } else if(var_type == PROPERTY) {
2983 if(prop)
2984 replacement = split_value_list(prop->value(var));
2985 } else if(var_type == FUNCTION) {
2986 replacement = doProjectExpand(var, args, place);
2987 } else if(var_type == VAR) {
2988 replacement = values(var, place);
2989 }
2990 if(!(replaced++) && start_var)
2991 current = str.left(start_var);
2992 if(!replacement.isEmpty()) {
2993 if(quote) {
2994 current += replacement.join(QString(Option::field_sep));
2995 } else {
2996 current += replacement.takeFirst();
2997 if(!replacement.isEmpty()) {
2998 if(!current.isEmpty())
2999 ret.append(current);
3000 current = replacement.takeLast();
3001 if(!replacement.isEmpty())
3002 ret += replacement;
3003 }
3004 }
3005 }
3006 debug_msg(2, "Project Parser [var replace]: %s -> %s",
3007 str.toLatin1().constData(), var.toLatin1().constData(),
3008 replacement.join("::").toLatin1().constData());
3009 } else {
3010 if(replaced)
3011 current.append("$");
3012 }
3013 }
3014 if(quote && unicode == quote) {
3015 unicode = 0;
3016 quote = 0;
3017 } else if(unicode == SLASH) {
3018 bool escape = false;
3019 const char *symbols = "[]{}()$\\'\"";
3020 for(const char *s = symbols; *s; ++s) {
3021 if(str_data[i+1].unicode() == (ushort)*s) {
3022 i++;
3023 escape = true;
3024 if(!(replaced++))
3025 current = str.left(start_var);
3026 current.append(str.at(i));
3027 break;
3028 }
3029 }
3030 if(escape || !replaced)
3031 unicode =0;
3032 } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
3033 quote = unicode;
3034 unicode = 0;
3035 if(!(replaced++) && i)
3036 current = str.left(i);
3037 } else if(!quote && (unicode == SPACE || unicode == TAB)) {
3038 unicode = 0;
3039 if(!current.isEmpty()) {
3040 ret.append(current);
3041 current.clear();
3042 }
3043 }
3044 if(replaced && unicode)
3045 current.append(QChar(unicode));
3046 }
3047 if(!replaced)
3048 ret = QStringList(str);
3049 else if(!current.isEmpty())
3050 ret.append(current);
3051 //qDebug() << "REPLACE" << str << ret;
3052 return ret;
3053}
3054
3055QStringList &QMakeProject::values(const QString &_var, QMap<QString, QStringList> &place)
3056{
3057 QString var = varMap(_var);
3058 if(var == QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token)
3059 var = ".BUILTIN." + var;
3060 place[var] = QStringList(QLatin1String("\t"));
3061 } else if(var == QLatin1String("LITERAL_DOLLAR")) { //a real $
3062 var = ".BUILTIN." + var;
3063 place[var] = QStringList(QLatin1String("$"));
3064 } else if(var == QLatin1String("LITERAL_HASH")) { //a real #
3065 var = ".BUILTIN." + var;
3066 place[var] = QStringList("#");
3067 } else if(var == QLatin1String("OUT_PWD")) { //the out going dir
3068 var = ".BUILTIN." + var;
3069 place[var] = QStringList(Option::output_dir);
3070 } else if(var == QLatin1String("PWD") || //current working dir (of _FILE_)
3071 var == QLatin1String("IN_PWD")) {
3072 var = ".BUILTIN." + var;
3073 place[var] = QStringList(qmake_getpwd());
3074 } else if(var == QLatin1String("DIR_SEPARATOR")) {
3075 var = ".BUILTIN." + var;
3076 place[var] = QStringList(Option::dir_sep);
3077 } else if(var == QLatin1String("DIRLIST_SEPARATOR")) {
3078 var = ".BUILTIN." + var;
3079 place[var] = QStringList(Option::dirlist_sep);
3080 } else if(var == QLatin1String("_LINE_")) { //parser line number
3081 var = ".BUILTIN." + var;
3082 place[var] = QStringList(QString::number(parser.line_no));
3083 } else if(var == QLatin1String("_FILE_")) { //parser file
3084 var = ".BUILTIN." + var;
3085 place[var] = QStringList(parser.file);
3086 } else if(var == QLatin1String("_DATE_")) { //current date/time
3087 var = ".BUILTIN." + var;
3088 place[var] = QStringList(QDateTime::currentDateTime().toString());
3089 } else if(var == QLatin1String("_PRO_FILE_")) {
3090 var = ".BUILTIN." + var;
3091 place[var] = QStringList(pfile);
3092 } else if(var == QLatin1String("_PRO_FILE_PWD_")) {
3093 var = ".BUILTIN." + var;
3094 place[var] = QStringList(QFileInfo(pfile).absolutePath());
3095 } else if(var == QLatin1String("_QMAKE_CACHE_")) {
3096 var = ".BUILTIN." + var;
3097 if(Option::mkfile::do_cache)
3098 place[var] = QStringList(Option::mkfile::cachefile);
3099 } else if(var == QLatin1String("TEMPLATE")) {
3100 if(!Option::user_template.isEmpty()) {
3101 var = ".BUILTIN.USER." + var;
3102 place[var] = QStringList(Option::user_template);
3103 } else if(!place[var].isEmpty()) {
3104 QString orig_template = place["TEMPLATE"].first(), real_template;
3105 if(!Option::user_template_prefix.isEmpty() && !orig_template.startsWith(Option::user_template_prefix))
3106 real_template = Option::user_template_prefix + orig_template;
3107 if(real_template.endsWith(".t"))
3108 real_template = real_template.left(real_template.length()-2);
3109 if(!real_template.isEmpty()) {
3110 var = ".BUILTIN." + var;
3111 place[var] = QStringList(real_template);
3112 }
3113 } else {
3114 var = ".BUILTIN." + var;
3115 place[var] = QStringList("app");
3116 }
3117 } else if(var.startsWith(QLatin1String("QMAKE_HOST."))) {
3118 QString ret, type = var.mid(11);
3119#if defined(Q_OS_WIN32)
3120 if(type == "os") {
3121 ret = "Windows";
3122 } else if(type == "name") {
3123 DWORD name_length = 1024;
3124 TCHAR name[1024];
3125 if(GetComputerName(name, &name_length))
3126 ret = QString::fromUtf16((ushort*)name, name_length);
3127 } else if(type == "version" || type == "version_string") {
3128 QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
3129 if(type == "version")
3130 ret = QString::number(ver);
3131 else if(ver == QSysInfo::WV_Me)
3132 ret = "WinMe";
3133 else if(ver == QSysInfo::WV_95)
3134 ret = "Win95";
3135 else if(ver == QSysInfo::WV_98)
3136 ret = "Win98";
3137 else if(ver == QSysInfo::WV_NT)
3138 ret = "WinNT";
3139 else if(ver == QSysInfo::WV_2000)
3140 ret = "Win2000";
3141 else if(ver == QSysInfo::WV_2000)
3142 ret = "Win2003";
3143 else if(ver == QSysInfo::WV_XP)
3144 ret = "WinXP";
3145 else if(ver == QSysInfo::WV_VISTA)
3146 ret = "WinVista";
3147 else
3148 ret = "Unknown";
3149 } else if(type == "arch") {
3150 SYSTEM_INFO info;
3151 GetSystemInfo(&info);
3152 switch(info.wProcessorArchitecture) {
3153#ifdef PROCESSOR_ARCHITECTURE_AMD64
3154 case PROCESSOR_ARCHITECTURE_AMD64:
3155 ret = "x86_64";
3156 break;
3157#endif
3158 case PROCESSOR_ARCHITECTURE_INTEL:
3159 ret = "x86";
3160 break;
3161 case PROCESSOR_ARCHITECTURE_IA64:
3162#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
3163 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
3164#endif
3165 ret = "IA64";
3166 break;
3167 default:
3168 ret = "Unknown";
3169 break;
3170 }
3171 }
3172#elif defined(Q_OS_OS2)
3173 if(type == "os") {
3174 ret = "OS2";
3175 } else if(type == "name") {
3176 ret = QString::fromLocal8Bit(qgetenv("HOSTNAME"));
3177 } else if(type == "version" || type == "version_string") {
3178 ULONG buf [3];
3179 DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
3180 &buf, sizeof(buf));
3181 if(type == "version")
3182 ret = QString().sprintf("%lu.%lu.%lu", buf[0], buf[1], buf[2]);
3183 else {
3184 /* Warp 3 is reported as 20.30 */
3185 /* Warp 4 is reported as 20.40 */
3186 /* Aurora and eCS are reported as 20.45 */
3187 if (buf[0] == 20 && buf[1] == 30)
3188 ret = "Warp3";
3189 else if (buf[0] == 20 && buf[1] == 40)
3190 ret = "Warp4";
3191 else if (buf[0] == 20 && buf[1] == 45) {
3192 if (QString::fromLocal8Bit(qgetenv("OS")) == "ecs")
3193 ret = "eComStation";
3194 else
3195 ret = "Aurora";
3196 }
3197 else
3198 ret = "Unknown";
3199 }
3200 } else if(type == "arch") {
3201 ret = "x86";
3202 }
3203#elif defined(Q_OS_UNIX)
3204 struct utsname name;
3205 if(!uname(&name)) {
3206 if(type == "os")
3207 ret = name.sysname;
3208 else if(type == "name")
3209 ret = name.nodename;
3210 else if(type == "version")
3211 ret = name.release;
3212 else if(type == "version_string")
3213 ret = name.version;
3214 else if(type == "arch")
3215 ret = name.machine;
3216 }
3217#endif
3218 var = ".BUILTIN.HOST." + type;
3219 place[var] = QStringList(ret);
3220 } else if (var == QLatin1String("QMAKE_DIR_SEP")) {
3221 if (place[var].isEmpty())
3222 return values("DIR_SEPARATOR", place);
3223 }
3224 //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::")));
3225 return place[var];
3226}
3227
3228QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.