source: trunk/qmake/project.cpp@ 89

Last change on this file since 89 was 82, checked in by dmik, 19 years ago

TARGET is no more necessary to be 'qt' or 'qt-mt' when building the Qt library; the 'build_qt' CONFIG keyword is used to indicate this instead.

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