forked from qt-creator/qt-creator
		
	This is more clear than passing in version and fallback path and calculating the real path from them somewhere down the line. No functional changes for now. Change-Id: Iae2fc8015c778d787ed6e0ce898f41a7a05b2607 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: David Schulz <david.schulz@qt.io>
		
			
				
	
	
		
			938 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			938 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/****************************************************************************
 | 
						|
**
 | 
						|
** 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 <qnx/qnxconstants.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 ¯o)
 | 
						|
{
 | 
						|
    switch (macro.type) {
 | 
						|
    case MacroType::Define:
 | 
						|
        return defineOption;
 | 
						|
    case MacroType::Undefine:
 | 
						|
        return undefineOption;
 | 
						|
    default:
 | 
						|
        return QByteArray();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static QByteArray toDefineOption(const Macro ¯o)
 | 
						|
{
 | 
						|
    return macro.toKeyValue(macroOption(macro));
 | 
						|
}
 | 
						|
 | 
						|
static QString defineDirectiveToDefineOption(const Macro ¯o)
 | 
						|
{
 | 
						|
    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 FilePath &clangIncludeDirectory)
 | 
						|
    : m_projectPart(projectPart)
 | 
						|
    , m_useSystemHeader(useSystemHeader)
 | 
						|
    , m_useTweakedHeaderPaths(useTweakedHeaderPaths)
 | 
						|
    , m_useLanguageDefines(useLanguageDefines)
 | 
						|
    , m_useBuildSystemWarnings(useBuildSystemWarnings)
 | 
						|
    , m_clangIncludeDirectory(clangIncludeDirectory)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
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();
 | 
						|
    addDefineFunctionMacrosQnx();
 | 
						|
 | 
						|
    addHeaderPathOptions();
 | 
						|
 | 
						|
    insertWrappedQtHeaders();
 | 
						|
    insertWrappedMingwHeaders();
 | 
						|
 | 
						|
    return options();
 | 
						|
}
 | 
						|
 | 
						|
void CompilerOptionsBuilder::provideAdditionalMacros(const ProjectExplorer::Macros ¯os)
 | 
						|
{
 | 
						|
    m_additionalMacros = macros;
 | 
						|
}
 | 
						|
 | 
						|
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.targetTripleIsAuthoritative
 | 
						|
            ? 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 ¯o) {
 | 
						|
        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_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) {
 | 
						|
 | 
						|
        // 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 == Qnx::Constants::QNX_TOOLCHAIN_ID
 | 
						|
        || m_projectPart.toolchainType.name().contains("BareMetal") || useMacros) {
 | 
						|
        addMacros(m_projectPart.toolChainMacros);
 | 
						|
    }
 | 
						|
 | 
						|
    addMacros(m_projectPart.projectMacros);
 | 
						|
    addMacros(m_additionalMacros);
 | 
						|
}
 | 
						|
 | 
						|
void CompilerOptionsBuilder::addMacros(const Macros ¯os)
 | 
						|
{
 | 
						|
    QStringList options;
 | 
						|
 | 
						|
    for (const Macro ¯o : 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 ¯os)
 | 
						|
{
 | 
						|
    for (const Macro ¯o : 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 ¯oName : 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 ¯o) 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 == QtMajorVersion::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 ¯oName : macroNames)
 | 
						|
                add(undefineOption + macroName);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CompilerOptionsBuilder::addDefineFunctionMacrosQnx()
 | 
						|
{
 | 
						|
    // QNX 7.0+ uses GCC with LIBCPP from Clang, and in that context GCC is giving
 | 
						|
    // the builtin operator new and delete.
 | 
						|
    //
 | 
						|
    // In our case we have only Clang and need to instruct LIBCPP that it doesn't
 | 
						|
    // have these operators. This makes the code model happy and doesn't produce errors.
 | 
						|
    if (m_projectPart.toolchainType == Qnx::Constants::QNX_TOOLCHAIN_ID)
 | 
						|
        addMacros({{"_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE"}});
 | 
						|
}
 | 
						|
 | 
						|
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
 |