Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the qmake application of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "project.h" |
43 | #include "property.h" |
44 | #include "option.h" |
45 | #include "cachekeys.h" |
46 | #include "generators/metamakefile.h" |
47 | |
48 | #include <qdatetime.h> |
49 | #include <qfile.h> |
50 | #include <qfileinfo.h> |
51 | #include <qdir.h> |
52 | #include <qregexp.h> |
53 | #include <qtextstream.h> |
54 | #include <qstack.h> |
55 | #include <qhash.h> |
56 | #include <qdebug.h> |
57 | #ifdef Q_OS_UNIX |
58 | #include <unistd.h> |
59 | #include <sys/utsname.h> |
60 | #elif defined(Q_OS_WIN32) |
61 | #include <windows.h> |
62 | #endif |
63 | #include <stdio.h> |
64 | #include <stdlib.h> |
65 | |
66 | // Included from tools/shared |
67 | #include <symbian/epocroot_p.h> |
68 | |
69 | #ifdef Q_OS_WIN32 |
70 | #define QT_POPEN _popen |
71 | #define QT_PCLOSE _pclose |
72 | #else |
73 | #define QT_POPEN popen |
74 | #define QT_PCLOSE pclose |
75 | #endif |
76 | |
77 | QT_BEGIN_NAMESPACE |
78 | |
79 | //expand functions |
80 | enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST, |
81 | E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, |
82 | E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND, |
83 | E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_REPLACE, |
84 | E_SIZE, E_GENERATE_UID }; |
85 | QMap<QString, ExpandFunc> qmake_expandFunctions() |
86 | { |
87 | static QMap<QString, ExpandFunc> *qmake_expand_functions = 0; |
88 | if(!qmake_expand_functions) { |
89 | qmake_expand_functions = new QMap<QString, ExpandFunc>; |
90 | qmakeAddCacheClear(qmakeDeleteCacheClear<QMap<QString, ExpandFunc> >, (void**)&qmake_expand_functions); |
91 | qmake_expand_functions->insert("member", E_MEMBER); |
92 | qmake_expand_functions->insert("first", E_FIRST); |
93 | qmake_expand_functions->insert("last", E_LAST); |
94 | qmake_expand_functions->insert("cat", E_CAT); |
95 | qmake_expand_functions->insert("fromfile", E_FROMFILE); |
96 | qmake_expand_functions->insert("eval", E_EVAL); |
97 | qmake_expand_functions->insert("list", E_LIST); |
98 | qmake_expand_functions->insert("sprintf", E_SPRINTF); |
99 | qmake_expand_functions->insert("join", E_JOIN); |
100 | qmake_expand_functions->insert("split", E_SPLIT); |
101 | qmake_expand_functions->insert("basename", E_BASENAME); |
102 | qmake_expand_functions->insert("dirname", E_DIRNAME); |
103 | qmake_expand_functions->insert("section", E_SECTION); |
104 | qmake_expand_functions->insert("find", E_FIND); |
105 | qmake_expand_functions->insert("system", E_SYSTEM); |
106 | qmake_expand_functions->insert("unique", E_UNIQUE); |
107 | qmake_expand_functions->insert("quote", E_QUOTE); |
108 | qmake_expand_functions->insert("escape_expand", E_ESCAPE_EXPAND); |
109 | qmake_expand_functions->insert("upper", E_UPPER); |
110 | qmake_expand_functions->insert("lower", E_LOWER); |
111 | qmake_expand_functions->insert("re_escape", E_RE_ESCAPE); |
112 | qmake_expand_functions->insert("files", E_FILES); |
113 | qmake_expand_functions->insert("prompt", E_PROMPT); |
114 | qmake_expand_functions->insert("replace", E_REPLACE); |
115 | qmake_expand_functions->insert("size", E_SIZE); |
116 | qmake_expand_functions->insert("generate_uid", E_GENERATE_UID); |
117 | } |
118 | return *qmake_expand_functions; |
119 | } |
120 | //replace functions |
121 | enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, |
122 | T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, |
123 | T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE, |
124 | T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_ERROR, |
125 | T_MESSAGE, T_WARNING, T_IF, T_OPTION }; |
126 | QMap<QString, TestFunc> qmake_testFunctions() |
127 | { |
128 | static QMap<QString, TestFunc> *qmake_test_functions = 0; |
129 | if(!qmake_test_functions) { |
130 | qmake_test_functions = new QMap<QString, TestFunc>; |
131 | qmake_test_functions->insert("requires", T_REQUIRES); |
132 | qmake_test_functions->insert("greaterThan", T_GREATERTHAN); |
133 | qmake_test_functions->insert("lessThan", T_LESSTHAN); |
134 | qmake_test_functions->insert("equals", T_EQUALS); |
135 | qmake_test_functions->insert("isEqual", T_EQUALS); |
136 | qmake_test_functions->insert("exists", T_EXISTS); |
137 | qmake_test_functions->insert("export", T_EXPORT); |
138 | qmake_test_functions->insert("clear", T_CLEAR); |
139 | qmake_test_functions->insert("unset", T_UNSET); |
140 | qmake_test_functions->insert("eval", T_EVAL); |
141 | qmake_test_functions->insert("CONFIG", T_CONFIG); |
142 | qmake_test_functions->insert("if", T_IF); |
143 | qmake_test_functions->insert("isActiveConfig", T_CONFIG); |
144 | qmake_test_functions->insert("system", T_SYSTEM); |
145 | qmake_test_functions->insert("return", T_RETURN); |
146 | qmake_test_functions->insert("break", T_BREAK); |
147 | qmake_test_functions->insert("next", T_NEXT); |
148 | qmake_test_functions->insert("defined", T_DEFINED); |
149 | qmake_test_functions->insert("contains", T_CONTAINS); |
150 | qmake_test_functions->insert("infile", T_INFILE); |
151 | qmake_test_functions->insert("count", T_COUNT); |
152 | qmake_test_functions->insert("isEmpty", T_ISEMPTY); |
153 | qmake_test_functions->insert("include", T_INCLUDE); |
154 | qmake_test_functions->insert("load", T_LOAD); |
155 | qmake_test_functions->insert("debug", T_DEBUG); |
156 | qmake_test_functions->insert("error", T_ERROR); |
157 | qmake_test_functions->insert("message", T_MESSAGE); |
158 | qmake_test_functions->insert("warning", T_WARNING); |
159 | qmake_test_functions->insert("option", T_OPTION); |
160 | } |
161 | return *qmake_test_functions; |
162 | } |
163 | |
164 | struct parser_info { |
165 | QString file; |
166 | int line_no; |
167 | bool from_file; |
168 | } parser; |
169 | |
170 | static QString remove_quotes(const QString &arg) |
171 | { |
172 | const ushort SINGLEQUOTE = |
173 | const ushort DOUBLEQUOTE = |
174 | |
175 | const QChar *arg_data = arg.data(); |
176 | const ushort first = arg_data->unicode(); |
177 | const int arg_len = arg.length(); |
178 | if(first == SINGLEQUOTE || first == DOUBLEQUOTE) { |
179 | const ushort last = (arg_data+arg_len-1)->unicode(); |
180 | if(last == first) |
181 | return arg.mid(1, arg_len-2); |
182 | } |
183 | return arg; |
184 | } |
185 | |
186 | static QString varMap(const QString &x) |
187 | { |
188 | QString ret(x); |
189 | if(ret == "INTERFACES") |
190 | ret = "FORMS"; |
191 | else if(ret == "QMAKE_POST_BUILD") |
192 | ret = "QMAKE_POST_LINK"; |
193 | else if(ret == "TARGETDEPS") |
194 | ret = "POST_TARGETDEPS"; |
195 | else if(ret == "LIBPATH") |
196 | ret = "QMAKE_LIBDIR"; |
197 | else if(ret == "QMAKE_EXT_MOC") |
198 | ret = "QMAKE_EXT_CPP_MOC"; |
199 | else if(ret == "QMAKE_MOD_MOC") |
200 | ret = "QMAKE_H_MOD_MOC"; |
201 | else if(ret == "QMAKE_LFLAGS_SHAPP") |
202 | ret = "QMAKE_LFLAGS_APP"; |
203 | else if(ret == "PRECOMPH") |
204 | ret = "PRECOMPILED_HEADER"; |
205 | else if(ret == "PRECOMPCPP") |
206 | ret = "PRECOMPILED_SOURCE"; |
207 | else if(ret == "INCPATH") |
208 | ret = "INCLUDEPATH"; |
209 | else if(ret == "QMAKE_EXTRA_WIN_COMPILERS"|| ret == "QMAKE_EXTRA_UNIX_COMPILERS") |
210 | ret = "QMAKE_EXTRA_COMPILERS"; |
211 | else if(ret == "QMAKE_EXTRA_WIN_TARGETS"|| ret == "QMAKE_EXTRA_UNIX_TARGETS") |
212 | ret = "QMAKE_EXTRA_TARGETS"; |
213 | else if(ret == "QMAKE_EXTRA_UNIX_INCLUDES") |
214 | ret = "QMAKE_EXTRA_INCLUDES"; |
215 | else if(ret == "QMAKE_EXTRA_UNIX_VARIABLES") |
216 | ret = "QMAKE_EXTRA_VARIABLES"; |
217 | else if(ret == "QMAKE_RPATH") |
218 | ret = "QMAKE_LFLAGS_RPATH"; |
219 | else if(ret == "QMAKE_FRAMEWORKDIR") |
220 | ret = "QMAKE_FRAMEWORKPATH"; |
221 | else if(ret == "QMAKE_FRAMEWORKDIR_FLAGS") |
222 | ret = "QMAKE_FRAMEWORKPATH_FLAGS"; |
223 | else |
224 | return ret; |
225 | warn_msg(WarnDeprecated, "%s:%d: Variable %s is deprecated; use %s instead.", |
226 | parser.file.toLatin1().constData(), parser.line_no, |
227 | x.toLatin1().constData(), ret.toLatin1().constData()); |
228 | return ret; |
229 | } |
230 | |
231 | static QStringList split_arg_list(const QString ¶ms) |
232 | { |
233 | int quote = 0; |
234 | QStringList args; |
235 | |
236 | const ushort LPAREN = |
237 | const ushort RPAREN = |
238 | const ushort SINGLEQUOTE = |
239 | const ushort DOUBLEQUOTE = |
240 | const ushort BACKSLASH = |
241 | const ushort COMMA = |
242 | const ushort SPACE = |
243 | //const ushort TAB = '\t'; |
244 | |
245 | const QChar *params_data = params.data(); |
246 | const int params_len = params.length(); |
247 | for(int last = 0; ;) { |
248 | while(last < params_len && (params_data[last].unicode() == SPACE |
249 | /*|| params_data[last].unicode() == TAB*/)) |
250 | ++last; |
251 | for(int x = last, parens = 0; ; x++) { |
252 | if(x == params_len) { |
253 | while(x > last && params_data[x-1].unicode() == SPACE) |
254 | --x; |
255 | args << params.mid(last, x - last); |
256 | // Could do a check for unmatched parens here, but split_value_list() |
257 | // is called on all our output, so mistakes will be caught anyway. |
258 | return args; |
259 | } |
260 | ushort unicode = params_data[x].unicode(); |
261 | if(x != (int)params_len-1 && unicode == BACKSLASH && |
262 | (params_data[x+1].unicode() == SINGLEQUOTE || params_data[x+1].unicode() == DOUBLEQUOTE)) { |
263 | x++; //get that 'escape' |
264 | } else if(quote && unicode == quote) { |
265 | quote = 0; |
266 | } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { |
267 | quote = unicode; |
268 | } else if(unicode == RPAREN) { |
269 | --parens; |
270 | } else if(unicode == LPAREN) { |
271 | ++parens; |
272 | } |
273 | if(!parens && !quote && unicode == COMMA) { |
274 | int prev = last; |
275 | last = x+1; |
276 | while(x > prev && params_data[x-1].unicode() == SPACE) |
277 | --x; |
278 | args << params.mid(prev, x - prev); |
279 | break; |
280 | } |
281 | } |
282 | } |
283 | } |
284 | |
285 | static QStringList split_value_list(const QString &vals) |
286 | { |
287 | QString build; |
288 | QStringList ret; |
289 | ushort quote = 0; |
290 | int parens = 0; |
291 | |
292 | const ushort LPAREN = |
293 | const ushort RPAREN = |
294 | const ushort SINGLEQUOTE = |
295 | const ushort DOUBLEQUOTE = |
296 | const ushort BACKSLASH = |
297 | |
298 | ushort unicode; |
299 | const QChar *vals_data = vals.data(); |
300 | const int vals_len = vals.length(); |
301 | for(int x = 0; x < vals_len; x++) { |
302 | unicode = vals_data[x].unicode(); |
303 | if(x != (int)vals_len-1 && unicode == BACKSLASH && |
304 | (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) { |
305 | build += vals_data[x++]; //get that 'escape' |
306 | } else if(quote && unicode == quote) { |
307 | quote = 0; |
308 | } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { |
309 | quote = unicode; |
310 | } else if(unicode == RPAREN) { |
311 | --parens; |
312 | } else if(unicode == LPAREN) { |
313 | ++parens; |
314 | } |
315 | |
316 | if(!parens && !quote && (vals_data[x] == Option::field_sep)) { |
317 | ret << build; |
318 | build.clear(); |
319 | } else { |
320 | build += vals_data[x]; |
321 | } |
322 | } |
323 | if(!build.isEmpty()) |
324 | ret << build; |
325 | if (parens) |
326 | warn_msg(WarnDeprecated, "%s:%d: Unmatched parentheses are deprecated.", |
327 | parser.file.toLatin1().constData(), parser.line_no); |
328 | // Could do a check for unmatched quotes here, but doVariableReplaceExpand() |
329 | // is called on all our output, so mistakes will be caught anyway. |
330 | return ret; |
331 | } |
332 | |
333 | //just a parsable entity |
334 | struct ParsableBlock |
335 | { |
336 | ParsableBlock() : ref_cnt(1) { } |
337 | virtual ~ParsableBlock() { } |
338 | |
339 | struct Parse { |
340 | QString text; |
341 | parser_info pi; |
342 | Parse(const QString &t) : text(t){ pi = parser; } |
343 | }; |
344 | QList<Parse> parselist; |
345 | |
346 | inline int ref() { return ++ref_cnt; } |
347 | inline int deref() { return --ref_cnt; } |
348 | |
349 | protected: |
350 | int ref_cnt; |
351 | virtual bool continueBlock() = 0; |
352 | bool eval(QMakeProject *p, QMap<QString, QStringList> &place); |
353 | }; |
354 | |
355 | bool ParsableBlock::eval(QMakeProject *p, QMap<QString, QStringList> &place) |
356 | { |
357 | //save state |
358 | parser_info pi = parser; |
359 | const int block_count = p->scope_blocks.count(); |
360 | |
361 | //execute |
362 | bool ret = true; |
363 | for(int i = 0; i < parselist.count(); i++) { |
364 | parser = parselist.at(i).pi; |
365 | if(!(ret = p->parse(parselist.at(i).text, place)) || !continueBlock()) |
366 | break; |
367 | } |
368 | |
369 | //restore state |
370 | parser = pi; |
371 | while(p->scope_blocks.count() > block_count) |
372 | p->scope_blocks.pop(); |
373 | return ret; |
374 | } |
375 | |
376 | //defined functions |
377 | struct FunctionBlock : public ParsableBlock |
378 | { |
379 | FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { } |
380 | |
381 | QMap<QString, QStringList> vars; |
382 | QMap<QString, QStringList> *calling_place; |
383 | QStringList return_value; |
384 | int scope_level; |
385 | bool cause_return; |
386 | |
387 | bool exec(const QList<QStringList> &args, |
388 | QMakeProject *p, QMap<QString, QStringList> &place, QStringList &functionReturn); |
389 | virtual bool continueBlock() { return !cause_return; } |
390 | }; |
391 | |
392 | bool FunctionBlock::exec(const QList<QStringList> &args, |
393 | QMakeProject *proj, QMap<QString, QStringList> &place, |
394 | QStringList &functionReturn) |
395 | { |
396 | //save state |
397 | #if 1 |
398 | calling_place = &place; |
399 | #else |
400 | calling_place = &proj->variables(); |
401 | #endif |
402 | return_value.clear(); |
403 | cause_return = false; |
404 | |
405 | //execute |
406 | #if 0 |
407 | vars = proj->variables(); // should be place so that local variables can be inherited |
408 | #else |
409 | vars = place; |
410 | #endif |
411 | vars["ARGS"].clear(); |
412 | for(int i = 0; i < args.count(); i++) { |
413 | vars["ARGS"] += args[i]; |
414 | vars[QString::number(i+1)] = args[i]; |
415 | } |
416 | bool ret = ParsableBlock::eval(proj, vars); |
417 | functionReturn = return_value; |
418 | |
419 | //restore state |
420 | calling_place = 0; |
421 | return_value.clear(); |
422 | vars.clear(); |
423 | return ret; |
424 | } |
425 | |
426 | //loops |
427 | struct IteratorBlock : public ParsableBlock |
428 | { |
429 | IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { } |
430 | |
431 | int scope_level; |
432 | |
433 | struct Test { |
434 | QString func; |
435 | QStringList args; |
436 | bool invert; |
437 | parser_info pi; |
438 | Test(const QString &f, QStringList &a, bool i) : func(f), args(a), invert(i) { pi = parser; } |
439 | }; |
440 | QList<Test> test; |
441 | |
442 | QString variable; |
443 | |
444 | bool loop_forever, cause_break, cause_next; |
445 | QStringList list; |
446 | |
447 | bool exec(QMakeProject *p, QMap<QString, QStringList> &place); |
448 | virtual bool continueBlock() { return !cause_next && !cause_break; } |
449 | }; |
450 | bool IteratorBlock::exec(QMakeProject *p, QMap<QString, QStringList> &place) |
451 | { |
452 | bool ret = true; |
453 | QStringList::Iterator it; |
454 | if(!loop_forever) |
455 | it = list.begin(); |
456 | int iterate_count = 0; |
457 | //save state |
458 | IteratorBlock *saved_iterator = p->iterator; |
459 | p->iterator = this; |
460 | |
461 | //do the loop |
462 | while(loop_forever || it != list.end()) { |
463 | cause_next = cause_break = false; |
464 | if(!loop_forever && (*it).isEmpty()) { //ignore empty items |
465 | ++it; |
466 | continue; |
467 | } |
468 | |
469 | //set up the loop variable |
470 | QStringList va; |
471 | if(!variable.isEmpty()) { |
472 | va = place[variable]; |
473 | if(loop_forever) |
474 | place[variable] = QStringList(QString::number(iterate_count)); |
475 | else |
476 | place[variable] = QStringList(*it); |
477 | } |
478 | //do the iterations |
479 | bool succeed = true; |
480 | for(QList<Test>::Iterator test_it = test.begin(); test_it != test.end(); ++test_it) { |
481 | parser = (*test_it).pi; |
482 | succeed = p->doProjectTest((*test_it).func, (*test_it).args, place); |
483 | if((*test_it).invert) |
484 | succeed = !succeed; |
485 | if(!succeed) |
486 | break; |
487 | } |
488 | if(succeed) |
489 | ret = ParsableBlock::eval(p, place); |
490 | //restore the variable in the map |
491 | if(!variable.isEmpty()) |
492 | place[variable] = va; |
493 | //loop counters |
494 | if(!loop_forever) |
495 | ++it; |
496 | iterate_count++; |
497 | if(!ret || cause_break) |
498 | break; |
499 | } |
500 | |
501 | //restore state |
502 | p->iterator = saved_iterator; |
503 | return ret; |
504 | } |
505 | |
506 | QMakeProject::ScopeBlock::~ScopeBlock() |
507 | { |
508 | #if 0 |
509 | if(iterate) |
510 | delete iterate; |
511 | #endif |
512 | } |
513 | |
514 | static void qmake_error_msg(const QString &msg) |
515 | { |
516 | fprintf(stderr, "%s:%d: %s\n", parser.file.toLatin1().constData(), parser.line_no, |
517 | msg.toLatin1().constData()); |
518 | } |
519 | |
520 | /* |
521 | 1) environment variable QMAKEFEATURES (as separated by colons) |
522 | 2) property variable QMAKEFEATURES (as separated by colons) |
523 | 3) <project_root> (where .qmake.cache lives) + FEATURES_DIR |
524 | 4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR |
525 | 5) your QMAKESPEC/features dir |
526 | 6) your data_install/mkspecs/FEATURES_DIR |
527 | 7) your QMAKESPEC/../FEATURES_DIR dir |
528 | |
529 | FEATURES_DIR is defined as: |
530 | |
531 | 1) features/(unix|win32|macx)/ |
532 | 2) features/ |
533 | */ |
534 | QStringList qmake_feature_paths(QMakeProperty *prop=0) |
535 | { |
536 | QStringList concat; |
537 | { |
538 | const QString base_concat = QDir::separator() + QString("features"); |
539 | switch(Option::target_mode) { |
540 | case Option::TARG_MACX_MODE: //also a unix |
541 | concat << base_concat + QDir::separator() + "mac"; |
542 | concat << base_concat + QDir::separator() + "macx"; |
543 | concat << base_concat + QDir::separator() + "unix"; |
544 | break; |
545 | default: // Can't happen, just make the compiler shut up |
546 | case Option::TARG_UNIX_MODE: |
547 | concat << base_concat + QDir::separator() + "unix"; |
548 | break; |
549 | case Option::TARG_WIN_MODE: |
550 | concat << base_concat + QDir::separator() + "win32"; |
551 | break; |
552 | case Option::TARG_SYMBIAN_MODE: |
553 | concat << base_concat + QDir::separator() + "symbian"; |
554 | break; |
555 | } |
556 | concat << base_concat; |
557 | } |
558 | const QString mkspecs_concat = QDir::separator() + QString("mkspecs"); |
559 | QStringList feature_roots; |
560 | QByteArray mkspec_path = qgetenv("QMAKEFEATURES"); |
561 | if(!mkspec_path.isNull()) |
562 | feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path)); |
563 | if(prop) |
564 | feature_roots += splitPathList(prop->value("QMAKEFEATURES")); |
565 | if(!Option::mkfile::cachefile.isEmpty()) { |
566 | QString path; |
567 | int last_slash = Option::mkfile::cachefile.lastIndexOf(QDir::separator()); |
568 | if(last_slash != -1) |
569 | path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash), false); |
570 | for(QStringList::Iterator concat_it = concat.begin(); |
571 | concat_it != concat.end(); ++concat_it) |
572 | feature_roots << (path + (*concat_it)); |
573 | } |
574 | QByteArray qmakepath = qgetenv("QMAKEPATH"); |
575 | if (!qmakepath.isNull()) { |
576 | const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath)); |
577 | for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { |
578 | for(QStringList::Iterator concat_it = concat.begin(); |
579 | concat_it != concat.end(); ++concat_it) |
580 | feature_roots << ((*it) + mkspecs_concat + (*concat_it)); |
581 | } |
582 | } |
583 | if(!Option::mkfile::qmakespec.isEmpty()) |
584 | feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features"; |
585 | if(!Option::mkfile::qmakespec.isEmpty()) { |
586 | QFileInfo specfi(Option::mkfile::qmakespec); |
587 | QDir specdir(specfi.absoluteFilePath()); |
588 | while(!specdir.isRoot()) { |
589 | if(!specdir.cdUp() || specdir.isRoot()) |
590 | break; |
591 | if(QFile::exists(specdir.path() + QDir::separator() + "features")) { |
592 | for(QStringList::Iterator concat_it = concat.begin(); |
593 | concat_it != concat.end(); ++concat_it) |
594 | feature_roots << (specdir.path() + (*concat_it)); |
595 | break; |
596 | } |
597 | } |
598 | } |
599 | for(QStringList::Iterator concat_it = concat.begin(); |
600 | concat_it != concat.end(); ++concat_it) |
601 | feature_roots << (QLibraryInfo::location(QLibraryInfo::PrefixPath) + |
602 | mkspecs_concat + (*concat_it)); |
603 | for(QStringList::Iterator concat_it = concat.begin(); |
604 | concat_it != concat.end(); ++concat_it) |
605 | feature_roots << (QLibraryInfo::location(QLibraryInfo::DataPath) + |
606 | mkspecs_concat + (*concat_it)); |
607 | return feature_roots; |
608 | } |
609 | |
610 | QStringList qmake_mkspec_paths() |
611 | { |
612 | QStringList ret; |
613 | const QString concat = QDir::separator() + QString("mkspecs"); |
614 | QByteArray qmakepath = qgetenv("QMAKEPATH"); |
615 | if (!qmakepath.isEmpty()) { |
616 | const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath)); |
617 | for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) |
618 | ret << ((*it) + concat); |
619 | } |
620 | ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat; |
621 | |
622 | return ret; |
623 | } |
624 | |
625 | QMakeProject::~QMakeProject() |
626 | { |
627 | if(own_prop) |
628 | delete prop; |
629 | for(QMap<QString, FunctionBlock*>::iterator it = replaceFunctions.begin(); it != replaceFunctions.end(); ++it) { |
630 | if(!it.value()->deref()) |
631 | delete it.value(); |
632 | } |
633 | replaceFunctions.clear(); |
634 | for(QMap<QString, FunctionBlock*>::iterator it = testFunctions.begin(); it != testFunctions.end(); ++it) { |
635 | if(!it.value()->deref()) |
636 | delete it.value(); |
637 | } |
638 | testFunctions.clear(); |
639 | } |
640 | |
641 | |
642 | void |
643 | QMakeProject::init(QMakeProperty *p, const QMap<QString, QStringList> *vars) |
644 | { |
645 | if(vars) |
646 | base_vars = *vars; |
647 | if(!p) { |
648 | prop = new QMakeProperty; |
649 | own_prop = true; |
650 | } else { |
651 | prop = p; |
652 | own_prop = false; |
653 | } |
654 | recursive = false; |
655 | reset(); |
656 | } |
657 | |
658 | QMakeProject::QMakeProject(QMakeProject *p, const QMap<QString, QStringList> *vars) |
659 | { |
660 | init(p->properties(), vars ? vars : &p->variables()); |
661 | for(QMap<QString, FunctionBlock*>::iterator it = p->replaceFunctions.begin(); it != p->replaceFunctions.end(); ++it) { |
662 | it.value()->ref(); |
663 | replaceFunctions.insert(it.key(), it.value()); |
664 | } |
665 | for(QMap<QString, FunctionBlock*>::iterator it = p->testFunctions.begin(); it != p->testFunctions.end(); ++it) { |
666 | it.value()->ref(); |
667 | testFunctions.insert(it.key(), it.value()); |
668 | } |
669 | } |
670 | |
671 | void |
672 | QMakeProject::reset() |
673 | { |
674 | // scope_blocks starts with one non-ignoring entity |
675 | scope_blocks.clear(); |
676 | scope_blocks.push(ScopeBlock()); |
677 | iterator = 0; |
678 | function = 0; |
679 | backslashWarned = false; |
680 | } |
681 | |
682 | bool |
683 | QMakeProject::parse(const QString &t, QMap<QString, QStringList> &place, int numLines) |
684 | { |
685 | // To preserve the integrity of any UTF-8 characters in .pro file, temporarily replace the |
686 | // non-breaking space (0xA0) characters with another non-space character, so that |
687 | // QString::simplified() call will not replace it with space. |
688 | // Note: There won't be any two byte characters in .pro files, so 0x10A0 should be a safe |
689 | // replacement character. |
690 | static QChar nbsp(0xA0); |
691 | static QChar nbspFix(0x01A0); |
692 | QString s; |
693 | if (t.indexOf(nbsp) != -1) { |
694 | s = t; |
695 | s.replace(nbsp, nbspFix); |
696 | s = s.simplified(); |
697 | s.replace(nbspFix, nbsp); |
698 | } else { |
699 | s = t.simplified(); |
700 | } |
701 | |
702 | int hash_mark = s.indexOf("#"); |
703 | if(hash_mark != -1) //good bye comments |
704 | s = s.left(hash_mark); |
705 | if(s.isEmpty()) // blank_line |
706 | return true; |
707 | |
708 | if(scope_blocks.top().ignore) { |
709 | bool continue_parsing = false; |
710 | // adjust scope for each block which appears on a single line |
711 | for(int i = 0; i < s.length(); i++) { |
712 | if(s[i] == |
713 | scope_blocks.push(ScopeBlock(true)); |
714 | } else if(s[i] == |
715 | if(scope_blocks.count() == 1) { |
716 | fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no); |
717 | return false; |
718 | } |
719 | ScopeBlock sb = scope_blocks.pop(); |
720 | if(sb.iterate) { |
721 | sb.iterate->exec(this, place); |
722 | delete sb.iterate; |
723 | sb.iterate = 0; |
724 | } |
725 | if(!scope_blocks.top().ignore) { |
726 | debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(), |
727 | parser.line_no, scope_blocks.count()+1); |
728 | s = s.mid(i+1).trimmed(); |
729 | continue_parsing = !s.isEmpty(); |
730 | break; |
731 | } |
732 | } |
733 | } |
734 | if(!continue_parsing) { |
735 | debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.", |
736 | parser.file.toLatin1().constData(), parser.line_no); |
737 | return true; |
738 | } |
739 | } |
740 | |
741 | if(function) { |
742 | QString append; |
743 | int d_off = 0; |
744 | const QChar *d = s.unicode(); |
745 | bool function_finished = false; |
746 | while(d_off < s.length()) { |
747 | if(*(d+d_off) == QLatin1Char( |
748 | function->scope_level--; |
749 | if(!function->scope_level) { |
750 | function_finished = true; |
751 | break; |
752 | } |
753 | } else if(*(d+d_off) == QLatin1Char( |
754 | function->scope_level++; |
755 | } |
756 | append += *(d+d_off); |
757 | ++d_off; |
758 | } |
759 | if(!append.isEmpty()) |
760 | function->parselist.append(IteratorBlock::Parse(append)); |
761 | if(function_finished) { |
762 | function = 0; |
763 | s = QString(d+d_off, s.length()-d_off); |
764 | } else { |
765 | return true; |
766 | } |
767 | } else if(IteratorBlock *it = scope_blocks.top().iterate) { |
768 | QString append; |
769 | int d_off = 0; |
770 | const QChar *d = s.unicode(); |
771 | bool iterate_finished = false; |
772 | while(d_off < s.length()) { |
773 | if(*(d+d_off) == QLatin1Char( |
774 | it->scope_level--; |
775 | if(!it->scope_level) { |
776 | iterate_finished = true; |
777 | break; |
778 | } |
779 | } else if(*(d+d_off) == QLatin1Char( |
780 | it->scope_level++; |
781 | } |
782 | append += *(d+d_off); |
783 | ++d_off; |
784 | } |
785 | if(!append.isEmpty()) |
786 | scope_blocks.top().iterate->parselist.append(IteratorBlock::Parse(append)); |
787 | if(iterate_finished) { |
788 | scope_blocks.top().iterate = 0; |
789 | bool ret = it->exec(this, place); |
790 | delete it; |
791 | if(!ret) |
792 | return false; |
793 | s = s.mid(d_off); |
794 | } else { |
795 | return true; |
796 | } |
797 | } |
798 | |
799 | QString scope, var, op; |
800 | QStringList val; |
801 | #define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o |
802 | const QChar *d = s.unicode(); |
803 | int d_off = 0; |
804 | SKIP_WS(d, d_off, s.length()); |
805 | IteratorBlock *iterator = 0; |
806 | bool scope_failed = false, else_line = false, or_op=false; |
807 | QChar quote = 0; |
808 | int parens = 0, scope_count=0, start_block = 0; |
809 | while(d_off < s.length()) { |
810 | if(!parens) { |
811 | if(*(d+d_off) == QLatin1Char( |
812 | break; |
813 | if(*(d+d_off) == QLatin1Char( |
814 | *(d+d_off) == QLatin1Char( |
815 | if(*(d+d_off+1) == QLatin1Char( |
816 | break; |
817 | } else if(*(d+d_off+1) == QLatin1Char( |
818 | const QChar *k = d+d_off+1; |
819 | int k_off = 0; |
820 | SKIP_WS(k, k_off, s.length()-d_off); |
821 | if(*(k+k_off) == QLatin1Char( |
822 | QString msg; |
823 | qmake_error_msg(QString(d+d_off, 1) + "must be followed immediately by ="); |
824 | return false; |
825 | } |
826 | } |
827 | } |
828 | } |
829 | |
830 | if(!quote.isNull()) { |
831 | if(*(d+d_off) == quote) |
832 | quote = QChar(); |
833 | } else if(*(d+d_off) == |
834 | ++parens; |
835 | } else if(*(d+d_off) == |
836 | --parens; |
837 | } else if(*(d+d_off) == |
838 | quote = *(d+d_off); |
839 | } |
840 | |
841 | if(!parens && quote.isNull() && |
842 | (*(d+d_off) == QLatin1Char( |
843 | *(d+d_off) == QLatin1Char( |
844 | scope_count++; |
845 | scope = var.trimmed(); |
846 | if(*(d+d_off) == QLatin1Char( |
847 | scope += *(d+d_off); // need this |
848 | var = ""; |
849 | |
850 | bool test = scope_failed; |
851 | if(scope.isEmpty()) { |
852 | test = true; |
853 | } else if(scope.toLower() == "else") { //else is a builtin scope here as it modifies state |
854 | if(scope_count != 1 || scope_blocks.top().else_status == ScopeBlock::TestNone) { |
855 | qmake_error_msg(("Unexpected "+ scope + " ('"+ s + "')").toLatin1()); |
856 | return false; |
857 | } |
858 | else_line = true; |
859 | test = (scope_blocks.top().else_status == ScopeBlock::TestSeek); |
860 | debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser.file.toLatin1().constData(), parser.line_no, |
861 | scope == "else"? "": QString( " ("+ scope + ")").toLatin1().constData(), |
862 | test ? "considered": "excluded"); |
863 | } else { |
864 | QString comp_scope = scope; |
865 | bool invert_test = (comp_scope.at(0) == QLatin1Char( |
866 | if(invert_test) |
867 | comp_scope = comp_scope.mid(1); |
868 | int lparen = comp_scope.indexOf( |
869 | if(or_op == scope_failed) { |
870 | if(lparen != -1) { // if there is an lparen in the scope, it IS a function |
871 | int rparen = comp_scope.lastIndexOf( |
872 | if(rparen == -1) { |
873 | qmake_error_msg("Function missing right paren: "+ comp_scope); |
874 | return false; |
875 | } |
876 | QString func = comp_scope.left(lparen); |
877 | QStringList args = split_arg_list(comp_scope.mid(lparen+1, rparen - lparen - 1)); |
878 | if(function) { |
879 | fprintf(stderr, "%s:%d: No tests can come after a function definition!\n", |
880 | parser.file.toLatin1().constData(), parser.line_no); |
881 | return false; |
882 | } else if(func == "for") { //for is a builtin function here, as it modifies state |
883 | if(args.count() > 2 || args.count() < 1) { |
884 | fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n", |
885 | parser.file.toLatin1().constData(), parser.line_no); |
886 | return false; |
887 | } else if(iterator) { |
888 | fprintf(stderr, "%s:%d unexpected nested for()\n", |
889 | parser.file.toLatin1().constData(), parser.line_no); |
890 | return false; |
891 | } |
892 | |
893 | iterator = new IteratorBlock; |
894 | QString it_list; |
895 | if(args.count() == 1) { |
896 | doVariableReplace(args[0], place); |
897 | it_list = args[0]; |
898 | if(args[0] != "ever") { |
899 | delete iterator; |
900 | iterator = 0; |
901 | fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n", |
902 | parser.file.toLatin1().constData(), parser.line_no); |
903 | return false; |
904 | } |
905 | it_list = "forever"; |
906 | } else if(args.count() == 2) { |
907 | iterator->variable = args[0]; |
908 | doVariableReplace(args[1], place); |
909 | it_list = args[1]; |
910 | } |
911 | QStringList list = place[it_list]; |
912 | if(list.isEmpty()) { |
913 | if(it_list == "forever") { |
914 | iterator->loop_forever = true; |
915 | } else { |
916 | int dotdot = it_list.indexOf(".."); |
917 | if(dotdot != -1) { |
918 | bool ok; |
919 | int start = it_list.left(dotdot).toInt(&ok); |
920 | if(ok) { |
921 | int end = it_list.mid(dotdot+2).toInt(&ok); |
922 | if(ok) { |
923 | if(start < end) { |
924 | for(int i = start; i <= end; i++) |
925 | list << QString::number(i); |
926 | } else { |
927 | for(int i = start; i >= end; i--) |
928 | list << QString::number(i); |
929 | } |
930 | } |
931 | } |
932 | } |
933 | } |
934 | } |
935 | iterator->list = list; |
936 | test = !invert_test; |
937 | } else if(iterator) { |
938 | iterator->test.append(IteratorBlock::Test(func, args, invert_test)); |
939 | test = !invert_test; |
940 | } else if(func == "defineTest"|| func == "defineReplace") { |
941 | if(!function_blocks.isEmpty()) { |
942 | fprintf(stderr, |
943 | "%s:%d: cannot define a function within another definition.\n", |
944 | parser.file.toLatin1().constData(), parser.line_no); |
945 | return false; |
946 | } |
947 | if(args.count() != 1) { |
948 | fprintf(stderr, "%s:%d: %s(function_name) requires one argument.\n", |
949 | parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData()); |
950 | return false; |
951 | } |
952 | QMap<QString, FunctionBlock*> *map = 0; |
953 | if(func == "defineTest") |
954 | map = &testFunctions; |
955 | else |
956 | map = &replaceFunctions; |
957 | #if 0 |
958 | if(!map || map->contains(args[0])) { |
959 | fprintf(stderr, "%s:%d: Function[%s] multiply defined.\n", |
960 | parser.file.toLatin1().constData(), parser.line_no, args[0].toLatin1().constData()); |
961 | return false; |
962 | } |
963 | #endif |
964 | function = new FunctionBlock; |
965 | map->insert(args[0], function); |
966 | test = true; |
967 | } else { |
968 | test = doProjectTest(func, args, place); |
969 | if(*(d+d_off) == QLatin1Char( |
970 | if(invert_test) |
971 | test = !test; |
972 | scope_blocks.top().else_status = |
973 | (test ? ScopeBlock::TestFound : ScopeBlock::TestSeek); |
974 | return true; // assume we are done |
975 | } |
976 | } |
977 | } else { |
978 | QString cscope = comp_scope.trimmed(); |
979 | doVariableReplace(cscope, place); |
980 | test = isActiveConfig(cscope.trimmed(), true, &place); |
981 | } |
982 | if(invert_test) |
983 | test = !test; |
984 | } |
985 | } |
986 | if(!test && !scope_failed) |
987 | debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser.file.toLatin1().constData(), |
988 | parser.line_no, scope.toLatin1().constData()); |
989 | if(test == or_op) |
990 | scope_failed = !test; |
991 | or_op = (*(d+d_off) == QLatin1Char( |
992 | |
993 | if(*(d+d_off) == QLatin1Char( |
994 | start_block++; |
995 | if(iterator) { |
996 | for(int off = 0, braces = 0; true; ++off) { |
997 | if(*(d+d_off+off) == QLatin1Char( |
998 | ++braces; |
999 | else if(*(d+d_off+off) == QLatin1Char( |
1000 | --braces; |
1001 | if(!braces || d_off+off == s.length()) { |
1002 | iterator->parselist.append(s.mid(d_off, off-1)); |
1003 | if(braces > 1) |
1004 | iterator->scope_level += braces-1; |
1005 | d_off += off-1; |
1006 | break; |
1007 | } |
1008 | } |
1009 | } |
1010 | } |
1011 | } else if(!parens && *(d+d_off) == QLatin1Char( |
1012 | if(start_block) { |
1013 | --start_block; |
1014 | } else if(!scope_blocks.count()) { |
1015 | warn_msg(WarnParser, "Possible braces mismatch %s:%d", parser.file.toLatin1().constData(), parser.line_no); |
1016 | } else { |
1017 | if(scope_blocks.count() == 1) { |
1018 | fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no); |
1019 | return false; |
1020 | } |
1021 | debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(), |
1022 | parser.line_no, scope_blocks.count()); |
1023 | ScopeBlock sb = scope_blocks.pop(); |
1024 | if(sb.iterate) |
1025 | sb.iterate->exec(this, place); |
1026 | } |
1027 | } else { |
1028 | var += *(d+d_off); |
1029 | } |
1030 | ++d_off; |
1031 | } |
1032 | var = var.trimmed(); |
1033 | |
1034 | if(!else_line || (else_line && !scope_failed)) |
1035 | scope_blocks.top().else_status = (!scope_failed ? ScopeBlock::TestFound : ScopeBlock::TestSeek); |
1036 | if(start_block) { |
1037 | ScopeBlock next_block(scope_failed); |
1038 | next_block.iterate = iterator; |
1039 | if(iterator) |
1040 | next_block.else_status = ScopeBlock::TestNone; |
1041 | else if(scope_failed) |
1042 | next_block.else_status = ScopeBlock::TestSeek; |
1043 | else |
1044 | next_block.else_status = ScopeBlock::TestFound; |
1045 | scope_blocks.push(next_block); |
1046 | debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser.file.toLatin1().constData(), |
1047 | parser.line_no, scope_blocks.count(), scope_failed, s.toLatin1().constData()); |
1048 | } else if(iterator) { |
1049 | iterator->parselist.append(var+s.mid(d_off)); |
1050 | bool ret = iterator->exec(this, place); |
1051 | delete iterator; |
1052 | return ret; |
1053 | } |
1054 | |
1055 | if((!scope_count && !var.isEmpty()) || (scope_count == 1 && else_line)) |
1056 | scope_blocks.top().else_status = ScopeBlock::TestNone; |
1057 | if(d_off == s.length()) { |
1058 | if(!var.trimmed().isEmpty()) |
1059 | qmake_error_msg(("Parse Error ('"+ s + "')").toLatin1()); |
1060 | return var.isEmpty(); // allow just a scope |
1061 | } |
1062 | |
1063 | SKIP_WS(d, d_off, s.length()); |
1064 | for(; d_off < s.length() && op.indexOf( |
1065 | ; |
1066 | op.replace(QRegExp("\\s"), ""); |
1067 | |
1068 | SKIP_WS(d, d_off, s.length()); |
1069 | QString vals = s.mid(d_off); // vals now contains the space separated list of values |
1070 | int rbraces = vals.count( |
1071 | if(scope_blocks.count() > 1 && rbraces - lbraces == 1 && vals.endsWith( |
1072 | debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(), |
1073 | parser.line_no, scope_blocks.count()); |
1074 | ScopeBlock sb = scope_blocks.pop(); |
1075 | if(sb.iterate) |
1076 | sb.iterate->exec(this, place); |
1077 | vals.truncate(vals.length()-1); |
1078 | } else if(rbraces != lbraces) { |
1079 | warn_msg(WarnParser, "Possible braces mismatch {%s} %s:%d", |
1080 | vals.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no); |
1081 | } |
1082 | if(scope_failed) |
1083 | return true; // oh well |
1084 | #undef SKIP_WS |
1085 | |
1086 | doVariableReplace(var, place); |
1087 | var = varMap(var); //backwards compatibility |
1088 | if(!var.isEmpty() && Option::mkfile::do_preprocess) { |
1089 | static QString last_file("*none*"); |
1090 | if(parser.file != last_file) { |
1091 | fprintf(stdout, "#file %s:%d\n", parser.file.toLatin1().constData(), parser.line_no); |
1092 | last_file = parser.file; |
1093 | } |
1094 | fprintf(stdout, "%s %s %s\n", var.toLatin1().constData(), op.toLatin1().constData(), vals.toLatin1().constData()); |
1095 | } |
1096 | |
1097 | if(vals.contains( |
1098 | warn_msg(WarnParser, "Possible accidental line continuation: {%s} at %s:%d", |
1099 | var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no); |
1100 | |
1101 | QStringList &varlist = place[var]; // varlist is the list in the symbol table |
1102 | |
1103 | if(Option::debug_level >= 1) { |
1104 | QString tmp_vals = vals; |
1105 | doVariableReplace(tmp_vals, place); |
1106 | debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser.file.toLatin1().constData(), parser.line_no, |
1107 | var.toLatin1().constData(), op.toLatin1().constData(), tmp_vals.toLatin1().constData()); |
1108 | } |
1109 | |
1110 | // now do the operation |
1111 | if(op == "~=") { |
1112 | doVariableReplace(vals, place); |
1113 | if(vals.length() < 4 || vals.at(0) != |
1114 | qmake_error_msg(("~= operator only can handle s/// function ('"+ |
1115 | s + "')").toLatin1()); |
1116 | return false; |
1117 | } |
1118 | QChar sep = vals.at(1); |
1119 | QStringList func = vals.split(sep); |
1120 | if(func.count() < 3 || func.count() > 4) { |
1121 | qmake_error_msg(("~= operator only can handle s/// function ('"+ |
1122 | s + "')").toLatin1()); |
1123 | return false; |
1124 | } |
1125 | bool global = false, case_sense = true, quote = false; |
1126 | if(func.count() == 4) { |
1127 | global = func[3].indexOf( |
1128 | case_sense = func[3].indexOf( |
1129 | quote = func[3].indexOf( |
1130 | } |
1131 | QString from = func[1], to = func[2]; |
1132 | if(quote) |
1133 | from = QRegExp::escape(from); |
1134 | QRegExp regexp(from, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive); |
1135 | for(QStringList::Iterator varit = varlist.begin(); varit != varlist.end();) { |
1136 | if((*varit).contains(regexp)) { |
1137 | (*varit) = (*varit).replace(regexp, to); |
1138 | if ((*varit).isEmpty()) |
1139 | varit = varlist.erase(varit); |
1140 | else |
1141 | ++varit; |
1142 | if(!global) |
1143 | break; |
1144 | } else |
1145 | ++varit; |
1146 | } |
1147 | } else { |
1148 | QStringList vallist; |
1149 | { |
1150 | //doVariableReplace(vals, place); |
1151 | QStringList tmp = split_value_list(vals); |
1152 | for(int i = 0; i < tmp.size(); ++i) |
1153 | vallist += doVariableReplaceExpand(tmp[i], place); |
1154 | } |
1155 | |
1156 | if(op == "=") { |
1157 | if(!varlist.isEmpty()) { |
1158 | bool send_warning = false; |
1159 | if(var != "TEMPLATE"&& var != "TARGET") { |
1160 | QSet<QString> incoming_vals = vallist.toSet(); |
1161 | for(int i = 0; i < varlist.size(); ++i) { |
1162 | const QString var = varlist.at(i).trimmed(); |
1163 | if(!var.isEmpty() && !incoming_vals.contains(var)) { |
1164 | send_warning = true; |
1165 | break; |
1166 | } |
1167 | } |
1168 | } |
1169 | if(send_warning) |
1170 | warn_msg(WarnParser, "Operator=(%s) clears variables previously set: %s:%d", |
1171 | var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no); |
1172 | } |
1173 | varlist.clear(); |
1174 | } |
1175 | for(QStringList::ConstIterator valit = vallist.begin(); |
1176 | valit != vallist.end(); ++valit) { |
1177 | if((*valit).isEmpty()) |
1178 | continue; |
1179 | if((op == "*="&& !varlist.contains((*valit))) || |
1180 | op == "="|| op == "+=") |
1181 | varlist.append((*valit)); |
1182 | else if(op == "-=") |
1183 | varlist.removeAll((*valit)); |
1184 | } |
1185 | if(var == "REQUIRES") // special case to get communicated to backends! |
1186 | doProjectCheckReqs(vallist, place); |
1187 | } |
1188 | return true; |
1189 | } |
1190 | |
1191 | bool |
1192 | QMakeProject::read(QTextStream &file, QMap<QString, QStringList> &place) |
1193 | { |
1194 | int numLines = 0; |
1195 | bool ret = true; |
1196 | QString s; |
1197 | while(!file.atEnd()) { |
1198 | parser.line_no++; |
1199 | QString line = file.readLine().trimmed(); |
1200 | int prelen = line.length(); |
1201 | |
1202 | int hash_mark = line.indexOf("#"); |
1203 | if(hash_mark != -1) //good bye comments |
1204 | line = line.left(hash_mark).trimmed(); |
1205 | if(!line.isEmpty() && line.right(1) == "\\") { |
1206 | if(!line.startsWith("#")) { |
1207 | line.truncate(line.length() - 1); |
1208 | s += line + Option::field_sep; |
1209 | ++numLines; |
1210 | } |
1211 | } else if(!line.isEmpty() || (line.isEmpty() && !prelen)) { |
1212 | if(s.isEmpty() && line.isEmpty()) |
1213 | continue; |
1214 | if(!line.isEmpty()) { |
1215 | s += line; |
1216 | ++numLines; |
1217 | } |
1218 | if(!s.isEmpty()) { |
1219 | if(!(ret = parse(s, place, numLines))) { |
1220 | s = ""; |
1221 | numLines = 0; |
1222 | break; |
1223 | } |
1224 | s = ""; |
1225 | numLines = 0; |
1226 | } |
1227 | } |
1228 | } |
1229 | if (!s.isEmpty()) |
1230 | ret = parse(s, place, numLines); |
1231 | return ret; |
1232 | } |
1233 | |
1234 | bool |
1235 | QMakeProject::read(const QString &file, QMap<QString, QStringList> &place) |
1236 | { |
1237 | parser_info pi = parser; |
1238 | reset(); |
1239 | |
1240 | const QString oldpwd = qmake_getpwd(); |
1241 | QString filename = Option::fixPathToLocalOS(file, false); |
1242 | bool ret = false, using_stdin = false; |
1243 | QFile qfile; |
1244 | if(!strcmp(filename.toLatin1(), "-")) { |
1245 | qfile.setFileName(""); |
1246 | ret = qfile.open(stdin, QIODevice::ReadOnly); |
1247 | using_stdin = true; |
1248 | } else if(QFileInfo(file).isDir()) { |
1249 | return false; |
1250 | } else { |
1251 | qfile.setFileName(filename); |
1252 | ret = qfile.open(QIODevice::ReadOnly); |
1253 | qmake_setpwd(QFileInfo(filename).absolutePath()); |
1254 | } |
1255 | if(ret) { |
1256 | parser_info pi = parser; |
1257 | parser.from_file = true; |
1258 | parser.file = filename; |
1259 | parser.line_no = 0; |
1260 | QTextStream t(&qfile); |
1261 | ret = read(t, place); |
1262 | if(!using_stdin) |
1263 | qfile.close(); |
1264 | } |
1265 | if(scope_blocks.count() != 1) { |
1266 | qmake_error_msg("Unterminated conditional block at end of file"); |
1267 | ret = false; |
1268 | } |
1269 | parser = pi; |
1270 | qmake_setpwd(oldpwd); |
1271 | return ret; |
1272 | } |
1273 | |
1274 | bool |
1275 | QMakeProject::read(const QString &project, uchar cmd) |
1276 | { |
1277 | pfile = QFileInfo(project).absoluteFilePath(); |
1278 | return read(cmd); |
1279 | } |
1280 | |
1281 | bool |
1282 | QMakeProject::read(uchar cmd) |
1283 | { |
1284 | if(cfile.isEmpty()) { |
1285 | // hack to get the Option stuff in there |
1286 | base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext; |
1287 | base_vars["QMAKE_EXT_C"] = Option::c_ext; |
1288 | base_vars["QMAKE_EXT_H"] = Option::h_ext; |
1289 | base_vars["QMAKE_SH"] = Option::shellPath; |
1290 | if(!Option::user_template_prefix.isEmpty()) |
1291 | base_vars["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix); |
1292 | |
1293 | if(cmd & ReadCache && Option::mkfile::do_cache) { // parse the cache |
1294 | int cache_depth = -1; |
1295 | QString qmake_cache = Option::mkfile::cachefile; |
1296 | if(qmake_cache.isEmpty()) { //find it as it has not been specified |
1297 | QString dir = QDir::toNativeSeparators(Option::output_dir); |
1298 | while(!QFile::exists((qmake_cache = dir + QDir::separator() + ".qmake.cache"))) { |
1299 | dir = dir.left(dir.lastIndexOf(QDir::separator())); |
1300 | if(dir.isEmpty() || dir.indexOf(QDir::separator()) == -1) { |
1301 | qmake_cache = ""; |
1302 | break; |
1303 | } |
1304 | if(cache_depth == -1) |
1305 | cache_depth = 1; |
1306 | else |
1307 | cache_depth++; |
1308 | } |
1309 | } else { |
1310 | QString abs_cache = QFileInfo(Option::mkfile::cachefile).absoluteDir().path(); |
1311 | if(Option::output_dir.startsWith(abs_cache)) |
1312 | cache_depth = Option::output_dir.mid(abs_cache.length()).count( |
1313 | } |
1314 | if(!qmake_cache.isEmpty()) { |
1315 | if(read(qmake_cache, cache)) { |
1316 | Option::mkfile::cachefile_depth = cache_depth; |
1317 | Option::mkfile::cachefile = qmake_cache; |
1318 | if(Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty()) |
1319 | Option::mkfile::qmakespec = cache["QMAKESPEC"].first(); |
1320 | } |
1321 | } |
1322 | } |
1323 | if(cmd & ReadConf) { // parse mkspec |
1324 | QString qmakespec = fixEnvVariables(Option::mkfile::qmakespec); |
1325 | QStringList mkspec_roots = qmake_mkspec_paths(); |
1326 | debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec.toLatin1().constData(), |
1327 | mkspec_roots.join("::").toLatin1().constData()); |
1328 | if(qmakespec.isEmpty()) { |
1329 | for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) { |
1330 | QString mkspec = (*it) + QDir::separator() + "default"; |
1331 | QFileInfo default_info(mkspec); |
1332 | if(default_info.exists() && default_info.isDir()) { |
1333 | qmakespec = mkspec; |
1334 | break; |
1335 | } |
1336 | } |
1337 | if(qmakespec.isEmpty()) { |
1338 | fprintf(stderr, "QMAKESPEC has not been set, so configuration cannot be deduced.\n"); |
1339 | return false; |
1340 | } |
1341 | Option::mkfile::qmakespec = qmakespec; |
1342 | } |
1343 | |
1344 | if(QDir::isRelativePath(qmakespec)) { |
1345 | if (QFile::exists(Option::output_dir+"/"+qmakespec+ "/qmake.conf")) { |
1346 | qmakespec = Option::mkfile::qmakespec = QFileInfo(Option::output_dir+"/"+qmakespec).absoluteFilePath(); |
1347 | } else if (QFile::exists(qmakespec+"/qmake.conf")) { |
1348 | Option::mkfile::qmakespec = QFileInfo(Option::mkfile::qmakespec).absoluteFilePath(); |
1349 | } else { |
1350 | bool found_mkspec = false; |
1351 | for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) { |
1352 | QString mkspec = (*it) + QDir::separator() + qmakespec; |
1353 | if(QFile::exists(mkspec)) { |
1354 | found_mkspec = true; |
1355 | Option::mkfile::qmakespec = qmakespec = mkspec; |
1356 | break; |
1357 | } |
1358 | } |
1359 | if(!found_mkspec) { |
1360 | fprintf(stderr, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n", |
1361 | qmakespec.toLatin1().constData(), mkspec_roots.join("\n\t").toLatin1().constData()); |
1362 | return false; |
1363 | } |
1364 | } |
1365 | } |
1366 | |
1367 | // parse qmake configuration |
1368 | while(qmakespec.endsWith(QString(QChar(QDir::separator())))) |
1369 | qmakespec.truncate(qmakespec.length()-1); |
1370 | QString spec = qmakespec + QDir::separator() + "qmake.conf"; |
1371 | debug_msg(1, "QMAKESPEC conf: reading %s", spec.toLatin1().constData()); |
1372 | if(!read(spec, base_vars)) { |
1373 | fprintf(stderr, "Failure to read QMAKESPEC conf file %s.\n", spec.toLatin1().constData()); |
1374 | return false; |
1375 | } |
1376 | validateModes(); |
1377 | |
1378 | if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty()) { |
1379 | debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile.toLatin1().constData()); |
1380 | read(Option::mkfile::cachefile, base_vars); |
1381 | } |
1382 | } |
1383 | |
1384 | if(cmd & ReadFeatures) { |
1385 | debug_msg(1, "Processing default_pre: %s", vars[ "CONFIG"].join( "::").toLatin1().constData()); |
1386 | if(doProjectInclude("default_pre", IncludeFlagFeature, base_vars) == IncludeNoExist) |
1387 | doProjectInclude("default", IncludeFlagFeature, base_vars); |
1388 | } |
1389 | } |
1390 | |
1391 | vars = base_vars; // start with the base |
1392 | |
1393 | //get a default |
1394 | if(pfile != "-"&& vars[ "TARGET"].isEmpty()) |
1395 | vars["TARGET"].append(QFileInfo(pfile).baseName()); |
1396 | |
1397 | //before commandline |
1398 | if(cmd & ReadCmdLine) { |
1399 | cfile = pfile; |
1400 | parser.file = "(internal)"; |
1401 | parser.from_file = false; |
1402 | parser.line_no = 1; //really arg count now.. duh |
1403 | reset(); |
1404 | for(QStringList::ConstIterator it = Option::before_user_vars.begin(); |
1405 | it != Option::before_user_vars.end(); ++it) { |
1406 | if(!parse((*it), vars)) { |
1407 | fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData()); |
1408 | return false; |
1409 | } |
1410 | parser.line_no++; |
1411 | } |
1412 | } |
1413 | |
1414 | //commandline configs |
1415 | if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) { |
1416 | parser.file = "(configs)"; |
1417 | parser.from_file = false; |
1418 | parser.line_no = 1; //really arg count now.. duh |
1419 | parse("CONFIG += "+ Option::user_configs.join( " "), vars); |
1420 | } |
1421 | |
1422 | if(cmd & ReadProFile) { // parse project file |
1423 | debug_msg(1, "Project file: reading %s", pfile.toLatin1().constData()); |
1424 | if(pfile != "-"&& !QFile::exists(pfile) && !pfile.endsWith(Option::pro_ext)) |
1425 | pfile += Option::pro_ext; |
1426 | if(!read(pfile, vars)) |
1427 | return false; |
1428 | } |
1429 | |
1430 | if(cmd & ReadCmdLine) { |
1431 | parser.file = "(internal)"; |
1432 | parser.from_file = false; |
1433 | parser.line_no = 1; //really arg count now.. duh |
1434 | reset(); |
1435 | for(QStringList::ConstIterator it = Option::after_user_vars.begin(); |
1436 | it != Option::after_user_vars.end(); ++it) { |
1437 | if(!parse((*it), vars)) { |
1438 | fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData()); |
1439 | return false; |
1440 | } |
1441 | parser.line_no++; |
1442 | } |
1443 | } |
1444 | |
1445 | //after configs (set in BUILDS) |
1446 | if(cmd & ReadConfigs && !Option::after_user_configs.isEmpty()) { |
1447 | parser.file = "(configs)"; |
1448 | parser.from_file = false; |
1449 | parser.line_no = 1; //really arg count now.. duh |
1450 | parse("CONFIG += "+ Option::after_user_configs.join( " "), vars); |
1451 | } |
1452 | |
1453 | if(pfile != "-"&& vars[ "TARGET"].isEmpty()) |
1454 | vars["TARGET"].append(QFileInfo(pfile).baseName()); |
1455 | |
1456 | if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) { |
1457 | parser.file = "(configs)"; |
1458 | parser.from_file = false; |
1459 | parser.line_no = 1; //really arg count now.. duh |
1460 | parse("CONFIG += "+ Option::user_configs.join( " "), base_vars); |
1461 | } |
1462 | |
1463 | if(cmd & ReadFeatures) { |
1464 | debug_msg(1, "Processing default_post: %s", vars[ "CONFIG"].join( "::").toLatin1().constData()); |
1465 | doProjectInclude("default_post", IncludeFlagFeature, vars); |
1466 | |
1467 | QHash<QString, bool> processed; |
1468 | const QStringList &configs = vars["CONFIG"]; |
1469 | debug_msg(1, "Processing CONFIG features: %s", configs.join( "::").toLatin1().constData()); |
1470 | while(1) { |
1471 | bool finished = true; |
1472 | for(int i = configs.size()-1; i >= 0; --i) { |
1473 | const QString config = configs[i].toLower(); |
1474 | if(!processed.contains(config)) { |
1475 | processed.insert(config, true); |
1476 | if(doProjectInclude(config, IncludeFlagFeature, vars) == IncludeSuccess) { |
1477 | finished = false; |
1478 | break; |
1479 | } |
1480 | } |
1481 | } |
1482 | if(finished) |
1483 | break; |
1484 | } |
1485 | } |
1486 | Option::postProcessProject(this); // let Option post-process |
1487 | return true; |
1488 | } |
1489 | |
1490 | void QMakeProject::validateModes() |
1491 | { |
1492 | if (Option::host_mode == Option::HOST_UNKNOWN_MODE |
1493 | || Option::target_mode == Option::TARG_UNKNOWN_MODE) { |
1494 | Option::HOST_MODE host_mode; |
1495 | Option::TARG_MODE target_mode; |
1496 | const QStringList &gen = base_vars.value("MAKEFILE_GENERATOR"); |
1497 | if (gen.isEmpty()) { |
1498 | fprintf(stderr, "%s:%d: Using OS scope before setting MAKEFILE_GENERATOR\n", |
1499 | parser.file.toLatin1().constData(), parser.line_no); |
1500 | } else if (MetaMakefileGenerator::modesForGenerator(gen.first(), |
1501 | &host_mode, &target_mode)) { |
1502 | if (Option::host_mode == Option::HOST_UNKNOWN_MODE) { |
1503 | Option::host_mode = host_mode; |
1504 | Option::applyHostMode(); |
1505 | } |
1506 | |
1507 | if (Option::target_mode == Option::TARG_UNKNOWN_MODE) { |
1508 | const QStringList &tgt = base_vars.value("TARGET_PLATFORM"); |
1509 | if (!tgt.isEmpty()) { |
1510 | const QString &os = tgt.first(); |
1511 | if (os == "unix") |
1512 | Option::target_mode = Option::TARG_UNIX_MODE; |
1513 | else if (os == "macx") |
1514 | Option::target_mode = Option::TARG_MACX_MODE; |
1515 | else if (os == "symbian") |
1516 | Option::target_mode = Option::TARG_SYMBIAN_MODE; |
1517 | else if (os == "win32") |
1518 | Option::target_mode = Option::TARG_WIN_MODE; |
1519 | else |
1520 | fprintf(stderr, "Unknown target platform specified: %s\n", |
1521 | os.toLatin1().constData()); |
1522 | } else { |
1523 | Option::target_mode = target_mode; |
1524 | } |
1525 | } |
1526 | } |
1527 | } |
1528 | } |
1529 | |
1530 | bool |
1531 | QMakeProject::isActiveConfig(const QString &x, bool regex, QMap<QString, QStringList> *place) |
1532 | { |
1533 | if(x.isEmpty()) |
1534 | return true; |
1535 | |
1536 | //magic types for easy flipping |
1537 | if(x == "true") |
1538 | return true; |
1539 | else if(x == "false") |
1540 | return false; |
1541 | |
1542 | if (x == "unix") { |
1543 | validateModes(); |
1544 | return Option::target_mode == Option::TARG_UNIX_MODE |
1545 | || Option::target_mode == Option::TARG_MACX_MODE |
1546 | || Option::target_mode == Option::TARG_SYMBIAN_MODE; |
1547 | } else if (x == "macx"|| x == "mac") { |
1548 | validateModes(); |
1549 | return Option::target_mode == Option::TARG_MACX_MODE; |
1550 | } else if (x == "symbian") { |
1551 | validateModes(); |
1552 | return Option::target_mode == Option::TARG_SYMBIAN_MODE; |
1553 | } else if (x == "win32") { |
1554 | validateModes(); |
1555 | return Option::target_mode == Option::TARG_WIN_MODE; |
1556 | } |
1557 | |
1558 | //mkspecs |
1559 | static QString spec; |
1560 | if(spec.isEmpty()) |
1561 | spec = QFileInfo(Option::mkfile::qmakespec).fileName(); |
1562 | QRegExp re(x, Qt::CaseSensitive, QRegExp::Wildcard); |
1563 | if((regex && re.exactMatch(spec)) || (!regex && spec == x)) |
1564 | return true; |
1565 | #ifdef Q_OS_UNIX |
1566 | else if(spec == "default") { |
1567 | static char *buffer = NULL; |
1568 | if(!buffer) { |
1569 | buffer = (char *)malloc(1024); |
1570 | qmakeAddCacheClear(qmakeFreeCacheClear, (void**)&buffer); |
1571 | } |
1572 | int l = readlink(Option::mkfile::qmakespec.toLatin1(), buffer, 1024); |
1573 | if(l != -1) { |
1574 | buffer[l] = |
1575 | QString r = buffer; |
1576 | if(r.lastIndexOf( |
1577 | r = r.mid(r.lastIndexOf( |
1578 | if((regex && re.exactMatch(r)) || (!regex && r == x)) |
1579 | return true; |
1580 | } |
1581 | } |
1582 | #elif defined(Q_OS_WIN) |
1583 | else if(spec == "default") { |
1584 | // We can't resolve symlinks as they do on Unix, so configure.exe puts the source of the |
1585 | // qmake.conf at the end of the default/qmake.conf in the QMAKESPEC_ORG variable. |
1586 | const QStringList &spec_org = (place ? (*place)["QMAKESPEC_ORIGINAL"] |
1587 | : vars["QMAKESPEC_ORIGINAL"]); |
1588 | if (!spec_org.isEmpty()) { |
1589 | spec = spec_org.at(0); |
1590 | int lastSlash = spec.lastIndexOf( |
1591 | if(lastSlash != -1) |
1592 | spec = spec.mid(lastSlash + 1); |
1593 | if((regex && re.exactMatch(spec)) || (!regex && spec == x)) |
1594 | return true; |
1595 | } |
1596 | } |
1597 | #endif |
1598 | |
1599 | //simple matching |
1600 | const QStringList &configs = (place ? (*place)["CONFIG"] : vars[ "CONFIG"]); |
1601 | for(QStringList::ConstIterator it = configs.begin(); it != configs.end(); ++it) { |
1602 | if(((regex && re.exactMatch((*it))) || (!regex && (*it) == x)) && re.exactMatch((*it))) |
1603 | return true; |
1604 | } |
1605 | return false; |
1606 | } |
1607 | |
1608 | bool |
1609 | QMakeProject::doProjectTest(QString str, QMap<QString, QStringList> &place) |
1610 | { |
1611 | QString chk = remove_quotes(str); |
1612 | if(chk.isEmpty()) |
1613 | return true; |
1614 | bool invert_test = (chk.left(1) == "!"); |
1615 | if(invert_test) |
1616 | chk = chk.mid(1); |
1617 | |
1618 | bool test=false; |
1619 | int lparen = chk.indexOf( |
1620 | if(lparen != -1) { // if there is an lparen in the chk, it IS a function |
1621 | int rparen = chk.indexOf( |
1622 | if(rparen == -1) { |
1623 | qmake_error_msg("Function missing right paren: "+ chk); |
1624 | } else { |
1625 | QString func = chk.left(lparen); |
1626 | test = doProjectTest(func, chk.mid(lparen+1, rparen - lparen - 1), place); |
1627 | } |
1628 | } else { |
1629 | test = isActiveConfig(chk, true, &place); |
1630 | } |
1631 | if(invert_test) |
1632 | return !test; |
1633 | return test; |
1634 | } |
1635 | |
1636 | bool |
1637 | QMakeProject::doProjectTest(QString func, const QString ¶ms, |
1638 | QMap<QString, QStringList> &place) |
1639 | { |
1640 | return doProjectTest(func, split_arg_list(params), place); |
1641 | } |
1642 | |
1643 | QMakeProject::IncludeStatus |
1644 | QMakeProject::doProjectInclude(QString file, uchar flags, QMap<QString, QStringList> &place) |
1645 | { |
1646 | enum { UnknownFormat, ProFormat, JSFormat } format = UnknownFormat; |
1647 | if(flags & IncludeFlagFeature) { |
1648 | if(!file.endsWith(Option::prf_ext)) |
1649 | file += Option::prf_ext; |
1650 | validateModes(); // init dir_sep |
1651 | if(file.indexOf(Option::dir_sep) == -1 || !QFile::exists(file)) { |
1652 | static QStringList *feature_roots = 0; |
1653 | if(!feature_roots) { |
1654 | feature_roots = new QStringList(qmake_feature_paths(prop)); |
1655 | qmakeAddCacheClear(qmakeDeleteCacheClear<QStringList>, (void**)&feature_roots); |
1656 | } |
1657 | debug_msg(2, "Looking for feature '%s' in (%s)", file.toLatin1().constData(), |
1658 | feature_roots->join("::").toLatin1().constData()); |
1659 | int start_root = 0; |
1660 | if(parser.from_file) { |
1661 | QFileInfo currFile(parser.file), prfFile(file); |
1662 | if(currFile.fileName() == prfFile.fileName()) { |
1663 | currFile = QFileInfo(currFile.canonicalFilePath()); |
1664 | for(int root = 0; root < feature_roots->size(); ++root) { |
1665 | prfFile = QFileInfo(feature_roots->at(root) + |
1666 | QDir::separator() + file).canonicalFilePath(); |
1667 | if(prfFile == currFile) { |
1668 | start_root = root+1; |
1669 | break; |
1670 | } |
1671 | } |
1672 | } |
1673 | } |
1674 | for(int root = start_root; root < feature_roots->size(); ++root) { |
1675 | QString prf(feature_roots->at(root) + QDir::separator() + file); |
1676 | if(QFile::exists(prf + Option::js_ext)) { |
1677 | format = JSFormat; |
1678 | file = prf + Option::js_ext; |
1679 | break; |
1680 | } else if(QFile::exists(prf)) { |
1681 | format = ProFormat; |
1682 | file = prf; |
1683 | break; |
1684 | } |
1685 | } |
1686 | if(format == UnknownFormat) |
1687 | return IncludeNoExist; |
1688 | } |
1689 | if(place["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file) != -1) |
1690 | return IncludeFeatureAlreadyLoaded; |
1691 | place["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file); |
1692 | } |
1693 | if(QDir::isRelativePath(file)) { |
1694 | QStringList include_roots; |
1695 | if(Option::output_dir != qmake_getpwd()) |
1696 | include_roots << qmake_getpwd(); |
1697 | include_roots << Option::output_dir; |
1698 | for(int root = 0; root < include_roots.size(); ++root) { |
1699 | QString testName = QDir::toNativeSeparators(include_roots[root]); |
1700 | if (!testName.endsWith(QString(QDir::separator()))) |
1701 | testName += QDir::separator(); |
1702 | testName += file; |
1703 | if(QFile::exists(testName)) { |
1704 | file = testName; |
1705 | break; |
1706 | } |
1707 | } |
1708 | } |
1709 | if(format == UnknownFormat) { |
1710 | if(QFile::exists(file)) { |
1711 | if(file.endsWith(Option::js_ext)) |
1712 | format = JSFormat; |
1713 | else |
1714 | format = ProFormat; |
1715 | } else { |
1716 | return IncludeNoExist; |
1717 | } |
1718 | } |
1719 | if(Option::mkfile::do_preprocess) //nice to see this first.. |
1720 | fprintf(stderr, "#switching file %s(%s) - %s:%d\n", (flags & IncludeFlagFeature) ? "load": "include", |
1721 | file.toLatin1().constData(), |
1722 | parser.file.toLatin1().constData(), parser.line_no); |
1723 | debug_msg(1, "Project Parser: %s'ing file %s.", (flags & IncludeFlagFeature) ? "load": "include", |
1724 | file.toLatin1().constData()); |
1725 | |
1726 | QString orig_file = file; |
1727 | int di = file.lastIndexOf(QDir::separator()); |
1728 | QString oldpwd = qmake_getpwd(); |
1729 | if(di != -1) { |
1730 | if(!qmake_setpwd(file.left(file.lastIndexOf(QDir::separator())))) { |
1731 | fprintf(stderr, "Cannot find directory: %s\n", file.left(di).toLatin1().constData()); |
1732 | return IncludeFailure; |
1733 | } |
1734 | } |
1735 | bool parsed = false; |
1736 | parser_info pi = parser; |
1737 | if(format == JSFormat) { |
1738 | warn_msg(WarnParser, "%s:%d: QtScript support disabled for %s.", |
1739 | pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData()); |
1740 | } else { |
1741 | QStack<ScopeBlock> sc = scope_blocks; |
1742 | IteratorBlock *it = iterator; |
1743 | FunctionBlock *fu = function; |
1744 | if(flags & (IncludeFlagNewProject|IncludeFlagNewParser)) { |
1745 | // The "project's variables" are used in other places (eg. export()) so it's not |
1746 | // possible to use "place" everywhere. Instead just set variables and grab them later |
1747 | QMakeProject proj(this, &place); |
1748 | if(flags & IncludeFlagNewParser) { |
1749 | #if 1 |
1750 | if(proj.doProjectInclude("default_pre", IncludeFlagFeature, proj.variables()) == IncludeNoExist) |
1751 | proj.doProjectInclude("default", IncludeFlagFeature, proj.variables()); |
1752 | #endif |
1753 | parsed = proj.read(file, proj.variables()); // parse just that file (fromfile, infile) |
1754 | } else { |
1755 | parsed = proj.read(file); // parse all aux files (load/include into) |
1756 | } |
1757 | place = proj.variables(); |
1758 | } else { |
1759 | parsed = read(file, place); |
1760 | } |
1761 | iterator = it; |
1762 | function = fu; |
1763 | scope_blocks = sc; |
1764 | } |
1765 | if(parsed) { |
1766 | if(place["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file) == -1) |
1767 | place["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file); |
1768 | } else { |
1769 | warn_msg(WarnParser, "%s:%d: Failure to include file %s.", |
1770 | pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData()); |
1771 | } |
1772 | parser = pi; |
1773 | qmake_setpwd(oldpwd); |
1774 | if(!parsed) |
1775 | return IncludeParseFailure; |
1776 | return IncludeSuccess; |
1777 | } |
1778 | |
1779 | QStringList |
1780 | QMakeProject::doProjectExpand(QString func, const QString ¶ms, |
1781 | QMap<QString, QStringList> &place) |
1782 | { |
1783 | return doProjectExpand(func, split_arg_list(params), place); |
1784 | } |
1785 | |
1786 | QStringList |
1787 | QMakeProject::doProjectExpand(QString func, QStringList args, |
1788 | QMap<QString, QStringList> &place) |
1789 | { |
1790 | QList<QStringList> args_list; |
1791 | for(int i = 0; i < args.size(); ++i) { |
1792 | QStringList arg = split_value_list(args[i]), tmp; |
1793 | for(int i = 0; i < arg.size(); ++i) |
1794 | tmp += doVariableReplaceExpand(arg[i], place);; |
1795 | args_list += tmp; |
1796 | } |
1797 | return doProjectExpand(func, args_list, place); |
1798 | } |
1799 | |
1800 | // defined in symbian generator |
1801 | extern QString generate_test_uid(const QString& target); |
1802 | |
1803 | QStringList |
1804 | QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list, |
1805 | QMap<QString, QStringList> &place) |
1806 | { |
1807 | func = func.trimmed(); |
1808 | if(replaceFunctions.contains(func)) { |
1809 | FunctionBlock *defined = replaceFunctions[func]; |
1810 | function_blocks.push(defined); |
1811 | QStringList ret; |
1812 | defined->exec(args_list, this, place, ret); |
1813 | Q_ASSERT(function_blocks.pop() == defined); |
1814 | return ret; |
1815 | } |
1816 | |
1817 | QStringList args; //why don't the builtin functions just use args_list? --Sam |
1818 | for(int i = 0; i < args_list.size(); ++i) |
1819 | args += args_list[i].join(QString(Option::field_sep)); |
1820 | |
1821 | ExpandFunc func_t = qmake_expandFunctions().value(func); |
1822 | if (!func_t && (func_t = qmake_expandFunctions().value(func.toLower()))) |
1823 | warn_msg(WarnDeprecated, "%s:%d: Using uppercased builtin functions is deprecated.", |
1824 | parser.file.toLatin1().constData(), parser.line_no); |
1825 | debug_msg(1, "Running project expand: %s(%s) [%d]", |
1826 | func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t); |
1827 | |
1828 | QStringList ret; |
1829 | switch(func_t) { |
1830 | case E_MEMBER: { |
1831 | if(args.count() < 1 || args.count() > 3) { |
1832 | fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n", |
1833 | parser.file.toLatin1().constData(), parser.line_no); |
1834 | } else { |
1835 | bool ok = true; |
1836 | const QStringList &var = values(args.first(), place); |
1837 | int start = 0, end = 0; |
1838 | if(args.count() >= 2) { |
1839 | QString start_str = args[1]; |
1840 | start = start_str.toInt(&ok); |
1841 | if(!ok) { |
1842 | if(args.count() == 2) { |
1843 | int dotdot = start_str.indexOf(".."); |
1844 | if(dotdot != -1) { |
1845 | start = start_str.left(dotdot).toInt(&ok); |
1846 | if(ok) |
1847 | end = start_str.mid(dotdot+2).toInt(&ok); |
1848 | } |
1849 | } |
1850 | if(!ok) |
1851 | fprintf(stderr, "%s:%d: member() argument 2 (start) '%s' invalid.\n", |
1852 | parser.file.toLatin1().constData(), parser.line_no, |
1853 | start_str.toLatin1().constData()); |
1854 | } else { |
1855 | end = start; |
1856 | if(args.count() == 3) |
1857 | end = args[2].toInt(&ok); |
1858 | if(!ok) |
1859 | fprintf(stderr, "%s:%d: member() argument 3 (end) '%s' invalid.\n", |
1860 | parser.file.toLatin1().constData(), parser.line_no, |
1861 | args[2].toLatin1().constData()); |
1862 | } |
1863 | } |
1864 | if(ok) { |
1865 | if(start < 0) |
1866 | start += var.count(); |
1867 | if(end < 0) |
1868 | end += var.count(); |
1869 | if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) { |
1870 | //nothing |
1871 | } else if(start < end) { |
1872 | for(int i = start; i <= end && (int)var.count() >= i; i++) |
1873 | ret += var[i]; |
1874 | } else { |
1875 | for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--) |
1876 | ret += var[i]; |
1877 | } |
1878 | } |
1879 | } |
1880 | break; } |
1881 | case E_FIRST: |
1882 | case E_LAST: { |
1883 | if(args.count() != 1) { |
1884 | fprintf(stderr, "%s:%d: %s(var) requires one argument.\n", |
1885 | parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData()); |
1886 | } else { |
1887 | const QStringList &var = values(args.first(), place); |
1888 | if(!var.isEmpty()) { |
1889 | if(func_t == E_FIRST) |
1890 | ret = QStringList(var[0]); |
1891 | else |
1892 | ret = QStringList(var[var.size()-1]); |
1893 | } |
1894 | } |
1895 | break; } |
1896 | case E_CAT: { |
1897 | if(args.count() < 1 || args.count() > 2) { |
1898 | fprintf(stderr, "%s:%d: cat(file) requires one argument.\n", |
1899 | parser.file.toLatin1().constData(), parser.line_no); |
1900 | } else { |
1901 | QString file = args[0]; |
1902 | file = Option::fixPathToLocalOS(file); |
1903 | |
1904 | bool singleLine = true; |
1905 | if(args.count() > 1) |
1906 | singleLine = (args[1].toLower() == "true"); |
1907 | |
1908 | QFile qfile(file); |
1909 | if(qfile.open(QIODevice::ReadOnly)) { |
1910 | QTextStream stream(&qfile); |
1911 | while(!stream.atEnd()) { |
1912 | ret += split_value_list(stream.readLine().trimmed()); |
1913 | if(!singleLine) |
1914 | ret += "\n"; |
1915 | } |
1916 | qfile.close(); |
1917 | } |
1918 | } |
1919 | break; } |
1920 | case E_FROMFILE: { |
1921 | if(args.count() != 2) { |
1922 | fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n", |
1923 | parser.file.toLatin1().constData(), parser.line_no); |
1924 | } else { |
1925 | QString file = args[0], seek_var = args[1]; |
1926 | file = Option::fixPathToLocalOS(file); |
1927 | |
1928 | QMap<QString, QStringList> tmp; |
1929 | if(doProjectInclude(file, IncludeFlagNewParser, tmp) == IncludeSuccess) { |
1930 | if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) { |
1931 | QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"]; |
1932 | const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"]; |
1933 | for(int i = 0; i < in.size(); ++i) { |
1934 | if(out.indexOf(in[i]) == -1) |
1935 | out += in[i]; |
1936 | } |
1937 | } |
1938 | ret = tmp[seek_var]; |
1939 | } |
1940 | } |
1941 | break; } |
1942 | case E_EVAL: { |
1943 | if(args.count() < 1 || args.count() > 2) { |
1944 | fprintf(stderr, "%s:%d: eval(variable) requires one argument.\n", |
1945 | parser.file.toLatin1().constData(), parser.line_no); |
1946 | |
1947 | } else { |
1948 | const QMap<QString, QStringList> *source = &place; |
1949 | if(args.count() == 2) { |
1950 | if(args.at(1) == "Global") { |
1951 | source = &vars; |
1952 | } else if(args.at(1) == "Local") { |
1953 | source = &place; |
1954 | } else { |
1955 | fprintf(stderr, "%s:%d: unexpected source to eval.\n", parser.file.toLatin1().constData(), |
1956 | parser.line_no); |
1957 | } |
1958 | } |
1959 | ret += source->value(args.at(0)); |
1960 | } |
1961 | break; } |
1962 | case E_LIST: { |
1963 | static int x = 0; |
1964 | QString tmp; |
1965 | tmp.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++); |
1966 | ret = QStringList(tmp); |
1967 | QStringList &lst = (*((QMap<QString, QStringList>*)&place))[tmp]; |
1968 | lst.clear(); |
1969 | for(QStringList::ConstIterator arg_it = args.begin(); |
1970 | arg_it != args.end(); ++arg_it) |
1971 | lst += split_value_list((*arg_it)); |
1972 | break; } |
1973 | case E_SPRINTF: { |
1974 | if(args.count() < 1) { |
1975 | fprintf(stderr, "%s:%d: sprintf(format, ...) requires one argument.\n", |
1976 | parser.file.toLatin1().constData(), parser.line_no); |
1977 | } else { |
1978 | QString tmp = args.at(0); |
1979 | for(int i = 1; i < args.count(); ++i) |
1980 | tmp = tmp.arg(args.at(i)); |
1981 | ret = split_value_list(tmp); |
1982 | } |
1983 | break; } |
1984 | case E_JOIN: { |
1985 | if(args.count() < 1 || args.count() > 4) { |
1986 | fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four" |
1987 | "arguments.\n", parser.file.toLatin1().constData(), parser.line_no); |
1988 | } else { |
1989 | QString glue, before, after; |
1990 | if(args.count() >= 2) |
1991 | glue = args[1]; |
1992 | if(args.count() >= 3) |
1993 | before = args[2]; |
1994 | if(args.count() == 4) |
1995 | after = args[3]; |
1996 | const QStringList &var = values(args.first(), place); |
1997 | if(!var.isEmpty()) |
1998 | ret = split_value_list(before + var.join(glue) + after); |
1999 | } |
2000 | break; } |
2001 | case E_SPLIT: { |
2002 | if(args.count() < 1 || args.count() > 2) { |
2003 | fprintf(stderr, "%s:%d split(var, sep) requires one or two arguments\n", |
2004 | parser.file.toLatin1().constData(), parser.line_no); |
2005 | } else { |
2006 | QString sep = QString(Option::field_sep); |
2007 | if(args.count() >= 2) |
2008 | sep = args[1]; |
2009 | QStringList var = values(args.first(), place); |
2010 | for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) { |
2011 | QStringList lst = (*vit).split(sep); |
2012 | for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit) |
2013 | ret += (*spltit); |
2014 | } |
2015 | } |
2016 | break; } |
2017 | case E_BASENAME: |
2018 | case E_DIRNAME: |
2019 | case E_SECTION: { |
2020 | bool regexp = false; |
2021 | QString sep, var; |
2022 | int beg=0, end=-1; |
2023 | if(func_t == E_SECTION) { |
2024 | if(args.count() != 3 && args.count() != 4) { |
2025 | fprintf(stderr, "%s:%d section(var, sep, begin, end) requires three argument\n", |
2026 | parser.file.toLatin1().constData(), parser.line_no); |
2027 | } else { |
2028 | var = args[0]; |
2029 | sep = args[1]; |
2030 | beg = args[2].toInt(); |
2031 | if(args.count() == 4) |
2032 | end = args[3].toInt(); |
2033 | } |
2034 | } else { |
2035 | if(args.count() != 1) { |
2036 | fprintf(stderr, "%s:%d %s(var) requires one argument.\n", |
2037 | parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData()); |
2038 | } else { |
2039 | var = args[0]; |
2040 | regexp = true; |
2041 | sep = "["+ QRegExp::escape(Option::dir_sep) + "/]"; |
2042 | if(func_t == E_DIRNAME) |
2043 | end = -2; |
2044 | else |
2045 | beg = -1; |
2046 | } |
2047 | } |
2048 | if(!var.isNull()) { |
2049 | const QStringList &l = values(var, place); |
2050 | for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { |
2051 | QString separator = sep; |
2052 | if(regexp) |
2053 | ret += (*it).section(QRegExp(separator), beg, end); |
2054 | else |
2055 | ret += (*it).section(separator, beg, end); |
2056 | } |
2057 | } |
2058 | break; } |
2059 | case E_FIND: { |
2060 | if(args.count() != 2) { |
2061 | fprintf(stderr, "%s:%d find(var, str) requires two arguments\n", |
2062 | parser.file.toLatin1().constData(), parser.line_no); |
2063 | } else { |
2064 | QRegExp regx(args[1]); |
2065 | const QStringList &var = values(args.first(), place); |
2066 | for(QStringList::ConstIterator vit = var.begin(); |
2067 | vit != var.end(); ++vit) { |
2068 | if(regx.indexIn(*vit) != -1) |
2069 | ret += (*vit); |
2070 | } |
2071 | } |
2072 | break; } |
2073 | case E_SYSTEM: { |
2074 | if(args.count() < 1 || args.count() > 2) { |
2075 | fprintf(stderr, "%s:%d system(execut) requires one argument.\n", |
2076 | parser.file.toLatin1().constData(), parser.line_no); |
2077 | } else { |
2078 | char buff[256]; |
2079 | bool singleLine = true; |
2080 | if(args.count() > 1) |
2081 | singleLine = (args[1].toLower() == "true"); |
2082 | QString output; |
2083 | FILE *proc = QT_POPEN(args[0].toLatin1(), "r"); |
2084 | while(proc && !feof(proc)) { |
2085 | int read_in = int(fread(buff, 1, 255, proc)); |
2086 | if(!read_in) |
2087 | break; |
2088 | for(int i = 0; i < read_in; i++) { |
2089 | if((singleLine && buff[i] == |
2090 | buff[i] = |
2091 | } |
2092 | buff[read_in] = |
2093 | output += buff; |
2094 | } |
2095 | ret += split_value_list(output); |
2096 | if(proc) |
2097 | QT_PCLOSE(proc); |
2098 | } |
2099 | break; } |
2100 | case E_UNIQUE: { |
2101 | if(args.count() != 1) { |
2102 | fprintf(stderr, "%s:%d unique(var) requires one argument.\n", |
2103 | parser.file.toLatin1().constData(), parser.line_no); |
2104 | } else { |
2105 | const QStringList &var = values(args.first(), place); |
2106 | for(int i = 0; i < var.count(); i++) { |
2107 | if(!ret.contains(var[i])) |
2108 | ret.append(var[i]); |
2109 | } |
2110 | } |
2111 | break; } |
2112 | case E_QUOTE: |
2113 | ret = args; |
2114 | break; |
2115 | case E_ESCAPE_EXPAND: { |
2116 | for(int i = 0; i < args.size(); ++i) { |
2117 | QChar *i_data = args[i].data(); |
2118 | int i_len = args[i].length(); |
2119 | for(int x = 0; x < i_len; ++x) { |
2120 | if(*(i_data+x) == |
2121 | if(*(i_data+x+1) == |
2122 | ++x; |
2123 | } else { |
2124 | struct { |
2125 | char in, out; |
2126 | } mapped_quotes[] = { |
2127 | { |
2128 | { |
2129 | { |
2130 | { 0, 0 } |
2131 | }; |
2132 | for(int i = 0; mapped_quotes[i].in; ++i) { |
2133 | if(*(i_data+x+1) == mapped_quotes[i].in) { |
2134 | *(i_data+x) = mapped_quotes[i].out; |
2135 | if(x < i_len-2) |
2136 | memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar)); |
2137 | --i_len; |
2138 | break; |
2139 | } |
2140 | } |
2141 | } |
2142 | } |
2143 | } |
2144 | ret.append(QString(i_data, i_len)); |
2145 | } |
2146 | break; } |
2147 | case E_RE_ESCAPE: { |
2148 | for(int i = 0; i < args.size(); ++i) |
2149 | ret += QRegExp::escape(args[i]); |
2150 | break; } |
2151 | case E_UPPER: |
2152 | case E_LOWER: { |
2153 | for(int i = 0; i < args.size(); ++i) { |
2154 | if(func_t == E_UPPER) |
2155 | ret += args[i].toUpper(); |
2156 | else |
2157 | ret += args[i].toLower(); |
2158 | } |
2159 | break; } |
2160 | case E_FILES: { |
2161 | if(args.count() != 1 && args.count() != 2) { |
2162 | fprintf(stderr, "%s:%d files(pattern) requires one argument.\n", |
2163 | parser.file.toLatin1().constData(), parser.line_no); |
2164 | } else { |
2165 | bool recursive = false; |
2166 | if(args.count() == 2) |
2167 | recursive = (args[1].toLower() == "true"|| args[1].toInt()); |
2168 | QStringList dirs; |
2169 | QString r = Option::fixPathToLocalOS(args[0]); |
2170 | int slash = r.lastIndexOf(QDir::separator()); |
2171 | if(slash != -1) { |
2172 | dirs.append(r.left(slash)); |
2173 | r = r.mid(slash+1); |
2174 | } else { |
2175 | dirs.append(""); |
2176 | } |
2177 | |
2178 | const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); |
2179 | for(int d = 0; d < dirs.count(); d++) { |
2180 | QString dir = dirs[d]; |
2181 | if(!dir.isEmpty() && !dir.endsWith(QDir::separator())) |
2182 | dir += "/"; |
2183 | |
2184 | QDir qdir(dir); |
2185 | for(int i = 0; i < (int)qdir.count(); ++i) { |
2186 | if(qdir[i] == "."|| qdir[i] == "..") |
2187 | continue; |
2188 | QString fname = dir + qdir[i]; |
2189 | if(QFileInfo(fname).isDir()) { |
2190 | if(recursive) |
2191 | dirs.append(fname); |
2192 | } |
2193 | if(regex.exactMatch(qdir[i])) |
2194 | ret += fname; |
2195 | } |
2196 | } |
2197 | } |
2198 | break; } |
2199 | case E_PROMPT: { |
2200 | if(args.count() != 1) { |
2201 | fprintf(stderr, "%s:%d prompt(question) requires one argument.\n", |
2202 | parser.file.toLatin1().constData(), parser.line_no); |
2203 | } else if(pfile == "-") { |
2204 | fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n", |
2205 | parser.file.toLatin1().constData(), parser.line_no); |
2206 | } else { |
2207 | QString msg = fixEnvVariables(args.first()); |
2208 | if(!msg.endsWith("?")) |
2209 | msg += "?"; |
2210 | fprintf(stderr, "Project %s: %s ", func.toUpper().toLatin1().constData(), |
2211 | msg.toLatin1().constData()); |
2212 | |
2213 | QFile qfile; |
2214 | if(qfile.open(stdin, QIODevice::ReadOnly)) { |
2215 | QTextStream t(&qfile); |
2216 | ret = split_value_list(t.readLine()); |
2217 | } |
2218 | } |
2219 | break; } |
2220 | case E_REPLACE: { |
2221 | if(args.count() != 3 ) { |
2222 | fprintf(stderr, "%s:%d replace(var, before, after) requires three arguments\n", |
2223 | parser.file.toLatin1().constData(), parser.line_no); |
2224 | } else { |
2225 | const QRegExp before( args[1] ); |
2226 | const QString after( args[2] ); |
2227 | QStringList var = values(args.first(), place); |
2228 | for(QStringList::Iterator it = var.begin(); it != var.end(); ++it) |
2229 | ret += it->replace(before, after); |
2230 | } |
2231 | break; } |
2232 | case E_SIZE: { |
2233 | if(args.count() != 1) { |
2234 | fprintf(stderr, "%s:%d: size(var) requires one argument.\n", |
2235 | parser.file.toLatin1().constData(), parser.line_no); |
2236 | } else { |
2237 | int size = values(args[0], place).size(); |
2238 | ret += QString::number(size); |
2239 | } |
2240 | break; } |
2241 | case E_GENERATE_UID: |
2242 | if (args.count() != 1) { |
2243 | fprintf(stderr, "%s:%d: generate_uid(var) requires one argument.\n", |
2244 | parser.file.toLatin1().constData(), parser.line_no); |
2245 | } else { |
2246 | ret += generate_test_uid(args.first()); |
2247 | } |
2248 | break; |
2249 | default: { |
2250 | fprintf(stderr, "%s:%d: Unknown replace function: %s\n", |
2251 | parser.file.toLatin1().constData(), parser.line_no, |
2252 | func.toLatin1().constData()); |
2253 | break; } |
2254 | } |
2255 | return ret; |
2256 | } |
2257 | |
2258 | bool |
2259 | QMakeProject::doProjectTest(QString func, QStringList args, QMap<QString, QStringList> &place) |
2260 | { |
2261 | QList<QStringList> args_list; |
2262 | for(int i = 0; i < args.size(); ++i) { |
2263 | QStringList arg = split_value_list(args[i]), tmp; |
2264 | for(int i = 0; i < arg.size(); ++i) |
2265 | tmp += doVariableReplaceExpand(arg[i], place); |
2266 | args_list += tmp; |
2267 | } |
2268 | return doProjectTest(func, args_list, place); |
2269 | } |
2270 | |
2271 | bool |
2272 | QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QMap<QString, QStringList> &place) |
2273 | { |
2274 | func = func.trimmed(); |
2275 | |
2276 | if(testFunctions.contains(func)) { |
2277 | FunctionBlock *defined = testFunctions[func]; |
2278 | QStringList ret; |
2279 | function_blocks.push(defined); |
2280 | defined->exec(args_list, this, place, ret); |
2281 | Q_ASSERT(function_blocks.pop() == defined); |
2282 | |
2283 | if(ret.isEmpty()) { |
2284 | return true; |
2285 | } else { |
2286 | if(ret.first() == "true") { |
2287 | return true; |
2288 | } else if(ret.first() == "false") { |
2289 | return false; |
2290 | } else { |
2291 | bool ok; |
2292 | int val = ret.first().toInt(&ok); |
2293 | if(ok) |
2294 | return val; |
2295 | fprintf(stderr, "%s:%d Unexpected return value from test %s [%s].\n", |
2296 | parser.file.toLatin1().constData(), |
2297 | parser.line_no, func.toLatin1().constData(), |
2298 | ret.join("::").toLatin1().constData()); |
2299 | } |
2300 | return false; |
2301 | } |
2302 | return false; |
2303 | } |
2304 | |
2305 | QStringList args; //why don't the builtin functions just use args_list? --Sam |
2306 | for(int i = 0; i < args_list.size(); ++i) |
2307 | args += args_list[i].join(QString(Option::field_sep)); |
2308 | |
2309 | TestFunc func_t = qmake_testFunctions().value(func); |
2310 | debug_msg(1, "Running project test: %s(%s) [%d]", |
2311 | func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t); |
2312 | |
2313 | switch(func_t) { |
2314 | case T_REQUIRES: |
2315 | return doProjectCheckReqs(args, place); |
2316 | case T_LESSTHAN: |
2317 | case T_GREATERTHAN: { |
2318 | if(args.count() != 2) { |
2319 | fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(), |
2320 | parser.line_no, func.toLatin1().constData()); |
2321 | return false; |
2322 | } |
2323 | QString rhs(args[1]), lhs(values(args[0], place).join(QString(Option::field_sep))); |
2324 | bool ok; |
2325 | int rhs_int = rhs.toInt(&ok); |
2326 | if(ok) { // do integer compare |
2327 | int lhs_int = lhs.toInt(&ok); |
2328 | if(ok) { |
2329 | if(func_t == T_GREATERTHAN) |
2330 | return lhs_int > rhs_int; |
2331 | return lhs_int < rhs_int; |
2332 | } |
2333 | } |
2334 | if(func_t == T_GREATERTHAN) |
2335 | return lhs > rhs; |
2336 | return lhs < rhs; } |
2337 | case T_IF: { |
2338 | if(args.count() != 1) { |
2339 | fprintf(stderr, "%s:%d: if(condition) requires one argument.\n", parser.file.toLatin1().constData(), |
2340 | parser.line_no); |
2341 | return false; |
2342 | } |
2343 | const QString cond = args.first(); |
2344 | const QChar *d = cond.unicode(); |
2345 | QChar quote = 0; |
2346 | bool ret = true, or_op = false; |
2347 | QString test; |
2348 | for(int d_off = 0, parens = 0, d_len = cond.size(); d_off < d_len; ++d_off) { |
2349 | if(!quote.isNull()) { |
2350 | if(*(d+d_off) == quote) |
2351 | quote = QChar(); |
2352 | } else if(*(d+d_off) == |
2353 | ++parens; |
2354 | } else if(*(d+d_off) == |
2355 | --parens; |
2356 | } else if(*(d+d_off) == |
2357 | quote = *(d+d_off); |
2358 | } |
2359 | if(!parens && quote.isNull() && (*(d+d_off) == QLatin1Char( |
2360 | if(d_off == d_len-1) |
2361 | test += *(d+d_off); |
2362 | if(!test.isEmpty()) { |
2363 | if (or_op != ret) |
2364 | ret = doProjectTest(test, place); |
2365 | test.clear(); |
2366 | } |
2367 | if(*(d+d_off) == QLatin1Char( |
2368 | or_op = false; |
2369 | } else if(*(d+d_off) == QLatin1Char( |
2370 | or_op = true; |
2371 | } |
2372 | } else { |
2373 | test += *(d+d_off); |
2374 | } |
2375 | } |
2376 | return ret; } |
2377 | case T_EQUALS: |
2378 | if(args.count() != 2) { |
2379 | fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(), |
2380 | parser.line_no, func.toLatin1().constData()); |
2381 | return false; |
2382 | } |
2383 | return values(args[0], place).join(QString(Option::field_sep)) == args[1]; |
2384 | case T_EXISTS: { |
2385 | if(args.count() != 1) { |
2386 | fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.toLatin1().constData(), |
2387 | parser.line_no); |
2388 | return false; |
2389 | } |
2390 | QString file = args.first(); |
2391 | file = Option::fixPathToLocalOS(file); |
2392 | |
2393 | if(QFile::exists(file)) |
2394 | return true; |
2395 | //regular expression I guess |
2396 | QString dirstr = qmake_getpwd(); |
2397 | int slsh = file.lastIndexOf(QDir::separator()); |
2398 | if(slsh != -1) { |
2399 | dirstr = file.left(slsh+1); |
2400 | file = file.right(file.length() - slsh - 1); |
2401 | } |
2402 | return QDir(dirstr).entryList(QStringList(file)).count(); } |
2403 | case T_EXPORT: |
2404 | if(args.count() != 1) { |
2405 | fprintf(stderr, "%s:%d: export(variable) requires one argument.\n", parser.file.toLatin1().constData(), |
2406 | parser.line_no); |
2407 | return false; |
2408 | } |
2409 | for(int i = 0; i < function_blocks.size(); ++i) { |
2410 | FunctionBlock *f = function_blocks.at(i); |
2411 | f->vars[args[0]] = values(args[0], place); |
2412 | if(!i && f->calling_place) |
2413 | (*f->calling_place)[args[0]] = values(args[0], place); |
2414 | } |
2415 | return true; |
2416 | case T_CLEAR: |
2417 | if(args.count() != 1) { |
2418 | fprintf(stderr, "%s:%d: clear(variable) requires one argument.\n", parser.file.toLatin1().constData(), |
2419 | parser.line_no); |
2420 | return false; |
2421 | } |
2422 | if(!place.contains(args[0])) |
2423 | return false; |
2424 | place[args[0]].clear(); |
2425 | return true; |
2426 | case T_UNSET: |
2427 | if(args.count() != 1) { |
2428 | fprintf(stderr, "%s:%d: unset(variable) requires one argument.\n", parser.file.toLatin1().constData(), |
2429 | parser.line_no); |
2430 | return false; |
2431 | } |
2432 | if(!place.contains(args[0])) |
2433 | return false; |
2434 | place.remove(args[0]); |
2435 | return true; |
2436 | case T_EVAL: { |
2437 | if(args.count() < 1 && 0) { |
2438 | fprintf(stderr, "%s:%d: eval(project) requires one argument.\n", parser.file.toLatin1().constData(), |
2439 | parser.line_no); |
2440 | return false; |
2441 | } |
2442 | QString project = args.join(" "); |
2443 | parser_info pi = parser; |
2444 | parser.from_file = false; |
2445 | parser.file = "(eval)"; |
2446 | parser.line_no = 0; |
2447 | QTextStream t(&project, QIODevice::ReadOnly); |
2448 | bool ret = read(t, place); |
2449 | parser = pi; |
2450 | return ret; } |
2451 | case T_CONFIG: { |
2452 | if(args.count() < 1 || args.count() > 2) { |
2453 | fprintf(stderr, "%s:%d: CONFIG(config) requires one argument.\n", parser.file.toLatin1().constData(), |
2454 | parser.line_no); |
2455 | return false; |
2456 | } |
2457 | if(args.count() == 1) |
2458 | return isActiveConfig(args[0]); |
2459 | const QStringList mutuals = args[1].split( |
2460 | const QStringList &configs = values("CONFIG", place); |
2461 | for(int i = configs.size()-1; i >= 0; i--) { |
2462 | for(int mut = 0; mut < mutuals.count(); mut++) { |
2463 | if(configs[i] == mutuals[mut].trimmed()) |
2464 | return (configs[i] == args[0]); |
2465 | } |
2466 | } |
2467 | return false; } |
2468 | case T_SYSTEM: |
2469 | if(args.count() < 1 || args.count() > 2) { |
2470 | fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.toLatin1().constData(), |
2471 | parser.line_no); |
2472 | return false; |
2473 | } |
2474 | if(args.count() == 2) { |
2475 | const QString sarg = args[1]; |
2476 | if (sarg.toLower() == "true"|| sarg.toInt()) |
2477 | warn_msg(WarnParser, "%s:%d: system()'s second argument is now hard-wired to false.\n", |
2478 | parser.file.toLatin1().constData(), parser.line_no); |
2479 | } |
2480 | return system(args[0].toLatin1().constData()) == 0; |
2481 | case T_RETURN: |
2482 | if(function_blocks.isEmpty()) { |
2483 | fprintf(stderr, "%s:%d unexpected return()\n", |
2484 | parser.file.toLatin1().constData(), parser.line_no); |
2485 | } else { |
2486 | FunctionBlock *f = function_blocks.top(); |
2487 | f->cause_return = true; |
2488 | if(args_list.count() >= 1) |
2489 | f->return_value += args_list[0]; |
2490 | } |
2491 | return true; |
2492 | case T_BREAK: |
2493 | if(iterator) |
2494 | iterator->cause_break = true; |
2495 | else |
2496 | fprintf(stderr, "%s:%d unexpected break()\n", |
2497 | parser.file.toLatin1().constData(), parser.line_no); |
2498 | return true; |
2499 | case T_NEXT: |
2500 | if(iterator) |
2501 | iterator->cause_next = true; |
2502 | else |
2503 | fprintf(stderr, "%s:%d unexpected next()\n", |
2504 | parser.file.toLatin1().constData(), parser.line_no); |
2505 | return true; |
2506 | case T_DEFINED: |
2507 | if(args.count() < 1 || args.count() > 2) { |
2508 | fprintf(stderr, "%s:%d: defined(function) requires one argument.\n", |
2509 | parser.file.toLatin1().constData(), parser.line_no); |
2510 | } else { |
2511 | if(args.count() > 1) { |
2512 | if(args[1] == "test") |
2513 | return testFunctions.contains(args[0]); |
2514 | else if(args[1] == "replace") |
2515 | return replaceFunctions.contains(args[0]); |
2516 | fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n", |
2517 | parser.file.toLatin1().constData(), parser.line_no, |
2518 | args[1].toLatin1().constData()); |
2519 | } else { |
2520 | if(replaceFunctions.contains(args[0]) || testFunctions.contains(args[0])) |
2521 | return true; |
2522 | } |
2523 | } |
2524 | return false; |
2525 | case T_CONTAINS: { |
2526 | if(args.count() < 2 || args.count() > 3) { |
2527 | fprintf(stderr, "%s:%d: contains(var, val) requires at least 2 arguments.\n", |
2528 | parser.file.toLatin1().constData(), parser.line_no); |
2529 | return false; |
2530 | } |
2531 | QRegExp regx(args[1]); |
2532 | const QStringList &l = values(args[0], place); |
2533 | if(args.count() == 2) { |
2534 | for(int i = 0; i < l.size(); ++i) { |
2535 | const QString val = l[i]; |
2536 | if(regx.exactMatch(val) || val == args[1]) |
2537 | return true; |
2538 | } |
2539 | } else { |
2540 | const QStringList mutuals = args[2].split( |
2541 | for(int i = l.size()-1; i >= 0; i--) { |
2542 | const QString val = l[i]; |
2543 | for(int mut = 0; mut < mutuals.count(); mut++) { |
2544 | if(val == mutuals[mut].trimmed()) |
2545 | return (regx.exactMatch(val) || val == args[1]); |
2546 | } |
2547 | } |
2548 | } |
2549 | return false; } |
2550 | case T_INFILE: { |
2551 | if(args.count() < 2 || args.count() > 3) { |
2552 | fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n", |
2553 | parser.file.toLatin1().constData(), parser.line_no); |
2554 | return false; |
2555 | } |
2556 | |
2557 | bool ret = false; |
2558 | QMap<QString, QStringList> tmp; |
2559 | if(doProjectInclude(Option::fixPathToLocalOS(args[0]), IncludeFlagNewParser, tmp) == IncludeSuccess) { |
2560 | if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) { |
2561 | QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"]; |
2562 | const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"]; |
2563 | for(int i = 0; i < in.size(); ++i) { |
2564 | if(out.indexOf(in[i]) == -1) |
2565 | out += in[i]; |
2566 | } |
2567 | } |
2568 | if(args.count() == 2) { |
2569 | ret = tmp.contains(args[1]); |
2570 | } else { |
2571 | QRegExp regx(args[2]); |
2572 | const QStringList &l = tmp[args[1]]; |
2573 | for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { |
2574 | if(regx.exactMatch((*it)) || (*it) == args[2]) { |
2575 | ret = true; |
2576 | break; |
2577 | } |
2578 | } |
2579 | } |
2580 | } |
2581 | return ret; } |
2582 | case T_COUNT: |
2583 | if(args.count() != 2 && args.count() != 3) { |
2584 | fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.toLatin1().constData(), |
2585 | parser.line_no); |
2586 | return false; |
2587 | } |
2588 | if(args.count() == 3) { |
2589 | QString comp = args[2]; |
2590 | if(comp == ">"|| comp == "greaterThan") |
2591 | return values(args[0], place).count() > args[1].toInt(); |
2592 | if(comp == ">=") |
2593 | return values(args[0], place).count() >= args[1].toInt(); |
2594 | if(comp == "<"|| comp == "lessThan") |
2595 | return values(args[0], place).count() < args[1].toInt(); |
2596 | if(comp == "<=") |
2597 | return values(args[0], place).count() <= args[1].toInt(); |
2598 | if(comp == "equals"|| comp == "isEqual"|| comp == "="|| comp == "==") |
2599 | return values(args[0], place).count() == args[1].toInt(); |
2600 | fprintf(stderr, "%s:%d: unexpected modifier to count(%s)\n", parser.file.toLatin1().constData(), |
2601 | parser.line_no, comp.toLatin1().constData()); |
2602 | return false; |
2603 | } |
2604 | return values(args[0], place).count() == args[1].toInt(); |
2605 | case T_ISEMPTY: |
2606 | if(args.count() != 1) { |
2607 | fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.toLatin1().constData(), |
2608 | parser.line_no); |
2609 | return false; |
2610 | } |
2611 | return values(args[0], place).isEmpty(); |
2612 | case T_INCLUDE: |
2613 | case T_LOAD: { |
2614 | QString parseInto; |
2615 | const bool include_statement = (func_t == T_INCLUDE); |
2616 | bool ignore_error = false; |
2617 | if(args.count() >= 2) { |
2618 | if(func_t == T_INCLUDE) { |
2619 | parseInto = args[1]; |
2620 | if (args.count() == 3){ |
2621 | QString sarg = args[2]; |
2622 | if (sarg.toLower() == "true"|| sarg.toInt()) |
2623 | ignore_error = true; |
2624 | } |
2625 | } else { |
2626 | QString sarg = args[1]; |
2627 | ignore_error = (sarg.toLower() == "true"|| sarg.toInt()); |
2628 | } |
2629 | } else if(args.count() != 1) { |
2630 | QString func_desc = "load(feature)"; |
2631 | if(include_statement) |
2632 | func_desc = "include(file)"; |
2633 | fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.toLatin1().constData(), |
2634 | parser.line_no, func_desc.toLatin1().constData()); |
2635 | return false; |
2636 | } |
2637 | QString file = args.first(); |
2638 | file = Option::fixPathToLocalOS(file); |
2639 | uchar flags = IncludeFlagNone; |
2640 | if(!include_statement) |
2641 | flags |= IncludeFlagFeature; |
2642 | IncludeStatus stat = IncludeFailure; |
2643 | if(!parseInto.isEmpty()) { |
2644 | QMap<QString, QStringList> symbols; |
2645 | stat = doProjectInclude(file, flags|IncludeFlagNewProject, symbols); |
2646 | if(stat == IncludeSuccess) { |
2647 | QMap<QString, QStringList> out_place; |
2648 | for(QMap<QString, QStringList>::ConstIterator it = place.begin(); it != place.end(); ++it) { |
2649 | const QString var = it.key(); |
2650 | if(var != parseInto && !var.startsWith(parseInto + ".")) |
2651 | out_place.insert(var, it.value()); |
2652 | } |
2653 | for(QMap<QString, QStringList>::ConstIterator it = symbols.begin(); it != symbols.end(); ++it) { |
2654 | const QString var = it.key(); |
2655 | if(!var.startsWith(".")) |
2656 | out_place.insert(parseInto + "."+ it.key(), it.value()); |
2657 | } |
2658 | place = out_place; |
2659 | } |
2660 | } else { |
2661 | stat = doProjectInclude(file, flags, place); |
2662 | } |
2663 | if(stat == IncludeFeatureAlreadyLoaded) { |
2664 | warn_msg(WarnParser, "%s:%d: Duplicate of loaded feature %s", |
2665 | parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData()); |
2666 | } else if(stat == IncludeNoExist && !ignore_error) { |
2667 | warn_msg(WarnAll, "%s:%d: Unable to find file for inclusion %s", |
2668 | parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData()); |
2669 | return false; |
2670 | } else if(stat >= IncludeFailure) { |
2671 | if(!ignore_error) { |
2672 | printf("Project LOAD(): Feature %s cannot be found.\n", file.toLatin1().constData()); |
2673 | if (!ignore_error) |
2674 | #if defined(QT_BUILD_QMAKE_LIBRARY) |
2675 | return false; |
2676 | #else |
2677 | exit(3); |
2678 | #endif |
2679 | } |
2680 | return false; |
2681 | } |
2682 | return true; } |
2683 | case T_DEBUG: { |
2684 | if(args.count() != 2) { |
2685 | fprintf(stderr, "%s:%d: debug(level, message) requires one argument.\n", parser.file.toLatin1().constData(), |
2686 | parser.line_no); |
2687 | return false; |
2688 | } |
2689 | QString msg = fixEnvVariables(args[1]); |
2690 | debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData()); |
2691 | return true; } |
2692 | case T_ERROR: |
2693 | case T_MESSAGE: |
2694 | case T_WARNING: { |
2695 | if(args.count() != 1) { |
2696 | fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.toLatin1().constData(), |
2697 | parser.line_no, func.toLatin1().constData()); |
2698 | return false; |
2699 | } |
2700 | QString msg = fixEnvVariables(args.first()); |
2701 | fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData()); |
2702 | if(func == "error") |
2703 | #if defined(QT_BUILD_QMAKE_LIBRARY) |
2704 | return false; |
2705 | #else |
2706 | exit(2); |
2707 | #endif |
2708 | return true; } |
2709 | case T_OPTION: |
2710 | if (args.count() != 1) { |
2711 | fprintf(stderr, "%s:%d: option() requires one argument.\n", |
2712 | parser.file.toLatin1().constData(), parser.line_no); |
2713 | return false; |
2714 | } |
2715 | if (args.first() == "recursive") { |
2716 | recursive = true; |
2717 | } else { |
2718 | fprintf(stderr, "%s:%d: unrecognized option() argument '%s'.\n", |
2719 | parser.file.toLatin1().constData(), parser.line_no, |
2720 | args.first().toLatin1().constData()); |
2721 | return false; |
2722 | } |
2723 | return true; |
2724 | default: |
2725 | fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no, |
2726 | func.toLatin1().constData()); |
2727 | } |
2728 | return false; |
2729 | } |
2730 | |
2731 | bool |
2732 | QMakeProject::doProjectCheckReqs(const QStringList &deps, QMap<QString, QStringList> &place) |
2733 | { |
2734 | bool ret = false; |
2735 | for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) { |
2736 | bool test = doProjectTest((*it), place); |
2737 | if(!test) { |
2738 | debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s", |
2739 | parser.file.toLatin1().constData(), parser.line_no, |
2740 | (*it).toLatin1().constData()); |
2741 | place["QMAKE_FAILED_REQUIREMENTS"].append((*it)); |
2742 | ret = false; |
2743 | } |
2744 | } |
2745 | return ret; |
2746 | } |
2747 | |
2748 | bool |
2749 | QMakeProject::test(const QString &v) |
2750 | { |
2751 | QMap<QString, QStringList> tmp = vars; |
2752 | return doProjectTest(v, tmp); |
2753 | } |
2754 | |
2755 | bool |
2756 | QMakeProject::test(const QString &func, const QList<QStringList> &args) |
2757 | { |
2758 | QMap<QString, QStringList> tmp = vars; |
2759 | return doProjectTest(func, args, tmp); |
2760 | } |
2761 | |
2762 | QStringList |
2763 | QMakeProject::expand(const QString &str) |
2764 | { |
2765 | bool ok; |
2766 | QMap<QString, QStringList> tmp = vars; |
2767 | const QStringList ret = doVariableReplaceExpand(str, tmp, &ok); |
2768 | if(ok) |
2769 | return ret; |
2770 | return QStringList(); |
2771 | } |
2772 | |
2773 | QString |
2774 | QMakeProject::expand(const QString &str, const QString &file, int line) |
2775 | { |
2776 | bool ok; |
2777 | parser_info pi = parser; |
2778 | parser.file = file; |
2779 | parser.line_no = line; |
2780 | parser.from_file = false; |
2781 | QMap<QString, QStringList> tmp = vars; |
2782 | const QStringList ret = doVariableReplaceExpand(str, tmp, &ok); |
2783 | parser = pi; |
2784 | return ok ? ret.join(QString(Option::field_sep)) : QString(); |
2785 | } |
2786 | |
2787 | QStringList |
2788 | QMakeProject::expand(const QString &func, const QList<QStringList> &args) |
2789 | { |
2790 | QMap<QString, QStringList> tmp = vars; |
2791 | return doProjectExpand(func, args, tmp); |
2792 | } |
2793 | |
2794 | bool |
2795 | QMakeProject::doVariableReplace(QString &str, QMap<QString, QStringList> &place) |
2796 | { |
2797 | bool ret; |
2798 | str = doVariableReplaceExpand(str, place, &ret).join(QString(Option::field_sep)); |
2799 | return ret; |
2800 | } |
2801 | |
2802 | QStringList |
2803 | QMakeProject::doVariableReplaceExpand(const QString &str, QMap<QString, QStringList> &place, bool *ok) |
2804 | { |
2805 | QStringList ret; |
2806 | if(ok) |
2807 | *ok = true; |
2808 | if(str.isEmpty()) |
2809 | return ret; |
2810 | |
2811 | const ushort LSQUARE = |
2812 | const ushort RSQUARE = |
2813 | const ushort LCURLY = |
2814 | const ushort RCURLY = |
2815 | const ushort LPAREN = |
2816 | const ushort RPAREN = |
2817 | const ushort DOLLAR = |
2818 | const ushort SLASH = |
2819 | const ushort UNDERSCORE = |
2820 | const ushort DOT = |
2821 | const ushort SPACE = |
2822 | const ushort TAB = |
2823 | const ushort SINGLEQUOTE = |
2824 | const ushort DOUBLEQUOTE = |
2825 | |
2826 | ushort unicode, quote = 0; |
2827 | const QChar *str_data = str.data(); |
2828 | const int str_len = str.length(); |
2829 | |
2830 | ushort term; |
2831 | QString var, args; |
2832 | |
2833 | int replaced = 0; |
2834 | QString current; |
2835 | for(int i = 0; i < str_len; ++i) { |
2836 | unicode = str_data[i].unicode(); |
2837 | const int start_var = i; |
2838 | if(unicode == DOLLAR && str_len > i+2) { |
2839 | unicode = str_data[++i].unicode(); |
2840 | if(unicode == DOLLAR) { |
2841 | term = 0; |
2842 | var.clear(); |
2843 | args.clear(); |
2844 | enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR; |
2845 | unicode = str_data[++i].unicode(); |
2846 | if(unicode == LSQUARE) { |
2847 | unicode = str_data[++i].unicode(); |
2848 | term = RSQUARE; |
2849 | var_type = PROPERTY; |
2850 | } else if(unicode == LCURLY) { |
2851 | unicode = str_data[++i].unicode(); |
2852 | var_type = VAR; |
2853 | term = RCURLY; |
2854 | } else if(unicode == LPAREN) { |
2855 | unicode = str_data[++i].unicode(); |
2856 | var_type = ENVIRON; |
2857 | term = RPAREN; |
2858 | } |
2859 | while(1) { |
2860 | if(!(unicode & (0xFF<<8)) && |
2861 | unicode != DOT && unicode != UNDERSCORE && |
2862 | //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE && |
2863 | (unicode < |
2864 | (unicode < |
2865 | break; |
2866 | var.append(QChar(unicode)); |
2867 | if(++i == str_len) |
2868 | break; |
2869 | unicode = str_data[i].unicode(); |
2870 | // at this point, i points to either the 'term' or 'next' character (which is in unicode) |
2871 | } |
2872 | if(var_type == VAR && unicode == LPAREN) { |
2873 | var_type = FUNCTION; |
2874 | int depth = 0; |
2875 | while(1) { |
2876 | if(++i == str_len) |
2877 | break; |
2878 | unicode = str_data[i].unicode(); |
2879 | if(unicode == LPAREN) { |
2880 | depth++; |
2881 | } else if(unicode == RPAREN) { |
2882 | if(!depth) |
2883 | break; |
2884 | --depth; |
2885 | } |
2886 | args.append(QChar(unicode)); |
2887 | } |
2888 | if(++i < str_len) |
2889 | unicode = str_data[i].unicode(); |
2890 | else |
2891 | unicode = 0; |
2892 | // at this point i is pointing to the 'next' character (which is in unicode) |
2893 | // this might actually be a term character since you can do $${func()} |
2894 | } |
2895 | if(term) { |
2896 | if(unicode != term) { |
2897 | qmake_error_msg("Missing "+ QString(term) + " terminator [found "+ (unicode?QString(unicode):QString( "end-of-line")) + "]"); |
2898 | if(ok) |
2899 | *ok = false; |
2900 | return QStringList(); |
2901 | } |
2902 | } else { |
2903 | // move the 'cursor' back to the last char of the thing we were looking at |
2904 | --i; |
2905 | } |
2906 | // since i never points to the 'next' character, there is no reason for this to be set |
2907 | unicode = 0; |
2908 | |
2909 | QStringList replacement; |
2910 | if(var_type == ENVIRON) { |
2911 | replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData()))); |
2912 | } else if(var_type == PROPERTY) { |
2913 | if(prop) |
2914 | replacement = split_value_list(prop->value(var)); |
2915 | } else if(var_type == FUNCTION) { |
2916 | replacement = doProjectExpand(var, args, place); |
2917 | } else if(var_type == VAR) { |
2918 | replacement = values(var, place); |
2919 | } |
2920 | if(!(replaced++) && start_var) |
2921 | current = str.left(start_var); |
2922 | if(!replacement.isEmpty()) { |
2923 | if(quote) { |
2924 | current += replacement.join(QString(Option::field_sep)); |
2925 | } else { |
2926 | current += replacement.takeFirst(); |
2927 | if(!replacement.isEmpty()) { |
2928 | if(!current.isEmpty()) |
2929 | ret.append(current); |
2930 | current = replacement.takeLast(); |
2931 | if(!replacement.isEmpty()) |
2932 | ret += replacement; |
2933 | } |
2934 | } |
2935 | } |
2936 | debug_msg(2, "Project Parser [var replace]: %s -> %s", |
2937 | str.toLatin1().constData(), var.toLatin1().constData(), |
2938 | replacement.join("::").toLatin1().constData()); |
2939 | } else { |
2940 | if(replaced) |
2941 | current.append("$"); |
2942 | } |
2943 | } |
2944 | if(quote && unicode == quote) { |
2945 | unicode = 0; |
2946 | quote = 0; |
2947 | } else if(unicode == SLASH) { |
2948 | bool escape = false; |
2949 | const char *symbols = "[]{}()$\\'\""; |
2950 | for(const char *s = symbols; *s; ++s) { |
2951 | if(str_data[i+1].unicode() == (ushort)*s) { |
2952 | i++; |
2953 | escape = true; |
2954 | if(!(replaced++)) |
2955 | current = str.left(start_var); |
2956 | current.append(str.at(i)); |
2957 | break; |
2958 | } |
2959 | } |
2960 | if(!escape && !backslashWarned) { |
2961 | backslashWarned = true; |
2962 | warn_msg(WarnDeprecated, "%s:%d: Unescaped backslashes are deprecated.", |
2963 | parser.file.toLatin1().constData(), parser.line_no); |
2964 | } |
2965 | if(escape || !replaced) |
2966 | unicode =0; |
2967 | } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { |
2968 | quote = unicode; |
2969 | unicode = 0; |
2970 | if(!(replaced++) && i) |
2971 | current = str.left(i); |
2972 | } else if(!quote && (unicode == SPACE || unicode == TAB)) { |
2973 | unicode = 0; |
2974 | if(!current.isEmpty()) { |
2975 | ret.append(current); |
2976 | current.clear(); |
2977 | } |
2978 | } |
2979 | if(replaced && unicode) |
2980 | current.append(QChar(unicode)); |
2981 | } |
2982 | if(!replaced) |
2983 | ret = QStringList(str); |
2984 | else if(!current.isEmpty()) |
2985 | ret.append(current); |
2986 | //qDebug() << "REPLACE" << str << ret; |
2987 | if (quote) |
2988 | warn_msg(WarnDeprecated, "%s:%d: Unmatched quotes are deprecated.", |
2989 | parser.file.toLatin1().constData(), parser.line_no); |
2990 | return ret; |
2991 | } |
2992 | |
2993 | QStringList &QMakeProject::values(const QString &_var, QMap<QString, QStringList> &place) |
2994 | { |
2995 | QString var = varMap(_var); |
2996 | if(var == QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token) |
2997 | var = ".BUILTIN."+ var; |
2998 | place[var] = QStringList(QLatin1String("\t")); |
2999 | } else if(var == QLatin1String("LITERAL_DOLLAR")) { //a real $ |
3000 | var = ".BUILTIN."+ var; |
3001 | place[var] = QStringList(QLatin1String("$")); |
3002 | } else if(var == QLatin1String("LITERAL_HASH")) { //a real # |
3003 | var = ".BUILTIN."+ var; |
3004 | place[var] = QStringList("#"); |
3005 | } else if(var == QLatin1String("OUT_PWD")) { //the out going dir |
3006 | var = ".BUILTIN."+ var; |
3007 | place[var] = QStringList(Option::output_dir); |
3008 | } else if(var == QLatin1String("PWD") || //current working dir (of _FILE_) |
3009 | var == QLatin1String("IN_PWD")) { |
3010 | var = ".BUILTIN."+ var; |
3011 | place[var] = QStringList(qmake_getpwd()); |
3012 | } else if(var == QLatin1String("DIR_SEPARATOR")) { |
3013 | validateModes(); |
3014 | var = ".BUILTIN."+ var; |
3015 | place[var] = QStringList(Option::dir_sep); |
3016 | } else if(var == QLatin1String("DIRLIST_SEPARATOR")) { |
3017 | var = ".BUILTIN."+ var; |
3018 | place[var] = QStringList(Option::dirlist_sep); |
3019 | } else if(var == QLatin1String("_LINE_")) { //parser line number |
3020 | var = ".BUILTIN."+ var; |
3021 | place[var] = QStringList(QString::number(parser.line_no)); |
3022 | } else if(var == QLatin1String("_FILE_")) { //parser file |
3023 | var = ".BUILTIN."+ var; |
3024 | place[var] = QStringList(parser.file); |
3025 | } else if(var == QLatin1String("_DATE_")) { //current date/time |
3026 | var = ".BUILTIN."+ var; |
3027 | place[var] = QStringList(QDateTime::currentDateTime().toString()); |
3028 | } else if(var == QLatin1String("_PRO_FILE_")) { |
3029 | var = ".BUILTIN."+ var; |
3030 | place[var] = QStringList(pfile); |
3031 | } else if(var == QLatin1String("_PRO_FILE_PWD_")) { |
3032 | var = ".BUILTIN."+ var; |
3033 | place[var] = QStringList(pfile.isEmpty() ? qmake_getpwd() : QFileInfo(pfile).absolutePath()); |
3034 | } else if(var == QLatin1String("_QMAKE_CACHE_")) { |
3035 | var = ".BUILTIN."+ var; |
3036 | if(Option::mkfile::do_cache) |
3037 | place[var] = QStringList(Option::mkfile::cachefile); |
3038 | } else if(var == QLatin1String("TEMPLATE")) { |
3039 | if(!Option::user_template.isEmpty()) { |
3040 | var = ".BUILTIN.USER."+ var; |
3041 | place[var] = QStringList(Option::user_template); |
3042 | } else { |
3043 | QString orig_template, real_template; |
3044 | if(!place[var].isEmpty()) |
3045 | orig_template = place[var].first(); |
3046 | real_template = orig_template.isEmpty() ? "app": orig_template; |
3047 | if(!Option::user_template_prefix.isEmpty() && !orig_template.startsWith(Option::user_template_prefix)) |
3048 | real_template.prepend(Option::user_template_prefix); |
3049 | if(real_template != orig_template) { |
3050 | var = ".BUILTIN."+ var; |
3051 | place[var] = QStringList(real_template); |
3052 | } |
3053 | } |
3054 | } else if(var.startsWith(QLatin1String("QMAKE_HOST."))) { |
3055 | QString ret, type = var.mid(11); |
3056 | #if defined(Q_OS_WIN32) |
3057 | if(type == "os") { |
3058 | ret = "Windows"; |
3059 | } else if(type == "name") { |
3060 | DWORD name_length = 1024; |
3061 | wchar_t name[1024]; |
3062 | if (GetComputerName(name, &name_length)) |
3063 | ret = QString::fromWCharArray(name); |
3064 | } else if(type == "version"|| type == "version_string") { |
3065 | QSysInfo::WinVersion ver = QSysInfo::WindowsVersion; |
3066 | if(type == "version") |
3067 | ret = QString::number(ver); |
3068 | else if(ver == QSysInfo::WV_Me) |
3069 | ret = "WinMe"; |
3070 | else if(ver == QSysInfo::WV_95) |
3071 | ret = "Win95"; |
3072 | else if(ver == QSysInfo::WV_98) |
3073 | ret = "Win98"; |
3074 | else if(ver == QSysInfo::WV_NT) |
3075 | ret = "WinNT"; |
3076 | else if(ver == QSysInfo::WV_2000) |
3077 | ret = "Win2000"; |
3078 | else if(ver == QSysInfo::WV_2000) |
3079 | ret = "Win2003"; |
3080 | else if(ver == QSysInfo::WV_XP) |
3081 | ret = "WinXP"; |
3082 | else if(ver == QSysInfo::WV_VISTA) |
3083 | ret = "WinVista"; |
3084 | else |
3085 | ret = "Unknown"; |
3086 | } else if(type == "arch") { |
3087 | SYSTEM_INFO info; |
3088 | GetSystemInfo(&info); |
3089 | switch(info.wProcessorArchitecture) { |
3090 | #ifdef PROCESSOR_ARCHITECTURE_AMD64 |
3091 | case PROCESSOR_ARCHITECTURE_AMD64: |
3092 | ret = "x86_64"; |
3093 | break; |
3094 | #endif |
3095 | case PROCESSOR_ARCHITECTURE_INTEL: |
3096 | ret = "x86"; |
3097 | break; |
3098 | case PROCESSOR_ARCHITECTURE_IA64: |
3099 | #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 |
3100 | case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: |
3101 | #endif |
3102 | ret = "IA64"; |
3103 | break; |
3104 | default: |
3105 | ret = "Unknown"; |
3106 | break; |
3107 | } |
3108 | } |
3109 | #elif defined(Q_OS_UNIX) |
3110 | struct utsname name; |
3111 | if(!uname(&name)) { |
3112 | if(type == "os") |
3113 | ret = name.sysname; |
3114 | else if(type == "name") |
3115 | ret = name.nodename; |
3116 | else if(type == "version") |
3117 | ret = name.release; |
3118 | else if(type == "version_string") |
3119 | ret = name.version; |
3120 | else if(type == "arch") |
3121 | ret = name.machine; |
3122 | } |
3123 | #endif |
3124 | var = ".BUILTIN.HOST."+ type; |
3125 | place[var] = QStringList(ret); |
3126 | } else if (var == QLatin1String("QMAKE_DIR_SEP")) { |
3127 | if (place[var].isEmpty()) |
3128 | return values("DIR_SEPARATOR", place); |
3129 | } else if (var == QLatin1String("QMAKE_EXT_OBJ")) { |
3130 | if (place[var].isEmpty()) { |
3131 | var = ".BUILTIN."+ var; |
3132 | place[var] = QStringList(Option::obj_ext); |
3133 | } |
3134 | } else if (var == QLatin1String("QMAKE_QMAKE")) { |
3135 | if (place[var].isEmpty()) |
3136 | place[var] = QStringList(Option::fixPathToTargetOS( |
3137 | !Option::qmake_abslocation.isEmpty() |
3138 | ? Option::qmake_abslocation |
3139 | : QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmake", |
3140 | false)); |
3141 | } else if (var == QLatin1String("EPOCROOT")) { |
3142 | if (place[var].isEmpty()) |
3143 | place[var] = QStringList(qt_epocRoot()); |
3144 | } |
3145 | #if defined(Q_OS_WIN32) && defined(Q_CC_MSVC) |
3146 | else if(var.startsWith(QLatin1String("QMAKE_TARGET."))) { |
3147 | QString ret, type = var.mid(13); |
3148 | if(type == "arch") { |
3149 | QString paths = qgetenv("PATH"); |
3150 | QString vcBin64 = qgetenv("VCINSTALLDIR"); |
3151 | if (!vcBin64.endsWith( |
3152 | vcBin64.append( |
3153 | vcBin64.append("bin\\amd64"); |
3154 | QString vcBinX86_64 = qgetenv("VCINSTALLDIR"); |
3155 | if (!vcBinX86_64.endsWith( |
3156 | vcBinX86_64.append( |
3157 | vcBinX86_64.append("bin\\x86_amd64"); |
3158 | if(paths.contains(vcBin64,Qt::CaseInsensitive) || paths.contains(vcBinX86_64,Qt::CaseInsensitive)) |
3159 | ret = "x86_64"; |
3160 | else |
3161 | ret = "x86"; |
3162 | } |
3163 | place[var] = QStringList(ret); |
3164 | } |
3165 | #endif |
3166 | //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::"))); |
3167 | return place[var]; |
3168 | } |
3169 | |
3170 | bool QMakeProject::isEmpty(const QString &v) |
3171 | { |
3172 | QMap<QString, QStringList>::ConstIterator it = vars.constFind(varMap(v)); |
3173 | return it == vars.constEnd() || it->isEmpty(); |
3174 | } |
3175 | |
3176 | QT_END_NAMESPACE |
3177 |
Warning: That file was not part of the compilation database. It may have many parsing errors.