QtBase  v6.3.1
makefiledeps.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2016 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the qmake application of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "makefiledeps.h"
31 #include "option.h"
32 #include <qdir.h>
33 #include <qdatetime.h>
34 #include <qfileinfo.h>
35 #include <qbuffer.h>
36 #include <qplatformdefs.h>
37 #if defined(Q_OS_UNIX)
38 # include <unistd.h>
39 #else
40 # include <io.h>
41 #endif
42 #include <qdebug.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <time.h>
46 #include <fcntl.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <limits.h>
50 #if defined(_MSC_VER) && _MSC_VER >= 1400
51 #include <share.h>
52 #endif
53 
55 
56 // FIXME: a line ending in CRLF gets counted as two lines.
57 #if 1
58 #define qmake_endOfLine(c) (c == '\r' || c == '\n')
59 #else
60 inline bool qmake_endOfLine(const char &c) { return (c == '\r' || c == '\n'); }
61 #endif
62 
64  : real_name(name)
65 {
66 }
67 const QString
69 {
70  if (!isNull() && local_name.isNull())
71  local_name = Option::normalizePath(real_name);
72  return local_name;
73 }
74 
76 struct SourceFile {
78  mocable(0), traversed(0), exists(1),
80  ~SourceFile();
84  uint mocable : 1, traversed : 1, exists : 1;
87 };
92  ~SourceDependChildren() { if (children) free(children); children = nullptr; }
94  if(num_nodes <= used_nodes) {
95  num_nodes += 200;
96  children = (SourceFile**)realloc(children, sizeof(SourceFile*)*(num_nodes));
97  }
98  children[used_nodes++] = s;
99  }
100 };
102 class SourceFiles {
103  int hash(const char *);
104 public:
105  SourceFiles();
106  ~SourceFiles();
107 
108  SourceFile *lookupFile(const char *);
109  inline SourceFile *lookupFile(const QString &f) { return lookupFile(f.toLatin1().constData()); }
110  inline SourceFile *lookupFile(const QMakeLocalFileName &f) { return lookupFile(f.local().toLatin1().constData()); }
111  void addFile(SourceFile *, const char *k = nullptr, bool own = true);
112 
113  struct SourceFileNode {
116  delete [] key;
117  if(own_file)
118  delete file;
119  }
120  char *key;
124  } **nodes;
126 };
128 {
129  nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037));
130  for(int n = 0; n < num_nodes; n++)
131  nodes[n] = nullptr;
132 }
133 
135 {
136  for(int n = 0; n < num_nodes; n++) {
137  for(SourceFileNode *next = nodes[n]; next;) {
138  SourceFileNode *next_next = next->next;
139  delete next;
140  next = next_next;
141  }
142  }
143  free(nodes);
144 }
145 
146 int SourceFiles::hash(const char *file)
147 {
148  uint h = 0, g;
149  while (*file) {
150  h = (h << 4) + *file;
151  if ((g = (h & 0xf0000000)) != 0)
152  h ^= g >> 23;
153  h &= ~g;
154  file++;
155  }
156  return h;
157 }
158 
160 {
161  int h = hash(file) % num_nodes;
162  for(SourceFileNode *p = nodes[h]; p; p = p->next) {
163  if(!strcmp(p->key, file))
164  return p->file;
165  }
166  return nullptr;
167 }
168 
169 void SourceFiles::addFile(SourceFile *p, const char *k, bool own_file)
170 {
171  const QByteArray ba = p->file.local().toLatin1();
172  if(!k)
173  k = ba.constData();
174  int h = hash(k) % num_nodes;
175  SourceFileNode *pn = new SourceFileNode;
176  pn->own_file = own_file;
177  pn->key = qstrdup(k);
178  pn->file = p;
179  pn->next = nodes[h];
180  nodes[h] = pn;
181 }
182 
183 void QMakeSourceFileInfo::dependTreeWalker(SourceFile *node, SourceDependChildren *place)
184 {
185  if(node->traversed || !node->exists)
186  return;
187  place->addChild(node);
188  node->traversed = true; //set flag
189  if(node->deps) {
190  for(int i = 0; i < node->deps->used_nodes; i++)
191  dependTreeWalker(node->deps->children[i], place);
192  }
193 }
194 
196 {
197  // Ensure that depdirs does not contain the same paths several times, to minimize the stats
199  for (int i = 0; i < l.count(); ++i) {
200  if (!ll.contains(l.at(i)))
201  ll.append(l.at(i));
202  }
203  depdirs = ll;
204 }
205 
207 {
209  if(!files)
210  return ret;
211 
213  if(node->deps) {
214  /* I stick them into a SourceDependChildren here because it is faster to just
215  iterate over the list to stick them in the list, and reset the flag, then it is
216  to loop over the tree (about 50% faster I saw) --Sam */
217  SourceDependChildren place;
218  for(int i = 0; i < node->deps->used_nodes; i++)
219  dependTreeWalker(node->deps->children[i], &place);
220  if(place.children) {
221  for(int i = 0; i < place.used_nodes; i++) {
222  place.children[i]->traversed = false; //reset flag
223  ret.append(place.children[i]->file.real());
224  }
225  }
226  }
227  }
228  return ret;
229 }
230 
231 int
233 {
234  if (!files)
235  return 0;
236 
238  return node->included_count;
239  return 0;
240 }
241 
243 {
245  return node->mocable;
246  return false;
247 }
248 
250 {
251  //dep_mode
252  dep_mode = Recursive;
253 
254  //quick project lookups
255  includes = files = nullptr;
256  files_changed = false;
257 
258  //buffer
259  spare_buffer = nullptr;
260  spare_buffer_size = 0;
261 }
262 
264 {
265  //buffer
266  if(spare_buffer) {
267  free(spare_buffer);
268  spare_buffer = nullptr;
269  spare_buffer_size = 0;
270  }
271 
272  //quick project lookup
273  delete files;
274  delete includes;
275 }
276 
279 {
280  for(int i=0; i<l.size(); ++i)
281  addSourceFile(l.at(i).toQString(), seek, type);
282 }
285 {
286  if(!files)
287  files = new SourceFiles;
288 
290  SourceFile *file = files->lookupFile(fn);
291  if(!file) {
292  file = new SourceFile;
293  file->file = fn;
294  files->addFile(file);
295  } else {
296  if(file->type != type && file->type != TYPE_UNKNOWN && type != TYPE_UNKNOWN)
297  warn_msg(WarnLogic, "%s is marked as %d, then %d!", f.toLatin1().constData(),
298  file->type, type);
299  }
300  if(type != TYPE_UNKNOWN)
301  file->type = type;
302 
303  if(seek & SEEK_MOCS && !file->moc_checked)
304  findMocs(file);
305  if(seek & SEEK_DEPS && !file->dep_checked)
306  findDeps(file);
307 }
308 
310 {
312  return (file->type == type || file->type == TYPE_UNKNOWN || type == TYPE_UNKNOWN);
313  return false;
314 }
315 
317 {
318  if (QDir::isRelativePath(name)) {
319  // if we got a relative path here, it's either an -I flag with a relative path
320  // or an include file we couldn't locate. Either way, conclude it's not
321  // a system include.
322  return false;
323  }
324 
325  for (int i = 0; i < systemIncludes.size(); ++i) {
326  // check if name is located inside the system include dir:
327  QDir systemDir(systemIncludes.at(i));
328  QString relativePath = systemDir.relativeFilePath(name);
329 
330  // the relative path might be absolute if we're crossing drives on Windows
331  if (QDir::isAbsolutePath(relativePath) || relativePath.startsWith("../"))
332  continue;
333  debug_msg(5, "File/dir %s is in system dir %s, skipping",
334  qPrintable(name), qPrintable(systemIncludes.at(i)));
335  return true;
336  }
337  return false;
338 }
339 
340 char *QMakeSourceFileInfo::getBuffer(int s) {
341  if(!spare_buffer || spare_buffer_size < s)
342  spare_buffer = (char *)realloc(spare_buffer, spare_buffer_size=s);
343  return spare_buffer;
344 }
345 
346 #ifndef S_ISDIR
347 #define S_ISDIR(x) (x & _S_IFDIR)
348 #endif
349 
351 {
352  return f;
353 }
354 
356  const QMakeLocalFileName &/*file*/)
357 {
358  return QMakeLocalFileName();
359 }
360 
362 {
363  return QFileInfo(dep.real());
364 }
365 
366 static int skipEscapedLineEnds(const char *buffer, int buffer_len, int offset, int *lines)
367 {
368  // Join physical lines to make logical lines, as in the C preprocessor
369  while (offset + 1 < buffer_len
370  && buffer[offset] == '\\'
371  && qmake_endOfLine(buffer[offset + 1])) {
372  offset += 2;
373  ++*lines;
374  if (offset < buffer_len
375  && buffer[offset - 1] == '\r'
376  && buffer[offset] == '\n') // CRLF
377  offset++;
378  }
379  return offset;
380 }
381 
382 static bool matchWhileUnsplitting(const char *buffer, int buffer_len, int start,
383  const char *needle, int needle_len,
384  int *matchlen, int *lines)
385 {
386  int x = start;
387  for (int n = 0; n < needle_len;
388  n++, x = skipEscapedLineEnds(buffer, buffer_len, x + 1, lines)) {
389  if (x >= buffer_len || buffer[x] != needle[n])
390  return false;
391  }
392  // That also skipped any remaining BSNLs immediately after the match.
393 
394  // Tell caller how long the match was:
395  *matchlen = x - start;
396 
397  return true;
398 }
399 
400 /* Advance from an opening quote at buffer[offset] to the matching close quote. */
401 static int scanPastString(char *buffer, int buffer_len, int offset, int *lines)
402 {
403  // http://en.cppreference.com/w/cpp/language/string_literal
404  // It might be a C++11 raw string.
405  bool israw = false;
406  if (buffer[offset] == '"' && offset > 0) {
407  int explore = offset - 1;
408  bool prefix = false; // One of L, U, u or u8 may appear before R
409  bool saw8 = false; // Partial scan of u8
410  while (explore >= 0) {
411  // Cope with backslash-newline interruptions of the prefix:
412  if (explore > 0
413  && qmake_endOfLine(buffer[explore])
414  && buffer[explore - 1] == '\\') {
415  explore -= 2;
416  } else if (explore > 1
417  && buffer[explore] == '\n'
418  && buffer[explore - 1] == '\r'
419  && buffer[explore - 2] == '\\') {
420  explore -= 3;
421  // Remaining cases can only decrement explore by one at a time:
422  } else if (saw8 && buffer[explore] == 'u') {
423  explore--;
424  saw8 = false;
425  prefix = true;
426  } else if (saw8 || prefix) {
427  break;
428  } else if (explore > 1 && buffer[explore] == '8') {
429  explore--;
430  saw8 = true;
431  } else if (buffer[explore] == 'L'
432  || buffer[explore] == 'U'
433  || buffer[explore] == 'u') {
434  explore--;
435  prefix = true;
436  } else if (buffer[explore] == 'R') {
437  if (israw)
438  break;
439  explore--;
440  israw = true;
441  } else {
442  break;
443  }
444  }
445  // Check the R (with possible prefix) isn't just part of an identifier:
446  if (israw && explore >= 0
447  && (isalnum(buffer[explore]) || buffer[explore] == '_')) {
448  israw = false;
449  }
450  }
451 
452  if (israw) {
453 #define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), lines)
454 
455  offset = SKIP_BSNL(offset + 1);
456  const char *const delim = buffer + offset;
457  int clean = offset;
458  while (offset < buffer_len && buffer[offset] != '(') {
459  if (clean < offset)
460  buffer[clean++] = buffer[offset];
461  else
462  clean++;
463 
464  offset = SKIP_BSNL(offset + 1);
465  }
466  /*
467  Not checking correctness (trust real compiler to do that):
468  - no controls, spaces, '(', ')', '\\' or (presumably) '"' in delim;
469  - at most 16 bytes in delim
470 
471  Raw strings are surely defined after phase 2, when BSNLs are resolved;
472  so the delimiter's exclusion of '\\' and space (including newlines)
473  applies too late to save us the need to cope with BSNLs in it.
474  */
475 
476  const int delimlen = buffer + clean - delim;
477  int matchlen = delimlen, extralines = 0;
478  while ((offset = SKIP_BSNL(offset + 1)) < buffer_len
479  && (buffer[offset] != ')'
480  || (delimlen > 0 &&
481  !matchWhileUnsplitting(buffer, buffer_len,
482  offset + 1, delim, delimlen,
483  &matchlen, &extralines))
484  || buffer[offset + 1 + matchlen] != '"')) {
485  // skip, but keep track of lines
487  ++*lines;
488  extralines = 0;
489  }
490  *lines += extralines; // from the match
491  // buffer[offset] is ')'
492  offset += 1 + matchlen; // 1 for ')', then delim
493  // buffer[offset] is '"'
494 
495 #undef SKIP_BSNL
496  } else { // Traditional string or char literal:
497  const char term = buffer[offset];
498  while (++offset < buffer_len && buffer[offset] != term) {
499  if (buffer[offset] == '\\')
500  ++offset;
501  else if (qmake_endOfLine(buffer[offset]))
502  ++*lines;
503  }
504  }
505 
506  return offset;
507 }
508 
509 bool QMakeSourceFileInfo::findDeps(SourceFile *file)
510 {
511  if(file->dep_checked || file->type == TYPE_UNKNOWN)
512  return true;
513  files_changed = true;
514  file->dep_checked = true;
515 
516  const QMakeLocalFileName sourceFile = fixPathForFile(file->file, true);
517 
518  struct stat fst;
519  char *buffer = nullptr;
520  int buffer_len = 0;
521  {
522  int fd;
523 #if defined(_MSC_VER) && _MSC_VER >= 1400
524  if (_sopen_s(&fd, sourceFile.local().toLatin1().constData(),
525  _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0)
526  fd = -1;
527 #else
528  fd = open(sourceFile.local().toLatin1().constData(), O_RDONLY);
529 #endif
530  if (fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode)) {
531  if (fd != -1)
532  QT_CLOSE(fd);
533  return false;
534  }
535  buffer = getBuffer(fst.st_size);
536  for(int have_read = 0;
537  (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len));
538  buffer_len += have_read) ;
539  QT_CLOSE(fd);
540  }
541  if(!buffer)
542  return false;
543  if(!file->deps)
544  file->deps = new SourceDependChildren;
545 
546  int line_count = 1;
547  enum {
548  /*
549  States of C preprocessing (for TYPE_C only), after backslash-newline
550  elimination and skipping comments and spaces (i.e. in ANSI X3.159-1989
551  section 2.1.1.2's phase 4). We're about to study buffer[x] to decide
552  on which transition to do.
553  */
554  AtStart, // start of logical line; a # may start a preprocessor directive
555  HadHash, // saw a # at start, looking for preprocessor keyword
556  WantName, // saw #include or #import, waiting for name
557  InCode // after directive, parsing non-#include directive or in actual code
558  } cpp_state = AtStart;
559 
560  int x = 0;
561  if (buffer_len >= 3) {
562  const unsigned char *p = (unsigned char *)buffer;
563  // skip UTF-8 BOM, if present
564  if (p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
565  x += 3;
566  }
567  for (; x < buffer_len; ++x) {
568  bool try_local = true;
569  char *inc = nullptr;
570  if(file->type == QMakeSourceFileInfo::TYPE_UI) {
571  // skip whitespaces
572  while (x < buffer_len && (buffer[x] == ' ' || buffer[x] == '\t'))
573  ++x;
574  if (buffer[x] == '<') {
575  ++x;
576  if (buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) &&
577  (buffer[x + 11] == ' ' || buffer[x + 11] == '>')) {
578  for (x += 11; x < buffer_len && buffer[x] != '>'; ++x) {} // skip
579  int inc_len = 0;
580  for (++x; x + inc_len < buffer_len && buffer[x + inc_len] != '<'; ++inc_len) {} // skip
581  if (x + inc_len < buffer_len) {
582  buffer[x + inc_len] = '\0';
583  inc = buffer + x;
584  }
585  } else if (buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) &&
586  (buffer[x + 12] == ' ' || buffer[x + 12] == '>')) {
587  for (x += 13; x < buffer_len && buffer[x] != '>'; ++x) {} // skip up to >
588  while(x < buffer_len) {
589  while (++x < buffer_len && buffer[x] != '<') {} // skip up to <
590  x++;
591  if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) &&
592  (buffer[x + 6] == ' ' || buffer[x + 6] == '>')) {
593  for (x += 7; x < buffer_len && buffer[x] != '>'; ++x) {} // skip up to >
594  int inc_len = 0;
595  for (++x; x + inc_len < buffer_len && buffer[x + inc_len] != '<';
596  ++inc_len) {} // skip
597  if (x + inc_len < buffer_len) {
598  buffer[x + inc_len] = '\0';
599  inc = buffer + x;
600  }
601  break;
602  } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) &&
603  (buffer[x + 13] == ' ' || buffer[x + 13] == '>')) {
604  x += 14;
605  break;
606  }
607  }
608  } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) &&
609  (buffer[x + 7] == ' ' || buffer[x + 7] == '>')) {
610  for (x += 8; x < buffer_len && buffer[x] != '>'; ++x) {
611  if (buffer_len >= x + 9 && buffer[x] == 'i' &&
612  !strncmp(buffer + x, "impldecl", 8)) {
613  for (x += 8; x < buffer_len && buffer[x] != '='; ++x) {} // skip
614  while (++x < buffer_len && (buffer[x] == '\t' || buffer[x] == ' ')) {} // skip
615  char quote = 0;
616  if (x < buffer_len && (buffer[x] == '\'' || buffer[x] == '"')) {
617  quote = buffer[x];
618  ++x;
619  }
620  int val_len;
621  for (val_len = 0; x + val_len < buffer_len; ++val_len) {
622  if(quote) {
623  if (buffer[x + val_len] == quote)
624  break;
625  } else if (buffer[x + val_len] == '>' ||
626  buffer[x + val_len] == ' ') {
627  break;
628  }
629  }
630 //? char saved = buffer[x + val_len];
631  if (x + val_len < buffer_len) {
632  buffer[x + val_len] = '\0';
633  if (!strcmp(buffer + x, "in implementation")) {
634  //### do this
635  }
636  }
637  }
638  }
639  int inc_len = 0;
640  for (++x; x + inc_len < buffer_len && buffer[x + inc_len] != '<';
641  ++inc_len) {} // skip
642 
643  if (x + inc_len < buffer_len) {
644  buffer[x + inc_len] = '\0';
645  inc = buffer + x;
646  }
647  }
648  }
649  //read past new line now..
650  for (; x < buffer_len && !qmake_endOfLine(buffer[x]); ++x) {} // skip
651  ++line_count;
652  } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) {
653  } else if(file->type == QMakeSourceFileInfo::TYPE_C) {
654  // We've studied all buffer[i] for i < x
655  for (; x < buffer_len; ++x) {
656  // How to handle backslash-newline (BSNL) pairs:
657 #define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), &line_count)
658 
659  // Seek code or directive, skipping comments and space:
660  for (; (x = SKIP_BSNL(x)) < buffer_len; ++x) {
661  if (buffer[x] == ' ' || buffer[x] == '\t') {
662  // keep going
663  } else if (buffer[x] == '/') {
664  int extralines = 0;
665  int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &extralines);
666  if (y >= buffer_len) {
667  x = y;
668  break;
669  } else if (buffer[y] == '/') { // C++-style comment
670  line_count += extralines;
671  x = SKIP_BSNL(y + 1);
672  while (x < buffer_len && !qmake_endOfLine(buffer[x]))
673  x = SKIP_BSNL(x + 1); // skip
674 
675  cpp_state = AtStart;
676  ++line_count;
677  } else if (buffer[y] == '*') { // C-style comment
678  line_count += extralines;
679  x = y;
680  while ((x = SKIP_BSNL(++x)) < buffer_len) {
681  if (buffer[x] == '*') {
682  extralines = 0;
683  y = skipEscapedLineEnds(buffer, buffer_len,
684  x + 1, &extralines);
685  if (y < buffer_len && buffer[y] == '/') {
686  line_count += extralines;
687  x = y; // for loop shall step past this
688  break;
689  }
690  } else if (qmake_endOfLine(buffer[x])) {
691  ++line_count;
692  }
693  }
694  } else {
695  // buffer[x] is the division operator
696  break;
697  }
698  } else if (qmake_endOfLine(buffer[x])) {
699  ++line_count;
700  cpp_state = AtStart;
701  } else {
702  /* Drop out of phases 1, 2, 3, into phase 4 */
703  break;
704  }
705  }
706  // Phase 4 study of buffer[x]:
707 
708  if(x >= buffer_len)
709  break;
710 
711  switch (cpp_state) {
712  case HadHash:
713  {
714  // Read keyword; buffer[x] starts first preprocessing token after #
715  const char *const keyword = buffer + x;
716  int clean = x;
717  while (x < buffer_len && buffer[x] >= 'a' && buffer[x] <= 'z') {
718  // skip over keyword, consolidating it if it contains BSNLs
719  // (see WantName's similar code consolidating inc, below)
720  if (clean < x)
721  buffer[clean++] = buffer[x];
722  else
723  clean++;
724 
725  x = SKIP_BSNL(x + 1);
726  }
727  const int keyword_len = buffer + clean - keyword;
728  x--; // Still need to study buffer[x] next time round for loop.
729 
730  cpp_state =
731  ((keyword_len == 7 && !strncmp(keyword, "include", 7)) // C & Obj-C
732  || (keyword_len == 6 && !strncmp(keyword, "import", 6))) // Obj-C
733  ? WantName : InCode;
734  break;
735  }
736 
737  case WantName:
738  {
739  char term = buffer[x];
740  if (term == '<') {
741  try_local = false;
742  term = '>';
743  } else if (term != '"') {
744  /*
745  Possibly malformed, but this may be something like:
746  #include IDENTIFIER
747  which does work, if #define IDENTIFIER "filename" is
748  in effect. This is beyond this noddy preprocessor's
749  powers of tracking. So give up and resume searching
750  for a directive. We haven't made sense of buffer[x],
751  so back up to ensure we do study it (now as code) next
752  time round the loop.
753  */
754  x--;
755  cpp_state = InCode;
756  continue;
757  }
758 
759  x = SKIP_BSNL(x + 1);
760  inc = buffer + x;
761  int clean = x; // offset if we need to clear \-newlines
762  for (; x < buffer_len && buffer[x] != term; x = SKIP_BSNL(x + 1)) {
763  if (qmake_endOfLine(buffer[x])) { // malformed
764  cpp_state = AtStart;
765  ++line_count;
766  break;
767  }
768 
769  /*
770  If we do skip any BSNLs, we need to consolidate the
771  surviving text by copying to lower indices. For that
772  to be possible, we also have to keep 'clean' advanced
773  in step with x even when we've yet to see any BSNLs.
774  */
775  if (clean < x)
776  buffer[clean++] = buffer[x];
777  else
778  clean++;
779  }
780  if (cpp_state == WantName)
781  buffer[clean] = '\0';
782  else // i.e. malformed
783  inc = nullptr;
784 
785  cpp_state = InCode; // hereafter
786  break;
787  }
788 
789  case AtStart:
790  // Preprocessor directive?
791  if (buffer[x] == '#') {
792  cpp_state = HadHash;
793  break;
794  }
795  cpp_state = InCode;
796  Q_FALLTHROUGH(); // to handle buffer[x] as such.
797  case InCode:
798  // matching quotes (string literals and character literals)
799  if (buffer[x] == '\'' || buffer[x] == '"') {
800  x = scanPastString(buffer, buffer_len, x, &line_count);
801  // for loop's ++x shall step over the closing quote.
802  }
803  // else: buffer[x] is just some code; move on.
804  break;
805  }
806 
807  if (inc) // We were in WantName and found a name.
808  break;
809 #undef SKIP_BSNL
810  }
811  if(x >= buffer_len)
812  break;
813  }
814 
815  if(inc) {
816  if(!includes)
817  includes = new SourceFiles;
818  /* QTBUG-72383: Local includes "foo.h" must first be resolved relative to the
819  * sourceDir, only global includes <bar.h> are unique. */
820  SourceFile *dep = try_local ? nullptr : includes->lookupFile(inc);
821  if(!dep) {
822  bool exists = false;
823  QMakeLocalFileName lfn(inc);
824  if(QDir::isRelativePath(lfn.real())) {
825  if(try_local) {
826  QDir sourceDir = findFileInfo(sourceFile).dir();
827  QMakeLocalFileName f(sourceDir.absoluteFilePath(lfn.local()));
828  if(findFileInfo(f).exists()) {
829  lfn = fixPathForFile(f);
830  exists = true;
831  }
832  }
833  if(!exists) { //path lookup
834  for (const QMakeLocalFileName &depdir : qAsConst(depdirs)) {
835  QMakeLocalFileName f(depdir.real() + Option::dir_sep + lfn.real());
836  QFileInfo fi(findFileInfo(f));
837  if(fi.exists() && !fi.isDir()) {
838  lfn = fixPathForFile(f);
839  exists = true;
840  break;
841  }
842  }
843  }
844  if(!exists) { //heuristic lookup
845  lfn = findFileForDep(QMakeLocalFileName(inc), file->file);
846  if((exists = !lfn.isNull()))
847  lfn = fixPathForFile(lfn);
848  }
849  } else {
850  exists = QFile::exists(lfn.real());
851  }
852  if (!lfn.isNull() && !isSystemInclude(lfn.real())) {
853  dep = files->lookupFile(lfn);
854  if(!dep) {
855  dep = new SourceFile;
856  dep->file = lfn;
858  files->addFile(dep);
859  /* QTBUG-72383: Local includes "foo.h" are keyed by the resolved
860  * path (stored in dep itself), only global includes <bar.h> are
861  * unique keys immediately. */
862  const char *key = try_local ? nullptr : inc;
863  includes->addFile(dep, key, false);
864  }
865  dep->exists = exists;
866  }
867  }
868  if(dep && dep->file != file->file) {
869  dep->included_count++;
870  if(dep->exists) {
871  debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(),
872  line_count, dep->file.local().toLatin1().constData());
873  file->deps->addChild(dep);
874  }
875  }
876  }
877  }
878  if(dependencyMode() == Recursive) { //done last because buffer is shared
879  for(int i = 0; i < file->deps->used_nodes; i++) {
880  if(!file->deps->children[i]->deps)
881  findDeps(file->deps->children[i]);
882  }
883  }
884  return true;
885 }
886 
887 static bool isCWordChar(char c) {
888  return c == '_'
889  || (c >= 'a' && c <= 'z')
890  || (c >= 'A' && c <= 'Z')
891  || (c >= '0' && c <= '9');
892 }
893 
894 bool QMakeSourceFileInfo::findMocs(SourceFile *file)
895 {
896  if(file->moc_checked)
897  return true;
898  files_changed = true;
899  file->moc_checked = true;
900 
901  int buffer_len = 0;
902  char *buffer = nullptr;
903  {
904  struct stat fst;
905  int fd;
906 #if defined(_MSC_VER) && _MSC_VER >= 1400
907  if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(),
908  _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0)
909  fd = -1;
910 #else
911  fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY);
912 #endif
913  if (fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode)) {
914  if (fd != -1)
915  QT_CLOSE(fd);
916  return false; //shouldn't happen
917  }
918  buffer = getBuffer(fst.st_size);
919  while (int have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len))
920  buffer_len += have_read;
921 
922  QT_CLOSE(fd);
923  }
924 
925  debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData());
926  int line_count = 1;
927  enum Keywords {
928  Q_OBJECT_Keyword,
929  Q_GADGET_Keyword,
930  Q_GADGET_EXPORT_Keyword,
931  Q_NAMESPACE_Keyword,
932  Q_NAMESPACE_EXPORT_Keyword,
933 
934  NumKeywords
935  };
936  static const char keywords[][19] = {
937  "Q_OBJECT",
938  "Q_GADGET",
939  "Q_GADGET_EXPORT",
940  "Q_NAMESPACE",
941  "Q_NAMESPACE_EXPORT",
942  };
943  static_assert(std::size(keywords) == NumKeywords);
944  bool ignore[NumKeywords] = {};
945  /* qmake ignore Q_GADGET */
946  /* qmake ignore Q_GADGET_EXPORT */
947  /* qmake ignore Q_OBJECT */
948  /* qmake ignore Q_NAMESPACE */
949  /* qmake ignore Q_NAMESPACE_EXPORT */
950  for(int x = 0; x < buffer_len; x++) {
951 #define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), &line_count)
952  x = SKIP_BSNL(x);
953  if (buffer[x] == '/') {
954  int extralines = 0;
955  int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &extralines);
956  if (buffer_len > y) {
957  // If comment, advance to the character that ends it:
958  if (buffer[y] == '/') { // C++-style comment
959  line_count += extralines;
960  x = y;
961  do {
962  x = SKIP_BSNL(x + 1);
963  } while (x < buffer_len && !qmake_endOfLine(buffer[x]));
964 
965  } else if (buffer[y] == '*') { // C-style comment
966  line_count += extralines;
967  x = SKIP_BSNL(y + 1);
968  for (; x < buffer_len; x = SKIP_BSNL(x + 1)) {
969  if (buffer[x] == 't' || buffer[x] == 'q') { // ignore
970  const char tag[] = "make ignore ";
971  const auto starts_with = [](const char *haystack, const char *needle) {
972  return strncmp(haystack, needle, strlen(needle)) == 0;
973  };
974  const auto is_ignore = [&](const char *keyword) {
975  return buffer_len >= int(x + strlen(tag) + strlen(keyword)) &&
976  starts_with(buffer + x + 1, tag) &&
977  starts_with(buffer + x + 1 + strlen(tag), keyword);
978  };
979  int interest = 0;
980  for (const char *keyword : keywords) {
981  if (is_ignore(keyword)){
982  debug_msg(2, "Mocgen: %s:%d Found \"q%s%s\"",
983  file->file.real().toLatin1().constData(), line_count,
984  tag, keyword);
985  x += static_cast<int>(strlen(tag));
986  x += static_cast<int>(strlen(keyword));
987  ignore[interest] = true;
988  }
989  ++interest;
990  }
991  } else if (buffer[x] == '*') {
992  extralines = 0;
993  y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &extralines);
994  if (buffer_len > y && buffer[y] == '/') {
995  line_count += extralines;
996  x = y;
997  break;
998  }
999  } else if (Option::debug_level && qmake_endOfLine(buffer[x])) {
1000  ++line_count;
1001  }
1002  }
1003  }
1004  // else: don't update x, buffer[x] is just the division operator.
1005  }
1006  } else if (buffer[x] == '\'' || buffer[x] == '"') {
1007  x = scanPastString(buffer, buffer_len, x, &line_count);
1008  // Leaves us on closing quote; for loop's x++ steps us past it.
1009  }
1010 
1011  if (x < buffer_len && Option::debug_level && qmake_endOfLine(buffer[x]))
1012  ++line_count;
1013  if (buffer_len > x + 8 && !isCWordChar(buffer[x])) {
1014  int morelines = 0;
1015  int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &morelines);
1016  if (buffer[y] == 'Q') {
1017  for (int interest = 0; interest < NumKeywords; ++interest) {
1018  if (ignore[interest])
1019  continue;
1020 
1021  int matchlen = 0, extralines = 0;
1022  size_t needle_len = strlen(keywords[interest]);
1023  Q_ASSERT(needle_len <= INT_MAX);
1024  if (matchWhileUnsplitting(buffer, buffer_len, y,
1025  keywords[interest],
1026  static_cast<int>(needle_len),
1027  &matchlen, &extralines)
1028  && y + matchlen < buffer_len
1029  && !isCWordChar(buffer[y + matchlen])) {
1030  if (Option::debug_level) {
1031  buffer[y + matchlen] = '\0';
1032  debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s",
1033  file->file.real().toLatin1().constData(),
1034  line_count + morelines, buffer + y);
1035  }
1036  file->mocable = true;
1037  return true;
1038  }
1039  }
1040  }
1041  }
1042 #undef SKIP_BSNL
1043  }
1044  return true;
1045 }
1046 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
#define QT_READ
#define QT_CLOSE
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
const char * constData() const noexcept
Definition: qbytearray.h:144
The QDir class provides access to directory structures and their contents.
Definition: qdir.h:55
static bool isAbsolutePath(const QString &path)
Definition: qdir.h:211
static bool isRelativePath(const QString &path)
Definition: qdir.cpp:2364
QString relativeFilePath(const QString &fileName) const
Definition: qdir.cpp:831
QString absoluteFilePath(const QString &fileName) const
Definition: qdir.cpp:796
bool exists() const
Definition: qfile.cpp:376
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:57
bool isDir() const
Definition: qfileinfo.cpp:1048
bool exists() const
Definition: qfileinfo.cpp:697
void append(parameter_type t)
Definition: qlist.h:469
const QString & local() const
bool isNull() const
Definition: makefiledeps.h:51
const QString & real() const
Definition: makefiledeps.h:52
QMakeLocalFileName()=default
virtual ~QMakeSourceFileInfo()
QStringList dependencies(const QString &file)
bool isSystemInclude(const QString &)
void addSourceFiles(const ProStringList &, uchar seek, SourceFileType type=TYPE_C)
virtual QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool forOpen=false)
virtual QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &)
int included(const QString &file)
void addSourceFile(const QString &, uchar seek, SourceFileType type=TYPE_C)
bool containsSourceFile(const QString &, SourceFileType type=TYPE_C)
void setDependencyPaths(const QList< QMakeLocalFileName > &)
bool mocable(const QString &file)
virtual QFileInfo findFileInfo(const QMakeLocalFileName &)
const QObjectList & children() const
Definition: qobject.h:206
The QString class provides a Unicode character string.
Definition: qstring.h:388
QByteArray toLatin1() const &
Definition: qstring.h:745
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:5092
bool isNull() const
Definition: qstring.h:1078
QByteArray toLocal8Bit() const &
Definition: qstring.h:753
The QStringList class provides a list of strings.
SourceFile * lookupFile(const QString &f)
struct SourceFiles::SourceFileNode ** nodes
SourceFile * lookupFile(const char *)
SourceFile * lookupFile(const QMakeLocalFileName &f)
void addFile(SourceFile *, const char *k=nullptr, bool own=true)
short next
Definition: keywords.cpp:454
#define SKIP_BSNL(pos)
#define qmake_endOfLine(c)
#define S_ISDIR(x)
QPainterPath node()
Definition: paths.cpp:574
void warn_msg(QMakeWarn type, const char *fmt,...)
Definition: option.cpp:565
int PRIV() strcmp(PCRE2_SPTR str1, PCRE2_SPTR str2)
int PRIV() strncmp(PCRE2_SPTR str1, PCRE2_SPTR str2, size_t len)
PCRE2_SIZE PRIV() strlen(PCRE2_SPTR str)
Q_CORE_EXPORT char * qstrdup(const char *)
#define Q_FALLTHROUGH()
QT_BEGIN_INCLUDE_NAMESPACE typedef unsigned char uchar
Definition: qglobal.h:332
unsigned int uint
Definition: qglobal.h:334
#define debug_msg
Definition: option.h:46
@ WarnLogic
Definition: option.h:51
GLenum type
Definition: qopengl.h:270
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat GLfloat f
GLenum GLuint buffer
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLuint64 GLenum GLint fd
GLuint name
GLfloat n
GLint y
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
Definition: qopenglext.h:12701
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLdouble s
[6]
Definition: qopenglext.h:235
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QByteArray ba
[0]
QFile file
[0]
file open(QIODevice::ReadOnly)
QFileInfo fi("c:/temp/foo")
[newstuff]
QObject::connect nullptr
QStringList files
[8]
static QString dir_sep
Definition: option.h:96
static int debug_level
Definition: option.h:169
static QString normalizePath(const QString &in, bool fix_env=true, bool canonical=true)
Definition: option.h:142
bool contains(const AT &t) const noexcept
Definition: qlist.h:78
SourceFile ** children
void addChild(SourceFile *s)
uint moc_checked
uint dep_checked
QMakeLocalFileName file
SourceDependChildren * deps
uchar included_count
QMakeSourceFileInfo::SourceFileType type
T clean(T &&t)
int fn(int &i)
XmlOutput::xml_output tag(const QString &name)
Definition: xmloutput.h:154