1 /****************************************************************************
3 ** Copyright (C) 2010 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
,
82 E_SIZE
, E_GENERATE_UID
};
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
);
114 qmake_expand_functions
->insert("generate_uid", E_GENERATE_UID
);
116 return *qmake_expand_functions
;
119 enum TestFunc
{ T_REQUIRES
=1, T_GREATERTHAN
, T_LESSTHAN
, T_EQUALS
,
120 T_EXISTS
, T_EXPORT
, T_CLEAR
, T_UNSET
, T_EVAL
, T_CONFIG
, T_SYSTEM
,
121 T_RETURN
, T_BREAK
, T_NEXT
, T_DEFINED
, T_CONTAINS
, T_INFILE
,
122 T_COUNT
, T_ISEMPTY
, T_INCLUDE
, T_LOAD
, T_DEBUG
, T_ERROR
,
123 T_MESSAGE
, T_WARNING
, T_IF
};
124 QMap
<QString
, TestFunc
> qmake_testFunctions()
126 static QMap
<QString
, TestFunc
> *qmake_test_functions
= 0;
127 if(!qmake_test_functions
) {
128 qmake_test_functions
= new QMap
<QString
, TestFunc
>;
129 qmake_test_functions
->insert("requires", T_REQUIRES
);
130 qmake_test_functions
->insert("greaterThan", T_GREATERTHAN
);
131 qmake_test_functions
->insert("lessThan", T_LESSTHAN
);
132 qmake_test_functions
->insert("equals", T_EQUALS
);
133 qmake_test_functions
->insert("isEqual", T_EQUALS
);
134 qmake_test_functions
->insert("exists", T_EXISTS
);
135 qmake_test_functions
->insert("export", T_EXPORT
);
136 qmake_test_functions
->insert("clear", T_CLEAR
);
137 qmake_test_functions
->insert("unset", T_UNSET
);
138 qmake_test_functions
->insert("eval", T_EVAL
);
139 qmake_test_functions
->insert("CONFIG", T_CONFIG
);
140 qmake_test_functions
->insert("if", T_IF
);
141 qmake_test_functions
->insert("isActiveConfig", T_CONFIG
);
142 qmake_test_functions
->insert("system", T_SYSTEM
);
143 qmake_test_functions
->insert("return", T_RETURN
);
144 qmake_test_functions
->insert("break", T_BREAK
);
145 qmake_test_functions
->insert("next", T_NEXT
);
146 qmake_test_functions
->insert("defined", T_DEFINED
);
147 qmake_test_functions
->insert("contains", T_CONTAINS
);
148 qmake_test_functions
->insert("infile", T_INFILE
);
149 qmake_test_functions
->insert("count", T_COUNT
);
150 qmake_test_functions
->insert("isEmpty", T_ISEMPTY
);
151 qmake_test_functions
->insert("include", T_INCLUDE
);
152 qmake_test_functions
->insert("load", T_LOAD
);
153 qmake_test_functions
->insert("debug", T_DEBUG
);
154 qmake_test_functions
->insert("error", T_ERROR
);
155 qmake_test_functions
->insert("message", T_MESSAGE
);
156 qmake_test_functions
->insert("warning", T_WARNING
);
158 return *qmake_test_functions
;
167 static QString
remove_quotes(const QString
&arg
)
169 const ushort SINGLEQUOTE
= '\'';
170 const ushort DOUBLEQUOTE
= '"';
172 const QChar
*arg_data
= arg
.data();
173 const ushort first
= arg_data
->unicode();
174 const int arg_len
= arg
.length();
175 if(first
== SINGLEQUOTE
|| first
== DOUBLEQUOTE
) {
176 const ushort last
= (arg_data
+arg_len
-1)->unicode();
178 return arg
.mid(1, arg_len
-2);
183 static QString
varMap(const QString
&x
)
186 if(ret
.startsWith("TMAKE")) //tmake no more!
187 ret
= "QMAKE" + ret
.mid(5);
188 else if(ret
== "INTERFACES")
190 else if(ret
== "QMAKE_POST_BUILD")
191 ret
= "QMAKE_POST_LINK";
192 else if(ret
== "TARGETDEPS")
193 ret
= "POST_TARGETDEPS";
194 else if(ret
== "LIBPATH")
195 ret
= "QMAKE_LIBDIR";
196 else if(ret
== "QMAKE_EXT_MOC")
197 ret
= "QMAKE_EXT_CPP_MOC";
198 else if(ret
== "QMAKE_MOD_MOC")
199 ret
= "QMAKE_H_MOD_MOC";
200 else if(ret
== "QMAKE_LFLAGS_SHAPP")
201 ret
= "QMAKE_LFLAGS_APP";
202 else if(ret
== "PRECOMPH")
203 ret
= "PRECOMPILED_HEADER";
204 else if(ret
== "PRECOMPCPP")
205 ret
= "PRECOMPILED_SOURCE";
206 else if(ret
== "INCPATH")
208 else if(ret
== "QMAKE_EXTRA_WIN_COMPILERS" || ret
== "QMAKE_EXTRA_UNIX_COMPILERS")
209 ret
= "QMAKE_EXTRA_COMPILERS";
210 else if(ret
== "QMAKE_EXTRA_WIN_TARGETS" || ret
== "QMAKE_EXTRA_UNIX_TARGETS")
211 ret
= "QMAKE_EXTRA_TARGETS";
212 else if(ret
== "QMAKE_EXTRA_UNIX_INCLUDES")
213 ret
= "QMAKE_EXTRA_INCLUDES";
214 else if(ret
== "QMAKE_EXTRA_UNIX_VARIABLES")
215 ret
= "QMAKE_EXTRA_VARIABLES";
216 else if(ret
== "QMAKE_RPATH")
217 ret
= "QMAKE_LFLAGS_RPATH";
218 else if(ret
== "QMAKE_FRAMEWORKDIR")
219 ret
= "QMAKE_FRAMEWORKPATH";
220 else if(ret
== "QMAKE_FRAMEWORKDIR_FLAGS")
221 ret
= "QMAKE_FRAMEWORKPATH_FLAGS";
225 static QStringList
split_arg_list(QString params
)
230 const ushort LPAREN
= '(';
231 const ushort RPAREN
= ')';
232 const ushort SINGLEQUOTE
= '\'';
233 const ushort DOUBLEQUOTE
= '"';
234 const ushort COMMA
= ',';
235 const ushort SPACE
= ' ';
236 //const ushort TAB = '\t';
239 const QChar
*params_data
= params
.data();
240 const int params_len
= params
.length();
242 while(last
< params_len
&& (params_data
[last
].unicode() == SPACE
243 /*|| params_data[last].unicode() == TAB*/))
245 for(int x
= last
, parens
= 0; x
<= params_len
; x
++) {
246 unicode
= params_data
[x
].unicode();
247 if(x
== params_len
) {
248 while(x
&& params_data
[x
-1].unicode() == SPACE
)
250 QString
mid(params_data
+last
, x
-last
);
252 if(mid
[0] == quote
&& mid
[(int)mid
.length()-1] == quote
)
253 mid
= mid
.mid(1, mid
.length()-2);
259 if(unicode
== LPAREN
) {
261 } else if(unicode
== RPAREN
) {
263 } else if(quote
&& unicode
== quote
) {
265 } else if(!quote
&& (unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
)) {
268 if(!parens
&& !quote
&& unicode
== COMMA
) {
269 QString mid
= params
.mid(last
, x
- last
).trimmed();
272 while(last
< params_len
&& (params_data
[last
].unicode() == SPACE
273 /*|| params_data[last].unicode() == TAB*/))
280 static QStringList
split_value_list(const QString
&vals
, bool do_semicolon
=false)
286 const ushort LPAREN
= '(';
287 const ushort RPAREN
= ')';
288 const ushort SINGLEQUOTE
= '\'';
289 const ushort DOUBLEQUOTE
= '"';
290 const ushort BACKSLASH
= '\\';
291 const ushort SEMICOLON
= ';';
294 const QChar
*vals_data
= vals
.data();
295 const int vals_len
= vals
.length();
296 for(int x
= 0, parens
= 0; x
< vals_len
; x
++) {
297 unicode
= vals_data
[x
].unicode();
298 if(x
!= (int)vals_len
-1 && unicode
== BACKSLASH
&&
299 (vals_data
[x
+1].unicode() == SINGLEQUOTE
|| vals_data
[x
+1].unicode() == DOUBLEQUOTE
)) {
300 build
+= vals_data
[x
++]; //get that 'escape'
301 } else if(!quote
.isEmpty() && unicode
== quote
.top()) {
303 } else if(unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
) {
305 } else if(unicode
== RPAREN
) {
307 } else if(unicode
== LPAREN
) {
311 if(!parens
&& quote
.isEmpty() && ((do_semicolon
&& unicode
== SEMICOLON
) ||
312 vals_data
[x
] == Option::field_sep
)) {
316 build
+= vals_data
[x
];
324 //just a parsable entity
327 ParsableBlock() : ref_cnt(1) { }
328 virtual ~ParsableBlock() { }
333 Parse(const QString
&t
) : text(t
){ pi
= parser
; }
335 QList
<Parse
> parselist
;
337 inline int ref() { return ++ref_cnt
; }
338 inline int deref() { return --ref_cnt
; }
342 virtual bool continueBlock() = 0;
343 bool eval(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
);
346 bool ParsableBlock::eval(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
)
349 parser_info pi
= parser
;
350 const int block_count
= p
->scope_blocks
.count();
354 for(int i
= 0; i
< parselist
.count(); i
++) {
355 parser
= parselist
.at(i
).pi
;
356 if(!(ret
= p
->parse(parselist
.at(i
).text
, place
)) || !continueBlock())
362 while(p
->scope_blocks
.count() > block_count
)
363 p
->scope_blocks
.pop();
368 struct FunctionBlock
: public ParsableBlock
370 FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { }
372 QMap
<QString
, QStringList
> vars
;
373 QMap
<QString
, QStringList
> *calling_place
;
374 QStringList return_value
;
378 bool exec(const QList
<QStringList
> &args
,
379 QMakeProject
*p
, QMap
<QString
, QStringList
> &place
, QStringList
&functionReturn
);
380 virtual bool continueBlock() { return !cause_return
; }
383 bool FunctionBlock::exec(const QList
<QStringList
> &args
,
384 QMakeProject
*proj
, QMap
<QString
, QStringList
> &place
,
385 QStringList
&functionReturn
)
389 calling_place
= &place
;
391 calling_place
= &proj
->variables();
393 return_value
.clear();
394 cause_return
= false;
398 vars
= proj
->variables(); // should be place so that local variables can be inherited
402 vars
["ARGS"].clear();
403 for(int i
= 0; i
< args
.count(); i
++) {
404 vars
["ARGS"] += args
[i
];
405 vars
[QString::number(i
+1)] = args
[i
];
407 bool ret
= ParsableBlock::eval(proj
, vars
);
408 functionReturn
= return_value
;
412 return_value
.clear();
418 struct IteratorBlock
: public ParsableBlock
420 IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { }
429 Test(const QString
&f
, QStringList
&a
, bool i
) : func(f
), args(a
), invert(i
) { pi
= parser
; }
435 bool loop_forever
, cause_break
, cause_next
;
438 bool exec(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
);
439 virtual bool continueBlock() { return !cause_next
&& !cause_break
; }
441 bool IteratorBlock::exec(QMakeProject
*p
, QMap
<QString
, QStringList
> &place
)
444 QStringList::Iterator it
;
447 int iterate_count
= 0;
449 IteratorBlock
*saved_iterator
= p
->iterator
;
453 while(loop_forever
|| it
!= list
.end()) {
454 cause_next
= cause_break
= false;
455 if(!loop_forever
&& (*it
).isEmpty()) { //ignore empty items
460 //set up the loop variable
462 if(!variable
.isEmpty()) {
463 va
= place
[variable
];
465 place
[variable
] = QStringList(QString::number(iterate_count
));
467 place
[variable
] = QStringList(*it
);
471 for(QList
<Test
>::Iterator test_it
= test
.begin(); test_it
!= test
.end(); ++test_it
) {
472 parser
= (*test_it
).pi
;
473 succeed
= p
->doProjectTest((*test_it
).func
, (*test_it
).args
, place
);
474 if((*test_it
).invert
)
480 ret
= ParsableBlock::eval(p
, place
);
481 //restore the variable in the map
482 if(!variable
.isEmpty())
483 place
[variable
] = va
;
488 if(!ret
|| cause_break
)
493 p
->iterator
= saved_iterator
;
497 QMakeProject::ScopeBlock::~ScopeBlock()
505 static void qmake_error_msg(const QString
&msg
)
507 fprintf(stderr
, "%s:%d: %s\n", parser
.file
.toLatin1().constData(), parser
.line_no
,
508 msg
.toLatin1().constData());
511 enum isForSymbian_enum
{
512 isForSymbian_NOT_SET
= -1,
513 isForSymbian_FALSE
= 0,
514 isForSymbian_ABLD
= 1,
515 isForSymbian_SBSV2
= 2,
518 static isForSymbian_enum isForSymbian_value
= isForSymbian_NOT_SET
;
520 // Checking for symbian build is primarily determined from the qmake spec,
521 // but if that is not specified, detect if symbian is the default spec
522 // by checking the MAKEFILE_GENERATOR variable value.
523 static void init_symbian(const QMap
<QString
, QStringList
>& vars
)
525 if (isForSymbian_value
!= isForSymbian_NOT_SET
)
528 QString spec
= QFileInfo(Option::mkfile::qmakespec
).fileName();
529 if (spec
.startsWith("symbian-abld", Qt::CaseInsensitive
)) {
530 isForSymbian_value
= isForSymbian_ABLD
;
531 } else if (spec
.startsWith("symbian-sbsv2", Qt::CaseInsensitive
)) {
532 isForSymbian_value
= isForSymbian_SBSV2
;
534 QStringList generatorList
= vars
["MAKEFILE_GENERATOR"];
536 if (!generatorList
.isEmpty()) {
537 QString generator
= generatorList
.first();
538 if (generator
.startsWith("SYMBIAN_ABLD"))
539 isForSymbian_value
= isForSymbian_ABLD
;
540 else if (generator
.startsWith("SYMBIAN_SBSV2"))
541 isForSymbian_value
= isForSymbian_SBSV2
;
543 isForSymbian_value
= isForSymbian_FALSE
;
545 isForSymbian_value
= isForSymbian_FALSE
;
549 // Force recursive on Symbian, as non-recursive is not really a viable option there
550 if (isForSymbian_value
!= isForSymbian_FALSE
)
551 Option::recursive
= true;
556 // If isForSymbian_value has not been initialized explicitly yet,
557 // call initializer with dummy map to check qmake spec.
558 if (isForSymbian_value
== isForSymbian_NOT_SET
)
559 init_symbian(QMap
<QString
, QStringList
>());
561 return (isForSymbian_value
!= isForSymbian_FALSE
);
564 bool isForSymbianSbsv2()
566 // If isForSymbian_value has not been initialized explicitly yet,
567 // call initializer with dummy map to check qmake spec.
568 if (isForSymbian_value
== isForSymbian_NOT_SET
)
569 init_symbian(QMap
<QString
, QStringList
>());
571 return (isForSymbian_value
== isForSymbian_SBSV2
);
575 1) environment variable QMAKEFEATURES (as separated by colons)
576 2) property variable QMAKEFEATURES (as separated by colons)
577 3) <project_root> (where .qmake.cache lives) + FEATURES_DIR
578 4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR
579 5) your QMAKESPEC/features dir
580 6) your data_install/mkspecs/FEATURES_DIR
581 7) your QMAKESPEC/../FEATURES_DIR dir
583 FEATURES_DIR is defined as:
585 1) features/(unix|win32|macx)/
588 QStringList
qmake_feature_paths(QMakeProperty
*prop
=0)
592 const QString base_concat
= QDir::separator() + QString("features");
593 switch(Option::target_mode
) {
594 case Option::TARG_MACX_MODE
: //also a unix
595 concat
<< base_concat
+ QDir::separator() + "mac";
596 concat
<< base_concat
+ QDir::separator() + "macx";
597 concat
<< base_concat
+ QDir::separator() + "unix";
599 case Option::TARG_UNIX_MODE
:
602 concat
<< base_concat
+ QDir::separator() + "symbian";
604 concat
<< base_concat
+ QDir::separator() + "unix";
607 case Option::TARG_WIN_MODE
:
610 concat
<< base_concat
+ QDir::separator() + "symbian";
612 concat
<< base_concat
+ QDir::separator() + "win32";
615 case Option::TARG_MAC9_MODE
:
616 concat
<< base_concat
+ QDir::separator() + "mac";
617 concat
<< base_concat
+ QDir::separator() + "mac9";
620 concat
<< base_concat
;
622 const QString mkspecs_concat
= QDir::separator() + QString("mkspecs");
623 QStringList feature_roots
;
624 QByteArray mkspec_path
= qgetenv("QMAKEFEATURES");
625 if(!mkspec_path
.isNull())
626 feature_roots
+= splitPathList(QString::fromLocal8Bit(mkspec_path
));
628 feature_roots
+= splitPathList(prop
->value("QMAKEFEATURES"));
629 if(!Option::mkfile::cachefile
.isEmpty()) {
631 int last_slash
= Option::mkfile::cachefile
.lastIndexOf(Option::dir_sep
);
633 path
= Option::fixPathToLocalOS(Option::mkfile::cachefile
.left(last_slash
));
634 for(QStringList::Iterator concat_it
= concat
.begin();
635 concat_it
!= concat
.end(); ++concat_it
)
636 feature_roots
<< (path
+ (*concat_it
));
638 QByteArray qmakepath
= qgetenv("QMAKEPATH");
639 if (!qmakepath
.isNull()) {
640 const QStringList lst
= splitPathList(QString::fromLocal8Bit(qmakepath
));
641 for(QStringList::ConstIterator it
= lst
.begin(); it
!= lst
.end(); ++it
) {
642 for(QStringList::Iterator concat_it
= concat
.begin();
643 concat_it
!= concat
.end(); ++concat_it
)
644 feature_roots
<< ((*it
) + mkspecs_concat
+ (*concat_it
));
647 if(!Option::mkfile::qmakespec
.isEmpty())
648 feature_roots
<< Option::mkfile::qmakespec
+ QDir::separator() + "features";
649 if(!Option::mkfile::qmakespec
.isEmpty()) {
650 QFileInfo
specfi(Option::mkfile::qmakespec
);
651 QDir
specdir(specfi
.absoluteFilePath());
652 while(!specdir
.isRoot()) {
653 if(!specdir
.cdUp() || specdir
.isRoot())
655 if(QFile::exists(specdir
.path() + QDir::separator() + "features")) {
656 for(QStringList::Iterator concat_it
= concat
.begin();
657 concat_it
!= concat
.end(); ++concat_it
)
658 feature_roots
<< (specdir
.path() + (*concat_it
));
663 for(QStringList::Iterator concat_it
= concat
.begin();
664 concat_it
!= concat
.end(); ++concat_it
)
665 feature_roots
<< (QLibraryInfo::location(QLibraryInfo::PrefixPath
) +
666 mkspecs_concat
+ (*concat_it
));
667 for(QStringList::Iterator concat_it
= concat
.begin();
668 concat_it
!= concat
.end(); ++concat_it
)
669 feature_roots
<< (QLibraryInfo::location(QLibraryInfo::DataPath
) +
670 mkspecs_concat
+ (*concat_it
));
671 return feature_roots
;
674 QStringList
qmake_mkspec_paths()
677 const QString concat
= QDir::separator() + QString("mkspecs");
678 QByteArray qmakepath
= qgetenv("QMAKEPATH");
679 if (!qmakepath
.isEmpty()) {
680 const QStringList lst
= splitPathList(QString::fromLocal8Bit(qmakepath
));
681 for(QStringList::ConstIterator it
= lst
.begin(); it
!= lst
.end(); ++it
)
682 ret
<< ((*it
) + concat
);
684 ret
<< QLibraryInfo::location(QLibraryInfo::DataPath
) + concat
;
689 class QMakeProjectEnv
693 QMakeProjectEnv() { }
694 QMakeProjectEnv(QMakeProject
*p
) { execute(p
->variables()); }
695 QMakeProjectEnv(const QMap
<QString
, QStringList
> &values
) { execute(values
); }
697 void execute(QMakeProject
*p
) { execute(p
->variables()); }
698 void execute(const QMap
<QString
, QStringList
> &values
) {
700 for(QMap
<QString
, QStringList
>::ConstIterator it
= values
.begin(); it
!= values
.end(); ++it
) {
701 const QString var
= it
.key(), val
= it
.value().join(" ");
702 if(!var
.startsWith(".")) {
703 const QString env_var
= Option::sysenv_mod
+ var
;
704 if(!putenv(strdup(QString(env_var
+ "=" + val
).toAscii().data())))
705 envs
.append(env_var
);
714 for(QStringList::ConstIterator it
= envs
.begin();it
!= envs
.end(); ++it
) {
715 putenv(strdup(QString(*it
+ "=").toAscii().data()));
721 QMakeProject::~QMakeProject()
725 for(QMap
<QString
, FunctionBlock
*>::iterator it
= replaceFunctions
.begin(); it
!= replaceFunctions
.end(); ++it
) {
726 if(!it
.value()->deref())
729 replaceFunctions
.clear();
730 for(QMap
<QString
, FunctionBlock
*>::iterator it
= testFunctions
.begin(); it
!= testFunctions
.end(); ++it
) {
731 if(!it
.value()->deref())
734 testFunctions
.clear();
739 QMakeProject::init(QMakeProperty
*p
, const QMap
<QString
, QStringList
> *vars
)
744 prop
= new QMakeProperty
;
753 QMakeProject::QMakeProject(QMakeProject
*p
, const QMap
<QString
, QStringList
> *vars
)
755 init(p
->properties(), vars
? vars
: &p
->variables());
756 for(QMap
<QString
, FunctionBlock
*>::iterator it
= p
->replaceFunctions
.begin(); it
!= p
->replaceFunctions
.end(); ++it
) {
758 replaceFunctions
.insert(it
.key(), it
.value());
760 for(QMap
<QString
, FunctionBlock
*>::iterator it
= p
->testFunctions
.begin(); it
!= p
->testFunctions
.end(); ++it
) {
762 testFunctions
.insert(it
.key(), it
.value());
767 QMakeProject::reset()
769 // scope_blocks starts with one non-ignoring entity
770 scope_blocks
.clear();
771 scope_blocks
.push(ScopeBlock());
777 QMakeProject::parse(const QString
&t
, QMap
<QString
, QStringList
> &place
, int numLines
)
779 QString s
= t
.simplified();
780 int hash_mark
= s
.indexOf("#");
781 if(hash_mark
!= -1) //good bye comments
782 s
= s
.left(hash_mark
);
783 if(s
.isEmpty()) // blank_line
786 if(scope_blocks
.top().ignore
) {
787 bool continue_parsing
= false;
788 // adjust scope for each block which appears on a single line
789 for(int i
= 0; i
< s
.length(); i
++) {
791 scope_blocks
.push(ScopeBlock(true));
792 } else if(s
[i
] == '}') {
793 if(scope_blocks
.count() == 1) {
794 fprintf(stderr
, "Braces mismatch %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
797 ScopeBlock sb
= scope_blocks
.pop();
799 sb
.iterate
->exec(this, place
);
803 if(!scope_blocks
.top().ignore
) {
804 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
805 parser
.line_no
, scope_blocks
.count()+1);
806 s
= s
.mid(i
+1).trimmed();
807 continue_parsing
= !s
.isEmpty();
812 if(!continue_parsing
) {
813 debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
814 parser
.file
.toLatin1().constData(), parser
.line_no
);
822 const QChar
*d
= s
.unicode();
823 bool function_finished
= false;
824 while(d_off
< s
.length()) {
825 if(*(d
+d_off
) == QLatin1Char('}')) {
826 function
->scope_level
--;
827 if(!function
->scope_level
) {
828 function_finished
= true;
831 } else if(*(d
+d_off
) == QLatin1Char('{')) {
832 function
->scope_level
++;
834 append
+= *(d
+d_off
);
837 if(!append
.isEmpty())
838 function
->parselist
.append(IteratorBlock::Parse(append
));
839 if(function_finished
) {
841 s
= QString(d
+d_off
, s
.length()-d_off
);
845 } else if(IteratorBlock
*it
= scope_blocks
.top().iterate
) {
848 const QChar
*d
= s
.unicode();
849 bool iterate_finished
= false;
850 while(d_off
< s
.length()) {
851 if(*(d
+d_off
) == QLatin1Char('}')) {
853 if(!it
->scope_level
) {
854 iterate_finished
= true;
857 } else if(*(d
+d_off
) == QLatin1Char('{')) {
860 append
+= *(d
+d_off
);
863 if(!append
.isEmpty())
864 scope_blocks
.top().iterate
->parselist
.append(IteratorBlock::Parse(append
));
865 if(iterate_finished
) {
866 scope_blocks
.top().iterate
= 0;
867 bool ret
= it
->exec(this, place
);
877 QString scope
, var
, op
;
879 #define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o
880 const QChar
*d
= s
.unicode();
882 SKIP_WS(d
, d_off
, s
.length());
883 IteratorBlock
*iterator
= 0;
884 bool scope_failed
= false, else_line
= false, or_op
=false;
886 int parens
= 0, scope_count
=0, start_block
= 0;
887 while(d_off
< s
.length()) {
889 if(*(d
+d_off
) == QLatin1Char('='))
891 if(*(d
+d_off
) == QLatin1Char('+') || *(d
+d_off
) == QLatin1Char('-') ||
892 *(d
+d_off
) == QLatin1Char('*') || *(d
+d_off
) == QLatin1Char('~')) {
893 if(*(d
+d_off
+1) == QLatin1Char('=')) {
895 } else if(*(d
+d_off
+1) == QLatin1Char(' ')) {
896 const QChar
*k
= d
+d_off
+1;
898 SKIP_WS(k
, k_off
, s
.length()-d_off
);
899 if(*(k
+k_off
) == QLatin1Char('=')) {
901 qmake_error_msg(QString(d
+d_off
, 1) + "must be followed immediately by =");
908 if(!quote
.isNull()) {
909 if(*(d
+d_off
) == quote
)
911 } else if(*(d
+d_off
) == '(') {
913 } else if(*(d
+d_off
) == ')') {
915 } else if(*(d
+d_off
) == '"' /*|| *(d+d_off) == '\''*/) {
919 if(!parens
&& quote
.isNull() &&
920 (*(d
+d_off
) == QLatin1Char(':') || *(d
+d_off
) == QLatin1Char('{') ||
921 *(d
+d_off
) == QLatin1Char(')') || *(d
+d_off
) == QLatin1Char('|'))) {
923 scope
= var
.trimmed();
924 if(*(d
+d_off
) == QLatin1Char(')'))
925 scope
+= *(d
+d_off
); // need this
928 bool test
= scope_failed
;
929 if(scope
.isEmpty()) {
931 } else if(scope
.toLower() == "else") { //else is a builtin scope here as it modifies state
932 if(scope_count
!= 1 || scope_blocks
.top().else_status
== ScopeBlock::TestNone
) {
933 qmake_error_msg(("Unexpected " + scope
+ " ('" + s
+ "')").toLatin1());
937 test
= (scope_blocks
.top().else_status
== ScopeBlock::TestSeek
);
938 debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser
.file
.toLatin1().constData(), parser
.line_no
,
939 scope
== "else" ? "" : QString(" (" + scope
+ ")").toLatin1().constData(),
940 test
? "considered" : "excluded");
942 QString comp_scope
= scope
;
943 bool invert_test
= (comp_scope
.at(0) == QLatin1Char('!'));
945 comp_scope
= comp_scope
.mid(1);
946 int lparen
= comp_scope
.indexOf('(');
947 if(or_op
== scope_failed
) {
948 if(lparen
!= -1) { // if there is an lparen in the scope, it IS a function
949 int rparen
= comp_scope
.lastIndexOf(')');
951 qmake_error_msg("Function missing right paren: " + comp_scope
);
954 QString func
= comp_scope
.left(lparen
);
955 QStringList args
= split_arg_list(comp_scope
.mid(lparen
+1, rparen
- lparen
- 1));
957 fprintf(stderr
, "%s:%d: No tests can come after a function definition!\n",
958 parser
.file
.toLatin1().constData(), parser
.line_no
);
960 } else if(func
== "for") { //for is a builtin function here, as it modifies state
961 if(args
.count() > 2 || args
.count() < 1) {
962 fprintf(stderr
, "%s:%d: for(iterate, list) requires two arguments.\n",
963 parser
.file
.toLatin1().constData(), parser
.line_no
);
965 } else if(iterator
) {
966 fprintf(stderr
, "%s:%d unexpected nested for()\n",
967 parser
.file
.toLatin1().constData(), parser
.line_no
);
971 iterator
= new IteratorBlock
;
973 if(args
.count() == 1) {
974 doVariableReplace(args
[0], place
);
976 if(args
[0] != "ever") {
979 fprintf(stderr
, "%s:%d: for(iterate, list) requires two arguments.\n",
980 parser
.file
.toLatin1().constData(), parser
.line_no
);
984 } else if(args
.count() == 2) {
985 iterator
->variable
= args
[0];
986 doVariableReplace(args
[1], place
);
989 QStringList list
= place
[it_list
];
991 if(it_list
== "forever") {
992 iterator
->loop_forever
= true;
994 int dotdot
= it_list
.indexOf("..");
997 int start
= it_list
.left(dotdot
).toInt(&ok
);
999 int end
= it_list
.mid(dotdot
+2).toInt(&ok
);
1002 for(int i
= start
; i
<= end
; i
++)
1003 list
<< QString::number(i
);
1005 for(int i
= start
; i
>= end
; i
--)
1006 list
<< QString::number(i
);
1013 iterator
->list
= list
;
1014 test
= !invert_test
;
1015 } else if(iterator
) {
1016 iterator
->test
.append(IteratorBlock::Test(func
, args
, invert_test
));
1017 test
= !invert_test
;
1018 } else if(func
== "defineTest" || func
== "defineReplace") {
1019 if(!function_blocks
.isEmpty()) {
1021 "%s:%d: cannot define a function within another definition.\n",
1022 parser
.file
.toLatin1().constData(), parser
.line_no
);
1025 if(args
.count() != 1) {
1026 fprintf(stderr
, "%s:%d: %s(function_name) requires one argument.\n",
1027 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
1030 QMap
<QString
, FunctionBlock
*> *map
= 0;
1031 if(func
== "defineTest")
1032 map
= &testFunctions
;
1034 map
= &replaceFunctions
;
1036 if(!map
|| map
->contains(args
[0])) {
1037 fprintf(stderr
, "%s:%d: Function[%s] multiply defined.\n",
1038 parser
.file
.toLatin1().constData(), parser
.line_no
, args
[0].toLatin1().constData());
1042 function
= new FunctionBlock
;
1043 map
->insert(args
[0], function
);
1046 test
= doProjectTest(func
, args
, place
);
1047 if(*(d
+d_off
) == QLatin1Char(')') && d_off
== s
.length()-1) {
1050 scope_blocks
.top().else_status
=
1051 (test
? ScopeBlock::TestFound
: ScopeBlock::TestSeek
);
1052 return true; // assume we are done
1056 QString cscope
= comp_scope
.trimmed();
1057 doVariableReplace(cscope
, place
);
1058 test
= isActiveConfig(cscope
.trimmed(), true, &place
);
1064 if(!test
&& !scope_failed
)
1065 debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser
.file
.toLatin1().constData(),
1066 parser
.line_no
, scope
.toLatin1().constData());
1068 scope_failed
= !test
;
1069 or_op
= (*(d
+d_off
) == QLatin1Char('|'));
1071 if(*(d
+d_off
) == QLatin1Char('{')) { // scoping block
1074 for(int off
= 0, braces
= 0; true; ++off
) {
1075 if(*(d
+d_off
+off
) == QLatin1Char('{'))
1077 else if(*(d
+d_off
+off
) == QLatin1Char('}') && braces
)
1079 if(!braces
|| d_off
+off
== s
.length()) {
1080 iterator
->parselist
.append(s
.mid(d_off
, off
-1));
1082 iterator
->scope_level
+= braces
-1;
1089 } else if(!parens
&& *(d
+d_off
) == QLatin1Char('}')) {
1092 } else if(!scope_blocks
.count()) {
1093 warn_msg(WarnParser
, "Possible braces mismatch %s:%d", parser
.file
.toLatin1().constData(), parser
.line_no
);
1095 if(scope_blocks
.count() == 1) {
1096 fprintf(stderr
, "Braces mismatch %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
1099 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
1100 parser
.line_no
, scope_blocks
.count());
1101 ScopeBlock sb
= scope_blocks
.pop();
1103 sb
.iterate
->exec(this, place
);
1110 var
= var
.trimmed();
1112 if(!else_line
|| (else_line
&& !scope_failed
))
1113 scope_blocks
.top().else_status
= (!scope_failed
? ScopeBlock::TestFound
: ScopeBlock::TestSeek
);
1115 ScopeBlock
next_block(scope_failed
);
1116 next_block
.iterate
= iterator
;
1118 next_block
.else_status
= ScopeBlock::TestNone
;
1119 else if(scope_failed
)
1120 next_block
.else_status
= ScopeBlock::TestSeek
;
1122 next_block
.else_status
= ScopeBlock::TestFound
;
1123 scope_blocks
.push(next_block
);
1124 debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser
.file
.toLatin1().constData(),
1125 parser
.line_no
, scope_blocks
.count(), scope_failed
, s
.toLatin1().constData());
1126 } else if(iterator
) {
1127 iterator
->parselist
.append(var
+s
.mid(d_off
));
1128 bool ret
= iterator
->exec(this, place
);
1133 if((!scope_count
&& !var
.isEmpty()) || (scope_count
== 1 && else_line
))
1134 scope_blocks
.top().else_status
= ScopeBlock::TestNone
;
1135 if(d_off
== s
.length()) {
1136 if(!var
.trimmed().isEmpty())
1137 qmake_error_msg(("Parse Error ('" + s
+ "')").toLatin1());
1138 return var
.isEmpty(); // allow just a scope
1141 SKIP_WS(d
, d_off
, s
.length());
1142 for(; d_off
< s
.length() && op
.indexOf('=') == -1; op
+= *(d
+(d_off
++)))
1144 op
.replace(QRegExp("\\s"), "");
1146 SKIP_WS(d
, d_off
, s
.length());
1147 QString vals
= s
.mid(d_off
); // vals now contains the space separated list of values
1148 int rbraces
= vals
.count('}'), lbraces
= vals
.count('{');
1149 if(scope_blocks
.count() > 1 && rbraces
- lbraces
== 1) {
1150 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser
.file
.toLatin1().constData(),
1151 parser
.line_no
, scope_blocks
.count());
1152 ScopeBlock sb
= scope_blocks
.pop();
1154 sb
.iterate
->exec(this, place
);
1155 vals
.truncate(vals
.length()-1);
1156 } else if(rbraces
!= lbraces
) {
1157 warn_msg(WarnParser
, "Possible braces mismatch {%s} %s:%d",
1158 vals
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1161 return true; // oh well
1164 doVariableReplace(var
, place
);
1165 var
= varMap(var
); //backwards compatability
1166 if(!var
.isEmpty() && Option::mkfile::do_preprocess
) {
1167 static QString
last_file("*none*");
1168 if(parser
.file
!= last_file
) {
1169 fprintf(stdout
, "#file %s:%d\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
1170 last_file
= parser
.file
;
1172 fprintf(stdout
, "%s %s %s\n", var
.toLatin1().constData(), op
.toLatin1().constData(), vals
.toLatin1().constData());
1175 if(vals
.contains('=') && numLines
> 1)
1176 warn_msg(WarnParser
, "Detected possible line continuation: {%s} %s:%d",
1177 var
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1179 QStringList
&varlist
= place
[var
]; // varlist is the list in the symbol table
1181 if(Option::debug_level
>= 1) {
1182 QString tmp_vals
= vals
;
1183 doVariableReplace(tmp_vals
, place
);
1184 debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser
.file
.toLatin1().constData(), parser
.line_no
,
1185 var
.toLatin1().constData(), op
.toLatin1().constData(), tmp_vals
.toLatin1().constData());
1188 // now do the operation
1190 doVariableReplace(vals
, place
);
1191 if(vals
.length() < 4 || vals
.at(0) != 's') {
1192 qmake_error_msg(("~= operator only can handle s/// function ('" +
1193 s
+ "')").toLatin1());
1196 QChar sep
= vals
.at(1);
1197 QStringList func
= vals
.split(sep
);
1198 if(func
.count() < 3 || func
.count() > 4) {
1199 qmake_error_msg(("~= operator only can handle s/// function ('" +
1200 s
+ "')").toLatin1());
1203 bool global
= false, case_sense
= true, quote
= false;
1204 if(func
.count() == 4) {
1205 global
= func
[3].indexOf('g') != -1;
1206 case_sense
= func
[3].indexOf('i') == -1;
1207 quote
= func
[3].indexOf('q') != -1;
1209 QString from
= func
[1], to
= func
[2];
1211 from
= QRegExp::escape(from
);
1212 QRegExp
regexp(from
, case_sense
? Qt::CaseSensitive
: Qt::CaseInsensitive
);
1213 for(QStringList::Iterator varit
= varlist
.begin(); varit
!= varlist
.end();) {
1214 if((*varit
).contains(regexp
)) {
1215 (*varit
) = (*varit
).replace(regexp
, to
);
1216 if ((*varit
).isEmpty())
1217 varit
= varlist
.erase(varit
);
1226 QStringList vallist
;
1228 //doVariableReplace(vals, place);
1229 QStringList tmp
= split_value_list(vals
, (var
== "DEPENDPATH" || var
== "INCLUDEPATH"));
1230 for(int i
= 0; i
< tmp
.size(); ++i
)
1231 vallist
+= doVariableReplaceExpand(tmp
[i
], place
);
1235 if(!varlist
.isEmpty()) {
1236 bool send_warning
= false;
1237 if(var
!= "TEMPLATE" && var
!= "TARGET") {
1238 QSet
<QString
> incoming_vals
= vallist
.toSet();
1239 for(int i
= 0; i
< varlist
.size(); ++i
) {
1240 const QString var
= varlist
.at(i
).trimmed();
1241 if(!var
.isEmpty() && !incoming_vals
.contains(var
)) {
1242 send_warning
= true;
1248 warn_msg(WarnParser
, "Operator=(%s) clears variables previously set: %s:%d",
1249 var
.toLatin1().constData(), parser
.file
.toLatin1().constData(), parser
.line_no
);
1253 for(QStringList::ConstIterator valit
= vallist
.begin();
1254 valit
!= vallist
.end(); ++valit
) {
1255 if((*valit
).isEmpty())
1257 if((op
== "*=" && !varlist
.contains((*valit
))) ||
1258 op
== "=" || op
== "+=")
1259 varlist
.append((*valit
));
1261 varlist
.removeAll((*valit
));
1263 if(var
== "REQUIRES") // special case to get communicated to backends!
1264 doProjectCheckReqs(vallist
, place
);
1270 QMakeProject::read(QTextStream
&file
, QMap
<QString
, QStringList
> &place
)
1275 while(!file
.atEnd()) {
1277 QString line
= file
.readLine().trimmed();
1278 int prelen
= line
.length();
1280 int hash_mark
= line
.indexOf("#");
1281 if(hash_mark
!= -1) //good bye comments
1282 line
= line
.left(hash_mark
).trimmed();
1283 if(!line
.isEmpty() && line
.right(1) == "\\") {
1284 if(!line
.startsWith("#")) {
1285 line
.truncate(line
.length() - 1);
1286 s
+= line
+ Option::field_sep
;
1289 } else if(!line
.isEmpty() || (line
.isEmpty() && !prelen
)) {
1290 if(s
.isEmpty() && line
.isEmpty())
1292 if(!line
.isEmpty()) {
1297 if(!(ret
= parse(s
, place
, numLines
))) {
1308 ret
= parse(s
, place
, numLines
);
1313 QMakeProject::read(const QString
&file
, QMap
<QString
, QStringList
> &place
)
1315 parser_info pi
= parser
;
1318 const QString oldpwd
= qmake_getpwd();
1319 QString filename
= Option::fixPathToLocalOS(file
);
1320 doVariableReplace(filename
, place
);
1321 bool ret
= false, using_stdin
= false;
1323 if(!strcmp(filename
.toLatin1(), "-")) {
1324 qfile
.setFileName("");
1325 ret
= qfile
.open(stdin
, QIODevice::ReadOnly
);
1327 } else if(QFileInfo(file
).isDir()) {
1330 qfile
.setFileName(filename
);
1331 ret
= qfile
.open(QIODevice::ReadOnly
);
1332 qmake_setpwd(QFileInfo(filename
).absolutePath());
1335 parser_info pi
= parser
;
1336 parser
.from_file
= true;
1337 parser
.file
= filename
;
1339 QTextStream
t(&qfile
);
1340 ret
= read(t
, place
);
1344 if(scope_blocks
.count() != 1) {
1345 qmake_error_msg("Unterminated conditional block at end of file");
1349 qmake_setpwd(oldpwd
);
1354 QMakeProject::read(const QString
&project
, uchar cmd
)
1356 pfile
= QFileInfo(project
).absoluteFilePath();
1361 QMakeProject::read(uchar cmd
)
1363 if(cfile
.isEmpty()) {
1364 //find out where qmake (myself) lives
1365 if (!base_vars
.contains("QMAKE_QMAKE")) {
1366 if (!Option::qmake_abslocation
.isNull())
1367 base_vars
["QMAKE_QMAKE"] = QStringList(Option::qmake_abslocation
);
1369 base_vars
["QMAKE_QMAKE"] = QStringList("qmake");
1372 // hack to get the Option stuff in there
1373 base_vars
["QMAKE_EXT_OBJ"] = QStringList(Option::obj_ext
);
1374 base_vars
["QMAKE_EXT_CPP"] = Option::cpp_ext
;
1375 base_vars
["QMAKE_EXT_C"] = Option::c_ext
;
1376 base_vars
["QMAKE_EXT_H"] = Option::h_ext
;
1377 base_vars
["QMAKE_SH"] = Option::shellPath
;
1378 if(!Option::user_template_prefix
.isEmpty())
1379 base_vars
["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix
);
1381 if(cmd
& ReadCache
&& Option::mkfile::do_cache
) { // parse the cache
1382 int cache_depth
= -1;
1383 QString qmake_cache
= Option::mkfile::cachefile
;
1384 if(qmake_cache
.isEmpty()) { //find it as it has not been specified
1385 QString dir
= QDir::toNativeSeparators(Option::output_dir
);
1386 while(!QFile::exists((qmake_cache
= dir
+ QDir::separator() + ".qmake.cache"))) {
1387 dir
= dir
.left(dir
.lastIndexOf(QDir::separator()));
1388 if(dir
.isEmpty() || dir
.indexOf(QDir::separator()) == -1) {
1392 if(cache_depth
== -1)
1398 QString abs_cache
= QFileInfo(Option::mkfile::cachefile
).absoluteDir().path();
1399 if(Option::output_dir
.startsWith(abs_cache
))
1400 cache_depth
= Option::output_dir
.mid(abs_cache
.length()).count('/');
1402 if(!qmake_cache
.isEmpty()) {
1403 if(read(qmake_cache
, cache
)) {
1404 Option::mkfile::cachefile_depth
= cache_depth
;
1405 Option::mkfile::cachefile
= qmake_cache
;
1406 if(Option::mkfile::qmakespec
.isEmpty() && !cache
["QMAKESPEC"].isEmpty())
1407 Option::mkfile::qmakespec
= cache
["QMAKESPEC"].first();
1411 if(cmd
& ReadConf
) { // parse mkspec
1412 QString qmakespec
= fixEnvVariables(Option::mkfile::qmakespec
);
1413 QStringList mkspec_roots
= qmake_mkspec_paths();
1414 debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec
.toLatin1().constData(),
1415 mkspec_roots
.join("::").toLatin1().constData());
1416 if(qmakespec
.isEmpty()) {
1417 for(QStringList::ConstIterator it
= mkspec_roots
.begin(); it
!= mkspec_roots
.end(); ++it
) {
1418 QString mkspec
= (*it
) + QDir::separator() + "default";
1419 QFileInfo
default_info(mkspec
);
1420 if(default_info
.exists() && default_info
.isDir()) {
1425 if(qmakespec
.isEmpty()) {
1426 fprintf(stderr
, "QMAKESPEC has not been set, so configuration cannot be deduced.\n");
1429 Option::mkfile::qmakespec
= qmakespec
;
1432 if(QDir::isRelativePath(qmakespec
)) {
1433 if (QFile::exists(qmakespec
+"/qmake.conf")) {
1434 Option::mkfile::qmakespec
= QFileInfo(Option::mkfile::qmakespec
).absoluteFilePath();
1435 } else if (QFile::exists(Option::output_dir
+"/"+qmakespec
+"/qmake.conf")) {
1436 qmakespec
= Option::mkfile::qmakespec
= QFileInfo(Option::output_dir
+"/"+qmakespec
).absoluteFilePath();
1438 bool found_mkspec
= false;
1439 for(QStringList::ConstIterator it
= mkspec_roots
.begin(); it
!= mkspec_roots
.end(); ++it
) {
1440 QString mkspec
= (*it
) + QDir::separator() + qmakespec
;
1441 if(QFile::exists(mkspec
)) {
1442 found_mkspec
= true;
1443 Option::mkfile::qmakespec
= qmakespec
= mkspec
;
1448 fprintf(stderr
, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n",
1449 qmakespec
.toLatin1().constData(), mkspec_roots
.join("\n\t").toLatin1().constData());
1455 // parse qmake configuration
1456 while(qmakespec
.endsWith(QString(QChar(QDir::separator()))))
1457 qmakespec
.truncate(qmakespec
.length()-1);
1458 QString spec
= qmakespec
+ QDir::separator() + "qmake.conf";
1459 if(!QFile::exists(spec
) &&
1460 QFile::exists(qmakespec
+ QDir::separator() + "tmake.conf"))
1461 spec
= qmakespec
+ QDir::separator() + "tmake.conf";
1462 debug_msg(1, "QMAKESPEC conf: reading %s", spec
.toLatin1().constData());
1463 if(!read(spec
, base_vars
)) {
1464 fprintf(stderr
, "Failure to read QMAKESPEC conf file %s.\n", spec
.toLatin1().constData());
1468 init_symbian(base_vars
);
1470 if(Option::mkfile::do_cache
&& !Option::mkfile::cachefile
.isEmpty()) {
1471 debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile
.toLatin1().constData());
1472 read(Option::mkfile::cachefile
, base_vars
);
1476 if(cmd
& ReadFeatures
) {
1477 debug_msg(1, "Processing default_pre: %s", vars
["CONFIG"].join("::").toLatin1().constData());
1478 if(doProjectInclude("default_pre", IncludeFlagFeature
, base_vars
) == IncludeNoExist
)
1479 doProjectInclude("default", IncludeFlagFeature
, base_vars
);
1483 vars
= base_vars
; // start with the base
1486 if(pfile
!= "-" && vars
["TARGET"].isEmpty())
1487 vars
["TARGET"].append(QFileInfo(pfile
).baseName());
1489 //before commandline
1490 if(cmd
& ReadCmdLine
) {
1492 parser
.file
= "(internal)";
1493 parser
.from_file
= false;
1494 parser
.line_no
= 1; //really arg count now.. duh
1496 for(QStringList::ConstIterator it
= Option::before_user_vars
.begin();
1497 it
!= Option::before_user_vars
.end(); ++it
) {
1498 if(!parse((*it
), vars
)) {
1499 fprintf(stderr
, "Argument failed to parse: %s\n", (*it
).toLatin1().constData());
1506 //commandline configs
1507 if(cmd
& ReadConfigs
&& !Option::user_configs
.isEmpty()) {
1508 parser
.file
= "(configs)";
1509 parser
.from_file
= false;
1510 parser
.line_no
= 1; //really arg count now.. duh
1511 parse("CONFIG += " + Option::user_configs
.join(" "), vars
);
1514 if(cmd
& ReadProFile
) { // parse project file
1515 debug_msg(1, "Project file: reading %s", pfile
.toLatin1().constData());
1516 if(pfile
!= "-" && !QFile::exists(pfile
) && !pfile
.endsWith(Option::pro_ext
))
1517 pfile
+= Option::pro_ext
;
1518 if(!read(pfile
, vars
))
1522 if(cmd
& ReadPostFiles
) { // parse post files
1523 const QStringList l
= vars
["QMAKE_POST_INCLUDE_FILES"];
1524 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
1525 if(read((*it
), vars
)) {
1526 if(vars
["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf((*it
)) == -1)
1527 vars
["QMAKE_INTERNAL_INCLUDED_FILES"].append((*it
));
1532 if(cmd
& ReadCmdLine
) {
1533 parser
.file
= "(internal)";
1534 parser
.from_file
= false;
1535 parser
.line_no
= 1; //really arg count now.. duh
1537 for(QStringList::ConstIterator it
= Option::after_user_vars
.begin();
1538 it
!= Option::after_user_vars
.end(); ++it
) {
1539 if(!parse((*it
), vars
)) {
1540 fprintf(stderr
, "Argument failed to parse: %s\n", (*it
).toLatin1().constData());
1547 //after configs (set in BUILDS)
1548 if(cmd
& ReadConfigs
&& !Option::after_user_configs
.isEmpty()) {
1549 parser
.file
= "(configs)";
1550 parser
.from_file
= false;
1551 parser
.line_no
= 1; //really arg count now.. duh
1552 parse("CONFIG += " + Option::after_user_configs
.join(" "), vars
);
1555 if(pfile
!= "-" && vars
["TARGET"].isEmpty())
1556 vars
["TARGET"].append(QFileInfo(pfile
).baseName());
1558 if(cmd
& ReadConfigs
&& !Option::user_configs
.isEmpty()) {
1559 parser
.file
= "(configs)";
1560 parser
.from_file
= false;
1561 parser
.line_no
= 1; //really arg count now.. duh
1562 parse("CONFIG += " + Option::user_configs
.join(" "), base_vars
);
1565 if(cmd
& ReadFeatures
) {
1566 debug_msg(1, "Processing default_post: %s", vars
["CONFIG"].join("::").toLatin1().constData());
1567 doProjectInclude("default_post", IncludeFlagFeature
, vars
);
1569 QHash
<QString
, bool> processed
;
1570 const QStringList
&configs
= vars
["CONFIG"];
1571 debug_msg(1, "Processing CONFIG features: %s", configs
.join("::").toLatin1().constData());
1573 bool finished
= true;
1574 for(int i
= configs
.size()-1; i
>= 0; --i
) {
1575 const QString config
= configs
[i
].toLower();
1576 if(!processed
.contains(config
)) {
1577 processed
.insert(config
, true);
1578 if(doProjectInclude(config
, IncludeFlagFeature
, vars
) == IncludeSuccess
) {
1588 Option::postProcessProject(this); // let Option post-process
1593 QMakeProject::isActiveConfig(const QString
&x
, bool regex
, QMap
<QString
, QStringList
> *place
)
1598 //magic types for easy flipping
1601 else if(x
== "false")
1604 static QString spec
;
1606 spec
= QFileInfo(Option::mkfile::qmakespec
).fileName();
1608 // Symbian is an exception to how scopes are resolved. Since we do not
1609 // have a separate target mode for Symbian, but we expect the scope to resolve
1610 // on other platforms we base it entirely on the mkspec. This means that
1611 // using a mkspec starting with 'symbian*' will resolve both the 'symbian'
1612 // and the 'unix' (because of Open C) scopes to true.
1613 if(isForSymbian() && (x
== "symbian" || x
== "unix"))
1617 if((Option::target_mode
== Option::TARG_MACX_MODE
||
1618 Option::target_mode
== Option::TARG_UNIX_MODE
) && x
== "unix")
1619 return !isForSymbian();
1620 else if(Option::target_mode
== Option::TARG_MACX_MODE
&& x
== "macx")
1621 return !isForSymbian();
1622 else if(Option::target_mode
== Option::TARG_MAC9_MODE
&& x
== "mac9")
1623 return !isForSymbian();
1624 else if((Option::target_mode
== Option::TARG_MAC9_MODE
|| Option::target_mode
== Option::TARG_MACX_MODE
) &&
1626 return !isForSymbian();
1627 else if(Option::target_mode
== Option::TARG_WIN_MODE
&& x
== "win32")
1628 return !isForSymbian();
1629 QRegExp
re(x
, Qt::CaseSensitive
, QRegExp::Wildcard
);
1630 if((regex
&& re
.exactMatch(spec
)) || (!regex
&& spec
== x
))
1633 else if(spec
== "default") {
1634 static char *buffer
= NULL
;
1636 buffer
= (char *)malloc(1024);
1637 qmakeAddCacheClear(qmakeFreeCacheClear
, (void**)&buffer
);
1639 int l
= readlink(Option::mkfile::qmakespec
.toLatin1(), buffer
, 1024);
1643 if(r
.lastIndexOf('/') != -1)
1644 r
= r
.mid(r
.lastIndexOf('/') + 1);
1645 if((regex
&& re
.exactMatch(r
)) || (!regex
&& r
== x
))
1649 #elif defined(Q_OS_WIN)
1650 else if(spec
== "default") {
1651 // We can't resolve symlinks as they do on Unix, so configure.exe puts the source of the
1652 // qmake.conf at the end of the default/qmake.conf in the QMAKESPEC_ORG variable.
1653 const QStringList
&spec_org
= (place
? (*place
)["QMAKESPEC_ORIGINAL"]
1654 : vars
["QMAKESPEC_ORIGINAL"]);
1655 if (!spec_org
.isEmpty()) {
1656 spec
= spec_org
.at(0);
1657 int lastSlash
= spec
.lastIndexOf('/');
1659 spec
= spec
.mid(lastSlash
+ 1);
1660 if((regex
&& re
.exactMatch(spec
)) || (!regex
&& spec
== x
))
1667 const QStringList
&configs
= (place
? (*place
)["CONFIG"] : vars
["CONFIG"]);
1668 for(QStringList::ConstIterator it
= configs
.begin(); it
!= configs
.end(); ++it
) {
1669 if(((regex
&& re
.exactMatch((*it
))) || (!regex
&& (*it
) == x
)) && re
.exactMatch((*it
)))
1676 QMakeProject::doProjectTest(QString str
, QMap
<QString
, QStringList
> &place
)
1678 QString chk
= remove_quotes(str
);
1681 bool invert_test
= (chk
.left(1) == "!");
1686 int lparen
= chk
.indexOf('(');
1687 if(lparen
!= -1) { // if there is an lparen in the chk, it IS a function
1688 int rparen
= chk
.indexOf(')', lparen
);
1690 qmake_error_msg("Function missing right paren: " + chk
);
1692 QString func
= chk
.left(lparen
);
1693 test
= doProjectTest(func
, chk
.mid(lparen
+1, rparen
- lparen
- 1), place
);
1696 test
= isActiveConfig(chk
, true, &place
);
1704 QMakeProject::doProjectTest(QString func
, const QString
¶ms
,
1705 QMap
<QString
, QStringList
> &place
)
1707 return doProjectTest(func
, split_arg_list(params
), place
);
1710 QMakeProject::IncludeStatus
1711 QMakeProject::doProjectInclude(QString file
, uchar flags
, QMap
<QString
, QStringList
> &place
)
1713 enum { UnknownFormat
, ProFormat
, JSFormat
} format
= UnknownFormat
;
1714 if(flags
& IncludeFlagFeature
) {
1715 if(!file
.endsWith(Option::prf_ext
))
1716 file
+= Option::prf_ext
;
1717 if(file
.indexOf(Option::dir_sep
) == -1 || !QFile::exists(file
)) {
1718 static QStringList
*feature_roots
= 0;
1719 if(!feature_roots
) {
1720 init_symbian(base_vars
);
1721 feature_roots
= new QStringList(qmake_feature_paths(prop
));
1722 qmakeAddCacheClear(qmakeDeleteCacheClear_QStringList
, (void**)&feature_roots
);
1724 debug_msg(2, "Looking for feature '%s' in (%s)", file
.toLatin1().constData(),
1725 feature_roots
->join("::").toLatin1().constData());
1727 if(parser
.from_file
) {
1728 QFileInfo
currFile(parser
.file
), prfFile(file
);
1729 if(currFile
.fileName() == prfFile
.fileName()) {
1730 currFile
= QFileInfo(currFile
.canonicalFilePath());
1731 for(int root
= 0; root
< feature_roots
->size(); ++root
) {
1732 prfFile
= QFileInfo(feature_roots
->at(root
) +
1733 QDir::separator() + file
).canonicalFilePath();
1734 if(prfFile
== currFile
) {
1735 start_root
= root
+1;
1741 for(int root
= start_root
; root
< feature_roots
->size(); ++root
) {
1742 QString
prf(feature_roots
->at(root
) + QDir::separator() + file
);
1743 if(QFile::exists(prf
+ Option::js_ext
)) {
1745 file
= prf
+ Option::js_ext
;
1747 } else if(QFile::exists(prf
)) {
1753 if(format
== UnknownFormat
)
1754 return IncludeNoExist
;
1755 if(place
["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file
) != -1)
1756 return IncludeFeatureAlreadyLoaded
;
1757 place
["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file
);
1760 if(QDir::isRelativePath(file
)) {
1761 QStringList include_roots
;
1762 if(Option::output_dir
!= qmake_getpwd())
1763 include_roots
<< qmake_getpwd();
1764 include_roots
<< Option::output_dir
;
1765 for(int root
= 0; root
< include_roots
.size(); ++root
) {
1766 QString testName
= QDir::toNativeSeparators(include_roots
[root
]);
1767 if (!testName
.endsWith(QString(QDir::separator())))
1768 testName
+= QDir::separator();
1770 if(QFile::exists(testName
)) {
1776 if(format
== UnknownFormat
) {
1777 if(QFile::exists(file
)) {
1778 if(file
.endsWith(Option::js_ext
))
1783 return IncludeNoExist
;
1786 if(Option::mkfile::do_preprocess
) //nice to see this first..
1787 fprintf(stderr
, "#switching file %s(%s) - %s:%d\n", (flags
& IncludeFlagFeature
) ? "load" : "include",
1788 file
.toLatin1().constData(),
1789 parser
.file
.toLatin1().constData(), parser
.line_no
);
1790 debug_msg(1, "Project Parser: %s'ing file %s.", (flags
& IncludeFlagFeature
) ? "load" : "include",
1791 file
.toLatin1().constData());
1793 QString orig_file
= file
;
1794 int di
= file
.lastIndexOf(QDir::separator());
1795 QString oldpwd
= qmake_getpwd();
1797 if(!qmake_setpwd(file
.left(file
.lastIndexOf(QDir::separator())))) {
1798 fprintf(stderr
, "Cannot find directory: %s\n", file
.left(di
).toLatin1().constData());
1799 return IncludeFailure
;
1801 file
= file
.right(file
.length() - di
- 1);
1803 bool parsed
= false;
1804 parser_info pi
= parser
;
1805 if(format
== JSFormat
) {
1806 warn_msg(WarnParser
, "%s:%d: QtScript support disabled for %s.",
1807 pi
.file
.toLatin1().constData(), pi
.line_no
, orig_file
.toLatin1().constData());
1809 QStack
<ScopeBlock
> sc
= scope_blocks
;
1810 IteratorBlock
*it
= iterator
;
1811 FunctionBlock
*fu
= function
;
1812 if(flags
& (IncludeFlagNewProject
|IncludeFlagNewParser
)) {
1813 // The "project's variables" are used in other places (eg. export()) so it's not
1814 // possible to use "place" everywhere. Instead just set variables and grab them later
1815 QMakeProject
proj(this, &place
);
1816 if(flags
& IncludeFlagNewParser
) {
1818 if(proj
.doProjectInclude("default_pre", IncludeFlagFeature
, proj
.variables()) == IncludeNoExist
)
1819 proj
.doProjectInclude("default", IncludeFlagFeature
, proj
.variables());
1821 parsed
= proj
.read(file
, proj
.variables());
1823 parsed
= proj
.read(file
);
1825 place
= proj
.variables();
1827 parsed
= read(file
, place
);
1834 if(place
["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file
) == -1)
1835 place
["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file
);
1837 warn_msg(WarnParser
, "%s:%d: Failure to include file %s.",
1838 pi
.file
.toLatin1().constData(), pi
.line_no
, orig_file
.toLatin1().constData());
1841 qmake_setpwd(oldpwd
);
1843 return IncludeParseFailure
;
1844 return IncludeSuccess
;
1848 QMakeProject::doProjectExpand(QString func
, const QString
¶ms
,
1849 QMap
<QString
, QStringList
> &place
)
1851 return doProjectExpand(func
, split_arg_list(params
), place
);
1855 QMakeProject::doProjectExpand(QString func
, QStringList args
,
1856 QMap
<QString
, QStringList
> &place
)
1858 QList
<QStringList
> args_list
;
1859 for(int i
= 0; i
< args
.size(); ++i
) {
1860 QStringList arg
= split_value_list(args
[i
]), tmp
;
1861 for(int i
= 0; i
< arg
.size(); ++i
)
1862 tmp
+= doVariableReplaceExpand(arg
[i
], place
);;
1865 return doProjectExpand(func
, args_list
, place
);
1868 // defined in symbian generator
1869 extern QString
generate_test_uid(const QString
& target
);
1872 QMakeProject::doProjectExpand(QString func
, QList
<QStringList
> args_list
,
1873 QMap
<QString
, QStringList
> &place
)
1875 func
= func
.trimmed();
1876 if(replaceFunctions
.contains(func
)) {
1877 FunctionBlock
*defined
= replaceFunctions
[func
];
1878 function_blocks
.push(defined
);
1880 defined
->exec(args_list
, this, place
, ret
);
1881 Q_ASSERT(function_blocks
.pop() == defined
);
1885 QStringList args
; //why don't the builtin functions just use args_list? --Sam
1886 for(int i
= 0; i
< args_list
.size(); ++i
)
1887 args
+= args_list
[i
].join(QString(Option::field_sep
));
1889 ExpandFunc func_t
= qmake_expandFunctions().value(func
.toLower());
1890 debug_msg(1, "Running project expand: %s(%s) [%d]",
1891 func
.toLatin1().constData(), args
.join("::").toLatin1().constData(), func_t
);
1896 if(args
.count() < 1 || args
.count() > 3) {
1897 fprintf(stderr
, "%s:%d: member(var, start, end) requires three arguments.\n",
1898 parser
.file
.toLatin1().constData(), parser
.line_no
);
1901 const QStringList
&var
= values(args
.first(), place
);
1902 int start
= 0, end
= 0;
1903 if(args
.count() >= 2) {
1904 QString start_str
= args
[1];
1905 start
= start_str
.toInt(&ok
);
1907 if(args
.count() == 2) {
1908 int dotdot
= start_str
.indexOf("..");
1910 start
= start_str
.left(dotdot
).toInt(&ok
);
1912 end
= start_str
.mid(dotdot
+2).toInt(&ok
);
1916 fprintf(stderr
, "%s:%d: member() argument 2 (start) '%s' invalid.\n",
1917 parser
.file
.toLatin1().constData(), parser
.line_no
,
1918 start_str
.toLatin1().constData());
1921 if(args
.count() == 3)
1922 end
= args
[2].toInt(&ok
);
1924 fprintf(stderr
, "%s:%d: member() argument 3 (end) '%s' invalid.\n",
1925 parser
.file
.toLatin1().constData(), parser
.line_no
,
1926 args
[2].toLatin1().constData());
1931 start
+= var
.count();
1934 if(start
< 0 || start
>= var
.count() || end
< 0 || end
>= var
.count()) {
1936 } else if(start
< end
) {
1937 for(int i
= start
; i
<= end
&& (int)var
.count() >= i
; i
++)
1940 for(int i
= start
; i
>= end
&& (int)var
.count() >= i
&& i
>= 0; i
--)
1948 if(args
.count() != 1) {
1949 fprintf(stderr
, "%s:%d: %s(var) requires one argument.\n",
1950 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
1952 const QStringList
&var
= values(args
.first(), place
);
1953 if(!var
.isEmpty()) {
1954 if(func_t
== E_FIRST
)
1955 ret
= QStringList(var
[0]);
1957 ret
= QStringList(var
[var
.size()-1]);
1962 if(args
.count() < 1 || args
.count() > 2) {
1963 fprintf(stderr
, "%s:%d: cat(file) requires one argument.\n",
1964 parser
.file
.toLatin1().constData(), parser
.line_no
);
1966 QString file
= args
[0];
1967 file
= Option::fixPathToLocalOS(file
);
1969 bool singleLine
= true;
1970 if(args
.count() > 1)
1971 singleLine
= (args
[1].toLower() == "true");
1974 if(qfile
.open(QIODevice::ReadOnly
)) {
1975 QTextStream
stream(&qfile
);
1976 while(!stream
.atEnd()) {
1977 ret
+= split_value_list(stream
.readLine().trimmed());
1986 if(args
.count() != 2) {
1987 fprintf(stderr
, "%s:%d: fromfile(file, variable) requires two arguments.\n",
1988 parser
.file
.toLatin1().constData(), parser
.line_no
);
1990 QString file
= args
[0], seek_var
= args
[1];
1991 file
= Option::fixPathToLocalOS(file
);
1993 QMap
<QString
, QStringList
> tmp
;
1994 if(doProjectInclude(file
, IncludeFlagNewParser
, tmp
) == IncludeSuccess
) {
1995 if(tmp
.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
1996 QStringList
&out
= place
["QMAKE_INTERNAL_INCLUDED_FILES"];
1997 const QStringList
&in
= tmp
["QMAKE_INTERNAL_INCLUDED_FILES"];
1998 for(int i
= 0; i
< in
.size(); ++i
) {
1999 if(out
.indexOf(in
[i
]) == -1)
2003 ret
= tmp
[seek_var
];
2008 if(args
.count() < 1 || args
.count() > 2) {
2009 fprintf(stderr
, "%s:%d: eval(variable) requires one argument.\n",
2010 parser
.file
.toLatin1().constData(), parser
.line_no
);
2013 const QMap
<QString
, QStringList
> *source
= &place
;
2014 if(args
.count() == 2) {
2015 if(args
.at(1) == "Global") {
2017 } else if(args
.at(1) == "Local") {
2020 fprintf(stderr
, "%s:%d: unexpected source to eval.\n", parser
.file
.toLatin1().constData(),
2024 ret
+= source
->value(args
.at(0));
2030 tmp
.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x
++);
2031 ret
= QStringList(tmp
);
2032 QStringList
&lst
= (*((QMap
<QString
, QStringList
>*)&place
))[tmp
];
2034 for(QStringList::ConstIterator arg_it
= args
.begin();
2035 arg_it
!= args
.end(); ++arg_it
)
2036 lst
+= split_value_list((*arg_it
));
2039 if(args
.count() < 1) {
2040 fprintf(stderr
, "%s:%d: sprintf(format, ...) requires one argument.\n",
2041 parser
.file
.toLatin1().constData(), parser
.line_no
);
2043 QString tmp
= args
.at(0);
2044 for(int i
= 1; i
< args
.count(); ++i
)
2045 tmp
= tmp
.arg(args
.at(i
));
2046 ret
= split_value_list(tmp
);
2050 if(args
.count() < 1 || args
.count() > 4) {
2051 fprintf(stderr
, "%s:%d: join(var, glue, before, after) requires four"
2052 "arguments.\n", parser
.file
.toLatin1().constData(), parser
.line_no
);
2054 QString glue
, before
, after
;
2055 if(args
.count() >= 2)
2057 if(args
.count() >= 3)
2059 if(args
.count() == 4)
2061 const QStringList
&var
= values(args
.first(), place
);
2063 ret
= split_value_list(before
+ var
.join(glue
) + after
);
2067 if(args
.count() < 1 || args
.count() > 2) {
2068 fprintf(stderr
, "%s:%d split(var, sep) requires one or two arguments\n",
2069 parser
.file
.toLatin1().constData(), parser
.line_no
);
2071 QString sep
= QString(Option::field_sep
);
2072 if(args
.count() >= 2)
2074 QStringList var
= values(args
.first(), place
);
2075 for(QStringList::ConstIterator vit
= var
.begin(); vit
!= var
.end(); ++vit
) {
2076 QStringList lst
= (*vit
).split(sep
);
2077 for(QStringList::ConstIterator spltit
= lst
.begin(); spltit
!= lst
.end(); ++spltit
)
2085 bool regexp
= false;
2088 if(func_t
== E_SECTION
) {
2089 if(args
.count() != 3 && args
.count() != 4) {
2090 fprintf(stderr
, "%s:%d section(var, sep, begin, end) requires three argument\n",
2091 parser
.file
.toLatin1().constData(), parser
.line_no
);
2095 beg
= args
[2].toInt();
2096 if(args
.count() == 4)
2097 end
= args
[3].toInt();
2100 if(args
.count() != 1) {
2101 fprintf(stderr
, "%s:%d %s(var) requires one argument.\n",
2102 parser
.file
.toLatin1().constData(), parser
.line_no
, func
.toLatin1().constData());
2106 sep
= "[" + QRegExp::escape(Option::dir_sep
) + "/]";
2107 if(func_t
== E_DIRNAME
)
2114 const QStringList
&l
= values(var
, place
);
2115 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
2116 QString separator
= sep
;
2118 ret
+= (*it
).section(QRegExp(separator
), beg
, end
);
2120 ret
+= (*it
).section(separator
, beg
, end
);
2125 if(args
.count() != 2) {
2126 fprintf(stderr
, "%s:%d find(var, str) requires two arguments\n",
2127 parser
.file
.toLatin1().constData(), parser
.line_no
);
2129 QRegExp
regx(args
[1]);
2130 const QStringList
&var
= values(args
.first(), place
);
2131 for(QStringList::ConstIterator vit
= var
.begin();
2132 vit
!= var
.end(); ++vit
) {
2133 if(regx
.indexIn(*vit
) != -1)
2139 if(args
.count() < 1 || args
.count() > 2) {
2140 fprintf(stderr
, "%s:%d system(execut) requires one argument.\n",
2141 parser
.file
.toLatin1().constData(), parser
.line_no
);
2143 QMakeProjectEnv
env(place
);
2145 bool singleLine
= true;
2146 if(args
.count() > 1)
2147 singleLine
= (args
[1].toLower() == "true");
2149 FILE *proc
= QT_POPEN(args
[0].toLatin1(), "r");
2150 while(proc
&& !feof(proc
)) {
2151 int read_in
= int(fread(buff
, 1, 255, proc
));
2154 for(int i
= 0; i
< read_in
; i
++) {
2155 if((singleLine
&& buff
[i
] == '\n') || buff
[i
] == '\t')
2158 buff
[read_in
] = '\0';
2161 ret
+= split_value_list(output
);
2167 if(args
.count() != 1) {
2168 fprintf(stderr
, "%s:%d unique(var) requires one argument.\n",
2169 parser
.file
.toLatin1().constData(), parser
.line_no
);
2171 const QStringList
&var
= values(args
.first(), place
);
2172 for(int i
= 0; i
< var
.count(); i
++) {
2173 if(!ret
.contains(var
[i
]))
2181 case E_ESCAPE_EXPAND
: {
2182 for(int i
= 0; i
< args
.size(); ++i
) {
2183 QChar
*i_data
= args
[i
].data();
2184 int i_len
= args
[i
].length();
2185 for(int x
= 0; x
< i_len
; ++x
) {
2186 if(*(i_data
+x
) == '\\' && x
< i_len
-1) {
2187 if(*(i_data
+x
+1) == '\\') {
2192 } mapped_quotes
[] = {
2198 for(int i
= 0; mapped_quotes
[i
].in
; ++i
) {
2199 if(*(i_data
+x
+1) == mapped_quotes
[i
].in
) {
2200 *(i_data
+x
) = mapped_quotes
[i
].out
;
2202 memmove(i_data
+x
+1, i_data
+x
+2, (i_len
-x
-2)*sizeof(QChar
));
2210 ret
.append(QString(i_data
, i_len
));
2214 for(int i
= 0; i
< args
.size(); ++i
)
2215 ret
+= QRegExp::escape(args
[i
]);
2219 for(int i
= 0; i
< args
.size(); ++i
) {
2220 if(func_t
== E_UPPER
)
2221 ret
+= args
[i
].toUpper();
2223 ret
+= args
[i
].toLower();
2227 if(args
.count() != 1 && args
.count() != 2) {
2228 fprintf(stderr
, "%s:%d files(pattern) requires one argument.\n",
2229 parser
.file
.toLatin1().constData(), parser
.line_no
);
2231 bool recursive
= false;
2232 if(args
.count() == 2)
2233 recursive
= (args
[1].toLower() == "true" || args
[1].toInt());
2235 QString r
= Option::fixPathToLocalOS(args
[0]);
2236 int slash
= r
.lastIndexOf(QDir::separator());
2238 dirs
.append(r
.left(slash
));
2244 const QRegExp
regex(r
, Qt::CaseSensitive
, QRegExp::Wildcard
);
2245 for(int d
= 0; d
< dirs
.count(); d
++) {
2246 QString dir
= dirs
[d
];
2247 if(!dir
.isEmpty() && !dir
.endsWith(Option::dir_sep
))
2251 for(int i
= 0; i
< (int)qdir
.count(); ++i
) {
2252 if(qdir
[i
] == "." || qdir
[i
] == "..")
2254 QString fname
= dir
+ qdir
[i
];
2255 if(QFileInfo(fname
).isDir()) {
2259 if(regex
.exactMatch(qdir
[i
]))
2266 if(args
.count() != 1) {
2267 fprintf(stderr
, "%s:%d prompt(question) requires one argument.\n",
2268 parser
.file
.toLatin1().constData(), parser
.line_no
);
2269 } else if(projectFile() == "-") {
2270 fprintf(stderr
, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
2271 parser
.file
.toLatin1().constData(), parser
.line_no
);
2273 QString msg
= fixEnvVariables(args
.first());
2274 if(!msg
.endsWith("?"))
2276 fprintf(stderr
, "Project %s: %s ", func
.toUpper().toLatin1().constData(),
2277 msg
.toLatin1().constData());
2280 if(qfile
.open(stdin
, QIODevice::ReadOnly
)) {
2281 QTextStream
t(&qfile
);
2282 ret
= split_value_list(t
.readLine());
2287 if(args
.count() != 3 ) {
2288 fprintf(stderr
, "%s:%d replace(var, before, after) requires three arguments\n",
2289 parser
.file
.toLatin1().constData(), parser
.line_no
);
2291 const QRegExp
before( args
[1] );
2292 const QString
after( args
[2] );
2293 QStringList var
= values(args
.first(), place
);
2294 for(QStringList::Iterator it
= var
.begin(); it
!= var
.end(); ++it
)
2295 ret
+= it
->replace(before
, after
);
2299 if(args
.count() != 1) {
2300 fprintf(stderr
, "%s:%d: size(var) requires one argument.\n",
2301 parser
.file
.toLatin1().constData(), parser
.line_no
);
2303 //QString target = args[0];
2304 int size
= values(args
[0]).size();
2305 ret
+= QString::number(size
);
2308 case E_GENERATE_UID
:
2309 if (args
.count() != 1) {
2310 fprintf(stderr
, "%s:%d: generate_uid(var) requires one argument.\n",
2311 parser
.file
.toLatin1().constData(), parser
.line_no
);
2313 ret
+= generate_test_uid(args
.first());
2317 fprintf(stderr
, "%s:%d: Unknown replace function: %s\n",
2318 parser
.file
.toLatin1().constData(), parser
.line_no
,
2319 func
.toLatin1().constData());
2326 QMakeProject::doProjectTest(QString func
, QStringList args
, QMap
<QString
, QStringList
> &place
)
2328 QList
<QStringList
> args_list
;
2329 for(int i
= 0; i
< args
.size(); ++i
) {
2330 QStringList arg
= split_value_list(args
[i
]), tmp
;
2331 for(int i
= 0; i
< arg
.size(); ++i
)
2332 tmp
+= doVariableReplaceExpand(arg
[i
], place
);
2335 return doProjectTest(func
, args_list
, place
);
2339 QMakeProject::doProjectTest(QString func
, QList
<QStringList
> args_list
, QMap
<QString
, QStringList
> &place
)
2341 func
= func
.trimmed();
2343 if(testFunctions
.contains(func
)) {
2344 FunctionBlock
*defined
= testFunctions
[func
];
2346 function_blocks
.push(defined
);
2347 defined
->exec(args_list
, this, place
, ret
);
2348 Q_ASSERT(function_blocks
.pop() == defined
);
2353 if(ret
.first() == "true") {
2355 } else if(ret
.first() == "false") {
2359 int val
= ret
.first().toInt(&ok
);
2362 fprintf(stderr
, "%s:%d Unexpected return value from test %s [%s].\n",
2363 parser
.file
.toLatin1().constData(),
2364 parser
.line_no
, func
.toLatin1().constData(),
2365 ret
.join("::").toLatin1().constData());
2372 QStringList args
; //why don't the builtin functions just use args_list? --Sam
2373 for(int i
= 0; i
< args_list
.size(); ++i
)
2374 args
+= args_list
[i
].join(QString(Option::field_sep
));
2376 TestFunc func_t
= qmake_testFunctions().value(func
);
2377 debug_msg(1, "Running project test: %s(%s) [%d]",
2378 func
.toLatin1().constData(), args
.join("::").toLatin1().constData(), func_t
);
2382 return doProjectCheckReqs(args
, place
);
2384 case T_GREATERTHAN
: {
2385 if(args
.count() != 2) {
2386 fprintf(stderr
, "%s:%d: %s(variable, value) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2387 parser
.line_no
, func
.toLatin1().constData());
2390 QString
rhs(args
[1]), lhs(values(args
[0], place
).join(QString(Option::field_sep
)));
2392 int rhs_int
= rhs
.toInt(&ok
);
2393 if(ok
) { // do integer compare
2394 int lhs_int
= lhs
.toInt(&ok
);
2396 if(func_t
== T_GREATERTHAN
)
2397 return lhs_int
> rhs_int
;
2398 return lhs_int
< rhs_int
;
2401 if(func_t
== T_GREATERTHAN
)
2405 if(args
.count() != 1) {
2406 fprintf(stderr
, "%s:%d: if(condition) requires one argument.\n", parser
.file
.toLatin1().constData(),
2410 const QString cond
= args
.first();
2411 const QChar
*d
= cond
.unicode();
2413 bool ret
= true, or_op
= false;
2415 for(int d_off
= 0, parens
= 0, d_len
= cond
.size(); d_off
< d_len
; ++d_off
) {
2416 if(!quote
.isNull()) {
2417 if(*(d
+d_off
) == quote
)
2419 } else if(*(d
+d_off
) == '(') {
2421 } else if(*(d
+d_off
) == ')') {
2423 } else if(*(d
+d_off
) == '"' /*|| *(d+d_off) == '\''*/) {
2426 if(!parens
&& quote
.isNull() && (*(d
+d_off
) == QLatin1Char(':') || *(d
+d_off
) == QLatin1Char('|') || d_off
== d_len
-1)) {
2427 if(d_off
== d_len
-1)
2429 if(!test
.isEmpty()) {
2430 const bool success
= doProjectTest(test
, place
);
2433 ret
= ret
|| success
;
2435 ret
= ret
&& success
;
2437 if(*(d
+d_off
) == QLatin1Char(':')) {
2439 } else if(*(d
+d_off
) == QLatin1Char('|')) {
2448 if(args
.count() != 2) {
2449 fprintf(stderr
, "%s:%d: %s(variable, value) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2450 parser
.line_no
, func
.toLatin1().constData());
2453 return values(args
[0], place
).join(QString(Option::field_sep
)) == args
[1];
2455 if(args
.count() != 1) {
2456 fprintf(stderr
, "%s:%d: exists(file) requires one argument.\n", parser
.file
.toLatin1().constData(),
2460 QString file
= args
.first();
2461 file
= Option::fixPathToLocalOS(file
);
2463 if(QFile::exists(file
))
2465 //regular expression I guess
2466 QString dirstr
= qmake_getpwd();
2467 int slsh
= file
.lastIndexOf(Option::dir_sep
);
2469 dirstr
= file
.left(slsh
+1);
2470 file
= file
.right(file
.length() - slsh
- 1);
2472 return QDir(dirstr
).entryList(QStringList(file
)).count(); }
2474 if(args
.count() != 1) {
2475 fprintf(stderr
, "%s:%d: export(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2479 for(int i
= 0; i
< function_blocks
.size(); ++i
) {
2480 FunctionBlock
*f
= function_blocks
.at(i
);
2481 f
->vars
[args
[0]] = values(args
[0], place
);
2482 if(!i
&& f
->calling_place
)
2483 (*f
->calling_place
)[args
[0]] = values(args
[0], place
);
2487 if(args
.count() != 1) {
2488 fprintf(stderr
, "%s:%d: clear(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2492 if(!place
.contains(args
[0]))
2494 place
[args
[0]].clear();
2497 if(args
.count() != 1) {
2498 fprintf(stderr
, "%s:%d: unset(variable) requires one argument.\n", parser
.file
.toLatin1().constData(),
2502 if(!place
.contains(args
[0]))
2504 place
.remove(args
[0]);
2507 if(args
.count() < 1 && 0) {
2508 fprintf(stderr
, "%s:%d: eval(project) requires one argument.\n", parser
.file
.toLatin1().constData(),
2512 QString project
= args
.join(" ");
2513 parser_info pi
= parser
;
2514 parser
.from_file
= false;
2515 parser
.file
= "(eval)";
2517 QTextStream
t(&project
, QIODevice::ReadOnly
);
2518 bool ret
= read(t
, place
);
2522 if(args
.count() < 1 || args
.count() > 2) {
2523 fprintf(stderr
, "%s:%d: CONFIG(config) requires one argument.\n", parser
.file
.toLatin1().constData(),
2527 if(args
.count() == 1)
2528 return isActiveConfig(args
[0]);
2529 const QStringList mutuals
= args
[1].split('|');
2530 const QStringList
&configs
= values("CONFIG", place
);
2531 for(int i
= configs
.size()-1; i
>= 0; i
--) {
2532 for(int mut
= 0; mut
< mutuals
.count(); mut
++) {
2533 if(configs
[i
] == mutuals
[mut
].trimmed())
2534 return (configs
[i
] == args
[0]);
2539 bool setup_env
= true;
2540 if(args
.count() < 1 || args
.count() > 2) {
2541 fprintf(stderr
, "%s:%d: system(exec) requires one argument.\n", parser
.file
.toLatin1().constData(),
2545 if(args
.count() == 2) {
2546 const QString sarg
= args
[1];
2547 setup_env
= (sarg
.toLower() == "true" || sarg
.toInt());
2549 QMakeProjectEnv env
;
2552 bool ret
= system(args
[0].toLatin1().constData()) == 0;
2555 if(function_blocks
.isEmpty()) {
2556 fprintf(stderr
, "%s:%d unexpected return()\n",
2557 parser
.file
.toLatin1().constData(), parser
.line_no
);
2559 FunctionBlock
*f
= function_blocks
.top();
2560 f
->cause_return
= true;
2561 if(args_list
.count() >= 1)
2562 f
->return_value
+= args_list
[0];
2567 iterator
->cause_break
= true;
2568 else if(!scope_blocks
.isEmpty())
2569 scope_blocks
.top().ignore
= true;
2571 fprintf(stderr
, "%s:%d unexpected break()\n",
2572 parser
.file
.toLatin1().constData(), parser
.line_no
);
2576 iterator
->cause_next
= true;
2578 fprintf(stderr
, "%s:%d unexpected next()\n",
2579 parser
.file
.toLatin1().constData(), parser
.line_no
);
2582 if(args
.count() < 1 || args
.count() > 2) {
2583 fprintf(stderr
, "%s:%d: defined(function) requires one argument.\n",
2584 parser
.file
.toLatin1().constData(), parser
.line_no
);
2586 if(args
.count() > 1) {
2587 if(args
[1] == "test")
2588 return testFunctions
.contains(args
[0]);
2589 else if(args
[1] == "replace")
2590 return replaceFunctions
.contains(args
[0]);
2591 fprintf(stderr
, "%s:%d: defined(function, type): unexpected type [%s].\n",
2592 parser
.file
.toLatin1().constData(), parser
.line_no
,
2593 args
[1].toLatin1().constData());
2595 if(replaceFunctions
.contains(args
[0]) || testFunctions
.contains(args
[0]))
2601 if(args
.count() < 2 || args
.count() > 3) {
2602 fprintf(stderr
, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n",
2603 parser
.file
.toLatin1().constData(), parser
.line_no
);
2606 QRegExp
regx(args
[1]);
2607 const QStringList
&l
= values(args
[0], place
);
2608 if(args
.count() == 2) {
2609 for(int i
= 0; i
< l
.size(); ++i
) {
2610 const QString val
= l
[i
];
2611 if(regx
.exactMatch(val
) || val
== args
[1])
2615 const QStringList mutuals
= args
[2].split('|');
2616 for(int i
= l
.size()-1; i
>= 0; i
--) {
2617 const QString val
= l
[i
];
2618 for(int mut
= 0; mut
< mutuals
.count(); mut
++) {
2619 if(val
== mutuals
[mut
].trimmed())
2620 return (regx
.exactMatch(val
) || val
== args
[1]);
2626 if(args
.count() < 2 || args
.count() > 3) {
2627 fprintf(stderr
, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
2628 parser
.file
.toLatin1().constData(), parser
.line_no
);
2633 QMap
<QString
, QStringList
> tmp
;
2634 if(doProjectInclude(Option::fixPathToLocalOS(args
[0]), IncludeFlagNewParser
, tmp
) == IncludeSuccess
) {
2635 if(tmp
.contains("QMAKE_INTERNAL_INCLUDED_FILES")) {
2636 QStringList
&out
= place
["QMAKE_INTERNAL_INCLUDED_FILES"];
2637 const QStringList
&in
= tmp
["QMAKE_INTERNAL_INCLUDED_FILES"];
2638 for(int i
= 0; i
< in
.size(); ++i
) {
2639 if(out
.indexOf(in
[i
]) == -1)
2643 if(args
.count() == 2) {
2644 ret
= tmp
.contains(args
[1]);
2646 QRegExp
regx(args
[2]);
2647 const QStringList
&l
= tmp
[args
[1]];
2648 for(QStringList::ConstIterator it
= l
.begin(); it
!= l
.end(); ++it
) {
2649 if(regx
.exactMatch((*it
)) || (*it
) == args
[2]) {
2658 if(args
.count() != 2 && args
.count() != 3) {
2659 fprintf(stderr
, "%s:%d: count(var, count) requires two arguments.\n", parser
.file
.toLatin1().constData(),
2663 if(args
.count() == 3) {
2664 QString comp
= args
[2];
2665 if(comp
== ">" || comp
== "greaterThan")
2666 return values(args
[0], place
).count() > args
[1].toInt();
2668 return values(args
[0], place
).count() >= args
[1].toInt();
2669 if(comp
== "<" || comp
== "lessThan")
2670 return values(args
[0], place
).count() < args
[1].toInt();
2672 return values(args
[0], place
).count() <= args
[1].toInt();
2673 if(comp
== "equals" || comp
== "isEqual" || comp
== "=" || comp
== "==")
2674 return values(args
[0], place
).count() == args
[1].toInt();
2675 fprintf(stderr
, "%s:%d: unexpected modifier to count(%s)\n", parser
.file
.toLatin1().constData(),
2676 parser
.line_no
, comp
.toLatin1().constData());
2679 return values(args
[0], place
).count() == args
[1].toInt();
2681 if(args
.count() != 1) {
2682 fprintf(stderr
, "%s:%d: isEmpty(var) requires one argument.\n", parser
.file
.toLatin1().constData(),
2686 return values(args
[0], place
).isEmpty();
2690 const bool include_statement
= (func_t
== T_INCLUDE
);
2691 bool ignore_error
= false;
2692 if(args
.count() >= 2) {
2693 if(func_t
== T_INCLUDE
) {
2694 parseInto
= args
[1];
2695 if (args
.count() == 3){
2696 QString sarg
= args
[2];
2697 if (sarg
.toLower() == "true" || sarg
.toInt())
2698 ignore_error
= true;
2701 QString sarg
= args
[1];
2702 ignore_error
= (sarg
.toLower() == "true" || sarg
.toInt());
2704 } else if(args
.count() != 1) {
2705 QString func_desc
= "load(feature)";
2706 if(include_statement
)
2707 func_desc
= "include(file)";
2708 fprintf(stderr
, "%s:%d: %s requires one argument.\n", parser
.file
.toLatin1().constData(),
2709 parser
.line_no
, func_desc
.toLatin1().constData());
2712 QString file
= args
.first();
2713 file
= Option::fixPathToLocalOS(file
);
2714 uchar flags
= IncludeFlagNone
;
2715 if(!include_statement
)
2716 flags
|= IncludeFlagFeature
;
2717 IncludeStatus stat
= IncludeFailure
;
2718 if(!parseInto
.isEmpty()) {
2719 QMap
<QString
, QStringList
> symbols
;
2720 stat
= doProjectInclude(file
, flags
|IncludeFlagNewProject
, symbols
);
2721 if(stat
== IncludeSuccess
) {
2722 QMap
<QString
, QStringList
> out_place
;
2723 for(QMap
<QString
, QStringList
>::ConstIterator it
= place
.begin(); it
!= place
.end(); ++it
) {
2724 const QString var
= it
.key();
2725 if(var
!= parseInto
&& !var
.startsWith(parseInto
+ "."))
2726 out_place
.insert(var
, it
.value());
2728 for(QMap
<QString
, QStringList
>::ConstIterator it
= symbols
.begin(); it
!= symbols
.end(); ++it
) {
2729 const QString var
= it
.key();
2730 if(!var
.startsWith("."))
2731 out_place
.insert(parseInto
+ "." + it
.key(), it
.value());
2736 stat
= doProjectInclude(file
, flags
, place
);
2738 if(stat
== IncludeFeatureAlreadyLoaded
) {
2739 warn_msg(WarnParser
, "%s:%d: Duplicate of loaded feature %s",
2740 parser
.file
.toLatin1().constData(), parser
.line_no
, file
.toLatin1().constData());
2741 } else if(stat
== IncludeNoExist
&& !ignore_error
) {
2742 warn_msg(WarnAll
, "%s:%d: Unable to find file for inclusion %s",
2743 parser
.file
.toLatin1().constData(), parser
.line_no
, file
.toLatin1().constData());
2745 } else if(stat
>= IncludeFailure
) {
2747 printf("Project LOAD(): Feature %s cannot be found.\n", file
.toLatin1().constData());
2749 #if defined(QT_BUILD_QMAKE_LIBRARY)
2759 if(args
.count() != 2) {
2760 fprintf(stderr
, "%s:%d: debug(level, message) requires one argument.\n", parser
.file
.toLatin1().constData(),
2764 QString msg
= fixEnvVariables(args
[1]);
2765 debug_msg(args
[0].toInt(), "Project DEBUG: %s", msg
.toLatin1().constData());
2770 if(args
.count() != 1) {
2771 fprintf(stderr
, "%s:%d: %s(message) requires one argument.\n", parser
.file
.toLatin1().constData(),
2772 parser
.line_no
, func
.toLatin1().constData());
2775 QString msg
= fixEnvVariables(args
.first());
2776 fprintf(stderr
, "Project %s: %s\n", func
.toUpper().toLatin1().constData(), msg
.toLatin1().constData());
2778 #if defined(QT_BUILD_QMAKE_LIBRARY)
2785 fprintf(stderr
, "%s:%d: Unknown test function: %s\n", parser
.file
.toLatin1().constData(), parser
.line_no
,
2786 func
.toLatin1().constData());
2792 QMakeProject::doProjectCheckReqs(const QStringList
&deps
, QMap
<QString
, QStringList
> &place
)
2795 for(QStringList::ConstIterator it
= deps
.begin(); it
!= deps
.end(); ++it
) {
2796 bool test
= doProjectTest((*it
), place
);
2798 debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
2799 parser
.file
.toLatin1().constData(), parser
.line_no
,
2800 (*it
).toLatin1().constData());
2801 place
["QMAKE_FAILED_REQUIREMENTS"].append((*it
));
2809 QMakeProject::test(const QString
&v
)
2811 QMap
<QString
, QStringList
> tmp
= vars
;
2812 return doProjectTest(v
, tmp
);
2816 QMakeProject::test(const QString
&func
, const QList
<QStringList
> &args
)
2818 QMap
<QString
, QStringList
> tmp
= vars
;
2819 return doProjectTest(func
, args
, tmp
);
2823 QMakeProject::expand(const QString
&str
)
2826 QMap
<QString
, QStringList
> tmp
= vars
;
2827 const QStringList ret
= doVariableReplaceExpand(str
, tmp
, &ok
);
2830 return QStringList();
2834 QMakeProject::expand(const QString
&func
, const QList
<QStringList
> &args
)
2836 QMap
<QString
, QStringList
> tmp
= vars
;
2837 return doProjectExpand(func
, args
, tmp
);
2841 QMakeProject::doVariableReplace(QString
&str
, QMap
<QString
, QStringList
> &place
)
2844 str
= doVariableReplaceExpand(str
, place
, &ret
).join(QString(Option::field_sep
));
2849 QMakeProject::doVariableReplaceExpand(const QString
&str
, QMap
<QString
, QStringList
> &place
, bool *ok
)
2857 const ushort LSQUARE
= '[';
2858 const ushort RSQUARE
= ']';
2859 const ushort LCURLY
= '{';
2860 const ushort RCURLY
= '}';
2861 const ushort LPAREN
= '(';
2862 const ushort RPAREN
= ')';
2863 const ushort DOLLAR
= '$';
2864 const ushort SLASH
= '\\';
2865 const ushort UNDERSCORE
= '_';
2866 const ushort DOT
= '.';
2867 const ushort SPACE
= ' ';
2868 const ushort TAB
= '\t';
2869 const ushort SINGLEQUOTE
= '\'';
2870 const ushort DOUBLEQUOTE
= '"';
2872 ushort unicode
, quote
= 0;
2873 const QChar
*str_data
= str
.data();
2874 const int str_len
= str
.length();
2881 for(int i
= 0; i
< str_len
; ++i
) {
2882 unicode
= str_data
[i
].unicode();
2883 const int start_var
= i
;
2884 if(unicode
== DOLLAR
&& str_len
> i
+2) {
2885 unicode
= str_data
[++i
].unicode();
2886 if(unicode
== DOLLAR
) {
2890 enum { VAR
, ENVIRON
, FUNCTION
, PROPERTY
} var_type
= VAR
;
2891 unicode
= str_data
[++i
].unicode();
2892 if(unicode
== LSQUARE
) {
2893 unicode
= str_data
[++i
].unicode();
2895 var_type
= PROPERTY
;
2896 } else if(unicode
== LCURLY
) {
2897 unicode
= str_data
[++i
].unicode();
2900 } else if(unicode
== LPAREN
) {
2901 unicode
= str_data
[++i
].unicode();
2906 if(!(unicode
& (0xFF<<8)) &&
2907 unicode
!= DOT
&& unicode
!= UNDERSCORE
&&
2908 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
2909 (unicode
< 'a' || unicode
> 'z') && (unicode
< 'A' || unicode
> 'Z') &&
2910 (unicode
< '0' || unicode
> '9'))
2912 var
.append(QChar(unicode
));
2915 unicode
= str_data
[i
].unicode();
2916 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
2918 if(var_type
== VAR
&& unicode
== LPAREN
) {
2919 var_type
= FUNCTION
;
2924 unicode
= str_data
[i
].unicode();
2925 if(unicode
== LPAREN
) {
2927 } else if(unicode
== RPAREN
) {
2932 args
.append(QChar(unicode
));
2935 unicode
= str_data
[i
].unicode();
2938 // at this point i is pointing to the 'next' character (which is in unicode)
2939 // this might actually be a term character since you can do $${func()}
2942 if(unicode
!= term
) {
2943 qmake_error_msg("Missing " + QString(term
) + " terminator [found " + (unicode
?QString(unicode
):QString("end-of-line")) + "]");
2946 return QStringList();
2949 // move the 'cursor' back to the last char of the thing we were looking at
2952 // since i never points to the 'next' character, there is no reason for this to be set
2955 QStringList replacement
;
2956 if(var_type
== ENVIRON
) {
2957 replacement
= split_value_list(QString::fromLocal8Bit(qgetenv(var
.toLatin1().constData())));
2958 } else if(var_type
== PROPERTY
) {
2960 replacement
= split_value_list(prop
->value(var
));
2961 } else if(var_type
== FUNCTION
) {
2962 replacement
= doProjectExpand(var
, args
, place
);
2963 } else if(var_type
== VAR
) {
2964 replacement
= values(var
, place
);
2966 if(!(replaced
++) && start_var
)
2967 current
= str
.left(start_var
);
2968 if(!replacement
.isEmpty()) {
2970 current
+= replacement
.join(QString(Option::field_sep
));
2972 current
+= replacement
.takeFirst();
2973 if(!replacement
.isEmpty()) {
2974 if(!current
.isEmpty())
2975 ret
.append(current
);
2976 current
= replacement
.takeLast();
2977 if(!replacement
.isEmpty())
2982 debug_msg(2, "Project Parser [var replace]: %s -> %s",
2983 str
.toLatin1().constData(), var
.toLatin1().constData(),
2984 replacement
.join("::").toLatin1().constData());
2987 current
.append("$");
2990 if(quote
&& unicode
== quote
) {
2993 } else if(unicode
== SLASH
) {
2994 bool escape
= false;
2995 const char *symbols
= "[]{}()$\\'\"";
2996 for(const char *s
= symbols
; *s
; ++s
) {
2997 if(str_data
[i
+1].unicode() == (ushort
)*s
) {
3001 current
= str
.left(start_var
);
3002 current
.append(str
.at(i
));
3006 if(escape
|| !replaced
)
3008 } else if(!quote
&& (unicode
== SINGLEQUOTE
|| unicode
== DOUBLEQUOTE
)) {
3011 if(!(replaced
++) && i
)
3012 current
= str
.left(i
);
3013 } else if(!quote
&& (unicode
== SPACE
|| unicode
== TAB
)) {
3015 if(!current
.isEmpty()) {
3016 ret
.append(current
);
3020 if(replaced
&& unicode
)
3021 current
.append(QChar(unicode
));
3024 ret
= QStringList(str
);
3025 else if(!current
.isEmpty())
3026 ret
.append(current
);
3027 //qDebug() << "REPLACE" << str << ret;
3031 QStringList
&QMakeProject::values(const QString
&_var
, QMap
<QString
, QStringList
> &place
)
3033 QString var
= varMap(_var
);
3034 if(var
== QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token)
3035 var
= ".BUILTIN." + var
;
3036 place
[var
] = QStringList(QLatin1String("\t"));
3037 } else if(var
== QLatin1String("LITERAL_DOLLAR")) { //a real $
3038 var
= ".BUILTIN." + var
;
3039 place
[var
] = QStringList(QLatin1String("$"));
3040 } else if(var
== QLatin1String("LITERAL_HASH")) { //a real #
3041 var
= ".BUILTIN." + var
;
3042 place
[var
] = QStringList("#");
3043 } else if(var
== QLatin1String("OUT_PWD")) { //the out going dir
3044 var
= ".BUILTIN." + var
;
3045 place
[var
] = QStringList(Option::output_dir
);
3046 } else if(var
== QLatin1String("PWD") || //current working dir (of _FILE_)
3047 var
== QLatin1String("IN_PWD")) {
3048 var
= ".BUILTIN." + var
;
3049 place
[var
] = QStringList(qmake_getpwd());
3050 } else if(var
== QLatin1String("DIR_SEPARATOR")) {
3051 var
= ".BUILTIN." + var
;
3052 place
[var
] = QStringList(Option::dir_sep
);
3053 } else if(var
== QLatin1String("DIRLIST_SEPARATOR")) {
3054 var
= ".BUILTIN." + var
;
3055 place
[var
] = QStringList(Option::dirlist_sep
);
3056 } else if(var
== QLatin1String("_LINE_")) { //parser line number
3057 var
= ".BUILTIN." + var
;
3058 place
[var
] = QStringList(QString::number(parser
.line_no
));
3059 } else if(var
== QLatin1String("_FILE_")) { //parser file
3060 var
= ".BUILTIN." + var
;
3061 place
[var
] = QStringList(parser
.file
);
3062 } else if(var
== QLatin1String("_DATE_")) { //current date/time
3063 var
= ".BUILTIN." + var
;
3064 place
[var
] = QStringList(QDateTime::currentDateTime().toString());
3065 } else if(var
== QLatin1String("_PRO_FILE_")) {
3066 var
= ".BUILTIN." + var
;
3067 place
[var
] = QStringList(pfile
);
3068 } else if(var
== QLatin1String("_PRO_FILE_PWD_")) {
3069 var
= ".BUILTIN." + var
;
3070 place
[var
] = QStringList(QFileInfo(pfile
).absolutePath());
3071 } else if(var
== QLatin1String("_QMAKE_CACHE_")) {
3072 var
= ".BUILTIN." + var
;
3073 if(Option::mkfile::do_cache
)
3074 place
[var
] = QStringList(Option::mkfile::cachefile
);
3075 } else if(var
== QLatin1String("TEMPLATE")) {
3076 if(!Option::user_template
.isEmpty()) {
3077 var
= ".BUILTIN.USER." + var
;
3078 place
[var
] = QStringList(Option::user_template
);
3079 } else if(!place
[var
].isEmpty()) {
3080 QString orig_template
= place
["TEMPLATE"].first(), real_template
;
3081 if(!Option::user_template_prefix
.isEmpty() && !orig_template
.startsWith(Option::user_template_prefix
))
3082 real_template
= Option::user_template_prefix
+ orig_template
;
3083 if(real_template
.endsWith(".t"))
3084 real_template
= real_template
.left(real_template
.length()-2);
3085 if(!real_template
.isEmpty()) {
3086 var
= ".BUILTIN." + var
;
3087 place
[var
] = QStringList(real_template
);
3090 var
= ".BUILTIN." + var
;
3091 place
[var
] = QStringList("app");
3093 } else if(var
.startsWith(QLatin1String("QMAKE_HOST."))) {
3094 QString ret
, type
= var
.mid(11);
3095 #if defined(Q_OS_WIN32)
3098 } else if(type
== "name") {
3099 DWORD name_length
= 1024;
3101 if (GetComputerName(name
, &name_length
))
3102 ret
= QString::fromWCharArray(name
);
3103 } else if(type
== "version" || type
== "version_string") {
3104 QSysInfo::WinVersion ver
= QSysInfo::WindowsVersion
;
3105 if(type
== "version")
3106 ret
= QString::number(ver
);
3107 else if(ver
== QSysInfo::WV_Me
)
3109 else if(ver
== QSysInfo::WV_95
)
3111 else if(ver
== QSysInfo::WV_98
)
3113 else if(ver
== QSysInfo::WV_NT
)
3115 else if(ver
== QSysInfo::WV_2000
)
3117 else if(ver
== QSysInfo::WV_2000
)
3119 else if(ver
== QSysInfo::WV_XP
)
3121 else if(ver
== QSysInfo::WV_VISTA
)
3125 } else if(type
== "arch") {
3127 GetSystemInfo(&info
);
3128 switch(info
.wProcessorArchitecture
) {
3129 #ifdef PROCESSOR_ARCHITECTURE_AMD64
3130 case PROCESSOR_ARCHITECTURE_AMD64
:
3134 case PROCESSOR_ARCHITECTURE_INTEL
:
3137 case PROCESSOR_ARCHITECTURE_IA64
:
3138 #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
3139 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
:
3148 #elif defined(Q_OS_UNIX)
3149 struct utsname name
;
3153 else if(type
== "name")
3154 ret
= name
.nodename
;
3155 else if(type
== "version")
3157 else if(type
== "version_string")
3159 else if(type
== "arch")
3163 var
= ".BUILTIN.HOST." + type
;
3164 place
[var
] = QStringList(ret
);
3165 } else if (var
== QLatin1String("QMAKE_DIR_SEP")) {
3166 if (place
[var
].isEmpty())
3167 return values("DIR_SEPARATOR", place
);
3168 } else if (var
== QLatin1String("EPOCROOT")) {
3169 if (place
[var
].isEmpty())
3170 place
[var
] = QStringList(epocRoot());
3172 //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::")));