1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Qt Software Information (qt-info@nokia.com)
6 ** This file is part of the qmake application of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
10 ** This file contains pre-release code and may not be distributed.
11 ** You may use this file in accordance with the terms and conditions
12 ** contained in the either Technology Preview License Agreement or the
13 ** Beta Release License Agreement.
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file. Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
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.
36 ** If you are unsure which license is appropriate for your use, please
37 ** contact the sales department at qt-sales@nokia.com.
40 ****************************************************************************/
45 #include "cachekeys.h"
47 #include <qdatetime.h>
49 #include <qfileinfo.h>
52 #include <qtextstream.h>
58 #include <sys/utsname.h>
59 #elif defined(Q_OS_WIN32)
66 #define QT_POPEN _popen
67 #define QT_PCLOSE _pclose
69 #define QT_POPEN popen
70 #define QT_PCLOSE pclose
76 enum ExpandFunc
{ E_MEMBER
=1, E_FIRST
, E_LAST
, E_CAT
, E_FROMFILE
, E_EVAL
, E_LIST
,
77 E_SPRINTF
, E_JOIN
, E_SPLIT
, E_BASENAME
, E_DIRNAME
, E_SECTION
,
78 E_FIND
, E_SYSTEM
, E_UNIQUE
, E_QUOTE
, E_ESCAPE_EXPAND
,
79 E_UPPER
, E_LOWER
, E_FILES
, E_PROMPT
, E_RE_ESCAPE
, E_REPLACE
};
80 QMap
<QString
, ExpandFunc
> qmake_expandFunctions()
82 static QMap
<QString
, ExpandFunc
> *qmake_expand_functions
= 0;
83 if(!qmake_expand_functions
) {
84 qmake_expand_functions
= new QMap
<QString
, ExpandFunc
>;
85 qmakeAddCacheClear(qmakeDeleteCacheClear_QMapStringInt
, (void**)&qmake_expand_functions
);
86 qmake_expand_functions
->insert("member", E_MEMBER
);
87 qmake_expand_functions
->insert("first", E_FIRST
);
88 qmake_expand_functions
->insert("last", E_LAST
);
89 qmake_expand_functions
->insert("cat", E_CAT
);
90 qmake_expand_functions
->insert("fromfile", E_FROMFILE
);
91 qmake_expand_functions
->insert("eval", E_EVAL
);
92 qmake_expand_functions
->insert("list", E_LIST
);
93 qmake_expand_functions
->insert("sprintf", E_SPRINTF
);
94 qmake_expand_functions
->insert("join", E_JOIN
);
95 qmake_expand_functions
->insert("split", E_SPLIT
);
96 qmake_expand_functions
->insert("basename", E_BASENAME
);
97 qmake_expand_functions
->insert("dirname", E_DIRNAME
);
98 qmake_expand_functions
->insert("section", E_SECTION
);
99 qmake_expand_functions
->insert("find", E_FIND
);
100 qmake_expand_functions
->insert("system", E_SYSTEM
);
101 qmake_expand_functions
->insert("unique", E_UNIQUE
);
102 qmake_expand_functions
->insert("quote", E_QUOTE
);
103 qmake_expand_functions
->insert("escape_expand", E_ESCAPE_EXPAND
);
104 qmake_expand_functions
->insert("upper", E_UPPER
);
105 qmake_expand_functions
->insert("lower", E_LOWER
);
106 qmake_expand_functions
->insert("re_escape", E_RE_ESCAPE
);
107 qmake_expand_functions
->insert("files", E_FILES
);
108 qmake_expand_functions
->insert("prompt", E_PROMPT
);
109 qmake_expand_functions
->insert("replace", E_REPLACE
);
111 return *qmake_expand_functions
;
114 enum TestFunc
{ T_REQUIRES
=1, T_GREATERTHAN
, T_LESSTHAN
, T_EQUALS
,
115 T_EXISTS
, T_EXPORT
, T_CLEAR
, T_UNSET
, T_EVAL
, T_CONFIG
, T_SYSTEM
,
116 T_RETURN
, T_BREAK
, T_NEXT
, T_DEFINED
, T_CONTAINS
, T_INFILE
,
117 T_COUNT
, T_ISEMPTY
, T_INCLUDE
, T_LOAD
, T_DEBUG
, T_ERROR
,
118 T_MESSAGE
, T_WARNING
, T_IF
};
119 QMap
<QString
, TestFunc
> qmake_testFunctions()
121 static QMap
<QString
, TestFunc
> *qmake_test_functions
= 0;
122 if(!qmake_test_functions
) {
123 qmake_test_functions
= new QMap
<QString
, TestFunc
>;
124 qmake_test_functions
->insert("requires", T_REQUIRES
);
125 qmake_test_functions
->insert("greaterThan", T_GREATERTHAN
);
126 qmake_test_functions
->insert("lessThan", T_LESSTHAN
);
127 qmake_test_functions
->insert("equals", T_EQUALS
);
128 qmake_test_functions
->insert("isEqual", T_EQUALS
);
129 qmake_test_functions
->insert("exists", T_EXISTS
);
130 qmake_test_functions
->insert("export", T_EXPORT
);
131 qmake_test_functions
->insert("clear", T_CLEAR
);
132 qmake_test_functions
->insert("unset", T_UNSET
);
133 qmake_test_functions
->insert("eval", T_EVAL
);
134 qmake_test_functions
->insert("CONFIG", T_CONFIG
);
135 qmake_test_functions
->insert("if", T_IF
);
136 qmake_test_functions
->insert("isActiveConfig", T_CONFIG
);
137 qmake_test_functions
->insert("system", T_SYSTEM
);
138 qmake_test_functions
->insert("return", T_RETURN
);
139 qmake_test_functions
->insert("break", T_BREAK
);
140 qmake_test_functions
->insert("next", T_NEXT
);
141 qmake_test_functions
->insert("defined", T_DEFINED
);
142 qmake_test_functions
->insert("contains", T_CONTAINS
);
143 qmake_test_functions
->insert("infile", T_INFILE
);
144 qmake_test_functions
->insert("count", T_COUNT
);
145 qmake_test_functions
->insert("isEmpty", T_ISEMPTY
);
146 qmake_test_functions
->insert("include", T_INCLUDE
);
147 qmake_test_functions
->insert("load", T_LOAD
);
148 qmake_test_functions
->insert("debug", T_DEBUG
);
149 qmake_test_functions
->insert("error", T_ERROR
);
150 qmake_test_functions
->insert("message", T_MESSAGE
);
151 qmake_test_functions
->insert("warning", T_WARNING
);
153 return *qmake_test_functions
;
158 #ifdef QTSCRIPT_SUPPORT
159 #include "qscriptvalue.h"
160 #include "qscriptengine.h"
161 #include "qscriptvalueiterator.h"
165 static QScriptValue
qscript_projectWrapper(QScriptEngine
*eng
, QMakeProject
*project
,
166 const QMap
<QString
, QStringList
> &place
);
168 static bool qscript_createQMakeProjectMap(QMap
<QString
, QStringList
> &vars
, QScriptValue js
)
170 QScriptValueIterator
it(js
);
171 while(it
.hasNext()) {
173 vars
[it
.name()] = qscriptvalue_cast
<QStringList
>(it
.value());
178 static QScriptValue
qscript_call_testfunction(QScriptContext
*context
, QScriptEngine
*engine
)
180 QMakeProject
*self
= qscriptvalue_cast
<QMakeProject
*>(context
->callee().property("qmakeProject"));
181 QString func
= context
->callee().property("functionName").toString();
183 for(int i
= 0; i
< context
->argumentCount(); ++i
)
184 args
+= context
->argument(i
).toString();
185 QMap
<QString
, QStringList
> place
= self
->variables();
186 qscript_createQMakeProjectMap(place
, engine
->globalObject().property("qmake"));
187 QScriptValue
ret(engine
, self
->doProjectTest(func
, args
, place
));
188 engine
->globalObject().setProperty("qmake", qscript_projectWrapper(engine
, self
, place
));
192 static QScriptValue
qscript_call_expandfunction(QScriptContext
*context
, QScriptEngine
*engine
)
194 QMakeProject
*self
= qscriptvalue_cast
<QMakeProject
*>(context
->callee().property("qmakeProject"));
195 QString func
= context
->callee().property("functionName").toString();
197 for(int i
= 0; i
< context
->argumentCount(); ++i
)
198 args
+= context
->argument(i
).toString();
199 QMap
<QString
, QStringList
> place
= self
->variables();
200 qscript_createQMakeProjectMap(place
, engine
->globalObject().property("qmake"));
201 QScriptValue ret
= qScriptValueFromValue(engine
, self
->doProjectExpand(func
, args
, place
));
202 engine
->globalObject().setProperty("qmake", qscript_projectWrapper(engine
, self
, place
));
206 static QScriptValue
qscript_projectWrapper(QScriptEngine
*eng
, QMakeProject
*project
,
207 const QMap
<QString
, QStringList
> &place
)
209 QScriptValue ret
= eng
->newObject();
211 QStringList testFuncs
= qmake_testFunctions().keys() + project
->userTestFunctions();
212 for(int i
= 0; i
< testFuncs
.size(); ++i
) {
213 QString funcName
= testFuncs
.at(i
);
214 QScriptValue fun
= eng
->newFunction(qscript_call_testfunction
);
215 fun
.setProperty("qmakeProject", eng
->newVariant(qVariantFromValue(project
)));
216 fun
.setProperty("functionName", QScriptValue(eng
, funcName
));
217 eng
->globalObject().setProperty(funcName
, fun
);
221 QStringList testFuncs
= qmake_expandFunctions().keys() + project
->userExpandFunctions();
222 for(int i
= 0; i
< testFuncs
.size(); ++i
) {
223 QString funcName
= testFuncs
.at(i
);
224 QScriptValue fun
= eng
->newFunction(qscript_call_expandfunction
);
225 fun
.setProperty("qmakeProject", eng
->newVariant(qVariantFromValue(project
)));
226 fun
.setProperty("functionName", QScriptValue(eng
, funcName
));
227 eng
->globalObject().setProperty(funcName
, fun
);
230 for(QMap
<QString
, QStringList
>::ConstIterator it
= place
.begin(); it
!= place
.end(); ++it
)
231 ret
.setProperty(it
.key(), qScriptValueFromValue(eng
, it
.value()));
235 static QScriptValue
qscript_toArray(QScriptEngine
*eng
, const QStringList
&elts
)
237 QScriptValue a
= eng
->newArray();
238 for (int i
= 0; i
< elts
.count(); ++i
)
239 a
.setProperty(i
, QScriptValue(eng
, elts
.at(i
)));
255 static QString
remove_quotes(const QString
&arg
)
257 const ushort SINGLEQUOTE
= '\'';
258 const ushort DOUBLEQUOTE
= '"';
260 const QChar
*arg_data
= arg
.data();
261 const ushort first
= arg_data
->unicode();
262 const int arg_len
= arg
.length();
263 if(first
== SINGLEQUOTE
|| first
== DOUBLEQUOTE
) {
264 const ushort last
= (arg_data
+arg_len
-1)->unicode();
266 return arg
.mid(1, arg_len
-2);
271 static QString
varMap(const QString
&x
)
274 if(ret
.startsWith("TMAKE")) //tmake no more!
275 ret
= "QMAKE" + ret
.mid(5);
276 else if(ret
== "INTERFACES")
278 else if(ret
== "QMAKE_POST_BUILD")
279 ret
= "QMAKE_POST_LINK";
280 else if(ret
== "TARGETDEPS")
281 ret
= "POST_TARGETDEPS";
282 else if(ret
== "LIBPATH")
283 ret
= "QMAKE_LIBDIR";
284 else if(ret
== "QMAKE_EXT_MOC")
285 ret
= "QMAKE_EXT_CPP_MOC";
286 else if(ret
== "QMAKE_MOD_MOC")
287 ret
= "QMAKE_H_MOD_MOC";
288 else if(ret
== "QMAKE_LFLAGS_SHAPP")
289 ret
= "QMAKE_LFLAGS_APP";
290 else if(ret
== "PRECOMPH")
291 ret
= "PRECOMPILED_HEADER";
292 else if(ret
== "PRECOMPCPP")
293 ret
= "PRECOMPILED_SOURCE";
294 else if(ret
== "INCPATH")
296 else if(ret
== "QMAKE_EXTRA_WIN_COMPILERS" || ret
== "QMAKE_EXTRA_UNIX_COMPILERS")
297 ret
= "QMAKE_EXTRA_COMPILERS";
298 else if(ret
== "QMAKE_EXTRA_WIN_TARGETS" || ret
== "QMAKE_EXTRA_UNIX_TARGETS")
299 ret
= "QMAKE_EXTRA_TARGETS";
300 else if(ret
== "QMAKE_EXTRA_UNIX_INCLUDES")
301 ret
= "QMAKE_EXTRA_INCLUDES";
302 else if(ret
== "QMAKE_EXTRA_UNIX_VARIABLES")
303 ret
= "QMAKE_EXTRA_VARIABLES";
304 else if(ret
== "QMAKE_RPATH")
305 ret
= "QMAKE_LFLAGS_RPATH";
306 else if(ret
== "QMAKE_FRAMEWORKDIR")
307 ret
= "QMAKE_FRAMEWORKPATH";
308 else if(ret
== "QMAKE_FRAMEWORKDIR_FLAGS")
309 ret
= "QMAKE_FRAMEWORKPATH_FLAGS";
313 static QStringList
split_arg_list(QString params
)
318 const ushort LPAREN
= '(';
319 const ushort RPAREN
= ')';
320 const ushort SINGLEQUOTE
= '\'';
321 const ushort DOUBLEQUOTE
= '"';
322 const ushort COMMA
= ',';
323 const ushort SPACE
= ' ';
324 //const ushort TAB = '\t';
327 const QChar
*params_data
= params
.data();
328 const int params_len
= params
.length();
330 while(last
< params_len
&& (params_data
[last
].unicode() == SPACE
331 /*|| params_data[last].unicode() == TAB*/))
333 for(int x
= last
, parens
= 0; x
<= params_len
; x
++) {
334 unicode
= params_data
[x
].unicode();
335 if(x
== params_len
) {
336 while(x
&& params_data
[x
-1].unicode() == SPACE
)
338 QString
mid(params_data
+last
, x
-last
);
340 if(mid
[0] == quote
&& mid
[(int)mid
.length()-1] == quote
)
341 mid
= mid
.mid(1, mid
.length()-2);
347 if(unicode
== LPAREN
) {
349 } else if(unicode
== RPAREN
) {
351 } else if(quote
&& unicode
== quote
) {
353 } else if(!quote
&& (unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
)) {
356 if(!parens
&& !quote
&& unicode
== COMMA
) {
357 QString mid
= params
.mid(last
, x
- last
).trimmed();
360 while(last
< params_len
&& (params_data
[last
].unicode() == SPACE
361 /*|| params_data[last].unicode() == TAB*/))
368 static QStringList
split_value_list(const QString
&vals
, bool do_semicolon
=false)
374 const ushort LPAREN
= '(';
375 const ushort RPAREN
= ')';
376 const ushort SINGLEQUOTE
= '\'';
377 const ushort DOUBLEQUOTE
= '"';
378 const ushort BACKSLASH
= '\\';
379 const ushort SEMICOLON
= ';';
382 const QChar
*vals_data
= vals
.data();
383 const int vals_len
= vals
.length();
384 for(int x
= 0, parens
= 0; x
< vals_len
; x
++) {
385 unicode
= vals_data
[x
].unicode();
386 if(x
!= (int)vals_len
-1 && unicode
== BACKSLASH
&&
387 (vals_data
[x
+1].unicode() == SINGLEQUOTE
|| vals_data
[x
+1].unicode() == DOUBLEQUOTE
)) {
388 build
+= vals_data
[x
++]; //get that 'escape'
389 } else if(!quote
.isEmpty() && unicode
== quote
.top()) {
391 } else if(unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
) {
393 } else if(unicode
== RPAREN
) {
395 } else if(unicode
== LPAREN
) {
399 if(!parens
&& quote
.isEmpty() && ((do_semicolon
&& unicode
== SEMICOLON
) ||
400 vals_data
[x
] == Option::field_sep
)) {
404 build
+= vals_data
[x
];
412 //just a parsable entity
415 ParsableBlock() : ref_cnt(1) { }
416 virtual ~ParsableBlock() { }
421 Parse(const QString
&t
) : text(t
){ pi
= parser
; }
423 QList
<Parse
> parselist
;
425 inline int ref() { return ++ref_cnt
; }
426 inline int deref() { return --ref_cnt
; }
430 virtual bool continueBlock() = 0;
431 bool eval(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
);
434 bool ParsableBlock::eval(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
)
437 parser_info pi
= parser
;
438 const int block_count
= p
->scope_blocks
.count();
442 for(int i
= 0; i
< parselist
.count(); i
++) {
443 parser
= parselist
.at(i
).pi
;
444 if(!(ret
= p
->parse(parselist
.at(i
).text
, place
)) || !continueBlock())
450 while(p
->scope_blocks
.count() > block_count
)
451 p
->scope_blocks
.pop();
456 struct FunctionBlock
: public ParsableBlock
458 FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { }
460 QMap
<QString
, QStringList
> vars
;
461 QMap
<QString
, QStringList
> *calling_place
;
462 QStringList return_value
;
466 bool exec(const QList
<QStringList
> &args
,
467 QMakeProject
*p
, QMap
<QString
, QStringList
> &place
, QStringList
&functionReturn
);
468 virtual bool continueBlock() { return !cause_return
; }
471 bool FunctionBlock::exec(const QList
<QStringList
> &args
,
472 QMakeProject
*proj
, QMap
<QString
, QStringList
> &place
,
473 QStringList
&functionReturn
)
477 calling_place
= &place
;
479 calling_place
= &proj
->variables();
481 return_value
.clear();
482 cause_return
= false;
486 vars
= proj
->variables(); // should be place so that local variables can be inherited
490 vars
["ARGS"].clear();
491 for(int i
= 0; i
< args
.count(); i
++) {
492 vars
["ARGS"] += args
[i
];
493 vars
[QString::number(i
+1)] = args
[i
];
495 bool ret
= ParsableBlock::eval(proj
, vars
);
496 functionReturn
= return_value
;
500 return_value
.clear();
506 struct IteratorBlock
: public ParsableBlock
508 IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { }
517 Test(const QString
&f
, QStringList
&a
, bool i
) : func(f
), args(a
), invert(i
) { pi
= parser
; }
523 bool loop_forever
, cause_break
, cause_next
;
526 bool exec(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
);
527 virtual bool continueBlock() { return !cause_next
&& !cause_break
; }
529 bool IteratorBlock::exec(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
)
532 QStringList::Iterator it
;
535 int iterate_count
= 0;
537 IteratorBlock
*saved_iterator
= p
->iterator
;
541 while(loop_forever
|| it
!= list
.end()) {
542 cause_next
= cause_break
= false;
543 if(!loop_forever
&& (*it
).isEmpty()) { //ignore empty items
548 //set up the loop variable
550 if(!variable
.isEmpty()) {
551 va
= place
[variable
];
553 place
[variable
] = QStringList(QString::number(iterate_count
));
555 place
[variable
] = QStringList(*it
);
559 for(QList
<Test
>::Iterator test_it
= test
.begin(); test_it
!= test
.end(); ++test_it
) {
560 parser
= (*test_it
).pi
;
561 succeed
= p
->doProjectTest((*test_it
).func
, (*test_it
).args
, place
);
562 if((*test_it
).invert
)
568 ret
= ParsableBlock::eval(p
, place
);
569 //restore the variable in the map
570 if(!variable
.isEmpty())
571 place
[variable
] = va
;
576 if(!ret
|| cause_break
)
581 p
->iterator
= saved_iterator
;
585 QMakeProject::ScopeBlock::~ScopeBlock()
593 static void qmake_error_msg(const QString
&msg
)
595 fprintf(stderr
, "%s:%d: %s\n", parser
.file
.toLatin1().constData(), parser
.line_no
,
596 msg
.toLatin1().constData());
600 1) environment variable QMAKEFEATURES (as separated by colons)
601 2) property variable QMAKEFEATURES (as separated by colons)
602 3) <project_root> (where .qmake.cache lives) + FEATURES_DIR
603 4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR
604 5) your QMAKESPEC/features dir
605 6) your data_install/mkspecs/FEATURES_DIR
606 7) your QMAKESPEC/../FEATURES_DIR dir
608 FEATURES_DIR is defined as:
610 1) features/(unix|win32|macx)/
613 QStringList
qmake_feature_paths(QMakeProperty
*prop
=0)
617 const QString base_concat
= QDir::separator() + QString("features");
618 switch(Option::target_mode
) {
619 case Option::TARG_MACX_MODE
: //also a unix
620 concat
<< base_concat
+ QDir::separator() + "mac";
621 concat
<< base_concat
+ QDir::separator() + "macx";
622 concat
<< base_concat
+ QDir::separator() + "unix";
624 case Option::TARG_UNIX_MODE
:
625 concat
<< base_concat
+ QDir::separator() + "unix";
627 case Option::TARG_WIN_MODE
:
628 concat
<< base_concat
+ QDir::separator() + "win32";
630 case Option::TARG_MAC9_MODE
:
631 concat
<< base_concat
+ QDir::separator() + "mac";
632 concat
<< base_concat
+ QDir::separator() + "mac9";
634 case Option::TARG_QNX6_MODE
: //also a unix
635 concat
<< base_concat
+ QDir::separator() + "qnx6";
636 concat
<< base_concat
+ QDir::separator() + "unix";
639 concat
<< base_concat
;
641 const QString mkspecs_concat
= QDir::separator() + QString("mkspecs");
642 QStringList feature_roots
;
643 QByteArray mkspec_path
= qgetenv("QMAKEFEATURES");
644 if(!mkspec_path
.isNull())
645 feature_roots
+= splitPathList(QString::fromLocal8Bit(mkspec_path
));
647 feature_roots
+= splitPathList(prop
->value("QMAKEFEATURES"));
648 if(!Option::mkfile::cachefile
.isEmpty()) {
650 int last_slash
= Option::mkfile::cachefile
.lastIndexOf(Option::dir_sep
);
652 path
= Option::fixPathToLocalOS(Option::mkfile::cachefile
.left(last_slash
));
653 for(QStringList::Iterator concat_it
= concat
.begin();
654 concat_it
!= concat
.end(); ++concat_it
)
655 feature_roots
<< (path
+ (*concat_it
));
657 QByteArray qmakepath
= qgetenv("QMAKEPATH");
658 if (!qmakepath
.isNull()) {
659 const QStringList lst
= splitPathList(QString::fromLocal8Bit(qmakepath
));
660 for(QStringList::ConstIterator it
= lst
.begin(); it
!= lst
.end(); ++it
) {
661 for(QStringList::Iterator concat_it
= concat
.begin();
662 concat_it
!= concat
.end(); ++concat_it
)
663 feature_roots
<< ((*it
) + mkspecs_concat
+ (*concat_it
));
666 if(!Option::mkfile::qmakespec
.isEmpty())
667 feature_roots
<< Option::mkfile::qmakespec
+ QDir::separator() + "features";
668 if(!Option::mkfile::qmakespec
.isEmpty()) {
669 QFileInfo
specfi(Option::mkfile::qmakespec
);
670 QDir
specdir(specfi
.absoluteFilePath());
671 while(!specdir
.isRoot()) {
672 if(!specdir
.cdUp() || specdir
.isRoot())
674 if(QFile::exists(specdir
.path() + QDir::separator() + "features")) {
675 for(QStringList::Iterator concat_it
= concat
.begin();
676 concat_it
!= concat
.end(); ++concat_it
)
677 feature_roots
<< (specdir
.path() + (*concat_it
));
682 for(QStringList::Iterator concat_it
= concat
.begin();
683 concat_it
!= concat
.end(); ++concat_it
)
684 feature_roots
<< (QLibraryInfo::location(QLibraryInfo::PrefixPath
) +
685 mkspecs_concat
+ (*concat_it
));
686 for(QStringList::Iterator concat_it
= concat
.begin();
687 concat_it
!= concat
.end(); ++concat_it
)
688 feature_roots
<< (QLibraryInfo::location(QLibraryInfo::DataPath
) +
689 mkspecs_concat
+ (*concat_it
));
690 return feature_roots
;
693 QStringList
qmake_mkspec_paths()
696 const QString concat
= QDir::separator() + QString("mkspecs");
697 QByteArray qmakepath
= qgetenv("QMAKEPATH");
698 if (!qmakepath
.isEmpty()) {
699 const QStringList lst
= splitPathList(QString::fromLocal8Bit(qmakepath
));
700 for(QStringList::ConstIterator it
= lst
.begin(); it
!= lst
.end(); ++it
)
701 ret
<< ((*it
) + concat
);
703 ret
<< QLibraryInfo::location(QLibraryInfo::DataPath
) + concat
;
708 class QMakeProjectEnv
712 QMakeProjectEnv() { }
713 QMakeProjectEnv(QMakeProject
*p
) { execute(p
->variables()); }
714 QMakeProjectEnv(const QMap
<QString
, QStringList
> &values
) { execute(values
); }
716 void execute(QMakeProject
*p
) { execute(p
->variables()); }
717 void execute(const QMap
<QString
, QStringList
> &values
) {
719 for(QMap
<QString
, QStringList
>::ConstIterator it
= values
.begin(); it
!= values
.end(); ++it
) {
720 const QString var
= it
.key(), val
= it
.value().join(" ");
721 if(!var
.startsWith(".")) {
722 const QString env_var
= Option::sysenv_mod
+ var
;
723 if(!putenv(strdup(QString(env_var
+ "=" + val
).toAscii().data())))
724 envs
.append(env_var
);
733 for(QStringList::ConstIterator it
= envs
.begin();it
!= envs
.end(); ++it
) {
734 putenv(strdup(QString(*it
+ "=").toAscii().data()));
740 QMakeProject::~QMakeProject()
744 for(QMap
<QString
, FunctionBlock
*>::iterator it
= replaceFunctions
.begin(); it
!= replaceFunctions
.end(); ++it
) {
745 if(!it
.value()->deref())
748 replaceFunctions
.clear();
749 for(QMap
<QString
, FunctionBlock
*>::iterator it
= testFunctions
.begin(); it
!= testFunctions
.end(); ++it
) {
750 if(!it
.value()->deref())
753 testFunctions
.clear();
758 QMakeProject::init(QMakeProperty
*p
, const QMap
<QString
, QStringList
> *vars
)
763 prop
= new QMakeProperty
;
772 QMakeProject::QMakeProject(QMakeProject
*p
, const QMap
<QString
, QStringList
> *vars
)
774 init(p
->properties(), vars
? vars
: &p
->variables());
775 for(QMap
<QString
, FunctionBlock
*>::iterator it
= p
->replaceFunctions
.begin(); it
!= p
->replaceFunctions
.end(); ++it
) {
777 replaceFunctions
.insert(it
.key(), it
.value());
779 for(QMap
<QString
, FunctionBlock
*>::iterator it
= p
->testFunctions
.begin(); it
!= p
->testFunctions
.end(); ++it
) {
781 testFunctions
.insert(it
.key(), it
.value());
786 QMakeProject::reset()
788 // scope_blocks starts with one non-ignoring entity
789 scope_blocks
.clear();
790 scope_blocks
.push(ScopeBlock());
796 QMakeProject::parse(const QString
&t
, QMap
<QString
, QStringList
> &place
, int numLines
)
798 QString s
= t
.simplified();
799 int hash_mark
= s
.indexOf("#");
800 if(hash_mark
!= -1) //good bye comments
801 s
= s
.left(hash_mark
);
802 if(s
.isEmpty()) // blank_line
805 if(scope_blocks
.top().ignore
) {
806 bool continue_parsing
= false;
807 // adjust scope for each block which appears on a single line
808 for(int i
= 0; i
< s
.length(); i
++) {
810 scope_blocks
.push(ScopeBlock(true));
811 } else if(s
[i
] == '}') {
812 if(scope_blocks
.count() == 1) {
813 fprintf(stderr
, "Braces mismatch %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
816 ScopeBlock sb
= scope_blocks
.pop();
818 sb
.iterate
->exec(this, place
);
822 if(!scope_blocks
.top().ignore
) {
823 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
824 parser
.line_no
, scope_blocks
.count()+1);
825 s
= s
.mid(i
+1).trimmed();
826 continue_parsing
= !s
.isEmpty();
831 if(!continue_parsing
) {
832 debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
833 parser
.file
.toLatin1().constData(), parser
.line_no
);
841 const QChar
*d
= s
.unicode();
842 bool function_finished
= false;
843 while(d_off
< s
.length()) {
844 if(*(d
+d_off
) == QLatin1Char('}')) {
845 function
->scope_level
--;
846 if(!function
->scope_level
) {
847 function_finished
= true;
850 } else if(*(d
+d_off
) == QLatin1Char('{')) {
851 function
->scope_level
++;
853 append
+= *(d
+d_off
);
856 if(!append
.isEmpty())
857 function
->parselist
.append(IteratorBlock::Parse(append
));
858 if(function_finished
) {
860 s
= QString(d
+d_off
, s
.length()-d_off
);
864 } else if(IteratorBlock
*it
= scope_blocks
.top().iterate
) {
867 const QChar
*d
= s
.unicode();
868 bool iterate_finished
= false;
869 while(d_off
< s
.length()) {
870 if(*(d
+d_off
) == QLatin1Char('}')) {
872 if(!it
->scope_level
) {
873 iterate_finished
= true;
876 } else if(*(d
+d_off
) == QLatin1Char('{')) {
879 append
+= *(d
+d_off
);
882 if(!append
.isEmpty())
883 scope_blocks
.top().iterate
->parselist
.append(IteratorBlock::Parse(append
));
884 if(iterate_finished
) {
885 scope_blocks
.top().iterate
= 0;
886 bool ret
= it
->exec(this, place
);
896 QString scope
, var
, op
;
898 #define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o
899 const QChar
*d
= s
.unicode();
901 SKIP_WS(d
, d_off
, s
.length());
902 IteratorBlock
*iterator
= 0;
903 bool scope_failed
= false, else_line
= false, or_op
=false;
905 int parens
= 0, scope_count
=0, start_block
= 0;
906 while(d_off
< s
.length()) {
908 if(*(d
+d_off
) == QLatin1Char('='))
910 if(*(d
+d_off
) == QLatin1Char('+') || *(d
+d_off
) == QLatin1Char('-') ||
911 *(d
+d_off
) == QLatin1Char('*') || *(d
+d_off
) == QLatin1Char('~')) {
912 if(*(d
+d_off
+1) == QLatin1Char('=')) {
914 } else if(*(d
+d_off
+1) == QLatin1Char(' ')) {
915 const QChar
*k
= d
+d_off
+1;
917 SKIP_WS(k
, k_off
, s
.length()-d_off
);
918 if(*(k
+k_off
) == QLatin1Char('=')) {
920 qmake_error_msg(QString(d
+d_off
, 1) + "must be followed immediately by =");
927 if(!quote
.isNull()) {
928 if(*(d
+d_off
) == quote
)
930 } else if(*(d
+d_off
) == '(') {
932 } else if(*(d
+d_off
) == ')') {
934 } else if(*(d
+d_off
) == '"' /*|| *(d+d_off) == '\''*/) {
938 if(!parens
&& quote
.isNull() &&
939 (*(d
+d_off
) == QLatin1Char(':') || *(d
+d_off
) == QLatin1Char('{') ||
940 *(d
+d_off
) == QLatin1Char(')') || *(d
+d_off
) == QLatin1Char('|'))) {
942 scope
= var
.trimmed();
943 if(*(d
+d_off
) == QLatin1Char(')'))
944 scope
+= *(d
+d_off
); // need this
947 bool test
= scope_failed
;
948 if(scope
.isEmpty()) {
950 } else if(scope
.toLower() == "else") { //else is a builtin scope here as it modifies state
951 if(scope_count
!= 1 || scope_blocks
.top().else_status
== ScopeBlock::TestNone
) {
952 qmake_error_msg(("Unexpected " + scope
+ " ('" + s
+ "')").toLatin1());
956 test
= (scope_blocks
.top().else_status
== ScopeBlock::TestSeek
);
957 debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser
.file
.toLatin1().constData(), parser
.line_no
,
958 scope
== "else" ? "" : QString(" (" + scope
+ ")").toLatin1().constData(),
959 test
? "considered" : "excluded");
961 QString comp_scope
= scope
;
962 bool invert_test
= (comp_scope
.at(0) == QLatin1Char('!'));
964 comp_scope
= comp_scope
.mid(1);
965 int lparen
= comp_scope
.indexOf('(');
966 if(or_op
== scope_failed
) {
967 if(lparen
!= -1) { // if there is an lparen in the scope, it IS a function
968 int rparen
= comp_scope
.lastIndexOf(')');
970 qmake_error_msg("Function missing right paren: " + comp_scope
);
973 QString func
= comp_scope
.left(lparen
);
974 QStringList args
= split_arg_list(comp_scope
.mid(lparen
+1, rparen
- lparen
- 1));
976 fprintf(stderr
, "%s:%d: No tests can come after a function definition!\n",
977 parser
.file
.toLatin1().constData(), parser
.line_no
);
979 } else if(func
== "for") { //for is a builtin function here, as it modifies state
980 if(args
.count() > 2 || args
.count() < 1) {
981 fprintf(stderr
, "%s:%d: for(iterate, list) requires two arguments.\n",
982 parser
.file
.toLatin1().constData(), parser
.line_no
);
984 } else if(iterator
) {
985 fprintf(stderr
, "%s:%d unexpected nested for()\n",
986 parser
.file
.toLatin1().constData(), parser
.line_no
);
990 iterator
= new IteratorBlock
;
992 if(args
.count() == 1) {
993 doVariableReplace(args
[0], place
);
995 if(args
[0] != "ever") {
998 fprintf(stderr
, "%s:%d: for(iterate, list) requires two arguments.\n",
999 parser
.file
.toLatin1().constData(), parser
.line_no
);
1002 it_list
= "forever";
1003 } else if(args
.count() == 2) {
1004 iterator
->variable
= args
[0];
1005 doVariableReplace(args
[1], place
);
1008 QStringList list
= place
[it_list
];
1009 if(list
.isEmpty()) {
1010 if(it_list
== "forever") {
1011 iterator
->loop_forever
= true;
1013 int dotdot
= it_list
.indexOf("..");
1016 int start
= it_list
.left(dotdot
).toInt(&ok
);
1018 int end
= it_list
.mid(dotdot
+2).toInt(&ok
);
1021 for(int i
= start
; i
<= end
; i
++)
1022 list
<< QString::number(i
);
1024 for(int i
= start
; i
>= end
; i
--)
1025 list
<< QString::number(i
);
1032 iterator
->list
= list
;
1033 test
= !invert_test
;
1034 } else if(iterator
) {
1035 iterator
->test
.append(IteratorBlock::Test(func
, args
, invert_test
));
1036 test
= !invert_test
;
1037 } else if(func
== "defineTest" || func
== "defineReplace") {
1038 if(!function_blocks
.isEmpty()) {
1040 "%s:%d: cannot define a function within another definition.\n",
1041 parser
.file
.toLatin1().constData(), parser
.line_no
);
1044 if(args
.count() != 1) {
1045 fprintf(stderr
, "%s:%d: %s(function_name) requires one argument.\n",
1046 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
1049 QMap
<QString
, FunctionBlock
*> *map
= 0;
1050 if(func
== "defineTest")
1051 map
= &testFunctions
;
1053 map
= &replaceFunctions
;
1055 if(!map
|| map
->contains(args
[0])) {
1056 fprintf(stderr
, "%s:%d: Function[%s] multiply defined.\n",
1057 parser
.file
.toLatin1().constData(), parser
.line_no
, args
[0].toLatin1().constData());
1061 function
= new FunctionBlock
;
1062 map
->insert(args
[0], function
);
1065 test
= doProjectTest(func
, args
, place
);
1066 if(*(d
+d_off
) == QLatin1Char(')') && d_off
== s
.length()-1) {
1069 scope_blocks
.top().else_status
=
1070 (test
? ScopeBlock::TestFound
: ScopeBlock::TestSeek
);
1071 return true; // assume we are done
1075 QString cscope
= comp_scope
.trimmed();
1076 doVariableReplace(cscope
, place
);
1077 test
= isActiveConfig(cscope
.trimmed(), true, &place
);
1083 if(!test
&& !scope_failed
)
1084 debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser
.file
.toLatin1().constData(),
1085 parser
.line_no
, scope
.toLatin1().constData());
1087 scope_failed
= !test
;
1088 or_op
= (*(d
+d_off
) == QLatin1Char('|'));
1090 if(*(d
+d_off
) == QLatin1Char('{')) { // scoping block
1093 for(int off
= 0, braces
= 0; true; ++off
) {
1094 if(*(d
+d_off
+off
) == QLatin1Char('{'))
1096 else if(*(d
+d_off
+off
) == QLatin1Char('}') && braces
)
1098 if(!braces
|| d_off
+off
== s
.length()) {
1099 iterator
->parselist
.append(s
.mid(d_off
, off
-1));
1101 iterator
->scope_level
+= braces
-1;
1108 } else if(!parens
&& *(d
+d_off
) == QLatin1Char('}')) {
1111 } else if(!scope_blocks
.count()) {
1112 warn_msg(WarnParser
, "Possible braces mismatch %s:%d", parser
.file
.toLatin1().constData(), parser
.line_no
);
1114 if(scope_blocks
.count() == 1) {
1115 fprintf(stderr
, "Braces mismatch %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
1118 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
1119 parser
.line_no
, scope_blocks
.count());
1120 ScopeBlock sb
= scope_blocks
.pop();
1122 sb
.iterate
->exec(this, place
);
1129 var
= var
.trimmed();
1131 if(!else_line
|| (else_line
&& !scope_failed
))
1132 scope_blocks
.top().else_status
= (!scope_failed
? ScopeBlock::TestFound
: ScopeBlock::TestSeek
);
1134 ScopeBlock
next_block(scope_failed
);
1135 next_block
.iterate
= iterator
;
1137 next_block
.else_status
= ScopeBlock::TestNone
;
1138 else if(scope_failed
)
1139 next_block
.else_status
= ScopeBlock::TestSeek
;
1141 next_block
.else_status
= ScopeBlock::TestFound
;
1142 scope_blocks
.push(next_block
);
1143 debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser
.file
.toLatin1().constData(),
1144 parser
.line_no
, scope_blocks
.count(), scope_failed
, s
.toLatin1().constData());
1145 } else if(iterator
) {
1146 iterator
->parselist
.append(var
+s
.mid(d_off
));
1147 bool ret
= iterator
->exec(this, place
);
1152 if((!scope_count
&& !var
.isEmpty()) || (scope_count
== 1 && else_line
))
1153 scope_blocks
.top().else_status
= ScopeBlock::TestNone
;
1154 if(d_off
== s
.length()) {
1155 if(!var
.trimmed().isEmpty())
1156 qmake_error_msg(("Parse Error ('" + s
+ "')").toLatin1());
1157 return var
.isEmpty(); // allow just a scope
1160 SKIP_WS(d
, d_off
, s
.length());
1161 for(; d_off
< s
.length() && op
.indexOf('=') == -1; op
+= *(d
+(d_off
++)))
1163 op
.replace(QRegExp("\\s"), "");
1165 SKIP_WS(d
, d_off
, s
.length());
1166 QString vals
= s
.mid(d_off
); // vals now contains the space separated list of values
1167 int rbraces
= vals
.count('}'), lbraces
= vals
.count('{');
1168 if(scope_blocks
.count() > 1 && rbraces
- lbraces
== 1) {
1169 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
1170 parser
.line_no
, scope_blocks
.count());
1171 ScopeBlock sb
= scope_blocks
.pop();
1173 sb
.iterate
->exec(this, place
);
1174 vals
.truncate(vals
.length()-1);
1175 } else if(rbraces
!= lbraces
) {
1176 warn_msg(WarnParser
, "Possible braces mismatch {%s} %s:%d",
1177 vals
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1180 return true; // oh well
1183 doVariableReplace(var
, place
);
1184 var
= varMap(var
); //backwards compatability
1185 if(!var
.isEmpty() && Option::mkfile::do_preprocess
) {
1186 static QString
last_file("*none*");
1187 if(parser
.file
!= last_file
) {
1188 fprintf(stdout
, "#file %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
1189 last_file
= parser
.file
;
1191 fprintf(stdout
, "%s %s %s\n", var
.toLatin1().constData(), op
.toLatin1().constData(), vals
.toLatin1().constData());
1194 if(vals
.contains('=') && numLines
> 1)
1195 warn_msg(WarnParser
, "Detected possible line continuation: {%s} %s:%d",
1196 var
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1198 QStringList
&varlist
= place
[var
]; // varlist is the list in the symbol table
1200 if(Option::debug_level
>= 1) {
1201 QString tmp_vals
= vals
;
1202 doVariableReplace(tmp_vals
, place
);
1203 debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser
.file
.toLatin1().constData(), parser
.line_no
,
1204 var
.toLatin1().constData(), op
.toLatin1().constData(), tmp_vals
.toLatin1().constData());
1207 // now do the operation
1209 doVariableReplace(vals
, place
);
1210 if(vals
.length() < 4 || vals
.at(0) != 's') {
1211 qmake_error_msg(("~= operator only can handle s/// function ('" +
1212 s
+ "')").toLatin1());
1215 QChar sep
= vals
.at(1);
1216 QStringList func
= vals
.split(sep
);
1217 if(func
.count() < 3 || func
.count() > 4) {
1218 qmake_error_msg(("~= operator only can handle s/// function ('" +
1219 s
+ "')").toLatin1());
1222 bool global
= false, case_sense
= true, quote
= false;
1223 if(func
.count() == 4) {
1224 global
= func
[3].indexOf('g') != -1;
1225 case_sense
= func
[3].indexOf('i') == -1;
1226 quote
= func
[3].indexOf('q') != -1;
1228 QString from
= func
[1], to
= func
[2];
1230 from
= QRegExp::escape(from
);
1231 QRegExp
regexp(from
, case_sense
? Qt::CaseSensitive
: Qt::CaseInsensitive
);
1232 for(QStringList::Iterator varit
= varlist
.begin(); varit
!= varlist
.end();) {
1233 if((*varit
).contains(regexp
)) {
1234 (*varit
) = (*varit
).replace(regexp
, to
);
1235 if ((*varit
).isEmpty())
1236 varit
= varlist
.erase(varit
);
1245 QStringList vallist
;
1247 //doVariableReplace(vals, place);
1248 QStringList tmp
= split_value_list(vals
, (var
== "DEPENDPATH" || var
== "INCLUDEPATH"));
1249 for(int i
= 0; i
< tmp
.size(); ++i
)
1250 vallist
+= doVariableReplaceExpand(tmp
[i
], place
);
1254 if(!varlist
.isEmpty()) {
1255 bool send_warning
= false;
1256 if(var
!= "TEMPLATE" && var
!= "TARGET") {
1257 QSet
<QString
> incoming_vals
= vallist
.toSet();
1258 for(int i
= 0; i
< varlist
.size(); ++i
) {
1259 const QString var
= varlist
.at(i
).trimmed();
1260 if(!var
.isEmpty() && !incoming_vals
.contains(var
)) {
1261 send_warning
= true;
1267 warn_msg(WarnParser
, "Operator=(%s) clears variables previously set: %s:%d",
1268 var
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1272 for(QStringList::ConstIterator valit
= vallist
.begin();
1273 valit
!= vallist
.end(); ++valit
) {
1274 if((*valit
).isEmpty())
1276 if((op
== "*=" && !varlist
.contains((*valit
))) ||
1277 op
== "=" || op
== "+=")
1278 varlist
.append((*valit
));
1280 varlist
.removeAll((*valit
));
1282 if(var
== "REQUIRES") // special case to get communicated to backends!
1283 doProjectCheckReqs(vallist
, place
);
1289 QMakeProject::read(QTextStream
&file
, QMap
<QString
, QStringList
> &place
)
1294 while(!file
.atEnd()) {
1296 QString line
= file
.readLine().trimmed();
1297 int prelen
= line
.length();
1299 int hash_mark
= line
.indexOf("#");
1300 if(hash_mark
!= -1) //good bye comments
1301 line
= line
.left(hash_mark
).trimmed();
1302 if(!line
.isEmpty() && line
.right(1) == "\\") {
1303 if(!line
.startsWith("#")) {
1304 line
.truncate(line
.length() - 1);
1305 s
+= line
+ Option::field_sep
;
1308 } else if(!line
.isEmpty() || (line
.isEmpty() && !prelen
)) {
1309 if(s
.isEmpty() && line
.isEmpty())
1311 if(!line
.isEmpty()) {
1316 if(!(ret
= parse(s
, place
, numLines
))) {
1327 ret
= parse(s
, place
, numLines
);
1332 QMakeProject::read(const QString
&file
, QMap
<QString
, QStringList
> &place
)
1334 parser_info pi
= parser
;
1337 const QString oldpwd
= qmake_getpwd();
1338 QString filename
= Option::fixPathToLocalOS(file
);
1339 doVariableReplace(filename
, place
);
1340 bool ret
= false, using_stdin
= false;
1342 if(!strcmp(filename
.toLatin1(), "-")) {
1343 qfile
.setFileName("");
1344 ret
= qfile
.open(stdin
, QIODevice::ReadOnly
);
1346 } else if(QFileInfo(file
).isDir()) {
1349 qfile
.setFileName(filename
);
1350 ret
= qfile
.open(QIODevice::ReadOnly
);
1351 qmake_setpwd(QFileInfo(filename
).absolutePath());
1354 parser_info pi
= parser
;
1355 parser
.from_file
= true;
1356 parser
.file
= filename
;
1358 QTextStream
t(&qfile
);
1359 ret
= read(t
, place
);
1363 if(scope_blocks
.count() != 1) {
1364 qmake_error_msg("Unterminated conditional block at end of file");
1368 qmake_setpwd(oldpwd
);
1373 QMakeProject::read(const QString
&project
, uchar cmd
)
1375 pfile
= QFileInfo(project
).absoluteFilePath();
1380 QMakeProject::read(uchar cmd
)
1382 if(cfile
.isEmpty()) {
1383 //find out where qmake (myself) lives
1384 if (!base_vars
.contains("QMAKE_QMAKE")) {
1385 if (!Option::qmake_abslocation
.isNull())
1386 base_vars
["QMAKE_QMAKE"] = QStringList(Option::qmake_abslocation
);
1388 base_vars
["QMAKE_QMAKE"] = QStringList("qmake");
1391 // hack to get the Option stuff in there
1392 base_vars
["QMAKE_EXT_OBJ"] = QStringList(Option::obj_ext
);
1393 base_vars
["QMAKE_EXT_CPP"] = Option::cpp_ext
;
1394 base_vars
["QMAKE_EXT_C"] = Option::c_ext
;
1395 base_vars
["QMAKE_EXT_H"] = Option::h_ext
;
1396 base_vars
["QMAKE_SH"] = Option::shellPath
;
1397 if(!Option::user_template_prefix
.isEmpty())
1398 base_vars
["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix
);
1400 if(cmd
& ReadCache
&& Option::mkfile::do_cache
) { // parse the cache
1401 int cache_depth
= -1;
1402 QString qmake_cache
= Option::mkfile::cachefile
;
1403 if(qmake_cache
.isEmpty()) { //find it as it has not been specified
1404 QString dir
= QDir::toNativeSeparators(Option::output_dir
);
1405 while(!QFile::exists((qmake_cache
= dir
+ QDir::separator() + ".qmake.cache"))) {
1406 dir
= dir
.left(dir
.lastIndexOf(QDir::separator()));
1407 if(dir
.isEmpty() || dir
.indexOf(QDir::separator()) == -1) {
1411 if(cache_depth
== -1)
1417 QString abs_cache
= QFileInfo(Option::mkfile::cachefile
).absoluteDir().path();
1418 if(Option::output_dir
.startsWith(abs_cache
))
1419 cache_depth
= Option::output_dir
.mid(abs_cache
.length()).count('/');
1421 if(!qmake_cache
.isEmpty()) {
1422 if(read(qmake_cache
, cache
)) {
1423 Option::mkfile::cachefile_depth
= cache_depth
;
1424 Option::mkfile::cachefile
= qmake_cache
;
1425 if(Option::mkfile::qmakespec
.isEmpty() && !cache
["QMAKESPEC"].isEmpty())
1426 Option::mkfile::qmakespec
= cache
["QMAKESPEC"].first();
1430 if(cmd
& ReadConf
) { // parse mkspec
1431 QString qmakespec
= fixEnvVariables(Option::mkfile::qmakespec
);
1432 QStringList mkspec_roots
= qmake_mkspec_paths();
1433 debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec
.toLatin1().constData(),
1434 mkspec_roots
.join("::").toLatin1().constData());
1435 if(qmakespec
.isEmpty()) {
1436 for(QStringList::ConstIterator it
= mkspec_roots
.begin(); it
!= mkspec_roots
.end(); ++it
) {
1437 QString mkspec
= (*it
) + QDir::separator() + "default";
1438 QFileInfo
default_info(mkspec
);
1439 if(default_info
.exists() && default_info
.isDir()) {
1444 if(qmakespec
.isEmpty()) {
1445 fprintf(stderr
, "QMAKESPEC has not been set, so configuration cannot be deduced.\n");
1448 Option::mkfile::qmakespec
= qmakespec
;
1451 if(QDir::isRelativePath(qmakespec
)) {
1452 if (QFile::exists(qmakespec
+"/qmake.conf")) {
1453 Option::mkfile::qmakespec
= QFileInfo(Option::mkfile::qmakespec
).absoluteFilePath();
1454 } else if (QFile::exists(Option::output_dir
+"/"+qmakespec
+"/qmake.conf")) {
1455 qmakespec
= Option::mkfile::qmakespec
= QFileInfo(Option::output_dir
+"/"+qmakespec
).absoluteFilePath();
1457 bool found_mkspec
= false;
1458 for(QStringList::ConstIterator it
= mkspec_roots
.begin(); it
!= mkspec_roots
.end(); ++it
) {
1459 QString mkspec
= (*it
) + QDir::separator() + qmakespec
;
1460 if(QFile::exists(mkspec
)) {
1461 found_mkspec
= true;
1462 Option::mkfile::qmakespec
= qmakespec
= mkspec
;
1467 fprintf(stderr
, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n",
1468 qmakespec
.toLatin1().constData(), mkspec_roots
.join("\n\t").toLatin1().constData());
1474 // parse qmake configuration
1475 while(qmakespec
.endsWith(QString(QChar(QDir::separator()))))
1476 qmakespec
.truncate(qmakespec
.length()-1);
1477 QString spec
= qmakespec
+ QDir::separator() + "qmake.conf";
1478 if(!QFile::exists(spec
) &&
1479 QFile::exists(qmakespec
+ QDir::separator() + "tmake.conf"))
1480 spec
= qmakespec
+ QDir::separator() + "tmake.conf";
1481 debug_msg(1, "QMAKESPEC conf: reading %s", spec
.toLatin1().constData());
1482 if(!read(spec
, base_vars
)) {
1483 fprintf(stderr
, "Failure to read QMAKESPEC conf file %s.\n", spec
.toLatin1().constData());
1486 if(Option::mkfile::do_cache
&& !Option::mkfile::cachefile
.isEmpty()) {
1487 debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile
.toLatin1().constData());
1488 read(Option::mkfile::cachefile
, base_vars
);
1492 if(cmd
& ReadFeatures
) {
1493 debug_msg(1, "Processing default_pre: %s", vars
["CONFIG"].join("::").toLatin1().constData());
1494 if(doProjectInclude("default_pre", IncludeFlagFeature
, base_vars
) == IncludeNoExist
)
1495 doProjectInclude("default", IncludeFlagFeature
, base_vars
);
1499 vars
= base_vars
; // start with the base
1502 if(pfile
!= "-" && vars
["TARGET"].isEmpty())
1503 vars
["TARGET"].append(QFileInfo(pfile
).baseName());
1505 //before commandline
1506 if(cmd
& ReadCmdLine
) {
1508 parser
.file
= "(internal)";
1509 parser
.from_file
= false;
1510 parser
.line_no
= 1; //really arg count now.. duh
1512 for(QStringList::ConstIterator it
= Option::before_user_vars
.begin();
1513 it
!= Option::before_user_vars
.end(); ++it
) {
1514 if(!parse((*it
), vars
)) {
1515 fprintf(stderr
, "Argument failed to parse: %s\n", (*it
).toLatin1().constData());
1522 //commandline configs
1523 if(cmd
& ReadConfigs
&& !Option::user_configs
.isEmpty()) {
1524 parser
.file
= "(configs)";
1525 parser
.from_file
= false;
1526 parser
.line_no
= 1; //really arg count now.. duh
1527 parse("CONFIG += " + Option::user_configs
.join(" "), vars
);
1530 if(cmd
& ReadProFile
) { // parse project file
1531 debug_msg(1, "Project file: reading %s", pfile
.toLatin1().constData());
1532 if(pfile
!= "-" && !QFile::exists(pfile
) && !pfile
.endsWith(".pro"))
1534 if(!read(pfile
, vars
))
1538 if(cmd
& ReadPostFiles
) { // parse post files
1539 const QStringList l
= vars
["QMAKE_POST_INCLUDE_FILES"];
1540 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
1541 if(read((*it
), vars
)) {
1542 if(vars
["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf((*it
)) == -1)
1543 vars
["QMAKE_INTERNAL_INCLUDED_FILES"].append((*it
));
1548 if(cmd
& ReadCmdLine
) {
1549 parser
.file
= "(internal)";
1550 parser
.from_file
= false;
1551 parser
.line_no
= 1; //really arg count now.. duh
1553 for(QStringList::ConstIterator it
= Option::after_user_vars
.begin();
1554 it
!= Option::after_user_vars
.end(); ++it
) {
1555 if(!parse((*it
), vars
)) {
1556 fprintf(stderr
, "Argument failed to parse: %s\n", (*it
).toLatin1().constData());
1563 //after configs (set in BUILDS)
1564 if(cmd
& ReadConfigs
&& !Option::after_user_configs
.isEmpty()) {
1565 parser
.file
= "(configs)";
1566 parser
.from_file
= false;
1567 parser
.line_no
= 1; //really arg count now.. duh
1568 parse("CONFIG += " + Option::after_user_configs
.join(" "), vars
);
1571 if(pfile
!= "-" && vars
["TARGET"].isEmpty())
1572 vars
["TARGET"].append(QFileInfo(pfile
).baseName());
1574 if(cmd
& ReadConfigs
&& !Option::user_configs
.isEmpty()) {
1575 parser
.file
= "(configs)";
1576 parser
.from_file
= false;
1577 parser
.line_no
= 1; //really arg count now.. duh
1578 parse("CONFIG += " + Option::user_configs
.join(" "), base_vars
);
1581 if(cmd
& ReadFeatures
) {
1582 debug_msg(1, "Processing default_post: %s", vars
["CONFIG"].join("::").toLatin1().constData());
1583 doProjectInclude("default_post", IncludeFlagFeature
, vars
);
1585 QHash
<QString
, bool> processed
;
1586 const QStringList
&configs
= vars
["CONFIG"];
1587 debug_msg(1, "Processing CONFIG features: %s", configs
.join("::").toLatin1().constData());
1589 bool finished
= true;
1590 for(int i
= configs
.size()-1; i
>= 0; --i
) {
1591 const QString config
= configs
[i
].toLower();
1592 if(!processed
.contains(config
)) {
1593 processed
.insert(config
, true);
1594 if(doProjectInclude(config
, IncludeFlagFeature
, vars
) == IncludeSuccess
) {
1604 Option::postProcessProject(this); // let Option post-process
1609 QMakeProject::isActiveConfig(const QString
&x
, bool regex
, QMap
<QString
, QStringList
> *place
)
1614 //magic types for easy flipping
1617 else if(x
== "false")
1621 if((Option::target_mode
== Option::TARG_MACX_MODE
|| Option::target_mode
== Option::TARG_QNX6_MODE
||
1622 Option::target_mode
== Option::TARG_UNIX_MODE
) && x
== "unix")
1624 else if(Option::target_mode
== Option::TARG_MACX_MODE
&& x
== "macx")
1626 else if(Option::target_mode
== Option::TARG_QNX6_MODE
&& x
== "qnx6")
1628 else if(Option::target_mode
== Option::TARG_MAC9_MODE
&& x
== "mac9")
1630 else if((Option::target_mode
== Option::TARG_MAC9_MODE
|| Option::target_mode
== Option::TARG_MACX_MODE
) &&
1633 else if(Option::target_mode
== Option::TARG_WIN_MODE
&& x
== "win32")
1635 QRegExp
re(x
, Qt::CaseSensitive
, QRegExp::Wildcard
);
1636 static QString spec
;
1638 spec
= QFileInfo(Option::mkfile::qmakespec
).fileName();
1639 if((regex
&& re
.exactMatch(spec
)) || (!regex
&& spec
== x
))
1642 else if(spec
== "default") {
1643 static char *buffer
= NULL
;
1645 buffer
= (char *)malloc(1024);
1646 qmakeAddCacheClear(qmakeFreeCacheClear
, (void**)&buffer
);
1648 int l
= readlink(Option::mkfile::qmakespec
.toLatin1(), buffer
, 1024);
1652 if(r
.lastIndexOf('/') != -1)
1653 r
= r
.mid(r
.lastIndexOf('/') + 1);
1654 if((regex
&& re
.exactMatch(r
)) || (!regex
&& r
== x
))
1658 #elif defined(Q_OS_WIN)
1659 else if(spec
== "default") {
1660 // We can't resolve symlinks as they do on Unix, so configure.exe puts the source of the
1661 // qmake.conf at the end of the default/qmake.conf in the QMAKESPEC_ORG variable.
1662 const QStringList
&spec_org
= (place
? (*place
)["QMAKESPEC_ORIGINAL"]
1663 : vars
["QMAKESPEC_ORIGINAL"]);
1664 if (!spec_org
.isEmpty()) {
1665 spec
= spec_org
.at(0);
1666 int lastSlash
= spec
.lastIndexOf('/');
1668 spec
= spec
.mid(lastSlash
+ 1);
1669 if((regex
&& re
.exactMatch(spec
)) || (!regex
&& spec
== x
))
1676 const QStringList
&configs
= (place
? (*place
)["CONFIG"] : vars
["CONFIG"]);
1677 for(QStringList::ConstIterator it
= configs
.begin(); it
!= configs
.end(); ++it
) {
1678 if(((regex
&& re
.exactMatch((*it
))) || (!regex
&& (*it
) == x
)) && re
.exactMatch((*it
)))
1685 QMakeProject::doProjectTest(QString str
, QMap
<QString
, QStringList
> &place
)
1687 QString chk
= remove_quotes(str
);
1690 bool invert_test
= (chk
.left(1) == "!");
1695 int lparen
= chk
.indexOf('(');
1696 if(lparen
!= -1) { // if there is an lparen in the chk, it IS a function
1697 int rparen
= chk
.indexOf(')', lparen
);
1699 qmake_error_msg("Function missing right paren: " + chk
);
1701 QString func
= chk
.left(lparen
);
1702 test
= doProjectTest(func
, chk
.mid(lparen
+1, rparen
- lparen
- 1), place
);
1705 test
= isActiveConfig(chk
, true, &place
);
1713 QMakeProject::doProjectTest(QString func
, const QString
¶ms
,
1714 QMap
<QString
, QStringList
> &place
)
1716 return doProjectTest(func
, split_arg_list(params
), place
);
1719 QMakeProject::IncludeStatus
1720 QMakeProject::doProjectInclude(QString file
, uchar flags
, QMap
<QString
, QStringList
> &place
)
1722 enum { UnknownFormat
, ProFormat
, JSFormat
} format
= UnknownFormat
;
1723 if(flags
& IncludeFlagFeature
) {
1724 if(!file
.endsWith(Option::prf_ext
))
1725 file
+= Option::prf_ext
;
1726 if(file
.indexOf(Option::dir_sep
) == -1 || !QFile::exists(file
)) {
1727 static QStringList
*feature_roots
= 0;
1728 if(!feature_roots
) {
1729 feature_roots
= new QStringList(qmake_feature_paths(prop
));
1730 qmakeAddCacheClear(qmakeDeleteCacheClear_QStringList
, (void**)&feature_roots
);
1732 debug_msg(2, "Looking for feature '%s' in (%s)", file
.toLatin1().constData(),
1733 feature_roots
->join("::").toLatin1().constData());
1735 if(parser
.from_file
) {
1736 QFileInfo
currFile(parser
.file
), prfFile(file
);
1737 if(currFile
.fileName() == prfFile
.fileName()) {
1738 currFile
= QFileInfo(currFile
.canonicalFilePath());
1739 for(int root
= 0; root
< feature_roots
->size(); ++root
) {
1740 prfFile
= QFileInfo(feature_roots
->at(root
) +
1741 QDir::separator() + file
).canonicalFilePath();
1742 if(prfFile
== currFile
) {
1743 start_root
= root
+1;
1749 for(int root
= start_root
; root
< feature_roots
->size(); ++root
) {
1750 QString
prf(feature_roots
->at(root
) + QDir::separator() + file
);
1751 if(QFile::exists(prf
+ Option::js_ext
)) {
1753 file
= prf
+ Option::js_ext
;
1755 } else if(QFile::exists(prf
)) {
1761 if(format
== UnknownFormat
)
1762 return IncludeNoExist
;
1763 if(place
["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file
) != -1)
1764 return IncludeFeatureAlreadyLoaded
;
1765 place
["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file
);
1768 if(QDir::isRelativePath(file
)) {
1769 QStringList include_roots
;
1770 if(Option::output_dir
!= qmake_getpwd())
1771 include_roots
<< qmake_getpwd();
1772 include_roots
<< Option::output_dir
;
1773 for(int root
= 0; root
< include_roots
.size(); ++root
) {
1774 QString testName
= QDir::toNativeSeparators(include_roots
[root
]);
1775 if (!testName
.endsWith(QString(QDir::separator())))
1776 testName
+= QDir::separator();
1778 if(QFile::exists(testName
)) {
1784 if(format
== UnknownFormat
) {
1785 if(QFile::exists(file
)) {
1786 if(file
.endsWith(Option::js_ext
))
1791 return IncludeNoExist
;
1794 if(Option::mkfile::do_preprocess
) //nice to see this first..
1795 fprintf(stderr
, "#switching file %s(%s) - %s:%d\n", (flags
& IncludeFlagFeature
) ? "load" : "include",
1796 file
.toLatin1().constData(),
1797 parser
.file
.toLatin1().constData(), parser
.line_no
);
1798 debug_msg(1, "Project Parser: %s'ing file %s.", (flags
& IncludeFlagFeature
) ? "load" : "include",
1799 file
.toLatin1().constData());
1801 QString orig_file
= file
;
1802 int di
= file
.lastIndexOf(QDir::separator());
1803 QString oldpwd
= qmake_getpwd();
1805 if(!qmake_setpwd(file
.left(file
.lastIndexOf(QDir::separator())))) {
1806 fprintf(stderr
, "Cannot find directory: %s\n", file
.left(di
).toLatin1().constData());
1807 return IncludeFailure
;
1809 file
= file
.right(file
.length() - di
- 1);
1811 bool parsed
= false;
1812 parser_info pi
= parser
;
1813 if(format
== JSFormat
) {
1814 #ifdef QTSCRIPT_SUPPORT
1815 eng
.globalObject().setProperty("qmake", qscript_projectWrapper(&eng
, this, place
));
1817 if (f
.open(QFile::ReadOnly
)) {
1818 QString code
= f
.readAll();
1819 QScriptValue r
= eng
.evaluate(code
);
1820 if(eng
.hasUncaughtException()) {
1821 const int lineNo
= eng
.uncaughtExceptionLineNumber();
1822 fprintf(stderr
, "%s:%d: %s\n", file
.toLatin1().constData(), lineNo
,
1823 r
.toString().toLatin1().constData());
1826 QScriptValue variables
= eng
.globalObject().property("qmake");
1827 if (variables
.isValid() && variables
.isObject())
1828 qscript_createQMakeProjectMap(place
, variables
);
1832 warn_msg(WarnParser
, "%s:%d: QtScript support disabled for %s.",
1833 pi
.file
.toLatin1().constData(), pi
.line_no
, orig_file
.toLatin1().constData());
1836 QStack
<ScopeBlock
> sc
= scope_blocks
;
1837 IteratorBlock
*it
= iterator
;
1838 FunctionBlock
*fu
= function
;
1839 if(flags
& (IncludeFlagNewProject
|IncludeFlagNewParser
)) {
1840 // The "project's variables" are used in other places (eg. export()) so it's not
1841 // possible to use "place" everywhere. Instead just set variables and grab them later
1842 QMakeProject
proj(this, &place
);
1843 if(flags
& IncludeFlagNewParser
) {
1845 if(proj
.doProjectInclude("default_pre", IncludeFlagFeature
, proj
.variables()) == IncludeNoExist
)
1846 proj
.doProjectInclude("default", IncludeFlagFeature
, proj
.variables());
1848 parsed
= proj
.read(file
, proj
.variables());
1850 parsed
= proj
.read(file
);
1852 place
= proj
.variables();
1854 parsed
= read(file
, place
);
1861 if(place
["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file
) == -1)
1862 place
["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file
);
1864 warn_msg(WarnParser
, "%s:%d: Failure to include file %s.",
1865 pi
.file
.toLatin1().constData(), pi
.line_no
, orig_file
.toLatin1().constData());
1868 qmake_setpwd(oldpwd
);
1870 return IncludeParseFailure
;
1871 return IncludeSuccess
;
1875 QMakeProject::doProjectExpand(QString func
, const QString
¶ms
,
1876 QMap
<QString
, QStringList
> &place
)
1878 return doProjectExpand(func
, split_arg_list(params
), place
);
1882 QMakeProject::doProjectExpand(QString func
, QStringList args
,
1883 QMap
<QString
, QStringList
> &place
)
1885 QList
<QStringList
> args_list
;
1886 for(int i
= 0; i
< args
.size(); ++i
) {
1887 QStringList arg
= split_value_list(args
[i
]), tmp
;
1888 for(int i
= 0; i
< arg
.size(); ++i
)
1889 tmp
+= doVariableReplaceExpand(arg
[i
], place
);;
1892 return doProjectExpand(func
, args_list
, place
);
1896 QMakeProject::doProjectExpand(QString func
, QList
<QStringList
> args_list
,
1897 QMap
<QString
, QStringList
> &place
)
1899 func
= func
.trimmed();
1900 if(replaceFunctions
.contains(func
)) {
1901 FunctionBlock
*defined
= replaceFunctions
[func
];
1902 function_blocks
.push(defined
);
1904 defined
->exec(args_list
, this, place
, ret
);
1905 Q_ASSERT(function_blocks
.pop() == defined
);
1909 QStringList args
; //why don't the builtin functions just use args_list? --Sam
1910 for(int i
= 0; i
< args_list
.size(); ++i
)
1911 args
+= args_list
[i
].join(QString(Option::field_sep
));
1913 ExpandFunc func_t
= qmake_expandFunctions().value(func
.toLower());
1914 debug_msg(1, "Running project expand: %s(%s) [%d]",
1915 func
.toLatin1().constData(), args
.join("::").toLatin1().constData(), func_t
);
1920 if(args
.count() < 1 || args
.count() > 3) {
1921 fprintf(stderr
, "%s:%d: member(var, start, end) requires three arguments.\n",
1922 parser
.file
.toLatin1().constData(), parser
.line_no
);
1925 const QStringList
&var
= values(args
.first(), place
);
1926 int start
= 0, end
= 0;
1927 if(args
.count() >= 2) {
1928 QString start_str
= args
[1];
1929 start
= start_str
.toInt(&ok
);
1931 if(args
.count() == 2) {
1932 int dotdot
= start_str
.indexOf("..");
1934 start
= start_str
.left(dotdot
).toInt(&ok
);
1936 end
= start_str
.mid(dotdot
+2).toInt(&ok
);
1940 fprintf(stderr
, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
1941 parser
.file
.toLatin1().constData(), parser
.line_no
,
1942 start_str
.toLatin1().constData());
1945 if(args
.count() == 3)
1946 end
= args
[2].toInt(&ok
);
1948 fprintf(stderr
, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
1949 parser
.file
.toLatin1().constData(), parser
.line_no
,
1950 args
[2].toLatin1().constData());
1955 start
+= var
.count();
1958 if(start
< 0 || start
>= var
.count() || end
< 0 || end
>= var
.count()) {
1960 } else if(start
< end
) {
1961 for(int i
= start
; i
<= end
&& (int)var
.count() >= i
; i
++)
1964 for(int i
= start
; i
>= end
&& (int)var
.count() >= i
&& i
>= 0; i
--)
1972 if(args
.count() != 1) {
1973 fprintf(stderr
, "%s:%d: %s(var) requires one argument.\n",
1974 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
1976 const QStringList
&var
= values(args
.first(), place
);
1977 if(!var
.isEmpty()) {
1978 if(func_t
== E_FIRST
)
1979 ret
= QStringList(var
[0]);
1981 ret
= QStringList(var
[var
.size()-1]);
1986 if(args
.count() < 1 || args
.count() > 2) {
1987 fprintf(stderr
, "%s:%d: cat(file) requires one argument.\n",
1988 parser
.file
.toLatin1().constData(), parser
.line_no
);
1990 QString file
= args
[0];
1991 file
= Option::fixPathToLocalOS(file
);
1993 bool singleLine
= true;
1994 if(args
.count() > 1)
1995 singleLine
= (args
[1].toLower() == "true");
1998 if(qfile
.open(QIODevice::ReadOnly
)) {
1999 QTextStream
stream(&qfile
);
2000 while(!stream
.atEnd()) {
2001 ret
+= split_value_list(stream
.readLine().trimmed());
2010 if(args
.count() != 2) {
2011 fprintf(stderr
, "%s:%d: fromfile(file, variable) requires two arguments.\n",
2012 parser
.file
.toLatin1().constData(), parser
.line_no
);
2014 QString file
= args
[0], seek_var
= args
[1];
2015 file
= Option::fixPathToLocalOS(file
);
2017 QMap
<QString
, QStringList
> tmp
;
2018 if(doProjectInclude(file
, IncludeFlagNewParser
, tmp
) == IncludeSuccess
) {
2019 if(tmp
.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2020 QStringList
&out
= place
["QMAKE_INTERNAL_INCLUDED_FILES"];
2021 const QStringList
&in
= tmp
["QMAKE_INTERNAL_INCLUDED_FILES"];
2022 for(int i
= 0; i
< in
.size(); ++i
) {
2023 if(out
.indexOf(in
[i
]) == -1)
2027 ret
= tmp
[seek_var
];
2032 if(args
.count() < 1 || args
.count() > 2) {
2033 fprintf(stderr
, "%s:%d: eval(variable) requires one argument.\n",
2034 parser
.file
.toLatin1().constData(), parser
.line_no
);
2037 const QMap
<QString
, QStringList
> *source
= &place
;
2038 if(args
.count() == 2) {
2039 if(args
.at(1) == "Global") {
2041 } else if(args
.at(1) == "Local") {
2044 fprintf(stderr
, "%s:%d: unexpected source to eval.\n", parser
.file
.toLatin1().constData(),
2048 ret
+= source
->value(args
.at(0));
2054 tmp
.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x
++);
2055 ret
= QStringList(tmp
);
2056 QStringList
&lst
= (*((QMap
<QString
, QStringList
>*)&place
))[tmp
];
2058 for(QStringList::ConstIterator arg_it
= args
.begin();
2059 arg_it
!= args
.end(); ++arg_it
)
2060 lst
+= split_value_list((*arg_it
));
2063 if(args
.count() < 1) {
2064 fprintf(stderr
, "%s:%d: sprintf(format, ...) requires one argument.\n",
2065 parser
.file
.toLatin1().constData(), parser
.line_no
);
2067 QString tmp
= args
.at(0);
2068 for(int i
= 1; i
< args
.count(); ++i
)
2069 tmp
= tmp
.arg(args
.at(i
));
2070 ret
= split_value_list(tmp
);
2074 if(args
.count() < 1 || args
.count() > 4) {
2075 fprintf(stderr
, "%s:%d: join(var, glue, before, after) requires four"
2076 "arguments.\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
2078 QString glue
, before
, after
;
2079 if(args
.count() >= 2)
2081 if(args
.count() >= 3)
2083 if(args
.count() == 4)
2085 const QStringList
&var
= values(args
.first(), place
);
2087 ret
= split_value_list(before
+ var
.join(glue
) + after
);
2091 if(args
.count() < 1 || args
.count() > 2) {
2092 fprintf(stderr
, "%s:%d split(var, sep) requires one or two arguments\n",
2093 parser
.file
.toLatin1().constData(), parser
.line_no
);
2095 QString sep
= QString(Option::field_sep
);
2096 if(args
.count() >= 2)
2098 QStringList var
= values(args
.first(), place
);
2099 for(QStringList::ConstIterator vit
= var
.begin(); vit
!= var
.end(); ++vit
) {
2100 QStringList lst
= (*vit
).split(sep
);
2101 for(QStringList::ConstIterator spltit
= lst
.begin(); spltit
!= lst
.end(); ++spltit
)
2109 bool regexp
= false;
2112 if(func_t
== E_SECTION
) {
2113 if(args
.count() != 3 && args
.count() != 4) {
2114 fprintf(stderr
, "%s:%d section(var, sep, begin, end) requires three argument\n",
2115 parser
.file
.toLatin1().constData(), parser
.line_no
);
2119 beg
= args
[2].toInt();
2120 if(args
.count() == 4)
2121 end
= args
[3].toInt();
2124 if(args
.count() != 1) {
2125 fprintf(stderr
, "%s:%d %s(var) requires one argument.\n",
2126 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
2130 sep
= "[" + QRegExp::escape(Option::dir_sep
) + "/]";
2131 if(func_t
== E_DIRNAME
)
2138 const QStringList
&l
= values(var
, place
);
2139 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
2140 QString separator
= sep
;
2142 ret
+= (*it
).section(QRegExp(separator
), beg
, end
);
2144 ret
+= (*it
).section(separator
, beg
, end
);
2149 if(args
.count() != 2) {
2150 fprintf(stderr
, "%s:%d find(var, str) requires two arguments\n",
2151 parser
.file
.toLatin1().constData(), parser
.line_no
);
2153 QRegExp
regx(args
[1]);
2154 const QStringList
&var
= values(args
.first(), place
);
2155 for(QStringList::ConstIterator vit
= var
.begin();
2156 vit
!= var
.end(); ++vit
) {
2157 if(regx
.indexIn(*vit
) != -1)
2163 if(args
.count() < 1 || args
.count() > 2) {
2164 fprintf(stderr
, "%s:%d system(execut) requires one argument.\n",
2165 parser
.file
.toLatin1().constData(), parser
.line_no
);
2167 QMakeProjectEnv
env(place
);
2169 FILE *proc
= QT_POPEN(args
[0].toLatin1(), "r");
2170 bool singleLine
= true;
2171 if(args
.count() > 1)
2172 singleLine
= (args
[1].toLower() == "true");
2174 while(proc
&& !feof(proc
)) {
2175 int read_in
= int(fread(buff
, 1, 255, proc
));
2178 for(int i
= 0; i
< read_in
; i
++) {
2179 if((singleLine
&& buff
[i
] == '\n') || buff
[i
] == '\t')
2182 buff
[read_in
] = '\0';
2185 ret
+= split_value_list(output
);
2191 if(args
.count() != 1) {
2192 fprintf(stderr
, "%s:%d unique(var) requires one argument.\n",
2193 parser
.file
.toLatin1().constData(), parser
.line_no
);
2195 const QStringList
&var
= values(args
.first(), place
);
2196 for(int i
= 0; i
< var
.count(); i
++) {
2197 if(!ret
.contains(var
[i
]))
2205 case E_ESCAPE_EXPAND
: {
2206 for(int i
= 0; i
< args
.size(); ++i
) {
2207 QChar
*i_data
= args
[i
].data();
2208 int i_len
= args
[i
].length();
2209 for(int x
= 0; x
< i_len
; ++x
) {
2210 if(*(i_data
+x
) == '\\' && x
< i_len
-1) {
2211 if(*(i_data
+x
+1) == '\\') {
2216 } mapped_quotes
[] = {
2222 for(int i
= 0; mapped_quotes
[i
].in
; ++i
) {
2223 if(*(i_data
+x
+1) == mapped_quotes
[i
].in
) {
2224 *(i_data
+x
) = mapped_quotes
[i
].out
;
2226 memmove(i_data
+x
+1, i_data
+x
+2, (i_len
-x
-2)*sizeof(QChar
));
2234 ret
.append(QString(i_data
, i_len
));
2238 for(int i
= 0; i
< args
.size(); ++i
)
2239 ret
+= QRegExp::escape(args
[i
]);
2243 for(int i
= 0; i
< args
.size(); ++i
) {
2244 if(func_t
== E_UPPER
)
2245 ret
+= args
[i
].toUpper();
2247 ret
+= args
[i
].toLower();
2251 if(args
.count() != 1 && args
.count() != 2) {
2252 fprintf(stderr
, "%s:%d files(pattern) requires one argument.\n",
2253 parser
.file
.toLatin1().constData(), parser
.line_no
);
2255 bool recursive
= false;
2256 if(args
.count() == 2)
2257 recursive
= (args
[1].toLower() == "true" || args
[1].toInt());
2259 QString r
= Option::fixPathToLocalOS(args
[0]);
2260 int slash
= r
.lastIndexOf(QDir::separator());
2262 dirs
.append(r
.left(slash
));
2268 const QRegExp
regex(r
, Qt::CaseSensitive
, QRegExp::Wildcard
);
2269 for(int d
= 0; d
< dirs
.count(); d
++) {
2270 QString dir
= dirs
[d
];
2271 if(!dir
.isEmpty() && !dir
.endsWith(Option::dir_sep
))
2275 for(int i
= 0; i
< (int)qdir
.count(); ++i
) {
2276 if(qdir
[i
] == "." || qdir
[i
] == "..")
2278 QString fname
= dir
+ qdir
[i
];
2279 if(QFileInfo(fname
).isDir()) {
2283 if(regex
.exactMatch(qdir
[i
]))
2290 if(args
.count() != 1) {
2291 fprintf(stderr
, "%s:%d prompt(question) requires one argument.\n",
2292 parser
.file
.toLatin1().constData(), parser
.line_no
);
2293 } else if(projectFile() == "-") {
2294 fprintf(stderr
, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
2295 parser
.file
.toLatin1().constData(), parser
.line_no
);
2297 QString msg
= fixEnvVariables(args
.first());
2298 if(!msg
.endsWith("?"))
2300 fprintf(stderr
, "Project %s: %s ", func
.toUpper().toLatin1().constData(),
2301 msg
.toLatin1().constData());
2304 if(qfile
.open(stdin
, QIODevice::ReadOnly
)) {
2305 QTextStream
t(&qfile
);
2306 ret
= split_value_list(t
.readLine());
2311 if(args
.count() != 3 ) {
2312 fprintf(stderr
, "%s:%d replace(var, before, after) requires three arguments\n",
2313 parser
.file
.toLatin1().constData(), parser
.line_no
);
2315 const QRegExp
before( args
[1] );
2316 const QString
after( args
[2] );
2317 QStringList var
= values(args
.first(), place
);
2318 for(QStringList::Iterator it
= var
.begin(); it
!= var
.end(); ++it
)
2319 ret
+= it
->replace(before
, after
);
2323 #ifdef QTSCRIPT_SUPPORT
2325 QScriptValue jsFunc
= eng
.globalObject().property(func
);
2326 if(jsFunc
.isFunction()) {
2327 QScriptValueList jsArgs
;
2328 for(int i
= 0; i
< args
.size(); ++i
)
2329 jsArgs
+= QScriptValue(&eng
, args
.at(i
));
2330 QScriptValue jsRet
= jsFunc
.call(eng
.globalObject(), jsArgs
);
2331 ret
= qscriptvalue_cast
<QStringList
>(jsRet
);
2336 fprintf(stderr
, "%s:%d: Unknown replace function: %s\n",
2337 parser
.file
.toLatin1().constData(), parser
.line_no
,
2338 func
.toLatin1().constData());
2345 QMakeProject::doProjectTest(QString func
, QStringList args
, QMap
<QString
, QStringList
> &place
)
2347 QList
<QStringList
> args_list
;
2348 for(int i
= 0; i
< args
.size(); ++i
) {
2349 QStringList arg
= split_value_list(args
[i
]), tmp
;
2350 for(int i
= 0; i
< arg
.size(); ++i
)
2351 tmp
+= doVariableReplaceExpand(arg
[i
], place
);
2354 return doProjectTest(func
, args_list
, place
);
2358 QMakeProject::doProjectTest(QString func
, QList
<QStringList
> args_list
, QMap
<QString
, QStringList
> &place
)
2360 func
= func
.trimmed();
2362 if(testFunctions
.contains(func
)) {
2363 FunctionBlock
*defined
= testFunctions
[func
];
2365 function_blocks
.push(defined
);
2366 defined
->exec(args_list
, this, place
, ret
);
2367 Q_ASSERT(function_blocks
.pop() == defined
);
2372 if(ret
.first() == "true") {
2374 } else if(ret
.first() == "false") {
2378 int val
= ret
.first().toInt(&ok
);
2381 fprintf(stderr
, "%s:%d Unexpected return value from test %s [%s].\n",
2382 parser
.file
.toLatin1().constData(),
2383 parser
.line_no
, func
.toLatin1().constData(),
2384 ret
.join("::").toLatin1().constData());
2391 QStringList args
; //why don't the builtin functions just use args_list? --Sam
2392 for(int i
= 0; i
< args_list
.size(); ++i
)
2393 args
+= args_list
[i
].join(QString(Option::field_sep
));
2395 TestFunc func_t
= qmake_testFunctions().value(func
);
2396 debug_msg(1, "Running project test: %s(%s) [%d]",
2397 func
.toLatin1().constData(), args
.join("::").toLatin1().constData(), func_t
);
2401 return doProjectCheckReqs(args
, place
);
2403 case T_GREATERTHAN
: {
2404 if(args
.count() != 2) {
2405 fprintf(stderr
, "%s:%d: %s(variable, value) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2406 parser
.line_no
, func
.toLatin1().constData());
2409 QString
rhs(args
[1]), lhs(values(args
[0], place
).join(QString(Option::field_sep
)));
2411 int rhs_int
= rhs
.toInt(&ok
);
2412 if(ok
) { // do integer compare
2413 int lhs_int
= lhs
.toInt(&ok
);
2415 if(func
== "greaterThan")
2416 return lhs_int
> rhs_int
;
2417 return lhs_int
< rhs_int
;
2420 if(func_t
== T_GREATERTHAN
)
2424 if(args
.count() != 1) {
2425 fprintf(stderr
, "%s:%d: if(condition) requires one argument.\n", parser
.file
.toLatin1().constData(),
2429 const QString cond
= args
.first();
2430 const QChar
*d
= cond
.unicode();
2432 bool ret
= true, or_op
= false;
2434 for(int d_off
= 0, parens
= 0, d_len
= cond
.size(); d_off
< d_len
; ++d_off
) {
2435 if(!quote
.isNull()) {
2436 if(*(d
+d_off
) == quote
)
2438 } else if(*(d
+d_off
) == '(') {
2440 } else if(*(d
+d_off
) == ')') {
2442 } else if(*(d
+d_off
) == '"' /*|| *(d+d_off) == '\''*/) {
2445 if(!parens
&& quote
.isNull() && (*(d
+d_off
) == QLatin1Char(':') || *(d
+d_off
) == QLatin1Char('|') || d_off
== d_len
-1)) {
2446 if(d_off
== d_len
-1)
2448 if(!test
.isEmpty()) {
2449 const bool success
= doProjectTest(test
, place
);
2452 ret
= ret
|| success
;
2454 ret
= ret
&& success
;
2456 if(*(d
+d_off
) == QLatin1Char(':')) {
2458 } else if(*(d
+d_off
) == QLatin1Char('|')) {
2467 if(args
.count() != 2) {
2468 fprintf(stderr
, "%s:%d: %s(variable, value) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2469 parser
.line_no
, func
.toLatin1().constData());
2472 return values(args
[0], place
).join(QString(Option::field_sep
)) == args
[1];
2474 if(args
.count() != 1) {
2475 fprintf(stderr
, "%s:%d: exists(file) requires one argument.\n", parser
.file
.toLatin1().constData(),
2479 QString file
= args
.first();
2480 file
= Option::fixPathToLocalOS(file
);
2482 if(QFile::exists(file
))
2484 //regular expression I guess
2485 QString dirstr
= qmake_getpwd();
2486 int slsh
= file
.lastIndexOf(Option::dir_sep
);
2488 dirstr
= file
.left(slsh
+1);
2489 file
= file
.right(file
.length() - slsh
- 1);
2491 return QDir(dirstr
).entryList(QStringList(file
)).count(); }
2493 if(args
.count() != 1) {
2494 fprintf(stderr
, "%s:%d: export(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2498 for(int i
= 0; i
< function_blocks
.size(); ++i
) {
2499 FunctionBlock
*f
= function_blocks
.at(i
);
2500 f
->vars
[args
[0]] = values(args
[0], place
);
2501 if(!i
&& f
->calling_place
)
2502 (*f
->calling_place
)[args
[0]] = values(args
[0], place
);
2506 if(args
.count() != 1) {
2507 fprintf(stderr
, "%s:%d: clear(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2511 if(!place
.contains(args
[0]))
2513 place
[args
[0]].clear();
2516 if(args
.count() != 1) {
2517 fprintf(stderr
, "%s:%d: unset(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2521 if(!place
.contains(args
[0]))
2523 place
.remove(args
[0]);
2526 if(args
.count() < 1 && 0) {
2527 fprintf(stderr
, "%s:%d: eval(project) requires one argument.\n", parser
.file
.toLatin1().constData(),
2531 QString project
= args
.join(" ");
2532 parser_info pi
= parser
;
2533 parser
.from_file
= false;
2534 parser
.file
= "(eval)";
2536 QTextStream
t(&project
, QIODevice::ReadOnly
);
2537 bool ret
= read(t
, place
);
2541 if(args
.count() < 1 || args
.count() > 2) {
2542 fprintf(stderr
, "%s:%d: CONFIG(config) requires one argument.\n", parser
.file
.toLatin1().constData(),
2546 if(args
.count() == 1)
2547 return isActiveConfig(args
[0]);
2548 const QStringList mutuals
= args
[1].split('|');
2549 const QStringList
&configs
= values("CONFIG", place
);
2550 for(int i
= configs
.size()-1; i
>= 0; i
--) {
2551 for(int mut
= 0; mut
< mutuals
.count(); mut
++) {
2552 if(configs
[i
] == mutuals
[mut
].trimmed())
2553 return (configs
[i
] == args
[0]);
2558 bool setup_env
= true;
2559 if(args
.count() < 1 || args
.count() > 2) {
2560 fprintf(stderr
, "%s:%d: system(exec) requires one argument.\n", parser
.file
.toLatin1().constData(),
2564 if(args
.count() == 2) {
2565 const QString sarg
= args
[1];
2566 setup_env
= (sarg
.toLower() == "true" || sarg
.toInt());
2568 QMakeProjectEnv env
;
2571 bool ret
= system(args
[0].toLatin1().constData()) == 0;
2574 if(function_blocks
.isEmpty()) {
2575 fprintf(stderr
, "%s:%d unexpected return()\n",
2576 parser
.file
.toLatin1().constData(), parser
.line_no
);
2578 FunctionBlock
*f
= function_blocks
.top();
2579 f
->cause_return
= true;
2580 if(args_list
.count() >= 1)
2581 f
->return_value
+= args_list
[0];
2586 iterator
->cause_break
= true;
2587 else if(!scope_blocks
.isEmpty())
2588 scope_blocks
.top().ignore
= true;
2590 fprintf(stderr
, "%s:%d unexpected break()\n",
2591 parser
.file
.toLatin1().constData(), parser
.line_no
);
2595 iterator
->cause_next
= true;
2597 fprintf(stderr
, "%s:%d unexpected next()\n",
2598 parser
.file
.toLatin1().constData(), parser
.line_no
);
2601 if(args
.count() < 1 || args
.count() > 2) {
2602 fprintf(stderr
, "%s:%d: defined(function) requires one argument.\n",
2603 parser
.file
.toLatin1().constData(), parser
.line_no
);
2605 if(args
.count() > 1) {
2606 if(args
[1] == "test")
2607 return testFunctions
.contains(args
[0]);
2608 else if(args
[1] == "replace")
2609 return replaceFunctions
.contains(args
[0]);
2610 fprintf(stderr
, "%s:%d: defined(function, type): unexpected type [%s].\n",
2611 parser
.file
.toLatin1().constData(), parser
.line_no
,
2612 args
[1].toLatin1().constData());
2614 if(replaceFunctions
.contains(args
[0]) || testFunctions
.contains(args
[0]))
2620 if(args
.count() < 2 || args
.count() > 3) {
2621 fprintf(stderr
, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n",
2622 parser
.file
.toLatin1().constData(), parser
.line_no
);
2625 QRegExp
regx(args
[1]);
2626 const QStringList
&l
= values(args
[0], place
);
2627 if(args
.count() == 2) {
2628 for(int i
= 0; i
< l
.size(); ++i
) {
2629 const QString val
= l
[i
];
2630 if(regx
.exactMatch(val
) || val
== args
[1])
2634 const QStringList mutuals
= args
[2].split('|');
2635 for(int i
= l
.size()-1; i
>= 0; i
--) {
2636 const QString val
= l
[i
];
2637 for(int mut
= 0; mut
< mutuals
.count(); mut
++) {
2638 if(val
== mutuals
[mut
].trimmed())
2639 return (regx
.exactMatch(val
) || val
== args
[1]);
2645 if(args
.count() < 2 || args
.count() > 3) {
2646 fprintf(stderr
, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
2647 parser
.file
.toLatin1().constData(), parser
.line_no
);
2652 QMap
<QString
, QStringList
> tmp
;
2653 if(doProjectInclude(Option::fixPathToLocalOS(args
[0]), IncludeFlagNewParser
, tmp
) == IncludeSuccess
) {
2654 if(tmp
.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2655 QStringList
&out
= place
["QMAKE_INTERNAL_INCLUDED_FILES"];
2656 const QStringList
&in
= tmp
["QMAKE_INTERNAL_INCLUDED_FILES"];
2657 for(int i
= 0; i
< in
.size(); ++i
) {
2658 if(out
.indexOf(in
[i
]) == -1)
2662 if(args
.count() == 2) {
2663 ret
= tmp
.contains(args
[1]);
2665 QRegExp
regx(args
[2]);
2666 const QStringList
&l
= tmp
[args
[1]];
2667 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
2668 if(regx
.exactMatch((*it
)) || (*it
) == args
[2]) {
2677 if(args
.count() != 2 && args
.count() != 3) {
2678 fprintf(stderr
, "%s:%d: count(var, count) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2682 if(args
.count() == 3) {
2683 QString comp
= args
[2];
2684 if(comp
== ">" || comp
== "greaterThan")
2685 return values(args
[0], place
).count() > args
[1].toInt();
2687 return values(args
[0], place
).count() >= args
[1].toInt();
2688 if(comp
== "<" || comp
== "lessThan")
2689 return values(args
[0], place
).count() < args
[1].toInt();
2691 return values(args
[0], place
).count() <= args
[1].toInt();
2692 if(comp
== "equals" || comp
== "isEqual" || comp
== "=" || comp
== "==")
2693 return values(args
[0], place
).count() == args
[1].toInt();
2694 fprintf(stderr
, "%s:%d: unexpected modifier to count(%s)\n", parser
.file
.toLatin1().constData(),
2695 parser
.line_no
, comp
.toLatin1().constData());
2698 return values(args
[0], place
).count() == args
[1].toInt();
2700 if(args
.count() != 1) {
2701 fprintf(stderr
, "%s:%d: isEmpty(var) requires one argument.\n", parser
.file
.toLatin1().constData(),
2705 return values(args
[0], place
).isEmpty();
2709 const bool include_statement
= (func_t
== T_INCLUDE
);
2710 bool ignore_error
= include_statement
;
2711 if(args
.count() == 2) {
2712 if(func_t
== T_INCLUDE
) {
2713 parseInto
= args
[1];
2715 QString sarg
= args
[1];
2716 ignore_error
= (sarg
.toLower() == "true" || sarg
.toInt());
2718 } else if(args
.count() != 1) {
2719 QString func_desc
= "load(feature)";
2720 if(include_statement
)
2721 func_desc
= "include(file)";
2722 fprintf(stderr
, "%s:%d: %s requires one argument.\n", parser
.file
.toLatin1().constData(),
2723 parser
.line_no
, func_desc
.toLatin1().constData());
2726 QString file
= args
.first();
2727 file
= Option::fixPathToLocalOS(file
);
2728 uchar flags
= IncludeFlagNone
;
2729 if(!include_statement
)
2730 flags
|= IncludeFlagFeature
;
2731 IncludeStatus stat
= IncludeFailure
;
2732 if(!parseInto
.isEmpty()) {
2733 QMap
<QString
, QStringList
> symbols
;
2734 stat
= doProjectInclude(file
, flags
|IncludeFlagNewProject
, symbols
);
2735 if(stat
== IncludeSuccess
) {
2736 QMap
<QString
, QStringList
> out_place
;
2737 for(QMap
<QString
, QStringList
>::ConstIterator it
= place
.begin(); it
!= place
.end(); ++it
) {
2738 const QString var
= it
.key();
2739 if(var
!= parseInto
&& !var
.startsWith(parseInto
+ "."))
2740 out_place
.insert(var
, it
.value());
2742 for(QMap
<QString
, QStringList
>::ConstIterator it
= symbols
.begin(); it
!= symbols
.end(); ++it
) {
2743 const QString var
= it
.key();
2744 if(!var
.startsWith("."))
2745 out_place
.insert(parseInto
+ "." + it
.key(), it
.value());
2750 stat
= doProjectInclude(file
, flags
, place
);
2752 if(stat
== IncludeFeatureAlreadyLoaded
) {
2753 warn_msg(WarnParser
, "%s:%d: Duplicate of loaded feature %s",
2754 parser
.file
.toLatin1().constData(), parser
.line_no
, file
.toLatin1().constData());
2755 } else if(stat
== IncludeNoExist
&& include_statement
) {
2756 warn_msg(WarnParser
, "%s:%d: Unable to find file for inclusion %s",
2757 parser
.file
.toLatin1().constData(), parser
.line_no
, file
.toLatin1().constData());
2759 } else if(stat
>= IncludeFailure
) {
2761 printf("Project LOAD(): Feature %s cannot be found.\n", file
.toLatin1().constData());
2763 #if defined(QT_BUILD_QMAKE_LIBRARY)
2773 if(args
.count() != 2) {
2774 fprintf(stderr
, "%s:%d: debug(level, message) requires one argument.\n", parser
.file
.toLatin1().constData(),
2778 QString msg
= fixEnvVariables(args
[1]);
2779 debug_msg(args
[0].toInt(), "Project DEBUG: %s", msg
.toLatin1().constData());
2784 if(args
.count() != 1) {
2785 fprintf(stderr
, "%s:%d: %s(message) requires one argument.\n", parser
.file
.toLatin1().constData(),
2786 parser
.line_no
, func
.toLatin1().constData());
2789 QString msg
= fixEnvVariables(args
.first());
2790 fprintf(stderr
, "Project %s: %s\n", func
.toUpper().toLatin1().constData(), msg
.toLatin1().constData());
2792 #if defined(QT_BUILD_QMAKE_LIBRARY)
2799 #ifdef QTSCRIPT_SUPPORT
2801 QScriptValue jsFunc
= eng
.globalObject().property(func
);
2802 if(jsFunc
.isFunction()) {
2803 QScriptValueList jsArgs
;
2804 for(int i
= 0; i
< args
.size(); ++i
)
2805 jsArgs
+= QScriptValue(&eng
, args
.at(i
));
2806 QScriptValue jsRet
= jsFunc
.call(eng
.globalObject(), jsArgs
);
2807 if(eng
.hasUncaughtException())
2809 return qscriptvalue_cast
<bool>(jsRet
);
2813 fprintf(stderr
, "%s:%d: Unknown test function: %s\n", parser
.file
.toLatin1().constData(), parser
.line_no
,
2814 func
.toLatin1().constData());
2820 QMakeProject::doProjectCheckReqs(const QStringList
&deps
, QMap
<QString
, QStringList
> &place
)
2823 for(QStringList::ConstIterator it
= deps
.begin(); it
!= deps
.end(); ++it
) {
2824 bool test
= doProjectTest((*it
), place
);
2826 debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
2827 parser
.file
.toLatin1().constData(), parser
.line_no
,
2828 (*it
).toLatin1().constData());
2829 place
["QMAKE_FAILED_REQUIREMENTS"].append((*it
));
2837 QMakeProject::test(const QString
&v
)
2839 QMap
<QString
, QStringList
> tmp
= vars
;
2840 return doProjectTest(v
, tmp
);
2844 QMakeProject::test(const QString
&func
, const QList
<QStringList
> &args
)
2846 QMap
<QString
, QStringList
> tmp
= vars
;
2847 return doProjectTest(func
, args
, tmp
);
2851 QMakeProject::expand(const QString
&str
)
2854 QMap
<QString
, QStringList
> tmp
= vars
;
2855 const QStringList ret
= doVariableReplaceExpand(str
, tmp
, &ok
);
2858 return QStringList();
2862 QMakeProject::expand(const QString
&func
, const QList
<QStringList
> &args
)
2864 QMap
<QString
, QStringList
> tmp
= vars
;
2865 return doProjectExpand(func
, args
, tmp
);
2869 QMakeProject::doVariableReplace(QString
&str
, QMap
<QString
, QStringList
> &place
)
2872 str
= doVariableReplaceExpand(str
, place
, &ret
).join(QString(Option::field_sep
));
2877 QMakeProject::doVariableReplaceExpand(const QString
&str
, QMap
<QString
, QStringList
> &place
, bool *ok
)
2885 const ushort LSQUARE
= '[';
2886 const ushort RSQUARE
= ']';
2887 const ushort LCURLY
= '{';
2888 const ushort RCURLY
= '}';
2889 const ushort LPAREN
= '(';
2890 const ushort RPAREN
= ')';
2891 const ushort DOLLAR
= '$';
2892 const ushort SLASH
= '\\';
2893 const ushort UNDERSCORE
= '_';
2894 const ushort DOT
= '.';
2895 const ushort SPACE
= ' ';
2896 const ushort TAB
= '\t';
2897 const ushort SINGLEQUOTE
= '\'';
2898 const ushort DOUBLEQUOTE
= '"';
2900 ushort unicode
, quote
= 0;
2901 const QChar
*str_data
= str
.data();
2902 const int str_len
= str
.length();
2909 for(int i
= 0; i
< str_len
; ++i
) {
2910 unicode
= str_data
[i
].unicode();
2911 const int start_var
= i
;
2912 if(unicode
== DOLLAR
&& str_len
> i
+2) {
2913 unicode
= str_data
[++i
].unicode();
2914 if(unicode
== DOLLAR
) {
2918 enum { VAR
, ENVIRON
, FUNCTION
, PROPERTY
} var_type
= VAR
;
2919 unicode
= str_data
[++i
].unicode();
2920 if(unicode
== LSQUARE
) {
2921 unicode
= str_data
[++i
].unicode();
2923 var_type
= PROPERTY
;
2924 } else if(unicode
== LCURLY
) {
2925 unicode
= str_data
[++i
].unicode();
2928 } else if(unicode
== LPAREN
) {
2929 unicode
= str_data
[++i
].unicode();
2934 if(!(unicode
& (0xFF<<8)) &&
2935 unicode
!= DOT
&& unicode
!= UNDERSCORE
&&
2936 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
2937 (unicode
< 'a' || unicode
> 'z') && (unicode
< 'A' || unicode
> 'Z') &&
2938 (unicode
< '0' || unicode
> '9'))
2940 var
.append(QChar(unicode
));
2943 unicode
= str_data
[i
].unicode();
2944 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
2946 if(var_type
== VAR
&& unicode
== LPAREN
) {
2947 var_type
= FUNCTION
;
2952 unicode
= str_data
[i
].unicode();
2953 if(unicode
== LPAREN
) {
2955 } else if(unicode
== RPAREN
) {
2960 args
.append(QChar(unicode
));
2963 unicode
= str_data
[i
].unicode();
2966 // at this point i is pointing to the 'next' character (which is in unicode)
2967 // this might actually be a term character since you can do $${func()}
2970 if(unicode
!= term
) {
2971 qmake_error_msg("Missing " + QString(term
) + " terminator [found " + (unicode
?QString(unicode
):QString("end-of-line")) + "]");
2974 return QStringList();
2977 // move the 'cursor' back to the last char of the thing we were looking at
2980 // since i never points to the 'next' character, there is no reason for this to be set
2983 QStringList replacement
;
2984 if(var_type
== ENVIRON
) {
2985 replacement
= split_value_list(QString::fromLocal8Bit(qgetenv(var
.toLatin1().constData())));
2986 } else if(var_type
== PROPERTY
) {
2988 replacement
= split_value_list(prop
->value(var
));
2989 } else if(var_type
== FUNCTION
) {
2990 replacement
= doProjectExpand(var
, args
, place
);
2991 } else if(var_type
== VAR
) {
2992 replacement
= values(var
, place
);
2994 if(!(replaced
++) && start_var
)
2995 current
= str
.left(start_var
);
2996 if(!replacement
.isEmpty()) {
2998 current
+= replacement
.join(QString(Option::field_sep
));
3000 current
+= replacement
.takeFirst();
3001 if(!replacement
.isEmpty()) {
3002 if(!current
.isEmpty())
3003 ret
.append(current
);
3004 current
= replacement
.takeLast();
3005 if(!replacement
.isEmpty())
3010 debug_msg(2, "Project Parser [var replace]: %s -> %s",
3011 str
.toLatin1().constData(), var
.toLatin1().constData(),
3012 replacement
.join("::").toLatin1().constData());
3015 current
.append("$");
3018 if(quote
&& unicode
== quote
) {
3021 } else if(unicode
== SLASH
) {
3022 bool escape
= false;
3023 const char *symbols
= "[]{}()$\\'\"";
3024 for(const char *s
= symbols
; *s
; ++s
) {
3025 if(str_data
[i
+1].unicode() == (ushort
)*s
) {
3029 current
= str
.left(start_var
);
3030 current
.append(str
.at(i
));
3034 if(escape
|| !replaced
)
3036 } else if(!quote
&& (unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
)) {
3039 if(!(replaced
++) && i
)
3040 current
= str
.left(i
);
3041 } else if(!quote
&& (unicode
== SPACE
|| unicode
== TAB
)) {
3043 if(!current
.isEmpty()) {
3044 ret
.append(current
);
3048 if(replaced
&& unicode
)
3049 current
.append(QChar(unicode
));
3052 ret
= QStringList(str
);
3053 else if(!current
.isEmpty())
3054 ret
.append(current
);
3055 //qDebug() << "REPLACE" << str << ret;
3059 QStringList
&QMakeProject::values(const QString
&_var
, QMap
<QString
, QStringList
> &place
)
3061 QString var
= varMap(_var
);
3062 if(var
== QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token)
3063 var
= ".BUILTIN." + var
;
3064 place
[var
] = QStringList(QLatin1String("\t"));
3065 } else if(var
== QLatin1String("LITERAL_DOLLAR")) { //a real $
3066 var
= ".BUILTIN." + var
;
3067 place
[var
] = QStringList(QLatin1String("$"));
3068 } else if(var
== QLatin1String("LITERAL_HASH")) { //a real #
3069 var
= ".BUILTIN." + var
;
3070 place
[var
] = QStringList("#");
3071 } else if(var
== QLatin1String("OUT_PWD")) { //the out going dir
3072 var
= ".BUILTIN." + var
;
3073 place
[var
] = QStringList(Option::output_dir
);
3074 } else if(var
== QLatin1String("PWD") || //current working dir (of _FILE_)
3075 var
== QLatin1String("IN_PWD")) {
3076 var
= ".BUILTIN." + var
;
3077 place
[var
] = QStringList(qmake_getpwd());
3078 } else if(var
== QLatin1String("DIR_SEPARATOR")) {
3079 var
= ".BUILTIN." + var
;
3080 place
[var
] = QStringList(Option::dir_sep
);
3081 } else if(var
== QLatin1String("DIRLIST_SEPARATOR")) {
3082 var
= ".BUILTIN." + var
;
3083 place
[var
] = QStringList(Option::dirlist_sep
);
3084 } else if(var
== QLatin1String("_LINE_")) { //parser line number
3085 var
= ".BUILTIN." + var
;
3086 place
[var
] = QStringList(QString::number(parser
.line_no
));
3087 } else if(var
== QLatin1String("_FILE_")) { //parser file
3088 var
= ".BUILTIN." + var
;
3089 place
[var
] = QStringList(parser
.file
);
3090 } else if(var
== QLatin1String("_DATE_")) { //current date/time
3091 var
= ".BUILTIN." + var
;
3092 place
[var
] = QStringList(QDateTime::currentDateTime().toString());
3093 } else if(var
== QLatin1String("_PRO_FILE_")) {
3094 var
= ".BUILTIN." + var
;
3095 place
[var
] = QStringList(pfile
);
3096 } else if(var
== QLatin1String("_PRO_FILE_PWD_")) {
3097 var
= ".BUILTIN." + var
;
3098 place
[var
] = QStringList(QFileInfo(pfile
).absolutePath());
3099 } else if(var
== QLatin1String("_QMAKE_CACHE_")) {
3100 var
= ".BUILTIN." + var
;
3101 if(Option::mkfile::do_cache
)
3102 place
[var
] = QStringList(Option::mkfile::cachefile
);
3103 } else if(var
== QLatin1String("TEMPLATE")) {
3104 if(!Option::user_template
.isEmpty()) {
3105 var
= ".BUILTIN.USER." + var
;
3106 place
[var
] = QStringList(Option::user_template
);
3107 } else if(!place
[var
].isEmpty()) {
3108 QString orig_template
= place
["TEMPLATE"].first(), real_template
;
3109 if(!Option::user_template_prefix
.isEmpty() && !orig_template
.startsWith(Option::user_template_prefix
))
3110 real_template
= Option::user_template_prefix
+ orig_template
;
3111 if(real_template
.endsWith(".t"))
3112 real_template
= real_template
.left(real_template
.length()-2);
3113 if(!real_template
.isEmpty()) {
3114 var
= ".BUILTIN." + var
;
3115 place
[var
] = QStringList(real_template
);
3118 var
= ".BUILTIN." + var
;
3119 place
[var
] = QStringList("app");
3121 } else if(var
.startsWith(QLatin1String("QMAKE_HOST."))) {
3122 QString ret
, type
= var
.mid(11);
3123 #if defined(Q_OS_WIN32)
3126 } else if(type
== "name") {
3127 DWORD name_length
= 1024;
3129 if(GetComputerName(name
, &name_length
))
3130 ret
= QString::fromUtf16((ushort
*)name
, name_length
);
3131 } else if(type
== "version" || type
== "version_string") {
3132 QSysInfo::WinVersion ver
= QSysInfo::WindowsVersion
;
3133 if(type
== "version")
3134 ret
= QString::number(ver
);
3135 else if(ver
== QSysInfo::WV_Me
)
3137 else if(ver
== QSysInfo::WV_95
)
3139 else if(ver
== QSysInfo::WV_98
)
3141 else if(ver
== QSysInfo::WV_NT
)
3143 else if(ver
== QSysInfo::WV_2000
)
3145 else if(ver
== QSysInfo::WV_2000
)
3147 else if(ver
== QSysInfo::WV_XP
)
3149 else if(ver
== QSysInfo::WV_VISTA
)
3153 } else if(type
== "arch") {
3155 GetSystemInfo(&info
);
3156 switch(info
.wProcessorArchitecture
) {
3157 #ifdef PROCESSOR_ARCHITECTURE_AMD64
3158 case PROCESSOR_ARCHITECTURE_AMD64
:
3162 case PROCESSOR_ARCHITECTURE_INTEL
:
3165 case PROCESSOR_ARCHITECTURE_IA64
:
3166 #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
3167 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
:
3176 #elif defined(Q_OS_UNIX)
3177 struct utsname name
;
3181 else if(type
== "name")
3182 ret
= name
.nodename
;
3183 else if(type
== "version")
3185 else if(type
== "version_string")
3187 else if(type
== "arch")
3191 var
= ".BUILTIN.HOST." + type
;
3192 place
[var
] = QStringList(ret
);
3193 } else if (var
== QLatin1String("QMAKE_DIR_SEP")) {
3194 if (place
[var
].isEmpty())
3195 return values("DIR_SEPARATOR", place
);
3197 //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::")));