1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the qmake application of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
45 #include "cachekeys.h"
49 #include <qdatetime.h>
51 #include <qfileinfo.h>
54 #include <qtextstream.h>
60 #include <sys/utsname.h>
61 #elif defined(Q_OS_WIN32)
68 #define QT_POPEN _popen
69 #define QT_PCLOSE _pclose
71 #define QT_POPEN popen
72 #define QT_PCLOSE pclose
78 enum ExpandFunc
{ E_MEMBER
=1, E_FIRST
, E_LAST
, E_CAT
, E_FROMFILE
, E_EVAL
, E_LIST
,
79 E_SPRINTF
, E_JOIN
, E_SPLIT
, E_BASENAME
, E_DIRNAME
, E_SECTION
,
80 E_FIND
, E_SYSTEM
, E_UNIQUE
, E_QUOTE
, E_ESCAPE_EXPAND
,
81 E_UPPER
, E_LOWER
, E_FILES
, E_PROMPT
, E_RE_ESCAPE
, E_REPLACE
,
83 QMap
<QString
, ExpandFunc
> qmake_expandFunctions()
85 static QMap
<QString
, ExpandFunc
> *qmake_expand_functions
= 0;
86 if(!qmake_expand_functions
) {
87 qmake_expand_functions
= new QMap
<QString
, ExpandFunc
>;
88 qmakeAddCacheClear(qmakeDeleteCacheClear_QMapStringInt
, (void**)&qmake_expand_functions
);
89 qmake_expand_functions
->insert("member", E_MEMBER
);
90 qmake_expand_functions
->insert("first", E_FIRST
);
91 qmake_expand_functions
->insert("last", E_LAST
);
92 qmake_expand_functions
->insert("cat", E_CAT
);
93 qmake_expand_functions
->insert("fromfile", E_FROMFILE
);
94 qmake_expand_functions
->insert("eval", E_EVAL
);
95 qmake_expand_functions
->insert("list", E_LIST
);
96 qmake_expand_functions
->insert("sprintf", E_SPRINTF
);
97 qmake_expand_functions
->insert("join", E_JOIN
);
98 qmake_expand_functions
->insert("split", E_SPLIT
);
99 qmake_expand_functions
->insert("basename", E_BASENAME
);
100 qmake_expand_functions
->insert("dirname", E_DIRNAME
);
101 qmake_expand_functions
->insert("section", E_SECTION
);
102 qmake_expand_functions
->insert("find", E_FIND
);
103 qmake_expand_functions
->insert("system", E_SYSTEM
);
104 qmake_expand_functions
->insert("unique", E_UNIQUE
);
105 qmake_expand_functions
->insert("quote", E_QUOTE
);
106 qmake_expand_functions
->insert("escape_expand", E_ESCAPE_EXPAND
);
107 qmake_expand_functions
->insert("upper", E_UPPER
);
108 qmake_expand_functions
->insert("lower", E_LOWER
);
109 qmake_expand_functions
->insert("re_escape", E_RE_ESCAPE
);
110 qmake_expand_functions
->insert("files", E_FILES
);
111 qmake_expand_functions
->insert("prompt", E_PROMPT
);
112 qmake_expand_functions
->insert("replace", E_REPLACE
);
113 qmake_expand_functions
->insert("size", E_SIZE
);
115 return *qmake_expand_functions
;
118 enum TestFunc
{ T_REQUIRES
=1, T_GREATERTHAN
, T_LESSTHAN
, T_EQUALS
,
119 T_EXISTS
, T_EXPORT
, T_CLEAR
, T_UNSET
, T_EVAL
, T_CONFIG
, T_SYSTEM
,
120 T_RETURN
, T_BREAK
, T_NEXT
, T_DEFINED
, T_CONTAINS
, T_INFILE
,
121 T_COUNT
, T_ISEMPTY
, T_INCLUDE
, T_LOAD
, T_DEBUG
, T_ERROR
,
122 T_MESSAGE
, T_WARNING
, T_IF
};
123 QMap
<QString
, TestFunc
> qmake_testFunctions()
125 static QMap
<QString
, TestFunc
> *qmake_test_functions
= 0;
126 if(!qmake_test_functions
) {
127 qmake_test_functions
= new QMap
<QString
, TestFunc
>;
128 qmake_test_functions
->insert("requires", T_REQUIRES
);
129 qmake_test_functions
->insert("greaterThan", T_GREATERTHAN
);
130 qmake_test_functions
->insert("lessThan", T_LESSTHAN
);
131 qmake_test_functions
->insert("equals", T_EQUALS
);
132 qmake_test_functions
->insert("isEqual", T_EQUALS
);
133 qmake_test_functions
->insert("exists", T_EXISTS
);
134 qmake_test_functions
->insert("export", T_EXPORT
);
135 qmake_test_functions
->insert("clear", T_CLEAR
);
136 qmake_test_functions
->insert("unset", T_UNSET
);
137 qmake_test_functions
->insert("eval", T_EVAL
);
138 qmake_test_functions
->insert("CONFIG", T_CONFIG
);
139 qmake_test_functions
->insert("if", T_IF
);
140 qmake_test_functions
->insert("isActiveConfig", T_CONFIG
);
141 qmake_test_functions
->insert("system", T_SYSTEM
);
142 qmake_test_functions
->insert("return", T_RETURN
);
143 qmake_test_functions
->insert("break", T_BREAK
);
144 qmake_test_functions
->insert("next", T_NEXT
);
145 qmake_test_functions
->insert("defined", T_DEFINED
);
146 qmake_test_functions
->insert("contains", T_CONTAINS
);
147 qmake_test_functions
->insert("infile", T_INFILE
);
148 qmake_test_functions
->insert("count", T_COUNT
);
149 qmake_test_functions
->insert("isEmpty", T_ISEMPTY
);
150 qmake_test_functions
->insert("include", T_INCLUDE
);
151 qmake_test_functions
->insert("load", T_LOAD
);
152 qmake_test_functions
->insert("debug", T_DEBUG
);
153 qmake_test_functions
->insert("error", T_ERROR
);
154 qmake_test_functions
->insert("message", T_MESSAGE
);
155 qmake_test_functions
->insert("warning", T_WARNING
);
157 return *qmake_test_functions
;
166 static QString
remove_quotes(const QString
&arg
)
168 const ushort SINGLEQUOTE
= '\'';
169 const ushort DOUBLEQUOTE
= '"';
171 const QChar
*arg_data
= arg
.data();
172 const ushort first
= arg_data
->unicode();
173 const int arg_len
= arg
.length();
174 if(first
== SINGLEQUOTE
|| first
== DOUBLEQUOTE
) {
175 const ushort last
= (arg_data
+arg_len
-1)->unicode();
177 return arg
.mid(1, arg_len
-2);
182 static QString
varMap(const QString
&x
)
185 if(ret
.startsWith("TMAKE")) //tmake no more!
186 ret
= "QMAKE" + ret
.mid(5);
187 else if(ret
== "INTERFACES")
189 else if(ret
== "QMAKE_POST_BUILD")
190 ret
= "QMAKE_POST_LINK";
191 else if(ret
== "TARGETDEPS")
192 ret
= "POST_TARGETDEPS";
193 else if(ret
== "LIBPATH")
194 ret
= "QMAKE_LIBDIR";
195 else if(ret
== "QMAKE_EXT_MOC")
196 ret
= "QMAKE_EXT_CPP_MOC";
197 else if(ret
== "QMAKE_MOD_MOC")
198 ret
= "QMAKE_H_MOD_MOC";
199 else if(ret
== "QMAKE_LFLAGS_SHAPP")
200 ret
= "QMAKE_LFLAGS_APP";
201 else if(ret
== "PRECOMPH")
202 ret
= "PRECOMPILED_HEADER";
203 else if(ret
== "PRECOMPCPP")
204 ret
= "PRECOMPILED_SOURCE";
205 else if(ret
== "INCPATH")
207 else if(ret
== "QMAKE_EXTRA_WIN_COMPILERS" || ret
== "QMAKE_EXTRA_UNIX_COMPILERS")
208 ret
= "QMAKE_EXTRA_COMPILERS";
209 else if(ret
== "QMAKE_EXTRA_WIN_TARGETS" || ret
== "QMAKE_EXTRA_UNIX_TARGETS")
210 ret
= "QMAKE_EXTRA_TARGETS";
211 else if(ret
== "QMAKE_EXTRA_UNIX_INCLUDES")
212 ret
= "QMAKE_EXTRA_INCLUDES";
213 else if(ret
== "QMAKE_EXTRA_UNIX_VARIABLES")
214 ret
= "QMAKE_EXTRA_VARIABLES";
215 else if(ret
== "QMAKE_RPATH")
216 ret
= "QMAKE_LFLAGS_RPATH";
217 else if(ret
== "QMAKE_FRAMEWORKDIR")
218 ret
= "QMAKE_FRAMEWORKPATH";
219 else if(ret
== "QMAKE_FRAMEWORKDIR_FLAGS")
220 ret
= "QMAKE_FRAMEWORKPATH_FLAGS";
224 static QStringList
split_arg_list(QString params
)
229 const ushort LPAREN
= '(';
230 const ushort RPAREN
= ')';
231 const ushort SINGLEQUOTE
= '\'';
232 const ushort DOUBLEQUOTE
= '"';
233 const ushort COMMA
= ',';
234 const ushort SPACE
= ' ';
235 //const ushort TAB = '\t';
238 const QChar
*params_data
= params
.data();
239 const int params_len
= params
.length();
241 while(last
< params_len
&& (params_data
[last
].unicode() == SPACE
242 /*|| params_data[last].unicode() == TAB*/))
244 for(int x
= last
, parens
= 0; x
<= params_len
; x
++) {
245 unicode
= params_data
[x
].unicode();
246 if(x
== params_len
) {
247 while(x
&& params_data
[x
-1].unicode() == SPACE
)
249 QString
mid(params_data
+last
, x
-last
);
251 if(mid
[0] == quote
&& mid
[(int)mid
.length()-1] == quote
)
252 mid
= mid
.mid(1, mid
.length()-2);
258 if(unicode
== LPAREN
) {
260 } else if(unicode
== RPAREN
) {
262 } else if(quote
&& unicode
== quote
) {
264 } else if(!quote
&& (unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
)) {
267 if(!parens
&& !quote
&& unicode
== COMMA
) {
268 QString mid
= params
.mid(last
, x
- last
).trimmed();
271 while(last
< params_len
&& (params_data
[last
].unicode() == SPACE
272 /*|| params_data[last].unicode() == TAB*/))
279 static QStringList
split_value_list(const QString
&vals
, bool do_semicolon
=false)
285 const ushort LPAREN
= '(';
286 const ushort RPAREN
= ')';
287 const ushort SINGLEQUOTE
= '\'';
288 const ushort DOUBLEQUOTE
= '"';
289 const ushort BACKSLASH
= '\\';
290 const ushort SEMICOLON
= ';';
293 const QChar
*vals_data
= vals
.data();
294 const int vals_len
= vals
.length();
295 for(int x
= 0, parens
= 0; x
< vals_len
; x
++) {
296 unicode
= vals_data
[x
].unicode();
297 if(x
!= (int)vals_len
-1 && unicode
== BACKSLASH
&&
298 (vals_data
[x
+1].unicode() == SINGLEQUOTE
|| vals_data
[x
+1].unicode() == DOUBLEQUOTE
)) {
299 build
+= vals_data
[x
++]; //get that 'escape'
300 } else if(!quote
.isEmpty() && unicode
== quote
.top()) {
302 } else if(unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
) {
304 } else if(unicode
== RPAREN
) {
306 } else if(unicode
== LPAREN
) {
310 if(!parens
&& quote
.isEmpty() && ((do_semicolon
&& unicode
== SEMICOLON
) ||
311 vals_data
[x
] == Option::field_sep
)) {
315 build
+= vals_data
[x
];
323 //just a parsable entity
326 ParsableBlock() : ref_cnt(1) { }
327 virtual ~ParsableBlock() { }
332 Parse(const QString
&t
) : text(t
){ pi
= parser
; }
334 QList
<Parse
> parselist
;
336 inline int ref() { return ++ref_cnt
; }
337 inline int deref() { return --ref_cnt
; }
341 virtual bool continueBlock() = 0;
342 bool eval(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
);
345 bool ParsableBlock::eval(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
)
348 parser_info pi
= parser
;
349 const int block_count
= p
->scope_blocks
.count();
353 for(int i
= 0; i
< parselist
.count(); i
++) {
354 parser
= parselist
.at(i
).pi
;
355 if(!(ret
= p
->parse(parselist
.at(i
).text
, place
)) || !continueBlock())
361 while(p
->scope_blocks
.count() > block_count
)
362 p
->scope_blocks
.pop();
367 struct FunctionBlock
: public ParsableBlock
369 FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { }
371 QMap
<QString
, QStringList
> vars
;
372 QMap
<QString
, QStringList
> *calling_place
;
373 QStringList return_value
;
377 bool exec(const QList
<QStringList
> &args
,
378 QMakeProject
*p
, QMap
<QString
, QStringList
> &place
, QStringList
&functionReturn
);
379 virtual bool continueBlock() { return !cause_return
; }
382 bool FunctionBlock::exec(const QList
<QStringList
> &args
,
383 QMakeProject
*proj
, QMap
<QString
, QStringList
> &place
,
384 QStringList
&functionReturn
)
388 calling_place
= &place
;
390 calling_place
= &proj
->variables();
392 return_value
.clear();
393 cause_return
= false;
397 vars
= proj
->variables(); // should be place so that local variables can be inherited
401 vars
["ARGS"].clear();
402 for(int i
= 0; i
< args
.count(); i
++) {
403 vars
["ARGS"] += args
[i
];
404 vars
[QString::number(i
+1)] = args
[i
];
406 bool ret
= ParsableBlock::eval(proj
, vars
);
407 functionReturn
= return_value
;
411 return_value
.clear();
417 struct IteratorBlock
: public ParsableBlock
419 IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { }
428 Test(const QString
&f
, QStringList
&a
, bool i
) : func(f
), args(a
), invert(i
) { pi
= parser
; }
434 bool loop_forever
, cause_break
, cause_next
;
437 bool exec(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
);
438 virtual bool continueBlock() { return !cause_next
&& !cause_break
; }
440 bool IteratorBlock::exec(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
)
443 QStringList::Iterator it
;
446 int iterate_count
= 0;
448 IteratorBlock
*saved_iterator
= p
->iterator
;
452 while(loop_forever
|| it
!= list
.end()) {
453 cause_next
= cause_break
= false;
454 if(!loop_forever
&& (*it
).isEmpty()) { //ignore empty items
459 //set up the loop variable
461 if(!variable
.isEmpty()) {
462 va
= place
[variable
];
464 place
[variable
] = QStringList(QString::number(iterate_count
));
466 place
[variable
] = QStringList(*it
);
470 for(QList
<Test
>::Iterator test_it
= test
.begin(); test_it
!= test
.end(); ++test_it
) {
471 parser
= (*test_it
).pi
;
472 succeed
= p
->doProjectTest((*test_it
).func
, (*test_it
).args
, place
);
473 if((*test_it
).invert
)
479 ret
= ParsableBlock::eval(p
, place
);
480 //restore the variable in the map
481 if(!variable
.isEmpty())
482 place
[variable
] = va
;
487 if(!ret
|| cause_break
)
492 p
->iterator
= saved_iterator
;
496 QMakeProject::ScopeBlock::~ScopeBlock()
504 static void qmake_error_msg(const QString
&msg
)
506 fprintf(stderr
, "%s:%d: %s\n", parser
.file
.toLatin1().constData(), parser
.line_no
,
507 msg
.toLatin1().constData());
510 enum isForSymbian_enum
{
511 isForSymbian_NOT_SET
= -1,
512 isForSymbian_FALSE
= 0,
513 isForSymbian_ABLD
= 1,
514 isForSymbian_SBSV2
= 2,
517 static isForSymbian_enum isForSymbian_value
= isForSymbian_NOT_SET
;
519 // Checking for symbian build is primarily determined from the qmake spec,
520 // but if that is not specified, detect if symbian is the default spec
521 // by checking the MAKEFILE_GENERATOR variable value.
522 static void init_symbian(const QMap
<QString
, QStringList
>& vars
)
524 if (isForSymbian_value
!= isForSymbian_NOT_SET
)
527 QString spec
= QFileInfo(Option::mkfile::qmakespec
).fileName();
528 if (spec
.startsWith("symbian-abld", Qt::CaseInsensitive
)) {
529 isForSymbian_value
= isForSymbian_ABLD
;
530 } else if (spec
.startsWith("symbian-sbsv2", Qt::CaseInsensitive
)) {
531 isForSymbian_value
= isForSymbian_SBSV2
;
533 QStringList generatorList
= vars
["MAKEFILE_GENERATOR"];
535 if (!generatorList
.isEmpty()) {
536 QString generator
= generatorList
.first();
537 if (generator
.startsWith("SYMBIAN_ABLD"))
538 isForSymbian_value
= isForSymbian_ABLD
;
539 else if (generator
.startsWith("SYMBIAN_SBSV2"))
540 isForSymbian_value
= isForSymbian_SBSV2
;
542 isForSymbian_value
= isForSymbian_FALSE
;
544 isForSymbian_value
= isForSymbian_FALSE
;
548 // Force recursive on Symbian, as non-recursive is not really a viable option there
549 if (isForSymbian_value
!= isForSymbian_FALSE
)
550 Option::recursive
= true;
555 // If isForSymbian_value has not been initialized explicitly yet,
556 // call initializer with dummy map to check qmake spec.
557 if (isForSymbian_value
== isForSymbian_NOT_SET
)
558 init_symbian(QMap
<QString
, QStringList
>());
560 return (isForSymbian_value
!= isForSymbian_FALSE
);
563 bool isForSymbianSbsv2()
565 // If isForSymbian_value has not been initialized explicitly yet,
566 // call initializer with dummy map to check qmake spec.
567 if (isForSymbian_value
== isForSymbian_NOT_SET
)
568 init_symbian(QMap
<QString
, QStringList
>());
570 return (isForSymbian_value
== isForSymbian_SBSV2
);
574 1) environment variable QMAKEFEATURES (as separated by colons)
575 2) property variable QMAKEFEATURES (as separated by colons)
576 3) <project_root> (where .qmake.cache lives) + FEATURES_DIR
577 4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR
578 5) your QMAKESPEC/features dir
579 6) your data_install/mkspecs/FEATURES_DIR
580 7) your QMAKESPEC/../FEATURES_DIR dir
582 FEATURES_DIR is defined as:
584 1) features/(unix|win32|macx)/
587 QStringList
qmake_feature_paths(QMakeProperty
*prop
=0)
591 const QString base_concat
= QDir::separator() + QString("features");
592 switch(Option::target_mode
) {
593 case Option::TARG_MACX_MODE
: //also a unix
594 concat
<< base_concat
+ QDir::separator() + "mac";
595 concat
<< base_concat
+ QDir::separator() + "macx";
596 concat
<< base_concat
+ QDir::separator() + "unix";
598 case Option::TARG_UNIX_MODE
:
601 concat
<< base_concat
+ QDir::separator() + "symbian";
603 concat
<< base_concat
+ QDir::separator() + "unix";
606 case Option::TARG_WIN_MODE
:
609 concat
<< base_concat
+ QDir::separator() + "symbian";
611 concat
<< base_concat
+ QDir::separator() + "win32";
614 case Option::TARG_MAC9_MODE
:
615 concat
<< base_concat
+ QDir::separator() + "mac";
616 concat
<< base_concat
+ QDir::separator() + "mac9";
619 concat
<< base_concat
;
621 const QString mkspecs_concat
= QDir::separator() + QString("mkspecs");
622 QStringList feature_roots
;
623 QByteArray mkspec_path
= qgetenv("QMAKEFEATURES");
624 if(!mkspec_path
.isNull())
625 feature_roots
+= splitPathList(QString::fromLocal8Bit(mkspec_path
));
627 feature_roots
+= splitPathList(prop
->value("QMAKEFEATURES"));
628 if(!Option::mkfile::cachefile
.isEmpty()) {
630 int last_slash
= Option::mkfile::cachefile
.lastIndexOf(Option::dir_sep
);
632 path
= Option::fixPathToLocalOS(Option::mkfile::cachefile
.left(last_slash
));
633 for(QStringList::Iterator concat_it
= concat
.begin();
634 concat_it
!= concat
.end(); ++concat_it
)
635 feature_roots
<< (path
+ (*concat_it
));
637 QByteArray qmakepath
= qgetenv("QMAKEPATH");
638 if (!qmakepath
.isNull()) {
639 const QStringList lst
= splitPathList(QString::fromLocal8Bit(qmakepath
));
640 for(QStringList::ConstIterator it
= lst
.begin(); it
!= lst
.end(); ++it
) {
641 for(QStringList::Iterator concat_it
= concat
.begin();
642 concat_it
!= concat
.end(); ++concat_it
)
643 feature_roots
<< ((*it
) + mkspecs_concat
+ (*concat_it
));
646 if(!Option::mkfile::qmakespec
.isEmpty())
647 feature_roots
<< Option::mkfile::qmakespec
+ QDir::separator() + "features";
648 if(!Option::mkfile::qmakespec
.isEmpty()) {
649 QFileInfo
specfi(Option::mkfile::qmakespec
);
650 QDir
specdir(specfi
.absoluteFilePath());
651 while(!specdir
.isRoot()) {
652 if(!specdir
.cdUp() || specdir
.isRoot())
654 if(QFile::exists(specdir
.path() + QDir::separator() + "features")) {
655 for(QStringList::Iterator concat_it
= concat
.begin();
656 concat_it
!= concat
.end(); ++concat_it
)
657 feature_roots
<< (specdir
.path() + (*concat_it
));
662 for(QStringList::Iterator concat_it
= concat
.begin();
663 concat_it
!= concat
.end(); ++concat_it
)
664 feature_roots
<< (QLibraryInfo::location(QLibraryInfo::PrefixPath
) +
665 mkspecs_concat
+ (*concat_it
));
666 for(QStringList::Iterator concat_it
= concat
.begin();
667 concat_it
!= concat
.end(); ++concat_it
)
668 feature_roots
<< (QLibraryInfo::location(QLibraryInfo::DataPath
) +
669 mkspecs_concat
+ (*concat_it
));
670 return feature_roots
;
673 QStringList
qmake_mkspec_paths()
676 const QString concat
= QDir::separator() + QString("mkspecs");
677 QByteArray qmakepath
= qgetenv("QMAKEPATH");
678 if (!qmakepath
.isEmpty()) {
679 const QStringList lst
= splitPathList(QString::fromLocal8Bit(qmakepath
));
680 for(QStringList::ConstIterator it
= lst
.begin(); it
!= lst
.end(); ++it
)
681 ret
<< ((*it
) + concat
);
683 ret
<< QLibraryInfo::location(QLibraryInfo::DataPath
) + concat
;
688 class QMakeProjectEnv
692 QMakeProjectEnv() { }
693 QMakeProjectEnv(QMakeProject
*p
) { execute(p
->variables()); }
694 QMakeProjectEnv(const QMap
<QString
, QStringList
> &values
) { execute(values
); }
696 void execute(QMakeProject
*p
) { execute(p
->variables()); }
697 void execute(const QMap
<QString
, QStringList
> &values
) {
699 for(QMap
<QString
, QStringList
>::ConstIterator it
= values
.begin(); it
!= values
.end(); ++it
) {
700 const QString var
= it
.key(), val
= it
.value().join(" ");
701 if(!var
.startsWith(".")) {
702 const QString env_var
= Option::sysenv_mod
+ var
;
703 if(!putenv(strdup(QString(env_var
+ "=" + val
).toAscii().data())))
704 envs
.append(env_var
);
713 for(QStringList::ConstIterator it
= envs
.begin();it
!= envs
.end(); ++it
) {
714 putenv(strdup(QString(*it
+ "=").toAscii().data()));
720 QMakeProject::~QMakeProject()
724 for(QMap
<QString
, FunctionBlock
*>::iterator it
= replaceFunctions
.begin(); it
!= replaceFunctions
.end(); ++it
) {
725 if(!it
.value()->deref())
728 replaceFunctions
.clear();
729 for(QMap
<QString
, FunctionBlock
*>::iterator it
= testFunctions
.begin(); it
!= testFunctions
.end(); ++it
) {
730 if(!it
.value()->deref())
733 testFunctions
.clear();
738 QMakeProject::init(QMakeProperty
*p
, const QMap
<QString
, QStringList
> *vars
)
743 prop
= new QMakeProperty
;
752 QMakeProject::QMakeProject(QMakeProject
*p
, const QMap
<QString
, QStringList
> *vars
)
754 init(p
->properties(), vars
? vars
: &p
->variables());
755 for(QMap
<QString
, FunctionBlock
*>::iterator it
= p
->replaceFunctions
.begin(); it
!= p
->replaceFunctions
.end(); ++it
) {
757 replaceFunctions
.insert(it
.key(), it
.value());
759 for(QMap
<QString
, FunctionBlock
*>::iterator it
= p
->testFunctions
.begin(); it
!= p
->testFunctions
.end(); ++it
) {
761 testFunctions
.insert(it
.key(), it
.value());
766 QMakeProject::reset()
768 // scope_blocks starts with one non-ignoring entity
769 scope_blocks
.clear();
770 scope_blocks
.push(ScopeBlock());
776 QMakeProject::parse(const QString
&t
, QMap
<QString
, QStringList
> &place
, int numLines
)
778 QString s
= t
.simplified();
779 int hash_mark
= s
.indexOf("#");
780 if(hash_mark
!= -1) //good bye comments
781 s
= s
.left(hash_mark
);
782 if(s
.isEmpty()) // blank_line
785 if(scope_blocks
.top().ignore
) {
786 bool continue_parsing
= false;
787 // adjust scope for each block which appears on a single line
788 for(int i
= 0; i
< s
.length(); i
++) {
790 scope_blocks
.push(ScopeBlock(true));
791 } else if(s
[i
] == '}') {
792 if(scope_blocks
.count() == 1) {
793 fprintf(stderr
, "Braces mismatch %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
796 ScopeBlock sb
= scope_blocks
.pop();
798 sb
.iterate
->exec(this, place
);
802 if(!scope_blocks
.top().ignore
) {
803 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
804 parser
.line_no
, scope_blocks
.count()+1);
805 s
= s
.mid(i
+1).trimmed();
806 continue_parsing
= !s
.isEmpty();
811 if(!continue_parsing
) {
812 debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
813 parser
.file
.toLatin1().constData(), parser
.line_no
);
821 const QChar
*d
= s
.unicode();
822 bool function_finished
= false;
823 while(d_off
< s
.length()) {
824 if(*(d
+d_off
) == QLatin1Char('}')) {
825 function
->scope_level
--;
826 if(!function
->scope_level
) {
827 function_finished
= true;
830 } else if(*(d
+d_off
) == QLatin1Char('{')) {
831 function
->scope_level
++;
833 append
+= *(d
+d_off
);
836 if(!append
.isEmpty())
837 function
->parselist
.append(IteratorBlock::Parse(append
));
838 if(function_finished
) {
840 s
= QString(d
+d_off
, s
.length()-d_off
);
844 } else if(IteratorBlock
*it
= scope_blocks
.top().iterate
) {
847 const QChar
*d
= s
.unicode();
848 bool iterate_finished
= false;
849 while(d_off
< s
.length()) {
850 if(*(d
+d_off
) == QLatin1Char('}')) {
852 if(!it
->scope_level
) {
853 iterate_finished
= true;
856 } else if(*(d
+d_off
) == QLatin1Char('{')) {
859 append
+= *(d
+d_off
);
862 if(!append
.isEmpty())
863 scope_blocks
.top().iterate
->parselist
.append(IteratorBlock::Parse(append
));
864 if(iterate_finished
) {
865 scope_blocks
.top().iterate
= 0;
866 bool ret
= it
->exec(this, place
);
876 QString scope
, var
, op
;
878 #define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o
879 const QChar
*d
= s
.unicode();
881 SKIP_WS(d
, d_off
, s
.length());
882 IteratorBlock
*iterator
= 0;
883 bool scope_failed
= false, else_line
= false, or_op
=false;
885 int parens
= 0, scope_count
=0, start_block
= 0;
886 while(d_off
< s
.length()) {
888 if(*(d
+d_off
) == QLatin1Char('='))
890 if(*(d
+d_off
) == QLatin1Char('+') || *(d
+d_off
) == QLatin1Char('-') ||
891 *(d
+d_off
) == QLatin1Char('*') || *(d
+d_off
) == QLatin1Char('~')) {
892 if(*(d
+d_off
+1) == QLatin1Char('=')) {
894 } else if(*(d
+d_off
+1) == QLatin1Char(' ')) {
895 const QChar
*k
= d
+d_off
+1;
897 SKIP_WS(k
, k_off
, s
.length()-d_off
);
898 if(*(k
+k_off
) == QLatin1Char('=')) {
900 qmake_error_msg(QString(d
+d_off
, 1) + "must be followed immediately by =");
907 if(!quote
.isNull()) {
908 if(*(d
+d_off
) == quote
)
910 } else if(*(d
+d_off
) == '(') {
912 } else if(*(d
+d_off
) == ')') {
914 } else if(*(d
+d_off
) == '"' /*|| *(d+d_off) == '\''*/) {
918 if(!parens
&& quote
.isNull() &&
919 (*(d
+d_off
) == QLatin1Char(':') || *(d
+d_off
) == QLatin1Char('{') ||
920 *(d
+d_off
) == QLatin1Char(')') || *(d
+d_off
) == QLatin1Char('|'))) {
922 scope
= var
.trimmed();
923 if(*(d
+d_off
) == QLatin1Char(')'))
924 scope
+= *(d
+d_off
); // need this
927 bool test
= scope_failed
;
928 if(scope
.isEmpty()) {
930 } else if(scope
.toLower() == "else") { //else is a builtin scope here as it modifies state
931 if(scope_count
!= 1 || scope_blocks
.top().else_status
== ScopeBlock::TestNone
) {
932 qmake_error_msg(("Unexpected " + scope
+ " ('" + s
+ "')").toLatin1());
936 test
= (scope_blocks
.top().else_status
== ScopeBlock::TestSeek
);
937 debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser
.file
.toLatin1().constData(), parser
.line_no
,
938 scope
== "else" ? "" : QString(" (" + scope
+ ")").toLatin1().constData(),
939 test
? "considered" : "excluded");
941 QString comp_scope
= scope
;
942 bool invert_test
= (comp_scope
.at(0) == QLatin1Char('!'));
944 comp_scope
= comp_scope
.mid(1);
945 int lparen
= comp_scope
.indexOf('(');
946 if(or_op
== scope_failed
) {
947 if(lparen
!= -1) { // if there is an lparen in the scope, it IS a function
948 int rparen
= comp_scope
.lastIndexOf(')');
950 qmake_error_msg("Function missing right paren: " + comp_scope
);
953 QString func
= comp_scope
.left(lparen
);
954 QStringList args
= split_arg_list(comp_scope
.mid(lparen
+1, rparen
- lparen
- 1));
956 fprintf(stderr
, "%s:%d: No tests can come after a function definition!\n",
957 parser
.file
.toLatin1().constData(), parser
.line_no
);
959 } else if(func
== "for") { //for is a builtin function here, as it modifies state
960 if(args
.count() > 2 || args
.count() < 1) {
961 fprintf(stderr
, "%s:%d: for(iterate, list) requires two arguments.\n",
962 parser
.file
.toLatin1().constData(), parser
.line_no
);
964 } else if(iterator
) {
965 fprintf(stderr
, "%s:%d unexpected nested for()\n",
966 parser
.file
.toLatin1().constData(), parser
.line_no
);
970 iterator
= new IteratorBlock
;
972 if(args
.count() == 1) {
973 doVariableReplace(args
[0], place
);
975 if(args
[0] != "ever") {
978 fprintf(stderr
, "%s:%d: for(iterate, list) requires two arguments.\n",
979 parser
.file
.toLatin1().constData(), parser
.line_no
);
983 } else if(args
.count() == 2) {
984 iterator
->variable
= args
[0];
985 doVariableReplace(args
[1], place
);
988 QStringList list
= place
[it_list
];
990 if(it_list
== "forever") {
991 iterator
->loop_forever
= true;
993 int dotdot
= it_list
.indexOf("..");
996 int start
= it_list
.left(dotdot
).toInt(&ok
);
998 int end
= it_list
.mid(dotdot
+2).toInt(&ok
);
1001 for(int i
= start
; i
<= end
; i
++)
1002 list
<< QString::number(i
);
1004 for(int i
= start
; i
>= end
; i
--)
1005 list
<< QString::number(i
);
1012 iterator
->list
= list
;
1013 test
= !invert_test
;
1014 } else if(iterator
) {
1015 iterator
->test
.append(IteratorBlock::Test(func
, args
, invert_test
));
1016 test
= !invert_test
;
1017 } else if(func
== "defineTest" || func
== "defineReplace") {
1018 if(!function_blocks
.isEmpty()) {
1020 "%s:%d: cannot define a function within another definition.\n",
1021 parser
.file
.toLatin1().constData(), parser
.line_no
);
1024 if(args
.count() != 1) {
1025 fprintf(stderr
, "%s:%d: %s(function_name) requires one argument.\n",
1026 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
1029 QMap
<QString
, FunctionBlock
*> *map
= 0;
1030 if(func
== "defineTest")
1031 map
= &testFunctions
;
1033 map
= &replaceFunctions
;
1035 if(!map
|| map
->contains(args
[0])) {
1036 fprintf(stderr
, "%s:%d: Function[%s] multiply defined.\n",
1037 parser
.file
.toLatin1().constData(), parser
.line_no
, args
[0].toLatin1().constData());
1041 function
= new FunctionBlock
;
1042 map
->insert(args
[0], function
);
1045 test
= doProjectTest(func
, args
, place
);
1046 if(*(d
+d_off
) == QLatin1Char(')') && d_off
== s
.length()-1) {
1049 scope_blocks
.top().else_status
=
1050 (test
? ScopeBlock::TestFound
: ScopeBlock::TestSeek
);
1051 return true; // assume we are done
1055 QString cscope
= comp_scope
.trimmed();
1056 doVariableReplace(cscope
, place
);
1057 test
= isActiveConfig(cscope
.trimmed(), true, &place
);
1063 if(!test
&& !scope_failed
)
1064 debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser
.file
.toLatin1().constData(),
1065 parser
.line_no
, scope
.toLatin1().constData());
1067 scope_failed
= !test
;
1068 or_op
= (*(d
+d_off
) == QLatin1Char('|'));
1070 if(*(d
+d_off
) == QLatin1Char('{')) { // scoping block
1073 for(int off
= 0, braces
= 0; true; ++off
) {
1074 if(*(d
+d_off
+off
) == QLatin1Char('{'))
1076 else if(*(d
+d_off
+off
) == QLatin1Char('}') && braces
)
1078 if(!braces
|| d_off
+off
== s
.length()) {
1079 iterator
->parselist
.append(s
.mid(d_off
, off
-1));
1081 iterator
->scope_level
+= braces
-1;
1088 } else if(!parens
&& *(d
+d_off
) == QLatin1Char('}')) {
1091 } else if(!scope_blocks
.count()) {
1092 warn_msg(WarnParser
, "Possible braces mismatch %s:%d", parser
.file
.toLatin1().constData(), parser
.line_no
);
1094 if(scope_blocks
.count() == 1) {
1095 fprintf(stderr
, "Braces mismatch %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
1098 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
1099 parser
.line_no
, scope_blocks
.count());
1100 ScopeBlock sb
= scope_blocks
.pop();
1102 sb
.iterate
->exec(this, place
);
1109 var
= var
.trimmed();
1111 if(!else_line
|| (else_line
&& !scope_failed
))
1112 scope_blocks
.top().else_status
= (!scope_failed
? ScopeBlock::TestFound
: ScopeBlock::TestSeek
);
1114 ScopeBlock
next_block(scope_failed
);
1115 next_block
.iterate
= iterator
;
1117 next_block
.else_status
= ScopeBlock::TestNone
;
1118 else if(scope_failed
)
1119 next_block
.else_status
= ScopeBlock::TestSeek
;
1121 next_block
.else_status
= ScopeBlock::TestFound
;
1122 scope_blocks
.push(next_block
);
1123 debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser
.file
.toLatin1().constData(),
1124 parser
.line_no
, scope_blocks
.count(), scope_failed
, s
.toLatin1().constData());
1125 } else if(iterator
) {
1126 iterator
->parselist
.append(var
+s
.mid(d_off
));
1127 bool ret
= iterator
->exec(this, place
);
1132 if((!scope_count
&& !var
.isEmpty()) || (scope_count
== 1 && else_line
))
1133 scope_blocks
.top().else_status
= ScopeBlock::TestNone
;
1134 if(d_off
== s
.length()) {
1135 if(!var
.trimmed().isEmpty())
1136 qmake_error_msg(("Parse Error ('" + s
+ "')").toLatin1());
1137 return var
.isEmpty(); // allow just a scope
1140 SKIP_WS(d
, d_off
, s
.length());
1141 for(; d_off
< s
.length() && op
.indexOf('=') == -1; op
+= *(d
+(d_off
++)))
1143 op
.replace(QRegExp("\\s"), "");
1145 SKIP_WS(d
, d_off
, s
.length());
1146 QString vals
= s
.mid(d_off
); // vals now contains the space separated list of values
1147 int rbraces
= vals
.count('}'), lbraces
= vals
.count('{');
1148 if(scope_blocks
.count() > 1 && rbraces
- lbraces
== 1) {
1149 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
1150 parser
.line_no
, scope_blocks
.count());
1151 ScopeBlock sb
= scope_blocks
.pop();
1153 sb
.iterate
->exec(this, place
);
1154 vals
.truncate(vals
.length()-1);
1155 } else if(rbraces
!= lbraces
) {
1156 warn_msg(WarnParser
, "Possible braces mismatch {%s} %s:%d",
1157 vals
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1160 return true; // oh well
1163 doVariableReplace(var
, place
);
1164 var
= varMap(var
); //backwards compatability
1165 if(!var
.isEmpty() && Option::mkfile::do_preprocess
) {
1166 static QString
last_file("*none*");
1167 if(parser
.file
!= last_file
) {
1168 fprintf(stdout
, "#file %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
1169 last_file
= parser
.file
;
1171 fprintf(stdout
, "%s %s %s\n", var
.toLatin1().constData(), op
.toLatin1().constData(), vals
.toLatin1().constData());
1174 if(vals
.contains('=') && numLines
> 1)
1175 warn_msg(WarnParser
, "Detected possible line continuation: {%s} %s:%d",
1176 var
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1178 QStringList
&varlist
= place
[var
]; // varlist is the list in the symbol table
1180 if(Option::debug_level
>= 1) {
1181 QString tmp_vals
= vals
;
1182 doVariableReplace(tmp_vals
, place
);
1183 debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser
.file
.toLatin1().constData(), parser
.line_no
,
1184 var
.toLatin1().constData(), op
.toLatin1().constData(), tmp_vals
.toLatin1().constData());
1187 // now do the operation
1189 doVariableReplace(vals
, place
);
1190 if(vals
.length() < 4 || vals
.at(0) != 's') {
1191 qmake_error_msg(("~= operator only can handle s/// function ('" +
1192 s
+ "')").toLatin1());
1195 QChar sep
= vals
.at(1);
1196 QStringList func
= vals
.split(sep
);
1197 if(func
.count() < 3 || func
.count() > 4) {
1198 qmake_error_msg(("~= operator only can handle s/// function ('" +
1199 s
+ "')").toLatin1());
1202 bool global
= false, case_sense
= true, quote
= false;
1203 if(func
.count() == 4) {
1204 global
= func
[3].indexOf('g') != -1;
1205 case_sense
= func
[3].indexOf('i') == -1;
1206 quote
= func
[3].indexOf('q') != -1;
1208 QString from
= func
[1], to
= func
[2];
1210 from
= QRegExp::escape(from
);
1211 QRegExp
regexp(from
, case_sense
? Qt::CaseSensitive
: Qt::CaseInsensitive
);
1212 for(QStringList::Iterator varit
= varlist
.begin(); varit
!= varlist
.end();) {
1213 if((*varit
).contains(regexp
)) {
1214 (*varit
) = (*varit
).replace(regexp
, to
);
1215 if ((*varit
).isEmpty())
1216 varit
= varlist
.erase(varit
);
1225 QStringList vallist
;
1227 //doVariableReplace(vals, place);
1228 QStringList tmp
= split_value_list(vals
, (var
== "DEPENDPATH" || var
== "INCLUDEPATH"));
1229 for(int i
= 0; i
< tmp
.size(); ++i
)
1230 vallist
+= doVariableReplaceExpand(tmp
[i
], place
);
1234 if(!varlist
.isEmpty()) {
1235 bool send_warning
= false;
1236 if(var
!= "TEMPLATE" && var
!= "TARGET") {
1237 QSet
<QString
> incoming_vals
= vallist
.toSet();
1238 for(int i
= 0; i
< varlist
.size(); ++i
) {
1239 const QString var
= varlist
.at(i
).trimmed();
1240 if(!var
.isEmpty() && !incoming_vals
.contains(var
)) {
1241 send_warning
= true;
1247 warn_msg(WarnParser
, "Operator=(%s) clears variables previously set: %s:%d",
1248 var
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1252 for(QStringList::ConstIterator valit
= vallist
.begin();
1253 valit
!= vallist
.end(); ++valit
) {
1254 if((*valit
).isEmpty())
1256 if((op
== "*=" && !varlist
.contains((*valit
))) ||
1257 op
== "=" || op
== "+=")
1258 varlist
.append((*valit
));
1260 varlist
.removeAll((*valit
));
1262 if(var
== "REQUIRES") // special case to get communicated to backends!
1263 doProjectCheckReqs(vallist
, place
);
1269 QMakeProject::read(QTextStream
&file
, QMap
<QString
, QStringList
> &place
)
1274 while(!file
.atEnd()) {
1276 QString line
= file
.readLine().trimmed();
1277 int prelen
= line
.length();
1279 int hash_mark
= line
.indexOf("#");
1280 if(hash_mark
!= -1) //good bye comments
1281 line
= line
.left(hash_mark
).trimmed();
1282 if(!line
.isEmpty() && line
.right(1) == "\\") {
1283 if(!line
.startsWith("#")) {
1284 line
.truncate(line
.length() - 1);
1285 s
+= line
+ Option::field_sep
;
1288 } else if(!line
.isEmpty() || (line
.isEmpty() && !prelen
)) {
1289 if(s
.isEmpty() && line
.isEmpty())
1291 if(!line
.isEmpty()) {
1296 if(!(ret
= parse(s
, place
, numLines
))) {
1307 ret
= parse(s
, place
, numLines
);
1312 QMakeProject::read(const QString
&file
, QMap
<QString
, QStringList
> &place
)
1314 parser_info pi
= parser
;
1317 const QString oldpwd
= qmake_getpwd();
1318 QString filename
= Option::fixPathToLocalOS(file
);
1319 doVariableReplace(filename
, place
);
1320 bool ret
= false, using_stdin
= false;
1322 if(!strcmp(filename
.toLatin1(), "-")) {
1323 qfile
.setFileName("");
1324 ret
= qfile
.open(stdin
, QIODevice::ReadOnly
);
1326 } else if(QFileInfo(file
).isDir()) {
1329 qfile
.setFileName(filename
);
1330 ret
= qfile
.open(QIODevice::ReadOnly
);
1331 qmake_setpwd(QFileInfo(filename
).absolutePath());
1334 parser_info pi
= parser
;
1335 parser
.from_file
= true;
1336 parser
.file
= filename
;
1338 QTextStream
t(&qfile
);
1339 ret
= read(t
, place
);
1343 if(scope_blocks
.count() != 1) {
1344 qmake_error_msg("Unterminated conditional block at end of file");
1348 qmake_setpwd(oldpwd
);
1353 QMakeProject::read(const QString
&project
, uchar cmd
)
1355 pfile
= QFileInfo(project
).absoluteFilePath();
1360 QMakeProject::read(uchar cmd
)
1362 if(cfile
.isEmpty()) {
1363 //find out where qmake (myself) lives
1364 if (!base_vars
.contains("QMAKE_QMAKE")) {
1365 if (!Option::qmake_abslocation
.isNull())
1366 base_vars
["QMAKE_QMAKE"] = QStringList(Option::qmake_abslocation
);
1368 base_vars
["QMAKE_QMAKE"] = QStringList("qmake");
1371 // hack to get the Option stuff in there
1372 base_vars
["QMAKE_EXT_OBJ"] = QStringList(Option::obj_ext
);
1373 base_vars
["QMAKE_EXT_CPP"] = Option::cpp_ext
;
1374 base_vars
["QMAKE_EXT_C"] = Option::c_ext
;
1375 base_vars
["QMAKE_EXT_H"] = Option::h_ext
;
1376 base_vars
["QMAKE_SH"] = Option::shellPath
;
1377 if(!Option::user_template_prefix
.isEmpty())
1378 base_vars
["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix
);
1380 if(cmd
& ReadCache
&& Option::mkfile::do_cache
) { // parse the cache
1381 int cache_depth
= -1;
1382 QString qmake_cache
= Option::mkfile::cachefile
;
1383 if(qmake_cache
.isEmpty()) { //find it as it has not been specified
1384 QString dir
= QDir::toNativeSeparators(Option::output_dir
);
1385 while(!QFile::exists((qmake_cache
= dir
+ QDir::separator() + ".qmake.cache"))) {
1386 dir
= dir
.left(dir
.lastIndexOf(QDir::separator()));
1387 if(dir
.isEmpty() || dir
.indexOf(QDir::separator()) == -1) {
1391 if(cache_depth
== -1)
1397 QString abs_cache
= QFileInfo(Option::mkfile::cachefile
).absoluteDir().path();
1398 if(Option::output_dir
.startsWith(abs_cache
))
1399 cache_depth
= Option::output_dir
.mid(abs_cache
.length()).count('/');
1401 if(!qmake_cache
.isEmpty()) {
1402 if(read(qmake_cache
, cache
)) {
1403 Option::mkfile::cachefile_depth
= cache_depth
;
1404 Option::mkfile::cachefile
= qmake_cache
;
1405 if(Option::mkfile::qmakespec
.isEmpty() && !cache
["QMAKESPEC"].isEmpty())
1406 Option::mkfile::qmakespec
= cache
["QMAKESPEC"].first();
1410 if(cmd
& ReadConf
) { // parse mkspec
1411 QString qmakespec
= fixEnvVariables(Option::mkfile::qmakespec
);
1412 QStringList mkspec_roots
= qmake_mkspec_paths();
1413 debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec
.toLatin1().constData(),
1414 mkspec_roots
.join("::").toLatin1().constData());
1415 if(qmakespec
.isEmpty()) {
1416 for(QStringList::ConstIterator it
= mkspec_roots
.begin(); it
!= mkspec_roots
.end(); ++it
) {
1417 QString mkspec
= (*it
) + QDir::separator() + "default";
1418 QFileInfo
default_info(mkspec
);
1419 if(default_info
.exists() && default_info
.isDir()) {
1424 if(qmakespec
.isEmpty()) {
1425 fprintf(stderr
, "QMAKESPEC has not been set, so configuration cannot be deduced.\n");
1428 Option::mkfile::qmakespec
= qmakespec
;
1431 if(QDir::isRelativePath(qmakespec
)) {
1432 if (QFile::exists(qmakespec
+"/qmake.conf")) {
1433 Option::mkfile::qmakespec
= QFileInfo(Option::mkfile::qmakespec
).absoluteFilePath();
1434 } else if (QFile::exists(Option::output_dir
+"/"+qmakespec
+"/qmake.conf")) {
1435 qmakespec
= Option::mkfile::qmakespec
= QFileInfo(Option::output_dir
+"/"+qmakespec
).absoluteFilePath();
1437 bool found_mkspec
= false;
1438 for(QStringList::ConstIterator it
= mkspec_roots
.begin(); it
!= mkspec_roots
.end(); ++it
) {
1439 QString mkspec
= (*it
) + QDir::separator() + qmakespec
;
1440 if(QFile::exists(mkspec
)) {
1441 found_mkspec
= true;
1442 Option::mkfile::qmakespec
= qmakespec
= mkspec
;
1447 fprintf(stderr
, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n",
1448 qmakespec
.toLatin1().constData(), mkspec_roots
.join("\n\t").toLatin1().constData());
1454 // parse qmake configuration
1455 while(qmakespec
.endsWith(QString(QChar(QDir::separator()))))
1456 qmakespec
.truncate(qmakespec
.length()-1);
1457 QString spec
= qmakespec
+ QDir::separator() + "qmake.conf";
1458 if(!QFile::exists(spec
) &&
1459 QFile::exists(qmakespec
+ QDir::separator() + "tmake.conf"))
1460 spec
= qmakespec
+ QDir::separator() + "tmake.conf";
1461 debug_msg(1, "QMAKESPEC conf: reading %s", spec
.toLatin1().constData());
1462 if(!read(spec
, base_vars
)) {
1463 fprintf(stderr
, "Failure to read QMAKESPEC conf file %s.\n", spec
.toLatin1().constData());
1467 init_symbian(base_vars
);
1469 if(Option::mkfile::do_cache
&& !Option::mkfile::cachefile
.isEmpty()) {
1470 debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile
.toLatin1().constData());
1471 read(Option::mkfile::cachefile
, base_vars
);
1475 if(cmd
& ReadFeatures
) {
1476 debug_msg(1, "Processing default_pre: %s", vars
["CONFIG"].join("::").toLatin1().constData());
1477 if(doProjectInclude("default_pre", IncludeFlagFeature
, base_vars
) == IncludeNoExist
)
1478 doProjectInclude("default", IncludeFlagFeature
, base_vars
);
1482 vars
= base_vars
; // start with the base
1485 if(pfile
!= "-" && vars
["TARGET"].isEmpty())
1486 vars
["TARGET"].append(QFileInfo(pfile
).baseName());
1488 //before commandline
1489 if(cmd
& ReadCmdLine
) {
1491 parser
.file
= "(internal)";
1492 parser
.from_file
= false;
1493 parser
.line_no
= 1; //really arg count now.. duh
1495 for(QStringList::ConstIterator it
= Option::before_user_vars
.begin();
1496 it
!= Option::before_user_vars
.end(); ++it
) {
1497 if(!parse((*it
), vars
)) {
1498 fprintf(stderr
, "Argument failed to parse: %s\n", (*it
).toLatin1().constData());
1505 //commandline configs
1506 if(cmd
& ReadConfigs
&& !Option::user_configs
.isEmpty()) {
1507 parser
.file
= "(configs)";
1508 parser
.from_file
= false;
1509 parser
.line_no
= 1; //really arg count now.. duh
1510 parse("CONFIG += " + Option::user_configs
.join(" "), vars
);
1513 if(cmd
& ReadProFile
) { // parse project file
1514 debug_msg(1, "Project file: reading %s", pfile
.toLatin1().constData());
1515 if(pfile
!= "-" && !QFile::exists(pfile
) && !pfile
.endsWith(Option::pro_ext
))
1516 pfile
+= Option::pro_ext
;
1517 if(!read(pfile
, vars
))
1521 if(cmd
& ReadPostFiles
) { // parse post files
1522 const QStringList l
= vars
["QMAKE_POST_INCLUDE_FILES"];
1523 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
1524 if(read((*it
), vars
)) {
1525 if(vars
["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf((*it
)) == -1)
1526 vars
["QMAKE_INTERNAL_INCLUDED_FILES"].append((*it
));
1531 if(cmd
& ReadCmdLine
) {
1532 parser
.file
= "(internal)";
1533 parser
.from_file
= false;
1534 parser
.line_no
= 1; //really arg count now.. duh
1536 for(QStringList::ConstIterator it
= Option::after_user_vars
.begin();
1537 it
!= Option::after_user_vars
.end(); ++it
) {
1538 if(!parse((*it
), vars
)) {
1539 fprintf(stderr
, "Argument failed to parse: %s\n", (*it
).toLatin1().constData());
1546 //after configs (set in BUILDS)
1547 if(cmd
& ReadConfigs
&& !Option::after_user_configs
.isEmpty()) {
1548 parser
.file
= "(configs)";
1549 parser
.from_file
= false;
1550 parser
.line_no
= 1; //really arg count now.. duh
1551 parse("CONFIG += " + Option::after_user_configs
.join(" "), vars
);
1554 if(pfile
!= "-" && vars
["TARGET"].isEmpty())
1555 vars
["TARGET"].append(QFileInfo(pfile
).baseName());
1557 if(cmd
& ReadConfigs
&& !Option::user_configs
.isEmpty()) {
1558 parser
.file
= "(configs)";
1559 parser
.from_file
= false;
1560 parser
.line_no
= 1; //really arg count now.. duh
1561 parse("CONFIG += " + Option::user_configs
.join(" "), base_vars
);
1564 if(cmd
& ReadFeatures
) {
1565 debug_msg(1, "Processing default_post: %s", vars
["CONFIG"].join("::").toLatin1().constData());
1566 doProjectInclude("default_post", IncludeFlagFeature
, vars
);
1568 QHash
<QString
, bool> processed
;
1569 const QStringList
&configs
= vars
["CONFIG"];
1570 debug_msg(1, "Processing CONFIG features: %s", configs
.join("::").toLatin1().constData());
1572 bool finished
= true;
1573 for(int i
= configs
.size()-1; i
>= 0; --i
) {
1574 const QString config
= configs
[i
].toLower();
1575 if(!processed
.contains(config
)) {
1576 processed
.insert(config
, true);
1577 if(doProjectInclude(config
, IncludeFlagFeature
, vars
) == IncludeSuccess
) {
1587 Option::postProcessProject(this); // let Option post-process
1592 QMakeProject::isActiveConfig(const QString
&x
, bool regex
, QMap
<QString
, QStringList
> *place
)
1597 //magic types for easy flipping
1600 else if(x
== "false")
1603 static QString spec
;
1605 spec
= QFileInfo(Option::mkfile::qmakespec
).fileName();
1607 // Symbian is an exception to how scopes are resolved. Since we do not
1608 // have a separate target mode for Symbian, but we expect the scope to resolve
1609 // on other platforms we base it entirely on the mkspec. This means that
1610 // using a mkspec starting with 'symbian*' will resolve both the 'symbian'
1611 // and the 'unix' (because of Open C) scopes to true.
1612 if(isForSymbian() && (x
== "symbian" || x
== "unix"))
1616 if((Option::target_mode
== Option::TARG_MACX_MODE
||
1617 Option::target_mode
== Option::TARG_UNIX_MODE
) && x
== "unix")
1618 return !isForSymbian();
1619 else if(Option::target_mode
== Option::TARG_MACX_MODE
&& x
== "macx")
1620 return !isForSymbian();
1621 else if(Option::target_mode
== Option::TARG_MAC9_MODE
&& x
== "mac9")
1622 return !isForSymbian();
1623 else if((Option::target_mode
== Option::TARG_MAC9_MODE
|| Option::target_mode
== Option::TARG_MACX_MODE
) &&
1625 return !isForSymbian();
1626 else if(Option::target_mode
== Option::TARG_WIN_MODE
&& x
== "win32")
1627 return !isForSymbian();
1628 QRegExp
re(x
, Qt::CaseSensitive
, QRegExp::Wildcard
);
1629 if((regex
&& re
.exactMatch(spec
)) || (!regex
&& spec
== x
))
1632 else if(spec
== "default") {
1633 static char *buffer
= NULL
;
1635 buffer
= (char *)malloc(1024);
1636 qmakeAddCacheClear(qmakeFreeCacheClear
, (void**)&buffer
);
1638 int l
= readlink(Option::mkfile::qmakespec
.toLatin1(), buffer
, 1024);
1642 if(r
.lastIndexOf('/') != -1)
1643 r
= r
.mid(r
.lastIndexOf('/') + 1);
1644 if((regex
&& re
.exactMatch(r
)) || (!regex
&& r
== x
))
1648 #elif defined(Q_OS_WIN)
1649 else if(spec
== "default") {
1650 // We can't resolve symlinks as they do on Unix, so configure.exe puts the source of the
1651 // qmake.conf at the end of the default/qmake.conf in the QMAKESPEC_ORG variable.
1652 const QStringList
&spec_org
= (place
? (*place
)["QMAKESPEC_ORIGINAL"]
1653 : vars
["QMAKESPEC_ORIGINAL"]);
1654 if (!spec_org
.isEmpty()) {
1655 spec
= spec_org
.at(0);
1656 int lastSlash
= spec
.lastIndexOf('/');
1658 spec
= spec
.mid(lastSlash
+ 1);
1659 if((regex
&& re
.exactMatch(spec
)) || (!regex
&& spec
== x
))
1666 const QStringList
&configs
= (place
? (*place
)["CONFIG"] : vars
["CONFIG"]);
1667 for(QStringList::ConstIterator it
= configs
.begin(); it
!= configs
.end(); ++it
) {
1668 if(((regex
&& re
.exactMatch((*it
))) || (!regex
&& (*it
) == x
)) && re
.exactMatch((*it
)))
1675 QMakeProject::doProjectTest(QString str
, QMap
<QString
, QStringList
> &place
)
1677 QString chk
= remove_quotes(str
);
1680 bool invert_test
= (chk
.left(1) == "!");
1685 int lparen
= chk
.indexOf('(');
1686 if(lparen
!= -1) { // if there is an lparen in the chk, it IS a function
1687 int rparen
= chk
.indexOf(')', lparen
);
1689 qmake_error_msg("Function missing right paren: " + chk
);
1691 QString func
= chk
.left(lparen
);
1692 test
= doProjectTest(func
, chk
.mid(lparen
+1, rparen
- lparen
- 1), place
);
1695 test
= isActiveConfig(chk
, true, &place
);
1703 QMakeProject::doProjectTest(QString func
, const QString
¶ms
,
1704 QMap
<QString
, QStringList
> &place
)
1706 return doProjectTest(func
, split_arg_list(params
), place
);
1709 QMakeProject::IncludeStatus
1710 QMakeProject::doProjectInclude(QString file
, uchar flags
, QMap
<QString
, QStringList
> &place
)
1712 enum { UnknownFormat
, ProFormat
, JSFormat
} format
= UnknownFormat
;
1713 if(flags
& IncludeFlagFeature
) {
1714 if(!file
.endsWith(Option::prf_ext
))
1715 file
+= Option::prf_ext
;
1716 if(file
.indexOf(Option::dir_sep
) == -1 || !QFile::exists(file
)) {
1717 static QStringList
*feature_roots
= 0;
1718 if(!feature_roots
) {
1719 init_symbian(base_vars
);
1720 feature_roots
= new QStringList(qmake_feature_paths(prop
));
1721 qmakeAddCacheClear(qmakeDeleteCacheClear_QStringList
, (void**)&feature_roots
);
1723 debug_msg(2, "Looking for feature '%s' in (%s)", file
.toLatin1().constData(),
1724 feature_roots
->join("::").toLatin1().constData());
1726 if(parser
.from_file
) {
1727 QFileInfo
currFile(parser
.file
), prfFile(file
);
1728 if(currFile
.fileName() == prfFile
.fileName()) {
1729 currFile
= QFileInfo(currFile
.canonicalFilePath());
1730 for(int root
= 0; root
< feature_roots
->size(); ++root
) {
1731 prfFile
= QFileInfo(feature_roots
->at(root
) +
1732 QDir::separator() + file
).canonicalFilePath();
1733 if(prfFile
== currFile
) {
1734 start_root
= root
+1;
1740 for(int root
= start_root
; root
< feature_roots
->size(); ++root
) {
1741 QString
prf(feature_roots
->at(root
) + QDir::separator() + file
);
1742 if(QFile::exists(prf
+ Option::js_ext
)) {
1744 file
= prf
+ Option::js_ext
;
1746 } else if(QFile::exists(prf
)) {
1752 if(format
== UnknownFormat
)
1753 return IncludeNoExist
;
1754 if(place
["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file
) != -1)
1755 return IncludeFeatureAlreadyLoaded
;
1756 place
["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file
);
1759 if(QDir::isRelativePath(file
)) {
1760 QStringList include_roots
;
1761 if(Option::output_dir
!= qmake_getpwd())
1762 include_roots
<< qmake_getpwd();
1763 include_roots
<< Option::output_dir
;
1764 for(int root
= 0; root
< include_roots
.size(); ++root
) {
1765 QString testName
= QDir::toNativeSeparators(include_roots
[root
]);
1766 if (!testName
.endsWith(QString(QDir::separator())))
1767 testName
+= QDir::separator();
1769 if(QFile::exists(testName
)) {
1775 if(format
== UnknownFormat
) {
1776 if(QFile::exists(file
)) {
1777 if(file
.endsWith(Option::js_ext
))
1782 return IncludeNoExist
;
1785 if(Option::mkfile::do_preprocess
) //nice to see this first..
1786 fprintf(stderr
, "#switching file %s(%s) - %s:%d\n", (flags
& IncludeFlagFeature
) ? "load" : "include",
1787 file
.toLatin1().constData(),
1788 parser
.file
.toLatin1().constData(), parser
.line_no
);
1789 debug_msg(1, "Project Parser: %s'ing file %s.", (flags
& IncludeFlagFeature
) ? "load" : "include",
1790 file
.toLatin1().constData());
1792 QString orig_file
= file
;
1793 int di
= file
.lastIndexOf(QDir::separator());
1794 QString oldpwd
= qmake_getpwd();
1796 if(!qmake_setpwd(file
.left(file
.lastIndexOf(QDir::separator())))) {
1797 fprintf(stderr
, "Cannot find directory: %s\n", file
.left(di
).toLatin1().constData());
1798 return IncludeFailure
;
1800 file
= file
.right(file
.length() - di
- 1);
1802 bool parsed
= false;
1803 parser_info pi
= parser
;
1804 if(format
== JSFormat
) {
1805 warn_msg(WarnParser
, "%s:%d: QtScript support disabled for %s.",
1806 pi
.file
.toLatin1().constData(), pi
.line_no
, orig_file
.toLatin1().constData());
1808 QStack
<ScopeBlock
> sc
= scope_blocks
;
1809 IteratorBlock
*it
= iterator
;
1810 FunctionBlock
*fu
= function
;
1811 if(flags
& (IncludeFlagNewProject
|IncludeFlagNewParser
)) {
1812 // The "project's variables" are used in other places (eg. export()) so it's not
1813 // possible to use "place" everywhere. Instead just set variables and grab them later
1814 QMakeProject
proj(this, &place
);
1815 if(flags
& IncludeFlagNewParser
) {
1817 if(proj
.doProjectInclude("default_pre", IncludeFlagFeature
, proj
.variables()) == IncludeNoExist
)
1818 proj
.doProjectInclude("default", IncludeFlagFeature
, proj
.variables());
1820 parsed
= proj
.read(file
, proj
.variables());
1822 parsed
= proj
.read(file
);
1824 place
= proj
.variables();
1826 parsed
= read(file
, place
);
1833 if(place
["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file
) == -1)
1834 place
["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file
);
1836 warn_msg(WarnParser
, "%s:%d: Failure to include file %s.",
1837 pi
.file
.toLatin1().constData(), pi
.line_no
, orig_file
.toLatin1().constData());
1840 qmake_setpwd(oldpwd
);
1842 return IncludeParseFailure
;
1843 return IncludeSuccess
;
1847 QMakeProject::doProjectExpand(QString func
, const QString
¶ms
,
1848 QMap
<QString
, QStringList
> &place
)
1850 return doProjectExpand(func
, split_arg_list(params
), place
);
1854 QMakeProject::doProjectExpand(QString func
, QStringList args
,
1855 QMap
<QString
, QStringList
> &place
)
1857 QList
<QStringList
> args_list
;
1858 for(int i
= 0; i
< args
.size(); ++i
) {
1859 QStringList arg
= split_value_list(args
[i
]), tmp
;
1860 for(int i
= 0; i
< arg
.size(); ++i
)
1861 tmp
+= doVariableReplaceExpand(arg
[i
], place
);;
1864 return doProjectExpand(func
, args_list
, place
);
1868 QMakeProject::doProjectExpand(QString func
, QList
<QStringList
> args_list
,
1869 QMap
<QString
, QStringList
> &place
)
1871 func
= func
.trimmed();
1872 if(replaceFunctions
.contains(func
)) {
1873 FunctionBlock
*defined
= replaceFunctions
[func
];
1874 function_blocks
.push(defined
);
1876 defined
->exec(args_list
, this, place
, ret
);
1877 Q_ASSERT(function_blocks
.pop() == defined
);
1881 QStringList args
; //why don't the builtin functions just use args_list? --Sam
1882 for(int i
= 0; i
< args_list
.size(); ++i
)
1883 args
+= args_list
[i
].join(QString(Option::field_sep
));
1885 ExpandFunc func_t
= qmake_expandFunctions().value(func
.toLower());
1886 debug_msg(1, "Running project expand: %s(%s) [%d]",
1887 func
.toLatin1().constData(), args
.join("::").toLatin1().constData(), func_t
);
1892 if(args
.count() < 1 || args
.count() > 3) {
1893 fprintf(stderr
, "%s:%d: member(var, start, end) requires three arguments.\n",
1894 parser
.file
.toLatin1().constData(), parser
.line_no
);
1897 const QStringList
&var
= values(args
.first(), place
);
1898 int start
= 0, end
= 0;
1899 if(args
.count() >= 2) {
1900 QString start_str
= args
[1];
1901 start
= start_str
.toInt(&ok
);
1903 if(args
.count() == 2) {
1904 int dotdot
= start_str
.indexOf("..");
1906 start
= start_str
.left(dotdot
).toInt(&ok
);
1908 end
= start_str
.mid(dotdot
+2).toInt(&ok
);
1912 fprintf(stderr
, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
1913 parser
.file
.toLatin1().constData(), parser
.line_no
,
1914 start_str
.toLatin1().constData());
1917 if(args
.count() == 3)
1918 end
= args
[2].toInt(&ok
);
1920 fprintf(stderr
, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
1921 parser
.file
.toLatin1().constData(), parser
.line_no
,
1922 args
[2].toLatin1().constData());
1927 start
+= var
.count();
1930 if(start
< 0 || start
>= var
.count() || end
< 0 || end
>= var
.count()) {
1932 } else if(start
< end
) {
1933 for(int i
= start
; i
<= end
&& (int)var
.count() >= i
; i
++)
1936 for(int i
= start
; i
>= end
&& (int)var
.count() >= i
&& i
>= 0; i
--)
1944 if(args
.count() != 1) {
1945 fprintf(stderr
, "%s:%d: %s(var) requires one argument.\n",
1946 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
1948 const QStringList
&var
= values(args
.first(), place
);
1949 if(!var
.isEmpty()) {
1950 if(func_t
== E_FIRST
)
1951 ret
= QStringList(var
[0]);
1953 ret
= QStringList(var
[var
.size()-1]);
1958 if(args
.count() < 1 || args
.count() > 2) {
1959 fprintf(stderr
, "%s:%d: cat(file) requires one argument.\n",
1960 parser
.file
.toLatin1().constData(), parser
.line_no
);
1962 QString file
= args
[0];
1963 file
= Option::fixPathToLocalOS(file
);
1965 bool singleLine
= true;
1966 if(args
.count() > 1)
1967 singleLine
= (args
[1].toLower() == "true");
1970 if(qfile
.open(QIODevice::ReadOnly
)) {
1971 QTextStream
stream(&qfile
);
1972 while(!stream
.atEnd()) {
1973 ret
+= split_value_list(stream
.readLine().trimmed());
1982 if(args
.count() != 2) {
1983 fprintf(stderr
, "%s:%d: fromfile(file, variable) requires two arguments.\n",
1984 parser
.file
.toLatin1().constData(), parser
.line_no
);
1986 QString file
= args
[0], seek_var
= args
[1];
1987 file
= Option::fixPathToLocalOS(file
);
1989 QMap
<QString
, QStringList
> tmp
;
1990 if(doProjectInclude(file
, IncludeFlagNewParser
, tmp
) == IncludeSuccess
) {
1991 if(tmp
.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
1992 QStringList
&out
= place
["QMAKE_INTERNAL_INCLUDED_FILES"];
1993 const QStringList
&in
= tmp
["QMAKE_INTERNAL_INCLUDED_FILES"];
1994 for(int i
= 0; i
< in
.size(); ++i
) {
1995 if(out
.indexOf(in
[i
]) == -1)
1999 ret
= tmp
[seek_var
];
2004 if(args
.count() < 1 || args
.count() > 2) {
2005 fprintf(stderr
, "%s:%d: eval(variable) requires one argument.\n",
2006 parser
.file
.toLatin1().constData(), parser
.line_no
);
2009 const QMap
<QString
, QStringList
> *source
= &place
;
2010 if(args
.count() == 2) {
2011 if(args
.at(1) == "Global") {
2013 } else if(args
.at(1) == "Local") {
2016 fprintf(stderr
, "%s:%d: unexpected source to eval.\n", parser
.file
.toLatin1().constData(),
2020 ret
+= source
->value(args
.at(0));
2026 tmp
.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x
++);
2027 ret
= QStringList(tmp
);
2028 QStringList
&lst
= (*((QMap
<QString
, QStringList
>*)&place
))[tmp
];
2030 for(QStringList::ConstIterator arg_it
= args
.begin();
2031 arg_it
!= args
.end(); ++arg_it
)
2032 lst
+= split_value_list((*arg_it
));
2035 if(args
.count() < 1) {
2036 fprintf(stderr
, "%s:%d: sprintf(format, ...) requires one argument.\n",
2037 parser
.file
.toLatin1().constData(), parser
.line_no
);
2039 QString tmp
= args
.at(0);
2040 for(int i
= 1; i
< args
.count(); ++i
)
2041 tmp
= tmp
.arg(args
.at(i
));
2042 ret
= split_value_list(tmp
);
2046 if(args
.count() < 1 || args
.count() > 4) {
2047 fprintf(stderr
, "%s:%d: join(var, glue, before, after) requires four"
2048 "arguments.\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
2050 QString glue
, before
, after
;
2051 if(args
.count() >= 2)
2053 if(args
.count() >= 3)
2055 if(args
.count() == 4)
2057 const QStringList
&var
= values(args
.first(), place
);
2059 ret
= split_value_list(before
+ var
.join(glue
) + after
);
2063 if(args
.count() < 1 || args
.count() > 2) {
2064 fprintf(stderr
, "%s:%d split(var, sep) requires one or two arguments\n",
2065 parser
.file
.toLatin1().constData(), parser
.line_no
);
2067 QString sep
= QString(Option::field_sep
);
2068 if(args
.count() >= 2)
2070 QStringList var
= values(args
.first(), place
);
2071 for(QStringList::ConstIterator vit
= var
.begin(); vit
!= var
.end(); ++vit
) {
2072 QStringList lst
= (*vit
).split(sep
);
2073 for(QStringList::ConstIterator spltit
= lst
.begin(); spltit
!= lst
.end(); ++spltit
)
2081 bool regexp
= false;
2084 if(func_t
== E_SECTION
) {
2085 if(args
.count() != 3 && args
.count() != 4) {
2086 fprintf(stderr
, "%s:%d section(var, sep, begin, end) requires three argument\n",
2087 parser
.file
.toLatin1().constData(), parser
.line_no
);
2091 beg
= args
[2].toInt();
2092 if(args
.count() == 4)
2093 end
= args
[3].toInt();
2096 if(args
.count() != 1) {
2097 fprintf(stderr
, "%s:%d %s(var) requires one argument.\n",
2098 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
2102 sep
= "[" + QRegExp::escape(Option::dir_sep
) + "/]";
2103 if(func_t
== E_DIRNAME
)
2110 const QStringList
&l
= values(var
, place
);
2111 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
2112 QString separator
= sep
;
2114 ret
+= (*it
).section(QRegExp(separator
), beg
, end
);
2116 ret
+= (*it
).section(separator
, beg
, end
);
2121 if(args
.count() != 2) {
2122 fprintf(stderr
, "%s:%d find(var, str) requires two arguments\n",
2123 parser
.file
.toLatin1().constData(), parser
.line_no
);
2125 QRegExp
regx(args
[1]);
2126 const QStringList
&var
= values(args
.first(), place
);
2127 for(QStringList::ConstIterator vit
= var
.begin();
2128 vit
!= var
.end(); ++vit
) {
2129 if(regx
.indexIn(*vit
) != -1)
2135 if(args
.count() < 1 || args
.count() > 2) {
2136 fprintf(stderr
, "%s:%d system(execut) requires one argument.\n",
2137 parser
.file
.toLatin1().constData(), parser
.line_no
);
2139 QMakeProjectEnv
env(place
);
2141 bool singleLine
= true;
2142 if(args
.count() > 1)
2143 singleLine
= (args
[1].toLower() == "true");
2145 FILE *proc
= QT_POPEN(args
[0].toLatin1(), "r");
2146 while(proc
&& !feof(proc
)) {
2147 int read_in
= int(fread(buff
, 1, 255, proc
));
2150 for(int i
= 0; i
< read_in
; i
++) {
2151 if((singleLine
&& buff
[i
] == '\n') || buff
[i
] == '\t')
2154 buff
[read_in
] = '\0';
2157 ret
+= split_value_list(output
);
2163 if(args
.count() != 1) {
2164 fprintf(stderr
, "%s:%d unique(var) requires one argument.\n",
2165 parser
.file
.toLatin1().constData(), parser
.line_no
);
2167 const QStringList
&var
= values(args
.first(), place
);
2168 for(int i
= 0; i
< var
.count(); i
++) {
2169 if(!ret
.contains(var
[i
]))
2177 case E_ESCAPE_EXPAND
: {
2178 for(int i
= 0; i
< args
.size(); ++i
) {
2179 QChar
*i_data
= args
[i
].data();
2180 int i_len
= args
[i
].length();
2181 for(int x
= 0; x
< i_len
; ++x
) {
2182 if(*(i_data
+x
) == '\\' && x
< i_len
-1) {
2183 if(*(i_data
+x
+1) == '\\') {
2188 } mapped_quotes
[] = {
2194 for(int i
= 0; mapped_quotes
[i
].in
; ++i
) {
2195 if(*(i_data
+x
+1) == mapped_quotes
[i
].in
) {
2196 *(i_data
+x
) = mapped_quotes
[i
].out
;
2198 memmove(i_data
+x
+1, i_data
+x
+2, (i_len
-x
-2)*sizeof(QChar
));
2206 ret
.append(QString(i_data
, i_len
));
2210 for(int i
= 0; i
< args
.size(); ++i
)
2211 ret
+= QRegExp::escape(args
[i
]);
2215 for(int i
= 0; i
< args
.size(); ++i
) {
2216 if(func_t
== E_UPPER
)
2217 ret
+= args
[i
].toUpper();
2219 ret
+= args
[i
].toLower();
2223 if(args
.count() != 1 && args
.count() != 2) {
2224 fprintf(stderr
, "%s:%d files(pattern) requires one argument.\n",
2225 parser
.file
.toLatin1().constData(), parser
.line_no
);
2227 bool recursive
= false;
2228 if(args
.count() == 2)
2229 recursive
= (args
[1].toLower() == "true" || args
[1].toInt());
2231 QString r
= Option::fixPathToLocalOS(args
[0]);
2232 int slash
= r
.lastIndexOf(QDir::separator());
2234 dirs
.append(r
.left(slash
));
2240 const QRegExp
regex(r
, Qt::CaseSensitive
, QRegExp::Wildcard
);
2241 for(int d
= 0; d
< dirs
.count(); d
++) {
2242 QString dir
= dirs
[d
];
2243 if(!dir
.isEmpty() && !dir
.endsWith(Option::dir_sep
))
2247 for(int i
= 0; i
< (int)qdir
.count(); ++i
) {
2248 if(qdir
[i
] == "." || qdir
[i
] == "..")
2250 QString fname
= dir
+ qdir
[i
];
2251 if(QFileInfo(fname
).isDir()) {
2255 if(regex
.exactMatch(qdir
[i
]))
2262 if(args
.count() != 1) {
2263 fprintf(stderr
, "%s:%d prompt(question) requires one argument.\n",
2264 parser
.file
.toLatin1().constData(), parser
.line_no
);
2265 } else if(projectFile() == "-") {
2266 fprintf(stderr
, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
2267 parser
.file
.toLatin1().constData(), parser
.line_no
);
2269 QString msg
= fixEnvVariables(args
.first());
2270 if(!msg
.endsWith("?"))
2272 fprintf(stderr
, "Project %s: %s ", func
.toUpper().toLatin1().constData(),
2273 msg
.toLatin1().constData());
2276 if(qfile
.open(stdin
, QIODevice::ReadOnly
)) {
2277 QTextStream
t(&qfile
);
2278 ret
= split_value_list(t
.readLine());
2283 if(args
.count() != 3 ) {
2284 fprintf(stderr
, "%s:%d replace(var, before, after) requires three arguments\n",
2285 parser
.file
.toLatin1().constData(), parser
.line_no
);
2287 const QRegExp
before( args
[1] );
2288 const QString
after( args
[2] );
2289 QStringList var
= values(args
.first(), place
);
2290 for(QStringList::Iterator it
= var
.begin(); it
!= var
.end(); ++it
)
2291 ret
+= it
->replace(before
, after
);
2295 if(args
.count() != 1) {
2296 fprintf(stderr
, "%s:%d: size(var) requires one argument.\n",
2297 parser
.file
.toLatin1().constData(), parser
.line_no
);
2299 //QString target = args[0];
2300 int size
= values(args
[0]).size();
2301 ret
+= QString::number(size
);
2305 fprintf(stderr
, "%s:%d: Unknown replace function: %s\n",
2306 parser
.file
.toLatin1().constData(), parser
.line_no
,
2307 func
.toLatin1().constData());
2314 QMakeProject::doProjectTest(QString func
, QStringList args
, QMap
<QString
, QStringList
> &place
)
2316 QList
<QStringList
> args_list
;
2317 for(int i
= 0; i
< args
.size(); ++i
) {
2318 QStringList arg
= split_value_list(args
[i
]), tmp
;
2319 for(int i
= 0; i
< arg
.size(); ++i
)
2320 tmp
+= doVariableReplaceExpand(arg
[i
], place
);
2323 return doProjectTest(func
, args_list
, place
);
2327 QMakeProject::doProjectTest(QString func
, QList
<QStringList
> args_list
, QMap
<QString
, QStringList
> &place
)
2329 func
= func
.trimmed();
2331 if(testFunctions
.contains(func
)) {
2332 FunctionBlock
*defined
= testFunctions
[func
];
2334 function_blocks
.push(defined
);
2335 defined
->exec(args_list
, this, place
, ret
);
2336 Q_ASSERT(function_blocks
.pop() == defined
);
2341 if(ret
.first() == "true") {
2343 } else if(ret
.first() == "false") {
2347 int val
= ret
.first().toInt(&ok
);
2350 fprintf(stderr
, "%s:%d Unexpected return value from test %s [%s].\n",
2351 parser
.file
.toLatin1().constData(),
2352 parser
.line_no
, func
.toLatin1().constData(),
2353 ret
.join("::").toLatin1().constData());
2360 QStringList args
; //why don't the builtin functions just use args_list? --Sam
2361 for(int i
= 0; i
< args_list
.size(); ++i
)
2362 args
+= args_list
[i
].join(QString(Option::field_sep
));
2364 TestFunc func_t
= qmake_testFunctions().value(func
);
2365 debug_msg(1, "Running project test: %s(%s) [%d]",
2366 func
.toLatin1().constData(), args
.join("::").toLatin1().constData(), func_t
);
2370 return doProjectCheckReqs(args
, place
);
2372 case T_GREATERTHAN
: {
2373 if(args
.count() != 2) {
2374 fprintf(stderr
, "%s:%d: %s(variable, value) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2375 parser
.line_no
, func
.toLatin1().constData());
2378 QString
rhs(args
[1]), lhs(values(args
[0], place
).join(QString(Option::field_sep
)));
2380 int rhs_int
= rhs
.toInt(&ok
);
2381 if(ok
) { // do integer compare
2382 int lhs_int
= lhs
.toInt(&ok
);
2384 if(func_t
== T_GREATERTHAN
)
2385 return lhs_int
> rhs_int
;
2386 return lhs_int
< rhs_int
;
2389 if(func_t
== T_GREATERTHAN
)
2393 if(args
.count() != 1) {
2394 fprintf(stderr
, "%s:%d: if(condition) requires one argument.\n", parser
.file
.toLatin1().constData(),
2398 const QString cond
= args
.first();
2399 const QChar
*d
= cond
.unicode();
2401 bool ret
= true, or_op
= false;
2403 for(int d_off
= 0, parens
= 0, d_len
= cond
.size(); d_off
< d_len
; ++d_off
) {
2404 if(!quote
.isNull()) {
2405 if(*(d
+d_off
) == quote
)
2407 } else if(*(d
+d_off
) == '(') {
2409 } else if(*(d
+d_off
) == ')') {
2411 } else if(*(d
+d_off
) == '"' /*|| *(d+d_off) == '\''*/) {
2414 if(!parens
&& quote
.isNull() && (*(d
+d_off
) == QLatin1Char(':') || *(d
+d_off
) == QLatin1Char('|') || d_off
== d_len
-1)) {
2415 if(d_off
== d_len
-1)
2417 if(!test
.isEmpty()) {
2418 const bool success
= doProjectTest(test
, place
);
2421 ret
= ret
|| success
;
2423 ret
= ret
&& success
;
2425 if(*(d
+d_off
) == QLatin1Char(':')) {
2427 } else if(*(d
+d_off
) == QLatin1Char('|')) {
2436 if(args
.count() != 2) {
2437 fprintf(stderr
, "%s:%d: %s(variable, value) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2438 parser
.line_no
, func
.toLatin1().constData());
2441 return values(args
[0], place
).join(QString(Option::field_sep
)) == args
[1];
2443 if(args
.count() != 1) {
2444 fprintf(stderr
, "%s:%d: exists(file) requires one argument.\n", parser
.file
.toLatin1().constData(),
2448 QString file
= args
.first();
2449 file
= Option::fixPathToLocalOS(file
);
2451 if(QFile::exists(file
))
2453 //regular expression I guess
2454 QString dirstr
= qmake_getpwd();
2455 int slsh
= file
.lastIndexOf(Option::dir_sep
);
2457 dirstr
= file
.left(slsh
+1);
2458 file
= file
.right(file
.length() - slsh
- 1);
2460 return QDir(dirstr
).entryList(QStringList(file
)).count(); }
2462 if(args
.count() != 1) {
2463 fprintf(stderr
, "%s:%d: export(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2467 for(int i
= 0; i
< function_blocks
.size(); ++i
) {
2468 FunctionBlock
*f
= function_blocks
.at(i
);
2469 f
->vars
[args
[0]] = values(args
[0], place
);
2470 if(!i
&& f
->calling_place
)
2471 (*f
->calling_place
)[args
[0]] = values(args
[0], place
);
2475 if(args
.count() != 1) {
2476 fprintf(stderr
, "%s:%d: clear(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2480 if(!place
.contains(args
[0]))
2482 place
[args
[0]].clear();
2485 if(args
.count() != 1) {
2486 fprintf(stderr
, "%s:%d: unset(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2490 if(!place
.contains(args
[0]))
2492 place
.remove(args
[0]);
2495 if(args
.count() < 1 && 0) {
2496 fprintf(stderr
, "%s:%d: eval(project) requires one argument.\n", parser
.file
.toLatin1().constData(),
2500 QString project
= args
.join(" ");
2501 parser_info pi
= parser
;
2502 parser
.from_file
= false;
2503 parser
.file
= "(eval)";
2505 QTextStream
t(&project
, QIODevice::ReadOnly
);
2506 bool ret
= read(t
, place
);
2510 if(args
.count() < 1 || args
.count() > 2) {
2511 fprintf(stderr
, "%s:%d: CONFIG(config) requires one argument.\n", parser
.file
.toLatin1().constData(),
2515 if(args
.count() == 1)
2516 return isActiveConfig(args
[0]);
2517 const QStringList mutuals
= args
[1].split('|');
2518 const QStringList
&configs
= values("CONFIG", place
);
2519 for(int i
= configs
.size()-1; i
>= 0; i
--) {
2520 for(int mut
= 0; mut
< mutuals
.count(); mut
++) {
2521 if(configs
[i
] == mutuals
[mut
].trimmed())
2522 return (configs
[i
] == args
[0]);
2527 bool setup_env
= true;
2528 if(args
.count() < 1 || args
.count() > 2) {
2529 fprintf(stderr
, "%s:%d: system(exec) requires one argument.\n", parser
.file
.toLatin1().constData(),
2533 if(args
.count() == 2) {
2534 const QString sarg
= args
[1];
2535 setup_env
= (sarg
.toLower() == "true" || sarg
.toInt());
2537 QMakeProjectEnv env
;
2540 bool ret
= system(args
[0].toLatin1().constData()) == 0;
2543 if(function_blocks
.isEmpty()) {
2544 fprintf(stderr
, "%s:%d unexpected return()\n",
2545 parser
.file
.toLatin1().constData(), parser
.line_no
);
2547 FunctionBlock
*f
= function_blocks
.top();
2548 f
->cause_return
= true;
2549 if(args_list
.count() >= 1)
2550 f
->return_value
+= args_list
[0];
2555 iterator
->cause_break
= true;
2556 else if(!scope_blocks
.isEmpty())
2557 scope_blocks
.top().ignore
= true;
2559 fprintf(stderr
, "%s:%d unexpected break()\n",
2560 parser
.file
.toLatin1().constData(), parser
.line_no
);
2564 iterator
->cause_next
= true;
2566 fprintf(stderr
, "%s:%d unexpected next()\n",
2567 parser
.file
.toLatin1().constData(), parser
.line_no
);
2570 if(args
.count() < 1 || args
.count() > 2) {
2571 fprintf(stderr
, "%s:%d: defined(function) requires one argument.\n",
2572 parser
.file
.toLatin1().constData(), parser
.line_no
);
2574 if(args
.count() > 1) {
2575 if(args
[1] == "test")
2576 return testFunctions
.contains(args
[0]);
2577 else if(args
[1] == "replace")
2578 return replaceFunctions
.contains(args
[0]);
2579 fprintf(stderr
, "%s:%d: defined(function, type): unexpected type [%s].\n",
2580 parser
.file
.toLatin1().constData(), parser
.line_no
,
2581 args
[1].toLatin1().constData());
2583 if(replaceFunctions
.contains(args
[0]) || testFunctions
.contains(args
[0]))
2589 if(args
.count() < 2 || args
.count() > 3) {
2590 fprintf(stderr
, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n",
2591 parser
.file
.toLatin1().constData(), parser
.line_no
);
2594 QRegExp
regx(args
[1]);
2595 const QStringList
&l
= values(args
[0], place
);
2596 if(args
.count() == 2) {
2597 for(int i
= 0; i
< l
.size(); ++i
) {
2598 const QString val
= l
[i
];
2599 if(regx
.exactMatch(val
) || val
== args
[1])
2603 const QStringList mutuals
= args
[2].split('|');
2604 for(int i
= l
.size()-1; i
>= 0; i
--) {
2605 const QString val
= l
[i
];
2606 for(int mut
= 0; mut
< mutuals
.count(); mut
++) {
2607 if(val
== mutuals
[mut
].trimmed())
2608 return (regx
.exactMatch(val
) || val
== args
[1]);
2614 if(args
.count() < 2 || args
.count() > 3) {
2615 fprintf(stderr
, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
2616 parser
.file
.toLatin1().constData(), parser
.line_no
);
2621 QMap
<QString
, QStringList
> tmp
;
2622 if(doProjectInclude(Option::fixPathToLocalOS(args
[0]), IncludeFlagNewParser
, tmp
) == IncludeSuccess
) {
2623 if(tmp
.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2624 QStringList
&out
= place
["QMAKE_INTERNAL_INCLUDED_FILES"];
2625 const QStringList
&in
= tmp
["QMAKE_INTERNAL_INCLUDED_FILES"];
2626 for(int i
= 0; i
< in
.size(); ++i
) {
2627 if(out
.indexOf(in
[i
]) == -1)
2631 if(args
.count() == 2) {
2632 ret
= tmp
.contains(args
[1]);
2634 QRegExp
regx(args
[2]);
2635 const QStringList
&l
= tmp
[args
[1]];
2636 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
2637 if(regx
.exactMatch((*it
)) || (*it
) == args
[2]) {
2646 if(args
.count() != 2 && args
.count() != 3) {
2647 fprintf(stderr
, "%s:%d: count(var, count) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2651 if(args
.count() == 3) {
2652 QString comp
= args
[2];
2653 if(comp
== ">" || comp
== "greaterThan")
2654 return values(args
[0], place
).count() > args
[1].toInt();
2656 return values(args
[0], place
).count() >= args
[1].toInt();
2657 if(comp
== "<" || comp
== "lessThan")
2658 return values(args
[0], place
).count() < args
[1].toInt();
2660 return values(args
[0], place
).count() <= args
[1].toInt();
2661 if(comp
== "equals" || comp
== "isEqual" || comp
== "=" || comp
== "==")
2662 return values(args
[0], place
).count() == args
[1].toInt();
2663 fprintf(stderr
, "%s:%d: unexpected modifier to count(%s)\n", parser
.file
.toLatin1().constData(),
2664 parser
.line_no
, comp
.toLatin1().constData());
2667 return values(args
[0], place
).count() == args
[1].toInt();
2669 if(args
.count() != 1) {
2670 fprintf(stderr
, "%s:%d: isEmpty(var) requires one argument.\n", parser
.file
.toLatin1().constData(),
2674 return values(args
[0], place
).isEmpty();
2678 const bool include_statement
= (func_t
== T_INCLUDE
);
2679 bool ignore_error
= false;
2680 if(args
.count() >= 2) {
2681 if(func_t
== T_INCLUDE
) {
2682 parseInto
= args
[1];
2683 if (args
.count() == 3){
2684 QString sarg
= args
[2];
2685 if (sarg
.toLower() == "true" || sarg
.toInt())
2686 ignore_error
= true;
2689 QString sarg
= args
[1];
2690 ignore_error
= (sarg
.toLower() == "true" || sarg
.toInt());
2692 } else if(args
.count() != 1) {
2693 QString func_desc
= "load(feature)";
2694 if(include_statement
)
2695 func_desc
= "include(file)";
2696 fprintf(stderr
, "%s:%d: %s requires one argument.\n", parser
.file
.toLatin1().constData(),
2697 parser
.line_no
, func_desc
.toLatin1().constData());
2700 QString file
= args
.first();
2701 file
= Option::fixPathToLocalOS(file
);
2702 uchar flags
= IncludeFlagNone
;
2703 if(!include_statement
)
2704 flags
|= IncludeFlagFeature
;
2705 IncludeStatus stat
= IncludeFailure
;
2706 if(!parseInto
.isEmpty()) {
2707 QMap
<QString
, QStringList
> symbols
;
2708 stat
= doProjectInclude(file
, flags
|IncludeFlagNewProject
, symbols
);
2709 if(stat
== IncludeSuccess
) {
2710 QMap
<QString
, QStringList
> out_place
;
2711 for(QMap
<QString
, QStringList
>::ConstIterator it
= place
.begin(); it
!= place
.end(); ++it
) {
2712 const QString var
= it
.key();
2713 if(var
!= parseInto
&& !var
.startsWith(parseInto
+ "."))
2714 out_place
.insert(var
, it
.value());
2716 for(QMap
<QString
, QStringList
>::ConstIterator it
= symbols
.begin(); it
!= symbols
.end(); ++it
) {
2717 const QString var
= it
.key();
2718 if(!var
.startsWith("."))
2719 out_place
.insert(parseInto
+ "." + it
.key(), it
.value());
2724 stat
= doProjectInclude(file
, flags
, place
);
2726 if(stat
== IncludeFeatureAlreadyLoaded
) {
2727 warn_msg(WarnParser
, "%s:%d: Duplicate of loaded feature %s",
2728 parser
.file
.toLatin1().constData(), parser
.line_no
, file
.toLatin1().constData());
2729 } else if(stat
== IncludeNoExist
&& !ignore_error
) {
2730 warn_msg(WarnAll
, "%s:%d: Unable to find file for inclusion %s",
2731 parser
.file
.toLatin1().constData(), parser
.line_no
, file
.toLatin1().constData());
2733 } else if(stat
>= IncludeFailure
) {
2735 printf("Project LOAD(): Feature %s cannot be found.\n", file
.toLatin1().constData());
2737 #if defined(QT_BUILD_QMAKE_LIBRARY)
2747 if(args
.count() != 2) {
2748 fprintf(stderr
, "%s:%d: debug(level, message) requires one argument.\n", parser
.file
.toLatin1().constData(),
2752 QString msg
= fixEnvVariables(args
[1]);
2753 debug_msg(args
[0].toInt(), "Project DEBUG: %s", msg
.toLatin1().constData());
2758 if(args
.count() != 1) {
2759 fprintf(stderr
, "%s:%d: %s(message) requires one argument.\n", parser
.file
.toLatin1().constData(),
2760 parser
.line_no
, func
.toLatin1().constData());
2763 QString msg
= fixEnvVariables(args
.first());
2764 fprintf(stderr
, "Project %s: %s\n", func
.toUpper().toLatin1().constData(), msg
.toLatin1().constData());
2766 #if defined(QT_BUILD_QMAKE_LIBRARY)
2773 fprintf(stderr
, "%s:%d: Unknown test function: %s\n", parser
.file
.toLatin1().constData(), parser
.line_no
,
2774 func
.toLatin1().constData());
2780 QMakeProject::doProjectCheckReqs(const QStringList
&deps
, QMap
<QString
, QStringList
> &place
)
2783 for(QStringList::ConstIterator it
= deps
.begin(); it
!= deps
.end(); ++it
) {
2784 bool test
= doProjectTest((*it
), place
);
2786 debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
2787 parser
.file
.toLatin1().constData(), parser
.line_no
,
2788 (*it
).toLatin1().constData());
2789 place
["QMAKE_FAILED_REQUIREMENTS"].append((*it
));
2797 QMakeProject::test(const QString
&v
)
2799 QMap
<QString
, QStringList
> tmp
= vars
;
2800 return doProjectTest(v
, tmp
);
2804 QMakeProject::test(const QString
&func
, const QList
<QStringList
> &args
)
2806 QMap
<QString
, QStringList
> tmp
= vars
;
2807 return doProjectTest(func
, args
, tmp
);
2811 QMakeProject::expand(const QString
&str
)
2814 QMap
<QString
, QStringList
> tmp
= vars
;
2815 const QStringList ret
= doVariableReplaceExpand(str
, tmp
, &ok
);
2818 return QStringList();
2822 QMakeProject::expand(const QString
&func
, const QList
<QStringList
> &args
)
2824 QMap
<QString
, QStringList
> tmp
= vars
;
2825 return doProjectExpand(func
, args
, tmp
);
2829 QMakeProject::doVariableReplace(QString
&str
, QMap
<QString
, QStringList
> &place
)
2832 str
= doVariableReplaceExpand(str
, place
, &ret
).join(QString(Option::field_sep
));
2837 QMakeProject::doVariableReplaceExpand(const QString
&str
, QMap
<QString
, QStringList
> &place
, bool *ok
)
2845 const ushort LSQUARE
= '[';
2846 const ushort RSQUARE
= ']';
2847 const ushort LCURLY
= '{';
2848 const ushort RCURLY
= '}';
2849 const ushort LPAREN
= '(';
2850 const ushort RPAREN
= ')';
2851 const ushort DOLLAR
= '$';
2852 const ushort SLASH
= '\\';
2853 const ushort UNDERSCORE
= '_';
2854 const ushort DOT
= '.';
2855 const ushort SPACE
= ' ';
2856 const ushort TAB
= '\t';
2857 const ushort SINGLEQUOTE
= '\'';
2858 const ushort DOUBLEQUOTE
= '"';
2860 ushort unicode
, quote
= 0;
2861 const QChar
*str_data
= str
.data();
2862 const int str_len
= str
.length();
2869 for(int i
= 0; i
< str_len
; ++i
) {
2870 unicode
= str_data
[i
].unicode();
2871 const int start_var
= i
;
2872 if(unicode
== DOLLAR
&& str_len
> i
+2) {
2873 unicode
= str_data
[++i
].unicode();
2874 if(unicode
== DOLLAR
) {
2878 enum { VAR
, ENVIRON
, FUNCTION
, PROPERTY
} var_type
= VAR
;
2879 unicode
= str_data
[++i
].unicode();
2880 if(unicode
== LSQUARE
) {
2881 unicode
= str_data
[++i
].unicode();
2883 var_type
= PROPERTY
;
2884 } else if(unicode
== LCURLY
) {
2885 unicode
= str_data
[++i
].unicode();
2888 } else if(unicode
== LPAREN
) {
2889 unicode
= str_data
[++i
].unicode();
2894 if(!(unicode
& (0xFF<<8)) &&
2895 unicode
!= DOT
&& unicode
!= UNDERSCORE
&&
2896 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
2897 (unicode
< 'a' || unicode
> 'z') && (unicode
< 'A' || unicode
> 'Z') &&
2898 (unicode
< '0' || unicode
> '9'))
2900 var
.append(QChar(unicode
));
2903 unicode
= str_data
[i
].unicode();
2904 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
2906 if(var_type
== VAR
&& unicode
== LPAREN
) {
2907 var_type
= FUNCTION
;
2912 unicode
= str_data
[i
].unicode();
2913 if(unicode
== LPAREN
) {
2915 } else if(unicode
== RPAREN
) {
2920 args
.append(QChar(unicode
));
2923 unicode
= str_data
[i
].unicode();
2926 // at this point i is pointing to the 'next' character (which is in unicode)
2927 // this might actually be a term character since you can do $${func()}
2930 if(unicode
!= term
) {
2931 qmake_error_msg("Missing " + QString(term
) + " terminator [found " + (unicode
?QString(unicode
):QString("end-of-line")) + "]");
2934 return QStringList();
2937 // move the 'cursor' back to the last char of the thing we were looking at
2940 // since i never points to the 'next' character, there is no reason for this to be set
2943 QStringList replacement
;
2944 if(var_type
== ENVIRON
) {
2945 replacement
= split_value_list(QString::fromLocal8Bit(qgetenv(var
.toLatin1().constData())));
2946 } else if(var_type
== PROPERTY
) {
2948 replacement
= split_value_list(prop
->value(var
));
2949 } else if(var_type
== FUNCTION
) {
2950 replacement
= doProjectExpand(var
, args
, place
);
2951 } else if(var_type
== VAR
) {
2952 replacement
= values(var
, place
);
2954 if(!(replaced
++) && start_var
)
2955 current
= str
.left(start_var
);
2956 if(!replacement
.isEmpty()) {
2958 current
+= replacement
.join(QString(Option::field_sep
));
2960 current
+= replacement
.takeFirst();
2961 if(!replacement
.isEmpty()) {
2962 if(!current
.isEmpty())
2963 ret
.append(current
);
2964 current
= replacement
.takeLast();
2965 if(!replacement
.isEmpty())
2970 debug_msg(2, "Project Parser [var replace]: %s -> %s",
2971 str
.toLatin1().constData(), var
.toLatin1().constData(),
2972 replacement
.join("::").toLatin1().constData());
2975 current
.append("$");
2978 if(quote
&& unicode
== quote
) {
2981 } else if(unicode
== SLASH
) {
2982 bool escape
= false;
2983 const char *symbols
= "[]{}()$\\'\"";
2984 for(const char *s
= symbols
; *s
; ++s
) {
2985 if(str_data
[i
+1].unicode() == (ushort
)*s
) {
2989 current
= str
.left(start_var
);
2990 current
.append(str
.at(i
));
2994 if(escape
|| !replaced
)
2996 } else if(!quote
&& (unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
)) {
2999 if(!(replaced
++) && i
)
3000 current
= str
.left(i
);
3001 } else if(!quote
&& (unicode
== SPACE
|| unicode
== TAB
)) {
3003 if(!current
.isEmpty()) {
3004 ret
.append(current
);
3008 if(replaced
&& unicode
)
3009 current
.append(QChar(unicode
));
3012 ret
= QStringList(str
);
3013 else if(!current
.isEmpty())
3014 ret
.append(current
);
3015 //qDebug() << "REPLACE" << str << ret;
3019 QStringList
&QMakeProject::values(const QString
&_var
, QMap
<QString
, QStringList
> &place
)
3021 QString var
= varMap(_var
);
3022 if(var
== QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token)
3023 var
= ".BUILTIN." + var
;
3024 place
[var
] = QStringList(QLatin1String("\t"));
3025 } else if(var
== QLatin1String("LITERAL_DOLLAR")) { //a real $
3026 var
= ".BUILTIN." + var
;
3027 place
[var
] = QStringList(QLatin1String("$"));
3028 } else if(var
== QLatin1String("LITERAL_HASH")) { //a real #
3029 var
= ".BUILTIN." + var
;
3030 place
[var
] = QStringList("#");
3031 } else if(var
== QLatin1String("OUT_PWD")) { //the out going dir
3032 var
= ".BUILTIN." + var
;
3033 place
[var
] = QStringList(Option::output_dir
);
3034 } else if(var
== QLatin1String("PWD") || //current working dir (of _FILE_)
3035 var
== QLatin1String("IN_PWD")) {
3036 var
= ".BUILTIN." + var
;
3037 place
[var
] = QStringList(qmake_getpwd());
3038 } else if(var
== QLatin1String("DIR_SEPARATOR")) {
3039 var
= ".BUILTIN." + var
;
3040 place
[var
] = QStringList(Option::dir_sep
);
3041 } else if(var
== QLatin1String("DIRLIST_SEPARATOR")) {
3042 var
= ".BUILTIN." + var
;
3043 place
[var
] = QStringList(Option::dirlist_sep
);
3044 } else if(var
== QLatin1String("_LINE_")) { //parser line number
3045 var
= ".BUILTIN." + var
;
3046 place
[var
] = QStringList(QString::number(parser
.line_no
));
3047 } else if(var
== QLatin1String("_FILE_")) { //parser file
3048 var
= ".BUILTIN." + var
;
3049 place
[var
] = QStringList(parser
.file
);
3050 } else if(var
== QLatin1String("_DATE_")) { //current date/time
3051 var
= ".BUILTIN." + var
;
3052 place
[var
] = QStringList(QDateTime::currentDateTime().toString());
3053 } else if(var
== QLatin1String("_PRO_FILE_")) {
3054 var
= ".BUILTIN." + var
;
3055 place
[var
] = QStringList(pfile
);
3056 } else if(var
== QLatin1String("_PRO_FILE_PWD_")) {
3057 var
= ".BUILTIN." + var
;
3058 place
[var
] = QStringList(QFileInfo(pfile
).absolutePath());
3059 } else if(var
== QLatin1String("_QMAKE_CACHE_")) {
3060 var
= ".BUILTIN." + var
;
3061 if(Option::mkfile::do_cache
)
3062 place
[var
] = QStringList(Option::mkfile::cachefile
);
3063 } else if(var
== QLatin1String("TEMPLATE")) {
3064 if(!Option::user_template
.isEmpty()) {
3065 var
= ".BUILTIN.USER." + var
;
3066 place
[var
] = QStringList(Option::user_template
);
3067 } else if(!place
[var
].isEmpty()) {
3068 QString orig_template
= place
["TEMPLATE"].first(), real_template
;
3069 if(!Option::user_template_prefix
.isEmpty() && !orig_template
.startsWith(Option::user_template_prefix
))
3070 real_template
= Option::user_template_prefix
+ orig_template
;
3071 if(real_template
.endsWith(".t"))
3072 real_template
= real_template
.left(real_template
.length()-2);
3073 if(!real_template
.isEmpty()) {
3074 var
= ".BUILTIN." + var
;
3075 place
[var
] = QStringList(real_template
);
3078 var
= ".BUILTIN." + var
;
3079 place
[var
] = QStringList("app");
3081 } else if(var
.startsWith(QLatin1String("QMAKE_HOST."))) {
3082 QString ret
, type
= var
.mid(11);
3083 #if defined(Q_OS_WIN32)
3086 } else if(type
== "name") {
3087 DWORD name_length
= 1024;
3089 if (GetComputerName(name
, &name_length
))
3090 ret
= QString::fromWCharArray(name
);
3091 } else if(type
== "version" || type
== "version_string") {
3092 QSysInfo::WinVersion ver
= QSysInfo::WindowsVersion
;
3093 if(type
== "version")
3094 ret
= QString::number(ver
);
3095 else if(ver
== QSysInfo::WV_Me
)
3097 else if(ver
== QSysInfo::WV_95
)
3099 else if(ver
== QSysInfo::WV_98
)
3101 else if(ver
== QSysInfo::WV_NT
)
3103 else if(ver
== QSysInfo::WV_2000
)
3105 else if(ver
== QSysInfo::WV_2000
)
3107 else if(ver
== QSysInfo::WV_XP
)
3109 else if(ver
== QSysInfo::WV_VISTA
)
3113 } else if(type
== "arch") {
3115 GetSystemInfo(&info
);
3116 switch(info
.wProcessorArchitecture
) {
3117 #ifdef PROCESSOR_ARCHITECTURE_AMD64
3118 case PROCESSOR_ARCHITECTURE_AMD64
:
3122 case PROCESSOR_ARCHITECTURE_INTEL
:
3125 case PROCESSOR_ARCHITECTURE_IA64
:
3126 #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
3127 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
:
3136 #elif defined(Q_OS_UNIX)
3137 struct utsname name
;
3141 else if(type
== "name")
3142 ret
= name
.nodename
;
3143 else if(type
== "version")
3145 else if(type
== "version_string")
3147 else if(type
== "arch")
3151 var
= ".BUILTIN.HOST." + type
;
3152 place
[var
] = QStringList(ret
);
3153 } else if (var
== QLatin1String("QMAKE_DIR_SEP")) {
3154 if (place
[var
].isEmpty())
3155 return values("DIR_SEPARATOR", place
);
3156 } else if (var
== QLatin1String("EPOCROOT")) {
3157 if (place
[var
].isEmpty())
3158 place
[var
] = QStringList(epocRoot());
3160 //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::")));