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