summaryrefslogtreecommitdiffstats
path: root/qmake/generators/makefiledeps.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qmake/generators/makefiledeps.cpp')
-rw-r--r--qmake/generators/makefiledeps.cpp963
1 files changed, 963 insertions, 0 deletions
diff --git a/qmake/generators/makefiledeps.cpp b/qmake/generators/makefiledeps.cpp
new file mode 100644
index 0000000..852d6a2
--- /dev/null
+++ b/qmake/generators/makefiledeps.cpp
@@ -0,0 +1,963 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the qmake application of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "makefiledeps.h"
+#include "option.h"
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qbuffer.h>
+#include <qplatformdefs.h>
+#if defined(Q_OS_UNIX)
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+#include <qdebug.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#include <share.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if 1
+#define qmake_endOfLine(c) (c == '\r' || c == '\n')
+#else
+inline bool qmake_endOfLine(const char &c) { return (c == '\r' || c == '\n'); }
+#endif
+
+//#define QMAKE_USE_CACHE
+
+QMakeLocalFileName::QMakeLocalFileName(const QString &name) : is_null(name.isNull())
+{
+ if(!name.isEmpty()) {
+ if(name.at(0) == QLatin1Char('"') && name.at(name.length()-2) == QLatin1Char('"'))
+ real_name = name.mid(1, name.length()-2);
+ else
+ real_name = name;
+ }
+}
+const QString
+&QMakeLocalFileName::local() const
+{
+ if(!is_null && local_name.isNull())
+ local_name = Option::fixPathToLocalOS(real_name, true);
+ return local_name;
+}
+
+struct SourceDependChildren;
+struct SourceFile {
+ SourceFile() : deps(0), type(QMakeSourceFileInfo::TYPE_UNKNOWN),
+ mocable(0), traversed(0), exists(1),
+ moc_checked(0), dep_checked(0), included_count(0) { }
+ ~SourceFile();
+ QMakeLocalFileName file;
+ SourceDependChildren *deps;
+ QMakeSourceFileInfo::SourceFileType type;
+ uint mocable : 1, traversed : 1, exists : 1;
+ uint moc_checked : 1, dep_checked : 1;
+ uchar included_count;
+};
+struct SourceDependChildren {
+ SourceFile **children;
+ int num_nodes, used_nodes;
+ SourceDependChildren() : children(0), num_nodes(0), used_nodes(0) { }
+ ~SourceDependChildren() { if(children) free(children); children = 0; }
+ void addChild(SourceFile *s) {
+ if(num_nodes <= used_nodes) {
+ num_nodes += 200;
+ children = (SourceFile**)realloc(children, sizeof(SourceFile*)*(num_nodes));
+ }
+ children[used_nodes++] = s;
+ }
+};
+SourceFile::~SourceFile() { delete deps; }
+class SourceFiles {
+ int hash(const char *);
+public:
+ SourceFiles();
+ ~SourceFiles();
+
+ SourceFile *lookupFile(const char *);
+ inline SourceFile *lookupFile(const QString &f) { return lookupFile(f.toLatin1().constData()); }
+ inline SourceFile *lookupFile(const QMakeLocalFileName &f) { return lookupFile(f.local().toLatin1().constData()); }
+ void addFile(SourceFile *, const char *k=0, bool own=true);
+
+ struct SourceFileNode {
+ SourceFileNode() : key(0), next(0), file(0), own_file(1) { }
+ ~SourceFileNode() {
+ delete [] key;
+ if(own_file)
+ delete file;
+ }
+ char *key;
+ SourceFileNode *next;
+ SourceFile *file;
+ uint own_file : 1;
+ } **nodes;
+ int num_nodes;
+};
+SourceFiles::SourceFiles()
+{
+ nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037));
+ for(int n = 0; n < num_nodes; n++)
+ nodes[n] = 0;
+}
+
+SourceFiles::~SourceFiles()
+{
+ for(int n = 0; n < num_nodes; n++) {
+ for(SourceFileNode *next = nodes[n]; next;) {
+ SourceFileNode *next_next = next->next;
+ delete next;
+ next = next_next;
+ }
+ }
+ free(nodes);
+}
+
+int SourceFiles::hash(const char *file)
+{
+ uint h = 0, g;
+ while (*file) {
+ h = (h << 4) + *file;
+ if ((g = (h & 0xf0000000)) != 0)
+ h ^= g >> 23;
+ h &= ~g;
+ file++;
+ }
+ return h;
+}
+
+SourceFile *SourceFiles::lookupFile(const char *file)
+{
+ int h = hash(file) % num_nodes;
+ for(SourceFileNode *p = nodes[h]; p; p = p->next) {
+ if(!strcmp(p->key, file))
+ return p->file;
+ }
+ return 0;
+}
+
+void SourceFiles::addFile(SourceFile *p, const char *k, bool own_file)
+{
+ QByteArray ba = p->file.local().toLatin1();
+ if(!k)
+ k = ba;
+ int h = hash(k) % num_nodes;
+ SourceFileNode *pn = new SourceFileNode;
+ pn->own_file = own_file;
+ pn->key = qstrdup(k);
+ pn->file = p;
+ pn->next = nodes[h];
+ nodes[h] = pn;
+}
+
+void QMakeSourceFileInfo::dependTreeWalker(SourceFile *node, SourceDependChildren *place)
+{
+ if(node->traversed || !node->exists)
+ return;
+ place->addChild(node);
+ node->traversed = true; //set flag
+ if(node->deps) {
+ for(int i = 0; i < node->deps->used_nodes; i++)
+ dependTreeWalker(node->deps->children[i], place);
+ }
+}
+
+void QMakeSourceFileInfo::setDependencyPaths(const QList<QMakeLocalFileName> &l)
+{
+ // Ensure that depdirs does not contain the same paths several times, to minimize the stats
+ QList<QMakeLocalFileName> ll;
+ for (int i = 0; i < l.count(); ++i) {
+ if (!ll.contains(l.at(i)))
+ ll.append(l.at(i));
+ }
+ depdirs = ll;
+}
+
+QStringList QMakeSourceFileInfo::dependencies(const QString &file)
+{
+ QStringList ret;
+ if(!files)
+ return ret;
+
+ if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) {
+ if(node->deps) {
+ /* I stick them into a SourceDependChildren here because it is faster to just
+ iterate over the list to stick them in the list, and reset the flag, then it is
+ to loop over the tree (about 50% faster I saw) --Sam */
+ SourceDependChildren place;
+ for(int i = 0; i < node->deps->used_nodes; i++)
+ dependTreeWalker(node->deps->children[i], &place);
+ if(place.children) {
+ for(int i = 0; i < place.used_nodes; i++) {
+ place.children[i]->traversed = false; //reset flag
+ ret.append(place.children[i]->file.real());
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+int
+QMakeSourceFileInfo::included(const QString &file)
+{
+ if (!files)
+ return 0;
+
+ if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file)))
+ return node->included_count;
+ return 0;
+}
+
+bool QMakeSourceFileInfo::mocable(const QString &file)
+{
+ if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file)))
+ return node->mocable;
+ return false;
+}
+
+QMakeSourceFileInfo::QMakeSourceFileInfo(const QString &cf)
+{
+ //dep_mode
+ dep_mode = Recursive;
+
+ //quick project lookups
+ includes = files = 0;
+ files_changed = false;
+
+ //buffer
+ spare_buffer = 0;
+ spare_buffer_size = 0;
+
+ //cache
+ cachefile = cf;
+ if(!cachefile.isEmpty())
+ loadCache(cachefile);
+}
+
+QMakeSourceFileInfo::~QMakeSourceFileInfo()
+{
+ //cache
+ if(!cachefile.isEmpty() /*&& files_changed*/)
+ saveCache(cachefile);
+
+ //buffer
+ if(spare_buffer) {
+ free(spare_buffer);
+ spare_buffer = 0;
+ spare_buffer_size = 0;
+ }
+
+ //quick project lookup
+ delete files;
+ delete includes;
+}
+
+void QMakeSourceFileInfo::setCacheFile(const QString &cf)
+{
+ cachefile = cf;
+ loadCache(cachefile);
+}
+
+void QMakeSourceFileInfo::addSourceFiles(const QStringList &l, uchar seek,
+ QMakeSourceFileInfo::SourceFileType type)
+{
+ for(int i=0; i<l.size(); ++i)
+ addSourceFile(l.at(i), seek, type);
+}
+void QMakeSourceFileInfo::addSourceFile(const QString &f, uchar seek,
+ QMakeSourceFileInfo::SourceFileType type)
+{
+ if(!files)
+ files = new SourceFiles;
+
+ QMakeLocalFileName fn(f);
+ SourceFile *file = files->lookupFile(fn);
+ if(!file) {
+ file = new SourceFile;
+ file->file = fn;
+ files->addFile(file);
+ } else {
+ if(file->type != type && file->type != TYPE_UNKNOWN && type != TYPE_UNKNOWN)
+ warn_msg(WarnLogic, "%s is marked as %d, then %d!", f.toLatin1().constData(),
+ file->type, type);
+ }
+ if(type != TYPE_UNKNOWN)
+ file->type = type;
+
+ if(seek & SEEK_MOCS && !file->moc_checked)
+ findMocs(file);
+ if(seek & SEEK_DEPS && !file->dep_checked)
+ findDeps(file);
+}
+
+bool QMakeSourceFileInfo::containsSourceFile(const QString &f, SourceFileType type)
+{
+ if(SourceFile *file = files->lookupFile(QMakeLocalFileName(f)))
+ return (file->type == type || file->type == TYPE_UNKNOWN || type == TYPE_UNKNOWN);
+ return false;
+}
+
+char *QMakeSourceFileInfo::getBuffer(int s) {
+ if(!spare_buffer || spare_buffer_size < s)
+ spare_buffer = (char *)realloc(spare_buffer, spare_buffer_size=s);
+ return spare_buffer;
+}
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (x & _S_IFDIR)
+#endif
+
+QMakeLocalFileName QMakeSourceFileInfo::fixPathForFile(const QMakeLocalFileName &f, bool)
+{
+ return f;
+}
+
+QMakeLocalFileName QMakeSourceFileInfo::findFileForDep(const QMakeLocalFileName &/*dep*/,
+ const QMakeLocalFileName &/*file*/)
+{
+ return QMakeLocalFileName();
+}
+
+QFileInfo QMakeSourceFileInfo::findFileInfo(const QMakeLocalFileName &dep)
+{
+ return QFileInfo(dep.real());
+}
+
+bool QMakeSourceFileInfo::findDeps(SourceFile *file)
+{
+ if(file->dep_checked || file->type == TYPE_UNKNOWN)
+ return true;
+ files_changed = true;
+ file->dep_checked = true;
+
+ struct stat fst;
+ char *buffer = 0;
+ int buffer_len = 0;
+ {
+ int fd;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLatin1().constData(),
+ _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0)
+ fd = -1;
+#else
+ fd = open(fixPathForFile(file->file, true).local().toLatin1().constData(), O_RDONLY);
+#endif
+ if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
+ return false;
+ buffer = getBuffer(fst.st_size);
+ for(int have_read = 0;
+ (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
+ buffer_len += have_read);
+ QT_CLOSE(fd);
+ }
+ if(!buffer)
+ return false;
+ if(!file->deps)
+ file->deps = new SourceDependChildren;
+
+ int line_count = 1;
+
+ for(int x = 0; x < buffer_len; ++x) {
+ bool try_local = true;
+ char *inc = 0;
+ if(file->type == QMakeSourceFileInfo::TYPE_UI) {
+ // skip whitespaces
+ while(x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t'))
+ ++x;
+ if(*(buffer + x) == '<') {
+ ++x;
+ if(buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) &&
+ (*(buffer + x + 11) == ' ' || *(buffer + x + 11) == '>')) {
+ for(x += 11; *(buffer + x) != '>'; ++x);
+ int inc_len = 0;
+ for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len);
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ } else if(buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) &&
+ (*(buffer + x + 12) == ' ' || *(buffer + x + 12) == '>')) {
+ for(x += 13; *(buffer + x) != '>'; ++x); //skip up to >
+ while(x < buffer_len) {
+ for(x++; *(buffer + x) != '<'; ++x); //skip up to <
+ x++;
+ if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) &&
+ (*(buffer + x + 6) == ' ' || *(buffer + x + 6) == '>')) {
+ for(x += 7; *(buffer + x) != '>'; ++x); //skip up to >
+ int inc_len = 0;
+ for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len);
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ break;
+ } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) &&
+ (*(buffer + x + 13) == ' ' || *(buffer + x + 13) == '>')) {
+ x += 14;
+ break;
+ }
+ }
+ } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) &&
+ (*(buffer + x + 7) == ' ' || *(buffer + x + 7) == '>')) {
+ for(x += 8; *(buffer + x) != '>'; ++x) {
+ if(buffer_len >= x + 9 && *(buffer + x) == 'i' &&
+ !strncmp(buffer + x, "impldecl", 8)) {
+ for(x += 8; *(buffer + x) != '='; ++x);
+ if(*(buffer + x) != '=')
+ continue;
+ for(++x; *(buffer+x) == '\t' || *(buffer+x) == ' '; ++x);
+ char quote = 0;
+ if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ quote = *(buffer + x);
+ ++x;
+ }
+ int val_len;
+ for(val_len = 0; true; ++val_len) {
+ if(quote) {
+ if(*(buffer+x+val_len) == quote)
+ break;
+ } else if(*(buffer + x + val_len) == '>' ||
+ *(buffer + x + val_len) == ' ') {
+ break;
+ }
+ }
+//? char saved = *(buffer + x + val_len);
+ *(buffer + x + val_len) = '\0';
+ if(!strcmp(buffer+x, "in implementation")) {
+ //### do this
+ }
+ }
+ }
+ int inc_len = 0;
+ for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len);
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ }
+ }
+ //read past new line now..
+ for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
+ ++line_count;
+ } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) {
+ } else if(file->type == QMakeSourceFileInfo::TYPE_C) {
+ for(int beginning=1; x < buffer_len; ++x) {
+ // whitespace comments and line-endings
+ for(; x < buffer_len; ++x) {
+ if(*(buffer+x) == ' ' || *(buffer+x) == '\t') {
+ // keep going
+ } else if(*(buffer+x) == '/') {
+ ++x;
+ if(buffer_len >= x) {
+ if(*(buffer+x) == '/') { //c++ style comment
+ for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
+ beginning = 1;
+ } else if(*(buffer+x) == '*') { //c style comment
+ for(++x; x < buffer_len; ++x) {
+ if(*(buffer+x) == '*') {
+ if(x < buffer_len-1 && *(buffer + (x+1)) == '/') {
+ ++x;
+ break;
+ }
+ } else if(qmake_endOfLine(*(buffer+x))) {
+ ++line_count;
+ }
+ }
+ }
+ }
+ } else if(qmake_endOfLine(*(buffer+x))) {
+ ++line_count;
+ beginning = 1;
+ } else {
+ break;
+ }
+ }
+
+ if(x >= buffer_len)
+ break;
+
+ // preprocessor directive
+ if(beginning && *(buffer+x) == '#')
+ break;
+
+ // quoted strings
+ if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ const char term = *(buffer+(x++));
+ for(; x < buffer_len; ++x) {
+ if(*(buffer+x) == term) {
+ ++x;
+ break;
+ } else if(*(buffer+x) == '\\') {
+ ++x;
+ } else if(qmake_endOfLine(*(buffer+x))) {
+ ++line_count;
+ }
+ }
+ }
+ beginning = 0;
+ }
+ if(x >= buffer_len)
+ break;
+
+ //got a preprocessor symbol
+ ++x;
+ while(x < buffer_len) {
+ if(*(buffer+x) != ' ' && *(buffer+x) != '\t')
+ break;
+ ++x;
+ }
+
+ int keyword_len = 0;
+ const char *keyword = buffer+x;
+ while(x+keyword_len < buffer_len) {
+ if(((*(buffer+x+keyword_len) < 'a' || *(buffer+x+keyword_len) > 'z')) &&
+ *(buffer+x+keyword_len) != '_') {
+ for(x+=keyword_len; //skip spaces after keyword
+ x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t');
+ x++);
+ break;
+ } else if(qmake_endOfLine(*(buffer+x+keyword_len))) {
+ x += keyword_len-1;
+ keyword_len = 0;
+ break;
+ }
+ keyword_len++;
+ }
+
+ if(keyword_len == 7 && !strncmp(keyword, "include", keyword_len)) {
+ char term = *(buffer + x);
+ if(term == '<') {
+ try_local = false;
+ term = '>';
+ } else if(term != '"') { //wtf?
+ continue;
+ }
+ x++;
+
+ int inc_len;
+ for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len);
+ *(buffer + x + inc_len) = '\0';
+ inc = buffer + x;
+ x += inc_len;
+ } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) {
+ char term = 0;
+ if(*(buffer + x) == '"')
+ term = '"';
+ if(*(buffer + x) == '\'')
+ term = '\'';
+ if(term)
+ x++;
+
+ int msg_len;
+ for(msg_len = 0; (term && *(buffer + x + msg_len) != term) &&
+ !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len);
+ *(buffer + x + msg_len) = '\0';
+ debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x);
+ x += msg_len;
+ } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ const char term = *(buffer+(x++));
+ while(x < buffer_len) {
+ if(*(buffer+x) == term)
+ break;
+ if(*(buffer+x) == '\\') {
+ x+=2;
+ } else {
+ if(qmake_endOfLine(*(buffer+x)))
+ ++line_count;
+ ++x;
+ }
+ }
+ } else {
+ --x;
+ }
+ }
+
+ if(inc) {
+ if(!includes)
+ includes = new SourceFiles;
+ SourceFile *dep = includes->lookupFile(inc);
+ if(!dep) {
+ bool exists = false;
+ QMakeLocalFileName lfn(inc);
+ if(QDir::isRelativePath(lfn.real())) {
+ if(try_local) {
+ QString dir = findFileInfo(file->file).path();
+ if(QDir::isRelativePath(dir))
+ dir.prepend(qmake_getpwd() + "/");
+ if(!dir.endsWith("/"))
+ dir += "/";
+ QMakeLocalFileName f(dir + lfn.local());
+ if(findFileInfo(f).exists()) {
+ lfn = fixPathForFile(f);
+ exists = true;
+ }
+ }
+ if(!exists) { //path lookup
+ for(QList<QMakeLocalFileName>::Iterator it = depdirs.begin(); it != depdirs.end(); ++it) {
+ QMakeLocalFileName f((*it).real() + Option::dir_sep + lfn.real());
+ QFileInfo fi(findFileInfo(f));
+ if(fi.exists() && !fi.isDir()) {
+ lfn = fixPathForFile(f);
+ exists = true;
+ break;
+ }
+ }
+ }
+ if(!exists) { //heuristic lookup
+ lfn = findFileForDep(QMakeLocalFileName(inc), file->file);
+ if((exists = !lfn.isNull()))
+ lfn = fixPathForFile(lfn);
+ }
+ } else {
+ exists = QFile::exists(lfn.real());
+ }
+ if(!lfn.isNull()) {
+ dep = files->lookupFile(lfn);
+ if(!dep) {
+ dep = new SourceFile;
+ dep->file = lfn;
+ dep->type = QMakeSourceFileInfo::TYPE_C;
+ files->addFile(dep);
+ includes->addFile(dep, inc, false);
+ }
+ dep->exists = exists;
+ }
+ }
+ if(dep && dep->file != file->file) {
+ dep->included_count++;
+ if(dep->exists) {
+ debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(),
+ line_count, dep->file.local().toLatin1().constData());
+ file->deps->addChild(dep);
+ }
+ }
+ }
+ }
+ if(dependencyMode() == Recursive) { //done last because buffer is shared
+ for(int i = 0; i < file->deps->used_nodes; i++) {
+ if(!file->deps->children[i]->deps)
+ findDeps(file->deps->children[i]);
+ }
+ }
+ return true;
+}
+
+bool QMakeSourceFileInfo::findMocs(SourceFile *file)
+{
+ if(file->moc_checked)
+ return true;
+ files_changed = true;
+ file->moc_checked = true;
+
+ int buffer_len;
+ char *buffer = 0;
+ {
+ struct stat fst;
+ int fd;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(),
+ _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0)
+ fd = -1;
+#else
+ fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY);
+#endif
+ if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
+ return false; //shouldn't happen
+ buffer = getBuffer(fst.st_size);
+ for(int have_read = buffer_len = 0;
+ (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
+ buffer_len += have_read);
+ QT_CLOSE(fd);
+ }
+
+ debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData());
+ int line_count = 1;
+ bool ignore_qobject = false, ignore_qgadget = false;
+ /* qmake ignore Q_GADGET */
+ /* qmake ignore Q_OBJECT */
+ for(int x = 0; x < buffer_len; x++) {
+ if(*(buffer + x) == '/') {
+ ++x;
+ if(buffer_len >= x) {
+ if(*(buffer + x) == '/') { //c++ style comment
+ for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
+ } else if(*(buffer + x) == '*') { //c style comment
+ for(++x; x < buffer_len; ++x) {
+ if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore
+ if(buffer_len >= (x + 20) &&
+ !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) {
+ debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"",
+ file->file.real().toLatin1().constData(), line_count);
+ x += 20;
+ ignore_qobject = true;
+ } else if(buffer_len >= (x + 20) &&
+ !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) {
+ debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"",
+ file->file.real().toLatin1().constData(), line_count);
+ x += 20;
+ ignore_qgadget = true;
+ }
+ } else if(*(buffer + x) == '*') {
+ if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') {
+ ++x;
+ break;
+ }
+ } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) {
+ ++line_count;
+ }
+ }
+ }
+ }
+ } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
+ const char term = *(buffer+(x++));
+ while(x < buffer_len) {
+ if(*(buffer+x) == term)
+ break;
+ if(*(buffer+x) == '\\') {
+ x+=2;
+ } else {
+ if(qmake_endOfLine(*(buffer+x)))
+ ++line_count;
+ ++x;
+ }
+ }
+ }
+ if(Option::debug_level && qmake_endOfLine(*(buffer+x)))
+ ++line_count;
+ if(((buffer_len > x+2 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_')
+ ||
+ (buffer_len > x+4 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O'
+ && *(buffer+x+3) == 'M' && *(buffer+x+4) == '_'))
+ &&
+ *(buffer + x) != '_' &&
+ (*(buffer + x) < 'a' || *(buffer + x) > 'z') &&
+ (*(buffer + x) < 'A' || *(buffer + x) > 'Z') &&
+ (*(buffer + x) < '0' || *(buffer + x) > '9')) {
+ ++x;
+ int match = 0;
+ static const char *interesting[] = { "OBJECT", "GADGET",
+ "M_OBJECT" };
+ for(int interest = 0, m1, m2; interest < 3; ++interest) {
+ if(interest == 0 && ignore_qobject)
+ continue;
+ else if(interest == 1 && ignore_qgadget)
+ continue;
+ for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) {
+ if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) {
+ m2 = -1;
+ break;
+ }
+ ++m2;
+ }
+ if(m1 == m2) {
+ match = m2 + 2;
+ break;
+ }
+ }
+ if(match && *(buffer+x+match) != '_' &&
+ (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') &&
+ (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') &&
+ (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) {
+ if(Option::debug_level) {
+ *(buffer+x+match) = '\0';
+ debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(),
+ line_count, buffer+x);
+ }
+ file->mocable = true;
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+
+void QMakeSourceFileInfo::saveCache(const QString &cf)
+{
+#ifdef QMAKE_USE_CACHE
+ if(cf.isEmpty())
+ return;
+
+ QFile file(QMakeLocalFileName(cf).local());
+ if(file.open(QIODevice::WriteOnly)) {
+ QTextStream stream(&file);
+ stream << qmake_version() << endl << endl; //version
+ { //cache verification
+ QMap<QString, QStringList> verify = getCacheVerification();
+ stream << verify.count() << endl;
+ for(QMap<QString, QStringList>::iterator it = verify.begin();
+ it != verify.end(); ++it) {
+ stream << it.key() << endl << it.value().join(";") << endl;
+ }
+ stream << endl;
+ }
+ if(files->nodes) {
+ for(int file = 0; file < files->num_nodes; ++file) {
+ for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) {
+ stream << node->file->file.local() << endl; //source
+ stream << node->file->type << endl; //type
+
+ //depends
+ stream << ";";
+ if(node->file->deps) {
+ for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) {
+ if(depend)
+ stream << ";";
+ stream << node->file->deps->children[depend]->file.local();
+ }
+ }
+ stream << endl;
+
+ stream << node->file->mocable << endl; //mocable
+ stream << endl; //just for human readability
+ }
+ }
+ }
+ stream.flush();
+ file.close();
+ }
+#else
+ Q_UNUSED(cf);
+#endif
+}
+
+void QMakeSourceFileInfo::loadCache(const QString &cf)
+{
+ if(cf.isEmpty())
+ return;
+
+#ifdef QMAKE_USE_CACHE
+ QMakeLocalFileName cache_file(cf);
+ int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY);
+ if(fd == -1)
+ return;
+ QFileInfo cache_fi = findFileInfo(cache_file);
+ if(!cache_fi.exists() || cache_fi.isDir())
+ return;
+
+ QFile file;
+ if(!file.open(QIODevice::ReadOnly, fd))
+ return;
+ QTextStream stream(&file);
+
+ if(stream.readLine() == qmake_version()) { //version check
+ stream.skipWhiteSpace();
+
+ bool verified = true;
+ { //cache verification
+ QMap<QString, QStringList> verify;
+ int len = stream.readLine().toInt();
+ for(int i = 0; i < len; ++i) {
+ QString var = stream.readLine();
+ QString val = stream.readLine();
+ verify.insert(var, val.split(';', QString::SkipEmptyParts));
+ }
+ verified = verifyCache(verify);
+ }
+ if(verified) {
+ stream.skipWhiteSpace();
+ if(!files)
+ files = new SourceFiles;
+ while(!stream.atEnd()) {
+ QString source = stream.readLine();
+ QString type = stream.readLine();
+ QString depends = stream.readLine();
+ QString mocable = stream.readLine();
+ stream.skipWhiteSpace();
+
+ QMakeLocalFileName fn(source);
+ QFileInfo fi = findFileInfo(fn);
+
+ SourceFile *file = files->lookupFile(fn);
+ if(!file) {
+ file = new SourceFile;
+ file->file = fn;
+ files->addFile(file);
+ file->type = (SourceFileType)type.toInt();
+ file->exists = fi.exists();
+ }
+ if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) {
+ if(!file->dep_checked) { //get depends
+ if(!file->deps)
+ file->deps = new SourceDependChildren;
+ file->dep_checked = true;
+ QStringList depend_list = depends.split(";", QString::SkipEmptyParts);
+ for(int depend = 0; depend < depend_list.size(); ++depend) {
+ QMakeLocalFileName dep_fn(depend_list.at(depend));
+ QFileInfo dep_fi(findFileInfo(dep_fn));
+ SourceFile *dep = files->lookupFile(dep_fn);
+ if(!dep) {
+ dep = new SourceFile;
+ dep->file = dep_fn;
+ dep->exists = dep_fi.exists();
+ dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN;
+ files->addFile(dep);
+ }
+ dep->included_count++;
+ file->deps->addChild(dep);
+ }
+ }
+ if(!file->moc_checked) { //get mocs
+ file->moc_checked = true;
+ file->mocable = mocable.toInt();
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification()
+{
+ return QMap<QString, QStringList>();
+}
+
+bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v)
+{
+ return v == getCacheVerification();
+}
+
+QT_END_NAMESPACE