Files
qt-creator/src/plugins/cppeditor/compileroptionsbuilder.cpp

927 lines
30 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "compileroptionsbuilder.h"
#include "cppmodelmanager.h"
#include "headerpathfilter.h"
#include <coreplugin/icore.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmacro.h>
#include <utils/algorithm.h>
#include <utils/cpplanguage_details.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <QDir>
#include <QRegularExpression>
#include <QtGlobal>
using namespace ProjectExplorer;
using namespace Utils;
namespace CppEditor {
const char defineOption[] = "-D";
const char undefineOption[] = "-U";
const char includeUserPathOption[] = "-I";
const char includeUserPathOptionWindows[] = "/I";
const char includeSystemPathOption[] = "-isystem";
const char includeFileOptionGcc[] = "-include";
const char includeFileOptionCl[] = "/FI";
static QByteArray macroOption(const Macro &macro)
{
switch (macro.type) {
case MacroType::Define:
return defineOption;
case MacroType::Undefine:
return undefineOption;
default:
return QByteArray();
}
}
static QByteArray toDefineOption(const Macro &macro)
{
return macro.toKeyValue(macroOption(macro));
}
static QString defineDirectiveToDefineOption(const Macro &macro)
{
const QByteArray option = toDefineOption(macro);
return QString::fromUtf8(option);
}
QStringList XclangArgs(const QStringList &args)
{
QStringList options;
for (const QString &arg : args) {
options.append("-Xclang");
options.append(arg);
}
return options;
}
QStringList clangArgsForCl(const QStringList &args)
{
QStringList options;
for (const QString &arg : args)
options.append("/clang:" + arg);
return options;
}
CompilerOptionsBuilder::CompilerOptionsBuilder(const ProjectPart &projectPart,
UseSystemHeader useSystemHeader,
UseTweakedHeaderPaths useTweakedHeaderPaths,
UseLanguageDefines useLanguageDefines,
UseBuildSystemWarnings useBuildSystemWarnings,
const QString &clangVersion,
const FilePath &clangIncludeDirectory)
: m_projectPart(projectPart)
, m_useSystemHeader(useSystemHeader)
, m_useTweakedHeaderPaths(useTweakedHeaderPaths)
, m_useLanguageDefines(useLanguageDefines)
, m_useBuildSystemWarnings(useBuildSystemWarnings)
, m_clangVersion(clangVersion)
, m_clangIncludeDirectory(clangIncludeDirectory)
{
}
CompilerOptionsBuilder::~CompilerOptionsBuilder() = default;
QStringList CompilerOptionsBuilder::build(ProjectFile::Kind fileKind,
UsePrecompiledHeaders usePrecompiledHeaders)
{
reset();
evaluateCompilerFlags();
if (fileKind == ProjectFile::CHeader || fileKind == ProjectFile::CSource) {
QTC_ASSERT(m_projectPart.languageVersion <= LanguageVersion::LatestC, return {});
}
if (fileKind == ProjectFile::CXXHeader || fileKind == ProjectFile::CXXSource) {
QTC_ASSERT(m_projectPart.languageVersion > LanguageVersion::LatestC, return {});
}
addCompilerFlags();
addSyntaxOnly();
addWordWidth();
addTargetTriple();
updateFileLanguage(fileKind);
addLanguageVersionAndExtensions();
addMsvcExceptions();
addIncludedFiles(m_projectPart.includedFiles); // GCC adds these before precompiled headers.
addPrecompiledHeaderOptions(usePrecompiledHeaders);
addProjectConfigFileInclude();
addMsvcCompatibilityVersion();
addProjectMacros();
undefineClangVersionMacrosForMsvc();
undefineCppLanguageFeatureMacrosForMsvc2015();
addDefineFunctionMacrosMsvc();
addHeaderPathOptions();
addExtraOptions();
insertWrappedQtHeaders();
insertWrappedMingwHeaders();
return options();
}
void CompilerOptionsBuilder::add(const QString &arg, bool gccOnlyOption)
{
add(QStringList{arg}, gccOnlyOption);
}
void CompilerOptionsBuilder::prepend(const QString &arg)
{
m_options.prepend(arg);
}
void CompilerOptionsBuilder::add(const QStringList &args, bool gccOnlyOptions)
{
m_options.append((gccOnlyOptions && isClStyle()) ? clangArgsForCl(args) : args);
}
void CompilerOptionsBuilder::addSyntaxOnly()
{
isClStyle() ? add("/Zs") : add("-fsyntax-only");
}
QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt)
{
QStringList options;
switch (fileKind) {
case ProjectFile::Unclassified:
case ProjectFile::Unsupported:
break;
case ProjectFile::CHeader:
if (objcExt)
options += "objective-c-header";
else
options += "c-header";
break;
case ProjectFile::CXXHeader:
default:
if (!objcExt) {
options += "c++-header";
break;
}
Q_FALLTHROUGH();
case ProjectFile::ObjCHeader:
case ProjectFile::ObjCXXHeader:
options += "objective-c++-header";
break;
case ProjectFile::CSource:
if (!objcExt) {
options += "c";
break;
}
Q_FALLTHROUGH();
case ProjectFile::ObjCSource:
options += "objective-c";
break;
case ProjectFile::CXXSource:
if (!objcExt) {
options += "c++";
break;
}
Q_FALLTHROUGH();
case ProjectFile::ObjCXXSource:
options += "objective-c++";
break;
case ProjectFile::OpenCLSource:
options += "cl";
break;
case ProjectFile::CudaSource:
options += "cuda";
break;
}
if (!options.isEmpty())
options.prepend("-x");
return options;
}
void CompilerOptionsBuilder::addWordWidth()
{
const QString argument = m_projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit
? QLatin1String("-m64")
: QLatin1String("-m32");
add(argument);
}
void CompilerOptionsBuilder::addTargetTriple()
{
const QString target = m_explicitTarget.isEmpty()
? m_projectPart.toolChainTargetTriple : m_explicitTarget;
// Only "--target=" style is accepted in both g++ and cl driver modes.
if (!target.isEmpty())
add("--target=" + target);
}
void CompilerOptionsBuilder::addExtraCodeModelFlags()
{
// extraCodeModelFlags keep build architecture for cross-compilation.
// In case of iOS build target triple has aarch64 archtecture set which makes
// code model fail with CXError_Failure. To fix that we explicitly provide architecture.
add(m_projectPart.extraCodeModelFlags);
}
void CompilerOptionsBuilder::addPicIfCompilerFlagsContainsIt()
{
if (m_projectPart.compilerFlags.contains("-fPIC"))
add("-fPIC");
}
void CompilerOptionsBuilder::addCompilerFlags()
{
add(m_compilerFlags.flags);
}
void CompilerOptionsBuilder::addMsvcExceptions()
{
if (!m_clStyle)
return;
if (Utils::anyOf(m_projectPart.toolChainMacros, [](const Macro &macro) {
return macro.key == "_CPPUNWIND";
})) {
enableExceptions();
}
}
void CompilerOptionsBuilder::enableExceptions()
{
// With "--driver-mode=cl" exceptions are disabled (clang 8).
// This is most likely due to incomplete exception support of clang.
// However, as we need exception support only in the frontend,
// enabling them explicitly should be fine.
if (m_projectPart.languageVersion > LanguageVersion::LatestC)
add("-fcxx-exceptions");
add("-fexceptions");
}
void CompilerOptionsBuilder::insertWrappedQtHeaders()
{
if (m_useTweakedHeaderPaths == UseTweakedHeaderPaths::Yes)
insertWrappedHeaders(wrappedQtHeadersIncludePath());
}
void CompilerOptionsBuilder::insertWrappedMingwHeaders()
{
insertWrappedHeaders(wrappedMingwHeadersIncludePath());
}
static QString creatorResourcePath()
{
#ifndef UNIT_TESTS
return Core::ICore::resourcePath().toString();
#else
return QDir::toNativeSeparators(QString::fromUtf8(QTC_RESOURCE_DIR ""));
#endif
}
void CompilerOptionsBuilder::insertWrappedHeaders(const QStringList &relPaths)
{
if (m_useTweakedHeaderPaths == UseTweakedHeaderPaths::No)
return;
if (relPaths.isEmpty())
return;
QStringList args;
for (const QString &relPath : relPaths) {
static const QString baseDir = creatorResourcePath() + "/cplusplus";
const QString fullPath = baseDir + '/' + relPath;
QTC_ASSERT(QDir(fullPath).exists(), continue);
args << includeUserPathOption << QDir::toNativeSeparators(fullPath);
}
const int index = m_options.indexOf(QRegularExpression("\\A-I.*\\z"));
if (index < 0)
add(args);
else
m_options = m_options.mid(0, index) + args + m_options.mid(index);
}
void CompilerOptionsBuilder::addHeaderPathOptions()
{
Internal::HeaderPathFilter filter{
m_projectPart,
m_useTweakedHeaderPaths,
m_clangVersion,
m_clangIncludeDirectory};
filter.process();
for (const HeaderPath &headerPath : qAsConst(filter.userHeaderPaths))
addIncludeDirOptionForPath(headerPath);
for (const HeaderPath &headerPath : qAsConst(filter.systemHeaderPaths))
addIncludeDirOptionForPath(headerPath);
if (m_useTweakedHeaderPaths != UseTweakedHeaderPaths::No) {
QTC_CHECK(!m_clangVersion.isEmpty()
&& "Clang resource directory is required with UseTweakedHeaderPaths::Yes.");
// Exclude all built-in includes and Clang resource directory.
m_options.prepend("-nostdinc++");
m_options.prepend("-nostdinc");
for (const HeaderPath &headerPath : qAsConst(filter.builtInHeaderPaths))
addIncludeDirOptionForPath(headerPath);
}
}
void CompilerOptionsBuilder::addIncludeFile(const QString &file)
{
if (QFile::exists(file)) {
add({isClStyle() ? QLatin1String(includeFileOptionCl)
: QLatin1String(includeFileOptionGcc),
QDir::toNativeSeparators(file)});
}
}
void CompilerOptionsBuilder::addIncludedFiles(const QStringList &files)
{
for (const QString &file : files) {
if (m_projectPart.precompiledHeaders.contains(file))
continue;
addIncludeFile(file);
}
}
void CompilerOptionsBuilder::addPrecompiledHeaderOptions(UsePrecompiledHeaders usePrecompiledHeaders)
{
if (usePrecompiledHeaders == UsePrecompiledHeaders::No)
return;
for (const QString &pchFile : m_projectPart.precompiledHeaders) {
addIncludeFile(pchFile);
}
}
void CompilerOptionsBuilder::addProjectMacros()
{
static const int useMacros = qEnvironmentVariableIntValue("QTC_CLANG_USE_TOOLCHAIN_MACROS");
if (m_projectPart.toolchainType == ProjectExplorer::Constants::CUSTOM_TOOLCHAIN_TYPEID
|| m_projectPart.toolchainType.name().contains("BareMetal")
|| useMacros) {
addMacros(m_projectPart.toolChainMacros);
}
addMacros(m_projectPart.projectMacros);
ClangStaticAnalyzer: Stop passing on toolchain defines for qmake/msvc case Re-produce with: 1) Open qt-essential-includes.pro and configure it with a Qt 5.7.0 MSVC2013 64 Bit Kit. 2) Run the analyzer. Errors during analyzing occur in winnt.h [1]. This is somehow related to /D__int32=long that we pass on. Removing it helps. It looks like there is no reason anymore to pass on the toolchain defines with clang-3.8 at all. Our unit test projects can be parsed by the analyzer without them. Tested with the following kits: Qt 5.6.0 (mingw39_32) Qt 5.6.0 (msvc2013) Qt 5.6.0 (msvc2013_64) Qt 5.6.0 (msvc2015) Qt 5.6.0 (msvc2015_64) Qt 5.7.0 (mingw53_32) Qt 5.7.0 (msvc2013) Qt 5.7.0 (msvc2013_64) Qt 5.7.0 (msvc2015) Qt 5.7.0 (msvc2015_64) [1] In file included from D:\dev\creator\creator-4.1\src\plugins\clangstaticanalyzer\unit-tests\qt-essential-includes\main.cpp:2: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include/QtGui\QtGui:32: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include/QtGui/qopenglcontext.h:60: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include\QtGui/qopengl.h:49: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include\QtCore/qt_windows.h:61: In file included from C:\Program Files (x86)\Windows Kits\8.1\include\um\windows.h:164: In file included from C:\Program Files (x86)\Windows Kits\8.1\include\shared\windef.h:24: In file included from C:\Program Files (x86)\Windows Kits\8.1\include\shared\minwindef.h:182: C:\Program Files (x86)\Windows Kits\8.1\include\um\winnt.h(3077,1) : error: functions that differ only in their return type cannot be overloaded __getcallerseflags ( ^ D:\dev\llvm\3.8\changingLibClang_install\bin\..\lib\clang\3.8.1\include\intrin.h(68,14) : note: previous declaration is here unsigned int __getcallerseflags(void); ~~~~~~~~ ^ 1 error generated. Change-Id: I2de8d0393a575f88dd59dfa71fbfb11f2debc158 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2016-07-07 11:45:46 +02:00
}
void CompilerOptionsBuilder::addMacros(const Macros &macros)
ClangStaticAnalyzer: Stop passing on toolchain defines for qmake/msvc case Re-produce with: 1) Open qt-essential-includes.pro and configure it with a Qt 5.7.0 MSVC2013 64 Bit Kit. 2) Run the analyzer. Errors during analyzing occur in winnt.h [1]. This is somehow related to /D__int32=long that we pass on. Removing it helps. It looks like there is no reason anymore to pass on the toolchain defines with clang-3.8 at all. Our unit test projects can be parsed by the analyzer without them. Tested with the following kits: Qt 5.6.0 (mingw39_32) Qt 5.6.0 (msvc2013) Qt 5.6.0 (msvc2013_64) Qt 5.6.0 (msvc2015) Qt 5.6.0 (msvc2015_64) Qt 5.7.0 (mingw53_32) Qt 5.7.0 (msvc2013) Qt 5.7.0 (msvc2013_64) Qt 5.7.0 (msvc2015) Qt 5.7.0 (msvc2015_64) [1] In file included from D:\dev\creator\creator-4.1\src\plugins\clangstaticanalyzer\unit-tests\qt-essential-includes\main.cpp:2: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include/QtGui\QtGui:32: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include/QtGui/qopenglcontext.h:60: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include\QtGui/qopengl.h:49: In file included from D:/usr/qt-5.7.0-msvc2013_64/5.7/msvc2013_64/include\QtCore/qt_windows.h:61: In file included from C:\Program Files (x86)\Windows Kits\8.1\include\um\windows.h:164: In file included from C:\Program Files (x86)\Windows Kits\8.1\include\shared\windef.h:24: In file included from C:\Program Files (x86)\Windows Kits\8.1\include\shared\minwindef.h:182: C:\Program Files (x86)\Windows Kits\8.1\include\um\winnt.h(3077,1) : error: functions that differ only in their return type cannot be overloaded __getcallerseflags ( ^ D:\dev\llvm\3.8\changingLibClang_install\bin\..\lib\clang\3.8.1\include\intrin.h(68,14) : note: previous declaration is here unsigned int __getcallerseflags(void); ~~~~~~~~ ^ 1 error generated. Change-Id: I2de8d0393a575f88dd59dfa71fbfb11f2debc158 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2016-07-07 11:45:46 +02:00
{
QStringList options;
for (const Macro &macro : macros) {
if (excludeDefineDirective(macro))
continue;
const QString defineOption = defineDirectiveToDefineOption(macro);
if (!options.contains(defineOption))
options.append(defineOption);
}
add(options);
}
void CompilerOptionsBuilder::updateFileLanguage(ProjectFile::Kind fileKind)
{
if (isClStyle()) {
QString option;
if (ProjectFile::isC(fileKind))
option = "/TC";
else if (ProjectFile::isCxx(fileKind))
option = "/TP";
else
return; // Do not add anything if we haven't set a file kind yet.
int langOptIndex = m_options.indexOf("/TC");
if (langOptIndex == -1)
langOptIndex = m_options.indexOf("/TP");
if (langOptIndex == -1)
add(option);
else
m_options[langOptIndex] = option;
return;
}
const bool objcExt = m_projectPart.languageExtensions & LanguageExtension::ObjectiveC;
const QStringList options = createLanguageOptionGcc(fileKind, objcExt);
if (options.isEmpty())
return;
QTC_ASSERT(options.size() == 2, return;);
int langOptIndex = m_options.indexOf("-x");
if (langOptIndex == -1)
add(options);
else
m_options[langOptIndex + 1] = options[1];
}
void CompilerOptionsBuilder::addLanguageVersionAndExtensions()
{
if (m_compilerFlags.isLanguageVersionSpecified)
return;
QString option;
if (isClStyle()) {
switch (m_projectPart.languageVersion) {
default:
break;
case LanguageVersion::CXX14:
option = "/std:c++14";
break;
case LanguageVersion::CXX17:
option = "/std:c++17";
break;
case LanguageVersion::CXX20:
option = "/std:c++20";
break;
case LanguageVersion::CXX2b:
option = "/std:c++latest";
break;
}
if (!option.isEmpty()) {
add(option);
return;
}
// Continue in case no cl-style option could be chosen.
}
const LanguageExtensions languageExtensions = m_projectPart.languageExtensions;
const bool gnuExtensions = languageExtensions & LanguageExtension::Gnu;
switch (m_projectPart.languageVersion) {
case LanguageVersion::C89:
option = (gnuExtensions ? QLatin1String("-std=gnu89") : QLatin1String("-std=c89"));
break;
case LanguageVersion::C99:
option = (gnuExtensions ? QLatin1String("-std=gnu99") : QLatin1String("-std=c99"));
break;
case LanguageVersion::C11:
option = (gnuExtensions ? QLatin1String("-std=gnu11") : QLatin1String("-std=c11"));
break;
case LanguageVersion::C18:
// Clang 6, 7 and current trunk do not accept "gnu18"/"c18", so use the "*17" variants.
option = (gnuExtensions ? QLatin1String("-std=gnu17") : QLatin1String("-std=c17"));
break;
case LanguageVersion::CXX11:
option = (gnuExtensions ? QLatin1String("-std=gnu++11") : QLatin1String("-std=c++11"));
break;
case LanguageVersion::CXX98:
option = (gnuExtensions ? QLatin1String("-std=gnu++98") : QLatin1String("-std=c++98"));
break;
case LanguageVersion::CXX03:
option = (gnuExtensions ? QLatin1String("-std=gnu++03") : QLatin1String("-std=c++03"));
break;
case LanguageVersion::CXX14:
option = (gnuExtensions ? QLatin1String("-std=gnu++14") : QLatin1String("-std=c++14"));
break;
case LanguageVersion::CXX17:
option = (gnuExtensions ? QLatin1String("-std=gnu++17") : QLatin1String("-std=c++17"));
break;
case LanguageVersion::CXX20:
option = (gnuExtensions ? QLatin1String("-std=gnu++20") : QLatin1String("-std=c++20"));
break;
case LanguageVersion::CXX2b:
option = (gnuExtensions ? QLatin1String("-std=gnu++2b") : QLatin1String("-std=c++2b"));
break;
case LanguageVersion::None:
break;
}
add(option, /*gccOnlyOption=*/true);
}
static QByteArray toMsCompatibilityVersionFormat(const QByteArray &mscFullVer)
{
return mscFullVer.left(2)
+ QByteArray(".")
+ mscFullVer.mid(2, 2);
}
static QByteArray msCompatibilityVersionFromDefines(const Macros &macros)
{
for (const Macro &macro : macros) {
if (macro.key == "_MSC_FULL_VER")
return toMsCompatibilityVersionFormat(macro.value);
}
return QByteArray();
}
QByteArray CompilerOptionsBuilder::msvcVersion() const
{
const QByteArray version = msCompatibilityVersionFromDefines(m_projectPart.toolChainMacros);
return !version.isEmpty() ? version
: msCompatibilityVersionFromDefines(m_projectPart.projectMacros);
}
void CompilerOptionsBuilder::addMsvcCompatibilityVersion()
{
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
|| m_projectPart.toolchainType == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) {
const QByteArray msvcVer = msvcVersion();
if (!msvcVer.isEmpty())
add(QLatin1String("-fms-compatibility-version=") + msvcVer);
}
}
static QStringList languageFeatureMacros()
{
// CLANG-UPGRADE-CHECK: Update known language features macros.
// Collected with the following command line.
// * Use latest -fms-compatibility-version and -std possible.
// * Compatibility version 19 vs 1910 did not matter.
// $ clang++ -fms-compatibility-version=19 -std=c++1z -dM -E D:\empty.cpp | grep __cpp_
static const QStringList macros{
"__cpp_aggregate_bases",
"__cpp_aggregate_nsdmi",
"__cpp_alias_templates",
"__cpp_aligned_new",
"__cpp_attributes",
"__cpp_binary_literals",
"__cpp_capture_star_this",
"__cpp_constexpr",
"__cpp_constexpr_in_decltype",
"__cpp_decltype",
"__cpp_decltype_auto",
"__cpp_deduction_guides",
"__cpp_delegating_constructors",
"__cpp_digit_separators",
"__cpp_enumerator_attributes",
"__cpp_exceptions",
"__cpp_fold_expressions",
"__cpp_generic_lambdas",
"__cpp_guaranteed_copy_elision",
"__cpp_hex_float",
"__cpp_if_constexpr",
"__cpp_impl_destroying_delete",
"__cpp_inheriting_constructors",
"__cpp_init_captures",
"__cpp_initializer_lists",
"__cpp_inline_variables",
"__cpp_lambdas",
"__cpp_namespace_attributes",
"__cpp_nested_namespace_definitions",
"__cpp_noexcept_function_type",
"__cpp_nontype_template_args",
"__cpp_nontype_template_parameter_auto",
"__cpp_nsdmi",
"__cpp_range_based_for",
"__cpp_raw_strings",
"__cpp_ref_qualifiers",
"__cpp_return_type_deduction",
"__cpp_rtti",
"__cpp_rvalue_references",
"__cpp_static_assert",
"__cpp_structured_bindings",
"__cpp_template_auto",
"__cpp_threadsafe_static_init",
"__cpp_unicode_characters",
"__cpp_unicode_literals",
"__cpp_user_defined_literals",
"__cpp_variable_templates",
"__cpp_variadic_templates",
"__cpp_variadic_using",
};
return macros;
}
void CompilerOptionsBuilder::undefineCppLanguageFeatureMacrosForMsvc2015()
{
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
&& m_projectPart.isMsvc2015Toolchain) {
// Undefine the language feature macros that are pre-defined in clang-cl,
// but not in MSVC's cl.exe.
const QStringList macroNames = languageFeatureMacros();
for (const QString &macroName : macroNames)
add(undefineOption + macroName);
}
}
void CompilerOptionsBuilder::addDefineFunctionMacrosMsvc()
{
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
addMacros({{"__FUNCSIG__", "\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\""},
{"__FUNCTION__", "\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\""},
{"__FUNCDNAME__", "\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\""}});
}
}
void CompilerOptionsBuilder::addIncludeDirOptionForPath(const HeaderPath &path)
{
if (path.type == HeaderPathType::Framework) {
QTC_ASSERT(!isClStyle(), return;);
add({"-F", QDir::toNativeSeparators(path.path)});
return;
}
bool systemPath = false;
if (path.type == HeaderPathType::BuiltIn) {
systemPath = true;
} else if (path.type == HeaderPathType::System) {
if (m_useSystemHeader == UseSystemHeader::Yes)
systemPath = true;
} else {
// ProjectExplorer::HeaderPathType::User
if (m_useSystemHeader == UseSystemHeader::Yes && m_projectPart.hasProject()
&& !Utils::FilePath::fromString(path.path).isChildOf(m_projectPart.topLevelProject)) {
systemPath = true;
}
}
if (systemPath) {
add({includeSystemPathOption, QDir::toNativeSeparators(path.path)}, true);
return;
}
add({includeUserPathOption, QDir::toNativeSeparators(path.path)});
}
bool CompilerOptionsBuilder::excludeDefineDirective(const Macro &macro) const
{
// Avoid setting __cplusplus & co as this might conflict with other command line flags.
// Clang should set __cplusplus based on -std= and -fms-compatibility-version version.
static const auto languageDefines = {"__cplusplus",
"__STDC_VERSION__",
"_MSC_BUILD",
"_MSVC_LANG",
"_MSC_FULL_VER",
"_MSC_VER"};
if (m_useLanguageDefines == UseLanguageDefines::No
&& std::find(languageDefines.begin(),
languageDefines.end(),
macro.key) != languageDefines.end()) {
return true;
}
// Ignore for all compiler toolchains since LLVM has it's own implementation for
// __has_include(STR) and __has_include_next(STR)
if (macro.key.startsWith("__has_include"))
return true;
// If _FORTIFY_SOURCE is defined (typically in release mode), it will
// enable the inclusion of extra headers to help catching buffer overflows
// (e.g. wchar.h includes wchar2.h). These extra headers use
// __builtin_va_arg_pack, which clang does not support (yet), so avoid
// including those.
if (m_projectPart.toolchainType == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID
&& macro.key == "_FORTIFY_SOURCE") {
return true;
}
// MinGW 6 supports some fancy asm output flags and uses them in an
// intrinsics header pulled in by windows.h. Clang does not know them.
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID
&& macro.key == "__GCC_ASM_FLAG_OUTPUTS__") {
return true;
}
return false;
}
QStringList CompilerOptionsBuilder::wrappedQtHeadersIncludePath() const
{
if (m_projectPart.qtVersion == QtVersion::None)
return {};
return {"wrappedQtHeaders", "wrappedQtHeaders/QtCore"};
}
QStringList CompilerOptionsBuilder::wrappedMingwHeadersIncludePath() const
{
if (m_projectPart.toolchainType != ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID)
return {};
return {"wrappedMingwHeaders"};
}
void CompilerOptionsBuilder::addProjectConfigFileInclude()
{
if (!m_projectPart.projectConfigFile.isEmpty()) {
add({isClStyle() ? QLatin1String(includeFileOptionCl) : QLatin1String(includeFileOptionGcc),
QDir::toNativeSeparators(m_projectPart.projectConfigFile)});
}
}
void CompilerOptionsBuilder::undefineClangVersionMacrosForMsvc()
{
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
const QByteArray msvcVer = msvcVersion();
if (msvcVer.toFloat() < 14.f) {
// Original fix was only for msvc 2013 (version 12.0)
// Undefying them for newer versions is not necessary and breaks boost.
static const QStringList macroNames {
"__clang__",
"__clang_major__",
"__clang_minor__",
"__clang_patchlevel__",
"__clang_version__"
};
for (const QString &macroName : macroNames)
add(undefineOption + macroName);
}
}
}
void CompilerOptionsBuilder::reset()
{
m_options.clear();
m_explicitTarget.clear();
}
// Some example command lines for a "Qt Console Application":
// CMakeProject: -fPIC -std=gnu++11
// QbsProject: -m64 -fPIC -std=c++11 -fexceptions
// QMakeProject: -pipe -Whello -g -std=gnu++11 -Wall -W -D_REENTRANT -fPIC
void CompilerOptionsBuilder::evaluateCompilerFlags()
{
static QStringList userBlackList = QString::fromLocal8Bit(
qgetenv("QTC_CLANG_CMD_OPTIONS_BLACKLIST"))
.split(';', Qt::SkipEmptyParts);
const Id toolChain = m_projectPart.toolchainType;
bool containsDriverMode = false;
bool skipNext = false;
bool nextIsTarget = false;
bool nextIsGccToolchain = false;
const QStringList allFlags = m_projectPart.extraCodeModelFlags + m_projectPart.compilerFlags;
for (const QString &option : allFlags) {
if (skipNext) {
skipNext = false;
continue;
}
if (nextIsTarget) {
nextIsTarget = false;
m_explicitTarget = option;
continue;
}
if (nextIsGccToolchain) {
nextIsGccToolchain = false;
m_compilerFlags.flags.append("--gcc-toolchain=" + option);
continue;
}
if (userBlackList.contains(option))
continue;
// TODO: Make it possible that the clang binary/driver ignores unknown options,
// as it is done for libclang/clangd (not checking for OPT_UNKNOWN).
if (toolChain == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) {
if (option == "-fkeep-inline-dllexport" || option == "-fno-keep-inline-dllexport")
continue;
}
// Ignore warning flags as these interfere with our user-configured diagnostics.
// Note that once "-w" is provided, no warnings will be emitted, even if "-Wall" follows.
if (m_useBuildSystemWarnings == UseBuildSystemWarnings::No
&& (option.startsWith("-w", Qt::CaseInsensitive)
|| option.startsWith("/w", Qt::CaseInsensitive) || option.startsWith("-pedantic"))) {
// -w, -W, /w, /W...
continue;
}
// An explicit target triple from the build system takes precedence over the generic one
// from the toolchain.
if (option.startsWith("--target=")) {
m_explicitTarget = option.mid(9);
continue;
}
if (option == "-target") {
nextIsTarget = true;
continue;
}
if (option == "-gcc-toolchain") {
nextIsGccToolchain = true;
continue;
}
if (option == includeUserPathOption || option == includeSystemPathOption
|| option == includeUserPathOptionWindows) {
skipNext = true;
continue;
}
if (option.startsWith("-O", Qt::CaseSensitive) || option.startsWith("/O", Qt::CaseSensitive)
|| option.startsWith("/M", Qt::CaseSensitive)
|| option.startsWith(includeUserPathOption)
|| option.startsWith(includeSystemPathOption)
|| option.startsWith(includeUserPathOptionWindows)) {
// Optimization and run-time flags.
continue;
}
// These were already parsed into ProjectPart::includedFiles.
if (option == includeFileOptionCl || option == includeFileOptionGcc) {
skipNext = true;
continue;
}
if (option.startsWith("/Y", Qt::CaseSensitive)
|| (option.startsWith("/F", Qt::CaseSensitive) && option != "/F")) {
// Precompiled header flags.
// Skip also the next option if it's not glued to the current one.
if (option.size() > 3)
skipNext = true;
continue;
}
// Check whether a language version is already used.
QString theOption = option;
if (theOption.startsWith("-std=") || theOption.startsWith("--std=")) {
m_compilerFlags.isLanguageVersionSpecified = true;
theOption.replace("=c18", "=c17");
theOption.replace("=gnu18", "=gnu17");
} else if (theOption.startsWith("/std:") || theOption.startsWith("-std:")) {
m_compilerFlags.isLanguageVersionSpecified = true;
}
if (theOption.startsWith("--driver-mode=")) {
if (theOption.endsWith("cl"))
m_clStyle = true;
containsDriverMode = true;
}
// Transfrom the "/" starting commands into "-" commands, which if
// unknown will not cause clang to fail because it thinks
// it's a missing file.
if (theOption.startsWith("/") &&
(toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID ||
toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID)) {
theOption[0] = '-';
}
// Clang-cl (as of Clang 12) frontend doesn't know about -std:c++20
// but the clang front end knows about -std=c++20
// https://github.com/llvm/llvm-project/blob/release/12.x/clang/lib/Driver/ToolChains/Clang.cpp#L5855
if (toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID ||
toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) {
theOption.replace("-std:c++20", "-clang:-std=c++20");
}
m_compilerFlags.flags.append(theOption);
}
if (!containsDriverMode
&& (toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
|| toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID)) {
m_clStyle = true;
m_compilerFlags.flags.prepend("--driver-mode=cl");
}
}
bool CompilerOptionsBuilder::isClStyle() const
{
return m_clStyle;
}
} // namespace CppEditor