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 the existence of a \c qt.conf with explicit
760 paths should not trigger the stable fallback-values from
761 QLibraryInfoPrivate::locationInfo() as usual, but instead
762 fall back to the Qt configure defaults (which may differ
763 from Qt installation to Qt installtion).
764
765 This mechanism is used by QtQml's internal build machinery
766 for making QML modules across different paths available
767 during development.
768
769 \internal
770*/
771static bool keepQtBuildDefaults()
772{
773#if QT_CONFIG(settings)
774 QSettings *config = QLibraryInfoPrivate::configuration();
775 return config && config->value("Config/MergeQtConf", false).toBool();
776#else
777 return false;
778#endif
779}
780
781#if QT_CONFIG(settings)
782QString QLibraryInfoPrivate::expandEnvVariables(QString ret)
783{
784 qsizetype startIndex = 0;
785 /* We support placeholders of the form $(<ENV_VAR>) in qt.conf.
786 The loop below tries to find all such placeholders, and replaces
787 them with the actual value of the ENV_VAR environment variable
788 */
789 while (true) {
790 startIndex = ret.indexOf(u'$', startIndex);
791 if (startIndex < 0)
792 break;
793 if (ret.size() < startIndex + 3)
794 break;
795 if (ret.at(startIndex + 1) != u'(') {
796 startIndex++;
797 continue;
798 }
799 qsizetype endIndex = ret.indexOf(u')', startIndex + 2);
800 if (endIndex < 0)
801 break;
802 auto envVarName = QStringView{ret}.sliced(startIndex + 2, endIndex - startIndex - 2);
803 QString value = qEnvironmentVariable(envVarName.toLocal8Bit().constData());
804 ret.replace(startIndex, endIndex - startIndex + 1, value);
805 startIndex += value.size();
806 }
807 return QDir::fromNativeSeparators(ret);
808}
809
810#endif // settings
811
812/*!
813 \internal
814
815 Roots any relative entries in \a paths at the directory returned by
816 \a resolveBaseDir, which is resolved lazily (only when there's actually a
817 relative path to root, and at most once).
818*/
819static QStringList makeAbsolute(QStringList paths, QString (*resolveBaseDir)())
820{
821 QString baseDir;
822 for (QString &path : paths) {
823 if (pathIsRelative(path)) {
824 if (baseDir.isNull())
825 baseDir = resolveBaseDir();
826 path = QDir::cleanPath(baseDir + u'/' + std::move(path));
827 }
828 }
829 return paths;
830}
831
832#if defined(Q_OS_APPLE)
833/*!
834 \internal
835
836 Whether a resolved \a prefix is rooted at an Apple bundle. Path-based, so it
837 works for any prefix (the app or the Qt prefix), not just the main bundle.
838*/
839static bool prefixIsBundle(const QString &prefix)
840{
841 QString bundlePath = prefix;
842#if defined(Q_OS_MACOS)
843 // On macOS the prefix is rooted at the bundle's Contents directory.
844 // On iOS/visionOS the prefix already is the bundle directory.
845 const QFileSystemEntry prefixInfo(prefix, QFileSystemEntry::FromInternalPath());
846 if (prefixInfo.fileName() == "Contents"_L1)
847 bundlePath = prefixInfo.path();
848#endif
849 return bool(qt_apple_bundleType(bundlePath));
850}
851#endif
852
853QStringList QLibraryInfoPrivate::paths(QLibraryInfo::LibraryPath location)
854{
855 QStringList ret = appPaths(location);
856
857 bool includeQtPaths = ret.isEmpty() || keepQtBuildDefaults();
858
859#if defined(Q_OS_APPLE)
860 // If the app prefix is an app bundle we always report bundle
861 // suffixes, but the app may still be under development, in
862 // which case we need to also include the Qt prefix paths.
863 // In theory the app bundle may also be an inner bundle of an outer
864 // bundle that bundles Qt, for example for an app extension app
865 // inside a main application. In that scenario we also want to report
866 // both bundle paths, as they may individually contain part of the
867 // deployment story.
868 includeQtPaths |= (prefixIsBundle(QLibraryPrefixes::appPrefix())
869 && QLibraryPrefixes::qtPrefix() != QLibraryPrefixes::appPrefix());
870#endif
871
872 if (includeQtPaths)
873 ret += qtPaths(location);
874
875 return ret;
876}
877
878/*!
879 \internal
880
881 The application-prefixed paths for \a location, rooted at the app prefix.
882 Sourced from a qt.conf with explicit paths, and/or — when the app prefix is
883 a bundle — the modern Apple suffixes plus the Unixy generic default. Returns
884 empty when neither source applies, signaling the caller to fall through to
885 the Qt-prefixed paths.
886*/
887QStringList QLibraryInfoPrivate::appPaths(QLibraryInfo::LibraryPath location)
888{
889 bool reportAppPaths = false;
890
891#if QT_CONFIG(settings)
892 QLibrarySettings *qtConfSettings = qt_library_settings();
893 reportAppPaths = qtConfSettings && qtConfSettings->havePaths();
894#endif
895
896#if defined(Q_OS_APPLE)
897 if (prefixIsBundle(QLibraryPrefixes::appPrefix()))
898 reportAppPaths = true;
899#endif
900
901 // We don't report app paths unless we have a qualifying qt.conf,
902 // or the app prefix is a bundle on Apple platforms.
903 if (!reportAppPaths)
904 return {};
905
906 if (location == QLibraryInfo::PrefixPath)
907 return { QLibraryPrefixes::appPrefix() };
908
909 QStringList paths;
910
911#if QT_CONFIG(settings)
912 if (qtConfSettings && qtConfSettings->havePaths())
913 paths += qtConfSettings->paths(location);
914#endif
915
916 if (paths.isEmpty()) {
917#if defined(Q_OS_APPLE)
918 if (prefixIsBundle(QLibraryPrefixes::appPrefix()))
919 paths += appleBundleSuffixes(location);
920#endif
921
922 const auto locationInfo = QLibraryInfoPrivate::locationInfo(location);
923 if (!locationInfo.defaultValue.isNull())
924 paths << locationInfo.defaultValue;
925 }
926
927 return makeAbsolute(std::move(paths), &QLibraryPrefixes::appPrefix);
928}
929
930/*!
931 \internal
932
933 The Qt-prefixed paths for \a location, rooted at the Qt prefix. The Unixy
934 configure-time suffix are preceded by the modern Apple suffixes when the Qt
935 prefix is itself a bundle.
936*/
937QStringList QLibraryInfoPrivate::qtPaths(QLibraryInfo::LibraryPath location)
938{
939 if (location == QLibraryInfo::PrefixPath)
940 return { QLibraryPrefixes::qtPrefix() };
941
942 QStringList paths;
943
944#if defined(Q_OS_APPLE)
945 if (prefixIsBundle(QLibraryPrefixes::qtPrefix()))
946 paths += appleBundleSuffixes(location);
947#endif
948
949 if (int(location) <= qt_configure_strs.count()) {
950 paths << fromRawStringView(qt_configure_strs.viewAt(location - 1));
951#if !defined(Q_OS_WIN) // On Windows we use the registry
952 } else if (location == QLibraryInfo::SettingsPath) {
953 constexpr QStringView path = u"" QT_CONFIGURE_SETTINGS_PATH;
954 paths << fromRawStringView(path);
955#endif
956 }
957
958 return makeAbsolute(std::move(paths), &QLibraryPrefixes::qtPrefix);
959}
960
961/*
962 Returns the path specified by \a p.
963 */
964QString QLibraryInfoPrivate::path(QLibraryInfo::LibraryPath location)
965{
966 return paths(location).value(0, QString());
967}
968
969/*!
970 Returns additional arguments to the platform plugin matching
971 \a platformName which can be specified as a string list using
972 the key \c Arguments in a group called \c Platforms of the
973 \c qt.conf file.
974
975 sa {Using qt.conf}
976
977 \internal
978
979 \since 5.3
980*/
981
982QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
983{
984#if QT_CONFIG(settings)
985 if (const QSettings *settings = QLibraryInfoPrivate::configuration()) {
986 const QString key = "Platforms/"_L1
987 + platformName
988 + "Arguments"_L1;
989 return settings->value(key).toStringList();
990 }
991#else
992 Q_UNUSED(platformName);
993#endif // settings
994 return QStringList();
995}
996
997/*!
998 \enum QLibraryInfo::LibraryPath
999
1000 \keyword library location
1001
1002 This enum type is used to query for a specific path:
1003
1004 \value PrefixPath The default prefix for all paths.
1005 \value DocumentationPath The path to documentation upon install.
1006 \value HeadersPath The path to all headers.
1007 \value LibrariesPath The path to installed libraries.
1008 \value LibraryExecutablesPath The path to installed executables required by libraries at runtime.
1009 \value BinariesPath The path to installed Qt binaries (tools and applications).
1010 \value PluginsPath The path to installed Qt plugins.
1011 \value QmlImportsPath The path to installed QML extensions to import.
1012 \value Qml2ImportsPath This value is deprecated. Use QmlImportsPath instead.
1013 \value ArchDataPath The path to general architecture-dependent Qt data.
1014 \value DataPath The path to general architecture-independent Qt data.
1015 \value TranslationsPath The path to translation information for Qt strings.
1016 \value ExamplesPath The path to examples upon install.
1017 \value TestsPath The path to installed Qt testcases.
1018 \value SettingsPath The path to Qt settings. Not applicable on Windows.
1019
1020 \sa path()
1021*/
1022
1023/*!
1024 \typealias QLibraryInfo::LibraryLocation
1025 \deprecated [6.0] Use LibraryPath with QLibraryInfo::path() instead.
1026*/
1027
1028/*!
1029 \headerfile <QtVersion>
1030 \inmodule QtCore
1031 \ingroup funclists
1032 \brief Information about which Qt version the application is running on,
1033 and the version it was compiled against.
1034*/
1035
1036/*!
1037 \macro QT_VERSION_STR
1038 \relates <QtVersion>
1039
1040 This macro expands to a string that specifies Qt's version number (for
1041 example, "6.1.2"). This is the version with which the application is
1042 compiled. This may be a different version than the version the application
1043 will find itself using at \e runtime.
1044
1045 \sa qVersion(), QT_VERSION
1046*/
1047
1048/*!
1049 \relates <QtVersion>
1050
1051 Returns the version number of Qt at runtime as a string (for example,
1052 "6.1.2"). This is the version of the Qt library in use at \e runtime,
1053 which need not be the version the application was \e compiled with.
1054
1055 \sa QT_VERSION_STR, QLibraryInfo::version()
1056*/
1057
1058const char *qVersion() noexcept
1059{
1060 return QT_VERSION_STR;
1061}
1062
1063#if QT_DEPRECATED_SINCE(6, 9)
1064
1065bool qSharedBuild() noexcept
1066{
1067 return QLibraryInfo::isSharedBuild();
1068}
1069
1070#endif // QT_DEPRECATED_SINCE(6, 9)
1071
1072QT_END_NAMESPACE
1073
1074#if defined(Q_CC_GNU) && defined(ELF_INTERPRETER)
1075# include <elf.h>
1076# include <stdio.h>
1077# include <stdlib.h>
1078
1079#include "private/qcoreapplication_p.h"
1080
1081QT_WARNING_DISABLE_GCC("-Wformat-overflow")
1082QT_WARNING_DISABLE_GCC("-Wattributes")
1083QT_WARNING_DISABLE_CLANG("-Wattributes")
1084QT_WARNING_DISABLE_INTEL(2621)
1085
1086# if defined(Q_OS_LINUX)
1087# include "minimum-linux_p.h"
1088# endif
1089# ifdef QT_ELF_NOTE_OS_TYPE
1090struct ElfNoteAbiTag
1091{
1092 static_assert(sizeof(Elf32_Nhdr) == sizeof(Elf64_Nhdr),
1093 "The size of an ELF note is wrong (should be 12 bytes)");
1094 struct Payload {
1095 Elf32_Word ostype = QT_ELF_NOTE_OS_TYPE;
1096 Elf32_Word major = QT_ELF_NOTE_OS_MAJOR;
1097 Elf32_Word minor = QT_ELF_NOTE_OS_MINOR;
1098# ifdef QT_ELF_NOTE_OS_PATCH
1099 Elf32_Word patch = QT_ELF_NOTE_OS_PATCH;
1100# endif
1101 };
1102
1103 Elf32_Nhdr header = {
1104 .n_namesz = sizeof(name),
1105 .n_descsz = sizeof(Payload),
1106 .n_type = NT_GNU_ABI_TAG
1107 };
1108 char name[sizeof ELF_NOTE_GNU] = ELF_NOTE_GNU; // yes, include the null terminator
1109 Payload payload = {};
1110};
1111__attribute__((section(".note.ABI-tag"), aligned(4), used))
1112extern constexpr ElfNoteAbiTag QT_MANGLE_NAMESPACE(qt_abi_tag) = {};
1113# endif
1114
1115extern const char qt_core_interpreter[] __attribute__((section(".interp")))
1116 = ELF_INTERPRETER;
1117
1118extern "C" void qt_core_boilerplate() __attribute__((force_align_arg_pointer));
1119void qt_core_boilerplate()
1120{
1121 QT_USE_NAMESPACE
1122
1123 printf("This is the QtCore library version %s\n"
1124 "%s\n"
1125 "Contact: https://www.qt.io/licensing/\n"
1126 "\n",
1127 QT_PREPEND_NAMESPACE(qt_build_string)(),
1128 QT_COPYRIGHT);
1129
1130 QByteArray libPath =
1131 qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1).toUtf8();
1132 QByteArray pluginPath =
1133 qt_configure_strs.viewAt(QLibraryInfo::PluginsPath - 1).toUtf8();
1134#if QT_CONFIG(relocatable)
1135 QByteArray prefix = QLibraryPrefixes::qtPrefix().toUtf8();
1136#else
1137 QByteArrayView prefix = QT_CONFIGURE_PREFIX_PATH;
1138#endif
1139 printf("Installation prefix: %s\n"
1140 "Library path: %s\n"
1141 "Plugin path: %s\n",
1142 prefix.data(),
1143 libPath.data(),
1144 pluginPath.data());
1145
1146 QT_PREPEND_NAMESPACE(qDumpCPUFeatures)();
1147
1148 exit(0);
1149}
1150
1151#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()