support the obj-c #import statement in the dependency generator
[qt:qt.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", 7)) // C & Obj-C
572                || (keyword_len == 6 && !strncmp(keyword, "import", 6))) { // Obj-C
573                 char term = *(buffer + x);
574                 if(term == '<') {
575                     try_local = false;
576                     term = '>';
577                 } else if(term != '"') { //wtf?
578                     continue;
579                 }
580                 x++;
581
582                 int inc_len;
583                 for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len) ;
584                 *(buffer + x + inc_len) = '\0';
585                 inc = buffer + x;
586                 x += inc_len;
587             } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) {
588                 char term = 0;
589                 if(*(buffer + x) == '"')
590                     term = '"';
591                 if(*(buffer + x) == '\'')
592                     term = '\'';
593                 if(term)
594                     x++;
595
596                 int msg_len;
597                 for(msg_len = 0; (term && *(buffer + x + msg_len) != term) &&
598                               !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len) ;
599                 *(buffer + x + msg_len) = '\0';
600                 debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x);
601                 x += msg_len;
602             } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
603                 const char term = *(buffer+(x++));
604                 while(x < buffer_len) {
605                     if(*(buffer+x) == term)
606                         break;
607                     if(*(buffer+x) == '\\') {
608                         x+=2;
609                     } else {
610                         if(qmake_endOfLine(*(buffer+x)))
611                             ++line_count;
612                         ++x;
613                     }
614                 }
615             } else {
616                 --x;
617             }
618         }
619
620         if(inc) {
621             if(!includes)
622                 includes = new SourceFiles;
623             SourceFile *dep = includes->lookupFile(inc);
624             if(!dep) {
625                 bool exists = false;
626                 QMakeLocalFileName lfn(inc);
627                 if(QDir::isRelativePath(lfn.real())) {
628                     if(try_local) {
629                         QDir sourceDir = findFileInfo(sourceFile).dir();
630                         QMakeLocalFileName f(sourceDir.absoluteFilePath(lfn.local()));
631                         if(findFileInfo(f).exists()) {
632                             lfn = fixPathForFile(f);
633                             exists = true;
634                         }
635                     }
636                     if(!exists) { //path lookup
637                         for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin(); it != depdirs.end(); ++it) {
638                             QMakeLocalFileName f((*it).real() + Option::dir_sep + lfn.real());
639                             QFileInfo fi(findFileInfo(f));
640                             if(fi.exists() && !fi.isDir()) {
641                                 lfn = fixPathForFile(f);
642                                 exists = true;
643                                 break;
644                             }
645                         }
646                     }
647                     if(!exists) { //heuristic lookup
648                         lfn = findFileForDep(QMakeLocalFileName(inc), file->file);
649                         if((exists = !lfn.isNull()))
650                             lfn = fixPathForFile(lfn);
651                     }
652                 } else {
653                     exists = QFile::exists(lfn.real());
654                 }
655                 if(!lfn.isNull()) {
656                     dep = files->lookupFile(lfn);
657                     if(!dep) {
658                         dep = new SourceFile;
659                         dep->file = lfn;
660                         dep->type = QMakeSourceFileInfo::TYPE_C;
661                         files->addFile(dep);
662                         includes->addFile(dep, inc, false);
663                     }
664                     dep->exists = exists;
665                 }
666             }
667             if(dep && dep->file != file->file) {
668                 dep->included_count++;
669                 if(dep->exists) {
670                     debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(),
671                               line_count, dep->file.local().toLatin1().constData());
672                     file->deps->addChild(dep);
673                 }
674             }
675         }
676     }
677     if(dependencyMode() == Recursive) { //done last because buffer is shared
678         for(int i = 0; i < file->deps->used_nodes; i++) {
679             if(!file->deps->children[i]->deps)
680                 findDeps(file->deps->children[i]);
681         }
682     }
683     return true;
684 }
685
686 bool QMakeSourceFileInfo::findMocs(SourceFile *file)
687 {
688     if(file->moc_checked)
689         return true;
690     files_changed = true;
691     file->moc_checked = true;
692
693     int buffer_len;
694     char *buffer = 0;
695     {
696         struct stat fst;
697         int fd;
698 #if defined(_MSC_VER) && _MSC_VER >= 1400
699         if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(),
700             _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0)
701             fd = -1;
702 #else
703         fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY);
704 #endif
705         if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
706             return false; //shouldn't happen
707         buffer = getBuffer(fst.st_size);
708         for(int have_read = buffer_len = 0;
709             (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
710             buffer_len += have_read) ;
711         QT_CLOSE(fd);
712     }
713
714     debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData());
715     int line_count = 1;
716     bool ignore_qobject = false, ignore_qgadget = false;
717  /* qmake ignore Q_GADGET */
718  /* qmake ignore Q_OBJECT */
719     for(int x = 0; x < buffer_len; x++) {
720         if(*(buffer + x) == '/') {
721             ++x;
722             if(buffer_len >= x) {
723                 if(*(buffer + x) == '/') { //c++ style comment
724                     for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ;
725                 } else if(*(buffer + x) == '*') { //c style comment
726                     for(++x; x < buffer_len; ++x) {
727                         if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore
728                             if(buffer_len >= (x + 20) &&
729                                !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) {
730                                 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"",
731                                           file->file.real().toLatin1().constData(), line_count);
732                                 x += 20;
733                                 ignore_qobject = true;
734                             } else if(buffer_len >= (x + 20) &&
735                                       !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) {
736                                 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"",
737                                           file->file.real().toLatin1().constData(), line_count);
738                                 x += 20;
739                                 ignore_qgadget = true;
740                             }
741                         } else if(*(buffer + x) == '*') {
742                             if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') {
743                                 ++x;
744                                 break;
745                             }
746                         } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) {
747                             ++line_count;
748                         }
749                     }
750                 }
751             }
752         } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
753             const char term = *(buffer+(x++));
754             while(x < buffer_len) {
755                 if(*(buffer+x) == term)
756                     break;
757                 if(*(buffer+x) == '\\') {
758                     x+=2;
759                 } else {
760                     if(qmake_endOfLine(*(buffer+x)))
761                         ++line_count;
762                     ++x;
763                 }
764             }
765         }
766         if(Option::debug_level && qmake_endOfLine(*(buffer+x)))
767             ++line_count;
768         if(((buffer_len > x+2 &&  *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_')
769                    ||
770             (buffer_len > x+4 &&  *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O'
771                               &&  *(buffer+x+3) == 'M' && *(buffer+x+4) == '_'))
772                    &&
773                   *(buffer + x) != '_' &&
774                   (*(buffer + x) < 'a' || *(buffer + x) > 'z') &&
775                   (*(buffer + x) < 'A' || *(buffer + x) > 'Z') &&
776                   (*(buffer + x) < '0' || *(buffer + x) > '9')) {
777             ++x;
778             int match = 0;
779             static const char *interesting[] = { "OBJECT", "GADGET",
780                                                  "M_OBJECT" };
781             for(int interest = 0, m1, m2; interest < 3; ++interest) {
782                 if(interest == 0 && ignore_qobject)
783                     continue;
784                 else if(interest == 1 && ignore_qgadget)
785                     continue;
786                 for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) {
787                     if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) {
788                         m2 = -1;
789                         break;
790                     }
791                     ++m2;
792                 }
793                 if(m1 == m2) {
794                     match = m2 + 2;
795                     break;
796                 }
797             }
798             if(match && *(buffer+x+match) != '_' &&
799                (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') &&
800                (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') &&
801                (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) {
802                 if(Option::debug_level) {
803                     *(buffer+x+match) = '\0';
804                     debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(),
805                               line_count, buffer+x);
806                 }
807                 file->mocable = true;
808                 return true;
809             }
810         }
811     }
812     return true;
813 }
814
815
816 void QMakeSourceFileInfo::saveCache(const QString &cf)
817 {
818 #ifdef QMAKE_USE_CACHE
819     if(cf.isEmpty())
820         return;
821
822     QFile file(QMakeLocalFileName(cf).local());
823     if(file.open(QIODevice::WriteOnly)) {
824         QTextStream stream(&file);
825         stream << qmake_version() << endl << endl; //version
826         { //cache verification
827             QMap<QString, QStringList> verify = getCacheVerification();
828              stream << verify.count() << endl;
829              for(QMap<QString, QStringList>::iterator it = verify.begin();
830                  it != verify.end(); ++it) {
831                  stream << it.key() << endl << it.value().join(";") << endl;
832              }
833              stream << endl;
834         }
835         if(files->nodes) {
836             for(int file = 0; file < files->num_nodes; ++file) {
837                 for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) {
838                     stream << node->file->file.local() << endl; //source
839                     stream << node->file->type << endl; //type
840
841                     //depends
842                     stream << ";";
843                     if(node->file->deps) {
844                         for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) {
845                             if(depend)
846                                 stream << ";";
847                             stream << node->file->deps->children[depend]->file.local();
848                         }
849                     }
850                     stream << endl;
851
852                     stream << node->file->mocable << endl; //mocable
853                     stream << endl; //just for human readability
854                 }
855             }
856         }
857         stream.flush();
858         file.close();
859     }
860 #else
861     Q_UNUSED(cf);
862 #endif
863 }
864
865 void QMakeSourceFileInfo::loadCache(const QString &cf)
866 {
867     if(cf.isEmpty())
868         return;
869
870 #ifdef QMAKE_USE_CACHE
871     QMakeLocalFileName cache_file(cf);
872     int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY);
873     if(fd == -1)
874         return;
875     QFileInfo cache_fi = findFileInfo(cache_file);
876     if(!cache_fi.exists() || cache_fi.isDir())
877         return;
878
879     QFile file;
880     if(!file.open(QIODevice::ReadOnly, fd))
881         return;
882     QTextStream stream(&file);
883
884     if(stream.readLine() == qmake_version()) { //version check
885         stream.skipWhiteSpace();
886
887         bool verified = true;
888         { //cache verification
889             QMap<QString, QStringList> verify;
890             int len = stream.readLine().toInt();
891             for(int i = 0; i < len; ++i) {
892                 QString var = stream.readLine();
893                 QString val = stream.readLine();
894                 verify.insert(var, val.split(';', QString::SkipEmptyParts));
895             }
896             verified = verifyCache(verify);
897         }
898         if(verified) {
899             stream.skipWhiteSpace();
900             if(!files)
901                 files = new SourceFiles;
902             while(!stream.atEnd()) {
903                 QString source = stream.readLine();
904                 QString type = stream.readLine();
905                 QString depends = stream.readLine();
906                 QString mocable = stream.readLine();
907                 stream.skipWhiteSpace();
908
909                 QMakeLocalFileName fn(source);
910                 QFileInfo fi = findFileInfo(fn);
911
912                 SourceFile *file = files->lookupFile(fn);
913                 if(!file) {
914                     file = new SourceFile;
915                     file->file = fn;
916                     files->addFile(file);
917                     file->type = (SourceFileType)type.toInt();
918                     file->exists = fi.exists();
919                 }
920                 if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) {
921                     if(!file->dep_checked) { //get depends
922                         if(!file->deps)
923                             file->deps = new SourceDependChildren;
924                         file->dep_checked = true;
925                         QStringList depend_list = depends.split(";", QString::SkipEmptyParts);
926                         for(int depend = 0; depend < depend_list.size(); ++depend) {
927                             QMakeLocalFileName dep_fn(depend_list.at(depend));
928                             QFileInfo dep_fi(findFileInfo(dep_fn));
929                             SourceFile *dep = files->lookupFile(dep_fn);
930                             if(!dep) {
931                                 dep = new SourceFile;
932                                 dep->file = dep_fn;
933                                 dep->exists = dep_fi.exists();
934                                 dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN;
935                                 files->addFile(dep);
936                             }
937                             dep->included_count++;
938                             file->deps->addChild(dep);
939                         }
940                     }
941                     if(!file->moc_checked) { //get mocs
942                         file->moc_checked = true;
943                         file->mocable = mocable.toInt();
944                     }
945                 }
946             }
947         }
948     }
949 #endif
950 }
951
952 QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification()
953 {
954     return QMap<QString, QStringList>();
955 }
956
957 bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v)
958 {
959     return v == getCacheVerification();
960 }
961
962 QT_END_NAMESPACE