qmake/generators/makefiledeps.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    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 < 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 
       
   685 bool QMakeSourceFileInfo::findMocs(SourceFile *file)
       
   686 {
       
   687     if(file->moc_checked)
       
   688         return true;
       
   689     files_changed = true;
       
   690     file->moc_checked = true;
       
   691 
       
   692     int buffer_len;
       
   693     char *buffer = 0;
       
   694     {
       
   695         struct stat fst;
       
   696         int fd;
       
   697 #if defined(_MSC_VER) && _MSC_VER >= 1400
       
   698         if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(),
       
   699             _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0)
       
   700             fd = -1;
       
   701 #else
       
   702         fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY);
       
   703 #endif
       
   704         if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode))
       
   705             return false; //shouldn't happen
       
   706         buffer = getBuffer(fst.st_size);
       
   707         for(int have_read = buffer_len = 0;
       
   708             (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
       
   709             buffer_len += have_read);
       
   710         QT_CLOSE(fd);
       
   711     }
       
   712 
       
   713     debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData());
       
   714     int line_count = 1;
       
   715     bool ignore_qobject = false, ignore_qgadget = false;
       
   716  /* qmake ignore Q_GADGET */
       
   717  /* qmake ignore Q_OBJECT */
       
   718     for(int x = 0; x < buffer_len; x++) {
       
   719         if(*(buffer + x) == '/') {
       
   720             ++x;
       
   721             if(buffer_len >= x) {
       
   722                 if(*(buffer + x) == '/') { //c++ style comment
       
   723                     for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x);
       
   724                 } else if(*(buffer + x) == '*') { //c style comment
       
   725                     for(++x; x < buffer_len; ++x) {
       
   726                         if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore
       
   727                             if(buffer_len >= (x + 20) &&
       
   728                                !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) {
       
   729                                 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"",
       
   730                                           file->file.real().toLatin1().constData(), line_count);
       
   731                                 x += 20;
       
   732                                 ignore_qobject = true;
       
   733                             } else if(buffer_len >= (x + 20) &&
       
   734                                       !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) {
       
   735                                 debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"",
       
   736                                           file->file.real().toLatin1().constData(), line_count);
       
   737                                 x += 20;
       
   738                                 ignore_qgadget = true;
       
   739                             }
       
   740                         } else if(*(buffer + x) == '*') {
       
   741                             if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') {
       
   742                                 ++x;
       
   743                                 break;
       
   744                             }
       
   745                         } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) {
       
   746                             ++line_count;
       
   747                         }
       
   748                     }
       
   749                 }
       
   750             }
       
   751         } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') {
       
   752             const char term = *(buffer+(x++));
       
   753             while(x < buffer_len) {
       
   754                 if(*(buffer+x) == term)
       
   755                     break;
       
   756                 if(*(buffer+x) == '\\') {
       
   757                     x+=2;
       
   758                 } else {
       
   759                     if(qmake_endOfLine(*(buffer+x)))
       
   760                         ++line_count;
       
   761                     ++x;
       
   762                 }
       
   763             }
       
   764         }
       
   765         if(Option::debug_level && qmake_endOfLine(*(buffer+x)))
       
   766             ++line_count;
       
   767         if(((buffer_len > x+2 &&  *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_')
       
   768                    ||
       
   769             (buffer_len > x+4 &&  *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O'
       
   770                               &&  *(buffer+x+3) == 'M' && *(buffer+x+4) == '_'))
       
   771                    &&
       
   772                   *(buffer + x) != '_' &&
       
   773                   (*(buffer + x) < 'a' || *(buffer + x) > 'z') &&
       
   774                   (*(buffer + x) < 'A' || *(buffer + x) > 'Z') &&
       
   775                   (*(buffer + x) < '0' || *(buffer + x) > '9')) {
       
   776             ++x;
       
   777             int match = 0;
       
   778             static const char *interesting[] = { "OBJECT", "GADGET",
       
   779                                                  "M_OBJECT" };
       
   780             for(int interest = 0, m1, m2; interest < 3; ++interest) {
       
   781                 if(interest == 0 && ignore_qobject)
       
   782                     continue;
       
   783                 else if(interest == 1 && ignore_qgadget)
       
   784                     continue;
       
   785                 for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) {
       
   786                     if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) {
       
   787                         m2 = -1;
       
   788                         break;
       
   789                     }
       
   790                     ++m2;
       
   791                 }
       
   792                 if(m1 == m2) {
       
   793                     match = m2 + 2;
       
   794                     break;
       
   795                 }
       
   796             }
       
   797             if(match && *(buffer+x+match) != '_' &&
       
   798                (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') &&
       
   799                (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') &&
       
   800                (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) {
       
   801                 if(Option::debug_level) {
       
   802                     *(buffer+x+match) = '\0';
       
   803                     debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(),
       
   804                               line_count, buffer+x);
       
   805                 }
       
   806                 file->mocable = true;
       
   807                 return true;
       
   808             }
       
   809         }
       
   810     }
       
   811     return true;
       
   812 }
       
   813 
       
   814 
       
   815 void QMakeSourceFileInfo::saveCache(const QString &cf)
       
   816 {
       
   817 #ifdef QMAKE_USE_CACHE
       
   818     if(cf.isEmpty())
       
   819         return;
       
   820 
       
   821     QFile file(QMakeLocalFileName(cf).local());
       
   822     if(file.open(QIODevice::WriteOnly)) {
       
   823         QTextStream stream(&file);
       
   824         stream << qmake_version() << endl << endl; //version
       
   825         { //cache verification
       
   826             QMap<QString, QStringList> verify = getCacheVerification();
       
   827              stream << verify.count() << endl;
       
   828              for(QMap<QString, QStringList>::iterator it = verify.begin();
       
   829                  it != verify.end(); ++it) {
       
   830                  stream << it.key() << endl << it.value().join(";") << endl;
       
   831              }
       
   832              stream << endl;
       
   833         }
       
   834         if(files->nodes) {
       
   835             for(int file = 0; file < files->num_nodes; ++file) {
       
   836                 for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) {
       
   837                     stream << node->file->file.local() << endl; //source
       
   838                     stream << node->file->type << endl; //type
       
   839 
       
   840                     //depends
       
   841                     stream << ";";
       
   842                     if(node->file->deps) {
       
   843                         for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) {
       
   844                             if(depend)
       
   845                                 stream << ";";
       
   846                             stream << node->file->deps->children[depend]->file.local();
       
   847                         }
       
   848                     }
       
   849                     stream << endl;
       
   850 
       
   851                     stream << node->file->mocable << endl; //mocable
       
   852                     stream << endl; //just for human readability
       
   853                 }
       
   854             }
       
   855         }
       
   856         stream.flush();
       
   857         file.close();
       
   858     }
       
   859 #else
       
   860     Q_UNUSED(cf);
       
   861 #endif
       
   862 }
       
   863 
       
   864 void QMakeSourceFileInfo::loadCache(const QString &cf)
       
   865 {
       
   866     if(cf.isEmpty())
       
   867         return;
       
   868 
       
   869 #ifdef QMAKE_USE_CACHE
       
   870     QMakeLocalFileName cache_file(cf);
       
   871     int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY);
       
   872     if(fd == -1)
       
   873         return;
       
   874     QFileInfo cache_fi = findFileInfo(cache_file);
       
   875     if(!cache_fi.exists() || cache_fi.isDir())
       
   876         return;
       
   877 
       
   878     QFile file;
       
   879     if(!file.open(QIODevice::ReadOnly, fd))
       
   880         return;
       
   881     QTextStream stream(&file);
       
   882 
       
   883     if(stream.readLine() == qmake_version()) { //version check
       
   884         stream.skipWhiteSpace();
       
   885 
       
   886         bool verified = true;
       
   887         { //cache verification
       
   888             QMap<QString, QStringList> verify;
       
   889             int len = stream.readLine().toInt();
       
   890             for(int i = 0; i < len; ++i) {
       
   891                 QString var = stream.readLine();
       
   892                 QString val = stream.readLine();
       
   893                 verify.insert(var, val.split(';', QString::SkipEmptyParts));
       
   894             }
       
   895             verified = verifyCache(verify);
       
   896         }
       
   897         if(verified) {
       
   898             stream.skipWhiteSpace();
       
   899             if(!files)
       
   900                 files = new SourceFiles;
       
   901             while(!stream.atEnd()) {
       
   902                 QString source = stream.readLine();
       
   903                 QString type = stream.readLine();
       
   904                 QString depends = stream.readLine();
       
   905                 QString mocable = stream.readLine();
       
   906                 stream.skipWhiteSpace();
       
   907 
       
   908                 QMakeLocalFileName fn(source);
       
   909                 QFileInfo fi = findFileInfo(fn);
       
   910 
       
   911                 SourceFile *file = files->lookupFile(fn);
       
   912                 if(!file) {
       
   913                     file = new SourceFile;
       
   914                     file->file = fn;
       
   915                     files->addFile(file);
       
   916                     file->type = (SourceFileType)type.toInt();
       
   917                     file->exists = fi.exists();
       
   918                 }
       
   919                 if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) {
       
   920                     if(!file->dep_checked) { //get depends
       
   921                         if(!file->deps)
       
   922                             file->deps = new SourceDependChildren;
       
   923                         file->dep_checked = true;
       
   924                         QStringList depend_list = depends.split(";", QString::SkipEmptyParts);
       
   925                         for(int depend = 0; depend < depend_list.size(); ++depend) {
       
   926                             QMakeLocalFileName dep_fn(depend_list.at(depend));
       
   927                             QFileInfo dep_fi(findFileInfo(dep_fn));
       
   928                             SourceFile *dep = files->lookupFile(dep_fn);
       
   929                             if(!dep) {
       
   930                                 dep = new SourceFile;
       
   931                                 dep->file = dep_fn;
       
   932                                 dep->exists = dep_fi.exists();
       
   933                                 dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN;
       
   934                                 files->addFile(dep);
       
   935                             }
       
   936                             dep->included_count++;
       
   937                             file->deps->addChild(dep);
       
   938                         }
       
   939                     }
       
   940                     if(!file->moc_checked) { //get mocs
       
   941                         file->moc_checked = true;
       
   942                         file->mocable = mocable.toInt();
       
   943                     }
       
   944                 }
       
   945             }
       
   946         }
       
   947     }
       
   948 #endif
       
   949 }
       
   950 
       
   951 QMap<QString, QStringList> QMakeSourceFileInfo::getCacheVerification()
       
   952 {
       
   953     return QMap<QString, QStringList>();
       
   954 }
       
   955 
       
   956 bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &v)
       
   957 {
       
   958     return v == getCacheVerification();
       
   959 }
       
   960 
       
   961 QT_END_NAMESPACE