aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp327
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;