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.cpp441
1 files changed, 308 insertions, 133 deletions
diff --git a/qmake/generators/makefiledeps.cpp b/qmake/generators/makefiledeps.cpp
index 0f11745f4c..047d17f18a 100644
--- a/qmake/generators/makefiledeps.cpp
+++ b/qmake/generators/makefiledeps.cpp
@@ -388,6 +388,121 @@ QFileInfo QMakeSourceFileInfo::findFileInfo(const QMakeLocalFileName &dep)
return QFileInfo(dep.real());
}
+static int skipEscapedLineEnds(const char *buffer, int buffer_len, int offset, int *lines)
+{
+ // Join physical lines to make logical lines, as in the C preprocessor
+ while (offset + 1 < buffer_len
+ && buffer[offset] == '\\'
+ && qmake_endOfLine(buffer[offset + 1])) {
+ offset += 2;
+ ++*lines;
+ if (offset < buffer_len
+ && buffer[offset - 1] == '\r'
+ && buffer[offset] == '\n') // CRLF
+ offset++;
+ }
+ return offset;
+}
+
+static bool matchWhileUnsplitting(const char *buffer, int buffer_len, int start,
+ const char *needle, int needle_len,
+ int *matchlen, int *lines)
+{
+ int x = start;
+ for (int n = 0; n < needle_len && x < buffer_len;
+ n++, x = skipEscapedLineEnds(buffer, buffer_len, x + 1, lines)) {
+ if (buffer[x] != needle[n])
+ return false;
+ }
+ // That also skipped any remaining BSNLs immediately after the match.
+
+ // Tell caller how long the match was:
+ *matchlen = x - start;
+
+ return true;
+}
+
+/* Advance from an opening quote at buffer[offset] to the matching close quote. */
+static int scanPastString(char *buffer, int buffer_len, int offset, int *lines)
+{
+ // It might be a C++11 raw string.
+ bool israw = false;
+ if (buffer[offset] == '"' && offset > 0) {
+ int explore = offset - 1;
+ while (explore > 0 && buffer[explore] != 'R') {
+ if (buffer[explore] == '8' || buffer[explore] == 'u' || buffer[explore] == 'U') {
+ explore--;
+ } else if (explore > 1 && qmake_endOfLine(buffer[explore])
+ && buffer[explore - 1] == '\\') {
+ explore -= 2;
+ } else if (explore > 2 && buffer[explore] == '\n'
+ && buffer[explore - 1] == '\r'
+ && buffer[explore - 2] == '\\') {
+ explore -= 3;
+ } else {
+ break;
+ }
+ }
+ israw = (buffer[explore] == 'R');
+ }
+
+ if (israw) {
+#define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), lines)
+
+ offset = SKIP_BSNL(offset + 1);
+ const char *const delim = buffer + offset;
+ int clean = offset;
+ while (offset < buffer_len && buffer[offset] != '(') {
+ if (clean < offset)
+ buffer[clean++] = buffer[offset];
+ else
+ clean++;
+
+ offset = SKIP_BSNL(offset + 1);
+ }
+ /*
+ Not checking correctness (trust real compiler to do that):
+ - no controls, spaces, '(', ')', '\\' or (presumably) '"' in delim;
+ - at most 16 bytes in delim
+
+ Raw strings are surely defined after phase 2, when BSNLs are resolved;
+ so the delimiter's exclusion of '\\' and space (including newlines)
+ applies too late to save us the need to cope with BSNLs in it.
+ */
+
+ const int delimlen = buffer + clean - delim;
+ int matchlen = delimlen, extralines = 0;
+ while ((offset = SKIP_BSNL(offset + 1)) < buffer_len
+ && (buffer[offset] != ')'
+ || (delimlen > 0 &&
+ !matchWhileUnsplitting(buffer, buffer_len,
+ offset + 1, delim, delimlen,
+ &matchlen, &extralines))
+ || buffer[offset + 1 + matchlen] != '"')) {
+ // skip, but keep track of lines
+ if (qmake_endOfLine(buffer[offset]))
+ ++*lines;
+ extralines = 0;
+ }
+ *lines += extralines; // from the match
+ // buffer[offset] is ')'
+ offset += 1 + matchlen; // 1 for ')', then delim
+ // buffer[offset] is '"'
+
+#undef SKIP_BSNL
+ } else { // Traditional string or char literal:
+ const char term = buffer[offset];
+ while (++offset < buffer_len && buffer[offset] != term) {
+ if (buffer[offset] == '\\')
+ ++offset;
+ else if (qmake_endOfLine(buffer[offset]))
+ ++*lines;
+ }
+ }
+
+ return offset;
+}
+
bool QMakeSourceFileInfo::findDeps(SourceFile *file)
{
if(file->dep_checked || file->type == TYPE_UNKNOWN)
@@ -426,6 +541,18 @@ bool QMakeSourceFileInfo::findDeps(SourceFile *file)
file->deps = new SourceDependChildren;
int line_count = 1;
+ enum {
+ /*
+ States of C preprocessing (for TYPE_C only), after backslash-newline
+ elimination and skipping comments and spaces (i.e. in ANSI X3.159-1989
+ section 2.1.1.2's phase 4). We're about to study buffer[x] to decide
+ on which transition to do.
+ */
+ AtStart, // start of logical line; a # may start a preprocessor directive
+ HadHash, // saw a # at start, looking for preprocessor keyword
+ WantName, // saw #include or #import, waiting for name
+ InCode // after directive, parsing non-#include directive or in actual code
+ } cpp_state = AtStart;
for(int x = 0; x < buffer_len; ++x) {
bool try_local = true;
@@ -505,118 +632,164 @@ bool QMakeSourceFileInfo::findDeps(SourceFile *file)
++line_count;
} else if(file->type == QMakeSourceFileInfo::TYPE_QRC) {
} else if(file->type == QMakeSourceFileInfo::TYPE_C) {
- for(int beginning=1; x < buffer_len; ++x) {
+ // We've studied all buffer[i] for i < x
+ for (; x < buffer_len; ++x) {
+ // How to handle backslash-newline (BSNL) pairs:
+#define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), &line_count)
+
// Seek code or directive, skipping comments and space:
for(; x < buffer_len; ++x) {
+ x = SKIP_BSNL(x);
if (buffer[x] == ' ' || buffer[x] == '\t') {
// keep going
- } else if (buffer[x] == '/' && x + 1 < buffer_len &&
- (buffer[x + 1] == '/' || buffer[x + 1] == '*')) {
- ++x;
- if (buffer[x] == '/') { // C++-style comment
- for (; x < buffer_len && !qmake_endOfLine(buffer[x]); ++x) {} // skip
- beginning = 1;
- } else { // C-style comment
+ } else if (buffer[x] == '/') {
+ int extralines = 0;
+ int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &extralines);
+ if (buffer[y] == '/') { // C++-style comment
+ line_count += extralines;
+ x = SKIP_BSNL(y + 1);
+ while (x < buffer_len && !qmake_endOfLine(buffer[x]))
+ x = SKIP_BSNL(x + 1); // skip
+
+ cpp_state = AtStart;
+ ++line_count;
+ } else if (buffer[y] == '*') { // C-style comment
+ line_count += extralines;
+ x = y;
while (++x < buffer_len) {
+ x = SKIP_BSNL(x);
if (buffer[x] == '*') {
- if (x + 1 < buffer_len && buffer[x + 1] == '/') {
- ++x; // skip '*'; for loop skips '/'.
+ extralines = 0;
+ y = skipEscapedLineEnds(buffer, buffer_len,
+ x + 1, &extralines);
+ if (y < buffer_len && buffer[y] == '/') {
+ line_count += extralines;
+ x = y; // for loop shall step past this
break;
}
} else if (qmake_endOfLine(buffer[x])) {
++line_count;
}
}
+ } else {
+ // buffer[x] is the division operator
+ break;
}
} else if (qmake_endOfLine(buffer[x])) {
++line_count;
- beginning = 1;
+ cpp_state = AtStart;
} else {
+ /* Drop out of phases 1, 2, 3, into phase 4 */
break;
}
}
+ // Phase 4 study of buffer[x]:
if(x >= buffer_len)
break;
- // preprocessor directive
- if (beginning && buffer[x] == '#') {
- // Advance to start of preprocessing directive
- while (++x < buffer_len
- && (buffer[x] == ' ' || buffer[x] == '\t')) {} // skip
-
- if (qmake_endOfLine(buffer[x])) {
- ++line_count;
- beginning = 1;
- continue;
+ switch (cpp_state) {
+ case HadHash:
+ {
+ // Read keyword; buffer[x] starts first preprocessing token after #
+ const char *const keyword = buffer + x;
+ int clean = x;
+ while (x < buffer_len && buffer[x] >= 'a' && buffer[x] <= 'z') {
+ // skip over keyword, consolidating it if it contains BSNLs
+ // (see WantName's similar code consolidating inc, below)
+ if (clean < x)
+ buffer[clean++] = buffer[x];
+ else
+ clean++;
+
+ x = SKIP_BSNL(x + 1);
}
+ const int keyword_len = buffer + clean - keyword;
+ x--; // Still need to study buffer[x] next time round for loop.
+
+ cpp_state =
+ ((keyword_len == 7 && !strncmp(keyword, "include", 7)) // C & Obj-C
+ || (keyword_len == 6 && !strncmp(keyword, "import", 6))) // Obj-C
+ ? WantName : InCode;
break;
}
- // quoted strings
- if (buffer[x] == '\'' || buffer[x] == '"') {
- const char term = buffer[x];
- while (++x < buffer_len) {
- if (buffer[x] == term) {
- ++x;
- break;
- } else if (buffer[x] == '\\') {
- ++x;
- } else if (qmake_endOfLine(buffer[x])) {
+ case WantName:
+ {
+ char term = buffer[x];
+ if (term == '<') {
+ try_local = false;
+ term = '>';
+ } else if (term != '"') {
+ /*
+ Possibly malformed, but this may be something like:
+ #include IDENTIFIER
+ which does work, if #define IDENTIFIER "filename" is
+ in effect. This is beyond this noddy preprocessor's
+ powers of tracking. So give up and resume searching
+ for a directive. We haven't made sense of buffer[x],
+ so back up to ensure we do study it (now as code) next
+ time round the loop.
+ */
+ x--;
+ cpp_state = InCode;
+ continue;
+ }
+
+ x = SKIP_BSNL(x + 1);
+ inc = buffer + x;
+ int clean = x; // offset if we need to clear \-newlines
+ for (; x < buffer_len && buffer[x] != term; x = SKIP_BSNL(x + 1)) {
+ if (qmake_endOfLine(buffer[x])) { // malformed
+ cpp_state = AtStart;
++line_count;
+ break;
}
+
+ /*
+ If we do skip any BSNLs, we need to consolidate the
+ surviving text by copying to lower indices. For that
+ to be possible, we also have to keep 'clean' advanced
+ in step with x even when we've yet to see any BSNLs.
+ */
+ if (clean < x)
+ buffer[clean++] = buffer[x];
+ else
+ clean++;
}
- }
- beginning = 0;
- }
- if(x >= buffer_len)
- break;
+ if (cpp_state == WantName)
+ buffer[clean] = '\0';
+ else // i.e. malformed
+ inc = 0;
- // Got a preprocessor directive
- const char *const keyword = buffer + x;
- for (;
- x < buffer_len && buffer[x] >= 'a' && buffer[x] <= 'z';
- x++) {} // skip over identifier
- int keyword_len = buffer + x - keyword;
- for (;
- x < buffer_len && (buffer[x] == ' ' || buffer[x] == '\t');
- x++) {} // skip spaces after keyword
-
- /* Keyword with nothing after it, e.g. #endif: not interesting. */
- if (qmake_endOfLine(buffer[x]))
- keyword_len = 0;
-
- if((keyword_len == 7 && !strncmp(keyword, "include", 7)) // C & Obj-C
- || (keyword_len == 6 && !strncmp(keyword, "import", 6))) { // Obj-C
- char term = buffer[x];
- if(term == '<') {
- try_local = false;
- term = '>';
- } else if(term != '"') { //wtf?
- continue;
+ cpp_state = InCode; // hereafter
+ break;
}
- x++;
- inc = buffer + x;
- for (;
- buffer[x] != term && !qmake_endOfLine(buffer[x]);
- ++x) {} // skip until end of include name
- buffer[x] = '\0';
- } else if (buffer[x] == '\'' || buffer[x] == '"') {
- const char term = buffer[x++];
- while(x < buffer_len) {
- if (buffer[x] == term)
+
+ case AtStart:
+ // Preprocessor directive?
+ if (buffer[x] == '#') {
+ cpp_state = HadHash;
break;
- if (buffer[x] == '\\') {
- x+=2;
- } else {
- if (qmake_endOfLine(buffer[x]))
- ++line_count;
- ++x;
}
+ cpp_state = InCode;
+ // ... and fall through to handle buffer[x] as such.
+ case InCode:
+ // matching quotes (string literals and character literals)
+ if (buffer[x] == '\'' || buffer[x] == '"') {
+ x = scanPastString(buffer, buffer_len, x, &line_count);
+ // for loop's ++x shall step over the closing quote.
+ }
+ // else: buffer[x] is just some code; move on.
+ break;
}
- } else {
- --x;
+
+ if (inc) // We were in WantName and found a name.
+ break;
+#undef SKIP_BSNL
}
+ if(x >= buffer_len)
+ break;
}
if(inc) {
@@ -699,7 +872,7 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file)
files_changed = true;
file->moc_checked = true;
- int buffer_len;
+ int buffer_len = 0;
char *buffer = 0;
{
struct stat fst;
@@ -717,42 +890,56 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file)
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) ;
+ while (int 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;
+ bool ignore[2] = { false, false }; // [0] for Q_OBJECT, [1] for Q_GADGET
/* qmake ignore Q_GADGET */
/* qmake ignore Q_OBJECT */
for(int x = 0; x < buffer_len; x++) {
+#define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), &line_count)
+ x = SKIP_BSNL(x);
if (buffer[x] == '/') {
- ++x;
- if(buffer_len >= x) {
- if (buffer[x] == '/') { // C++-style comment
- for (; x < buffer_len && !qmake_endOfLine(buffer[x]); ++x) {} // skip
- } else if (buffer[x] == '*') { // C-style comment
- for(++x; x < buffer_len; ++x) {
+ int extralines = 0;
+ int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &extralines);
+ if (buffer_len > y) {
+ // If comment, advance to the character that ends it:
+ if (buffer[y] == '/') { // C++-style comment
+ line_count += extralines;
+ x = y;
+ do {
+ x = SKIP_BSNL(x + 1);
+ } while (x < buffer_len && !qmake_endOfLine(buffer[x]));
+
+ } else if (buffer[y] == '*') { // C-style comment
+ line_count += extralines;
+ x = SKIP_BSNL(y + 1);
+ for (; x < buffer_len; x = SKIP_BSNL(x + 1)) {
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;
+ ignore[0] = 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;
+ ignore[1] = true;
}
} else if (buffer[x] == '*') {
- if (buffer_len >= x + 1 && buffer[x + 1] == '/') {
- ++x;
+ extralines = 0;
+ y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &extralines);
+ if (buffer_len > y && buffer[y] == '/') {
+ line_count += extralines;
+ x = y;
break;
}
} else if (Option::debug_level && qmake_endOfLine(buffer[x])) {
@@ -760,56 +947,44 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file)
}
}
}
+ // else: don't update x, buffer[x] is just the division operator.
}
} 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;
- }
- }
+ x = scanPastString(buffer, buffer_len, x, &line_count);
+ // Leaves us on closing quote; for loop's x++ steps us past it.
}
- if (Option::debug_level && qmake_endOfLine(buffer[x]))
+
+ if (x < buffer_len && Option::debug_level && qmake_endOfLine(buffer[x]))
++line_count;
- if (buffer_len > x + 2 && buffer[x + 1] == 'Q' &&
- buffer[x + 2] == '_' && !isCWordChar(buffer[x])) {
- ++x;
- int match = 0;
- static const char *interesting[] = { "OBJECT", "GADGET" };
- for (int interest = 0, m1, m2; interest < 2; ++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;
+ if (buffer_len > x + 8 && !isCWordChar(buffer[x])) {
+ int morelines = 0;
+ int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &morelines);
+ if (buffer[y] == 'Q') {
+ static const char interesting[][9] = { "Q_OBJECT", "Q_GADGET" };
+ for (int interest = 0; interest < 2; ++interest) {
+ if (ignore[interest])
+ continue;
+
+ int matchlen = 0, extralines = 0;
+ if (matchWhileUnsplitting(buffer, buffer_len, y,
+ interesting[interest],
+ strlen(interesting[interest]),
+ &matchlen, &extralines)
+ && y + matchlen < buffer_len
+ && !isCWordChar(buffer[y + matchlen])) {
+ if (Option::debug_level) {
+ buffer[y + matchlen] = '\0';
+ debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s",
+ file->file.real().toLatin1().constData(),
+ line_count + morelines, buffer + y);
+ }
+ file->mocable = true;
+ return true;
}
- ++m2;
- }
- if(m1 == m2) {
- match = m2 + 2;
- break;
- }
- }
- if (match && !isCWordChar(buffer[x + match])) {
- 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;
}
}
+#undef SKIP_BSNL
}
return true;
}