diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp | 327 |
1 files changed, 176 insertions, 151 deletions
diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp index 7631916fb..4c13b141f 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp @@ -1,36 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "compilersupport.h" #include "header_paths.h" +#include "clangutils.h" #include <reporthandler.h> +#include "qtcompat.h" + #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QFile> @@ -42,10 +20,11 @@ #include <clang-c/Index.h> -#include <string.h> #include <algorithm> #include <iterator> +using namespace Qt::StringLiterals; + namespace clang { QVersionNumber libClangVersion() @@ -53,6 +32,74 @@ QVersionNumber libClangVersion() return QVersionNumber(CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR); } +static Compiler _compiler = +#if defined (Q_CC_CLANG) + Compiler::Clang; +#elif defined (Q_CC_MSVC) + Compiler::Msvc; +#else + Compiler::Gpp; +#endif + +Compiler compiler() { return _compiler; } + +bool setCompiler(const QString &name) +{ + bool result = true; + if (name == u"msvc") + _compiler = Compiler::Msvc; + else if (name == u"g++") + _compiler = Compiler::Gpp; + else if (name == u"clang") + _compiler = Compiler::Clang; + else + result = false; + return result; +} + +QString _compilerPath; // Pre-defined compiler path (from command line) + +const QString &compilerPath() +{ + return _compilerPath; +} + +void setCompilerPath(const QString &name) +{ + _compilerPath = name; +} + +static Platform _platform = +#if defined (Q_OS_DARWIN) + Platform::macOS; +#elif defined (Q_OS_WIN) + Platform::Windows; +#else + Platform::Unix; +#endif + +Platform platform() { return _platform; } + +bool setPlatform(const QString &name) +{ + bool result = true; + if (name == u"windows") + _platform = Platform::Windows; + else if (name == u"darwin") + _platform = Platform::macOS; + else if (name == u"unix") + _platform = Platform::Unix; + else + result = false; + return result; +} + +// 3/2024: Use a recent MSVC2022 for libclang 18.X +static QByteArray msvcCompatVersion() +{ + return libClangVersion() >= QVersionNumber(0, 64) ? "19.39"_ba : "19.26"_ba; +} + static bool runProcess(const QString &program, const QStringList &arguments, QByteArray *stdOutIn = nullptr, QByteArray *stdErrIn = nullptr) { @@ -91,11 +138,8 @@ static bool runProcess(const QString &program, const QStringList &arguments, return true; } -#if defined(Q_CC_GNU) - static QByteArray frameworkPath() { return QByteArrayLiteral(" (framework directory)"); } -# if defined(Q_OS_MACOS) static void filterHomebrewHeaderPaths(HeaderPaths &headerPaths) { QByteArray homebrewPrefix = qgetenv("HOMEBREW_OPT"); @@ -123,7 +167,6 @@ static void filterHomebrewHeaderPaths(HeaderPaths &headerPaths) } } } -# endif // Determine g++'s internal include paths from the output of // g++ -E -x c++ - -v </dev/null @@ -135,15 +178,20 @@ static void filterHomebrewHeaderPaths(HeaderPaths &headerPaths) static HeaderPaths gppInternalIncludePaths(const QString &compiler) { HeaderPaths result; - QStringList arguments; - arguments << QStringLiteral("-E") << QStringLiteral("-x") << QStringLiteral("c++") - << QStringLiteral("-") << QStringLiteral("-v"); + QStringList arguments{u"-E"_s, u"-x"_s, u"c++"_s, u"-"_s, u"-v"_s}; QByteArray stdOut; QByteArray stdErr; if (!runProcess(compiler, arguments, &stdOut, &stdErr)) return result; const QByteArrayList stdErrLines = stdErr.split('\n'); bool isIncludeDir = false; + + if (ReportHandler::isDebug(ReportHandler::MediumDebug)) + qCInfo(lcShiboken()).noquote().nospace() + << "gppInternalIncludePaths:\n compiler: " << compiler + << "\n stdOut: " << stdOut + << "\n stdErr: " << stdErr; + for (const QByteArray &line : stdErrLines) { if (isIncludeDir) { if (line.startsWith(QByteArrayLiteral("End of search list"))) { @@ -161,60 +209,27 @@ static HeaderPaths gppInternalIncludePaths(const QString &compiler) } } -# if defined(Q_OS_MACOS) - filterHomebrewHeaderPaths(result); -# endif + if (platform() == Platform::macOS) + filterHomebrewHeaderPaths(result); + return result; } -#endif // Q_CC_MSVC // Detect Vulkan as supported from Qt 5.10 by checking the environment variables. -static void detectVulkan(HeaderPaths *headerPaths) +QByteArrayList detectVulkan() { static const char *vulkanVariables[] = {"VULKAN_SDK", "VK_SDK_PATH"}; for (const char *vulkanVariable : vulkanVariables) { if (qEnvironmentVariableIsSet(vulkanVariable)) { - const QByteArray path = qgetenv(vulkanVariable) + QByteArrayLiteral("/include"); - headerPaths->append(HeaderPath{path, HeaderType::System}); - break; + const auto option = QByteArrayLiteral("-isystem") + + qgetenv(vulkanVariable) + + QByteArrayLiteral("/include"); + return {option}; } } + return {}; } -#if defined(Q_CC_GNU) -enum class LinuxDistribution { RedHat, CentOs, Other }; - -static LinuxDistribution linuxDistribution() -{ - const QString &productType = QSysInfo::productType(); - if (productType == QLatin1String("rhel")) - return LinuxDistribution::RedHat; - if (productType.compare(QLatin1String("centos"), Qt::CaseInsensitive) == 0) - return LinuxDistribution::CentOs; - return LinuxDistribution::Other; -} - -static bool checkProductVersion(const QVersionNumber &minimum, - const QVersionNumber &excludedMaximum) -{ - const QVersionNumber osVersion = QVersionNumber::fromString(QSysInfo::productVersion()); - return osVersion.isNull() || (osVersion >= minimum && osVersion < excludedMaximum); -} - -static inline bool needsGppInternalHeaders() -{ - const LinuxDistribution distro = linuxDistribution(); - switch (distro) { - case LinuxDistribution::RedHat: - case LinuxDistribution::CentOs: - return checkProductVersion(QVersionNumber(6, 10), QVersionNumber(8)); - case LinuxDistribution::Other: - break; - } - return false; -} -#endif // Q_CC_GNU - // For MSVC, we set the MS compatibility version and let Clang figure out its own // options and include paths. // For the others, we pass "-nostdinc" since libclang tries to add it's own system @@ -222,9 +237,7 @@ static inline bool needsGppInternalHeaders() // which causes std types not being found and construct -I/-F options from the // include paths of the host compiler. -#ifdef Q_CC_CLANG static QByteArray noStandardIncludeOption() { return QByteArrayLiteral("-nostdinc"); } -#endif // The clang builtin includes directory is used to find the definitions for // intrinsic functions and builtin types. It is necessary to use the clang @@ -234,35 +247,40 @@ static QByteArray noStandardIncludeOption() { return QByteArrayLiteral("-nostdin // Besides g++/Linux, as of MSVC 19.28.29334, MSVC needs clang includes // due to PYSIDE-1433, LLVM-47099 -#if !defined(Q_OS_DARWIN) -# define NEED_CLANG_BUILTIN_INCLUDES 1 -#else -# define NEED_CLANG_BUILTIN_INCLUDES 0 -#endif -#if NEED_CLANG_BUILTIN_INCLUDES +static bool needsClangBuiltinIncludes() +{ + return platform() != Platform::macOS; +} + +static QString queryLlvmConfigDir(const QString &arg) +{ + static const QString llvmConfig = QStandardPaths::findExecutable(u"llvm-config"_s); + if (llvmConfig.isEmpty()) + return {}; + QByteArray stdOut; + if (!runProcess(llvmConfig, QStringList{arg}, &stdOut)) + return {}; + const QString path = QFile::decodeName(stdOut.trimmed()); + if (!QFileInfo::exists(path)) { + qWarning(R"(%s: "%s" as returned by llvm-config "%s" does not exist.)", + __FUNCTION__, qPrintable(QDir::toNativeSeparators(path)), qPrintable(arg)); + return {}; + } + return path; +} + static QString findClangLibDir() { for (const char *envVar : {"LLVM_INSTALL_DIR", "CLANG_INSTALL_DIR"}) { if (qEnvironmentVariableIsSet(envVar)) { - const QString path = QFile::decodeName(qgetenv(envVar)) + QLatin1String("/lib"); + const QString path = QFile::decodeName(qgetenv(envVar)) + u"/lib"_s; if (QFileInfo::exists(path)) return path; qWarning("%s: %s as pointed to by %s does not exist.", __FUNCTION__, qPrintable(path), envVar); } } - const QString llvmConfig = - QStandardPaths::findExecutable(QLatin1String("llvm-config")); - if (!llvmConfig.isEmpty()) { - QByteArray stdOut; - if (runProcess(llvmConfig, QStringList{QLatin1String("--libdir")}, &stdOut)) { - const QString path = QFile::decodeName(stdOut.trimmed()); - if (QFileInfo::exists(path)) - return path; - qWarning("%s: %s as returned by llvm-config does not exist.", __FUNCTION__, qPrintable(path)); - } - } - return QString(); + return queryLlvmConfigDir(u"--libdir"_s); } static QString findClangBuiltInIncludesDir() @@ -272,7 +290,7 @@ static QString findClangBuiltInIncludesDir() if (!clangPathLibDir.isEmpty()) { QString candidate; QVersionNumber lastVersionNumber(1, 0, 0); - const QString clangDirName = clangPathLibDir + QLatin1String("/clang"); + const QString clangDirName = clangPathLibDir + u"/clang"_s; QDir clangDir(clangDirName); const QFileInfoList versionDirs = clangDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); @@ -289,26 +307,46 @@ static QString findClangBuiltInIncludesDir() } } if (!candidate.isEmpty()) - return candidate + QStringLiteral("/include"); + return candidate + "/include"_L1; } - return QString(); + return queryLlvmConfigDir(u"--includedir"_s); } -#endif // NEED_CLANG_BUILTIN_INCLUDES -#if defined(Q_CC_CLANG) || defined(Q_CC_GNU) -static QString compilerFromCMake(const QString &defaultCompiler) +QString compilerFromCMake() { -// Added !defined(Q_OS_DARWIN) due to PYSIDE-1032 -# if defined(CMAKE_CXX_COMPILER) && !defined(Q_OS_DARWIN) - Q_UNUSED(defaultCompiler); +#ifdef CMAKE_CXX_COMPILER return QString::fromLocal8Bit(CMAKE_CXX_COMPILER); -# else - return defaultCompiler; -# endif +#else + return {}; +#endif +} + +// Return a compiler suitable for determining the internal include paths +static QString compilerFromCMake(const QString &defaultCompiler) +{ + if (!compilerPath().isEmpty()) + return compilerPath(); + // Exclude macOS since cmakeCompiler returns the full path instead of the + // /usr/bin/clang shim, which results in the default SDK sysroot path + // missing (PYSIDE-1032) + if (platform() == Platform::macOS) + return defaultCompiler; + QString cmakeCompiler = compilerFromCMake(); + if (cmakeCompiler.isEmpty()) + return defaultCompiler; + QFileInfo fi(cmakeCompiler); + // Should be absolute by default, but a user may specify -DCMAKE_CXX_COMPILER=cl.exe + if (fi.isRelative()) + return cmakeCompiler; + if (fi.exists()) + return fi.absoluteFilePath(); + // The compiler may not exist in case something like icecream or + // a non-standard-path was used on the build machine. Check + // the executable. + cmakeCompiler = QStandardPaths::findExecutable(fi.fileName()); + return cmakeCompiler.isEmpty() ? defaultCompiler : cmakeCompiler; } -#endif // Q_CC_CLANG, Q_CC_GNU -#if NEED_CLANG_BUILTIN_INCLUDES static void appendClangBuiltinIncludes(HeaderPaths *p) { const QString clangBuiltinIncludesDir = @@ -318,61 +356,48 @@ static void appendClangBuiltinIncludes(HeaderPaths *p) "(neither by checking the environment variables LLVM_INSTALL_DIR, CLANG_INSTALL_DIR " " nor running llvm-config). This may lead to parse errors."); } else { - qCInfo(lcShiboken, "CLANG builtins includes directory: %s", + qCInfo(lcShiboken, "CLANG v%d.%d, builtins includes directory: %s", + CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR, qPrintable(clangBuiltinIncludesDir)); p->append(HeaderPath{QFile::encodeName(clangBuiltinIncludesDir), HeaderType::System}); } } -#endif // NEED_CLANG_BUILTIN_INCLUDES // Returns clang options needed for emulating the host compiler QByteArrayList emulatedCompilerOptions() { -#if defined(Q_CC_GNU) - // Needed to silence a warning, but needsGppInternalHeaders is used below. - // This seems to be a compiler bug on macOS. - Q_UNUSED(needsGppInternalHeaders); -#endif QByteArrayList result; -#if defined(Q_CC_MSVC) - HeaderPaths headerPaths; - result.append(QByteArrayLiteral("-fms-compatibility-version=19.26.28806")); - result.append(QByteArrayLiteral("-fdelayed-template-parsing")); - result.append(QByteArrayLiteral("-Wno-microsoft-enum-value")); - // Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update) - result.append(QByteArrayLiteral("-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH")); -# if NEED_CLANG_BUILTIN_INCLUDES - appendClangBuiltinIncludes(&headerPaths); -# endif // NEED_CLANG_BUILTIN_INCLUDES - -#elif defined(Q_CC_CLANG) - HeaderPaths headerPaths = gppInternalIncludePaths(compilerFromCMake(QStringLiteral("clang++"))); - result.append(noStandardIncludeOption()); -#elif defined(Q_CC_GNU) HeaderPaths headerPaths; + switch (compiler()) { + case Compiler::Msvc: + result.append("-fms-compatibility-version="_ba + msvcCompatVersion()); + result.append(QByteArrayLiteral("-fdelayed-template-parsing")); + result.append(QByteArrayLiteral("-Wno-microsoft-enum-value")); + result.append("/Zc:__cplusplus"_ba); + // Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update) + result.append(QByteArrayLiteral("-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH")); + if (needsClangBuiltinIncludes()) + appendClangBuiltinIncludes(&headerPaths); + break; + case Compiler::Clang: + headerPaths.append(gppInternalIncludePaths(compilerFromCMake(u"clang++"_s))); + result.append(noStandardIncludeOption()); + break; + case Compiler::Gpp: + if (needsClangBuiltinIncludes()) + appendClangBuiltinIncludes(&headerPaths); -# if NEED_CLANG_BUILTIN_INCLUDES - appendClangBuiltinIncludes(&headerPaths); -# endif // NEED_CLANG_BUILTIN_INCLUDES - - // Append the c++ include paths since Clang is unable to find <list> etc - // on RHEL 7 with g++ 6.3 or CentOS 7.2. - // A fix for this has been added to Clang 5.0, so, the code can be removed - // once Clang 5.0 is the minimum version. - if (needsGppInternalHeaders()) { - const HeaderPaths gppPaths = gppInternalIncludePaths(compilerFromCMake(QStringLiteral("g++"))); + // Append the c++ include paths since Clang is unable to find + // <type_traits> etc (g++ 11.3). + const HeaderPaths gppPaths = gppInternalIncludePaths(compilerFromCMake(u"g++"_s)); for (const HeaderPath &h : gppPaths) { - if (h.path.contains("c++") - || h.path.contains("sysroot")) { // centOS + if (h.path.contains("c++") || h.path.contains("sysroot")) headerPaths.append(h); - } } + break; } -#else - HeaderPaths headerPaths; -#endif - detectVulkan(&headerPaths); + std::transform(headerPaths.cbegin(), headerPaths.cend(), std::back_inserter(result), HeaderPath::includeOption); return result; |