Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / qmake / generators / makefiledeps.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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+1 < buffer_len && *(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