Source code
Revision control
Copy as Markdown
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2021 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:critical reason:execute-external-code
#include "qdir.h"
#include "qstringlist.h"
#include "qfile.h"
#if QT_CONFIG(settings)
#include "qresource.h"
#include "qsettings.h"
#endif
#include "qlibraryinfo.h"
#include "qlibraryinfo_p.h"
#include "qcoreapplication.h"
#include "private/qcoreapplication_p.h"
#include "private/qfilesystementry_p.h"
#include "archdetect.cpp"
#include "qconfig.cpp"
#if defined(Q_OS_APPLE)
# include "private/qcore_mac_p.h"
#endif
#if QT_CONFIG(relocatable) && QT_CONFIG(dlopen)
# include <dlfcn.h>
#endif
#if QT_CONFIG(relocatable) && defined(Q_OS_WIN)
# include <qt_windows.h>
#endif
#include <memory>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
extern void qDumpCPUFeatures(); // in qsimd.cpp
static QString fromRawStringView(QStringView string)
{
return QString::fromRawData(string.data(), string.size());
}
static bool pathIsRelative(const QString &path)
{
using FromInternalPath = QFileSystemEntry::FromInternalPath;
return !path.startsWith(':'_L1) && QFileSystemEntry(path, FromInternalPath{}).isRelative();
}
// Resolves and caches the Qt and application prefix roots. Resolution
// may be costly and is queried repeatedly during startup, so results
// are memoized. Wrapped in a Q_GLOBAL_STATIC (prefixCache) so lookups
// during static destruction fall back to resolving from scratch.
struct QLibraryPrefixes
{
static QString qtPrefix();
static QString appPrefix();
private:
// Pure resolvers, without caching, so they're usable even without a
// cache instance during static destruction. resolveAppPrefix() returns an
// empty string when no stable source is available yet; appPrefix()
// then falls back to the (uncached) working directory.
static QString resolveQtPrefix();
static QString resolveAppPrefix();
// The directory the application prefix is rooted at: the app bundle on
// Apple platforms, or the application directory once a QCoreApplication
// exists. Empty when no stable source is available yet.
static QString resolveAppDir();
// The qt.conf "Prefix" entry the application prefix is rooted at,
// or its "." default when there's no qt.conf (or settings) to read.
static QString qtConfPrefix();
// Memoizes resolve() in the given member. A resolver may return an
// empty string to signal that the result is not yet stable and should
// not be cached, in which case it is resolved anew on each call. During
// static destruction prefixCache() returns nullptr and resolve() is
// used directly.
static QString cached(QString QLibraryPrefixes::*member, QString (*resolve)());
QString m_qt;
QString m_app;
};
Q_GLOBAL_STATIC(QLibraryPrefixes, prefixCache)
QString QLibraryPrefixes::cached(QString QLibraryPrefixes::*member, QString (*resolve)())
{
QLibraryPrefixes *cache = prefixCache();
if (cache && !(cache->*member).isEmpty())
return cache->*member;
QString prefix = resolve();
if (cache && !prefix.isEmpty())
cache->*member = prefix;
return prefix;
}
#if QT_CONFIG(settings)
static std::unique_ptr<QSettings> findConfiguration();
struct QLibrarySettings
{
QLibrarySettings();
void load();
bool havePaths();
QSettings *configuration();
QVariant value(QLibraryInfo::LibraryPath location);
QStringList paths(QLibraryInfo::LibraryPath location);
std::unique_ptr<QSettings> settings;
bool configHasPaths;
bool reloadOnQAppAvailable;
};
Q_GLOBAL_STATIC(QLibrarySettings, qt_library_settings)
QLibrarySettings::QLibrarySettings() : configHasPaths(false), reloadOnQAppAvailable(false)
{
load();
}
QSettings *QLibrarySettings::configuration()
{
if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
load();
return settings.get();
}
bool QLibrarySettings::havePaths()
{
if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
load();
return configHasPaths;
}
void QLibrarySettings::load()
{
// If we get any settings here, those won't change when the application shows up.
settings = findConfiguration();
reloadOnQAppAvailable = !settings && !QCoreApplication::instanceExists();
if (settings) {
// This code needs to be in the regular library, as otherwise a qt.conf that
// works for qmake would break things for dynamically built Qt tools.
QStringList children = settings->childGroups();
configHasPaths = !children.contains("Platforms"_L1)
|| children.contains("Paths"_L1);
} else {
configHasPaths = false;
}
}
/*!
Returns the value for a given \a location from \c qt.conf
If no \c qt.conf is found, or the configuration doesn't
specify a value for the given \a path a null-variant is
returned.
\internal
*/
QVariant QLibrarySettings::value(QLibraryInfo::LibraryPath location)
{
const auto locationInfo = QLibraryInfoPrivate::locationInfo(location);
if (locationInfo.key.isNull())
return {};
QSettings *config = configuration();
if (!settings || !configHasPaths)
return {};
config->beginGroup("Paths"_L1);
auto cleanup = qScopeGuard([&]() { config->endGroup(); });
QVariant value = config->value(locationInfo.key);
if (!value.isValid() && !locationInfo.fallbackKey.isNull())
value = config->value(locationInfo.fallbackKey);
return value;
}
QStringList QLibrarySettings::paths(QLibraryInfo::LibraryPath location)
{
QStringList ret;
if (QVariant v = value(location); v.isValid()) {
if (auto *asList = get_if<QStringList>(&v))
ret = std::move(*asList);
else
ret << v.toString();
for (qsizetype i = 0, end = ret.size(); i < end; ++i)
ret[i] = QLibraryInfoPrivate::expandEnvVariables(ret[i]);
}
return ret;
}
namespace {
const QString *qtconfManualPath = nullptr;
}
void QLibraryInfoPrivate::setQtconfManualPath(const QString *path)
{
qtconfManualPath = path;
}
static std::unique_ptr<QSettings> findConfiguration()
{
if (qtconfManualPath)
return std::make_unique<QSettings>(*qtconfManualPath, QSettings::IniFormat);
QString qtconfig = QStringLiteral(":/qt/etc/qt.conf");
if (QResource(qtconfig, QLocale::c()).isValid())
return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
#if defined(Q_OS_APPLE)
CFBundleRef bundleRef = CFBundleGetMainBundle();
if (bundleRef) {
QCFType<CFURLRef> urlRef = CFBundleCopyResourceURL(bundleRef,
QCFString("qt.conf"_L1),
0,
0);
if (urlRef) {
QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle);
qtconfig = QDir::cleanPath(path);
if (QFile::exists(qtconfig))
return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
}
}
#endif
if (QCoreApplication::instanceExists()) {
QString pwd = QCoreApplication::applicationDirPath();
qtconfig = pwd + u"/qt" QT_STRINGIFY(QT_VERSION_MAJOR) ".conf"_s;
if (QFile::exists(qtconfig))
return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
qtconfig = pwd + u"/qt.conf";
if (QFile::exists(qtconfig))
return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
}
return nullptr; //no luck
}
QSettings *QLibraryInfoPrivate::configuration()
{
QLibrarySettings *ls = qt_library_settings();
return ls ? ls->configuration() : nullptr;
}
#endif // settings
void QLibraryInfoPrivate::reload()
{
#if QT_CONFIG(settings)
if (qt_library_settings.exists())
qt_library_settings->load();
#endif
if (prefixCache.exists())
*prefixCache = {};
}
/*!
\class QLibraryInfo
\inmodule QtCore
\brief The QLibraryInfo class provides information about the Qt libraries.
This class provides an abstraction for accessing information about the
Qt libraries which the application is using, such as run-time paths,
or the build configuration of the Qt libraries.
The default run-time paths depend on the Qt build configuration, as well
as whether the Qt libraries have been relocated, and possibly bundled along
with the application.
You can use a \c qt.conf file to override the default paths,
in case your deployment situation does not match the defaults.
For more information, see the \l {Using qt.conf} documentation.
\sa QSysInfo, {Using qt.conf}
*/
/*!
\internal
You cannot create a QLibraryInfo, instead only the static functions are available to query
information.
*/
QLibraryInfo::QLibraryInfo()
{ }
#if defined(Q_CC_CLANG) // must be before GNU, because clang claims to be GNU too
# define COMPILER_STRING __VERSION__ /* already includes the compiler's name */
#elif defined(Q_CC_GHS)
# define COMPILER_STRING "GHS " QT_STRINGIFY(__GHS_VERSION_NUMBER)
#elif defined(Q_CC_GNU)
# define COMPILER_STRING "GCC " __VERSION__
#elif defined(Q_CC_MSVC)
# if _MSC_VER < 1910
# define COMPILER_STRING "MSVC 2015"
# elif _MSC_VER < 1917
# define COMPILER_STRING "MSVC 2017"
# elif _MSC_VER < 1930
# define COMPILER_STRING "MSVC 2019"
# elif _MSC_VER < 1950
# define COMPILER_STRING "MSVC 2022"
# elif _MSC_VER < 2000
# define COMPILER_STRING "MSVC 2026"
# else
# define COMPILER_STRING "MSVC _MSC_VER " QT_STRINGIFY(_MSC_VER)
# endif
#else
# define COMPILER_STRING "<unknown compiler>"
#endif
#ifdef QT_NO_DEBUG
# define DEBUG_STRING " release"
#else
# define DEBUG_STRING " debug"
#endif
#ifdef QT_SHARED
# define SHARED_STRING " shared (dynamic)"
#else
# define SHARED_STRING " static"
#endif
static const char *qt_build_string() noexcept
{
return "Qt " QT_VERSION_STR " (" ARCH_FULL SHARED_STRING DEBUG_STRING " build; by " COMPILER_STRING ")";
}
/*!
Returns a string describing how this version of Qt was built.
\internal
\since 5.3
*/
const char *QLibraryInfo::build() noexcept
{
return qt_build_string();
}
/*!
\since 5.0
Returns \c true if this build of Qt was built with debugging enabled, or
false if it was built in release mode.
*/
bool
QLibraryInfo::isDebugBuild() noexcept
{
#ifdef QT_DEBUG
return true;
#else
return false;
#endif
}
/*!
\since 6.5
Returns \c true if this is a shared (dynamic) build of Qt.
*/
bool QLibraryInfo::isSharedBuild() noexcept
{
#ifdef QT_SHARED
return true;
#else
return false;
#endif
}
/*!
\since 5.8
Returns the version of the Qt library.
\sa qVersion()
*/
QVersionNumber QLibraryInfo::version() noexcept
{
return QVersionNumber(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH);
}
/*!
\internal
Returns the qt.conf "Prefix" entry the application prefix is rooted at,
falling back to its "." default when there's no qt.conf with paths (or
settings support), so the prefix then resolves to the application directory.
*/
QString QLibraryPrefixes::qtConfPrefix()
{
#if QT_CONFIG(settings)
QLibrarySettings *settings = qt_library_settings();
if (settings && settings->havePaths()) {
const QString prefix = settings->paths(QLibraryInfo::PrefixPath).value(0);
if (!prefix.isEmpty())
return prefix;
}
#endif
return u"."_s;
}
/*!
\internal
The directory the application prefix is rooted at: the app bundle on
Apple platforms, or the application directory once a QCoreApplication
exists. Returns an empty string when no stable source is available yet.
*/
QString QLibraryPrefixes::resolveAppDir()
{
#if defined(Q_OS_APPLE)
// Resolve the app prefix from the app bundle instead of the
// executable, as that correctly handles both the main executable
// as well as possibly deeply nested helper tools, and lets us
// root the app prefix at the base of the bundle. Note that
// CFBundleGetMainBundle returns a bundle representation even
// for unbundled apps, so we verify the URL is actually a bundle.
if (CFBundleRef bundleRef = CFBundleGetMainBundle()) {
if (QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef)) {
const QString bundlePath = QCFString(CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle));
if (qt_apple_bundleType(bundlePath)) {
#if defined(Q_OS_MACOS)
// On macOS the prefix is rooted at the bundle's Contents directory.
return QFileInfo(bundlePath + "/Contents"_L1).canonicalFilePath();
#else
return QFileInfo(bundlePath).canonicalFilePath(); // iOS
#endif // Q_OS_MACOS
}
}
}
#endif // Q_OS_APPLE
if (QCoreApplication::instanceExists()) {
// We make the prefix path absolute to the executable's directory.
return QCoreApplication::applicationDirPath();
}
// Without an application instance there's no stable source; return an
// empty string so the result isn't cached, and let appPrefix() fall back
// to the (volatile) working directory until a stable source is available.
return {};
}
/*!
\internal
The fully resolved application prefix: the qt.conf "Prefix" entry
(explicit, or the "." default) rooted at the application directory.
An absolute Prefix is used as-is, independent of the application
directory. Without a qt.conf the prefix is the application directory
itself, which is also what static builds use as their Qt prefix.
Returns an empty string when the result would depend on a not-yet-stable
application directory, so it isn't cached; appPrefix() then roots the
prefix at the (volatile) working directory until a stable source exists.
*/
QString QLibraryPrefixes::resolveAppPrefix()
{
const QString prefix = qtConfPrefix();
// An absolute qt.conf Prefix is stable on its own, regardless of appDir.
if (!pathIsRelative(prefix))
return prefix;
const QString appDir = resolveAppDir();
if (appDir.isEmpty())
return {};
return QDir::cleanPath(appDir + u'/' + prefix);
}
QString QLibraryPrefixes::appPrefix()
{
QString prefix = cached(&QLibraryPrefixes::m_app, &QLibraryPrefixes::resolveAppPrefix);
if (!prefix.isNull())
return prefix;
// No stable source yet; root the possible qt.conf Prefix at the volatile
// working directory, without caching, until one is available.
return QDir::cleanPath(QDir::currentPath() + u'/' + qtConfPrefix());
}
#if QT_CONFIG(relocatable)
#if !defined(QT_STATIC) && (QT_CONFIG(dlopen) || defined(Q_OS_WIN))
static QString prefixFromQtCoreLibraryHelper(const QString &qtCoreLibraryPath)
{
const QString qtCoreLibrary = QDir::fromNativeSeparators(qtCoreLibraryPath);
QString libDir = QFileInfo(qtCoreLibrary).absolutePath();
#if QT_CONFIG(framework)
# if defined(Q_OS_MACOS)
// The library in a macOS framework lives in a `Versions/A/` subdirectory
libDir += "/../.."_L1;
# endif
// And both macOS and iOS frameworks are `.framework` bundle directories
libDir += "/.."_L1;
#endif
const QString prefixDir = libDir + "/" QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH;
return QDir::cleanPath(prefixDir);
}
#endif
#if defined(Q_OS_WIN)
static HMODULE getWindowsModuleHandle()
{
HMODULE hModule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)&QLibraryInfo::isDebugBuild, &hModule);
return hModule;
}
#endif // Q_OS_WIN
static QString relocatablePrefixFromQt()
{
QString prefixPath;
// For static builds, the prefix will be the app directory, unless it's a non-sandboxed Apple app.
// For regular builds, the prefix will be relative to the location of the QtCore shared library.
#if defined(QT_STATIC)
# if defined(Q_OS_APPLE)
// The default value for QT_CONFIG(relocatable) for static builds
// used to be false, giving end users QT_CONFIGURE_PREFIX_PATH as
// the prefix path. We've since changed the default to true for
// Apple platforms, since sandboxed applications are always
// relocated, even for static builds. To avoid regressions for
// those that shipped non-sandboxed apps with static Qt relying
// on the previous behavior, we fall back to the Qt configure
// prefix for non-sandboxed apps.
if (!qt_apple_isSandboxed())
return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH);
# endif
prefixPath = QLibraryPrefixes::appPrefix();
#elif defined(Q_OS_WASM)
// Emscripten expects to find shared libraries at the root of the in-memory
// file system when resolving dependencies for for dlopen() calls. So that's
// where libqt6core.so would be.
prefixPath = QStringLiteral("/");
#elif QT_CONFIG(dlopen)
Dl_info info;
int result = dladdr(reinterpret_cast<void *>(&QLibraryInfo::isDebugBuild), &info);
if (result > 0 && info.dli_fname)
prefixPath = prefixFromQtCoreLibraryHelper(QString::fromLocal8Bit(info.dli_fname));
#elif defined(Q_OS_WIN)
HMODULE hModule = getWindowsModuleHandle();
const int kBufferSize = 4096;
wchar_t buffer[kBufferSize];
DWORD pathSize = GetModuleFileName(hModule, buffer, kBufferSize);
const QString qtCoreFilePath = QString::fromWCharArray(buffer, int(pathSize));
const QString qtCoreDirPath = QFileInfo(qtCoreFilePath).absolutePath();
hModule = reinterpret_cast<HMODULE>(QCoreApplicationPrivate::mainInstanceHandle);
pathSize = GetModuleFileName(hModule, buffer, kBufferSize);
const QString exeDirPath = QFileInfo(QString::fromWCharArray(buffer, int(pathSize))).absolutePath();
if (QFileInfo(exeDirPath) == QFileInfo(qtCoreDirPath)) {
// QtCore DLL is next to the executable. This is either a windeployqt'ed executable or an
// executable within the QT_HOST_BIN directory. We're detecting the latter case by checking
// whether there's an import library corresponding to our QtCore DLL in PREFIX/lib.
QStringView libdir = qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1);
const QLatin1Char slash('/');
#if defined(Q_CC_MINGW)
const QString implibPrefix = QStringLiteral("lib");
const QString implibSuffix = QStringLiteral(".a");
#else
const QString implibPrefix;
const QString implibSuffix = QStringLiteral(".lib");
#endif
const QString qtCoreImpLibFileName = implibPrefix
+ QFileInfo(qtCoreFilePath).completeBaseName() + implibSuffix;
const QString qtCoreImpLibPath = qtCoreDirPath
+ slash + QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH
+ slash + libdir
+ slash + qtCoreImpLibFileName;
if (!QFileInfo::exists(qtCoreImpLibPath)) {
// We did not find a corresponding import library and conclude that this is a
// windeployqt'ed executable.
return exeDirPath;
}
}
if (!qtCoreFilePath.isEmpty())
prefixPath = prefixFromQtCoreLibraryHelper(qtCoreFilePath);
#else
#error "The chosen platform / config does not support querying for a dynamic prefix."
#endif
#if defined(Q_OS_LINUX) && !defined(QT_STATIC) && defined(__GLIBC__)
// QTBUG-78948: libQt5Core.so may be located in subdirectories below libdir.
// See "Hardware capabilities" in the ld.so documentation and the Qt 5.3.0
// changelog regarding SSE2 support.
QString libdir = fromRawStringView(qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1));
QDir prefixDir(prefixPath);
while (!prefixDir.exists(libdir)) {
prefixDir.cdUp();
prefixPath = prefixDir.absolutePath();
if (prefixDir.isRoot()) {
prefixPath.clear();
break;
}
}
#endif
Q_ASSERT_X(!prefixPath.isEmpty(), "relocatablePrefixFromQt",
"Failed to find the Qt prefix path.");
return prefixPath;
}
#endif
QString QLibraryPrefixes::resolveQtPrefix()
{
#if QT_CONFIG(relocatable)
return relocatablePrefixFromQt();
#else
return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH);
#endif
}
QString QLibraryPrefixes::qtPrefix()
{
#if defined(QT_STATIC)
// In static builds the Qt prefix resolves via the app directory (see
// relocatablePrefixFromQt), which is not stable until an application
// instance exists, and is cached and invalidated by appPrefix().
// Don't cache it a second time here, to avoid freezing a transient
// working-directory value.
return resolveQtPrefix();
#else
return cached(&QLibraryPrefixes::m_qt, &QLibraryPrefixes::resolveQtPrefix);
#endif
}
QLibraryInfoPrivate::LocationInfo QLibraryInfoPrivate::locationInfo(QLibraryInfo::LibraryPath location)
{
/*
* To add a new entry in QLibraryInfo::LibraryPath, add it to the enum
* in qtbase/src/corelib/global/qlibraryinfo.h and:
* - add its relative path in the qtConfEntries[] array below
* (the key is what appears in a qt.conf file)
*/
static constexpr auto qtConfEntries = qOffsetStringArray(
"Prefix", ".",
"Documentation", "doc", // should be ${Data}/doc
"Headers", "include",
"Libraries", "lib",
#ifdef Q_OS_WIN
"LibraryExecutables", "bin",
#else
"LibraryExecutables", "libexec", // should be ${ArchData}/libexec
#endif
"Binaries", "bin",
"Plugins", "plugins", // should be ${ArchData}/plugins
"QmlImports", "qml", // should be ${ArchData}/qml
"ArchData", ".",
"Data", ".",
"Translations", "translations", // should be ${Data}/translations
"Examples", "examples",
"Tests", "tests"
);
[[maybe_unused]]
constexpr QByteArrayView dot{"."};
LocationInfo result;
if (int(location) < qtConfEntries.count()) {
result.key = QLatin1StringView(qtConfEntries.viewAt(location * 2));
result.defaultValue = QLatin1StringView(qtConfEntries.viewAt(location * 2 + 1));
if (result.key == u"QmlImports")
result.fallbackKey = u"Qml2Imports"_s;
#if !defined(Q_OS_WIN) // On Windows we use the registry
} else if (location == QLibraryInfo::SettingsPath) {
result.key = "Settings"_L1;
result.defaultValue = QLatin1StringView(dot);
#endif
}
return result;
}
#if defined(Q_OS_APPLE)
/*!
\internal
Relative suffixes for the modern Apple bundle layout, reported in addition to
the unixy scheme when a prefix is a bundle. Root-agnostic: the same suffixes
apply whether rooted at the app or the Qt prefix. Mirrors the conventions that
macdeployqt and the CMake deployment machinery follow. Returns empty for
locations with no modern equivalent (only the unixy suffix is then reported).
*/
static QStringList appleBundleSuffixes(QLibraryInfo::LibraryPath location)
{
constexpr bool isMacOS =
QOperatingSystemVersion::currentType() == QOperatingSystemVersion::MacOS;
constexpr QStringView resourcesDir = isMacOS ? u"Resources/" : u"./";
constexpr QStringView sharedSupportDir = u"SharedSupport/";
switch (location) {
case QLibraryInfo::BinariesPath:
return { isMacOS ? u"MacOS"_s : u"."_s };
case QLibraryInfo::PluginsPath:
return { u"PlugIns"_s };
case QLibraryInfo::LibrariesPath:
return { u"Frameworks"_s };
case QLibraryInfo::HeadersPath:
return { u"Headers"_s };
case QLibraryInfo::LibraryExecutablesPath:
return { u"Helpers"_s };
case QLibraryInfo::DataPath:
return { resourcesDir.toString() };
case QLibraryInfo::QmlImportsPath:
return { resourcesDir + u"qml"_s };
case QLibraryInfo::ArchDataPath:
return { resourcesDir + QStringLiteral(ARCH_PROCESSOR) };
case QLibraryInfo::TranslationsPath:
return { resourcesDir + u"translations"_s };
case QLibraryInfo::DocumentationPath:
return { sharedSupportDir + u"doc"_s };
case QLibraryInfo::ExamplesPath:
return { sharedSupportDir + u"examples"_s };
case QLibraryInfo::TestsPath:
return { sharedSupportDir + u"tests"_s };
case QLibraryInfo::PrefixPath:
case QLibraryInfo::SettingsPath:
return {};
}
Q_UNREACHABLE_RETURN({});
}
#endif
/*! \fn QString QLibraryInfo::location(LibraryLocation loc)
\deprecated [6.0] Use path() instead.
Returns the path specified by \a loc.
*/
/*!
\since 6.0
Returns the path specified by \a p.
If there is more than one path listed in qt.conf, it will
only return the first one.
\sa paths
*/
QString QLibraryInfo::path(LibraryPath p)
{
return QLibraryInfoPrivate::path(p);
}
/*!
\since 6.8
Returns all paths specificied by \a p.
\sa path
*/
QStringList QLibraryInfo::paths(LibraryPath p)
{
return QLibraryInfoPrivate::paths(p);
}
/*!
Returns whether we have a user-written qt.conf with explicit paths.
A user-written qt.conf is an expression of exclusivity: the
application declares the layout it follows, so its paths replace the Qt
ones, and the stable fallback-values from locationInfo() are rooted at the
application prefix for the locations the qt.conf leaves out.
A qt.conf with MergeQtConf = true is an artifact generated by the
Qt QML CMake machinery to support development builds, and does not
qualify as a user-written qt.conf.
\internal
*/
static bool haveUserQtConf()
{
#if QT_CONFIG(settings)
if (QLibrarySettings *qtConf = qt_library_settings()) {
if (!qtConf->havePaths())
return false;
if (qtConf->configuration()->value("Config/MergeQtConf", false).toBool())
return false;
return true;
}
#endif
return false;
}
#if QT_CONFIG(settings)
QString QLibraryInfoPrivate::expandEnvVariables(QString ret)
{
qsizetype startIndex = 0;
/* We support placeholders of the form $(<ENV_VAR>) in qt.conf.
The loop below tries to find all such placeholders, and replaces
them with the actual value of the ENV_VAR environment variable
*/
while (true) {
startIndex = ret.indexOf(u'$', startIndex);
if (startIndex < 0)
break;
if (ret.size() < startIndex + 3)
break;
if (ret.at(startIndex + 1) != u'(') {
startIndex++;
continue;
}
qsizetype endIndex = ret.indexOf(u')', startIndex + 2);
if (endIndex < 0)
break;
auto envVarName = QStringView{ret}.sliced(startIndex + 2, endIndex - startIndex - 2);
QString value = qEnvironmentVariable(envVarName.toLocal8Bit().constData());
ret.replace(startIndex, endIndex - startIndex + 1, value);
startIndex += value.size();
}
return QDir::fromNativeSeparators(ret);
}
#endif // settings
/*!
\internal
Roots any relative entries in \a paths at the directory returned by
\a resolveBaseDir, which is resolved lazily (only when there's actually a
relative path to root, and at most once).
*/
static QStringList makeAbsolute(QStringList paths, QString (*resolveBaseDir)())
{
QString baseDir;
for (QString &path : paths) {
if (pathIsRelative(path)) {
if (baseDir.isNull())
baseDir = resolveBaseDir();
path = QDir::cleanPath(baseDir + u'/' + std::move(path));
}
}
return paths;
}
#if defined(Q_OS_APPLE)
/*!
\internal
Whether a resolved \a prefix is rooted at an Apple bundle. Path-based, so it
works for any prefix (the app or the Qt prefix), not just the main bundle.
*/
static bool prefixIsBundle(const QString &prefix)
{
QString bundlePath = prefix;
#if defined(Q_OS_MACOS)
// On macOS the prefix is rooted at the bundle's Contents directory.
// On iOS/visionOS the prefix already is the bundle directory.
const QFileSystemEntry prefixInfo(prefix, QFileSystemEntry::FromInternalPath());
if (prefixInfo.fileName() == "Contents"_L1)
bundlePath = prefixInfo.path();
#endif
return bool(qt_apple_bundleType(bundlePath));
}
#endif
/*!
\internal
All the paths \a location can resolve to, in order of priority, deduplicated.
*/
QStringList QLibraryInfoPrivate::paths(QLibraryInfo::LibraryPath location)
{
// The paths a qt.conf lists explicitly always come first, as they are the only
// ones anyone declared. What follows depends on whether the qt.conf was written
// by the user, as described in haveUserQtConf().
QStringList paths = qtConfPaths(location);
if (!haveUserQtConf()) {
// Without a user-written qt.conf the Qt-prefixed paths come first, so that an
// application running against a non-bundled Qt keeps reporting that Qt's paths
// first, for compatibility, as that's what consumers using path() rely on.
paths += qtPaths(location);
}
// We always report the app-rooted paths, to allow picking up libraries,
// plugins, and other resources from the app's location during both development
// and deployment. And as we use paths() in Qt itself, for e.g. plugin or QML
// module lookup, we will find them either way. This also covers an app bundle
// nested inside an outer bundle that bundles Qt, for example an app extension
// inside its main application, where each bundle may hold part of the
// deployment story.
paths += appPaths(location);
paths.removeDuplicates();
return paths;
}
/*!
\internal
The paths a qt.conf lists explicitly for \a location, rooted at the app
prefix. Empty if there is no qt.conf, or it doesn't list the location.
Both a user-written and a generated qt.conf are reported here. A generated
one merges with the Qt build defaults, and is an overlay: the locations it
lists still take priority, as that's the point of the mechanism, but it
neither suppresses the other paths, nor implies anything about the locations
it leaves out.
*/
QStringList QLibraryInfoPrivate::qtConfPaths(QLibraryInfo::LibraryPath location)
{
#if QT_CONFIG(settings)
if (QLibrarySettings *qtConfSettings = qt_library_settings()) {
if (location == QLibraryInfo::PrefixPath) {
// An explicit Prefix is resolved against the application directory,
// and not the app prefix like the other values, as we'd otherwise
// end up double-applying any relative paths in the prefix.
if (!qtConfSettings->paths(location).isEmpty())
return { QLibraryPrefixes::appPrefix() };
else
return {};
}
return makeAbsolute(qtConfSettings->paths(location), &QLibraryPrefixes::appPrefix);
}
#else
Q_UNUSED(location);
#endif
return {};
}
/*!
\internal
The paths implied by the application's own layout for \a location, rooted at
the app prefix.
We add the modern Apple suffixes when the app prefix is a bundle, followed by
the Unixy stable fallback-value when a user-written qt.conf says the application
follows that layout.
Returns empty when neither applies: a plain executable with no qt.conf, or one
with only a merging qt.conf, which doesn't imply that the application has paths
of its own. A bundle always reports its Apple suffixes, whatever the qt.conf says.
*/
QStringList QLibraryInfoPrivate::appPaths(QLibraryInfo::LibraryPath location)
{
bool appPrefixIsBundle = false;
#if defined(Q_OS_APPLE)
appPrefixIsBundle = prefixIsBundle(QLibraryPrefixes::appPrefix());
#endif
const bool appDeclaresItsLayout = haveUserQtConf();
// The prefix itself is reported whenever anything implies that the
// application has paths of its own, so that the application prefix stays
// discoverable via QLibraryInfo, but not for a generated qt.conf alone, as
// that only overlays the locations it lists.
if (location == QLibraryInfo::PrefixPath) {
if (appPrefixIsBundle || appDeclaresItsLayout)
return { QLibraryPrefixes::appPrefix() };
return {};
}
QStringList paths;
#if defined(Q_OS_APPLE)
if (appPrefixIsBundle)
paths += appleBundleSuffixes(location);
#endif
// A bundle on its own only implies the modern Apple layout. The Unixy
// layout is legacy in a bundle, and outside of one it's not implied by
// anything but a user-written qt.conf, so we leave it to the Qt prefix
// otherwise.
if (appDeclaresItsLayout) {
const auto locationInfo = QLibraryInfoPrivate::locationInfo(location);
if (!locationInfo.defaultValue.isNull())
paths << locationInfo.defaultValue;
}
return makeAbsolute(std::move(paths), &QLibraryPrefixes::appPrefix);
}
/*!
\internal
The Qt-prefixed paths for \a location, rooted at the Qt prefix. The Unixy
configure-time suffix is preceded by the modern Apple suffixes when the Qt
prefix is itself a bundle, which is the case when Qt was deployed into an
application bundle.
*/
QStringList QLibraryInfoPrivate::qtPaths(QLibraryInfo::LibraryPath location)
{
if (location == QLibraryInfo::PrefixPath)
return { QLibraryPrefixes::qtPrefix() };
QStringList paths;
#if defined(Q_OS_APPLE)
if (prefixIsBundle(QLibraryPrefixes::qtPrefix()))
paths += appleBundleSuffixes(location);
#endif
if (int(location) <= qt_configure_strs.count()) {
paths << fromRawStringView(qt_configure_strs.viewAt(location - 1));
#if !defined(Q_OS_WIN) // On Windows we use the registry
} else if (location == QLibraryInfo::SettingsPath) {
constexpr QStringView path = u"" QT_CONFIGURE_SETTINGS_PATH;
paths << fromRawStringView(path);
#endif
}
return makeAbsolute(std::move(paths), &QLibraryPrefixes::qtPrefix);
}
/*
Returns the path specified by \a p.
*/
QString QLibraryInfoPrivate::path(QLibraryInfo::LibraryPath location)
{
return paths(location).value(0, QString());
}
/*!
Returns additional arguments to the platform plugin matching
\a platformName which can be specified as a string list using
the key \c Arguments in a group called \c Platforms of the
\c qt.conf file.
sa {Using qt.conf}
\internal
\since 5.3
*/
QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
{
#if QT_CONFIG(settings)
if (const QSettings *settings = QLibraryInfoPrivate::configuration()) {
const QString key = "Platforms/"_L1
+ platformName
+ "Arguments"_L1;
return settings->value(key).toStringList();
}
#else
Q_UNUSED(platformName);
#endif // settings
return QStringList();
}
/*!
\enum QLibraryInfo::LibraryPath
\keyword library location
This enum type is used to query for a specific path:
\value PrefixPath The default prefix for all paths.
\value DocumentationPath The path to documentation upon install.
\value HeadersPath The path to all headers.
\value LibrariesPath The path to installed libraries.
\value LibraryExecutablesPath The path to installed executables required by libraries at runtime.
\value BinariesPath The path to installed Qt binaries (tools and applications).
\value PluginsPath The path to installed Qt plugins.
\value QmlImportsPath The path to installed QML extensions to import.
\value Qml2ImportsPath This value is deprecated. Use QmlImportsPath instead.
\value ArchDataPath The path to general architecture-dependent Qt data.
\value DataPath The path to general architecture-independent Qt data.
\value TranslationsPath The path to translation information for Qt strings.
\value ExamplesPath The path to examples upon install.
\value TestsPath The path to installed Qt testcases.
\value SettingsPath The path to Qt settings. Not applicable on Windows.
\sa path()
*/
/*!
\typealias QLibraryInfo::LibraryLocation
\deprecated [6.0] Use LibraryPath with QLibraryInfo::path() instead.
*/
/*!
\headerfile <QtVersion>
\inmodule QtCore
\ingroup funclists
\brief Information about which Qt version the application is running on,
and the version it was compiled against.
*/
/*!
\macro QT_VERSION_STR
\relates <QtVersion>
This macro expands to a string that specifies Qt's version number (for
example, "6.1.2"). This is the version with which the application is
compiled. This may be a different version than the version the application
will find itself using at \e runtime.
\sa qVersion(), QT_VERSION
*/
/*!
\relates <QtVersion>
Returns the version number of Qt at runtime as a string (for example,
"6.1.2"). This is the version of the Qt library in use at \e runtime,
which need not be the version the application was \e compiled with.
\sa QT_VERSION_STR, QLibraryInfo::version()
*/
const char *qVersion() noexcept
{
return QT_VERSION_STR;
}
#if QT_DEPRECATED_SINCE(6, 9)
bool qSharedBuild() noexcept
{
return QLibraryInfo::isSharedBuild();
}
#endif // QT_DEPRECATED_SINCE(6, 9)
QT_END_NAMESPACE
#if defined(Q_CC_GNU) && defined(ELF_INTERPRETER)
# include <elf.h>
# include <stdio.h>
# include <stdlib.h>
#include "private/qcoreapplication_p.h"
QT_WARNING_DISABLE_GCC("-Wformat-overflow")
QT_WARNING_DISABLE_GCC("-Wattributes")
QT_WARNING_DISABLE_CLANG("-Wattributes")
QT_WARNING_DISABLE_INTEL(2621)
# if defined(Q_OS_LINUX)
# include "minimum-linux_p.h"
# endif
# ifdef QT_ELF_NOTE_OS_TYPE
struct ElfNoteAbiTag
{
static_assert(sizeof(Elf32_Nhdr) == sizeof(Elf64_Nhdr),
"The size of an ELF note is wrong (should be 12 bytes)");
struct Payload {
Elf32_Word ostype = QT_ELF_NOTE_OS_TYPE;
Elf32_Word major = QT_ELF_NOTE_OS_MAJOR;
Elf32_Word minor = QT_ELF_NOTE_OS_MINOR;
# ifdef QT_ELF_NOTE_OS_PATCH
Elf32_Word patch = QT_ELF_NOTE_OS_PATCH;
# endif
};
Elf32_Nhdr header = {
.n_namesz = sizeof(name),
.n_descsz = sizeof(Payload),
.n_type = NT_GNU_ABI_TAG
};
char name[sizeof ELF_NOTE_GNU] = ELF_NOTE_GNU; // yes, include the null terminator
Payload payload = {};
};
__attribute__((section(".note.ABI-tag"), aligned(4), used))
extern constexpr ElfNoteAbiTag QT_MANGLE_NAMESPACE(qt_abi_tag) = {};
# endif
extern const char qt_core_interpreter[] __attribute__((section(".interp")))
= ELF_INTERPRETER;
extern "C" void qt_core_boilerplate() __attribute__((force_align_arg_pointer));
void qt_core_boilerplate()
{
QT_USE_NAMESPACE
printf("This is the QtCore library version %s\n"
"%s\n"
"\n",
QT_PREPEND_NAMESPACE(qt_build_string)(),
QT_COPYRIGHT);
QByteArray libPath =
qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1).toUtf8();
QByteArray pluginPath =
qt_configure_strs.viewAt(QLibraryInfo::PluginsPath - 1).toUtf8();
#if QT_CONFIG(relocatable)
QByteArray prefix = QLibraryPrefixes::qtPrefix().toUtf8();
#else
QByteArrayView prefix = QT_CONFIGURE_PREFIX_PATH;
#endif
printf("Installation prefix: %s\n"
"Library path: %s\n"
"Plugin path: %s\n",
prefix.data(),
libPath.data(),
pluginPath.data());
QT_PREPEND_NAMESPACE(qDumpCPUFeatures)();
exit(0);
}
#endif