|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the qmake application of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "makefiledeps.h" |
|
43 #include "option.h" |
|
44 #include <qdir.h> |
|
45 #include <qdatetime.h> |
|
46 #include <qfileinfo.h> |
|
47 #include <qbuffer.h> |
|
48 #include <qplatformdefs.h> |
|
49 #if defined(Q_OS_UNIX) |
|
50 # include <unistd.h> |
|
51 #else |
|
52 # include <io.h> |
|
53 #endif |
|
54 #include <qdebug.h> |
|
55 #include <stdio.h> |
|
56 #include <stdlib.h> |
|
57 #include <time.h> |
|
58 #include <fcntl.h> |
|
59 #include <sys/types.h> |
|
60 #include <sys/stat.h> |
|
61 #if defined(_MSC_VER) && _MSC_VER >= 1400 |
|
62 #include <share.h> |
|
63 #endif |
|
64 |
|
65 QT_BEGIN_NAMESPACE |
|
66 |
|
67 #if 1 |
|
68 #define qmake_endOfLine(c) (c == '\r' || c == '\n') |
|
69 #else |
|
70 inline bool qmake_endOfLine(const char &c) { return (c == '\r' || c == '\n'); } |
|
71 #endif |
|
72 |
|
73 //#define QMAKE_USE_CACHE |
|
74 |
|
75 QMakeLocalFileName::QMakeLocalFileName(const QString &name) : is_null(name.isNull()) |
|
76 { |
|
77 if(!name.isEmpty()) { |
|
78 if(name.at(0) == QLatin1Char('"') && name.at(name.length()-2) == QLatin1Char('"')) |
|
79 real_name = name.mid(1, name.length()-2); |
|
80 else |
|
81 real_name = name; |
|
82 } |
|
83 } |
|
84 const QString |
|
85 &QMakeLocalFileName::local() const |
|
86 { |
|
87 if(!is_null && local_name.isNull()) |
|
88 local_name = Option::fixPathToLocalOS(real_name, true); |
|
89 return local_name; |
|
90 } |
|
91 |
|
92 struct SourceDependChildren; |
|
93 struct SourceFile { |
|
94 SourceFile() : deps(0), type(QMakeSourceFileInfo::TYPE_UNKNOWN), |
|
95 mocable(0), traversed(0), exists(1), |
|
96 moc_checked(0), dep_checked(0), included_count(0) { } |
|
97 ~SourceFile(); |
|
98 QMakeLocalFileName file; |
|
99 SourceDependChildren *deps; |
|
100 QMakeSourceFileInfo::SourceFileType type; |
|
101 uint mocable : 1, traversed : 1, exists : 1; |
|
102 uint moc_checked : 1, dep_checked : 1; |
|
103 uchar included_count; |
|
104 }; |
|
105 struct SourceDependChildren { |
|
106 SourceFile **children; |
|
107 int num_nodes, used_nodes; |
|
108 SourceDependChildren() : children(0), num_nodes(0), used_nodes(0) { } |
|
109 ~SourceDependChildren() { if(children) free(children); children = 0; } |
|
110 void addChild(SourceFile *s) { |
|
111 if(num_nodes <= used_nodes) { |
|
112 num_nodes += 200; |
|
113 children = (SourceFile**)realloc(children, sizeof(SourceFile*)*(num_nodes)); |
|
114 } |
|
115 children[used_nodes++] = s; |
|
116 } |
|
117 }; |
|
118 SourceFile::~SourceFile() { delete deps; } |
|
119 class SourceFiles { |
|
120 int hash(const char *); |
|
121 public: |
|
122 SourceFiles(); |
|
123 ~SourceFiles(); |
|
124 |
|
125 SourceFile *lookupFile(const char *); |
|
126 inline SourceFile *lookupFile(const QString &f) { return lookupFile(f.toLatin1().constData()); } |
|
127 inline SourceFile *lookupFile(const QMakeLocalFileName &f) { return lookupFile(f.local().toLatin1().constData()); } |
|
128 void addFile(SourceFile *, const char *k=0, bool own=true); |
|
129 |
|
130 struct SourceFileNode { |
|
131 SourceFileNode() : key(0), next(0), file(0), own_file(1) { } |
|
132 ~SourceFileNode() { |
|
133 delete [] key; |
|
134 if(own_file) |
|
135 delete file; |
|
136 } |
|
137 char *key; |
|
138 SourceFileNode *next; |
|
139 SourceFile *file; |
|
140 uint own_file : 1; |
|
141 } **nodes; |
|
142 int num_nodes; |
|
143 }; |
|
144 SourceFiles::SourceFiles() |
|
145 { |
|
146 nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037)); |
|
147 for(int n = 0; n < num_nodes; n++) |
|
148 nodes[n] = 0; |
|
149 } |
|
150 |
|
151 SourceFiles::~SourceFiles() |
|
152 { |
|
153 for(int n = 0; n < num_nodes; n++) { |
|
154 for(SourceFileNode *next = nodes[n]; next;) { |
|
155 SourceFileNode *next_next = next->next; |
|
156 delete next; |
|
157 next = next_next; |
|
158 } |
|
159 } |
|
160 free(nodes); |
|
161 } |
|
162 |
|
163 int SourceFiles::hash(const char *file) |
|
164 { |
|
165 uint h = 0, g; |
|
166 while (*file) { |
|
167 h = (h << 4) + *file; |
|
168 if ((g = (h & 0xf0000000)) != 0) |
|
169 h ^= g >> 23; |
|
170 h &= ~g; |
|
171 file++; |
|
172 } |
|
173 return h; |
|
174 } |
|
175 |
|
176 SourceFile *SourceFiles::lookupFile(const char *file) |
|
177 { |
|
178 int h = hash(file) % num_nodes; |
|
179 for(SourceFileNode *p = nodes[h]; p; p = p->next) { |
|
180 if(!strcmp(p->key, file)) |
|
181 return p->file; |
|
182 } |
|
183 return 0; |
|
184 } |
|
185 |
|
186 void SourceFiles::addFile(SourceFile *p, const char *k, bool own_file) |
|
187 { |
|
188 QByteArray ba = p->file.local().toLatin1(); |
|
189 if(!k) |
|
190 k = ba; |
|
191 int h = hash(k) % num_nodes; |
|
192 SourceFileNode *pn = new SourceFileNode; |
|
193 pn->own_file = own_file; |
|
194 pn->key = qstrdup(k); |
|
195 pn->file = p; |
|
196 pn->next = nodes[h]; |
|
197 nodes[h] = pn; |
|
198 } |
|
199 |
|
200 void QMakeSourceFileInfo::dependTreeWalker(SourceFile *node, SourceDependChildren *place) |
|
201 { |
|
202 if(node->traversed || !node->exists) |
|
203 return; |
|
204 place->addChild(node); |
|
205 node->traversed = true; //set flag |
|
206 if(node->deps) { |
|
207 for(int i = 0; i < node->deps->used_nodes; i++) |
|
208 dependTreeWalker(node->deps->children[i], place); |
|
209 } |
|
210 } |
|
211 |
|
212 void QMakeSourceFileInfo::setDependencyPaths(const QList<QMakeLocalFileName> &l) |
|
213 { |
|
214 // Ensure that depdirs does not contain the same paths several times, to minimize the stats |
|
215 QList<QMakeLocalFileName> ll; |
|
216 for (int i = 0; i < l.count(); ++i) { |
|
217 if (!ll.contains(l.at(i))) |
|
218 ll.append(l.at(i)); |
|
219 } |
|
220 depdirs = ll; |
|
221 } |
|
222 |
|
223 QStringList QMakeSourceFileInfo::dependencies(const QString &file) |
|
224 { |
|
225 QStringList ret; |
|
226 if(!files) |
|
227 return ret; |
|
228 |
|
229 if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) { |
|
230 if(node->deps) { |
|
231 /* I stick them into a SourceDependChildren here because it is faster to just |
|
232 iterate over the list to stick them in the list, and reset the flag, then it is |
|
233 to loop over the tree (about 50% faster I saw) --Sam */ |
|
234 SourceDependChildren place; |
|
235 for(int i = 0; i < node->deps->used_nodes; i++) |
|
236 dependTreeWalker(node->deps->children[i], &place); |
|
237 if(place.children) { |
|
238 for(int i = 0; i < place.used_nodes; i++) { |
|
239 place.children[i]->traversed = false; //reset flag |
|
240 ret.append(place.children[i]->file.real()); |
|
241 } |
|
242 } |
|
243 } |
|
244 } |
|
245 return ret; |
|
246 } |
|
247 |
|
248 int |
|
249 QMakeSourceFileInfo::included(const QString &file) |
|
250 { |
|
251 if (!files) |
|
252 return 0; |
|
253 |
|
254 if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) |
|
255 return node->included_count; |
|
256 return 0; |
|
257 } |
|
258 |
|
259 bool QMakeSourceFileInfo::mocable(const QString &file) |
|
260 { |
|
261 if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) |
|
262 return node->mocable; |
|
263 return false; |
|
264 } |
|
265 |
|
266 QMakeSourceFileInfo::QMakeSourceFileInfo(const QString &cf) |
|
267 { |
|
268 //dep_mode |
|
269 dep_mode = Recursive; |
|
270 |
|
271 //quick project lookups |
|
272 includes = files = 0; |
|
273 files_changed = false; |
|
274 |
|
275 //buffer |
|
276 spare_buffer = 0; |
|
277 spare_buffer_size = 0; |
|
278 |
|
279 //cache |
|
280 cachefile = cf; |
|
281 if(!cachefile.isEmpty()) |
|
282 loadCache(cachefile); |
|
283 } |
|
284 |
|
285 QMakeSourceFileInfo::~QMakeSourceFileInfo() |
|
286 { |
|
287 //cache |
|
288 if(!cachefile.isEmpty() /*&& files_changed*/) |
|
289 saveCache(cachefile); |
|
290 |
|
291 //buffer |
|
292 if(spare_buffer) { |
|
293 free(spare_buffer); |
|
294 spare_buffer = 0; |
|
295 spare_buffer_size = 0; |
|
296 } |
|
297 |
|
298 //quick project lookup |
|
299 delete files; |
|
300 delete includes; |
|
301 } |
|
302 |
|
303 void QMakeSourceFileInfo::setCacheFile(const QString &cf) |
|
304 { |
|
305 cachefile = cf; |
|
306 loadCache(cachefile); |
|
307 } |
|
308 |
|
309 void QMakeSourceFileInfo::addSourceFiles(const QStringList &l, uchar seek, |
|
310 QMakeSourceFileInfo::SourceFileType type) |
|
311 { |
|
312 for(int i=0; i<l.size(); ++i) |
|
313 addSourceFile(l.at(i), seek, type); |
|
314 } |
|
315 void QMakeSourceFileInfo::addSourceFile(const QString &f, uchar seek, |
|
316 QMakeSourceFileInfo::SourceFileType type) |
|
317 { |
|
318 if(!files) |
|
319 files = new SourceFiles; |
|
320 |
|
321 QMakeLocalFileName fn(f); |
|
322 SourceFile *file = files->lookupFile(fn); |
|
323 if(!file) { |
|
324 file = new SourceFile; |
|
325 file->file = fn; |
|
326 files->addFile(file); |
|
327 } else { |
|
328 if(file->type != type && file->type != TYPE_UNKNOWN && type != TYPE_UNKNOWN) |
|
329 warn_msg(WarnLogic, "%s is marked as %d, then %d!", f.toLatin1().constData(), |
|
330 file->type, type); |
|
331 } |
|
332 if(type != TYPE_UNKNOWN) |
|
333 file->type = type; |
|
334 |
|
335 if(seek & SEEK_MOCS && !file->moc_checked) |
|
336 findMocs(file); |
|
337 if(seek & SEEK_DEPS && !file->dep_checked) |
|
338 findDeps(file); |
|
339 } |
|
340 |
|
341 bool QMakeSourceFileInfo::containsSourceFile(const QString &f, SourceFileType type) |
|
342 { |
|
343 if(SourceFile *file = files->lookupFile(QMakeLocalFileName(f))) |
|
344 return (file->type == type || file->type == TYPE_UNKNOWN || type == TYPE_UNKNOWN); |
|
345 return false; |
|
346 } |
|
347 |
|
348 char *QMakeSourceFileInfo::getBuffer(int s) { |
|
349 if(!spare_buffer || spare_buffer_size < s) |
|
350 spare_buffer = (char *)realloc(spare_buffer, spare_buffer_size=s); |
|
351 return spare_buffer; |
|
352 } |
|
353 |
|
354 #ifndef S_ISDIR |
|
355 #define S_ISDIR(x) (x & _S_IFDIR) |
|
356 #endif |
|
357 |
|
358 QMakeLocalFileName QMakeSourceFileInfo::fixPathForFile(const QMakeLocalFileName &f, bool) |
|
359 { |
|
360 return f; |
|
361 } |
|
362 |
|
363 QMakeLocalFileName QMakeSourceFileInfo::findFileForDep(const QMakeLocalFileName &/*dep*/, |
|
364 const QMakeLocalFileName &/*file*/) |
|
365 { |
|
366 return QMakeLocalFileName(); |
|
367 } |
|
368 |
|
369 QFileInfo QMakeSourceFileInfo::findFileInfo(const QMakeLocalFileName &dep) |
|
370 { |
|
371 return QFileInfo(dep.real()); |
|
372 } |
|
373 |
|
374 bool QMakeSourceFileInfo::findDeps(SourceFile *file) |
|
375 { |
|
376 if(file->dep_checked || file->type == TYPE_UNKNOWN) |
|
377 return true; |
|
378 files_changed = true; |
|
379 file->dep_checked = true; |
|
380 |
|
381 const QMakeLocalFileName sourceFile = fixPathForFile(file->file, true); |
|
382 |
|
383 struct stat fst; |
|
384 char *buffer = 0; |
|
385 int buffer_len = 0; |
|
386 { |
|
387 int fd; |
|
388 #if defined(_MSC_VER) && _MSC_VER >= 1400 |
|
389 if (_sopen_s(&fd, sourceFile.local().toLatin1().constData(), |
|
390 _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0) |
|
391 fd = -1; |
|
392 #else |
|
393 fd = open(sourceFile.local().toLatin1().constData(), O_RDONLY); |
|
394 #endif |
|
395 if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode)) |
|
396 return false; |
|
397 buffer = getBuffer(fst.st_size); |
|
398 for(int have_read = 0; |
|
399 (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len)); |
|
400 buffer_len += have_read); |
|
401 QT_CLOSE(fd); |
|
402 } |
|
403 if(!buffer) |
|
404 return false; |
|
405 if(!file->deps) |
|
406 file->deps = new SourceDependChildren; |
|
407 |
|
408 int line_count = 1; |
|
409 |
|
410 for(int x = 0; x < buffer_len; ++x) { |
|
411 bool try_local = true; |
|
412 char *inc = 0; |
|
413 if(file->type == QMakeSourceFileInfo::TYPE_UI) { |
|
414 // skip whitespaces |
|
415 while(x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t')) |
|
416 ++x; |
|
417 if(*(buffer + x) == '<') { |
|
418 ++x; |
|
419 if(buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) && |
|
420 (*(buffer + x + 11) == ' ' || *(buffer + x + 11) == '>')) { |
|
421 for(x += 11; *(buffer + x) != '>'; ++x); |
|
422 int inc_len = 0; |
|
423 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len); |
|
424 *(buffer + x + inc_len) = '\0'; |
|
425 inc = buffer + x; |
|
426 } else if(buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) && |
|
427 (*(buffer + x + 12) == ' ' || *(buffer + x + 12) == '>')) { |
|
428 for(x += 13; *(buffer + x) != '>'; ++x); //skip up to > |
|
429 while(x < buffer_len) { |
|
430 for(x++; *(buffer + x) != '<'; ++x); //skip up to < |
|
431 x++; |
|
432 if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) && |
|
433 (*(buffer + x + 6) == ' ' || *(buffer + x + 6) == '>')) { |
|
434 for(x += 7; *(buffer + x) != '>'; ++x); //skip up to > |
|
435 int inc_len = 0; |
|
436 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len); |
|
437 *(buffer + x + inc_len) = '\0'; |
|
438 inc = buffer + x; |
|
439 break; |
|
440 } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) && |
|
441 (*(buffer + x + 13) == ' ' || *(buffer + x + 13) == '>')) { |
|
442 x += 14; |
|
443 break; |
|
444 } |
|
445 } |
|
446 } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) && |
|
447 (*(buffer + x + 7) == ' ' || *(buffer + x + 7) == '>')) { |
|
448 for(x += 8; *(buffer + x) != '>'; ++x) { |
|
449 if(buffer_len >= x + 9 && *(buffer + x) == 'i' && |
|
450 !strncmp(buffer + x, "impldecl", 8)) { |
|
451 for(x += 8; *(buffer + x) != '='; ++x); |
|
452 if(*(buffer + x) != '=') |
|
453 continue; |
|
454 for(++x; *(buffer+x) == '\t' || *(buffer+x) == ' '; ++x); |
|
455 char quote = 0; |
|
456 if(*(buffer+x) == '\'' || *(buffer+x) == '"') { |
|
457 quote = *(buffer + x); |
|
458 ++x; |
|
459 } |
|
460 int val_len; |
|
461 for(val_len = 0; true; ++val_len) { |
|
462 if(quote) { |
|
463 if(*(buffer+x+val_len) == quote) |
|
464 break; |
|
465 } else if(*(buffer + x + val_len) == '>' || |
|
466 *(buffer + x + val_len) == ' ') { |
|
467 break; |
|
468 } |
|
469 } |
|
470 //? char saved = *(buffer + x + val_len); |
|
471 *(buffer + x + val_len) = '\0'; |
|
472 if(!strcmp(buffer+x, "in implementation")) { |
|
473 //### do this |
|
474 } |
|
475 } |
|
476 } |
|
477 int inc_len = 0; |
|
478 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len); |
|
479 *(buffer + x + inc_len) = '\0'; |
|
480 inc = buffer + x; |
|
481 } |
|
482 } |
|
483 //read past new line now.. |
|
484 for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x); |
|
485 ++line_count; |
|
486 } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) { |
|
487 } else if(file->type == QMakeSourceFileInfo::TYPE_C) { |
|
488 for(int beginning=1; x < buffer_len; ++x) { |
|
489 // whitespace comments and line-endings |
|
490 for(; x < buffer_len; ++x) { |
|
491 if(*(buffer+x) == ' ' || *(buffer+x) == '\t') { |
|
492 // keep going |
|
493 } else if(*(buffer+x) == '/') { |
|
494 ++x; |
|
495 if(buffer_len >= x) { |
|
496 if(*(buffer+x) == '/') { //c++ style comment |
|
497 for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x); |
|
498 beginning = 1; |
|
499 } else if(*(buffer+x) == '*') { //c style comment |
|
500 for(++x; x < buffer_len; ++x) { |
|
501 if(*(buffer+x) == '*') { |
|
502 if(x < buffer_len-1 && *(buffer + (x+1)) == '/') { |
|
503 ++x; |
|
504 break; |
|
505 } |
|
506 } else if(qmake_endOfLine(*(buffer+x))) { |
|
507 ++line_count; |
|
508 } |
|
509 } |
|
510 } |
|
511 } |
|
512 } else if(qmake_endOfLine(*(buffer+x))) { |
|
513 ++line_count; |
|
514 beginning = 1; |
|
515 } else { |
|
516 break; |
|
517 } |
|
518 } |
|
519 |
|
520 if(x >= buffer_len) |
|
521 break; |
|
522 |
|
523 // preprocessor directive |
|
524 if(beginning && *(buffer+x) == '#') |
|
525 break; |
|
526 |
|
527 // quoted strings |
|
528 if(*(buffer+x) == '\'' || *(buffer+x) == '"') { |
|
529 const char term = *(buffer+(x++)); |
|
530 for(; x < buffer_len; ++x) { |
|
531 if(*(buffer+x) == term) { |
|
532 ++x; |
|
533 break; |
|
534 } else if(*(buffer+x) == '\\') { |
|
535 ++x; |
|
536 } else if(qmake_endOfLine(*(buffer+x))) { |
|
537 ++line_count; |
|
538 } |
|
539 } |
|
540 } |
|
541 beginning = 0; |
|
542 } |
|
543 if(x >= buffer_len) |
|
544 break; |
|
545 |
|
546 //got a preprocessor symbol |
|
547 ++x; |
|
548 while(x < buffer_len) { |
|
549 if(*(buffer+x) != ' ' && *(buffer+x) != '\t') |
|
550 break; |
|
551 ++x; |
|
552 } |
|
553 |
|
554 int keyword_len = 0; |
|
555 const char *keyword = buffer+x; |
|
556 while(x+keyword_len < buffer_len) { |
|
557 if(((*(buffer+x+keyword_len) < 'a' || *(buffer+x+keyword_len) > 'z')) && |
|
558 *(buffer+x+keyword_len) != '_') { |
|
559 for(x+=keyword_len; //skip spaces after keyword |
|
560 x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t'); |
|
561 x++); |
|
562 break; |
|
563 } else if(qmake_endOfLine(*(buffer+x+keyword_len))) { |
|
564 x += keyword_len-1; |
|
565 keyword_len = 0; |
|
566 break; |
|
567 } |
|
568 keyword_len++; |
|
569 } |
|
570 |
|
571 if(keyword_len == 7 && !strncmp(keyword, "include", keyword_len)) { |
|
572 char term = *(buffer + x); |
|
573 if(term == '<') { |
|
574 try_local = false; |
|
575 term = '>'; |
|
576 } else if(term != '"') { //wtf? |
|
577 continue; |
|
578 } |
|
579 x++; |
|
580 |
|
581 int inc_len; |
|
582 for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len); |
|
583 *(buffer + x + inc_len) = '\0'; |
|
584 inc = buffer + x; |
|
585 x += inc_len; |
|
586 } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) { |
|
587 char term = 0; |
|
588 if(*(buffer + x) == '"') |
|
589 term = '"'; |
|
590 if(*(buffer + x) == '\'') |
|
591 term = '\''; |
|
592 if(term) |
|
593 x++; |
|
594 |
|
595 int msg_len; |
|
596 for(msg_len = 0; (term && *(buffer + x + msg_len) != term) && |
|
597 !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len); |
|
598 *(buffer + x + msg_len) = '\0'; |
|
599 debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x); |
|
600 x += msg_len; |
|
601 } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') { |
|
602 const char term = *(buffer+(x++)); |
|
603 while(x < buffer_len) { |
|
604 if(*(buffer+x) == term) |
|
605 break; |
|
606 if(*(buffer+x) == '\\') { |
|
607 x+=2; |
|
608 } else { |
|
609 if(qmake_endOfLine(*(buffer+x))) |
|
610 ++line_count; |
|
611 ++x; |
|
612 } |
|
613 } |
|
614 } else { |
|
615 --x; |
|
616 } |
|
617 } |
|
618 |
|
619 if(inc) { |
|
620 if(!includes) |
|
621 includes = new SourceFiles; |
|
622 SourceFile *dep = includes->lookupFile(inc); |
|
623 if(!dep) { |
|
624 bool exists = false; |
|
625 QMakeLocalFileName lfn(inc); |
|
626 if(QDir::isRelativePath(lfn.real())) { |
|
627 if(try_local) { |
|
628 QDir sourceDir = findFileInfo(sourceFile).dir(); |
|
629 QMakeLocalFileName f(sourceDir.absoluteFilePath(lfn.local())); |
|
630 if(findFileInfo(f).exists()) { |
|
631 lfn = fixPathForFile(f); |
|
632 exists = true; |
|
633 } |
|
634 } |
|
635 if(!exists) { //path lookup |
|
636 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin(); it != depdirs.end(); ++it) { |
|
637 QMakeLocalFileName f((*it).real() + Option::dir_sep + lfn.real()); |
|
638 QFileInfo fi(findFileInfo(f)); |
|
639 if(fi.exists() && !fi.isDir()) { |
|
640 lfn = fixPathForFile(f); |
|
641 exists = true; |
|
642 break; |
|
643 } |
|
644 } |
|
645 } |
|
646 if(!exists) { //heuristic lookup |
|
647 lfn = findFileForDep(QMakeLocalFileName(inc), file->file); |
|
648 if((exists = !lfn.isNull())) |
|
649 lfn = fixPathForFile(lfn); |
|
650 } |
|
651 } else { |
|
652 exists = QFile::exists(lfn.real()); |
|
653 } |
|
654 if(!lfn.isNull()) { |
|
655 dep = files->lookupFile(lfn); |
|
656 if(!dep) { |
|
657 dep = new SourceFile; |
|
658 dep->file = lfn; |
|
659 dep->type = QMakeSourceFileInfo::TYPE_C; |
|
660 files->addFile(dep); |
|
661 includes->addFile(dep, inc, false); |
|
662 } |
|
663 dep->exists = exists; |
|
664 } |
|
665 } |
|
666 if(dep && dep->file != file->file) { |
|
667 dep->included_count++; |
|
668 if(dep->exists) { |
|
669 debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(), |
|
670 line_count, dep->file.local().toLatin1().constData()); |
|
671 file->deps->addChild(dep); |
|
672 } |
|
673 } |
|
674 } |
|
675 } |
|
676 if(dependencyMode() == Recursive) { //done last because buffer is shared |
|
677 for(int i = 0; i < file->deps->used_nodes; i++) { |
|
678 if(!file->deps->children[i]->deps) |
|
679 findDeps(file->deps->children[i]); |
|
680 } |
|
681 } |
|
682 return true; |
|
683 } |
|
684 |
|
685 bool QMakeSourceFileInfo::findMocs(SourceFile *file) |
|
686 { |
|
687 if(file->moc_checked) |
|
688 return true; |
|
689 files_changed = true; |
|
690 file->moc_checked = true; |
|
691 |
|
692 int buffer_len; |
|
693 char *buffer = 0; |
|
694 { |
|
695 struct stat fst; |
|
696 int fd; |
|
697 #if defined(_MSC_VER) && _MSC_VER >= 1400 |
|
698 if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(), |
|
699 _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0) |
|
700 fd = -1; |
|
701 #else |
|
702 fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY); |
|
703 #endif |
|
704 if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode)) |
|
705 return false; //shouldn't happen |
|
706 buffer = getBuffer(fst.st_size); |
|
707 for(int have_read = buffer_len = 0; |
|
708 (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len)); |
|
709 buffer_len += have_read); |
|
710 QT_CLOSE(fd); |
|
711 } |
|
712 |
|
713 debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData()); |
|
714 int line_count = 1; |
|
715 bool ignore_qobject = false, ignore_qgadget = false; |
|
716 /* qmake ignore Q_GADGET */ |
|
717 /* qmake ignore Q_OBJECT */ |
|
718 for(int x = 0; x < buffer_len; x++) { |
|
719 if(*(buffer + x) == '/') { |
|
720 ++x; |
|
721 if(buffer_len >= x) { |
|
722 if(*(buffer + x) == '/') { //c++ style comment |
|
723 for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x); |
|
724 } else if(*(buffer + x) == '*') { //c style comment |
|
725 for(++x; x < buffer_len; ++x) { |
|
726 if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore |
|
727 if(buffer_len >= (x + 20) && |
|
728 !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) { |
|
729 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"", |
|
730 file->file.real().toLatin1().constData(), line_count); |
|
731 x += 20; |
|
732 ignore_qobject = true; |
|
733 } else if(buffer_len >= (x + 20) && |
|
734 !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) { |
|
735 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"", |
|
736 file->file.real().toLatin1().constData(), line_count); |
|
737 x += 20; |
|
738 ignore_qgadget = true; |
|
739 } |
|
740 } else if(*(buffer + x) == '*') { |
|
741 if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') { |
|
742 ++x; |
|
743 break; |
|
744 } |
|
745 } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) { |
|
746 ++line_count; |
|
747 } |
|
748 } |
|
749 } |
|
750 } |
|
751 } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') { |
|
752 const char term = *(buffer+(x++)); |
|
753 while(x < buffer_len) { |
|
754 if(*(buffer+x) == term) |
|
755 break; |
|
756 if(*(buffer+x) == '\\') { |
|
757 x+=2; |
|
758 } else { |
|
759 if(qmake_endOfLine(*(buffer+x))) |
|
760 ++line_count; |
|
761 ++x; |
|
762 } |
|
763 } |
|
764 } |
|
765 if(Option::debug_level && qmake_endOfLine(*(buffer+x))) |
|
766 ++line_count; |
|
767 if(((buffer_len > x+2 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_') |
|
768 || |
|
769 (buffer_len > x+4 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O' |
|
770 && *(buffer+x+3) == 'M' && *(buffer+x+4) == '_')) |
|
771 && |
|
772 *(buffer + x) != '_' && |
|
773 (*(buffer + x) < 'a' || *(buffer + x) > 'z') && |
|
774 (*(buffer + x) < 'A' || *(buffer + x) > 'Z') && |
|
775 (*(buffer + x) < '0' || *(buffer + x) > '9')) { |
|
776 ++x; |
|
777 int match = 0; |
|
778 static const char *interesting[] = { "OBJECT", "GADGET", |
|
779 "M_OBJECT" }; |
|
780 for(int interest = 0, m1, m2; interest < 3; ++interest) { |
|
781 if(interest == 0 && ignore_qobject) |
|
782 continue; |
|
783 else if(interest == 1 && ignore_qgadget) |
|
784 continue; |
|
785 for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) { |
|
786 if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) { |
|
787 m2 = -1; |
|
788 break; |
|
789 } |
|
790 ++m2; |
|
791 } |
|
792 if(m1 == m2) { |
|
793 match = m2 + 2; |
|
794 break; |
|
795 } |
|
796 } |
|
797 if(match && *(buffer+x+match) != '_' && |
|
798 (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') && |
|
799 (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') && |
|
800 (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) { |
|
801 if(Option::debug_level) { |
|
802 *(buffer+x+match) = '\0'; |
|
803 debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(), |
|
804 line_count, buffer+x); |
|
805 } |
|
806 file->mocable = true; |
|
807 return true; |
|
808 } |
|
809 } |
|
810 } |
|
811 return true; |
|
812 } |
|
813 |
|
814 |
|
815 void QMakeSourceFileInfo::saveCache(const QString &cf) |
|
816 { |
|
817 #ifdef QMAKE_USE_CACHE |
|
818 if(cf.isEmpty()) |
|
819 return; |
|
820 |
|
821 QFile file(QMakeLocalFileName(cf).local()); |
|
822 if(file.open(QIODevice::WriteOnly)) { |
|
823 QTextStream stream(&file); |
|
824 stream << qmake_version() << endl << endl; //version |
|
825 { //cache verification |
|
826 QMap<QString, QStringList> verify = getCacheVerification(); |
|
827 stream << verify.count() << endl; |
|
828 for(QMap<QString, QStringList>::iterator it = verify.begin(); |
|
829 it != verify.end(); ++it) { |
|
830 stream << it.key() << endl << it.value().join(";") << endl; |
|
831 } |
|
832 stream << endl; |
|
833 } |
|
834 if(files->nodes) { |
|
835 for(int file = 0; file < files->num_nodes; ++file) { |
|
836 for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) { |
|
837 stream << node->file->file.local() << endl; //source |
|
838 stream << node->file->type << endl; //type |
|
839 |
|
840 //depends |
|
841 stream << ";"; |
|
842 if(node->file->deps) { |
|
843 for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) { |
|
844 if(depend) |
|
845 stream << ";"; |
|
846 stream << node->file->deps->children[depend]->file.local(); |
|
847 } |
|
848 } |
|
849 stream << endl; |
|
850 |
|
851 stream << node->file->mocable << endl; //mocable |
|
852 stream << endl; //just for human readability |
|
853 } |
|
854 } |
|
855 } |
|
856 stream.flush(); |
|
857 file.close(); |
|
858 } |
|
859 #else |
|
860 Q_UNUSED(cf); |
|
861 #endif |
|
862 } |
|
863 |
|
864 void QMakeSourceFileInfo::loadCache(const QString &cf) |
|
865 { |
|
866 if(cf.isEmpty()) |
|
867 return; |
|
868 |
|
869 #ifdef QMAKE_USE_CACHE |
|
870 QMakeLocalFileName cache_file(cf); |
|
871 int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY); |
|
872 if(fd == -1) |
|
873 return; |
|
874 QFileInfo cache_fi = findFileInfo(cache_file); |
|
875 if(!cache_fi.exists() || cache_fi.isDir()) |
|
876 return; |
|
877 |
|
878 QFile file; |
|
879 if(!file.open(QIODevice::ReadOnly, fd)) |
|
880 return; |
|
881 QTextStream stream(&file); |
|
882 |
|
883 if(stream.readLine() == qmake_version()) { //version check |
|
884 stream.skipWhiteSpace(); |
|
885 |
|
886 bool verified = true; |
|
887 { //cache verification |
|
888 QMap<QString, QStringList> verify; |
|
889 int len = stream.readLine().toInt(); |
|
890 for(int i = 0; i < len; ++i) { |
|
891 QString var = stream.readLine(); |
|
892 QString val = stream.readLine(); |
|
893 verify.insert(var, val.split(';', QString::SkipEmptyParts)); |
|
894 } |
|
895 verified = verifyCache(verify); |
|
896 } |
|
897 if(verified) { |
|
898 stream.skipWhiteSpace(); |
|
899 if(!files) |
|
900 files = new SourceFiles; |
|
901 while(!stream.atEnd()) { |
|
902 QString source = stream.readLine(); |
|
903 QString type = stream.readLine(); |
|
904 QString depends = stream.readLine(); |
|
905 QString mocable = stream.readLine(); |
|
906 stream.skipWhiteSpace(); |
|
907 |
|
908 QMakeLocalFileName fn(source); |
|
909 QFileInfo fi = findFileInfo(fn); |
|
910 |
|
911 SourceFile *file = files->lookupFile(fn); |
|
912 if(!file) { |
|
913 file = new SourceFile; |
|
914 file->file = fn; |
|
915 files->addFile(file); |
|
916 file->type = (SourceFileType)type.toInt(); |
|
917 file->exists = fi.exists(); |
|
918 } |
|
919 if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) { |
|
920 if(!file->dep_checked) { //get depends |
|
921 if(!file->deps) |
|
922 file->deps = new SourceDependChildren; |
|
923 file->dep_checked = true; |
|
924 QStringList depend_list = depends.split(";", QString::SkipEmptyParts); |
|
925 for(int depend = 0; depend < depend_list.size(); ++depend) { |
|
926 QMakeLocalFileName dep_fn(depend_list.at(depend)); |
|
927 QFileInfo dep_fi(findFileInfo(dep_fn)); |
|
928 SourceFile *dep = files->lookupFile(dep_fn); |
|
929 if(!dep) { |
|
930 dep = new SourceFile; |
|
931 dep->file = dep_fn; |
|
932 dep->exists = dep_fi.exists(); |
|
933 dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN; |
|
934 files->addFile(dep); |
|
935 } |
|
936 dep->included_count++; |
|
937 file->deps->addChild(dep); |
|
938 } |
|
939 } |
|
940 if(!file->moc_checked) { //get mocs |
|
941 file->moc_checked = true; |
|
942 file->mocable = mocable.toInt(); |
|
943 } |
|
944 } |
|
945 } |
|
946 } |
|
947 } |
|
948 #endif |
|
949 } |
|
950 |
|
951 QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification() |
|
952 { |
|
953 return QMap<QString, QStringList>(); |
|
954 } |
|
955 |
|
956 bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v) |
|
957 { |
|
958 return v == getCacheVerification(); |
|
959 } |
|
960 |
|
961 QT_END_NAMESPACE |