Blame SOURCES/moc-get-the-system-defines-from-the-compiler-itself.patch

2d2b1a
From fa0d02eedcacc22db1026b902801b29176755362 Mon Sep 17 00:00:00 2001
2d2b1a
From: Thiago Macieira <thiago.macieira@intel.com>
2d2b1a
Date: Fri, 21 Aug 2015 17:08:19 -0700
2d2b1a
Subject: [PATCH] moc: get the system #defines from the compiler itself
2d2b1a
2d2b1a
In order for moc to properly parse #ifdefs and family, we've had
2d2b1a
QMAKE_COMPILER_DEFINES as a list of pre-defined macros from the
2d2b1a
compiler. That list is woefully incomplete.
2d2b1a
2d2b1a
Instead, let's simply ask the compiler for the list. With GCC and
2d2b1a
family, we use the -dM flag while preprocessing. With ICC on Windows,
2d2b1a
the flag gains an extra "Q" but is otherwise the same. For MSVC, it
2d2b1a
requires using some undocumented switches and parsing environment
2d2b1a
variables (I've tested MSVC 2012, 2013 and 2015).
2d2b1a
2d2b1a
The new moc option is called --include to be similar to GCC's -include
2d2b1a
option. It does more than just parse a list of pre-defined macros and
2d2b1a
can be used to insert any sort of code that moc needs to parse prior to
2d2b1a
the main file.
2d2b1a
2d2b1a
Change-Id: I7de033f80b0e4431b7f1ffff13fca02dbb60a0a6
2d2b1a
---
2d2b1a
 mkspecs/features/moc.prf                   | 31 +++++++++++++--
2d2b1a
 qmake/main.cpp                             | 38 +++++++++++++++++++
2d2b1a
 src/tools/moc/main.cpp                     | 16 +++++++-
2d2b1a
 src/tools/moc/preprocessor.cpp             | 60 +++++++++++++++++-------------
2d2b1a
 src/tools/moc/preprocessor.h               |  1 +
2d2b1a
 tests/auto/tools/moc/subdir/extradefines.h |  1 +
2d2b1a
 tests/auto/tools/moc/tst_moc.cpp           | 42 +++++++++++++++++++++
2d2b1a
 7 files changed, 158 insertions(+), 31 deletions(-)
2d2b1a
 create mode 100644 tests/auto/tools/moc/subdir/extradefines.h
2d2b1a
2d2b1a
diff --git a/mkspecs/features/moc.prf b/mkspecs/features/moc.prf
2d2b1a
index c0b5682..af885c3 100644
2d2b1a
--- a/mkspecs/features/moc.prf
2d2b1a
+++ b/mkspecs/features/moc.prf
2d2b1a
@@ -24,8 +24,25 @@ win32:count(MOC_INCLUDEPATH, 40, >) {
2d2b1a
     write_file($$absolute_path($$WIN_INCLUDETEMP, $$OUT_PWD), WIN_INCLUDETEMP_CONT)|error("Aborting.")
2d2b1a
 }
2d2b1a
 
2d2b1a
+# QNX's compiler sets "gcc" config, but does not support the -dM option;
2d2b1a
+# iOS builds are multi-arch, so this feature cannot possibly work.
2d2b1a
+if(gcc|intel_icl|msvc):!rim_qcc:!ios {
2d2b1a
+    moc_predefs.CONFIG = no_link
2d2b1a
+    gcc:            moc_predefs.commands = $$QMAKE_CXX $$QMAKE_CXXFLAGS -dM -E -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
2d2b1a
+    else:intel_icl: moc_predefs.commands = $$QMAKE_CXX $$QMAKE_CXXFLAGS -QdM -P -Fi${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
2d2b1a
+    else:msvc {
2d2b1a
+        # make sure that our bin dir is first in path, so qmake is found
2d2b1a
+        moc_predefs.commands = PATH $$shell_path($$[QT_INSTALL_BINS/src]);%PATH%&
2d2b1a
+        moc_predefs.commands += $$QMAKE_CXX -Bxqmake $$QMAKE_CXXFLAGS -E ${QMAKE_FILE_IN} 2>NUL >${QMAKE_FILE_OUT}
2d2b1a
+    } else:         error("Oops, I messed up")
2d2b1a
+    moc_predefs.output = $$MOC_DIR/moc_predefs.h
2d2b1a
+    moc_predefs.input = MOC_PREDEF_FILE
2d2b1a
+    silent: moc_predefs.commands = @echo generating $$moc_predefs.output$$escape_expand(\n\t)@$$moc_predefs.commands
2d2b1a
+    QMAKE_EXTRA_COMPILERS += moc_predefs
2d2b1a
+    MOC_PREDEF_FILE = $$[QT_HOST_DATA/src]/mkspecs/features/data/dummy.cpp
2d2b1a
+}
2d2b1a
+
2d2b1a
 defineReplace(mocCmdBase) {
2d2b1a
-    RET =
2d2b1a
     !isEmpty(WIN_INCLUDETEMP) {
2d2b1a
         incvar = @$$WIN_INCLUDETEMP
2d2b1a
     } else {
2d2b1a
@@ -34,7 +51,13 @@ defineReplace(mocCmdBase) {
2d2b1a
             incvar += -I$$shell_quote($$inc)
2d2b1a
         incvar += $$QMAKE_FRAMEWORKPATH_FLAGS
2d2b1a
     }
2d2b1a
-    RET += $$QMAKE_MOC $(DEFINES) $$join(QMAKE_COMPILER_DEFINES, " -D", -D) $$incvar $$QMAKE_MOC_OPTIONS
2d2b1a
+
2d2b1a
+    RET = $$QMAKE_MOC $(DEFINES)
2d2b1a
+
2d2b1a
+    isEmpty(MOC_PREDEF_FILE): RET += $$join(QMAKE_COMPILER_DEFINES, " -D", -D)
2d2b1a
+    else: RET += --include $$moc_predefs.output
2d2b1a
+
2d2b1a
+    RET += $$incvar $$QMAKE_MOC_OPTIONS
2d2b1a
     return($$RET)
2d2b1a
 }
2d2b1a
 
2d2b1a
@@ -46,7 +69,7 @@ moc_header.output = $$MOC_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAK
2d2b1a
 moc_header.input = HEADERS
2d2b1a
 moc_header.variable_out = SOURCES
2d2b1a
 moc_header.name = MOC ${QMAKE_FILE_IN}
2d2b1a
-moc_header.depends += $$WIN_INCLUDETEMP
2d2b1a
+moc_header.depends += $$WIN_INCLUDETEMP $$moc_predefs.output
2d2b1a
 silent:moc_header.commands = @echo moc ${QMAKE_FILE_IN} && $$moc_header.commands
2d2b1a
 QMAKE_EXTRA_COMPILERS += moc_header
2d2b1a
 INCREDIBUILD_XGE += moc_header
2d2b1a
@@ -58,7 +81,7 @@ moc_source.commands = ${QMAKE_FUNC_mocCmdBase} ${QMAKE_FILE_IN} -o ${QMAKE_FILE_
2d2b1a
 moc_source.output = $$MOC_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC}
2d2b1a
 moc_source.input = SOURCES OBJECTIVE_SOURCES
2d2b1a
 moc_source.name = MOC ${QMAKE_FILE_IN}
2d2b1a
-moc_source.depends += $$WIN_INCLUDETEMP
2d2b1a
+moc_source.depends += $$WIN_INCLUDETEMP $$moc_predefs.output
2d2b1a
 silent:moc_source.commands = @echo moc ${QMAKE_FILE_IN} && $$moc_source.commands
2d2b1a
 QMAKE_EXTRA_COMPILERS += moc_source
2d2b1a
 INCREDIBUILD_XGE += moc_source
2d2b1a
diff --git a/qmake/main.cpp b/qmake/main.cpp
2d2b1a
index bde537d..e9b8bde 100644
2d2b1a
--- a/qmake/main.cpp
2d2b1a
+++ b/qmake/main.cpp
2d2b1a
@@ -1,6 +1,7 @@
2d2b1a
 /****************************************************************************
2d2b1a
 **
2d2b1a
 ** Copyright (C) 2015 The Qt Company Ltd.
2d2b1a
+** Copyright (C) 2015 Intel Corporation.
2d2b1a
 ** Contact: http://www.qt.io/licensing/
2d2b1a
 **
2d2b1a
 ** This file is part of the qmake application of the Qt Toolkit.
2d2b1a
@@ -47,6 +48,10 @@
2d2b1a
 #include <sys/types.h>
2d2b1a
 #include <sys/stat.h>
2d2b1a
 
2d2b1a
+#ifdef Q_OS_WIN
2d2b1a
+#  include <qt_windows.h>
2d2b1a
+#endif
2d2b1a
+
2d2b1a
 QT_BEGIN_NAMESPACE
2d2b1a
 
2d2b1a
 #ifdef Q_OS_WIN
2d2b1a
@@ -246,6 +251,30 @@ static int doInstall(int argc, char **argv)
2d2b1a
     return 3;
2d2b1a
 }
2d2b1a
 
2d2b1a
+static int dumpMacros(const wchar_t *cmdline)
2d2b1a
+{
2d2b1a
+    // from http://stackoverflow.com/questions/3665537/how-to-find-out-cl-exes-built-in-macros
2d2b1a
+    int argc;
2d2b1a
+    wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
2d2b1a
+    if (!argv)
2d2b1a
+        return 2;
2d2b1a
+    for (int i = 0; i < argc; ++i) {
2d2b1a
+        if (argv[i][0] != L'-' || argv[i][1] != 'D')
2d2b1a
+            continue;
2d2b1a
+
2d2b1a
+        wchar_t *value = wcschr(argv[i], L'=');
2d2b1a
+        if (value) {
2d2b1a
+            *value = 0;
2d2b1a
+            ++value;
2d2b1a
+        } else {
2d2b1a
+            // point to the NUL at the end, so we don't print anything
2d2b1a
+            value = argv[i] + wcslen(argv[i]);
2d2b1a
+        }
2d2b1a
+        wprintf(L"#define %Ls %Ls\n", argv[i] + 2, value);
2d2b1a
+    }
2d2b1a
+    return 0;
2d2b1a
+}
2d2b1a
+
2d2b1a
 #endif // Q_OS_WIN
2d2b1a
 
2d2b1a
 /* This is to work around lame implementation on Darwin. It has been noted that the getpwd(3) function
2d2b1a
@@ -280,6 +309,15 @@ int runQMake(int argc, char **argv)
2d2b1a
     // Workaround for inferior/missing command line tools on Windows: make our own!
2d2b1a
     if (argc >= 2 && !strcmp(argv[1], "-install"))
2d2b1a
         return doInstall(argc - 2, argv + 2);
2d2b1a
+
2d2b1a
+    {
2d2b1a
+        // Support running as Visual C++'s compiler
2d2b1a
+        const wchar_t *cmdline = _wgetenv(L"MSC_CMD_FLAGS");
2d2b1a
+        if (!cmdline || !*cmdline)
2d2b1a
+            cmdline = _wgetenv(L"MSC_IDE_FLAGS");
2d2b1a
+        if (cmdline && *cmdline)
2d2b1a
+            return dumpMacros(cmdline);
2d2b1a
+    }
2d2b1a
 #endif
2d2b1a
 
2d2b1a
     QMakeVfs vfs;
2d2b1a
diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp
2d2b1a
index a5cbad7..d06335d 100644
2d2b1a
--- a/src/tools/moc/main.cpp
2d2b1a
+++ b/src/tools/moc/main.cpp
2d2b1a
@@ -259,6 +259,11 @@ int runMoc(int argc, char **argv)
2d2b1a
     prependIncludeOption.setValueName(QStringLiteral("file"));
2d2b1a
     parser.addOption(prependIncludeOption);
2d2b1a
 
2d2b1a
+    QCommandLineOption includeOption(QStringLiteral("include"));
2d2b1a
+    includeOption.setDescription(QStringLiteral("Parse <file> as an #include before the main source(s)."));
2d2b1a
+    includeOption.setValueName(QStringLiteral("file"));
2d2b1a
+    parser.addOption(includeOption);
2d2b1a
+
2d2b1a
     QCommandLineOption noNotesWarningsCompatOption(QStringLiteral("n"));
2d2b1a
     noNotesWarningsCompatOption.setDescription(QStringLiteral("Do not display notes (-nn) or warnings (-nw). Compatibility option."));
2d2b1a
     noNotesWarningsCompatOption.setValueName(QStringLiteral("which"));
2d2b1a
@@ -406,7 +411,16 @@ int runMoc(int argc, char **argv)
2d2b1a
     moc.includes = pp.includes;
2d2b1a
 
2d2b1a
     // 1. preprocess
2d2b1a
-    moc.symbols = pp.preprocessed(moc.filename, &in);
2d2b1a
+    foreach (const QString &includeName, parser.values(includeOption)) {
2d2b1a
+        QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename);
2d2b1a
+        QFile f(QFile::decodeName(rawName));
2d2b1a
+        if (f.open(QIODevice::ReadOnly)) {
2d2b1a
+            moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName);
2d2b1a
+            moc.symbols += pp.preprocessed(rawName, &f);
2d2b1a
+            moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName);
2d2b1a
+        }
2d2b1a
+    }
2d2b1a
+    moc.symbols += pp.preprocessed(moc.filename, &in);
2d2b1a
 
2d2b1a
     if (!pp.preprocessOnly) {
2d2b1a
         // 2. parse
2d2b1a
diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp
2d2b1a
index d036c40..70cf14a 100644
2d2b1a
--- a/src/tools/moc/preprocessor.cpp
2d2b1a
+++ b/src/tools/moc/preprocessor.cpp
2d2b1a
@@ -1001,6 +1001,37 @@ static void mergeStringLiterals(Symbols *_symbols)
2d2b1a
     }
2d2b1a
 }
2d2b1a
 
2d2b1a
+QByteArray Preprocessor::resolveInclude(const QByteArray &include, const QByteArray &relativeTo)
2d2b1a
+{
2d2b1a
+    // #### stringery
2d2b1a
+    QFileInfo fi;
2d2b1a
+    if (!relativeTo.isEmpty())
2d2b1a
+        fi.setFile(QFileInfo(QString::fromLocal8Bit(relativeTo.constData())).dir(), QString::fromLocal8Bit(include.constData()));
2d2b1a
+    for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) {
2d2b1a
+        const IncludePath &p = Preprocessor::includes.at(j);
2d2b1a
+        if (p.isFrameworkPath) {
2d2b1a
+            const int slashPos = include.indexOf('/');
2d2b1a
+            if (slashPos == -1)
2d2b1a
+                continue;
2d2b1a
+            QByteArray frameworkCandidate = include.left(slashPos);
2d2b1a
+            frameworkCandidate.append(".framework/Headers/");
2d2b1a
+            fi.setFile(QString::fromLocal8Bit(QByteArray(p.path + '/' + frameworkCandidate).constData()), QString::fromLocal8Bit(include.mid(slashPos + 1).constData()));
2d2b1a
+        } else {
2d2b1a
+            fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(include.constData()));
2d2b1a
+        }
2d2b1a
+        // try again, maybe there's a file later in the include paths with the same name
2d2b1a
+        // (186067)
2d2b1a
+        if (fi.isDir()) {
2d2b1a
+            fi = QFileInfo();
2d2b1a
+            continue;
2d2b1a
+        }
2d2b1a
+    }
2d2b1a
+
2d2b1a
+    if (!fi.exists() || fi.isDir())
2d2b1a
+        return QByteArray();
2d2b1a
+    return fi.canonicalFilePath().toLocal8Bit();
2d2b1a
+}
2d2b1a
+
2d2b1a
 void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
2d2b1a
 {
2d2b1a
     currentFilenames.push(filename);
2d2b1a
@@ -1021,33 +1052,9 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
2d2b1a
                 continue;
2d2b1a
             until(PP_NEWLINE);
2d2b1a
 
2d2b1a
-            // #### stringery
2d2b1a
-            QFileInfo fi;
2d2b1a
-            if (local)
2d2b1a
-                fi.setFile(QFileInfo(QString::fromLocal8Bit(filename.constData())).dir(), QString::fromLocal8Bit(include.constData()));
2d2b1a
-            for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) {
2d2b1a
-                const IncludePath &p = Preprocessor::includes.at(j);
2d2b1a
-                if (p.isFrameworkPath) {
2d2b1a
-                    const int slashPos = include.indexOf('/');
2d2b1a
-                    if (slashPos == -1)
2d2b1a
-                        continue;
2d2b1a
-                    QByteArray frameworkCandidate = include.left(slashPos);
2d2b1a
-                    frameworkCandidate.append(".framework/Headers/");
2d2b1a
-                    fi.setFile(QString::fromLocal8Bit(QByteArray(p.path + '/' + frameworkCandidate).constData()), QString::fromLocal8Bit(include.mid(slashPos + 1).constData()));
2d2b1a
-                } else {
2d2b1a
-                    fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(include.constData()));
2d2b1a
-                }
2d2b1a
-                // try again, maybe there's a file later in the include paths with the same name
2d2b1a
-                // (186067)
2d2b1a
-                if (fi.isDir()) {
2d2b1a
-                    fi = QFileInfo();
2d2b1a
-                    continue;
2d2b1a
-                }
2d2b1a
-            }
2d2b1a
-
2d2b1a
-            if (!fi.exists() || fi.isDir())
2d2b1a
+            include = resolveInclude(include, local ? filename : QByteArray());
2d2b1a
+            if (include.isNull())
2d2b1a
                 continue;
2d2b1a
-            include = fi.canonicalFilePath().toLocal8Bit();
2d2b1a
 
2d2b1a
             if (Preprocessor::preprocessedIncludes.contains(include))
2d2b1a
                 continue;
2d2b1a
@@ -1202,6 +1209,7 @@ Symbols Preprocessor::preprocessed(const QByteArray &filename, QFile *file)
2d2b1a
     input = cleaned(input);
2d2b1a
 
2d2b1a
     // phase 2: tokenize for the preprocessor
2d2b1a
+    index = 0;
2d2b1a
     symbols = tokenize(input);
2d2b1a
 
2d2b1a
 #if 0
2d2b1a
diff --git a/src/tools/moc/preprocessor.h b/src/tools/moc/preprocessor.h
2d2b1a
index 9c81f86..d876caf 100644
2d2b1a
--- a/src/tools/moc/preprocessor.h
2d2b1a
+++ b/src/tools/moc/preprocessor.h
2d2b1a
@@ -67,6 +67,7 @@ public:
2d2b1a
     QList<QByteArray> frameworks;
2d2b1a
     QSet<QByteArray> preprocessedIncludes;
2d2b1a
     Macros macros;
2d2b1a
+    QByteArray resolveInclude(const QByteArray &filename, const QByteArray &relativeTo);
2d2b1a
     Symbols preprocessed(const QByteArray &filename, QFile *device);
2d2b1a
 
2d2b1a
     void parseDefineArguments(Macro *m);
2d2b1a
diff --git a/tests/auto/tools/moc/subdir/extradefines.h b/tests/auto/tools/moc/subdir/extradefines.h
2d2b1a
new file mode 100644
2d2b1a
index 0000000..e7888ce
2d2b1a
--- /dev/null
2d2b1a
+++ b/tests/auto/tools/moc/subdir/extradefines.h
2d2b1a
@@ -0,0 +1 @@
2d2b1a
+#define FOO     1
2d2b1a
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
2d2b1a
index fa1b68b..1d6a911 100644
2d2b1a
--- a/tests/auto/tools/moc/tst_moc.cpp
2d2b1a
+++ b/tests/auto/tools/moc/tst_moc.cpp
2d2b1a
@@ -552,6 +552,8 @@ private slots:
2d2b1a
     void frameworkSearchPath();
2d2b1a
     void cstyleEnums();
2d2b1a
     void defineMacroViaCmdline();
2d2b1a
+    void defineMacroViaForcedInclude();
2d2b1a
+    void defineMacroViaForcedIncludeRelative();
2d2b1a
     void specifyMetaTagsFromCmdline();
2d2b1a
     void invokable();
2d2b1a
     void singleFunctionKeywordSignalAndSlot();
2d2b1a
@@ -1219,6 +1221,46 @@ void tst_Moc::defineMacroViaCmdline()
2d2b1a
 #endif
2d2b1a
 }
2d2b1a
 
2d2b1a
+void tst_Moc::defineMacroViaForcedInclude()
2d2b1a
+{
2d2b1a
+#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
2d2b1a
+    QProcess proc;
2d2b1a
+
2d2b1a
+    QStringList args;
2d2b1a
+    args << "--include" << m_sourceDirectory + QLatin1String("/subdir/extradefines.h");
2d2b1a
+    args << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h");
2d2b1a
+
2d2b1a
+    proc.start(m_moc, args);
2d2b1a
+    QVERIFY(proc.waitForFinished());
2d2b1a
+    QCOMPARE(proc.exitCode(), 0);
2d2b1a
+    QCOMPARE(proc.readAllStandardError(), QByteArray());
2d2b1a
+    QByteArray mocOut = proc.readAllStandardOutput();
2d2b1a
+    QVERIFY(!mocOut.isEmpty());
2d2b1a
+#else
2d2b1a
+    QSKIP("Only tested on linux/gcc");
2d2b1a
+#endif
2d2b1a
+}
2d2b1a
+
2d2b1a
+void tst_Moc::defineMacroViaForcedIncludeRelative()
2d2b1a
+{
2d2b1a
+#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
2d2b1a
+    QProcess proc;
2d2b1a
+
2d2b1a
+    QStringList args;
2d2b1a
+    args << "--include" << QStringLiteral("extradefines.h") << "-I" + m_sourceDirectory + "/subdir";
2d2b1a
+    args << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h");
2d2b1a
+
2d2b1a
+    proc.start(m_moc, args);
2d2b1a
+    QVERIFY(proc.waitForFinished());
2d2b1a
+    QCOMPARE(proc.exitCode(), 0);
2d2b1a
+    QCOMPARE(proc.readAllStandardError(), QByteArray());
2d2b1a
+    QByteArray mocOut = proc.readAllStandardOutput();
2d2b1a
+    QVERIFY(!mocOut.isEmpty());
2d2b1a
+#else
2d2b1a
+    QSKIP("Only tested on linux/gcc");
2d2b1a
+#endif
2d2b1a
+}
2d2b1a
+
2d2b1a
 // tst_Moc::specifyMetaTagsFromCmdline()
2d2b1a
 // plugin_metadata.h contains a plugin which we register here. Since we're not building this
2d2b1a
 // application as a plugin, we need top copy some of the initializer code found in qplugin.h:
2d2b1a
-- 
2d2b1a
1.9.3
2d2b1a