Make moc obey the preprocessor environment variable for include paths

C preprocessors augment their standard list of include paths from the
environment: Unix preprocessors use $C_INCLUDE_PATH (for C) and
$CPLUS_INCLUDE_PATH (for C++), plus CPATH for both, whereas MSVC uses
the an environment variable simply called "INCLUDE". Handling this for
MSVC is particularly important because the VCVARSALL.BAT script sets the
necessary #include paths in the environment for important things.
Without that being parsed, moc won't find some #defines, like
WINAPI_DESKTOP_FAMILY.

[ChangeLog][moc] qmake and moc now cooperate to use the Visual Studio
environment variables (set by the VCVARSALL.BAT script) to find system
include files. A possible consequence is that moc parses application
headers slightly differently, depending on #if conditions that depended
on macros that previous versions had not seen #define'd. Implementers of
other buildsystems are advised to pass the --compiler-flavor=msvc option
to moc.

Change-Id: I7e06274214d1939b0124e5b4bf169cceaef9ca46
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
bb10
Thiago Macieira 2016-04-13 17:25:37 -07:00
parent 36d524e6a3
commit da04322b4c
5 changed files with 85 additions and 0 deletions

View File

@ -51,6 +51,7 @@ defineReplace(mocCmdBase) {
}
RET = $$QMAKE_MOC $(DEFINES)
msvc: RET += --compiler-flavor=msvc
isEmpty(MOC_PREDEF_FILE): RET += $$join(QMAKE_COMPILER_DEFINES, " -D", -D)
else: RET += --include $$moc_predefs.output

View File

@ -90,6 +90,9 @@ macro(QT5_GET_MOC_FLAGS _moc_flags)
if(WIN32)
set(${_moc_flags} ${${_moc_flags}} -DWIN32)
endif()
if (MSVC)
set(${_moc_flags} --compiler-flavor=msvc)
endif()
endmacro()

View File

@ -39,6 +39,10 @@ unix: {
unix:HEADERS += \
socket/qnet_unix_p.h
# Suppress deprecation warnings with moc because MS headers have
# invalid C/C++ code otherwise.
msvc: QMAKE_MOC_OPTIONS += -D_WINSOCK_DEPRECATED_NO_WARNINGS
win32:!winrt:SOURCES += socket/qnativesocketengine_win.cpp \
socket/qlocalsocket_win.cpp \
socket/qlocalserver_win.cpp

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@ -242,6 +243,11 @@ int runMoc(int argc, char **argv)
metadataOption.setFlags(QCommandLineOption::ShortOptionStyle);
parser.addOption(metadataOption);
QCommandLineOption compilerFlavorOption(QStringLiteral("compiler-flavor"));
compilerFlavorOption.setDescription(QStringLiteral("Set the compiler flavor: either \"msvc\" or \"unix\"."));
compilerFlavorOption.setValueName(QStringLiteral("flavor"));
parser.addOption(compilerFlavorOption);
QCommandLineOption noIncludeOption(QStringLiteral("i"));
noIncludeOption.setDescription(QStringLiteral("Do not generate an #include statement."));
parser.addOption(noIncludeOption);
@ -328,9 +334,32 @@ int runMoc(int argc, char **argv)
if (parser.isSet(pathPrefixOption))
moc.includePath = QFile::encodeName(parser.value(pathPrefixOption));
}
const auto includePaths = parser.values(includePathOption);
for (const QString &path : includePaths)
pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
QString compilerFlavor = parser.value(compilerFlavorOption);
if (compilerFlavor.isEmpty() || compilerFlavor == QLatin1String("unix")) {
// traditional Unix compilers use both CPATH and CPLUS_INCLUDE_PATH
// $CPATH feeds to #include <...> and #include "...", whereas
// CPLUS_INCLUDE_PATH is equivalent to GCC's -isystem, so we parse later
const auto cpath = qgetenv("CPATH").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : cpath)
pp.includes += Preprocessor::IncludePath(p);
const auto cplus_include_path = qgetenv("CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : cplus_include_path)
pp.includes += Preprocessor::IncludePath(p);
} else if (compilerFlavor == QLatin1String("msvc")) {
// MSVC uses one environment variable: INCLUDE
const auto include = qgetenv("INCLUDE").split(QDir::listSeparator().toLatin1());
for (const QByteArray &p : include)
pp.includes += Preprocessor::IncludePath(p);
} else {
error(qPrintable(QLatin1String("Unknown compiler flavor '") + compilerFlavor +
QLatin1String("'; valid values are: msvc, unix.")));
parser.showHelp(1);
}
const auto macFrameworks = parser.values(macFrameworkOption);
for (const QString &path : macFrameworks) {
// minimalistic framework support for the mac

View File

@ -577,6 +577,8 @@ private slots:
void defineMacroViaCmdline();
void defineMacroViaForcedInclude();
void defineMacroViaForcedIncludeRelative();
void environmentIncludePaths_data();
void environmentIncludePaths();
void specifyMetaTagsFromCmdline();
void invokable();
void singleFunctionKeywordSignalAndSlot();
@ -1295,6 +1297,52 @@ void tst_Moc::defineMacroViaForcedIncludeRelative()
#endif
}
void tst_Moc::environmentIncludePaths_data()
{
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QTest::addColumn<QString>("cmdline");
QTest::addColumn<QString>("varname");
QTest::newRow("INCLUDE") << "--compiler-flavor=msvc" << "INCLUDE";
QTest::newRow("CPATH1") << QString() << "CPATH";
QTest::newRow("CPATH2") << "--compiler-flavor=unix" << "CPATH";
QTest::newRow("CPLUS_INCLUDE_PATH1") << QString() << "CPLUS_INCLUDE_PATH";
QTest::newRow("CPLUS_INCLUDE_PATH2") << "--compiler-flavor=unix" << "CPLUS_INCLUDE_PATH";
#endif
}
void tst_Moc::environmentIncludePaths()
{
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QFETCH(QString, cmdline);
QFETCH(QString, varname);
QStringList args;
if (!cmdline.isEmpty())
args << cmdline;
args << "--include" << QStringLiteral("extradefines.h")
<< m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h");
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.remove("INCLUDE");
env.remove("CPATH");
env.remove("CPLUS_INCLUDE_PATH");
env.insert(varname, m_sourceDirectory + "/subdir");
QProcess proc;
proc.setProcessEnvironment(env);
proc.start(m_moc, args);
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QCOMPARE(proc.readAllStandardError(), QByteArray());
QByteArray mocOut = proc.readAllStandardOutput();
QVERIFY(!mocOut.isEmpty());
#else
QSKIP("Only tested on linux/gcc");
#endif
}
// tst_Moc::specifyMetaTagsFromCmdline()
// plugin_metadata.h contains a plugin which we register here. Since we're not building this
// application as a plugin, we need top copy some of the initializer code found in qplugin.h: