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 | |
5 | #include "qdir.h" |
6 | #include "qstringlist.h" |
7 | #include "qfile.h" |
8 | #if QT_CONFIG(settings) |
9 | #include "qresource.h" |
10 | #include "qsettings.h" |
11 | #endif |
12 | #include "qlibraryinfo.h" |
13 | #include "qlibraryinfo_p.h" |
14 | |
15 | #include "qcoreapplication.h" |
16 | |
17 | #include "private/qfilesystementry_p.h" |
18 | #include "archdetect.cpp" |
19 | #include "qconfig.cpp" |
20 | |
21 | #ifdef Q_OS_DARWIN |
22 | # include "private/qcore_mac_p.h" |
23 | #endif // Q_OS_DARWIN |
24 | |
25 | #if QT_CONFIG(relocatable) && QT_CONFIG(dlopen) && !QT_CONFIG(framework) |
26 | # include <dlfcn.h> |
27 | #endif |
28 | |
29 | #if QT_CONFIG(relocatable) && defined(Q_OS_WIN) |
30 | # include <qt_windows.h> |
31 | #endif |
32 | |
33 | #include <memory> |
34 | |
35 | QT_BEGIN_NAMESPACE |
36 | |
37 | using namespace Qt::StringLiterals; |
38 | |
39 | extern void qDumpCPUFeatures(); // in qsimd.cpp |
40 | |
41 | #if QT_CONFIG(settings) |
42 | |
43 | static std::unique_ptr<QSettings> findConfiguration(); |
44 | |
45 | struct QLibrarySettings |
46 | { |
47 | QLibrarySettings(); |
48 | void load(); |
49 | bool havePaths(); |
50 | QSettings *configuration(); |
51 | |
52 | std::unique_ptr<QSettings> settings; |
53 | bool paths; |
54 | bool reloadOnQAppAvailable; |
55 | }; |
56 | Q_GLOBAL_STATIC(QLibrarySettings, qt_library_settings) |
57 | |
58 | QLibrarySettings::QLibrarySettings() : paths(false), reloadOnQAppAvailable(false) |
59 | { |
60 | load(); |
61 | } |
62 | |
63 | QSettings *QLibrarySettings::configuration() |
64 | { |
65 | if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr) |
66 | load(); |
67 | return settings.get(); |
68 | } |
69 | |
70 | bool QLibrarySettings::havePaths() |
71 | { |
72 | if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr) |
73 | load(); |
74 | return paths; |
75 | } |
76 | |
77 | void QLibrarySettings::load() |
78 | { |
79 | // If we get any settings here, those won't change when the application shows up. |
80 | settings = findConfiguration(); |
81 | reloadOnQAppAvailable = !settings && !QCoreApplication::instance(); |
82 | |
83 | if (settings) { |
84 | // This code needs to be in the regular library, as otherwise a qt.conf that |
85 | // works for qmake would break things for dynamically built Qt tools. |
86 | QStringList children = settings->childGroups(); |
87 | paths = !children.contains(str: "Platforms"_L1) |
88 | || children.contains(str: "Paths"_L1); |
89 | } |
90 | } |
91 | |
92 | namespace { |
93 | const QString *qtconfManualPath = nullptr; |
94 | } |
95 | |
96 | void QLibraryInfoPrivate::setQtconfManualPath(const QString *path) |
97 | { |
98 | qtconfManualPath = path; |
99 | } |
100 | |
101 | static std::unique_ptr<QSettings> findConfiguration() |
102 | { |
103 | if (qtconfManualPath) |
104 | return std::make_unique<QSettings>(args: *qtconfManualPath, args: QSettings::IniFormat); |
105 | |
106 | QString qtconfig = QStringLiteral(":/qt/etc/qt.conf"); |
107 | if (QResource(qtconfig, QLocale::c()).isValid()) |
108 | return std::make_unique<QSettings>(args&: qtconfig, args: QSettings::IniFormat); |
109 | #ifdef Q_OS_DARWIN |
110 | CFBundleRef bundleRef = CFBundleGetMainBundle(); |
111 | if (bundleRef) { |
112 | QCFType<CFURLRef> urlRef = CFBundleCopyResourceURL(bundleRef, |
113 | QCFString("qt.conf"_L1), |
114 | 0, |
115 | 0); |
116 | if (urlRef) { |
117 | QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); |
118 | qtconfig = QDir::cleanPath(path); |
119 | if (QFile::exists(qtconfig)) |
120 | return std::make_unique<QSettings>(qtconfig, QSettings::IniFormat); |
121 | } |
122 | } |
123 | #endif |
124 | if (QCoreApplication::instance()) { |
125 | QString pwd = QCoreApplication::applicationDirPath(); |
126 | qtconfig = pwd + u"/qt"QT_STRINGIFY(QT_VERSION_MAJOR) ".conf"_s; |
127 | if (QFile::exists(fileName: qtconfig)) |
128 | return std::make_unique<QSettings>(args&: qtconfig, args: QSettings::IniFormat); |
129 | qtconfig = pwd + u"/qt.conf"; |
130 | if (QFile::exists(fileName: qtconfig)) |
131 | return std::make_unique<QSettings>(args&: qtconfig, args: QSettings::IniFormat); |
132 | } |
133 | return nullptr; //no luck |
134 | } |
135 | |
136 | QSettings *QLibraryInfoPrivate::configuration() |
137 | { |
138 | QLibrarySettings *ls = qt_library_settings(); |
139 | return ls ? ls->configuration() : nullptr; |
140 | } |
141 | |
142 | void QLibraryInfoPrivate::reload() |
143 | { |
144 | if (qt_library_settings.exists()) |
145 | qt_library_settings->load(); |
146 | } |
147 | |
148 | static bool havePaths() { |
149 | QLibrarySettings *ls = qt_library_settings(); |
150 | return ls && ls->havePaths(); |
151 | } |
152 | |
153 | #endif // settings |
154 | |
155 | /*! |
156 | \class QLibraryInfo |
157 | \inmodule QtCore |
158 | \brief The QLibraryInfo class provides information about the Qt library. |
159 | |
160 | Many pieces of information are established when Qt is configured and built. |
161 | This class provides an abstraction for accessing that information. |
162 | By using the static functions of this class, an application can obtain |
163 | information about the instance of the Qt library which the application |
164 | is using at run-time. |
165 | |
166 | You can also use a \c qt.conf file to override the hard-coded paths |
167 | that are compiled into the Qt library. For more information, see |
168 | the \l {Using qt.conf} documentation. |
169 | |
170 | \sa QSysInfo, {Using qt.conf} |
171 | */ |
172 | |
173 | /*! |
174 | \internal |
175 | |
176 | You cannot create a QLibraryInfo, instead only the static functions are available to query |
177 | information. |
178 | */ |
179 | |
180 | QLibraryInfo::QLibraryInfo() |
181 | { } |
182 | |
183 | #if defined(Q_CC_CLANG) // must be before GNU, because clang claims to be GNU too |
184 | # define COMPILER_STRING __VERSION__ /* already includes the compiler's name */ |
185 | #elif defined(Q_CC_GHS) |
186 | # define COMPILER_STRING "GHS " QT_STRINGIFY(__GHS_VERSION_NUMBER) |
187 | #elif defined(Q_CC_GNU) |
188 | # define COMPILER_STRING "GCC " __VERSION__ |
189 | #elif defined(Q_CC_MSVC) |
190 | # if _MSC_VER < 1910 |
191 | # define COMPILER_STRING "MSVC 2015" |
192 | # elif _MSC_VER < 1917 |
193 | # define COMPILER_STRING "MSVC 2017" |
194 | # elif _MSC_VER < 1930 |
195 | # define COMPILER_STRING "MSVC 2019" |
196 | # elif _MSC_VER < 2000 |
197 | # define COMPILER_STRING "MSVC 2022" |
198 | # else |
199 | # define COMPILER_STRING "MSVC _MSC_VER " QT_STRINGIFY(_MSC_VER) |
200 | # endif |
201 | #else |
202 | # define COMPILER_STRING "<unknown compiler>" |
203 | #endif |
204 | #ifdef QT_NO_DEBUG |
205 | # define DEBUG_STRING " release" |
206 | #else |
207 | # define DEBUG_STRING " debug" |
208 | #endif |
209 | #ifdef QT_SHARED |
210 | # define SHARED_STRING " shared (dynamic)" |
211 | #else |
212 | # define SHARED_STRING " static" |
213 | #endif |
214 | static const char *qt_build_string() noexcept |
215 | { |
216 | return "Qt "QT_VERSION_STR " ("ARCH_FULL SHARED_STRING DEBUG_STRING " build; by "COMPILER_STRING ")"; |
217 | } |
218 | |
219 | /*! |
220 | Returns a string describing how this version of Qt was built. |
221 | |
222 | \internal |
223 | |
224 | \since 5.3 |
225 | */ |
226 | |
227 | const char *QLibraryInfo::build() noexcept |
228 | { |
229 | return qt_build_string(); |
230 | } |
231 | |
232 | /*! |
233 | \since 5.0 |
234 | Returns \c true if this build of Qt was built with debugging enabled, or |
235 | false if it was built in release mode. |
236 | */ |
237 | bool |
238 | QLibraryInfo::isDebugBuild() noexcept |
239 | { |
240 | #ifdef QT_DEBUG |
241 | return true; |
242 | #else |
243 | return false; |
244 | #endif |
245 | } |
246 | |
247 | /*! |
248 | \since 6.5 |
249 | Returns \c true if this is a shared (dynamic) build of Qt. |
250 | */ |
251 | bool QLibraryInfo::isSharedBuild() noexcept |
252 | { |
253 | #ifdef QT_SHARED |
254 | return true; |
255 | #else |
256 | return false; |
257 | #endif |
258 | } |
259 | |
260 | /*! |
261 | \since 5.8 |
262 | Returns the version of the Qt library. |
263 | |
264 | \sa qVersion() |
265 | */ |
266 | QVersionNumber QLibraryInfo::version() noexcept |
267 | { |
268 | return QVersionNumber(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH); |
269 | } |
270 | |
271 | static QString prefixFromAppDirHelper() |
272 | { |
273 | if (QCoreApplication::instance()) { |
274 | #ifdef Q_OS_DARWIN |
275 | CFBundleRef bundleRef = CFBundleGetMainBundle(); |
276 | if (bundleRef) { |
277 | QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); |
278 | if (urlRef) { |
279 | QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); |
280 | #ifdef Q_OS_MACOS |
281 | QString bundleContentsDir = QString(path) + "/Contents/"_L1; |
282 | if (QDir(bundleContentsDir).exists()) |
283 | return QDir::cleanPath(bundleContentsDir); |
284 | #else |
285 | return QDir::cleanPath(QString(path)); // iOS |
286 | #endif // Q_OS_MACOS |
287 | } |
288 | } |
289 | #endif // Q_OS_DARWIN |
290 | // We make the prefix path absolute to the executable's directory. |
291 | return QCoreApplication::applicationDirPath(); |
292 | } else { |
293 | return QDir::currentPath(); |
294 | } |
295 | } |
296 | |
297 | #if QT_CONFIG(relocatable) |
298 | #if !defined(QT_STATIC) && !(defined(Q_OS_DARWIN) && QT_CONFIG(framework)) \ |
299 | && (QT_CONFIG(dlopen) || defined(Q_OS_WIN)) |
300 | static QString prefixFromQtCoreLibraryHelper(const QString &qtCoreLibraryPath) |
301 | { |
302 | const QString qtCoreLibrary = QDir::fromNativeSeparators(pathName: qtCoreLibraryPath); |
303 | const QString libDir = QFileInfo(qtCoreLibrary).absolutePath(); |
304 | const QString prefixDir = libDir + "/"QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH; |
305 | return QDir::cleanPath(path: prefixDir); |
306 | } |
307 | #endif |
308 | |
309 | #if defined(Q_OS_WIN) |
310 | static HMODULE getWindowsModuleHandle() |
311 | { |
312 | HMODULE hModule = NULL; |
313 | GetModuleHandleEx( |
314 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, |
315 | (LPCTSTR)&QLibraryInfo::isDebugBuild, &hModule); |
316 | return hModule; |
317 | } |
318 | #endif // Q_OS_WIN |
319 | |
320 | static QString getRelocatablePrefix(QLibraryInfoPrivate::UsageMode usageMode) |
321 | { |
322 | QString prefixPath; |
323 | |
324 | // For static builds, the prefix will be the app directory. |
325 | // For regular builds, the prefix will be relative to the location of the QtCore shared library. |
326 | #if defined(QT_STATIC) |
327 | prefixPath = prefixFromAppDirHelper(); |
328 | if (usageMode == QLibraryInfoPrivate::UsedFromQtBinDir) { |
329 | // For Qt tools in a static build, we must chop off the bin directory. |
330 | constexpr QByteArrayView binDir = qt_configure_strs.viewAt(QLibraryInfo::BinariesPath - 1); |
331 | constexpr size_t binDirLength = binDir.size() + 1; |
332 | prefixPath.chop(binDirLength); |
333 | } |
334 | #elif defined(Q_OS_DARWIN) && QT_CONFIG(framework) |
335 | Q_UNUSED(usageMode); |
336 | #ifndef QT_LIBINFIX |
337 | #define QT_LIBINFIX "" |
338 | #endif |
339 | auto qtCoreBundle = CFBundleGetBundleWithIdentifier(CFSTR("org.qt-project.QtCore"QT_LIBINFIX)); |
340 | if (!qtCoreBundle) { |
341 | // When running Qt apps over Samba shares, CoreFoundation will fail to find |
342 | // the Resources directory inside the bundle, This directory is a symlink, |
343 | // and CF relies on readdir() and dtent.dt_type to detect symlinks, which |
344 | // does not work reliably for Samba shares. We work around it by manually |
345 | // looking for the QtCore bundle. |
346 | auto allBundles = CFBundleGetAllBundles(); |
347 | auto bundleCount = CFArrayGetCount(allBundles); |
348 | for (int i = 0; i < bundleCount; ++i) { |
349 | auto bundle = CFBundleRef(CFArrayGetValueAtIndex(allBundles, i)); |
350 | auto url = QCFType<CFURLRef>(CFBundleCopyBundleURL(bundle)); |
351 | auto path = QCFType<CFStringRef>(CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle)); |
352 | if (CFStringHasSuffix(path, CFSTR("/QtCore"QT_LIBINFIX ".framework"))) { |
353 | qtCoreBundle = bundle; |
354 | break; |
355 | } |
356 | } |
357 | } |
358 | Q_ASSERT(qtCoreBundle); |
359 | |
360 | QCFType<CFURLRef> qtCorePath = CFBundleCopyBundleURL(qtCoreBundle); |
361 | Q_ASSERT(qtCorePath); |
362 | |
363 | QCFType<CFURLRef> qtCorePathAbsolute = CFURLCopyAbsoluteURL(qtCorePath); |
364 | Q_ASSERT(qtCorePathAbsolute); |
365 | |
366 | QCFType<CFURLRef> libDirCFPath = CFURLCreateCopyDeletingLastPathComponent(NULL, qtCorePathAbsolute); |
367 | |
368 | const QCFString libDirCFString = CFURLCopyFileSystemPath(libDirCFPath, kCFURLPOSIXPathStyle); |
369 | |
370 | const QString prefixDir = QString(libDirCFString) + "/"QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH; |
371 | |
372 | prefixPath = QDir::cleanPath(prefixDir); |
373 | #elif defined(Q_OS_WASM) |
374 | // Emscripten expects to find shared libraries at the root of the in-memory |
375 | // file system when resolving dependencies for for dlopen() calls. So that's |
376 | // where libqt6core.so would be. |
377 | prefixPath = QStringLiteral("/"); |
378 | #elif QT_CONFIG(dlopen) |
379 | Q_UNUSED(usageMode); |
380 | Dl_info info; |
381 | int result = dladdr(address: reinterpret_cast<void *>(&QLibraryInfo::isDebugBuild), info: &info); |
382 | if (result > 0 && info.dli_fname) |
383 | prefixPath = prefixFromQtCoreLibraryHelper(qtCoreLibraryPath: QString::fromLocal8Bit(ba: info.dli_fname)); |
384 | #elif defined(Q_OS_WIN) |
385 | Q_UNUSED(usageMode); |
386 | HMODULE hModule = getWindowsModuleHandle(); |
387 | const int kBufferSize = 4096; |
388 | wchar_t buffer[kBufferSize]; |
389 | DWORD pathSize = GetModuleFileName(hModule, buffer, kBufferSize); |
390 | const QString qtCoreFilePath = QString::fromWCharArray(buffer, int(pathSize)); |
391 | const QString qtCoreDirPath = QFileInfo(qtCoreFilePath).absolutePath(); |
392 | pathSize = GetModuleFileName(NULL, buffer, kBufferSize); |
393 | const QString exeDirPath = QFileInfo(QString::fromWCharArray(buffer, int(pathSize))).absolutePath(); |
394 | if (QFileInfo(exeDirPath) == QFileInfo(qtCoreDirPath)) { |
395 | // QtCore DLL is next to the executable. This is either a windeployqt'ed executable or an |
396 | // executable within the QT_HOST_BIN directory. We're detecting the latter case by checking |
397 | // whether there's an import library corresponding to our QtCore DLL in PREFIX/lib. |
398 | const QString libdir = QString::fromLocal8Bit( |
399 | qt_configure_strs.viewAt(QLibraryInfo::LibrariesPath - 1)); |
400 | const QLatin1Char slash('/'); |
401 | #if defined(Q_CC_MINGW) |
402 | const QString implibPrefix = QStringLiteral("lib"); |
403 | const QString implibSuffix = QStringLiteral(".a"); |
404 | #else |
405 | const QString implibPrefix; |
406 | const QString implibSuffix = QStringLiteral(".lib"); |
407 | #endif |
408 | const QString qtCoreImpLibFileName = implibPrefix |
409 | + QFileInfo(qtCoreFilePath).completeBaseName() + implibSuffix; |
410 | const QString qtCoreImpLibPath = qtCoreDirPath |
411 | + slash + QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH |
412 | + slash + libdir |
413 | + slash + qtCoreImpLibFileName; |
414 | if (!QFileInfo::exists(qtCoreImpLibPath)) { |
415 | // We did not find a corresponding import library and conclude that this is a |
416 | // windeployqt'ed executable. |
417 | return exeDirPath; |
418 | } |
419 | } |
420 | if (!qtCoreFilePath.isEmpty()) |
421 | prefixPath = prefixFromQtCoreLibraryHelper(qtCoreFilePath); |
422 | #else |
423 | #error "The chosen platform / config does not support querying for a dynamic prefix." |
424 | #endif |
425 | |
426 | #if defined(Q_OS_LINUX) && !defined(QT_STATIC) && defined(__GLIBC__) |
427 | // QTBUG-78948: libQt5Core.so may be located in subdirectories below libdir. |
428 | // See "Hardware capabilities" in the ld.so documentation and the Qt 5.3.0 |
429 | // changelog regarding SSE2 support. |
430 | const QString libdir = QString::fromLocal8Bit( |
431 | ba: qt_configure_strs.viewAt(index: QLibraryInfo::LibrariesPath - 1)); |
432 | QDir prefixDir(prefixPath); |
433 | while (!prefixDir.exists(name: libdir)) { |
434 | prefixDir.cdUp(); |
435 | prefixPath = prefixDir.absolutePath(); |
436 | if (prefixDir.isRoot()) { |
437 | prefixPath.clear(); |
438 | break; |
439 | } |
440 | } |
441 | #endif |
442 | |
443 | Q_ASSERT_X(!prefixPath.isEmpty(), "getRelocatablePrefix", |
444 | "Failed to find the Qt prefix path."); |
445 | return prefixPath; |
446 | } |
447 | #endif |
448 | |
449 | static QString getPrefix(QLibraryInfoPrivate::UsageMode usageMode) |
450 | { |
451 | #if QT_CONFIG(relocatable) |
452 | return getRelocatablePrefix(usageMode); |
453 | #else |
454 | Q_UNUSED(usageMode); |
455 | return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH); |
456 | #endif |
457 | } |
458 | |
459 | QLibraryInfoPrivate::LocationInfo QLibraryInfoPrivate::locationInfo(QLibraryInfo::LibraryPath loc) |
460 | { |
461 | /* |
462 | * To add a new entry in QLibraryInfo::LibraryPath, add it to the enum |
463 | * in qtbase/src/corelib/global/qlibraryinfo.h and: |
464 | * - add its relative path in the qtConfEntries[] array below |
465 | * (the key is what appears in a qt.conf file) |
466 | */ |
467 | static constexpr auto qtConfEntries = qOffsetStringArray( |
468 | strings: "Prefix", strings: ".", |
469 | strings: "Documentation", strings: "doc", // should be ${Data}/doc |
470 | strings: "Headers", strings: "include", |
471 | strings: "Libraries", strings: "lib", |
472 | #ifdef Q_OS_WIN |
473 | "LibraryExecutables", "bin", |
474 | #else |
475 | strings: "LibraryExecutables", strings: "libexec", // should be ${ArchData}/libexec |
476 | #endif |
477 | strings: "Binaries", strings: "bin", |
478 | strings: "Plugins", strings: "plugins", // should be ${ArchData}/plugins |
479 | |
480 | strings: "QmlImports", strings: "qml", // should be ${ArchData}/qml |
481 | |
482 | strings: "ArchData", strings: ".", |
483 | strings: "Data", strings: ".", |
484 | strings: "Translations", strings: "translations", // should be ${Data}/translations |
485 | strings: "Examples", strings: "examples", |
486 | strings: "Tests", strings: "tests" |
487 | ); |
488 | [[maybe_unused]] |
489 | constexpr QByteArrayView dot{"."}; |
490 | |
491 | LocationInfo result; |
492 | |
493 | if (int(loc) < qtConfEntries.count()) { |
494 | result.key = QLatin1StringView(qtConfEntries.viewAt(index: loc * 2)); |
495 | result.defaultValue = QLatin1StringView(qtConfEntries.viewAt(index: loc * 2 + 1)); |
496 | if (result.key == u"QmlImports") |
497 | result.fallbackKey = u"Qml2Imports"_s; |
498 | #ifndef Q_OS_WIN // On Windows we use the registry |
499 | } else if (loc == QLibraryInfo::SettingsPath) { |
500 | result.key = "Settings"_L1; |
501 | result.defaultValue = QLatin1StringView(dot); |
502 | #endif |
503 | } |
504 | |
505 | return result; |
506 | } |
507 | |
508 | /*! \fn QString QLibraryInfo::location(LibraryLocation loc) |
509 | \deprecated [6.0] Use path() instead. |
510 | |
511 | Returns the path specified by \a loc. |
512 | */ |
513 | |
514 | /*! |
515 | \since 6.0 |
516 | Returns the path specified by \a p. |
517 | |
518 | If there is more than one path listed in qt.conf, it will |
519 | only return the first one. |
520 | \sa paths |
521 | */ |
522 | QString QLibraryInfo::path(LibraryPath p) |
523 | { |
524 | return QLibraryInfoPrivate::path(p); |
525 | } |
526 | |
527 | /*! |
528 | \since 6.8 |
529 | Returns all paths specificied by \a p. |
530 | |
531 | \sa path |
532 | */ |
533 | QStringList QLibraryInfo::paths(LibraryPath p) |
534 | { |
535 | return QLibraryInfoPrivate::paths(p); |
536 | } |
537 | |
538 | static bool keepQtBuildDefaults() |
539 | { |
540 | #if QT_CONFIG(settings) |
541 | QSettings *config = QLibraryInfoPrivate::configuration(); |
542 | Q_ASSERT(config != nullptr); |
543 | return config->value(key: "Config/MergeQtConf", defaultValue: false).toBool(); |
544 | #else |
545 | return false; |
546 | #endif |
547 | } |
548 | |
549 | #if QT_CONFIG(settings) |
550 | static QString normalizePath(QString ret) |
551 | { |
552 | qsizetype startIndex = 0; |
553 | /* We support placeholders of the form $(<ENV_VAR>) in qt.conf. |
554 | The loop below tries to find all such placeholders, and replaces |
555 | them with the actual value of the ENV_VAR environment variable |
556 | */ |
557 | while (true) { |
558 | startIndex = ret.indexOf(c: u'$', from: startIndex); |
559 | if (startIndex < 0) |
560 | break; |
561 | if (ret.size() < startIndex + 3) |
562 | break; |
563 | if (ret.at(i: startIndex + 1) != u'(') { |
564 | startIndex++; |
565 | continue; |
566 | } |
567 | qsizetype endIndex = ret.indexOf(c: u')', from: startIndex + 2); |
568 | if (endIndex < 0) |
569 | break; |
570 | auto envVarName = QStringView{ret}.sliced(pos: startIndex + 2, n: endIndex - startIndex - 2); |
571 | QString value = qEnvironmentVariable(varName: envVarName.toLocal8Bit().constData()); |
572 | ret.replace(i: startIndex, len: endIndex - startIndex + 1, after: value); |
573 | startIndex += value.size(); |
574 | } |
575 | return QDir::fromNativeSeparators(pathName: ret); |
576 | }; |
577 | |
578 | static QVariant libraryPathToValue(QLibraryInfo::LibraryPath loc) |
579 | { |
580 | QVariant value; |
581 | auto li = QLibraryInfoPrivate::locationInfo(loc); |
582 | if (li.key.isNull()) |
583 | return value; |
584 | QSettings *config = QLibraryInfoPrivate::configuration(); |
585 | Q_ASSERT(config != nullptr); |
586 | // if keepQtBuildDefaults returns true, |
587 | // we only consider explicit values listed in qt.conf |
588 | QVariant defaultValue = keepQtBuildDefaults() |
589 | ? QVariant() |
590 | : QVariant(li.defaultValue); |
591 | config->beginGroup(prefix: "Paths"_L1); |
592 | auto cleanup = qScopeGuard(f: [&]() { config->endGroup(); }); |
593 | if (li.fallbackKey.isNull()) { |
594 | value = config->value(key: li.key, defaultValue); |
595 | } else { |
596 | value = config->value(key: li.key); |
597 | if (!value.isValid()) |
598 | value = config->value(key: li.fallbackKey, defaultValue); |
599 | } |
600 | return value; |
601 | } |
602 | #endif // settings |
603 | |
604 | // TODO: There apparently are paths that are both absolute and relative for QFileSystemEntry. |
605 | // In particular on windows. |
606 | |
607 | static bool pathIsRelative(const QString &path) |
608 | { |
609 | using FromInternalPath = QFileSystemEntry::FromInternalPath; |
610 | return !path.startsWith(c: ':'_L1) && QFileSystemEntry(path, FromInternalPath{}).isRelative(); |
611 | } |
612 | |
613 | static bool pathIsAbsolute(const QString &path) |
614 | { |
615 | using FromInternalPath = QFileSystemEntry::FromInternalPath; |
616 | return path.startsWith(c: ':'_L1) || QFileSystemEntry(path, FromInternalPath{}).isAbsolute(); |
617 | } |
618 | |
619 | QStringList QLibraryInfoPrivate::paths(QLibraryInfo::LibraryPath p, |
620 | UsageMode usageMode) |
621 | { |
622 | const QLibraryInfo::LibraryPath loc = p; |
623 | QList<QString> ret; |
624 | bool fromConf = false; |
625 | bool pathsAreAbsolute = true; |
626 | #if QT_CONFIG(settings) |
627 | if (havePaths()) { |
628 | fromConf = true; |
629 | |
630 | QVariant value = libraryPathToValue(loc); |
631 | if (value.isValid()) { |
632 | if (auto *asList = get_if<QList<QString>>(v: &value)) |
633 | ret = std::move(*asList); |
634 | else |
635 | ret = QList<QString>({ std::move(value).toString()}); |
636 | for (qsizetype i = 0, end = ret.size(); i < end; ++i) { |
637 | ret[i] = normalizePath(ret: ret[i]); |
638 | pathsAreAbsolute = pathsAreAbsolute && pathIsAbsolute(path: ret[i]); |
639 | } |
640 | } |
641 | } |
642 | #endif // settings |
643 | |
644 | if (!fromConf || keepQtBuildDefaults()) { |
645 | QString noConfResult; |
646 | if (loc == QLibraryInfo::PrefixPath) { |
647 | noConfResult = getPrefix(usageMode); |
648 | } else if (int(loc) <= qt_configure_strs.count()) { |
649 | noConfResult = QString::fromLocal8Bit(ba: qt_configure_strs.viewAt(index: loc - 1)); |
650 | #ifndef Q_OS_WIN // On Windows we use the registry |
651 | } else if (loc == QLibraryInfo::SettingsPath) { |
652 | // Use of volatile is a hack to discourage compilers from calling |
653 | // strlen(), in the inlined fromLocal8Bit(const char *)'s body, at |
654 | // compile-time, as Qt installers binary-patch the path, replacing |
655 | // the dummy path seen at compile-time, typically changing length. |
656 | const char *volatile path = QT_CONFIGURE_SETTINGS_PATH; |
657 | noConfResult = QString::fromLocal8Bit(ba: path); |
658 | #endif |
659 | } |
660 | if (!noConfResult.isEmpty()) { |
661 | pathsAreAbsolute = pathsAreAbsolute && pathIsAbsolute(path: noConfResult); |
662 | ret.push_back(t: std::move(noConfResult)); |
663 | } |
664 | } |
665 | if (ret.isEmpty() || pathsAreAbsolute) |
666 | return ret; |
667 | |
668 | QString baseDir; |
669 | if (loc == QLibraryInfo::PrefixPath) { |
670 | baseDir = prefixFromAppDirHelper(); |
671 | } else { |
672 | // we make any other path absolute to the prefix directory |
673 | baseDir = QLibraryInfoPrivate::path(p: QLibraryInfo::PrefixPath, usageMode); |
674 | } |
675 | for (qsizetype i = 0, end = ret.size(); i < end; ++i) |
676 | if (pathIsRelative(path: ret[i])) |
677 | ret[i] = QDir::cleanPath(path: baseDir + u'/' + std::move(ret[i])); |
678 | return ret; |
679 | } |
680 | |
681 | /* |
682 | Returns the path specified by \a p. |
683 | |
684 | The usage mode can be set to UsedFromQtBinDir to enable special handling for executables that |
685 | live in <install-prefix>/bin. |
686 | */ |
687 | QString QLibraryInfoPrivate::path(QLibraryInfo::LibraryPath p, UsageMode usageMode) |
688 | { |
689 | return paths(p, usageMode).value(i: 0, defaultValue: QString()); |
690 | } |
691 | |
692 | /*! |
693 | Returns additional arguments to the platform plugin matching |
694 | \a platformName which can be specified as a string list using |
695 | the key \c Arguments in a group called \c Platforms of the |
696 | \c qt.conf file. |
697 | |
698 | sa {Using qt.conf} |
699 | |
700 | \internal |
701 | |
702 | \since 5.3 |
703 | */ |
704 | |
705 | QStringList QLibraryInfo::platformPluginArguments(const QString &platformName) |
706 | { |
707 | #if QT_CONFIG(settings) |
708 | if (const auto settings = findConfiguration()) { |
709 | const QString key = "Platforms/"_L1 |
710 | + platformName |
711 | + "Arguments"_L1; |
712 | return settings->value(key).toStringList(); |
713 | } |
714 | #else |
715 | Q_UNUSED(platformName); |
716 | #endif // settings |
717 | return QStringList(); |
718 | } |
719 | |
720 | /*! |
721 | \enum QLibraryInfo::LibraryPath |
722 | |
723 | \keyword library location |
724 | |
725 | This enum type is used to query for a specific path: |
726 | |
727 | \value PrefixPath The default prefix for all paths. |
728 | \value DocumentationPath The path to documentation upon install. |
729 | \value HeadersPath The path to all headers. |
730 | \value LibrariesPath The path to installed libraries. |
731 | \value LibraryExecutablesPath The path to installed executables required by libraries at runtime. |
732 | \value BinariesPath The path to installed Qt binaries (tools and applications). |
733 | \value PluginsPath The path to installed Qt plugins. |
734 | \value QmlImportsPath The path to installed QML extensions to import. |
735 | \value Qml2ImportsPath This value is deprecated. Use QmlImportsPath instead. |
736 | \value ArchDataPath The path to general architecture-dependent Qt data. |
737 | \value DataPath The path to general architecture-independent Qt data. |
738 | \value TranslationsPath The path to translation information for Qt strings. |
739 | \value ExamplesPath The path to examples upon install. |
740 | \value TestsPath The path to installed Qt testcases. |
741 | \value SettingsPath The path to Qt settings. Not applicable on Windows. |
742 | |
743 | \sa path() |
744 | */ |
745 | |
746 | /*! |
747 | \typealias QLibraryInfo::LibraryLocation |
748 | \deprecated [6.0] Use LibraryPath with QLibraryInfo::path() instead. |
749 | */ |
750 | |
751 | /*! |
752 | \macro QT_VERSION_STR |
753 | \relates <QtVersion> |
754 | |
755 | This macro expands to a string that specifies Qt's version number (for |
756 | example, "6.1.2"). This is the version with which the application is |
757 | compiled. This may be a different version than the version the application |
758 | will find itself using at \e runtime. |
759 | |
760 | \sa qVersion(), QT_VERSION |
761 | */ |
762 | |
763 | /*! |
764 | \relates <QtVersion> |
765 | |
766 | Returns the version number of Qt at runtime as a string (for example, |
767 | "6.1.2"). This is the version of the Qt library in use at \e runtime, |
768 | which need not be the version the application was \e compiled with. |
769 | |
770 | \sa QT_VERSION_STR, QLibraryInfo::version() |
771 | */ |
772 | |
773 | const char *qVersion() noexcept |
774 | { |
775 | return QT_VERSION_STR; |
776 | } |
777 | |
778 | #if QT_DEPRECATED_SINCE(6, 9) |
779 | |
780 | bool qSharedBuild() noexcept |
781 | { |
782 | return QLibraryInfo::isSharedBuild(); |
783 | } |
784 | |
785 | #endif // QT_DEPRECATED_SINCE(6, 9) |
786 | |
787 | QT_END_NAMESPACE |
788 | |
789 | #if defined(Q_CC_GNU) && defined(ELF_INTERPRETER) |
790 | # include <elf.h> |
791 | # include <stdio.h> |
792 | # include <stdlib.h> |
793 | |
794 | #include "private/qcoreapplication_p.h" |
795 | |
796 | QT_WARNING_DISABLE_GCC("-Wformat-overflow") |
797 | QT_WARNING_DISABLE_GCC("-Wattributes") |
798 | QT_WARNING_DISABLE_CLANG("-Wattributes") |
799 | QT_WARNING_DISABLE_INTEL(2621) |
800 | |
801 | # if defined(Q_OS_LINUX) |
802 | # include "minimum-linux_p.h" |
803 | # endif |
804 | # ifdef QT_ELF_NOTE_OS_TYPE |
805 | struct ElfNoteAbiTag |
806 | { |
807 | static_assert(sizeof(Elf32_Nhdr) == sizeof(Elf64_Nhdr), |
808 | "The size of an ELF note is wrong (should be 12 bytes)"); |
809 | struct Payload { |
810 | Elf32_Word ostype = QT_ELF_NOTE_OS_TYPE; |
811 | Elf32_Word major = QT_ELF_NOTE_OS_MAJOR; |
812 | Elf32_Word minor = QT_ELF_NOTE_OS_MINOR; |
813 | # ifdef QT_ELF_NOTE_OS_PATCH |
814 | Elf32_Word patch = QT_ELF_NOTE_OS_PATCH; |
815 | # endif |
816 | }; |
817 | |
818 | Elf32_Nhdr header = { |
819 | .n_namesz = sizeof(name), |
820 | .n_descsz = sizeof(Payload), |
821 | .n_type = NT_GNU_ABI_TAG |
822 | }; |
823 | char name[sizeof ELF_NOTE_GNU] = ELF_NOTE_GNU; // yes, include the null terminator |
824 | Payload payload = {}; |
825 | }; |
826 | __attribute__((section(".note.ABI-tag"), aligned(4), used)) |
827 | extern constexpr ElfNoteAbiTag QT_MANGLE_NAMESPACE(qt_abi_tag) = {}; |
828 | # endif |
829 | |
830 | extern const char qt_core_interpreter[] __attribute__((section(".interp"))) |
831 | = ELF_INTERPRETER; |
832 | |
833 | extern "C"void qt_core_boilerplate() __attribute__((force_align_arg_pointer)); |
834 | void qt_core_boilerplate() |
835 | { |
836 | printf(format: "This is the QtCore library version %s\n" |
837 | "%s\n" |
838 | "Contact: https://www.qt.io/licensing/\n" |
839 | "\n" |
840 | "Installation prefix: %s\n" |
841 | "Library path: %s\n" |
842 | "Plugin path: %s\n", |
843 | QT_PREPEND_NAMESPACE(qt_build_string)(), |
844 | QT_COPYRIGHT, |
845 | QT_CONFIGURE_PREFIX_PATH, |
846 | qt_configure_strs[QT_PREPEND_NAMESPACE(QLibraryInfo)::LibrariesPath - 1], |
847 | qt_configure_strs[QT_PREPEND_NAMESPACE(QLibraryInfo)::PluginsPath - 1]); |
848 | |
849 | QT_PREPEND_NAMESPACE(qDumpCPUFeatures)(); |
850 | |
851 | exit(status: 0); |
852 | } |
853 | |
854 | #endif |
855 |
Definitions
- QLibrarySettings
- qt_library_settings
- QLibrarySettings
- configuration
- havePaths
- load
- qtconfManualPath
- setQtconfManualPath
- findConfiguration
- configuration
- reload
- havePaths
- QLibraryInfo
- qt_build_string
- build
- isDebugBuild
- isSharedBuild
- version
- prefixFromAppDirHelper
- prefixFromQtCoreLibraryHelper
- getRelocatablePrefix
- getPrefix
- locationInfo
- path
- paths
- keepQtBuildDefaults
- normalizePath
- libraryPathToValue
- pathIsRelative
- pathIsAbsolute
- paths
- path
- platformPluginArguments
- qVersion
- qSharedBuild
- ElfNoteAbiTag
- Payload
- qt_abi_tag
- qt_core_interpreter
Learn Advanced QML with KDAB
Find out more