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

Last change on this file since 886 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 34.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the qmake application of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** 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 have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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 const QMakeLocalFileName sourceFile = fixPathForFile(file->file, true);
382
383 struct stat fst;
384 char *buffer = 0;
385 int buffer_len = 0;
386 {
387 int fd;
388#if defined(_MSC_VER) && _MSC_VER >= 1400
389 if (_sopen_s(&fd, sourceFile.local().toLatin1().constData(),
390 _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0)
391 fd = -1;
392#else
393 fd = open(sourceFile.local().toLatin1().constData(), O_RDONLY);
394#endif
395 if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
396 return false;
397 buffer = getBuffer(fst.st_size);
398 for(int have_read = 0;
399 (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
400 buffer_len += have_read) ;
401 QT_CLOSE(fd);
402 }
403 if(!buffer)
404 return false;
405 if(!file->deps)
406 file->deps = new SourceDependChildren;
407
408 int line_count = 1;
409
410 for(int x = 0; x < buffer_len; ++x) {
411 bool try_local = true;
412 char *inc = 0;
413 if(file->type == QMakeSourceFileInfo::TYPE_UI) {
414 // skip whitespaces
415 while(x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t'))
416 ++x;
417 if(*(buffer + x) == '<') {
418 ++x;
419 if(buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) &&
420 (*(buffer + x + 11) == ' ' || *(buffer + x + 11) == '>')) {
421 for(x += 11; *(buffer + x) != '>'; ++x) ;
422 int inc_len = 0;
423 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ;
424 *(buffer + x + inc_len) = '\0';
425 inc = buffer + x;
426 } else if(buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) &&
427 (*(buffer + x + 12) == ' ' || *(buffer + x + 12) == '>')) {
428 for(x += 13; *(buffer + x) != '>'; ++x) ; //skip up to >
429 while(x < buffer_len) {
430 for(x++; *(buffer + x) != '<'; ++x) ; //skip up to <
431 x++;
432 if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) &&
433 (*(buffer + x + 6) == ' ' || *(buffer + x + 6) == '>')) {
434 for(x += 7; *(buffer + x) != '>'; ++x) ; //skip up to >
435 int inc_len = 0;
436 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ;
437 *(buffer + x + inc_len) = '\0';
438 inc = buffer + x;
439 break;
440 } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) &&
441 (*(buffer + x + 13) == ' ' || *(buffer + x + 13) == '>')) {
442 x += 14;
443 break;
444 }
445 }
446 } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) &&
447 (*(buffer + x + 7) == ' ' || *(buffer + x + 7) == '>')) {
448 for(x += 8; *(buffer + x) != '>'; ++x) {
449 if(buffer_len >= x + 9 && *(buffer + x) == 'i' &&
450 !strncmp(buffer + x, "impldecl", 8)) {
451 for(x += 8; *(buffer + x) != '='; ++x) ;
452 if(*(buffer + x) != '=')
453 continue;
454 for(++x; *(buffer+x) == '\t' || *(buffer+x) == ' '; ++x) ;
455 char quote = 0;
456 if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
457 quote = *(buffer + x);
458 ++x;
459 }
460 int val_len;
461 for(val_len = 0; true; ++val_len) {
462 if(quote) {
463 if(*(buffer+x+val_len) == quote)
464 break;
465 } else if(*(buffer + x + val_len) == '>' ||
466 *(buffer + x + val_len) == ' ') {
467 break;
468 }
469 }
470//? char saved = *(buffer + x + val_len);
471 *(buffer + x + val_len) = '\0';
472 if(!strcmp(buffer+x, "in implementation")) {
473 //### do this
474 }
475 }
476 }
477 int inc_len = 0;
478 for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ;
479 *(buffer + x + inc_len) = '\0';
480 inc = buffer + x;
481 }
482 }
483 //read past new line now..
484 for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ;
485 ++line_count;
486 } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) {
487 } else if(file->type == QMakeSourceFileInfo::TYPE_C) {
488 for(int beginning=1; x < buffer_len; ++x) {
489 // whitespace comments and line-endings
490 for(; x < buffer_len; ++x) {
491 if(*(buffer+x) == ' ' || *(buffer+x) == '\t') {
492 // keep going
493 } else if(*(buffer+x) == '/') {
494 ++x;
495 if(buffer_len >= x) {
496 if(*(buffer+x) == '/') { //c++ style comment
497 for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ;
498 beginning = 1;
499 } else if(*(buffer+x) == '*') { //c style comment
500 for(++x; x < buffer_len; ++x) {
501 if(*(buffer+x) == '*') {
502 if(x < buffer_len-1 && *(buffer + (x+1)) == '/') {
503 ++x;
504 break;
505 }
506 } else if(qmake_endOfLine(*(buffer+x))) {
507 ++line_count;
508 }
509 }
510 }
511 }
512 } else if(qmake_endOfLine(*(buffer+x))) {
513 ++line_count;
514 beginning = 1;
515 } else {
516 break;
517 }
518 }
519
520 if(x >= buffer_len)
521 break;
522
523 // preprocessor directive
524 if(beginning && *(buffer+x) == '#')
525 break;
526
527 // quoted strings
528 if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
529 const char term = *(buffer+(x++));
530 for(; x < buffer_len; ++x) {
531 if(*(buffer+x) == term) {
532 ++x;
533 break;
534 } else if(*(buffer+x) == '\\') {
535 ++x;
536 } else if(qmake_endOfLine(*(buffer+x))) {
537 ++line_count;
538 }
539 }
540 }
541 beginning = 0;
542 }
543 if(x >= buffer_len)
544 break;
545
546 //got a preprocessor symbol
547 ++x;
548 while(x < buffer_len) {
549 if(*(buffer+x) != ' ' && *(buffer+x) != '\t')
550 break;
551 ++x;
552 }
553
554 int keyword_len = 0;
555 const char *keyword = buffer+x;
556 while(x+keyword_len < buffer_len) {
557 if(((*(buffer+x+keyword_len) < 'a' || *(buffer+x+keyword_len) > 'z')) &&
558 *(buffer+x+keyword_len) != '_') {
559 for(x+=keyword_len; //skip spaces after keyword
560 x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t');
561 x++) ;
562 break;
563 } else if(qmake_endOfLine(*(buffer+x+keyword_len))) {
564 x += keyword_len-1;
565 keyword_len = 0;
566 break;
567 }
568 keyword_len++;
569 }
570
571 if(keyword_len == 7 && !strncmp(keyword, "include", keyword_len)) {
572 char term = *(buffer + x);
573 if(term == '<') {
574 try_local = false;
575 term = '>';
576 } else if(term != '"') { //wtf?
577 continue;
578 }
579 x++;
580
581 int inc_len;
582 for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len) ;
583 *(buffer + x + inc_len) = '\0';
584 inc = buffer + x;
585 x += inc_len;
586 } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) {
587 char term = 0;
588 if(*(buffer + x) == '"')
589 term = '"';
590 if(*(buffer + x) == '\'')
591 term = '\'';
592 if(term)
593 x++;
594
595 int msg_len;
596 for(msg_len = 0; (term && *(buffer + x + msg_len) != term) &&
597 !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len) ;
598 *(buffer + x + msg_len) = '\0';
599 debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x);
600 x += msg_len;
601 } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
602 const char term = *(buffer+(x++));
603 while(x < buffer_len) {
604 if(*(buffer+x) == term)
605 break;
606 if(*(buffer+x) == '\\') {
607 x+=2;
608 } else {
609 if(qmake_endOfLine(*(buffer+x)))
610 ++line_count;
611 ++x;
612 }
613 }
614 } else {
615 --x;
616 }
617 }
618
619 if(inc) {
620 if(!includes)
621 includes = new SourceFiles;
622 SourceFile *dep = includes->lookupFile(inc);
623 if(!dep) {
624 bool exists = false;
625 QMakeLocalFileName lfn(inc);
626 if(QDir::isRelativePath(lfn.real())) {
627 if(try_local) {
628 QDir sourceDir = findFileInfo(sourceFile).dir();
629 QMakeLocalFileName f(sourceDir.absoluteFilePath(lfn.local()));
630 if(findFileInfo(f).exists()) {
631 lfn = fixPathForFile(f);
632 exists = true;
633 }
634 }
635 if(!exists) { //path lookup
636 for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin(); it != depdirs.end(); ++it) {
637 QMakeLocalFileName f((*it).real() + Option::dir_sep + lfn.real());
638 QFileInfo fi(findFileInfo(f));
639 if(fi.exists() && !fi.isDir()) {
640 lfn = fixPathForFile(f);
641 exists = true;
642 break;
643 }
644 }
645 }
646 if(!exists) { //heuristic lookup
647 lfn = findFileForDep(QMakeLocalFileName(inc), file->file);
648 if((exists = !lfn.isNull()))
649 lfn = fixPathForFile(lfn);
650 }
651 } else {
652 exists = QFile::exists(lfn.real());
653 }
654 if(!lfn.isNull()) {
655 dep = files->lookupFile(lfn);
656 if(!dep) {
657 dep = new SourceFile;
658 dep->file = lfn;
659 dep->type = QMakeSourceFileInfo::TYPE_C;
660 files->addFile(dep);
661 includes->addFile(dep, inc, false);
662 }
663 dep->exists = exists;
664 }
665 }
666 if(dep && dep->file != file->file) {
667 dep->included_count++;
668 if(dep->exists) {
669 debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(),
670 line_count, dep->file.local().toLatin1().constData());
671 file->deps->addChild(dep);
672 }
673 }
674 }
675 }
676 if(dependencyMode() == Recursive) { //done last because buffer is shared
677 for(int i = 0; i < file->deps->used_nodes; i++) {
678 if(!file->deps->children[i]->deps)
679 findDeps(file->deps->children[i]);
680 }
681 }
682 return true;
683}
684
685bool 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
815void 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
864void 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
951QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification()
952{
953 return QMap<QString, QStringList>();
954}
955
956bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v)
957{
958 return v == getCacheVerification();
959}
960
961QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.