source: trunk/qmake/project.cpp@ 1040

Last change on this file since 1040 was 967, checked in by Dmitry A. Kuminov, 14 years ago

qmake: Added replace functions fixpath_target and fixpath_local.

They match to Option::fixPathToTargetOS() and Option::fixPathToTargetOS()
respectively. These operations are frequently used to fix paths when generating
makefiles and having them in .pro allows to do many things that would otherwise
require patching qmake itself. See the next changeset for a real life example.

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