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

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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