source: trunk/qmake/generators/makefiledeps.cpp@ 362

Last change on this file since 362 was 362, checked in by Dmitry A. Kuminov, 16 years ago

qmake: Don't assume that relative paths in all OSes look like in *nix [vendor bug].

File size: 34.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the qmake application of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
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
65QT_BEGIN_NAMESPACE
66
67#if 1
68#define qmake_endOfLine(c) (c == '\r' || c == '\n')
69#else
70inline bool qmake_endOfLine(const char &c) { return (c == '\r' || c == '\n'); }
71#endif
72
73//#define QMAKE_USE_CACHE
74
75QMakeLocalFileName::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}
84const 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
92struct SourceDependChildren;
93struct 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};
105struct 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};
118SourceFile::~SourceFile() { delete deps; }
119class SourceFiles {
120 int hash(const char *);
121public:
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};
144SourceFiles::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
151SourceFiles::~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
163int 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
176SourceFile *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
186void 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
200void 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
212void 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
223QStringList 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
248int
249QMakeSourceFileInfo::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
259bool QMakeSourceFileInfo::mocable(const QString &file)
260{
261 if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file)))
262 return node->mocable;
263 return false;
264}
265
266QMakeSourceFileInfo::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
285QMakeSourceFileInfo::~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
303void QMakeSourceFileInfo::setCacheFile(const QString &cf)
304{
305 cachefile = cf;
306 loadCache(cachefile);
307}
308
309void 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}
315void 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
341bool 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
348char *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
358QMakeLocalFileName QMakeSourceFileInfo::fixPathForFile(const QMakeLocalFileName &f, bool)
359{
360 return f;
361}
362
363QMakeLocalFileName QMakeSourceFileInfo::findFileForDep(const QMakeLocalFileName &/*dep*/,
364 const QMakeLocalFileName &/*file*/)
365{
366 return QMakeLocalFileName();
367}
368
369QFileInfo QMakeSourceFileInfo::findFileInfo(const QMakeLocalFileName &dep)
370{
371 return QFileInfo(dep.real());
372}
373
374bool 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 struct stat fst;
382 char *buffer = 0;
383 int buffer_len = 0;
384 {
385 int fd;
386#if defined(_MSC_VER) && _MSC_VER >= 1400
387 if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLatin1().constData(),
388 _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0)
389 fd = -1;
390#else
391 fd = open(fixPathForFile(file->file, true).local().toLatin1().constData(), O_RDONLY);
392#endif
393 if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
394 return false;
395 buffer = getBuffer(fst.st_size);
396 for(int have_read = 0;
397 (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
398 buffer_len += have_read);
399 QT_CLOSE(fd);
400 }
401 if(!buffer)
402 return false;
403 if(!file->deps)
404 file->deps = new SourceDependChildren;
405
406 int line_count = 1;
407
408 for(int x = 0; x < buffer_len; ++x) {
409 bool try_local = true;
410 char *inc = 0;
411 if(file->type == QMakeSourceFileInfo::TYPE_UI) {
412 // skip whitespaces
413 while(x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t'))
414 ++x;
415 if(*(buffer + x) == '<') {
416 ++x;
417 if(buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) &&
418 (*(buffer + x + 11) == ' ' || *(buffer + x + 11) == '>')) {
419 for(x += 11; *(buffer + x) != '>'; ++x);
420 int inc_len = 0;
421 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len);
422 *(buffer + x + inc_len) = '\0';
423 inc = buffer + x;
424 } else if(buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) &&
425 (*(buffer + x + 12) == ' ' || *(buffer + x + 12) == '>')) {
426 for(x += 13; *(buffer + x) != '>'; ++x); //skip up to >
427 while(x < buffer_len) {
428 for(x++; *(buffer + x) != '<'; ++x); //skip up to <
429 x++;
430 if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) &&
431 (*(buffer + x + 6) == ' ' || *(buffer + x + 6) == '>')) {
432 for(x += 7; *(buffer + x) != '>'; ++x); //skip up to >
433 int inc_len = 0;
434 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len);
435 *(buffer + x + inc_len) = '\0';
436 inc = buffer + x;
437 break;
438 } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) &&
439 (*(buffer + x + 13) == ' ' || *(buffer + x + 13) == '>')) {
440 x += 14;
441 break;
442 }
443 }
444 } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) &&
445 (*(buffer + x + 7) == ' ' || *(buffer + x + 7) == '>')) {
446 for(x += 8; *(buffer + x) != '>'; ++x) {
447 if(buffer_len >= x + 9 && *(buffer + x) == 'i' &&
448 !strncmp(buffer + x, "impldecl", 8)) {
449 for(x += 8; *(buffer + x) != '='; ++x);
450 if(*(buffer + x) != '=')
451 continue;
452 for(++x; *(buffer+x) == '\t' || *(buffer+x) == ' '; ++x);
453 char quote = 0;
454 if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
455 quote = *(buffer + x);
456 ++x;
457 }
458 int val_len;
459 for(val_len = 0; true; ++val_len) {
460 if(quote) {
461 if(*(buffer+x+val_len) == quote)
462 break;
463 } else if(*(buffer + x + val_len) == '>' ||
464 *(buffer + x + val_len) == ' ') {
465 break;
466 }
467 }
468//? char saved = *(buffer + x + val_len);
469 *(buffer + x + val_len) = '\0';
470 if(!strcmp(buffer+x, "in implementation")) {
471 //### do this
472 }
473 }
474 }
475 int inc_len = 0;
476 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len);
477 *(buffer + x + inc_len) = '\0';
478 inc = buffer + x;
479 }
480 }
481 //read past new line now..
482 for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
483 ++line_count;
484 } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) {
485 } else if(file->type == QMakeSourceFileInfo::TYPE_C) {
486 for(int beginning=1; x < buffer_len; ++x) {
487 // whitespace comments and line-endings
488 for(; x < buffer_len; ++x) {
489 if(*(buffer+x) == ' ' || *(buffer+x) == '\t') {
490 // keep going
491 } else if(*(buffer+x) == '/') {
492 ++x;
493 if(buffer_len >= x) {
494 if(*(buffer+x) == '/') { //c++ style comment
495 for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
496 beginning = 1;
497 } else if(*(buffer+x) == '*') { //c style comment
498 for(++x; x < buffer_len; ++x) {
499 if(*(buffer+x) == '*') {
500 if(x < buffer_len-1 && *(buffer + (x+1)) == '/') {
501 ++x;
502 break;
503 }
504 } else if(qmake_endOfLine(*(buffer+x))) {
505 ++line_count;
506 }
507 }
508 }
509 }
510 } else if(qmake_endOfLine(*(buffer+x))) {
511 ++line_count;
512 beginning = 1;
513 } else {
514 break;
515 }
516 }
517
518 if(x >= buffer_len)
519 break;
520
521 // preprocessor directive
522 if(beginning && *(buffer+x) == '#')
523 break;
524
525 // quoted strings
526 if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
527 const char term = *(buffer+(x++));
528 for(; x < buffer_len; ++x) {
529 if(*(buffer+x) == term) {
530 ++x;
531 break;
532 } else if(*(buffer+x) == '\\') {
533 ++x;
534 } else if(qmake_endOfLine(*(buffer+x))) {
535 ++line_count;
536 }
537 }
538 }
539 beginning = 0;
540 }
541 if(x >= buffer_len)
542 break;
543
544 //got a preprocessor symbol
545 ++x;
546 while(x < buffer_len) {
547 if(*(buffer+x) != ' ' && *(buffer+x) != '\t')
548 break;
549 ++x;
550 }
551
552 int keyword_len = 0;
553 const char *keyword = buffer+x;
554 while(x+keyword_len < buffer_len) {
555 if(((*(buffer+x+keyword_len) < 'a' || *(buffer+x+keyword_len) > 'z')) &&
556 *(buffer+x+keyword_len) != '_') {
557 for(x+=keyword_len; //skip spaces after keyword
558 x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t');
559 x++);
560 break;
561 } else if(qmake_endOfLine(*(buffer+x+keyword_len))) {
562 x += keyword_len-1;
563 keyword_len = 0;
564 break;
565 }
566 keyword_len++;
567 }
568
569 if(keyword_len == 7 && !strncmp(keyword, "include", keyword_len)) {
570 char term = *(buffer + x);
571 if(term == '<') {
572 try_local = false;
573 term = '>';
574 } else if(term != '"') { //wtf?
575 continue;
576 }
577 x++;
578
579 int inc_len;
580 for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len);
581 *(buffer + x + inc_len) = '\0';
582 inc = buffer + x;
583 x += inc_len;
584 } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) {
585 char term = 0;
586 if(*(buffer + x) == '"')
587 term = '"';
588 if(*(buffer + x) == '\'')
589 term = '\'';
590 if(term)
591 x++;
592
593 int msg_len;
594 for(msg_len = 0; (term && *(buffer + x + msg_len) != term) &&
595 !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len);
596 *(buffer + x + msg_len) = '\0';
597 debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x);
598 x += msg_len;
599 } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
600 const char term = *(buffer+(x++));
601 while(x < buffer_len) {
602 if(*(buffer+x) == term)
603 break;
604 if(*(buffer+x) == '\\') {
605 x+=2;
606 } else {
607 if(qmake_endOfLine(*(buffer+x)))
608 ++line_count;
609 ++x;
610 }
611 }
612 } else {
613 --x;
614 }
615 }
616
617 if(inc) {
618 if(!includes)
619 includes = new SourceFiles;
620 SourceFile *dep = includes->lookupFile(inc);
621 if(!dep) {
622 bool exists = false;
623 QMakeLocalFileName lfn(inc);
624 if(QDir::isRelativePath(lfn.real())) {
625 if(try_local) {
626 QString dir = findFileInfo(file->file).path();
627 QMakeLocalFileName f(QDir(dir).absoluteFilePath(lfn.local()));
628 if(findFileInfo(f).exists()) {
629 lfn = fixPathForFile(f);
630 exists = true;
631 }
632 }
633 if(!exists) { //path lookup
634 foreach(const QMakeLocalFileName &dd, depdirs) {
635 QMakeLocalFileName f(QDir(dd.real()).absoluteFilePath(lfn.real()));
636 QFileInfo fi(findFileInfo(f));
637 if(fi.exists() && !fi.isDir()) {
638 lfn = fixPathForFile(f);
639 exists = true;
640 break;
641 }
642 }
643 }
644 if(!exists) { //heuristic lookup
645 lfn = findFileForDep(QMakeLocalFileName(inc), file->file);
646 if((exists = !lfn.isNull()))
647 lfn = fixPathForFile(lfn);
648 }
649 } else {
650 exists = QFile::exists(lfn.real());
651 }
652 if(!lfn.isNull()) {
653 dep = files->lookupFile(lfn);
654 if(!dep) {
655 dep = new SourceFile;
656 dep->file = lfn;
657 dep->type = QMakeSourceFileInfo::TYPE_C;
658 files->addFile(dep);
659 includes->addFile(dep, inc, false);
660 }
661 dep->exists = exists;
662 }
663 }
664 if(dep && dep->file != file->file) {
665 dep->included_count++;
666 if(dep->exists) {
667 debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(),
668 line_count, dep->file.local().toLatin1().constData());
669 file->deps->addChild(dep);
670 }
671 }
672 }
673 }
674 if(dependencyMode() == Recursive) { //done last because buffer is shared
675 for(int i = 0; i < file->deps->used_nodes; i++) {
676 if(!file->deps->children[i]->deps)
677 findDeps(file->deps->children[i]);
678 }
679 }
680 return true;
681}
682
683bool QMakeSourceFileInfo::findMocs(SourceFile *file)
684{
685 if(file->moc_checked)
686 return true;
687 files_changed = true;
688 file->moc_checked = true;
689
690 int buffer_len;
691 char *buffer = 0;
692 {
693 struct stat fst;
694 int fd;
695#if defined(_MSC_VER) && _MSC_VER >= 1400
696 if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(),
697 _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0)
698 fd = -1;
699#else
700 fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY);
701#endif
702 if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
703 return false; //shouldn't happen
704 buffer = getBuffer(fst.st_size);
705 for(int have_read = buffer_len = 0;
706 (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
707 buffer_len += have_read);
708 QT_CLOSE(fd);
709 }
710
711 debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData());
712 int line_count = 1;
713 bool ignore_qobject = false, ignore_qgadget = false;
714 /* qmake ignore Q_GADGET */
715 /* qmake ignore Q_OBJECT */
716 for(int x = 0; x < buffer_len; x++) {
717 if(*(buffer + x) == '/') {
718 ++x;
719 if(buffer_len >= x) {
720 if(*(buffer + x) == '/') { //c++ style comment
721 for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
722 } else if(*(buffer + x) == '*') { //c style comment
723 for(++x; x < buffer_len; ++x) {
724 if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore
725 if(buffer_len >= (x + 20) &&
726 !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) {
727 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"",
728 file->file.real().toLatin1().constData(), line_count);
729 x += 20;
730 ignore_qobject = true;
731 } else if(buffer_len >= (x + 20) &&
732 !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) {
733 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"",
734 file->file.real().toLatin1().constData(), line_count);
735 x += 20;
736 ignore_qgadget = true;
737 }
738 } else if(*(buffer + x) == '*') {
739 if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') {
740 ++x;
741 break;
742 }
743 } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) {
744 ++line_count;
745 }
746 }
747 }
748 }
749 } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
750 const char term = *(buffer+(x++));
751 while(x < buffer_len) {
752 if(*(buffer+x) == term)
753 break;
754 if(*(buffer+x) == '\\') {
755 x+=2;
756 } else {
757 if(qmake_endOfLine(*(buffer+x)))
758 ++line_count;
759 ++x;
760 }
761 }
762 }
763 if(Option::debug_level && qmake_endOfLine(*(buffer+x)))
764 ++line_count;
765 if(((buffer_len > x+2 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_')
766 ||
767 (buffer_len > x+4 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O'
768 && *(buffer+x+3) == 'M' && *(buffer+x+4) == '_'))
769 &&
770 *(buffer + x) != '_' &&
771 (*(buffer + x) < 'a' || *(buffer + x) > 'z') &&
772 (*(buffer + x) < 'A' || *(buffer + x) > 'Z') &&
773 (*(buffer + x) < '0' || *(buffer + x) > '9')) {
774 ++x;
775 int match = 0;
776 static const char *interesting[] = { "OBJECT", "GADGET",
777 "M_OBJECT" };
778 for(int interest = 0, m1, m2; interest < 3; ++interest) {
779 if(interest == 0 && ignore_qobject)
780 continue;
781 else if(interest == 1 && ignore_qgadget)
782 continue;
783 for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) {
784 if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) {
785 m2 = -1;
786 break;
787 }
788 ++m2;
789 }
790 if(m1 == m2) {
791 match = m2 + 2;
792 break;
793 }
794 }
795 if(match && *(buffer+x+match) != '_' &&
796 (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') &&
797 (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') &&
798 (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) {
799 if(Option::debug_level) {
800 *(buffer+x+match) = '\0';
801 debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(),
802 line_count, buffer+x);
803 }
804 file->mocable = true;
805 return true;
806 }
807 }
808 }
809 return true;
810}
811
812
813void QMakeSourceFileInfo::saveCache(const QString &cf)
814{
815#ifdef QMAKE_USE_CACHE
816 if(cf.isEmpty())
817 return;
818
819 QFile file(QMakeLocalFileName(cf).local());
820 if(file.open(QIODevice::WriteOnly)) {
821 QTextStream stream(&file);
822 stream << qmake_version() << endl << endl; //version
823 { //cache verification
824 QMap<QString, QStringList> verify = getCacheVerification();
825 stream << verify.count() << endl;
826 for(QMap<QString, QStringList>::iterator it = verify.begin();
827 it != verify.end(); ++it) {
828 stream << it.key() << endl << it.value().join(";") << endl;
829 }
830 stream << endl;
831 }
832 if(files->nodes) {
833 for(int file = 0; file < files->num_nodes; ++file) {
834 for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) {
835 stream << node->file->file.local() << endl; //source
836 stream << node->file->type << endl; //type
837
838 //depends
839 stream << ";";
840 if(node->file->deps) {
841 for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) {
842 if(depend)
843 stream << ";";
844 stream << node->file->deps->children[depend]->file.local();
845 }
846 }
847 stream << endl;
848
849 stream << node->file->mocable << endl; //mocable
850 stream << endl; //just for human readability
851 }
852 }
853 }
854 stream.flush();
855 file.close();
856 }
857#else
858 Q_UNUSED(cf);
859#endif
860}
861
862void QMakeSourceFileInfo::loadCache(const QString &cf)
863{
864 if(cf.isEmpty())
865 return;
866
867#ifdef QMAKE_USE_CACHE
868 QMakeLocalFileName cache_file(cf);
869 int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY);
870 if(fd == -1)
871 return;
872 QFileInfo cache_fi = findFileInfo(cache_file);
873 if(!cache_fi.exists() || cache_fi.isDir())
874 return;
875
876 QFile file;
877 if(!file.open(QIODevice::ReadOnly, fd))
878 return;
879 QTextStream stream(&file);
880
881 if(stream.readLine() == qmake_version()) { //version check
882 stream.skipWhiteSpace();
883
884 bool verified = true;
885 { //cache verification
886 QMap<QString, QStringList> verify;
887 int len = stream.readLine().toInt();
888 for(int i = 0; i < len; ++i) {
889 QString var = stream.readLine();
890 QString val = stream.readLine();
891 verify.insert(var, val.split(';', QString::SkipEmptyParts));
892 }
893 verified = verifyCache(verify);
894 }
895 if(verified) {
896 stream.skipWhiteSpace();
897 if(!files)
898 files = new SourceFiles;
899 while(!stream.atEnd()) {
900 QString source = stream.readLine();
901 QString type = stream.readLine();
902 QString depends = stream.readLine();
903 QString mocable = stream.readLine();
904 stream.skipWhiteSpace();
905
906 QMakeLocalFileName fn(source);
907 QFileInfo fi = findFileInfo(fn);
908
909 SourceFile *file = files->lookupFile(fn);
910 if(!file) {
911 file = new SourceFile;
912 file->file = fn;
913 files->addFile(file);
914 file->type = (SourceFileType)type.toInt();
915 file->exists = fi.exists();
916 }
917 if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) {
918 if(!file->dep_checked) { //get depends
919 if(!file->deps)
920 file->deps = new SourceDependChildren;
921 file->dep_checked = true;
922 QStringList depend_list = depends.split(";", QString::SkipEmptyParts);
923 for(int depend = 0; depend < depend_list.size(); ++depend) {
924 QMakeLocalFileName dep_fn(depend_list.at(depend));
925 QFileInfo dep_fi(findFileInfo(dep_fn));
926 SourceFile *dep = files->lookupFile(dep_fn);
927 if(!dep) {
928 dep = new SourceFile;
929 dep->file = dep_fn;
930 dep->exists = dep_fi.exists();
931 dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN;
932 files->addFile(dep);
933 }
934 dep->included_count++;
935 file->deps->addChild(dep);
936 }
937 }
938 if(!file->moc_checked) { //get mocs
939 file->moc_checked = true;
940 file->mocable = mocable.toInt();
941 }
942 }
943 }
944 }
945 }
946#endif
947}
948
949QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification()
950{
951 return QMap<QString, QStringList>();
952}
953
954bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v)
955{
956 return v == getCacheVerification();
957}
958
959QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.