1 /****************************************************************************
2 ** $Id: qt/project.cpp 3.3.8 edited Jan 11 14:37 $
3 **
4 ** Implementation of QMakeProject class.
5 **
6 ** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
7 **
8 ** This file is part of qmake.
9 **
10 ** This file may be distributed under the terms of the Q Public License
11 ** as defined by Trolltech ASA of Norway and appearing in the file
12 ** LICENSE.QPL included in the packaging of this file.
13 **
14 ** This file may be distributed and/or modified under the terms of the
15 ** GNU General Public License version 2 as published by the Free Software
16 ** Foundation and appearing in the file LICENSE.GPL included in the
17 ** packaging of this file.
18 **
19 ** Licensees holding valid Qt Enterprise Edition licenses may use this
20 ** file in accordance with the Qt Commercial License Agreement provided
21 ** with the Software.
22 **
23 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25 **
26 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
27 ** information about Qt Commercial License Agreements.
28 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
29 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
30 **
31 ** Contact info@trolltech.com if any conditions of this licensing are
32 ** not clear to you.
33 **
34 **********************************************************************/
35
36 #include "project.h"
37 #include "property.h"
38 #include "option.h"
39 #include <qfile.h>
40 #include <qdir.h>
41 #include <qregexp.h>
42 #include <qtextstream.h>
43 #include <qvaluestack.h>
44 #ifdef Q_OS_UNIX
45 # include <unistd.h>
46 #endif
47 #include <stdio.h>
48 #include <stdlib.h>
49
50 #ifdef Q_OS_WIN32
51 #define QT_POPEN _popen
52 #else
53 #define QT_POPEN popen
54 #endif
55
56 struct parser_info {
57 QString file;
58 int line_no;
59 } parser;
60
qmake_error_msg(const char * msg)61 static void qmake_error_msg(const char *msg)
62 {
63 fprintf(stderr, "%s:%d: %s\n", parser.file.latin1(), parser.line_no, msg);
64 }
65
qmake_mkspec_paths()66 QStringList qmake_mkspec_paths()
67 {
68 QStringList ret;
69 const QString concat = QDir::separator() + QString("mkspecs");
70 if(const char *qmakepath = getenv("QMAKEPATH")) {
71 #ifdef Q_OS_WIN
72 QStringList lst = QStringList::split(';', qmakepath);
73 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it) {
74 QStringList lst2 = QStringList::split(':', (*it));
75 for(QStringList::Iterator it2 = lst2.begin(); it2 != lst2.end(); ++it2)
76 ret << ((*it2) + concat);
77 }
78 #else
79 QStringList lst = QStringList::split(':', qmakepath);
80 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it)
81 ret << ((*it) + concat);
82 #endif
83 }
84 if(const char *qtdir = getenv("QTDIR"))
85 ret << (QString(qtdir) + concat);
86 #ifdef QT_INSTALL_PREFIX
87 ret << (QT_INSTALL_PREFIX + concat);
88 #endif
89 #if defined(HAVE_QCONFIG_CPP)
90 ret << (qInstallPath() + concat);
91 #endif
92 #ifdef QT_INSTALL_DATA
93 ret << (QT_INSTALL_DATA + concat);
94 #endif
95 #if defined(HAVE_QCONFIG_CPP)
96 ret << (qInstallPathData() + concat);
97 #endif
98
99 /* prefer $QTDIR if it is set */
100 if (getenv("QTDIR"))
101 ret << getenv("QTDIR");
102 ret << qInstallPathData();
103 return ret;
104 }
105
varMap(const QString & x)106 static QString varMap(const QString &x)
107 {
108 QString ret(x);
109 if(ret.startsWith("TMAKE")) //tmake no more!
110 ret = "QMAKE" + ret.mid(5);
111 else if(ret == "INTERFACES")
112 ret = "FORMS";
113 else if(ret == "QMAKE_POST_BUILD")
114 ret = "QMAKE_POST_LINK";
115 else if(ret == "TARGETDEPS")
116 ret = "POST_TARGETDEPS";
117 else if(ret == "LIBPATH")
118 ret = "QMAKE_LIBDIR";
119 else if(ret == "QMAKE_EXT_MOC")
120 ret = "QMAKE_EXT_CPP_MOC";
121 else if(ret == "QMAKE_MOD_MOC")
122 ret = "QMAKE_H_MOD_MOC";
123 else if(ret == "QMAKE_LFLAGS_SHAPP")
124 ret = "QMAKE_LFLAGS_APP";
125 else if(ret == "PRECOMPH")
126 ret = "PRECOMPILED_HEADER";
127 else if(ret == "PRECOMPCPP")
128 ret = "PRECOMPILED_SOURCE";
129 else if(ret == "INCPATH")
130 ret = "INCLUDEPATH";
131 return ret;
132 }
133
split_arg_list(const QString & params)134 static QStringList split_arg_list(const QString ¶ms)
135 {
136 QChar quote = 0;
137 QStringList args;
138 for(int x = 0, last = 0, parens = 0; x <= (int)params.length(); x++) {
139 if(x == (int)params.length()) {
140 QString mid = params.mid(last, x - last).stripWhiteSpace();
141 if(quote) {
142 if(mid[(int)mid.length()-1] == quote)
143 mid.truncate(1);
144 quote = 0;
145 }
146 args << mid;
147 } else if(params[x] == ')') {
148 parens--;
149 } else if(params[x] == '(') {
150 parens++;
151 } else if(params[x] == quote) {
152 quote = 0;
153 } else if(!quote && (params[x] == '\'' || params[x] == '"')) {
154 quote = params[x];
155 } else if(!parens && !quote && params[x] == ',') {
156 args << params.mid(last, x - last).stripWhiteSpace();
157 last = x+1;
158 }
159 }
160 return args;
161 }
162
split_value_list(const QString & vals,bool do_semicolon=FALSE)163 static QStringList split_value_list(const QString &vals, bool do_semicolon=FALSE)
164 {
165 QString build;
166 QStringList ret;
167 QValueStack<QChar> quote;
168 for(int x = 0; x < (int)vals.length(); x++) {
169 if(x != (int)vals.length()-1 && vals[x] == '\\' && (vals[x+1] == '\'' || vals[x+1] == '"'))
170 build += vals[x++]; //get that 'escape'
171 else if(!quote.isEmpty() && vals[x] == quote.top())
172 quote.pop();
173 else if(vals[x] == '\'' || vals[x] == '"')
174 quote.push(vals[x]);
175
176 if(quote.isEmpty() && ((do_semicolon && vals[x] == ';') || vals[x] == ' ')) {
177 ret << build;
178 build = "";
179 } else {
180 build += vals[x];
181 }
182 }
183 if(!build.isEmpty())
184 ret << build;
185 return ret;
186 }
187
QMakeProject()188 QMakeProject::QMakeProject()
189 {
190 prop = NULL;
191 }
192
QMakeProject(QMakeProperty * p)193 QMakeProject::QMakeProject(QMakeProperty *p)
194 {
195 prop = p;
196 }
197
198 void
reset()199 QMakeProject::reset()
200 {
201 /* scope blocks start at true */
202 test_status = TestNone;
203 scope_flag = 0x01;
204 scope_block = 0;
205 }
206
207 bool
parse(const QString & t,QMap<QString,QStringList> & place)208 QMakeProject::parse(const QString &t, QMap<QString, QStringList> &place)
209 {
210 QString s = t.simplifyWhiteSpace();
211 int hash_mark = s.find("#");
212 if(hash_mark != -1) //good bye comments
213 s = s.left(hash_mark);
214 if(s.isEmpty()) /* blank_line */
215 return TRUE;
216
217 if(s.stripWhiteSpace().left(1) == "}") {
218 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.latin1(),
219 parser.line_no, scope_block);
220 test_status = ((scope_flag & (0x01 << scope_block)) ? TestFound : TestSeek);
221 scope_block--;
222 s = s.mid(1).stripWhiteSpace();
223 if(s.isEmpty())
224 return TRUE;
225 }
226 if(!(scope_flag & (0x01 << scope_block))) {
227 /* adjust scope for each block which appears on a single line */
228 for(int i = (s.contains('{')-s.contains('}')); i>0; i--)
229 scope_flag &= ~(0x01 << (++scope_block));
230 debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.",
231 parser.file.latin1(), parser.line_no);
232 return TRUE;
233 }
234
235 QString scope, var, op;
236 QStringList val;
237 #define SKIP_WS(d) while(*d && (*d == ' ' || *d == '\t')) d++
238 const char *d = s.latin1();
239 SKIP_WS(d);
240 bool scope_failed = FALSE, else_line = FALSE, or_op=FALSE;
241 int parens = 0, scope_count=0;
242 while(*d) {
243 if(!parens) {
244 if(*d == '=')
245 break;
246 if(*d == '+' || *d == '-' || *d == '*' || *d == '~') {
247 if(*(d+1) == '=') {
248 break;
249 } else if(*(d+1) == ' ') {
250 const char *k = d + 1;
251 SKIP_WS(k);
252 if(*k == '=') {
253 QString msg;
254 qmake_error_msg(*d + "must be followed immediately by =");
255 return FALSE;
256 }
257 }
258 }
259 }
260
261 if ( *d == '(' )
262 ++parens;
263 else if ( *d == ')' )
264 --parens;
265
266 if(!parens && (*d == ':' || *d == '{' || *d == ')' || *d == '|')) {
267 scope_count++;
268 scope = var.stripWhiteSpace();
269 if ( *d == ')' )
270 scope += *d; /* need this */
271 var = "";
272
273 bool test = scope_failed;
274 if(scope.lower() == "else") {
275 if(scope_count != 1 || test_status == TestNone) {
276 qmake_error_msg("Unexpected " + scope + " ('" + s + "')");
277 return FALSE;
278 }
279 else_line = TRUE;
280 test = (test_status == TestSeek);
281 debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser.file.latin1(), parser.line_no,
282 scope == "else" ? "" : QString(" (" + scope + ")").latin1(),
283 test ? "considered" : "excluded");
284 } else {
285 QString comp_scope = scope;
286 bool invert_test = (comp_scope.left(1) == "!");
287 if(invert_test)
288 comp_scope = comp_scope.right(comp_scope.length()-1);
289 int lparen = comp_scope.find('(');
290 if(or_op == scope_failed) {
291 if(lparen != -1) { /* if there is an lparen in the scope, it IS a function */
292 int rparen = comp_scope.findRev(')');
293 if(rparen == -1) {
294 QCString error;
295 error.sprintf("Function missing right paren: %s ('%s')",
296 comp_scope.latin1(), s.latin1());
297 qmake_error_msg(error);
298 return FALSE;
299 }
300 QString func = comp_scope.left(lparen);
301 test = doProjectTest(func, comp_scope.mid(lparen+1, rparen - lparen - 1), place);
302 if ( *d == ')' && !*(d+1) ) {
303 if(invert_test)
304 test = !test;
305 test_status = (test ? TestFound : TestSeek);
306 return TRUE; /* assume we are done */
307 }
308 } else {
309 test = isActiveConfig(comp_scope.stripWhiteSpace(), TRUE, &place);
310 }
311 if(invert_test)
312 test = !test;
313 }
314 }
315 if(!test && !scope_failed)
316 debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser.file.latin1(),
317 parser.line_no, scope.latin1());
318 if(test == or_op)
319 scope_failed = !test;
320 or_op = (*d == '|');
321 if(*d == '{') { /* scoping block */
322 if(!scope_failed)
323 scope_flag |= (0x01 << (++scope_block));
324 else
325 scope_flag &= ~(0x01 << (++scope_block));
326 debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d).", parser.file.latin1(),
327 parser.line_no, scope_block, !scope_failed);
328 }
329 } else if(!parens && *d == '}') {
330 if(!scope_block) {
331 warn_msg(WarnParser, "Possible braces mismatch %s:%d", parser.file.latin1(), parser.line_no);
332 } else {
333 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.latin1(),
334 parser.line_no, scope_block);
335 --scope_block;
336 }
337 } else {
338 var += *d;
339 }
340 d++;
341 }
342 var = var.stripWhiteSpace();
343 if(!scope_count || (scope_count == 1 && else_line))
344 test_status = TestNone;
345 else if(!else_line || test_status != TestFound)
346 test_status = (scope_failed ? TestSeek : TestFound);
347 if(!*d) {
348 if(!var.stripWhiteSpace().isEmpty())
349 qmake_error_msg("Parse Error ('" + s + "')");
350 return var.isEmpty(); /* allow just a scope */
351 }
352
353 SKIP_WS(d);
354 for( ; *d && op.find('=') == -1; op += *(d++))
355 ;
356 op.replace(QRegExp("\\s"), "");
357
358 SKIP_WS(d);
359 QString vals(d); /* vals now contains the space separated list of values */
360 int rbraces = vals.contains('}'), lbraces = vals.contains('{');
361 if(scope_block && rbraces - lbraces == 1) {
362 debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.latin1(),
363 parser.line_no, scope_block);
364 test_status = ((scope_flag & (0x01 << scope_block)) ? TestFound : TestSeek);
365 scope_block--;
366 vals.truncate(vals.length()-1);
367 } else if(rbraces != lbraces) {
368 warn_msg(WarnParser, "Possible braces mismatch {%s} %s:%d",
369 vals.latin1(), parser.file.latin1(), parser.line_no);
370 }
371 doVariableReplace(vals, place);
372 if(scope_failed)
373 return TRUE; /* oh well */
374 #undef SKIP_WS
375
376 if(!var.isEmpty() && Option::mkfile::do_preprocess) {
377 static QString last_file("*none*");
378 if(parser.file != last_file) {
379 fprintf(stdout, "#file %s:%d\n", parser.file.latin1(), parser.line_no);
380 last_file = parser.file;
381 }
382 fprintf(stdout, "%s %s %s\n", var.latin1(), op.latin1(), vals.latin1());
383 }
384 var = varMap(var); //backwards compatability
385
386 /* vallist is the broken up list of values */
387 QStringList vallist = split_value_list(vals, (var == "DEPENDPATH" || var == "INCLUDEPATH"));
388 if(!vallist.grep("=").isEmpty())
389 warn_msg(WarnParser, "Detected possible line continuation: {%s} %s:%d",
390 var.latin1(), parser.file.latin1(), parser.line_no);
391
392 QStringList &varlist = place[var]; /* varlist is the list in the symbol table */
393 debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser.file.latin1(), parser.line_no,
394 var.latin1(), op.latin1(), vallist.isEmpty() ? "" : vallist.join(" :: ").latin1());
395
396 /* now do the operation */
397 if(op == "~=") {
398 if(vallist.count() != 1) {
399 qmake_error_msg("~= operator only accepts one right hand paramater ('" +
400 s + "')");
401 return FALSE;
402 }
403 QString val(vallist.first());
404 if(val.length() < 4 || val.at(0) != 's') {
405 qmake_error_msg("~= operator only can handle s/// function ('" +
406 s + "')");
407 return FALSE;
408 }
409 QChar sep = val.at(1);
410 QStringList func = QStringList::split(sep, val, TRUE);
411 if(func.count() < 3 || func.count() > 4) {
412 qmake_error_msg("~= operator only can handle s/// function ('" +
413 s + "')");
414 return FALSE;
415 }
416 bool global = FALSE, case_sense = TRUE;
417 if(func.count() == 4) {
418 global = func[3].find('g') != -1;
419 case_sense = func[3].find('i') == -1;
420 }
421 QRegExp regexp(func[1], case_sense);
422 for(QStringList::Iterator varit = varlist.begin();
423 varit != varlist.end(); ++varit) {
424 if((*varit).contains(regexp)) {
425 (*varit) = (*varit).replace(regexp, func[2]);
426 if(!global)
427 break;
428 }
429 }
430 } else {
431 if(op == "=") {
432 if(!varlist.isEmpty())
433 warn_msg(WarnParser, "Operator=(%s) clears variables previously set: %s:%d",
434 var.latin1(), parser.file.latin1(), parser.line_no);
435 varlist.clear();
436 }
437 for(QStringList::Iterator valit = vallist.begin();
438 valit != vallist.end(); ++valit) {
439 if((*valit).isEmpty())
440 continue;
441 if((op == "*=" && !(*varlist.find((*valit)))) ||
442 op == "=" || op == "+=")
443 varlist.append((*valit));
444 else if(op == "-=")
445 varlist.remove((*valit));
446 }
447 }
448 if(var == "REQUIRES") /* special case to get communicated to backends! */
449 doProjectCheckReqs(vallist, place);
450
451 return TRUE;
452 }
453
454 bool
read(const QString & file,QMap<QString,QStringList> & place)455 QMakeProject::read(const QString &file, QMap<QString, QStringList> &place)
456 {
457 parser_info pi = parser;
458 reset();
459
460 QString filename = Option::fixPathToLocalOS(file);
461 doVariableReplace(filename, place);
462 bool ret = FALSE, using_stdin = FALSE;
463 QFile qfile;
464 if(!strcmp(filename, "-")) {
465 qfile.setName("");
466 ret = qfile.open(IO_ReadOnly, stdin);
467 using_stdin = TRUE;
468 } else {
469 qfile.setName(filename);
470 ret = qfile.open(IO_ReadOnly);
471 }
472 if ( ret ) {
473 QTextStream t( &qfile );
474 QString s, line;
475 parser.file = filename;
476 parser.line_no = 0;
477 while ( !t.eof() ) {
478 parser.line_no++;
479 line = t.readLine().stripWhiteSpace();
480 int prelen = line.length();
481
482 int hash_mark = line.find("#");
483 if(hash_mark != -1) //good bye comments
484 line = line.left(hash_mark);
485 if(!line.isEmpty() && line.right(1) == "\\") {
486 if (!line.startsWith("#")) {
487 line.truncate(line.length() - 1);
488 s += line + " ";
489 }
490 } else if(!line.isEmpty() || (line.isEmpty() && !prelen)) {
491 if(s.isEmpty() && line.isEmpty())
492 continue;
493 if(!line.isEmpty())
494 s += line;
495 if(!s.isEmpty()) {
496 if(!(ret = parse(s, place)))
497 break;
498 s = "";
499 }
500 }
501 }
502 if(!using_stdin)
503 qfile.close();
504 }
505 parser = pi;
506 if(scope_block != 0)
507 warn_msg(WarnParser, "%s: Unterminated conditional at end of file.",
508 file.latin1());
509 return ret;
510 }
511
512 bool
read(const QString & project,const QString &,uchar cmd)513 QMakeProject::read(const QString &project, const QString &, uchar cmd)
514 {
515 pfile = project;
516 return read(cmd);
517 }
518
519 bool
read(uchar cmd)520 QMakeProject::read(uchar cmd)
521 {
522 if(cfile.isEmpty()) {
523 // hack to get the Option stuff in there
524 base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext;
525 base_vars["QMAKE_EXT_H"] = Option::h_ext;
526 if(!Option::user_template_prefix.isEmpty())
527 base_vars["TEMPLATE_PREFIX"] = Option::user_template_prefix;
528
529 if(cmd & ReadCache && Option::mkfile::do_cache) { /* parse the cache */
530 if(Option::mkfile::cachefile.isEmpty()) { //find it as it has not been specified
531 QString dir = QDir::convertSeparators(Option::output_dir);
532 while(!QFile::exists((Option::mkfile::cachefile = dir +
533 QDir::separator() + ".qmake.cache"))) {
534 dir = dir.left(dir.findRev(QDir::separator()));
535 if(dir.isEmpty() || dir.find(QDir::separator()) == -1) {
536 Option::mkfile::cachefile = "";
537 break;
538 }
539 if(Option::mkfile::cachefile_depth == -1)
540 Option::mkfile::cachefile_depth = 1;
541 else
542 Option::mkfile::cachefile_depth++;
543 }
544 }
545 if(!Option::mkfile::cachefile.isEmpty()) {
546 read(Option::mkfile::cachefile, cache);
547 if(Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty())
548 Option::mkfile::qmakespec = cache["QMAKESPEC"].first();
549 }
550 }
551 if(cmd & ReadConf) { /* parse mkspec */
552 QStringList mkspec_roots = qmake_mkspec_paths();
553 if(Option::mkfile::qmakespec.isEmpty()) {
554 for(QStringList::Iterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
555 QString mkspec = (*it) + QDir::separator() + "default";
556 QFileInfo default_info(mkspec);
557 if(default_info.exists() && default_info.isSymLink()) {
558 Option::mkfile::qmakespec = mkspec;
559 break;
560 }
561 }
562 if(Option::mkfile::qmakespec.isEmpty()) {
563 fprintf(stderr, "QMAKESPEC has not been set, so configuration cannot be deduced.\n");
564 return FALSE;
565 }
566 }
567
568 if(QDir::isRelativePath(Option::mkfile::qmakespec)) {
569 bool found_mkspec = FALSE;
570 for(QStringList::Iterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
571 QString mkspec = (*it) + QDir::separator() + Option::mkfile::qmakespec;
572 if(QFile::exists(mkspec)) {
573 found_mkspec = TRUE;
574 Option::mkfile::qmakespec = mkspec;
575 break;
576 }
577 }
578 if(!found_mkspec) {
579 fprintf(stderr, "Could not find mkspecs for your QMAKESPEC after trying:\n\t%s\n",
580 mkspec_roots.join("\n\t").latin1());
581 return FALSE;
582 }
583 }
584
585 /* parse qmake configuration */
586 while(Option::mkfile::qmakespec.endsWith(QString(QChar(QDir::separator()))))
587 Option::mkfile::qmakespec.truncate(Option::mkfile::qmakespec.length()-1);
588 QString spec = Option::mkfile::qmakespec + QDir::separator() + "qmake.conf";
589 if(!QFile::exists(spec) &&
590 QFile::exists(Option::mkfile::qmakespec + QDir::separator() + "tmake.conf"))
591 spec = Option::mkfile::qmakespec + QDir::separator() + "tmake.conf";
592 debug_msg(1, "QMAKESPEC conf: reading %s", spec.latin1());
593 if(!read(spec, base_vars)) {
594 fprintf(stderr, "Failure to read QMAKESPEC conf file %s.\n", spec.latin1());
595 return FALSE;
596 }
597 if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty()) {
598 debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile.latin1());
599 read(Option::mkfile::cachefile, base_vars);
600 }
601 }
602 if(cmd & ReadCmdLine) {
603 /* commandline */
604 cfile = pfile;
605 parser.line_no = 1; //really arg count now.. duh
606 parser.file = "(internal)";
607 reset();
608 for(QStringList::Iterator it = Option::before_user_vars.begin();
609 it != Option::before_user_vars.end(); ++it) {
610 if(!parse((*it), base_vars)) {
611 fprintf(stderr, "Argument failed to parse: %s\n", (*it).latin1());
612 return FALSE;
613 }
614 parser.line_no++;
615 }
616 }
617 }
618
619 vars = base_vars; /* start with the base */
620
621 if(cmd & ReadProFile) { /* parse project file */
622 debug_msg(1, "Project file: reading %s", pfile.latin1());
623 if(pfile != "-" && !QFile::exists(pfile) && !pfile.endsWith(".pro"))
624 pfile += ".pro";
625 if(!read(pfile, vars))
626 return FALSE;
627 }
628
629 if(cmd & ReadPostFiles) { /* parse post files */
630 const QStringList l = vars["QMAKE_POST_INCLUDE_FILES"];
631 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it)
632 read((*it), vars);
633 }
634
635 if(cmd & ReadCmdLine) {
636 parser.line_no = 1; //really arg count now.. duh
637 parser.file = "(internal)";
638 reset();
639 for(QStringList::Iterator it = Option::after_user_vars.begin();
640 it != Option::after_user_vars.end(); ++it) {
641 if(!parse((*it), vars)) {
642 fprintf(stderr, "Argument failed to parse: %s\n", (*it).latin1());
643 return FALSE;
644 }
645 parser.line_no++;
646 }
647 }
648
649 /* now let the user override the template from an option.. */
650 if(!Option::user_template.isEmpty()) {
651 debug_msg(1, "Overriding TEMPLATE (%s) with: %s", vars["TEMPLATE"].first().latin1(),
652 Option::user_template.latin1());
653 vars["TEMPLATE"].clear();
654 vars["TEMPLATE"].append(Option::user_template);
655 }
656
657 QStringList &templ = vars["TEMPLATE"];
658 if(templ.isEmpty())
659 templ.append(QString("app"));
660 else if(vars["TEMPLATE"].first().endsWith(".t"))
661 templ = QStringList(templ.first().left(templ.first().length() - 2));
662 if ( !Option::user_template_prefix.isEmpty() )
663 templ.first().prepend(Option::user_template_prefix);
664
665 if(vars["TARGET"].isEmpty()) {
666 // ### why not simply use:
667 // QFileInfo fi(pfile);
668 // fi.baseName();
669 QString tmp = pfile;
670 if(tmp.findRev('/') != -1)
671 tmp = tmp.right( tmp.length() - tmp.findRev('/') - 1 );
672 if(tmp.findRev('.') != -1)
673 tmp = tmp.left(tmp.findRev('.'));
674 vars["TARGET"].append(tmp);
675 }
676
677 QString test_version = getenv("QTESTVERSION");
678 if (!test_version.isEmpty()) {
679 QString s = vars["TARGET"].first();
680 if (s == "qt" || s == "qt-mt" || s == "qte" || s == "qte-mt") {
681 QString &ver = vars["VERSION"].first();
682 // fprintf(stderr,"Current QT version number: " + ver + "\n");
683 if (ver != "" && ver != test_version) {
684 ver = test_version;
685 fprintf(stderr,"Changed QT version number to " + test_version + "!\n");
686 }
687 }
688 }
689 return TRUE;
690 }
691
692 bool
isActiveConfig(const QString & x,bool regex,QMap<QString,QStringList> * place)693 QMakeProject::isActiveConfig(const QString &x, bool regex, QMap<QString, QStringList> *place)
694 {
695 if(x.isEmpty())
696 return TRUE;
697
698 if((Option::target_mode == Option::TARG_MACX_MODE || Option::target_mode == Option::TARG_QNX6_MODE ||
699 Option::target_mode == Option::TARG_UNIX_MODE) && x == "unix")
700 return TRUE;
701 else if(Option::target_mode == Option::TARG_MACX_MODE && x == "macx")
702 return TRUE;
703 else if(Option::target_mode == Option::TARG_QNX6_MODE && x == "qnx6")
704 return TRUE;
705 else if(Option::target_mode == Option::TARG_MAC9_MODE && x == "mac9")
706 return TRUE;
707 else if((Option::target_mode == Option::TARG_MAC9_MODE || Option::target_mode == Option::TARG_MACX_MODE) &&
708 x == "mac")
709 return TRUE;
710 else if(Option::target_mode == Option::TARG_WIN_MODE && x == "win32")
711 return TRUE;
712
713
714 QRegExp re(x, FALSE, TRUE);
715 QString spec = Option::mkfile::qmakespec.right(Option::mkfile::qmakespec.length() -
716 (Option::mkfile::qmakespec.findRev(QDir::separator())+1));
717 if((regex && re.exactMatch(spec)) || (!regex && spec == x))
718 return TRUE;
719 #ifdef Q_OS_UNIX
720 else if(spec == "default") {
721 static char *buffer = NULL;
722 if(!buffer)
723 buffer = (char *)malloc(1024);
724 int l = readlink(Option::mkfile::qmakespec, buffer, 1024);
725 if(l != -1) {
726 buffer[l] = '\0';
727 QString r = buffer;
728 if(r.findRev('/') != -1)
729 r = r.mid(r.findRev('/') + 1);
730 if((regex && re.exactMatch(r)) || (!regex && r == x))
731 return TRUE;
732 }
733 }
734 #endif
735
736
737 QStringList &configs = (place ? (*place)["CONFIG"] : vars["CONFIG"]);
738 for(QStringList::Iterator it = configs.begin(); it != configs.end(); ++it) {
739 if((regex && re.exactMatch((*it))) || (!regex && (*it) == x))
740 if(re.exactMatch((*it)))
741 return TRUE;
742 }
743 return FALSE;
744 }
745
746 bool
doProjectTest(const QString & func,const QString & params,QMap<QString,QStringList> & place)747 QMakeProject::doProjectTest(const QString& func, const QString ¶ms, QMap<QString, QStringList> &place)
748 {
749 QStringList args = split_arg_list(params);
750 for(QStringList::Iterator arit = args.begin(); arit != args.end(); ++arit) {
751 QString tmp = (*arit).stripWhiteSpace();
752 if((tmp[0] == '\'' || tmp[0] == '"') && tmp.right(1) == tmp.left(1))
753 tmp = tmp.mid(1, tmp.length() - 2);
754 }
755 return doProjectTest(func.stripWhiteSpace(), args, place);
756 }
757
758 bool
doProjectTest(const QString & func,QStringList args,QMap<QString,QStringList> & place)759 QMakeProject::doProjectTest(const QString& func, QStringList args, QMap<QString, QStringList> &place)
760 {
761 for(QStringList::Iterator arit = args.begin(); arit != args.end(); ++arit) {
762 (*arit) = (*arit).stripWhiteSpace(); // blah, get rid of space
763 doVariableReplace((*arit), place);
764 }
765 debug_msg(1, "Running project test: %s( %s )", func.latin1(), args.join("::").latin1());
766
767 if(func == "requires") {
768 return doProjectCheckReqs(args, place);
769 } else if(func == "equals") {
770 if(args.count() != 2) {
771 fprintf(stderr, "%s:%d: equals(variable, value) requires two arguments.\n", parser.file.latin1(),
772 parser.line_no);
773 return FALSE;
774 }
775 QString value = args[1];
776 if((value.left(1) == "\"" || value.left(1) == "'") && value.right(1) == value.left(1))
777 value = value.mid(1, value.length()-2);
778 return vars[args[0]].join(" ") == value;
779 } else if(func == "exists") {
780 if(args.count() != 1) {
781 fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.latin1(),
782 parser.line_no);
783 return FALSE;
784 }
785 QString file = args.first();
786 file = Option::fixPathToLocalOS(file);
787 doVariableReplace(file, place);
788
789 if(QFile::exists(file))
790 return TRUE;
791 //regular expression I guess
792 QString dirstr = QDir::currentDirPath();
793 int slsh = file.findRev(Option::dir_sep);
794 if(slsh != -1) {
795 dirstr = file.left(slsh+1);
796 file = file.right(file.length() - slsh - 1);
797 }
798 QDir dir(dirstr, file);
799 return dir.count() != 0;
800 } else if(func == "system") {
801 if(args.count() != 1) {
802 fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.latin1(),
803 parser.line_no);
804 return FALSE;
805 }
806 return system(args.first().latin1()) == 0;
807 } else if(func == "contains") {
808 if(args.count() != 2) {
809 fprintf(stderr, "%s:%d: contains(var, val) requires two arguments.\n", parser.file.latin1(),
810 parser.line_no);
811 return FALSE;
812 }
813 QRegExp regx(args[1]);
814 QStringList &l = place[args[0]];
815 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
816 if(regx.exactMatch((*it)))
817 return TRUE;
818 }
819 return FALSE;
820 } else if(func == "infile") {
821 if(args.count() < 2 || args.count() > 3) {
822 fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n",
823 parser.file.latin1(), parser.line_no);
824 return FALSE;
825 }
826 QMakeProject proj;
827 QString file = args[0];
828 doVariableReplace(file, place);
829 fixEnvVariables(file);
830 int di = file.findRev(Option::dir_sep);
831 QDir sunworkshop42workaround = QDir::current();
832 QString oldpwd = sunworkshop42workaround.currentDirPath();
833 if(di != -1) {
834 if(!QDir::setCurrent(file.left(file.findRev(Option::dir_sep)))) {
835 fprintf(stderr, "Cannot find directory: %s\n", file.left(di).latin1());
836 return FALSE;
837 }
838 file = file.right(file.length() - di - 1);
839 }
840 parser_info pi = parser;
841 bool ret = !proj.read(file, oldpwd);
842 parser = pi;
843 if(ret) {
844 fprintf(stderr, "Error processing project file: %s\n", file.latin1());
845 QDir::setCurrent(oldpwd);
846 return FALSE;
847 }
848 if(args.count() == 2) {
849 ret = !proj.isEmpty(args[1]);
850 } else {
851 QRegExp regx(args[2]);
852 QStringList &l = proj.values(args[1]);
853 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
854 if(regx.exactMatch((*it))) {
855 ret = TRUE;
856 break;
857 }
858 }
859 }
860 QDir::setCurrent(oldpwd);
861 return ret;
862 } else if(func == "count") {
863 if(args.count() != 2) {
864 fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.latin1(),
865 parser.line_no);
866 return FALSE;
867 }
868 return vars[args[0]].count() == args[1].toUInt();
869 } else if(func == "isEmpty") {
870 if(args.count() != 1) {
871 fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.latin1(),
872 parser.line_no);
873 return FALSE;
874 }
875 return vars[args[0]].isEmpty();
876 } else if(func == "include" || func == "load") {
877 QString seek_var;
878 if(args.count() == 2 && func == "include") {
879 seek_var = args[1];
880 } else if(args.count() != 1) {
881 QString func_desc = "include(file)";
882 if(func == "load")
883 func_desc = "load(feature)";
884 fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.latin1(),
885 parser.line_no, func_desc.latin1());
886 return FALSE;
887 }
888
889 QString file = args.first();
890 file = Option::fixPathToLocalOS(file);
891 file.replace("\"", "");
892 doVariableReplace(file, place);
893 if(func == "load") {
894 if(!file.endsWith(Option::prf_ext))
895 file += Option::prf_ext;
896 if(file.find(Option::dir_sep) == -1 || !QFile::exists(file)) {
897 bool found = FALSE;
898 const QString concat = QDir::separator() + QString("mkspecs") +
899 QDir::separator() + QString("features");
900 QStringList feature_roots;
901 if(const char *mkspec_path = getenv("QMAKEFEATURES")) {
902 #ifdef Q_OS_WIN
903 QStringList lst = QStringList::split(';', mkspec_path);
904 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it)
905 feature_roots += QStringList::split(':', (*it));
906 #else
907 feature_roots += QStringList::split(':', mkspec_path);
908 #endif
909 }
910 if(const char *qmakepath = getenv("QMAKEPATH")) {
911 #ifdef Q_OS_WIN
912 QStringList lst = QStringList::split(';', qmakepath);
913 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it) {
914 QStringList lst2 = QStringList::split(':', (*it));
915 for(QStringList::Iterator it2 = lst2.begin(); it2 != lst2.end(); ++it2)
916 feature_roots << ((*it2) + concat);
917 }
918 #else
919 QStringList lst = QStringList::split(':', qmakepath);
920 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it)
921 feature_roots << ((*it) + concat);
922 #endif
923 }
924 feature_roots << Option::mkfile::qmakespec;
925 if(const char *qtdir = getenv("QTDIR"))
926 feature_roots << (qtdir + concat);
927 #ifdef QT_INSTALL_PREFIX
928 feature_roots << (QT_INSTALL_PREFIX + concat);
929 #endif
930 #if defined(HAVE_QCONFIG_CPP)
931 feature_roots << (qInstallPath() + concat);
932 #endif
933 #ifdef QT_INSTALL_DATA
934 feature_roots << (QT_INSTALL_DATA + concat);
935 #endif
936 #if defined(HAVE_QCONFIG_CPP)
937 feature_roots << (qInstallPathData() + concat);
938 #endif
939 for(QStringList::Iterator it = feature_roots.begin(); it != feature_roots.end(); ++it) {
940 QString prf = (*it) + QDir::separator() + file;
941 if(QFile::exists(prf)) {
942 found = TRUE;
943 file = prf;
944 break;
945 }
946 }
947 if(!found) {
948 printf("Project LOAD(): Feature %s cannot be found.\n", args.first().latin1());
949 exit(3);
950 }
951 }
952 }
953 if(QDir::isRelativePath(file)) {
954 QStringList include_roots;
955 include_roots << Option::output_dir;
956 QString pfilewd = QFileInfo(parser.file).dirPath();
957 if(!pfilewd.isEmpty() && pfilewd != QDir::currentDirPath() && pfilewd != ".")
958 include_roots << pfilewd;
959 if(Option::output_dir != QDir::currentDirPath())
960 include_roots << QDir::currentDirPath();
961 for(QStringList::Iterator it = include_roots.begin(); it != include_roots.end(); ++it) {
962 if(QFile::exists((*it) + QDir::separator() + file)) {
963 file = (*it) + QDir::separator() + file;
964 break;
965 }
966 }
967 }
968
969 if(Option::mkfile::do_preprocess) //nice to see this first..
970 fprintf(stderr, "#switching file %s(%s) - %s:%d\n", func.latin1(), file.latin1(),
971 parser.file.latin1(), parser.line_no);
972 debug_msg(1, "Project Parser: %s'ing file %s.", func.latin1(), file.latin1());
973 QString orig_file = file;
974 int di = file.findRev(Option::dir_sep);
975 QDir sunworkshop42workaround = QDir::current();
976 QString oldpwd = sunworkshop42workaround.currentDirPath();
977 if(di != -1) {
978 if(!QDir::setCurrent(file.left(file.findRev(Option::dir_sep)))) {
979 fprintf(stderr, "Cannot find directory: %s\n", file.left(di).latin1());
980 return FALSE;
981 }
982 file = file.right(file.length() - di - 1);
983 }
984 parser_info pi = parser;
985 int sb = scope_block;
986 int sf = scope_flag;
987 TestStatus sc = test_status;
988 bool r = FALSE;
989 if(!seek_var.isNull()) {
990 QMap<QString, QStringList> tmp;
991 if((r = read(file.latin1(), tmp)))
992 place[seek_var] += tmp[seek_var];
993 } else {
994 r = read(file.latin1(), place);
995 }
996 if(r)
997 vars["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file);
998 else
999 warn_msg(WarnParser, "%s:%d: Failure to include file %s.",
1000 pi.file.latin1(), pi.line_no, orig_file.latin1());
1001 parser = pi;
1002 test_status = sc;
1003 scope_flag = sf;
1004 scope_block = sb;
1005 QDir::setCurrent(oldpwd);
1006 return r;
1007 } else if(func == "error" || func == "message") {
1008 if(args.count() != 1) {
1009 fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.latin1(),
1010 parser.line_no, func.latin1());
1011 return FALSE;
1012 }
1013 QString msg = args.first();
1014 if((msg.startsWith("\"") || msg.startsWith("'")) && msg.endsWith(msg.left(1)))
1015 msg = msg.mid(1, msg.length()-2);
1016 msg.replace(QString("${QMAKE_FILE}"), parser.file.latin1());
1017 msg.replace(QString("${QMAKE_LINE_NUMBER}"), QString::number(parser.line_no));
1018 msg.replace(QString("${QMAKE_DATE}"), QDateTime::currentDateTime().toString());
1019 doVariableReplace(msg, place);
1020 fixEnvVariables(msg);
1021 fprintf(stderr, "Project %s: %s\n", func.upper().latin1(), msg.latin1());
1022 if(func == "message")
1023 return TRUE;
1024 exit(2);
1025 } else {
1026 fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.latin1(), parser.line_no,
1027 func.latin1());
1028 }
1029 return FALSE;
1030 }
1031
1032 bool
doProjectCheckReqs(const QStringList & deps,QMap<QString,QStringList> & place)1033 QMakeProject::doProjectCheckReqs(const QStringList &deps, QMap<QString, QStringList> &place)
1034 {
1035 bool ret = FALSE;
1036 for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) {
1037 QString chk = (*it);
1038 if(chk.isEmpty())
1039 continue;
1040 bool invert_test = (chk.left(1) == "!");
1041 if(invert_test)
1042 chk = chk.right(chk.length() - 1);
1043
1044 bool test;
1045 int lparen = chk.find('(');
1046 if(lparen != -1) { /* if there is an lparen in the chk, it IS a function */
1047 int rparen = chk.findRev(')');
1048 if(rparen == -1) {
1049 QCString error;
1050 error.sprintf("Function (in REQUIRES) missing right paren: %s", chk.latin1());
1051 qmake_error_msg(error);
1052 } else {
1053 QString func = chk.left(lparen);
1054 test = doProjectTest(func, chk.mid(lparen+1, rparen - lparen - 1), place);
1055 }
1056 } else {
1057 test = isActiveConfig(chk, TRUE, &place);
1058 }
1059 if(invert_test) {
1060 chk.prepend("!");
1061 test = !test;
1062 }
1063 if(!test) {
1064 debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s",
1065 parser.file.latin1(), parser.line_no, chk.latin1());
1066 place["QMAKE_FAILED_REQUIREMENTS"].append(chk);
1067 ret = FALSE;
1068 }
1069 }
1070 return ret;
1071 }
1072
1073
1074 QString
doVariableReplace(QString & str,const QMap<QString,QStringList> & place)1075 QMakeProject::doVariableReplace(QString &str, const QMap<QString, QStringList> &place)
1076 {
1077 for(int var_begin, var_last=0; (var_begin = str.find("$$", var_last)) != -1; var_last = var_begin) {
1078 if(var_begin >= (int)str.length() + 2) {
1079 break;
1080 } else if(var_begin != 0 && str[var_begin-1] == '\\') {
1081 str.replace(var_begin-1, 1, "");
1082 var_begin += 1;
1083 continue;
1084 }
1085
1086 int var_incr = var_begin + 2;
1087 bool in_braces = FALSE, as_env = FALSE, as_prop = FALSE;
1088 if(str[var_incr] == '{') {
1089 in_braces = TRUE;
1090 var_incr++;
1091 while(var_incr < (int)str.length() &&
1092 (str[var_incr] == ' ' || str[var_incr] == '\t' || str[var_incr] == '\n'))
1093 var_incr++;
1094 }
1095 if(str[var_incr] == '(') {
1096 as_env = TRUE;
1097 var_incr++;
1098 } else if(str[var_incr] == '[') {
1099 as_prop = TRUE;
1100 var_incr++;
1101 }
1102 QString val, args;
1103 while(var_incr < (int)str.length() &&
1104 (str[var_incr].isLetter() || str[var_incr].isNumber() || str[var_incr] == '.' || str[var_incr] == '_'))
1105 val += str[var_incr++];
1106 if(as_env) {
1107 if(str[var_incr] != ')') {
1108 var_incr++;
1109 warn_msg(WarnParser, "%s:%d: Unterminated env-variable replacement '%s' (%s)",
1110 parser.file.latin1(), parser.line_no,
1111 str.mid(var_begin, QMAX(var_incr - var_begin,
1112 (int)str.length())).latin1(), str.latin1());
1113 var_begin += var_incr;
1114 continue;
1115 }
1116 var_incr++;
1117 } else if(as_prop) {
1118 if(str[var_incr] != ']') {
1119 var_incr++;
1120 warn_msg(WarnParser, "%s:%d: Unterminated prop-variable replacement '%s' (%s)",
1121 parser.file.latin1(), parser.line_no,
1122 str.mid(var_begin, QMAX(var_incr - var_begin, int(str.length()))).latin1(), str.latin1());
1123 var_begin += var_incr;
1124 continue;
1125 }
1126 var_incr++;
1127 } else if(str[var_incr] == '(') { //args
1128 for(int parens = 0; var_incr < (int)str.length(); var_incr++) {
1129 if(str[var_incr] == '(') {
1130 parens++;
1131 if(parens == 1)
1132 continue;
1133 } else if(str[var_incr] == ')') {
1134 parens--;
1135 if(!parens) {
1136 var_incr++;
1137 break;
1138 }
1139 }
1140 args += str[var_incr];
1141 }
1142 }
1143 if(var_incr > (int)str.length() || (in_braces && str[var_incr] != '}')) {
1144 var_incr++;
1145 warn_msg(WarnParser, "%s:%d: Unterminated variable replacement '%s' (%s)",
1146 parser.file.latin1(), parser.line_no,
1147 str.mid(var_begin, QMAX(var_incr - var_begin,
1148 (int)str.length())).latin1(), str.latin1());
1149 var_begin += var_incr;
1150 continue;
1151 } else if(in_braces) {
1152 var_incr++;
1153 }
1154
1155 QString replacement;
1156 if(as_env) {
1157 replacement = getenv(val);
1158 } else if(as_prop) {
1159 if(prop)
1160 replacement = prop->value(val);
1161 } else if(args.isEmpty()) {
1162 if(val.left(1) == ".")
1163 replacement = "";
1164 else if(val == "LITERAL_WHITESPACE")
1165 replacement = "\t";
1166 else if(val == "LITERAL_DOLLAR")
1167 replacement = "$";
1168 else if(val == "LITERAL_HASH")
1169 replacement = "#";
1170 else if(val == "PWD")
1171 replacement = QDir::currentDirPath();
1172 else if(val == "DIR_SEPARATOR")
1173 replacement = Option::dir_sep;
1174 else
1175 replacement = place[varMap(val)].join(" ");
1176 } else {
1177 QStringList arg_list = split_arg_list(doVariableReplace(args, place));
1178 debug_msg(1, "Running function: %s( %s )", val.latin1(), arg_list.join("::").latin1());
1179 if(val.lower() == "member") {
1180 if(arg_list.count() < 1 || arg_list.count() > 3) {
1181 fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n",
1182 parser.file.latin1(), parser.line_no);
1183 } else {
1184 const QStringList &var = place[varMap(arg_list.first())];
1185 int start = 0;
1186 if(arg_list.count() >= 2)
1187 start = arg_list[1].toInt();
1188 if(start < 0)
1189 start += int(var.count());
1190 int end = start;
1191 if(arg_list.count() == 3)
1192 end = arg_list[2].toInt();
1193 if(end < 0)
1194 end += int(var.count());
1195 if(end < start)
1196 end = start;
1197 for(int i = start; i <= end && (int)var.count() >= i; i++) {
1198 if(!replacement.isEmpty())
1199 replacement += " ";
1200 replacement += var[i];
1201 }
1202 }
1203 } else if(val.lower() == "fromfile") {
1204 if(arg_list.count() != 2) {
1205 fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n",
1206 parser.file.latin1(), parser.line_no);
1207 } else {
1208 QString file = arg_list[0];
1209 file = Option::fixPathToLocalOS(file);
1210 file.replace("\"", "");
1211
1212 if(QDir::isRelativePath(file)) {
1213 QStringList include_roots;
1214 include_roots << Option::output_dir;
1215 QString pfilewd = QFileInfo(parser.file).dirPath();
1216 if(pfilewd.isEmpty())
1217 include_roots << pfilewd;
1218 if(Option::output_dir != QDir::currentDirPath())
1219 include_roots << QDir::currentDirPath();
1220 for(QStringList::Iterator it = include_roots.begin(); it != include_roots.end(); ++it) {
1221 if(QFile::exists((*it) + QDir::separator() + file)) {
1222 file = (*it) + QDir::separator() + file;
1223 break;
1224 }
1225 }
1226 }
1227 QString orig_file = file;
1228 int di = file.findRev(Option::dir_sep);
1229 QDir sunworkshop42workaround = QDir::current();
1230 QString oldpwd = sunworkshop42workaround.currentDirPath();
1231 if(di != -1 && QDir::setCurrent(file.left(file.findRev(Option::dir_sep))))
1232 file = file.right(file.length() - di - 1);
1233 parser_info pi = parser;
1234 int sb = scope_block;
1235 int sf = scope_flag;
1236 TestStatus sc = test_status;
1237 QMap<QString, QStringList> tmp;
1238 bool r = read(file.latin1(), tmp);
1239 if(r) {
1240 replacement = tmp[arg_list[1]].join(" ");
1241 vars["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file);
1242 } else {
1243 warn_msg(WarnParser, "%s:%d: Failure to include file %s.",
1244 pi.file.latin1(), pi.line_no, orig_file.latin1());
1245 }
1246 parser = pi;
1247 test_status = sc;
1248 scope_flag = sf;
1249 scope_block = sb;
1250 QDir::setCurrent(oldpwd);
1251 }
1252 } else if(val.lower() == "eval") {
1253 for(QStringList::ConstIterator arg_it = arg_list.begin();
1254 arg_it != arg_list.end(); ++arg_it) {
1255 if(!replacement.isEmpty())
1256 replacement += " ";
1257 replacement += place[(*arg_it)].join(" ");
1258 }
1259 } else if(val.lower() == "list") {
1260 static int x = 0;
1261 replacement.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++);
1262 QStringList &lst = (*((QMap<QString, QStringList>*)&place))[replacement];
1263 lst.clear();
1264 for(QStringList::ConstIterator arg_it = arg_list.begin();
1265 arg_it != arg_list.end(); ++arg_it)
1266 lst += split_value_list((*arg_it));
1267 } else if(val.lower() == "join") {
1268 if(arg_list.count() < 1 || arg_list.count() > 4) {
1269 fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four"
1270 "arguments.\n", parser.file.latin1(), parser.line_no);
1271 } else {
1272 QString glue, before, after;
1273 if(arg_list.count() >= 2)
1274 glue = arg_list[1].replace("\"", "" );
1275 if(arg_list.count() >= 3)
1276 before = arg_list[2].replace("\"", "" );
1277 if(arg_list.count() == 4)
1278 after = arg_list[3].replace("\"", "" );
1279 const QStringList &var = place[varMap(arg_list.first())];
1280 if(!var.isEmpty())
1281 replacement = before + var.join(glue) + after;
1282 }
1283 } else if(val.lower() == "split") {
1284 if(arg_list.count() < 2 || arg_list.count() > 3) {
1285 fprintf(stderr, "%s:%d split(var, sep, join) requires three arguments\n",
1286 parser.file.latin1(), parser.line_no);
1287 } else {
1288 QString sep = arg_list[1], join = " ";
1289 if(arg_list.count() == 3)
1290 join = arg_list[2];
1291 QStringList var = place[varMap(arg_list.first())];
1292 for(QStringList::Iterator vit = var.begin(); vit != var.end(); ++vit) {
1293 QStringList lst = QStringList::split(sep, (*vit));
1294 for(QStringList::Iterator spltit = lst.begin(); spltit != lst.end(); ++spltit) {
1295 if(!replacement.isEmpty())
1296 replacement += join;
1297 replacement += (*spltit);
1298 }
1299 }
1300 }
1301 } else if(val.lower() == "find") {
1302 if(arg_list.count() != 2) {
1303 fprintf(stderr, "%s:%d find(var, str) requires two arguments\n",
1304 parser.file.latin1(), parser.line_no);
1305 } else {
1306 QRegExp regx(arg_list[1]);
1307 const QStringList &var = place[varMap(arg_list.first())];
1308 for(QStringList::ConstIterator vit = var.begin();
1309 vit != var.end(); ++vit) {
1310 if(regx.search(*vit) != -1) {
1311 if(!replacement.isEmpty())
1312 replacement += " ";
1313 replacement += (*vit);
1314 }
1315 }
1316 }
1317 } else if(val.lower() == "system") {
1318 if(arg_list.count() != 1) {
1319 fprintf(stderr, "%s:%d system(execut) requires one argument\n",
1320 parser.file.latin1(), parser.line_no);
1321 } else {
1322 char buff[256];
1323 FILE *proc = QT_POPEN(arg_list.join(" ").latin1(), "r");
1324 while(proc && !feof(proc)) {
1325 int read_in = int(fread(buff, 1, 255, proc));
1326 if(!read_in)
1327 break;
1328 for(int i = 0; i < read_in; i++) {
1329 if(buff[i] == '\n' || buff[i] == '\t')
1330 buff[i] = ' ';
1331 }
1332 buff[read_in] = '\0';
1333 replacement += buff;
1334 }
1335 }
1336 } else if(val.lower() == "files") {
1337 if(arg_list.count() != 1) {
1338 fprintf(stderr, "%s:%d files(pattern) requires one argument\n",
1339 parser.file.latin1(), parser.line_no);
1340 } else {
1341 QString dir, regex = arg_list[0];
1342 regex = Option::fixPathToLocalOS(regex);
1343 regex.replace("\"", "");
1344 if(regex.findRev(QDir::separator()) != -1) {
1345 dir = regex.left(regex.findRev(QDir::separator()) + 1);
1346 regex = regex.right(regex.length() - dir.length());
1347 }
1348 QDir d(dir, regex);
1349 for(int i = 0; i < (int)d.count(); i++) {
1350 if(!replacement.isEmpty())
1351 replacement += " ";
1352 replacement += dir + d[i];
1353 }
1354 }
1355 } else if(val.lower() == "prompt") {
1356 if(arg_list.count() != 1) {
1357 fprintf(stderr, "%s:%d prompt(question) requires one argument\n",
1358 parser.file.latin1(), parser.line_no);
1359 } else if(projectFile() == "-") {
1360 fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n",
1361 parser.file.latin1(), parser.line_no);
1362 } else {
1363 QString msg = arg_list.first();
1364 if((msg.startsWith("\"") || msg.startsWith("'")) && msg.endsWith(msg.left(1)))
1365 msg = msg.mid(1, msg.length()-2);
1366 msg.replace(QString("${QMAKE_FILE}"), parser.file.latin1());
1367 msg.replace(QString("${QMAKE_LINE_NUMBER}"), QString::number(parser.line_no));
1368 msg.replace(QString("${QMAKE_DATE}"), QDateTime::currentDateTime().toString());
1369 doVariableReplace(msg, place);
1370 fixEnvVariables(msg);
1371 if(!msg.endsWith("?"))
1372 msg += "?";
1373 fprintf(stderr, "Project %s: %s ", val.upper().latin1(), msg.latin1());
1374
1375 QFile qfile;
1376 if(qfile.open(IO_ReadOnly, stdin)) {
1377 QTextStream t(&qfile);
1378 replacement = t.readLine();
1379 }
1380 }
1381 } else {
1382 fprintf(stderr, "%s:%d: Unknown replace function: %s\n",
1383 parser.file.latin1(), parser.line_no, val.latin1());
1384 }
1385 }
1386 //actually do replacement now..
1387 int mlen = var_incr - var_begin;
1388 debug_msg(2, "Project Parser [var replace]: '%s' :: %s -> %s", str.latin1(),
1389 str.mid(var_begin, mlen).latin1(), replacement.latin1());
1390 str.replace(var_begin, mlen, replacement);
1391 var_begin += replacement.length();
1392 }
1393 return str;
1394 }
1395