Revision control
++ // Copyright (C) 2021 The Qt Company Ltd.
++ // Copyright (C) 2016 Intel Corporation.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "qmakelibraryinfo.h"
++
++ #include <qcoreapplication.h>
++ #include <qdir.h>
++ #include <qfile.h>
++ #include <qglobalstatic.h>
++ #include <qsettings.h>
++ #include <qscopedpointer.h>
++ #include <qstringlist.h>
++ #include <private/qlibraryinfo_p.h>
++
++ #include <utility>
++
++ QT_BEGIN_NAMESPACE
++
++ struct QMakeLibrarySettings
++ {
++ QMakeLibrarySettings() { load(); }
++ void load();
++
++ bool haveDevicePaths;
++ bool haveEffectiveSourcePaths;
++ bool haveEffectivePaths;
++ bool havePaths;
++ };
++ Q_GLOBAL_STATIC(QMakeLibrarySettings, qmake_library_settings)
++
++ void QMakeLibrarySettings::load()
++ {
++ QSettings *settings = QLibraryInfoPrivate::configuration();
++ if (settings) {
++ QStringList children = settings->childGroups();
++ haveDevicePaths = children.contains(QLatin1String("DevicePaths"));
++ haveEffectiveSourcePaths = children.contains(QLatin1String("EffectiveSourcePaths"));
++ haveEffectivePaths =
++ haveEffectiveSourcePaths || children.contains(QLatin1String("EffectivePaths"));
++ // Backwards compat: an existing but empty file is claimed to contain the Paths section.
++ havePaths = (!haveDevicePaths && !haveEffectivePaths
++ && !children.contains(QLatin1String("Platforms")))
++ || children.contains(QLatin1String("Paths"));
++ } else {
++ haveDevicePaths = false;
++ haveEffectiveSourcePaths = false;
++ haveEffectivePaths = false;
++ havePaths = false;
++ }
++ }
++
++ void QMakeLibraryInfo::reload()
++ {
++ QLibraryInfoPrivate::reload();
++ if (qmake_library_settings.exists())
++ qmake_library_settings->load();
++ }
++
++ bool QMakeLibraryInfo::haveGroup(PathGroup group)
++ {
++ QMakeLibrarySettings *ls = qmake_library_settings();
++ return ls
++ && (group == EffectiveSourcePaths ? ls->haveEffectiveSourcePaths
++ : group == EffectivePaths ? ls->haveEffectivePaths
++ : group == DevicePaths ? ls->haveDevicePaths
++ : ls->havePaths);
++ }
++
++ void QMakeLibraryInfo::sysrootify(QString &path)
++ {
++ // Acceptable values for SysrootifyPrefixPath are "true" and "false"
++ if (!QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool())
++ return;
++
++ const QString sysroot = rawLocation(SysrootPath, FinalPaths);
++ if (sysroot.isEmpty())
++ return;
++
++ if (path.size() > 2 && path.at(1) == QLatin1Char(':')
++ && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) {
++ path.replace(0, 2, sysroot); // Strip out the drive on Windows targets
++ } else {
++ path.prepend(sysroot);
++ }
++ }
++
++ QString QMakeLibraryInfo::path(int loc)
++ {
++ QString ret = rawLocation(loc, QMakeLibraryInfo::FinalPaths);
++
++ // Automatically prepend the sysroot to target paths
++ if (loc < QMakeLibraryInfo::FirstHostPath)
++ sysrootify(ret);
++
++ return ret;
++ }
++
++ static QLibraryInfo::LibraryPath hostToTargetPathEnum(int loc)
++ {
++ static std::pair<int, QLibraryInfo::LibraryPath> mapping[] = {
++ { QMakeLibraryInfo::HostBinariesPath, QLibraryInfo::BinariesPath },
++ { QMakeLibraryInfo::HostLibraryExecutablesPath, QLibraryInfo::LibraryExecutablesPath },
++ { QMakeLibraryInfo::HostLibrariesPath, QLibraryInfo::LibrariesPath },
++ { QMakeLibraryInfo::HostDataPath, QLibraryInfo::DataPath },
++ { QMakeLibraryInfo::HostPrefixPath, QLibraryInfo::PrefixPath }
++ };
++ for (size_t i = 0; i < sizeof(mapping) / sizeof(mapping[0]); ++i) {
++ if (mapping[i].first == loc)
++ return mapping[i].second;
++ }
++ qFatal("Unhandled host path %d in hostToTargetPathEnum.", loc);
++ Q_UNREACHABLE();
++ }
++
++ static QLibraryInfoPrivate::LocationInfo defaultLocationInfo(int loc)
++ {
++ QLibraryInfoPrivate::LocationInfo result;
++
++ if (loc < QMakeLibraryInfo::FirstHostPath) {
++ result = QLibraryInfoPrivate::locationInfo(static_cast<QLibraryInfo::LibraryPath>(loc));
++ } else if (loc <= QMakeLibraryInfo::LastHostPath) {
++ result = QLibraryInfoPrivate::locationInfo(hostToTargetPathEnum(loc));
++ result.key.prepend(QStringLiteral("Host"));
++ } else if (loc == QMakeLibraryInfo::SysrootPath) {
++ result.key = QStringLiteral("Sysroot");
++ } else if (loc == QMakeLibraryInfo::SysrootifyPrefixPath) {
++ result.key = QStringLiteral("SysrootifyPrefix");
++ } else if (loc == QMakeLibraryInfo::TargetSpecPath) {
++ result.key = QStringLiteral("TargetSpec");
++ } else if (loc == QMakeLibraryInfo::HostSpecPath) {
++ result.key = QStringLiteral("HostSpec");
++ }
++ return result;
++ }
++
++ static QString libraryInfoPath(QLibraryInfo::LibraryPath location)
++ {
++ return QLibraryInfoPrivate::path(location);
++ }
++
++ static QString storedPath(int loc)
++ {
++ QString result;
++ if (loc < QMakeLibraryInfo::FirstHostPath) {
++ result = libraryInfoPath(static_cast<QLibraryInfo::LibraryPath>(loc));
++ } else if (loc <= QMakeLibraryInfo::LastHostPath) {
++ if (loc == QMakeLibraryInfo::HostDataPath) {
++ // Handle QT_HOST_DATADIR specially. It is not necessarily equal to QT_INSTALL_DATA.
++ result = QT_HOST_DATADIR;
++ } else {
++ result = libraryInfoPath(hostToTargetPathEnum(loc));
++ }
++ } else if (loc == QMakeLibraryInfo::SysrootPath) {
++ // empty result
++ } else if (loc == QMakeLibraryInfo::SysrootifyPrefixPath) {
++ result = QStringLiteral("false");
++ } else if (loc == QMakeLibraryInfo::TargetSpecPath) {
++ result = QT_TARGET_MKSPEC;
++ } else if (loc == QMakeLibraryInfo::HostSpecPath) {
++ result = QT_HOST_MKSPEC;
++ }
++ return result;
++ }
++
++ QString QMakeLibraryInfo::rawLocation(int loc, QMakeLibraryInfo::PathGroup group)
++ {
++ QString ret;
++ bool fromConf = false;
++ // Logic for choosing the right data source: if EffectivePaths are requested
++ // and qt.conf with that section is present, use it, otherwise fall back to
++ // FinalPaths. For FinalPaths, use qt.conf if present and contains not only
++ // [EffectivePaths], otherwise fall back to builtins.
++ // EffectiveSourcePaths falls back to EffectivePaths.
++ // DevicePaths falls back to FinalPaths.
++ PathGroup orig_group = group;
++ if (QMakeLibraryInfo::haveGroup(group)
++ || (group == EffectiveSourcePaths
++ && (group = EffectivePaths, QMakeLibraryInfo::haveGroup(group)))
++ || ((group == EffectivePaths || group == DevicePaths)
++ && (group = FinalPaths, QMakeLibraryInfo::haveGroup(group)))
++ || (group = orig_group, false)) {
++ fromConf = true;
++
++ QLibraryInfoPrivate::LocationInfo locinfo = defaultLocationInfo(loc);
++ if (!locinfo.key.isNull()) {
++ QSettings *config = QLibraryInfoPrivate::configuration();
++ Q_ASSERT(config != nullptr);
++ config->beginGroup(QLatin1String(group == DevicePaths ? "DevicePaths"
++ : group == EffectiveSourcePaths
++ ? "EffectiveSourcePaths"
++ : group == EffectivePaths ? "EffectivePaths"
++ : "Paths"));
++
++ if (locinfo.fallbackKey.isNull()) {
++ ret = config->value(locinfo.key).toString();
++ } else {
++ QVariant v = config->value(locinfo.key);
++ if (!v.isValid())
++ v = config->value(locinfo.fallbackKey);
++ ret = v.toString();
++ }
++
++ if (ret.isEmpty()) {
++ // Host paths that a qt.conf does not list follow the target paths.
++ if (loc == HostPrefixPath || loc == TargetSpecPath || loc == HostSpecPath
++ || loc == SysrootifyPrefixPath || loc == QLibraryInfo::PrefixPath
++ || (loc >= FirstHostPath && loc <= LastHostPath)) {
++ fromConf = false;
++ } else {
++ ret = locinfo.defaultValue;
++ }
++ // The last case here is SysrootPath, which can be legitimately empty.
++ // All other keys have non-empty fallbacks to start with.
++ }
++
++ ret = QLibraryInfoPrivate::expandEnvVariables(ret);
++ config->endGroup();
++ }
++ }
++
++ if (!fromConf)
++ ret = storedPath(loc);
++
++ // These values aren't actually paths and thus need to be returned verbatim.
++ if (loc == TargetSpecPath || loc == HostSpecPath || loc == SysrootifyPrefixPath)
++ return ret;
++
++ if (!ret.isEmpty() && QDir::isRelativePath(ret)) {
++ QString baseDir;
++ if (loc == HostPrefixPath || loc == QLibraryInfo::PrefixPath || loc == SysrootPath) {
++ // We make the prefix/sysroot path absolute to the directory of the qt.conf
++ // file. This is crucial for cross-builds, where the qmake wrapper passes a
++ // target_qt.conf located in the target prefix to the host qmake binary: the
++ // relative prefix must be resolved against that qt.conf, not against the host
++ // qmake's location.
++ // loc == PrefixPath while a sysroot is set would make no sense here.
++ // loc == SysrootPath only makes sense if qmake lives inside the sysroot itself.
++ if (QSettings *config = QLibraryInfoPrivate::configuration()) {
++ const QString configFileName = config->fileName();
++ if (configFileName.startsWith(u':')) {
++ // qt.conf is embedded as resource. We can't resolve against a resource path.
++ baseDir = QCoreApplication::applicationDirPath();
++ } else {
++ baseDir = QFileInfo(configFileName).absolutePath();
++ }
++ }
++ } else if (loc >= FirstHostPath && loc <= LastHostPath) {
++ // We make any other host path absolute to the host prefix directory.
++ baseDir = rawLocation(HostPrefixPath, group);
++ } else {
++ // we make any other path absolute to the prefix directory
++ baseDir = rawLocation(QLibraryInfo::PrefixPath, group);
++ if (group == EffectivePaths)
++ sysrootify(baseDir);
++ }
++ ret = QDir::cleanPath(baseDir + QLatin1Char('/') + ret);
++ }
++ return ret;
++ }
++
++ QT_END_NAMESPACE