source: trunk/qmake/project.cpp@ 275

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

qmake: More OS/2-specific fixes. Enabled GNUMakefileGenerator (turned on by MAKEFILE_GENERATOR=GNUMAKE).

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