Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlibraryinfo.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2021 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:execute-external-code
5
6#include "qdir.h"
7#include "qstringlist.h"
8#include "qfile.h"
9#if QT_CONFIG(settings)
10#include "qresource.h"
11#include "qsettings.h"
12#endif
13#include "qlibraryinfo.h"
14#include "qlibraryinfo_p.h"
15
17
18#include "private/qcoreapplication_p.h"
19#include "private/qfilesystementry_p.h"
20#include "archdetect.cpp"
21#include "qconfig.cpp"
22
23#if defined(Q_OS_APPLE)
24# include "private/qcore_mac_p.h"
25#endif
26
27#if QT_CONFIG(relocatable) && QT_CONFIG(dlopen)
28# include <dlfcn.h>
29#endif
30
31#if QT_CONFIG(relocatable) && defined(Q_OS_WIN)
32# include <qt_windows.h>
33#endif
34
35#include <memory>
36
37QT_BEGIN_NAMESPACE
38
39using namespace Qt::StringLiterals;
40
41extern void qDumpCPUFeatures(); // in qsimd.cpp
42
43static QString fromRawStringView(QStringView string)
44{
45 return QString::fromRawData(string.data(), string.size());
46}
47
48static bool pathIsRelative(const QString &path)
49{
50 using FromInternalPath = QFileSystemEntry::FromInternalPath;
51 return !path.startsWith(':'_L1) && QFileSystemEntry(path, FromInternalPath{}).isRelative();
52}
53
54// Resolves and caches the Qt and application prefix roots. Resolution
55// may be costly and is queried repeatedly during startup, so results
56// are memoized. Wrapped in a Q_GLOBAL_STATIC (prefixCache) so lookups
57// during static destruction fall back to resolving from scratch.
59{
60 static QString qtPrefix();
62
63private:
64 // Pure resolvers, without caching, so they're usable even without a
65 // cache instance during static destruction. resolveAppPrefix() returns an
66 // empty string when no stable source is available yet; appPrefix()
67 // then falls back to the (uncached) working directory.
68 static QString resolveQtPrefix();
69 static QString resolveAppPrefix();
70
71 // The directory the application prefix is rooted at: the app bundle on
72 // Apple platforms, or the application directory once a QCoreApplication
73 // exists. Empty when no stable source is available yet.
74 static QString resolveAppDir();
75
76 // The qt.conf "Prefix" entry the application prefix is rooted at,
77 // or its "." default when there's no qt.conf (or settings) to read.
78 static QString qtConfPrefix();
79
80 // Memoizes resolve() in the given member. A resolver may return an
81 // empty string to signal that the result is not yet stable and should
82 // not be cached, in which case it is resolved anew on each call. During
83 // static destruction prefixCache() returns nullptr and resolve() is
84 // used directly.
85 static QString cached(QString QLibraryPrefixes::*member, QString (*resolve)());
86
87 QString m_qt;
88 QString m_app;
89};
90Q_GLOBAL_STATIC(QLibraryPrefixes, prefixCache)
91
92QString QLibraryPrefixes::cached(QString QLibraryPrefixes::*member, QString (*resolve)())
93{
94 QLibraryPrefixes *cache = prefixCache();
95 if (cache && !(cache->*member).isEmpty())
96 return cache->*member;
97 QString prefix = resolve();
98 if (cache && !prefix.isEmpty())
99 cache->*member = prefix;
100 return prefix;
101}
102
103#if QT_CONFIG(settings)
104
105static std::unique_ptr<QSettings> findConfiguration();
106
107struct QLibrarySettings
108{
109 QLibrarySettings();
110 void load();
111 bool havePaths();
112 QSettings *configuration();
113 QVariant value(QLibraryInfo::LibraryPath location);
114 QStringList paths(QLibraryInfo::LibraryPath location);
115
116 std::unique_ptr<QSettings> settings;
117 bool configHasPaths;
118 bool reloadOnQAppAvailable;
119};
120Q_GLOBAL_STATIC(QLibrarySettings, qt_library_settings)
121
122QLibrarySettings::QLibrarySettings() : configHasPaths(false), reloadOnQAppAvailable(false)
123{
124 load();
125}
126
127QSettings *QLibrarySettings::configuration()
128{
129 if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
130 load();
131 return settings.get();
132}
133
134bool QLibrarySettings::havePaths()
135{
136 if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
137 load();
138 return configHasPaths;
139}
140
141void QLibrarySettings::load()
142{
143 // If we get any settings here, those won't change when the application shows up.
144 settings = findConfiguration();
145 reloadOnQAppAvailable = !settings && !QCoreApplication::instanceExists();
146
147 if (settings) {
148 // This code needs to be in the regular library, as otherwise a qt.conf that
149 // works for qmake would break things for dynamically built Qt tools.
150 QStringList children = settings->childGroups();
151 configHasPaths = !children.contains("Platforms"_L1)
152 || children.contains("Paths"_L1);
153 } else {
154 configHasPaths = false;
155 }
156}
157
158/*!
159 Returns the value for a given \a location from \c qt.conf
160
161 If no \c qt.conf is found, or the configuration doesn't
162 specify a value for the given \a path a null-variant is
163 returned.
164
165 \internal
166*/
167QVariant QLibrarySettings::value(QLibraryInfo::LibraryPath location)
168{
169 const auto locationInfo = QLibraryInfoPrivate::locationInfo(location);
170 if (locationInfo.key.isNull())
171 return {};
172
173 QSettings *config = configuration();
174 if (!settings || !configHasPaths)
175 return {};
176
177 config->beginGroup("Paths"_L1);
178 auto cleanup = qScopeGuard([&]() { config->endGroup(); });
179 QVariant value = config->value(locationInfo.key);
180 if (!value.isValid() && !locationInfo.fallbackKey.isNull())
181 value = config->value(locationInfo.fallbackKey);
182 return value;
183}
184
185QStringList QLibrarySettings::paths(QLibraryInfo::LibraryPath location)
186{
187 QStringList ret;
188 if (QVariant v = value(location); v.isValid()) {
189 if (auto *asList = get_if<QStringList>(&v))
190 ret = std::move(*asList);
191 else
192 ret << v.toString();
193
194 for (qsizetype i = 0, end = ret.size(); i < end; ++i)
195 ret[i] = QLibraryInfoPrivate::expandEnvVariables(ret[i]);
196 }
197 return ret;
198}
199
200namespace {
201const QString *qtconfManualPath = nullptr;
202}
203
204void QLibraryInfoPrivate::setQtconfManualPath(const QString *path)
205{
206 qtconfManualPath = path;
207}
208
209static std::unique_ptr<QSettings> findConfiguration()
210{
211 if (qtconfManualPath)
212 return std::make_unique<QSettings>(*qtconfManualPath, QSettings::IniFormat);
213
214 QString qtconfig = QStringLiteral(":/qt/etc/qt.conf");
215 if (QResource(qtconfig, QLocale::c()).isValid())
216 return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
217#if defined(Q_OS_APPLE)
218 CFBundleRef bundleRef = CFBundleGetMainBundle();
219 if (bundleRef) {
220 QCFType<CFURLRef> urlRef = CFBundleCopyResourceURL(bundleRef,
221 QCFString("qt.conf"_L1),
222 0,
223 0);
224 if (urlRef) {
225 QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle);
226 qtconfig = QDir::cleanPath(path);
227 if (QFile::exists(qtconfig))
228 return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
229 }
230 }
231#endif
232 if (QCoreApplication::instanceExists()) {
233 QString pwd = QCoreApplication::applicationDirPath();
234 qtconfig = pwd + u"/qt" QT_STRINGIFY(QT_VERSION_MAJOR) ".conf"_s;
235 if (QFile::exists(qtconfig))
236 return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
237 qtconfig = pwd + u"/qt.conf";
238 if (QFile::exists(qtconfig))
239 return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat);
240 }
241 return nullptr; //no luck
242}
243
244QSettings *QLibraryInfoPrivate::configuration()
245{
246 QLibrarySettings *ls = qt_library_settings();
247 return ls ? ls->configuration() : nullptr;
248}
249#endif // settings
250
251void QLibraryInfoPrivate::reload()
252{
253#if QT_CONFIG(settings)
254 if (qt_library_settings.exists())
255 qt_library_settings->load();
256#endif
257 if (prefixCache.exists())
258 *prefixCache = {};
259}
260
261/*!
262 \class QLibraryInfo
263 \inmodule QtCore
264 \brief The QLibraryInfo class provides information about the Qt libraries.
265
266 This class provides an abstraction for accessing information about the
267 Qt libraries which the application is using, such as run-time paths,
268 or the build configuration of the Qt libraries.
269
270 The default run-time paths depend on the Qt build configuration, as well
271 as whether the Qt libraries have been relocated, and possibly bundled along
272 with the application.
273
274 You can use a \c qt.conf file to override the default paths,
275 in case your deployment situation does not match the defaults.
276 For more information, see the \l {Using qt.conf} documentation.
277
278 \sa QSysInfo, {Using qt.conf}
279*/
280
281/*!
282 \internal
283
284 You cannot create a QLibraryInfo, instead only the static functions are available to query
285 information.
286*/
287
288QLibraryInfo::QLibraryInfo()
289{ }
290
291#if defined(Q_CC_CLANG) // must be before GNU, because clang claims to be GNU too
292# define COMPILER_STRING __VERSION__ /* already includes the compiler's name */
293#elif defined(Q_CC_GHS)
294# define COMPILER_STRING "GHS " QT_STRINGIFY(__GHS_VERSION_NUMBER)
295#elif defined(Q_CC_GNU)
296# define COMPILER_STRING "GCC " __VERSION__
297#elif defined(Q_CC_MSVC)
298# if _MSC_VER < 1910
299# define COMPILER_STRING "MSVC 2015"
300# elif _MSC_VER < 1917
301# define COMPILER_STRING "MSVC 2017"
302# elif _MSC_VER < 1930
303# define COMPILER_STRING "MSVC 2019"
304# elif _MSC_VER < 1950
305# define COMPILER_STRING "MSVC 2022"
306# elif _MSC_VER < 2000
307# define COMPILER_STRING "MSVC 2026"
308# else
309# define COMPILER_STRING "MSVC _MSC_VER " QT_STRINGIFY(_MSC_VER)
310# endif
311#else
312# define COMPILER_STRING "<unknown compiler>"
313#endif
314#ifdef QT_NO_DEBUG
315# define DEBUG_STRING " release"
316#else
317# define DEBUG_STRING " debug"
318#endif
319#ifdef QT_SHARED
320# define SHARED_STRING " shared (dynamic)"
321#else
322# define SHARED_STRING " static"
323#endif
324static const char *qt_build_string() noexcept
325{
326 return "Qt " QT_VERSION_STR " (" ARCH_FULL SHARED_STRING DEBUG_STRING " build; by " COMPILER_STRING ")";
327}
328
329/*!
330 Returns a string describing how this version of Qt was built.
331
332 \internal
333
334 \since 5.3
335*/
336
337const char *QLibraryInfo::build() noexcept
338{
339 return qt_build_string();
340}
341
342/*!
343 \since 5.0
344 Returns \c true if this build of Qt was built with debugging enabled, or
345 false if it was built in release mode.
346*/
347bool
348QLibraryInfo::isDebugBuild() noexcept
349{
350#ifdef QT_DEBUG
351 return true;
352#else
353 return false;
354#endif
355}
356
357/*!
358 \since 6.5
359 Returns \c true if this is a shared (dynamic) build of Qt.
360*/
361bool QLibraryInfo::isSharedBuild() noexcept
362{
363#ifdef QT_SHARED
364 return true;
365#else
366 return false;
367#endif
368}
369
370/*!
371 \since 5.8
372 Returns the version of the Qt library.
373
374 \sa qVersion()
375*/
376QVersionNumber QLibraryInfo::version() noexcept
377{
378 return QVersionNumber(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH);
379}
380
381/*!
382 \internal
383
384 Returns the qt.conf "Prefix" entry the application prefix is rooted at,
385 falling back to its "." default when there's no qt.conf with paths (or
386 settings support), so the prefix then resolves to the application directory.
387*/
388QString QLibraryPrefixes::qtConfPrefix()
389{
390#if QT_CONFIG(settings)
391 QLibrarySettings *settings = qt_library_settings();
392 if (settings && settings->havePaths()) {
393 const QString prefix = settings->paths(QLibraryInfo::PrefixPath).value(0);
394 if (!prefix.isEmpty())
395 return prefix;
396 }
397#endif
398 return u"."_s;
399}
400
401/*!
402 \internal
403
404 The directory the application prefix is rooted at: the app bundle on
405 Apple platforms, or the application directory once a QCoreApplication
406 exists. Returns an empty string when no stable source is available yet.
407*/
408QString QLibraryPrefixes::resolveAppDir()
409{
410#if defined(Q_OS_APPLE)
411 // Resolve the app prefix from the app bundle instead of the
412 // executable, as that correctly handles both the main executable
413 // as well as possibly deeply nested helper tools, and lets us
414 // root the app prefix at the base of the bundle. Note that
415 // CFBundleGetMainBundle returns a bundle representation even
416 // for unbundled apps, so we verify the URL is actually a bundle.
417 if (CFBundleRef bundleRef = CFBundleGetMainBundle()) {
418 if (QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef)) {
419 const QString bundlePath = QCFString(CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle));
420 if (qt_apple_bundleType(bundlePath)) {
421#if defined(Q_OS_MACOS)
422 // On macOS the prefix is rooted at the bundle's Contents directory.
423 return QFileInfo(bundlePath + "/Contents"_L1).canonicalFilePath();
424#else
425 return QFileInfo(bundlePath).canonicalFilePath(); // iOS
426#endif // Q_OS_MACOS
427 }
428 }
429 }
430#endif // Q_OS_APPLE
431
432 if (QCoreApplication::instanceExists()) {
433 // We make the prefix path absolute to the executable's directory.
434 return QCoreApplication::applicationDirPath();
435 }
436
437 // Without an application instance there's no stable source; return an
438 // empty string so the result isn't cached, and let appPrefix() fall back
439 // to the (volatile) working directory until a stable source is available.
440 return {};
441}
442
443/*!
444 \internal
445
446 The fully resolved application prefix: the qt.conf "Prefix" entry
447 (explicit, or the "." default) rooted at the application directory.
448 An absolute Prefix is used as-is, independent of the application
449 directory. Without a qt.conf the prefix is the application directory
450 itself, which is also what static builds use as their Qt prefix.
451
452 Returns an empty string when the result would depend on a not-yet-stable
453 application directory, so it isn't cached; appPrefix() then roots the
454 prefix at the (volatile) working directory until a stable source exists.
455*/
456QString QLibraryPrefixes::resolveAppPrefix()
457{
458 const QString prefix = qtConfPrefix();
459 // An absolute qt.conf Prefix is stable on its own, regardless of appDir.
460 if (!pathIsRelative(prefix))
461 return prefix;
462 const QString appDir = resolveAppDir();
463 if (appDir.isEmpty())
464 return {};
465 return QDir::cleanPath(appDir + u'/' + prefix);
466}
467
468QString QLibraryPrefixes::appPrefix()
469{
470 QString prefix = cached(&QLibraryPrefixes::m_app, &QLibraryPrefixes::resolveAppPrefix);
471 if (!prefix.isNull())
472 return prefix;
473
474 // No stable source yet; root the possible qt.conf Prefix at the volatile
475 // working directory, without caching, until one is available.
476 return QDir::cleanPath(QDir::currentPath() + u'/' + qtConfPrefix());
477}
478
479#if QT_CONFIG(relocatable)
480#if !defined(QT_STATIC) && (QT_CONFIG(dlopen) || defined(Q_OS_WIN))
481static QString prefixFromQtCoreLibraryHelper(const QString &qtCoreLibraryPath)
482{
483 const QString qtCoreLibrary = QDir::fromNativeSeparators(qtCoreLibraryPath);
484 QString libDir = QFileInfo(qtCoreLibrary).absolutePath();
485
486#if QT_CONFIG(framework)
487# if defined(Q_OS_MACOS)
488 // The library in a macOS framework lives in a `Versions/A/` subdirectory
489 libDir += "/../.."_L1;
490# endif
491 // And both macOS and iOS frameworks are `.framework` bundle directories
492 libDir += "/.."_L1;
493#endif
494
495 const QString prefixDir = libDir + "/" QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH;
496 return QDir::cleanPath(prefixDir);
497}
498#endif
499
500#if defined(Q_OS_WIN)
501static HMODULE getWindowsModuleHandle()
502{
503 HMODULE hModule = NULL;
504 GetModuleHandleEx(
505 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
506 (LPCTSTR)&QLibraryInfo::isDebugBuild, &hModule);
507 return hModule;
508}
509#endif // Q_OS_WIN
510
511static QString relocatablePrefixFromQt()
512{
513 QString prefixPath;
514
515 // For static builds, the prefix will be the app directory, unless it's a non-sandboxed Apple app.
516 // For regular builds, the prefix will be relative to the location of the QtCore shared library.
517#if defined(QT_STATIC)
518# if defined(Q_OS_APPLE)
519 // The default value for QT_CONFIG(relocatable) for static builds
520 // used to be false, giving end users QT_CONFIGURE_PREFIX_PATH as
521 // the prefix path. We've since changed the default to true for
522 // Apple platforms, since sandboxed applications are always
523 // relocated, even for static builds. To avoid regressions for
524 // those that shipped non-sandboxed apps with static Qt relying
525 // on the previous behavior, we fall back to the Qt configure
526 // prefix for non-sandboxed apps.
527 if (!qt_apple_isSandboxed())
528 return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH);
529# endif
530 prefixPath = QLibraryPrefixes::appPrefix();
531#elif defined(Q_OS_WASM)
532 // Emscripten expects to find shared libraries at the root of the in-memory
533 // file system when resolving dependencies for for dlopen() calls. So that's
534 // where libqt6core.so would be.
535 prefixPath = QStringLiteral("/");
536#elif QT_CONFIG(dlopen)
537 Dl_info info;
538 int result = dladdr(reinterpret_cast<void *>(&QLibraryInfo::isDebugBuild), &info);
539 if (result > 0 && info.dli_fname)
540 prefixPath = prefixFromQtCoreLibraryHelper(QString::fromLocal8Bit(info.dli_fname));
541#elif defined(Q_OS_WIN)
542 HMODULE hModule = getWindowsModuleHandle();
543 const int kBufferSize = 4096;
544 wchar_t buffer[kBufferSize];
545 DWORD pathSize = GetModuleFileName(hModule, buffer, kBufferSize);
546 const QString qtCoreFilePath = QString::fromWCharArray(buffer, int(pathSize));
547 const QString qtCoreDirPath = QFileInfo(qtCoreFilePath).absolutePath();
548
549 hModule = reinterpret_cast<HMODULE>(QCoreApplicationPrivate::mainInstanceHandle);
550 pathSize = GetModuleFileName(hModule, buffer, kBufferSize);
551 const QString exeDirPath = QFileInfo(QString::fromWCharArray(buffer, int(pathSize))).absolutePath();
552 if (QFileInfo(exeDirPath) == QFileInfo(qtCoreDirPath)) {
553 // QtCore DLL is next to the executable. This is either a windeployqt'ed executable or an
554 // executable within the QT_HOST_BIN directory. We're detecting the latter case by checking
555 // whether there's an import library corresponding to our QtCore DLL in PREFIX/lib.
556 QStringView libdir = qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1);
557 const QLatin1Char slash('/');
558#if defined(Q_CC_MINGW)
559 const QString implibPrefix = QStringLiteral("lib");
560 const QString implibSuffix = QStringLiteral(".a");
561#else
562 const QString implibPrefix;
563 const QString implibSuffix = QStringLiteral(".lib");
564#endif
565 const QString qtCoreImpLibFileName = implibPrefix
566 + QFileInfo(qtCoreFilePath).completeBaseName() + implibSuffix;
567 const QString qtCoreImpLibPath = qtCoreDirPath
568 + slash + QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH
569 + slash + libdir
570 + slash + qtCoreImpLibFileName;
571 if (!QFileInfo::exists(qtCoreImpLibPath)) {
572 // We did not find a corresponding import library and conclude that this is a
573 // windeployqt'ed executable.
574 return exeDirPath;
575 }
576 }
577 if (!qtCoreFilePath.isEmpty())
578 prefixPath = prefixFromQtCoreLibraryHelper(qtCoreFilePath);
579#else
580#error "The chosen platform / config does not support querying for a dynamic prefix."
581#endif
582
583#if defined(Q_OS_LINUX) && !defined(QT_STATIC) && defined(__GLIBC__)
584 // QTBUG-78948: libQt5Core.so may be located in subdirectories below libdir.
585 // See "Hardware capabilities" in the ld.so documentation and the Qt 5.3.0
586 // changelog regarding SSE2 support.
587 QString libdir = fromRawStringView(qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1));
588 QDir prefixDir(prefixPath);
589 while (!prefixDir.exists(libdir)) {
590 prefixDir.cdUp();
591 prefixPath = prefixDir.absolutePath();
592 if (prefixDir.isRoot()) {
593 prefixPath.clear();
594 break;
595 }
596 }
597#endif
598
599 Q_ASSERT_X(!prefixPath.isEmpty(), "relocatablePrefixFromQt",
600 "Failed to find the Qt prefix path.");
601 return prefixPath;
602}
603#endif
604
605QString QLibraryPrefixes::resolveQtPrefix()
606{
607#if QT_CONFIG(relocatable)
608 return relocatablePrefixFromQt();
609#else
610 return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH);
611#endif
612}
613
614QString QLibraryPrefixes::qtPrefix()
615{
616#if defined(QT_STATIC)
617 // In static builds the Qt prefix resolves via the app directory (see
618 // relocatablePrefixFromQt), which is not stable until an application
619 // instance exists, and is cached and invalidated by appPrefix().
620 // Don't cache it a second time here, to avoid freezing a transient
621 // working-directory value.
622 return resolveQtPrefix();
623#else
624 return cached(&QLibraryPrefixes::m_qt, &QLibraryPrefixes::resolveQtPrefix);
625#endif
626}
627
628QLibraryInfoPrivate::LocationInfo QLibraryInfoPrivate::locationInfo(QLibraryInfo::LibraryPath location)
629{
630 /*
631 * To add a new entry in QLibraryInfo::LibraryPath, add it to the enum
632 * in qtbase/src/corelib/global/qlibraryinfo.h and:
633 * - add its relative path in the qtConfEntries[] array below
634 * (the key is what appears in a qt.conf file)
635 */
636 static constexpr auto qtConfEntries = qOffsetStringArray(
637 "Prefix", ".",
638 "Documentation", "doc", // should be ${Data}/doc
639 "Headers", "include",
640 "Libraries", "lib",
641#ifdef Q_OS_WIN
642 "LibraryExecutables", "bin",
643#else
644 "LibraryExecutables", "libexec", // should be ${ArchData}/libexec
645#endif
646 "Binaries", "bin",
647 "Plugins", "plugins", // should be ${ArchData}/plugins
648
649 "QmlImports", "qml", // should be ${ArchData}/qml
650
651 "ArchData", ".",
652 "Data", ".",
653 "Translations", "translations", // should be ${Data}/translations
654 "Examples", "examples",
655 "Tests", "tests"
656 );
657 [[maybe_unused]]
658 constexpr QByteArrayView dot{"."};
659
660 LocationInfo result;
661
662 if (int(location) < qtConfEntries.count()) {
663 result.key = QLatin1StringView(qtConfEntries.viewAt(location * 2));
664 result.defaultValue = QLatin1StringView(qtConfEntries.viewAt(location * 2 + 1));
665 if (result.key == u"QmlImports")
666 result.fallbackKey = u"Qml2Imports"_s;
667#if !defined(Q_OS_WIN) // On Windows we use the registry
668 } else if (location == QLibraryInfo::SettingsPath) {
669 result.key = "Settings"_L1;
670 result.defaultValue = QLatin1StringView(dot);
671#endif
672 }
673
674 return result;
675}
676
677#if defined(Q_OS_APPLE)
678/*!
679 \internal
680
681 Relative suffixes for the modern Apple bundle layout, reported in addition to
682 the unixy scheme when a prefix is a bundle. Root-agnostic: the same suffixes
683 apply whether rooted at the app or the Qt prefix. Mirrors the conventions that
684 macdeployqt and the CMake deployment machinery follow. Returns empty for
685 locations with no modern equivalent (only the unixy suffix is then reported).
686*/
687static QStringList appleBundleSuffixes(QLibraryInfo::LibraryPath location)
688{
689 constexpr bool isMacOS =
690 QOperatingSystemVersion::currentType() == QOperatingSystemVersion::MacOS;
691 constexpr QStringView resourcesDir = isMacOS ? u"Resources/" : u"./";
692 constexpr QStringView sharedSupportDir = u"SharedSupport/";
693
694 switch (location) {
695 case QLibraryInfo::BinariesPath:
696 return { isMacOS ? u"MacOS"_s : u"."_s };
697 case QLibraryInfo::PluginsPath:
698 return { u"PlugIns"_s };
699 case QLibraryInfo::LibrariesPath:
700 return { u"Frameworks"_s };
701 case QLibraryInfo::HeadersPath:
702 return { u"Headers"_s };
703 case QLibraryInfo::LibraryExecutablesPath:
704 return { u"Helpers"_s };
705 case QLibraryInfo::DataPath:
706 return { resourcesDir.toString() };
707 case QLibraryInfo::QmlImportsPath:
708 return { resourcesDir + u"qml"_s };
709 case QLibraryInfo::ArchDataPath:
710 return { resourcesDir + QStringLiteral(ARCH_PROCESSOR) };
711 case QLibraryInfo::TranslationsPath:
712 return { resourcesDir + u"translations"_s };
713 case QLibraryInfo::DocumentationPath:
714 return { sharedSupportDir + u"doc"_s };
715 case QLibraryInfo::ExamplesPath:
716 return { sharedSupportDir + u"examples"_s };
717 case QLibraryInfo::TestsPath:
718 return { sharedSupportDir + u"tests"_s };
719 case QLibraryInfo::PrefixPath:
720 case QLibraryInfo::SettingsPath:
721 return {};
722 }
723
724 Q_UNREACHABLE_RETURN({});
725}
726#endif
727
728/*! \fn QString QLibraryInfo::location(LibraryLocation loc)
729 \deprecated [6.0] Use path() instead.
730
731 Returns the path specified by \a loc.
732*/
733
734/*!
735 \since 6.0
736 Returns the path specified by \a p.
737
738 If there is more than one path listed in qt.conf, it will
739 only return the first one.
740 \sa paths
741*/
742QString QLibraryInfo::path(LibraryPath p)
743{
744 return QLibraryInfoPrivate::path(p);
745}
746
747/*!
748 \since 6.8
749 Returns all paths specificied by \a p.
750
751 \sa path
752 */
753QStringList QLibraryInfo::paths(LibraryPath p)
754{
755 return QLibraryInfoPrivate::paths(p);
756}
757
758/*!
759 Returns whether we have a user-written qt.conf with explicit paths.
760
761 A user-written qt.conf is an expression of exclusivity: the
762 application declares the layout it follows, so its paths replace the Qt
763 ones, and the stable fallback-values from locationInfo() are rooted at the
764 application prefix for the locations the qt.conf leaves out.
765
766 A qt.conf with MergeQtConf = true is an artifact generated by the
767 Qt QML CMake machinery to support development builds, and does not
768 qualify as a user-written qt.conf.
769
770 \internal
771*/
772static bool haveUserQtConf()
773{
774#if QT_CONFIG(settings)
775 if (QLibrarySettings *qtConf = qt_library_settings()) {
776 if (!qtConf->havePaths())
777 return false;
778
779 if (qtConf->configuration()->value("Config/MergeQtConf", false).toBool())
780 return false;
781
782 return true;
783 }
784#endif
785 return false;
786}
787
788#if QT_CONFIG(settings)
789QString QLibraryInfoPrivate::expandEnvVariables(QString ret)
790{
791 qsizetype startIndex = 0;
792 /* We support placeholders of the form $(<ENV_VAR>) in qt.conf.
793 The loop below tries to find all such placeholders, and replaces
794 them with the actual value of the ENV_VAR environment variable
795 */
796 while (true) {
797 startIndex = ret.indexOf(u'$', startIndex);
798 if (startIndex < 0)
799 break;
800 if (ret.size() < startIndex + 3)
801 break;
802 if (ret.at(startIndex + 1) != u'(') {
803 startIndex++;
804 continue;
805 }
806 qsizetype endIndex = ret.indexOf(u')', startIndex + 2);
807 if (endIndex < 0)
808 break;
809 auto envVarName = QStringView{ret}.sliced(startIndex + 2, endIndex - startIndex - 2);
810 QString value = qEnvironmentVariable(envVarName.toLocal8Bit().constData());
811 ret.replace(startIndex, endIndex - startIndex + 1, value);
812 startIndex += value.size();
813 }
814 return QDir::fromNativeSeparators(ret);
815}
816
817#endif // settings
818
819/*!
820 \internal
821
822 Roots any relative entries in \a paths at the directory returned by
823 \a resolveBaseDir, which is resolved lazily (only when there's actually a
824 relative path to root, and at most once).
825*/
826static QStringList makeAbsolute(QStringList paths, QString (*resolveBaseDir)())
827{
828 QString baseDir;
829 for (QString &path : paths) {
830 if (pathIsRelative(path)) {
831 if (baseDir.isNull())
832 baseDir = resolveBaseDir();
833 path = QDir::cleanPath(baseDir + u'/' + std::move(path));
834 }
835 }
836 return paths;
837}
838
839#if defined(Q_OS_APPLE)
840/*!
841 \internal
842
843 Whether a resolved \a prefix is rooted at an Apple bundle. Path-based, so it
844 works for any prefix (the app or the Qt prefix), not just the main bundle.
845*/
846static bool prefixIsBundle(const QString &prefix)
847{
848 QString bundlePath = prefix;
849#if defined(Q_OS_MACOS)
850 // On macOS the prefix is rooted at the bundle's Contents directory.
851 // On iOS/visionOS the prefix already is the bundle directory.
852 const QFileSystemEntry prefixInfo(prefix, QFileSystemEntry::FromInternalPath());
853 if (prefixInfo.fileName() == "Contents"_L1)
854 bundlePath = prefixInfo.path();
855#endif
856 return bool(qt_apple_bundleType(bundlePath));
857}
858#endif
859
860/*!
861 \internal
862
863 All the paths \a location can resolve to, in order of priority, deduplicated.
864*/
865QStringList QLibraryInfoPrivate::paths(QLibraryInfo::LibraryPath location)
866{
867 // The paths a qt.conf lists explicitly always come first, as they are the only
868 // ones anyone declared. What follows depends on whether the qt.conf was written
869 // by the user, as described in haveUserQtConf().
870 QStringList paths = qtConfPaths(location);
871
872 if (!haveUserQtConf()) {
873 // Without a user-written qt.conf the Qt-prefixed paths come first, so that an
874 // application running against a non-bundled Qt keeps reporting that Qt's paths
875 // first, for compatibility, as that's what consumers using path() rely on.
876 paths += qtPaths(location);
877 }
878
879 // We always report the app-rooted paths, to allow picking up libraries,
880 // plugins, and other resources from the app's location during both development
881 // and deployment. And as we use paths() in Qt itself, for e.g. plugin or QML
882 // module lookup, we will find them either way. This also covers an app bundle
883 // nested inside an outer bundle that bundles Qt, for example an app extension
884 // inside its main application, where each bundle may hold part of the
885 // deployment story.
886 paths += appPaths(location);
887
888 paths.removeDuplicates();
889 return paths;
890}
891
892/*!
893 \internal
894
895 The paths a qt.conf lists explicitly for \a location, rooted at the app
896 prefix. Empty if there is no qt.conf, or it doesn't list the location.
897
898 Both a user-written and a generated qt.conf are reported here. A generated
899 one merges with the Qt build defaults, and is an overlay: the locations it
900 lists still take priority, as that's the point of the mechanism, but it
901 neither suppresses the other paths, nor implies anything about the locations
902 it leaves out.
903*/
904QStringList QLibraryInfoPrivate::qtConfPaths(QLibraryInfo::LibraryPath location)
905{
906#if QT_CONFIG(settings)
907 if (QLibrarySettings *qtConfSettings = qt_library_settings()) {
908 if (location == QLibraryInfo::PrefixPath) {
909 // An explicit Prefix is resolved against the application directory,
910 // and not the app prefix like the other values, as we'd otherwise
911 // end up double-applying any relative paths in the prefix.
912 if (!qtConfSettings->paths(location).isEmpty())
913 return { QLibraryPrefixes::appPrefix() };
914 else
915 return {};
916 }
917
918 return makeAbsolute(qtConfSettings->paths(location), &QLibraryPrefixes::appPrefix);
919 }
920#else
921 Q_UNUSED(location);
922#endif
923 return {};
924}
925
926/*!
927 \internal
928
929 The paths implied by the application's own layout for \a location, rooted at
930 the app prefix.
931
932 We add the modern Apple suffixes when the app prefix is a bundle, followed by
933 the Unixy stable fallback-value when a user-written qt.conf says the application
934 follows that layout.
935
936 Returns empty when neither applies: a plain executable with no qt.conf, or one
937 with only a merging qt.conf, which doesn't imply that the application has paths
938 of its own. A bundle always reports its Apple suffixes, whatever the qt.conf says.
939*/
940QStringList QLibraryInfoPrivate::appPaths(QLibraryInfo::LibraryPath location)
941{
942 bool appPrefixIsBundle = false;
943#if defined(Q_OS_APPLE)
944 appPrefixIsBundle = prefixIsBundle(QLibraryPrefixes::appPrefix());
945#endif
946
947 const bool appDeclaresItsLayout = haveUserQtConf();
948
949 // The prefix itself is reported whenever anything implies that the
950 // application has paths of its own, so that the application prefix stays
951 // discoverable via QLibraryInfo, but not for a generated qt.conf alone, as
952 // that only overlays the locations it lists.
953 if (location == QLibraryInfo::PrefixPath) {
954 if (appPrefixIsBundle || appDeclaresItsLayout)
955 return { QLibraryPrefixes::appPrefix() };
956 return {};
957 }
958
959 QStringList paths;
960
961#if defined(Q_OS_APPLE)
962 if (appPrefixIsBundle)
963 paths += appleBundleSuffixes(location);
964#endif
965
966 // A bundle on its own only implies the modern Apple layout. The Unixy
967 // layout is legacy in a bundle, and outside of one it's not implied by
968 // anything but a user-written qt.conf, so we leave it to the Qt prefix
969 // otherwise.
970 if (appDeclaresItsLayout) {
971 const auto locationInfo = QLibraryInfoPrivate::locationInfo(location);
972 if (!locationInfo.defaultValue.isNull())
973 paths << locationInfo.defaultValue;
974 }
975
976 return makeAbsolute(std::move(paths), &QLibraryPrefixes::appPrefix);
977}
978
979/*!
980 \internal
981
982 The Qt-prefixed paths for \a location, rooted at the Qt prefix. The Unixy
983 configure-time suffix is preceded by the modern Apple suffixes when the Qt
984 prefix is itself a bundle, which is the case when Qt was deployed into an
985 application bundle.
986*/
987QStringList QLibraryInfoPrivate::qtPaths(QLibraryInfo::LibraryPath location)
988{
989 if (location == QLibraryInfo::PrefixPath)
990 return { QLibraryPrefixes::qtPrefix() };
991
992 QStringList paths;
993
994#if defined(Q_OS_APPLE)
995 if (prefixIsBundle(QLibraryPrefixes::qtPrefix()))
996 paths += appleBundleSuffixes(location);
997#endif
998
999 if (int(location) <= qt_configure_strs.count()) {
1000 paths << fromRawStringView(qt_configure_strs.viewAt(location - 1));
1001#if !defined(Q_OS_WIN) // On Windows we use the registry
1002 } else if (location == QLibraryInfo::SettingsPath) {
1003 constexpr QStringView path = u"" QT_CONFIGURE_SETTINGS_PATH;
1004 paths << fromRawStringView(path);
1005#endif
1006 }
1007
1008 return makeAbsolute(std::move(paths), &QLibraryPrefixes::qtPrefix);
1009}
1010
1011/*
1012 Returns the path specified by \a p.
1013 */
1014QString QLibraryInfoPrivate::path(QLibraryInfo::LibraryPath location)
1015{
1016 return paths(location).value(0, QString());
1017}
1018
1019/*!
1020 Returns additional arguments to the platform plugin matching
1021 \a platformName which can be specified as a string list using
1022 the key \c Arguments in a group called \c Platforms of the
1023 \c qt.conf file.
1024
1025 sa {Using qt.conf}
1026
1027 \internal
1028
1029 \since 5.3
1030*/
1031
1032QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
1033{
1034#if QT_CONFIG(settings)
1035 if (const QSettings *settings = QLibraryInfoPrivate::configuration()) {
1036 const QString key = "Platforms/"_L1
1037 + platformName
1038 + "Arguments"_L1;
1039 return settings->value(key).toStringList();
1040 }
1041#else
1042 Q_UNUSED(platformName);
1043#endif // settings
1044 return QStringList();
1045}
1046
1047/*!
1048 \enum QLibraryInfo::LibraryPath
1049
1050 \keyword library location
1051
1052 This enum type is used to query for a specific path:
1053
1054 \value PrefixPath The default prefix for all paths.
1055 \value DocumentationPath The path to documentation upon install.
1056 \value HeadersPath The path to all headers.
1057 \value LibrariesPath The path to installed libraries.
1058 \value LibraryExecutablesPath The path to installed executables required by libraries at runtime.
1059 \value BinariesPath The path to installed Qt binaries (tools and applications).
1060 \value PluginsPath The path to installed Qt plugins.
1061 \value QmlImportsPath The path to installed QML extensions to import.
1062 \value Qml2ImportsPath This value is deprecated. Use QmlImportsPath instead.
1063 \value ArchDataPath The path to general architecture-dependent Qt data.
1064 \value DataPath The path to general architecture-independent Qt data.
1065 \value TranslationsPath The path to translation information for Qt strings.
1066 \value ExamplesPath The path to examples upon install.
1067 \value TestsPath The path to installed Qt testcases.
1068 \value SettingsPath The path to Qt settings. Not applicable on Windows.
1069
1070 \sa path()
1071*/
1072
1073/*!
1074 \typealias QLibraryInfo::LibraryLocation
1075 \deprecated [6.0] Use LibraryPath with QLibraryInfo::path() instead.
1076*/
1077
1078/*!
1079 \headerfile <QtVersion>
1080 \inmodule QtCore
1081 \ingroup funclists
1082 \brief Information about which Qt version the application is running on,
1083 and the version it was compiled against.
1084*/
1085
1086/*!
1087 \macro QT_VERSION_STR
1088 \relates <QtVersion>
1089
1090 This macro expands to a string that specifies Qt's version number (for
1091 example, "6.1.2"). This is the version with which the application is
1092 compiled. This may be a different version than the version the application
1093 will find itself using at \e runtime.
1094
1095 \sa qVersion(), QT_VERSION
1096*/
1097
1098/*!
1099 \relates <QtVersion>
1100
1101 Returns the version number of Qt at runtime as a string (for example,
1102 "6.1.2"). This is the version of the Qt library in use at \e runtime,
1103 which need not be the version the application was \e compiled with.
1104
1105 \sa QT_VERSION_STR, QLibraryInfo::version()
1106*/
1107
1108const char *qVersion() noexcept
1109{
1110 return QT_VERSION_STR;
1111}
1112
1113#if QT_DEPRECATED_SINCE(6, 9)
1114
1115bool qSharedBuild() noexcept
1116{
1117 return QLibraryInfo::isSharedBuild();
1118}
1119
1120#endif // QT_DEPRECATED_SINCE(6, 9)
1121
1122QT_END_NAMESPACE
1123
1124#if defined(Q_CC_GNU) && defined(ELF_INTERPRETER)
1125# include <elf.h>
1126# include <stdio.h>
1127# include <stdlib.h>
1128
1129#include "private/qcoreapplication_p.h"
1130
1131QT_WARNING_DISABLE_GCC("-Wformat-overflow")
1132QT_WARNING_DISABLE_GCC("-Wattributes")
1133QT_WARNING_DISABLE_CLANG("-Wattributes")
1134QT_WARNING_DISABLE_INTEL(2621)
1135
1136# if defined(Q_OS_LINUX)
1137# include "minimum-linux_p.h"
1138# endif
1139# ifdef QT_ELF_NOTE_OS_TYPE
1140struct ElfNoteAbiTag
1141{
1142 static_assert(sizeof(Elf32_Nhdr) == sizeof(Elf64_Nhdr),
1143 "The size of an ELF note is wrong (should be 12 bytes)");
1144 struct Payload {
1145 Elf32_Word ostype = QT_ELF_NOTE_OS_TYPE;
1146 Elf32_Word major = QT_ELF_NOTE_OS_MAJOR;
1147 Elf32_Word minor = QT_ELF_NOTE_OS_MINOR;
1148# ifdef QT_ELF_NOTE_OS_PATCH
1149 Elf32_Word patch = QT_ELF_NOTE_OS_PATCH;
1150# endif
1151 };
1152
1153 Elf32_Nhdr header = {
1154 .n_namesz = sizeof(name),
1155 .n_descsz = sizeof(Payload),
1156 .n_type = NT_GNU_ABI_TAG
1157 };
1158 char name[sizeof ELF_NOTE_GNU] = ELF_NOTE_GNU; // yes, include the null terminator
1159 Payload payload = {};
1160};
1161__attribute__((section(".note.ABI-tag"), aligned(4), used))
1162extern constexpr ElfNoteAbiTag QT_MANGLE_NAMESPACE(qt_abi_tag) = {};
1163# endif
1164
1165extern const char qt_core_interpreter[] __attribute__((section(".interp")))
1166 = ELF_INTERPRETER;
1167
1168extern "C" void qt_core_boilerplate() __attribute__((force_align_arg_pointer));
1169void qt_core_boilerplate()
1170{
1171 QT_USE_NAMESPACE
1172
1173 printf("This is the QtCore library version %s\n"
1174 "%s\n"
1175 "Contact: https://www.qt.io/licensing/\n"
1176 "\n",
1177 QT_PREPEND_NAMESPACE(qt_build_string)(),
1178 QT_COPYRIGHT);
1179
1180 QByteArray libPath =
1181 qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1).toUtf8();
1182 QByteArray pluginPath =
1183 qt_configure_strs.viewAt(QLibraryInfo::PluginsPath - 1).toUtf8();
1184#if QT_CONFIG(relocatable)
1185 QByteArray prefix = QLibraryPrefixes::qtPrefix().toUtf8();
1186#else
1187 QByteArrayView prefix = QT_CONFIGURE_PREFIX_PATH;
1188#endif
1189 printf("Installation prefix: %s\n"
1190 "Library path: %s\n"
1191 "Plugin path: %s\n",
1192 prefix.data(),
1193 libPath.data(),
1194 pluginPath.data());
1195
1196 QT_PREPEND_NAMESPACE(qDumpCPUFeatures)();
1197
1198 exit(0);
1199}
1200
1201#endif
#define ARCH_FULL
static QString fromRawStringView(QStringView string)
static bool pathIsRelative(const QString &path)
void qDumpCPUFeatures()
Definition qsimd.cpp:683
#define SHARED_STRING
#define COMPILER_STRING
#define DEBUG_STRING
static QString qtPrefix()
static QString appPrefix()